1use std::{
4 cmp::Reverse,
5 collections::{BinaryHeap, HashMap, HashSet},
6 fmt::{Debug, Display},
7};
8
9use crate::tables::{
10 layout::VariationIndex,
11 variations::{
12 common_builder::{TemporaryDeltaSetId, VarStoreRemapping},
13 ItemVariationData, ItemVariationStore, VariationRegion, VariationRegionList,
14 },
15};
16use indexmap::IndexMap;
17
18pub type VariationIndexRemapping = VarStoreRemapping<VariationIndex>;
19
20#[derive(Clone, Debug)]
25pub struct VariationStoreBuilder {
26 all_regions: HashMap<VariationRegion, usize>,
28 delta_sets: DeltaSetStorage,
29 axis_count: u16,
32}
33
34#[derive(Clone, Debug)]
36enum DeltaSetStorage {
37 Direct(Vec<DeltaSet>),
39 Deduplicated(IndexMap<DeltaSet, TemporaryDeltaSetId>),
41}
42
43#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
47struct DeltaSet(Vec<(u16, i32)>);
48
49impl VariationStoreBuilder {
50 pub fn new(axis_count: u16) -> Self {
58 Self {
59 axis_count,
60 delta_sets: DeltaSetStorage::Deduplicated(Default::default()),
61 all_regions: Default::default(),
62 }
63 }
64
65 pub fn is_empty(&self) -> bool {
67 match &self.delta_sets {
68 DeltaSetStorage::Direct(val) => val.is_empty(),
69 DeltaSetStorage::Deduplicated(val) => val.is_empty(),
70 }
71 }
72
73 pub fn new_with_implicit_indices(axis_count: u16) -> Self {
79 VariationStoreBuilder {
80 axis_count,
81 all_regions: Default::default(),
82 delta_sets: DeltaSetStorage::Direct(Default::default()),
83 }
84 }
85
86 pub fn add_deltas<T: Into<i32>>(
87 &mut self,
88 deltas: Vec<(VariationRegion, T)>,
89 ) -> TemporaryDeltaSetId {
90 let mut delta_set = Vec::with_capacity(deltas.len());
91 for (region, delta) in deltas {
92 let region_idx = self.canonical_index_for_region(region) as u16;
93 delta_set.push((region_idx, delta.into()));
94 }
95 delta_set.sort_unstable();
96 if delta_set.iter().all(|(_, delta)| *delta == 0) {
101 delta_set.clear();
102 }
103 self.delta_sets.add(DeltaSet(delta_set))
104 }
105
106 fn canonical_index_for_region(&mut self, region: VariationRegion) -> usize {
107 let next_idx = self.all_regions.len();
108 *self.all_regions.entry(region).or_insert(next_idx)
109 }
110
111 fn make_region_list(&self, subtables: &mut [Option<ItemVariationData>]) -> VariationRegionList {
112 let used_regions = subtables
114 .iter()
115 .flatten()
116 .flat_map(|var_data| var_data.region_indexes.iter())
117 .map(|idx| *idx as usize)
118 .collect::<HashSet<_>>();
119 let mut region_list = self
121 .all_regions
122 .iter()
123 .filter_map(|(reg, idx)| {
124 if used_regions.contains(idx) {
125 Some((idx, reg.to_owned()))
126 } else {
127 None
128 }
129 })
130 .collect::<Vec<_>>();
131 region_list.sort_unstable();
132 let mut new_regions = Vec::new();
133 let mut region_map = HashMap::new();
134 for (old_idx, reg) in region_list.into_iter() {
135 region_map.insert(*old_idx as u16, new_regions.len() as u16);
136 new_regions.push(reg);
137 }
138 for var_data in subtables.iter_mut().flatten() {
140 var_data.region_indexes = var_data
141 .region_indexes
142 .iter()
143 .map(|idx| region_map[idx])
144 .collect();
145 }
146 VariationRegionList::new(self.axis_count, new_regions)
147 }
148
149 fn encoder(&self) -> Encoder<'_> {
150 Encoder::new(&self.delta_sets, self.all_regions.len() as u16)
151 }
152
153 pub fn build(self) -> (ItemVariationStore, VariationIndexRemapping) {
158 let mut key_map = VariationIndexRemapping::default();
159 let mut subtables = if matches!(self.delta_sets, DeltaSetStorage::Direct(_)) {
160 vec![self.build_unoptimized(&mut key_map)]
161 } else {
162 let mut encoder = self.encoder();
163 encoder.optimize();
164 encoder.encode(&mut key_map)
165 };
166 let region_list = self.make_region_list(&mut subtables);
167 (ItemVariationStore::new(region_list, subtables), key_map)
168 }
169
170 fn build_unoptimized(
172 &self,
173 key_map: &mut VariationIndexRemapping,
174 ) -> Option<ItemVariationData> {
175 let n_regions = self.all_regions.len() as u16;
177 let mut shape = RowShape(vec![ColumnBits::None; n_regions as usize]);
178 let mut temp = RowShape::default();
179
180 for (delta, _) in self.delta_sets.iter() {
181 temp.reuse(delta, n_regions);
182 if !shape.can_cover(&temp) {
183 shape = shape.merge(&temp);
184 }
185 }
186
187 let encoding = Encoding {
189 shape,
190 deltas: self.delta_sets.iter().collect(),
191 };
192 debug_assert!(
193 encoding.deltas.len() <= u16::MAX as usize,
194 "unmapped variation store supports at most u16::MAX items"
195 );
196 encoding.encode(key_map, 0)
197 }
198}
199
200struct Encoder<'a> {
204 encodings: Vec<Encoding<'a>>,
205}
206
207struct Encoding<'a> {
209 shape: RowShape,
210 deltas: Vec<(&'a DeltaSet, TemporaryDeltaSetId)>,
211}
212
213struct RegionMap {
218 regions_to_columns: Vec<(u16, ColumnBits)>,
225 n_active_regions: u16,
226 n_long_regions: u16,
227 long_words: bool,
228}
229
230#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
237struct RowShape(Vec<ColumnBits>);
238
239#[repr(u8)]
243#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
244enum ColumnBits {
245 None = 0,
247 One = 1,
249 Two = 2,
251 Four = 4,
253}
254
255impl<'a> Encoder<'a> {
256 fn new(delta_map: &'a DeltaSetStorage, total_regions: u16) -> Self {
257 let mut shape = RowShape::default();
258 let mut encodings: IndexMap<_, Vec<_>> = Default::default();
259
260 for (delta, idx) in delta_map.iter() {
261 shape.reuse(delta, total_regions);
262 match encodings.get_mut(&shape) {
263 Some(items) => items.push((delta, idx)),
264 None => {
265 encodings.insert(shape.clone(), vec![(delta, idx)]);
266 }
267 }
268 }
269 let encodings = encodings
270 .into_iter()
271 .map(|(shape, deltas)| Encoding { shape, deltas })
272 .collect();
273
274 Encoder { encodings }
275 }
276
277 fn cost(&self) -> usize {
278 self.encodings.iter().map(Encoding::cost).sum()
279 }
280
281 fn optimize(&mut self) {
288 let cost = self.cost();
289 log::trace!("optimizing {} encodings, {cost}B", self.encodings.len(),);
290 struct DebugTodoList<'a>(&'a [Option<Encoding<'a>>]);
292 impl Debug for DebugTodoList<'_> {
293 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294 write!(f, "Todo({} items", self.0.len())?;
295 for (i, enc) in self.0.iter().enumerate() {
296 if let Some(enc) = enc {
297 write!(f, "\n {i:>4}: {enc:?}")?;
298 }
299 }
300 writeln!(f, ")")
301 }
302 }
303 let mut to_process = std::mem::take(&mut self.encodings);
305 to_process.sort_unstable_by(Encoding::ord_matching_fonttools);
307 let mut to_process = to_process.into_iter().map(Option::Some).collect::<Vec<_>>();
310
311 let mut queue = BinaryHeap::with_capacity(to_process.len());
314
315 for (i, red) in to_process.iter().enumerate() {
316 for (j, blue) in to_process.iter().enumerate().skip(i + 1) {
317 let gain = red.as_ref().unwrap().compute_gain(blue.as_ref().unwrap());
318 if gain > 0 {
319 log::trace!("adding ({i}, {j} ({gain})) to queue");
320 queue.push(Reverse((-gain, i, j)));
323 }
324 }
325 }
326
327 while let Some(Reverse((_gain, i, j))) = queue.pop() {
329 if to_process[i].is_none() || to_process[j].is_none() {
330 continue;
331 }
332 let (Some(mut to_update), Some(to_add)) = (
335 to_process.get_mut(i).and_then(Option::take),
336 to_process.get_mut(j).and_then(Option::take),
337 ) else {
338 unreachable!("checked above")
339 };
340 log::trace!(
341 "combining {i}+{j} ({}, {} {_gain})",
342 to_update.shape,
343 to_add.shape
344 );
345
346 to_update.merge_with(to_add);
350 let n = to_process.len(); let mut maybe_existing_encoding = None;
352 for (ii, opt_encoding) in to_process.iter_mut().enumerate() {
353 let Some(encoding) = opt_encoding.take() else {
356 continue;
357 };
358
359 if encoding.shape == to_update.shape {
360 maybe_existing_encoding = Some(encoding);
365 continue;
366 }
367 let gain = to_update.compute_gain(&encoding);
368 if gain > 0 {
369 log::trace!("adding ({n}, {ii} ({gain})) to queue");
370 queue.push(Reverse((-gain, ii, n)));
371 }
372 *opt_encoding = Some(encoding);
373 }
374 if let Some(existing) = maybe_existing_encoding.take() {
375 to_update.deltas.extend(existing.deltas);
376 }
377 to_process.push(Some(to_update));
378 log::trace!("{:?}", DebugTodoList(&to_process));
379 }
380 self.encodings = to_process.into_iter().flatten().collect();
381 self.encodings
383 .iter_mut()
384 .for_each(|enc| enc.deltas.sort_unstable());
385 self.encodings
388 .sort_unstable_by(Encoding::ord_matching_fonttools);
389 log::trace!(
390 "optimized {} encodings, {}B, ({}B saved)",
391 self.encodings.len(),
392 self.cost(),
393 cost.saturating_sub(self.cost()),
394 );
395 }
396
397 fn encode(self, key_map: &mut VariationIndexRemapping) -> Vec<Option<ItemVariationData>> {
404 self.encodings
405 .into_iter()
406 .flat_map(Encoding::iter_split_into_table_size_chunks)
407 .enumerate()
408 .map(|(i, encoding)| encoding.encode(key_map, i as u16))
409 .collect()
410 }
411}
412
413impl ColumnBits {
414 fn for_val(val: i32) -> Self {
415 if val == 0 {
416 Self::None
417 } else if i8::try_from(val).is_ok() {
418 Self::One
419 } else if i16::try_from(val).is_ok() {
420 Self::Two
421 } else {
422 Self::Four
423 }
424 }
425
426 fn cost(self) -> usize {
428 self as u8 as _
429 }
430}
431
432impl RowShape {
433 fn reuse(&mut self, deltas: &DeltaSet, n_regions: u16) {
440 self.0.clear();
441 self.0.resize(n_regions as _, ColumnBits::None);
442 for (region, delta) in &deltas.0 {
443 self.0[*region as usize] = ColumnBits::for_val(*delta);
444 }
445 }
446
447 fn merge(&self, other: &Self) -> Self {
451 Self(
452 self.0
453 .iter()
454 .zip(other.0.iter())
455 .map(|(us, them)| *us.max(them))
456 .collect(),
457 )
458 }
459
460 fn can_cover(&self, other: &Self) -> bool {
462 debug_assert_eq!(self.0.len(), other.0.len());
463 self.0
464 .iter()
465 .zip(other.0.iter())
466 .all(|(us, them)| us >= them)
467 }
468
469 fn row_cost(&self) -> usize {
471 self.0.iter().copied().map(ColumnBits::cost).sum()
472 }
473
474 fn overhead(&self) -> usize {
475 const SUBTABLE_FIXED_COST: usize = 10;
477 const COST_PER_REGION: usize = 2;
478 SUBTABLE_FIXED_COST + (self.n_non_zero_regions() * COST_PER_REGION)
479 }
480
481 fn n_non_zero_regions(&self) -> usize {
482 self.0.iter().map(|x| (*x as u8).min(1) as usize).sum()
483 }
484
485 fn count_lengths(&self) -> (u16, u16, u16) {
487 self.0
488 .iter()
489 .fold((0, 0, 0), |(byte, short, long), this| match this {
490 ColumnBits::One => (byte + 1, short, long),
491 ColumnBits::Two => (byte, short + 1, long),
492 ColumnBits::Four => (byte, short, long + 1),
493 _ => (byte, short, long),
494 })
495 }
496
497 fn region_map(&self) -> RegionMap {
500 let mut with_idx = self.0.iter().copied().enumerate().collect::<Vec<_>>();
501 with_idx.sort_unstable_by_key(|(idx, bit)| (std::cmp::Reverse(*bit), *idx));
503 let mut map = vec![(0u16, ColumnBits::None); with_idx.len()];
505 for (new_idx, (canonical_idx, bits)) in with_idx.iter().enumerate() {
506 map[*canonical_idx] = (new_idx as _, *bits);
507 }
508
509 let (count_8, count_16, count_32) = self.count_lengths();
510 let long_words = count_32 > 0;
511 let n_long_regions = if long_words { count_32 } else { count_16 };
512 let n_active_regions = count_8 + count_16 + count_32;
513 RegionMap {
514 regions_to_columns: map,
515 n_active_regions,
516 n_long_regions,
517 long_words,
518 }
519 }
520
521 #[cfg(test)]
524 fn to_fonttools_repr(&self) -> u128 {
525 assert!(
526 self.0.len() <= u128::BITS as usize / 4,
527 "we can only pack 128 bits"
528 );
529
530 let has_long_word = self.0.iter().any(|bits| *bits == ColumnBits::Four);
531 let mut chars = 0;
532 let mut i = 1;
533
534 if !has_long_word {
535 for v in &self.0 {
536 if *v != ColumnBits::None {
537 chars += i;
538 }
539 if *v == ColumnBits::Two {
540 chars += i * 0b0010;
541 }
542 i <<= 4;
543 }
544 } else {
545 for v in &self.0 {
546 if *v != ColumnBits::None {
547 chars += i * 0b0011;
548 }
549 if *v == ColumnBits::Four {
550 chars += i * 0b1100;
551 }
552 i <<= 4;
553 }
554 }
555 chars
556 }
557}
558
559impl<'a> Encoding<'a> {
560 fn cost(&self) -> usize {
561 self.shape.overhead() + (self.shape.row_cost() * self.deltas.len())
562 }
563
564 fn compute_gain(&self, other: &Encoding) -> i64 {
565 let current_cost = self.cost() + other.cost();
566
567 let combined = self.shape.merge(&other.shape);
568 let combined_cost =
569 combined.overhead() + (combined.row_cost() * (self.deltas.len() + other.deltas.len()));
570 current_cost as i64 - combined_cost as i64
571 }
572
573 fn merge_with(&mut self, other: Encoding<'a>) {
574 self.shape = self.shape.merge(&other.shape);
575 self.deltas.extend(other.deltas);
576 }
577
578 fn iter_split_into_table_size_chunks(self) -> impl Iterator<Item = Encoding<'a>> {
583 let mut next = Some(self);
584 std::iter::from_fn(move || {
585 let mut this = next.take()?;
586 next = this.split_off_back();
587 Some(this)
588 })
589 }
590
591 fn split_off_back(&mut self) -> Option<Self> {
595 const MAX_ITEMS: usize = 0xFFFF;
596 if self.deltas.len() <= MAX_ITEMS {
597 return None;
598 }
599 let deltas = self.deltas.split_off(MAX_ITEMS);
600 Some(Self {
601 shape: self.shape.clone(),
602 deltas,
603 })
604 }
605
606 fn encode(
607 self,
608 key_map: &mut VariationIndexRemapping,
609 subtable_idx: u16,
610 ) -> Option<ItemVariationData> {
611 log::trace!(
612 "encoding subtable {subtable_idx} ({} rows, {}B)",
613 self.deltas.len(),
614 self.cost()
615 );
616 assert!(self.deltas.len() <= 0xffff, "call split_off_back first");
617 let item_count = self.deltas.len() as u16;
618 if item_count == 0 {
619 return None;
621 }
622
623 let region_map = self.shape.region_map();
624 let n_regions = self.shape.n_non_zero_regions();
625 let total_n_delta_values = self.deltas.len() * n_regions;
626 let mut raw_deltas = vec![0i32; total_n_delta_values];
627
628 for (i, (delta, raw_key)) in self.deltas.iter().enumerate() {
631 let pos = i * n_regions;
632 for (region, val) in &delta.0 {
633 let Some(column_idx) = region_map.column_index_for_region(*region) else {
634 continue;
635 };
636 let idx = pos + column_idx as usize;
637 raw_deltas[idx] = *val;
638 }
639 let final_key = VariationIndex::new(subtable_idx, i as u16);
640 key_map.set(*raw_key, final_key);
641 }
642
643 let delta_sets = region_map.encode_raw_delta_values(raw_deltas);
646 let word_delta_count = region_map.word_delta_count();
647 let region_indexes = region_map.indices();
648
649 Some(ItemVariationData::new(
650 item_count,
651 word_delta_count,
652 region_indexes,
653 delta_sets,
654 ))
655 }
656
657 fn ord_matching_fonttools(&self, other: &Self) -> std::cmp::Ordering {
664 let cost_ord = self.shape.row_cost().cmp(&other.shape.row_cost());
666 if cost_ord != std::cmp::Ordering::Equal {
667 return cost_ord;
668 }
669
670 debug_assert_eq!(
671 self.shape.0.len(),
672 other.shape.0.len(),
673 "all shapes have same # of regions"
674 );
675
676 for (a, b) in self.shape.0.iter().rev().zip(other.shape.0.iter().rev()) {
678 match a.cmp(b) {
679 std::cmp::Ordering::Equal => (), not_eq => return not_eq,
681 }
682 }
683 std::cmp::Ordering::Equal
684 }
685}
686
687impl RegionMap {
688 fn encode_raw_delta_values(&self, raw_deltas: Vec<i32>) -> Vec<u8> {
696 fn encode_words<'a>(
698 long: &'a [i32],
699 short: &'a [i32],
700 long_words: bool,
701 ) -> impl Iterator<Item = u8> + 'a {
702 let left = long_words.then(|| {
705 long.iter()
706 .flat_map(|x| x.to_be_bytes().into_iter())
707 .chain(short.iter().flat_map(|x| (*x as i16).to_be_bytes()))
708 });
709 let right = (!long_words).then(|| {
710 long.iter()
711 .flat_map(|x| (*x as i16).to_be_bytes().into_iter())
712 .chain(short.iter().flat_map(|x| (*x as i8).to_be_bytes()))
713 });
714
715 left.into_iter()
717 .flatten()
718 .chain(right.into_iter().flatten())
719 }
720
721 if self.n_active_regions == 0 {
722 return Default::default();
723 }
724
725 raw_deltas
726 .chunks(self.n_active_regions as usize)
727 .flat_map(|delta_set| {
728 let (long, short) = delta_set.split_at(self.n_long_regions as usize);
729 encode_words(long, short, self.long_words)
730 })
731 .collect()
732 }
733
734 fn word_delta_count(&self) -> u16 {
739 let long_flag = if self.long_words { 0x8000 } else { 0 };
740 self.n_long_regions | long_flag
741 }
742
743 fn column_index_for_region(&self, region: u16) -> Option<u16> {
746 let (column, bits) = self.regions_to_columns[region as usize];
747 (bits != ColumnBits::None).then_some(column)
748 }
749
750 fn indices(&self) -> Vec<u16> {
752 let mut result: Vec<_> = self
753 .regions_to_columns
754 .iter()
755 .enumerate()
756 .filter_map(|(i, (_, bits))| (*bits as u8 > 0).then_some(i as _))
757 .collect();
758 result.sort_unstable_by_key(|region_idx| {
760 self.regions_to_columns
761 .get(*region_idx as usize)
762 .map(|(column, _)| *column)
763 .unwrap_or(u16::MAX)
767 });
768 result
769 }
770}
771
772impl VariationIndexRemapping {
773 #[cfg(test)]
775 fn get_raw(&self, from: TemporaryDeltaSetId) -> Option<(u16, u16)> {
776 self.map
777 .get(&from)
778 .map(|var| (var.delta_set_outer_index, var.delta_set_inner_index))
779 }
780}
781
782impl DeltaSetStorage {
783 fn add(&mut self, delta_set: DeltaSet) -> TemporaryDeltaSetId {
784 match self {
785 DeltaSetStorage::Direct(deltas) => {
786 let next_id = deltas.len() as u32;
787 deltas.push(delta_set);
788 next_id
789 }
790 DeltaSetStorage::Deduplicated(deltas) => {
791 let next_id = deltas.len() as u32;
792 *deltas.entry(delta_set).or_insert(next_id)
793 }
794 }
795 }
796
797 fn iter(&self) -> impl Iterator<Item = (&DeltaSet, TemporaryDeltaSetId)> + '_ {
798 let (a_vec, a_map) = match self {
801 DeltaSetStorage::Direct(deltas) => (Some(deltas), None),
802 DeltaSetStorage::Deduplicated(deltas) => (None, Some(deltas)),
803 };
804 a_vec
805 .into_iter()
806 .flat_map(|x| x.iter().enumerate().map(|(i, val)| (val, i as u32)))
807 .chain(
808 a_map
809 .into_iter()
810 .flat_map(|map| map.iter().map(|(val, idx)| (val, *idx))),
811 )
812 }
813}
814
815impl PartialOrd for DeltaSet {
828 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
829 Some(self.cmp(other))
830 }
831}
832
833impl Ord for DeltaSet {
834 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
835 let max_region_idx = self
836 .0
837 .iter()
838 .chain(other.0.iter())
839 .map(|(idx, _)| *idx)
840 .max()
841 .unwrap_or(0);
842
843 let left = DenseDeltaIter::new(&self.0, max_region_idx);
844 let right = DenseDeltaIter::new(&other.0, max_region_idx);
845
846 for (l, r) in left.zip(right) {
847 match l.cmp(&r) {
848 std::cmp::Ordering::Equal => (),
849 non_eq => return non_eq,
850 }
851 }
852 std::cmp::Ordering::Equal
853 }
854}
855
856struct DenseDeltaIter<'a> {
861 total_len: u16,
862 cur_pos: u16,
863 deltas: &'a [(u16, i32)],
864}
865
866impl<'a> DenseDeltaIter<'a> {
867 fn new(deltas: &'a [(u16, i32)], max_idx: u16) -> Self {
868 DenseDeltaIter {
869 total_len: max_idx,
870 deltas,
871 cur_pos: 0,
872 }
873 }
874}
875
876impl Iterator for DenseDeltaIter<'_> {
877 type Item = i32;
878
879 fn next(&mut self) -> Option<Self::Item> {
880 if self.cur_pos > self.total_len {
881 return None;
882 }
883 let result = if self.deltas.first().map(|(idx, _)| *idx) == Some(self.cur_pos) {
884 let result = self.deltas.first().unwrap().1;
885 self.deltas = &self.deltas[1..];
886 result
887 } else {
888 0
889 };
890 self.cur_pos += 1;
891 Some(result)
892 }
893}
894
895impl Default for DeltaSetStorage {
896 fn default() -> Self {
897 Self::Deduplicated(Default::default())
898 }
899}
900
901impl Display for RowShape {
902 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
903 for col in &self.0 {
904 match col {
905 ColumnBits::None => write!(f, "-"),
906 ColumnBits::One => write!(f, "B"),
907 ColumnBits::Two => write!(f, "S"),
908 ColumnBits::Four => write!(f, "L"),
909 }?
910 }
911 Ok(())
912 }
913}
914
915impl Debug for Encoding<'_> {
916 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
917 write!(
918 f,
919 "Encoding({}, {} items {} bytes)",
920 self.shape,
921 self.deltas.len(),
922 self.cost()
923 )
924 }
925}
926
927impl Debug for Encoder<'_> {
928 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
929 f.debug_struct("Encoder")
930 .field("encodings", &self.encodings)
931 .finish_non_exhaustive()
932 }
933}
934
935#[cfg(test)]
936mod tests {
937 use crate::tables::variations::RegionAxisCoordinates;
938 use font_types::F2Dot14;
939 use read_fonts::{FontData, FontRead};
940
941 use super::*;
942
943 fn reg_coords(min: f32, default: f32, max: f32) -> RegionAxisCoordinates {
944 RegionAxisCoordinates {
945 start_coord: F2Dot14::from_f32(min),
946 peak_coord: F2Dot14::from_f32(default),
947 end_coord: F2Dot14::from_f32(max),
948 }
949 }
950
951 fn test_regions() -> [VariationRegion; 3] {
952 [
953 VariationRegion::new(vec![reg_coords(0.0, 0.2, 1.0), reg_coords(0.0, 0.0, 1.0)]),
954 VariationRegion::new(vec![reg_coords(0.0, 0.1, 0.3), reg_coords(0.0, 0.1, 0.3)]),
955 VariationRegion::new(vec![reg_coords(0.0, 0.1, 0.5), reg_coords(0.0, 0.1, 0.3)]),
956 ]
957 }
958
959 #[test]
960 #[allow(clippy::redundant_clone)]
961 fn smoke_test() {
962 let [r1, r2, r3] = test_regions();
963
964 let mut builder = VariationStoreBuilder::new(2);
965 builder.add_deltas(vec![(r1.clone(), 512), (r2, 266), (r3.clone(), 1115)]);
966 builder.add_deltas(vec![(r3.clone(), 20)]);
967 builder.add_deltas(vec![(r3.clone(), 21)]);
968 builder.add_deltas(vec![(r3, 22)]);
969
970 let (store, _) = builder.build();
972 assert_eq!(store.variation_region_list.variation_regions.len(), 3);
973 assert_eq!(store.item_variation_data.len(), 2);
974 assert_eq!(
975 store.item_variation_data[0]
976 .as_ref()
977 .unwrap()
978 .region_indexes,
979 vec![2]
980 );
981 assert_eq!(
982 store.item_variation_data[1]
983 .as_ref()
984 .unwrap()
985 .region_indexes,
986 vec![0, 1, 2]
987 );
988 }
989
990 #[test]
991 fn key_mapping() {
992 let [r1, r2, r3] = test_regions();
993
994 let mut builder = VariationStoreBuilder::new(2);
995 let k1 = builder.add_deltas(vec![(r1.clone(), 5), (r2, 1000), (r3.clone(), 1500)]);
996 let k2 = builder.add_deltas(vec![(r1.clone(), -3), (r3.clone(), 20)]);
997 let k3 = builder.add_deltas(vec![(r1.clone(), -12), (r3.clone(), 7)]);
998 let _ = builder.add_deltas(vec![(r1.clone(), -10), (r3.clone(), 7)]);
1000 let _ = builder.add_deltas(vec![(r1.clone(), -9), (r3.clone(), 7)]);
1001 let _ = builder.add_deltas(vec![(r1, -11), (r3, 7)]);
1002
1003 let (_, key_lookup) = builder.build();
1007
1008 assert_eq!(key_lookup.get_raw(k1).unwrap(), (1, 0),);
1011 assert_eq!(key_lookup.get_raw(k2).unwrap(), (0, 4),); assert_eq!(key_lookup.get_raw(k3).unwrap(), (0, 0),); assert_eq!(key_lookup.map.len(), 6);
1017 }
1018
1019 #[test]
1021 fn fontools_rowshape_repr() {
1022 use ColumnBits as C;
1023 let shape1 = RowShape(vec![C::None, C::One, C::One, C::Two]);
1024 assert_eq!(shape1.to_fonttools_repr(), 0b0011_0001_0001_0000);
1025 let shape2 = RowShape(vec![C::Two, C::One, C::One, C::None]);
1026 assert_eq!(shape2.to_fonttools_repr(), 0b0000_0001_0001_0011);
1027
1028 assert!(shape1.to_fonttools_repr() > shape2.to_fonttools_repr());
1029 }
1030
1031 #[test]
1032 fn encoding_sort_order() {
1033 let _ = env_logger::builder().is_test(true).try_init();
1034 let [r1, r2, r3] = test_regions();
1035
1036 let mut builder = VariationStoreBuilder::new(2);
1039 builder.add_deltas(vec![(r1.clone(), 1000), (r2.clone(), 5)]);
1041 builder.add_deltas(vec![(r1.clone(), 1013), (r2.clone(), 20)]);
1042 builder.add_deltas(vec![(r1.clone(), 1014), (r2.clone(), 21)]);
1043 builder.add_deltas(vec![(r2.clone(), 1212), (r3.clone(), 7)]);
1045 builder.add_deltas(vec![(r2.clone(), 1213), (r3.clone(), 8)]);
1046 builder.add_deltas(vec![(r2.clone(), 1214), (r3.clone(), 8)]);
1047
1048 builder.add_deltas(vec![(r1.clone(), 12), (r3.clone(), 7)]);
1050 builder.add_deltas(vec![(r1.clone(), 13), (r3.clone(), 9)]);
1051 builder.add_deltas(vec![(r1.clone(), 14), (r3.clone(), 10)]);
1052 builder.add_deltas(vec![(r1.clone(), 15), (r3.clone(), 11)]);
1053 builder.add_deltas(vec![(r1.clone(), 16), (r3.clone(), 12)]);
1054
1055 let (var_store, key_lookup) = builder.build();
1056 assert_eq!(var_store.item_variation_data.len(), 3);
1057 assert_eq!(key_lookup.map.len(), 11);
1058
1059 assert_eq!(
1061 var_store.item_variation_data[0]
1062 .as_ref()
1063 .unwrap()
1064 .region_indexes,
1065 vec![0, 2]
1066 );
1067
1068 assert_eq!(
1071 var_store.item_variation_data[1]
1072 .as_ref()
1073 .unwrap()
1074 .region_indexes,
1075 vec![0, 1]
1076 );
1077 }
1078
1079 #[test]
1080 #[allow(clippy::redundant_clone)]
1081 fn to_binary() {
1082 let [r1, r2, r3] = test_regions();
1083
1084 let mut builder = VariationStoreBuilder::new(2);
1085 builder.add_deltas(vec![(r1.clone(), 512), (r2, 1000), (r3.clone(), 265)]);
1086 builder.add_deltas(vec![(r1.clone(), -3), (r3.clone(), 20)]);
1087 builder.add_deltas(vec![(r1.clone(), -12), (r3.clone(), 7)]);
1088 builder.add_deltas(vec![(r1.clone(), -11), (r3.clone(), 8)]);
1089 builder.add_deltas(vec![(r1.clone(), -10), (r3.clone(), 9)]);
1090 let (table, _) = builder.build();
1091 let bytes = crate::dump_table(&table).unwrap();
1092 let data = FontData::new(&bytes);
1093
1094 let reloaded = read_fonts::tables::variations::ItemVariationStore::read(data).unwrap();
1095
1096 assert_eq!(reloaded.item_variation_data_count(), 2);
1097 let var_data_array = reloaded.item_variation_data();
1098
1099 let var_data = var_data_array.get(0).unwrap().unwrap();
1100 assert_eq!(var_data.region_indexes(), &[0, 2]);
1101 assert_eq!(var_data.item_count(), 4);
1102 assert_eq!(var_data.delta_set(0).collect::<Vec<_>>(), vec![-12, 7]);
1103 assert_eq!(var_data.delta_set(1).collect::<Vec<_>>(), vec![-11, 8]);
1104 assert_eq!(var_data.delta_set(2).collect::<Vec<_>>(), vec![-10, 9]);
1105 assert_eq!(var_data.delta_set(3).collect::<Vec<_>>(), vec![-3, 20]);
1106
1107 let var_data = var_data_array.get(1).unwrap().unwrap();
1108 assert_eq!(var_data.region_indexes(), &[0, 1, 2]);
1109 assert_eq!(var_data.item_count(), 1);
1110 assert_eq!(
1111 var_data.delta_set(0).collect::<Vec<_>>(),
1112 vec![512, 1000, 265]
1113 );
1114 }
1115
1116 #[test]
1117 fn reuse_identical_variation_data() {
1118 let _ = env_logger::builder().is_test(true).try_init();
1119 let [r1, r2, r3] = test_regions();
1120
1121 let mut builder = VariationStoreBuilder::new(2);
1122 let k1 = builder.add_deltas(vec![(r1.clone(), 5), (r2, 10), (r3.clone(), 15)]);
1123 let k2 = builder.add_deltas(vec![(r1.clone(), -12), (r3.clone(), 7)]);
1124 let k3 = builder.add_deltas(vec![(r1.clone(), -12), (r3.clone(), 7)]);
1125 let k4 = builder.add_deltas(vec![(r1, 322), (r3, 532)]);
1126
1127 let (_, key_lookup) = builder.build();
1129 assert_eq!(k2, k3);
1130 assert_ne!(k1, k2);
1131 assert_ne!(k1, k4);
1132 assert_eq!(key_lookup.map.len(), 3);
1133 }
1134
1135 #[test]
1139 #[allow(clippy::redundant_clone)]
1140 fn long_deltas_split() {
1141 let [r1, r2, _] = test_regions();
1142 let mut builder = VariationStoreBuilder::new(2);
1143 builder.add_deltas(vec![(r1.clone(), 1), (r2.clone(), 2)]);
1145 builder.add_deltas(vec![(r1.clone(), 3), (r2.clone(), 4)]);
1146 builder.add_deltas(vec![(r1.clone(), 5), (r2.clone(), 6)]);
1147 builder.add_deltas(vec![(r1.clone(), 0xffff + 1), (r2.clone(), 0xffff + 2)]);
1149 let mut encoder = builder.encoder();
1150 assert_eq!(encoder.encodings.len(), 2);
1151 encoder.optimize();
1152 assert_eq!(encoder.encodings.len(), 2);
1153 }
1154
1155 #[test]
1157 #[allow(clippy::redundant_clone)]
1158 fn long_deltas_combine() {
1159 let [r1, r2, _] = test_regions();
1160 let mut builder = VariationStoreBuilder::new(2);
1161 builder.add_deltas(vec![(r1.clone(), 1), (r2.clone(), 2)]);
1163 builder.add_deltas(vec![(r1.clone(), 3), (r2.clone(), 4)]);
1164 builder.add_deltas(vec![(r1.clone(), 0xffff + 1), (r2.clone(), 0xffff + 2)]);
1166
1167 let mut encoder = builder.encoder();
1168 assert_eq!(encoder.encodings.len(), 2);
1169 assert_eq!(encoder.encodings[0].shape.overhead(), 14); assert_eq!(encoder.encodings[0].cost(), 14 + 4); assert_eq!(encoder.encodings[1].shape.overhead(), 14);
1172 assert_eq!(encoder.encodings[1].cost(), 14 + 8); encoder.optimize();
1174 assert_eq!(encoder.encodings.len(), 1);
1175 }
1176
1177 #[test]
1179 #[allow(clippy::redundant_clone)]
1180 fn combine_many_shapes() {
1181 let _ = env_logger::builder().is_test(true).try_init();
1182 let [r1, r2, r3] = test_regions();
1183 let mut builder = VariationStoreBuilder::new(2);
1184 builder.add_deltas(vec![(r1.clone(), 0xffff + 5)]); builder.add_deltas(vec![(r1.clone(), 2)]); builder.add_deltas(vec![(r1.clone(), 300)]); builder.add_deltas(vec![(r2.clone(), 0xffff + 5)]); builder.add_deltas(vec![(r2.clone(), 2)]); builder.add_deltas(vec![(r2.clone(), 300)]); builder.add_deltas(vec![(r3.clone(), 0xffff + 5)]); builder.add_deltas(vec![(r3.clone(), 2)]); builder.add_deltas(vec![(r3.clone(), 300)]); let mut encoder = builder.encoder();
1196 encoder.optimize();
1197 assert_eq!(encoder.encodings.len(), 3);
1199 assert!(encoder.encodings[0]
1200 .compute_gain(&encoder.encodings[1])
1201 .is_negative());
1202 }
1203
1204 #[test]
1205 #[allow(clippy::redundant_clone)]
1206 fn combine_two_big_fellas() {
1207 let _ = env_logger::builder().is_test(true).try_init();
1208 let [r1, r2, r3] = test_regions();
1209 let mut builder = VariationStoreBuilder::new(2);
1210 builder.add_deltas(vec![(r1.clone(), 0xffff + 5)]); builder.add_deltas(vec![(r2.clone(), 0xffff + 5)]); builder.add_deltas(vec![(r3.clone(), 0xffff + 5)]); let mut encoder = builder.encoder();
1217 assert_eq!(encoder.encodings[0].cost(), 16);
1218 let merge_cost = 2 + 4 + 8; assert_eq!(
1222 encoder.encodings[0].compute_gain(&encoder.encodings[1]),
1223 16 - merge_cost
1224 );
1225 encoder.optimize();
1226
1227 let next_merge_cost = 2
1229 + 2 * 4 + 12; assert_eq!(encoder.encodings.len(), 2);
1232 assert_eq!(encoder.encodings[0].cost(), 16);
1233 assert_eq!(
1234 encoder.encodings[0].compute_gain(&encoder.encodings[1]),
1235 16 - next_merge_cost
1236 );
1237 }
1238
1239 #[test]
1242 fn ensure_zero_deltas_dont_write() {
1243 let _ = env_logger::builder().is_test(true).try_init();
1244 let [r1, r2, _] = test_regions();
1245 let mut builder = VariationStoreBuilder::new(2);
1246 builder.add_deltas(vec![(r1.clone(), 0), (r2.clone(), 4)]);
1247 let _ = builder.build();
1248 }
1249
1250 #[test]
1253 fn ensure_all_zeros_dont_write() {
1254 let _ = env_logger::builder().is_test(true).try_init();
1255 let [r1, r2, _] = test_regions();
1256 let mut builder = VariationStoreBuilder::new(2);
1257 builder.add_deltas(vec![(r1.clone(), 0), (r2.clone(), 0)]);
1258 let _ = builder.build();
1259 }
1260
1261 #[test]
1262 fn vardata_region_indices_order() {
1263 let r0 = VariationRegion::new(vec![reg_coords(0.0, 0.5, 1.0)]);
1264 let r1 = VariationRegion::new(vec![reg_coords(0.5, 1.0, 1.0)]);
1265
1266 let mut builder = VariationStoreBuilder::new(1);
1267 builder.add_deltas(vec![(r0.clone(), 1), (r1.clone(), 2)]);
1268 builder.add_deltas(vec![(r0.clone(), 3), (r1.clone(), 256)]);
1271
1272 let (store, _varidx_map) = builder.build();
1273
1274 assert_eq!(store.variation_region_list.variation_regions.len(), 2);
1275 assert_eq!(store.item_variation_data.len(), 1);
1276
1277 let var_data = store.item_variation_data[0].as_ref().unwrap();
1278
1279 assert_eq!(var_data.item_count, 2);
1280 assert_eq!(var_data.word_delta_count, 1);
1281 assert_eq!(var_data.region_indexes, vec![1, 0]);
1287 assert_eq!(
1288 var_data.delta_sets,
1289 vec![
1294 0, 2, 1, 1, 0, 3, ],
1301 );
1302 }
1303
1304 #[test]
1305 fn unoptimized_version() {
1306 let r0 = VariationRegion::new(vec![reg_coords(0.0, 0.5, 1.0)]);
1307 let r1 = VariationRegion::new(vec![reg_coords(0.5, 1.0, 1.0)]);
1308
1309 let mut builder = VariationStoreBuilder::new_with_implicit_indices(1);
1310 builder.add_deltas(vec![(r0.clone(), 1), (r1.clone(), 2)]);
1311 builder.add_deltas(vec![(r0.clone(), 1), (r1.clone(), 2)]);
1314 builder.add_deltas(vec![(r0.clone(), 3), (r1.clone(), 256)]);
1315 builder.add_deltas(vec![(r0.clone(), 3), (r1.clone(), 256)]);
1316
1317 let (ivs, key_map) = builder.build();
1318 assert_eq!(ivs.item_variation_data.len(), 1);
1320
1321 let var_data = ivs.item_variation_data[0].as_ref().unwrap();
1322 assert_eq!(var_data.item_count, 4);
1323 assert_eq!(var_data.region_indexes, vec![1, 0]);
1324
1325 assert_eq!(
1326 var_data.delta_sets,
1327 &[
1328 0x0, 0x2, 0x1, 0x0, 0x2, 0x1, 0x1, 0x0, 0x3, 0x1, 0x0, 0x3, ]
1337 );
1338
1339 assert_eq!(key_map.map.len(), 4);
1341 assert!(key_map
1342 .map
1343 .iter()
1344 .all(|(key, idx)| *key == idx.delta_set_inner_index as u32))
1345 }
1346
1347 #[test]
1348 fn delta_set_ordering() {
1349 let left = DeltaSet(vec![(0, 1), (1, 2), (2, -11)]);
1350 let right = DeltaSet(vec![(0, 1), (1, 2)]);
1351
1352 assert!(left.0 > right.0);
1355 assert!(left < right);
1357
1358 let left = DeltaSet(vec![(0, 1), (1, 2), (2, 11)]);
1360 let right = DeltaSet(vec![(0, 1), (1, 2)]);
1361
1362 assert!(left > right);
1363 let left = DeltaSet(vec![(0, 1), (1, 2), (2, -11)]);
1364 let right = DeltaSet(vec![(0, 1), (1, 2), (3, 0)]);
1365
1366 assert!(left < right);
1367
1368 let left = DeltaSet(vec![(0, 1), (1, -2), (2, -11)]);
1370 let right = DeltaSet(vec![(0, 1), (2, -11)]);
1371 assert!(left < right)
1372 }
1373
1374 #[test]
1375 fn no_duplicate_zero_delta_sets() {
1376 let r0 = VariationRegion::new(vec![reg_coords(0.0, 5.0, 1.0)]);
1377 let r1 = VariationRegion::new(vec![reg_coords(0.5, 1.0, 1.0)]);
1378 let mut builder = VariationStoreBuilder::new(1);
1379 let varidxes = vec![
1380 builder.add_deltas::<i32>(Vec::new()),
1384 builder.add_deltas(vec![(r0.clone(), 50), (r1.clone(), 100)]),
1385 builder.add_deltas(vec![(r0.clone(), 0), (r1.clone(), 0)]),
1388 builder.add_deltas(vec![(r0.clone(), 50), (r1.clone(), 100)]),
1391 builder.add_deltas(vec![(r0.clone(), 0), (r1.clone(), 100)]),
1394 ];
1395 let (store, key_map) = builder.build();
1396
1397 let varidx_map: Vec<u32> = varidxes
1398 .into_iter()
1399 .map(|idx| key_map.get(idx).unwrap().into())
1400 .collect::<Vec<_>>();
1401
1402 assert_eq!(store.variation_region_list.variation_regions.len(), 2);
1403 assert_eq!(store.item_variation_data.len(), 1);
1404
1405 let var_data = store.item_variation_data[0].as_ref().unwrap();
1406
1407 assert_eq!(var_data.item_count, 3);
1408 assert_eq!(var_data.word_delta_count, 0);
1409 assert_eq!(var_data.region_indexes, vec![0, 1]);
1410 assert_eq!(var_data.delta_sets, vec![0, 0, 0, 100, 50, 100],);
1411 assert_eq!(varidx_map, vec![0, 2, 0, 2, 1]);
1415 }
1416
1417 #[test]
1418 fn prune_unused_regions() {
1419 let r0 = VariationRegion::new(vec![reg_coords(-1.0, -0.5, 0.0)]);
1421 let r1 = VariationRegion::new(vec![reg_coords(-1.0, -1.0, 0.0)]);
1422 let r2 = VariationRegion::new(vec![reg_coords(0.0, 0.5, 1.0)]);
1423 let r3 = VariationRegion::new(vec![reg_coords(0.0, 1.0, 1.0)]);
1424 let mut builder = VariationStoreBuilder::new(1);
1425 builder.add_deltas(vec![
1426 (r0.clone(), 0),
1427 (r1.clone(), 50),
1428 (r2.clone(), 0),
1429 (r3.clone(), 100),
1430 ]);
1431 let (store, _) = builder.build();
1432
1433 assert_eq!(store.variation_region_list.variation_regions.len(), 2);
1435 assert_eq!(store.item_variation_data.len(), 1);
1436
1437 let var_data = store.item_variation_data[0].as_ref().unwrap();
1438 assert_eq!(var_data.item_count, 1);
1439 assert_eq!(var_data.word_delta_count, 0);
1440 assert_eq!(var_data.region_indexes, vec![0, 1]); assert_eq!(var_data.delta_sets, vec![50, 100]);
1442 }
1443
1444 #[test]
1445 fn we_match_fonttools_stable_order() {
1446 use rand::{seq::SliceRandom, thread_rng};
1447
1448 let mut builder = VariationStoreBuilder::new(1);
1449 let r1 = VariationRegion::new(vec![reg_coords(-1.0, -1.0, 0.0)]);
1450 let r2 = VariationRegion::new(vec![reg_coords(0.0, 1.0, 1.0)]);
1451
1452 let mut delta_sets = vec![
1453 vec![(r1.clone(), 1), (r2.clone(), 2)],
1455 vec![(r1.clone(), 3), (r2.clone(), 4)],
1456 vec![(r1.clone(), 5), (r2.clone(), 6)],
1457 vec![(r1.clone(), 7), (r2.clone(), 8)],
1458 vec![(r1.clone(), 9), (r2.clone(), 10)],
1459 vec![(r1.clone(), 11), (r2.clone(), 12)],
1460 vec![(r1.clone(), 13), (r2.clone(), 14)],
1461 vec![(r1.clone(), 0), (r2.clone(), -130)],
1463 vec![(r1.clone(), 0), (r2.clone(), -129)],
1464 vec![(r1.clone(), 0), (r2.clone(), 128)],
1465 vec![(r1.clone(), 0), (r2.clone(), 129)],
1466 vec![(r1.clone(), 0), (r2.clone(), -1)],
1475 ];
1476
1477 let mut rng = thread_rng();
1479 delta_sets.shuffle(&mut rng);
1480 for deltas in delta_sets {
1481 builder.add_deltas(deltas);
1482 }
1483
1484 let (store, _) = builder.build();
1485
1486 let bytes = crate::dump_table(&store).unwrap();
1487 let data = FontData::new(&bytes);
1488 let reloaded = read_fonts::tables::variations::ItemVariationStore::read(data).unwrap();
1489
1490 assert_eq!(reloaded.item_variation_data_count(), 2);
1491 let var_data_array = reloaded.item_variation_data();
1492
1493 let var_data = var_data_array.get(0).unwrap().unwrap();
1494 assert_eq!(var_data.region_indexes(), &[0, 1]);
1495 assert_eq!(var_data.item_count(), 8);
1497 assert_eq!(var_data.delta_set(0).collect::<Vec<_>>(), vec![0, -1]);
1498 assert_eq!(var_data.delta_set(1).collect::<Vec<_>>(), vec![1, 2]);
1499 assert_eq!(var_data.delta_set(2).collect::<Vec<_>>(), vec![3, 4]);
1500 assert_eq!(var_data.delta_set(3).collect::<Vec<_>>(), vec![5, 6]);
1501 assert_eq!(var_data.delta_set(4).collect::<Vec<_>>(), vec![7, 8]);
1502 assert_eq!(var_data.delta_set(5).collect::<Vec<_>>(), vec![9, 10]);
1503 assert_eq!(var_data.delta_set(6).collect::<Vec<_>>(), vec![11, 12]);
1504 assert_eq!(var_data.delta_set(7).collect::<Vec<_>>(), vec![13, 14]);
1505
1506 let var_data = var_data_array.get(1).unwrap().unwrap();
1507 assert_eq!(var_data.region_indexes(), &[1]);
1508 assert_eq!(var_data.item_count(), 4);
1509 assert_eq!(var_data.delta_set(0).collect::<Vec<_>>(), vec![-130]);
1511 assert_eq!(var_data.delta_set(1).collect::<Vec<_>>(), vec![-129]);
1512 assert_eq!(var_data.delta_set(2).collect::<Vec<_>>(), vec![128]);
1513 assert_eq!(var_data.delta_set(3).collect::<Vec<_>>(), vec![129]);
1514 }
1515}