use core::ffi::c_int;
use core::ffi::c_void;
use core::ffi::c_float;
use core::marker::PhantomData;
use alloc::boxed::Box;
use sys::traits::AsRaw;
use sys::ffi::SpriteCollisionInfo;
use sys::ffi::LCDRect;
use sys::ffi::LCDSprite;
use sys::ffi::PDRect;
use gfx::bitmap::AnyBitmap;
use gfx::bitmap::BitmapRef;
use gfx::bitmap::BitmapDrawMode;
use gfx::bitmap::BitmapFlip;
use crate::AnySprite;
use crate::SpriteApi;
use crate::TypedSprite;
use crate::api;
pub use crate::ext::*;
pub type OwnedSprite<Userdata, Api> = Sprite<Userdata, Api, true>;
pub type SharedSprite<Userdata, Api> = Sprite<Userdata, Api, false>;
impl<UD, Api: api::Api, const FOD: bool> TypedSprite for Sprite<UD, Api, FOD> {
type Userdata = UD;
const FREE_ON_DROP: bool = FOD;
}
impl AnySprite for SpriteRef {}
impl<UD, Api: api::Api, const FOD: bool> AnySprite for Sprite<UD, Api, FOD> {}
impl SpriteApi for SpriteRef {
type Api = api::Default;
fn api(&self) -> Self::Api
where Self::Api: Copy {
api::Default::default()
}
fn api_ref(&self) -> &Self::Api {
static API: api::Default = api::Default;
&API
}
}
impl<UD, Api: api::Api, const FOD: bool> SpriteApi for Sprite<UD, Api, FOD> {
type Api = Api;
fn api(&self) -> Api
where Self::Api: Copy {
self.1
}
fn api_ref(&self) -> &Self::Api { &self.1 }
}
#[repr(transparent)]
#[derive(Copy, Clone, Debug)]
pub struct SpriteRef(*mut LCDSprite);
impl From<*mut LCDSprite> for SpriteRef {
fn from(ptr: *mut LCDSprite) -> Self { Self(ptr) }
}
impl AsRaw for SpriteRef {
type Type = LCDSprite;
unsafe fn as_raw(&self) -> *mut LCDSprite { self.0 }
}
impl SpriteRef {
pub fn into_sprite<UD>(self) -> Sprite<UD, <Self as SpriteApi>::Api, false> {
Sprite(unsafe { self.as_raw() }, self.api(), PhantomData)
}
pub fn into_sprite_with<UD, Api: api::Api>(self, api: Api) -> Sprite<UD, Api, false> {
Sprite(unsafe { self.as_raw() }, api, PhantomData)
}
}
#[derive(Debug)]
pub struct Sprite<Userdata = (), Api: api::Api = api::Default, const FREE_ON_DROP: bool = true>(*mut LCDSprite, Api, PhantomData<Userdata>);
impl<UD, Api: api::Api, const FOD: bool> AsRaw for Sprite<UD, Api, FOD> {
type Type = LCDSprite;
unsafe fn as_raw(&self) -> *mut LCDSprite { self.0 }
}
impl<UD, Api: api::Api, const FOD: bool> AsRef<Self> for Sprite<UD, Api, FOD> {
fn as_ref(&self) -> &Self { self }
}
impl<UD, Api: api::Api, const FOD: bool> AsMut<Self> for Sprite<UD, Api, FOD> {
fn as_mut(&mut self) -> &mut Self { self }
}
impl<UD, Api, const FOD: bool> From<SpriteRef> for Sprite<UD, Api, FOD> where Api: api::Api + Default {
fn from(sprite: SpriteRef) -> Self { Self(unsafe { sprite.as_raw() }, Api::default(), PhantomData) }
}
impl<UD, Api: api::Api + Clone, const FOD: bool> Clone for Sprite<UD, Api, FOD> {
fn clone(&self) -> Self {
let f = self.1.copy();
let ptr = unsafe { f(self.0) };
Self(ptr, self.1.clone(), PhantomData)
}
}
impl<UD, Api: api::Api, const FOD: bool> Drop for Sprite<UD, Api, FOD> {
fn drop(&mut self) {
if FOD && !self.0.is_null() {
if let Some(ud) = self.take_userdata() {
drop(ud);
let f = self.1.set_userdata();
unsafe { f(self.0, core::ptr::null_mut()) }
}
let f = self.1.free_sprite();
unsafe { f(self.0) }
self.0 = core::ptr::null_mut();
}
}
}
impl<UD, Api: api::Api + Copy> Sprite<UD, Api, true> {
pub fn into_shared(mut self) -> Sprite<UD, Api, false> {
let res = Sprite(self.0, self.1, self.2);
self.0 = core::ptr::null_mut();
res
}
}
impl<UD, Api: Default + api::Api, const FOD: bool> Sprite<UD, Api, FOD> {
#[doc(alias = "sys::ffi::playdate_sprite::newSprite")]
pub fn new() -> Self {
let api = Default::default();
Self::new_with(api)
}
}
impl<UD, Api: api::Api, const FOD: bool> Sprite<UD, Api, FOD> {
#[doc(alias = "sys::ffi::playdate_sprite::newSprite")]
pub fn new_with(api: Api) -> Self {
let f = api.new_sprite();
let ptr = unsafe { f() };
Self(ptr, api, PhantomData)
}
}
impl<Userdata, Api: api::Api, const FOD: bool> Sprite<Userdata, Api, FOD> {
#[doc(alias = "sys::ffi::playdate_sprite::addSprite")]
pub fn add(&self) {
let f = self.1.add_sprite();
unsafe { f(self.0) }
}
#[doc(alias = "sys::ffi::playdate_sprite::removeSprite")]
pub fn remove(&self) {
let f = self.1.remove_sprite();
unsafe { f(self.0) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setBounds")]
pub fn set_bounds(&self, bounds: PDRect) {
let f = self.1.set_bounds();
unsafe { f(self.0, bounds) }
}
#[doc(alias = "sys::ffi::playdate_sprite::getBounds")]
pub fn bounds(&self) -> PDRect {
let f = self.1.get_bounds();
unsafe { f(self.0) }
}
#[doc(alias = "sys::ffi::playdate_sprite::moveTo")]
pub fn move_to(&self, x: c_float, y: c_float) {
let f = self.1.move_to();
unsafe { f(self.0, x, y) }
}
#[doc(alias = "sys::ffi::playdate_sprite::moveBy")]
pub fn move_by(&self, dx: c_float, dy: c_float) {
let f = self.1.move_by();
unsafe { f(self.0, dx, dy) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setImage")]
pub fn set_image(&self, image: impl AnyBitmap, flip: BitmapFlip) {
let f = self.1.set_image();
unsafe { f(self.0, image.as_raw(), flip) }
}
#[doc(alias = "sys::ffi::playdate_sprite::getImage")]
pub fn image<'t>(&'t self) -> Option<BitmapRef<'t>> {
let f = self.1.get_image();
let ptr = unsafe { f(self.0) };
if ptr.is_null() {
None
} else {
Some(BitmapRef::from(ptr))
}
}
#[doc(alias = "sys::ffi::playdate_sprite::setSize")]
pub fn set_size(&self, width: c_float, height: c_float) {
let f = self.1.set_size();
unsafe { f(self.0, width, height) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setZIndex")]
pub fn set_z_index(&self, z_index: i16) {
let f = self.1.set_z_index();
unsafe { f(self.0, z_index) }
}
#[doc(alias = "sys::ffi::playdate_sprite::getZIndex")]
pub fn z_index(&self) -> i16 {
let f = self.1.get_z_index();
unsafe { f(self.0) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setDrawMode")]
pub fn set_draw_mode(&self, mode: BitmapDrawMode) {
let f = self.1.set_draw_mode();
unsafe { f(self.0, mode) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setImageFlip")]
pub fn set_image_flip(&self, flip: BitmapFlip) {
let f = self.1.set_image_flip();
unsafe { f(self.0, flip) }
}
#[doc(alias = "sys::ffi::playdate_sprite::getImageFlip")]
pub fn image_flip(&self) -> BitmapFlip {
let f = self.1.get_image_flip();
unsafe { f(self.0) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setStencil")]
pub fn set_stencil(&self, stencil: impl AnyBitmap) {
let f = self.1.set_stencil();
unsafe { f(self.0, stencil.as_raw()) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setClipRect")]
pub fn set_clip_rect(&self, clip: LCDRect) {
let f = self.1.set_clip_rect();
unsafe { f(self.0, clip) }
}
#[doc(alias = "sys::ffi::playdate_sprite::clearClipRect")]
pub fn clear_clip_rect(&self) {
let f = self.1.clear_clip_rect();
unsafe { f(self.0) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setUpdatesEnabled")]
pub fn set_updates_enabled(&self, value: bool) {
let f = self.1.set_updates_enabled();
unsafe { f(self.0, value.into()) }
}
#[doc(alias = "sys::ffi::playdate_sprite::updatesEnabled")]
pub fn updates_enabled(&self) -> bool {
let f = self.1.updates_enabled();
unsafe { f(self.0) == 1 }
}
#[doc(alias = "sys::ffi::playdate_sprite::setCollisionsEnabled")]
pub fn set_collisions_enabled(&self, value: bool) {
let f = self.1.set_collisions_enabled();
unsafe { f(self.0, value.into()) }
}
#[doc(alias = "sys::ffi::playdate_sprite::collisionsEnabled")]
pub fn collisions_enabled(&self) -> bool {
let f = self.1.collisions_enabled();
unsafe { f(self.0) == 1 }
}
#[doc(alias = "sys::ffi::playdate_sprite::setVisible")]
pub fn set_visible(&self, value: bool) {
let f = self.1.set_visible();
unsafe { f(self.0, value.into()) }
}
#[doc(alias = "sys::ffi::playdate_sprite::isVisible")]
pub fn is_visible(&self) -> bool {
let f = self.1.is_visible();
unsafe { f(self.0) == 1 }
}
#[doc(alias = "sys::ffi::playdate_sprite::setOpaque")]
pub fn set_opaque(&self, value: bool) {
let f = self.1.set_opaque();
unsafe { f(self.0, value.into()) }
}
#[doc(alias = "sys::ffi::playdate_sprite::markDirty")]
pub fn mark_dirty(&self) {
let f = self.1.mark_dirty();
unsafe { f(self.0) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setTag")]
pub fn set_tag(&self, tag: u8) {
let f = self.1.set_tag();
unsafe { f(self.0, tag) }
}
#[doc(alias = "sys::ffi::playdate_sprite::getTag")]
pub fn tag(&self) -> u8 {
let f = self.1.get_tag();
unsafe { f(self.0) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setIgnoresDrawOffset")]
pub fn set_ignores_draw_offset(&self, value: bool) {
let f = self.1.set_ignores_draw_offset();
unsafe { f(self.0, value.into()) }
}
#[doc(alias = "sys::ffi::playdate_sprite::getPosition")]
pub fn position(&self) -> (c_float, c_float) {
let (mut x, mut y) = Default::default();
self.position_to(&mut x, &mut y);
(x, y)
}
#[doc(alias = "sys::ffi::playdate_sprite::getPosition")]
pub fn position_to(&self, x: &mut c_float, y: &mut c_float) {
let f = self.1.get_position();
unsafe { f(self.0, x, y) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setCollideRect")]
pub fn set_collide_rect(&self, collide: PDRect) {
let f = self.1.set_collide_rect();
unsafe { f(self.0, collide) }
}
#[doc(alias = "sys::ffi::playdate_sprite::getCollideRect")]
pub fn collide_rect(&self) -> PDRect {
let f = self.1.get_collide_rect();
unsafe { f(self.0) }
}
#[doc(alias = "sys::ffi::playdate_sprite::clearCollideRect")]
pub fn clear_collide_rect(&self) {
let f = self.1.clear_collide_rect();
unsafe { f(self.0) }
}
#[doc(alias = "sys::ffi::playdate_sprite::check_collisions")]
#[must_use = "Result is borrowed by C-API"]
pub fn check_collisions(&self,
goal_x: c_float,
goal_y: c_float,
actual_x: &mut c_float,
actual_y: &mut c_float)
-> &[SpriteCollisionInfo] {
let f = self.1.check_collisions();
let mut len: c_int = 0;
let ptr = unsafe { f(self.0, goal_x, goal_y, actual_x, actual_y, &mut len) };
if ptr.is_null() || len == 0 {
&[]
} else {
let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
slice
}
}
#[doc(alias = "sys::ffi::playdate_sprite::moveWithCollisions")]
#[must_use = "Result is borrowed by C-API"]
pub fn move_with_collisions<'t>(&'t self,
goal_x: c_float,
goal_y: c_float,
actual_x: &mut c_float,
actual_y: &mut c_float)
-> &'t [SpriteCollisionInfo] {
let f = self.1.move_with_collisions();
let mut len: c_int = 0;
let ptr = unsafe { f(self.0, goal_x, goal_y, actual_x, actual_y, &mut len) };
if ptr.is_null() || len == 0 {
&[]
} else {
let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
slice
}
}
#[doc(alias = "sys::ffi::playdate_sprite::overlapping_sprites")]
#[must_use = "Result is borrowed by C-API"]
pub fn overlapping_sprites(&self) -> &[SpriteRef] {
let f = self.1.overlapping_sprites();
let mut len: c_int = 0;
let ptr = unsafe { f(self.0, &mut len) };
let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
unsafe { core::mem::transmute(slice) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setStencilPattern")]
pub fn set_stencil_pattern(&self, pattern: &mut [u8; 8]) {
let f = self.1.set_stencil_pattern();
unsafe { f(self.0, pattern) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setStencilImage")]
pub fn set_stencil_image(&self, stencil: impl AnyBitmap, tile: bool) {
let f = self.1.set_stencil_image();
unsafe { f(self.0, stencil.as_raw(), tile.into()) }
}
#[doc(alias = "sys::ffi::playdate_sprite::clearStencil")]
pub fn clear_stencil(&self) {
let f = self.1.clear_stencil();
unsafe { f(self.0) }
}
#[doc(alias = "sys::ffi::playdate_sprite::setCenter")]
#[inline(always)]
pub fn set_center(&self, x: c_float, y: c_float) {
let f = self.1.set_center();
unsafe { f(self.0, x, y) }
}
#[doc(alias = "sys::ffi::playdate_sprite::getCenter")]
#[inline(always)]
pub fn center(&self) -> (c_float, c_float) {
let (mut x, mut y) = (0.0, 0.0);
let f = self.1.get_center();
unsafe { f(self.0, &mut x, &mut y) };
(x, y)
}
#[doc(alias = "sys::ffi::playdate_sprite::setUserdata")]
pub fn set_userdata(&self, data: Userdata) {
let f = self.1.set_userdata();
let userdata = Box::into_raw(Box::new(data));
let ptr = userdata as *mut c_void;
unsafe { f(self.0, ptr) }
}
#[doc(alias = "sys::ffi::playdate_sprite::get_userdata")]
pub fn userdata(&self) -> Option<&mut Userdata> {
let f = self.1.get_userdata();
let ptr = unsafe { f(self.0) };
if ptr.is_null() {
None
} else {
let ptr = ptr as *mut Userdata;
unsafe { ptr.as_mut() }
}
}
#[doc(alias = "sys::ffi::playdate_sprite::get_userdata")]
pub(crate) fn take_userdata(&self) -> Option<Box<Userdata>> {
let f = self.1.get_userdata();
let ptr = unsafe { f(self.0) };
if ptr.is_null() {
None
} else {
let ud = unsafe { Box::from_raw(ptr as *mut Userdata) };
Some(ud)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sprite_ref_layout() {
assert_eq!(
core::mem::size_of::<SpriteRef>(),
core::mem::size_of::<*mut LCDSprite>()
);
assert_eq!(
core::mem::size_of::<&[SpriteRef]>(),
core::mem::size_of::<&[*mut LCDSprite]>()
);
}
}