diff --git a/src/hb/ot/contextual.rs b/src/hb/ot/contextual.rs index 8314fbc3..8f2bfbfc 100644 --- a/src/hb/ot/contextual.rs +++ b/src/hb/ot/contextual.rs @@ -1,10 +1,10 @@ -use super::{coverage_binary_cached, coverage_index, covered, glyph_class}; +use super::{coverage_index, covered, glyph_class}; use crate::hb::buffer::GlyphInfo; -use crate::hb::ot::{ClassDefInfo, CoverageInfo}; +use crate::hb::ot::ClassDefInfo; use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; use crate::hb::ot_layout_gsubgpos::{ apply_lookup, match_always, match_backtrack, match_glyph, match_input, match_lookahead, - may_skip_t, skipping_iterator_t, Apply, BinaryCache, ChainContextFormat2Cache, + may_skip_t, skipping_iterator_t, Apply, ApplyState, ChainContextFormat2Cache, ContextFormat2Cache, SubtableExternalCache, SubtableExternalCacheMode, WouldApply, WouldApplyContext, }; @@ -47,9 +47,8 @@ impl WouldApply for SequenceContextFormat1<'_> { } impl Apply for SequenceContextFormat1<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let index = self.coverage().ok()?.get(glyph)? as usize; + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let index = state.first_coverage_index as usize; let set = self.seq_rule_sets().get(index)?.ok()?; apply_context_rules(ctx, &set.seq_rules(), match_glyph) } @@ -85,44 +84,24 @@ impl WouldApply for SequenceContextFormat2<'_> { } impl Apply for SequenceContextFormat2<'_> { - fn apply_with_external_cache( - &self, - ctx: &mut hb_ot_apply_context_t, - external_cache: &SubtableExternalCache, - ) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let SubtableExternalCache::ContextFormat2Cache(cache) = external_cache else { + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let SubtableExternalCache::ContextFormat2Cache(cache) = state.external_cache else { return None; }; let offset_data = self.offset_data(); - coverage_binary_cached( - |gid| cache.coverage.index(&offset_data, gid), - glyph, - &cache.coverage_cache, - )?; let input_class = |gid| cache.input.class(&offset_data, gid); - let index = input_class(glyph) as usize; + let index = input_class(state.first_glyph) as usize; let set = self.class_seq_rule_sets().get(index)?.ok()?; apply_context_rules(ctx, &set.class_seq_rules(), |info, value| { input_class(info.as_glyph()) == value }) } - fn apply_cached( - &self, - ctx: &mut hb_ot_apply_context_t, - external_cache: &SubtableExternalCache, - ) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let SubtableExternalCache::ContextFormat2Cache(cache) = external_cache else { + fn apply_cached(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let SubtableExternalCache::ContextFormat2Cache(cache) = state.external_cache else { return None; }; let offset_data = self.offset_data(); - coverage_binary_cached( - |gid| cache.coverage.index(&offset_data, gid), - glyph, - &cache.coverage_cache, - )?; let input_class = |gid| cache.input.class(&offset_data, gid); let index = get_class_cached(&input_class, &mut ctx.buffer.info[ctx.buffer.idx]) as usize; let set = self.class_seq_rule_sets().get(index)?.ok()?; @@ -142,9 +121,6 @@ impl Apply for SequenceContextFormat2<'_> { fn external_cache_create(&self, _mode: SubtableExternalCacheMode) -> SubtableExternalCache { let data = self.offset_data(); SubtableExternalCache::ContextFormat2Cache(ContextFormat2Cache { - coverage_cache: BinaryCache::new(), - coverage: CoverageInfo::new(&data, self.coverage_offset().to_u32() as u16) - .unwrap_or_default(), input: ClassDefInfo::new(&data, self.class_def_offset().to_u32() as u16) .unwrap_or_default(), }) @@ -163,10 +139,8 @@ impl WouldApply for SequenceContextFormat3<'_> { } impl Apply for SequenceContextFormat3<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); + fn apply(&self, ctx: &mut hb_ot_apply_context_t, _state: &ApplyState) -> Option<()> { let input_coverages = self.coverages(); - input_coverages.get(0).ok()?.get(glyph)?; let input = |info: &mut GlyphInfo, index: u16| { input_coverages .get(index as usize + 1) @@ -230,9 +204,8 @@ impl WouldApply for ChainedSequenceContextFormat1<'_> { } impl Apply for ChainedSequenceContextFormat1<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let index = self.coverage().ok()?.get(glyph)? as usize; + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let index = state.first_coverage_index as usize; let set = self.chained_seq_rule_sets().get(index)?.ok()?; apply_chain_context_rules( ctx, @@ -344,22 +317,12 @@ fn match_class_cached2<'a>( } impl Apply for ChainedSequenceContextFormat2<'_> { - fn apply_with_external_cache( - &self, - ctx: &mut hb_ot_apply_context_t, - external_cache: &SubtableExternalCache, - ) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let SubtableExternalCache::ChainContextFormat2Cache(cache) = external_cache else { + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let SubtableExternalCache::ChainContextFormat2Cache(cache) = state.external_cache else { return None; }; let offset_data = self.offset_data(); - coverage_binary_cached( - |gid| cache.coverage.index(&offset_data, gid), - glyph, - &cache.coverage_cache, - )?; - let index = cache.input.class(&offset_data, glyph) as usize; + let index = cache.input.class(&offset_data, state.first_glyph) as usize; let set = self.chained_class_seq_rule_sets().get(index)?.ok()?; apply_chain_context_rules( ctx, @@ -371,21 +334,11 @@ impl Apply for ChainedSequenceContextFormat2<'_> { ), ) } - fn apply_cached( - &self, - ctx: &mut hb_ot_apply_context_t, - external_cache: &SubtableExternalCache, - ) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let SubtableExternalCache::ChainContextFormat2Cache(cache) = external_cache else { + fn apply_cached(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let SubtableExternalCache::ChainContextFormat2Cache(cache) = state.external_cache else { return None; }; let offset_data = self.offset_data(); - coverage_binary_cached( - |gid| cache.coverage.index(&offset_data, gid), - glyph, - &cache.coverage_cache, - )?; let input_class = |gid| cache.input.class(&offset_data, gid); let lookahead_class = |gid| cache.lookahead.class(&offset_data, gid); let index = get_class_cached2(&input_class, &mut ctx.buffer.info[ctx.buffer.idx]) as usize; @@ -413,9 +366,6 @@ impl Apply for ChainedSequenceContextFormat2<'_> { fn external_cache_create(&self, _mode: SubtableExternalCacheMode) -> SubtableExternalCache { let data = self.offset_data(); SubtableExternalCache::ChainContextFormat2Cache(ChainContextFormat2Cache { - coverage_cache: BinaryCache::new(), - coverage: CoverageInfo::new(&data, self.coverage_offset().to_u32() as u16) - .unwrap_or_default(), backtrack: ClassDefInfo::new(&data, self.backtrack_class_def_offset().to_u32() as u16) .unwrap_or_default(), input: ClassDefInfo::new(&data, self.input_class_def_offset().to_u32() as u16) @@ -446,11 +396,8 @@ impl WouldApply for ChainedSequenceContextFormat3<'_> { } impl Apply for ChainedSequenceContextFormat3<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - + fn apply(&self, ctx: &mut hb_ot_apply_context_t, _state: &ApplyState) -> Option<()> { let input_coverages = self.input_coverages(); - input_coverages.get(0).ok()?.get(glyph)?; let backtrack_coverages = self.backtrack_coverages(); let lookahead_coverages = self.lookahead_coverages(); diff --git a/src/hb/ot/gpos/cursive.rs b/src/hb/ot/gpos/cursive.rs index e408ce1b..011da4e3 100644 --- a/src/hb/ot/gpos/cursive.rs +++ b/src/hb/ot/gpos/cursive.rs @@ -2,16 +2,13 @@ use crate::hb::buffer::HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; use crate::hb::ot_layout_common::lookup_flags; use crate::hb::ot_layout_gpos_table::attach_type; use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{skipping_iterator_t, Apply}; +use crate::hb::ot_layout_gsubgpos::{skipping_iterator_t, Apply, ApplyState}; use crate::{Direction, GlyphPosition}; use read_fonts::tables::gpos::CursivePosFormat1; impl Apply for CursivePosFormat1<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let this = ctx.buffer.cur(0).as_glyph(); - - let coverage = self.coverage().ok()?; - let index_this = coverage.get(this)? as usize; + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let index_this = state.first_coverage_index as usize; let records = self.entry_exit_record(); let offset_data = self.offset_data(); let entry_this = records.get(index_this)?.entry_anchor(offset_data)?.ok()?; @@ -28,7 +25,7 @@ impl Apply for CursivePosFormat1<'_> { let i = iter.index(); let prev = iter.buffer.info[i].as_glyph(); - let index_prev = coverage.get(prev)? as usize; + let index_prev = state.coverage.index(&self.offset_data(), prev)? as usize; let Some(exit_prev) = records .get(index_prev) .and_then(|rec| rec.exit_anchor(offset_data).transpose().ok().flatten()) diff --git a/src/hb/ot/gpos/mark.rs b/src/hb/ot/gpos/mark.rs index 7cd531a6..f68563c0 100644 --- a/src/hb/ot/gpos/mark.rs +++ b/src/hb/ot/gpos/mark.rs @@ -2,7 +2,7 @@ use crate::hb::buffer::{hb_buffer_t, HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT} use crate::hb::ot_layout_common::lookup_flags; use crate::hb::ot_layout_gpos_table::attach_type; use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{match_t, skipping_iterator_t, Apply, MatchSource}; +use crate::hb::ot_layout_gsubgpos::{match_t, skipping_iterator_t, Apply, ApplyState, MatchSource}; use read_fonts::tables::gpos::{ AnchorTable, MarkArray, MarkBasePosFormat1, MarkLigPosFormat1, MarkMarkPosFormat1, }; @@ -49,9 +49,8 @@ impl MarkArrayExt for MarkArray<'_> { } impl Apply for MarkBasePosFormat1<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let mark_glyph = ctx.buffer.cur(0).as_glyph(); - let mark_index = self.mark_coverage().ok()?.get(mark_glyph)?; + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let mark_index = state.first_coverage_index; let base_coverage = self.base_coverage().ok()?; let last_base_until = ctx.last_base_until; @@ -142,9 +141,8 @@ fn accept(buffer: &hb_buffer_t, idx: usize) -> bool { } impl Apply for MarkMarkPosFormat1<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let mark1_glyph = ctx.buffer.cur(0).as_glyph(); - let mark1_index = self.mark1_coverage().ok()?.get(mark1_glyph)?; + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let mark1_index = state.first_coverage_index; let lookup_props = ctx.lookup_props; // Now we search backwards for a suitable mark glyph until a non-mark glyph let mut iter = skipping_iterator_t::new(ctx, false); @@ -205,9 +203,8 @@ impl Apply for MarkMarkPosFormat1<'_> { } impl Apply for MarkLigPosFormat1<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let mark_glyph = ctx.buffer.cur(0).as_glyph(); - let mark_index = self.mark_coverage().ok()?.get(mark_glyph)? as usize; + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let mark_index = state.first_coverage_index as usize; // Due to borrowing rules, we have this piece of code before creating the // iterator, unlike in harfbuzz. diff --git a/src/hb/ot/gpos/pair.rs b/src/hb/ot/gpos/pair.rs index 4f5ad18c..b59c27cc 100644 --- a/src/hb/ot/gpos/pair.rs +++ b/src/hb/ot/gpos/pair.rs @@ -1,32 +1,16 @@ -use crate::hb::ot::{coverage_index, coverage_index_cached, ClassDefInfo, CoverageInfo}; +use crate::hb::ot::ClassDefInfo; use crate::hb::ot::{glyph_class, glyph_class_cached}; use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; use crate::hb::ot_layout_gsubgpos::{ - skipping_iterator_t, Apply, PairPosFormat1Cache, PairPosFormat1SmallCache, PairPosFormat2Cache, - PairPosFormat2SmallCache, SubtableExternalCache, SubtableExternalCacheMode, + skipping_iterator_t, Apply, ApplyState, PairPosFormat2Cache, PairPosFormat2SmallCache, + SubtableExternalCache, SubtableExternalCacheMode, }; use alloc::boxed::Box; use read_fonts::tables::gpos::{PairPosFormat1, PairPosFormat2}; impl Apply for PairPosFormat1<'_> { - fn apply_with_external_cache( - &self, - ctx: &mut hb_ot_apply_context_t, - external_cache: &SubtableExternalCache, - ) -> Option<()> { - let first_glyph = ctx.buffer.cur(0).as_glyph(); - - let first_glyph_coverage_index = match external_cache { - SubtableExternalCache::PairPosFormat1Cache(cache) => coverage_index_cached( - |gid| self.coverage().ok()?.get(gid), - first_glyph, - &cache.coverage, - )?, - SubtableExternalCache::PairPosFormat1SmallCache(cache) => { - cache.coverage.index(&self.offset_data(), first_glyph)? - } - _ => coverage_index(self.coverage(), first_glyph)?, - }; + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let first_glyph_coverage_index = state.first_coverage_index; let mut iter = skipping_iterator_t::new(ctx, false); iter.reset(iter.buffer.idx); @@ -120,46 +104,11 @@ impl Apply for PairPosFormat1<'_> { } None } - - fn external_cache_create(&self, mode: SubtableExternalCacheMode) -> SubtableExternalCache { - match mode { - SubtableExternalCacheMode::Full => { - SubtableExternalCache::PairPosFormat1Cache(Box::new(PairPosFormat1Cache::new())) - } - SubtableExternalCacheMode::Small => { - if let Some(coverage) = - CoverageInfo::new(&self.offset_data(), self.coverage_offset().to_u32() as u16) - { - SubtableExternalCache::PairPosFormat1SmallCache(PairPosFormat1SmallCache { - coverage, - }) - } else { - SubtableExternalCache::None - } - } - SubtableExternalCacheMode::None => SubtableExternalCache::None, - } - } } impl Apply for PairPosFormat2<'_> { - fn apply_with_external_cache( - &self, - ctx: &mut hb_ot_apply_context_t, - external_cache: &SubtableExternalCache, - ) -> Option<()> { - let first_glyph = ctx.buffer.cur(0).as_glyph(); - match external_cache { - SubtableExternalCache::PairPosFormat2Cache(cache) => coverage_index_cached( - |gid| self.coverage().ok()?.get(gid), - first_glyph, - &cache.coverage, - )?, - SubtableExternalCache::PairPosFormat2SmallCache(cache) => { - cache.coverage.index(&self.offset_data(), first_glyph)? - } - _ => coverage_index(self.coverage(), first_glyph)?, - }; + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let first_glyph = state.first_glyph; let mut iter = skipping_iterator_t::new(ctx, false); iter.reset(iter.buffer.idx); @@ -204,7 +153,7 @@ impl Apply for PairPosFormat2<'_> { } }; let data = self.offset_data(); - let (class1, class2) = match external_cache { + let (class1, class2) = match state.external_cache { SubtableExternalCache::PairPosFormat2Cache(cache) => ( glyph_class_cached( |gid| glyph_class(self.class_def1(), gid), @@ -256,12 +205,10 @@ impl Apply for PairPosFormat2<'_> { } SubtableExternalCacheMode::Small => { let data = self.offset_data(); - let coverage = CoverageInfo::new(&data, self.coverage_offset().to_u32() as u16); let class1 = ClassDefInfo::new(&data, self.class_def1_offset().to_u32() as u16); let class2 = ClassDefInfo::new(&data, self.class_def2_offset().to_u32() as u16); - if let Some((coverage, (first, second))) = coverage.zip(class1.zip(class2)) { + if let Some((first, second)) = class1.zip(class2) { SubtableExternalCache::PairPosFormat2SmallCache(PairPosFormat2SmallCache { - coverage, first, second, }) diff --git a/src/hb/ot/gpos/single.rs b/src/hb/ot/gpos/single.rs index a5b3ffb3..38daeb62 100644 --- a/src/hb/ot/gpos/single.rs +++ b/src/hb/ot/gpos/single.rs @@ -1,11 +1,9 @@ -use crate::hb::ot_layout_gsubgpos::Apply; use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; +use crate::hb::ot_layout_gsubgpos::{Apply, ApplyState}; use read_fonts::tables::gpos::{SinglePosFormat1, SinglePosFormat2}; impl Apply for SinglePosFormat1<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - self.coverage().ok()?.get(glyph)?; + fn apply(&self, ctx: &mut hb_ot_apply_context_t, _state: &ApplyState) -> Option<()> { let format = self.value_format(); let offset = self.shape().value_record_byte_range().start; super::apply_value(ctx, ctx.buffer.idx, &self.offset_data(), offset, format); @@ -15,9 +13,8 @@ impl Apply for SinglePosFormat1<'_> { } impl Apply for SinglePosFormat2<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let index = self.coverage().ok()?.get(glyph)? as usize; + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let index = state.first_coverage_index as usize; let format = self.value_format(); let offset = self.shape().value_records_byte_range().start + (format.record_byte_len() * index); diff --git a/src/hb/ot/gsub/alternate.rs b/src/hb/ot/gsub/alternate.rs index d093d92a..3827ac3d 100644 --- a/src/hb/ot/gsub/alternate.rs +++ b/src/hb/ot/gsub/alternate.rs @@ -1,9 +1,9 @@ use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{Apply, WouldApply, WouldApplyContext}; +use crate::hb::ot_layout_gsubgpos::{Apply, ApplyState, WouldApply, WouldApplyContext}; use read_fonts::tables::gsub::{AlternateSet, AlternateSubstFormat1}; impl Apply for AlternateSet<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { + fn apply(&self, ctx: &mut hb_ot_apply_context_t, _state: &ApplyState) -> Option<()> { let alternates = self.alternate_glyph_ids(); let len = alternates.len() as u16; if len == 0 { @@ -41,10 +41,9 @@ impl WouldApply for AlternateSubstFormat1<'_> { } impl Apply for AlternateSubstFormat1<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let index = self.coverage().ok()?.get(glyph)?; - let set = self.alternate_sets().get(index as usize).ok()?; - set.apply(ctx) + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let index = state.first_coverage_index as usize; + let set = self.alternate_sets().get(index).ok()?; + set.apply(ctx, state) } } diff --git a/src/hb/ot/gsub/ligature.rs b/src/hb/ot/gsub/ligature.rs index b10c05ce..ee9611eb 100644 --- a/src/hb/ot/gsub/ligature.rs +++ b/src/hb/ot/gsub/ligature.rs @@ -1,15 +1,13 @@ use crate::hb::buffer::GlyphInfo; -use crate::hb::ot::{coverage_index, coverage_index_cached, CoverageInfo}; use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; use crate::hb::ot_layout_gsubgpos::{ ligate_input, match_always, match_glyph, match_input, may_skip_t, skipping_iterator_t, Apply, - LigatureSubstFormat1Cache, LigatureSubstFormat1SmallCache, SubtableExternalCache, - SubtableExternalCacheMode, WouldApply, WouldApplyContext, + ApplyState, LigatureSubstFormat1Cache, SubtableExternalCache, SubtableExternalCacheMode, + WouldApply, WouldApplyContext, }; use crate::hb::set_digest::hb_set_digest_t; -use alloc::boxed::Box; use read_fonts::tables::gsub::{Ligature, LigatureSet, LigatureSubstFormat1}; -use read_fonts::types::GlyphId; +use read_fonts::types::{BigEndian, GlyphId, GlyphId16}; impl WouldApply for Ligature<'_> { fn would_apply(&self, ctx: &WouldApplyContext) -> bool { @@ -23,44 +21,45 @@ impl WouldApply for Ligature<'_> { } } -impl Apply for Ligature<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - // Special-case to make it in-place and not consider this - // as a "ligated" substitution. - let components = self.component_glyph_ids(); - if components.is_empty() { - ctx.replace_glyph(self.ligature_glyph().into()); - Some(()) - } else { - let f = |info: &mut GlyphInfo, index| { - let value = components.get(index as usize).unwrap().get().to_u16(); - match_glyph(info, value) - }; +fn apply_ligature( + ligature: &Ligature, + ctx: &mut hb_ot_apply_context_t, + components: &[BigEndian], +) -> Option<()> { + // Special-case to make it in-place and not consider this + // as a "ligated" substitution. + if components.is_empty() { + ctx.replace_glyph(ligature.ligature_glyph().into()); + Some(()) + } else { + let f = |info: &mut GlyphInfo, index| { + let value = components.get(index as usize).unwrap().get().to_u16(); + match_glyph(info, value) + }; - let mut match_end = 0; - let mut total_component_count = 0; + let mut match_end = 0; + let mut total_component_count = 0; - if !match_input( - ctx, - components.len() as u16, - f, - &mut match_end, - Some(&mut total_component_count), - ) { - ctx.buffer - .unsafe_to_concat(Some(ctx.buffer.idx), Some(match_end)); - return None; - } - let count = components.len() + 1; - ligate_input( - ctx, - count, - match_end, - total_component_count, - self.ligature_glyph().into(), - ); - Some(()) + if !match_input( + ctx, + components.len() as u16, + f, + &mut match_end, + Some(&mut total_component_count), + ) { + ctx.buffer + .unsafe_to_concat(Some(ctx.buffer.idx), Some(match_end)); + return None; } + let count = components.len() + 1; + ligate_input( + ctx, + count, + match_end, + total_component_count, + ligature.ligature_glyph().into(), + ); + Some(()) } } @@ -91,19 +90,20 @@ impl ApplyLigatureSet for LigatureSet<'_> { if !matched { true } else { - second = iter.buffer.info[iter.index()].glyph_id.into(); + let second_info = &iter.buffer.info[iter.index()]; + second = second_info.glyph_id.into(); unsafe_to = iter.index() + 1; // Can't use the fast path if eg. the next char is a default-ignorable // or other skippable. - iter.may_skip(&iter.buffer.info[iter.index()]) != may_skip_t::SKIP_NO + iter.may_skip(second_info) != may_skip_t::SKIP_NO } }; if slow_path { // Slow path for lig in ligatures.iter().filter_map(Result::ok) { - if lig.apply(ctx).is_some() { + if apply_ligature(&lig, ctx, lig.component_glyph_ids()).is_some() { return Some(()); } } @@ -116,7 +116,7 @@ impl ApplyLigatureSet for LigatureSet<'_> { for lig in ligatures.iter().filter_map(|lig| lig.ok()) { let components = lig.component_glyph_ids(); if components.is_empty() || components[0].get() == second { - if lig.apply(ctx).is_some() { + if apply_ligature(&lig, ctx, components).is_some() { if unsafe_to_concat { ctx.buffer .unsafe_to_concat(Some(ctx.buffer.idx), Some(unsafe_to)); @@ -147,57 +147,23 @@ impl WouldApply for LigatureSubstFormat1<'_> { } impl Apply for LigatureSubstFormat1<'_> { - fn apply_with_external_cache( - &self, - ctx: &mut hb_ot_apply_context_t, - external_cache: &SubtableExternalCache, - ) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - - let (index, seconds) = match external_cache { - SubtableExternalCache::LigatureSubstFormat1Cache(cache) => ( - coverage_index_cached( - |gid| self.coverage().ok()?.get(gid), - glyph, - &cache.coverage, - )?, - &cache.seconds, - ), - SubtableExternalCache::LigatureSubstFormat1SmallCache(cache) => ( - cache.coverage.index(&self.offset_data(), glyph)?, - &cache.seconds, - ), - _ => ( - coverage_index(self.coverage(), glyph)?, - &hb_set_digest_t::full(), - ), + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let seconds = match state.external_cache { + SubtableExternalCache::LigatureSubstFormat1Cache(cache) => &cache.seconds, + _ => &hb_set_digest_t::full(), }; self.ligature_sets() - .get(index as usize) + .get(state.first_coverage_index as usize) .ok() .and_then(|set| set.apply(ctx, seconds)) } fn external_cache_create(&self, mode: SubtableExternalCacheMode) -> SubtableExternalCache { match mode { - SubtableExternalCacheMode::Full => SubtableExternalCache::LigatureSubstFormat1Cache( - Box::new(LigatureSubstFormat1Cache::new(collect_seconds(self))), - ), - SubtableExternalCacheMode::Small => { - if let Some(coverage) = - CoverageInfo::new(&self.offset_data(), self.coverage_offset().to_u32() as u16) - { - SubtableExternalCache::LigatureSubstFormat1SmallCache( - LigatureSubstFormat1SmallCache { - coverage, - seconds: collect_seconds(self), - }, - ) - } else { - SubtableExternalCache::None - } - } SubtableExternalCacheMode::None => SubtableExternalCache::None, + _ => SubtableExternalCache::LigatureSubstFormat1Cache(LigatureSubstFormat1Cache::new( + collect_seconds(self), + )), } } } diff --git a/src/hb/ot/gsub/multiple.rs b/src/hb/ot/gsub/multiple.rs index 3df37932..4368774e 100644 --- a/src/hb/ot/gsub/multiple.rs +++ b/src/hb/ot/gsub/multiple.rs @@ -1,6 +1,6 @@ use crate::hb::buffer::GlyphPropsFlags; use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{Apply, WouldApply, WouldApplyContext}; +use crate::hb::ot_layout_gsubgpos::{Apply, ApplyState, WouldApply, WouldApplyContext}; use read_fonts::tables::gsub::MultipleSubstFormat1; impl WouldApply for MultipleSubstFormat1<'_> { @@ -13,9 +13,8 @@ impl WouldApply for MultipleSubstFormat1<'_> { } impl Apply for MultipleSubstFormat1<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let gid = ctx.buffer.cur(0).as_glyph(); - let index = self.coverage().ok()?.get(gid)? as usize; + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let index = state.first_coverage_index as usize; let substs = self.sequences().get(index).ok()?.substitute_glyph_ids(); match substs.len() { // Spec disallows this, but Uniscribe allows it. diff --git a/src/hb/ot/gsub/reverse_chain.rs b/src/hb/ot/gsub/reverse_chain.rs index ed990764..c24bb7f1 100644 --- a/src/hb/ot/gsub/reverse_chain.rs +++ b/src/hb/ot/gsub/reverse_chain.rs @@ -2,7 +2,7 @@ use crate::hb::buffer::GlyphInfo; use crate::hb::ot_layout::MAX_NESTING_LEVEL; use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; use crate::hb::ot_layout_gsubgpos::{ - match_backtrack, match_lookahead, Apply, WouldApply, WouldApplyContext, + match_backtrack, match_lookahead, Apply, ApplyState, WouldApply, WouldApplyContext, }; use read_fonts::tables::gsub::ReverseChainSingleSubstFormat1; @@ -18,15 +18,12 @@ impl WouldApply for ReverseChainSingleSubstFormat1<'_> { } impl Apply for ReverseChainSingleSubstFormat1<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { // No chaining to this type. if ctx.nesting_level_left != MAX_NESTING_LEVEL { return None; } - - let glyph = ctx.buffer.cur(0).as_glyph(); - let coverage = self.coverage().ok()?; - let index = coverage.get(glyph)? as usize; + let index = state.first_coverage_index as usize; let substitutes = self.substitute_glyph_ids(); if index >= substitutes.len() { return None; diff --git a/src/hb/ot/gsub/single.rs b/src/hb/ot/gsub/single.rs index 10b2fa77..e7244866 100644 --- a/src/hb/ot/gsub/single.rs +++ b/src/hb/ot/gsub/single.rs @@ -1,5 +1,5 @@ use crate::hb::ot_layout_gsubgpos::OT::hb_ot_apply_context_t; -use crate::hb::ot_layout_gsubgpos::{Apply, WouldApply, WouldApplyContext}; +use crate::hb::ot_layout_gsubgpos::{Apply, ApplyState, WouldApply, WouldApplyContext}; use read_fonts::tables::gsub::{SingleSubstFormat1, SingleSubstFormat2}; impl WouldApply for SingleSubstFormat1<'_> { @@ -10,10 +10,8 @@ impl WouldApply for SingleSubstFormat1<'_> { } impl Apply for SingleSubstFormat1<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - self.coverage().ok()?.get(glyph)?; - let subst = (glyph.to_u32() as i32 + self.delta_glyph_id() as i32) as u16; + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let subst = (state.first_glyph.to_u32() as i32 + self.delta_glyph_id() as i32) as u16; ctx.replace_glyph(subst.into()); Some(()) } @@ -29,10 +27,12 @@ impl WouldApply for SingleSubstFormat2<'_> { } impl Apply for SingleSubstFormat2<'_> { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - let glyph = ctx.buffer.cur(0).as_glyph(); - let index = self.coverage().ok()?.get(glyph)? as usize; - let subst = self.substitute_glyph_ids().get(index)?.get().to_u16(); + fn apply(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { + let subst = self + .substitute_glyph_ids() + .get(state.first_coverage_index as usize)? + .get() + .to_u16(); ctx.replace_glyph(subst.into()); Some(()) } diff --git a/src/hb/ot/lookup.rs b/src/hb/ot/lookup.rs index 43162b74..a7d7a18d 100644 --- a/src/hb/ot/lookup.rs +++ b/src/hb/ot/lookup.rs @@ -2,8 +2,8 @@ use crate::hb::{ hb_font_t, ot_layout::TableIndex, ot_layout_gsubgpos::{ - Apply, SubtableExternalCache, SubtableExternalCacheMode, WouldApply, WouldApplyContext, - OT::hb_ot_apply_context_t, + Apply, ApplyState, SubtableCoverage, SubtableExternalCache, SubtableExternalCacheMode, + WouldApply, WouldApplyContext, OT::hb_ot_apply_context_t, }, set_digest::hb_set_digest_t, GlyphInfo, @@ -26,6 +26,7 @@ use read_fonts::{ SequenceContextFormat1, SequenceContextFormat2, SequenceContextFormat3, }, }, + types::GlyphId, FontData, FontRead, Offset, ReadError, }; @@ -254,7 +255,10 @@ impl LookupInfo { } let is_cached = use_hot_subtable_cache & (self.subtable_cache_user_idx == Some(subtable_idx)); - if subtable_info.apply(ctx, table_data, is_cached).is_some() { + if subtable_info + .apply(ctx, table_data, is_cached, glyph.into()) + .is_some() + { return Some(()); } } @@ -367,13 +371,13 @@ pub struct SubtableInfo { /// Byte offset to the subtable from the base of the GSUB or GPOS /// table. pub offset: u32, + pub coverage: SubtableCoverage, pub digest: hb_set_digest_t, pub apply_fns: [SubtableApplyFn; 2], pub external_cache: SubtableExternalCache, } -pub type SubtableApplyFn = - fn(&mut hb_ot_apply_context_t, &SubtableExternalCache, &[u8]) -> Option<()>; +pub type SubtableApplyFn = fn(&mut hb_ot_apply_context_t, &ApplyState, &[u8]) -> Option<()>; impl SubtableInfo { #[inline] @@ -382,9 +386,17 @@ impl SubtableInfo { ctx: &mut hb_ot_apply_context_t, table_data: &[u8], is_cached: bool, + gid: GlyphId, ) -> Option<()> { let subtable_data = table_data.get(self.offset as usize..)?; - self.apply_fns[is_cached as usize](ctx, &self.external_cache, subtable_data) + let coverage_index = self.coverage.index(&FontData::new(subtable_data), gid)?; + let state = ApplyState { + first_glyph: gid, + first_coverage_index: coverage_index, + coverage: &self.coverage, + external_cache: &self.external_cache, + }; + self.apply_fns[is_cached as usize](ctx, &state, subtable_data) } } @@ -392,20 +404,20 @@ macro_rules! apply_fns { ($apply:ident, $apply_cached:ident, $ty:ident) => { fn $apply( ctx: &mut hb_ot_apply_context_t, - external_cache: &SubtableExternalCache, + state: &ApplyState, table_data: &[u8], ) -> Option<()> { let t = $ty::read(FontData::new(table_data)).ok()?; - t.apply_with_external_cache(ctx, external_cache) + t.apply(ctx, state) } fn $apply_cached( ctx: &mut hb_ot_apply_context_t, - external_cache: &SubtableExternalCache, + state: &ApplyState, table_data: &[u8], ) -> Option<()> { let t = $ty::read(FontData::new(table_data)).ok()?; - t.apply_cached(ctx, external_cache) + t.apply_cached(ctx, state) } }; } @@ -427,6 +439,7 @@ apply_fns!( ligature_subst1_cached, LigatureSubstFormat1 ); + apply_fns!(single_pos1, single_pos1_cached, SinglePosFormat1); apply_fns!(single_pos2, single_pos2_cached, SinglePosFormat2); apply_fns!(pair_pos1, pair_pos1_cached, PairPosFormat1); @@ -494,72 +507,122 @@ impl SubtableInfo { ) -> Option<(Self, u32)> { let data = table_data.split_off(subtable_offset as usize)?; let maybe_external_cache = |s: &dyn Apply| s.external_cache_create(cache_mode); - let (kind, (external_cache, cache_cost, coverage), apply_fns): ( + let (kind, (external_cache, cache_cost, coverage_offset, coverage), apply_fns): ( SubtableKind, - (SubtableExternalCache, u32, CoverageTable), + (SubtableExternalCache, u32, u32, CoverageTable), [SubtableApplyFn; 2], ) = match (is_subst, lookup_type) { (true, 1) => match SingleSubst::read(data).ok()? { SingleSubst::Format1(s) => ( SubtableKind::SingleSubst1, - (maybe_external_cache(&s), s.cache_cost(), s.coverage().ok()?), + ( + maybe_external_cache(&s), + s.cache_cost(), + s.coverage_offset().to_u32(), + s.coverage().ok()?, + ), [single_subst1, single_subst1_cached as _], ), SingleSubst::Format2(s) => ( SubtableKind::SingleSubst2, - (maybe_external_cache(&s), s.cache_cost(), s.coverage().ok()?), + ( + maybe_external_cache(&s), + s.cache_cost(), + s.coverage_offset().to_u32(), + s.coverage().ok()?, + ), [single_subst2, single_subst2_cached as _], ), }, (false, 1) => match SinglePos::read(data).ok()? { SinglePos::Format1(s) => ( SubtableKind::SinglePos1, - (maybe_external_cache(&s), s.cache_cost(), s.coverage().ok()?), + ( + maybe_external_cache(&s), + s.cache_cost(), + s.coverage_offset().to_u32(), + s.coverage().ok()?, + ), [single_pos1, single_pos1_cached as _], ), SinglePos::Format2(s) => ( SubtableKind::SinglePos2, - (maybe_external_cache(&s), s.cache_cost(), s.coverage().ok()?), + ( + maybe_external_cache(&s), + s.cache_cost(), + s.coverage_offset().to_u32(), + s.coverage().ok()?, + ), [single_pos2, single_pos2_cached as _], ), }, (true, 2) => ( SubtableKind::MultipleSubst1, MultipleSubstFormat1::read(data).ok().and_then(|t| { - Some((maybe_external_cache(&t), t.cache_cost(), t.coverage().ok()?)) + Some(( + maybe_external_cache(&t), + t.cache_cost(), + t.coverage_offset().to_u32(), + t.coverage().ok()?, + )) })?, [multiple_subst1, multiple_subst1_cached as _], ), (false, 2) => match PairPos::read(data).ok()? { PairPos::Format1(s) => ( SubtableKind::PairPos1, - (maybe_external_cache(&s), s.cache_cost(), s.coverage().ok()?), + ( + maybe_external_cache(&s), + s.cache_cost(), + s.coverage_offset().to_u32(), + s.coverage().ok()?, + ), [pair_pos1, pair_pos1_cached as _], ), PairPos::Format2(s) => ( SubtableKind::PairPos2, - (maybe_external_cache(&s), s.cache_cost(), s.coverage().ok()?), + ( + maybe_external_cache(&s), + s.cache_cost(), + s.coverage_offset().to_u32(), + s.coverage().ok()?, + ), [pair_pos2, pair_pos2_cached as _], ), }, (true, 3) => ( SubtableKind::AlternateSubst1, AlternateSubstFormat1::read(data).ok().and_then(|t| { - Some((maybe_external_cache(&t), t.cache_cost(), t.coverage().ok()?)) + Some(( + maybe_external_cache(&t), + t.cache_cost(), + t.coverage_offset().to_u32(), + t.coverage().ok()?, + )) })?, [alternate_subst1, alternate_subst1_cached as _], ), (false, 3) => ( SubtableKind::CursivePos1, CursivePosFormat1::read(data).ok().and_then(|t| { - Some((maybe_external_cache(&t), t.cache_cost(), t.coverage().ok()?)) + Some(( + maybe_external_cache(&t), + t.cache_cost(), + t.coverage_offset().to_u32(), + t.coverage().ok()?, + )) })?, [cursive_pos1, cursive_pos1_cached as _], ), (true, 4) => ( SubtableKind::LigatureSubst1, LigatureSubstFormat1::read(data).ok().and_then(|t| { - Some((maybe_external_cache(&t), t.cache_cost(), t.coverage().ok()?)) + Some(( + maybe_external_cache(&t), + t.cache_cost(), + t.coverage_offset().to_u32(), + t.coverage().ok()?, + )) })?, [ligature_subst1, ligature_subst1_cached as _], ), @@ -569,6 +632,7 @@ impl SubtableInfo { Some(( maybe_external_cache(&t), t.cache_cost(), + t.mark_coverage_offset().to_u32(), t.mark_coverage().ok()?, )) })?, @@ -577,12 +641,22 @@ impl SubtableInfo { (true, 5) | (false, 7) => match SequenceContext::read(data).ok()? { SequenceContext::Format1(s) => ( SubtableKind::ContextFormat1, - (maybe_external_cache(&s), s.cache_cost(), s.coverage().ok()?), + ( + maybe_external_cache(&s), + s.cache_cost(), + s.coverage_offset().to_u32(), + s.coverage().ok()?, + ), [context1, context1_cached as _], ), SequenceContext::Format2(s) => ( SubtableKind::ContextFormat2, - (maybe_external_cache(&s), s.cache_cost(), s.coverage().ok()?), + ( + maybe_external_cache(&s), + s.cache_cost(), + s.coverage_offset().to_u32(), + s.coverage().ok()?, + ), [context2, context2_cached as _], ), SequenceContext::Format3(s) => ( @@ -590,6 +664,7 @@ impl SubtableInfo { ( maybe_external_cache(&s), s.cache_cost(), + s.coverage_offsets().first()?.get().to_u32(), s.coverages().get(0).ok()?, ), [context3, context3_cached as _], @@ -601,6 +676,7 @@ impl SubtableInfo { Some(( maybe_external_cache(&t), t.cache_cost(), + t.mark_coverage_offset().to_u32(), t.mark_coverage().ok()?, )) })?, @@ -609,12 +685,22 @@ impl SubtableInfo { (true, 6) | (false, 8) => match ChainedSequenceContext::read(data).ok()? { ChainedSequenceContext::Format1(s) => ( SubtableKind::ChainedContextFormat1, - (maybe_external_cache(&s), s.cache_cost(), s.coverage().ok()?), + ( + maybe_external_cache(&s), + s.cache_cost(), + s.coverage_offset().to_u32(), + s.coverage().ok()?, + ), [chained_context1, chained_context1_cached as _], ), ChainedSequenceContext::Format2(s) => ( SubtableKind::ChainedContextFormat2, - (maybe_external_cache(&s), s.cache_cost(), s.coverage().ok()?), + ( + maybe_external_cache(&s), + s.cache_cost(), + s.coverage_offset().to_u32(), + s.coverage().ok()?, + ), [chained_context2, chained_context2_cached as _], ), ChainedSequenceContext::Format3(s) => ( @@ -622,6 +708,7 @@ impl SubtableInfo { ( maybe_external_cache(&s), s.cache_cost(), + s.input_coverage_offsets().first()?.get().to_u32(), s.input_coverages().get(0).ok()?, ), [chained_context3, chained_context3_cached as _], @@ -645,6 +732,7 @@ impl SubtableInfo { Some(( maybe_external_cache(&t), t.cache_cost(), + t.mark1_coverage_offset().to_u32(), t.mark1_coverage().ok()?, )) })?, @@ -655,7 +743,12 @@ impl SubtableInfo { ReverseChainSingleSubstFormat1::read(data) .ok() .and_then(|t| { - Some((maybe_external_cache(&t), t.cache_cost(), t.coverage().ok()?)) + Some(( + maybe_external_cache(&t), + t.cache_cost(), + t.coverage_offset().to_u32(), + t.coverage().ok()?, + )) })?, [rev_chain_single_subst1, rev_chain_single_subst1_cached as _], ), @@ -663,11 +756,28 @@ impl SubtableInfo { }; let mut digest = hb_set_digest_t::new(); digest.add_coverage(&coverage); + let coverage = SubtableCoverage::new( + &data, + coverage_offset as u16, + cache_mode, + // The following subtables don't need a coverage index so + // we use a smaller binary cache instead + matches!( + kind, + SubtableKind::SingleSubst1 + | SubtableKind::SinglePos1 + | SubtableKind::PairPos2 + | SubtableKind::ContextFormat2 + | SubtableKind::ChainedContextFormat2 + ), + ) + .unwrap_or_default(); Some(( SubtableInfo { kind, offset: subtable_offset, digest, + coverage, apply_fns, external_cache, }, diff --git a/src/hb/ot/mod.rs b/src/hb/ot/mod.rs index 4f6c2b55..1f06d60c 100644 --- a/src/hb/ot/mod.rs +++ b/src/hb/ot/mod.rs @@ -2,7 +2,7 @@ use super::buffer::GlyphPropsFlags; use super::ot_layout::TableIndex; use super::{common::TagExt, set_digest::hb_set_digest_t}; use crate::hb::hb_tag_t; -use crate::hb::ot_layout_gsubgpos::{BinaryCache, MappingCache}; +use crate::hb::ot_layout_gsubgpos::MappingCache; use crate::hb::tables::TableRanges; use alloc::vec::Vec; use lookup::{LookupCache, LookupInfo}; @@ -508,54 +508,6 @@ fn coverage_index(coverage: Result, gid: GlyphId) -> O coverage.ok().and_then(|coverage| coverage.get(gid)) } -fn coverage_index_cached( - coverage: impl Fn(GlyphId) -> Option, - gid: GlyphId, - cache: &MappingCache, -) -> Option { - if let Some(index) = cache.get(gid.into()) { - if index == MappingCache::MAX_VALUE { - None - } else { - Some(index as u16) - } - } else { - let index = coverage(gid); - if let Some(index) = index { - if (index as u32) < MappingCache::MAX_VALUE { - cache.set(gid.into(), index as u32); - } - Some(index) - } else { - cache.set(gid.into(), MappingCache::MAX_VALUE); - None - } - } -} - -fn coverage_binary_cached( - coverage: impl Fn(GlyphId) -> Option, - gid: GlyphId, - cache: &BinaryCache, -) -> Option { - if let Some(index) = cache.get(gid.into()) { - if index == BinaryCache::MAX_VALUE { - None - } else { - Some(true) - } - } else { - let index = coverage(gid); - if index.is_some() { - cache.set(gid.into(), 0); - Some(true) - } else { - cache.set(gid.into(), BinaryCache::MAX_VALUE); - None - } - } -} - fn covered(coverage: Result, gid: GlyphId) -> bool { coverage_index(coverage, gid).is_some() } diff --git a/src/hb/ot_layout_gsubgpos.rs b/src/hb/ot_layout_gsubgpos.rs index a865ce2d..abffbdd6 100644 --- a/src/hb/ot_layout_gsubgpos.rs +++ b/src/hb/ot_layout_gsubgpos.rs @@ -14,6 +14,7 @@ use crate::hb::unicode::GeneralCategory; use alloc::boxed::Box; use read_fonts::tables::layout::SequenceLookupRecord; use read_fonts::types::GlyphId; +use read_fonts::FontData; pub(crate) type MatchPositions = smallvec::SmallVec<[u32; 8]>; @@ -674,43 +675,90 @@ pub(crate) enum SubtableExternalCacheMode { Full, } -pub(crate) struct LigatureSubstFormat1Cache { - pub seconds: hb_set_digest_t, - pub coverage: MappingCache, -} - -impl LigatureSubstFormat1Cache { - pub fn new(seconds: hb_set_digest_t) -> Self { - LigatureSubstFormat1Cache { - coverage: MappingCache::new(), - seconds, - } - } +#[derive(Default)] +pub enum SubtableCoverageCache { + #[default] + None, + Mapping(Box), + Binary(Box), } -pub(crate) struct LigatureSubstFormat1SmallCache { +#[derive(Default)] +pub(crate) struct SubtableCoverage { pub coverage: CoverageInfo, - pub seconds: hb_set_digest_t, + pub cache: SubtableCoverageCache, } -pub(crate) struct PairPosFormat1Cache { - pub coverage: MappingCache, -} +impl SubtableCoverage { + pub fn new( + parent_data: &FontData, + coverage_offset: u16, + mode: SubtableExternalCacheMode, + use_binary: bool, + ) -> Option { + let coverage = CoverageInfo::new(parent_data, coverage_offset)?; + let cache = if mode == SubtableExternalCacheMode::Full { + if use_binary { + SubtableCoverageCache::Binary(Box::new(BinaryCache::new())) + } else { + SubtableCoverageCache::Mapping(Box::new(MappingCache::new())) + } + } else { + SubtableCoverageCache::None + }; + Some(Self { coverage, cache }) + } -impl PairPosFormat1Cache { - pub fn new() -> Self { - PairPosFormat1Cache { - coverage: MappingCache::new(), + pub fn index(&self, parent_data: &FontData, gid: GlyphId) -> Option { + match &self.cache { + SubtableCoverageCache::None => self.coverage.index(parent_data, gid), + SubtableCoverageCache::Binary(cache) => { + if let Some(index) = cache.get(gid.into()) { + if index == BinaryCache::MAX_VALUE { + None + } else { + Some(1) + } + } else if self.coverage.index(parent_data, gid).is_some() { + cache.set(gid.into(), 0); + Some(1) + } else { + cache.set(gid.into(), BinaryCache::MAX_VALUE); + None + } + } + SubtableCoverageCache::Mapping(cache) => { + if let Some(index) = cache.get(gid.into()) { + if index == MappingCache::MAX_VALUE { + None + } else { + Some(index as u16) + } + } else if let Some(index) = self.coverage.index(parent_data, gid) { + if (index as u32) < MappingCache::MAX_VALUE { + cache.set(gid.into(), index as u32); + } + Some(index) + } else { + cache.set(gid.into(), MappingCache::MAX_VALUE); + None + } + } } } } -pub(crate) struct PairPosFormat1SmallCache { - pub coverage: CoverageInfo, +pub(crate) struct LigatureSubstFormat1Cache { + pub seconds: hb_set_digest_t, +} + +impl LigatureSubstFormat1Cache { + pub fn new(seconds: hb_set_digest_t) -> Self { + LigatureSubstFormat1Cache { seconds } + } } pub(crate) struct PairPosFormat2Cache { - pub coverage: MappingCache, pub first: MappingCache, pub second: MappingCache, } @@ -718,7 +766,6 @@ pub(crate) struct PairPosFormat2Cache { impl PairPosFormat2Cache { pub fn new() -> Self { PairPosFormat2Cache { - coverage: MappingCache::new(), first: MappingCache::new(), second: MappingCache::new(), } @@ -726,63 +773,44 @@ impl PairPosFormat2Cache { } pub(crate) struct PairPosFormat2SmallCache { - pub coverage: CoverageInfo, pub first: ClassDefInfo, pub second: ClassDefInfo, } pub(crate) struct ContextFormat2Cache { - pub coverage: CoverageInfo, pub input: ClassDefInfo, - pub coverage_cache: BinaryCache, } pub(crate) struct ChainContextFormat2Cache { - pub coverage: CoverageInfo, pub backtrack: ClassDefInfo, pub input: ClassDefInfo, pub lookahead: ClassDefInfo, - pub coverage_cache: BinaryCache, } pub(crate) enum SubtableExternalCache { None, - LigatureSubstFormat1Cache(Box), - LigatureSubstFormat1SmallCache(LigatureSubstFormat1SmallCache), - PairPosFormat1Cache(Box), - PairPosFormat1SmallCache(PairPosFormat1SmallCache), + LigatureSubstFormat1Cache(LigatureSubstFormat1Cache), PairPosFormat2Cache(Box), PairPosFormat2SmallCache(PairPosFormat2SmallCache), ContextFormat2Cache(ContextFormat2Cache), ChainContextFormat2Cache(ChainContextFormat2Cache), } +pub struct ApplyState<'a> { + pub first_glyph: GlyphId, + pub first_coverage_index: u16, + pub coverage: &'a SubtableCoverage, + pub external_cache: &'a SubtableExternalCache, +} + /// Apply a lookup. pub trait Apply { - fn apply(&self, ctx: &mut hb_ot_apply_context_t) -> Option<()> { - // Default implementation just calls `apply_with_external_cache`. - self.apply_with_external_cache(ctx, &SubtableExternalCache::None) - } - - // The rest are relevant to subtables only - - fn apply_with_external_cache( - &self, - ctx: &mut hb_ot_apply_context_t, - _external_cache: &SubtableExternalCache, - ) -> Option<()> { - // Default implementation just calls `apply`. - self.apply(ctx) - } + fn apply(&self, ctx: &mut hb_ot_apply_context_t, _state: &ApplyState) -> Option<()>; - fn apply_cached( - &self, - ctx: &mut hb_ot_apply_context_t, - external_cache: &SubtableExternalCache, - ) -> Option<()> { + fn apply_cached(&self, ctx: &mut hb_ot_apply_context_t, state: &ApplyState) -> Option<()> { // Default implementation just calls `apply_with_external_cache`. // This is used to apply the lookup with glyph-info caching. - self.apply_with_external_cache(ctx, external_cache) + self.apply(ctx, state) } fn cache_cost(&self) -> u32 {