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 fn clear(&self, color: impl Into<ColorOrPattern>) {
36 unsafe {
37 ((*self.handle).clear.unwrap())(color.into());
38 }
39 }
40
41 pub fn set_background_color(&self, color: Color) {
43 unsafe {
44 ((*self.handle).setBackgroundColor.unwrap())(color);
45 }
46 }
47
48 pub fn set_stencil(&self, stencil: impl AsRef<Bitmap>) {
50 unsafe {
51 ((*self.handle).setStencil.unwrap())(stencil.as_ref().handle);
52 }
53 }
54
55 pub fn set_draw_mode(&self, mode: BitmapDrawMode) {
57 unsafe {
58 ((*self.handle).setDrawMode.unwrap())(mode);
59 }
60 }
61
62 pub fn set_draw_offset(&self, delta: Vec2<i32>) {
65 unsafe {
66 ((*self.handle).setDrawOffset.unwrap())(delta.x, delta.y);
67 }
68 }
69
70 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 pub fn clear_clip_rect(&self) {
79 unsafe {
80 ((*self.handle).clearClipRect.unwrap())();
81 }
82 }
83
84 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 pub fn set_font(&self, font: &Font) {
93 unsafe {
94 ((*self.handle).setFont.unwrap())(font.handle);
95 }
96 }
97
98 pub fn set_text_tracking(&self, tracking: i32) {
100 unsafe {
101 ((*self.handle).setTextTracking.unwrap())(tracking);
102 }
103 }
104
105 pub fn push_context(&self, target: impl AsRef<Bitmap>) {
107 unsafe {
108 ((*self.handle).pushContext.unwrap())(target.as_ref().handle);
109 }
110 }
111
112 pub fn pop_context(&self) {
114 unsafe {
115 ((*self.handle).popContext.unwrap())();
116 }
117 }
118
119 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 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 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 #[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 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 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 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 #[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 #[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 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 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 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 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 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 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 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 pub fn get_frame(&self) -> *mut u8 {
342 unsafe { ((*self.handle).getFrame.unwrap())() }
343 }
344
345 pub fn get_display_frame(&self) -> *mut u8 {
347 unsafe { ((*self.handle).getDisplayFrame.unwrap())() }
348 }
349
350 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 pub fn copy_frame_buffer_bitmap(&self) -> Bitmap {
362 Bitmap::from(unsafe { ((*self.handle).copyFrameBufferBitmap.unwrap())() })
363 }
364
365 pub fn mark_updated_rows(&self, start: i32, end: i32) {
367 unsafe {
368 ((*self.handle).markUpdatedRows.unwrap())(start, end);
369 }
370 }
371
372 pub fn display(&self) {
374 unsafe {
375 ((*self.handle).display.unwrap())();
376 }
377 }
378
379 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 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 pub fn get_display_buffer_bitmap(&self) -> Ref<Bitmap> {
407 Bitmap::from_ref(unsafe { ((*self.handle).getDisplayBufferBitmap.unwrap())() })
408 }
409
410 #[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 pub fn set_text_leading(&self, leading: i32) {
436 unsafe {
437 ((*self.handle).setTextLeading.unwrap())(leading);
438 }
439 }
440
441 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 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#[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 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 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 pub fn clear(&self, bgcolor: impl Into<ColorOrPattern>) {
500 unsafe { ((*PLAYDATE.graphics.handle).clearBitmap.unwrap())(self.handle, bgcolor.into()) }
501 }
502
503 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 pub fn get_mask(&self) -> Ref<Bitmap> {
517 Self::from_ref(unsafe { ((*PLAYDATE.graphics.handle).getBitmapMask.unwrap())(self.handle) })
518 }
519
520 #[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 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 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 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 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 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#[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 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 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 pub fn get_height(&self) -> u8 {
766 unsafe { ((*PLAYDATE.graphics.handle).getFontHeight.unwrap())(self.handle) }
767 }
768
769 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 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 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 pub fn get_kerning(&self, c1: u32, c2: u32) -> i32 {
840 unsafe { ((*PLAYDATE.graphics.handle).getGlyphKerning.unwrap())(self.handle, c1, c2) }
841 }
842}