playdate_sprite/
sprite.rs

1//! Sprite implementations.
2
3/*
4TODO: Cover api-methods:
5	- [] querySpritesInRect
6	- [] querySpritesAlongLine
7	- [] querySpriteInfoAlongLine
8	- [] overlappingSprites
9	- [] allOverlappingSprites
10*/
11
12
13use core::ffi::c_int;
14use core::ffi::c_void;
15use core::ffi::c_float;
16use core::marker::PhantomData;
17use alloc::boxed::Box;
18
19use sys::traits::AsRaw;
20
21use sys::ffi::SpriteCollisionInfo;
22use sys::ffi::LCDRect;
23use sys::ffi::LCDSprite;
24use sys::ffi::PDRect;
25
26use gfx::bitmap::AnyBitmap;
27use gfx::bitmap::BitmapRef;
28use gfx::bitmap::BitmapDrawMode;
29use gfx::bitmap::BitmapFlip;
30
31use crate::utils;
32use crate::AnySprite;
33use crate::SpriteApi;
34use crate::TypedSprite;
35use crate::api;
36
37pub use crate::ext::*;
38
39
40pub type OwnedSprite<Userdata, Api> = Sprite<Userdata, Api, true>;
41pub type SharedSprite<Userdata, Api> = Sprite<Userdata, Api, false>;
42
43
44impl<UD, Api: api::Api, const FOD: bool> TypedSprite for Sprite<UD, Api, FOD> {
45	type Userdata = UD;
46	const FREE_ON_DROP: bool = FOD;
47}
48
49
50impl AnySprite for SpriteRef {}
51impl<UD, Api: api::Api, const FOD: bool> AnySprite for Sprite<UD, Api, FOD> {}
52
53
54impl SpriteApi for SpriteRef {
55	type Api = api::Default;
56
57	fn api(&self) -> Self::Api
58		where Self::Api: Copy {
59		api::Default::default()
60	}
61
62	fn api_ref(&self) -> &Self::Api {
63		static API: api::Default = api::Default;
64		&API
65	}
66}
67
68impl<UD, Api: api::Api, const FOD: bool> SpriteApi for Sprite<UD, Api, FOD> {
69	type Api = Api;
70	fn api(&self) -> Api
71		where Self::Api: Copy {
72		self.1
73	}
74
75	fn api_ref(&self) -> &Self::Api { &self.1 }
76}
77
78
79#[repr(transparent)]
80#[derive(Copy, Clone, Debug)]
81pub struct SpriteRef(*mut LCDSprite);
82
83impl From<*mut LCDSprite> for SpriteRef {
84	fn from(ptr: *mut LCDSprite) -> Self { Self(ptr) }
85}
86
87impl AsRaw for SpriteRef {
88	type Type = LCDSprite;
89	unsafe fn as_raw(&self) -> *mut LCDSprite { self.0 }
90}
91
92impl SpriteRef {
93	pub fn into_sprite<UD>(self) -> Sprite<UD, <Self as SpriteApi>::Api, false> {
94		Sprite(unsafe { self.as_raw() }, self.api(), PhantomData)
95	}
96
97	pub fn into_sprite_with<UD, Api: api::Api>(self, api: Api) -> Sprite<UD, Api, false> {
98		Sprite(unsafe { self.as_raw() }, api, PhantomData)
99	}
100}
101
102
103#[derive(Debug)]
104pub struct Sprite<Userdata = (), Api: api::Api = api::Default, const FREE_ON_DROP: bool = true>(*mut LCDSprite, Api, PhantomData<Userdata>);
105
106impl<UD, Api: api::Api, const FOD: bool> AsRaw for Sprite<UD, Api, FOD> {
107	type Type = LCDSprite;
108	unsafe fn as_raw(&self) -> *mut LCDSprite { self.0 }
109}
110
111impl<UD, Api: api::Api, const FOD: bool> AsRef<Self> for Sprite<UD, Api, FOD> {
112	fn as_ref(&self) -> &Self { self }
113}
114impl<UD, Api: api::Api, const FOD: bool> AsMut<Self> for Sprite<UD, Api, FOD> {
115	fn as_mut(&mut self) -> &mut Self { self }
116}
117
118impl<UD, Api, const FOD: bool> From<SpriteRef> for Sprite<UD, Api, FOD> where Api: api::Api + Default {
119	fn from(sprite: SpriteRef) -> Self { Self(unsafe { sprite.as_raw() }, Api::default(), PhantomData) }
120}
121
122impl<UD, Api: api::Api + Clone, const FOD: bool> Clone for Sprite<UD, Api, FOD> {
123	fn clone(&self) -> Self {
124		let f = self.1.copy();
125		let ptr = unsafe { f(self.0) };
126		Self(ptr, self.1.clone(), PhantomData)
127	}
128}
129
130impl<UD, Api: api::Api, const FOD: bool> Drop for Sprite<UD, Api, FOD> {
131	fn drop(&mut self) {
132		if FOD && !self.0.is_null() {
133			if let Some(ud) = self.take_userdata() {
134				drop(ud);
135				let f = self.1.set_userdata();
136				unsafe { f(self.0, core::ptr::null_mut()) }
137			}
138
139			let f = self.1.free_sprite();
140			unsafe { f(self.0) }
141			self.0 = core::ptr::null_mut();
142		}
143	}
144}
145
146impl<UD, Api: api::Api + Copy> Sprite<UD, Api, true> {
147	/// Convert this sprite into the same sprite that will not be freed on drop.
148	/// That means that only C-part of the sprite will be freed.
149	///
150	/// __Safety is guaranteed by the caller.__
151	pub fn into_shared(mut self) -> Sprite<UD, Api, false> {
152		let res = Sprite(self.0, self.1, self.2);
153		self.0 = core::ptr::null_mut();
154		res
155	}
156}
157
158
159impl<UD, Api: Default + api::Api, const FOD: bool> Sprite<UD, Api, FOD> {
160	/// Allocates and returns a new Sprite with [`default`](api::Default) api access-point.
161	///
162	/// To create a sprite with a custom api access-point, use [`new_with`](Sprite::new_with).
163	///
164	/// See also [`sys::ffi::playdate_sprite::newSprite`]
165	#[doc(alias = "sys::ffi::playdate_sprite::newSprite")]
166	pub fn new() -> Self {
167		let api = Default::default();
168		Self::new_with(api)
169	}
170}
171
172impl<UD, Api: api::Api, const FOD: bool> Sprite<UD, Api, FOD> {
173	/// Allocates and returns a new Sprite with given `api`.
174	///
175	/// See also [`sys::ffi::playdate_sprite::newSprite`]
176	#[doc(alias = "sys::ffi::playdate_sprite::newSprite")]
177	pub fn new_with(api: Api) -> Self {
178		let f = api.new_sprite();
179		let ptr = unsafe { f() };
180		Self(ptr, api, PhantomData)
181	}
182}
183
184
185impl<Userdata, Api: api::Api, const FOD: bool> Sprite<Userdata, Api, FOD> {
186	/// Adds the this sprite to the display list, so that it is drawn in the current scene.
187	///
188	/// Equivalent to [`sys::ffi::playdate_sprite::addSprite`]
189	#[doc(alias = "sys::ffi::playdate_sprite::addSprite")]
190	pub fn add(&self) {
191		let f = self.1.add_sprite();
192		unsafe { f(self.0) }
193	}
194
195	/// Removes the this sprite from the display list.
196	///
197	/// Equivalent to [`sys::ffi::playdate_sprite::removeSprite`]
198	#[doc(alias = "sys::ffi::playdate_sprite::removeSprite")]
199	pub fn remove(&self) {
200		let f = self.1.remove_sprite();
201		unsafe { f(self.0) }
202	}
203
204
205	/// Sets the bounds of the sprite with bounds.
206	///
207	/// Equivalent to [`sys::ffi::playdate_sprite::setBounds`]
208	#[doc(alias = "sys::ffi::playdate_sprite::setBounds")]
209	pub fn set_bounds(&self, bounds: PDRect) {
210		let f = self.1.set_bounds();
211		unsafe { f(self.0, bounds) }
212	}
213
214	/// Returns the bounds of the sprite.
215	///
216	/// Equivalent to [`sys::ffi::playdate_sprite::getBounds`]
217	#[doc(alias = "sys::ffi::playdate_sprite::getBounds")]
218	pub fn bounds(&self) -> PDRect {
219		let f = self.1.get_bounds();
220		unsafe { f(self.0) }
221	}
222
223
224	/// Moves the sprite to `x`, `y` and resets its bounds based on the bitmap dimensions and center.
225	///
226	/// Equivalent to [`sys::ffi::playdate_sprite::moveTo`]
227	#[doc(alias = "sys::ffi::playdate_sprite::moveTo")]
228	pub fn move_to(&self, x: c_float, y: c_float) {
229		let f = self.1.move_to();
230		unsafe { f(self.0, x, y) }
231	}
232
233	/// Moves the sprite to by offsetting its current position by `dx`, `dy`.
234	///
235	/// Equivalent to [`sys::ffi::playdate_sprite::moveBy`]
236	#[doc(alias = "sys::ffi::playdate_sprite::moveBy")]
237	pub fn move_by(&self, dx: c_float, dy: c_float) {
238		let f = self.1.move_by();
239		unsafe { f(self.0, dx, dy) }
240	}
241
242
243	/// Sets the sprite's image to the given bitmap.
244	///
245	/// ⚠️ Caution: Using with draw function, call this method __before__ set callback.
246	/// Setting image __after__ setting draw callback is mostly crashes with SIGBUS.
247	///
248	/// See also [`set_opaque`](Sprite::set_opaque).
249	///
250	/// Equivalent to [`sys::ffi::playdate_sprite::setImage`]
251	#[doc(alias = "sys::ffi::playdate_sprite::setImage")]
252	pub fn set_image(&self, image: impl AnyBitmap, flip: BitmapFlip) {
253		let f = self.1.set_image();
254		unsafe { f(self.0, image.as_raw(), flip) }
255	}
256
257	/// Returns the bitmap currently assigned to the given sprite.
258	///
259	/// Equivalent to [`sys::ffi::playdate_sprite::getImage`]
260	#[doc(alias = "sys::ffi::playdate_sprite::getImage")]
261	pub fn image<'t>(&'t self) -> Option<BitmapRef<'t>> {
262		let f = self.1.get_image();
263		let ptr = unsafe { f(self.0) };
264		if ptr.is_null() {
265			None
266		} else {
267			Some(BitmapRef::from(ptr))
268		}
269	}
270
271
272	/// Sets the size.
273	/// The size is used to set the sprite’s bounds when calling [`move_to`](Sprite::move_to).
274	///
275	/// Equivalent to [`sys::ffi::playdate_sprite::setSize`]
276	#[doc(alias = "sys::ffi::playdate_sprite::setSize")]
277	pub fn set_size(&self, width: c_float, height: c_float) {
278		let f = self.1.set_size();
279		unsafe { f(self.0, width, height) }
280	}
281
282
283	/// Sets the Z order of the sprite.
284	/// Higher Z sprites are drawn on top of those with lower Z order.
285	///
286	/// Equivalent to [`sys::ffi::playdate_sprite::setZIndex`]
287	#[doc(alias = "sys::ffi::playdate_sprite::setZIndex")]
288	pub fn set_z_index(&self, z_index: i16) {
289		let f = self.1.set_z_index();
290		unsafe { f(self.0, z_index) }
291	}
292
293	/// Returns the Z index of the sprite.
294	///
295	/// Equivalent to [`sys::ffi::playdate_sprite::getZIndex`]
296	#[doc(alias = "sys::ffi::playdate_sprite::getZIndex")]
297	pub fn z_index(&self) -> i16 {
298		let f = self.1.get_z_index();
299		unsafe { f(self.0) }
300	}
301
302
303	/// Sets the mode for drawing the sprite’s bitmap.
304	///
305	/// Equivalent to [`sys::ffi::playdate_sprite::setDrawMode`]
306	#[doc(alias = "sys::ffi::playdate_sprite::setDrawMode")]
307	pub fn set_draw_mode(&self, mode: BitmapDrawMode) {
308		let f = self.1.set_draw_mode();
309		unsafe { f(self.0, mode) }
310	}
311
312
313	/// Flips the sprite's bitmap.
314	///
315	/// Equivalent to [`sys::ffi::playdate_sprite::setImageFlip`]
316	#[doc(alias = "sys::ffi::playdate_sprite::setImageFlip")]
317	pub fn set_image_flip(&self, flip: BitmapFlip) {
318		let f = self.1.set_image_flip();
319		unsafe { f(self.0, flip) }
320	}
321
322	/// Returns the flip setting of the sprite’s bitmap.
323	///
324	/// Equivalent to [`sys::ffi::playdate_sprite::getImageFlip`]
325	#[doc(alias = "sys::ffi::playdate_sprite::getImageFlip")]
326	pub fn image_flip(&self) -> BitmapFlip {
327		let f = self.1.get_image_flip();
328		unsafe { f(self.0) }
329	}
330
331
332	/// Specifies a stencil image to be set on the frame buffer before the sprite is drawn.
333	///
334	/// Equivalent to [`sys::ffi::playdate_sprite::setStencil`]
335	#[doc(alias = "sys::ffi::playdate_sprite::setStencil")]
336	pub fn set_stencil(&self, stencil: impl AnyBitmap) {
337		let f = self.1.set_stencil();
338		unsafe { f(self.0, stencil.as_raw()) }
339	}
340
341
342	/// Sets the clipping rectangle for sprite drawing.
343	///
344	/// Equivalent to [`sys::ffi::playdate_sprite::setClipRect`]
345	#[doc(alias = "sys::ffi::playdate_sprite::setClipRect")]
346	pub fn set_clip_rect(&self, clip: LCDRect) {
347		let f = self.1.set_clip_rect();
348		unsafe { f(self.0, clip) }
349	}
350
351	/// Clears the sprite’s clipping rectangle.
352	///
353	/// Equivalent to [`sys::ffi::playdate_sprite::clearClipRect`]
354	#[doc(alias = "sys::ffi::playdate_sprite::clearClipRect")]
355	pub fn clear_clip_rect(&self) {
356		let f = self.1.clear_clip_rect();
357		unsafe { f(self.0) }
358	}
359
360
361	/// Set the `updates_enabled` flag of the sprite
362	/// (determines whether the sprite has its update function called).
363	///
364	/// Equivalent to [`sys::ffi::playdate_sprite::setUpdatesEnabled`]
365	#[doc(alias = "sys::ffi::playdate_sprite::setUpdatesEnabled")]
366	pub fn set_updates_enabled(&self, value: bool) {
367		let f = self.1.set_updates_enabled();
368		unsafe { f(self.0, value.into()) }
369	}
370
371	/// Get the `updates_enabled` flag of the sprite.
372	///
373	/// Equivalent to [`sys::ffi::playdate_sprite::updatesEnabled`]
374	#[doc(alias = "sys::ffi::playdate_sprite::updatesEnabled")]
375	pub fn updates_enabled(&self) -> bool {
376		let f = self.1.updates_enabled();
377		unsafe { f(self.0) == 1 }
378	}
379
380	/// Set the collisions_enabled flag of the sprite
381	/// (along with the `collide_rect`, this determines whether the sprite participates in collisions).
382	///
383	/// Set to `true` by default.
384	///
385	/// See also [`collide_rect`](Sprite::collide_rect),
386	/// [`set_collide_rect`](Sprite::set_collide_rect),
387	/// [`clear_collide_rect`](Sprite::clear_collide_rect).
388	///
389	/// Equivalent to [`sys::ffi::playdate_sprite::setCollisionsEnabled`]
390	#[doc(alias = "sys::ffi::playdate_sprite::setCollisionsEnabled")]
391	pub fn set_collisions_enabled(&self, value: bool) {
392		let f = self.1.set_collisions_enabled();
393		unsafe { f(self.0, value.into()) }
394	}
395
396	/// Get the `collisions_enabled` flag of the sprite.
397	///
398	/// Equivalent to [`sys::ffi::playdate_sprite::collisionsEnabled`]
399	#[doc(alias = "sys::ffi::playdate_sprite::collisionsEnabled")]
400	pub fn collisions_enabled(&self) -> bool {
401		let f = self.1.collisions_enabled();
402		unsafe { f(self.0) == 1 }
403	}
404
405	/// Set the visible flag of the given sprite
406	/// (determines whether the sprite has its draw function called).
407	///
408	/// Equivalent to [`sys::ffi::playdate_sprite::setVisible`]
409	#[doc(alias = "sys::ffi::playdate_sprite::setVisible")]
410	pub fn set_visible(&self, value: bool) {
411		let f = self.1.set_visible();
412		unsafe { f(self.0, value.into()) }
413	}
414
415	/// Get the visible flag of the sprite.
416	///
417	/// Equivalent to [`sys::ffi::playdate_sprite::isVisible`]
418	#[doc(alias = "sys::ffi::playdate_sprite::isVisible")]
419	pub fn is_visible(&self) -> bool {
420		let f = self.1.is_visible();
421		unsafe { f(self.0) == 1 }
422	}
423
424	/// Marking a sprite opaque tells the sprite system that it doesn’t need to draw anything underneath the sprite,
425	/// since it will be overdrawn anyway.
426	///
427	/// If you set an image without a mask/alpha channel on the sprite, it automatically sets the opaque flag.
428	///
429	/// Equivalent to [`sys::ffi::playdate_sprite::setOpaque`]
430	#[doc(alias = "sys::ffi::playdate_sprite::setOpaque")]
431	pub fn set_opaque(&self, value: bool) {
432		let f = self.1.set_opaque();
433		unsafe { f(self.0, value.into()) }
434	}
435
436	/// Forces the sprite to redraw.
437	///
438	/// Equivalent to [`sys::ffi::playdate_sprite::markDirty`]
439	#[doc(alias = "sys::ffi::playdate_sprite::markDirty")]
440	pub fn mark_dirty(&self) {
441		let f = self.1.mark_dirty();
442		unsafe { f(self.0) }
443	}
444
445	/// Sets the tag of the sprite.
446	///
447	/// This can be useful for identifying sprites or types of sprites when using the [collision][] API.
448	///
449	/// [collision]: crate::callback::collision
450	///
451	/// Equivalent to [`sys::ffi::playdate_sprite::setTag`]
452	#[doc(alias = "sys::ffi::playdate_sprite::setTag")]
453	pub fn set_tag(&self, tag: u8) {
454		let f = self.1.set_tag();
455		unsafe { f(self.0, tag) }
456	}
457
458	/// Returns the tag of the given sprite.
459	///
460	/// Equivalent to [`sys::ffi::playdate_sprite::getTag`]
461	#[doc(alias = "sys::ffi::playdate_sprite::getTag")]
462	pub fn tag(&self) -> u8 {
463		let f = self.1.get_tag();
464		unsafe { f(self.0) }
465	}
466
467	/// When flag is set to `true`,
468	/// the sprite will draw in screen coordinates,
469	/// ignoring the currently-set `draw_offset`.
470	///
471	/// This only affects drawing,
472	/// and should not be used on sprites being used for collisions,
473	/// which will still happen in world-space.
474	///
475	/// See also [`gfx::set_draw_offset`].
476	///
477	/// Equivalent to [`sys::ffi::playdate_sprite::setIgnoresDrawOffset`]
478	#[doc(alias = "sys::ffi::playdate_sprite::setIgnoresDrawOffset")]
479	pub fn set_ignores_draw_offset(&self, value: bool) {
480		let f = self.1.set_ignores_draw_offset();
481		unsafe { f(self.0, value.into()) }
482	}
483
484
485	/// Sets `x` and `y` to the current position of sprite.
486	///
487	/// Equivalent to [`get_position_to`](Sprite::position_to) and [`sys::ffi::playdate_sprite::getPosition`]
488	///
489	#[doc(alias = "sys::ffi::playdate_sprite::getPosition")]
490	pub fn position(&self) -> (c_float, c_float) {
491		let (mut x, mut y) = Default::default();
492		self.position_to(&mut x, &mut y);
493		(x, y)
494	}
495
496	/// Sets `x` and `y` to the current position of sprite.
497	///
498	/// Equivalent to [`sys::ffi::playdate_sprite::getPosition`]
499	#[doc(alias = "sys::ffi::playdate_sprite::getPosition")]
500	pub fn position_to(&self, x: &mut c_float, y: &mut c_float) {
501		let f = self.1.get_position();
502		unsafe { f(self.0, x, y) }
503	}
504
505
506	/// Marks the area of the sprite, relative to its bounds,
507	/// to be checked for collisions with other sprites' collide rects.
508	///
509	/// Equivalent to [`sys::ffi::playdate_sprite::setCollideRect`]
510	#[doc(alias = "sys::ffi::playdate_sprite::setCollideRect")]
511	pub fn set_collide_rect(&self, collide: PDRect) {
512		let f = self.1.set_collide_rect();
513		unsafe { f(self.0, collide) }
514	}
515
516	/// Returns the sprite’s collide rect.
517	///
518	/// Equivalent to [`sys::ffi::playdate_sprite::getCollideRect`]
519	#[doc(alias = "sys::ffi::playdate_sprite::getCollideRect")]
520	pub fn collide_rect(&self) -> PDRect {
521		let f = self.1.get_collide_rect();
522		unsafe { f(self.0) }
523	}
524
525	/// Clears the sprite’s collide rect.
526	///
527	/// Equivalent to [`sys::ffi::playdate_sprite::clearCollideRect`]
528	#[doc(alias = "sys::ffi::playdate_sprite::clearCollideRect")]
529	pub fn clear_collide_rect(&self) {
530		let f = self.1.clear_collide_rect();
531		unsafe { f(self.0) }
532	}
533
534	/// Returns the same values as [`move_with_collisions`] but does not actually move the sprite.
535	///
536	/// Equivalent to [`sys::ffi::playdate_sprite::checkCollisions`]
537	#[doc(alias = "sys::ffi::playdate_sprite::check_collisions")]
538	#[must_use = "Expensive op, allocated array by C-API"]
539	pub fn check_collisions(&self,
540	                        goal_x: c_float,
541	                        goal_y: c_float,
542	                        actual_x: &mut c_float,
543	                        actual_y: &mut c_float)
544	                        -> Option<utils::Arr<SpriteCollisionInfo>> {
545		let f = self.1.check_collisions();
546		let mut len: c_int = 0;
547		let ptr = unsafe { f(self.0, goal_x, goal_y, actual_x, actual_y, &mut len) };
548
549		if ptr.is_null() || len == 0 {
550			None
551		} else {
552			let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
553			Some(utils::Arr(slice))
554		}
555	}
556
557	/// Moves the sprite towards `goal_x`, `goal_y` taking collisions into account
558	/// and returns a slice of [`SpriteCollisionInfo`].
559	///
560	/// `actual_x`, `actual_y` are set to the sprite’s position after collisions.
561	/// If no collisions occurred, this will be the same as `goal_x`, `goal_y`.
562	///
563	/// Resulting slice with entire content can be freely dropped.
564	///
565	/// Equivalent to [`sys::ffi::playdate_sprite::moveWithCollisions`]
566	#[doc(alias = "sys::ffi::playdate_sprite::moveWithCollisions")]
567	#[must_use = "Expensive op, allocated array by C-API"]
568	pub fn move_with_collisions<'t>(&'t self,
569	                                goal_x: c_float,
570	                                goal_y: c_float,
571	                                actual_x: &mut c_float,
572	                                actual_y: &mut c_float)
573	                                -> Option<utils::Arr<'t, SpriteCollisionInfo>> {
574		let f = self.1.move_with_collisions();
575		let mut len: c_int = 0;
576		let ptr = unsafe { f(self.0, goal_x, goal_y, actual_x, actual_y, &mut len) };
577
578		if ptr.is_null() || len == 0 {
579			None
580		} else {
581			let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
582			Some(utils::Arr(slice))
583		}
584	}
585
586
587	/// Returns an slice of sprites that have collide rects
588	/// that are currently overlapping the given sprite’s collide rect.
589	///
590	/// Equivalent to [`sys::ffi::playdate_sprite::overlappingSprites`]
591	#[doc(alias = "sys::ffi::playdate_sprite::overlapping_sprites")]
592	#[must_use = "Expensive op, allocated array by C-API"]
593	pub fn overlapping_sprites(&self) -> Option<utils::Arr<SpriteRef>> {
594		let f = self.1.overlapping_sprites();
595		let mut len: c_int = 0;
596		let ptr = unsafe { f(self.0, &mut len) };
597		if ptr.is_null() || len == 0 {
598			None
599		} else {
600			let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
601			let res = unsafe { core::mem::transmute(slice) };
602			Some(utils::Arr(res))
603		}
604	}
605
606
607	/// Sets the sprite’s stencil to the given pattern.
608	///
609	/// Equivalent to [`sys::ffi::playdate_sprite::setStencilPattern`]
610	#[doc(alias = "sys::ffi::playdate_sprite::setStencilPattern")]
611	pub fn set_stencil_pattern(&self, pattern: &mut [u8; 8]) {
612		let f = self.1.set_stencil_pattern();
613		unsafe { f(self.0, pattern) }
614	}
615
616	/// Specifies a stencil image to be set on the frame buffer before the sprite is drawn.
617	///
618	/// If tile is set, the stencil will be tiled.
619	///
620	/// Tiled stencils must have __width__ evenly __divisible by 32__.
621	///
622	/// Equivalent to [`sys::ffi::playdate_sprite::setStencilImage`]
623	#[doc(alias = "sys::ffi::playdate_sprite::setStencilImage")]
624	pub fn set_stencil_image(&self, stencil: impl AnyBitmap, tile: bool) {
625		let f = self.1.set_stencil_image();
626		unsafe { f(self.0, stencil.as_raw(), tile.into()) }
627	}
628
629	/// Clears the sprite’s stencil.
630	///
631	/// Equivalent to [`sys::ffi::playdate_sprite::clearStencil`]
632	#[doc(alias = "sys::ffi::playdate_sprite::clearStencil")]
633	pub fn clear_stencil(&self) {
634		let f = self.1.clear_stencil();
635		unsafe { f(self.0) }
636	}
637
638	/// Sets the sprite’s drawing center as a fraction (ranging from `0.0` to `1.0`) of the height and width.
639	///
640	/// Default is `0.5, 0.5` (the center of the sprite).
641	///
642	/// This means that when you call [`Sprite::move_to`]`(x, y)`,
643	/// the center of your sprite will be positioned at `x, y`.
644	///
645	/// If you want `x` and `y` to represent the upper left corner of your sprite, specify the center as `0, 0`.
646	///
647	/// Equivalent to [`sys::ffi::playdate_sprite::setCenter`].
648	#[doc(alias = "sys::ffi::playdate_sprite::setCenter")]
649	#[inline(always)]
650	pub fn set_center(&self, x: c_float, y: c_float) {
651		let f = self.1.set_center();
652		unsafe { f(self.0, x, y) }
653	}
654
655	/// Returns the sprite’s drawing center as a fraction (ranging from `0.0` to `1.0`) of the height and width.
656	///
657	/// Equivalent to [`sys::ffi::playdate_sprite::getCenter`].
658	#[doc(alias = "sys::ffi::playdate_sprite::getCenter")]
659	#[inline(always)]
660	pub fn center(&self) -> (c_float, c_float) {
661		let (mut x, mut y) = (0.0, 0.0);
662		let f = self.1.get_center();
663		unsafe { f(self.0, &mut x, &mut y) };
664		(x, y)
665	}
666
667
668	/// Sets custom data to the sprite.
669	///
670	/// Used for associating the sprite with other data.
671	///
672	/// Equivalent to [`sys::ffi::playdate_sprite::setUserdata`]
673	#[doc(alias = "sys::ffi::playdate_sprite::setUserdata")]
674	pub fn set_userdata(&self, data: Userdata) {
675		let f = self.1.set_userdata();
676		let userdata = Box::into_raw(Box::new(data));
677		let ptr = userdata as *mut c_void;
678		unsafe { f(self.0, ptr) }
679	}
680
681	/// Gets the _mutable__ reference to sprite’s userdata.
682	///
683	/// Used for associating the sprite with other data.
684	///
685	/// Equivalent to [`sys::ffi::playdate_sprite::getUserdata`]
686	#[doc(alias = "sys::ffi::playdate_sprite::get_userdata")]
687	pub fn userdata(&self) -> Option<&mut Userdata> {
688		let f = self.1.get_userdata();
689		let ptr = unsafe { f(self.0) };
690		if ptr.is_null() {
691			None
692		} else {
693			let ptr = ptr as *mut Userdata;
694			// TODO: check ptr is aligned to `UD`
695			unsafe { ptr.as_mut() }
696		}
697	}
698
699	/// Returns __taken__ value the sprite’s userdata.
700	///
701	/// Equivalent to [`sys::ffi::playdate_sprite::getUserdata`]
702	#[doc(alias = "sys::ffi::playdate_sprite::get_userdata")]
703	pub(crate) fn take_userdata(&self) -> Option<Box<Userdata>> {
704		let f = self.1.get_userdata();
705		let ptr = unsafe { f(self.0) };
706		if ptr.is_null() {
707			None
708		} else {
709			// TODO: check ptr is aligned to `UD`
710			let ud = unsafe { Box::from_raw(ptr as *mut Userdata) };
711			Some(ud)
712		}
713	}
714}
715
716
717#[cfg(test)]
718mod tests {
719	use super::*;
720
721	#[test]
722	/// Ensure that SpriteRef have same size as LCDSprite.
723	fn sprite_ref_layout() {
724		assert_eq!(
725		           core::mem::size_of::<SpriteRef>(),
726		           core::mem::size_of::<*mut LCDSprite>()
727		);
728		assert_eq!(
729		           core::mem::size_of::<&[SpriteRef]>(),
730		           core::mem::size_of::<&[*mut LCDSprite]>()
731		);
732	}
733}