playdate_rs/
graphics.rs

1use core::{
2    ffi::{c_char, c_void},
3    marker::PhantomData,
4};
5
6use crate::math::{Rect, Size, Vec2};
7use alloc::ffi::CString;
8
9use crate::{math::SideOffsets, util::Ref};
10
11pub use sys::{
12    LCDBitmapDrawMode as BitmapDrawMode, LCDBitmapFlip as BitmapFlip, LCDColor as ColorOrPattern,
13    LCDLineCapStyle as LineCapStyle, LCDPattern as Pattern, LCDPolygonFillRule as PolygonFillRule,
14    LCDSolidColor as Color, LCD_COLUMNS, LCD_ROWS, LCD_ROWSIZE,
15};
16
17use crate::{error::Error, PLAYDATE};
18
19pub struct PlaydateGraphics {
20    handle: *const sys::playdate_graphics,
21    pub video: crate::video::PlaydateVideo,
22}
23
24impl PlaydateGraphics {
25    pub(crate) fn new(handle: *const sys::playdate_graphics) -> Self {
26        Self {
27            handle,
28            video: crate::video::PlaydateVideo::new(unsafe { (*handle).video }),
29        }
30    }
31
32    // pub video: *const playdate_video,
33
34    /// Clears the entire display, filling it with color.
35    pub fn clear(&self, color: impl Into<ColorOrPattern>) {
36        unsafe {
37            ((*self.handle).clear.unwrap())(color.into());
38        }
39    }
40
41    /// Sets the background color shown when the display is offset or for clearing dirty areas in the sprite system.
42    pub fn set_background_color(&self, color: Color) {
43        unsafe {
44            ((*self.handle).setBackgroundColor.unwrap())(color);
45        }
46    }
47
48    /// Sets the stencil used for drawing. For a tiled stencil, use setStencilImage() instead.
49    pub fn set_stencil(&self, stencil: impl AsRef<Bitmap>) {
50        unsafe {
51            ((*self.handle).setStencil.unwrap())(stencil.as_ref().handle);
52        }
53    }
54
55    /// Sets the mode used for drawing bitmaps. Note that text drawing uses bitmaps, so this affects how fonts are displayed as well.
56    pub fn set_draw_mode(&self, mode: BitmapDrawMode) {
57        unsafe {
58            ((*self.handle).setDrawMode.unwrap())(mode);
59        }
60    }
61
62    /// Offsets the origin point for all drawing calls to x, y (can be negative).
63    /// This is useful, for example, for centering a "camera" on a sprite that is moving around a world larger than the screen.
64    pub fn set_draw_offset(&self, delta: Vec2<i32>) {
65        unsafe {
66            ((*self.handle).setDrawOffset.unwrap())(delta.x, delta.y);
67        }
68    }
69
70    /// Sets the current clip rect, using world coordinates—​that is, the given rectangle will be translated by the current drawing offset. The clip rect is cleared at the beginning of each update.
71    pub fn set_clip_rect(&self, rect: Rect<i32>) {
72        unsafe {
73            ((*self.handle).setClipRect.unwrap())(rect.x, rect.y, rect.width, rect.height);
74        }
75    }
76
77    /// Clears the current clip rect.
78    pub fn clear_clip_rect(&self) {
79        unsafe {
80            ((*self.handle).clearClipRect.unwrap())();
81        }
82    }
83
84    /// Sets the end cap style used in the line drawing functions.
85    pub fn set_line_cap_style(&self, end_cap_style: LineCapStyle) {
86        unsafe {
87            ((*self.handle).setLineCapStyle.unwrap())(end_cap_style);
88        }
89    }
90
91    /// Sets the font to use in subsequent drawText calls.
92    pub fn set_font(&self, font: &Font) {
93        unsafe {
94            ((*self.handle).setFont.unwrap())(font.handle);
95        }
96    }
97
98    /// Sets the tracking to use when drawing text.
99    pub fn set_text_tracking(&self, tracking: i32) {
100        unsafe {
101            ((*self.handle).setTextTracking.unwrap())(tracking);
102        }
103    }
104
105    /// Push a new drawing context for drawing into the given bitmap. If target is nil, the drawing functions will use the display framebuffer.
106    pub fn push_context(&self, target: impl AsRef<Bitmap>) {
107        unsafe {
108            ((*self.handle).pushContext.unwrap())(target.as_ref().handle);
109        }
110    }
111
112    /// Pops a context off the stack (if any are left), restoring the drawing settings from before the context was pushed.
113    pub fn pop_context(&self) {
114        unsafe {
115            ((*self.handle).popContext.unwrap())();
116        }
117    }
118
119    /// Draws the bitmap with its upper-left corner at location x, y, using the given flip orientation.
120    pub fn draw_bitmap(&self, bitmap: impl AsRef<Bitmap>, pos: Vec2<i32>, flip: BitmapFlip) {
121        unsafe {
122            ((*self.handle).drawBitmap.unwrap())(bitmap.as_ref().handle, pos.x, pos.y, flip);
123        }
124    }
125
126    /// Draws the bitmap with its upper-left corner at location x, y tiled inside a width by height rectangle.
127    pub fn tile_bitmap(&self, bitmap: impl AsRef<Bitmap>, rect: Rect<i32>, flip: BitmapFlip) {
128        unsafe {
129            ((*self.handle).tileBitmap.unwrap())(
130                bitmap.as_ref().handle,
131                rect.x,
132                rect.y,
133                rect.width,
134                rect.height,
135                flip,
136            );
137        }
138    }
139
140    /// Draws a line from x1, y1 to x2, y2 with a stroke width of width.
141    pub fn draw_line(
142        &self,
143        start: Vec2<i32>,
144        end: Vec2<i32>,
145        width: i32,
146        color: impl Into<ColorOrPattern>,
147    ) {
148        unsafe {
149            ((*self.handle).drawLine.unwrap())(start.x, start.y, end.x, end.y, width, color.into());
150        }
151    }
152
153    /// Draws a filled triangle with points at x1, y1, x2, y2, and x3, y3.
154    #[allow(clippy::too_many_arguments)]
155    pub fn fill_triangle(
156        &self,
157        pos1: Vec2<i32>,
158        pos2: Vec2<i32>,
159        pos3: Vec2<i32>,
160        color: impl Into<ColorOrPattern>,
161    ) {
162        unsafe {
163            ((*self.handle).fillTriangle.unwrap())(
164                pos1.x,
165                pos1.y,
166                pos2.x,
167                pos2.y,
168                pos3.x,
169                pos3.y,
170                color.into(),
171            );
172        }
173    }
174
175    /// Draws a pixel at x, y.
176    pub fn draw_pixel(&self, pos: Vec2<i32>, color: Color) {
177        let fb = self.get_frame();
178        let byte_ptr = unsafe { fb.add((pos.y * LCD_ROWSIZE as i32 + (pos.x >> 3)) as usize) };
179        if color == Color::Black {
180            unsafe { *byte_ptr &= !(1 << (7 - (pos.x & 7))) };
181        } else {
182            unsafe { *byte_ptr |= 1 << (7 - (pos.x & 7)) };
183        }
184    }
185
186    /// Draws a width by height rect at x, y.
187    pub fn draw_rect(&self, rect: Rect<i32>, color: impl Into<ColorOrPattern>) {
188        unsafe {
189            ((*self.handle).drawRect.unwrap())(
190                rect.x,
191                rect.y,
192                rect.width,
193                rect.height,
194                color.into(),
195            );
196        }
197    }
198
199    /// Draws a filled width by height rect at x, y.
200    pub fn fill_rect(&self, rect: Rect<i32>, color: impl Into<ColorOrPattern>) {
201        unsafe {
202            ((*self.handle).fillRect.unwrap())(
203                rect.x,
204                rect.y,
205                rect.width,
206                rect.height,
207                color.into(),
208            );
209        }
210    }
211
212    /// Draws an ellipse inside the rectangle {x, y, width, height} of width lineWidth (inset from the rectangle bounds). If startAngle != _endAngle, this draws an arc between the given angles. Angles are given in degrees, clockwise from due north.
213    #[allow(clippy::too_many_arguments)]
214    pub fn draw_ellipse(
215        &self,
216        rect: Rect<i32>,
217        line_width: i32,
218        start_angle: f32,
219        end_angle: f32,
220        color: impl Into<ColorOrPattern>,
221    ) {
222        unsafe {
223            ((*self.handle).drawEllipse.unwrap())(
224                rect.x,
225                rect.y,
226                rect.width,
227                rect.height,
228                line_width,
229                start_angle,
230                end_angle,
231                color.into(),
232            );
233        }
234    }
235
236    /// Fills an ellipse inside the rectangle {x, y, width, height}. If startAngle != _endAngle, this draws a wedge/Pacman between the given angles. Angles are given in degrees, clockwise from due north.
237    #[allow(clippy::too_many_arguments)]
238    pub fn fill_ellipse(
239        &self,
240        rect: Rect<i32>,
241        start_angle: f32,
242        end_angle: f32,
243        color: impl Into<ColorOrPattern>,
244    ) {
245        unsafe {
246            ((*self.handle).fillEllipse.unwrap())(
247                rect.x,
248                rect.y,
249                rect.width,
250                rect.height,
251                start_angle,
252                end_angle,
253                color.into(),
254            );
255        }
256    }
257
258    /// Draws the bitmap scaled to xscale and yscale with its upper-left corner at location x, y. Note that flip is not available when drawing scaled bitmaps but negative scale values will achieve the same effect.
259    pub fn draw_scaled_bitmap(&self, bitmap: impl AsRef<Bitmap>, pos: Vec2<i32>, scale: Vec2<f32>) {
260        unsafe {
261            ((*self.handle).drawScaledBitmap.unwrap())(
262                bitmap.as_ref().handle,
263                pos.x,
264                pos.y,
265                scale.x,
266                scale.y,
267            );
268        }
269    }
270
271    /// Draws the given text using the provided options. If no font has been set with setFont, the default system font Asheville Sans 14 Light is used.
272    pub fn draw_text(&self, text: impl AsRef<str>, pos: Vec2<i32>) -> i32 {
273        let ptr = text.as_ref().as_ptr() as *const c_void;
274        let len = text.as_ref().chars().count();
275        unsafe {
276            ((*self.handle).drawText.unwrap())(ptr, len, sys::PDStringEncoding::UTF8, pos.x, pos.y)
277        }
278    }
279
280    /// Allocates and returns a new width by height LCDBitmap filled with bgcolor.
281    pub fn new_bitmap(
282        &self,
283        width: i32,
284        height: i32,
285        bgcolor: impl Into<ColorOrPattern>,
286    ) -> Bitmap {
287        Bitmap::from(unsafe { ((*self.handle).newBitmap.unwrap())(width, height, bgcolor.into()) })
288    }
289
290    /// Allocates and returns a new LCDBitmap from the file at path. If there is no file at path, the function returns null.
291    pub fn load_bitmap(&self, path: impl AsRef<str>) -> Result<Bitmap, Error> {
292        unsafe {
293            let c_string = CString::new(path.as_ref()).unwrap();
294            let mut err: *const c_char = core::ptr::null();
295            let ptr = ((*self.handle).loadBitmap.unwrap())(c_string.as_ptr() as _, &mut err);
296            if !err.is_null() {
297                let err = CString::from_raw(err as *mut c_char);
298                let err = err.into_string().unwrap();
299                return Err(Error::FailedToLoadBitMapFromFile(err));
300            }
301            Ok(Bitmap::from(ptr))
302        }
303    }
304
305    /// Allocates and returns a new LCDBitmapTable that can hold count width by height LCDBitmaps.
306    pub fn new_bitmap_table(&self, count: i32, width: i32, height: i32) -> BitmapTable {
307        BitmapTable::from(unsafe { ((*self.handle).newBitmapTable.unwrap())(count, width, height) })
308    }
309
310    /// Allocates and returns a new LCDBitmap from the file at path. If there is no file at path, the function returns null.
311    pub fn load_bitmap_table(&self, path: impl AsRef<str>) -> Result<BitmapTable, Error> {
312        unsafe {
313            let c_string = CString::new(path.as_ref()).unwrap();
314            let mut err = core::ptr::null();
315            let ptr = ((*self.handle).loadBitmapTable.unwrap())(c_string.as_ptr() as _, &mut err);
316            if !err.is_null() {
317                let err = CString::from_raw(err as *mut c_char);
318                let err = err.into_string().unwrap();
319                return Err(Error::FailedToLoadBitMapTableFromFile(err));
320            }
321            Ok(BitmapTable::from(ptr))
322        }
323    }
324
325    /// Returns the LCDFont object for the font file at path. In case of error, outErr points to a string describing the error.
326    pub fn load_font(&self, path: impl AsRef<str>) -> Result<Font, Error> {
327        unsafe {
328            let c_string = CString::new(path.as_ref()).unwrap();
329            let mut err = core::ptr::null();
330            let font = ((*self.handle).loadFont.unwrap())(c_string.as_ptr() as _, &mut err);
331            if !err.is_null() {
332                let err = CString::from_raw(err as *mut c_char);
333                let err = err.into_string().unwrap();
334                return Err(Error::FailedToLoadFont(err));
335            }
336            Ok(Font::new(font))
337        }
338    }
339
340    /// Returns the current display frame buffer. Rows are 32-bit aligned, so the row stride is 52 bytes, with the extra 2 bytes per row ignored. Bytes are MSB-ordered; i.e., the pixel in column 0 is the 0x80 bit of the first byte of the row.
341    pub fn get_frame(&self) -> *mut u8 {
342        unsafe { ((*self.handle).getFrame.unwrap())() }
343    }
344
345    /// Returns the current display frame buffer. Rows are 32-bit aligned, so the row stride is 52 bytes, with the extra 2 bytes per row ignored. Bytes are MSB-ordered; i.e., the pixel in column 0 is the 0x80 bit of the first byte of the row.
346    pub fn get_display_frame(&self) -> *mut u8 {
347        unsafe { ((*self.handle).getDisplayFrame.unwrap())() }
348    }
349
350    /// Only valid in the Simulator, returns the debug framebuffer as a bitmap. Function is NULL on device.
351    pub fn get_debug_bitmap(&self) -> Option<Ref<Bitmap>> {
352        let ptr = unsafe { ((*self.handle).getDebugBitmap.unwrap())() };
353        if ptr.is_null() {
354            None
355        } else {
356            Some(Bitmap::from_ref(ptr))
357        }
358    }
359
360    /// Returns a copy the contents of the working frame buffer as a bitmap. The caller is responsible for freeing the returned bitmap with playdate->graphics->freeBitmap().
361    pub fn copy_frame_buffer_bitmap(&self) -> Bitmap {
362        Bitmap::from(unsafe { ((*self.handle).copyFrameBufferBitmap.unwrap())() })
363    }
364
365    /// After updating pixels in the buffer returned by getFrame(), you must tell the graphics system which rows were updated. This function marks a contiguous range of rows as updated (e.g., markUpdatedRows(0,LCD_ROWS-1) tells the system to update the entire display). Both “start” and “end” are included in the range.
366    pub fn mark_updated_rows(&self, start: i32, end: i32) {
367        unsafe {
368            ((*self.handle).markUpdatedRows.unwrap())(start, end);
369        }
370    }
371
372    /// Manually flushes the current frame buffer out to the display. This function is automatically called after each pass through the run loop, so there shouldn’t be any need to call it yourself.
373    pub fn display(&self) {
374        unsafe {
375            ((*self.handle).display.unwrap())();
376        }
377    }
378
379    /// Sets the current clip rect in screen coordinates.
380    pub fn set_screen_clip_rect(&self, rect: Rect<i32>) {
381        unsafe {
382            ((*self.handle).setScreenClipRect.unwrap())(rect.x, rect.y, rect.width, rect.height);
383        }
384    }
385
386    /// Fills the polygon with vertices at the given coordinates (an array of 2*nPoints ints containing alternating x and y values) using the given color and fill, or winding, rule. See [Nonzero-rule](https://en.wikipedia.org/wiki/Nonzero-rule) for an explanation of the winding rule.
387    pub fn fill_polygon(
388        &self,
389        n_points: i32,
390        coords: impl AsRef<[i32]>,
391        color: impl Into<ColorOrPattern>,
392        fillrule: PolygonFillRule,
393    ) {
394        unsafe {
395            let mut coords = coords.as_ref().to_vec();
396            ((*self.handle).fillPolygon.unwrap())(
397                n_points,
398                coords.as_mut_ptr(),
399                color.into(),
400                fillrule,
401            );
402        }
403    }
404
405    /// Returns a bitmap containing the contents of the display buffer. The system owns this bitmap—​do not free it!
406    pub fn get_display_buffer_bitmap(&self) -> Ref<Bitmap> {
407        Bitmap::from_ref(unsafe { ((*self.handle).getDisplayBufferBitmap.unwrap())() })
408    }
409
410    /// Draws the bitmap scaled to xscale and yscale then rotated by degrees with its center as given by proportions centerx and centery at x, y; that is: if centerx and centery are both 0.5 the center of the image is at (x,y), if centerx and centery are both 0 the top left corner of the image (before rotation) is at (x,y), etc.
411    #[allow(clippy::too_many_arguments)]
412    pub fn draw_rotated_bitmap(
413        &self,
414        bitmap: impl AsRef<Bitmap>,
415        pos: Vec2<i32>,
416        rotation: f32,
417        center_pos: Vec2<f32>,
418        scale: Vec2<f32>,
419    ) {
420        unsafe {
421            ((*self.handle).drawRotatedBitmap.unwrap())(
422                bitmap.as_ref().handle,
423                pos.x,
424                pos.y,
425                rotation,
426                center_pos.x,
427                center_pos.y,
428                scale.x,
429                scale.y,
430            );
431        }
432    }
433
434    /// Sets the leading adjustment (added to the leading specified in the font) to use when drawing text.
435    pub fn set_text_leading(&self, leading: i32) {
436        unsafe {
437            ((*self.handle).setTextLeading.unwrap())(leading);
438        }
439    }
440
441    /// Sets the stencil used for drawing. If the tile flag is set the stencil image will be tiled. Tiled stencils must have width equal to a multiple of 32 pixels.
442    pub fn set_stencil_image(&self, stencil: impl AsRef<Bitmap>, tile: i32) {
443        unsafe {
444            ((*self.handle).setStencilImage.unwrap())(stencil.as_ref().handle, tile);
445        }
446    }
447
448    /// Returns an LCDFont object wrapping the LCDFontData data comprising the contents (minus 16-byte header) of an uncompressed pft file. wide corresponds to the flag in the header indicating whether the font contains glyphs at codepoints above U+1FFFF.
449    /// # Safety
450    /// Assumes that the LCDFontData is valid.
451    pub unsafe fn make_font_from_data(&self, data: *mut sys::LCDFontData, wide: i32) -> Font {
452        Font::new(((*self.handle).makeFontFromData.unwrap())(data, wide))
453    }
454}
455
456/// A bitmap instance with ownership to the underlying data.
457#[derive(Debug)]
458pub struct Bitmap {
459    pub(crate) handle: *mut sys::LCDBitmap,
460}
461
462impl Bitmap {
463    pub(crate) fn from(handle: *mut sys::LCDBitmap) -> Self {
464        Self { handle }
465    }
466
467    pub(crate) fn from_ref<'a>(handle: *mut sys::LCDBitmap) -> Ref<'a, Self> {
468        Ref::new(Self { handle })
469    }
470
471    /// Allocates and returns a new width by height Bitmap filled with bgcolor.
472    pub fn new(size: Size<u32>, bgcolor: impl Into<ColorOrPattern>) -> Self {
473        Self::from(unsafe {
474            ((*PLAYDATE.graphics.handle).newBitmap.unwrap())(
475                size.width as _,
476                size.height as _,
477                bgcolor.into(),
478            )
479        })
480    }
481
482    /// Open an image as a bitmap.
483    pub fn open(path: impl AsRef<str>) -> Result<Self, Error> {
484        unsafe {
485            let c_string = CString::new(path.as_ref()).unwrap();
486            let mut err: *const c_char = core::ptr::null();
487            let ptr =
488                ((*PLAYDATE.graphics.handle).loadBitmap.unwrap())(c_string.as_ptr() as _, &mut err);
489            if !err.is_null() {
490                let err = CString::from_raw(err as *mut c_char);
491                let err = err.into_string().unwrap();
492                return Err(Error::FailedToLoadBitMapFromFile(err));
493            }
494            Ok(Bitmap::from(ptr))
495        }
496    }
497
498    /// Clears bitmap, filling with the given bgcolor.
499    pub fn clear(&self, bgcolor: impl Into<ColorOrPattern>) {
500        unsafe { ((*PLAYDATE.graphics.handle).clearBitmap.unwrap())(self.handle, bgcolor.into()) }
501    }
502
503    /// Sets a mask image for the given bitmap. The set mask must be the same size as the target bitmap.
504    pub fn set_mask(&self, mask: impl AsRef<Bitmap>) -> Result<(), Error> {
505        let result = unsafe {
506            ((*PLAYDATE.graphics.handle).setBitmapMask.unwrap())(self.handle, mask.as_ref().handle)
507        };
508        if result != 1 {
509            Err(Error::FailedToSetBitmapMask)
510        } else {
511            Ok(())
512        }
513    }
514
515    /// Gets a mask image for the given bitmap. If the image doesn’t have a mask, getBitmapMask returns NULL.
516    pub fn get_mask(&self) -> Ref<Bitmap> {
517        Self::from_ref(unsafe { ((*PLAYDATE.graphics.handle).getBitmapMask.unwrap())(self.handle) })
518    }
519
520    /// Returns `true` if any of the opaque pixels in `self` when positioned at `x1`, `y1` with `flip1` overlap any of the opaque pixels in `other` at `x2`, `y2` with `flip2` within the non-empty rect, or `false` if no pixels overlap or if one or both fall completely outside of rect.
521    #[allow(clippy::too_many_arguments)]
522    pub fn check_mask_collision(
523        &self,
524        x1: i32,
525        y1: i32,
526        flip1: BitmapFlip,
527        other: impl AsRef<Bitmap>,
528        x2: i32,
529        y2: i32,
530        flip2: BitmapFlip,
531        rect: SideOffsets<i32>,
532    ) -> bool {
533        unsafe {
534            ((*PLAYDATE.graphics.handle).checkMaskCollision.unwrap())(
535                self.handle,
536                x1,
537                y1,
538                flip1,
539                other.as_ref().handle,
540                x2,
541                y2,
542                flip2,
543                rect.into(),
544            ) == 1
545        }
546    }
547
548    /// Gets various info about bitmap including its width and height and raw pixel data. The data is 1 bit per pixel packed format, in MSB order; in other words, the high bit of the first byte in data is the top left pixel of the image. If the bitmap has a mask, a pointer to its data is returned in mask, else NULL is returned.
549    pub fn get_bitmap_data(&self) -> BitmapData {
550        let mut data = BitmapData::new();
551        unsafe {
552            ((*PLAYDATE.graphics.handle).getBitmapData.unwrap())(
553                self.handle,
554                &mut data.size.width,
555                &mut data.size.height,
556                &mut data.rowbytes,
557                &mut data.mask,
558                &mut data.data,
559            )
560        }
561        data
562    }
563
564    /// Loads the image at path into the previously allocated bitmap.
565    pub fn load(&self, path: impl AsRef<str>) -> Result<(), Error> {
566        let c_string = CString::new(path.as_ref()).unwrap();
567        let mut err: *const c_char = core::ptr::null();
568        unsafe {
569            ((*PLAYDATE.graphics.handle).loadIntoBitmap.unwrap())(
570                c_string.as_ptr() as _,
571                self.handle,
572                &mut err,
573            )
574        }
575        if !err.is_null() {
576            let err = unsafe { CString::from_raw(err as *mut c_char) };
577            let err = err.into_string().unwrap();
578            return Err(Error::FailedToLoadBitMapFromFile(err));
579        }
580        Ok(())
581    }
582
583    /// Returns a new, rotated and scaled LCDBitmap based on the given bitmap.
584    pub fn rotated(&self, rotation: f32, scale: Vec2<f32>) -> Bitmap {
585        let mut alloced_size = 0;
586        Self::from(unsafe {
587            ((*PLAYDATE.graphics.handle).rotatedBitmap.unwrap())(
588                self.handle,
589                rotation,
590                scale.x,
591                scale.y,
592                &mut alloced_size,
593            )
594        })
595    }
596
597    /// Get color as an 8 x 8 pattern using the given bitmap. x, y indicates the top left corner of the 8 x 8 pattern.
598    pub fn get_color_pattern(&self, pos: Vec2<i32>) -> ColorPatternData {
599        let mut color = ColorOrPattern::default();
600        unsafe {
601            ((*PLAYDATE.graphics.handle).setColorToPattern.unwrap())(
602                &mut color,
603                self.handle,
604                pos.x,
605                pos.y,
606            );
607        }
608        if let Some(scolor) = color.as_solid_color() {
609            ColorPatternData::Solid(scolor)
610        } else {
611            ColorPatternData::Pattern(unsafe { color.as_pattern().unwrap() })
612        }
613    }
614}
615
616impl AsRef<Self> for Bitmap {
617    fn as_ref(&self) -> &Self {
618        self
619    }
620}
621
622unsafe impl Send for Bitmap {}
623unsafe impl Sync for Bitmap {}
624
625impl PartialEq for Bitmap {
626    fn eq(&self, other: &Self) -> bool {
627        self.handle == other.handle
628    }
629}
630
631impl Eq for Bitmap {}
632
633impl Drop for Bitmap {
634    fn drop(&mut self) {
635        unsafe { ((*PLAYDATE.graphics.handle).freeBitmap.unwrap())(self.handle) }
636    }
637}
638
639impl Clone for Bitmap {
640    fn clone(&self) -> Self {
641        Bitmap {
642            handle: unsafe { ((*PLAYDATE.graphics.handle).copyBitmap.unwrap())(self.handle) },
643        }
644    }
645}
646
647pub enum ColorPatternData {
648    Solid(Color),
649    Pattern(Pattern),
650}
651
652#[derive(PartialEq, Eq, Debug, Clone)]
653pub struct BitmapData<'a> {
654    pub size: Size<i32>,
655    pub rowbytes: i32,
656    pub mask: *mut u8,
657    pub data: *mut u8,
658    _p: PhantomData<&'a ()>,
659}
660
661impl<'a> BitmapData<'a> {
662    fn new() -> Self {
663        BitmapData {
664            size: Size::default(),
665            rowbytes: 0,
666            mask: core::ptr::null_mut(),
667            data: core::ptr::null_mut(),
668            _p: PhantomData,
669        }
670    }
671
672    /// Get the value of a pixel at x, y. Returns true if the pixel is black
673    pub fn get_pixel(&self, pos: Vec2<u32>) -> bool {
674        let byte_index = pos.y * self.rowbytes as u32 + pos.x / 8;
675        let byte_ptr = unsafe { self.data.add(byte_index as _) };
676        let v = unsafe { *byte_ptr };
677        let bit_index = pos.x % 8;
678        let mask = 1 << (7 - bit_index);
679        v & mask != 0
680    }
681}
682
683/// There are two kinds of image tables: matrix and sequential.
684///
685/// Matrix image tables are great as sources of imagery for tilemap. They are loaded from a single file in your game’s source folder with the suffix -table-<w>-<h> before the file extension. The compiler splits the image into separate bitmaps of dimension w by h pixels that are accessible via imagetable:getImage(x,y).
686///
687/// Sequential image tables are useful as a way to load up sequential frames of animation. They are loaded from a sequence of files in your game’s source folder at compile time from filenames with the suffix -table-<sequenceNumber> before the file extension. Individual images in the sequence are accessible via imagetable:getImage(n). The images employed by a sequential image table are not required to be the same size, unlike the images used in a matrix image table.
688#[derive(PartialEq, Eq, Debug)]
689pub struct BitmapTable {
690    handle: *mut sys::LCDBitmapTable,
691}
692
693unsafe impl Send for BitmapTable {}
694unsafe impl Sync for BitmapTable {}
695
696impl BitmapTable {
697    fn from(handle: *mut sys::LCDBitmapTable) -> Self {
698        Self { handle }
699    }
700
701    pub fn new(count: usize, width: u32, height: u32) -> Self {
702        BitmapTable::from(unsafe {
703            ((*PLAYDATE.graphics.handle).newBitmapTable.unwrap())(
704                count as _,
705                width as _,
706                height as _,
707            )
708        })
709    }
710
711    pub fn open(path: impl AsRef<str>) -> Result<Self, Error> {
712        PLAYDATE.graphics.load_bitmap_table(path)
713    }
714
715    /// Returns the idx bitmap in table, If idx is out of bounds, the function returns NULL.
716    pub fn get(&self, idx: usize) -> Option<Ref<Bitmap>> {
717        let ptr =
718            unsafe { ((*PLAYDATE.graphics.handle).getTableBitmap.unwrap())(self.handle, idx as _) };
719        if ptr.is_null() {
720            return None;
721        }
722        Some(Bitmap::from_ref(ptr))
723    }
724
725    /// Allocates and returns a new LCDBitmap from the file at path. If there is no file at path, the function returns null.
726    pub fn load(&mut self, path: impl AsRef<str>) -> Result<(), Error> {
727        let c_string = CString::new(path.as_ref()).unwrap();
728        let mut err: *const c_char = core::ptr::null();
729        unsafe {
730            ((*PLAYDATE.graphics.handle).loadIntoBitmapTable.unwrap())(
731                c_string.as_ptr() as _,
732                self.handle,
733                &mut err,
734            )
735        }
736        if !err.is_null() {
737            let err = unsafe { CString::from_raw(err as *mut c_char) };
738            let err = err.into_string().unwrap();
739            return Err(Error::FailedToLoadBitMapFromBitMapTable(err));
740        }
741        Ok(())
742    }
743}
744
745impl Drop for BitmapTable {
746    fn drop(&mut self) {
747        unsafe { ((*PLAYDATE.graphics.handle).freeBitmapTable.unwrap())(self.handle) }
748    }
749}
750
751#[derive(PartialEq, Eq, Debug)]
752pub struct Font {
753    handle: *mut sys::LCDFont,
754}
755
756unsafe impl Send for Font {}
757unsafe impl Sync for Font {}
758
759impl Font {
760    fn new(handle: *mut sys::LCDFont) -> Self {
761        Self { handle }
762    }
763
764    /// Returns the height of the given font.
765    pub fn get_height(&self) -> u8 {
766        unsafe { ((*PLAYDATE.graphics.handle).getFontHeight.unwrap())(self.handle) }
767    }
768
769    /// Returns the width of the given text in the given font.
770    pub fn get_text_width(&self, text: impl AsRef<str>, tracking: i32) -> u32 {
771        let ptr = text.as_ref().as_ptr() as *const c_void;
772        let len = text.as_ref().chars().count();
773        unsafe {
774            ((*PLAYDATE.graphics.handle).getTextWidth.unwrap())(
775                self.handle,
776                ptr,
777                len,
778                sys::PDStringEncoding::UTF8,
779                tracking,
780            ) as _
781        }
782    }
783
784    /// Returns an LCDFontPage object for the given character code. Each LCDFontPage contains information for 256 characters; specifically, if (c1 & ~0xff) == (c2 & ~0xff), then c1 and c2 belong to the same page and the same LCDFontPage can be used to fetch the character data for both instead of searching for the page twice.
785    pub fn get_page(&self, c: u32) -> FontPage {
786        FontPage::new(unsafe { ((*PLAYDATE.graphics.handle).getFontPage.unwrap())(self.handle, c) })
787    }
788}
789
790#[derive(PartialEq, Eq, Debug)]
791pub struct FontPage {
792    handle: *mut sys::LCDFontPage,
793}
794
795unsafe impl Send for FontPage {}
796unsafe impl Sync for FontPage {}
797
798impl FontPage {
799    fn new(handle: *mut sys::LCDFontPage) -> Self {
800        Self { handle }
801    }
802
803    /// Returns an LCDFontGlyph object for character c in LCDFontPage page, and optionally returns the glyph’s bitmap and advance value.
804    pub fn get_glyph(&self, c: u32) -> (FontGlyph, Option<Ref<Bitmap>>, Option<i32>) {
805        let mut bitmap = core::ptr::null_mut();
806        let mut advance = 0;
807        let glyph = FontGlyph::new(unsafe {
808            (*PLAYDATE.graphics.handle).getPageGlyph.unwrap()(
809                self.handle,
810                c,
811                &mut bitmap,
812                &mut advance,
813            )
814        });
815        let bitmap = if bitmap.is_null() {
816            None
817        } else {
818            Some(Bitmap::from_ref(bitmap))
819        };
820        let advance = if advance == 0 { None } else { Some(advance) };
821        (glyph, bitmap, advance)
822    }
823}
824
825#[derive(PartialEq, Eq, Debug)]
826pub struct FontGlyph {
827    handle: *mut sys::LCDFontGlyph,
828}
829
830unsafe impl Send for FontGlyph {}
831unsafe impl Sync for FontGlyph {}
832
833impl FontGlyph {
834    fn new(handle: *mut sys::LCDFontGlyph) -> Self {
835        Self { handle }
836    }
837
838    /// Returns the kerning adjustment between characters c1 and c2 as specified by the font.
839    pub fn get_kerning(&self, c1: u32, c2: u32) -> i32 {
840        unsafe { ((*PLAYDATE.graphics.handle).getGlyphKerning.unwrap())(self.handle, c1, c2) }
841    }
842}