crankstart/
graphics.rs

1use {
2    crate::{
3        geometry::{ScreenPoint, ScreenRect, ScreenSize, ScreenVector},
4        log_to_console, pd_func_caller, pd_func_caller_log,
5        system::System,
6    },
7    alloc::{format, rc::Rc},
8    anyhow::{anyhow, ensure, Error},
9    core::{cell::RefCell, ops::RangeInclusive, ptr, slice},
10    crankstart_sys::{ctypes::c_int, LCDBitmapTable, LCDPattern},
11    cstr_core::{CStr, CString},
12    euclid::default::{Point2D, Vector2D},
13    hashbrown::HashMap,
14};
15
16pub use crankstart_sys::{
17    LCDBitmapDrawMode, LCDBitmapFlip, LCDLineCapStyle, LCDRect, LCDSolidColor, PDRect,
18    PDStringEncoding, LCD_COLUMNS, LCD_ROWS, LCD_ROWSIZE,
19};
20
21pub fn rect_make(x: f32, y: f32, width: f32, height: f32) -> PDRect {
22    PDRect {
23        x,
24        y,
25        width,
26        height,
27    }
28}
29
30#[derive(Clone, Debug)]
31pub enum LCDColor {
32    Solid(LCDSolidColor),
33    Pattern(LCDPattern),
34}
35
36impl From<LCDColor> for usize {
37    fn from(color: LCDColor) -> Self {
38        match color {
39            LCDColor::Solid(solid_color) => solid_color as usize,
40            LCDColor::Pattern(pattern) => {
41                let pattern_ptr = &pattern as *const u8;
42                pattern_ptr as usize
43            }
44        }
45    }
46}
47
48#[derive(Debug)]
49pub struct BitmapData {
50    pub width: c_int,
51    pub height: c_int,
52    pub rowbytes: c_int,
53    pub hasmask: bool,
54}
55
56#[derive(Debug)]
57pub struct BitmapInner {
58    pub(crate) raw_bitmap: *mut crankstart_sys::LCDBitmap,
59}
60
61impl BitmapInner {
62    pub fn get_data(&self) -> Result<BitmapData, Error> {
63        let mut width = 0;
64        let mut height = 0;
65        let mut rowbytes = 0;
66        let mut mask_ptr = ptr::null_mut();
67        pd_func_caller!(
68            (*Graphics::get_ptr()).getBitmapData,
69            self.raw_bitmap,
70            &mut width,
71            &mut height,
72            &mut rowbytes,
73            &mut mask_ptr,
74            ptr::null_mut(),
75        )?;
76        Ok(BitmapData {
77            width,
78            height,
79            rowbytes,
80            hasmask: mask_ptr != ptr::null_mut(),
81        })
82    }
83
84    pub fn draw(&self, location: ScreenPoint, flip: LCDBitmapFlip) -> Result<(), Error> {
85        pd_func_caller!(
86            (*Graphics::get_ptr()).drawBitmap,
87            self.raw_bitmap,
88            location.x,
89            location.y,
90            flip.into(),
91        )?;
92        Ok(())
93    }
94
95    pub fn draw_scaled(&self, location: ScreenPoint, scale: Vector2D<f32>) -> Result<(), Error> {
96        pd_func_caller!(
97            (*Graphics::get_ptr()).drawScaledBitmap,
98            self.raw_bitmap,
99            location.x,
100            location.y,
101            scale.x,
102            scale.y,
103        )
104    }
105
106    pub fn draw_rotated(
107        &self,
108        location: ScreenPoint,
109        degrees: f32,
110        center: Vector2D<f32>,
111        scale: Vector2D<f32>,
112    ) -> Result<(), Error> {
113        pd_func_caller!(
114            (*Graphics::get_ptr()).drawRotatedBitmap,
115            self.raw_bitmap,
116            location.x,
117            location.y,
118            degrees,
119            center.x,
120            center.y,
121            scale.x,
122            scale.y,
123        )
124    }
125
126    pub fn rotated(&self, degrees: f32, scale: Vector2D<f32>) -> Result<Self, Error> {
127        let raw_bitmap = pd_func_caller!(
128            (*Graphics::get_ptr()).rotatedBitmap,
129            self.raw_bitmap,
130            degrees,
131            scale.x,
132            scale.y,
133            // No documentation on this anywhere, but null works in testing.
134            ptr::null_mut(), // allocedSize
135        )?;
136        Ok(Self { raw_bitmap })
137    }
138
139    pub fn tile(
140        &self,
141        location: ScreenPoint,
142        size: ScreenSize,
143        flip: LCDBitmapFlip,
144    ) -> Result<(), Error> {
145        pd_func_caller!(
146            (*Graphics::get_ptr()).tileBitmap,
147            self.raw_bitmap,
148            location.x,
149            location.y,
150            size.width,
151            size.height,
152            flip.into(),
153        )?;
154        Ok(())
155    }
156
157    pub fn clear(&self, color: LCDColor) -> Result<(), Error> {
158        pd_func_caller!(
159            (*Graphics::get_ptr()).clearBitmap,
160            self.raw_bitmap,
161            color.into()
162        )
163    }
164
165    pub fn duplicate(&self) -> Result<Self, Error> {
166        let raw_bitmap = pd_func_caller!((*Graphics::get_ptr()).copyBitmap, self.raw_bitmap)?;
167
168        Ok(Self { raw_bitmap })
169    }
170
171    pub fn transform(&self, rotation: f32, scale: Vector2D<f32>) -> Result<Self, Error> {
172        // let raw_bitmap = pd_func_caller!(
173        //     (*Graphics::get_ptr()).transformedBitmap,
174        //     self.raw_bitmap,
175        //     rotation,
176        //     scale.x,
177        //     scale.y,
178        //     core::ptr::null_mut(),
179        // )?;
180        // Ok(Self { raw_bitmap })
181        todo!();
182    }
183
184    pub fn into_color(&self, bitmap: Bitmap, top_left: Point2D<i32>) -> Result<LCDColor, Error> {
185        let mut pattern = LCDPattern::default();
186        let pattern_ptr = pattern.as_mut_ptr();
187        let mut pattern_val = pattern_ptr as usize;
188        let graphics = Graphics::get();
189        pd_func_caller!(
190            (*graphics.0).setColorToPattern,
191            &mut pattern_val,
192            self.raw_bitmap,
193            top_left.x,
194            top_left.y
195        )?;
196        Ok(LCDColor::Pattern(pattern))
197    }
198
199    pub fn load(&self, path: &str) -> Result<(), Error> {
200        let c_path = CString::new(path).map_err(Error::msg)?;
201        let mut out_err: *const crankstart_sys::ctypes::c_char = ptr::null_mut();
202        let graphics = Graphics::get();
203        pd_func_caller!(
204            (*graphics.0).loadIntoBitmap,
205            c_path.as_ptr(),
206            self.raw_bitmap,
207            &mut out_err
208        )?;
209        if out_err != ptr::null_mut() {
210            let err_msg = unsafe { CStr::from_ptr(out_err).to_string_lossy().into_owned() };
211            Err(anyhow!(err_msg))
212        } else {
213            Ok(())
214        }
215    }
216
217    pub fn check_mask_collision(
218        &self,
219        my_location: ScreenPoint,
220        my_flip: LCDBitmapFlip,
221        other: Bitmap,
222        other_location: ScreenPoint,
223        other_flip: LCDBitmapFlip,
224        rect: ScreenRect,
225    ) -> Result<bool, Error> {
226        let graphics = Graphics::get();
227        let other_raw = other.inner.borrow().raw_bitmap;
228        let lcd_rect: LCDRect = rect.to_untyped().into();
229        let pixels_covered = pd_func_caller!(
230            (*graphics.0).checkMaskCollision,
231            self.raw_bitmap,
232            my_location.x,
233            my_location.y,
234            my_flip,
235            other_raw,
236            other_location.x,
237            other_location.y,
238            other_flip,
239            lcd_rect,
240        )?;
241        Ok(pixels_covered != 0)
242    }
243}
244
245impl Drop for BitmapInner {
246    fn drop(&mut self) {
247        pd_func_caller_log!((*Graphics::get_ptr()).freeBitmap, self.raw_bitmap);
248    }
249}
250
251pub type BitmapInnerPtr = Rc<RefCell<BitmapInner>>;
252
253#[derive(Clone, Debug)]
254pub struct Bitmap {
255    pub(crate) inner: BitmapInnerPtr,
256}
257
258impl Bitmap {
259    fn new(raw_bitmap: *mut crankstart_sys::LCDBitmap) -> Self {
260        Bitmap {
261            inner: Rc::new(RefCell::new(BitmapInner { raw_bitmap })),
262        }
263    }
264
265    pub fn get_data(&self) -> Result<BitmapData, Error> {
266        self.inner.borrow().get_data()
267    }
268
269    pub fn draw(&self, location: ScreenPoint, flip: LCDBitmapFlip) -> Result<(), Error> {
270        self.inner.borrow().draw(location, flip)
271    }
272
273    pub fn draw_scaled(&self, location: ScreenPoint, scale: Vector2D<f32>) -> Result<(), Error> {
274        self.inner.borrow().draw_scaled(location, scale)
275    }
276
277    /// Draw the `Bitmap` to the given `location`, rotated `degrees` about the `center` point,
278    /// scaled up or down in size by `scale`.  `center` is given by two numbers between 0.0 and
279    /// 1.0, where (0, 0) is the top left and (0.5, 0.5) is the center point.
280    pub fn draw_rotated(
281        &self,
282        location: ScreenPoint,
283        degrees: f32,
284        center: Vector2D<f32>,
285        scale: Vector2D<f32>,
286    ) -> Result<(), Error> {
287        self.inner
288            .borrow()
289            .draw_rotated(location, degrees, center, scale)
290    }
291
292    /// Return a copy of self, rotated by `degrees` and scaled up or down in size by `scale`.
293    pub fn rotated(&self, degrees: f32, scale: Vector2D<f32>) -> Result<Bitmap, Error> {
294        let raw_bitmap = self.inner.borrow().rotated(degrees, scale)?;
295        Ok(Self {
296            inner: Rc::new(RefCell::new(raw_bitmap)),
297        })
298    }
299
300    pub fn tile(
301        &self,
302        location: ScreenPoint,
303        size: ScreenSize,
304        flip: LCDBitmapFlip,
305    ) -> Result<(), Error> {
306        self.inner.borrow().tile(location, size, flip)
307    }
308
309    pub fn clear(&self, color: LCDColor) -> Result<(), Error> {
310        self.inner.borrow().clear(color)
311    }
312
313    pub fn transform(&self, rotation: f32, scale: Vector2D<f32>) -> Result<Bitmap, Error> {
314        let inner = self.inner.borrow().transform(rotation, scale)?;
315        Ok(Self {
316            inner: Rc::new(RefCell::new(inner)),
317        })
318    }
319
320    pub fn into_color(&self, bitmap: Bitmap, top_left: Point2D<i32>) -> Result<LCDColor, Error> {
321        self.inner.borrow().into_color(bitmap, top_left)
322    }
323
324    pub fn load(&self, path: &str) -> Result<(), Error> {
325        self.inner.borrow().load(path)
326    }
327
328    pub fn check_mask_collision(
329        &self,
330        my_location: ScreenPoint,
331        my_flip: LCDBitmapFlip,
332        other: Bitmap,
333        other_location: ScreenPoint,
334        other_flip: LCDBitmapFlip,
335        rect: ScreenRect,
336    ) -> Result<bool, Error> {
337        self.inner.borrow().check_mask_collision(
338            my_location,
339            my_flip,
340            other,
341            other_location,
342            other_flip,
343            rect,
344        )
345    }
346}
347
348type OptionalBitmap<'a> = Option<&'a mut Bitmap>;
349
350fn raw_bitmap(bitmap: OptionalBitmap<'_>) -> *mut crankstart_sys::LCDBitmap {
351    if let Some(bitmap) = bitmap {
352        bitmap.inner.borrow().raw_bitmap
353    } else {
354        ptr::null_mut() as *mut crankstart_sys::LCDBitmap
355    }
356}
357
358pub struct Font(*mut crankstart_sys::LCDFont);
359
360impl Font {
361    pub fn new(font: *mut crankstart_sys::LCDFont) -> Result<Self, Error> {
362        anyhow::ensure!(font != ptr::null_mut(), "Null pointer passed to Font::new");
363        Ok(Self(font))
364    }
365}
366
367impl Drop for Font {
368    fn drop(&mut self) {
369        log_to_console!("Leaking a font");
370    }
371}
372
373#[derive(Debug)]
374struct BitmapTableInner {
375    raw_bitmap_table: *mut LCDBitmapTable,
376    bitmaps: HashMap<usize, Bitmap>,
377}
378
379impl BitmapTableInner {
380    fn get_bitmap(&mut self, index: usize) -> Result<Bitmap, Error> {
381        if let Some(bitmap) = self.bitmaps.get(&index) {
382            Ok(bitmap.clone())
383        } else {
384            let raw_bitmap = pd_func_caller!(
385                (*Graphics::get_ptr()).getTableBitmap,
386                self.raw_bitmap_table,
387                index as c_int
388            )?;
389            ensure!(
390                raw_bitmap != ptr::null_mut(),
391                "Failed to load bitmap {} from table {:?}",
392                index,
393                self.raw_bitmap_table
394            );
395            let bitmap = Bitmap::new(raw_bitmap);
396            self.bitmaps.insert(index, bitmap.clone());
397            Ok(bitmap)
398        }
399    }
400
401    fn load(&mut self, path: &str) -> Result<(), Error> {
402        let c_path = CString::new(path).map_err(Error::msg)?;
403        let mut out_err: *const crankstart_sys::ctypes::c_char = ptr::null_mut();
404        let graphics = Graphics::get();
405        pd_func_caller!(
406            (*graphics.0).loadIntoBitmapTable,
407            c_path.as_ptr(),
408            self.raw_bitmap_table,
409            &mut out_err
410        )?;
411        if out_err != ptr::null_mut() {
412            let err_msg = unsafe { CStr::from_ptr(out_err).to_string_lossy().into_owned() };
413            Err(anyhow!(err_msg))
414        } else {
415            Ok(())
416        }
417    }
418}
419
420impl Drop for BitmapTableInner {
421    fn drop(&mut self) {
422        pd_func_caller_log!(
423            (*Graphics::get_ptr()).freeBitmapTable,
424            self.raw_bitmap_table
425        );
426    }
427}
428
429type BitmapTableInnerPtr = Rc<RefCell<BitmapTableInner>>;
430
431#[derive(Clone, Debug)]
432pub struct BitmapTable {
433    inner: BitmapTableInnerPtr,
434}
435
436impl BitmapTable {
437    pub fn new(raw_bitmap_table: *mut LCDBitmapTable) -> Self {
438        Self {
439            inner: Rc::new(RefCell::new(BitmapTableInner {
440                raw_bitmap_table,
441                bitmaps: HashMap::new(),
442            })),
443        }
444    }
445
446    pub fn load(&self, path: &str) -> Result<(), Error> {
447        self.inner.borrow_mut().load(path)
448    }
449
450    pub fn get_bitmap(&self, index: usize) -> Result<Bitmap, Error> {
451        self.inner.borrow_mut().get_bitmap(index)
452    }
453}
454
455static mut GRAPHICS: Graphics = Graphics(ptr::null_mut());
456
457#[derive(Clone, Debug)]
458pub struct Graphics(*const crankstart_sys::playdate_graphics);
459
460impl Graphics {
461    pub(crate) fn new(graphics: *const crankstart_sys::playdate_graphics) {
462        unsafe {
463            GRAPHICS = Self(graphics);
464        }
465    }
466
467    pub fn get() -> Self {
468        unsafe { GRAPHICS.clone() }
469    }
470
471    pub fn get_ptr() -> *const crankstart_sys::playdate_graphics {
472        Self::get().0
473    }
474
475    /// Allows drawing directly into an image rather than the framebuffer, for example for
476    /// drawing text into a sprite's image.
477    pub fn with_context<F, T>(&self, bitmap: &mut Bitmap, f: F) -> Result<T, Error>
478    where
479        F: FnOnce() -> Result<T, Error>,
480    {
481        // Any calls in this context are directly modifying the bitmap, so borrow mutably
482        // for safety.
483        self.push_context(bitmap.inner.borrow_mut().raw_bitmap)?;
484        let res = f();
485        self.pop_context()?;
486        res
487    }
488
489    /// Internal function; use `with_context`.
490    fn push_context(&self, raw_bitmap: *mut crankstart_sys::LCDBitmap) -> Result<(), Error> {
491        pd_func_caller!((*self.0).pushContext, raw_bitmap)
492    }
493
494    /// Internal function; use `with_context`.
495    fn pop_context(&self) -> Result<(), Error> {
496        pd_func_caller!((*self.0).popContext)
497    }
498
499    pub fn get_frame(&self) -> Result<&'static mut [u8], Error> {
500        let ptr = pd_func_caller!((*self.0).getFrame)?;
501        anyhow::ensure!(
502            ptr != ptr::null_mut(),
503            "Null pointer returned from getFrame"
504        );
505        let frame = unsafe { slice::from_raw_parts_mut(ptr, (LCD_ROWSIZE * LCD_ROWS) as usize) };
506        Ok(frame)
507    }
508
509    pub fn get_display_frame(&self) -> Result<&'static mut [u8], Error> {
510        let ptr = pd_func_caller!((*self.0).getDisplayFrame)?;
511        anyhow::ensure!(
512            ptr != ptr::null_mut(),
513            "Null pointer returned from getDisplayFrame"
514        );
515        let frame = unsafe { slice::from_raw_parts_mut(ptr, (LCD_ROWSIZE * LCD_ROWS) as usize) };
516        Ok(frame)
517    }
518
519    pub fn get_debug_bitmap(&self) -> Result<Bitmap, Error> {
520        let raw_bitmap = pd_func_caller!((*self.0).getDebugBitmap)?;
521        anyhow::ensure!(
522            raw_bitmap != ptr::null_mut(),
523            "Null pointer returned from getDebugImage"
524        );
525        Ok(Bitmap::new(raw_bitmap))
526    }
527
528    pub fn get_framebuffer_bitmap(&self) -> Result<Bitmap, Error> {
529        let raw_bitmap = pd_func_caller!((*self.0).copyFrameBufferBitmap)?;
530        anyhow::ensure!(
531            raw_bitmap != ptr::null_mut(),
532            "Null pointer returned from getFrameBufferBitmap"
533        );
534        Ok(Bitmap::new(raw_bitmap))
535    }
536
537    pub fn set_background_color(&self, color: LCDSolidColor) -> Result<(), Error> {
538        pd_func_caller!((*self.0).setBackgroundColor, color.into())
539    }
540
541    pub fn set_draw_mode(&self, mode: LCDBitmapDrawMode) -> Result<(), Error> {
542        pd_func_caller!((*self.0).setDrawMode, mode)
543    }
544
545    pub fn mark_updated_rows(&self, range: RangeInclusive<i32>) -> Result<(), Error> {
546        let (start, end) = range.into_inner();
547        pd_func_caller!((*self.0).markUpdatedRows, start, end)
548    }
549
550    pub fn display(&self) -> Result<(), Error> {
551        pd_func_caller!((*self.0).display)
552    }
553
554    pub fn set_draw_offset(&self, offset: ScreenVector) -> Result<(), Error> {
555        pd_func_caller!((*self.0).setDrawOffset, offset.x, offset.y)
556    }
557
558    pub fn new_bitmap(&self, size: ScreenSize, bg_color: LCDColor) -> Result<Bitmap, Error> {
559        let raw_bitmap = pd_func_caller!(
560            (*self.0).newBitmap,
561            size.width,
562            size.height,
563            bg_color.into()
564        )?;
565        anyhow::ensure!(
566            raw_bitmap != ptr::null_mut(),
567            "Null pointer returned from new_bitmap"
568        );
569        Ok(Bitmap::new(raw_bitmap))
570    }
571
572    pub fn load_bitmap(&self, path: &str) -> Result<Bitmap, Error> {
573        let c_path = CString::new(path).map_err(Error::msg)?;
574        let mut out_err: *const crankstart_sys::ctypes::c_char = ptr::null_mut();
575        let raw_bitmap = pd_func_caller!((*self.0).loadBitmap, c_path.as_ptr(), &mut out_err)?;
576        if raw_bitmap == ptr::null_mut() {
577            if out_err != ptr::null_mut() {
578                let err_msg = unsafe { CStr::from_ptr(out_err).to_string_lossy().into_owned() };
579                Err(anyhow!(err_msg))
580            } else {
581                Err(anyhow!(
582                    "load_bitmap failed without providing an error message"
583                ))
584            }
585        } else {
586            Ok(Bitmap::new(raw_bitmap))
587        }
588    }
589
590    pub fn new_bitmap_table(&self, count: usize, size: ScreenSize) -> Result<BitmapTable, Error> {
591        let raw_bitmap_table = pd_func_caller!(
592            (*self.0).newBitmapTable,
593            count as i32,
594            size.width,
595            size.height
596        )?;
597
598        Ok(BitmapTable::new(raw_bitmap_table))
599    }
600
601    pub fn load_bitmap_table(&self, path: &str) -> Result<BitmapTable, Error> {
602        let c_path = CString::new(path).map_err(Error::msg)?;
603        let mut out_err: *const crankstart_sys::ctypes::c_char = ptr::null_mut();
604        let raw_bitmap_table =
605            pd_func_caller!((*self.0).loadBitmapTable, c_path.as_ptr(), &mut out_err)?;
606        if raw_bitmap_table == ptr::null_mut() {
607            if out_err != ptr::null_mut() {
608                let err_msg = unsafe { CStr::from_ptr(out_err).to_string_lossy().into_owned() };
609                Err(anyhow!(err_msg))
610            } else {
611                Err(anyhow!(
612                    "load_bitmap_table failed without providing an error message"
613                ))
614            }
615        } else {
616            Ok(BitmapTable::new(raw_bitmap_table))
617        }
618    }
619
620    pub fn clear(&self, color: LCDColor) -> Result<(), Error> {
621        pd_func_caller!((*self.0).clear, color.into())
622    }
623
624    pub fn draw_line(
625        &self,
626        p1: ScreenPoint,
627        p2: ScreenPoint,
628        width: i32,
629        color: LCDColor,
630    ) -> Result<(), Error> {
631        pd_func_caller!(
632            (*self.0).drawLine,
633            p1.x,
634            p1.y,
635            p2.x,
636            p2.y,
637            width,
638            color.into(),
639        )
640    }
641
642    pub fn fill_triangle(
643        &self,
644        p1: ScreenPoint,
645        p2: ScreenPoint,
646        p3: ScreenPoint,
647        color: LCDColor,
648    ) -> Result<(), Error> {
649        pd_func_caller!(
650            (*self.0).fillTriangle,
651            p1.x,
652            p1.y,
653            p2.x,
654            p2.y,
655            p3.x,
656            p3.y,
657            color.into(),
658        )
659    }
660
661    pub fn draw_rect(&self, rect: ScreenRect, color: LCDColor) -> Result<(), Error> {
662        pd_func_caller!(
663            (*self.0).drawRect,
664            rect.origin.x,
665            rect.origin.y,
666            rect.size.width,
667            rect.size.height,
668            color.into(),
669        )
670    }
671
672    pub fn fill_rect(&self, rect: ScreenRect, color: LCDColor) -> Result<(), Error> {
673        pd_func_caller!(
674            (*self.0).fillRect,
675            rect.origin.x,
676            rect.origin.y,
677            rect.size.width,
678            rect.size.height,
679            color.into(),
680        )
681    }
682
683    pub fn draw_ellipse(
684        &self,
685        center: ScreenPoint,
686        size: ScreenSize,
687        line_width: i32,
688        start_angle: f32,
689        end_angle: f32,
690        color: LCDColor,
691    ) -> Result<(), Error> {
692        pd_func_caller!(
693            (*self.0).drawEllipse,
694            center.x,
695            center.y,
696            size.width,
697            size.height,
698            line_width,
699            start_angle,
700            end_angle,
701            color.into(),
702        )
703    }
704
705    pub fn fill_ellipse(
706        &self,
707        target: OptionalBitmap,
708        stencil: OptionalBitmap,
709        center: ScreenPoint,
710        size: ScreenSize,
711        line_width: i32,
712        start_angle: f32,
713        end_angle: f32,
714        color: LCDColor,
715        clip: LCDRect,
716    ) -> Result<(), Error> {
717        pd_func_caller!(
718            (*self.0).fillEllipse,
719            center.x,
720            center.y,
721            size.width,
722            size.height,
723            start_angle,
724            end_angle,
725            color.into(),
726        )
727    }
728
729    pub fn load_font(&self, path: &str) -> Result<Font, Error> {
730        let c_path = CString::new(path).map_err(Error::msg)?;
731        let font = pd_func_caller!((*self.0).loadFont, c_path.as_ptr(), ptr::null_mut())?;
732        Font::new(font)
733    }
734
735    pub fn set_font(&self, font: &Font) -> Result<(), Error> {
736        pd_func_caller_log!((*self.0).setFont, font.0);
737        Ok(())
738    }
739
740    pub fn draw_text(&self, text: &str, position: ScreenPoint) -> Result<i32, Error> {
741        let c_text = CString::new(text).map_err(Error::msg)?;
742        pd_func_caller!(
743            (*self.0).drawText,
744            c_text.as_ptr() as *const core::ffi::c_void,
745            text.len() as usize,
746            PDStringEncoding::kUTF8Encoding,
747            position.x,
748            position.y,
749        )
750    }
751
752    pub fn get_text_width(&self, font: &Font, text: &str, tracking: i32) -> Result<i32, Error> {
753        let c_text = CString::new(text).map_err(Error::msg)?;
754        pd_func_caller!(
755            (*self.0).getTextWidth,
756            font.0,
757            c_text.as_ptr() as *const core::ffi::c_void,
758            text.len() as usize,
759            PDStringEncoding::kUTF8Encoding,
760            tracking,
761        )
762    }
763
764    pub fn get_font_height(&self, font: &Font) -> Result<u8, Error> {
765        pd_func_caller!((*self.0).getFontHeight, font.0)
766    }
767
768    pub fn get_system_text_width(&self, text: &str, tracking: i32) -> Result<i32, Error> {
769        let c_text = CString::new(text).map_err(Error::msg)?;
770        pd_func_caller!(
771            (*self.0).getTextWidth,
772            ptr::null_mut(),
773            c_text.as_ptr() as *const core::ffi::c_void,
774            text.len(),
775            PDStringEncoding::kUTF8Encoding,
776            tracking,
777        )
778    }
779}