diff --git a/read-fonts/src/tables/cvar.rs b/read-fonts/src/tables/cvar.rs index 2582c5a23..1ef669c3f 100644 --- a/read-fonts/src/tables/cvar.rs +++ b/read-fonts/src/tables/cvar.rs @@ -4,11 +4,19 @@ include!("../../generated/generated_cvar.rs"); use super::variations::{ - PackedPointNumbers, TupleVariationCount, TupleVariationData, TupleVariationHeader, + PackedPointNumbers, TupleDelta, TupleVariationCount, TupleVariationData, TupleVariationHeader, }; +/// Variation data specialized for the CVT variation table. +pub type CvtVariationData<'a> = TupleVariationData<'a, CvtDelta>; + impl<'a> Cvar<'a> { - pub fn variation_data(&self, axis_count: u16) -> Result, ReadError> { + /// Returns the variation data containing the tuples and deltas for the + /// control value table. + /// + /// This table doesn't contain an axis count field so this must be provided + /// by the user and can be read from the `fvar` table. + pub fn variation_data(&self, axis_count: u16) -> Result, ReadError> { let count = self.tuple_variation_count(); let data = self.data()?; let header_data = self.raw_tuple_header_data(); @@ -19,14 +27,14 @@ impl<'a> Cvar<'a> { } else { (None, data) }; - Ok(TupleVariationData { - is_gvar: false, + Ok(CvtVariationData { tuple_count: count, axis_count, shared_tuples: None, shared_point_numbers, header_data, serialized_data, + _marker: std::marker::PhantomData, }) } @@ -36,6 +44,32 @@ impl<'a> Cvar<'a> { } } +/// Delta for an entry in the control value table. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CvtDelta { + /// The index in the CVT. + pub position: u16, + /// The delta to apply to the value in the CVT. + pub value: i16, +} + +impl CvtDelta { + /// Applies a tuple scalar to this delta. + pub fn apply_scalar(self, scalar: Fixed) -> Fixed { + Fixed::from_i32(self.value as i32) * scalar + } +} + +impl TupleDelta for CvtDelta { + fn is_point() -> bool { + false + } + + fn new(position: u16, x: i16, _y: i16) -> Self { + Self { position, value: x } + } +} + #[cfg(test)] mod tests { use font_types::F2Dot14; @@ -146,7 +180,7 @@ mod tests { for (tuple, weight) in cvar_data.active_tuples_at(&coords) { for delta in tuple.deltas() { let scaled_delta = delta.apply_scalar(weight); - deltas[delta.position as usize] += scaled_delta.x.to_bits(); + deltas[delta.position as usize] += scaled_delta.to_bits(); } } assert_eq!(&deltas, expected_deltas); @@ -194,7 +228,7 @@ mod tests { count += 1; let deltas = tuple .deltas() - .map(|delta| (delta.position, delta.x_delta)) + .map(|delta| (delta.position, delta.value)) .collect::>(); assert_eq!(&deltas, expected); } diff --git a/read-fonts/src/tables/gvar.rs b/read-fonts/src/tables/gvar.rs index 7811f9b32..b552fcf67 100644 --- a/read-fonts/src/tables/gvar.rs +++ b/read-fonts/src/tables/gvar.rs @@ -3,9 +3,13 @@ include!("../../generated/generated_gvar.rs"); -use super::variations::{PackedPointNumbers, Tuple, TupleVariationCount, TupleVariationHeader}; +use super::variations::{ + PackedPointNumbers, Tuple, TupleDelta, TupleVariationCount, TupleVariationData, + TupleVariationHeader, +}; -pub use super::variations::{TupleDelta as GlyphDelta, TupleVariationData as GlyphVariationData}; +/// Variation data specialized for the glyph variations table. +pub type GlyphVariationData<'a> = TupleVariationData<'a, GlyphDelta>; #[derive(Clone, Copy, Debug)] pub struct U16Or32(u32); @@ -92,17 +96,49 @@ impl<'a> GlyphVariationData<'a> { }; Ok(GlyphVariationData { - is_gvar: true, tuple_count: count, axis_count, shared_tuples: Some(shared_tuples), shared_point_numbers, header_data, serialized_data, + _marker: std::marker::PhantomData, }) } } +/// Delta information for a single point or component in a glyph. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct GlyphDelta { + /// The point or component index. + pub position: u16, + /// The x delta. + pub x_delta: i16, + /// The y delta. + pub y_delta: i16, +} + +impl GlyphDelta { + /// Applies a tuple scalar to this delta. + pub fn apply_scalar(self, scalar: Fixed) -> Point { + Point::new(self.x_delta as i32, self.y_delta as i32).map(Fixed::from_i32) * scalar + } +} + +impl TupleDelta for GlyphDelta { + fn is_point() -> bool { + true + } + + fn new(position: u16, x: i16, y: i16) -> Self { + Self { + position, + x_delta: x, + y_delta: y, + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/read-fonts/src/tables/variations.rs b/read-fonts/src/tables/variations.rs index 111bc8458..d28943ecf 100644 --- a/read-fonts/src/tables/variations.rs +++ b/read-fonts/src/tables/variations.rs @@ -503,8 +503,7 @@ impl<'a> Iterator for TupleVariationHeaderIter<'a> { } #[derive(Clone)] -pub struct TupleVariationData<'a> { - pub(crate) is_gvar: bool, +pub struct TupleVariationData<'a, T> { pub(crate) axis_count: u16, pub(crate) shared_tuples: Option>, pub(crate) shared_point_numbers: Option>, @@ -513,10 +512,14 @@ pub struct TupleVariationData<'a> { pub(crate) header_data: FontData<'a>, // the data for all the tuple bodies pub(crate) serialized_data: FontData<'a>, + pub(crate) _marker: std::marker::PhantomData, } -impl<'a> TupleVariationData<'a> { - pub fn tuples(&self) -> TupleVariationIter<'a> { +impl<'a, T> TupleVariationData<'a, T> +where + T: TupleDelta + 'a, +{ + pub fn tuples(&self) -> TupleVariationIter<'a, T> { TupleVariationIter { current: 0, parent: self.clone(), @@ -526,6 +529,7 @@ impl<'a> TupleVariationData<'a> { self.axis_count, ), serialized_data: self.serialized_data, + _marker: std::marker::PhantomData, } } @@ -535,7 +539,7 @@ impl<'a> TupleVariationData<'a> { pub fn active_tuples_at( &self, coords: &'a [F2Dot14], - ) -> impl Iterator, Fixed)> + 'a { + ) -> impl Iterator, Fixed)> + 'a { self.tuples().filter_map(|tuple| { let scaler = tuple.compute_scalar(coords)?; Some((tuple, scaler)) @@ -548,15 +552,19 @@ impl<'a> TupleVariationData<'a> { } /// An iterator over the [`TupleVariation`]s for a specific glyph. -pub struct TupleVariationIter<'a> { +pub struct TupleVariationIter<'a, T> { current: usize, - parent: TupleVariationData<'a>, + parent: TupleVariationData<'a, T>, header_iter: TupleVariationHeaderIter<'a>, serialized_data: FontData<'a>, + _marker: std::marker::PhantomData, } -impl<'a> TupleVariationIter<'a> { - fn next_tuple(&mut self) -> Option> { +impl<'a, T> TupleVariationIter<'a, T> +where + T: TupleDelta, +{ + fn next_tuple(&mut self) -> Option> { if self.parent.tuple_count() == self.current { return None; } @@ -573,18 +581,21 @@ impl<'a> TupleVariationIter<'a> { (self.parent.shared_point_numbers.clone()?, var_data) }; Some(TupleVariation { - is_gvar: self.parent.is_gvar, axis_count: self.parent.axis_count, header, shared_tuples: self.parent.shared_tuples.clone(), packed_deltas: PackedDeltas::new(packed_deltas), point_numbers, + _marker: std::marker::PhantomData, }) } } -impl<'a> Iterator for TupleVariationIter<'a> { - type Item = TupleVariation<'a>; +impl<'a, T> Iterator for TupleVariationIter<'a, T> +where + T: TupleDelta, +{ + type Item = TupleVariation<'a, T>; fn next(&mut self) -> Option { self.next_tuple() @@ -593,16 +604,19 @@ impl<'a> Iterator for TupleVariationIter<'a> { /// A single set of tuple variation data #[derive(Clone)] -pub struct TupleVariation<'a> { - is_gvar: bool, +pub struct TupleVariation<'a, T> { axis_count: u16, header: TupleVariationHeader<'a>, shared_tuples: Option>, packed_deltas: PackedDeltas<'a>, point_numbers: PackedPointNumbers<'a>, + _marker: std::marker::PhantomData, } -impl<'a> TupleVariation<'a> { +impl<'a, T> TupleVariation<'a, T> +where + T: TupleDelta, +{ /// Returns true if this tuple provides deltas for all points in a glyph. pub fn has_deltas_for_all_points(&self) -> bool { self.point_numbers.count() == 0 @@ -677,63 +691,59 @@ impl<'a> TupleVariation<'a> { /// /// This does not account for scaling. Returns only explicitly encoded /// deltas, e.g. an omission by IUP will not be present. - pub fn deltas(&'a self) -> TupleDeltaIter<'a> { - TupleDeltaIter::new(&self.point_numbers, &self.packed_deltas, self.is_gvar) + pub fn deltas(&'a self) -> TupleDeltaIter<'a, T> { + TupleDeltaIter::new(&self.point_numbers, &self.packed_deltas) } } /// An iterator over the deltas for a glyph. #[derive(Clone, Debug)] -pub struct TupleDeltaIter<'a> { +pub struct TupleDeltaIter<'a, T> { pub cur: usize, // if None all points get deltas, if Some specifies subset of points that do points: Option>, next_point: usize, x_iter: DeltaRunIter<'a>, y_iter: Option>>, + _marker: std::marker::PhantomData, } -impl<'a> TupleDeltaIter<'a> { - fn new( - points: &'a PackedPointNumbers, - deltas: &'a PackedDeltas, - is_gvar: bool, - ) -> TupleDeltaIter<'a> { +impl<'a, T> TupleDeltaIter<'a, T> +where + T: TupleDelta, +{ + fn new(points: &'a PackedPointNumbers, deltas: &'a PackedDeltas) -> TupleDeltaIter<'a, T> { let mut points = points.iter(); let next_point = points.next(); let num_encoded_points = deltas.count() / 2; // x and y encoded independently - let y_iter = is_gvar.then(|| deltas.iter().skip(num_encoded_points)); + let y_iter = T::is_point().then(|| deltas.iter().skip(num_encoded_points)); TupleDeltaIter { cur: 0, points: next_point.map(|_| points), next_point: next_point.unwrap_or_default() as usize, x_iter: deltas.iter(), y_iter, + _marker: std::marker::PhantomData, } } } -/// Delta information for a single point or component in a glyph or for a -/// CVT entry. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct TupleDelta { - /// The point, component or CVT index. - pub position: u16, - /// The x delta - pub x_delta: i16, - /// The y delta - pub y_delta: i16, -} +/// Trait for deltas that are computed in a tuple variation store. +pub trait TupleDelta: Sized + Copy { + /// Returns true if the delta is a point and requires reading two values + /// from the packed delta stream. + fn is_point() -> bool; -impl TupleDelta { - /// Applies a tuple scalar to this delta. - pub fn apply_scalar(self, scalar: Fixed) -> Point { - Point::new(self.x_delta as i32, self.y_delta as i32).map(Fixed::from_i32) * scalar - } + /// Creates a new delta for the given position and coordinates. If + /// the delta is not a point, the y value will always be zero. + fn new(position: u16, x: i16, y: i16) -> Self; } -impl<'a> Iterator for TupleDeltaIter<'a> { - type Item = TupleDelta; +impl<'a, T> Iterator for TupleDeltaIter<'a, T> +where + T: TupleDelta, +{ + type Item = T; fn next(&mut self) -> Option { let (position, dx, dy) = loop { @@ -759,11 +769,7 @@ impl<'a> Iterator for TupleDeltaIter<'a> { self.cur += 1; }; self.cur += 1; - Some(TupleDelta { - position: position as u16, - x_delta: dx, - y_delta: dy, - }) + Some(T::new(position as u16, dx, dy)) } } diff --git a/skrifa/src/outline/glyf/deltas.rs b/skrifa/src/outline/glyf/deltas.rs index 8dd45670d..84f5cb3e7 100644 --- a/skrifa/src/outline/glyf/deltas.rs +++ b/skrifa/src/outline/glyf/deltas.rs @@ -2,7 +2,7 @@ use core::ops::RangeInclusive; use read_fonts::{ tables::glyf::{PointFlags, PointMarker}, - tables::gvar::Gvar, + tables::gvar::{GlyphDelta, Gvar}, tables::variations::TupleVariation, types::{F2Dot14, Fixed, GlyphId, Point}, ReadError, @@ -111,7 +111,7 @@ fn compute_deltas_for_glyph( deltas: &mut [Delta], mut apply_tuple_missing_deltas_fn: impl FnMut( Fixed, - TupleVariation, + TupleVariation, &mut [Point], ) -> Result<(), ReadError>, ) -> Result<(), ReadError> {