diff --git a/Cargo.toml b/Cargo.toml index 2bde283..334ee92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,4 @@ render = ["scale", "zeno/eval"] [dependencies] yazi = { version = "0.1.6", optional = true } zeno = { version = "0.2.2", optional = true, default_features = false } -read-fonts = { version = "0.19.0", default_features = false } - -[dev-dependencies] -font-test-data= { git = "https://github.com/googlefonts/fontations", rev = "10c27ef7bba1549fa37a3f41cd4870b2a24b1073" } -read-fonts = { version = "0.19.0", features = ["scaler_test"] } +skrifa = { version = "0.19.3" } diff --git a/src/internal/var.rs b/src/internal/var.rs index 6b792ea..c8dc0b0 100644 --- a/src/internal/var.rs +++ b/src/internal/var.rs @@ -1,6 +1,6 @@ //! Font and metric variation tables. -use read_fonts::{FontData, FontRead}; +use skrifa::raw::{FontData, FontRead}; use super::{fixed::Fixed, raw_tag, Array, Bytes, RawFont, RawTag, U24}; @@ -240,7 +240,7 @@ pub fn adjust_axis(data: &[u8], avar: u32, axis: u16, coord: Fixed) -> Option Option -const ICF_TOP: Fixed = Fixed::from_i32(880); -const ICF_BOTTOM: Fixed = Fixed::from_i32(-120); - -// -const MAX_BLUES: usize = 7; -const MAX_OTHER_BLUES: usize = 5; -const MAX_BLUE_ZONES: usize = MAX_BLUES + MAX_OTHER_BLUES; - -// -const MAX_HINTS: usize = 96; - -// One bit per stem hint -// -const HINT_MASK_SIZE: usize = (MAX_HINTS + 7) / 8; - -// Constant for hint adjustment and em box hint placement. -// -const MIN_COUNTER: Fixed = Fixed::from_bits(0x8000); - -// -const EPSILON: Fixed = Fixed::from_bits(1); - -/// Parameters used to generate the stem and counter zones for the hinting -/// algorithm. -#[derive(Clone)] -pub(crate) struct HintParams { - pub blues: Blues, - pub family_blues: Blues, - pub other_blues: Blues, - pub family_other_blues: Blues, - pub blue_scale: Fixed, - pub blue_shift: Fixed, - pub blue_fuzz: Fixed, - pub language_group: i32, -} - -impl Default for HintParams { - fn default() -> Self { - Self { - blues: Blues::default(), - other_blues: Blues::default(), - family_blues: Blues::default(), - family_other_blues: Blues::default(), - // See - blue_scale: Fixed::from_f64(0.039625), - blue_shift: Fixed::from_i32(7), - blue_fuzz: Fixed::ONE, - language_group: 0, - } - } -} - -/// See -#[derive(Copy, Clone, PartialEq, Default, Debug)] -struct BlueZone { - is_bottom: bool, - cs_bottom_edge: Fixed, - cs_top_edge: Fixed, - cs_flat_edge: Fixed, - ds_flat_edge: Fixed, -} - -/// Hinting state for a PostScript subfont. -/// -/// Note that hinter states depend on the scale, subfont index and -/// variation coordinates of a glyph. They can be retained and reused -/// if those values remain the same. -#[derive(Copy, Clone, PartialEq, Default)] -pub(crate) struct HintState { - scale: Fixed, - blue_scale: Fixed, - blue_shift: Fixed, - blue_fuzz: Fixed, - language_group: i32, - supress_overshoot: bool, - do_em_box_hints: bool, - boost: Fixed, - darken_y: Fixed, - zones: [BlueZone; MAX_BLUE_ZONES], - zone_count: usize, -} - -impl HintState { - pub fn new(params: &HintParams, scale: Fixed) -> Self { - let mut state = Self { - scale, - blue_scale: params.blue_scale, - blue_shift: params.blue_shift, - blue_fuzz: params.blue_fuzz, - language_group: params.language_group, - supress_overshoot: false, - do_em_box_hints: false, - boost: Fixed::ZERO, - darken_y: Fixed::ZERO, - zones: [BlueZone::default(); MAX_BLUE_ZONES], - zone_count: 0, - }; - state.build_zones(params); - state - } - - fn zones(&self) -> &[BlueZone] { - &self.zones[..self.zone_count] - } - - /// Initialize zones from the set of blues values. - /// - /// See - fn build_zones(&mut self, params: &HintParams) { - self.do_em_box_hints = false; - // - match (self.language_group, params.blues.values().len()) { - (1, 2) => { - let blues = params.blues.values(); - if blues[0].0 < ICF_BOTTOM - && blues[0].1 < ICF_BOTTOM - && blues[1].0 > ICF_TOP - && blues[1].1 > ICF_TOP - { - // FreeType generates synthetic hints here. We'll do it - // later when building the hint map. - self.do_em_box_hints = true; - return; - } - } - (1, 0) => { - self.do_em_box_hints = true; - return; - } - _ => {} - } - let mut zones = [BlueZone::default(); MAX_BLUE_ZONES]; - let mut max_zone_height = Fixed::ZERO; - let mut zone_ix = 0usize; - // Copy blues and other blues to a combined array of top and bottom zones. - for blue in params.blues.values().iter().take(MAX_BLUES) { - // FreeType loads blues as integers and then expands to 16.16 - // at initialization. We load them as 16.16 so floor them here - // to ensure we match. - // - let bottom = blue.0.floor(); - let top = blue.1.floor(); - let zone_height = top - bottom; - if zone_height < Fixed::ZERO { - // Reject zones with negative height - continue; - } - max_zone_height = max_zone_height.max(zone_height); - let zone = &mut zones[zone_ix]; - zone.cs_bottom_edge = bottom; - zone.cs_top_edge = top; - if zone_ix == 0 { - // First blue value is bottom zone - zone.is_bottom = true; - zone.cs_flat_edge = top; - } else { - // Remaining blue values are top zones - zone.is_bottom = false; - // Adjust both edges of top zone upward by twice darkening amount - zone.cs_top_edge += twice(self.darken_y); - zone.cs_bottom_edge += twice(self.darken_y); - zone.cs_flat_edge = zone.cs_bottom_edge; - } - zone_ix += 1; - } - for blue in params.other_blues.values().iter().take(MAX_OTHER_BLUES) { - let bottom = blue.0.floor(); - let top = blue.1.floor(); - let zone_height = top - bottom; - if zone_height < Fixed::ZERO { - // Reject zones with negative height - continue; - } - max_zone_height = max_zone_height.max(zone_height); - let zone = &mut zones[zone_ix]; - // All "other" blues are bottom zone - zone.is_bottom = true; - zone.cs_bottom_edge = bottom; - zone.cs_top_edge = top; - zone.cs_flat_edge = top; - zone_ix += 1; - } - // Adjust for family blues - let units_per_pixel = Fixed::ONE / self.scale; - for zone in &mut zones[..zone_ix] { - let flat = zone.cs_flat_edge; - let mut min_diff = Fixed::MAX; - if zone.is_bottom { - // In a bottom zone, the top edge is the flat edge. - // Search family other blues for bottom zones. Look for the - // closest edge that is within the one pixel threshold. - for blue in params.family_other_blues.values() { - let family_flat = blue.1; - let diff = (flat - family_flat).abs(); - if diff < min_diff && diff < units_per_pixel { - zone.cs_flat_edge = family_flat; - min_diff = diff; - if diff == Fixed::ZERO { - break; - } - } - } - // Check the first member of family blues, which is a bottom - // zone - if !params.family_blues.values().is_empty() { - let family_flat = params.family_blues.values()[0].1; - let diff = (flat - family_flat).abs(); - if diff < min_diff && diff < units_per_pixel { - zone.cs_flat_edge = family_flat; - } - } - } else { - // In a top zone, the bottom edge is the flat edge. - // Search family blues for top zones, skipping the first, which - // is a bottom zone. Look for closest family edge that is - // within the one pixel threshold. - for blue in params.family_blues.values().iter().skip(1) { - let family_flat = blue.0 + twice(self.darken_y); - let diff = (flat - family_flat).abs(); - if diff < min_diff && diff < units_per_pixel { - zone.cs_flat_edge = family_flat; - min_diff = diff; - if diff == Fixed::ZERO { - break; - } - } - } - } - } - if max_zone_height > Fixed::ZERO && self.blue_scale > (Fixed::ONE / max_zone_height) { - // Clamp at maximum scale - self.blue_scale = Fixed::ONE / max_zone_height; - } - // Suppress overshoot and boost blue zones at small sizes - if self.scale < self.blue_scale { - self.supress_overshoot = true; - self.boost = - Fixed::from_f64(0.6) - Fixed::from_f64(0.6).mul_div(self.scale, self.blue_scale); - // boost must remain less than 0.5, or baseline could go negative - self.boost = self.boost.min(Fixed::from_bits(0x7FFF)); - } - if self.darken_y != Fixed::ZERO { - self.boost = Fixed::ZERO; - } - // Set device space alignment for each zone; apply boost amount before - // rounding flat edge - let scale = self.scale; - let boost = self.boost; - for zone in &mut zones[..zone_ix] { - let boost = if zone.is_bottom { -boost } else { boost }; - zone.ds_flat_edge = (zone.cs_flat_edge * scale + boost).round(); - } - self.zones = zones; - self.zone_count = zone_ix; - } - - /// Check whether a hint is captured by one of the blue zones. - /// - /// See - fn capture(&self, bottom_edge: &mut Hint, top_edge: &mut Hint) -> bool { - let fuzz = self.blue_fuzz; - let mut captured = false; - let mut adjustment = Fixed::ZERO; - for zone in self.zones() { - if zone.is_bottom - && bottom_edge.is_bottom() - && (zone.cs_bottom_edge - fuzz) <= bottom_edge.cs_coord - && bottom_edge.cs_coord <= (zone.cs_top_edge + fuzz) - { - // Bottom edge captured by bottom zone. - adjustment = if self.supress_overshoot { - zone.ds_flat_edge - } else if zone.cs_top_edge - bottom_edge.cs_coord >= self.blue_shift { - // Guarantee minimum of 1 pixel overshoot - bottom_edge - .ds_coord - .round() - .min(zone.ds_flat_edge - Fixed::ONE) - } else { - bottom_edge.ds_coord.round() - }; - adjustment -= bottom_edge.ds_coord; - captured = true; - break; - } - if !zone.is_bottom - && top_edge.is_top() - && (zone.cs_bottom_edge - fuzz) <= top_edge.cs_coord - && top_edge.cs_coord <= (zone.cs_top_edge + fuzz) - { - // Top edge captured by top zone. - adjustment = if self.supress_overshoot { - zone.ds_flat_edge - } else if top_edge.cs_coord - zone.cs_bottom_edge >= self.blue_shift { - // Guarantee minimum of 1 pixel overshoot - top_edge - .ds_coord - .round() - .max(zone.ds_flat_edge + Fixed::ONE) - } else { - top_edge.ds_coord.round() - }; - adjustment -= top_edge.ds_coord; - captured = true; - break; - } - } - if captured { - // Move both edges and mark them as "locked" - if bottom_edge.is_valid() { - bottom_edge.ds_coord += adjustment; - bottom_edge.lock(); - } - if top_edge.is_valid() { - top_edge.ds_coord += adjustment; - top_edge.lock(); - } - } - captured - } -} - -/// -#[derive(Copy, Clone, Default)] -struct StemHint { - /// If true, device space position is valid - is_used: bool, - // Character space position - min: Fixed, - max: Fixed, - // Device space position after first use - ds_min: Fixed, - ds_max: Fixed, -} - -// Hint flags -const GHOST_BOTTOM: u8 = 0x1; -const GHOST_TOP: u8 = 0x2; -const PAIR_BOTTOM: u8 = 0x4; -const PAIR_TOP: u8 = 0x8; -const LOCKED: u8 = 0x10; -const SYNTHETIC: u8 = 0x20; - -/// -#[derive(Copy, Clone, PartialEq, Default, Debug)] -struct Hint { - flags: u8, - /// Index in original stem hint array (if not synthetic) - index: u8, - cs_coord: Fixed, - ds_coord: Fixed, - scale: Fixed, -} - -impl Hint { - fn is_valid(&self) -> bool { - self.flags != 0 - } - - fn is_bottom(&self) -> bool { - self.flags & (GHOST_BOTTOM | PAIR_BOTTOM) != 0 - } - - fn is_top(&self) -> bool { - self.flags & (GHOST_TOP | PAIR_TOP) != 0 - } - - fn is_pair(&self) -> bool { - self.flags & (PAIR_BOTTOM | PAIR_TOP) != 0 - } - - fn is_pair_top(&self) -> bool { - self.flags & PAIR_TOP != 0 - } - - fn is_locked(&self) -> bool { - self.flags & LOCKED != 0 - } - - fn is_synthetic(&self) -> bool { - self.flags & SYNTHETIC != 0 - } - - fn lock(&mut self) { - self.flags |= LOCKED - } - - /// Hint initialization from an incoming stem hint. - /// - /// See - fn setup( - &mut self, - stem: &StemHint, - index: u8, - origin: Fixed, - scale: Fixed, - darken_y: Fixed, - is_bottom: bool, - ) { - // "Ghost hints" are used to align a single edge rather than a - // stem-- think the top and bottom edges of an uppercase - // sans-serif I. - // These are encoded internally with stem hints of width -21 - // and -20 for bottom and top hints, respectively. - const GHOST_BOTTOM_WIDTH: Fixed = Fixed::from_i32(-21); - const GHOST_TOP_WIDTH: Fixed = Fixed::from_i32(-20); - let width = stem.max - stem.min; - if width == GHOST_BOTTOM_WIDTH { - if is_bottom { - self.cs_coord = stem.max; - self.flags = GHOST_BOTTOM; - } else { - self.flags = 0; - } - } else if width == GHOST_TOP_WIDTH { - if !is_bottom { - self.cs_coord = stem.min; - self.flags = GHOST_TOP; - } else { - self.flags = 0; - } - } else if width < Fixed::ZERO { - // If width < 0, this is an inverted pair. We follow FreeType and - // swap the coordinates - if is_bottom { - self.cs_coord = stem.max; - self.flags = PAIR_BOTTOM; - } else { - self.cs_coord = stem.min; - self.flags = PAIR_TOP; - } - } else { - // This is a normal pair - if is_bottom { - self.cs_coord = stem.min; - self.flags = PAIR_BOTTOM; - } else { - self.cs_coord = stem.max; - self.flags = PAIR_TOP; - } - } - if self.is_top() { - // For top hints, adjust character space position up by twice the - // darkening amount - self.cs_coord += twice(darken_y); - } - self.cs_coord += origin; - self.scale = scale; - self.index = index; - // If original stem hint was used, copy the position - if self.flags != 0 && stem.is_used { - if self.is_top() { - self.ds_coord = stem.ds_max; - } else { - self.ds_coord = stem.ds_min; - } - self.lock(); - } else { - self.ds_coord = self.cs_coord * scale; - } - } -} - -/// Collection of adjusted hint edges. -/// -/// -#[derive(Copy, Clone)] -struct HintMap { - edges: [Hint; MAX_HINTS], - len: usize, - is_valid: bool, - scale: Fixed, -} - -impl HintMap { - fn new(scale: Fixed) -> Self { - Self { - edges: [Hint::default(); MAX_HINTS], - len: 0, - is_valid: false, - scale, - } - } - - fn clear(&mut self) { - self.len = 0; - self.is_valid = false; - } - - /// Transform character space coordinate to device space. - /// - /// Based on - fn transform(&self, coord: Fixed) -> Fixed { - if self.len == 0 { - return coord * self.scale; - } - let limit = self.len - 1; - let mut i = 0; - while i < limit && coord >= self.edges[i + 1].cs_coord { - i += 1; - } - while i > 0 && coord < self.edges[i].cs_coord { - i -= 1; - } - let first_edge = &self.edges[0]; - if i == 0 && coord < first_edge.cs_coord { - // Special case for points below first edge: use uniform scale - ((coord - first_edge.cs_coord) * self.scale) + first_edge.ds_coord - } else { - // Use highest edge where cs_coord >= edge.cs_coord - let edge = &self.edges[i]; - ((coord - edge.cs_coord) * edge.scale) + edge.ds_coord - } - } - - /// Insert hint edges into map, sorted by character space coordinate. - /// - /// Based on - fn insert(&mut self, bottom: &Hint, top: &Hint, initial: Option<&HintMap>) { - let (is_pair, mut first_edge) = if !bottom.is_valid() { - // Bottom is invalid: insert only top edge - (false, *top) - } else if !top.is_valid() { - // Top is invalid: insert only bottom edge - (false, *bottom) - } else { - // We have a valid pair! - (true, *bottom) - }; - let mut second_edge = *top; - if is_pair && top.cs_coord < bottom.cs_coord { - // Paired edges must be in proper order. FT just ignores the hint. - return; - } - let edge_count = if is_pair { 2 } else { 1 }; - if self.len + edge_count > MAX_HINTS { - // Won't fit. Again, ignore. - return; - } - // Find insertion index that keeps the edge list sorted - let mut insert_ix = 0; - while insert_ix < self.len { - if self.edges[insert_ix].cs_coord >= first_edge.cs_coord { - break; - } - insert_ix += 1; - } - // Discard hints that overlap in character space - if insert_ix < self.len { - let current = &self.edges[insert_ix]; - // Existing edge is the same - if (current.cs_coord == first_edge.cs_coord) - // Pair straddles the next edge - || (is_pair && current.cs_coord <= second_edge.cs_coord) - // Inserting between paired edges - || current.is_pair_top() - { - return; - } - } - // Recompute device space locations using initial hint map - if !first_edge.is_locked() { - if let Some(initial) = initial { - if is_pair { - // Preserve stem width: position center of stem with - // initial hint map and two edges with nominal scale - let mid = initial.transform( - first_edge.cs_coord + half(second_edge.cs_coord - first_edge.cs_coord), - ); - let half_width = half(second_edge.cs_coord - first_edge.cs_coord) * self.scale; - first_edge.ds_coord = mid - half_width; - second_edge.ds_coord = mid + half_width; - } else { - first_edge.ds_coord = initial.transform(first_edge.cs_coord); - } - } - } - // Now discard hints that overlap in device space: - if insert_ix > 0 && first_edge.ds_coord < self.edges[insert_ix - 1].ds_coord { - // Inserting after an existing edge - return; - } - if insert_ix < self.len - && ((is_pair && second_edge.ds_coord > self.edges[insert_ix].ds_coord) - || first_edge.ds_coord > self.edges[insert_ix].ds_coord) - { - // Inserting before an existing edge - return; - } - // If we're inserting in the middle, make room in the edge array - if insert_ix != self.len { - let mut src_index = self.len - 1; - let mut dst_index = self.len + edge_count - 1; - loop { - self.edges[dst_index] = self.edges[src_index]; - if src_index == insert_ix { - break; - } - src_index -= 1; - dst_index -= 1; - } - } - self.edges[insert_ix] = first_edge; - if is_pair { - self.edges[insert_ix + 1] = second_edge; - } - self.len += edge_count; - } - - /// Adjust hint pairs so that one of the two edges is on a pixel boundary. - /// - /// Based on - fn adjust(&mut self) { - let mut saved = [(0usize, Fixed::ZERO); MAX_HINTS]; - let mut saved_count = 0usize; - let mut i = 0; - // From FT with adjustments for variable names: - // "First pass is bottom-up (font hint order) without look-ahead. - // Locked edges are already adjusted. - // Unlocked edges begin with ds_coord from `initial_map'. - // Save edges that are not optimally adjusted in `saved' array, - // and process them in second pass." - let limit = self.len; - while i < limit { - let is_pair = self.edges[i].is_pair(); - let j = if is_pair { i + 1 } else { i }; - if !self.edges[i].is_locked() { - // We can adjust hint edges that are not locked - let frac_down = self.edges[i].ds_coord.fract(); - let frac_up = self.edges[j].ds_coord.fract(); - // There are four possibilities. We compute them all. - // (moves down are negative) - let down_move_down = Fixed::ZERO - frac_down; - let up_move_down = Fixed::ZERO - frac_up; - let down_move_up = if frac_down == Fixed::ZERO { - Fixed::ZERO - } else { - Fixed::ONE - frac_down - }; - let up_move_up = if frac_up == Fixed::ZERO { - Fixed::ZERO - } else { - Fixed::ONE - frac_up - }; - // Smallest move up - let move_up = down_move_up.min(up_move_up); - // Smallest move down - let move_down = down_move_down.max(up_move_down); - let mut save_edge = false; - let adjustment; - // Check for room to move up: - // 1. We're at the top of the array, or - // 2. The next edge is at or above the proposed move up - if j >= self.len - 1 - || self.edges[j + 1].ds_coord - >= (self.edges[j].ds_coord + move_up + MIN_COUNTER) - { - // Also check for room to move down... - if i == 0 - || self.edges[i - 1].ds_coord - <= (self.edges[i].ds_coord + move_down - MIN_COUNTER) - { - // .. and move the smallest distance - adjustment = if -move_down < move_up { - move_down - } else { - move_up - }; - } else { - adjustment = move_up; - } - } else if i == 0 - || self.edges[i - 1].ds_coord - <= (self.edges[i].ds_coord + move_down - MIN_COUNTER) - { - // We can move down - adjustment = move_down; - // True if the move is not optimum - save_edge = move_up < -move_down; - } else { - // We can't move either way without overlapping - adjustment = Fixed::ZERO; - save_edge = true; - } - // Capture non-optimal adjustments and save them for a second - // pass. This is only possible if the edge above is unlocked - // and can be moved. - if save_edge && j < self.len - 1 && !self.edges[j + 1].is_locked() { - // (index, desired adjustment) - saved[saved_count] = (j, move_up - adjustment); - saved_count += 1; - } - // Apply the adjustment - self.edges[i].ds_coord += adjustment; - if is_pair { - self.edges[j].ds_coord += adjustment; - } - } - // Compute the new edge scale - if i > 0 && self.edges[i].cs_coord != self.edges[i - 1].cs_coord { - let a = self.edges[i]; - let b = self.edges[i - 1]; - self.edges[i - 1].scale = (a.ds_coord - b.ds_coord) / (a.cs_coord - b.cs_coord); - } - if is_pair { - if self.edges[j].cs_coord != self.edges[j - 1].cs_coord { - let a = self.edges[j]; - let b = self.edges[j - 1]; - self.edges[j - 1].scale = (a.ds_coord - b.ds_coord) / (a.cs_coord - b.cs_coord); - } - i += 1; - } - i += 1; - } - // Second pass tries to move non-optimal edges up if the first - // pass created room - for (j, adjustment) in saved[..saved_count].iter().copied().rev() { - if self.edges[j + 1].ds_coord >= (self.edges[j].ds_coord + adjustment + MIN_COUNTER) { - self.edges[j].ds_coord += adjustment; - if self.edges[j].is_pair() { - self.edges[j - 1].ds_coord += adjustment; - } - } - } - } - - /// Builds a hintmap from hints and mask. - /// - /// If `initial_map` is invalid, this recurses one level to initialize - /// it. If `is_initial` is true, simply build the initial map. - /// - /// Based on - fn build( - &mut self, - state: &HintState, - mask: Option, - mut initial_map: Option<&mut HintMap>, - stems: &mut [StemHint], - origin: Fixed, - is_initial: bool, - ) { - let scale = state.scale; - let darken_y = Fixed::ZERO; - if !is_initial { - if let Some(initial_map) = &mut initial_map { - if !initial_map.is_valid { - // Note: recursive call here to build the initial map if it - // is provided and invalid - initial_map.build(state, Some(HintMask::all()), None, stems, origin, true); - } - } - } - let initial_map = initial_map.map(|x| x as &HintMap); - self.clear(); - // If the mask is missing or invalid, assume all hints are active - let mut mask = mask.unwrap_or_else(HintMask::all); - if !mask.is_valid { - mask = HintMask::all(); - } - if state.do_em_box_hints { - // FreeType generates these during blues initialization. Do - // it here just to avoid carrying the extra state in the - // already large HintState struct. - // - let mut bottom = Hint::default(); - bottom.cs_coord = ICF_BOTTOM - EPSILON; - bottom.ds_coord = (bottom.cs_coord * scale).round() - MIN_COUNTER; - bottom.scale = scale; - bottom.flags = GHOST_BOTTOM | LOCKED | SYNTHETIC; - let mut top = Hint::default(); - top.cs_coord = ICF_TOP + EPSILON + twice(state.darken_y); - top.ds_coord = (top.cs_coord * scale).round() + MIN_COUNTER; - top.scale = scale; - top.flags = GHOST_TOP | LOCKED | SYNTHETIC; - let invalid = Hint::default(); - self.insert(&bottom, &invalid, initial_map); - self.insert(&invalid, &top, initial_map); - } - let mut tmp_mask = mask; - // FreeType iterates over the hint mask with some fancy bit logic. We - // do the simpler thing and loop over the stems. - // - for (i, stem) in stems.iter().enumerate() { - if !tmp_mask.get(i) { - continue; - } - let hint_ix = i as u8; - let mut bottom = Hint::default(); - let mut top = Hint::default(); - bottom.setup(stem, hint_ix, origin, scale, darken_y, true); - top.setup(stem, hint_ix, origin, scale, darken_y, false); - // Insert hints that are locked or captured by a blue zone - if bottom.is_locked() || top.is_locked() || state.capture(&mut bottom, &mut top) { - if is_initial { - self.insert(&bottom, &top, None); - } else { - self.insert(&bottom, &top, initial_map); - } - // Avoid processing this hint in the second pass - tmp_mask.clear(i); - } - } - if is_initial { - // Heuristic: insert a point at (0, 0) if it's not covered by a - // mapping. Ensures a lock at baseline for glyphs missing a - // baseline hint. - if self.len == 0 - || self.edges[0].cs_coord > Fixed::ZERO - || self.edges[self.len - 1].cs_coord < Fixed::ZERO - { - let edge = Hint { - flags: GHOST_BOTTOM | LOCKED | SYNTHETIC, - scale, - ..Default::default() - }; - let invalid = Hint::default(); - self.insert(&edge, &invalid, None); - } - } else { - // Insert hints that were skipped in the first pass - for (i, stem) in stems.iter().enumerate() { - if !tmp_mask.get(i) { - continue; - } - let hint_ix = i as u8; - let mut bottom = Hint::default(); - let mut top = Hint::default(); - bottom.setup(stem, hint_ix, origin, scale, darken_y, true); - top.setup(stem, hint_ix, origin, scale, darken_y, false); - self.insert(&bottom, &top, initial_map); - } - } - // Adjust edges that are not locked to blue zones - self.adjust(); - if !is_initial { - // Save position of edges that were used by the hint map. - for edge in &self.edges[..self.len] { - if edge.is_synthetic() { - continue; - } - let stem = &mut stems[edge.index as usize]; - if edge.is_top() { - stem.ds_max = edge.ds_coord; - } else { - stem.ds_min = edge.ds_coord; - } - stem.is_used = true; - } - } - self.is_valid = true; - } -} - -/// Bitmask that specifies which hints are currently active. -/// -/// "Each bit of the mask, starting with the most-significant bit of -/// the first byte, represents the corresponding hint zone in the -/// order in which the hints were declared at the beginning of -/// the charstring." -/// -/// See -/// Also -#[derive(Copy, Clone, PartialEq, Default)] -struct HintMask { - mask: [u8; HINT_MASK_SIZE], - is_valid: bool, -} - -impl HintMask { - fn new(bytes: &[u8]) -> Option { - let len = bytes.len(); - if len > HINT_MASK_SIZE { - return None; - } - let mut mask = Self::default(); - mask.mask[..len].copy_from_slice(&bytes[..len]); - mask.is_valid = true; - Some(mask) - } - - fn all() -> Self { - Self { - mask: [0xFF; HINT_MASK_SIZE], - is_valid: true, - } - } - - fn clear(&mut self, bit: usize) { - self.mask[bit >> 3] &= !msb_mask(bit); - } - - fn get(&self, bit: usize) -> bool { - self.mask[bit >> 3] & msb_mask(bit) != 0 - } -} - -/// Returns a bit mask for the selected bit with the -/// most significant bit at index 0. -fn msb_mask(bit: usize) -> u8 { - 1 << (7 - (bit & 0x7)) -} - -pub(super) struct HintingSink<'a, S> { - state: &'a HintState, - sink: &'a mut S, - stem_hints: [StemHint; MAX_HINTS], - stem_count: u8, - mask: HintMask, - initial_map: HintMap, - map: HintMap, - /// Most recent move_to in character space. - start_point: Option<[Fixed; 2]>, - /// Most recent line_to. First two elements are coords in character - /// space and the last two are in device space. - pending_line: Option<[Fixed; 4]>, -} - -impl<'a, S: CommandSink> HintingSink<'a, S> { - pub fn new(state: &'a HintState, sink: &'a mut S) -> Self { - let scale = state.scale; - Self { - state, - sink, - stem_hints: [StemHint::default(); MAX_HINTS], - stem_count: 0, - mask: HintMask::all(), - initial_map: HintMap::new(scale), - map: HintMap::new(scale), - start_point: None, - pending_line: None, - } - } - - pub fn finish(&mut self) { - self.maybe_close_subpath(); - } - - fn maybe_close_subpath(&mut self) { - // This requires some explanation. The hint mask can be modified - // during charstring evaluation which changes the set of hints that - // are applied. FreeType ensures that the closing line for any subpath - // is transformed with the same hint map as the starting point for the - // subpath. This is done by stashing a copy of the hint map that is - // active when a new subpath is started. Unlike FreeType, we make use - // of close elements, so we can cheat a bit here and avoid the - // extra hintmap. If we're closing an open subpath and have a pending - // line and the line is not equal to the start point in character - // space, then we emit the saved device space coordinates for the - // line. If the coordinates do match in character space, we omit - // that line. The unconditional close command ensures that the - // start and end points coincide. - // Note: this doesn't apply to subpaths that end in cubics. - match (self.start_point.take(), self.pending_line.take()) { - (Some(start), Some([cs_x, cs_y, ds_x, ds_y])) => { - if start != [cs_x, cs_y] { - self.sink.line_to(ds_x, ds_y); - } - self.sink.close(); - } - (Some(_), _) => self.sink.close(), - _ => {} - } - } - - fn flush_pending_line(&mut self) { - if let Some([_, _, x, y]) = self.pending_line.take() { - self.sink.line_to(x, y); - } - } - - fn hint(&mut self, coord: Fixed) -> Fixed { - if !self.map.is_valid { - self.build_hint_map(Some(self.mask), Fixed::ZERO); - } - trunc(self.map.transform(coord)) - } - - fn scale(&self, coord: Fixed) -> Fixed { - trunc(coord * self.state.scale) - } - - fn add_stem(&mut self, min: Fixed, max: Fixed) { - let index = self.stem_count as usize; - if index >= MAX_HINTS || self.map.is_valid { - return; - } - let stem = &mut self.stem_hints[index]; - stem.min = min; - stem.max = max; - stem.is_used = false; - stem.ds_min = Fixed::ZERO; - stem.ds_max = Fixed::ZERO; - self.stem_count = index as u8 + 1; - } - - fn build_hint_map(&mut self, mask: Option, origin: Fixed) { - self.map.build( - self.state, - mask, - Some(&mut self.initial_map), - &mut self.stem_hints[..self.stem_count as usize], - origin, - false, - ); - } -} - -impl<'a, S: CommandSink> CommandSink for HintingSink<'a, S> { - fn hstem(&mut self, min: Fixed, max: Fixed) { - self.add_stem(min, max); - } - - fn hint_mask(&mut self, mask: &[u8]) { - // For invalid hint masks, FreeType assumes all hints are active. - // See - let mask = HintMask::new(mask).unwrap_or_else(HintMask::all); - if mask != self.mask { - self.mask = mask; - self.map.is_valid = false; - } - } - - fn counter_mask(&mut self, mask: &[u8]) { - // For counter masks, we build a temporary hint map "just to - // place and lock those stems participating in the counter - // mask." Building the map modifies the stem hint array as a - // side effect. - // See - let mask = HintMask::new(mask).unwrap_or_else(HintMask::all); - let mut map = HintMap::new(self.state.scale); - map.build( - self.state, - Some(mask), - Some(&mut self.initial_map), - &mut self.stem_hints[..self.stem_count as usize], - Fixed::ZERO, - false, - ); - } - - fn move_to(&mut self, x: Fixed, y: Fixed) { - self.maybe_close_subpath(); - self.start_point = Some([x, y]); - let x = self.scale(x); - let y = self.hint(y); - self.sink.move_to(x, y); - } - - fn line_to(&mut self, x: Fixed, y: Fixed) { - self.flush_pending_line(); - let ds_x = self.scale(x); - let ds_y = self.hint(y); - self.pending_line = Some([x, y, ds_x, ds_y]); - } - - fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) { - self.flush_pending_line(); - let cx1 = self.scale(cx1); - let cy1 = self.hint(cy1); - let cx2 = self.scale(cx2); - let cy2 = self.hint(cy2); - let x = self.scale(x); - let y = self.hint(y); - self.sink.curve_to(cx1, cy1, cx2, cy2, x, y); - } - - fn close(&mut self) { - // We emit close commands based on the sequence of moves. - // See `maybe_close_subpath` - } -} - -/// FreeType converts from 16.16 to 26.6 by truncation. We keep our -/// values in 16.16 so simply zero the low 10 bits to match the -/// precision when converting to f32. -fn trunc(value: Fixed) -> Fixed { - Fixed::from_bits(value.to_bits() & !0x3FF) -} - -fn half(value: Fixed) -> Fixed { - Fixed::from_bits(value.to_bits() / 2) -} - -fn twice(value: Fixed) -> Fixed { - Fixed::from_bits(value.to_bits().wrapping_mul(2)) -} - -#[cfg(test)] -mod tests { - use read_fonts::{tables::postscript::charstring::CommandSink, types::F2Dot14, FontRef}; - - use super::{ - BlueZone, Blues, Fixed, Hint, HintMap, HintMask, HintParams, HintState, HintingSink, - StemHint, GHOST_BOTTOM, GHOST_TOP, HINT_MASK_SIZE, LOCKED, PAIR_BOTTOM, PAIR_TOP, - }; - - fn make_hint_state() -> HintState { - fn make_blues(values: &[f64]) -> Blues { - Blues::new(values.iter().copied().map(Fixed::from_f64)) - } - // - // - // - // - // - let params = HintParams { - blues: make_blues(&[ - -15.0, 0.0, 536.0, 547.0, 571.0, 582.0, 714.0, 726.0, 760.0, 772.0, - ]), - other_blues: make_blues(&[-255.0, -240.0]), - blue_scale: Fixed::from_f64(0.05), - blue_shift: Fixed::from_i32(7), - blue_fuzz: Fixed::ZERO, - ..Default::default() - }; - HintState::new(¶ms, Fixed::ONE / Fixed::from_i32(64)) - } - - #[test] - fn scaled_blue_zones() { - let state = make_hint_state(); - assert!(!state.do_em_box_hints); - assert_eq!(state.zone_count, 6); - assert_eq!(state.boost, Fixed::from_bits(27035)); - assert!(state.supress_overshoot); - // FreeType generates the following zones: - let expected_zones = &[ - // csBottomEdge -983040 int - // csTopEdge 0 int - // csFlatEdge 0 int - // dsFlatEdge 0 int - // bottomZone 1 '\x1' unsigned char - BlueZone { - cs_bottom_edge: Fixed::from_bits(-983040), - is_bottom: true, - ..Default::default() - }, - // csBottomEdge 35127296 int - // csTopEdge 35848192 int - // csFlatEdge 35127296 int - // dsFlatEdge 589824 int - // bottomZone 0 '\0' unsigned char - BlueZone { - cs_bottom_edge: Fixed::from_bits(35127296), - cs_top_edge: Fixed::from_bits(35848192), - cs_flat_edge: Fixed::from_bits(35127296), - ds_flat_edge: Fixed::from_bits(589824), - is_bottom: false, - }, - // csBottomEdge 37421056 int - // csTopEdge 38141952 int - // csFlatEdge 37421056 int - // dsFlatEdge 589824 int - // bottomZone 0 '\0' unsigned char - BlueZone { - cs_bottom_edge: Fixed::from_bits(37421056), - cs_top_edge: Fixed::from_bits(38141952), - cs_flat_edge: Fixed::from_bits(37421056), - ds_flat_edge: Fixed::from_bits(589824), - is_bottom: false, - }, - // csBottomEdge 46792704 int - // csTopEdge 47579136 int - // csFlatEdge 46792704 int - // dsFlatEdge 786432 int - // bottomZone 0 '\0' unsigned char - BlueZone { - cs_bottom_edge: Fixed::from_bits(46792704), - cs_top_edge: Fixed::from_bits(47579136), - cs_flat_edge: Fixed::from_bits(46792704), - ds_flat_edge: Fixed::from_bits(786432), - is_bottom: false, - }, - // csBottomEdge 49807360 int - // csTopEdge 50593792 int - // csFlatEdge 49807360 int - // dsFlatEdge 786432 int - // bottomZone 0 '\0' unsigned char - BlueZone { - cs_bottom_edge: Fixed::from_bits(49807360), - cs_top_edge: Fixed::from_bits(50593792), - cs_flat_edge: Fixed::from_bits(49807360), - ds_flat_edge: Fixed::from_bits(786432), - is_bottom: false, - }, - // csBottomEdge -16711680 int - // csTopEdge -15728640 int - // csFlatEdge -15728640 int - // dsFlatEdge -262144 int - // bottomZone 1 '\x1' unsigned char - BlueZone { - cs_bottom_edge: Fixed::from_bits(-16711680), - cs_top_edge: Fixed::from_bits(-15728640), - cs_flat_edge: Fixed::from_bits(-15728640), - ds_flat_edge: Fixed::from_bits(-262144), - is_bottom: true, - }, - ]; - assert_eq!(state.zones(), expected_zones); - } - - #[test] - fn blue_zone_capture() { - let state = make_hint_state(); - let bottom_edge = Hint { - flags: PAIR_BOTTOM, - ds_coord: Fixed::from_f64(2.3), - ..Default::default() - }; - let top_edge = Hint { - flags: PAIR_TOP, - // This value chosen to fit within the first "top" blue zone - cs_coord: Fixed::from_bits(35127297), - ds_coord: Fixed::from_f64(2.3), - ..Default::default() - }; - // Capture both - { - let (mut bottom_edge, mut top_edge) = (bottom_edge, top_edge); - assert!(state.capture(&mut bottom_edge, &mut top_edge)); - assert!(bottom_edge.is_locked()); - assert!(top_edge.is_locked()); - } - // Capture none - { - // Used to guarantee the edges are below all blue zones and will - // not be captured - let min_cs_coord = Fixed::MIN; - let mut bottom_edge = Hint { - cs_coord: min_cs_coord, - ..bottom_edge - }; - let mut top_edge = Hint { - cs_coord: min_cs_coord, - ..top_edge - }; - assert!(!state.capture(&mut bottom_edge, &mut top_edge)); - assert!(!bottom_edge.is_locked()); - assert!(!top_edge.is_locked()); - } - // Capture bottom, ignore invalid top - { - let mut bottom_edge = bottom_edge; - let mut top_edge = Hint { - // Empty flags == invalid hint - flags: 0, - ..top_edge - }; - assert!(state.capture(&mut bottom_edge, &mut top_edge)); - assert!(bottom_edge.is_locked()); - assert!(!top_edge.is_locked()); - } - // Capture top, ignore invalid bottom - { - let mut bottom_edge = Hint { - // Empty flags == invalid hint - flags: 0, - ..bottom_edge - }; - let mut top_edge = top_edge; - assert!(state.capture(&mut bottom_edge, &mut top_edge)); - assert!(!bottom_edge.is_locked()); - assert!(top_edge.is_locked()); - } - } - - #[test] - fn hint_mask_ops() { - const MAX_BITS: usize = HINT_MASK_SIZE * 8; - let all_bits = HintMask::all(); - for i in 0..MAX_BITS { - assert!(all_bits.get(i)); - } - let odd_bits = HintMask::new(&[0b01010101; HINT_MASK_SIZE]).unwrap(); - for i in 0..MAX_BITS { - assert_eq!(i & 1 != 0, odd_bits.get(i)); - } - let mut cleared_bits = odd_bits; - for i in 0..MAX_BITS { - if i & 1 != 0 { - cleared_bits.clear(i); - } - } - assert_eq!(cleared_bits.mask, HintMask::default().mask); - } - - #[test] - fn hint_mapping() { - let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap(); - let cff_font = super::super::outlines::Outlines::new(&font).unwrap(); - let state = cff_font - .subfont(0, 8.0, &[F2Dot14::from_f32(-1.0); 2]) - .unwrap() - .hint_state; - let mut initial_map = HintMap::new(state.scale); - let mut map = HintMap::new(state.scale); - // Stem hints from Cantarell-VF.otf glyph id 2 - let mut stems = [ - StemHint { - min: Fixed::from_bits(1376256), - max: Fixed::ZERO, - ..Default::default() - }, - StemHint { - min: Fixed::from_bits(16318464), - max: Fixed::from_bits(17563648), - ..Default::default() - }, - StemHint { - min: Fixed::from_bits(45481984), - max: Fixed::from_bits(44171264), - ..Default::default() - }, - ]; - map.build( - &state, - Some(HintMask::all()), - Some(&mut initial_map), - &mut stems, - Fixed::ZERO, - false, - ); - // FT generates the following hint map: - // - // index csCoord dsCoord scale flags - // 0 0.00 0.00 526 gbL - // 1 249.00 250.14 524 pb - // 1 268.00 238.22 592 pt - // 2 694.00 750.41 524 gtL - let expected_edges = [ - Hint { - index: 0, - cs_coord: Fixed::from_f64(0.0), - ds_coord: Fixed::from_f64(0.0), - scale: Fixed::from_bits(526), - flags: GHOST_BOTTOM | LOCKED, - }, - Hint { - index: 1, - cs_coord: Fixed::from_bits(16318464), - ds_coord: Fixed::from_bits(131072), - scale: Fixed::from_bits(524), - flags: PAIR_BOTTOM, - }, - Hint { - index: 1, - cs_coord: Fixed::from_bits(17563648), - ds_coord: Fixed::from_bits(141028), - scale: Fixed::from_bits(592), - flags: PAIR_TOP, - }, - Hint { - index: 2, - cs_coord: Fixed::from_bits(45481984), - ds_coord: Fixed::from_bits(393216), - scale: Fixed::from_bits(524), - flags: GHOST_TOP | LOCKED, - }, - ]; - assert_eq!(expected_edges, &map.edges[..map.len]); - // And FT generates the following mappings - let mappings = [ - // (coord in font units, expected hinted coord in device space) in 16.16 - (0, 0), // 0 -> 0 - (44302336, 382564), // 676 -> 5.828125 - (45481984, 393216), // 694 -> 6 - (16318464, 131072), // 249 -> 2 - (17563648, 141028), // 268 -> 2.140625 - (49676288, 426752), // 758 -> 6.5 - (56754176, 483344), // 866 -> 7.375 - (57868288, 492252), // 883 -> 7.5 - (50069504, 429896), // 764 -> 6.546875 - ]; - for (coord, expected) in mappings { - assert_eq!( - map.transform(Fixed::from_bits(coord)), - Fixed::from_bits(expected) - ); - } - } - - /// HintingSink is mostly pass-through. This test captures the logic - /// around omission of pending lines that match subpath start. - /// See HintingSink::maybe_close_subpath for details. - #[test] - fn hinting_sink_omits_closing_line_that_matches_start() { - let state = HintState { - scale: Fixed::ONE, - ..Default::default() - }; - let mut path = Path::default(); - let mut sink = HintingSink::new(&state, &mut path); - let move1_2 = [Fixed::from_f64(1.0), Fixed::from_f64(2.0)]; - let line2_3 = [Fixed::from_f64(2.0), Fixed::from_f64(3.0)]; - let line1_2 = [Fixed::from_f64(1.0), Fixed::from_f64(2.0)]; - let line3_4 = [Fixed::from_f64(3.0), Fixed::from_f64(4.0)]; - let curve = [ - Fixed::from_f64(3.0), - Fixed::from_f64(4.0), - Fixed::from_f64(5.0), - Fixed::from_f64(6.0), - Fixed::from_f64(1.0), - Fixed::from_f64(2.0), - ]; - // First subpath, closing line matches start - sink.move_to(move1_2[0], move1_2[1]); - sink.line_to(line2_3[0], line2_3[1]); - sink.line_to(line1_2[0], line1_2[1]); - // Second subpath, closing line does not match start - sink.move_to(move1_2[0], move1_2[1]); - sink.line_to(line2_3[0], line2_3[1]); - sink.line_to(line3_4[0], line3_4[1]); - // Third subpath, ends with cubic. Still emits a close command - // even though end point matches start. - sink.move_to(move1_2[0], move1_2[1]); - sink.line_to(line2_3[0], line2_3[1]); - sink.curve_to(curve[0], curve[1], curve[2], curve[3], curve[4], curve[5]); - sink.finish(); - // Subpaths always end with a close command. If a final line coincides - // with the start of a subpath, it is omitted. - assert_eq!( - &path.0, - &[ - // First subpath - MoveTo(move1_2), - LineTo(line2_3), - // line1_2 is omitted - Close, - // Second subpath - MoveTo(move1_2), - LineTo(line2_3), - LineTo(line3_4), - Close, - // Third subpath - MoveTo(move1_2), - LineTo(line2_3), - CurveTo(curve), - Close, - ] - ); - } - - #[derive(Copy, Clone, PartialEq, Debug)] - enum Command { - MoveTo([Fixed; 2]), - LineTo([Fixed; 2]), - CurveTo([Fixed; 6]), - Close, - } - - use Command::*; - - #[derive(Default)] - struct Path(Vec); - - impl CommandSink for Path { - fn move_to(&mut self, x: Fixed, y: Fixed) { - self.0.push(MoveTo([x, y])); - } - fn line_to(&mut self, x: Fixed, y: Fixed) { - self.0.push(LineTo([x, y])); - } - fn curve_to(&mut self, cx0: Fixed, cy0: Fixed, cx1: Fixed, cy1: Fixed, x: Fixed, y: Fixed) { - self.0.push(CurveTo([cx0, cy0, cx1, cy1, x, y])); - } - fn close(&mut self) { - self.0.push(Close); - } - } -} diff --git a/src/scale/cff/mod.rs b/src/scale/cff/mod.rs deleted file mode 100644 index 0ffbbae..0000000 --- a/src/scale/cff/mod.rs +++ /dev/null @@ -1,137 +0,0 @@ -mod hint; -mod outlines; - -pub(crate) use outlines::Outlines; - -use super::Outline; -use read_fonts::types::{F2Dot14, GlyphId}; - -pub struct SubfontCache { - entries: Vec, - max_entries: usize, - epoch: u64, -} - -impl SubfontCache { - pub fn new(max_entries: usize) -> Self { - Self { - entries: Vec::new(), - max_entries, - epoch: 0, - } - } - - pub fn scale( - &mut self, - outlines: &outlines::Outlines, - id: [u64; 2], - glyph_id: u16, - size: f32, - coords: &[i16], - hint: bool, - outline: &mut Outline, - ) -> Option<()> { - let epoch = self.epoch; - let gid = GlyphId::new(glyph_id); - let subfont_index = outlines.subfont_index(gid); - let (found, entry_index) = self.find_entry(id, subfont_index, coords, size); - let (subfont, coords) = if found { - let entry = &mut self.entries[entry_index]; - entry.epoch = epoch; - (&entry.subfont, &entry.coords) - } else { - self.epoch += 1; - let epoch = self.epoch; - if entry_index == self.entries.len() { - let coords: Vec = coords.iter().map(|x| F2Dot14::from_bits(*x)).collect(); - let subfont = outlines.subfont(subfont_index, size, &coords).ok()?; - self.entries.push(Entry { - id, - epoch, - subfont, - subfont_index, - size, - coords, - }); - let entry = &self.entries[entry_index]; - (&entry.subfont, &entry.coords) - } else { - let entry = &mut self.entries[entry_index]; - entry.id = [u64::MAX; 2]; - entry.epoch = epoch; - entry.coords.clear(); - entry - .coords - .extend(coords.iter().map(|x| F2Dot14::from_bits(*x))); - entry.subfont = outlines.subfont(subfont_index, size, &entry.coords).ok()?; - entry.id = id; - entry.subfont_index = subfont_index; - entry.size = size; - (&entry.subfont, &entry.coords) - } - }; - outlines - .draw(subfont, gid, coords, hint, &mut OutlineBuilder(outline)) - .ok()?; - Some(()) - } - - fn find_entry(&self, id: [u64; 2], index: u32, coords: &[i16], size: f32) -> (bool, usize) { - let mut lowest_epoch = self.epoch; - let mut lowest_index = 0; - for (i, entry) in self.entries.iter().enumerate() { - if entry.id == id - && entry.subfont_index == index - && entry.size == size - && coords - .iter() - .map(|x| F2Dot14::from_bits(*x)) - .eq(entry.coords.iter().copied()) - { - return (true, i); - } - if entry.epoch < lowest_epoch { - lowest_epoch = entry.epoch; - lowest_index = i; - } - } - if self.entries.len() < self.max_entries { - lowest_index = self.entries.len(); - } - (false, lowest_index) - } -} - -struct Entry { - epoch: u64, - id: [u64; 2], - subfont: outlines::Subfont, - subfont_index: u32, - size: f32, - coords: Vec, -} - -struct OutlineBuilder<'a>(&'a mut Outline); - -impl read_fonts::types::Pen for OutlineBuilder<'_> { - fn move_to(&mut self, x: f32, y: f32) { - self.0.move_to((x, y).into()); - } - - fn line_to(&mut self, x: f32, y: f32) { - self.0.line_to((x, y).into()); - } - - fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) { - self.0.quad_to((cx0, cy0).into(), (x, y).into()); - } - - fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) { - self.0 - .curve_to((cx0, cy0).into(), (cx1, cy1).into(), (x, y).into()); - } - - fn close(&mut self) { - self.0.close(); - } -} diff --git a/src/scale/cff/outlines.rs b/src/scale/cff/outlines.rs deleted file mode 100644 index 63b18c1..0000000 --- a/src/scale/cff/outlines.rs +++ /dev/null @@ -1,709 +0,0 @@ -//! Support for scaling CFF outlines. - -use std::ops::Range; - -use read_fonts::{ - tables::{ - cff::Cff, - cff2::Cff2, - postscript::{ - charstring::{self, CommandSink}, - dict, BlendState, Error, FdSelect, Index, - }, - variations::ItemVariationStore, - }, - types::{F2Dot14, Fixed, GlyphId, Pen}, - FontData, FontRead, TableProvider, -}; - -use super::hint::{HintParams, HintState, HintingSink}; - -/// Type for loading, scaling and hinting outlines in CFF/CFF2 tables. -/// -/// The skrifa crate provides a higher level interface for this that handles -/// caching and abstracting over the different outline formats. Consider using -/// that if detailed control over resources is not required. -/// -/// # Subfonts -/// -/// CFF tables can contain multiple logical "subfonts" which determine the -/// state required for processing some subset of glyphs. This state is -/// accessed using the [`FDArray and FDSelect`](https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf#page=28) -/// operators to select an appropriate subfont for any given glyph identifier. -/// This process is exposed on this type with the -/// [`subfont_index`](Self::subfont_index) method to retrieve the subfont -/// index for the requested glyph followed by using the -/// [`subfont`](Self::subfont) method to create an appropriately configured -/// subfont for that glyph. -#[derive(Clone)] -pub struct Outlines<'a> { - version: Version<'a>, - top_dict: TopDict<'a>, - units_per_em: u16, -} - -impl<'a> Outlines<'a> { - /// Creates a new scaler for the given font. - /// - /// This will choose an underyling CFF2 or CFF table from the font, in that - /// order. - pub fn new(font: &impl TableProvider<'a>) -> Result { - let units_per_em = font.head()?.units_per_em(); - if let Ok(cff2) = font.cff2() { - Self::from_cff2(cff2, units_per_em) - } else { - // "The Name INDEX in the CFF data must contain only one entry; - // that is, there must be only one font in the CFF FontSet" - // So we always pass 0 for Top DICT index when reading from an - // OpenType font. - // - Self::from_cff(font.cff()?, 0, units_per_em) - } - } - - pub fn from_cff( - cff1: Cff<'a>, - top_dict_index: usize, - units_per_em: u16, - ) -> Result { - let top_dict_data = cff1.top_dicts().get(top_dict_index)?; - let top_dict = TopDict::new(cff1.offset_data().as_bytes(), top_dict_data, false)?; - Ok(Self { - version: Version::Version1(cff1), - top_dict, - units_per_em, - }) - } - - pub fn from_cff2(cff2: Cff2<'a>, units_per_em: u16) -> Result { - let table_data = cff2.offset_data().as_bytes(); - let top_dict = TopDict::new(table_data, cff2.top_dict_data(), true)?; - Ok(Self { - version: Version::Version2(cff2), - top_dict, - units_per_em, - }) - } - - pub fn is_cff2(&self) -> bool { - matches!(self.version, Version::Version2(_)) - } - - /// Returns the number of available glyphs. - pub fn glyph_count(&self) -> usize { - self.top_dict - .charstrings - .as_ref() - .map(|cs| cs.count() as usize) - .unwrap_or_default() - } - - /// Returns the number of available subfonts. - pub fn subfont_count(&self) -> u32 { - self.top_dict - .font_dicts - .as_ref() - .map(|font_dicts| font_dicts.count()) - // All CFF fonts have at least one logical subfont. - .unwrap_or(1) - } - - /// Returns the subfont (or Font DICT) index for the given glyph - /// identifier. - pub fn subfont_index(&self, glyph_id: GlyphId) -> u32 { - // For CFF tables, an FDSelect index will be present for CID-keyed - // fonts. Otherwise, the Top DICT will contain an entry for the - // "global" Private DICT. - // See - // - // CFF2 tables always contain a Font DICT and an FDSelect is only - // present if the size of the DICT is greater than 1. - // See - // - // In both cases, we return a subfont index of 0 when FDSelect is missing. - self.top_dict - .fd_select - .as_ref() - .and_then(|select| select.font_index(glyph_id)) - .unwrap_or(0) as u32 - } - - /// Creates a new subfont for the given index, size, normalized - /// variation coordinates and hinting state. - /// - /// The index of a subfont for a particular glyph can be retrieved with - /// the [`subfont_index`](Self::subfont_index) method. - pub fn subfont(&self, index: u32, size: f32, coords: &[F2Dot14]) -> Result { - let private_dict_range = self.private_dict_range(index)?; - let private_dict_data = self.offset_data().read_array(private_dict_range.clone())?; - let mut hint_params = HintParams::default(); - let mut subrs_offset = None; - let mut store_index = 0; - let blend_state = self - .top_dict - .var_store - .clone() - .map(|store| BlendState::new(store, coords, store_index)) - .transpose()?; - for entry in dict::entries(private_dict_data, blend_state) { - use dict::Entry::*; - match entry? { - BlueValues(values) => hint_params.blues = values, - FamilyBlues(values) => hint_params.family_blues = values, - OtherBlues(values) => hint_params.other_blues = values, - FamilyOtherBlues(values) => hint_params.family_blues = values, - BlueScale(value) => hint_params.blue_scale = value, - BlueShift(value) => hint_params.blue_shift = value, - BlueFuzz(value) => hint_params.blue_fuzz = value, - LanguageGroup(group) => hint_params.language_group = group, - // Subrs offset is relative to the private DICT - SubrsOffset(offset) => subrs_offset = Some(private_dict_range.start + offset), - VariationStoreIndex(index) => store_index = index, - _ => {} - } - } - let scale = if size <= 0.0 { - Fixed::ONE - } else { - // Note: we do an intermediate scale to 26.6 to ensure we - // match FreeType - Fixed::from_bits((size * 64.) as i32) / Fixed::from_bits(self.units_per_em as i32) - }; - // When hinting, use a modified scale factor - // - let hint_scale = Fixed::from_bits((scale.to_bits() + 32) / 64); - let hint_state = HintState::new(&hint_params, hint_scale); - Ok(Subfont { - is_cff2: self.is_cff2(), - scale, - subrs_offset, - hint_state, - store_index, - }) - } - - /// Loads and scales an outline for the given subfont instance, glyph - /// identifier and normalized variation coordinates. - /// - /// Before calling this method, use [`subfont_index`](Self::subfont_index) - /// to retrieve the subfont index for the desired glyph and then - /// [`subfont`](Self::subfont) to create an instance of the subfont for a - /// particular size and location in variation space. - /// Creating subfont instances is not free, so this process is exposed in - /// discrete steps to allow for caching. - /// - /// The result is emitted to the specified pen. - pub fn draw( - &self, - subfont: &Subfont, - glyph_id: GlyphId, - coords: &[F2Dot14], - hint: bool, - pen: &mut impl Pen, - ) -> Result<(), Error> { - let charstring_data = self - .top_dict - .charstrings - .as_ref() - .ok_or(Error::MissingCharstrings)? - .get(glyph_id.to_u16() as usize)?; - let subrs = subfont.subrs(self)?; - let blend_state = subfont.blend_state(self, coords)?; - let mut pen_sink = charstring::PenSink::new(pen); - let mut simplifying_adapter = NopFilteringSink::new(&mut pen_sink); - if hint { - let mut hinting_adapter = - HintingSink::new(&subfont.hint_state, &mut simplifying_adapter); - charstring::evaluate( - charstring_data, - self.global_subrs(), - subrs, - blend_state, - &mut hinting_adapter, - )?; - hinting_adapter.finish(); - } else { - let mut scaling_adapter = - ScalingSink26Dot6::new(&mut simplifying_adapter, subfont.scale); - charstring::evaluate( - charstring_data, - self.global_subrs(), - subrs, - blend_state, - &mut scaling_adapter, - )?; - } - simplifying_adapter.finish(); - Ok(()) - } - - fn offset_data(&self) -> FontData<'a> { - match &self.version { - Version::Version1(cff1) => cff1.offset_data(), - Version::Version2(cff2) => cff2.offset_data(), - } - } - - fn global_subrs(&self) -> Index<'a> { - match &self.version { - Version::Version1(cff1) => cff1.global_subrs().into(), - Version::Version2(cff2) => cff2.global_subrs().into(), - } - } - - fn private_dict_range(&self, subfont_index: u32) -> Result, Error> { - if let Some(font_dicts) = &self.top_dict.font_dicts { - // If we have a font dict array, extract the private dict range - // from the font dict at the given index. - let font_dict_data = font_dicts.get(subfont_index as usize)?; - let mut range = None; - for entry in dict::entries(font_dict_data, None) { - if let dict::Entry::PrivateDictRange(r) = entry? { - range = Some(r); - break; - } - } - range - } else { - // Last chance, use the private dict range from the top dict if - // available. - self.top_dict.private_dict_range.clone() - } - .ok_or(Error::MissingPrivateDict) - } -} - -#[derive(Clone)] -enum Version<'a> { - /// - Version1(Cff<'a>), - /// - Version2(Cff2<'a>), -} - -/// Specifies local subroutines and hinting parameters for some subset of -/// glyphs in a CFF or CFF2 table. -/// -/// This type is designed to be cacheable to avoid re-evaluating the private -/// dict every time a charstring is processed. -/// -/// For variable fonts, this is dependent on a location in variation space. -#[derive(Clone)] -pub struct Subfont { - is_cff2: bool, - scale: Fixed, - subrs_offset: Option, - pub(crate) hint_state: HintState, - store_index: u16, -} - -impl Subfont { - /// Returns the local subroutine index. - pub fn subrs<'a>(&self, scaler: &Outlines<'a>) -> Result>, Error> { - if let Some(subrs_offset) = self.subrs_offset { - let offset_data = scaler.offset_data().as_bytes(); - let index_data = offset_data.get(subrs_offset..).unwrap_or_default(); - Ok(Some(Index::new(index_data, self.is_cff2)?)) - } else { - Ok(None) - } - } - - /// Creates a new blend state for the given normalized variation - /// coordinates. - pub fn blend_state<'a>( - &self, - scaler: &Outlines<'a>, - coords: &'a [F2Dot14], - ) -> Result>, Error> { - if let Some(var_store) = scaler.top_dict.var_store.clone() { - Ok(Some(BlendState::new(var_store, coords, self.store_index)?)) - } else { - Ok(None) - } - } -} - -/// Entries that we parse from the Top DICT that are required to support -/// charstring evaluation. -#[derive(Clone, Default)] -struct TopDict<'a> { - charstrings: Option>, - font_dicts: Option>, - fd_select: Option>, - private_dict_range: Option>, - var_store: Option>, -} - -impl<'a> TopDict<'a> { - fn new(table_data: &'a [u8], top_dict_data: &'a [u8], is_cff2: bool) -> Result { - let mut items = TopDict::default(); - for entry in dict::entries(top_dict_data, None) { - match entry? { - dict::Entry::CharstringsOffset(offset) => { - items.charstrings = Some(Index::new( - table_data.get(offset..).unwrap_or_default(), - is_cff2, - )?); - } - dict::Entry::FdArrayOffset(offset) => { - items.font_dicts = Some(Index::new( - table_data.get(offset..).unwrap_or_default(), - is_cff2, - )?); - } - dict::Entry::FdSelectOffset(offset) => { - items.fd_select = Some(FdSelect::read(FontData::new( - table_data.get(offset..).unwrap_or_default(), - ))?); - } - dict::Entry::PrivateDictRange(range) => { - items.private_dict_range = Some(range); - } - dict::Entry::VariationStoreOffset(offset) if is_cff2 => { - items.var_store = Some(ItemVariationStore::read(FontData::new( - // IVS is preceded by a 2 byte length - table_data.get(offset + 2..).unwrap_or_default(), - ))?); - } - _ => {} - } - } - Ok(items) - } -} - -/// Command sink adapter that applies a scaling factor. -/// -/// This assumes a 26.6 scaling factor packed into a Fixed and thus, -/// this is not public and exists only to match FreeType's exact -/// scaling process. -struct ScalingSink26Dot6<'a, S> { - inner: &'a mut S, - scale: Fixed, -} - -impl<'a, S> ScalingSink26Dot6<'a, S> { - fn new(sink: &'a mut S, scale: Fixed) -> Self { - Self { scale, inner: sink } - } - - fn scale(&self, coord: Fixed) -> Fixed { - // The following dance is necessary to exactly match FreeType's - // application of scaling factors. This seems to be the result - // of merging the contributed Adobe code while not breaking the - // FreeType public API. - // - // The first two steps apply to both scaled and unscaled outlines: - // - // 1. Multiply by 1/64 - // - let a = coord * Fixed::from_bits(0x0400); - // 2. Truncate the bottom 10 bits. Combined with the division by 64, - // converts to font units. - // - let b = Fixed::from_bits(a.to_bits() >> 10); - if self.scale != Fixed::ONE { - // Scaled case: - // 3. Multiply by the original scale factor (to 26.6) - // - let c = b * self.scale; - // 4. Convert from 26.6 to 16.16 - Fixed::from_bits(c.to_bits() << 10) - } else { - // Unscaled case: - // 3. Convert from integer to 16.16 - Fixed::from_bits(b.to_bits() << 16) - } - } -} - -impl<'a, S: CommandSink> CommandSink for ScalingSink26Dot6<'a, S> { - fn hstem(&mut self, y: Fixed, dy: Fixed) { - self.inner.hstem(y, dy); - } - - fn vstem(&mut self, x: Fixed, dx: Fixed) { - self.inner.vstem(x, dx); - } - - fn hint_mask(&mut self, mask: &[u8]) { - self.inner.hint_mask(mask); - } - - fn counter_mask(&mut self, mask: &[u8]) { - self.inner.counter_mask(mask); - } - - fn move_to(&mut self, x: Fixed, y: Fixed) { - self.inner.move_to(self.scale(x), self.scale(y)); - } - - fn line_to(&mut self, x: Fixed, y: Fixed) { - self.inner.line_to(self.scale(x), self.scale(y)); - } - - fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) { - self.inner.curve_to( - self.scale(cx1), - self.scale(cy1), - self.scale(cx2), - self.scale(cy2), - self.scale(x), - self.scale(y), - ); - } - - fn close(&mut self) { - self.inner.close(); - } -} - -/// Command sink adapter that supresses degenerate move and line commands. -/// -/// FreeType avoids emitting empty contours and zero length lines to prevent -/// artifacts when stem darkening is enabled. We don't support stem darkening -/// because it's not enabled by any of our clients but we remove the degenerate -/// elements regardless to match the output. -/// -/// See -struct NopFilteringSink<'a, S> { - start: Option<(Fixed, Fixed)>, - last: Option<(Fixed, Fixed)>, - pending_move: Option<(Fixed, Fixed)>, - inner: &'a mut S, -} - -impl<'a, S> NopFilteringSink<'a, S> -where - S: CommandSink, -{ - fn new(inner: &'a mut S) -> Self { - Self { - start: None, - last: None, - pending_move: None, - inner, - } - } - - fn flush_pending_move(&mut self) { - if let Some((x, y)) = self.pending_move.take() { - if let Some((last_x, last_y)) = self.start { - if self.last != self.start { - self.inner.line_to(last_x, last_y); - } - } - self.start = Some((x, y)); - self.last = None; - self.inner.move_to(x, y); - } - } - - pub fn finish(&mut self) { - match self.start { - Some((x, y)) if self.last != self.start => { - self.inner.line_to(x, y); - } - _ => {} - } - } -} - -impl<'a, S> CommandSink for NopFilteringSink<'a, S> -where - S: CommandSink, -{ - fn hstem(&mut self, y: Fixed, dy: Fixed) { - self.inner.hstem(y, dy); - } - - fn vstem(&mut self, x: Fixed, dx: Fixed) { - self.inner.vstem(x, dx); - } - - fn hint_mask(&mut self, mask: &[u8]) { - self.inner.hint_mask(mask); - } - - fn counter_mask(&mut self, mask: &[u8]) { - self.inner.counter_mask(mask); - } - - fn move_to(&mut self, x: Fixed, y: Fixed) { - self.pending_move = Some((x, y)); - } - - fn line_to(&mut self, x: Fixed, y: Fixed) { - if self.pending_move == Some((x, y)) { - return; - } - self.flush_pending_move(); - if self.last == Some((x, y)) || (self.last.is_none() && self.start == Some((x, y))) { - return; - } - self.inner.line_to(x, y); - self.last = Some((x, y)); - } - - fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) { - self.flush_pending_move(); - self.last = Some((x, y)); - self.inner.curve_to(cx1, cy1, cx2, cy2, x, y); - } - - fn close(&mut self) { - if self.pending_move.is_none() { - self.inner.close(); - self.start = None; - self.last = None; - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use read_fonts::FontRef; - - #[test] - fn unscaled_scaling_sink_produces_integers() { - let nothing = &mut (); - let sink = ScalingSink26Dot6::new(nothing, Fixed::ONE); - for coord in [50.0, 50.1, 50.125, 50.5, 50.9] { - assert_eq!(sink.scale(Fixed::from_f64(coord)).to_f32(), 50.0); - } - } - - #[test] - fn scaled_scaling_sink() { - let ppem = 20.0; - let upem = 1000.0; - // match FreeType scaling with intermediate conversion to 26.6 - let scale = Fixed::from_bits((ppem * 64.) as i32) / Fixed::from_bits(upem as i32); - let nothing = &mut (); - let sink = ScalingSink26Dot6::new(nothing, scale); - let inputs = [ - // input coord, expected scaled output - (0.0, 0.0), - (8.0, 0.15625), - (16.0, 0.3125), - (32.0, 0.640625), - (72.0, 1.4375), - (128.0, 2.5625), - ]; - for (coord, expected) in inputs { - assert_eq!( - sink.scale(Fixed::from_f64(coord)).to_f32(), - expected, - "scaling coord {coord}" - ); - } - } - - #[test] - fn read_cff_static() { - let font = FontRef::new(font_test_data::NOTO_SERIF_DISPLAY_TRIMMED).unwrap(); - let cff = Outlines::new(&font).unwrap(); - assert!(!cff.is_cff2()); - assert!(cff.top_dict.var_store.is_none()); - assert!(cff.top_dict.font_dicts.is_none()); - assert!(cff.top_dict.private_dict_range.is_some()); - assert!(cff.top_dict.fd_select.is_none()); - assert_eq!(cff.subfont_count(), 1); - assert_eq!(cff.subfont_index(GlyphId::new(1)), 0); - assert_eq!(cff.global_subrs().count(), 17); - } - - #[test] - fn read_cff2_static() { - let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap(); - let cff = Outlines::new(&font).unwrap(); - assert!(cff.is_cff2()); - assert!(cff.top_dict.var_store.is_some()); - assert!(cff.top_dict.font_dicts.is_some()); - assert!(cff.top_dict.private_dict_range.is_none()); - assert!(cff.top_dict.fd_select.is_none()); - assert_eq!(cff.subfont_count(), 1); - assert_eq!(cff.subfont_index(GlyphId::new(1)), 0); - assert_eq!(cff.global_subrs().count(), 0); - } - - #[test] - fn read_example_cff2_table() { - let cff = Outlines::from_cff2( - Cff2::read(FontData::new(font_test_data::cff2::EXAMPLE)).unwrap(), - 1000, - ) - .unwrap(); - assert!(cff.is_cff2()); - assert!(cff.top_dict.var_store.is_some()); - assert!(cff.top_dict.font_dicts.is_some()); - assert!(cff.top_dict.private_dict_range.is_none()); - assert!(cff.top_dict.fd_select.is_none()); - assert_eq!(cff.subfont_count(), 1); - assert_eq!(cff.subfont_index(GlyphId::new(1)), 0); - assert_eq!(cff.global_subrs().count(), 0); - } - - #[test] - fn cff2_variable_outlines_match_freetype() { - compare_glyphs( - font_test_data::CANTARELL_VF_TRIMMED, - font_test_data::CANTARELL_VF_TRIMMED_GLYPHS, - ); - } - - #[test] - fn cff_static_outlines_match_freetype() { - compare_glyphs( - font_test_data::NOTO_SERIF_DISPLAY_TRIMMED, - font_test_data::NOTO_SERIF_DISPLAY_TRIMMED_GLYPHS, - ); - } - - /// For the given font data and extracted outlines, parse the extracted - /// outline data into a set of expected values and compare these with the - /// results generated by the scaler. - /// - /// This will compare all outlines at various sizes and (for variable - /// fonts), locations in variation space. - fn compare_glyphs(font_data: &[u8], expected_outlines: &str) { - let font = FontRef::new(font_data).unwrap(); - let outlines = read_fonts::scaler_test::parse_glyph_outlines(expected_outlines); - let scaler = super::Outlines::new(&font).unwrap(); - let mut path = read_fonts::scaler_test::Path::default(); - for expected_outline in &outlines { - if expected_outline.size == 0.0 && !expected_outline.coords.is_empty() { - continue; - } - path.elements.clear(); - let subfont = scaler - .subfont( - scaler.subfont_index(expected_outline.glyph_id), - expected_outline.size, - &expected_outline.coords, - ) - .unwrap(); - scaler - .draw( - &subfont, - expected_outline.glyph_id, - &expected_outline.coords, - false, - &mut path, - ) - .unwrap(); - if path.elements != expected_outline.path { - panic!( - "mismatch in glyph path for id {} (size: {}, coords: {:?}): path: {:?} expected_path: {:?}", - expected_outline.glyph_id, - expected_outline.size, - expected_outline.coords, - &path.elements, - &expected_outline.path - ); - } - } - } -} diff --git a/src/scale/glyf/cache.rs b/src/scale/glyf/cache.rs deleted file mode 100644 index ac7dd9a..0000000 --- a/src/scale/glyf/cache.rs +++ /dev/null @@ -1,279 +0,0 @@ -use super::hint::*; -use super::proxy::GlyfProxy; -use super::Point; - -pub type CacheSlot = (u8, u8); - -#[derive(Default)] -pub struct Cache { - fonts: Vec, - sizes: Vec, - stack: Vec, - twilight: Vec, - twilight_tags: Vec, - epoch: u64, - max_entries: usize, -} - -impl Cache { - pub fn new(max_entries: usize) -> Self { - Self { - fonts: Vec::new(), - sizes: Vec::new(), - stack: Vec::new(), - twilight: Vec::new(), - twilight_tags: Vec::new(), - epoch: 0, - max_entries, - } - } - - pub fn prepare( - &mut self, - font_id: [u64; 2], - data: &[u8], - proxy: &GlyfProxy, - coords: &[i16], - ppem: u16, - scale: i32, - mode: HinterMode, - ) -> Option { - self.epoch += 1; - let epoch = self.epoch; - let mut run_fpgm = false; - let max_twilight = proxy.max_twilight as usize; - self.twilight.resize(max_twilight * 3, Point::default()); - self.twilight_tags.resize(max_twilight, 0); - self.stack.resize(proxy.max_stack as usize, 0); - let font_entry = self.find_font(font_id); - if !font_entry.0 { - let max_fdefs = proxy.max_fdefs as usize; - let max_idefs = proxy.max_idefs as usize; - let font = &mut self.fonts[font_entry.1]; - font.font_id = font_id; - font.epoch = epoch; - font.definitions.clear(); - font.definitions - .resize(max_fdefs + max_idefs, Function::default()); - font.max_fdefs = max_fdefs; - font.cvt_len = (proxy.cvt.1 - proxy.cvt.0) as usize * 2; - run_fpgm = true; - } else { - self.fonts[font_entry.1].epoch = epoch; - } - let size_entry = self.find_size(font_id, coords, scale, mode); - let mut run_prep = false; - if !size_entry.0 { - let size = &mut self.sizes[size_entry.1]; - size.font_id = font_id; - size.epoch = epoch; - size.state = HinterState::default(); - size.mode = mode; - size.scale = scale; - size.coords.clear(); - size.coords.extend_from_slice(coords); - let cvt_len = (proxy.cvt.1 - proxy.cvt.0) as usize * 2; - size.store.clear(); - size.store.resize(cvt_len + proxy.max_storage as usize, 0); - proxy.cvt(data, Some(scale), coords, &mut size.store); - run_prep = true; - } else { - self.sizes[size_entry.1].epoch = epoch; - } - if run_fpgm | run_prep { - let font = &mut self.fonts[font_entry.1]; - let size = &mut self.sizes[size_entry.1]; - let (cvt, store) = size.store.split_at_mut(font.cvt_len); - let (fdefs, idefs) = font.definitions.split_at_mut(font.max_fdefs); - let glyph = Zone::new(&mut [], &mut [], &mut [], &mut [], &[]); - let max_twilight = self.twilight_tags.len(); - let (unscaled, rest) = self.twilight.split_at_mut(max_twilight); - let (original, points) = rest.split_at_mut(max_twilight); - let twilight_contours = [max_twilight as u16]; - let twilight = Zone::new( - unscaled, - original, - points, - &mut self.twilight_tags[..], - &twilight_contours, - ); - let mut hinter = Hinter::new( - store, - cvt, - fdefs, - idefs, - &mut self.stack, - twilight, - glyph, - coords, - proxy.axis_count, - ); - if run_fpgm { - let mut state = HinterState::default(); - if !hinter.run_fpgm(&mut state, proxy.fpgm(data)) { - return None; - } - } - if run_prep { - size.state = HinterState::default(); - if !hinter.run_prep( - &mut size.state, - mode, - proxy.fpgm(data), - proxy.prep(data), - ppem, - scale, - ) { - return None; - } - } - } - if !self.sizes[size_entry.1].state.hinting_enabled() { - return None; - } - Some((font_entry.1 as u8, size_entry.1 as u8)) - } - - pub fn hint( - &mut self, - data: &[u8], - proxy: &GlyfProxy, - coords: &[i16], - slot: CacheSlot, - unscaled: &mut [Point], - original: &mut [Point], - scaled: &mut [Point], - tags: &mut [u8], - contours: &mut [u16], - phantom: &mut [Point], - point_base: usize, - contour_base: usize, - ins: &[u8], - is_composite: bool, - ) { - let font = &mut self.fonts[slot.0 as usize]; - let size = &mut self.sizes[slot.1 as usize]; - if is_composite && point_base != 0 { - for c in &mut contours[contour_base..] { - *c -= point_base as u16; - } - } - let glyph = Zone::new( - unscaled, - original, - &mut scaled[point_base..], - &mut tags[point_base..], - &contours[contour_base..], - ); - let twilight_len = self.twilight_tags.len(); - let twilight_contours = [twilight_len as u16]; - let (twilight_original, rest) = self.twilight.split_at_mut(twilight_len); - let (twilight_unscaled, twilight_points) = rest.split_at_mut(twilight_len); - let twilight = Zone::new( - twilight_unscaled, - twilight_original, - twilight_points, - &mut self.twilight_tags[..], - &twilight_contours, - ); - let (cvt, store) = size.store.split_at_mut(font.cvt_len); - let (fdefs, idefs) = font.definitions.split_at_mut(font.max_fdefs); - let mut hinter = Hinter::new( - store, - cvt, - fdefs, - idefs, - &mut self.stack[..], - twilight, - glyph, - coords, - proxy.axis_count, - ); - hinter.run( - &mut size.state, - proxy.fpgm(data), - proxy.prep(data), - ins, - is_composite, - ); - if !size.state.compat_enabled() { - for (i, p) in (&scaled[scaled.len() - 4..]).iter().enumerate() { - phantom[i] = *p; - } - } - if is_composite && point_base != 0 { - for c in &mut contours[contour_base..] { - *c += point_base as u16; - } - } - } - - fn find_font(&mut self, font_id: [u64; 2]) -> (bool, usize) { - let mut lowest_epoch = self.epoch; - let mut lowest_index = 0; - for (i, font) in self.fonts.iter().enumerate() { - if font.font_id == font_id { - return (true, i); - } - if font.epoch < lowest_epoch { - lowest_epoch = font.epoch; - lowest_index = i; - } - } - if self.fonts.len() < self.max_entries { - lowest_index = self.fonts.len(); - self.fonts.push(FontEntry::default()); - } - (false, lowest_index) - } - - fn find_size( - &mut self, - font_id: [u64; 2], - coords: &[i16], - scale: i32, - mode: HinterMode, - ) -> (bool, usize) { - let mut lowest_epoch = self.epoch; - let mut lowest_index = 0; - let vary = !coords.is_empty(); - for (i, size) in self.sizes.iter().enumerate() { - if size.epoch < lowest_epoch { - lowest_epoch = size.epoch; - lowest_index = i; - } - if size.font_id == font_id - && size.scale == scale - && size.mode == mode - && (!vary || (coords == &size.coords[..])) - { - return (true, i); - } - } - if self.sizes.len() < self.max_entries { - lowest_index = self.sizes.len(); - self.sizes.push(SizeEntry::default()); - } - (false, lowest_index) - } -} - -#[derive(Default)] -struct FontEntry { - font_id: [u64; 2], - definitions: Vec, - max_fdefs: usize, - cvt_len: usize, - epoch: u64, -} - -#[derive(Default)] -struct SizeEntry { - font_id: [u64; 2], - state: HinterState, - mode: HinterMode, - coords: Vec, - scale: i32, - store: Vec, - epoch: u64, -} diff --git a/src/scale/glyf/hint.rs b/src/scale/glyf/hint.rs deleted file mode 100644 index c247a75..0000000 --- a/src/scale/glyf/hint.rs +++ /dev/null @@ -1,2602 +0,0 @@ -use super::Point; - -const TRACE: bool = false; - -/// Modes for hinting. -#[derive(Copy, Clone, PartialEq, Debug)] -#[allow(dead_code)] -pub enum HinterMode { - /// "Full" hinting mode. May generate rough outlines and poor horizontal - /// spacing. - Legacy, - /// Subpixel mode for grayscale rendering. - GrayscaleSubpixel, - /// Cleartype subpixel mode. - Subpixel, - /// Same as subpixel, but always prevents adjustment in the horizontal - /// direction. - Modern, -} - -impl Default for HinterMode { - fn default() -> Self { - Self::Modern - } -} - -/// State for the TrueType hinter. -#[derive(Copy, Clone)] -pub struct HinterState { - gs: GraphicsState, - default_gs: GraphicsState, - ppem: u16, - point_size: i32, - scale: i32, - coord_count: u16, - compat: bool, - mode: HinterMode, -} - -impl Default for HinterState { - fn default() -> Self { - Self { - gs: DEFAULT_GRAPHICS_STATE, - default_gs: DEFAULT_GRAPHICS_STATE, - ppem: 0, - point_size: 0, - scale: 0, - coord_count: 0, - compat: false, - mode: HinterMode::Modern, - } - } -} - -impl HinterState { - /// Returns true if hinting is enabled for this state. - pub fn hinting_enabled(&self) -> bool { - self.gs.instruct_control & 1 == 0 - } - - /// Returns true if compatibility mode is enabled for this state. - pub fn compat_enabled(&self) -> bool { - self.compat - } -} - -/// Function or instruction definition for TrueType hinting. -#[derive(Copy, Clone, Default)] -pub struct Function { - program: u8, - active: bool, - offset: u32, - end: u32, - opcode: u16, -} - -/// Glyph zone for TrueType hinting. -pub struct Zone<'a> { - unscaled: &'a mut [Point], - original: &'a mut [Point], - points: &'a mut [Point], - tags: &'a mut [u8], - contours: &'a [u16], -} - -impl<'a> Zone<'a> { - /// Creates a new hinting zone. - pub fn new( - unscaled: &'a mut [Point], - original: &'a mut [Point], - points: &'a mut [Point], - tags: &'a mut [u8], - contours: &'a [u16], - ) -> Self { - Self { - unscaled, - original, - points, - tags, - contours, - } - } - - fn shift(&mut self, is_x: bool, p1: usize, p2: usize, p: usize) -> Option<()> { - if p1 > p2 || p1 > p || p > p2 { - return Some(()); - } - let z = self; - if is_x { - let delta = z.points.get(p)?.x - z.original.get(p)?.x; - if delta != 0 { - let (first, second) = z.points.get_mut(p1..=p2)?.split_at_mut(p - p1); - for point in first.iter_mut().chain(second.get_mut(1..)?) { - point.x += delta; - } - } - } else { - let delta = z.points.get(p)?.y - z.original.get(p)?.y; - if delta != 0 { - let (first, second) = z.points.get_mut(p1..=p2)?.split_at_mut(p - p1); - for point in first.iter_mut().chain(second.get_mut(1..)?) { - point.y += delta; - } - } - } - Some(()) - } - - fn interpolate( - &mut self, - is_x: bool, - p1: usize, - p2: usize, - mut ref1: usize, - mut ref2: usize, - ) -> Option<()> { - if p1 > p2 { - return Some(()); - } - let z = self; - let max_points = z.points.len(); - if ref1 >= max_points || ref2 >= max_points { - return Some(()); - } - let (mut orus1, mut orus2) = if is_x { - (z.unscaled.get(ref1)?.x, z.unscaled.get(ref2)?.x) - } else { - (z.unscaled.get(ref1)?.y, z.unscaled.get(ref2)?.y) - }; - if orus1 > orus2 { - core::mem::swap(&mut orus1, &mut orus2); - core::mem::swap(&mut ref1, &mut ref2); - } - let (org1, org2, cur1, cur2) = if is_x { - ( - z.original.get(ref1)?.x, - z.original.get(ref2)?.x, - z.points.get(ref1)?.x, - z.points.get(ref2)?.x, - ) - } else { - ( - z.original.get(ref1)?.y, - z.original.get(ref2)?.y, - z.points.get(ref1)?.y, - z.points.get(ref2)?.y, - ) - }; - let delta1 = cur1 - org1; - let delta2 = cur2 - org2; - let iter = z - .original - .get(p1..=p2)? - .iter() - .zip(z.unscaled.get(p1..=p2)?) - .zip(z.points.get_mut(p1..=p2)?); - if cur1 == cur2 || orus1 == orus2 { - if is_x { - for ((orig, _unscaled), point) in iter { - let a = orig.x; - point.x = if a <= org1 { - a + delta1 - } else if a >= org2 { - a + delta2 - } else { - cur1 - }; - } - } else { - for ((orig, _unscaled), point) in iter { - let a = orig.y; - point.y = if a <= org1 { - a + delta1 - } else if a >= org2 { - a + delta2 - } else { - cur1 - }; - } - } - } else { - let scale = div(cur2 - cur1, orus2 - orus1); - if is_x { - for ((orig, unscaled), point) in iter { - let a = orig.x; - point.x = if a <= org1 { - a + delta1 - } else if a >= org2 { - a + delta2 - } else { - cur1 + mul(unscaled.x - orus1, scale) - }; - } - } else { - for ((orig, unscaled), point) in iter { - let a = orig.y; - point.y = if a <= org1 { - a + delta1 - } else if a >= org2 { - a + delta2 - } else { - cur1 + mul(unscaled.y - orus1, scale) - }; - } - } - } - Some(()) - } -} - -/// TrueType hinting engine. -pub struct Hinter<'a> { - store: &'a mut [i32], - cvt: &'a mut [i32], - fdefs: &'a mut [Function], - idefs: &'a mut [Function], - stack: &'a mut [i32], - twilight: Zone<'a>, - glyph: Zone<'a>, - coords: &'a [i16], - axis_count: u16, - zp0: u8, - zp1: u8, - zp2: u8, - ppem: u16, - point_size: i32, - scale: i32, - yscale: i32, - rotated: bool, - round_state: u8, - round_threshold: i32, - round_phase: i32, - round_period: i32, - project_state: u8, - dual_project_state: u8, - move_state: u8, - pv: Point, - dv: Point, - fv: Point, - fdotp: i32, - iupx: bool, - iupy: bool, - v35: bool, - subpixel: bool, - compat: bool, -} - -impl<'a> Hinter<'a> { - /// Creates a new hinting engine. - pub fn new( - storage: &'a mut [i32], - cvt: &'a mut [i32], - function_defs: &'a mut [Function], - instruction_defs: &'a mut [Function], - stack: &'a mut [i32], - twilight: Zone<'a>, - glyph: Zone<'a>, - coords: &'a [i16], - axis_count: u16, - ) -> Self { - Self { - store: storage, - cvt, - fdefs: function_defs, - idefs: instruction_defs, - stack, - twilight, - glyph, - coords, - axis_count, - zp0: 1, - zp1: 1, - zp2: 1, - ppem: 0, - point_size: 0, - scale: 0, - yscale: 0, - rotated: false, - iupx: false, - iupy: false, - dv: Point::new(0x4000, 0), - fv: Point::new(0x4000, 0), - round_phase: 0, - round_period: 64, - pv: Point::new(0x4000, 0), - fdotp: 0x4000, - round_threshold: 0, - round_state: 1, - project_state: 0, - dual_project_state: 0, - move_state: 0, - v35: false, - subpixel: true, - compat: false, - } - } - - pub fn run_fpgm<'b>(&mut self, state: &'b mut HinterState, fpgm: &[u8]) -> bool { - let programs = [fpgm, &[], &[]]; - for f in self.fdefs.iter_mut() { - *f = Function::default(); - } - for f in self.idefs.iter_mut() { - *f = Function::default(); - } - state.ppem = 0; - state.point_size = 0; - state.scale = 0; - state.mode = HinterMode::Modern; - state.gs = DEFAULT_GRAPHICS_STATE; - state.default_gs = DEFAULT_GRAPHICS_STATE; - state.coord_count = self.axis_count; - self.execute(state, programs, 0, false).is_some() - } - - pub fn run_prep<'b>( - &mut self, - state: &'b mut HinterState, - mode: HinterMode, - fpgm: &[u8], - prep: &[u8], - ppem: u16, - scale: i32, - ) -> bool { - let programs = [fpgm, prep, &[]]; - for p in self.twilight.unscaled.iter_mut() { - *p = Point::new(0, 0); - } - for p in self.twilight.original.iter_mut() { - *p = Point::new(0, 0); - } - for p in self.twilight.points.iter_mut() { - *p = Point::new(0, 0); - } - state.mode = mode; - state.ppem = ppem; - state.scale = scale; - state.point_size = muldiv(ppem as i32, 64 * 72, 72); - state.coord_count = self.axis_count; - self.ppem = state.ppem; - self.point_size = state.point_size; - self.scale = state.scale; - self.yscale = state.scale; - let res = self.execute(state, programs, 1, false); - if res.is_some() { - state.default_gs = state.gs; - true - } else { - false - } - } - - pub fn run<'b>( - &mut self, - state: &'b mut HinterState, - fpgm: &[u8], - prep: &[u8], - ins: &[u8], - is_composite: bool, - ) -> bool { - let programs = [fpgm, prep, ins]; - self.ppem = state.ppem; - self.point_size = state.point_size; - self.scale = state.scale; - self.yscale = state.scale; - if is_composite { - self.yscale = 1 << 16; - } - if state.default_gs.instruct_control & 2 != 0 { - state.gs = DEFAULT_GRAPHICS_STATE; - } else { - state.gs = state.default_gs; - } - let res = self.execute(state, programs, 2, is_composite); - self.yscale = self.scale; - res.is_some() - } -} - -impl<'a> Hinter<'a> { - #[inline(always)] - fn zp(&self, zone: u8) -> &Zone { - if zone == 1 { - return &self.glyph; - } - &self.twilight - } - - #[inline(always)] - fn zp_mut<'b>(&'b mut self, zone: u8) -> &'b mut Zone<'a> { - if zone == 1 { - return &mut self.glyph; - } - &mut self.twilight - } - - #[inline(always)] - fn zp0(&self) -> &Zone { - if self.zp0 == 1 { - return &self.glyph; - } - &self.twilight - } - - #[inline(always)] - fn zp0_mut<'b>(&'b mut self) -> &'b mut Zone<'a> { - if self.zp0 == 1 { - return &mut self.glyph; - } - &mut self.twilight - } - - #[inline(always)] - fn zp1(&self) -> &Zone { - if self.zp1 == 1 { - return &self.glyph; - } - &self.twilight - } - - #[inline(always)] - fn zp1_mut<'b>(&'b mut self) -> &'b mut Zone<'a> { - if self.zp1 == 1 { - return &mut self.glyph; - } - &mut self.twilight - } - - #[inline(always)] - fn zp2(&self) -> &Zone { - if self.zp2 == 1 { - return &self.glyph; - } - &self.twilight - } - - #[inline(always)] - fn zp2_mut<'b>(&'b mut self) -> &'b mut Zone<'a> { - if self.zp2 == 1 { - return &mut self.glyph; - } - &mut self.twilight - } - - fn round(&self, distance: i32) -> i32 { - match self.round_state { - ROUND_TO_HALF_GRID => { - if distance >= 0 { - (floor(distance) + 32).max(0) - } else { - (-(floor(-distance) + 32)).min(0) - } - } - ROUND_TO_GRID => { - if distance >= 0 { - round(distance).max(0) - } else { - (-round(-distance)).min(0) - } - } - ROUND_TO_DOUBLE_GRID => { - if distance >= 0 { - round_pad(distance, 32).max(0) - } else { - (-round_pad(-distance, 32)).min(0) - } - } - ROUND_DOWN_TO_GRID => { - if distance >= 0 { - floor(distance).max(0) - } else { - (-floor(-distance)).min(0) - } - } - ROUND_UP_TO_GRID => { - if distance >= 0 { - ceil(distance).max(0) - } else { - (-ceil(-distance)).min(0) - } - } - ROUND_SUPER => { - if distance >= 0 { - let val = ((distance + (self.round_threshold - self.round_phase)) - & -self.round_period) - + self.round_phase; - if val < 0 { - self.round_phase - } else { - val - } - } else { - let val = -(((self.round_threshold - self.round_phase) - distance) - & -self.round_period) - - self.round_phase; - if val > 0 { - -self.round_phase - } else { - val - } - } - } - ROUND_SUPER45 => { - if distance >= 0 { - let val = (((distance + (self.round_threshold - self.round_phase)) - / self.round_period) - * self.round_period) - + self.round_phase; - if val < 0 { - self.round_phase - } else { - val - } - } else { - let val = -((((self.round_threshold - self.round_phase) - distance) - / self.round_period) - * self.round_period) - - self.round_phase; - if val > 0 { - -self.round_phase - } else { - val - } - } - } - _ => distance, - } - } - - fn move_original(&mut self, zone: u8, point: usize, distance: i32) -> Option<()> { - let fdotp = self.fdotp; - let x = self.fv.x; - let y = self.fv.y; - let state = self.move_state; - let z = self.zp_mut(zone); - let p = z.original.get_mut(point)?; - match state { - 1 => p.x += distance, - 2 => p.y += distance, - _ => { - if x != 0 { - p.x += muldiv(distance, x as i32, fdotp); - } - if y != 0 { - p.y += muldiv(distance, y as i32, fdotp); - } - } - } - Some(()) - } - - fn move_point(&mut self, zone: u8, point: usize, distance: i32) -> Option<()> { - let legacy = self.v35; - let bc = self.compat; - let iupx = self.iupx; - let iupy = self.iupy; - let x = self.fv.x; - let y = self.fv.y; - let fdotp = self.fdotp; - let state = self.move_state; - let z = self.zp_mut(zone); - let p = z.points.get_mut(point)?; - let tag = z.tags.get_mut(point)?; - match state { - 1 => { - if legacy || !bc { - p.x += distance; - } - *tag |= TOUCH_X; - } - 2 => { - if !(!legacy && bc && iupx && iupy) { - p.y += distance; - } - *tag |= TOUCH_Y; - } - _ => { - if x != 0 { - if legacy || !bc { - p.x += muldiv(distance, x as i32, fdotp); - } - *tag |= TOUCH_X; - } - if y != 0 { - if !(!legacy && bc && iupx && iupy) { - p.y += muldiv(distance, y as i32, fdotp); - } - *tag |= TOUCH_Y; - } - } - } - Some(()) - } - - #[inline(always)] - fn project(&self, v1: Point, v2: Point) -> i32 { - match self.project_state { - 1 => v1.x - v2.x, - 2 => v1.y - v2.y, - _ => { - let x = v1.x - v2.x; - let y = v1.y - v2.y; - dot14(x, y, self.pv.x as i32, self.pv.y as i32) - } - } - } - - #[inline(always)] - fn dual_project(&self, v1: Point, v2: Point) -> i32 { - match self.project_state { - 1 => v1.x - v2.x, - 2 => v1.y - v2.y, - _ => { - let x = v1.x - v2.x; - let y = v1.y - v2.y; - dot14(x, y, self.dv.x as i32, self.dv.y as i32) - } - } - } - - #[inline(always)] - fn fast_project(&self, v: Point) -> i32 { - self.project(v, Point::new(0, 0)) - } - - #[inline(always)] - fn fast_dual_project(&self, v: Point) -> i32 { - self.dual_project(v, Point::new(0, 0)) - } - - fn vectors_changed(&mut self) { - if self.fv.x == 0x4000 { - self.fdotp = self.pv.x as i32; - } else if self.fv.y == 0x4000 { - self.fdotp = self.pv.y as i32; - } else { - let px = self.pv.x as i32; - let py = self.pv.y as i32; - let fx = self.fv.x as i32; - let fy = self.fv.y as i32; - self.fdotp = (px * fx + py * fy) >> 14; - } - if self.pv.x == 0x4000 { - self.project_state = 1; - } else if self.pv.y == 0x4000 { - self.project_state = 2; - } else { - self.project_state = 0; - } - if self.dv.x == 0x4000 { - self.dual_project_state = 1; - } else if self.dv.y == 0x4000 { - self.dual_project_state = 2; - } else { - self.dual_project_state = 0; - } - self.move_state = 0; - if self.fdotp == 0x4000 { - if self.fv.x == 0x4000 { - self.move_state = 1; - } else if self.fv.y == 0x4000 { - self.move_state = 2; - } - } - if self.fdotp.abs() < 0x400 { - self.fdotp = 0x4000; - } - } - - fn skip_instruction(&mut self, code: &[u8], pc: usize, next_pc: &mut usize) -> Option { - let len = code.len(); - if pc < len { - let opcode = *code.get(pc)?; - let l = OPCODE_LEN[opcode as usize] as i32; - let opcode_len; - if l < 0 { - opcode_len = (2 - l * *code.get(pc + 1)? as i32) as usize - } else { - opcode_len = l as usize; - } - if pc + opcode_len <= len { - *next_pc = pc + opcode_len; - return Some(true); - } - } - None - } - - fn compute_point_displacement( - &mut self, - opcode: u8, - rp1: usize, - rp2: usize, - ) -> Option<(i32, i32, u8, usize)> { - let (zone, index) = if (opcode & 1) != 0 { - (self.zp0, rp1) - } else { - (self.zp1, rp2) - }; - let z = self.zp(zone); - let point = z.points.get(index)?; - let original = z.original.get(index)?; - let d = self.project(*point, *original); - let x = muldiv(d, self.fv.x as i32, self.fdotp); - let y = muldiv(d, self.fv.y as i32, self.fdotp); - Some((x, y, zone, index)) - } - - fn move_zp2_point(&mut self, point: usize, dx: i32, dy: i32, touch: bool) -> Option<()> { - let v35 = self.v35; - let x = self.fv.x; - let y = self.fv.y; - let (iupx, iupy) = (self.iupx, self.iupy); - let compat = self.compat; - let z = self.zp2_mut(); - let p = z.points.get_mut(point)?; - let tag = z.tags.get_mut(point)?; - if x != 0 { - if v35 || !compat { - p.x += dx; - } - if touch { - *tag |= TOUCH_X; - } - } - if y != 0 { - if !(!v35 && compat && iupx && iupy) { - p.y += dy; - } - if touch { - *tag |= TOUCH_Y; - } - } - Some(()) - } - - fn normalize(&self, x: i32, y: i32, r: &mut Point) { - use core::num::Wrapping; - let (mut sx, mut sy) = (Wrapping(1i32), Wrapping(1i32)); - let mut ux = Wrapping(x as u32); - let mut uy = Wrapping(y as u32); - const ZERO: Wrapping = Wrapping(0); - if x < 0 { - ux = ZERO - ux; - sx = -sx; - } - if y < 0 { - uy = ZERO - uy; - sy = -sy; - } - if ux == ZERO { - r.x = x / 4; - if uy.0 > 0 { - r.y = (sy * Wrapping(0x10000) / Wrapping(4)).0; - } - return; - } - if uy == ZERO { - r.y = y / 4; - if ux.0 > 0 { - r.x = (sx * Wrapping(0x10000) / Wrapping(4)).0; - } - return; - } - let mut len = if ux > uy { - ux + (uy >> 1) - } else { - uy + (ux >> 1) - }; - let mut shift = Wrapping(len.0.leading_zeros() as i32); - shift -= Wrapping(15) - + if len >= (Wrapping(0xAAAAAAAAu32) >> shift.0 as usize) { - Wrapping(1) - } else { - Wrapping(0) - }; - if shift.0 > 0 { - let s = shift.0 as usize; - ux <<= s; - uy <<= s; - len = if ux > uy { - ux + (uy >> 1) - } else { - uy + (ux >> 1) - }; - } else { - let s = -shift.0 as usize; - ux >>= s; - uy >>= s; - len >>= s; - } - let mut b = Wrapping(0x10000) - Wrapping(len.0 as i32); - let x = Wrapping(ux.0 as i32); - let y = Wrapping(uy.0 as i32); - let mut z; - let mut u; - let mut v; - loop { - u = Wrapping((x + ((x * b) >> 16)).0 as u32); - v = Wrapping((y + ((y * b) >> 16)).0 as u32); - z = Wrapping(-((u * u + v * v).0 as i32)) / Wrapping(0x200); - z = z * ((Wrapping(0x10000) + b) >> 8) / Wrapping(0x10000); - b += z; - if z <= Wrapping(0) { - break; - } - } - r.x = (Wrapping(u.0 as i32) * sx / Wrapping(4)).0; - r.y = (Wrapping(v.0 as i32) * sy / Wrapping(4)).0; - } -} - -impl<'a> Hinter<'a> { - fn execute<'b>( - &mut self, - state: &'b mut HinterState, - programs: [&[u8]; 3], - mut program: u8, - composite: bool, - ) -> Option { - let mut code = programs[program as usize]; - if code.is_empty() { - return Some(0); - } - let (v35, grayscale, subpixel, grayscale_cleartype) = match state.mode { - HinterMode::Legacy => (true, true, false, false), - HinterMode::GrayscaleSubpixel => (false, false, true, true), - HinterMode::Subpixel => (false, false, true, false), - HinterMode::Modern => (false, false, true, false), - }; - self.v35 = v35; - self.subpixel = subpixel; - if state.mode == HinterMode::Modern { - self.compat = true; - } else if !v35 && subpixel { - self.compat = (state.gs.instruct_control & 0x4) == 0; - } else { - self.compat = false; - } - self.compat = true; - state.compat = self.compat; - self.dv = Point::new(0x4000, 0); - self.pv = self.dv; - self.fv = self.dv; - self.project_state = 0; - self.move_state = 0; - self.dual_project_state = 0; - self.fdotp = 0x4000; - self.zp0 = 1; - self.zp1 = 1; - self.zp2 = 1; - self.round_state = ROUND_TO_GRID; - self.round_threshold = 0; - self.round_phase = 0; - self.round_period = 64; - self.iupx = false; - self.iupy = false; - self.vectors_changed(); - let mut stack_top = 0usize; - let mut new_top: usize; - let mut args_top: usize; - let mut pc = 0usize; - let mut count = 0u32; - #[derive(Copy, Clone, Default)] - struct CallRecord { - caller_program: u8, - caller_ip: usize, - current_count: u32, - definition: Function, - } - let mut callstack = [CallRecord::default(); 32]; - let mut callstack_top = 0; - let callstack_len = callstack.len(); - let stack_size = self.stack.len(); - let mut rp0 = 0usize; - let mut rp1 = 0usize; - let mut rp2 = 0usize; - let mut iloop = 1u32; - loop { - let opcode = *code.get(pc)?; - let mut opcode_len = OPCODE_LEN[opcode as usize] as i32; - if opcode_len < 0 { - opcode_len = 2 - opcode_len * *code.get(pc + 1)? as i32; - } - let oplen = opcode_len as usize; - let mut next_pc = pc + oplen; - let pop_push = OPCODE_POP_PUSH[opcode as usize] as usize; - let pop_count = pop_push >> 4; - let push_count = pop_push & 0xF; - if pop_count > stack_top { - return None; - } - let args = stack_top - pop_count; - args_top = args; - new_top = args + push_count; - if new_top > stack_size { - return None; - } - - if TRACE { - // let name = OPCODE_NAME[opcode as usize]; - // for _ in 0..callstack_top { - // print!("."); - // } - // print!("{} [{}] {}", count, pc, name); - // let pcnt = if stack_top < 8 { stack_top } else { 8 }; - // for i in 1..=pcnt { - // print!(" {}", self.stack[stack_top - i]); - // } - // println!(""); - } - - let a0 = args; - let a1 = args + 1; - let a2 = args + 2; - - match opcode { - OP_SVTCA0..=OP_SFVTCA1 => { - let aa = ((opcode as i32 & 1) << 14) as i32; - let bb = aa ^ 0x4000; - if opcode < 4 { - self.pv = Point::new(aa, bb); - self.dv = self.pv; - } - if (opcode & 2) == 0 { - self.fv = Point::new(aa, bb); - } - self.vectors_changed(); - } - OP_SPVTL0..=OP_SFVTL1 => { - let index1 = *self.stack.get(a1)? as usize; - let index2 = *self.stack.get(a0)? as usize; - let mut v = Point::new(0, 0); - let p1 = self.zp1().points.get(index2)?; - let p2 = self.zp2().points.get(index1)?; - let mut a = p1.x - p2.x; - let mut b = p1.y - p2.y; - let mut op = opcode; - if a == 0 && b == 0 { - a = 0x4000; - op = 0; - } - if (op & 1) != 0 { - let c = b; - b = a; - a = -c; - } - self.normalize(a, b, &mut v); - if opcode <= OP_SPVTL1 { - self.pv = v; - self.dv = v; - } else { - self.fv = v; - } - self.vectors_changed(); - } - OP_SPVFS => { - let y = *self.stack.get(a1)? as i16 as i32; - let x = *self.stack.get(a0)? as i16 as i32; - let mut v = self.pv; - self.normalize(x, y, &mut v); - self.pv = v; - self.dv = v; - self.vectors_changed(); - } - OP_SFVFS => { - let y = *self.stack.get(a1)? as i16 as i32; - let x = *self.stack.get(a0)? as i16 as i32; - let mut v = self.fv; - self.normalize(x, y, &mut v); - self.fv = v; - self.vectors_changed(); - } - OP_GPV => { - *self.stack.get_mut(a0)? = self.pv.x; - *self.stack.get_mut(a1)? = self.pv.y; - } - OP_GFV => { - *self.stack.get_mut(a0)? = self.fv.x; - *self.stack.get_mut(a1)? = self.fv.y; - } - OP_SFVTPV => { - self.fv = self.pv; - self.vectors_changed(); - } - OP_ISECT => { - let point = *self.stack.get(args)? as usize; - let a0 = *self.stack.get(args + 1)? as usize; - let a1 = *self.stack.get(args + 2)? as usize; - let b0 = *self.stack.get(args + 3)? as usize; - let b1 = *self.stack.get(args + 4)? as usize; - let (pa0, pa1) = { - let z = self.zp1(); - (*z.points.get(a0)?, *z.points.get(a1)?) - }; - let (pb0, pb1) = { - let z = self.zp0(); - (*z.points.get(b0)?, *z.points.get(b1)?) - }; - let dbx = pb1.x - pb0.x; - let dby = pb1.y - pb0.y; - let dax = pa1.x - pa0.x; - let day = pa1.y - pa0.y; - let dx = pb0.x - pa0.x; - let dy = pb0.y - pa0.y; - let discriminant = muldiv(dax, -dby, 0x40) + muldiv(day, dbx, 0x40); - let dp = muldiv(dax, dbx, 0x40) + muldiv(day, dby, 0x40); - if 19 * discriminant.abs() > dp.abs() { - let v = muldiv(dx, -dby, 0x40) + muldiv(dy, dbx, 0x40); - let x = muldiv(v, dax, discriminant); - let y = muldiv(v, day, discriminant); - let p = self.zp2_mut().points.get_mut(point)?; - p.x = pa0.x + x; - p.y = pa0.y + y; - } else { - let p = self.zp2_mut().points.get_mut(point)?; - p.x = (pa0.x + pa1.x + pb0.x + pb1.x) / 4; - p.y = (pa0.y + pa1.y + pb0.y + pb1.y) / 4; - } - *self.zp2_mut().tags.get_mut(point)? |= TOUCH_X | TOUCH_Y; - } - OP_SRP0 => rp0 = *self.stack.get(a0)? as usize, - OP_SRP1 => rp1 = *self.stack.get(a0)? as usize, - OP_SRP2 => rp2 = *self.stack.get(a0)? as usize, - OP_SZP0 => { - let z = *self.stack.get(a0)? as u8; - if z > 1 { - return None; - } else { - self.zp0 = z; - } - } - OP_SZP1 => { - let z = *self.stack.get(a0)? as u8; - if z > 1 { - return None; - } else { - self.zp1 = z; - } - } - OP_SZP2 => { - let z = *self.stack.get(a0)? as u8; - if z > 1 { - return None; - } else { - self.zp2 = z; - } - } - OP_SZPS => { - let z = *self.stack.get(a0)? as u8; - if z > 1 { - return None; - } else { - self.zp0 = z; - self.zp1 = z; - self.zp2 = z; - } - } - OP_SLOOP => { - let c = *self.stack.get(a0)?; - if c < 0 { - return None; - } else { - iloop = (c as u32).min(0xFFFF); - } - } - OP_RTG => self.round_state = ROUND_TO_GRID, - OP_RTHG => self.round_state = ROUND_TO_HALF_GRID, - OP_SMD => state.gs.min_distance = *self.stack.get(a0)?, - OP_ELSE => { - let mut n = 1; - next_pc = pc; - while n != 0 && self.skip_instruction(code, next_pc, &mut next_pc)? { - match code[next_pc] { - OP_IF => n += 1, - OP_EIF => n -= 1, - _ => {} - } - } - next_pc += 1; - } - OP_SCVTCI => state.gs.control_value_cutin = *self.stack.get(a0)?, - OP_SSWCI => state.gs.single_width_cutin = *self.stack.get(a0)?, - OP_SSW => state.gs.single_width = *self.stack.get(a0)?, - OP_DUP => *self.stack.get_mut(a1)? = *self.stack.get(a0)?, - OP_POP => {} - OP_CLEAR => new_top = 0, - OP_SWAP => { - let s = &mut *self.stack; - let t = *s.get(a0)?; - *s.get_mut(a0)? = *s.get(a1)?; - *s.get_mut(a1)? = t; - } - OP_DEPTH => *self.stack.get_mut(a0)? = stack_top as i32, - OP_CINDEX => { - let index = *self.stack.get(a0)? as usize; - if a0 == 0 || index > a0 { - return None; - } else { - let v = *self.stack.get(a0 - index)?; - *self.stack.get_mut(a0)? = v; - } - } - OP_MINDEX => { - let index = *self.stack.get(a0)? as usize; - if a0 == 0 || index > a0 { - return None; - } else { - let s = &mut *self.stack; - let e = *s.get(a0 - index)?; - for i in (a0 - index)..(a0 - 1) { - let v = *s.get(i + 1)?; - *s.get_mut(i)? = v; - } - *s.get_mut(a0 - 1)? = e; - } - } - OP_ALIGNPTS => { - let p1 = *self.stack.get(a0)? as usize; - let p2 = *self.stack.get(a1)? as usize; - let distance = - self.project(*self.zp0().points.get(p2)?, *self.zp1().points.get(p1)?) / 2; - self.move_point(self.zp1, p1, distance); - self.move_point(self.zp0, p2, -distance); - } - OP_UTP => { - let point = *self.stack.get(a0)? as usize; - let mut mask = 0xFF; - if self.fv.x != 0 { - mask &= !TOUCH_X; - } - if self.fv.y != 0 { - mask &= !TOUCH_Y; - } - *self.zp0_mut().tags.get_mut(point)? &= mask; - } - OP_LOOPCALL | OP_CALL => { - let (n, count) = if opcode == OP_LOOPCALL { - (*self.stack.get(a1)? as usize, *self.stack.get(a0)?) - } else { - (*self.stack.get(a0)? as usize, 1) - }; - if callstack_top >= callstack_len { - return None; - } - if count > 0 { - let def = self.fdefs.get(n)?; - if !def.active { - return None; - } - let rec = CallRecord { - caller_program: program, - caller_ip: pc + 1, - current_count: count as u32, - definition: *def, - }; - callstack[callstack_top] = rec; - callstack_top += 1; - program = def.program; - code = programs[program as usize]; - next_pc = def.offset as usize; - } - } - OP_FDEF => { - let n = *self.stack.get(a0)? as usize; - if program == 2 || n >= self.fdefs.len() { - return None; - } - let mut def = *self.fdefs.get(n)?; - def.active = true; - def.program = program; - def.offset = (pc + 1) as u32; - def.end = def.offset; - next_pc = pc; - while self.skip_instruction(code, next_pc, &mut next_pc)? { - match code[next_pc] { - 0x89 | 0x2C => { - return None; - } - 0x2D => { - def.end = next_pc as u32; - self.fdefs[n] = def; - break; - } - _ => {} - } - } - next_pc += 1; - } - OP_ENDF => { - if callstack_top == 0 { - return None; - } - let rec = callstack.get_mut(callstack_top - 1)?; - if rec.current_count > 1 { - rec.current_count -= 1; - next_pc = rec.definition.offset as usize; - } else { - program = rec.caller_program; - code = *programs.get(program as usize)?; - next_pc = rec.caller_ip; - callstack_top -= 1; - } - } - OP_MDAP0 | OP_MDAP1 => { - let point = *self.stack.get(a0)? as usize; - let mut distance = 0; - if (opcode & 1) != 0 { - let c = self.fast_project(*self.zp0().points.get(point)?); - distance = self.round(c) - c; - } - self.move_point(self.zp0, point, distance)?; - rp0 = point; - rp1 = point; - } - OP_IUP0 | OP_IUP1 => { - let is_x = (opcode & 1) != 0; - let mut run = !self.glyph.contours.is_empty(); - if !self.v35 && self.compat { - if self.iupx && self.iupy { - run = false; - } - if is_x { - self.iupx = true; - } else { - self.iupy = true; - } - } - if run { - let mask = if is_x { TOUCH_X } else { TOUCH_Y }; - let mut point = 0; - for i in 0..self.glyph.contours.len() { - let mut end_point = *self.glyph.contours.get(i)? as usize; - let first_point = point; - if end_point >= self.glyph.points.len() { - end_point = self.glyph.points.len() - 1; - } - while point <= end_point && (*self.glyph.tags.get(point)? & mask) == 0 { - point += 1; - } - if point <= end_point { - let first_touched = point; - let mut cur_touched = point; - point += 1; - while point <= end_point { - if (*self.glyph.tags.get(point)? & mask) != 0 { - self.glyph.interpolate( - is_x, - cur_touched + 1, - point - 1, - cur_touched, - point, - )?; - cur_touched = point; - } - point += 1; - } - if cur_touched == first_touched { - self.glyph - .shift(is_x, first_point, end_point, cur_touched)?; - } else { - self.glyph.interpolate( - is_x, - cur_touched + 1, - end_point, - cur_touched, - first_touched, - )?; - if first_touched > 0 { - self.glyph.interpolate( - is_x, - first_point, - first_touched - 1, - cur_touched, - first_touched, - )?; - } - } - } - } - } - } - OP_SHP0 | OP_SHP1 => { - if stack_top < iloop as usize { - return None; - } - let (dx, dy, _, _) = self.compute_point_displacement(opcode, rp1, rp2)?; - while iloop > 0 { - args_top -= 1; - let index = *self.stack.get(args_top)? as usize; - self.move_zp2_point(index, dx, dy, true)?; - iloop -= 1; - } - iloop = 1; - new_top = args_top; - } - OP_SHC0 | OP_SHC1 => { - let contour = *self.stack.get(a0)? as usize; - let bound = if self.zp2 == 0 { - 1 - } else { - self.zp2().contours.len() - }; - if contour >= bound { - return None; - } - let (dx, dy, zone, index) = - self.compute_point_displacement(opcode, rp1, rp2)?; - let mut start = 0; - if contour != 0 { - let z = self.zp2(); - start = *z.contours.get(contour - 1)? as usize + 1; - } - let limit; - if self.zp2 == 0 { - limit = self.zp2().points.len(); - } else { - let z = self.zp2(); - limit = *z.contours.get(contour)? as usize + 1; - } - for i in start..limit { - if zone != self.zp2 || index != i { - self.move_zp2_point(i, dx, dy, true)?; - } - } - } - OP_SHZ0 | OP_SHZ1 => { - if *self.stack.get(a0)? >= 2 { - return None; - } - let (dx, dy, zone, index) = - self.compute_point_displacement(opcode, rp1, rp2)?; - let limit = if self.zp2 == 0 { - self.zp2().points.len() - } else if self.zp2 == 1 && !self.zp2().contours.is_empty() { - let z = self.zp2(); - *z.contours.get(z.contours.len() - 1)? as usize + 1 - } else { - 0 - }; - for i in 0..limit { - if zone != self.zp2 || i != index { - self.move_zp2_point(i, dx, dy, false)?; - } - } - } - OP_SHPIX => { - if stack_top < iloop as usize + 1 { - return None; - } - let in_twilight = self.zp0 == 0 || self.zp1 == 0 || self.zp2 == 0; - let a = *self.stack.get(a0)?; - let dx = mul14(a, self.fv.x as i32); - let dy = mul14(a, self.fv.y as i32); - while iloop > 0 { - args_top -= 1; - let point = *self.stack.get(args_top)? as usize; - if !self.v35 && self.compat { - if in_twilight - || (!(self.iupx && self.iupy) - && ((composite && self.fv.y != 0) - || (*self.zp2().tags.get(point)? & TOUCH_Y != 0))) - { - self.move_zp2_point(point, dx, dy, true)?; - } - } else { - self.move_zp2_point(point, dx, dy, true)?; - } - iloop -= 1; - } - iloop = 1; - new_top = args_top; - } - OP_IP => { - if stack_top < iloop as usize { - return None; - } - let in_twilight = self.zp0 == 0 || self.zp1 == 0 || self.zp2 == 0; - let orus_base = if in_twilight { - *self.zp0().original.get(rp1)? - } else { - *self.zp0().unscaled.get(rp1)? - }; - let cur_base = *self.zp0().points.get(rp1)?; - let old_range = if in_twilight { - self.dual_project(*self.zp1().original.get(rp2)?, orus_base) - } else { - self.dual_project(*self.zp1().unscaled.get(rp2)?, orus_base) - }; - let cur_range = self.project(*self.zp1().points.get(rp2)?, cur_base); - while iloop > 0 { - iloop -= 1; - args_top -= 1; - let point = *self.stack.get(args_top)? as usize; - let original_distance = if in_twilight { - self.dual_project(*self.zp2().original.get(point)?, orus_base) - } else { - self.dual_project(*self.zp2().unscaled.get(point)?, orus_base) - }; - let cur_distance = self.project(*self.zp2().points.get(point)?, cur_base); - let mut new_distance = 0; - if original_distance != 0 { - if old_range != 0 { - new_distance = muldiv(original_distance, cur_range, old_range); - } else { - new_distance = original_distance; - } - } - self.move_point(self.zp2, point, new_distance - cur_distance)?; - } - iloop = 1; - new_top = args_top; - } - OP_MSIRP0 | OP_MSIRP1 => { - let point = *self.stack.get(args)? as usize; - if self.zp1 == 0 { - *self.zp1_mut().points.get_mut(point)? = *self.zp0().original.get(rp0)?; - self.move_original(self.zp1, point, *self.stack.get(args + 1)?)?; - *self.zp1_mut().points.get_mut(point)? = *self.zp1().original.get(point)?; - } - let d = - self.project(*self.zp1().points.get(point)?, *self.zp0().points.get(rp0)?); - let a = *self.stack.get(args + 1)?; - self.move_point(self.zp1, point, a.wrapping_sub(d))?; - rp1 = rp0; - rp2 = point; - if (opcode & 1) != 0 { - rp0 = point; - } - } - OP_ALIGNRP => { - if stack_top < iloop as usize { - return None; - } - while iloop > 0 { - args_top -= 1; - let point = *self.stack.get(args_top)? as usize; - let distance = self - .project(*self.zp1().points.get(point)?, *self.zp0().points.get(rp0)?); - self.move_point(self.zp1, point, -distance)?; - iloop -= 1; - } - iloop = 1; - new_top = args_top; - } - OP_RTDG => self.round_state = ROUND_TO_DOUBLE_GRID, - OP_MIAP0 | OP_MIAP1 => { - let point = *self.stack.get(a0)? as usize; - let cvt_entry = *self.stack.get(a1)? as usize; - let mut distance = *self.cvt.get(cvt_entry)?; - if self.zp0 == 0 { - let f = self.fv; - let z = self.zp0_mut(); - let p = z.points.get_mut(point)?; - let o = z.original.get_mut(point)?; - o.x = mul14(distance, f.x as i32); - o.y = mul14(distance, f.y as i32); - *p = *o; - } - let original_distance = self.fast_project(*self.zp0().points.get(point)?); - if (opcode & 1) != 0 { - let delta = (distance - original_distance).abs(); - if delta > state.gs.control_value_cutin { - distance = original_distance; - } - distance = self.round(distance); - } - self.move_point(self.zp0, point, distance - original_distance)?; - rp0 = point; - rp1 = point; - } - OP_NPUSHB => { - let count = code[pc + 1] as usize; - for (sp, cp) in self - .stack - .get_mut(a0..a0 + count)? - .iter_mut() - .zip(code.get(pc + 2..)?) - { - *sp = *cp as u32 as i32; - } - new_top += count; - } - OP_NPUSHW => { - let count = code[pc + 1] as usize; - for (sp, cp) in self - .stack - .get_mut(a0..a0 + count)? - .iter_mut() - .zip(code.get(pc + 2..)?.chunks(2)) - { - let word = ((*cp.get(0)? as u16) << 8) | *cp.get(1)? as u16; - *sp = word as i16 as i32; - } - new_top += count; - } - OP_WS => { - let index = *self.stack.get(a0)? as usize; - *self.store.get_mut(index)? = *self.stack.get(a1)?; - } - OP_RS => { - let sp = self.stack.get_mut(a0)?; - let index = *sp as usize; - *sp = *self.store.get(index)?; - } - OP_WCVTP => { - let index = *self.stack.get(a0)? as usize; - *self.cvt.get_mut(index)? = *self.stack.get(a1)?; - } - OP_WCVTF => { - let index = *self.stack.get(a0)? as usize; - *self.cvt.get_mut(index)? = mul(*self.stack.get(a1)?, self.scale); - } - OP_RCVT => { - let sp = self.stack.get_mut(a0)?; - let index = *sp as usize; - *sp = *self.cvt.get(index)?; - } - OP_GC0 | OP_GC1 => { - let index = *self.stack.get(a0)? as usize; - let r = if (opcode & 1) != 0 { - self.fast_dual_project(*self.zp2().original.get(index)?) - } else { - self.fast_project(*self.zp2().points.get(index)?) - }; - *self.stack.get_mut(a0)? = r; - } - OP_SCFS => { - let index = *self.stack.get(a0)? as usize; - let a = self.fast_project(*self.zp2().points.get(index)?); - self.move_point(self.zp2, index, self.stack.get(a1)?.wrapping_sub(a))?; - if self.zp2 == 0 { - *self.twilight.original.get_mut(index)? = - *self.twilight.points.get(index)?; - } - } - OP_MD0 | OP_MD1 => { - let a = *self.stack.get(a1)? as usize; - let b = *self.stack.get(a0)? as usize; - let d = if (opcode & 1) != 0 { - let v1 = self.zp0().points.get(b)?; - let v2 = self.zp1().points.get(a)?; - self.project(*v1, *v2) - } else if self.zp0 == 0 || self.zp1 == 0 { - let v1 = self.zp0().original.get(b)?; - let v2 = self.zp1().original.get(a)?; - self.dual_project(*v1, *v2) - } else { - let v1 = self.zp0().unscaled.get(b)?; - let v2 = self.zp1().unscaled.get(a)?; - mul(self.dual_project(*v1, *v2), self.yscale) - }; - *self.stack.get_mut(a0)? = d; - } - OP_MPPEM => { - *self.stack.get_mut(a0)? = self.ppem as i32; - } - OP_MPS => { - *self.stack.get_mut(a0)? = if self.v35 { - self.ppem as i32 - } else { - self.point_size - }; - } - OP_FLIPON => state.gs.auto_flip = true, - OP_FLIPOFF => state.gs.auto_flip = false, - OP_DEBUG => {} - OP_LT => { - *self.stack.get_mut(a0)? = (*self.stack.get(a0)? < *self.stack.get(a1)?) as i32 - } - OP_LTEQ => { - *self.stack.get_mut(a0)? = (*self.stack.get(a0)? <= *self.stack.get(a1)?) as i32 - } - OP_GT => { - *self.stack.get_mut(a0)? = (*self.stack.get(a0)? > *self.stack.get(a1)?) as i32 - } - OP_GTEQ => { - *self.stack.get_mut(a0)? = (*self.stack.get(a0)? >= *self.stack.get(a1)?) as i32 - } - OP_EQ => { - *self.stack.get_mut(a0)? = (*self.stack.get(a0)? == *self.stack.get(a1)?) as i32 - } - OP_NEQ => { - *self.stack.get_mut(a0)? = (*self.stack.get(a0)? != *self.stack.get(a1)?) as i32 - } - OP_ODD => { - *self.stack.get_mut(a0)? = (self.round(*self.stack.get(a0)?) & 127 == 64) as i32 - } - OP_EVEN => { - *self.stack.get_mut(a0)? = (self.round(*self.stack.get(a0)?) & 127 == 0) as i32 - } - OP_IF => { - if *self.stack.get(a0)? == 0 { - let mut n = 1; - let mut out = false; - next_pc = pc; - while !out && self.skip_instruction(code, next_pc, &mut next_pc)? { - match *code.get(next_pc)? { - OP_IF => n += 1, - OP_ELSE => out = n == 1, - OP_EIF => { - n -= 1; - out = n == 0; - } - _ => {} - } - } - next_pc += 1; - } - } - OP_EIF => {} - OP_AND => { - *self.stack.get_mut(a0)? = - (*self.stack.get(a0)? != 0 && *self.stack.get(a1)? != 0) as i32 - } - OP_OR => { - *self.stack.get_mut(a0)? = - (*self.stack.get(a0)? != 0 || *self.stack.get(a1)? != 0) as i32 - } - OP_NOT => *self.stack.get_mut(a0)? = (*self.stack.get(a0)? == 0) as i32, - OP_SDB => state.gs.delta_base = *self.stack.get(a0)? as u16, - OP_SDS => state.gs.delta_shift = (*self.stack.get(a0)?).min(6) as u16, - OP_ADD => *self.stack.get_mut(a0)? += *self.stack.get(a1)?, - OP_SUB => *self.stack.get_mut(a0)? -= *self.stack.get(a1)?, - OP_DIV => { - let d = *self.stack.get(a1)?; - if d == 0 { - return None; - } - let sp = self.stack.get_mut(a0)?; - *sp = muldiv_no_round(*sp, 64, d); - } - OP_MUL => { - *self.stack.get_mut(a0)? = - muldiv(*self.stack.get(a0)?, *self.stack.get(a1)?, 64) - } - OP_ABS => *self.stack.get_mut(a0)? = (*self.stack.get(a0)?).abs(), - OP_NEG => *self.stack.get_mut(a0)? = -*self.stack.get(a0)?, - OP_FLOOR => *self.stack.get_mut(a0)? = floor(*self.stack.get(a0)?), - OP_CEILING => *self.stack.get_mut(a0)? = ceil(*self.stack.get(a0)?), - OP_ROUND00..=OP_ROUND11 => { - *self.stack.get_mut(a0)? = self.round(*self.stack.get(a0)?) - } - OP_NROUND00..=OP_NROUND11 => {} - OP_DELTAP1 | OP_DELTAP2 | OP_DELTAP3 => { - let p = self.ppem as u32; - let nump = *self.stack.get(a0)? as u32; - let bias = match opcode { - OP_DELTAP2 => 16, - OP_DELTAP3 => 32, - _ => 0, - } + state.gs.delta_base as u32; - for _ in 1..=nump { - if args_top < 2 { - return None; - } - args_top -= 2; - let a = *self.stack.get(args_top + 1)? as usize; - if a >= self.zp0().points.len() { - continue; - } - let mut b = *self.stack.get(args_top)?; - let mut c = (b as u32 & 0xF0) >> 4; - c += bias; - if p == c { - b = (b & 0xF) - 8; - if b >= 0 { - b += 1; - } - b *= 1 << (6 - state.gs.delta_shift as i32); - if !self.v35 && self.compat { - if !(self.iupx && self.iupy) - && ((composite && self.fv.y != 0) - || (*self.zp0().tags.get(a)? & TOUCH_Y) != 0) - { - self.move_point(self.zp0, a, b)?; - } - } else { - self.move_point(self.zp0, a, b)?; - } - } - } - new_top = args_top; - } - OP_DELTAC1 | OP_DELTAC2 | OP_DELTAC3 => { - let p = self.ppem as u32; - let nump = *self.stack.get(args)? as u32; - let bias = match opcode { - OP_DELTAC2 => 16, - OP_DELTAC3 => 32, - _ => 0, - } + state.gs.delta_base as u32; - for _ in 1..=nump { - if args_top < 2 { - return None; - } - args_top -= 2; - let a = *self.stack.get(args_top + 1)? as usize; - let mut b = *self.stack.get(args_top)?; - let mut c = (b as u32 & 0xF0) >> 4; - c += bias; - if p == c { - b = (b & 0xF) - 8; - if b >= 0 { - b += 1; - } - b *= 1 << (6 - state.gs.delta_shift as i32); - *self.cvt.get_mut(a)? += b; - } - } - new_top = args_top; - } - OP_SROUND | OP_S45ROUND => { - let selector = *self.stack.get(a0)?; - let grid_period = if opcode == OP_SROUND { - self.round_state = ROUND_SUPER; - 0x4000 - } else { - self.round_state = ROUND_SUPER45; - 0x2D41 - }; - match selector & 0xC0 { - 0 => self.round_period = grid_period / 2, - 0x40 => self.round_period = grid_period, - 0x80 => self.round_period = grid_period * 2, - 0xC0 => self.round_period = grid_period, - _ => {} - } - match selector & 0x30 { - 0 => self.round_phase = 0, - 0x10 => self.round_phase = self.round_period / 4, - 0x20 => self.round_phase = self.round_period / 2, - 0x30 => self.round_phase = self.round_period * 3 / 4, - _ => {} - } - if (selector & 0x0F) == 0 { - self.round_threshold = self.round_period - 1; - } else { - self.round_threshold = ((selector & 0x0F) - 4) * self.round_period / 8; - } - self.round_period >>= 8; - self.round_phase >>= 8; - self.round_threshold >>= 8; - } - OP_JMPR | OP_JROT | OP_JROF => { - let cond = match opcode { - OP_JROT => *self.stack.get(a1)? != 0, - OP_JROF => *self.stack.get(a1)? == 0, - _ => true, - }; - if cond { - let o = *self.stack.get(a0)?; - if o == 0 && args == 0 { - return None; - } - if o < 0 { - next_pc = pc - (-o) as usize; - } else { - next_pc = pc + o as usize; - } - if callstack_top > 0 - && next_pc > callstack[callstack_top - 1].definition.end as usize - { - return None; - } - } - } - OP_ROFF => self.round_state = ROUND_OFF, - OP_RUTG => self.round_state = ROUND_UP_TO_GRID, - OP_RDTG => self.round_state = ROUND_DOWN_TO_GRID, - OP_SANGW => {} - OP_AA => {} - OP_FLIPPT => { - if !self.v35 && self.compat && self.iupx && self.iupy { - // nothing - } else if stack_top < iloop as usize { - return None; - } else { - while iloop > 0 { - args_top -= 1; - let point = *self.stack.get(args_top)? as usize; - *self.glyph.tags.get_mut(point)? ^= 1; - iloop -= 1; - } - } - iloop = 1; - new_top = args_top; - } - OP_FLIPRGON | OP_FLIPRGOFF => { - if !self.v35 && self.compat && self.iupx && self.iupy { - // nothing - } else { - let a = *self.stack.get(a1)? as usize; - let b = *self.stack.get(a0)? as usize; - if b > a { - return None; - } - if opcode == OP_FLIPRGON { - for tag in self.glyph.tags.get_mut(b..=a)? { - *tag |= 1; - } - } else { - for tag in self.glyph.tags.get_mut(b..=a)? { - *tag &= !1; - } - } - } - } - OP_SCANCTRL => { - let a = *self.stack.get(a0)? as u16; - let b = a & 0xFF; - let scan_control = &mut state.gs.scan_control; - if b == 0xFF { - *scan_control = true; - } else if b == 0 { - *scan_control = false; - } else { - if (a & 0x100) != 0 && self.ppem <= b { - *scan_control = true; - } - if (a & 0x200) != 0 && self.rotated { - *scan_control = true; - } - if (a & 0x800) != 0 && self.ppem > b { - *scan_control = false; - } - if (a & 0x1000) != 0 && self.rotated { - *scan_control = false; - } - } - } - OP_SDPVTL0 | OP_SDPVTL1 => { - let mut op = opcode; - let p1 = *self.stack.get(a1)? as usize; - let p2 = *self.stack.get(a0)? as usize; - let mut a; - let mut b; - { - let v1 = self.zp1().original.get(p2)?; - let v2 = self.zp2().original.get(p1)?; - a = v1.x - v2.x; - b = v1.y - v2.y; - if a == 0 && b == 0 { - a = 0x4000; - op = 0; - } - } - if (op & 1) != 0 { - let c = b; - b = a; - a = -c; - } - let mut v = self.dv; - self.normalize(a, b, &mut v); - self.dv = v; - { - let v1 = self.zp1().points.get(p2)?; - let v2 = self.zp2().points.get(p1)?; - a = v1.x - v2.x; - b = v1.y - v2.y; - if a == 0 && b == 0 { - a = 0x4000; - op = 0; - } - } - if (op & 1) != 0 { - let c = b; - b = a; - a = -c; - } - let mut v = self.pv; - self.normalize(a, b, &mut v); - self.pv = v; - self.vectors_changed(); - } - OP_GETINFO => { - let a = *self.stack.get(a0)?; - let mut k = 0; - if (a & 1) != 0 { - k = if self.v35 { 35 } else { 42 }; - } - if (a & 2) != 0 && self.rotated { - k |= 1 << 8; - } - if (a & 8) != 0 && !self.coords.is_empty() { - k |= 1 << 10; - } - if (a & 32) != 0 && grayscale { - k |= 1 << 12; - } - if !self.v35 && self.subpixel { - if (a & 64) != 0 { - k |= 1 << 13; - } - // if (a & 256) != 0 && false - // /* self.vertical_lcd */ - // { - // k |= 1 << 15; - // } - if (a & 1024) != 0 { - k |= 1 << 17; - } - // if (a & 2048) != 0 && self.subpixel { - // k |= 1 << 18; - // } - if (a & 4096) != 0 && grayscale_cleartype { - k |= 1 << 19; - } - } - *self.stack.get_mut(a0)? = k; - } - OP_IDEF => { - if program == 2 { - return None; - } - let n = *self.stack.get(args)? as usize; - let mut index = !0; - for i in 0..self.idefs.len() { - if !self.idefs[i].active { - index = i; - break; - } - } - if index == !0 { - return None; - } - let mut def = self.idefs[index]; - def.program = program; - def.opcode = n as u16; - def.offset = pc as u32 + 1; - def.active = true; - next_pc = pc; - while self.skip_instruction(code, next_pc, &mut next_pc)? { - match *code.get(next_pc)? { - 0x89 | 0x2C => { - return None; - } - 0x2D => { - def.end = next_pc as u32; - self.idefs[index] = def; - break; - } - _ => {} - } - } - next_pc += 1; - } - OP_ROLL => { - let s = &mut *self.stack; - let (a, b, c) = (*s.get(a2)?, *s.get(a1)?, *s.get(a0)?); - *s.get_mut(a2)? = c; - *s.get_mut(a1)? = a; - *s.get_mut(a0)? = b; - } - OP_MAX => { - *self.stack.get_mut(a0)? = (*self.stack.get(a0)?).max(*self.stack.get(a1)?) - } - OP_MIN => { - *self.stack.get_mut(a0)? = (*self.stack.get(a0)?).min(*self.stack.get(a1)?) - } - OP_SCANTYPE => { - let a = *self.stack.get(a0)?; - if a >= 0 { - state.gs.scan_type = a & 0xFFFF; - } - } - OP_INSTCTRL => { - let a = *self.stack.get(a1)? as u32; - let b = *self.stack.get(a0)? as u32; - let af = 1 << (a - 1); - if !(1..=3).contains(&a) || (b != 0 && b != af) { - // nothing - } else { - state.gs.instruct_control &= !(af as u8); - state.gs.instruct_control |= b as u8; - if a == 3 && !self.v35 && state.mode != HinterMode::Modern { - self.compat = b != 4; - } - } - } - OP_PUSHB000..=OP_PUSHB111 => { - let count = (opcode - 0xB0 + 1) as usize; - for (sp, cp) in (self.stack.get_mut(a0..a0 + count)?) - .iter_mut() - .zip(code.get(pc + 1..)?) - { - *sp = *cp as u32 as i32; - } - } - OP_PUSHW000..=OP_PUSHW111 => { - let count = (opcode - 0xB8 + 1) as usize; - for (sp, cp) in (self.stack.get_mut(a0..a0 + count)?) - .iter_mut() - .zip(code.get(pc + 1..)?.chunks(2)) - { - let word = ((cp[0] as u16) << 8) | cp[1] as u16; - *sp = word as i16 as i32; - } - } - OP_MDRP00000..=OP_MDRP11111 => { - let point = *self.stack.get(args)? as usize; - let mut original_distance; - if self.zp0 == 0 || self.zp1 == 0 { - original_distance = self.dual_project( - *self.zp1().original.get(point)?, - *self.zp0().original.get(rp0)?, - ); - } else { - let v1 = self.zp1().unscaled.get(point)?; - let v2 = self.zp0().unscaled.get(rp0)?; - original_distance = self.dual_project(*v1, *v2); - original_distance = mul(original_distance, self.yscale); - } - let cutin = state.gs.single_width_cutin; - let value = state.gs.single_width; - if cutin > 0 - && original_distance < value + cutin - && original_distance > value - cutin - { - original_distance = if original_distance >= 0 { - value - } else { - -value - }; - } - let mut distance = if (opcode & 4) != 0 { - self.round(original_distance) - } else { - original_distance - }; - let min_distance = state.gs.min_distance; - if (opcode & 8) != 0 { - if original_distance >= 0 { - if distance < min_distance { - distance = min_distance; - } - } else if distance > -min_distance { - distance = -min_distance; - } - } - original_distance = - self.project(*self.zp1().points.get(point)?, *self.zp0().points.get(rp0)?); - self.move_point(self.zp1, point, distance.wrapping_sub(original_distance))?; - rp1 = rp0; - rp2 = point; - if (opcode & 16) != 0 { - rp0 = point; - } - } - OP_MIRP00000..=OP_MIRP11111 => { - let point = *self.stack.get(a0)? as usize; - let cvt_entry = (*self.stack.get(a1)? + 1) as usize; - let mut cvt_distance = if cvt_entry == 0 { - 0 - } else { - *self.cvt.get(cvt_entry - 1)? - }; - let cutin = state.gs.single_width_cutin; - let value = state.gs.single_width; - let mut delta = (cvt_distance - value).abs(); - if delta < cutin { - cvt_distance = if cvt_distance >= 0 { value } else { -value }; - } - if self.zp1 == 0 { - let fv = self.fv; - let p = { - let p2 = *self.zp0().original.get(rp0)?; - let p1 = self.zp1_mut().original.get_mut(point)?; - p1.x = p2.x + mul(cvt_distance, fv.x as i32); - p1.y = p2.y + mul(cvt_distance, fv.y as i32); - *p1 - }; - *self.zp1_mut().points.get_mut(point)? = p; - } - let original_distance = self.dual_project( - *self.zp1().original.get(point)?, - *self.zp0().original.get(rp0)?, - ); - let current_distance = - self.project(*self.zp1().points.get(point)?, *self.zp0().points.get(rp0)?); - if state.gs.auto_flip && (original_distance ^ cvt_distance) < 0 { - cvt_distance = -cvt_distance; - } - let mut distance = if (opcode & 4) != 0 { - if self.zp0 == self.zp1 { - delta = (cvt_distance - original_distance).abs(); - if delta > state.gs.control_value_cutin { - cvt_distance = original_distance; - } - } - self.round(cvt_distance) - } else { - cvt_distance - }; - let min_distance = state.gs.min_distance; - if (opcode & 8) != 0 { - if original_distance >= 0 { - if distance < min_distance { - distance = min_distance - }; - } else if distance > -min_distance { - distance = -min_distance - } - } - self.move_point(self.zp1, point, distance.wrapping_sub(current_distance))?; - rp1 = rp0; - if (opcode & 16) != 0 { - rp0 = point; - } - rp2 = point; - } - _ => { - let axis_count = self.axis_count as usize; - if axis_count != 0 && opcode == OP_GETVAR { - if stack_top + axis_count < self.stack.len() { - if axis_count == self.coords.len() { - for (sp, coord) in self - .stack - .get_mut(a0..a0 + axis_count)? - .iter_mut() - .zip(self.coords) - { - *sp = *coord as i32; - } - } else { - for sp in self.stack.get_mut(a0..a0 + axis_count)? { - *sp = 0; - } - } - new_top = stack_top + axis_count; - } else { - return None; - } - } else if axis_count != 0 && opcode == 0x92 { - *self.stack.get_mut(a0)? = 17; - } else { - let mut index = !0; - for i in 0..self.idefs.len() { - let idef = &self.idefs[i]; - if idef.active && idef.opcode == opcode as u16 { - index = i; - break; - } - } - if index != !0 && callstack_top < callstack_len { - let def = self.idefs[index]; - let rec = CallRecord { - caller_program: program, - caller_ip: pc + 1, - current_count: count as u32, - definition: def, - }; - callstack[callstack_top] = rec; - callstack_top += 1; - program = def.program; - code = *programs.get(program as usize)?; - next_pc = def.offset as usize; - } else { - return None; - } - } - } - } - - if TRACE { - // if trpt < self.glyph.points.len() { - // println!( - // ">>>>>> {}, {}", - // self.glyph.points[trpt].x, self.glyph.points[trpt].y - // ); - // } - } - - count += 1; - stack_top = new_top; - pc = next_pc; - if pc >= code.len() { - if callstack_top > 0 { - return None; - } - break; - } - } - Some(count) - } -} - -#[derive(Clone, Copy)] -struct GraphicsState { - auto_flip: bool, - control_value_cutin: i32, - delta_base: u16, - delta_shift: u16, - instruct_control: u8, - min_distance: i32, - scan_control: bool, - scan_type: i32, - single_width_cutin: i32, - single_width: i32, -} - -const DEFAULT_GRAPHICS_STATE: GraphicsState = GraphicsState { - auto_flip: true, - control_value_cutin: 68, - delta_base: 9, - delta_shift: 3, - instruct_control: 0, - min_distance: 64, - scan_control: false, - scan_type: 0, - single_width_cutin: 0, - single_width: 0, -}; - -use math::*; - -mod math { - pub fn floor(x: i32) -> i32 { - x & !63 - } - - pub fn ceil(x: i32) -> i32 { - floor(x + 63) - } - - pub fn round(x: i32) -> i32 { - floor(x + 32) - } - - pub fn floor_pad(x: i32, n: i32) -> i32 { - x & !(n - 1) - } - - pub fn round_pad(x: i32, n: i32) -> i32 { - floor_pad(x + n / 2, n) - } - - #[inline(always)] - pub fn mul(a: i32, b: i32) -> i32 { - let ab = a as i64 * b as i64; - ((ab + 0x8000 - if ab < 0 { 1 } else { 0 }) >> 16) as i32 - } - - pub fn div(mut a: i32, mut b: i32) -> i32 { - let mut s = 1; - if a < 0 { - a = -a; - s = -1; - } - if b < 0 { - b = -b; - s = -s; - } - let q = if b == 0 { - 0x7FFFFFFF - } else { - ((((a as u64) << 16) + ((b as u64) >> 1)) / (b as u64)) as u32 - }; - if s < 0 { - -(q as i32) - } else { - q as i32 - } - } - - pub fn muldiv(mut a: i32, mut b: i32, mut c: i32) -> i32 { - let mut s = 1; - if a < 0 { - a = -a; - s = -1; - } - if b < 0 { - b = -b; - s = -s; - } - if c < 0 { - c = -c; - s = -s; - } - let d = if c > 0 { - ((a as i64) * (b as i64) + ((c as i64) >> 1)) / c as i64 - } else { - 0x7FFFFFFF - }; - if s < 0 { - -(d as i32) - } else { - d as i32 - } - } - - pub fn muldiv_no_round(mut a: i32, mut b: i32, mut c: i32) -> i32 { - let mut s = 1; - if a < 0 { - a = -a; - s = -1; - } - if b < 0 { - b = -b; - s = -s; - } - if c < 0 { - c = -c; - s = -s; - } - let d = if c > 0 { - ((a as i64) * (b as i64)) / c as i64 - } else { - 0x7FFFFFFF - }; - if s < 0 { - -(d as i32) - } else { - d as i32 - } - } - - pub fn mul14(a: i32, b: i32) -> i32 { - let mut v = a as i64 * b as i64; - v += 0x2000 + (v >> 63); - (v >> 14) as i32 - } - - pub fn dot14(ax: i32, ay: i32, bx: i32, by: i32) -> i32 { - let mut v1 = ax as i64 * bx as i64; - let v2 = ay as i64 * by as i64; - v1 += v2; - v1 += 0x2000 + (v1 >> 63); - (v1 >> 14) as i32 - } -} - -use ops::*; - -#[allow(dead_code)] -mod ops { - pub const OPCODE_LEN: [i8; 256] = [ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, -1, -2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, - 6, 7, 8, 9, 3, 5, 7, 9, 11, 13, 15, 17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - ]; - - #[rustfmt::skip] - #[allow(clippy::eq_op, clippy::identity_op)] - pub const OPCODE_POP_PUSH: [u8; 256] = [ - (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, - (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, - (0 << 4) | 2, (0 << 4) | 2, (0 << 4) | 0, (5 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, - (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, - (0 << 4) | 0, (0 << 4) | 0, (1 << 4) | 0, (0 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, - (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 2, (1 << 4) | 0, (0 << 4) | 0, (2 << 4) | 2, - (0 << 4) | 1, (1 << 4) | 1, (1 << 4) | 0, (2 << 4) | 0, (0 << 4) | 0, (1 << 4) | 0, - (2 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (0 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, - (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, - (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (0 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, - (0 << 4) | 0, (0 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, - (2 << 4) | 0, (1 << 4) | 1, (2 << 4) | 0, (1 << 4) | 1, (1 << 4) | 1, (1 << 4) | 1, - (2 << 4) | 0, (2 << 4) | 1, (2 << 4) | 1, (0 << 4) | 1, (0 << 4) | 1, (0 << 4) | 0, - (0 << 4) | 0, (1 << 4) | 0, (2 << 4) | 1, (2 << 4) | 1, (2 << 4) | 1, (2 << 4) | 1, - (2 << 4) | 1, (2 << 4) | 1, (1 << 4) | 1, (1 << 4) | 1, (1 << 4) | 0, (0 << 4) | 0, - (2 << 4) | 1, (2 << 4) | 1, (1 << 4) | 1, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, - (2 << 4) | 1, (2 << 4) | 1, (2 << 4) | 1, (2 << 4) | 1, (1 << 4) | 1, (1 << 4) | 1, - (1 << 4) | 1, (1 << 4) | 1, (1 << 4) | 1, (1 << 4) | 1, (1 << 4) | 1, (1 << 4) | 1, - (1 << 4) | 1, (1 << 4) | 1, (1 << 4) | 1, (1 << 4) | 1, (2 << 4) | 0, (1 << 4) | 0, - (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, - (2 << 4) | 0, (2 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, - (1 << 4) | 0, (1 << 4) | 0, (0 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (0 << 4) | 0, - (0 << 4) | 0, (1 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (1 << 4) | 1, (1 << 4) | 0, - (3 << 4) | 3, (2 << 4) | 1, (2 << 4) | 1, (1 << 4) | 0, (2 << 4) | 0, (0 << 4) | 0, - (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 1, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, - (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, - (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, - (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, - (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 0, - (0 << 4) | 0, (0 << 4) | 0, (0 << 4) | 1, (0 << 4) | 2, (0 << 4) | 3, (0 << 4) | 4, - (0 << 4) | 5, (0 << 4) | 6, (0 << 4) | 7, (0 << 4) | 8, (0 << 4) | 1, (0 << 4) | 2, - (0 << 4) | 3, (0 << 4) | 4, (0 << 4) | 5, (0 << 4) | 6, (0 << 4) | 7, (0 << 4) | 8, - (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, - (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, - (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, - (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, - (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, (1 << 4) | 0, - (1 << 4) | 0, (1 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, - (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, - (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, - (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, - (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, - (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, (2 << 4) | 0, - ]; - - #[rustfmt::skip] - pub const OPCODE_NAME: [&'static str; 256] = [ - "SVTCA0", "SVTCA1", "SPVTCA0", "SPVTCA1", "SFVTCA0", "SFVTCA1", "SPVTL0", "SPVTL1", "SFVTL0", - "SFVTL1", "SPVFS", "SFVFS", "GPV", "GFV", "SFVTPV", "ISECT", "SRP0", "SRP1", "SRP2", "SZP0", - "SZP1", "SZP2", "SZPS", "SLOOP", "RTG", "RTHG", "SMD", "ELSE", "JMPR", "SCVTCI", "SSWCI", - "SSW", "DUP", "POP", "CLEAR", "SWAP", "DEPTH", "CINDEX", "MINDEX", "ALIGNPTS", "OP28", "UTP", - "LOOPCALL", "CALL", "FDEF", "ENDF", "MDAP0", "MDAP1", "IUP0", "IUP1", "SHP0", "SHP1", "SHC0", - "SHC1", "SHZ0", "SHZ1", "SHPIX", "IP", "MSIRP0", "MSIRP1", "ALIGNRP", "RTDG", "MIAP0", "MIAP1", - "NPUSHB", "NPUSHW", "WS", "RS", "WCVTP", "RCVT", "GC0", "GC1", "SCFS", "MD0", "MD1", "MPPEM", - "MPS", "FLIPON", "FLIPOFF", "DEBUG", "LT", "LTEQ", "GT", "GTEQ", "EQ", "NEQ", "ODD", "EVEN", - "IF", "EIF", "AND", "OR", "NOT", "DELTAP1", "SDB", "SDS", "ADD", "SUB", "DIV", "MUL", "ABS", - "NEG", "FLOOR", "CEILING", "ROUND00", "ROUND01", "ROUND10", "ROUND11", "NROUND00", "NROUND01", - "NROUND10", "NROUND11", "WCVTF", "DELTAP2", "DELTAP3", "DELTAC1", "DELTAC2", "DELTAC3", - "SROUND", "S45ROUND", "JROT", "JROF", "ROFF", "OP7B", "RUTG", "RDTG", "SANGW", "AA", "FLIPPT", - "FLIPRGON", "FLIPRGOFF", "OP83", "OP84", "SCANCTRL", "SDPVTL0", "SDPVTL1", "GETINFO", "IDEF", - "ROLL", "MAX", "MIN", "SCANTYPE", "INSTCTRL", "OP8F", "OP90", "OP91", "OP92", "OP93", "OP94", - "OP95", "OP96", "OP97", "OP98", "OP99", "OP9A", "OP9B", "OP9C", "OP9D", "OP9E", "OP9F", "OPA0", - "OPA1", "OPA2", "OPA3", "OPA4", "OPA5", "OPA6", "OPA7", "OPA8", "OPA9", "OPAA", "OPAB", "OPAC", - "OPAD", "OPAE", "OPAF", "PUSHB000", "PUSHB001", "PUSHB010", "PUSHB011", "PUSHB100", "PUSHB101", - "PUSHB110", "PUSHB111", "PUSHW000", "PUSHW001", "PUSHW010", "PUSHW011", "PUSHW100", "PUSHW101", - "PUSHW110", "PUSHW111", "MDRP00000", "MDRP00001", "MDRP00010", "MDRP00011", "MDRP00100", - "MDRP00101", "MDRP00110", "MDRP00111", "MDRP01000", "MDRP01001", "MDRP01010", "MDRP01011", - "MDRP01100", "MDRP01101", "MDRP01110", "MDRP01111", "MDRP10000", "MDRP10001", "MDRP10010", - "MDRP10011", "MDRP10100", "MDRP10101", "MDRP10110", "MDRP10111", "MDRP11000", "MDRP11001", - "MDRP11010", "MDRP11011", "MDRP11100", "MDRP11101", "MDRP11110", "MDRP11111", "MIRP00000", - "MIRP00001", "MIRP00010", "MIRP00011", "MIRP00100", "MIRP00101", "MIRP00110", "MIRP00111", - "MIRP01000", "MIRP01001", "MIRP01010", "MIRP01011", "MIRP01100", "MIRP01101", "MIRP01110", - "MIRP01111", "MIRP10000", "MIRP10001", "MIRP10010", "MIRP10011", "MIRP10100", "MIRP10101", - "MIRP10110", "MIRP10111", "MIRP11000", "MIRP11001", "MIRP11010", "MIRP11011", "MIRP11100", - "MIRP11101", "MIRP11110", "MIRP11111", - ]; - - pub const OP_SVTCA0: u8 = 0x00; - pub const OP_SFVTCA1: u8 = 0x05; - pub const OP_SPVTL0: u8 = 0x06; - pub const OP_SPVTL1: u8 = 0x07; - pub const OP_SFVTL1: u8 = 0x09; - pub const OP_SPVFS: u8 = 0x0A; - pub const OP_SFVFS: u8 = 0x0B; - pub const OP_GPV: u8 = 0x0C; - pub const OP_GFV: u8 = 0x0D; - pub const OP_SFVTPV: u8 = 0x0E; - pub const OP_ISECT: u8 = 0x0F; - pub const OP_SRP0: u8 = 0x10; - pub const OP_SRP1: u8 = 0x11; - pub const OP_SRP2: u8 = 0x12; - pub const OP_SZP0: u8 = 0x13; - pub const OP_SZP1: u8 = 0x14; - pub const OP_SZP2: u8 = 0x15; - pub const OP_SZPS: u8 = 0x16; - pub const OP_SLOOP: u8 = 0x17; - pub const OP_RTG: u8 = 0x18; - pub const OP_RTHG: u8 = 0x19; - pub const OP_SMD: u8 = 0x1A; - pub const OP_ELSE: u8 = 0x1B; - pub const OP_JMPR: u8 = 0x1C; - pub const OP_SCVTCI: u8 = 0x1D; - pub const OP_SSWCI: u8 = 0x1E; - pub const OP_SSW: u8 = 0x1F; - pub const OP_DUP: u8 = 0x20; - pub const OP_POP: u8 = 0x21; - pub const OP_CLEAR: u8 = 0x22; - pub const OP_SWAP: u8 = 0x23; - pub const OP_DEPTH: u8 = 0x24; - pub const OP_CINDEX: u8 = 0x25; - pub const OP_MINDEX: u8 = 0x26; - pub const OP_ALIGNPTS: u8 = 0x27; - pub const OP_UTP: u8 = 0x29; - pub const OP_LOOPCALL: u8 = 0x2A; - pub const OP_CALL: u8 = 0x2B; - pub const OP_FDEF: u8 = 0x2C; - pub const OP_ENDF: u8 = 0x2D; - pub const OP_MDAP0: u8 = 0x2E; - pub const OP_MDAP1: u8 = 0x2F; - pub const OP_IUP0: u8 = 0x30; - pub const OP_IUP1: u8 = 0x31; - pub const OP_SHP0: u8 = 0x32; - pub const OP_SHP1: u8 = 0x33; - pub const OP_SHC0: u8 = 0x34; - pub const OP_SHC1: u8 = 0x35; - pub const OP_SHZ0: u8 = 0x36; - pub const OP_SHZ1: u8 = 0x37; - pub const OP_SHPIX: u8 = 0x38; - pub const OP_IP: u8 = 0x39; - pub const OP_MSIRP0: u8 = 0x3A; - pub const OP_MSIRP1: u8 = 0x3B; - pub const OP_ALIGNRP: u8 = 0x3C; - pub const OP_RTDG: u8 = 0x3D; - pub const OP_MIAP0: u8 = 0x3E; - pub const OP_MIAP1: u8 = 0x3F; - pub const OP_NPUSHB: u8 = 0x40; - pub const OP_NPUSHW: u8 = 0x41; - pub const OP_WS: u8 = 0x42; - pub const OP_RS: u8 = 0x43; - pub const OP_WCVTP: u8 = 0x44; - pub const OP_RCVT: u8 = 0x45; - pub const OP_GC0: u8 = 0x46; - pub const OP_GC1: u8 = 0x47; - pub const OP_SCFS: u8 = 0x48; - pub const OP_MD0: u8 = 0x49; - pub const OP_MD1: u8 = 0x4A; - pub const OP_MPPEM: u8 = 0x4B; - pub const OP_MPS: u8 = 0x4C; - pub const OP_FLIPON: u8 = 0x4D; - pub const OP_FLIPOFF: u8 = 0x4E; - pub const OP_DEBUG: u8 = 0x4F; - pub const OP_LT: u8 = 0x50; - pub const OP_LTEQ: u8 = 0x51; - pub const OP_GT: u8 = 0x52; - pub const OP_GTEQ: u8 = 0x53; - pub const OP_EQ: u8 = 0x54; - pub const OP_NEQ: u8 = 0x55; - pub const OP_ODD: u8 = 0x56; - pub const OP_EVEN: u8 = 0x57; - pub const OP_IF: u8 = 0x58; - pub const OP_EIF: u8 = 0x59; - pub const OP_AND: u8 = 0x5A; - pub const OP_OR: u8 = 0x5B; - pub const OP_NOT: u8 = 0x5C; - pub const OP_DELTAP1: u8 = 0x5D; - pub const OP_SDB: u8 = 0x5E; - pub const OP_SDS: u8 = 0x5F; - pub const OP_ADD: u8 = 0x60; - pub const OP_SUB: u8 = 0x61; - pub const OP_DIV: u8 = 0x62; - pub const OP_MUL: u8 = 0x63; - pub const OP_ABS: u8 = 0x64; - pub const OP_NEG: u8 = 0x65; - pub const OP_FLOOR: u8 = 0x66; - pub const OP_CEILING: u8 = 0x67; - pub const OP_ROUND00: u8 = 0x68; - pub const OP_ROUND11: u8 = 0x6B; - pub const OP_NROUND00: u8 = 0x6C; - pub const OP_NROUND11: u8 = 0x6F; - pub const OP_WCVTF: u8 = 0x70; - pub const OP_DELTAP2: u8 = 0x71; - pub const OP_DELTAP3: u8 = 0x72; - pub const OP_DELTAC1: u8 = 0x73; - pub const OP_DELTAC2: u8 = 0x74; - pub const OP_DELTAC3: u8 = 0x75; - pub const OP_SROUND: u8 = 0x76; - pub const OP_S45ROUND: u8 = 0x77; - pub const OP_JROT: u8 = 0x78; - pub const OP_JROF: u8 = 0x79; - pub const OP_ROFF: u8 = 0x7A; - pub const OP_RUTG: u8 = 0x7C; - pub const OP_RDTG: u8 = 0x7D; - pub const OP_SANGW: u8 = 0x7E; - pub const OP_AA: u8 = 0x7F; - pub const OP_FLIPPT: u8 = 0x80; - pub const OP_FLIPRGON: u8 = 0x81; - pub const OP_FLIPRGOFF: u8 = 0x82; - pub const OP_SCANCTRL: u8 = 0x85; - pub const OP_SDPVTL0: u8 = 0x86; - pub const OP_SDPVTL1: u8 = 0x87; - pub const OP_GETINFO: u8 = 0x88; - pub const OP_IDEF: u8 = 0x89; - pub const OP_ROLL: u8 = 0x8A; - pub const OP_MAX: u8 = 0x8B; - pub const OP_MIN: u8 = 0x8C; - pub const OP_SCANTYPE: u8 = 0x8D; - pub const OP_INSTCTRL: u8 = 0x8E; - pub const OP_GETVAR: u8 = 0x91; - pub const OP_PUSHB000: u8 = 0xB0; - pub const OP_PUSHB111: u8 = 0xB7; - pub const OP_PUSHW000: u8 = 0xB8; - pub const OP_PUSHW111: u8 = 0xBF; - pub const OP_MDRP00000: u8 = 0xC0; - pub const OP_MDRP11111: u8 = 0xDF; - pub const OP_MIRP00000: u8 = 0xE0; - pub const OP_MIRP11111: u8 = 0xFF; - - // Rounding modes - pub const ROUND_TO_HALF_GRID: u8 = 0; - pub const ROUND_TO_GRID: u8 = 1; - pub const ROUND_TO_DOUBLE_GRID: u8 = 2; - pub const ROUND_DOWN_TO_GRID: u8 = 3; - pub const ROUND_UP_TO_GRID: u8 = 4; - pub const ROUND_OFF: u8 = 5; - pub const ROUND_SUPER: u8 = 6; - pub const ROUND_SUPER45: u8 = 7; - - // Tag bits - pub const TOUCH_X: u8 = 0x08; - pub const TOUCH_Y: u8 = 0x10; -} diff --git a/src/scale/glyf/mod.rs b/src/scale/glyf/mod.rs deleted file mode 100644 index 7a7cdfd..0000000 --- a/src/scale/glyf/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -mod cache; -mod hint; -mod proxy; -mod scale; -mod var; - -use super::internal; - -pub use proxy::GlyfProxy; -pub use scale::{Scaler, ScalerState}; - -#[derive(Copy, Clone, Default, Debug)] -pub struct Point { - pub x: i32, - pub y: i32, -} - -impl Point { - pub fn new(x: i32, y: i32) -> Self { - Self { x, y } - } -} diff --git a/src/scale/glyf/proxy.rs b/src/scale/glyf/proxy.rs deleted file mode 100644 index 0f65c62..0000000 --- a/src/scale/glyf/proxy.rs +++ /dev/null @@ -1,335 +0,0 @@ -use super::internal::{ - fixed::{mul, Fixed}, - glyf::*, - var::Fvar, - Array, RawFont, -}; -use crate::{FontRef, GlyphId}; - -use super::{var, Point}; - -#[derive(Copy, Clone)] -pub struct GlyfProxy { - pub glyf: u32, - pub loca: u32, - pub cvt: (u32, u32), - pub fpgm: (u32, u32), - pub prep: (u32, u32), - pub cvar: u32, - pub gvar: u32, - pub max_storage: u16, - pub max_stack: u16, - pub max_fdefs: u16, - pub max_idefs: u16, - pub max_twilight: u16, - pub axis_count: u16, - pub loca_fmt: u8, -} - -impl GlyfProxy { - pub fn from_font(font: &FontRef) -> Option { - let maxp = font.maxp()?; - let head = font.head()?; - let loca_fmt = head.index_to_location_format() as u8; - let glyf = font.table_offset(GLYF); - let loca = font.table_offset(LOCA); - if glyf == 0 || loca == 0 || loca_fmt > 1 { - return None; - } - let cvt = font.table_range(CVT_).unwrap_or((0, 0)); - let fpgm = font.table_range(FPGM).unwrap_or((0, 0)); - let prep = font.table_range(PREP).unwrap_or((0, 0)); - let cvar = font.table_offset(CVAR); - let gvar = font.table_offset(GVAR); - let axis_count = Fvar::from_font(font).map(|f| f.axis_count()).unwrap_or(0); - Some(Self { - glyf, - loca, - loca_fmt, - cvt, - fpgm, - prep, - cvar, - gvar, - max_storage: maxp.max_storage(), - max_stack: maxp.max_stack_depth(), - max_fdefs: maxp.max_function_definitions(), - max_idefs: maxp.max_instruction_definitions(), - // Four phantom points. - max_twilight: maxp.max_twilight_points() + 4, - axis_count, - }) - } - - pub fn fpgm<'a>(&self, data: &'a [u8]) -> &'a [u8] { - data.get(self.fpgm.0 as usize..self.fpgm.1 as usize) - .unwrap_or(&[]) - } - - pub fn prep<'a>(&self, data: &'a [u8]) -> &'a [u8] { - data.get(self.prep.0 as usize..self.prep.1 as usize) - .unwrap_or(&[]) - } - - pub fn glyph_data<'a>(&self, data: &'a [u8], glyph_id: GlyphId) -> Option<&'a [u8]> { - get(data, self.loca_fmt, self.loca, self.glyf, glyph_id) - } - - pub fn cvt( - &self, - data: &[u8], - scale: Option, - coords: &[i16], - values: &mut Vec, - ) -> Option<()> { - if self.cvt.0 == 0 { - return Some(()); - } - let cvt = data.get(self.cvt.0 as usize..self.cvt.1 as usize)?; - let entries = Array::::new(cvt); - let len = entries.len(); - if values.len() < len { - values.resize(len, 0); - } - for (a, b) in entries.iter().zip(values.iter_mut()) { - *b = a as i32 - } - if !coords.is_empty() && self.cvar != 0 { - if let Some(tuples) = var::cvar_tuples(data, self.cvar, coords, self.axis_count) { - for deltas in tuples { - for (index, delta, _) in deltas { - if let Some(value) = values.get_mut(index) { - *value += delta.to_i32(); - } - } - } - } - } - if let Some(scale) = scale { - for v in values.iter_mut() { - *v = mul(*v, scale); - } - } - Some(()) - } - - pub fn deltas( - &self, - data: &[u8], - coords: &[i16], - glyph_id: u16, - points: &[Point], - tags: &mut [u8], - contours: &[u16], - accum: &mut [Point], - deltas: &mut [Point], - ) -> bool { - const HAS_DELTA_TAG: u8 = 4; - if let Some(tuples) = var::gvar_tuples(data, self.gvar, coords, glyph_id) { - let len = points.len(); - if len > tags.len() || len > deltas.len() || len > accum.len() { - return false; - } - let tags = &mut tags[..len]; - let accum = &mut accum[..len]; - let deltas = &mut deltas[..len]; - for (d, t) in deltas.iter_mut().zip(tags.iter_mut()) { - *d = Point::default(); - *t &= !HAS_DELTA_TAG; - } - for tuple_deltas in tuples { - if tuple_deltas.full_coverage() { - for (index, x, y) in tuple_deltas { - if let Some(point) = deltas.get_mut(index) { - point.x += x.0; - point.y += y.0; - } - } - } else { - for p in accum.iter_mut() { - *p = Point::default(); - } - for (index, x, y) in tuple_deltas { - if let Some(tag) = tags.get_mut(index) { - *tag |= HAS_DELTA_TAG; - } - if let Some(point) = accum.get_mut(index) { - point.x += x.0; - point.y += y.0; - } - } - let mut next_start = 0; - for end in contours.iter() { - let start = next_start; - let end = *end as usize; - next_start = end + 1; - if start >= len || end >= len { - continue; - } - let mut idx = start; - while idx <= end && tags[idx] & HAS_DELTA_TAG == 0 { - idx += 1; - } - if idx <= end { - let first_delta = idx; - let mut cur_delta = idx; - idx += 1; - while idx <= end { - if tags[idx] & HAS_DELTA_TAG != 0 { - interpolate( - cur_delta + 1, - idx - 1, - cur_delta, - idx, - points, - accum, - ); - cur_delta = idx; - } - idx += 1; - } - if cur_delta == first_delta { - let d = accum[cur_delta]; - for a in accum[start..=end].iter_mut() { - *a = d; - } - } else { - interpolate( - cur_delta + 1, - end, - cur_delta, - first_delta, - points, - accum, - ); - if first_delta > 0 { - interpolate( - start, - first_delta - 1, - cur_delta, - first_delta, - points, - accum, - ); - } - } - } - } - for ((d, t), a) in deltas.iter_mut().zip(tags.iter_mut()).zip(accum.iter()) { - *t &= !HAS_DELTA_TAG; - d.x += a.x; - d.y += a.y; - } - } - } - for d in deltas.iter_mut() { - d.x = Fixed(d.x).round().to_i32(); - d.y = Fixed(d.y).round().to_i32(); - } - return true; - } - false - } - - pub fn composite_deltas( - &self, - data: &[u8], - coords: &[i16], - glyph_id: u16, - deltas: &mut [Point], - ) -> bool { - if let Some(tuples) = var::gvar_tuples(data, self.gvar, coords, glyph_id) { - for delta in deltas.iter_mut() { - *delta = Point::default(); - } - for tuple_deltas in tuples { - for (index, x, y) in tuple_deltas { - if let Some(point) = deltas.get_mut(index) { - point.x += x.round().to_i32(); - point.y += y.round().to_i32(); - } - } - } - return true; - } - false - } -} - -fn interpolate( - p1: usize, - p2: usize, - ref1: usize, - ref2: usize, - points: &[Point], - deltas: &mut [Point], -) { - if p1 > p2 { - return; - } - let (ref1, ref2) = if points[ref1].x > points[ref2].x { - (ref2, ref1) - } else { - (ref1, ref2) - }; - let in1 = Fixed::from_i32(points[ref1].x); - let in2 = Fixed::from_i32(points[ref2].x); - let out1 = Fixed(deltas[ref1].x); - let out2 = Fixed(deltas[ref2].x); - if in1 == in2 { - if out1 == out2 { - for delta in deltas[p1..=p2].iter_mut() { - delta.x = out1.0; - } - } else { - for delta in deltas[p1..=p2].iter_mut() { - delta.x = 0; - } - } - } else { - for p in p1..=p2 { - let t = Fixed::from_i32(points[p].x); - if t <= in1 { - deltas[p].x = out1.0; - } else if t >= in2 { - deltas[p].x = out2.0; - } else { - let f = (t - in1) / (in2 - in1); - deltas[p].x = ((Fixed::ONE - f) * out1 + f * out2).0; - } - } - } - // Repeat for y - let (ref1, ref2) = if points[ref1].y > points[ref2].y { - (ref2, ref1) - } else { - (ref1, ref2) - }; - let in1 = Fixed::from_i32(points[ref1].y); - let in2 = Fixed::from_i32(points[ref2].y); - let out1 = Fixed(deltas[ref1].y); - let out2 = Fixed(deltas[ref2].y); - if in1 == in2 { - if out1 == out2 { - for delta in deltas[p1..=p2].iter_mut() { - delta.y = out1.0; - } - } else { - for delta in deltas[p1..=p2].iter_mut() { - delta.y = 0; - } - } - } else { - for p in p1..=p2 { - let t = Fixed::from_i32(points[p].y); - if t <= in1 { - deltas[p].y = out1.0; - } else if t >= in2 { - deltas[p].y = out2.0; - } else { - let f = (t - in1) / (in2 - in1); - deltas[p].y = ((Fixed::ONE - f) * out1 + f * out2).0; - } - } - } -} diff --git a/src/scale/glyf/scale.rs b/src/scale/glyf/scale.rs deleted file mode 100644 index 2acee30..0000000 --- a/src/scale/glyf/scale.rs +++ /dev/null @@ -1,567 +0,0 @@ -use super::cache::{Cache, CacheSlot}; -use super::internal::{ - fixed::{div, mul, muldiv, round}, - Stream, -}; -use super::proxy::GlyfProxy; -use super::Point; -use crate::{metrics::MetricsProxy, CacheKey, FontRef}; - -#[derive(Default)] -pub struct Scaler { - pub unscaled: Vec, - pub scaled: Vec, - pub original: Vec, - pub deltas: Vec, - pub tags: Vec, - pub contours: Vec, - pub cache: Cache, -} - -impl Scaler { - pub fn new(max_entries: usize) -> Self { - Self { - cache: Cache::new(max_entries), - ..Self::default() - } - } - - pub fn scale(&mut self, state: &mut ScalerState, glyph_id: u16) -> Option<()> { - self.scaled.clear(); - self.unscaled.clear(); - self.original.clear(); - self.deltas.clear(); - self.tags.clear(); - self.contours.clear(); - if glyph_id >= state.metrics.glyph_count() { - return None; - } - self.load(state, glyph_id, 0)?; - let pp0x = state.phantom[0].x; - if pp0x != 0 { - for p in &mut self.scaled { - p.x -= pp0x; - } - } - Some(()) - } -} - -/// Loading -impl Scaler { - fn load(&mut self, state: &mut ScalerState, glyph_id: u16, recurse: u8) -> Option<()> { - use core::cmp::Ordering::*; - if recurse > 32 { - return None; - } - let mut s = Stream::new(state.proxy.glyph_data(state.data, glyph_id)?); - if s.len() == 0 { - return Some(()); - } - let point_base = self.scaled.len(); - let contour_base = self.contours.len(); - let initial = s.read::()?; - match initial.cmp(&0) { - Greater => { - const XSHORT: u8 = 2; - const YSHORT: u8 = 4; - const REPEAT: u8 = 8; - const XSAME: u8 = 16; - const YSAME: u8 = 32; - let contour_count = initial as usize; - let contour_end = contour_base + contour_count; - let bounds = [s.read_i16()?, s.read_i16()?, s.read_i16()?, s.read_i16()?]; - self.setup(state, bounds, glyph_id, recurse); - self.contours.resize(contour_end, 0); - let mut last_contour = 0; - for i in 0..contour_count { - let contour = s.read::()?; - if contour < last_contour { - return None; - } - self.contours[contour_base + i] = contour; - last_contour = contour; - } - let mut point_count = last_contour as usize + 1; - self.scaled - .resize(point_base + point_count, Point::new(0, 0)); - self.tags.resize(point_base + point_count, 0); - let ins_len = s.read::()?; - let ins = s.read_bytes(ins_len as usize)?; - let point_end = point_base + point_count; - let mut i = point_base; - while i < point_end { - let tag = s.read::()?; - if tag & REPEAT != 0 { - let count = (s.read::()? as usize + 1).min(point_end - i); - for t in &mut self.tags[i..i + count] { - *t = tag; - } - i += count; - } else { - self.tags[i] = tag; - i += 1; - } - } - let mut v = 0i32; - for (&tag, point) in (&self.tags[point_base..]) - .iter() - .zip(&mut self.scaled[point_base..]) - { - let mut delta = 0i32; - if tag & XSHORT != 0 { - delta = s.read::()? as i32; - if tag & XSAME == 0 { - delta = -delta; - } - } else if tag & XSAME == 0 { - delta = s.read::()? as i32; - } - v += delta; - point.x = v; - } - v = 0; - for (tag, point) in (&mut self.tags[point_base..]) - .iter_mut() - .zip(&mut self.scaled[point_base..]) - { - let mut delta = 0i32; - let t = *tag; - if t & YSHORT != 0 { - delta = s.read::()? as i32; - if t & YSAME == 0 { - delta = -delta; - } - } else if t & YSAME == 0 { - delta = s.read::()? as i32; - } - v += delta; - point.y = v; - *tag &= 1; - } - self.push_phantom(state); - point_count += 4; - let point_end = point_base + point_count; - if state.vary { - self.unscaled.clear(); - self.unscaled.resize(point_count, Point::new(0, 0)); - self.original.clear(); - self.original.resize(point_count, Point::new(0, 0)); - if state.proxy.deltas( - state.data, - state.coords, - glyph_id, - &self.scaled[point_base..], - &mut self.tags[point_base..], - &self.contours[contour_base..], - &mut self.unscaled[..], - &mut self.original[..], - ) { - for (d, p) in self.original[..point_count] - .iter() - .zip(self.scaled[point_base..].iter_mut()) - { - p.x += d.x; - p.y += d.y; - } - } - } - let hinted = state.hint && !ins.is_empty(); - if hinted { - self.unscaled.clear(); - self.unscaled.extend_from_slice(&self.scaled[point_base..]); - } - if state.have_scale { - let scale = state.scale; - for p in &mut self.scaled[point_base..] { - p.x = mul(p.x, scale); - p.y = mul(p.y, scale); - } - self.save_phantom(state, point_base, point_count); - } - if hinted { - self.original.clear(); - self.original - .extend_from_slice(&self.scaled[point_base..point_end]); - for p in &mut self.scaled[point_end - 4..] { - p.x = round(p.x); - p.y = round(p.y); - } - self.hint(state, point_base, contour_base, ins, false); - } - if point_base != 0 { - for c in &mut self.contours[contour_base..contour_end] { - *c += point_base as u16; - } - } - self.scaled.truncate(self.scaled.len() - 4); - self.tags.truncate(self.tags.len() - 4); - Some(()) - } - Less => { - const ARGS_ARE_WORDS: u16 = 0x001; - const ARGS_ARE_XY_VALUES: u16 = 0x002; - const ROUND_XY_TO_GRID: u16 = 0x004; - const HAVE_SCALE: u16 = 0x008; - const MORE_COMPONENTS: u16 = 32; - const HAVE_XY_SCALE: u16 = 64; - const HAVE_MATRIX: u16 = 128; - const HAVE_TRANSFORM: u16 = HAVE_SCALE | HAVE_XY_SCALE | HAVE_MATRIX; - const HAVE_INSTRUCTIONS: u16 = 256; - const USE_MY_METRICS: u16 = 512; - const SCALED_COMPONENT_OFFSET: u16 = 0x800; - const UNSCALED_COMPONENT_OFFSET: u16 = 4096; - let bounds = [s.read_i16()?, s.read_i16()?, s.read_i16()?, s.read_i16()?]; - self.setup(state, bounds, glyph_id, recurse); - if state.have_scale { - let scale = state.scale; - for p in state.phantom.iter_mut() { - p.x = mul(p.x, scale); - p.y = mul(p.y, scale); - } - } - let delta_base = self.deltas.len(); - let mut have_deltas = false; - let base_offset = s.offset(); - if state.vary { - let mut flags: u16; - let mut count = 0usize; - loop { - flags = s.read()?; - s.skip(2)?; - if flags & ARGS_ARE_WORDS != 0 { - s.skip(4)?; - } else { - s.skip(2)?; - } - if flags & HAVE_SCALE != 0 { - s.skip(2)?; - } else if flags & HAVE_XY_SCALE != 0 { - s.skip(4)?; - } else if flags & HAVE_MATRIX != 0 { - s.skip(8)?; - } - count += 1; - if flags & MORE_COMPONENTS == 0 { - break; - } - } - s.set_offset(base_offset)?; - self.deltas.resize(delta_base + count, Point::new(0, 0)); - if state.proxy.composite_deltas( - state.data, - state.coords, - glyph_id, - &mut self.deltas[delta_base..], - ) { - have_deltas = true; - } - } - let mut flags: u16; - let mut i = 0; - loop { - flags = s.read()?; - let id = s.read::()?; - let phantom = state.phantom; - let start_point = self.scaled.len(); - self.load(state, id, recurse + 1)?; - let end_point = self.scaled.len(); - if flags & USE_MY_METRICS == 0 { - state.phantom = phantom; - } - let x: i32; - let y: i32; - let mut have_offset = false; - if flags & ARGS_ARE_XY_VALUES != 0 { - if flags & ARGS_ARE_WORDS != 0 { - x = s.read::()? as i32; - y = s.read::()? as i32; - } else { - x = s.read::()? as i32; - y = s.read::()? as i32; - } - have_offset = true; - } else if flags & ARGS_ARE_WORDS != 0 { - x = s.read::()? as i32; - y = s.read::()? as i32; - } else { - x = s.read::()? as i32; - y = s.read::()? as i32; - } - let mut xx = 0x10000; - let mut xy = 0; - let mut yx = 0; - let mut yy = 0x10000; - let have_xform = flags & HAVE_TRANSFORM != 0; - if flags & HAVE_SCALE != 0 { - xx = s.read::()? as i32 * 4; - yy = xx; - } else if flags & HAVE_XY_SCALE != 0 { - xx = s.read::()? as i32 * 4; - yy = s.read::()? as i32 * 4; - } else if flags & HAVE_MATRIX != 0 { - xx = s.read::()? as i32 * 4; - yx = s.read::()? as i32 * 4; - xy = s.read::()? as i32 * 4; - yy = s.read::()? as i32 * 4; - } - let xx = xx; - let xy = xy; - let yx = yx; - let yy = yy; - if have_xform { - for p in &mut self.scaled[start_point..end_point] { - let (x, y) = transform(p.x, p.y, xx, xy, yx, yy); - p.x = x; - p.y = y; - } - } - let (dx, dy) = if have_offset { - let (mut dx, mut dy) = (x, y); - if have_xform - && (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET) - == SCALED_COMPONENT_OFFSET) - { - dx = mul(dx, hypot(xx, xy)); - dy = mul(dy, hypot(yy, yx)); - } - if have_deltas { - let d = self.deltas[delta_base + i]; - dx += d.x; - dy += d.y; - } - if state.have_scale { - dx = mul(dx, state.scale); - dy = mul(dy, state.scale); - if state.hint && flags & ROUND_XY_TO_GRID != 0 { - dy = round(dy); - } - } - (dx, dy) - } else { - let (a1, a2) = (x as usize, y as usize); - let pi1 = point_base + a1; - let pi2 = start_point + a2; - if pi1 >= self.scaled.len() || pi2 >= self.scaled.len() { - return None; - } - let p1 = self.scaled[pi1]; - let p2 = self.scaled[pi2]; - (p1.x - p2.x, p1.y - p2.y) - }; - if dx != 0 || dy != 0 { - for p in &mut self.scaled[start_point..end_point] { - p.x += dx; - p.y += dy; - } - } - if flags & MORE_COMPONENTS == 0 { - break; - } - i += 1; - } - let mut ins: &[u8] = &[]; - if flags & HAVE_INSTRUCTIONS != 0 { - let ins_len = s.read::()? as usize; - ins = s.read_bytes(ins_len)?; - } - self.deltas.resize(delta_base, Point::new(0, 0)); - if state.hint && !ins.is_empty() { - self.push_phantom(state); - self.unscaled.clear(); - self.unscaled.extend_from_slice(&self.scaled[point_base..]); - self.original.clear(); - self.original.extend_from_slice(&self.scaled[point_base..]); - let point_end = self.scaled.len(); - for p in &mut self.scaled[point_end - 4..] { - p.x = round(p.x); - p.y = round(p.y); - } - for t in &mut self.tags[point_base..] { - *t &= !(0x08 | 0x10); - } - self.hint(state, point_base, contour_base, ins, true); - self.scaled.truncate(self.scaled.len() - 4); - self.tags.truncate(self.tags.len() - 4); - } - Some(()) - } - Equal => Some(()), - } - } -} - -/// Hinting -impl Scaler { - fn hint( - &mut self, - state: &mut ScalerState, - point_base: usize, - contour_base: usize, - ins: &[u8], - is_composite: bool, - ) -> bool { - use super::hint::HinterMode; - let slot = match state.slot { - Some(slot) => slot, - None => { - match self.cache.prepare( - state.id, - state.data, - state.proxy, - state.coords, - state.ppem, - state.scale, - HinterMode::Modern, - ) { - Some(slot) => { - state.slot = Some(slot); - slot - } - None => { - state.hint = false; - return false; - } - } - } - }; - self.cache.hint( - state.data, - state.proxy, - state.coords, - slot, - &mut self.unscaled[..], - &mut self.original[..], - &mut self.scaled[..], - &mut self.tags[..], - &mut self.contours[..], - &mut state.phantom[..], - point_base, - contour_base, - ins, - is_composite, - ); - true - } -} - -/// Per-component setup. -impl Scaler { - fn setup(&mut self, state: &mut ScalerState, bounds: [i16; 4], glyph_id: u16, depth: u8) { - let metrics = state.metrics.materialize_glyph_metrics( - &FontRef { - data: state.data, - offset: 0, - key: CacheKey(0), - }, - state.coords, - ); - let lsb = metrics.lsb(glyph_id) as i16; - let advance = metrics.advance_width(glyph_id) as i32; - let vadvance = 0; - let tsb = 0; - state.phantom[0].x = (bounds[0] - lsb) as i32; - state.phantom[0].y = 0; - state.phantom[1].x = state.phantom[0].x + advance as i32; - state.phantom[1].y = 0; - state.phantom[2].x = advance as i32 / 2; - state.phantom[2].y = (bounds[3] + tsb) as i32; - state.phantom[3].x = advance as i32 / 2; - state.phantom[3].y = state.phantom[2].y - vadvance; - if depth == 0 && state.have_scale { - state.xmin = mul(bounds[0] as i32, state.scale); - state.xmax = mul(bounds[2] as i32, state.scale); - state.lsb = mul(lsb as i32, state.scale); - } - state.advance = mul(advance, state.scale); - } - - fn push_phantom(&mut self, state: &mut ScalerState) { - for i in 0..4 { - self.scaled.push(state.phantom[i]); - self.tags.push(0); - } - } - - fn save_phantom(&mut self, state: &mut ScalerState, point_base: usize, point_count: usize) { - for i in 0..4 { - state.phantom[3 - i] = self.scaled[point_base + point_count - i - 1]; - } - } -} - -pub struct ScalerState<'a> { - pub data: &'a [u8], - pub id: [u64; 2], - pub coords: &'a [i16], - pub proxy: &'a GlyfProxy, - pub metrics: &'a MetricsProxy, - pub slot: Option, - pub have_scale: bool, - pub ppem: u16, - pub scale: i32, - pub hint: bool, - pub vary: bool, - pub xmin: i32, - pub xmax: i32, - pub lsb: i32, - pub advance: i32, - pub phantom: [Point; 4], -} - -impl<'a> ScalerState<'a> { - pub fn new( - data: &'a [u8], - id: [u64; 2], - coords: &'a [i16], - proxy: &'a GlyfProxy, - metrics: &'a MetricsProxy, - size: f32, - hint: bool, - ) -> Self { - let size = size.abs(); - let ppem = size as u16; - let upem = metrics.units_per_em(); - let (have_scale, scale) = if size != 0. && upem != 0 { - (true, div((size * 64.) as i32, upem as i32)) - } else { - (false, 0) - }; - Self { - data, - id, - coords, - proxy, - metrics, - slot: None, - have_scale, - ppem, - scale, - hint, - vary: proxy.axis_count != 0 && !coords.is_empty() && proxy.gvar != 0, - xmin: 0, - xmax: 0, - lsb: 0, - advance: 0, - phantom: Default::default(), - } - } -} - -fn hypot(mut a: i32, mut b: i32) -> i32 { - a = a.abs(); - b = b.abs(); - if a > b { - a + ((3 * b) >> 3) - } else { - b + ((3 * a) >> 3) - } -} - -fn transform(x: i32, y: i32, xx: i32, xy: i32, yx: i32, yy: i32) -> (i32, i32) { - let scale = 0x10000; - ( - muldiv(x, xx, scale) + muldiv(y, xy, scale), - muldiv(x, yx, scale) + muldiv(y, yy, scale), - ) -} diff --git a/src/scale/glyf/var.rs b/src/scale/glyf/var.rs deleted file mode 100644 index 4dce414..0000000 --- a/src/scale/glyf/var.rs +++ /dev/null @@ -1,611 +0,0 @@ -use super::internal::{fixed::Fixed, Array, Bytes}; - -pub fn cvar_tuples<'a>( - data: &'a [u8], - cvar: u32, - coords: &'a [i16], - axis_count: u16, -) -> Option> { - if cvar == 0 { - return None; - } - let b = Bytes::with_offset(data, cvar as usize)?; - Some(TupleStore::from_cvar(b, axis_count).tuples(coords)) -} - -pub fn gvar_tuples<'a>( - data: &'a [u8], - gvar: u32, - coords: &'a [i16], - glyph_id: u16, -) -> Option> { - if gvar == 0 { - return None; - } - let b = Bytes::with_offset(data, gvar as usize)?; - TupleStore::from_gvar(b, glyph_id).map(|store| store.tuples(coords)) -} - -/// Tuple variation store. -#[derive(Copy, Clone)] -pub struct TupleStore<'a> { - data: Bytes<'a>, - shared_coords: SharedCoords<'a>, - tuple_count: usize, - shared_point_numbers: Option>, - data_offset: usize, - is_cvar: bool, - offset: usize, -} - -impl<'a> TupleStore<'a> { - fn from_cvar(b: Bytes<'a>, axis_count: u16) -> Self { - let mut tuple_count = b.read_or_default::(4) as usize; - let mut data_offset = b.read_or_default::(6) as usize; - let shared_point_numbers = if tuple_count & 0x8000 != 0 { - if let Some(nums) = PointNumbers::new(b, data_offset) { - data_offset += nums.data_size(); - Some(nums) - } else { - tuple_count = 0; - None - } - } else { - None - }; - tuple_count = tuple_count as usize & 0xFFF; - Self::new( - b, - SharedCoords { - data: b, - offset: 0, - count: 0, - axis_count: axis_count as usize, - }, - tuple_count, - shared_point_numbers, - data_offset, - true, - 8, - ) - } - - fn from_gvar(b: Bytes<'a>, glyph_id: u16) -> Option { - let axis_count = b.read::(4)? as usize; - let shared_coord_count = b.read::(6)? as usize; - let offset_to_coord = b.read::(8)? as usize; - let glyph_count = b.read::(12)?; - if glyph_id >= glyph_count { - return None; - } - let flags = b.read::(14)?; - let offset_to_data = b.read::(16)? as usize; - let offsets_base = 20usize; - let idx = glyph_id as usize; - let mut range = if flags & 1 != 0 { - b.read::(offsets_base + idx * 4)? as usize - ..b.read::(offsets_base + (idx + 1) * 4)? as usize - } else { - b.read::(offsets_base + idx * 2)? as usize * 2 - ..b.read::(offsets_base + (idx + 1) * 2)? as usize * 2 - }; - let len = range.len(); - if len == 0 || !b.check_range(range.start, len) { - return None; - } - range.start += offset_to_data; - range.end += offset_to_data; - let shared_coords = SharedCoords { - data: b, - offset: offset_to_coord, - count: shared_coord_count, - axis_count, - }; - let b = Bytes::new(b.data().get(range)?); - let tuple_count = b.read::(0)?; - let mut data_offset = b.read::(2)? as usize; - let shared_point_numbers = if tuple_count & 0x8000 != 0 { - let nums = PointNumbers::new(b, data_offset)?; - data_offset += nums.data_size(); - Some(nums) - } else { - None - }; - let tuple_count = tuple_count as usize & 0xFFF; - Some(Self::new( - b, - shared_coords, - tuple_count, - shared_point_numbers, - data_offset, - false, - 4, - )) - } - - fn new( - data: Bytes<'a>, - shared_coords: SharedCoords<'a>, - tuple_count: usize, - shared_point_numbers: Option>, - data_offset: usize, - is_cvar: bool, - offset: usize, - ) -> Self { - Self { - data, - shared_coords, - tuple_count, - shared_point_numbers, - data_offset, - is_cvar, - offset, - } - } - - /// Returns an iterator over the tuples in this store for the specified - /// normalized variation coordinates. - pub fn tuples(&self, coords: &'a [i16]) -> Tuples<'a> { - Tuples { - data: self.data, - shared_coords: self.shared_coords, - tuple_count: self.tuple_count, - shared_point_numbers: self.shared_point_numbers, - data_offset: self.data_offset, - is_cvar: self.is_cvar, - offset: self.offset, - coords, - cur: 0, - } - } -} - -/// Iterator over the tuples in a tuple variation store. -#[derive(Copy, Clone)] -pub struct Tuples<'a> { - data: Bytes<'a>, - shared_coords: SharedCoords<'a>, - tuple_count: usize, - shared_point_numbers: Option>, - data_offset: usize, - is_cvar: bool, - offset: usize, - coords: &'a [i16], - cur: usize, -} - -impl<'a> Tuples<'a> { - fn next_tuple(&mut self) -> Option> { - let d = &self.data; - let size = d.read::(self.offset)? as usize; - let index = d.read::(self.offset + 2)?; - let axis_count = self.shared_coords.axis_count; - self.offset += 4; - let peak = if index & 0x8000 != 0 { - let embedded = d.read_array::(self.offset, axis_count)?; - self.offset += 2 * axis_count; - embedded - } else { - self.shared_coords.get(index as usize & 0xFFF)? - }; - let intermediate = if index & 0x4000 != 0 { - let intermidate = d.read_array::(self.offset, axis_count)?; - self.offset += 2 * axis_count; - let end = d.read_array::(self.offset, axis_count)?; - self.offset += 2 * axis_count; - (intermidate, end) - } else { - (Array::new(&[]), Array::new(&[])) - }; - let mut offset = self.data_offset; - let end = offset + size; - let point_numbers = if index & 0x2000 != 0 { - let nums = PointNumbers::new(self.data, self.data_offset)?; - offset += nums.data_size; - nums - } else { - self.shared_point_numbers? - }; - self.data_offset += size; - self.cur += 1; - Some(Tuple { - data: self.data, - point_numbers, - peak, - intermediate, - offset, - end, - is_cvar: self.is_cvar, - }) - } -} - -impl<'a> Iterator for Tuples<'a> { - type Item = Deltas<'a>; - - fn next(&mut self) -> Option { - loop { - if self.cur > self.tuple_count { - return None; - } - self.cur += 1; - let tuple = self.next_tuple()?; - let scalar = tuple.compute_scalar(self.coords); - if scalar == Fixed::ZERO { - continue; - } - let deltas = tuple.deltas(scalar); - if deltas.len == 0 { - continue; - } - return Some(deltas); - } - } -} - -/// Represents a single region of applicability within a font variation space. -#[derive(Copy, Clone)] -struct Tuple<'a> { - data: Bytes<'a>, - point_numbers: PointNumbers<'a>, - peak: Array<'a, i16>, - intermediate: (Array<'a, i16>, Array<'a, i16>), - offset: usize, - end: usize, - is_cvar: bool, -} - -impl<'a> Tuple<'a> { - /// Returns a scaling factor based on the specified variation coordinates - /// for the deltas associated with this region. - #[allow(clippy::needless_range_loop)] - fn compute_scalar(&self, coords: &[i16]) -> Fixed { - const ZERO: Fixed = Fixed::ZERO; - const ONE: Fixed = Fixed::ONE; - let mut scalar = ONE; - let len = coords.len(); - if len != self.peak.len() { - return ZERO; - } - let inter_start = self.intermediate.0; - let inter_end = self.intermediate.1; - let has_intermediate = inter_start.len() != 0; - for i in 0..len { - let peak = Fixed::from_f2dot14(self.peak.get_or(i, 0)); - let coord = Fixed::from_f2dot14(coords[i]); - if peak == ZERO || peak == coord { - continue; - } - if coord == ZERO { - return ZERO; - } - if !has_intermediate { - if coord < peak.min(ZERO) || coord > peak.max(ZERO) { - return ZERO; - } - scalar = scalar * coord / peak; - } else { - let start = Fixed::from_f2dot14(inter_start.get_or(i, 0)); - let end = Fixed::from_f2dot14(inter_end.get_or(i, 0)); - if coord <= start || coord >= end { - return ZERO; - } - if coord < peak { - scalar = scalar * (coord - start) / (peak - start); - } else { - scalar = scalar * (end - coord) / (end - peak); - } - } - } - scalar - } - - /// Returns an iterator over the deltas for this region. - fn deltas(&self, scalar: Fixed) -> Deltas<'a> { - if let Some(deltas) = Deltas::new(self, scalar) { - deltas - } else { - Deltas { - data: self.data, - pts: self.point_numbers.iter(), - len: 0, - cur: 0, - xs: 0, - ys: 0, - xrun: (0, 0, false, false), - yrun: (0, 0, false, false), - scalar, - } - } - } -} - -/// Iterator over the delta values of a particular region. -#[derive(Copy, Clone)] -pub struct Deltas<'a> { - data: Bytes<'a>, - pts: PointNumbersIter<'a>, - len: usize, - cur: usize, - xs: usize, - ys: usize, - xrun: (u8, u8, bool, bool), - yrun: (u8, u8, bool, bool), - scalar: Fixed, -} - -impl<'a> Deltas<'a> { - fn new(parent: &Tuple<'a>, scalar: Fixed) -> Option { - let d = &parent.data; - let len = if parent.point_numbers.all { - let len = Self::count_deltas(d, parent.offset, parent.end)?; - if parent.is_cvar { - len - } else { - len / 2 - } - } else { - parent.point_numbers.len as usize - }; - let xs = parent.offset; - let ys = if !parent.is_cvar { - Self::compute_ys_offset(d, parent.offset, parent.end, len)? - } else { - 0 - }; - Some(Self { - data: parent.data, - pts: parent.point_numbers.iter(), - len, - cur: 0, - xs, - ys, - xrun: (0, 0, false, false), - yrun: (0, 0, false, false), - scalar, - }) - } - - /// Returns true if this iterator provides deltas for all points. - pub fn full_coverage(&self) -> bool { - self.pts.parent.len == 0 - } - - fn count_deltas(d: &Bytes, offset: usize, end: usize) -> Option { - let mut offs = offset; - let mut n = 0; - while offs < end { - let control = d.read::(offs)?; - offs += 1; - let count = (control as usize & 0x3F) + 1; - n += count; - if control & 0x80 == 0 { - let words = control & 0x40 != 0; - if words { - offs += count as usize * 2; - } else { - offs += count as usize; - } - } - } - Some(n) - } - - fn compute_ys_offset(d: &Bytes, offset: usize, end: usize, x_count: usize) -> Option { - let mut offs = offset; - let mut n = 0; - while offs < end { - let control = d.read::(offs)?; - offs += 1; - let count = (control as usize & 0x3F) + 1; - n += count; - if control & 0x80 == 0 { - let words = control & 0x40 != 0; - if words { - offs += count as usize * 2; - } else { - offs += count as usize; - } - } - if n == x_count { - return Some(offs); - } - } - None - } - - fn next_item(&mut self) -> Option<(usize, Fixed, Fixed)> { - let d = &self.data; - let point_index = self.pts.next()?; - if self.xrun.0 >= self.xrun.1 { - let control = d.read::(self.xs)?; - self.xs += 1; - self.xrun.0 = 0; - self.xrun.1 = (control & 0x3F) + 1; - self.xrun.2 = control & 0x80 != 0; - self.xrun.3 = control & 0x40 != 0; - } - self.xrun.0 += 1; - let x = if self.xrun.2 { - 0 - } else if self.xrun.3 { - let offs = self.xs; - self.xs += 2; - d.read::(offs)? - } else { - let offs = self.xs; - self.xs += 1; - d.read::(offs)? as i16 - }; - let y = if self.ys != 0 { - if self.yrun.0 >= self.yrun.1 { - let control = d.read::(self.ys)?; - self.ys += 1; - self.yrun.0 = 0; - self.yrun.1 = (control & 0x3F) + 1; - self.yrun.2 = control & 0x80 != 0; - self.yrun.3 = control & 0x40 != 0; - } - self.yrun.0 += 1; - if self.yrun.2 { - 0 - } else if self.yrun.3 { - let offs = self.ys; - self.ys += 2; - d.read::(offs)? - } else { - let offs = self.ys; - self.ys += 1; - d.read::(offs)? as i16 - } - } else { - 0 - }; - let dx = self.scalar * Fixed::from_i32(x as i32); - let dy = self.scalar * Fixed::from_i32(y as i32); - Some((point_index as usize, dx, dy)) - } -} - -impl<'a> Iterator for Deltas<'a> { - type Item = (usize, Fixed, Fixed); - - fn next(&mut self) -> Option { - if self.cur >= self.len { - return None; - } - self.cur += 1; - self.next_item() - } -} - -#[derive(Copy, Clone)] -struct PointNumbers<'a> { - data: Bytes<'a>, - offset: usize, - len: u16, - all: bool, - data_size: usize, -} - -impl<'a> PointNumbers<'a> { - fn new(data: Bytes<'a>, offset: usize) -> Option { - let base = offset; - let mut offset = offset; - let control = data.read::(offset)? as usize; - offset += 1; - if control & 0x7F == 0 { - return Some(Self { - data, - offset, - len: 0, - all: true, - data_size: 1, - }); - } - let mut data_size = 1usize; - let len = if control & 0x80 != 0 { - data_size += 1; - offset += 1; - (control & 0x7F) << 8 | data.read::(offset - 1)? as usize - } else { - control & 0x7F - }; - let mut points_read = 0; - while points_read < len { - let control = data.read::(base + data_size)?; - data_size += 1; - let words = control & 0x80 != 0; - let count = (control as usize & 0x7F) + 1; - if words { - data_size += count * 2; - } else { - data_size += count; - } - points_read += count; - } - Some(Self { - data, - offset, - len: len as u16, - all: false, - data_size, - }) - } - - pub fn data_size(&self) -> usize { - self.data_size - } - - fn iter(&self) -> PointNumbersIter<'a> { - PointNumbersIter { - parent: *self, - cur: 0, - offset: self.offset, - num: 0, - run_len: 0, - run_cur: 0, - run_words: false, - } - } -} - -#[derive(Copy, Clone)] -struct PointNumbersIter<'a> { - parent: PointNumbers<'a>, - cur: u16, - offset: usize, - num: u16, - run_len: u16, - run_cur: u16, - run_words: bool, -} - -impl<'a> Iterator for PointNumbersIter<'a> { - type Item = u16; - - fn next(&mut self) -> Option { - if self.parent.all { - let value = self.num; - self.num += 1; - return Some(value); - } - if self.cur >= self.parent.len { - return None; - } - let d = &self.parent.data; - if self.run_cur >= self.run_len { - let control = d.read::(self.offset)? as u16; - self.offset += 1; - self.run_len = (control & 0x7F) + 1; - self.run_words = control & 0x80 != 0; - self.run_cur = 0; - } - let offset = self.offset; - self.num += if self.run_words { - self.offset += 2; - d.read::(offset)? - } else { - self.offset += 1; - d.read::(offset)? as u16 - }; - self.run_cur += 1; - self.cur += 1; - Some(self.num) - } -} - -#[derive(Copy, Clone)] -struct SharedCoords<'a> { - pub data: Bytes<'a>, - pub offset: usize, - pub count: usize, - pub axis_count: usize, -} - -impl<'a> SharedCoords<'a> { - fn get(&self, index: usize) -> Option> { - self.data - .read_array(self.offset + (self.axis_count * index * 2), self.axis_count) - } -} diff --git a/src/scale/hinting_cache.rs b/src/scale/hinting_cache.rs new file mode 100644 index 0000000..83e0461 --- /dev/null +++ b/src/scale/hinting_cache.rs @@ -0,0 +1,93 @@ +use skrifa::{ + instance::{NormalizedCoord, Size}, + outline::{ + HintingInstance, HintingMode, LcdLayout, OutlineGlyphCollection, OutlineGlyphFormat, + }, +}; +/// We keep this small to enable a simple LRU cache with a linear +/// search. Regenerating hinting data is low to medium cost so it's fine +/// to redo it occasionally. +const MAX_CACHED_HINT_INSTANCES: usize = 8; + +pub struct HintingKey<'a> { + pub id: [u64; 2], + pub outlines: &'a OutlineGlyphCollection<'a>, + pub size: Size, + pub coords: &'a [NormalizedCoord], +} + +impl<'a> HintingKey<'a> { + fn new_instance(&self) -> Option { + HintingInstance::new(self.outlines, self.size, self.coords, HINTING_MODE).ok() + } +} + +const HINTING_MODE: HintingMode = HintingMode::Smooth { + lcd_subpixel: Some(LcdLayout::Horizontal), + preserve_linear_metrics: true, +}; + +#[derive(Default)] +pub(super) struct HintingCache { + // Split caches for glyf/cff because the instance type can reuse + // internal memory when reconfigured for the same format. + glyf_entries: Vec, + cff_entries: Vec, + serial: u64, +} + +impl HintingCache { + pub(super) fn get(&mut self, key: &HintingKey) -> Option<&HintingInstance> { + let entries = match key.outlines.format()? { + OutlineGlyphFormat::Glyf => &mut self.glyf_entries, + OutlineGlyphFormat::Cff | OutlineGlyphFormat::Cff2 => &mut self.cff_entries, + }; + let (entry_ix, is_current) = find_hinting_entry(entries, key)?; + let entry = entries.get_mut(entry_ix)?; + self.serial += 1; + entry.serial = self.serial; + if !is_current { + entry.id = key.id; + entry + .instance + .reconfigure(key.outlines, key.size, key.coords, HINTING_MODE) + .ok()?; + } + Some(&entry.instance) + } +} + +struct HintingEntry { + id: [u64; 2], + instance: HintingInstance, + serial: u64, +} + +fn find_hinting_entry(entries: &mut Vec, key: &HintingKey) -> Option<(usize, bool)> { + let mut found_serial = u64::MAX; + let mut found_index = 0; + for (ix, entry) in entries.iter().enumerate() { + if entry.id == key.id + && entry.instance.size() == key.size + && entry.instance.location().coords() == key.coords + { + return Some((ix, true)); + } + if entry.serial < found_serial { + found_serial = entry.serial; + found_index = ix; + } + } + if entries.len() < MAX_CACHED_HINT_INSTANCES { + let instance = key.new_instance()?; + let ix = entries.len(); + entries.push(HintingEntry { + id: key.id, + instance, + serial: 0, + }); + Some((ix, true)) + } else { + Some((found_index, false)) + } +} diff --git a/src/scale/mod.rs b/src/scale/mod.rs index b66d400..e022ea6 100644 --- a/src/scale/mod.rs +++ b/src/scale/mod.rs @@ -213,19 +213,22 @@ affine matrix, and applying path effects. See the methods on [`Render`] for more detail. */ -const TRACE: bool = false; - pub mod image; pub mod outline; mod bitmap; -mod cff; mod color; -mod glyf; +mod hinting_cache; mod proxy; +use hinting_cache::HintingCache; use image::*; use outline::*; +use skrifa::{ + instance::{NormalizedCoord as SkrifaNormalizedCoord, Size as SkrifaSize}, + outline::OutlineGlyphCollection, + GlyphId as SkrifaGlyphId, MetadataProvider, +}; use super::internal; use super::{cache::FontCache, setting::Setting, FontRef, GlyphId, NormalizedCoord}; @@ -281,12 +284,11 @@ impl Default for Source { pub struct ScaleContext { fonts: FontCache, state: State, - coords: Vec, + hinting_cache: HintingCache, + coords: Vec, } struct State { - glyf_scaler: glyf::Scaler, - cff_cache: cff::SubfontCache, scratch0: Vec, scratch1: Vec, outline: Outline, @@ -307,14 +309,13 @@ impl ScaleContext { Self { fonts: FontCache::new(max_entries), state: State { - glyf_scaler: glyf::Scaler::new(max_entries), - cff_cache: cff::SubfontCache::new(max_entries), scratch0: Vec::new(), scratch1: Vec::new(), outline: Outline::new(), #[cfg(feature = "render")] rcx: Scratch::new(), }, + hinting_cache: HintingCache::default(), coords: Vec::new(), } } @@ -335,10 +336,12 @@ impl Default for ScaleContext { /// Builder for configuring a scaler. pub struct ScalerBuilder<'a> { state: &'a mut State, + hinting_cache: &'a mut HintingCache, font: FontRef<'a>, + outlines: Option>, proxy: &'a ScalerProxy, id: [u64; 2], - coords: &'a mut Vec, + coords: &'a mut Vec, size: f32, hint: bool, } @@ -349,9 +352,20 @@ impl<'a> ScalerBuilder<'a> { let (id, proxy) = context .fonts .get(&font, None, |font| ScalerProxy::from_font(font)); + let skrifa_font = if font.offset == 0 { + skrifa::FontRef::new(font.data).ok() + } else { + // TODO: make this faster + let index = crate::FontDataRef::new(font.data) + .and_then(|font_data| font_data.fonts().position(|f| f.offset == font.offset)); + index.and_then(|index| skrifa::FontRef::from_index(font.data, index as u32).ok()) + }; + let outlines = skrifa_font.map(|font_ref| font_ref.outline_glyphs()); Self { state: &mut context.state, + hinting_cache: &mut context.hinting_cache, font, + outlines, proxy, id, coords: &mut context.coords, @@ -381,14 +395,14 @@ impl<'a> ScalerBuilder<'a> { { if self.proxy.coord_count != 0 { let vars = self.font.variations(); - self.coords.resize(vars.len(), 0); + self.coords.resize(vars.len(), Default::default()); for setting in settings { let setting = setting.into(); for var in vars { if var.tag() == setting.tag { let value = var.normalize(setting.value); if let Some(c) = self.coords.get_mut(var.index()) { - *c = value; + *c = SkrifaNormalizedCoord::from_bits(value); } } } @@ -405,48 +419,43 @@ impl<'a> ScalerBuilder<'a> { I::Item: Borrow, { self.coords.clear(); - self.coords.extend(coords.into_iter().map(|c| *c.borrow())); + self.coords.extend( + coords + .into_iter() + .map(|c| SkrifaNormalizedCoord::from_bits(*c.borrow())), + ); self } /// Builds a scaler for the current configuration. pub fn build(self) -> Scaler<'a> { let upem = self.proxy.metrics.units_per_em(); - let scale = if self.size != 0. && upem != 0 { - self.size / upem as f32 + let skrifa_size = if self.size != 0.0 && upem != 0 { + SkrifaSize::new(self.size) } else { - 1. + SkrifaSize::unscaled() }; - // Handle read-fonts conversion for CFF - let cff = if matches!(&self.proxy.outlines, OutlinesProxy::Cff) { - let font = if self.font.offset == 0 { - read_fonts::FontRef::new(self.font.data).ok() - } else { - // TODO: make this faster - let index = crate::FontDataRef::new(self.font.data).and_then(|font_data| { - font_data - .fonts() - .position(|font| font.offset == self.font.offset) - }); - index.and_then(|index| { - read_fonts::FontRef::from_index(self.font.data, index as u32).ok() - }) - }; - font.and_then(|font| cff::Outlines::new(&font).ok()) - } else { - None + let hinting_instance = match (self.hint, &self.outlines) { + (true, Some(outlines)) => { + let key = hinting_cache::HintingKey { + id: self.id, + outlines, + size: skrifa_size, + coords: &self.coords, + }; + self.hinting_cache.get(&key) + } + _ => None, }; Scaler { state: self.state, font: self.font, + outlines: self.outlines, + hinting_instance, proxy: self.proxy, - cff, - id: self.id, coords: &self.coords[..], size: self.size, - scale, - hint: self.hint, - glyf_state: None, + skrifa_size, } } } @@ -457,20 +466,21 @@ impl<'a> ScalerBuilder<'a> { pub struct Scaler<'a> { state: &'a mut State, font: FontRef<'a>, + outlines: Option>, + hinting_instance: Option<&'a skrifa::outline::HintingInstance>, proxy: &'a ScalerProxy, - cff: Option>, - id: [u64; 2], - coords: &'a [i16], + coords: &'a [SkrifaNormalizedCoord], size: f32, - scale: f32, - hint: bool, - glyf_state: Option>, + skrifa_size: SkrifaSize, } impl<'a> Scaler<'a> { /// Returns true if scalable glyph outlines are available. pub fn has_outlines(&self) -> bool { - !matches!(self.proxy.outlines, OutlinesProxy::None) + self.outlines + .as_ref() + .map(|outlines| outlines.format().is_some()) + .unwrap_or_default() } /// Scales an outline for the specified glyph into the provided outline. @@ -537,63 +547,27 @@ impl<'a> Scaler<'a> { Some(x) => x, _ => &mut self.state.outline, }; - match &self.proxy.outlines { - OutlinesProxy::Cff if self.cff.is_some() => { - let cff_scaler = self.cff.as_ref().unwrap(); - outline.begin_layer(color_index); - if self - .state - .cff_cache - .scale( - cff_scaler, - self.id, - glyph_id, - self.size, - &self.coords, - self.hint, - outline, - ) - .is_some() - { - outline.maybe_close(); - outline.finish(); - true - } else { - false - } - } - OutlinesProxy::Glyf(proxy) => { - if self.glyf_state.is_none() { - self.glyf_state = Some(glyf::ScalerState::new( - self.font.data, - self.id, - &self.coords, - proxy, - &self.proxy.metrics, - self.size, - self.hint, - )); - } - let state = self.glyf_state.as_mut().unwrap(); + if let Some(outlines) = &self.outlines { + if let Some(glyph) = outlines.get(SkrifaGlyphId::from(glyph_id)) { outline.begin_layer(color_index); - if self.state.glyf_scaler.scale(state, glyph_id).is_some() { - let scaler = &self.state.glyf_scaler; - fill_outline( - outline, - &scaler.scaled, - &scaler.contours, - &scaler.tags, - self.size != 0., - ); + let settings: skrifa::outline::DrawSettings = + if let Some(hinting_instance) = &self.hinting_instance { + (*hinting_instance).into() + } else { + ( + self.skrifa_size, + skrifa::instance::LocationRef::new(&self.coords), + ) + .into() + }; + if glyph.draw(settings, outline).is_ok() { outline.maybe_close(); outline.finish(); - true - } else { - false + return true; } } - _ => false, } + false } // Unused when render feature is disabled. @@ -998,111 +972,3 @@ impl<'a> Render<'a> { } } } - -fn fill_outline( - outline: &mut Outline, - points: &[glyf::Point], - contours: &[u16], - tags: &[u8], - scaled: bool, -) -> Option<()> { - use glyf::Point as PointI; - #[inline(always)] - fn conv(p: glyf::Point, s: f32) -> Point { - Point::new(p.x as f32 * s, p.y as f32 * s) - } - const TAG_MASK: u8 = 0x3; - const CONIC: u8 = 0x0; - const ON: u8 = 0x1; - const CUBIC: u8 = 0x2; - let s = if scaled { 1. / 64. } else { 1. }; - for c in 0..contours.len() { - let mut cur = if c > 0 { - contours[c - 1] as usize + 1 - } else { - 0 - }; - let mut last = contours[c] as usize; - if last < cur || last >= points.len() { - return None; - } - let mut v_start = points[cur]; - let v_last = points[last]; - let mut tag = tags[cur] & TAG_MASK; - if tag == CUBIC { - return None; - } - let mut step_point = true; - if tag == CONIC { - if tags[last] & TAG_MASK == ON { - v_start = v_last; - last -= 1; - } else { - v_start.x = (v_start.x + v_last.x) / 2; - v_start.y = (v_start.y + v_last.y) / 2; - } - step_point = false; - } - outline.move_to(conv(v_start, s)); - // let mut do_close = true; - while cur < last { - if step_point { - cur += 1; - } - step_point = true; - tag = tags[cur] & TAG_MASK; - match tag { - ON => { - outline.line_to(conv(points[cur], s)); - continue; - } - CONIC => { - let mut do_close_conic = true; - let mut v_control = points[cur]; - while cur < last { - cur += 1; - let point = points[cur]; - tag = tags[cur] & TAG_MASK; - if tag == ON { - outline.quad_to(conv(v_control, s), conv(point, s)); - do_close_conic = false; - break; - } - if tag != CONIC { - return None; - } - let v_middle = - PointI::new((v_control.x + point.x) / 2, (v_control.y + point.y) / 2); - outline.quad_to(conv(v_control, s), conv(v_middle, s)); - v_control = point; - } - if do_close_conic { - outline.quad_to(conv(v_control, s), conv(v_start, s)); - // do_close = false; - break; - } - continue; - } - _ => { - if cur + 1 > last || (tags[cur + 1] & TAG_MASK != CUBIC) { - return None; - } - let v1 = conv(points[cur], s); - let v2 = conv(points[cur + 1], s); - cur += 2; - if cur <= last { - outline.curve_to(v1, v2, conv(points[cur], s)); - continue; - } - outline.curve_to(v1, v2, conv(v_start, s)); - // do_close = false; - break; - } - } - } - if true { - outline.maybe_close(); - } - } - Some(()) -} diff --git a/src/scale/outline.rs b/src/scale/outline.rs index fb6ea1b..055dd77 100644 --- a/src/scale/outline.rs +++ b/src/scale/outline.rs @@ -398,3 +398,25 @@ fn compute_winding(points: &[Point]) -> u8 { 0 } } + +impl skrifa::outline::OutlinePen for Outline { + fn move_to(&mut self, x: f32, y: f32) { + self.move_to((x, y).into()); + } + + fn line_to(&mut self, x: f32, y: f32) { + self.line_to((x, y).into()); + } + + fn quad_to(&mut self, cx0: f32, cy0: f32, x: f32, y: f32) { + self.quad_to((cx0, cy0).into(), (x, y).into()); + } + + fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) { + self.curve_to((cx0, cy0).into(), (cx1, cy1).into(), (x, y).into()); + } + + fn close(&mut self) { + self.close(); + } +} diff --git a/src/scale/proxy.rs b/src/scale/proxy.rs index c17d8ad..3706e4b 100644 --- a/src/scale/proxy.rs +++ b/src/scale/proxy.rs @@ -1,40 +1,21 @@ -use crate::internal::raw_tag; - use super::{ super::{metrics::MetricsProxy, strike::BitmapStrikesProxy, FontRef}, color::ColorProxy, - glyf::GlyfProxy, }; -#[derive(Copy, Clone)] -pub enum OutlinesProxy { - None, - Cff, - Glyf(GlyfProxy), -} - #[derive(Copy, Clone)] pub struct ScalerProxy { pub metrics: MetricsProxy, pub color: ColorProxy, - pub outlines: OutlinesProxy, pub bitmaps: BitmapStrikesProxy, pub coord_count: u16, } impl ScalerProxy { pub fn from_font(font: &FontRef) -> Self { - let outlines = if let Some(glyf) = GlyfProxy::from_font(font) { - OutlinesProxy::Glyf(glyf) - } else if font.table(raw_tag(b"CFF ")).is_some() || font.table(raw_tag(b"CFF2")).is_some() { - OutlinesProxy::Cff - } else { - OutlinesProxy::None - }; Self { metrics: MetricsProxy::from_font(font), color: ColorProxy::from_font(font), - outlines, bitmaps: BitmapStrikesProxy::from_font(font), coord_count: font.variations().len() as u16, } diff --git a/src/strike.rs b/src/strike.rs index 2a2c771..cb681c8 100644 --- a/src/strike.rs +++ b/src/strike.rs @@ -666,7 +666,6 @@ fn get_data<'a>(table: &'a [u8], loc: &Location) -> Option> { let depth = loc.bit_depth as usize; let mut bitmap = BitmapData { data: &[], - flags: loc.flags, id: loc.id, ppem: loc.ppem, bit_depth: loc.bit_depth, @@ -780,7 +779,6 @@ struct BitmapData<'a> { pub data: &'a [u8], pub id: u16, pub ppem: u16, - pub flags: u8, pub bit_depth: u8, pub width: u8, pub height: u8,