From f976ca6e23d938f43addb4305bf1695dabccc949 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sat, 16 Feb 2019 21:20:35 -0800 Subject: [PATCH] add rusttype rasterizer --- src/font/hbwrap.rs | 2 +- src/font/mod.rs | 1 + src/font/rtype.rs | 108 ++++++++++++++++++++++++++++++++++++--------- src/font/system.rs | 1 + 4 files changed, 90 insertions(+), 22 deletions(-) diff --git a/src/font/hbwrap.rs b/src/font/hbwrap.rs index 879f42d03..1f4097764 100644 --- a/src/font/hbwrap.rs +++ b/src/font/hbwrap.rs @@ -1,5 +1,5 @@ //! Higher level harfbuzz bindings - +#![allow(dead_code)] #[cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))] use freetype; diff --git a/src/font/mod.rs b/src/font/mod.rs index 3b671eead..058bc35a4 100644 --- a/src/font/mod.rs +++ b/src/font/mod.rs @@ -110,6 +110,7 @@ impl FontConfiguration { } } +#[allow(dead_code)] pub fn shape_with_harfbuzz( font: &mut NamedFont, font_idx: system::FallbackIdx, diff --git a/src/font/rtype.rs b/src/font/rtype.rs index f3996a743..b5b1ce554 100644 --- a/src/font/rtype.rs +++ b/src/font/rtype.rs @@ -2,20 +2,19 @@ use super::hbwrap as harfbuzz; use config::{Config, TextStyle}; -use failure::{self, Error}; -use font::{ - shape_with_harfbuzz, FallbackIdx, Font, FontMetrics, FontSystem, GlyphInfo, NamedFont, - RasterizedGlyph, -}; +use failure::Error; +use font::{FallbackIdx, Font, FontMetrics, FontSystem, GlyphInfo, NamedFont, RasterizedGlyph}; use font_loader::system_fonts; -use rusttype::{Font as RTFont, FontCollection, Scale}; -use std::cell::RefCell; +use rusttype::{ + point, Codepoint, Font as RTFont, FontCollection, PositionedGlyph, Rect, Scale, VMetrics, +}; struct NamedFontImpl<'a> { - collection: FontCollection<'a>, + _collection: FontCollection<'a>, font: RTFont<'a>, - hbfont: RefCell, scale: Scale, + vmetrics: VMetrics, + cell_height: f64, } pub type FontSystemImpl = RustTypeFonts; @@ -36,37 +35,75 @@ impl FontSystem for RustTypeFonts { let (data, idx) = system_fonts::get(&font_props) .ok_or_else(|| format_err!("no font matching {:?}", style))?; eprintln!("want idx {} in bytes of len {}", idx, data.len()); - let hbfont = RefCell::new(harfbuzz::Font::new_from_slice(&data, idx as u32)?); let collection = FontCollection::from_bytes(data)?; let font = collection.font_at(idx as usize)?; eprintln!("made a font for {:?}", style); let scale = Scale::uniform(config.font_size as f32 * 96.0 / 72.0); + let vmetrics = font.v_metrics(scale); + eprintln!("vmetrics {:?}", vmetrics); + let cell_height = f64::from(vmetrics.ascent - vmetrics.descent + vmetrics.line_gap); Ok(Box::new(NamedFontImpl { - collection, - hbfont, + _collection: collection, + cell_height, font, scale, + vmetrics, })) } } +fn bounds(g: &PositionedGlyph) -> Rect { + match g.pixel_bounding_box() { + Some(bounds) => bounds, + None => rusttype::Rect { + min: point(0, 0), + max: point(0, 0), + }, + } +} + impl<'a> NamedFont for NamedFontImpl<'a> { fn get_fallback(&mut self, idx: FallbackIdx) -> Result<&Font, Error> { ensure!(idx == 0, "no fallback fonts available (idx={})", idx); Ok(self) } fn shape(&mut self, s: &str) -> Result, Error> { - shape_with_harfbuzz(self, 0, s) + let mut shaped = Vec::new(); + + for (cluster, c) in s.chars().enumerate() { + let glyph = self.font.glyph(Codepoint(c as u32)).scaled(self.scale); + let hmetrics = glyph.h_metrics(); + let glyph = glyph.positioned(point(0.0, 0.0)); + + if c != ' ' { + eprintln!("{} -> glyph {} {:?}", c, glyph.id().0, hmetrics); + } + + shaped.push(GlyphInfo { + #[cfg(debug_assertions)] + text: s[cluster..cluster].to_string(), + cluster: cluster as u32, + num_cells: 1, + font_idx: 0, + glyph_pos: glyph.id().0, + x_advance: hmetrics.advance_width.into(), + x_offset: (-hmetrics.left_side_bearing).into(), + y_advance: 0.0, + y_offset: 0.0, //(-bounds.max.y).into(), + // vmetrics.descent.into(), + }) + } + Ok(shaped) } } impl<'a> Font for NamedFontImpl<'a> { fn harfbuzz_shape( &self, - buf: &mut harfbuzz::Buffer, - features: Option<&[harfbuzz::hb_feature_t]>, + _buf: &mut harfbuzz::Buffer, + _features: Option<&[harfbuzz::hb_feature_t]>, ) { - self.hbfont.borrow_mut().shape(buf, features) + unimplemented!(); } fn has_color(&self) -> bool { @@ -74,20 +111,49 @@ impl<'a> Font for NamedFontImpl<'a> { } fn metrics(&self) -> FontMetrics { - let vmetrics = self.font.v_metrics(self.scale); let hmetrics = self .font - .glyph(rusttype::Codepoint(33)) + .glyph(Codepoint(33)) .scaled(self.scale) .h_metrics(); FontMetrics { - cell_height: f64::from(vmetrics.ascent - vmetrics.descent + vmetrics.line_gap), + cell_height: self.cell_height, cell_width: hmetrics.advance_width.into(), - descender: vmetrics.descent as i16, + descender: self.vmetrics.descent as i16, } } fn rasterize_glyph(&self, glyph_pos: u32) -> Result { - unimplemented!(); + let g = self + .font + .glyph(rusttype::GlyphId(glyph_pos)) + .scaled(self.scale) + .positioned(point(0.0, 0.0)); + let bounds = bounds(&g); + let width = bounds.width() as usize; + let height = bounds.height() as usize; + let mut data = Vec::with_capacity(width * height * 4); + g.draw(|_x, _y, value| { + let v = (value * 255.0) as u8; + data.push(v); // alpha + data.push(v); // red + data.push(v); // green + data.push(v); // blue + }); + eprintln!( + "rasterize_glyph {} {}x{} {} bounds {:?}", + glyph_pos, width, height, self.cell_height, bounds + ); + // FIXME: there's something funky about either the bearing + // calculation here or the y_offset calculation in the + // shape function that causes the baseline to vary and + // the text look crazy. + Ok(RasterizedGlyph { + data, + height, + width, + bearing_x: bounds.min.x, + bearing_y: -bounds.min.y, + }) } } diff --git a/src/font/system.rs b/src/font/system.rs index ed9247b0b..c377ebab2 100644 --- a/src/font/system.rs +++ b/src/font/system.rs @@ -40,6 +40,7 @@ pub struct GlyphInfo { } impl GlyphInfo { + #[allow(dead_code)] pub fn new( text: &str, font_idx: usize,