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 ptr::null_mut(), )?;
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 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 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 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 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 self.push_context(bitmap.inner.borrow_mut().raw_bitmap)?;
484 let res = f();
485 self.pop_context()?;
486 res
487 }
488
489 fn push_context(&self, raw_bitmap: *mut crankstart_sys::LCDBitmap) -> Result<(), Error> {
491 pd_func_caller!((*self.0).pushContext, raw_bitmap)
492 }
493
494 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}