diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3814605e..58558b54 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -54,7 +54,7 @@ jobs: with: # From docs: `github.head_ref` is only available when the event that triggers # a workflow run is either `pull_request` or `pull_request_target`. - dry-run: ${{ github.event.inputs.dry-run == 'true' || github.head_ref }} + dry-run: ${{ github.event.inputs.dry-run == 'true' || (github.head_ref && 'true') || 'false' }} check-repo: ${{ (github.event.inputs && github.event.inputs.check-repo == 'true') || true }} ignore-unpublished-changes: ${{ github.head_ref == 'false' }} registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/Cargo.lock b/Cargo.lock index 809145ee..9b9eab2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -387,7 +387,7 @@ dependencies = [ "target", "termcolor", "toml", - "toml_edit 0.20.0", + "toml_edit 0.20.1", "try-lazy-init", "walkdir", "zip", @@ -2507,7 +2507,7 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "playdate" -version = "0.1.1" +version = "0.1.2" dependencies = [ "playdate-controls", "playdate-display", @@ -2579,7 +2579,7 @@ dependencies = [ [[package]] name = "playdate-display" -version = "0.2.0" +version = "0.3.0" dependencies = [ "playdate-sys", ] @@ -2593,7 +2593,7 @@ dependencies = [ [[package]] name = "playdate-graphics" -version = "0.2.3" +version = "0.3.0" dependencies = [ "playdate-color", "playdate-fs", @@ -2617,7 +2617,7 @@ dependencies = [ [[package]] name = "playdate-sprite" -version = "0.1.2" +version = "0.1.3" dependencies = [ "playdate-graphics", "playdate-sys", @@ -2634,7 +2634,7 @@ dependencies = [ [[package]] name = "playdate-system" -version = "0.2.0" +version = "0.3.0" dependencies = [ "playdate-sys", ] @@ -3057,9 +3057,9 @@ checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -3415,9 +3415,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95" +checksum = "ca676d9ba1a322c1b64eb8045a5ec5c0cfb0c9d08e15e9ff622589ad5221c8fe" dependencies = [ "indexmap 2.0.0", "serde", diff --git a/Cargo.toml b/Cargo.toml index 1d7de7ec..db5c091a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,13 +21,13 @@ repository = "https://github.com/boozook/playdate.git" [workspace.dependencies] color = { version = "0.1", path = "api/color", package = "playdate-color", default-features = false } ctrl = { version = "0.1", path = "api/ctrl", package = "playdate-controls", default-features = false } -display = { version = "0.2", path = "api/display", package = "playdate-display", default-features = false } +display = { version = "0.3", path = "api/display", package = "playdate-display", default-features = false } fs = { version = "0.1", path = "api/fs", package = "playdate-fs", default-features = false } -gfx = { version = "0.2", path = "api/gfx", package = "playdate-graphics", default-features = false } +gfx = { version = "0.3", path = "api/gfx", package = "playdate-graphics", default-features = false } menu = { version = "0.1", path = "api/menu", package = "playdate-menu", default-features = false } sound = { version = "0.1", path = "api/sound", package = "playdate-sound", default-features = false } sprite = { version = "0.1", path = "api/sprite", package = "playdate-sprite", default-features = false } -system = { version = "0.2", path = "api/system", package = "playdate-system", default-features = false } +system = { version = "0.3", path = "api/system", package = "playdate-system", default-features = false } sys = { version = "0.1", path = "api/sys", package = "playdate-sys", default-features = false } build = { version = "0.2", path = "support/build", package = "playdate-build", default-features = false } diff --git a/api/display/Cargo.toml b/api/display/Cargo.toml index e20d85b5..09a4abfe 100644 --- a/api/display/Cargo.toml +++ b/api/display/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate-display" -version = "0.2.0" +version = "0.3.0" readme = "README.md" description = "High-level Display API built on-top of Playdate API" keywords = ["playdate", "sdk", "api", "gamedev"] diff --git a/api/display/src/lib.rs b/api/display/src/lib.rs index a2a41540..48247946 100644 --- a/api/display/src/lib.rs +++ b/api/display/src/lib.rs @@ -17,6 +17,14 @@ impl Display { pub fn Default() -> Self { Self(Default::default()) } } +impl Display { + /// Creates [`Display`] without type parameter requirement. + /// + /// Uses [`api::Cache`]. + #[allow(non_snake_case)] + pub fn Cached() -> Self { Self(Default::default()) } +} + impl Default for Display { fn default() -> Self { Self(Default::default()) } } @@ -169,14 +177,97 @@ pub mod api { use core::ffi::c_float; use core::ffi::c_int; use core::ffi::c_uint; + use core::ptr::NonNull; + use sys::ffi::playdate_display; + /// Default display api end-point, ZST. + /// + /// All calls approximately costs ~3 derefs. #[derive(Debug, Clone, Copy, core::default::Default)] pub struct Default; - impl Api for Default {} + /// Cached display api end-point. + /// + /// Stores one reference, so size on stack is eq `usize`. + /// + /// All calls approximately costs ~1 deref. + #[derive(Clone, Copy)] + #[cfg_attr(feature = "bindings-derive-debug", derive(Debug))] + pub struct Cache(&'static playdate_display); + + impl core::default::Default for Cache { + fn default() -> Self { Self(sys::api!(display)) } + } + + impl From<*const playdate_display> for Cache { + #[inline(always)] + fn from(ptr: *const playdate_display) -> Self { Self(unsafe { ptr.as_ref() }.expect("display")) } + } + + impl From<&'static playdate_display> for Cache { + #[inline(always)] + fn from(r: &'static playdate_display) -> Self { Self(r) } + } + + impl From> for Cache { + #[inline(always)] + fn from(ptr: NonNull) -> Self { Self(unsafe { ptr.as_ref() }) } + } + + impl From<&'_ NonNull> for Cache { + #[inline(always)] + fn from(ptr: &NonNull) -> Self { Self(unsafe { ptr.as_ref() }) } + } + + + impl Api for Cache { + /// Equivalent to [`sys::ffi::playdate_display::getWidth`] + #[doc(alias = "sys::ffi::playdate_display::getWidth")] + #[inline(always)] + fn get_width(&self) -> unsafe extern "C" fn() -> c_int { self.0.getWidth.expect("getWidth") } + + /// Equivalent to [`sys::ffi::playdate_display::getHeight`] + #[doc(alias = "sys::ffi::playdate_display::getHeight")] + #[inline(always)] + fn get_height(&self) -> unsafe extern "C" fn() -> c_int { self.0.getHeight.expect("getHeight") } + + /// Equivalent to [`sys::ffi::playdate_display::setRefreshRate`] + #[doc(alias = "sys::ffi::playdate_display::setRefreshRate")] + #[inline(always)] + fn set_refresh_rate(&self) -> unsafe extern "C" fn(rate: c_float) { + self.0.setRefreshRate.expect("setRefreshRate") + } + + /// Equivalent to [`sys::ffi::playdate_display::setInverted`] + #[doc(alias = "sys::ffi::playdate_display::setInverted")] + #[inline(always)] + fn set_inverted(&self) -> unsafe extern "C" fn(flag: c_int) { self.0.setInverted.expect("setInverted") } + + /// Equivalent to [`sys::ffi::playdate_display::setScale`] + #[doc(alias = "sys::ffi::playdate_display::setScale")] + #[inline(always)] + fn set_scale(&self) -> unsafe extern "C" fn(s: c_uint) { self.0.setScale.expect("setScale") } + + /// Equivalent to [`sys::ffi::playdate_display::setMosaic`] + #[doc(alias = "sys::ffi::playdate_display::setMosaic")] + #[inline(always)] + fn set_mosaic(&self) -> unsafe extern "C" fn(x: c_uint, y: c_uint) { self.0.setMosaic.expect("setMosaic") } + + /// Equivalent to [`sys::ffi::playdate_display::setFlipped`] + #[doc(alias = "sys::ffi::playdate_display::setFlipped")] + #[inline(always)] + fn set_flipped(&self) -> unsafe extern "C" fn(x: c_int, y: c_int) { self.0.setFlipped.expect("setFlipped") } + + /// Equivalent to [`sys::ffi::playdate_display::setOffset`] + #[doc(alias = "sys::ffi::playdate_display::setOffset")] + #[inline(always)] + fn set_offset(&self) -> unsafe extern "C" fn(x: c_int, y: c_int) { self.0.setOffset.expect("setOffset") } + } + + pub trait Api { /// Equivalent to [`sys::ffi::playdate_display::getWidth`] #[doc(alias = "sys::ffi::playdate_display::getWidth")] diff --git a/api/gfx/Cargo.toml b/api/gfx/Cargo.toml index a96d3cee..3005dfa2 100644 --- a/api/gfx/Cargo.toml +++ b/api/gfx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate-graphics" -version = "0.2.3" +version = "0.3.0" readme = "README.md" description = "High-level graphics API built on-top of Playdate API" keywords = ["playdate", "sdk", "api", "gamedev"] diff --git a/api/gfx/src/api.rs b/api/gfx/src/api.rs new file mode 100644 index 00000000..58fc1afe --- /dev/null +++ b/api/gfx/src/api.rs @@ -0,0 +1,738 @@ +//! Global Playdate graphics API. + +use core::ffi::c_void; +use core::ffi::c_char; +use core::ffi::c_float; +use core::ffi::c_int; +use core::ptr::NonNull; + +use sys::ffi::LCDBitmap; +use sys::ffi::LCDColor; +use sys::ffi::LCDRect; +use sys::ffi::LCDLineCapStyle; +use sys::ffi::LCDPolygonFillRule; +use sys::ffi::playdate_graphics; +use sys::ffi::LCDBitmapDrawMode; +use sys::ffi::LCDSolidColor; +use sys::ffi::LCDBitmapFlip; +use sys::ffi::LCDBitmapTable; +use sys::ffi::LCDFontPage; +use sys::ffi::LCDFontGlyph; +use sys::ffi::LCDFont; +use sys::ffi::PDStringEncoding; +use sys::ffi::LCDFontData; +use sys::ffi::playdate_video; + + +/// Default graphics api end-point, ZST. +/// +/// All calls approximately costs ~3 derefs. +#[derive(Debug, Clone, Copy, core::default::Default)] +pub struct Default; +impl crate::bitmap::api::Api for Default {} +impl crate::bitmap::table::api::Api for Default {} +impl crate::text::api::Api for Default {} + +impl Api for Default { + #[inline(always)] + fn video>(&self) -> VApi { + VApi::from(sys::api!(graphics.video)) + } +} + + +/// Cached graphics api end-point. +/// +/// Stores one reference, so size on stack is eq `usize`. +/// +/// All calls approximately costs ~1 deref. +#[derive(Clone, Copy)] +#[cfg_attr(feature = "bindings-derive-debug", derive(Debug))] +pub struct Cache(&'static playdate_graphics); + +impl core::default::Default for Cache { + fn default() -> Self { Self(sys::api!(graphics)) } +} + +impl From<*const playdate_graphics> for Cache { + #[inline(always)] + fn from(ptr: *const playdate_graphics) -> Self { Self(unsafe { ptr.as_ref() }.expect("system")) } +} + +impl From<&'static playdate_graphics> for Cache { + #[inline(always)] + fn from(r: &'static playdate_graphics) -> Self { Self(r) } +} + +impl From> for Cache { + #[inline(always)] + fn from(ptr: NonNull) -> Self { Self(unsafe { ptr.as_ref() }) } +} + +impl From<&'_ NonNull> for Cache { + #[inline(always)] + fn from(ptr: &NonNull) -> Self { Self(unsafe { ptr.as_ref() }) } +} + + +impl crate::text::api::Api for Cache { + /// Equivalent to [`sys::ffi::playdate_graphics::drawText`] + #[doc(alias = "sys::ffi::playdate_graphics::drawText")] + #[inline(always)] + fn draw_text( + &self) + -> unsafe extern "C" fn(text: *const c_void, + len: usize, + encoding: PDStringEncoding, + x: c_int, + y: c_int) -> c_int { + self.0.drawText.expect("drawText") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getTextWidth`] + #[doc(alias = "sys::ffi::playdate_graphics::getTextWidth")] + #[inline(always)] + fn get_text_width( + &self) + -> unsafe extern "C" fn(font: *mut LCDFont, + text: *const c_void, + len: usize, + encoding: PDStringEncoding, + tracking: c_int) -> c_int { + self.0.getTextWidth.expect("getTextWidth") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getFontHeight`] + #[doc(alias = "sys::ffi::playdate_graphics::getFontHeight")] + #[inline(always)] + fn get_font_height(&self) -> unsafe extern "C" fn(font: *mut LCDFont) -> u8 { + self.0.getFontHeight.expect("getFontHeight") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::setFont`] + #[doc(alias = "sys::ffi::playdate_graphics::setFont")] + #[inline(always)] + fn set_font(&self) -> unsafe extern "C" fn(font: *mut LCDFont) { self.0.setFont.expect("setFont") } + + /// Equivalent to [`sys::ffi::playdate_graphics::setTextTracking`] + #[doc(alias = "sys::ffi::playdate_graphics::setTextTracking")] + #[inline(always)] + fn set_text_tracking(&self) -> unsafe extern "C" fn(tracking: c_int) { + self.0.setTextTracking.expect("setTextTracking") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getGlyphKerning`] + #[doc(alias = "sys::ffi::playdate_graphics::getGlyphKerning")] + #[inline(always)] + fn get_glyph_kerning( + &self) + -> unsafe extern "C" fn(glyph: *mut LCDFontGlyph, glyphcode: u32, nextcode: u32) -> c_int { + self.0.getGlyphKerning.expect("getGlyphKerning") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::loadFont`] + #[doc(alias = "sys::ffi::playdate_graphics::loadFont")] + #[inline(always)] + fn load_font(&self) -> unsafe extern "C" fn(path: *const c_char, outErr: *mut *const c_char) -> *mut LCDFont { + self.0.loadFont.expect("loadFont") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getFontPage`] + #[doc(alias = "sys::ffi::playdate_graphics::getFontPage")] + #[inline(always)] + fn get_font_page(&self) -> unsafe extern "C" fn(font: *mut LCDFont, c: u32) -> *mut LCDFontPage { + self.0.getFontPage.expect("getFontPage") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getPageGlyph`] + #[doc(alias = "sys::ffi::playdate_graphics::getPageGlyph")] + #[inline(always)] + fn get_page_glyph( + &self) + -> unsafe extern "C" fn(page: *mut LCDFontPage, + c: u32, + bitmap: *mut *mut LCDBitmap, + advance: *mut c_int) -> *mut LCDFontGlyph { + self.0.getPageGlyph.expect("getPageGlyph") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::makeFontFromData`] + #[doc(alias = "sys::ffi::playdate_graphics::makeFontFromData")] + #[inline(always)] + fn make_font_from_data(&self) -> unsafe extern "C" fn(data: *mut LCDFontData, wide: c_int) -> *mut LCDFont { + self.0.makeFontFromData.expect("makeFontFromData") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::setTextLeading`] + #[doc(alias = "sys::ffi::playdate_graphics::setTextLeading")] + #[inline(always)] + fn set_text_leading(&self) -> unsafe extern "C" fn(lineHeightAdustment: c_int) { + self.0.setTextLeading.expect("setTextLeading") + } +} + + +impl crate::bitmap::table::api::Api for Cache { + #[inline(always)] + fn new_bitmap_table( + &self) + -> unsafe extern "C" fn(count: c_int, width: c_int, height: c_int) -> *mut LCDBitmapTable { + self.0.newBitmapTable.expect("newBitmapTable") + } + + #[inline(always)] + fn free_bitmap_table(&self) -> unsafe extern "C" fn(table: *mut LCDBitmapTable) { + self.0.freeBitmapTable.expect("freeBitmapTable") + } + + #[inline(always)] + fn load_bitmap_table( + &self) + -> unsafe extern "C" fn(path: *const c_char, out_err: *mut *const c_char) -> *mut LCDBitmapTable { + self.0.loadBitmapTable.expect("loadBitmapTable") + } + + #[inline(always)] + fn load_into_bitmap_table( + &self) + -> unsafe extern "C" fn(path: *const c_char, table: *mut LCDBitmapTable, out_err: *mut *const c_char) { + self.0.loadIntoBitmapTable.expect("loadIntoBitmapTable") + } + + #[inline(always)] + fn get_table_bitmap(&self) -> unsafe extern "C" fn(table: *mut LCDBitmapTable, idx: c_int) -> *mut LCDBitmap { + self.0.getTableBitmap.expect("getTableBitmap") + } +} + + +impl crate::bitmap::api::Api for Cache { + #[inline(always)] + fn new_bitmap(&self) + -> unsafe extern "C" fn(width: c_int, height: c_int, bgcolor: LCDColor) -> *mut LCDBitmap { + self.0.newBitmap.expect("newBitmap") + } + + #[inline(always)] + fn free_bitmap(&self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap) { + self.0.freeBitmap.expect("freeBitmap") + } + + #[inline(always)] + fn load_bitmap(&self) + -> unsafe extern "C" fn(path: *const c_char, outerr: *mut *const c_char) -> *mut LCDBitmap { + self.0.loadBitmap.expect("loadBitmap") + } + + #[inline(always)] + fn copy_bitmap(&self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap) -> *mut LCDBitmap { + self.0.copyBitmap.expect("copyBitmap") + } + + #[inline(always)] + fn load_into_bitmap( + &self) + -> unsafe extern "C" fn(path: *const c_char, bitmap: *mut LCDBitmap, out_err: *mut *const c_char) { + self.0.loadIntoBitmap.expect("loadIntoBitmap") + } + + #[inline(always)] + fn get_bitmap_data( + &self) + -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, + width: *mut c_int, + height: *mut c_int, + row_bytes: *mut c_int, + mask: *mut *mut u8, + data: *mut *mut u8) { + self.0.getBitmapData.expect("getBitmapData") + } + + #[inline(always)] + fn clear_bitmap(&self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, bgcolor: LCDColor) { + self.0.clearBitmap.expect("clearBitmap") + } + + #[inline(always)] + fn rotated_bitmap( + &self) + -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, + rotation: c_float, + x_scale: c_float, + y_scale: c_float, + allocedSize: *mut c_int) -> *mut LCDBitmap { + self.0.rotatedBitmap.expect("rotatedBitmap") + } + + #[inline(always)] + fn set_bitmap_mask(&self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, mask: *mut LCDBitmap) -> c_int { + self.0.setBitmapMask.expect("setBitmapMask") + } + + #[inline(always)] + fn get_bitmap_mask(&self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap) -> *mut LCDBitmap { + self.0.getBitmapMask.expect("getBitmapMask") + } + + #[inline(always)] + fn draw_bitmap(&self) + -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, x: c_int, y: c_int, flip: LCDBitmapFlip) { + self.0.drawBitmap.expect("drawBitmap") + } + + #[inline(always)] + fn tile_bitmap( + &self) + -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, + x: c_int, + y: c_int, + width: c_int, + height: c_int, + flip: LCDBitmapFlip) { + self.0.tileBitmap.expect("tileBitmap") + } + + #[inline(always)] + fn draw_rotated_bitmap( + &self) + -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, + x: c_int, + y: c_int, + rotation: c_float, + center_x: c_float, + center_y: c_float, + x_scale: c_float, + y_scale: c_float) { + self.0.drawRotatedBitmap.expect("drawRotatedBitmap") + } + + #[inline(always)] + fn draw_scaled_bitmap( + &self) + -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, x: c_int, y: c_int, x_scale: c_float, y_scale: c_float) { + self.0.drawScaledBitmap.expect("drawScaledBitmap") + } + + #[inline(always)] + fn check_mask_collision( + &self) + -> unsafe extern "C" fn(bitmap1: *mut LCDBitmap, + x1: c_int, + y1: c_int, + flip1: LCDBitmapFlip, + bitmap2: *mut LCDBitmap, + x2: c_int, + y2: c_int, + flip2: LCDBitmapFlip, + rect: LCDRect) -> c_int { + self.0.checkMaskCollision.expect("checkMaskCollision") + } + + #[inline(always)] + fn set_color_to_pattern( + &self) + -> unsafe extern "C" fn(color: *mut LCDColor, + bitmap: *mut LCDBitmap, + x: core::ffi::c_int, + y: core::ffi::c_int) { + self.0.setColorToPattern.expect("setColorToPattern") + } +} + + +impl Api for Cache { + /// Equivalent to [`sys::ffi::playdate_graphics::video`] + #[doc(alias = "sys::ffi::playdate_graphics::video")] + #[inline(always)] + fn video>(&self) -> VApi { + VApi::from(self.0.video) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::clear`] + #[doc(alias = "sys::ffi::playdate_graphics::clear")] + #[inline(always)] + fn clear(&self) -> unsafe extern "C" fn(color: LCDColor) { self.0.clear.expect("clear") } + + /// Equivalent to [`sys::ffi::playdate_graphics::setBackgroundColor`] + #[doc(alias = "sys::ffi::playdate_graphics::setBackgroundColor")] + #[inline(always)] + fn set_background_color(&self) -> unsafe extern "C" fn(color: LCDSolidColor) { + self.0.setBackgroundColor.expect("setBackgroundColor") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::setStencil`] + #[doc(alias = "sys::ffi::playdate_graphics::setStencil")] + #[inline(always)] + fn set_stencil(&self) -> unsafe extern "C" fn(stencil: *mut LCDBitmap) { + self.0.setStencil.expect("setStencil") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::setDrawMode`] + #[doc(alias = "sys::ffi::playdate_graphics::setDrawMode")] + #[inline(always)] + fn set_draw_mode(&self) -> unsafe extern "C" fn(mode: LCDBitmapDrawMode) { + self.0.setDrawMode.expect("setDrawMode") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::setDrawOffset`] + #[doc(alias = "sys::ffi::playdate_graphics::setDrawOffset")] + #[inline(always)] + fn set_draw_offset(&self) -> unsafe extern "C" fn(dx: c_int, dy: c_int) { + self.0.setDrawOffset.expect("setDrawOffset") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::setClipRect`] + #[doc(alias = "sys::ffi::playdate_graphics::setClipRect")] + #[inline(always)] + fn set_clip_rect(&self) -> unsafe extern "C" fn(x: c_int, y: c_int, width: c_int, height: c_int) { + self.0.setClipRect.expect("setClipRect") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::clearClipRect`] + #[doc(alias = "sys::ffi::playdate_graphics::clearClipRect")] + #[inline(always)] + fn clear_clip_rect(&self) -> unsafe extern "C" fn() { self.0.clearClipRect.expect("clearClipRect") } + + /// Equivalent to [`sys::ffi::playdate_graphics::setLineCapStyle`] + #[doc(alias = "sys::ffi::playdate_graphics::setLineCapStyle")] + #[inline(always)] + fn set_line_cap_style(&self) -> unsafe extern "C" fn(endCapStyle: LCDLineCapStyle) { + self.0.setLineCapStyle.expect("setLineCapStyle") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::pushContext`] + #[doc(alias = "sys::ffi::playdate_graphics::pushContext")] + #[inline(always)] + fn push_context(&self) -> unsafe extern "C" fn(target: *mut LCDBitmap) { + self.0.pushContext.expect("pushContext") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::popContext`] + #[doc(alias = "sys::ffi::playdate_graphics::popContext")] + #[inline(always)] + fn pop_context(&self) -> unsafe extern "C" fn() { self.0.popContext.expect("popContext") } + + /// Equivalent to [`sys::ffi::playdate_graphics::drawLine`] + #[doc(alias = "sys::ffi::playdate_graphics::drawLine")] + #[inline(always)] + fn draw_line( + &self) + -> unsafe extern "C" fn(x1: c_int, y1: c_int, x2: c_int, y2: c_int, width: c_int, color: LCDColor) { + self.0.drawLine.expect("drawLine") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::fillTriangle`] + #[doc(alias = "sys::ffi::playdate_graphics::fillTriangle")] + #[inline(always)] + fn fill_triangle( + &self) + -> unsafe extern "C" fn(x1: c_int, y1: c_int, x2: c_int, y2: c_int, x3: c_int, y3: c_int, color: LCDColor) { + self.0.fillTriangle.expect("fillTriangle") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::drawRect`] + #[doc(alias = "sys::ffi::playdate_graphics::drawRect")] + #[inline(always)] + fn draw_rect(&self) -> unsafe extern "C" fn(x: c_int, y: c_int, width: c_int, height: c_int, color: LCDColor) { + self.0.drawRect.expect("drawRect") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::fillRect`] + #[doc(alias = "sys::ffi::playdate_graphics::fillRect")] + #[inline(always)] + fn fill_rect(&self) -> unsafe extern "C" fn(x: c_int, y: c_int, width: c_int, height: c_int, color: LCDColor) { + self.0.fillRect.expect("fillRect") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::drawEllipse`] + #[doc(alias = "sys::ffi::playdate_graphics::drawEllipse")] + #[inline(always)] + fn draw_ellipse( + &self) + -> unsafe extern "C" fn(x: c_int, + y: c_int, + width: c_int, + height: c_int, + lineWidth: c_int, + startAngle: c_float, + endAngle: c_float, + color: LCDColor) { + self.0.drawEllipse.expect("drawEllipse") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::fillEllipse`] + #[doc(alias = "sys::ffi::playdate_graphics::fillEllipse")] + #[inline(always)] + fn fill_ellipse( + &self) + -> unsafe extern "C" fn(x: c_int, + y: c_int, + width: c_int, + height: c_int, + startAngle: c_float, + endAngle: c_float, + color: LCDColor) { + self.0.fillEllipse.expect("fillEllipse") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getFrame`] + #[doc(alias = "sys::ffi::playdate_graphics::getFrame")] + #[inline(always)] + fn get_frame(&self) -> unsafe extern "C" fn() -> *mut u8 { self.0.getFrame.expect("getFrame") } + + /// Equivalent to [`sys::ffi::playdate_graphics::getDisplayFrame`] + #[doc(alias = "sys::ffi::playdate_graphics::getDisplayFrame")] + #[inline(always)] + fn get_display_frame(&self) -> unsafe extern "C" fn() -> *mut u8 { + self.0.getDisplayFrame.expect("getDisplayFrame") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getDebugBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::getDebugBitmap")] + #[inline(always)] + fn get_debug_bitmap(&self) -> Option *mut LCDBitmap> { + sys::api!(graphics).getDebugBitmap + } + + /// Equivalent to [`sys::ffi::playdate_graphics::copyFrameBufferBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::copyFrameBufferBitmap")] + #[inline(always)] + fn copy_frame_buffer_bitmap(&self) -> unsafe extern "C" fn() -> *mut LCDBitmap { + self.0.copyFrameBufferBitmap.expect("copyFrameBufferBitmap") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::markUpdatedRows`] + #[doc(alias = "sys::ffi::playdate_graphics::markUpdatedRows")] + #[inline(always)] + fn mark_updated_rows(&self) -> unsafe extern "C" fn(start: c_int, end: c_int) { + self.0.markUpdatedRows.expect("markUpdatedRows") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::display`] + #[doc(alias = "sys::ffi::playdate_graphics::display")] + #[inline(always)] + fn display(&self) -> unsafe extern "C" fn() { self.0.display.expect("display") } + + /// Equivalent to [`sys::ffi::playdate_graphics::setScreenClipRect`] + #[doc(alias = "sys::ffi::playdate_graphics::setScreenClipRect")] + #[inline(always)] + fn set_screen_clip_rect(&self) -> unsafe extern "C" fn(x: c_int, y: c_int, width: c_int, height: c_int) { + self.0.setScreenClipRect.expect("setScreenClipRect") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::fillPolygon`] + #[doc(alias = "sys::ffi::playdate_graphics::fillPolygon")] + #[inline(always)] + fn fill_polygon( + &self) + -> unsafe extern "C" fn(nPoints: c_int, coords: *mut c_int, color: LCDColor, fillrule: LCDPolygonFillRule) { + self.0.fillPolygon.expect("fillPolygon") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getDisplayBufferBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::getDisplayBufferBitmap")] + #[inline(always)] + fn get_display_buffer_bitmap(&self) -> unsafe extern "C" fn() -> *mut LCDBitmap { + self.0.getDisplayBufferBitmap.expect("getDisplayBufferBitmap") + } + + /// Equivalent to [`sys::ffi::playdate_graphics::setStencilImage`] + #[doc(alias = "sys::ffi::playdate_graphics::setStencilImage")] + #[inline(always)] + fn set_stencil_image(&self) -> unsafe extern "C" fn(stencil: *mut LCDBitmap, tile: c_int) { + self.0.setStencilImage.expect("setStencilImage") + } +} + + +pub trait Api: crate::bitmap::api::Api + crate::bitmap::table::api::Api + crate::text::api::Api { + /// Equivalent to [`sys::ffi::playdate_graphics::video`] + #[doc(alias = "sys::ffi::playdate_graphics::video")] + fn video(&self) -> VApi + where VApi: From<*const playdate_video> + crate::video::api::Api; + + /// Equivalent to [`sys::ffi::playdate_graphics::clear`] + #[doc(alias = "sys::ffi::playdate_graphics::clear")] + #[inline(always)] + fn clear(&self) -> unsafe extern "C" fn(color: LCDColor) { *sys::api!(graphics.clear) } + + /// Equivalent to [`sys::ffi::playdate_graphics::setBackgroundColor`] + #[doc(alias = "sys::ffi::playdate_graphics::setBackgroundColor")] + #[inline(always)] + fn set_background_color(&self) -> unsafe extern "C" fn(color: LCDSolidColor) { + *sys::api!(graphics.setBackgroundColor) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::setStencil`] + #[doc(alias = "sys::ffi::playdate_graphics::setStencil")] + #[inline(always)] + fn set_stencil(&self) -> unsafe extern "C" fn(stencil: *mut LCDBitmap) { *sys::api!(graphics.setStencil) } + + /// Equivalent to [`sys::ffi::playdate_graphics::setDrawMode`] + #[doc(alias = "sys::ffi::playdate_graphics::setDrawMode")] + #[inline(always)] + fn set_draw_mode(&self) -> unsafe extern "C" fn(mode: LCDBitmapDrawMode) { *sys::api!(graphics.setDrawMode) } + + /// Equivalent to [`sys::ffi::playdate_graphics::setDrawOffset`] + #[doc(alias = "sys::ffi::playdate_graphics::setDrawOffset")] + #[inline(always)] + fn set_draw_offset(&self) -> unsafe extern "C" fn(dx: c_int, dy: c_int) { *sys::api!(graphics.setDrawOffset) } + + /// Equivalent to [`sys::ffi::playdate_graphics::setClipRect`] + #[doc(alias = "sys::ffi::playdate_graphics::setClipRect")] + #[inline(always)] + fn set_clip_rect(&self) -> unsafe extern "C" fn(x: c_int, y: c_int, width: c_int, height: c_int) { + *sys::api!(graphics.setClipRect) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::clearClipRect`] + #[doc(alias = "sys::ffi::playdate_graphics::clearClipRect")] + #[inline(always)] + fn clear_clip_rect(&self) -> unsafe extern "C" fn() { *sys::api!(graphics.clearClipRect) } + + /// Equivalent to [`sys::ffi::playdate_graphics::setLineCapStyle`] + #[doc(alias = "sys::ffi::playdate_graphics::setLineCapStyle")] + #[inline(always)] + fn set_line_cap_style(&self) -> unsafe extern "C" fn(endCapStyle: LCDLineCapStyle) { + *sys::api!(graphics.setLineCapStyle) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::pushContext`] + #[doc(alias = "sys::ffi::playdate_graphics::pushContext")] + #[inline(always)] + fn push_context(&self) -> unsafe extern "C" fn(target: *mut LCDBitmap) { *sys::api!(graphics.pushContext) } + + /// Equivalent to [`sys::ffi::playdate_graphics::popContext`] + #[doc(alias = "sys::ffi::playdate_graphics::popContext")] + #[inline(always)] + fn pop_context(&self) -> unsafe extern "C" fn() { *sys::api!(graphics.popContext) } + + /// Equivalent to [`sys::ffi::playdate_graphics::drawLine`] + #[doc(alias = "sys::ffi::playdate_graphics::drawLine")] + #[inline(always)] + fn draw_line( + &self) + -> unsafe extern "C" fn(x1: c_int, y1: c_int, x2: c_int, y2: c_int, width: c_int, color: LCDColor) { + *sys::api!(graphics.drawLine) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::fillTriangle`] + #[doc(alias = "sys::ffi::playdate_graphics::fillTriangle")] + #[inline(always)] + fn fill_triangle( + &self) + -> unsafe extern "C" fn(x1: c_int, y1: c_int, x2: c_int, y2: c_int, x3: c_int, y3: c_int, color: LCDColor) { + *sys::api!(graphics.fillTriangle) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::drawRect`] + #[doc(alias = "sys::ffi::playdate_graphics::drawRect")] + #[inline(always)] + fn draw_rect(&self) -> unsafe extern "C" fn(x: c_int, y: c_int, width: c_int, height: c_int, color: LCDColor) { + *sys::api!(graphics.drawRect) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::fillRect`] + #[doc(alias = "sys::ffi::playdate_graphics::fillRect")] + #[inline(always)] + fn fill_rect(&self) -> unsafe extern "C" fn(x: c_int, y: c_int, width: c_int, height: c_int, color: LCDColor) { + *sys::api!(graphics.fillRect) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::drawEllipse`] + #[doc(alias = "sys::ffi::playdate_graphics::drawEllipse")] + #[inline(always)] + fn draw_ellipse( + &self) + -> unsafe extern "C" fn(x: c_int, + y: c_int, + width: c_int, + height: c_int, + lineWidth: c_int, + startAngle: c_float, + endAngle: c_float, + color: LCDColor) { + *sys::api!(graphics.drawEllipse) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::fillEllipse`] + #[doc(alias = "sys::ffi::playdate_graphics::fillEllipse")] + #[inline(always)] + fn fill_ellipse( + &self) + -> unsafe extern "C" fn(x: c_int, + y: c_int, + width: c_int, + height: c_int, + startAngle: c_float, + endAngle: c_float, + color: LCDColor) { + *sys::api!(graphics.fillEllipse) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getFrame`] + #[doc(alias = "sys::ffi::playdate_graphics::getFrame")] + #[inline(always)] + fn get_frame(&self) -> unsafe extern "C" fn() -> *mut u8 { *sys::api!(graphics.getFrame) } + + /// Equivalent to [`sys::ffi::playdate_graphics::getDisplayFrame`] + #[doc(alias = "sys::ffi::playdate_graphics::getDisplayFrame")] + #[inline(always)] + fn get_display_frame(&self) -> unsafe extern "C" fn() -> *mut u8 { *sys::api!(graphics.getDisplayFrame) } + + /// Equivalent to [`sys::ffi::playdate_graphics::getDebugBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::getDebugBitmap")] + #[inline(always)] + fn get_debug_bitmap(&self) -> Option *mut LCDBitmap> { + sys::api!(graphics).getDebugBitmap + } + + /// Equivalent to [`sys::ffi::playdate_graphics::copyFrameBufferBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::copyFrameBufferBitmap")] + #[inline(always)] + fn copy_frame_buffer_bitmap(&self) -> unsafe extern "C" fn() -> *mut LCDBitmap { + *sys::api!(graphics.copyFrameBufferBitmap) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::markUpdatedRows`] + #[doc(alias = "sys::ffi::playdate_graphics::markUpdatedRows")] + #[inline(always)] + fn mark_updated_rows(&self) -> unsafe extern "C" fn(start: c_int, end: c_int) { + *sys::api!(graphics.markUpdatedRows) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::display`] + #[doc(alias = "sys::ffi::playdate_graphics::display")] + #[inline(always)] + fn display(&self) -> unsafe extern "C" fn() { *sys::api!(graphics.display) } + + /// Equivalent to [`sys::ffi::playdate_graphics::setScreenClipRect`] + #[doc(alias = "sys::ffi::playdate_graphics::setScreenClipRect")] + #[inline(always)] + fn set_screen_clip_rect(&self) -> unsafe extern "C" fn(x: c_int, y: c_int, width: c_int, height: c_int) { + *sys::api!(graphics.setScreenClipRect) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::fillPolygon`] + #[doc(alias = "sys::ffi::playdate_graphics::fillPolygon")] + #[inline(always)] + fn fill_polygon( + &self) + -> unsafe extern "C" fn(nPoints: c_int, coords: *mut c_int, color: LCDColor, fillrule: LCDPolygonFillRule) { + *sys::api!(graphics.fillPolygon) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getDisplayBufferBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::getDisplayBufferBitmap")] + #[inline(always)] + fn get_display_buffer_bitmap(&self) -> unsafe extern "C" fn() -> *mut LCDBitmap { + *sys::api!(graphics.getDisplayBufferBitmap) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::setStencilImage`] + #[doc(alias = "sys::ffi::playdate_graphics::setStencilImage")] + #[inline(always)] + fn set_stencil_image(&self) -> unsafe extern "C" fn(stencil: *mut LCDBitmap, tile: c_int) { + *sys::api!(graphics.setStencilImage) + } +} diff --git a/api/gfx/src/bitmap/api.rs b/api/gfx/src/bitmap/api.rs index b07cc049..9449d342 100644 --- a/api/gfx/src/bitmap/api.rs +++ b/api/gfx/src/bitmap/api.rs @@ -13,31 +13,42 @@ pub struct Default; impl Api for Default {} +/// End-point with methods about ops over bitmap. pub trait Api { + /// Equivalent to [`sys::ffi::playdate_graphics::newBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::newBitmap")] fn new_bitmap(&self) -> unsafe extern "C" fn(width: c_int, height: c_int, bgcolor: LCDColor) -> *mut LCDBitmap { *sys::api!(graphics.newBitmap) } + /// Equivalent to [`sys::ffi::playdate_graphics::freeBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::freeBitmap")] fn free_bitmap(&self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap) { *sys::api!(graphics.freeBitmap) } - + /// Equivalent to [`sys::ffi::playdate_graphics::loadBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::loadBitmap")] fn load_bitmap(&self) -> unsafe extern "C" fn(path: *const c_char, outerr: *mut *const c_char) -> *mut LCDBitmap { *sys::api!(graphics.loadBitmap) } + /// Equivalent to [`sys::ffi::playdate_graphics::copyBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::copyBitmap")] fn copy_bitmap(&self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap) -> *mut LCDBitmap { *sys::api!(graphics.copyBitmap) } + /// Equivalent to [`sys::ffi::playdate_graphics::loadIntoBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::loadIntoBitmap")] fn load_into_bitmap( &self) -> unsafe extern "C" fn(path: *const c_char, bitmap: *mut LCDBitmap, out_err: *mut *const c_char) { *sys::api!(graphics.loadIntoBitmap) } - + /// Equivalent to [`sys::ffi::playdate_graphics::getBitmapData`] + #[doc(alias = "sys::ffi::playdate_graphics::getBitmapData")] fn get_bitmap_data( &self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, @@ -49,11 +60,14 @@ pub trait Api { *sys::api!(graphics.getBitmapData) } - + /// Equivalent to [`sys::ffi::playdate_graphics::clearBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::clearBitmap")] fn clear_bitmap(&self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, bgcolor: LCDColor) { *sys::api!(graphics.clearBitmap) } + /// Equivalent to [`sys::ffi::playdate_graphics::rotatedBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::rotatedBitmap")] fn rotated_bitmap( &self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, @@ -64,19 +78,27 @@ pub trait Api { *sys::api!(graphics.rotatedBitmap) } + /// Equivalent to [`sys::ffi::playdate_graphics::setBitmapMask`] + #[doc(alias = "sys::ffi::playdate_graphics::setBitmapMask")] fn set_bitmap_mask(&self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, mask: *mut LCDBitmap) -> c_int { *sys::api!(graphics.setBitmapMask) } + /// Equivalent to [`sys::ffi::playdate_graphics::getBitmapMask`] + #[doc(alias = "sys::ffi::playdate_graphics::getBitmapMask")] fn get_bitmap_mask(&self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap) -> *mut LCDBitmap { *sys::api!(graphics.getBitmapMask) } + /// Equivalent to [`sys::ffi::playdate_graphics::drawBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::drawBitmap")] fn draw_bitmap(&self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, x: c_int, y: c_int, flip: LCDBitmapFlip) { *sys::api!(graphics.drawBitmap) } + /// Equivalent to [`sys::ffi::playdate_graphics::tileBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::tileBitmap")] fn tile_bitmap( &self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, @@ -88,6 +110,8 @@ pub trait Api { *sys::api!(graphics.tileBitmap) } + /// Equivalent to [`sys::ffi::playdate_graphics::drawRotatedBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::drawRotatedBitmap")] fn draw_rotated_bitmap( &self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, @@ -101,12 +125,16 @@ pub trait Api { *sys::api!(graphics.drawRotatedBitmap) } + /// Equivalent to [`sys::ffi::playdate_graphics::drawScaledBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::drawScaledBitmap")] fn draw_scaled_bitmap( &self) -> unsafe extern "C" fn(bitmap: *mut LCDBitmap, x: c_int, y: c_int, x_scale: c_float, y_scale: c_float) { *sys::api!(graphics.drawScaledBitmap) } + /// Equivalent to [`sys::ffi::playdate_graphics::checkMaskCollision`] + #[doc(alias = "sys::ffi::playdate_graphics::checkMaskCollision")] fn check_mask_collision( &self) -> unsafe extern "C" fn(bitmap1: *mut LCDBitmap, @@ -121,6 +149,8 @@ pub trait Api { *sys::api!(graphics.checkMaskCollision) } + /// Equivalent to [`sys::ffi::playdate_graphics::setColorToPattern`] + #[doc(alias = "sys::ffi::playdate_graphics::setColorToPattern")] fn set_color_to_pattern( &self) -> unsafe extern "C" fn(color: *mut LCDColor, diff --git a/api/gfx/src/bitmap/bitmap.rs b/api/gfx/src/bitmap/bitmap.rs index 8d18de3d..34ff148b 100644 --- a/api/gfx/src/bitmap/bitmap.rs +++ b/api/gfx/src/bitmap/bitmap.rs @@ -6,12 +6,15 @@ use core::ffi::c_int; use core::marker::PhantomData; use alloc::boxed::Box; +use sys::error::OkOrNullFnErr; use sys::traits::AsRaw; use sys::ffi::CString; use sys::ffi::LCDColor; use sys::ffi::LCDRect; use sys::ffi::LCDBitmap; use fs::Path; + +use crate::Graphics; use crate::error::ApiError; use crate::error::Error; use super::api; @@ -83,7 +86,7 @@ impl From<*mut LCDBitmap> for Bitmap Bitmap { /// Convert this bitmap into the same bitmap that will not be freed on drop. - /// That means that only C-part of the bitmap will be freed. + /// That means that only C-part of the bitmap will __not__ be freed. /// /// __Safety is guaranteed by the caller.__ pub fn into_shared(mut self) -> Bitmap { @@ -530,48 +533,40 @@ impl<'bitmap> BitmapData<'bitmap> { /// /// Returns error on device. /// +/// This function is shorthand for [`Graphics::debug_bitmap`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::getDebugBitmap`]. #[doc(alias = "sys::ffi::playdate_graphics::getDebugBitmap")] -pub fn debug_bitmap() -> Result, ApiError> { - let f = sys::api_ok!(graphics.getDebugBitmap)?; - let ptr = unsafe { f() }; - if ptr.is_null() { - Err(Error::Alloc.into()) - } else { - Ok(Bitmap(ptr, Default::default())) - } -} +#[inline(always)] +pub fn debug_bitmap() -> Result, ApiError> { Graphics::Default().debug_bitmap() } /// Returns a bitmap containing the contents of the display buffer. /// /// __The system owns this bitmap—​do not free it.__ /// +/// This function is shorthand for [`Graphics::display_buffer_bitmap`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::getDisplayBufferBitmap`]. #[doc(alias = "sys::ffi::playdate_graphics::getDisplayBufferBitmap")] +#[inline(always)] pub fn display_buffer_bitmap() -> Result, Error> { - let f = *sys::api!(graphics.getDisplayBufferBitmap); - let ptr = unsafe { f() }; - if ptr.is_null() { - Err(Error::Alloc) - } else { - Ok(Bitmap(ptr, Default::default())) - } + Graphics::Default().display_buffer_bitmap() } /// Returns a copy the contents of the working frame buffer as a bitmap. /// /// The caller is responsible for freeing the returned bitmap, it will automatically on drop. /// +/// This function is shorthand for [`Graphics::frame_buffer_bitmap`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::copyFrameBufferBitmap`]. #[doc(alias = "sys::ffi::playdate_graphics::copyFrameBufferBitmap")] +#[inline(always)] pub fn copy_frame_buffer_bitmap() -> Result, Error> { - let f = *sys::api!(graphics.copyFrameBufferBitmap); - let ptr = unsafe { f() }; - if ptr.is_null() { - Err(Error::Alloc) - } else { - Ok(Bitmap(ptr, Default::default())) - } + Graphics::Default().frame_buffer_bitmap() } @@ -581,11 +576,14 @@ pub fn copy_frame_buffer_bitmap() -> Result, Error> { /// /// Tiled stencils must have width equal to a multiple of 32 pixels. /// +/// This function is shorthand for [`Graphics::set_stencil_tiled`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::setStencilImage`]. #[doc(alias = "sys::ffi::playdate_graphics::setStencilImage")] +#[inline(always)] pub fn set_stencil_tiled(image: &impl AnyBitmap, tile: bool) { - let f = *sys::api!(graphics.setStencilImage); - unsafe { f(image.as_raw(), tile as _) }; + Graphics::Default().set_stencil_tiled(image, tile) } /// Sets the stencil used for drawing. @@ -593,23 +591,25 @@ pub fn set_stencil_tiled(image: &impl AnyBitmap, tile: bool) { /// /// NOTE: Officially deprecated in favor of [`set_stencil_tiled`], which adds a `tile` flag /// +/// This function is shorthand for [`Graphics::set_stencil`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::setStencil`]. #[doc(alias = "sys::ffi::playdate_graphics::setStencil")] -pub fn set_stencil(image: &impl AnyBitmap) { - let f = *sys::api!(graphics.setStencil); - unsafe { f(image.as_raw()) }; -} +#[inline(always)] +pub fn set_stencil(image: &impl AnyBitmap) { Graphics::Default().set_stencil(image) } /// Sets the mode used for drawing bitmaps. /// /// Note that text drawing uses bitmaps, so this affects how fonts are displayed as well. /// +/// This function is shorthand for [`Graphics::set_draw_mode`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::setDrawMode`]. #[doc(alias = "sys::ffi::playdate_graphics::setDrawMode")] -pub fn set_draw_mode(mode: BitmapDrawMode) { - let f = *sys::api!(graphics.setDrawMode); - unsafe { f(mode) }; -} +#[inline(always)] +pub fn set_draw_mode(mode: BitmapDrawMode) { Graphics::Default().set_draw_mode(mode) } /// Push a new drawing context for drawing into the given bitmap. /// @@ -618,30 +618,157 @@ pub fn set_draw_mode(mode: BitmapDrawMode) { /// /// To clear entire context use [`clear_context`]. /// +/// This function is shorthand for [`Graphics::push_context`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::pushContext`]. #[doc(alias = "sys::ffi::playdate_graphics::pushContext")] -pub fn push_context(target: &impl AnyBitmap) { - let f = *sys::api!(graphics.pushContext); - unsafe { f(target.as_raw()) }; -} +#[inline(always)] +pub fn push_context(target: &impl AnyBitmap) { Graphics::Default().push_context(target) } /// Resets drawing context for drawing into the system display framebuffer. /// /// So drawing functions will use the display framebuffer. /// +/// This function is shorthand for [`Graphics::clear_context`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::pushContext`]. #[doc(alias = "sys::ffi::playdate_graphics::pushContext")] -pub fn clear_context() { - let f = *sys::api!(graphics.pushContext); - unsafe { f(core::ptr::null_mut()) }; -} +#[inline(always)] +pub fn clear_context() { Graphics::Default().clear_context() } /// Pops a context off the stack (if any are left), /// restoring the drawing settings from before the context was pushed. /// +/// This function is shorthand for [`Graphics::pop_context`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::popContext`]. #[doc(alias = "sys::ffi::playdate_graphics::popContext")] -pub fn pop_context() { - let f = *sys::api!(graphics.popContext); - unsafe { f() }; +#[inline(always)] +pub fn pop_context() { Graphics::Default().pop_context() } + + +impl Graphics { + /// Only valid in the Simulator, + /// returns the debug framebuffer as a bitmap. + /// + /// Returns error on device. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::getDebugBitmap`]. + #[doc(alias = "sys::ffi::playdate_graphics::getDebugBitmap")] + pub fn debug_bitmap(&self) -> Result, ApiError> { + let f = self.0.get_debug_bitmap().ok_or_null()?; + let ptr = unsafe { f() }; + if ptr.is_null() { + Err(Error::Alloc.into()) + } else { + Ok(Bitmap(ptr, Default::default())) + } + } + + /// Returns a bitmap containing the contents of the display buffer. + /// + /// __The system owns this bitmap—​do not free it.__ + /// + /// Equivalent to [`sys::ffi::playdate_graphics::getDisplayBufferBitmap`]. + #[doc(alias = "sys::ffi::playdate_graphics::getDisplayBufferBitmap")] + pub fn display_buffer_bitmap(&self) -> Result, Error> { + let f = self.0.get_display_buffer_bitmap(); + let ptr = unsafe { f() }; + if ptr.is_null() { + Err(Error::Alloc) + } else { + Ok(Bitmap(ptr, Default::default())) + } + } + + /// Returns a __copy__ the contents of the working frame buffer as a bitmap. + /// + /// The caller is responsible for freeing the returned bitmap, it will automatically on drop. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::copyFrameBufferBitmap`]. + #[doc(alias = "sys::ffi::playdate_graphics::copyFrameBufferBitmap")] + pub fn frame_buffer_bitmap(&self) -> Result, Error> { + let f = self.0.copy_frame_buffer_bitmap(); + let ptr = unsafe { f() }; + if ptr.is_null() { + Err(Error::Alloc) + } else { + Ok(Bitmap(ptr, Default::default())) + } + } + + + /// Sets the stencil used for drawing. + /// + /// If the `tile` is `true` the stencil image will be tiled. + /// + /// Tiled stencils must have width equal to a multiple of 32 pixels. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::setStencilImage`]. + #[doc(alias = "sys::ffi::playdate_graphics::setStencilImage")] + pub fn set_stencil_tiled(&self, image: &impl AnyBitmap, tile: bool) { + let f = self.0.set_stencil_image(); + unsafe { f(image.as_raw(), tile as _) }; + } + + /// Sets the stencil used for drawing. + /// For a tiled stencil, use [`set_stencil_tiled`] instead. + /// + /// NOTE: Officially deprecated in favor of [`set_stencil_tiled`], which adds a `tile` flag + /// + /// Equivalent to [`sys::ffi::playdate_graphics::setStencil`]. + #[doc(alias = "sys::ffi::playdate_graphics::setStencil")] + pub fn set_stencil(&self, image: &impl AnyBitmap) { + let f = self.0.set_stencil(); + unsafe { f(image.as_raw()) }; + } + + /// Sets the mode used for drawing bitmaps. + /// + /// Note that text drawing uses bitmaps, so this affects how fonts are displayed as well. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::setDrawMode`]. + #[doc(alias = "sys::ffi::playdate_graphics::setDrawMode")] + pub fn set_draw_mode(&self, mode: BitmapDrawMode) { + let f = self.0.set_draw_mode(); + unsafe { f(mode) }; + } + + /// Push a new drawing context for drawing into the given bitmap. + /// + /// If underlying ptr in the `target` is `null`, the drawing functions will use the display framebuffer. + /// This mostly should not happen, just for note. + /// + /// To clear entire context use [`clear_context`]. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::pushContext`]. + #[doc(alias = "sys::ffi::playdate_graphics::pushContext")] + pub fn push_context(&self, target: &impl AnyBitmap) { + let f = self.0.push_context(); + unsafe { f(target.as_raw()) }; + } + + /// Resets drawing context for drawing into the system display framebuffer. + /// + /// So drawing functions will use the display framebuffer. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::pushContext`]. + #[doc(alias = "sys::ffi::playdate_graphics::pushContext")] + pub fn clear_context(&self) { + let f = self.0.push_context(); + unsafe { f(core::ptr::null_mut()) }; + } + + /// Pops a context off the stack (if any are left), + /// restoring the drawing settings from before the context was pushed. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::popContext`]. + #[doc(alias = "sys::ffi::playdate_graphics::popContext")] + pub fn pop_context(&self) { + let f = self.0.pop_context(); + unsafe { f() }; + } } diff --git a/api/gfx/src/bitmap/table.rs b/api/gfx/src/bitmap/table.rs index 25411e37..85985ca2 100644 --- a/api/gfx/src/bitmap/table.rs +++ b/api/gfx/src/bitmap/table.rs @@ -55,7 +55,7 @@ impl BitmapTable { } - /// Allocates and returns a new [`Bitmap`] from the file at `path`. + /// Allocates and returns a new [`BitmapTable`] from the file at `path`. /// /// If there is no file at `path`, the function returns error. /// @@ -67,7 +67,7 @@ impl BitmapTable { Self::load_with(api, path) } - /// Allocates and returns a new [`Bitmap`] from the file at `path`. + /// Allocates and returns a new [`BitmapTable`] from the file at `path`. /// /// If there is no file at `path`, the function returns error. /// @@ -165,27 +165,42 @@ pub mod api { impl Api for Default {} + /// End-point with methods about ops over bitmap-table. pub trait Api { + /// Equivalent to [`sys::ffi::playdate_graphics::newBitmapTable`] + #[doc(alias = "sys::ffi::playdate_graphics::newBitmapTable")] fn new_bitmap_table( &self) -> unsafe extern "C" fn(count: c_int, width: c_int, height: c_int) -> *mut LCDBitmapTable { *sys::api!(graphics.newBitmapTable) } + + /// Equivalent to [`sys::ffi::playdate_graphics::freeBitmapTable`] + #[doc(alias = "sys::ffi::playdate_graphics::freeBitmapTable")] fn free_bitmap_table(&self) -> unsafe extern "C" fn(table: *mut LCDBitmapTable) { *sys::api!(graphics.freeBitmapTable) } + + /// Equivalent to [`sys::ffi::playdate_graphics::loadBitmapTable`] + #[doc(alias = "sys::ffi::playdate_graphics::loadBitmapTable")] fn load_bitmap_table( &self) -> unsafe extern "C" fn(path: *const c_char, out_err: *mut *const c_char) -> *mut LCDBitmapTable { *sys::api!(graphics.loadBitmapTable) } + + /// Equivalent to [`sys::ffi::playdate_graphics::loadIntoBitmapTable`] + #[doc(alias = "sys::ffi::playdate_graphics::loadIntoBitmapTable")] fn load_into_bitmap_table( &self) -> unsafe extern "C" fn(path: *const c_char, table: *mut LCDBitmapTable, out_err: *mut *const c_char) { *sys::api!(graphics.loadIntoBitmapTable) } + + /// Equivalent to [`sys::ffi::playdate_graphics::getTableBitmap`] + #[doc(alias = "sys::ffi::playdate_graphics::getTableBitmap")] fn get_table_bitmap(&self) -> unsafe extern "C" fn(table: *mut LCDBitmapTable, idx: c_int) -> *mut LCDBitmap { *sys::api!(graphics.getTableBitmap) diff --git a/api/gfx/src/error.rs b/api/gfx/src/error.rs index 465e1530..4d3154a5 100644 --- a/api/gfx/src/error.rs +++ b/api/gfx/src/error.rs @@ -1,4 +1,6 @@ use core::fmt; +use sys::ffi::{CString, CStr}; +use crate::alloc::borrow::ToOwned; pub type ApiError = sys::error::Error; @@ -9,6 +11,7 @@ pub enum Error { /// Causes when loading graphics from path fails. /// This occurs when file does not exist or invalid format. Fs(fs::error::Error), + /// Causes when allocation failed and/or null-ptr returned. Alloc, @@ -18,6 +21,12 @@ pub enum Error { /// Font error. /// This occurs when char or page not found. Font, + + /// Video error. + Video(CString), + + /// Unknown error. + Unknown, } impl fmt::Display for Error { @@ -27,6 +36,13 @@ impl fmt::Display for Error { Error::Alloc => write!(f, "Allocation failed"), Error::Font => write!(f, "Font error"), Error::InvalidMask => write!(f, "Mask must be the same size as the target bitmap"), + Error::Video(cs) => { + match cs.to_str() { + Ok(err) => err.fmt(f), + Err(_) => f.write_fmt(format_args!("Video error: {cs:?}")), + } + }, + Error::Unknown => write!(f, "Unknown error"), } } } @@ -42,3 +58,8 @@ impl Into for Error { impl core::error::Error for Error {} + + +impl Error { + pub(crate) fn video_from(c: &CStr) -> Self { Self::Video(c.to_owned()) } +} diff --git a/api/gfx/src/lib.rs b/api/gfx/src/lib.rs index 6f5535dc..9f787333 100644 --- a/api/gfx/src/lib.rs +++ b/api/gfx/src/lib.rs @@ -14,29 +14,31 @@ pub mod bitmap { pub mod table; pub use bitmap::*; } +pub mod video; +pub mod api; + +use core::ffi::c_float; +use core::ffi::c_int; +use error::ApiError; pub use sys::ffi::LCDBitmapFlip as BitmapFlip; pub use sys::ffi::LCDBitmapDrawMode as BitmapDrawMode; +pub use sys::ffi::LCDLineCapStyle as LineCapStyle; + +use sys::ffi::LCDColor; +use sys::ffi::LCD_ROWS; +use sys::ffi::LCD_ROWSIZE; +use sys::ffi::LCDPolygonFillRule; +use sys::ffi::LCDSolidColor; pub use bitmap::debug_bitmap; pub use bitmap::display_buffer_bitmap; pub use bitmap::copy_frame_buffer_bitmap; - pub use bitmap::set_stencil; pub use bitmap::set_stencil_tiled; pub use bitmap::set_draw_mode; pub use bitmap::push_context; pub use bitmap::pop_context; -use sys::ffi::LCDPolygonFillRule; -use sys::ffi::LCDSolidColor; - - -use core::ffi::c_float; -use core::ffi::c_int; -use error::ApiError; -use sys::ffi::LCDColor; -use sys::ffi::LCD_ROWS; -use sys::ffi::LCD_ROWSIZE; unsafe fn as_slice_mut(buf: *mut u8) -> Result<&'static mut [u8], ApiError> { @@ -55,23 +57,25 @@ unsafe fn as_slice_mut(buf: *mut u8) -> Result<&'static mut [u8], ApiError> { /// 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. /// +/// This function is shorthand for [`Graphics::get_frame`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::getFrame`]. #[doc(alias = "sys::ffi::playdate_graphics::getFrame")] -pub fn get_frame() -> Result<&'static mut [u8], ApiError> { - let f = *sys::api!(graphics.getFrame); - unsafe { as_slice_mut(f()) } -} +#[inline(always)] +pub fn get_frame() -> Result<&'static mut [u8], ApiError> { Graphics::Default().get_frame() } /// Returns the raw bits in the display buffer, /// __the last completed frame__. /// +/// This function is shorthand for [`Graphics::get_display_frame`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::getDisplayFrame`]. #[doc(alias = "sys::ffi::playdate_graphics::getDisplayFrame")] -pub fn get_display_frame() -> Result<&'static mut [u8], ApiError> { - let f = *sys::api!(graphics.getDisplayFrame); - unsafe { as_slice_mut(f()) } -} +#[inline(always)] +pub fn get_display_frame() -> Result<&'static mut [u8], ApiError> { Graphics::Default().get_display_frame() } /// After updating pixels in the buffer returned by [`get_frame`], /// you must tell the graphics system which rows were updated. @@ -81,26 +85,31 @@ pub fn get_display_frame() -> Result<&'static mut [u8], ApiError> { /// /// Both `start` and `end` are __included__ in the range. /// +/// This function is shorthand for [`Graphics::mark_updated_rows`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::markUpdatedRows`]. #[doc(alias = "sys::ffi::playdate_graphics::markUpdatedRows")] -pub fn mark_updated_rows(start: c_int, end: c_int) { - let f = *sys::api!(graphics.markUpdatedRows); - unsafe { f(start, end) } -} +#[inline(always)] +pub fn mark_updated_rows(start: c_int, end: c_int) { Graphics::Default().mark_updated_rows(start, end) } /// 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. /// +/// This function is shorthand for [`Graphics::display`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::display`]. #[doc(alias = "sys::ffi::playdate_graphics::display")] -pub fn display() { - let f = *sys::api!(graphics.display); - unsafe { f() } -} +#[inline(always)] +pub fn display() { Graphics::Default().display() } /// Clears the entire display, filling it with `color`. /// +/// This function is shorthand for [`Graphics::always`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::clear`]. #[doc(alias = "sys::ffi::playdate_graphics::clear")] #[inline(always)] @@ -113,20 +122,24 @@ pub fn clear(color: color::Color) { clear_raw(color.into()) } /// That conversion is really cheap, /// so this function is useful if you're working with `LCDColor` directly. /// +/// This function is shorthand for [`Graphics::clear_raw`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::clear`]. #[doc(alias = "sys::ffi::playdate_graphics::clear")] -pub fn clear_raw(color: LCDColor) { - let f = *sys::api!(graphics.clear); - unsafe { f(color) } -} +#[inline(always)] +pub fn clear_raw(color: LCDColor) { Graphics::Default().clear_raw(color) } /// Sets the current clip rect in __screen__ coordinates. /// +/// This function is shorthand for [`Graphics::set_screen_clip_rect`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::setScreenClipRect`]. #[doc(alias = "sys::ffi::playdate_graphics::setScreenClipRect")] +#[inline(always)] pub fn set_screen_clip_rect(x: c_int, y: c_int, width: c_int, height: c_int) { - let f = *sys::api!(graphics.setScreenClipRect); - unsafe { f(x, y, width, height) } + Graphics::Default().set_screen_clip_rect(x, y, width, height) } /// Offsets the origin point for all drawing calls to `x, y` (can be negative). @@ -134,43 +147,49 @@ pub fn set_screen_clip_rect(x: c_int, y: c_int, width: c_int, height: c_int) { /// This is useful, for example, for centering a "camera" /// on a sprite that is moving around a world larger than the screen. /// +/// This function is shorthand for [`Graphics::set_draw_offset`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::setDrawOffset`]. #[doc(alias = "sys::ffi::playdate_graphics::setDrawOffset")] -pub fn set_draw_offset(dx: c_int, dy: c_int) { - let f = *sys::api!(graphics.setDrawOffset); - unsafe { f(dx, dy) } -} +#[inline(always)] +pub fn set_draw_offset(dx: c_int, dy: c_int) { Graphics::Default().set_draw_offset(dx, dy) } /// 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. /// +/// This function is shorthand for [`Graphics::set_clip_rect`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::setClipRect`]. #[doc(alias = "sys::ffi::playdate_graphics::setClipRect")] +#[inline(always)] pub fn set_clip_rect(x: c_int, y: c_int, width: c_int, height: c_int) { - let f = *sys::api!(graphics.setClipRect); - unsafe { f(x, y, width, height) } + Graphics::Default().set_clip_rect(x, y, width, height) } /// Clears the current clip rect. /// +/// This function is shorthand for [`Graphics::clear_clip_rect`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::clearClipRect`]. #[doc(alias = "sys::ffi::playdate_graphics::clearClipRect")] -pub fn clear_clip_rect() { - let f = *sys::api!(graphics.clearClipRect); - unsafe { f() } -} +#[inline(always)] +pub fn clear_clip_rect() { Graphics::Default().clear_clip_rect() } /// Sets the background color shown when the display is offset /// or for clearing dirty areas in the sprite system. /// +/// This function is shorthand for [`Graphics::set_background_color`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::setBackgroundColor`]. #[doc(alias = "sys::ffi::playdate_graphics::setBackgroundColor")] -pub fn set_background_color(color: LCDSolidColor) { - let f = *sys::api!(graphics.setBackgroundColor); - unsafe { f(color) } -} +#[inline(always)] +pub fn set_background_color(color: LCDSolidColor) { Graphics::Default().set_background_color(color) } // @@ -181,49 +200,64 @@ pub fn set_background_color(color: LCDSolidColor) { /// (an array of `2 * num_points` ints containing alternating x and y values) /// using the given `color` and fill, or winding, `rule`. /// -/// See [https://en.wikipedia.org/wiki/Nonzero-rule](https://en.wikipedia.org/wiki/Nonzero-rule) for an explanation of the winding rule. +/// See [wikipedia](https://en.wikipedia.org/wiki/Nonzero-rule) for an explanation of the winding rule. +/// +/// This function is shorthand for [`Graphics::fill_polygon`], +/// using default ZST end-point. /// /// Equivalent to [`sys::ffi::playdate_graphics::fillPolygon`]. #[doc(alias = "sys::ffi::playdate_graphics::fillPolygon")] +#[inline(always)] pub fn fill_polygon(num_points: c_int, coords: &mut [c_int], color: LCDColor, rule: LCDPolygonFillRule) { - let f = *sys::api!(graphics.fillPolygon); - unsafe { f(num_points, coords.as_mut_ptr(), color, rule) } + Graphics::Default().fill_polygon(num_points, coords, color, rule) } /// Draws a line from `x1, y1` to `x2, y2` with a stroke width of `width`. /// +/// This function is shorthand for [`Graphics::draw_line`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::drawLine`]. #[doc(alias = "sys::ffi::playdate_graphics::drawLine")] +#[inline(always)] pub fn draw_line(x1: c_int, y1: c_int, x2: c_int, y2: c_int, width: c_int, color: LCDColor) { - let f = *sys::api!(graphics.drawLine); - unsafe { f(x1, y1, x2, y2, width, color) } + Graphics::Default().draw_line(x1, y1, x2, y2, width, color) } /// Draws a filled triangle with points at `x1, y1`, `x2, y2`, and `x3, y3`. /// +/// This function is shorthand for [`Graphics::fill_triangle`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::fillTriangle`]. #[doc(alias = "sys::ffi::playdate_graphics::fillTriangle")] +#[inline(always)] pub fn fill_triangle(x1: c_int, y1: c_int, x2: c_int, y2: c_int, x3: c_int, y3: c_int, color: LCDColor) { - let f = *sys::api!(graphics.fillTriangle); - unsafe { f(x1, y1, x2, y2, x3, y3, color) } + Graphics::Default().fill_triangle(x1, y1, x2, y2, x3, y3, color); } /// Draws a `width` by `height` rect at `x, y`. /// +/// This function is shorthand for [`Graphics::draw_rect`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::drawRect`]. #[doc(alias = "sys::ffi::playdate_graphics::drawRect")] +#[inline(always)] pub fn draw_rect(x: c_int, y: c_int, width: c_int, height: c_int, color: LCDColor) { - let f = *sys::api!(graphics.drawRect); - unsafe { f(x, y, width, height, color) } + Graphics::Default().draw_rect(x, y, width, height, color) } /// Draws a filled `width` by `height` rect at `x, y`. /// +/// This function is shorthand for [`Graphics::fill_rect`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::fillRect`]. #[doc(alias = "sys::ffi::playdate_graphics::fillRect")] +#[inline(always)] pub fn fill_rect(x: c_int, y: c_int, width: c_int, height: c_int, color: LCDColor) { - let f = *sys::api!(graphics.fillRect); - unsafe { f(x, y, width, height, color) } + Graphics::Default().fill_rect(x, y, width, height, color) } /// Draw an ellipse stroked inside the rect. @@ -235,8 +269,12 @@ pub fn fill_rect(x: c_int, y: c_int, width: c_int, height: c_int, color: LCDColo /// /// Angles are given in degrees, clockwise from due north. /// +/// This function is shorthand for [`Graphics::draw_ellipse`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::drawEllipse`]. #[doc(alias = "sys::ffi::playdate_graphics::drawEllipse")] +#[inline(always)] pub fn draw_ellipse(x: c_int, y: c_int, width: c_int, @@ -245,8 +283,7 @@ pub fn draw_ellipse(x: c_int, start_angle: c_float, end_angle: c_float, color: LCDColor) { - let f = *sys::api!(graphics.drawEllipse); - unsafe { f(x, y, width, height, line_width, start_angle, end_angle, color) } + Graphics::Default().draw_ellipse(x, y, width, height, line_width, start_angle, end_angle, color) } /// Fills an ellipse inside the rectangle `x, y, width, height`. @@ -255,8 +292,12 @@ pub fn draw_ellipse(x: c_int, /// /// Angles are given in degrees, clockwise from due north. /// +/// This function is shorthand for [`Graphics::fill_ellipse`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::fillEllipse`]. #[doc(alias = "sys::ffi::playdate_graphics::fillEllipse")] +#[inline(always)] pub fn fill_ellipse(x: c_int, y: c_int, width: c_int, @@ -264,8 +305,292 @@ pub fn fill_ellipse(x: c_int, start_angle: c_float, end_angle: c_float, color: LCDColor) { - let f = *sys::api!(graphics.fillEllipse); - unsafe { f(x, y, width, height, start_angle, end_angle, color) } + Graphics::Default().fill_ellipse(x, y, width, height, start_angle, end_angle, color) +} + + +/// Sets the end cap style used in the line drawing functions. +/// +/// This function is shorthand for [`Graphics::set_line_cap_style`], +/// using default ZST end-point. +/// +/// Equivalent to [`sys::ffi::playdate_graphics::setLineCapStyle`]. +#[doc(alias = "sys::ffi::playdate_graphics::setLineCapStyle")] +#[inline(always)] +pub fn set_line_cap_style(end_cap_style: LineCapStyle) { Graphics::Default().set_line_cap_style(end_cap_style) } + + +#[derive(Debug, Clone, Copy)] +pub struct Graphics(Api); + +impl Graphics { + /// Creates default [`Graphics`] without type parameter requirement. + /// + /// Uses ZST [`api::Default`]. + #[allow(non_snake_case)] + pub fn Default() -> Self { Self(Default::default()) } +} + +impl Graphics { + /// Creates [`Graphics`] without type parameter requirement. + /// + /// Uses [`api::Cache`]. + #[allow(non_snake_case)] + pub fn Cached() -> Self { Self(Default::default()) } +} + +impl Default for Graphics { + fn default() -> Self { Self(Default::default()) } +} + +impl Graphics { + pub fn new() -> Self { Self(Default::default()) } +} + +impl Graphics { + pub fn new_with(api: Api) -> Self { Self(api) } +} + +impl Graphics { + /// 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. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::getFrame`]. + #[doc(alias = "sys::ffi::playdate_graphics::getFrame")] + pub fn get_frame(&self) -> Result<&'static mut [u8], ApiError> { + let f = self.0.get_frame(); + unsafe { as_slice_mut(f()) } + } + + + /// Returns the raw bits in the display buffer, + /// __the last completed frame__. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::getDisplayFrame`]. + #[doc(alias = "sys::ffi::playdate_graphics::getDisplayFrame")] + pub fn get_display_frame(&self) -> Result<&'static mut [u8], ApiError> { + let f = self.0.get_display_frame(); + unsafe { as_slice_mut(f()) } + } + + /// After updating pixels in the buffer returned by [`get_frame`], + /// 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. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::markUpdatedRows`]. + #[doc(alias = "sys::ffi::playdate_graphics::markUpdatedRows")] + pub fn mark_updated_rows(&self, start: c_int, end: c_int) { + let f = self.0.mark_updated_rows(); + unsafe { f(start, end) } + } + + /// 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. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::display`]. + #[doc(alias = "sys::ffi::playdate_graphics::display")] + pub fn display(&self) { + let f = self.0.display(); + unsafe { f() } + } + + /// Clears the entire display, filling it with `color`. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::clear`]. + #[doc(alias = "sys::ffi::playdate_graphics::clear")] + #[inline(always)] + pub fn clear(&self, color: color::Color) { clear_raw(color.into()) } + + + /// Clears the entire display, filling it with `color`. + /// + /// Same as [`clear`], but without conversion `Color` -> `LCDColor`. + /// That conversion is really cheap, + /// so this function is useful if you're working with `LCDColor` directly. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::clear`]. + #[doc(alias = "sys::ffi::playdate_graphics::clear")] + pub fn clear_raw(&self, color: LCDColor) { + let f = self.0.clear(); + unsafe { f(color) } + } + + /// Sets the current clip rect in __screen__ coordinates. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::setScreenClipRect`]. + #[doc(alias = "sys::ffi::playdate_graphics::setScreenClipRect")] + pub fn set_screen_clip_rect(&self, x: c_int, y: c_int, width: c_int, height: c_int) { + let f = self.0.set_screen_clip_rect(); + unsafe { f(x, y, width, height) } + } + + /// Offsets the origin point for all drawing calls to `x, y` (can be negative). + /// + /// This is useful, for example, for centering a "camera" + /// on a sprite that is moving around a world larger than the screen. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::setDrawOffset`]. + #[doc(alias = "sys::ffi::playdate_graphics::setDrawOffset")] + pub fn set_draw_offset(&self, dx: c_int, dy: c_int) { + let f = self.0.set_draw_offset(); + unsafe { f(dx, dy) } + } + + /// 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. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::setClipRect`]. + #[doc(alias = "sys::ffi::playdate_graphics::setClipRect")] + pub fn set_clip_rect(&self, x: c_int, y: c_int, width: c_int, height: c_int) { + let f = self.0.set_clip_rect(); + unsafe { f(x, y, width, height) } + } + + /// Clears the current clip rect. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::clearClipRect`]. + #[doc(alias = "sys::ffi::playdate_graphics::clearClipRect")] + pub fn clear_clip_rect(&self) { + let f = self.0.clear_clip_rect(); + unsafe { f() } + } + + /// Sets the background color shown when the display is offset + /// or for clearing dirty areas in the sprite system. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::setBackgroundColor`]. + #[doc(alias = "sys::ffi::playdate_graphics::setBackgroundColor")] + pub fn set_background_color(&self, color: LCDSolidColor) { + let f = self.0.set_background_color(); + unsafe { f(color) } + } + + + // + // Geometry + // + + /// Fills the polygon with vertices at the given coordinates + /// (an array of `2 * num_points` ints containing alternating x and y values) + /// using the given `color` and fill, or winding, `rule`. + /// + /// See [wikipedia](https://en.wikipedia.org/wiki/Nonzero-rule) for an explanation of the winding rule. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::fillPolygon`]. + #[doc(alias = "sys::ffi::playdate_graphics::fillPolygon")] + pub fn fill_polygon(&self, + num_points: c_int, + coords: &mut [c_int], + color: LCDColor, + rule: LCDPolygonFillRule) { + let f = self.0.fill_polygon(); + unsafe { f(num_points, coords.as_mut_ptr(), color, rule) } + } + + /// Draws a line from `x1, y1` to `x2, y2` with a stroke width of `width`. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::drawLine`]. + #[doc(alias = "sys::ffi::playdate_graphics::drawLine")] + pub fn draw_line(&self, x1: c_int, y1: c_int, x2: c_int, y2: c_int, width: c_int, color: LCDColor) { + let f = self.0.draw_line(); + unsafe { f(x1, y1, x2, y2, width, color) } + } + + /// Draws a filled triangle with points at `x1, y1`, `x2, y2`, and `x3, y3`. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::fillTriangle`]. + #[doc(alias = "sys::ffi::playdate_graphics::fillTriangle")] + pub fn fill_triangle(&self, + x1: c_int, + y1: c_int, + x2: c_int, + y2: c_int, + x3: c_int, + y3: c_int, + color: LCDColor) { + let f = self.0.fill_triangle(); + unsafe { f(x1, y1, x2, y2, x3, y3, color) } + } + + /// Draws a `width` by `height` rect at `x, y`. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::drawRect`]. + #[doc(alias = "sys::ffi::playdate_graphics::drawRect")] + pub fn draw_rect(&self, x: c_int, y: c_int, width: c_int, height: c_int, color: LCDColor) { + let f = self.0.draw_rect(); + unsafe { f(x, y, width, height, color) } + } + + /// Draws a filled `width` by `height` rect at `x, y`. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::fillRect`]. + #[doc(alias = "sys::ffi::playdate_graphics::fillRect")] + pub fn fill_rect(&self, x: c_int, y: c_int, width: c_int, height: c_int, color: LCDColor) { + let f = self.0.fill_rect(); + unsafe { f(x, y, width, height, color) } + } + + /// Draw an ellipse stroked inside the rect. + /// + /// Draws an ellipse inside the rectangle `x, y, width, height` of width `line_width` + /// (inset from the rectangle bounds). + /// + /// If `start_angle != end_angle`, this draws an arc between the given angles. + /// + /// Angles are given in degrees, clockwise from due north. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::drawEllipse`]. + #[doc(alias = "sys::ffi::playdate_graphics::drawEllipse")] + pub fn draw_ellipse(&self, + x: c_int, + y: c_int, + width: c_int, + height: c_int, + line_width: c_int, + start_angle: c_float, + end_angle: c_float, + color: LCDColor) { + let f = self.0.draw_ellipse(); + unsafe { f(x, y, width, height, line_width, start_angle, end_angle, color) } + } + + /// Fills an ellipse inside the rectangle `x, y, width, height`. + /// + /// If `start_angle != end_angle`, this draws a wedge/Pacman between the given angles. + /// + /// Angles are given in degrees, clockwise from due north. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::fillEllipse`]. + #[doc(alias = "sys::ffi::playdate_graphics::fillEllipse")] + pub fn fill_ellipse(&self, + x: c_int, + y: c_int, + width: c_int, + height: c_int, + start_angle: c_float, + end_angle: c_float, + color: LCDColor) { + let f = self.0.fill_ellipse(); + unsafe { f(x, y, width, height, start_angle, end_angle, color) } + } + + + /// Sets the end cap style used in the line drawing functions. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::setLineCapStyle`]. + #[doc(alias = "sys::ffi::playdate_graphics::setLineCapStyle")] + pub fn set_line_cap_style(&self, end_cap_style: LineCapStyle) { + let f = self.0.set_line_cap_style(); + unsafe { f(end_cap_style) } + } } @@ -292,5 +617,13 @@ pub trait BitmapDrawModeExt { const Inverted: BitmapDrawMode = BitmapDrawMode::kDrawModeInverted; } - impl BitmapDrawModeExt for BitmapDrawMode {} + + +pub trait LineCapStyleExt { + #![allow(non_upper_case_globals)] + const Butt: LineCapStyle = LineCapStyle::kLineCapStyleButt; + const Square: LineCapStyle = LineCapStyle::kLineCapStyleSquare; + const Round: LineCapStyle = LineCapStyle::kLineCapStyleRound; +} +impl LineCapStyleExt for LineCapStyle {} diff --git a/api/gfx/src/text.rs b/api/gfx/src/text.rs index 5e8d7bbd..2860930d 100644 --- a/api/gfx/src/text.rs +++ b/api/gfx/src/text.rs @@ -9,8 +9,8 @@ use sys::ffi::{CString, CStr, LCDFont, LCDFontGlyph, LCDFontPage, LCDBitmap}; use sys::traits::AsRaw; pub use sys::ffi::PDStringEncoding as StringEncoding; -pub use sys::ffi::LCDLineCapStyle as LineCapStyle; +use crate::Graphics; use crate::bitmap::BitmapRef; use crate::error::{Error, ApiError}; @@ -23,13 +23,14 @@ use crate::error::{Error, ApiError}; /// If no `font` has been set with [`set_font`], /// the default system font `Asheville Sans 14 Light` is used. /// +/// This function is shorthand for [`Graphics::draw_text`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::drawText`]. #[doc(alias = "sys::ffi::playdate_graphics::drawText")] +#[inline(always)] pub fn draw_text>(text: S, x: c_int, y: c_int) -> Result { - let s = CString::new(text.as_ref())?; - let f = *sys::api!(graphics.drawText); - let res = unsafe { f(s.as_ptr().cast(), text.as_ref().len(), StringEncoding::UTF8, x, y) }; - Ok(res) + Graphics::Default().draw_text(text, x, y) } /// Draws the given `text` using the provided options. @@ -40,34 +41,27 @@ pub fn draw_text>(text: S, x: c_int, y: c_int) -> Result c_int { - let f = *sys::api!(graphics.drawText); - let len = text.to_bytes().len(); - unsafe { f(text.as_ptr().cast(), len, encoding, x, y) } + Graphics::Default().draw_text_cstr(text, encoding, x, y) } /// Returns the width of the given `text` in the given `font`. /// +/// This function is shorthand for [`Graphics::get_text_width`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::getTextWidth`]. #[doc(alias = "sys::ffi::playdate_graphics::getTextWidth")] +#[inline(always)] pub fn get_text_width>(text: S, font: Option<&Font>, tracking: c_int) -> Result { - let s = CString::new(text.as_ref())?; - let f = *sys::api!(graphics.getTextWidth); - let font = font.map(|font| unsafe { font.as_raw() }) - .unwrap_or(core::ptr::null_mut()); - let res = unsafe { - f( - font, - s.as_ptr().cast(), - text.as_ref().len(), - StringEncoding::UTF8, - tracking, - ) - }; - Ok(res) + Graphics::Default().get_text_width(text, font, tracking) } /// Returns the width of the given `text` in the given `font`. @@ -75,42 +69,47 @@ pub fn get_text_width>(text: S, font: Option<&Font>, tracking: c_i /// Same as [`get_text_width`] but takes a [`sys::ffi::CStr`], /// but little bit more efficient. /// +/// This function is shorthand for [`Graphics::get_text_width_cstr`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::getTextWidth`]. #[doc(alias = "sys::ffi::playdate_graphics::getTextWidth")] +#[inline(always)] pub fn get_text_width_cstr(text: &CStr, encoding: StringEncoding, font: Option<&Font>, tracking: c_int) -> c_int { - let f = *sys::api!(graphics.getTextWidth); - let len = text.to_bytes().len(); - let font = font.map(|font| unsafe { font.as_raw() }) - .unwrap_or(core::ptr::null_mut()); - unsafe { f(font, text.as_ptr().cast(), len, encoding, tracking) } + Graphics::Default().get_text_width_cstr(text, encoding, font, tracking) } /// Returns the height of the given `font`. /// +/// This function is shorthand for [`Graphics::get_font_height`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::getFontHeight`]. #[doc(alias = "sys::ffi::playdate_graphics::getFontHeight")] -pub fn get_font_height(font: &Font) -> u8 { - let f = *sys::api!(graphics.getFontHeight); - unsafe { f(font.as_raw()) } -} +#[inline(always)] +pub fn get_font_height(font: &Font) -> u8 { Graphics::Default().get_font_height(font) } /// Sets the `font` to use in subsequent [`draw_text`] calls. /// +/// This function is shorthand for [`Graphics::set_font`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::setFont`]. #[doc(alias = "sys::ffi::playdate_graphics::setFont")] -pub fn set_font(font: &Font) { - let f = *sys::api!(graphics.setFont); - unsafe { f(font.as_raw()) } -} +#[inline(always)] +pub fn set_font(font: &Font) { Graphics::Default().set_font(font) } /// Returns the kerning adjustment between characters `glyph_code` and `next_code` as specified by the font /// +/// This function is shorthand for [`Graphics::get_glyph_kerning`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::getGlyphKerning`]. #[doc(alias = "sys::ffi::playdate_graphics::getGlyphKerning")] +#[inline(always)] pub fn get_glyph_kerning(glyph: &Glyph, glyph_code: u32, next_code: u32) -> c_int { - let f = *sys::api!(graphics.getGlyphKerning); - unsafe { f(glyph.as_raw(), glyph_code, next_code) } + Graphics::Default().get_glyph_kerning(glyph, glyph_code, next_code) } /// Returns an [`Glyph`] object for character `c` in [`FontPage`] page, @@ -118,17 +117,14 @@ pub fn get_glyph_kerning(glyph: &Glyph, glyph_code: u32, next_code: u32) -> c_in /// To also get the glyph’s bitmap and `advance` value /// use [`get_page_glyph_with_bitmap`] instead. /// +/// This function is shorthand for [`Graphics::get_page_glyph`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::getPageGlyph`]. #[doc(alias = "sys::ffi::playdate_graphics::getPageGlyph")] +#[inline(always)] pub fn get_page_glyph(page: &FontPage, c: u32) -> Result { - let f = *sys::api!(graphics.getPageGlyph); - let ptr = unsafe { f(page.as_raw(), c, core::ptr::null_mut(), core::ptr::null_mut()) }; - - if ptr.is_null() { - Err(Error::Font) - } else { - Ok(Glyph(ptr)) - } + Graphics::Default().get_page_glyph(page, c) } /// Returns an [`Glyph`] object for character `c` in [`FontPage`] page, @@ -136,28 +132,17 @@ pub fn get_page_glyph(page: &FontPage, c: u32) -> Result { /// /// If bitmap is not needed, use [`get_page_glyph`] instead. /// +/// This function is shorthand for [`Graphics::get_page_glyph_with_bitmap`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::getPageGlyph`]. #[doc(alias = "sys::ffi::playdate_graphics::getPageGlyph")] +#[inline(always)] pub fn get_page_glyph_with_bitmap<'p>(page: &'p FontPage, c: u32, advance: &mut c_int) -> Result<(Glyph, BitmapRef<'p>), Error> { - let bitmap = Box::new(core::ptr::null_mut() as *mut LCDBitmap); - let out_bitmap = Box::into_raw(bitmap); - - let f = *sys::api!(graphics.getPageGlyph); - let ptr = unsafe { f(page.as_raw(), c, out_bitmap, advance) }; - - if ptr.is_null() { - Err(Error::Font) - } else { - let bitmap = unsafe { Box::from_raw(out_bitmap) }; - if bitmap.is_null() { - Err(Error::Font) - } else { - Ok((Glyph(ptr), BitmapRef::from(*bitmap))) - } - } + Graphics::Default().get_page_glyph_with_bitmap(page, c, advance) } @@ -168,44 +153,26 @@ pub fn get_page_glyph_with_bitmap<'p>(page: &'p FontPage, /// then `c1` and `c2` belong to the same page and the same [`FontPage`] /// can be used to fetch the character data for both instead of searching for the page twice. /// +/// This function is shorthand for [`Graphics::get_font_page`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::getFontPage`]. #[doc(alias = "sys::ffi::playdate_graphics::getFontPage")] +#[inline(always)] pub fn get_font_page(font: &Font, c: u32) -> Result { - let f = *sys::api!(graphics.getFontPage); - let ptr = unsafe { f(font.as_raw(), c) }; - - if ptr.is_null() { - Err(Error::Font) - } else { - Ok(FontPage(ptr)) - } + Graphics::Default().get_font_page(font, c) } /// Returns the [`Font`] object for the font file at `path`. /// +/// This function is shorthand for [`Graphics::load_font`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::loadFont`]. #[doc(alias = "sys::ffi::playdate_graphics::loadFont")] -pub fn load_font>(path: P) -> Result { - let mut err = Box::new(core::ptr::null() as *const c_char); - let out_err = Box::into_raw(err); - - let path = CString::new(path.as_ref())?; - - let f = *sys::api!(graphics.loadFont); - let ptr = unsafe { f(path.as_ptr() as *mut c_char, out_err as _) }; - - if ptr.is_null() { - err = unsafe { Box::from_raw(out_err) }; - if let Some(err) = fs::error::Error::from_ptr(*err).map_err(ApiError::from_err)? { - Err(Error::Fs(err).into()) - } else { - Err(Error::Alloc.into()) - } - } else { - Ok(Font(ptr)) - } -} +#[inline(always)] +pub fn load_font>(path: P) -> Result { Graphics::Default().load_font(path) } /// ⚠️ Caution: This function is not tested. @@ -216,45 +183,283 @@ pub fn load_font>(path: P) -> Result { /// The `wide` corresponds to the flag in the header indicating /// whether the font contains glyphs at codepoints above `U+1FFFF`. /// +/// This function is shorthand for [`Graphics::make_font_from_bytes`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::makeFontFromData`]. #[doc(alias = "sys::ffi::playdate_graphics::makeFontFromData")] +#[inline(always)] pub fn make_font_from_bytes(data: &[u8], wide: c_int) -> Result { - let f = *sys::api!(graphics.makeFontFromData); - let ptr = unsafe { f(data.as_ptr() as _, wide) }; - - if ptr.is_null() { - Err(Error::Alloc) - } else { - Ok(Font(ptr)) - } + Graphics::Default().make_font_from_bytes(data, wide) } /// Sets the leading adjustment (added to the leading specified in the font) to use when drawing text. /// +/// This function is shorthand for [`Graphics::set_text_leading`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::setTextLeading`]. #[doc(alias = "sys::ffi::playdate_graphics::setTextLeading")] +#[inline(always)] pub fn set_text_leading(line_height_adjustment: c_int) { - let f = *sys::api!(graphics.setTextLeading); - unsafe { f(line_height_adjustment) } + Graphics::Default().set_text_leading(line_height_adjustment) } /// Sets the tracking to use when drawing text. /// +/// This function is shorthand for [`Graphics::set_text_tracking`], +/// using default ZST end-point. +/// /// Equivalent to [`sys::ffi::playdate_graphics::setTextTracking`]. #[doc(alias = "sys::ffi::playdate_graphics::setTextTracking")] -pub fn set_text_tracking(tracking: c_int) { - let f = *sys::api!(graphics.setTextTracking); - unsafe { f(tracking) } -} +#[inline(always)] +pub fn set_text_tracking(tracking: c_int) { Graphics::Default().set_text_tracking(tracking) } + + +impl Graphics { + /// Draws the given `text` using the provided coords `x`, `y`. + /// + /// Encoding is always `StringEncoding::UTF8`. + /// If another encoding is desired, use [`draw_text_cstr`] instead. + /// + /// If no `font` has been set with [`set_font`], + /// the default system font `Asheville Sans 14 Light` is used. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::drawText`]. + #[doc(alias = "sys::ffi::playdate_graphics::drawText")] + pub fn draw_text>(&self, text: S, x: c_int, y: c_int) -> Result { + let s = CString::new(text.as_ref())?; + let f = self.0.draw_text(); + let res = unsafe { f(s.as_ptr().cast(), text.as_ref().len(), StringEncoding::UTF8, x, y) }; + Ok(res) + } -/// Sets the end cap style used in the line drawing functions. -/// -/// Equivalent to [`sys::ffi::playdate_graphics::setLineCapStyle`]. -#[doc(alias = "sys::ffi::playdate_graphics::setLineCapStyle")] -pub fn set_line_cap_style(end_cap_style: LineCapStyle) { - let f = *sys::api!(graphics.setLineCapStyle); - unsafe { f(end_cap_style) } + /// Draws the given `text` using the provided options. + /// + /// If no `font` has been set with [`set_font`], + /// the default system font `Asheville Sans 14 Light` is used. + /// + /// Same as [`draw_text`] but takes a [`sys::ffi::CStr`], + /// but little bit more efficient. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::drawText`]. + #[doc(alias = "sys::ffi::playdate_graphics::drawText")] + pub fn draw_text_cstr(&self, text: &CStr, encoding: StringEncoding, x: c_int, y: c_int) -> c_int { + let f = self.0.draw_text(); + let len = text.to_bytes().len(); + unsafe { f(text.as_ptr().cast(), len, encoding, x, y) } + } + + + /// Returns the width of the given `text` in the given `font`. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::getTextWidth`]. + #[doc(alias = "sys::ffi::playdate_graphics::getTextWidth")] + pub fn get_text_width>(&self, + text: S, + font: Option<&Font>, + tracking: c_int) + -> Result { + let s = CString::new(text.as_ref())?; + let f = self.0.get_text_width(); + let font = font.map(|font| unsafe { font.as_raw() }) + .unwrap_or(core::ptr::null_mut()); + let res = unsafe { + f( + font, + s.as_ptr().cast(), + text.as_ref().len(), + StringEncoding::UTF8, + tracking, + ) + }; + Ok(res) + } + + /// Returns the width of the given `text` in the given `font`. + /// + /// Same as [`get_text_width`] but takes a [`sys::ffi::CStr`], + /// but little bit more efficient. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::getTextWidth`]. + #[doc(alias = "sys::ffi::playdate_graphics::getTextWidth")] + pub fn get_text_width_cstr(&self, + text: &CStr, + encoding: StringEncoding, + font: Option<&Font>, + tracking: c_int) + -> c_int { + let f = self.0.get_text_width(); + let len = text.to_bytes().len(); + let font = font.map(|font| unsafe { font.as_raw() }) + .unwrap_or(core::ptr::null_mut()); + unsafe { f(font, text.as_ptr().cast(), len, encoding, tracking) } + } + + + /// Returns the height of the given `font`. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::getFontHeight`]. + #[doc(alias = "sys::ffi::playdate_graphics::getFontHeight")] + pub fn get_font_height(&self, font: &Font) -> u8 { + let f = self.0.get_font_height(); + unsafe { f(font.as_raw()) } + } + + /// Sets the `font` to use in subsequent [`draw_text`] calls. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::setFont`]. + #[doc(alias = "sys::ffi::playdate_graphics::setFont")] + pub fn set_font(&self, font: &Font) { + let f = self.0.set_font(); + unsafe { f(font.as_raw()) } + } + + /// Returns the kerning adjustment between characters `glyph_code` and `next_code` as specified by the font + /// + /// Equivalent to [`sys::ffi::playdate_graphics::getGlyphKerning`]. + #[doc(alias = "sys::ffi::playdate_graphics::getGlyphKerning")] + pub fn get_glyph_kerning(&self, glyph: &Glyph, glyph_code: u32, next_code: u32) -> c_int { + let f = self.0.get_glyph_kerning(); + unsafe { f(glyph.as_raw(), glyph_code, next_code) } + } + + /// Returns an [`Glyph`] object for character `c` in [`FontPage`] page, + /// + /// To also get the glyph’s bitmap and `advance` value + /// use [`get_page_glyph_with_bitmap`] instead. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::getPageGlyph`]. + #[doc(alias = "sys::ffi::playdate_graphics::getPageGlyph")] + pub fn get_page_glyph(&self, page: &FontPage, c: u32) -> Result { + let f = self.0.get_page_glyph(); + let ptr = unsafe { f(page.as_raw(), c, core::ptr::null_mut(), core::ptr::null_mut()) }; + + if ptr.is_null() { + Err(Error::Font) + } else { + Ok(Glyph(ptr)) + } + } + + /// Returns an [`Glyph`] object for character `c` in [`FontPage`] page, + /// and optionally returns the glyph’s bitmap and `advance` value. + /// + /// If bitmap is not needed, use [`get_page_glyph`] instead. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::getPageGlyph`]. + #[doc(alias = "sys::ffi::playdate_graphics::getPageGlyph")] + pub fn get_page_glyph_with_bitmap<'p>(&self, + page: &'p FontPage, + c: u32, + advance: &mut c_int) + -> Result<(Glyph, BitmapRef<'p>), Error> { + let bitmap = Box::new(core::ptr::null_mut() as *mut LCDBitmap); + let out_bitmap = Box::into_raw(bitmap); + + let f = self.0.get_page_glyph(); + let ptr = unsafe { f(page.as_raw(), c, out_bitmap, advance) }; + + if ptr.is_null() { + Err(Error::Font) + } else { + let bitmap = unsafe { Box::from_raw(out_bitmap) }; + if bitmap.is_null() { + Err(Error::Font) + } else { + Ok((Glyph(ptr), BitmapRef::from(*bitmap))) + } + } + } + + + /// Returns an [`FontPage`] object for the given character code `c`. + /// + /// Each [`FontPage`] contains information for 256 characters; + /// specifically, if `(c1 & ~0xff) == (c2 & ~0xff)`, + /// then `c1` and `c2` belong to the same page and the same [`FontPage`] + /// can be used to fetch the character data for both instead of searching for the page twice. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::getFontPage`]. + #[doc(alias = "sys::ffi::playdate_graphics::getFontPage")] + pub fn get_font_page(&self, font: &Font, c: u32) -> Result { + let f = self.0.get_font_page(); + let ptr = unsafe { f(font.as_raw(), c) }; + + if ptr.is_null() { + Err(Error::Font) + } else { + Ok(FontPage(ptr)) + } + } + + + /// Returns the [`Font`] object for the font file at `path`. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::loadFont`]. + #[doc(alias = "sys::ffi::playdate_graphics::loadFont")] + pub fn load_font>(&self, path: P) -> Result { + let mut err = Box::new(core::ptr::null() as *const c_char); + let out_err = Box::into_raw(err); + + let path = CString::new(path.as_ref())?; + + let f = self.0.load_font(); + let ptr = unsafe { f(path.as_ptr() as *mut c_char, out_err as _) }; + + if ptr.is_null() { + err = unsafe { Box::from_raw(out_err) }; + if let Some(err) = fs::error::Error::from_ptr(*err).map_err(ApiError::from_err)? { + Err(Error::Fs(err).into()) + } else { + Err(Error::Alloc.into()) + } + } else { + Ok(Font(ptr)) + } + } + + + /// ⚠️ Caution: This function is not tested. + /// + /// Returns an [`Font`] object wrapping the LCDFontData data + /// comprising the contents (minus 16-byte header) of an uncompressed pft file. + /// + /// The `wide` corresponds to the flag in the header indicating + /// whether the font contains glyphs at codepoints above `U+1FFFF`. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::makeFontFromData`]. + #[doc(alias = "sys::ffi::playdate_graphics::makeFontFromData")] + pub fn make_font_from_bytes(&self, data: &[u8], wide: c_int) -> Result { + let f = self.0.make_font_from_data(); + let ptr = unsafe { f(data.as_ptr() as _, wide) }; + + if ptr.is_null() { + Err(Error::Alloc) + } else { + Ok(Font(ptr)) + } + } + + + /// Sets the leading adjustment (added to the leading specified in the font) to use when drawing text. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::setTextLeading`]. + #[doc(alias = "sys::ffi::playdate_graphics::setTextLeading")] + pub fn set_text_leading(&self, line_height_adjustment: c_int) { + let f = self.0.set_text_leading(); + unsafe { f(line_height_adjustment) } + } + + /// Sets the tracking to use when drawing text. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::setTextTracking`]. + #[doc(alias = "sys::ffi::playdate_graphics::setTextTracking")] + pub fn set_text_tracking(&self, tracking: c_int) { + let f = self.0.set_text_tracking(); + unsafe { f(tracking) } + } } @@ -287,15 +492,6 @@ impl AsRaw for FontPage { } -pub trait LineCapStyleExt { - #![allow(non_upper_case_globals)] - const Butt: LineCapStyle = LineCapStyle::kLineCapStyleButt; - const Square: LineCapStyle = LineCapStyle::kLineCapStyleSquare; - const Round: LineCapStyle = LineCapStyle::kLineCapStyleRound; -} -impl LineCapStyleExt for LineCapStyle {} - - pub trait StringEncodingExt { #![allow(non_upper_case_globals)] const ASCII: StringEncoding = StringEncoding::kASCIIEncoding; @@ -303,3 +499,116 @@ pub trait StringEncodingExt { const LE16Bit: StringEncoding = StringEncoding::k16BitLEEncoding; } impl StringEncodingExt for StringEncoding {} + + +pub mod api { + use core::ffi::c_char; + use core::ffi::c_int; + use core::ffi::c_void; + + use sys::ffi::LCDBitmap; + use sys::ffi::LCDFont; + use sys::ffi::LCDFontData; + use sys::ffi::LCDFontGlyph; + use sys::ffi::LCDFontPage; + use sys::ffi::PDStringEncoding; + + + /// End-point with methods about ops with text. + pub trait Api { + /// Equivalent to [`sys::ffi::playdate_graphics::drawText`] + #[doc(alias = "sys::ffi::playdate_graphics::drawText")] + #[inline(always)] + fn draw_text( + &self) + -> unsafe extern "C" fn(text: *const c_void, + len: usize, + encoding: PDStringEncoding, + x: c_int, + y: c_int) -> c_int { + *sys::api!(graphics.drawText) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getTextWidth`] + #[doc(alias = "sys::ffi::playdate_graphics::getTextWidth")] + #[inline(always)] + fn get_text_width( + &self) + -> unsafe extern "C" fn(font: *mut LCDFont, + text: *const c_void, + len: usize, + encoding: PDStringEncoding, + tracking: c_int) -> c_int { + *sys::api!(graphics.getTextWidth) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getFontHeight`] + #[doc(alias = "sys::ffi::playdate_graphics::getFontHeight")] + #[inline(always)] + fn get_font_height(&self) -> unsafe extern "C" fn(font: *mut LCDFont) -> u8 { + *sys::api!(graphics.getFontHeight) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::setFont`] + #[doc(alias = "sys::ffi::playdate_graphics::setFont")] + #[inline(always)] + fn set_font(&self) -> unsafe extern "C" fn(font: *mut LCDFont) { *sys::api!(graphics.setFont) } + + /// Equivalent to [`sys::ffi::playdate_graphics::setTextTracking`] + #[doc(alias = "sys::ffi::playdate_graphics::setTextTracking")] + #[inline(always)] + fn set_text_tracking(&self) -> unsafe extern "C" fn(tracking: c_int) { + *sys::api!(graphics.setTextTracking) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getGlyphKerning`] + #[doc(alias = "sys::ffi::playdate_graphics::getGlyphKerning")] + #[inline(always)] + fn get_glyph_kerning( + &self) + -> unsafe extern "C" fn(glyph: *mut LCDFontGlyph, glyphcode: u32, nextcode: u32) -> c_int { + *sys::api!(graphics.getGlyphKerning) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::loadFont`] + #[doc(alias = "sys::ffi::playdate_graphics::loadFont")] + #[inline(always)] + fn load_font(&self) + -> unsafe extern "C" fn(path: *const c_char, outErr: *mut *const c_char) -> *mut LCDFont { + *sys::api!(graphics.loadFont) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getFontPage`] + #[doc(alias = "sys::ffi::playdate_graphics::getFontPage")] + #[inline(always)] + fn get_font_page(&self) -> unsafe extern "C" fn(font: *mut LCDFont, c: u32) -> *mut LCDFontPage { + *sys::api!(graphics.getFontPage) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::getPageGlyph`] + #[doc(alias = "sys::ffi::playdate_graphics::getPageGlyph")] + #[inline(always)] + fn get_page_glyph( + &self) + -> unsafe extern "C" fn(page: *mut LCDFontPage, + c: u32, + bitmap: *mut *mut LCDBitmap, + advance: *mut c_int) -> *mut LCDFontGlyph { + *sys::api!(graphics.getPageGlyph) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::makeFontFromData`] + #[doc(alias = "sys::ffi::playdate_graphics::makeFontFromData")] + #[inline(always)] + fn make_font_from_data(&self) -> unsafe extern "C" fn(data: *mut LCDFontData, wide: c_int) -> *mut LCDFont { + *sys::api!(graphics.makeFontFromData) + } + + /// Equivalent to [`sys::ffi::playdate_graphics::setTextLeading`] + #[doc(alias = "sys::ffi::playdate_graphics::setTextLeading")] + #[inline(always)] + fn set_text_leading(&self) -> unsafe extern "C" fn(lineHeightAdustment: c_int) { + *sys::api!(graphics.setTextLeading) + } + } +} diff --git a/api/gfx/src/video.rs b/api/gfx/src/video.rs new file mode 100644 index 00000000..6ac5cd96 --- /dev/null +++ b/api/gfx/src/video.rs @@ -0,0 +1,446 @@ +//! Playdate video API + +use core::ffi::{c_char, c_int, c_float}; + +use sys::ffi::LCDVideoPlayer; +use sys::ffi::{CString, CStr}; +use fs::Path; + +use crate::Graphics; +use crate::bitmap::{AnyBitmap, BitmapRef}; +use crate::error::ApiError; +use crate::error::Error; + + +#[derive(Debug, Clone, Copy)] +pub struct Video(Api); + +impl Video { + /// Creates default [`Video`] without type parameter requirement. + /// + /// Uses ZST [`api::Default`]. + #[allow(non_snake_case)] + pub fn Default() -> Self { Self(Default::default()) } +} + +impl Video { + /// Creates [`Video`] without type parameter requirement. + /// + /// Uses [`api::Cache`]. + #[allow(non_snake_case)] + pub fn Cached() -> Self { Self(Default::default()) } +} + +impl Default for Video { + fn default() -> Self { Self(Default::default()) } +} + +impl Video { + pub fn new() -> Self { Self(Default::default()) } +} + +impl Video { + pub fn new_with(api: Api) -> Self { Self(api) } +} + +impl Video { + /// Opens the `pdv` file at path and returns a new [`VideoPlayer`] for rendering its frames. + /// + /// Calls [`sys::ffi::playdate_video::loadVideo`]. + #[doc(alias = "sys::ffi::playdate_video::loadVideo")] + pub fn load>(&self, path: P) -> Result, ApiError> { + VideoPlayer::load_with(self.0, path) + } +} + + +impl Graphics { + /// Creates a new [`Video`] instance with [cached api](api::Cache). + /// + /// Equivalent to [`sys::ffi::playdate_graphics::video`] + #[doc(alias = "sys::ffi::playdate_graphics::video")] + pub fn video(&self) -> Video { Video::new_with(self.0.video::()) } + + /// Creates a new [`Video`] instance using given `api`. + /// + /// Equivalent to [`sys::ffi::playdate_graphics::video`] + #[doc(alias = "sys::ffi::playdate_graphics::video")] + pub fn video_with(&self, api: VideoApi) -> Video { Video::new_with(api) } +} + + +#[cfg_attr(feature = "bindings-derive-debug", derive(Debug))] +pub struct VideoPlayer(*mut LCDVideoPlayer, Api); + +impl Drop for VideoPlayer { + fn drop(&mut self) { + if FOD && !self.0.is_null() { + let f = self.1.free_player(); + unsafe { f(self.0) }; + self.0 = core::ptr::null_mut(); + } + } +} + + +impl VideoPlayer { + /// Convert this video player into the same but that will not be freed on drop. + /// That means that only C-part of the player will __not__ be freed. + /// + /// __Safety is guaranteed by the caller.__ + pub fn into_shared(mut self) -> VideoPlayer { + let res = VideoPlayer(self.0, self.1); + self.0 = core::ptr::null_mut(); + res + } +} + + +impl VideoPlayer { + /// Opens the `pdv` file at path and returns a new video player object for rendering its frames. + /// + /// Calls [`sys::ffi::playdate_video::loadVideo`]. + #[doc(alias = "sys::ffi::playdate_video::loadVideo")] + pub fn load>(path: P) -> Result + where Api: Default { + let api = Api::default(); + Self::load_with(api, path) + } + + /// Opens the `pdv` file at path and returns a new video player object for rendering its frames. + /// + /// Calls [`sys::ffi::playdate_video::loadVideo`]. + #[doc(alias = "sys::ffi::playdate_video::loadVideo")] + pub fn load_with>(api: Api, path: P) -> Result { + let path = CString::new(path.as_ref())?; + + let f = api.load_video(); + let ptr = unsafe { f(path.as_ptr() as *mut c_char) }; + if ptr.is_null() { + // Maybe we able to `get_error` for null pointer? + Err(Error::Alloc.into()) + } else { + Ok(Self(ptr, api)) + } + } +} + + +impl VideoPlayer { + /// Sets the rendering destination for the video player to the given bitmap. + /// + /// If the function fails, it returns [`Error::Video`] if err-message supplied by C-API, + /// or [`Error::Unknown`] in other cases. + /// + /// Calls [`sys::ffi::playdate_video::setContext`]. + #[doc(alias = "sys::ffi::playdate_video::setContext")] + pub fn set_context<'a, 'b: 'a>(&'a self, bitmap: &'b impl AnyBitmap) -> Result<(), Error> { + let f = self.1.set_context(); + if unsafe { f(self.0, bitmap.as_raw()) } != 0 { + Ok(()) + } else { + Err(self.get_error().unwrap_or(Error::Unknown)) + } + } + + /// Gets the rendering destination for the video player. + /// + /// If no rendering context has been set allocates a context bitmap with the same dimensions as the video will be allocated. + /// + /// Calls [`sys::ffi::playdate_video::getContext`]. + #[doc(alias = "sys::ffi::playdate_video::getContext")] + pub fn get_context(&self) -> Result, Error> { + let f = self.1.get_context(); + let ptr = unsafe { f(self.0) }; + if ptr.is_null() { + Err(Error::Alloc) + } else { + Ok(BitmapRef::from(ptr)) + } + } + + + /// Calls [`sys::ffi::playdate_video::useScreenContext`]. + #[doc(alias = "sys::ffi::playdate_video::useScreenContext")] + pub fn use_screen_context(&self) { + let f = self.1.use_screen_context(); + unsafe { f(self.0) } + } + + /// Renders frame number `n` into the current context. + /// + /// In case of error, it returns [`Error::Video`] if err-message supplied by C-API, + /// or [`Error::Unknown`] in other cases. + /// + /// Calls [`sys::ffi::playdate_video::renderFrame`]. + #[doc(alias = "sys::ffi::playdate_video::renderFrame")] + pub fn render_frame(&self, n: c_int) -> Result<(), Error> { + let f = self.1.render_frame(); + if unsafe { f(self.0, n) } != 0 { + Ok(()) + } else { + Err(self.get_error().unwrap_or(Error::Unknown)) + } + } + + /// Retrieves information about the video. + /// + /// Calls [`sys::ffi::playdate_video::renderFrame`]. + #[doc(alias = "sys::ffi::playdate_video::renderFrame")] + pub fn info(&self) -> VideoPlayerOutInfo { + let mut info = VideoPlayerOutInfo::default(); + self.info_to(&mut info); + info + } + + /// Retrieves information about the video, by passing values into given `info`. + /// + /// Calls [`sys::ffi::playdate_video::renderFrame`]. + #[doc(alias = "sys::ffi::playdate_video::renderFrame")] + pub fn info_to(&self, info: &mut VideoPlayerOutInfo) { + let f = self.1.get_info(); + unsafe { + f( + self.0, + &mut info.width, + &mut info.height, + &mut info.frame_rate, + &mut info.frame_count, + &mut info.current_frame, + ) + }; + } + + /// Retrieves information about the video, by passing optional mutable references. + /// + /// Calls [`sys::ffi::playdate_video::renderFrame`]. + #[doc(alias = "sys::ffi::playdate_video::renderFrame")] + pub fn info_raw(&self, + width: Option<&mut c_int>, + height: Option<&mut c_int>, + frame_rate: Option<&mut c_float>, + frame_count: Option<&mut c_int>, + current_frame: Option<&mut c_int>) { + let f = self.1.get_info(); + unsafe { + use core::ptr::null_mut; + f( + self.0, + width.map_or(null_mut() as _, |v| v as *mut _), + height.map_or(null_mut() as _, |v| v as *mut _), + frame_rate.map_or(null_mut() as _, |v| v as *mut _), + frame_count.map_or(null_mut() as _, |v| v as *mut _), + current_frame.map_or(null_mut() as _, |v| v as *mut _), + ) + }; + } + + + /// Returns [`Error`] with text describing the most recent error. + /// + /// Inner text is borrowed by C, so it should be used immediately or converted to something owned. + /// + /// See also [`VideoPlayer::get_error_cstr`]. + /// + /// Calls [`sys::ffi::playdate_video::getError`]. + #[must_use = "Error message is borrowed from C part, must be used immediately or converted to owned string."] + #[inline(always)] + pub fn get_error(&self) -> Option { self.get_error_cstr().map(Error::video_from) } + + /// Returns [`CStr`] describing the most recent error. + /// + /// String-slice is borrowed by C, so it should be used immediately or converted to something owned. + /// + /// Calls [`sys::ffi::playdate_video::getError`]. + #[doc(alias = "sys::ffi::playdate_video::getError")] + #[must_use = "Error message is borrowed from C part, must be used immediately or converted to owned string."] + pub fn get_error_cstr(&self) -> Option<&CStr> { + let f = self.1.get_error(); + let ptr = unsafe { f(self.0) }; + if ptr.is_null() { + None + } else { + unsafe { CStr::from_ptr(ptr as _) }.into() + } + } +} + + +#[derive(Debug, Clone, Default)] +pub struct VideoPlayerOutInfo { + pub width: c_int, + pub height: c_int, + pub frame_rate: c_float, + pub frame_count: c_int, + pub current_frame: c_int, +} + + +pub mod api { + use core::ffi::c_char; + use core::ffi::c_float; + use core::ffi::c_int; + use core::ptr::NonNull; + + use sys::ffi::LCDBitmap; + use sys::ffi::LCDVideoPlayer; + use sys::ffi::playdate_video; + + + /// Default video api end-point, ZST. + /// + /// All calls approximately costs ~4 derefs. + #[derive(Debug, Clone, Copy, core::default::Default)] + pub struct Default; + impl Api for Default {} + + + /// Cached video api end-point. + /// + /// Stores one reference, so size on stack is eq `usize`. + /// + /// All calls approximately costs ~1 deref. + #[derive(Clone, Copy)] + #[cfg_attr(feature = "bindings-derive-debug", derive(Debug))] + pub struct Cache(&'static playdate_video); + + impl core::default::Default for Cache { + fn default() -> Self { Self(sys::api!(graphics.video)) } + } + + impl From<*const playdate_video> for Cache { + #[inline(always)] + fn from(ptr: *const playdate_video) -> Self { Self(unsafe { ptr.as_ref() }.expect("video")) } + } + + impl From<&'static playdate_video> for Cache { + #[inline(always)] + fn from(r: &'static playdate_video) -> Self { Self(r) } + } + + impl From> for Cache { + #[inline(always)] + fn from(ptr: NonNull) -> Self { Self(unsafe { ptr.as_ref() }) } + } + + impl From<&'_ NonNull> for Cache { + #[inline(always)] + fn from(ptr: &NonNull) -> Self { Self(unsafe { ptr.as_ref() }) } + } + + + impl Api for Cache { + #[inline(always)] + fn load_video(&self) -> unsafe extern "C" fn(path: *const c_char) -> *mut LCDVideoPlayer { + self.0.loadVideo.expect("loadVideo") + } + + #[inline(always)] + fn free_player(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) { + self.0.freePlayer.expect("freePlayer") + } + + #[inline(always)] + fn set_context(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer, context: *mut LCDBitmap) -> c_int { + self.0.setContext.expect("setContext") + } + + #[inline(always)] + fn use_screen_context(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) { + self.0.useScreenContext.expect("useScreenContext") + } + + #[inline(always)] + fn render_frame(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer, n: c_int) -> c_int { + self.0.renderFrame.expect("renderFrame") + } + + #[inline(always)] + fn get_error(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) -> *const c_char { + self.0.getError.expect("getError") + } + + #[inline(always)] + fn get_info( + &self) + -> unsafe extern "C" fn(p: *mut LCDVideoPlayer, + outWidth: *mut c_int, + outHeight: *mut c_int, + outFrameRate: *mut c_float, + outFrameCount: *mut c_int, + outCurrentFrame: *mut c_int) { + *sys::api!(graphics.video.getInfo) + } + + #[inline(always)] + fn get_context(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) -> *mut LCDBitmap { + self.0.getContext.expect("getContext") + } + } + + + pub trait Api { + /// Equivalent to [`sys::ffi::playdate_video::loadVideo`] + #[doc(alias = "sys::ffi::playdate_video::loadVideo")] + #[inline(always)] + fn load_video(&self) -> unsafe extern "C" fn(path: *const c_char) -> *mut LCDVideoPlayer { + *sys::api!(graphics.video.loadVideo) + } + + /// Equivalent to [`sys::ffi::playdate_video::freePlayer`] + #[doc(alias = "sys::ffi::playdate_video::freePlayer")] + #[inline(always)] + fn free_player(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) { + *sys::api!(graphics.video.freePlayer) + } + + /// Equivalent to [`sys::ffi::playdate_video::setContext`] + #[doc(alias = "sys::ffi::playdate_video::setContext")] + #[inline(always)] + fn set_context(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer, context: *mut LCDBitmap) -> c_int { + *sys::api!(graphics.video.setContext) + } + + /// Equivalent to [`sys::ffi::playdate_video::useScreenContext`] + #[doc(alias = "sys::ffi::playdate_video::useScreenContext")] + #[inline(always)] + fn use_screen_context(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) { + *sys::api!(graphics.video.useScreenContext) + } + + /// Equivalent to [`sys::ffi::playdate_video::renderFrame`] + #[doc(alias = "sys::ffi::playdate_video::renderFrame")] + #[inline(always)] + fn render_frame(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer, n: c_int) -> c_int { + *sys::api!(graphics.video.renderFrame) + } + + /// Equivalent to [`sys::ffi::playdate_video::getError`] + #[doc(alias = "sys::ffi::playdate_video::getError")] + #[inline(always)] + fn get_error(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) -> *const c_char { + *sys::api!(graphics.video.getError) + } + + /// Equivalent to [`sys::ffi::playdate_video::getInfo`] + #[doc(alias = "sys::ffi::playdate_video::getInfo")] + #[inline(always)] + fn get_info( + &self) + -> unsafe extern "C" fn(p: *mut LCDVideoPlayer, + outWidth: *mut c_int, + outHeight: *mut c_int, + outFrameRate: *mut c_float, + outFrameCount: *mut c_int, + outCurrentFrame: *mut c_int) { + *sys::api!(graphics.video.getInfo) + } + + /// Equivalent to [`sys::ffi::playdate_video::getContext`] + #[doc(alias = "sys::ffi::playdate_video::getContext")] + #[inline(always)] + fn get_context(&self) -> unsafe extern "C" fn(p: *mut LCDVideoPlayer) -> *mut LCDBitmap { + *sys::api!(graphics.video.getContext) + } + } +} diff --git a/api/playdate/Cargo.toml b/api/playdate/Cargo.toml index 5085859f..b6de4ee2 100644 --- a/api/playdate/Cargo.toml +++ b/api/playdate/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate" -version = "0.1.1" +version = "0.1.2" readme = "README.md" description = "High-level Playdate API" keywords = ["playdate", "sdk", "api", "gamedev"] @@ -195,19 +195,25 @@ crate-type = ["dylib", "staticlib"] path = "examples/hello-world.rs" required-features = ["entry-point"] -# TODO: REMOVE ME! [[example]] name = "minimal" crate-type = ["dylib", "staticlib"] path = "examples/minimal.rs" required-features = ["entry-point"] +[[example]] +name = "video" +crate-type = ["dylib", "staticlib"] +path = "examples/video.rs" +required-features = ["entry-point"] + [package.metadata.playdate] bundle-id = "rs.playdate.core" [package.metadata.playdate.dev-assets] "examples/ferris.png" = true "examples/" = "${PLAYDATE_SDK_PATH}/Examples/Level 1-1/Source/sfx/main_theme.wav" +"examples/video.pdv" = "${PLAYDATE_SDK_PATH}/Disk/System/Setup.pdx/videos/outro.pdv" [package.metadata.docs.rs] diff --git a/api/playdate/examples/video.rs b/api/playdate/examples/video.rs new file mode 100644 index 00000000..a4a2c0fb --- /dev/null +++ b/api/playdate/examples/video.rs @@ -0,0 +1,76 @@ +#![no_std] +extern crate alloc; + +#[macro_use] +extern crate playdate as pd; + +use core::ffi::*; +use core::ptr::NonNull; +use pd::ext::PlaydateAPIExt; +use pd::sys::ffi::PlaydateAPI; +use pd::graphics::video::VideoPlayer; + +use fs::Path; +use pd::graphics::*; +use pd::system::prelude::*; + + +const VIDEO_PATH: &Path = "examples/video.pdv"; + + +/// Game state +struct State { + player: VideoPlayer, + + // Current frame + current: c_int, + // Number of frames + length: c_int, +} + + +/// Entry point +#[no_mangle] +fn event_handler(api: NonNull, event: SystemEvent, _sim_key_code: u32) -> bool { + // Ignore any other events, just for this minimalistic example + if !matches!(event, SystemEvent::Init) { + return true; + } + + // Set FPS + api.display().set_refresh_rate(20.0); + + // Create video player + let player = api.graphics().video().load(VIDEO_PATH).unwrap(); + // Set draw-target to the screen + player.use_screen_context(); + + // Register update handler + api.system().set_update_callback_boxed( + |state| { + // Draw current frame of the player + state.player.render_frame(state.current).unwrap(); + + // Advance to the next frame + state.current += 1; + if state.current >= state.length { + state.current = 0; + } + + // Draw FPS on-top of the player's render + System::Default().draw_fps(0, 0); + + // Continue + true + }, + State { length: player.info().frame_count, + current: 0, + player, }, + ); + + true +} + + +// Needed for debug build, absolutely optional +ll_symbols!(); diff --git a/api/playdate/src/lib.rs b/api/playdate/src/lib.rs index 0c2fbf70..7814fc7f 100644 --- a/api/playdate/src/lib.rs +++ b/api/playdate/src/lib.rs @@ -33,3 +33,59 @@ pub mod fs { pub use fs::*; pub use fs::prelude::*; } + + +pub mod ext { + use core::ptr::NonNull; + + + /// Main Playdate API entry point. + pub trait PlaydateAPIExt { + /// Playdate System API. + fn system(&self) -> system::System; + + // fn file() -> file::File; + + /// Playdate Graphics API. + fn graphics(&self) -> graphics::Graphics; + + // fn sprite() -> sprite::Sprite; + + /// Playdate Display API. + fn display(&self) -> display::Display; + + // fn sound() -> sound::Sound; + // fn lua() -> lua::Lua; + // fn json() -> json::Json; + // fn scoreboards() -> scoreboards::Scoreboards; + } + + + impl PlaydateAPIExt for NonNull { + fn system(&self) -> system::System { + system::System::new_with(system::api::Cache::from(unsafe { self.as_ref() }.system)) + } + + fn graphics(&self) -> graphics::Graphics { + graphics::Graphics::new_with(graphics::api::Cache::from(unsafe { self.as_ref() }.graphics)) + } + + fn display(&self) -> display::Display { + display::Display::new_with(display::api::Cache::from(unsafe { self.as_ref() }.display)) + } + } + + impl PlaydateAPIExt for *const sys::ffi::PlaydateAPI { + fn system(&self) -> system::System { + system::System::new_with(system::api::Cache::from(unsafe { self.as_ref() }.expect("api").system)) + } + + fn graphics(&self) -> graphics::Graphics { + graphics::Graphics::new_with(graphics::api::Cache::from(unsafe { self.as_ref() }.expect("api").graphics)) + } + + fn display(&self) -> display::Display { + display::Display::new_with(display::api::Cache::from(unsafe { self.as_ref() }.expect("api").display)) + } + } +} diff --git a/api/sprite/Cargo.toml b/api/sprite/Cargo.toml index 20b7af34..3a62ed18 100644 --- a/api/sprite/Cargo.toml +++ b/api/sprite/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate-sprite" -version = "0.1.2" +version = "0.1.3" readme = "README.md" description = "High-level sprite API built on-top of Playdate API" keywords = ["playdate", "sdk", "api", "gamedev"] diff --git a/api/sprite/src/lib.rs b/api/sprite/src/lib.rs index bf537fea..946ce4d9 100644 --- a/api/sprite/src/lib.rs +++ b/api/sprite/src/lib.rs @@ -36,7 +36,9 @@ pub use sprite::*; use crate::api::Api; -/// When flag is set to `true`, the given sprite will always redraw. +/// If set to `true`, causes all sprites to draw each frame, whether or not they have been marked dirty. +/// This may speed up the performance of your game if the system’s dirty rect tracking is taking up too much time - +/// for example if there are many sprites moving around on screen at once. /// /// Equivalent to [`sys::ffi::playdate_sprite::setAlwaysRedraw`] #[doc(alias = "sys::ffi::playdate_sprite::setAlwaysRedraw")] diff --git a/api/system/Cargo.toml b/api/system/Cargo.toml index 394dd20c..9c49e507 100644 --- a/api/system/Cargo.toml +++ b/api/system/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "playdate-system" -version = "0.2.0" +version = "0.3.0" readme = "README.md" description = "High-level System API built on-top of Playdate API" keywords = ["playdate", "sdk", "api", "gamedev"] diff --git a/api/system/src/lib.rs b/api/system/src/lib.rs index 17bc9fcb..d24158ba 100644 --- a/api/system/src/lib.rs +++ b/api/system/src/lib.rs @@ -35,6 +35,14 @@ impl System { pub fn Default() -> Self { Self(Default::default()) } } +impl System { + /// Creates [`System`] without type parameter requirement. + /// + /// Uses [`api::Cache`]. + #[allow(non_snake_case)] + pub fn Cached() -> Self { Self(Default::default()) } +} + impl Default for System { fn default() -> Self { Self(Default::default()) } } @@ -199,18 +207,167 @@ pub mod api { use core::ffi::c_int; use core::ffi::c_uint; use core::ffi::c_void; + use core::ptr::NonNull; use sys::ffi::PDCallbackFunction; use sys::ffi::PDDateTime; use sys::ffi::PDLanguage; + use sys::ffi::playdate_sys; + /// Default system api end-point, ZST. + /// + /// All calls approximately costs ~3 derefs. #[derive(Debug, Clone, Copy, core::default::Default)] pub struct Default; - impl Api for Default {} + /// Cached system api end-point. + /// + /// Stores one reference, so size on stack is eq `usize`. + /// + /// All calls approximately costs ~1 deref. + #[derive(Clone, Copy)] + #[cfg_attr(feature = "bindings-derive-debug", derive(Debug))] + pub struct Cache(&'static playdate_sys); + + impl core::default::Default for Cache { + fn default() -> Self { Self(sys::api!(system)) } + } + + impl From<*const playdate_sys> for Cache { + #[inline(always)] + fn from(ptr: *const playdate_sys) -> Self { Self(unsafe { ptr.as_ref() }.expect("system")) } + } + + impl From<&'static playdate_sys> for Cache { + #[inline(always)] + fn from(r: &'static playdate_sys) -> Self { Self(r) } + } + + impl From> for Cache { + #[inline(always)] + fn from(ptr: NonNull) -> Self { Self(unsafe { ptr.as_ref() }) } + } + + impl From<&'_ NonNull> for Cache { + #[inline(always)] + fn from(ptr: &NonNull) -> Self { Self(unsafe { ptr.as_ref() }) } + } + + + impl Api for Cache { + /// Equivalent to [`sys::ffi::playdate_sys::getLanguage`] + #[doc(alias = "sys::ffi::playdate_sys::getLanguage")] + #[inline(always)] + fn get_language(&self) -> unsafe extern "C" fn() -> PDLanguage { self.0.getLanguage.expect("getLanguage") } + + /// Equivalent to [`sys::ffi::playdate_sys::getCurrentTimeMilliseconds`] + #[doc(alias = "sys::ffi::playdate_sys::getCurrentTimeMilliseconds")] + #[inline(always)] + fn get_current_time_milliseconds(&self) -> unsafe extern "C" fn() -> c_uint { + self.0 + .getCurrentTimeMilliseconds + .expect("getCurrentTimeMilliseconds") + } + + /// Equivalent to [`sys::ffi::playdate_sys::getSecondsSinceEpoch`] + #[doc(alias = "sys::ffi::playdate_sys::getSecondsSinceEpoch")] + #[inline(always)] + fn get_seconds_since_epoch(&self) -> unsafe extern "C" fn(milliseconds: *mut c_uint) -> c_uint { + self.0.getSecondsSinceEpoch.expect("getSecondsSinceEpoch") + } + + /// Equivalent to [`sys::ffi::playdate_sys::drawFPS`] + #[doc(alias = "sys::ffi::playdate_sys::drawFPS")] + #[inline(always)] + fn draw_fps(&self) -> unsafe extern "C" fn(x: c_int, y: c_int) { self.0.drawFPS.expect("drawFPS") } + + /// Equivalent to [`sys::ffi::playdate_sys::setUpdateCallback`] + #[doc(alias = "sys::ffi::playdate_sys::setUpdateCallback")] + #[inline(always)] + fn set_update_callback(&self) -> unsafe extern "C" fn(update: PDCallbackFunction, userdata: *mut c_void) { + self.0.setUpdateCallback.expect("setUpdateCallback") + } + + /// Equivalent to [`sys::ffi::playdate_sys::getFlipped`] + #[doc(alias = "sys::ffi::playdate_sys::getFlipped")] + #[inline(always)] + fn get_flipped(&self) -> unsafe extern "C" fn() -> c_int { self.0.getFlipped.expect("getFlipped") } + + /// Equivalent to [`sys::ffi::playdate_sys::setAutoLockDisabled`] + #[doc(alias = "sys::ffi::playdate_sys::setAutoLockDisabled")] + #[inline(always)] + fn set_auto_lock_disabled(&self) -> unsafe extern "C" fn(disable: c_int) { + self.0.setAutoLockDisabled.expect("setAutoLockDisabled") + } + + /// Equivalent to [`sys::ffi::playdate_sys::getReduceFlashing`] + #[doc(alias = "sys::ffi::playdate_sys::getReduceFlashing")] + #[inline(always)] + fn get_reduce_flashing(&self) -> unsafe extern "C" fn() -> c_int { + self.0.getReduceFlashing.expect("getReduceFlashing") + } + + /// Equivalent to [`sys::ffi::playdate_sys::getElapsedTime`] + #[doc(alias = "sys::ffi::playdate_sys::getElapsedTime")] + #[inline(always)] + fn get_elapsed_time(&self) -> unsafe extern "C" fn() -> c_float { + self.0.getElapsedTime.expect("getElapsedTime") + } + + /// Equivalent to [`sys::ffi::playdate_sys::resetElapsedTime`] + #[doc(alias = "sys::ffi::playdate_sys::resetElapsedTime")] + #[inline(always)] + fn reset_elapsed_time(&self) -> unsafe extern "C" fn() { + self.0.resetElapsedTime.expect("resetElapsedTime") + } + + /// Equivalent to [`sys::ffi::playdate_sys::getBatteryPercentage`] + #[doc(alias = "sys::ffi::playdate_sys::getBatteryPercentage")] + #[inline(always)] + fn get_battery_percentage(&self) -> unsafe extern "C" fn() -> c_float { + self.0.getBatteryPercentage.expect("getBatteryPercentage") + } + + /// Equivalent to [`sys::ffi::playdate_sys::getBatteryVoltage`] + #[doc(alias = "sys::ffi::playdate_sys::getBatteryVoltage")] + #[inline(always)] + fn get_battery_voltage(&self) -> unsafe extern "C" fn() -> c_float { + self.0.getBatteryVoltage.expect("getBatteryVoltage") + } + + /// Equivalent to [`sys::ffi::playdate_sys::getTimezoneOffset`] + #[doc(alias = "sys::ffi::playdate_sys::getTimezoneOffset")] + #[inline(always)] + fn get_timezone_offset(&self) -> unsafe extern "C" fn() -> i32 { + self.0.getTimezoneOffset.expect("getTimezoneOffset") + } + + /// Equivalent to [`sys::ffi::playdate_sys::shouldDisplay24HourTime`] + #[doc(alias = "sys::ffi::playdate_sys::shouldDisplay24HourTime")] + #[inline(always)] + fn should_display_24_hour_time(&self) -> unsafe extern "C" fn() -> c_int { + self.0.shouldDisplay24HourTime.expect("shouldDisplay24HourTime") + } + + /// Equivalent to [`sys::ffi::playdate_sys::convertEpochToDateTime`] + #[doc(alias = "sys::ffi::playdate_sys::convertEpochToDateTime")] + #[inline(always)] + fn convert_epoch_to_date_time(&self) -> unsafe extern "C" fn(epoch: u32, datetime: *mut PDDateTime) { + self.0.convertEpochToDateTime.expect("convertEpochToDateTime") + } + + /// Equivalent to [`sys::ffi::playdate_sys::convertDateTimeToEpoch`] + #[doc(alias = "sys::ffi::playdate_sys::convertDateTimeToEpoch")] + #[inline(always)] + fn convert_date_time_to_epoch(&self) -> unsafe extern "C" fn(datetime: *mut PDDateTime) -> u32 { + self.0.convertDateTimeToEpoch.expect("convertDateTimeToEpoch") + } + } + + pub trait Api { /// Equivalent to [`sys::ffi::playdate_sys::getLanguage`] #[doc(alias = "sys::ffi::playdate_sys::getLanguage")]