mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 00:31:34 +11:00
add dynamic sprite support
This commit is contained in:
parent
1acf7142da
commit
5d541631ae
|
@ -154,12 +154,14 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream {
|
||||||
let width = f.width;
|
let width = f.width;
|
||||||
let height = f.height;
|
let height = f.height;
|
||||||
quote! {
|
quote! {
|
||||||
|
unsafe {
|
||||||
Sprite::new(
|
Sprite::new(
|
||||||
&PALETTES[#assignment],
|
&PALETTES[#assignment],
|
||||||
align_bytes!(u16, #data),
|
align_bytes!(u16, #data),
|
||||||
Size::from_width_height(#width, #height)
|
Size::from_width_height(#width, #height)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let tags = tags.iter().flat_map(|(tag, num_images)| {
|
let tags = tags.iter().flat_map(|(tag, num_images)| {
|
||||||
|
|
|
@ -108,6 +108,50 @@ pub struct Sprite {
|
||||||
size: Size,
|
size: Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sprite data that can be used to create sprites in vram.
|
||||||
|
pub struct DynamicSprite<'a> {
|
||||||
|
data: &'a [u8],
|
||||||
|
size: Size,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynamicSprite<'_> {
|
||||||
|
#[must_use]
|
||||||
|
/// Creates a new dynamic sprite from underlying bytes. Note that despite
|
||||||
|
/// being an array of u8, this must be aligned to at least a 2 byte
|
||||||
|
/// boundary.
|
||||||
|
pub fn new(data: &[u8], size: Size) -> DynamicSprite {
|
||||||
|
let ptr = &data[0] as *const _ as usize;
|
||||||
|
if ptr % 2 != 0 {
|
||||||
|
panic!("data is not aligned to a 2 byte boundary");
|
||||||
|
}
|
||||||
|
if data.len() != size.number_of_tiles() * BYTES_PER_TILE_4BPP {
|
||||||
|
panic!(
|
||||||
|
"data is not of expected length, got {} expected {}",
|
||||||
|
data.len(),
|
||||||
|
size.number_of_tiles() * BYTES_PER_TILE_4BPP
|
||||||
|
);
|
||||||
|
}
|
||||||
|
DynamicSprite { data, size }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Tries to copy the sprite to vram to be used to set object sprites.
|
||||||
|
/// Returns None if there is no room in sprite vram.
|
||||||
|
pub fn try_vram(&self, palette: PaletteVram) -> Option<SpriteBorrow> {
|
||||||
|
Some(SpriteBorrow {
|
||||||
|
sprite: unsafe { SpriteVram::new(self.data, self.size, palette)? },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Tries to copy the sprite to vram to be used to set object sprites.
|
||||||
|
/// Panics if there is no room in sprite vram.
|
||||||
|
pub fn to_vram(&self, palette: PaletteVram) -> SpriteBorrow {
|
||||||
|
self.try_vram(palette)
|
||||||
|
.expect("No slot for sprite available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The sizes of sprite supported by the GBA.
|
/// The sizes of sprite supported by the GBA.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
@ -522,7 +566,9 @@ impl Location {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct PaletteVram(Rc<PaletteData>);
|
/// The palette data in Vram, this is reference counted and the palette data is
|
||||||
|
/// removed and can be reused from vram when no strong references remain.
|
||||||
|
pub struct PaletteVram(Rc<PaletteData>);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct SpriteVram(Rc<SpriteArena>);
|
struct SpriteVram(Rc<SpriteArena>);
|
||||||
|
@ -531,6 +577,45 @@ struct PaletteData {
|
||||||
location: Location,
|
location: Location,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PaletteVram {
|
||||||
|
/// Creates a palette in vram from the given palette. Can be used to create
|
||||||
|
/// sprites in vram in the [DynamicSprite] functions.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(palette: &Palette16) -> Option<Self> {
|
||||||
|
let dest = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout())? };
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
dma::dma_copy16(
|
||||||
|
palette.colours.as_ptr().cast(),
|
||||||
|
dest.as_ptr().cast(),
|
||||||
|
palette.colours.len(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(PaletteVram(Rc::new(PaletteData {
|
||||||
|
location: Location::from_palette_ptr(dest),
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpriteVram {
|
||||||
|
/// # Safety
|
||||||
|
/// data should be aligned to a 2 byte boundary
|
||||||
|
unsafe fn new(data: &[u8], size: Size, palette: PaletteVram) -> Option<Self> {
|
||||||
|
let dest = unsafe { SPRITE_ALLOCATOR.alloc(size.layout())? };
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
dma::dma_copy16(data.as_ptr().cast(), dest.as_ptr().cast(), data.len() / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(SpriteVram(Rc::new(SpriteArena {
|
||||||
|
location: Location::from_sprite_ptr(dest),
|
||||||
|
size,
|
||||||
|
palette,
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Drop for PaletteData {
|
impl Drop for PaletteData {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { PALETTE_ALLOCATOR.dealloc(self.location.as_palette_ptr(), Palette16::layout()) };
|
unsafe { PALETTE_ALLOCATOR.dealloc(self.location.as_palette_ptr(), Palette16::layout()) };
|
||||||
|
@ -1041,8 +1126,11 @@ impl Sprite {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// Creates a sprite from it's constituent data, used internally by
|
/// Creates a sprite from it's constituent data, used internally by
|
||||||
/// [include_aseprite] and should generally not be used outside it.
|
/// [include_aseprite] and should generally not be used outside it.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// The data should be aligned to a 2 byte boundary
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new(palette: &'static Palette16, data: &'static [u8], size: Size) -> Self {
|
pub const unsafe fn new(palette: &'static Palette16, data: &'static [u8], size: Size) -> Self {
|
||||||
Self {
|
Self {
|
||||||
palette,
|
palette,
|
||||||
data,
|
data,
|
||||||
|
@ -1057,7 +1145,7 @@ impl Sprite {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpriteControllerInner {
|
impl SpriteControllerInner {
|
||||||
fn try_get_sprite<'a>(&mut self, sprite: &'static Sprite) -> Option<SpriteBorrow> {
|
fn try_get_sprite(&mut self, sprite: &'static Sprite) -> Option<SpriteBorrow> {
|
||||||
let id = sprite.id();
|
let id = sprite.id();
|
||||||
if let Some(storage) = self.static_sprite_map.get_mut(&id) {
|
if let Some(storage) = self.static_sprite_map.get_mut(&id) {
|
||||||
if let Some(strong) = storage.upgrade() {
|
if let Some(strong) = storage.upgrade() {
|
||||||
|
@ -1069,17 +1157,16 @@ impl SpriteControllerInner {
|
||||||
|
|
||||||
// layout is non zero sized, so this is safe to call
|
// layout is non zero sized, so this is safe to call
|
||||||
|
|
||||||
let dest = unsafe { SPRITE_ALLOCATOR.alloc(sprite.layout())? };
|
|
||||||
|
|
||||||
let palette_location = self.palette(sprite.palette);
|
let palette_location = self.palette(sprite.palette);
|
||||||
let palette_location = match palette_location {
|
let palette_location = match palette_location {
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => {
|
None => {
|
||||||
unsafe { SPRITE_ALLOCATOR.dealloc(dest.as_ptr(), sprite.layout()) }
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let dest = unsafe { SPRITE_ALLOCATOR.alloc(sprite.layout())? };
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
dma::dma_copy16(
|
dma::dma_copy16(
|
||||||
sprite.data.as_ptr().cast(),
|
sprite.data.as_ptr().cast(),
|
||||||
|
@ -1110,23 +1197,12 @@ impl SpriteControllerInner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let dest = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout())? };
|
let palette_vram = PaletteVram::new(palette)?;
|
||||||
|
|
||||||
unsafe {
|
|
||||||
dma::dma_copy16(
|
|
||||||
palette.colours.as_ptr().cast(),
|
|
||||||
dest.as_ptr().cast(),
|
|
||||||
palette.colours.len(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let storage = PaletteVram(Rc::new(PaletteData {
|
|
||||||
location: Location::from_palette_ptr(dest),
|
|
||||||
}));
|
|
||||||
self.static_palette_map
|
self.static_palette_map
|
||||||
.insert(id, Rc::downgrade(&storage.0));
|
.insert(id, Rc::downgrade(&palette_vram.0));
|
||||||
|
|
||||||
Some(storage)
|
Some(palette_vram)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue