diff --git a/Cargo.lock b/Cargo.lock index fa3173a..a8ca1bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169" + [[package]] name = "autocfg" version = "1.5.0" @@ -335,6 +341,16 @@ name = "osgui" version = "0.1.0" dependencies = [ "minifb", + "rusttype", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e6affeb1632d6ff6a23d2cd40ffed138e82f1532571a26f527c8a284bb2fbb" +dependencies = [ + "ttf-parser", ] [[package]] @@ -407,6 +423,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rusttype" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff8374aa04134254b7995b63ad3dc41c7f7236f69528b28553da7d72efaa967" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + [[package]] name = "rustversion" version = "1.0.21" @@ -522,6 +548,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "ttf-parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" + [[package]] name = "unicode-ident" version = "1.0.18" diff --git a/Cargo.toml b/Cargo.toml index 2f9016e..3f6ca34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,5 @@ name = "osgui" [dependencies] minifb = "0.28.0" +rusttype = "0.9.3" diff --git a/src/elements/mod.rs b/src/elements/mod.rs index 811eccd..07e9c54 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -16,3 +16,17 @@ impl Element for Rect { self } } + +impl Element for String { + fn render(&mut self, scope: &mut crate::render::RenderScope) { + scope.draw_text(0, 0, 40.0, self); + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } +} diff --git a/src/lib.rs b/src/lib.rs index 411a63f..a435275 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ pub mod extensions; pub mod macros; pub mod render; pub mod style; +pub mod utils; pub mod widget; pub struct Screen { diff --git a/src/main.rs b/src/main.rs index 206716e..fb9ee30 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,11 @@ use minifb::{Window, WindowOptions}; -use osgui::{ - elements::Rect, - extensions::velocity::{Velocity, VelocityExtension}, - style::Transform, - Screen, -}; +use osgui::{extensions::velocity::VelocityExtension, style::Transform, Screen}; fn main() { let window = Window::new( "minifb example", - 500, - 500, + 200, + 200, WindowOptions { resize: false, ..Default::default() @@ -21,9 +16,7 @@ fn main() { let mut app = Screen::new(window); app.extension(VelocityExtension); - app.draw(Rect) - .component(Transform::new().dimensions(40, 40)) - .component(Velocity(500, 0)); + app.draw("test".to_string()).component(Transform::center()); app.run().unwrap(); } diff --git a/src/render.rs b/src/render.rs index 2ab2ebe..d849e47 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,7 +1,14 @@ -use crate::style::{RawTransform, Transform}; +use rusttype::{Font, Scale}; + +use crate::{ + style::{RawTransform, Transform}, + utils, +}; + +const FONT: &[u8] = include_bytes!("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"); enum RenderMethod { - // Text(String), + Text(usize, usize, f32, String), Rectangle(usize, usize, usize, usize, u32), } @@ -29,6 +36,22 @@ impl RenderScope { transform.use_position(self.parent_width, self.parent_height, &mut self.transform); } + pub fn draw_text( + &mut self, + x: usize, + y: usize, + scale: f32, + text: &str, + // color: u32, + ) { + self.render_stack + .push(RenderMethod::Text(x, y, scale, text.to_string())); + let font = Font::try_from_bytes(FONT).unwrap(); + let (w, h) = utils::measure_text(&font, &text, Scale::uniform(scale)); + self.transform.width = self.transform.width.max(w as usize); + self.transform.height = self.transform.height.max(h as usize); + } + pub fn draw_rect(&mut self, x: usize, y: usize, width: usize, height: usize, color: u32) { self.render_stack .push(RenderMethod::Rectangle(x, y, width, height, color)); @@ -50,6 +73,42 @@ impl RenderScope { } } } + + RenderMethod::Text(px, py, scale, text) => { + let font = Font::try_from_bytes(FONT).unwrap(); + let scale = rusttype::Scale::uniform(*scale); + let v_metrics = font.v_metrics(scale); + let glyphs: Vec<_> = font + .layout( + text, + scale, + rusttype::point( + (self.transform.x + px) as f32, + (self.transform.y + py) as f32 + v_metrics.ascent, + ), + ) + .collect(); + for glyph in glyphs { + if let Some(bb) = glyph.pixel_bounding_box() { + glyph.draw(|x, y, v| { + let x = x as i32 + bb.min.x + *px as i32; + let y = y as i32 + bb.min.y + *py as i32; + + if x >= 0 + && x < self.parent_width as i32 + && y >= 0 + && y < self.parent_height as i32 + { + let index = + (y as usize * self.parent_width + x as usize) as usize; + let intensity = (v * 255.0) as u32; + let color = (intensity << 16) | (intensity << 8) | intensity; + self.buffer[index] = color; + } + }); + } + } + } } } } diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..ec6d158 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,24 @@ +use rusttype::{point, Font, Scale}; + +pub fn measure_text(font: &Font, text: &str, scale: Scale) -> (u32, u32) { + let v_metrics = font.v_metrics(scale); + let ascent = v_metrics.ascent; + let descent = v_metrics.descent; + let height = (ascent - descent).ceil() as u32; + + let width = font + .layout(text, scale, point(0.0, 0.0)) + .last() + .map(|g| { + let bb = g.pixel_bounding_box(); + if let Some(bb) = bb { + bb.max.x as f32 + } else { + g.position().x + } + }) + .unwrap_or(0.0) + .ceil() as u32; + + (width, height) +}