diff --git a/src/graphics_api_impl.rs b/src/graphics_api_impl.rs index f0f07e9..c72b380 100644 --- a/src/graphics_api_impl.rs +++ b/src/graphics_api_impl.rs @@ -1,6 +1,6 @@ #![allow(dead_code, unused)] -use ab_glyph::FontRef; +use ab_glyph::{FontRef, ScaleFont}; use able_graphics_library::{AglApi, FrameBuffer, Point, HEIGHT, RGBA, WIDTH}; use mini_gl_fb::glutin::platform::unix::x11::ffi::XK_R10; @@ -78,25 +78,50 @@ impl AglApi for GraphicsRenderer { fn draw_text(&mut self, coords: Point, scale: u8, color: RGBA, text: String) { use ab_glyph::{point, Font, FontRef, Glyph}; - let font: FontRef = - FontRef::try_from_slice(include_bytes!("../font/Roboto-Regular.ttf")).unwrap(); + let font = FontRef::try_from_slice(include_bytes!("../font/Roboto-Regular.ttf")).unwrap(); + let font_scaled = font.as_scaled(scale as f32); - // Get a glyph for 'q' with a scale & position. - let q_glyph: Glyph = font - .glyph_id('a') - .with_scale_and_position(scale as f32, point(coords.x as f32, coords.y as f32)); - // Draw it. - if let Some(q) = font.outline_glyph(q_glyph) { - // dbg!(q); + // Individual characters are drawn at floating-point offsets. + let mut coords = point(coords.x as f32, coords.y as f32); + let mut last_char = None; - q.draw(|x, y, c| { - /* draw pixel `(x, y)` with coverage: `c` */ - dbg!(x, y, c); + for ch in text.chars() { + let glyph_id = font.glyph_id(ch); - let coordinates = x as u16 + (WIDTH as u16).wrapping_mul(y as u16); + // Kern character pairs as necessary. + if let Some(last) = last_char { + coords.x -= font_scaled.kern(last, glyph_id); + } + last_char = Some(font.glyph_id(ch)); - self.buff[coordinates as usize] = color; - }); + // Get a glyph with a scale & position. + let glyph: Glyph = glyph_id + .with_scale_and_position(scale as f32, point(coords.x as f32, coords.y as f32)); + + // Draw it. + if let Some(outline) = font.outline_glyph(glyph) { + outline.draw(|x, y, c| { + // ab_glyph gives us coordinates within the + // bounding box; convert these to coordinates + // within the image. + let corner = outline.px_bounds().min; + let (x, y) = (x + corner.x as u32, y + corner.y as u32); + + // Flip y to draw characters right-side-up. + let y = 2 * coords.y as u32 - y; + + // draw pixel `(x, y)` with coverage: `c` + let coordinates = x as u16 + (WIDTH as u16).wrapping_mul(y as u16); + let pixel = &mut self.buff[coordinates as usize]; + + // Interpolate between the existing background + // character and the new foreground color. + *pixel = lerp_rgba(*pixel, color, c); + }); + } + + // Advance to the next glyph's position. + coords.x += font_scaled.h_advance(glyph_id); } } /// Actually move the double buffer to the single buffer and "update" the screen @@ -107,3 +132,16 @@ impl AglApi for GraphicsRenderer { } } } + +/// Linearly interpolate between two colors according to a factor. +/// This method is cheap, perfectly accurate for shades of gray, and +/// reasonably accurate for other colors; if better results are +/// needed, LCH interpolation should be used instead. +fn lerp_rgba(a: RGBA, b: RGBA, fac: f32) -> RGBA { + RGBA { + r: ((b.r as f32 - a.r as f32) * fac + a.r as f32) as _, + g: ((b.g as f32 - a.g as f32) * fac + a.g as f32) as _, + b: ((b.b as f32 - a.b as f32) * fac + a.b as f32) as _, + a: ((b.a as f32 - a.a as f32) * fac + a.a as f32) as _, + } +} diff --git a/src/main.rs b/src/main.rs index 0bda1e5..6a3187f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,8 +73,8 @@ fn main() { ); xyz.draw_text( - Point { x: 1000, y: 90 }, - 1, + Point { x: 100, y: 90 }, + 24, RGBA { r: 0, g: 0,