From 986a2a967721f6b99519564b5a6c501ec4be4a0e Mon Sep 17 00:00:00 2001 From: gered Date: Thu, 9 Mar 2023 16:19:52 -0500 Subject: [PATCH] make fonts generic over GeneralBitmap types this also includes a semi-important change where the existing BitmaskCharacter draw implementation will now not draw anything if FontRenderOpts::None is passed in, instead of just substituting color 0. this probably makes more sense this way anyway --- examples/audio_playback/src/main.rs | 1 + examples/balls/src/main.rs | 1 + examples/balls_v2/src/states.rs | 1 + examples/slimed/src/states.rs | 1 + examples/template_minimal/src/main.rs | 1 + ggdt/src/graphics/bitmap.rs | 7 ++ ggdt/src/graphics/{indexed => }/font.rs | 68 ++++++++++++------- .../src/graphics/indexed/bitmap/primitives.rs | 5 +- ggdt/src/graphics/indexed/mod.rs | 2 - ggdt/src/graphics/mod.rs | 2 + ggdt/src/system/res/dos_like.rs | 2 + ggdt/tests/graphics.rs | 1 + 12 files changed, 62 insertions(+), 30 deletions(-) rename ggdt/src/graphics/{indexed => }/font.rs (74%) diff --git a/examples/audio_playback/src/main.rs b/examples/audio_playback/src/main.rs index 47a6981..b2dbe33 100644 --- a/examples/audio_playback/src/main.rs +++ b/examples/audio_playback/src/main.rs @@ -3,6 +3,7 @@ use std::path::Path; use anyhow::Result; use ggdt::audio::*; +use ggdt::graphics::*; use ggdt::graphics::indexed::*; use ggdt::system::*; use ggdt::utils::rnd_value; diff --git a/examples/balls/src/main.rs b/examples/balls/src/main.rs index a618546..a3d5f08 100644 --- a/examples/balls/src/main.rs +++ b/examples/balls/src/main.rs @@ -3,6 +3,7 @@ use std::path::Path; use anyhow::Result; use ggdt::*; +use ggdt::graphics::*; use ggdt::graphics::indexed::*; use ggdt::math::*; use ggdt::system::*; diff --git a/examples/balls_v2/src/states.rs b/examples/balls_v2/src/states.rs index 100e405..ba42e1c 100644 --- a/examples/balls_v2/src/states.rs +++ b/examples/balls_v2/src/states.rs @@ -1,5 +1,6 @@ use ggdt::entities::*; use ggdt::events::*; +use ggdt::graphics::*; use ggdt::graphics::indexed::*; use ggdt::math::*; use ggdt::states::*; diff --git a/examples/slimed/src/states.rs b/examples/slimed/src/states.rs index 528a9d0..4da5a6a 100644 --- a/examples/slimed/src/states.rs +++ b/examples/slimed/src/states.rs @@ -2,6 +2,7 @@ use std::path::Path; use ggdt::base::*; use ggdt::entities::*; +use ggdt::graphics::font::*; use ggdt::graphics::indexed::*; use ggdt::states::*; use ggdt::system::*; diff --git a/examples/template_minimal/src/main.rs b/examples/template_minimal/src/main.rs index d69be68..27c01c0 100644 --- a/examples/template_minimal/src/main.rs +++ b/examples/template_minimal/src/main.rs @@ -1,6 +1,7 @@ use anyhow::Result; use ggdt::{SCREEN_BOTTOM, SCREEN_RIGHT}; +use ggdt::graphics::*; use ggdt::graphics::indexed::*; use ggdt::system::*; use ggdt::utils::rnd_value; diff --git a/ggdt/src/graphics/bitmap.rs b/ggdt/src/graphics/bitmap.rs index 27a728e..5bd68cc 100644 --- a/ggdt/src/graphics/bitmap.rs +++ b/ggdt/src/graphics/bitmap.rs @@ -35,6 +35,9 @@ pub trait GeneralBitmap: Sized + Clone { /// Returns the height of the bitmap in pixels. fn height(&self) -> u32; + /// Returns the current clipping region set on this bitmap. + fn clip_region(&self) -> &Rect; + /// Returns a rect representing the full bitmap boundaries, ignoring the current clipping /// region set on this bitmap. fn full_bounds(&self) -> Rect; @@ -111,6 +114,10 @@ impl GeneralBitmap for indexed::Bitmap { self.height() } + fn clip_region(&self) -> &Rect { + self.clip_region() + } + #[inline] fn full_bounds(&self) -> Rect { self.full_bounds() diff --git a/ggdt/src/graphics/indexed/font.rs b/ggdt/src/graphics/font.rs similarity index 74% rename from ggdt/src/graphics/indexed/font.rs rename to ggdt/src/graphics/font.rs index 94e11d9..f31f324 100644 --- a/ggdt/src/graphics/indexed/font.rs +++ b/ggdt/src/graphics/font.rs @@ -4,12 +4,14 @@ use std::io::{BufReader, BufWriter, Cursor}; use std::path::Path; use byteorder::{ReadBytesExt, WriteBytesExt}; +use num_traits::{PrimInt, Unsigned}; use thiserror::Error; +use crate::graphics::*; use crate::graphics::indexed::*; use crate::math::*; -pub static VGA_FONT_BYTES: &[u8] = include_bytes!("../../../assets/vga.fnt"); +pub static VGA_FONT_BYTES: &[u8] = include_bytes!("../../assets/vga.fnt"); pub const NUM_CHARS: usize = 256; pub const CHAR_HEIGHT: usize = 8; @@ -25,14 +27,16 @@ pub enum FontError { } #[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum FontRenderOpts { - Color(u8), +pub enum FontRenderOpts { + Color(PixelType), None, } pub trait Character { fn bounds(&self) -> &Rect; - fn draw(&self, dest: &mut Bitmap, x: i32, y: i32, opts: FontRenderOpts); + fn draw(&self, dest: &mut BitmapType, x: i32, y: i32, opts: FontRenderOpts) + where + BitmapType: GeneralBitmap; } pub trait Font { @@ -41,7 +45,9 @@ pub trait Font { fn character(&self, ch: char) -> &Self::CharacterType; fn space_width(&self) -> u8; fn line_height(&self) -> u8; - fn measure(&self, text: &str, opts: FontRenderOpts) -> (u32, u32); + fn measure(&self, text: &str, opts: FontRenderOpts) -> (u32, u32) + where + PixelType: PrimInt + Unsigned; } #[derive(Debug, Clone, Eq, PartialEq)] @@ -56,7 +62,10 @@ impl Character for BitmaskCharacter { &self.bounds } - fn draw(&self, dest: &mut Bitmap, x: i32, y: i32, opts: FontRenderOpts) { + fn draw(&self, dest: &mut BitmapType, x: i32, y: i32, opts: FontRenderOpts) + where + BitmapType: GeneralBitmap + { // out of bounds check if ((x + self.bounds.width as i32) < dest.clip_region().x) || ((y + self.bounds.height as i32) < dest.clip_region().y) @@ -68,7 +77,11 @@ impl Character for BitmaskCharacter { let color = match opts { FontRenderOpts::Color(color) => color, - _ => 0, + // this kind of highlights a weakness of this design i guess. what does it mean to render a BitmaskFont, + // which has no inherent colour information in it, when there is no specific render colour passed in? + // TODO: is it better to return an error here? should a BitmaskFont have a "default colour" to fall back to? + // or, should a Bitmap have a "default colour" property we could fall back to? not sure! + _ => return, }; // TODO: i'm sure this can be optimized, lol @@ -195,7 +208,10 @@ impl Font for BitmaskFont { self.line_height } - fn measure(&self, text: &str, _opts: FontRenderOpts) -> (u32, u32) { + fn measure(&self, text: &str, _opts: FontRenderOpts) -> (u32, u32) + where + PixelType: PrimInt + Unsigned + { if text.is_empty() { return (0, 0); } @@ -249,29 +265,29 @@ pub mod tests { { let font = BitmaskFont::load_from_file(Path::new("./assets/vga.fnt"))?; - assert_eq!((40, 8), font.measure("Hello", FontRenderOpts::None)); - assert_eq!((40, 16), font.measure("Hello\nthere", FontRenderOpts::None)); - assert_eq!((88, 24), font.measure("longer line\nshort\nthe end", FontRenderOpts::None)); - assert_eq!((0, 0), font.measure("", FontRenderOpts::None)); - assert_eq!((0, 0), font.measure(" ", FontRenderOpts::None)); - assert_eq!((40, 16), font.measure("\nhello", FontRenderOpts::None)); - assert_eq!((0, 0), font.measure("\n", FontRenderOpts::None)); - assert_eq!((40, 8), font.measure("hello\n", FontRenderOpts::None)); - assert_eq!((40, 24), font.measure("hello\n\nthere", FontRenderOpts::None)); + assert_eq!((40, 8), font.measure("Hello", FontRenderOpts::::None)); + assert_eq!((40, 16), font.measure("Hello\nthere", FontRenderOpts::::None)); + assert_eq!((88, 24), font.measure("longer line\nshort\nthe end", FontRenderOpts::::None)); + assert_eq!((0, 0), font.measure("", FontRenderOpts::::None)); + assert_eq!((0, 0), font.measure(" ", FontRenderOpts::::None)); + assert_eq!((40, 16), font.measure("\nhello", FontRenderOpts::::None)); + assert_eq!((0, 0), font.measure("\n", FontRenderOpts::::None)); + assert_eq!((40, 8), font.measure("hello\n", FontRenderOpts::::None)); + assert_eq!((40, 24), font.measure("hello\n\nthere", FontRenderOpts::::None)); } { let font = BitmaskFont::load_from_file(Path::new("./test-assets/small.fnt"))?; - assert_eq!((22, 7), font.measure("Hello", FontRenderOpts::None)); - assert_eq!((24, 14), font.measure("Hello\nthere", FontRenderOpts::None)); - assert_eq!((50, 21), font.measure("longer line\nshort\nthe end", FontRenderOpts::None)); - assert_eq!((0, 0), font.measure("", FontRenderOpts::None)); - assert_eq!((0, 0), font.measure(" ", FontRenderOpts::None)); - assert_eq!((21, 14), font.measure("\nhello", FontRenderOpts::None)); - assert_eq!((0, 0), font.measure("\n", FontRenderOpts::None)); - assert_eq!((21, 7), font.measure("hello\n", FontRenderOpts::None)); - assert_eq!((24, 21), font.measure("hello\n\nthere", FontRenderOpts::None)); + assert_eq!((22, 7), font.measure("Hello", FontRenderOpts::::None)); + assert_eq!((24, 14), font.measure("Hello\nthere", FontRenderOpts::::None)); + assert_eq!((50, 21), font.measure("longer line\nshort\nthe end", FontRenderOpts::::None)); + assert_eq!((0, 0), font.measure("", FontRenderOpts::::None)); + assert_eq!((0, 0), font.measure(" ", FontRenderOpts::::None)); + assert_eq!((21, 14), font.measure("\nhello", FontRenderOpts::::None)); + assert_eq!((0, 0), font.measure("\n", FontRenderOpts::::None)); + assert_eq!((21, 7), font.measure("hello\n", FontRenderOpts::::None)); + assert_eq!((24, 21), font.measure("hello\n\nthere", FontRenderOpts::::None)); } Ok(()) diff --git a/ggdt/src/graphics/indexed/bitmap/primitives.rs b/ggdt/src/graphics/indexed/bitmap/primitives.rs index 52d3bfe..ba12db8 100644 --- a/ggdt/src/graphics/indexed/bitmap/primitives.rs +++ b/ggdt/src/graphics/indexed/bitmap/primitives.rs @@ -1,5 +1,6 @@ use std::mem::swap; +use crate::graphics::*; use crate::graphics::indexed::*; use crate::math::*; @@ -76,13 +77,13 @@ impl Bitmap { /// Renders a single character using the font given. #[inline] - pub fn print_char(&mut self, ch: char, x: i32, y: i32, opts: FontRenderOpts, font: &T) { + pub fn print_char(&mut self, ch: char, x: i32, y: i32, opts: FontRenderOpts, font: &T) { font.character(ch) .draw(self, x, y, opts); } /// Renders the string of text using the font given. - pub fn print_string(&mut self, text: &str, x: i32, y: i32, opts: FontRenderOpts, font: &T) { + pub fn print_string(&mut self, text: &str, x: i32, y: i32, opts: FontRenderOpts, font: &T) { let mut current_x = x; let mut current_y = y; for ch in text.chars() { diff --git a/ggdt/src/graphics/indexed/mod.rs b/ggdt/src/graphics/indexed/mod.rs index 10eec38..a112e59 100644 --- a/ggdt/src/graphics/indexed/mod.rs +++ b/ggdt/src/graphics/indexed/mod.rs @@ -3,10 +3,8 @@ pub use self::bitmap::*; pub use self::blendmap::*; -pub use self::font::*; pub use self::palette::*; pub mod bitmap; pub mod blendmap; -pub mod font; pub mod palette; \ No newline at end of file diff --git a/ggdt/src/graphics/mod.rs b/ggdt/src/graphics/mod.rs index e3ef640..f4c614c 100644 --- a/ggdt/src/graphics/mod.rs +++ b/ggdt/src/graphics/mod.rs @@ -1,7 +1,9 @@ pub use self::bitmap::*; pub use self::bitmapatlas::*; +pub use self::font::*; pub mod bitmap; pub mod bitmapatlas; +pub mod font; pub mod indexed; pub mod rgb; diff --git a/ggdt/src/system/res/dos_like.rs b/ggdt/src/system/res/dos_like.rs index 0c6dcf6..b2bda8a 100644 --- a/ggdt/src/system/res/dos_like.rs +++ b/ggdt/src/system/res/dos_like.rs @@ -2,6 +2,7 @@ //! instance to provide something resembling an old DOS VGA mode 13h style experience (there are differences, however). //! //! ```no_run +//! use ggdt::graphics::*; //! use ggdt::graphics::indexed::*; //! use ggdt::system::*; //! @@ -30,6 +31,7 @@ use sdl2::video::Window; use crate::system::*; +use crate::graphics::*; use crate::graphics::indexed::*; /// Configuration / builder for configuring and constructing an instance of [`DosLike`]. diff --git a/ggdt/tests/graphics.rs b/ggdt/tests/graphics.rs index 3d5ca7a..430b00b 100644 --- a/ggdt/tests/graphics.rs +++ b/ggdt/tests/graphics.rs @@ -2,6 +2,7 @@ use std::path::Path; use std::rc::Rc; use ggdt::{SCREEN_HEIGHT, SCREEN_WIDTH}; +use ggdt::graphics::*; use ggdt::graphics::indexed::*; fn setup() -> (Bitmap, Palette) {