From 788e8b523956b5c7addedb7fb524f22c53fe07dd Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Tue, 21 May 2019 23:43:28 -0700 Subject: [PATCH 1/2] Add conversion of font outlines to kurbo Also some minor cleanups, plus fixes to compound glyph transforms. Note that point-matching is not implemented yet. --- Cargo.toml | 8 +++-- src/accumulate.rs | 6 ++-- src/font.rs | 77 +++++++++++++++++++++++++++++++++++++++++++---- src/geom.rs | 23 ++++++++++++++ src/lib.rs | 3 ++ 5 files changed, 107 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 19443ab..3fb9739 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,10 +2,14 @@ name = "font-rs" version = "0.1.3" license = "Apache-2.0" -authors = [ "Raph Levien " ] +authors = [ "Raph Levien " ] keywords = ["font", "truetype", "ttf"] description = "A font renderer written (mostly) in pure, safe Rust" -repository = "https://github.com/google/font-rs" +repository = "https://github.com/raphlinus/font-rs" +readme = "README.md" + +[dependencies] +kurbo = { version = "0.2.2", optional = true } [features] sse = [] diff --git a/src/accumulate.rs b/src/accumulate.rs index d41c536..fb89f73 100644 --- a/src/accumulate.rs +++ b/src/accumulate.rs @@ -12,14 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. +#[cfg(feature = "sse")] use std::mem; -#[cfg(target_arch = "x86_64")] +#[cfg(all(feature = "sse", target_arch = "x86_64"))] use std::arch::x86_64::*; -#[cfg(target_arch = "x86")] +#[cfg(all(feature = "sse", target_arch = "x86"))] use std::arch::x86::*; +#[cfg(feature = "sse")] macro_rules! _mm_shuffle { ($z:expr, $y:expr, $x:expr, $w:expr) => { ($z << 6) | ($y << 4) | ($x << 2) | $w diff --git a/src/font.rs b/src/font.rs index c4903e8..7188561 100644 --- a/src/font.rs +++ b/src/font.rs @@ -22,6 +22,9 @@ use std::result::Result; use geom::{affine_pt, Affine, Point}; use raster::Raster; +#[cfg(feature = "kurbo")] +use kurbo::BezPath; + #[derive(PartialEq, Eq, Hash)] struct Tag(u32); @@ -633,7 +636,8 @@ struct Components<'a> { ix: usize, } -const ARG_1_AND_2_ARE_WORDS: u16 = 1; +const ARG_1_AND_2_ARE_WORDS: u16 = 1 << 0; +const ARGS_ARE_XY_VALUES: u16 = 1 << 1; const WE_HAVE_A_SCALE: u16 = 1 << 3; const MORE_COMPONENTS: u16 = 1 << 5; const WE_HAVE_AN_X_AND_Y_SCALE: u16 = 1 << 6; @@ -657,10 +661,17 @@ impl<'a> Iterator for Components<'a> { arg2 = get_i16(self.data, self.ix).unwrap(); self.ix += 2; } else { - arg1 = self.data[self.ix] as i16; + let byte1 = self.data[self.ix]; self.ix += 1; - arg2 = self.data[self.ix] as i16; + let byte2 = self.data[self.ix]; self.ix += 1; + if (flags & ARGS_ARE_XY_VALUES) != 0 { + arg1 = (byte1 as i8) as i16; + arg2 = (byte2 as i8) as i16; + } else { + arg1 = byte1 as i16; + arg2 = byte2 as i16; + } } let mut a = 1.0; let mut b = 0.0; @@ -685,9 +696,14 @@ impl<'a> Iterator for Components<'a> { self.ix += 2; d = a; } - // TODO: handle non-ARGS_ARE_XY_VALUES case - let x = arg1 as f32; - let y = arg2 as f32; + let (x, y) = if (flags & ARGS_ARE_XY_VALUES) != 0 { + (arg1 as f32, arg2 as f32) + } else { + // This will require significant refactoring, we need access to + // points so we should store them instead of using only iterators. + println!("warning: need to look up point, offset ignored"); + (0.0, 0.0) + }; let z = Affine::new(a, b, c, d, x, y); self.more = (flags & MORE_COMPONENTS) != 0; Some((glyph_index, z)) @@ -924,6 +940,55 @@ impl<'a> Font<'a> { None } } + + /// The number of glyphs in the font. + /// + /// Limited to 16 bits due to limitations in OpenType. + pub fn num_glyphs(&self) -> u16 { + self.maxp.num_glyphs() + } + + #[cfg(feature = "kurbo")] + pub fn get_glyph_path(&self, glyph_ix: u16) -> Option { + let _ = self.get_glyph(glyph_ix)?; + let affine = Default::default(); + let mut bp = BezPath::new(); + self.append_glyph_path(glyph_ix, &affine, &mut bp); + Some(bp) + } + + #[cfg(feature = "kurbo")] + fn append_glyph_path(&self, glyph_ix: u16, z: &Affine, bp: &mut BezPath) { + if let Some(glyph) = self.get_glyph(glyph_ix) { + match glyph { + Glyph::Simple(s) => { + let mut p = s.points(); + for n in s.contour_sizes() { + append_kurbo_path(bp, z, &mut path_from_pts(p.by_ref().take(n))); + } + } + Glyph::Compound(c) => { + for (glyph_ix, affine) in c.components() { + let concat = Affine::concat(z, &affine); + self.append_glyph_path(glyph_ix, &concat, bp); + } + } + Glyph::Empty => (), + } + } + } +} + +#[cfg(feature = "kurbo")] +fn append_kurbo_path>(bp: &mut BezPath, z: &Affine, path: &mut I) { + for op in path { + match op { + MoveTo(p) => bp.moveto(z * p), + LineTo(p) => bp.lineto(z * p), + QuadTo(p1, p2) => bp.quadto(z * p1, z * p2), + } + } + bp.closepath() } #[derive(Debug)] diff --git a/src/geom.rs b/src/geom.rs index c292cad..c812951 100644 --- a/src/geom.rs +++ b/src/geom.rs @@ -41,6 +41,13 @@ impl Point { } } +#[cfg(feature = "kurbo")] +impl From for kurbo::Vec2 { + fn from(pt: Point) -> kurbo::Vec2 { + kurbo::Vec2::new(pt.x as f64, pt.y as f64) + } +} + impl Debug for Point { fn fmt(&self, f: &mut Formatter) -> Result { write!(f, "({}, {})", self.x, self.y) @@ -57,8 +64,16 @@ pub struct Affine { f: f32, } +impl Default for Affine { + fn default() -> Affine { + Affine { a: 1.0, b: 0.0, c: 0.0, d: 1.0, e: 0.0, f: 0.0 } + } +} + impl Affine { /// Concatenate two affine transforms. + /// + /// TODO: deprecate use of this in favor of the multiply op. pub fn concat(t1: &Affine, t2: &Affine) -> Affine { Affine { a: t1.a * t2.a + t1.c * t2.b, @@ -78,4 +93,12 @@ pub fn affine_pt(z: &Affine, p: &Point) -> Point { } } +impl<'a> std::ops::Mul for &'a Affine { + type Output = Point; + + fn mul(self, rhs: Point) -> Point { + affine_pt(self, &rhs) + } +} + gen_new!(Affine, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32); diff --git a/src/lib.rs b/src/lib.rs index b7b189f..ae74b61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,9 @@ //! A very high performance font renderer. +#[cfg(feature = "kurbo")] +extern crate kurbo; + #[macro_use] pub mod macros; pub mod accumulate; From d10473e5db539f1c1ef7cc83ba6b45dbad0a8832 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Thu, 27 Jun 2019 08:49:37 -0700 Subject: [PATCH 2/2] Update kurbo to 0.4 --- Cargo.toml | 2 +- src/font.rs | 8 ++++---- src/geom.rs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3fb9739..b462a93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/raphlinus/font-rs" readme = "README.md" [dependencies] -kurbo = { version = "0.2.2", optional = true } +kurbo = { version = "0.4", optional = true } [features] sse = [] diff --git a/src/font.rs b/src/font.rs index 7188561..a401e9c 100644 --- a/src/font.rs +++ b/src/font.rs @@ -983,12 +983,12 @@ impl<'a> Font<'a> { fn append_kurbo_path>(bp: &mut BezPath, z: &Affine, path: &mut I) { for op in path { match op { - MoveTo(p) => bp.moveto(z * p), - LineTo(p) => bp.lineto(z * p), - QuadTo(p1, p2) => bp.quadto(z * p1, z * p2), + MoveTo(p) => bp.move_to(z * p), + LineTo(p) => bp.line_to(z * p), + QuadTo(p1, p2) => bp.quad_to(z * p1, z * p2), } } - bp.closepath() + bp.close_path() } #[derive(Debug)] diff --git a/src/geom.rs b/src/geom.rs index c812951..55e9855 100644 --- a/src/geom.rs +++ b/src/geom.rs @@ -42,9 +42,9 @@ impl Point { } #[cfg(feature = "kurbo")] -impl From for kurbo::Vec2 { - fn from(pt: Point) -> kurbo::Vec2 { - kurbo::Vec2::new(pt.x as f64, pt.y as f64) +impl From for kurbo::Point { + fn from(pt: Point) -> kurbo::Point { + kurbo::Point::new(pt.x as f64, pt.y as f64) } }