read_fonts/tables/
variations.rs

1//! OpenType font variations common tables.
2
3include!("../../generated/generated_variations.rs");
4
5use super::{
6    glyf::{PointCoord, PointFlags, PointMarker},
7    gvar::GlyphDelta,
8};
9
10pub const NO_VARIATION_INDEX: u32 = 0xFFFFFFFF;
11/// Outer and inner indices for reading from an [ItemVariationStore].
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct DeltaSetIndex {
14    /// Outer delta set index.
15    pub outer: u16,
16    /// Inner delta set index.
17    pub inner: u16,
18}
19
20impl DeltaSetIndex {
21    pub const NO_VARIATION_INDEX: Self = Self {
22        outer: (NO_VARIATION_INDEX >> 16) as u16,
23        inner: (NO_VARIATION_INDEX & 0xFFFF) as u16,
24    };
25}
26
27#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub struct TupleIndex(u16);
30
31impl TupleIndex {
32    /// Flag indicating that this tuple variation header includes an embedded
33    /// peak tuple record, immediately after the tupleIndex field.
34    ///
35    /// If set, the low 12 bits of the tupleIndex value are ignored.
36    ///
37    /// Note that this must always be set within the 'cvar' table.
38    pub const EMBEDDED_PEAK_TUPLE: u16 = 0x8000;
39
40    /// Flag indicating that this tuple variation table applies to an
41    /// intermediate region within the variation space.
42    ///
43    /// If set, the header includes the two intermediate-region, start and end
44    /// tuple records, immediately after the peak tuple record (if present).
45    pub const INTERMEDIATE_REGION: u16 = 0x4000;
46    /// Flag indicating that the serialized data for this tuple variation table
47    /// includes packed “point” number data.
48    ///
49    /// If set, this tuple variation table uses that number data; if clear,
50    /// this tuple variation table uses shared number data found at the start
51    /// of the serialized data for this glyph variation data or 'cvar' table.
52    pub const PRIVATE_POINT_NUMBERS: u16 = 0x2000;
53    //0x1000	Reserved	Reserved for future use — set to 0.
54    //
55    /// Mask for the low 12 bits to give the shared tuple records index.
56    pub const TUPLE_INDEX_MASK: u16 = 0x0FFF;
57
58    #[inline(always)]
59    fn tuple_len(self, axis_count: u16, flag: usize) -> usize {
60        if flag == 0 {
61            self.embedded_peak_tuple() as usize * axis_count as usize
62        } else {
63            self.intermediate_region() as usize * axis_count as usize
64        }
65    }
66
67    pub fn bits(self) -> u16 {
68        self.0
69    }
70
71    pub fn from_bits(bits: u16) -> Self {
72        TupleIndex(bits)
73    }
74
75    /// `true` if the header includes an embedded peak tuple.
76    pub fn embedded_peak_tuple(self) -> bool {
77        (self.0 & Self::EMBEDDED_PEAK_TUPLE) != 0
78    }
79
80    /// `true` if the header includes the two intermediate region tuple records.
81    pub fn intermediate_region(self) -> bool {
82        (self.0 & Self::INTERMEDIATE_REGION) != 0
83    }
84
85    /// `true` if the data for this table includes packed point number data.
86    pub fn private_point_numbers(self) -> bool {
87        (self.0 & Self::PRIVATE_POINT_NUMBERS) != 0
88    }
89
90    pub fn tuple_records_index(self) -> Option<u16> {
91        (!self.embedded_peak_tuple()).then_some(self.0 & Self::TUPLE_INDEX_MASK)
92    }
93}
94
95impl types::Scalar for TupleIndex {
96    type Raw = <u16 as types::Scalar>::Raw;
97    fn to_raw(self) -> Self::Raw {
98        self.0.to_raw()
99    }
100    fn from_raw(raw: Self::Raw) -> Self {
101        let t = <u16>::from_raw(raw);
102        Self(t)
103    }
104}
105
106/// The 'tupleVariationCount' field of the [Tuple Variation Store Header][header]
107///
108/// The high 4 bits are flags, and the low 12 bits are the number of tuple
109/// variation tables for this glyph. The count can be any number between 1 and 4095.
110///
111/// [header]: https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuple-variation-store-header
112#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114pub struct TupleVariationCount(u16);
115
116impl TupleVariationCount {
117    /// Flag indicating that some or all tuple variation tables reference a
118    /// shared set of “point” numbers.
119    ///
120    /// These shared numbers are represented as packed point number data at the
121    /// start of the serialized data.
122    pub const SHARED_POINT_NUMBERS: u16 = 0x8000;
123
124    /// Mask for the low 12 bits to give the shared tuple records index.
125    pub const COUNT_MASK: u16 = 0x0FFF;
126
127    pub fn bits(self) -> u16 {
128        self.0
129    }
130
131    pub fn from_bits(bits: u16) -> Self {
132        Self(bits)
133    }
134
135    /// `true` if any tables reference a shared set of point numbers
136    pub fn shared_point_numbers(self) -> bool {
137        (self.0 & Self::SHARED_POINT_NUMBERS) != 0
138    }
139
140    pub fn count(self) -> u16 {
141        self.0 & Self::COUNT_MASK
142    }
143}
144
145impl types::Scalar for TupleVariationCount {
146    type Raw = <u16 as types::Scalar>::Raw;
147    fn to_raw(self) -> Self::Raw {
148        self.0.to_raw()
149    }
150    fn from_raw(raw: Self::Raw) -> Self {
151        let t = <u16>::from_raw(raw);
152        Self(t)
153    }
154}
155
156impl<'a> TupleVariationHeader<'a> {
157    #[cfg(feature = "experimental_traverse")]
158    fn traverse_tuple_index(&self) -> traversal::FieldType<'a> {
159        self.tuple_index().0.into()
160    }
161
162    /// Peak tuple record for this tuple variation table — optional,
163    /// determined by flags in the tupleIndex value.  Note that this
164    /// must always be included in the 'cvar' table.
165    #[inline(always)]
166    pub fn peak_tuple(&self) -> Option<Tuple<'a>> {
167        self.tuple_index().embedded_peak_tuple().then(|| {
168            let range = self.shape.peak_tuple_byte_range();
169            Tuple {
170                values: self.data.read_array(range).unwrap(),
171            }
172        })
173    }
174
175    /// Intermediate start tuple record for this tuple variation table
176    /// — optional, determined by flags in the tupleIndex value.
177    #[inline(always)]
178    pub fn intermediate_start_tuple(&self) -> Option<Tuple<'a>> {
179        self.tuple_index().intermediate_region().then(|| {
180            let range = self.shape.intermediate_start_tuple_byte_range();
181            Tuple {
182                values: self.data.read_array(range).unwrap(),
183            }
184        })
185    }
186
187    /// Intermediate end tuple record for this tuple variation table
188    /// — optional, determined by flags in the tupleIndex value.
189    #[inline(always)]
190    pub fn intermediate_end_tuple(&self) -> Option<Tuple<'a>> {
191        self.tuple_index().intermediate_region().then(|| {
192            let range = self.shape.intermediate_end_tuple_byte_range();
193            Tuple {
194                values: self.data.read_array(range).unwrap(),
195            }
196        })
197    }
198
199    /// Intermediate tuple records for this tuple variation table
200    /// — optional, determined by flags in the tupleIndex value.
201    #[inline(always)]
202    pub fn intermediate_tuples(&self) -> Option<(Tuple<'a>, Tuple<'a>)> {
203        self.tuple_index().intermediate_region().then(|| {
204            let start_range = self.shape.intermediate_start_tuple_byte_range();
205            let end_range = self.shape.intermediate_end_tuple_byte_range();
206            (
207                Tuple {
208                    values: self.data.read_array(start_range).unwrap(),
209                },
210                Tuple {
211                    values: self.data.read_array(end_range).unwrap(),
212                },
213            )
214        })
215    }
216
217    /// Compute the actual length of this table in bytes
218    #[inline(always)]
219    fn byte_len(&self, axis_count: u16) -> usize {
220        const FIXED_LEN: usize = u16::RAW_BYTE_LEN + TupleIndex::RAW_BYTE_LEN;
221        let tuple_byte_len = F2Dot14::RAW_BYTE_LEN * axis_count as usize;
222        let index = self.tuple_index();
223        FIXED_LEN
224            + index
225                .embedded_peak_tuple()
226                .then_some(tuple_byte_len)
227                .unwrap_or_default()
228            + index
229                .intermediate_region()
230                .then_some(tuple_byte_len * 2)
231                .unwrap_or_default()
232    }
233}
234
235impl Tuple<'_> {
236    pub fn len(&self) -> usize {
237        self.values().len()
238    }
239
240    pub fn is_empty(&self) -> bool {
241        self.values.is_empty()
242    }
243
244    #[inline(always)]
245    pub fn get(&self, idx: usize) -> Option<F2Dot14> {
246        self.values.get(idx).map(BigEndian::get)
247    }
248}
249
250//FIXME: add an #[extra_traits(..)] attribute!
251#[allow(clippy::derivable_impls)]
252impl Default for Tuple<'_> {
253    fn default() -> Self {
254        Self {
255            values: Default::default(),
256        }
257    }
258}
259
260/// [Packed "Point" Numbers](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-point-numbers)
261#[derive(Clone, Default, Debug)]
262pub struct PackedPointNumbers<'a> {
263    data: FontData<'a>,
264}
265
266impl<'a> PackedPointNumbers<'a> {
267    /// read point numbers off the front of this data, returning the remaining data
268    pub fn split_off_front(data: FontData<'a>) -> (Self, FontData<'a>) {
269        let this = PackedPointNumbers { data };
270        let total_len = this.total_len();
271        let remainder = data.split_off(total_len).unwrap_or_default();
272        (this, remainder)
273    }
274
275    /// The number of points in this set
276    pub fn count(&self) -> u16 {
277        self.count_and_count_bytes().0
278    }
279
280    /// compute the count, and the number of bytes used to store it
281    fn count_and_count_bytes(&self) -> (u16, usize) {
282        match self.data.read_at::<u8>(0).unwrap_or(0) {
283            0 => (0, 1),
284            count @ 1..=127 => (count as u16, 1),
285            _ => {
286                // "If the high bit of the first byte is set, then a second byte is used.
287                // The count is read from interpreting the two bytes as a big-endian
288                // uint16 value with the high-order bit masked out."
289
290                let count = self.data.read_at::<u16>(0).unwrap_or_default() & 0x7FFF;
291                // a weird case where I'm following fonttools: if the 'use words' bit
292                // is set, but the total count is still 0, treat it like 0 first byte
293                if count == 0 {
294                    (0, 2)
295                } else {
296                    (count & 0x7FFF, 2)
297                }
298            }
299        }
300    }
301
302    /// the number of bytes to encode the packed point numbers
303    #[inline(never)]
304    fn total_len(&self) -> usize {
305        let (n_points, mut n_bytes) = self.count_and_count_bytes();
306        if n_points == 0 {
307            return n_bytes;
308        }
309        let mut cursor = self.data.cursor();
310        cursor.advance_by(n_bytes);
311
312        let mut n_seen = 0;
313        while n_seen < n_points {
314            let Some((count, two_bytes)) = read_control_byte(&mut cursor) else {
315                return n_bytes;
316            };
317            let word_size = 1 + usize::from(two_bytes);
318            let run_size = word_size * count as usize;
319            n_bytes += run_size + 1; // plus the control byte;
320            cursor.advance_by(run_size);
321            n_seen += count as u16;
322        }
323
324        n_bytes
325    }
326
327    /// Iterate over the packed points
328    pub fn iter(&self) -> PackedPointNumbersIter<'a> {
329        let (count, n_bytes) = self.count_and_count_bytes();
330        let mut cursor = self.data.cursor();
331        cursor.advance_by(n_bytes);
332        PackedPointNumbersIter::new(count, cursor)
333    }
334}
335
336/// An iterator over the packed point numbers data.
337#[derive(Clone, Debug)]
338pub struct PackedPointNumbersIter<'a> {
339    count: u16,
340    seen: u16,
341    last_val: u16,
342    current_run: PointRunIter<'a>,
343}
344
345impl<'a> PackedPointNumbersIter<'a> {
346    fn new(count: u16, cursor: Cursor<'a>) -> Self {
347        PackedPointNumbersIter {
348            count,
349            seen: 0,
350            last_val: 0,
351            current_run: PointRunIter {
352                remaining: 0,
353                two_bytes: false,
354                cursor,
355            },
356        }
357    }
358}
359
360/// Implements the logic for iterating over the individual runs
361#[derive(Clone, Debug)]
362struct PointRunIter<'a> {
363    remaining: u8,
364    two_bytes: bool,
365    cursor: Cursor<'a>,
366}
367
368impl Iterator for PointRunIter<'_> {
369    type Item = u16;
370
371    fn next(&mut self) -> Option<Self::Item> {
372        // if no items remain in this run, start the next one.
373        while self.remaining == 0 {
374            (self.remaining, self.two_bytes) = read_control_byte(&mut self.cursor)?;
375        }
376
377        self.remaining -= 1;
378        if self.two_bytes {
379            self.cursor.read().ok()
380        } else {
381            self.cursor.read::<u8>().ok().map(|v| v as u16)
382        }
383    }
384}
385
386/// returns the count and the 'uses_two_bytes' flag from the control byte
387fn read_control_byte(cursor: &mut Cursor) -> Option<(u8, bool)> {
388    let control: u8 = cursor.read().ok()?;
389    let two_bytes = (control & 0x80) != 0;
390    let count = (control & 0x7F) + 1;
391    Some((count, two_bytes))
392}
393
394impl Iterator for PackedPointNumbersIter<'_> {
395    type Item = u16;
396
397    fn next(&mut self) -> Option<Self::Item> {
398        // if our count is zero, we keep incrementing forever
399        if self.count == 0 {
400            let result = self.last_val;
401            self.last_val = self.last_val.checked_add(1)?;
402            return Some(result);
403        }
404
405        if self.count == self.seen {
406            return None;
407        }
408        self.seen += 1;
409        self.last_val = self.last_val.checked_add(self.current_run.next()?)?;
410        Some(self.last_val)
411    }
412
413    fn size_hint(&self) -> (usize, Option<usize>) {
414        (self.count as usize, Some(self.count as usize))
415    }
416}
417
418// completely unnecessary?
419impl ExactSizeIterator for PackedPointNumbersIter<'_> {}
420
421/// [Packed Deltas](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas)
422#[derive(Clone, Debug)]
423pub struct PackedDeltas<'a> {
424    data: FontData<'a>,
425    // How many values we expect
426    count: usize,
427}
428
429impl<'a> PackedDeltas<'a> {
430    pub(crate) fn new(data: FontData<'a>, count: usize) -> Self {
431        Self { data, count }
432    }
433
434    /// NOTE: this is unbounded, and assumes all of data is deltas.
435    #[doc(hidden)] // used by tests in write-fonts
436    pub fn consume_all(data: FontData<'a>) -> Self {
437        let count = count_all_deltas(data);
438        Self { data, count }
439    }
440
441    pub(crate) fn count(&self) -> usize {
442        self.count
443    }
444
445    pub fn iter(&self) -> DeltaRunIter<'a> {
446        DeltaRunIter::new(self.data.cursor(), Some(self.count))
447    }
448
449    fn x_deltas(&self) -> DeltaRunIter<'a> {
450        DeltaRunIter::new(self.data.cursor(), Some(self.count / 2))
451    }
452
453    fn y_deltas(&self) -> DeltaRunIter<'a> {
454        DeltaRunIter::new(self.data.cursor(), Some(self.count)).skip_fast(self.count / 2)
455    }
456}
457
458/// Flag indicating that this run contains no data,
459/// and that the deltas for this run are all zero.
460const DELTAS_ARE_ZERO: u8 = 0x80;
461/// Flag indicating the data type for delta values in the run.
462const DELTAS_ARE_WORDS: u8 = 0x40;
463/// Mask for the low 6 bits to provide the number of delta values in the run, minus one.
464const DELTA_RUN_COUNT_MASK: u8 = 0x3F;
465
466/// The type of values for a given delta run (influences the number of bytes per delta)
467///
468/// The variants are intentionally set to the byte size of the type to allow usage
469/// as a multiplier when computing offsets.
470#[derive(Clone, Copy, Debug, PartialEq)]
471pub enum DeltaRunType {
472    Zero = 0,
473    I8 = 1,
474    I16 = 2,
475    I32 = 4,
476}
477
478impl DeltaRunType {
479    /// The run type for a given control byte
480    pub fn new(control: u8) -> Self {
481        // if the top two bits of the control byte (DELTAS_ARE_ZERO and DELTAS_ARE_WORDS) are both set,
482        // then the following values are 32-bit.
483        // <https://github.com/harfbuzz/boring-expansion-spec/blob/main/VARC.md#tuplevalues>
484        let are_zero = (control & DELTAS_ARE_ZERO) != 0;
485        let are_words = (control & DELTAS_ARE_WORDS) != 0;
486        match (are_zero, are_words) {
487            (false, false) => Self::I8,
488            (false, true) => Self::I16,
489            (true, false) => Self::Zero,
490            (true, true) => Self::I32,
491        }
492    }
493}
494
495/// Implements the logic for iterating over the individual runs
496#[derive(Clone, Debug)]
497pub struct DeltaRunIter<'a> {
498    limit: Option<usize>, // when None, consume all available data
499    remaining_in_run: u8,
500    value_type: DeltaRunType,
501    cursor: Cursor<'a>,
502}
503
504impl<'a> DeltaRunIter<'a> {
505    fn new(cursor: Cursor<'a>, limit: Option<usize>) -> Self {
506        DeltaRunIter {
507            limit,
508            remaining_in_run: 0,
509            value_type: DeltaRunType::I8,
510            cursor,
511        }
512    }
513
514    pub(crate) fn end(mut self) -> Cursor<'a> {
515        while self.next().is_some() {}
516        self.cursor
517    }
518
519    /// Skips `n` deltas without reading the actual delta values.
520    fn skip_fast(mut self, n: usize) -> Self {
521        let mut wanted = n;
522        loop {
523            let remaining = self.remaining_in_run as usize;
524            if wanted > remaining {
525                // Haven't seen enough deltas yet; consume the remaining
526                // data bytes and move to the next run
527                self.cursor.advance_by(remaining * self.value_type as usize);
528                wanted -= remaining;
529                if self.read_next_control().is_none() {
530                    self.limit = Some(0);
531                    break;
532                }
533                continue;
534            }
535            let consumed = wanted.min(remaining);
536            self.remaining_in_run -= consumed as u8;
537            self.cursor.advance_by(consumed * self.value_type as usize);
538            if let Some(limit) = self.limit.as_mut() {
539                *limit = limit.saturating_sub(n);
540            }
541            break;
542        }
543        self
544    }
545
546    fn read_next_control(&mut self) -> Option<()> {
547        self.remaining_in_run = 0;
548        let control: u8 = self.cursor.read().ok()?;
549        self.value_type = DeltaRunType::new(control);
550        self.remaining_in_run = (control & DELTA_RUN_COUNT_MASK) + 1;
551        Some(())
552    }
553}
554
555impl Iterator for DeltaRunIter<'_> {
556    type Item = i32;
557
558    fn next(&mut self) -> Option<Self::Item> {
559        if let Some(limit) = self.limit {
560            if limit == 0 {
561                return None;
562            }
563            self.limit = Some(limit - 1);
564        }
565        if self.remaining_in_run == 0 {
566            self.read_next_control()?;
567        }
568        self.remaining_in_run -= 1;
569        match self.value_type {
570            DeltaRunType::Zero => Some(0),
571            DeltaRunType::I8 => self.cursor.read::<i8>().ok().map(|v| v as i32),
572            DeltaRunType::I16 => self.cursor.read::<i16>().ok().map(|v| v as i32),
573            DeltaRunType::I32 => self.cursor.read().ok(),
574        }
575    }
576}
577
578/// Counts the number of deltas available in the given data, avoiding
579/// excessive reads.
580fn count_all_deltas(data: FontData) -> usize {
581    let mut count = 0;
582    let mut offset = 0;
583    while let Ok(control) = data.read_at::<u8>(offset) {
584        let run_count = (control & DELTA_RUN_COUNT_MASK) as usize + 1;
585        count += run_count;
586        offset += run_count * DeltaRunType::new(control) as usize + 1;
587    }
588    count
589}
590
591/// A helper type for iterating over [`TupleVariationHeader`]s.
592pub struct TupleVariationHeaderIter<'a> {
593    data: FontData<'a>,
594    n_headers: usize,
595    current: usize,
596    axis_count: u16,
597}
598
599impl<'a> TupleVariationHeaderIter<'a> {
600    pub(crate) fn new(data: FontData<'a>, n_headers: usize, axis_count: u16) -> Self {
601        Self {
602            data,
603            n_headers,
604            current: 0,
605            axis_count,
606        }
607    }
608}
609
610impl<'a> Iterator for TupleVariationHeaderIter<'a> {
611    type Item = Result<TupleVariationHeader<'a>, ReadError>;
612
613    #[inline(always)]
614    fn next(&mut self) -> Option<Self::Item> {
615        if self.current == self.n_headers {
616            return None;
617        }
618        self.current += 1;
619        let next = TupleVariationHeader::read(self.data, self.axis_count);
620
621        let next_len = next
622            .as_ref()
623            .map(|table| table.byte_len(self.axis_count))
624            .unwrap_or(0);
625        self.data = self.data.split_off(next_len)?;
626        Some(next)
627    }
628}
629
630#[derive(Clone)]
631pub struct TupleVariationData<'a, T> {
632    pub(crate) axis_count: u16,
633    pub(crate) shared_tuples: Option<ComputedArray<'a, Tuple<'a>>>,
634    pub(crate) shared_point_numbers: Option<PackedPointNumbers<'a>>,
635    pub(crate) tuple_count: TupleVariationCount,
636    // the data for all the tuple variation headers
637    pub(crate) header_data: FontData<'a>,
638    // the data for all the tuple bodies
639    pub(crate) serialized_data: FontData<'a>,
640    pub(crate) _marker: std::marker::PhantomData<fn() -> T>,
641}
642
643impl<'a, T> TupleVariationData<'a, T>
644where
645    T: TupleDelta,
646{
647    pub fn tuples(&self) -> TupleVariationIter<'a, T> {
648        TupleVariationIter {
649            current: 0,
650            parent: self.clone(),
651            header_iter: TupleVariationHeaderIter::new(
652                self.header_data,
653                self.tuple_count.count() as usize,
654                self.axis_count,
655            ),
656            serialized_data: self.serialized_data,
657            _marker: std::marker::PhantomData,
658        }
659    }
660
661    /// Returns an iterator over all of the pairs of (variation tuple, scalar)
662    /// for this glyph that are active for the given set of normalized
663    /// coordinates.
664    pub fn active_tuples_at(
665        &self,
666        coords: &'a [F2Dot14],
667    ) -> impl Iterator<Item = (TupleVariation<'a, T>, Fixed)> + 'a {
668        ActiveTupleVariationIter {
669            coords,
670            parent: self.clone(),
671            header_iter: TupleVariationHeaderIter::new(
672                self.header_data,
673                self.tuple_count.count() as usize,
674                self.axis_count,
675            ),
676            serialized_data: self.serialized_data,
677            data_offset: 0,
678            _marker: std::marker::PhantomData,
679        }
680    }
681
682    pub(crate) fn tuple_count(&self) -> usize {
683        self.tuple_count.count() as usize
684    }
685}
686
687/// An iterator over the [`TupleVariation`]s for a specific glyph.
688pub struct TupleVariationIter<'a, T> {
689    current: usize,
690    parent: TupleVariationData<'a, T>,
691    header_iter: TupleVariationHeaderIter<'a>,
692    serialized_data: FontData<'a>,
693    _marker: std::marker::PhantomData<fn() -> T>,
694}
695
696impl<'a, T> TupleVariationIter<'a, T>
697where
698    T: TupleDelta,
699{
700    #[inline(always)]
701    fn next_tuple(&mut self) -> Option<TupleVariation<'a, T>> {
702        if self.parent.tuple_count() == self.current {
703            return None;
704        }
705        self.current += 1;
706
707        // FIXME: is it okay to discard an error here?
708        let header = self.header_iter.next()?.ok()?;
709        let data_len = header.variation_data_size() as usize;
710        let var_data = self.serialized_data.take_up_to(data_len)?;
711
712        Some(TupleVariation {
713            axis_count: self.parent.axis_count,
714            header,
715            shared_tuples: self.parent.shared_tuples.clone(),
716            serialized_data: var_data,
717            shared_point_numbers: self.parent.shared_point_numbers.clone(),
718            _marker: std::marker::PhantomData,
719        })
720    }
721}
722
723impl<'a, T> Iterator for TupleVariationIter<'a, T>
724where
725    T: TupleDelta,
726{
727    type Item = TupleVariation<'a, T>;
728
729    #[inline(always)]
730    fn next(&mut self) -> Option<Self::Item> {
731        self.next_tuple()
732    }
733}
734
735/// An iterator over the active [`TupleVariation`]s for a specific glyph
736/// for a given set of coordinates.
737struct ActiveTupleVariationIter<'a, T> {
738    coords: &'a [F2Dot14],
739    parent: TupleVariationData<'a, T>,
740    header_iter: TupleVariationHeaderIter<'a>,
741    serialized_data: FontData<'a>,
742    data_offset: usize,
743    _marker: std::marker::PhantomData<fn() -> T>,
744}
745
746impl<'a, T> Iterator for ActiveTupleVariationIter<'a, T>
747where
748    T: TupleDelta,
749{
750    type Item = (TupleVariation<'a, T>, Fixed);
751
752    #[inline(always)]
753    fn next(&mut self) -> Option<Self::Item> {
754        loop {
755            let header = self.header_iter.next()?.ok()?;
756            let data_len = header.variation_data_size() as usize;
757            let data_start = self.data_offset;
758            let data_end = data_start.checked_add(data_len)?;
759            self.data_offset = data_end;
760            if let Some(scalar) = compute_scalar(
761                &header,
762                self.parent.axis_count as usize,
763                &self.parent.shared_tuples,
764                self.coords,
765            ) {
766                let var_data = self.serialized_data.slice(data_start..data_end)?;
767                return Some((
768                    TupleVariation {
769                        axis_count: self.parent.axis_count,
770                        header,
771                        shared_tuples: self.parent.shared_tuples.clone(),
772                        serialized_data: var_data,
773                        shared_point_numbers: self.parent.shared_point_numbers.clone(),
774                        _marker: std::marker::PhantomData,
775                    },
776                    scalar,
777                ));
778            }
779        }
780    }
781}
782
783/// A single set of tuple variation data
784#[derive(Clone)]
785pub struct TupleVariation<'a, T> {
786    axis_count: u16,
787    header: TupleVariationHeader<'a>,
788    shared_tuples: Option<ComputedArray<'a, Tuple<'a>>>,
789    serialized_data: FontData<'a>,
790    shared_point_numbers: Option<PackedPointNumbers<'a>>,
791    _marker: std::marker::PhantomData<fn() -> T>,
792}
793
794impl<'a, T> TupleVariation<'a, T>
795where
796    T: TupleDelta,
797{
798    /// Returns true if this tuple provides deltas for all points in a glyph.
799    pub fn has_deltas_for_all_points(&self) -> bool {
800        if self.header.tuple_index().private_point_numbers() {
801            PackedPointNumbers {
802                data: self.serialized_data,
803            }
804            .count()
805                == 0
806        } else if let Some(shared) = &self.shared_point_numbers {
807            shared.count() == 0
808        } else {
809            false
810        }
811    }
812
813    pub fn point_numbers(&self) -> PackedPointNumbersIter<'a> {
814        let (point_numbers, _) = self.point_numbers_and_packed_deltas();
815        point_numbers.iter()
816    }
817
818    /// Returns the 'peak' tuple for this variation
819    pub fn peak(&self) -> Tuple<'a> {
820        self.header
821            .tuple_index()
822            .tuple_records_index()
823            .and_then(|idx| self.shared_tuples.as_ref()?.get(idx as usize).ok())
824            .or_else(|| self.header.peak_tuple())
825            .unwrap_or_default()
826    }
827
828    pub fn intermediate_start(&self) -> Option<Tuple<'a>> {
829        self.header.intermediate_start_tuple()
830    }
831
832    pub fn intermediate_end(&self) -> Option<Tuple<'a>> {
833        self.header.intermediate_end_tuple()
834    }
835
836    /// Compute the fixed point scalar for this tuple at the given location in
837    /// variation space.
838    ///
839    /// The `coords` slice must be of lesser or equal length to the number of
840    /// axes. If it is less, missing (trailing) axes will be assumed to have
841    /// zero values.
842    ///
843    /// Returns `None` if this tuple is not applicable at the provided
844    /// coordinates (e.g. if the resulting scalar is zero).
845    pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Option<Fixed> {
846        compute_scalar(
847            &self.header,
848            self.axis_count as usize,
849            &self.shared_tuples,
850            coords,
851        )
852    }
853
854    /// Compute the floating point scalar for this tuple at the given location
855    /// in variation space.
856    ///
857    /// The `coords` slice must be of lesser or equal length to the number of
858    /// axes. If it is less, missing (trailing) axes will be assumed to have
859    /// zero values.
860    ///
861    /// Returns `None` if this tuple is not applicable at the provided
862    /// coordinates (e.g. if the resulting scalar is zero).
863    pub fn compute_scalar_f32(&self, coords: &[F2Dot14]) -> Option<f32> {
864        let mut scalar = 1.0;
865        let peak = self.peak();
866        let inter_start = self.header.intermediate_start_tuple();
867        let inter_end = self.header.intermediate_end_tuple();
868        if peak.len() != self.axis_count as usize {
869            return None;
870        }
871        for i in 0..self.axis_count {
872            let i = i as usize;
873            let coord = coords.get(i).copied().unwrap_or_default().to_bits() as i32;
874            let peak = peak.get(i).unwrap_or_default().to_bits() as i32;
875            if peak == 0 || peak == coord {
876                continue;
877            }
878            if coord == 0 {
879                return None;
880            }
881            if let (Some(inter_start), Some(inter_end)) = (&inter_start, &inter_end) {
882                let start = inter_start.get(i).unwrap_or_default().to_bits() as i32;
883                let end = inter_end.get(i).unwrap_or_default().to_bits() as i32;
884                if start > peak || peak > end || (start < 0 && end > 0 && peak != 0) {
885                    continue;
886                }
887                if coord < start || coord > end {
888                    return None;
889                }
890                if coord < peak {
891                    if peak != start {
892                        scalar *= (coord - start) as f32 / (peak - start) as f32;
893                    }
894                } else if peak != end {
895                    scalar *= (end - coord) as f32 / (end - peak) as f32;
896                }
897            } else {
898                if coord < peak.min(0) || coord > peak.max(0) {
899                    return None;
900                }
901                scalar *= coord as f32 / peak as f32;
902            }
903        }
904        Some(scalar)
905    }
906
907    /// Iterate over the deltas for this tuple.
908    ///
909    /// This does not account for scaling. Returns only explicitly encoded
910    /// deltas, e.g. an omission by IUP will not be present.
911    pub fn deltas(&self) -> TupleDeltaIter<'a, T> {
912        let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas();
913        let count = point_numbers.count() as usize;
914        let packed_deltas = if count == 0 {
915            PackedDeltas::consume_all(packed_deltas)
916        } else {
917            PackedDeltas::new(packed_deltas, if T::is_point() { count * 2 } else { count })
918        };
919        TupleDeltaIter::new(&point_numbers, packed_deltas)
920    }
921
922    fn point_numbers_and_packed_deltas(&self) -> (PackedPointNumbers<'a>, FontData<'a>) {
923        if self.header.tuple_index().private_point_numbers() {
924            PackedPointNumbers::split_off_front(self.serialized_data)
925        } else {
926            (
927                self.shared_point_numbers.clone().unwrap_or_default(),
928                self.serialized_data,
929            )
930        }
931    }
932}
933
934impl TupleVariation<'_, GlyphDelta> {
935    /// Reads the set of deltas from this tuple variation.
936    ///
937    /// This is significantly faster than using the [`Self::deltas`]
938    /// method but requires preallocated memory to store deltas and
939    /// flags.
940    ///
941    /// This method should only be used when the tuple variation is dense,
942    /// that is, [`Self::has_deltas_for_all_points`] returns true.
943    ///
944    /// The size of `deltas` must be the same as the target value set to
945    /// which the variation is applied. For simple outlines, this is
946    /// `num_points + 4` and for composites it is `num_components + 4`
947    /// (where the `+ 4` is to accommodate phantom points).
948    ///
949    /// The `deltas` slice will not be zeroed before accumulation and each
950    /// delta will be multiplied by the given `scalar`.
951    pub fn accumulate_dense_deltas<D: PointCoord>(
952        &self,
953        deltas: &mut [Point<D>],
954        scalar: Fixed,
955    ) -> Result<(), ReadError> {
956        let (_, packed_deltas) = self.point_numbers_and_packed_deltas();
957        let mut cursor = packed_deltas.cursor();
958        if scalar == Fixed::ONE {
959            // scalar of 1.0 is common so avoid the costly conversions and
960            // multiplications per coord
961            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
962                delta.x += D::from_i32(new_delta);
963            })?;
964            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
965                delta.y += D::from_i32(new_delta);
966            })?;
967        } else {
968            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
969                delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
970            })?;
971            read_dense_deltas(&mut cursor, deltas, |delta, new_delta| {
972                delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
973            })?;
974        }
975        Ok(())
976    }
977
978    /// Reads the set of deltas from this tuple variation.
979    ///
980    /// This is significantly faster than using the [`Self::deltas`]
981    /// method but requires preallocated memory to store deltas and
982    /// flags.
983    ///
984    /// This method should only be used when the tuple variation is sparse,
985    /// that is, [`Self::has_deltas_for_all_points`] returns false.
986    ///
987    /// The size of `deltas` must be the same as the target value set to
988    /// which the variation is applied. For simple outlines, this is
989    /// `num_points + 4` and for composites it is `num_components + 4`
990    /// (where the `+ 4` is to accommodate phantom points).
991    ///
992    /// The `deltas` and `flags` slices must be the same size. Modifications
993    /// to `deltas` will be sparse and for each entry that is modified, the
994    /// [PointMarker::HAS_DELTA] marker will be set for the corresponding
995    /// entry in the `flags` slice.
996    ///
997    /// The `deltas` slice will not be zeroed before accumulation and each
998    /// delta will be multiplied by the given `scalar`.
999    pub fn accumulate_sparse_deltas<D: PointCoord>(
1000        &self,
1001        deltas: &mut [Point<D>],
1002        flags: &mut [PointFlags],
1003        scalar: Fixed,
1004    ) -> Result<(), ReadError> {
1005        let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas();
1006        let mut cursor = packed_deltas.cursor();
1007        let count = point_numbers.count() as usize;
1008        if scalar == Fixed::ONE {
1009            // scalar of 1.0 is common so avoid the costly conversions and
1010            // multiplications per coord
1011            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
1012                if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) {
1013                    delta.x += D::from_i32(new_delta);
1014                    flag.set_marker(PointMarker::HAS_DELTA);
1015                }
1016            })?;
1017            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
1018                if let Some(delta) = deltas.get_mut(ix) {
1019                    delta.y += D::from_i32(new_delta);
1020                }
1021            })?;
1022        } else {
1023            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
1024                if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) {
1025                    delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
1026                    flag.set_marker(PointMarker::HAS_DELTA);
1027                }
1028            })?;
1029            read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| {
1030                if let Some(delta) = deltas.get_mut(ix) {
1031                    delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar);
1032                }
1033            })?;
1034        }
1035        Ok(())
1036    }
1037}
1038
1039/// This is basically a manually applied loop unswitching optimization
1040/// for reading deltas. It reads each typed run into a slice for processing
1041/// instead of handling each delta individually with all the necessary
1042/// branching that implies.
1043fn read_dense_deltas<T>(
1044    cursor: &mut Cursor,
1045    deltas: &mut [T],
1046    mut f: impl FnMut(&mut T, i32),
1047) -> Result<(), ReadError> {
1048    let count = deltas.len();
1049    let mut cur = 0;
1050    while cur < count {
1051        let control: u8 = cursor.read()?;
1052        let value_type = DeltaRunType::new(control);
1053        let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize;
1054        let dest = deltas
1055            .get_mut(cur..cur + run_count)
1056            .ok_or(ReadError::OutOfBounds)?;
1057        match value_type {
1058            DeltaRunType::Zero => {}
1059            DeltaRunType::I8 => {
1060                let packed_deltas = cursor.read_array::<i8>(run_count)?;
1061                for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) {
1062                    f(delta, *new_delta as i32);
1063                }
1064            }
1065            DeltaRunType::I16 => {
1066                let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?;
1067                for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) {
1068                    f(delta, new_delta.get() as i32);
1069                }
1070            }
1071            DeltaRunType::I32 => {
1072                let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?;
1073                for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) {
1074                    f(delta, new_delta.get());
1075                }
1076            }
1077        }
1078        cur += run_count;
1079    }
1080    Ok(())
1081}
1082
1083/// See [read_dense_deltas] docs.
1084fn read_sparse_deltas(
1085    cursor: &mut Cursor,
1086    point_numbers: &PackedPointNumbers,
1087    count: usize,
1088    mut f: impl FnMut(usize, i32),
1089) -> Result<(), ReadError> {
1090    let mut cur = 0;
1091    let mut points_iter = point_numbers.iter().map(|ix| ix as usize);
1092    while cur < count {
1093        let control: u8 = cursor.read()?;
1094        let value_type = DeltaRunType::new(control);
1095        let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize;
1096        match value_type {
1097            DeltaRunType::Zero => {
1098                for _ in 0..run_count {
1099                    let point_ix = points_iter.next().ok_or(ReadError::OutOfBounds)?;
1100                    f(point_ix, 0);
1101                }
1102            }
1103            DeltaRunType::I8 => {
1104                let packed_deltas = cursor.read_array::<i8>(run_count)?;
1105                for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) {
1106                    f(point_ix, *new_delta as i32);
1107                }
1108            }
1109            DeltaRunType::I16 => {
1110                let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?;
1111                for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) {
1112                    f(point_ix, new_delta.get() as i32);
1113                }
1114            }
1115            DeltaRunType::I32 => {
1116                let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?;
1117                for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) {
1118                    f(point_ix, new_delta.get());
1119                }
1120            }
1121        }
1122        cur += run_count;
1123    }
1124    Ok(())
1125}
1126
1127/// Compute the fixed point scalar for this tuple at the given location in
1128/// variation space.
1129///
1130/// The `coords` slice must be of lesser or equal length to the number of
1131/// axes. If it is less, missing (trailing) axes will be assumed to have
1132/// zero values.
1133///
1134/// Returns `None` if this tuple is not applicable at the provided
1135/// coordinates (e.g. if the resulting scalar is zero).
1136#[inline(always)]
1137fn compute_scalar<'a>(
1138    header: &TupleVariationHeader,
1139    axis_count: usize,
1140    shared_tuples: &Option<ComputedArray<'a, Tuple<'a>>>,
1141    coords: &[F2Dot14],
1142) -> Option<Fixed> {
1143    let mut scalar = Fixed::ONE;
1144    let tuple_idx = header.tuple_index();
1145    let peak = if let Some(shared_index) = tuple_idx.tuple_records_index() {
1146        shared_tuples.as_ref()?.get(shared_index as usize).ok()?
1147    } else {
1148        header.peak_tuple()?
1149    };
1150    if peak.len() != axis_count {
1151        return None;
1152    }
1153    let intermediate = header.intermediate_tuples();
1154    for (i, peak) in peak
1155        .values
1156        .iter()
1157        .enumerate()
1158        .filter(|(_, peak)| peak.get() != F2Dot14::ZERO)
1159    {
1160        let coord = coords.get(i).copied().unwrap_or_default();
1161        if coord == F2Dot14::ZERO {
1162            return None;
1163        }
1164        let peak = peak.get();
1165        if peak == coord {
1166            continue;
1167        }
1168        if let Some((inter_start, inter_end)) = &intermediate {
1169            let start = inter_start.get(i).unwrap_or_default();
1170            let end = inter_end.get(i).unwrap_or_default();
1171            if coord <= start || coord >= end {
1172                return None;
1173            }
1174            let coord = coord.to_fixed();
1175            let peak = peak.to_fixed();
1176            if coord < peak {
1177                let start = start.to_fixed();
1178                scalar = scalar.mul_div(coord - start, peak - start);
1179            } else {
1180                let end = end.to_fixed();
1181                scalar = scalar.mul_div(end - coord, end - peak);
1182            }
1183        } else {
1184            if coord < peak.min(F2Dot14::ZERO) || coord > peak.max(F2Dot14::ZERO) {
1185                return None;
1186            }
1187            let coord = coord.to_fixed();
1188            let peak = peak.to_fixed();
1189            scalar = scalar.mul_div(coord, peak);
1190        }
1191    }
1192    (scalar != Fixed::ZERO).then_some(scalar)
1193}
1194
1195#[derive(Clone, Debug)]
1196enum TupleDeltaValues<'a> {
1197    // Point deltas have separate runs for x and y coordinates.
1198    Points(DeltaRunIter<'a>, DeltaRunIter<'a>),
1199    Scalars(DeltaRunIter<'a>),
1200}
1201
1202/// An iterator over the deltas for a glyph.
1203#[derive(Clone, Debug)]
1204pub struct TupleDeltaIter<'a, T> {
1205    pub cur: usize,
1206    // if None all points get deltas, if Some specifies subset of points that do
1207    points: Option<PackedPointNumbersIter<'a>>,
1208    next_point: usize,
1209    values: TupleDeltaValues<'a>,
1210    _marker: std::marker::PhantomData<fn() -> T>,
1211}
1212
1213impl<'a, T> TupleDeltaIter<'a, T>
1214where
1215    T: TupleDelta,
1216{
1217    fn new(points: &PackedPointNumbers<'a>, deltas: PackedDeltas<'a>) -> TupleDeltaIter<'a, T> {
1218        let mut points = points.iter();
1219        let next_point = points.next();
1220        let values = if T::is_point() {
1221            TupleDeltaValues::Points(deltas.x_deltas(), deltas.y_deltas())
1222        } else {
1223            TupleDeltaValues::Scalars(deltas.iter())
1224        };
1225        TupleDeltaIter {
1226            cur: 0,
1227            points: next_point.map(|_| points),
1228            next_point: next_point.unwrap_or_default() as usize,
1229            values,
1230            _marker: std::marker::PhantomData,
1231        }
1232    }
1233}
1234
1235/// Trait for deltas that are computed in a tuple variation store.
1236pub trait TupleDelta: Sized + Copy + 'static {
1237    /// Returns true if the delta is a point and requires reading two values
1238    /// from the packed delta stream.
1239    fn is_point() -> bool;
1240
1241    /// Creates a new delta for the given position and coordinates. If
1242    /// the delta is not a point, the y value will always be zero.
1243    fn new(position: u16, x: i32, y: i32) -> Self;
1244}
1245
1246impl<T> Iterator for TupleDeltaIter<'_, T>
1247where
1248    T: TupleDelta,
1249{
1250    type Item = T;
1251
1252    fn next(&mut self) -> Option<Self::Item> {
1253        let (position, dx, dy) = loop {
1254            let position = if let Some(points) = &mut self.points {
1255                // if we have points then result is sparse; only some points have deltas
1256                if self.cur > self.next_point {
1257                    self.next_point = points.next()? as usize;
1258                }
1259                self.next_point
1260            } else {
1261                // no points, every point has a delta. Just take the next one.
1262                self.cur
1263            };
1264            if position == self.cur {
1265                let (dx, dy) = match &mut self.values {
1266                    TupleDeltaValues::Points(x, y) => (x.next()?, y.next()?),
1267                    TupleDeltaValues::Scalars(scalars) => (scalars.next()?, 0),
1268                };
1269                break (position, dx, dy);
1270            }
1271            self.cur += 1;
1272        };
1273        self.cur += 1;
1274        Some(T::new(position as u16, dx, dy))
1275    }
1276}
1277
1278impl EntryFormat {
1279    pub fn entry_size(self) -> u8 {
1280        ((self.bits() & Self::MAP_ENTRY_SIZE_MASK.bits()) >> 4) + 1
1281    }
1282
1283    pub fn bit_count(self) -> u8 {
1284        (self.bits() & Self::INNER_INDEX_BIT_COUNT_MASK.bits()) + 1
1285    }
1286
1287    // called from codegen
1288    pub(crate) fn map_size(self, map_count: impl Into<u32>) -> usize {
1289        self.entry_size() as usize * map_count.into() as usize
1290    }
1291}
1292
1293impl DeltaSetIndexMap<'_> {
1294    /// Returns the delta set index for the specified value.
1295    pub fn get(&self, index: u32) -> Result<DeltaSetIndex, ReadError> {
1296        let (entry_format, map_count, data) = match self {
1297            Self::Format0(fmt) => (fmt.entry_format(), fmt.map_count() as u32, fmt.map_data()),
1298            Self::Format1(fmt) => (fmt.entry_format(), fmt.map_count(), fmt.map_data()),
1299        };
1300        let entry_size = entry_format.entry_size();
1301        let data = FontData::new(data);
1302        // "if an index into the mapping array is used that is greater than or equal to
1303        // mapCount, then the last logical entry of the mapping array is used."
1304        // https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats
1305        // #associating-target-items-to-variation-data
1306        let index = index.min(map_count.saturating_sub(1));
1307        let offset = index as usize * entry_size as usize;
1308        let entry = match entry_size {
1309            1 => data.read_at::<u8>(offset)? as u32,
1310            2 => data.read_at::<u16>(offset)? as u32,
1311            3 => data.read_at::<Uint24>(offset)?.into(),
1312            4 => data.read_at::<u32>(offset)?,
1313            _ => {
1314                return Err(ReadError::MalformedData(
1315                    "invalid entry size in DeltaSetIndexMap",
1316                ))
1317            }
1318        };
1319        let bit_count = entry_format.bit_count();
1320        Ok(DeltaSetIndex {
1321            outer: (entry >> bit_count) as u16,
1322            inner: (entry & ((1 << bit_count) - 1)) as u16,
1323        })
1324    }
1325}
1326
1327impl ItemVariationStore<'_> {
1328    /// Computes the delta value for the specified index and set of normalized
1329    /// variation coordinates.
1330    pub fn compute_delta(
1331        &self,
1332        index: DeltaSetIndex,
1333        coords: &[F2Dot14],
1334    ) -> Result<i32, ReadError> {
1335        if coords.is_empty() {
1336            return Ok(0);
1337        }
1338        let data = match self.item_variation_data().get(index.outer as usize) {
1339            Some(data) => data?,
1340            None => return Ok(0),
1341        };
1342        let regions = self.variation_region_list()?.variation_regions();
1343        let region_indices = data.region_indexes();
1344        // Compute deltas with 64-bit precision.
1345        // See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/7ab541a2/src/truetype/ttgxvar.c#L1094>
1346        let mut accum = 0i64;
1347        for (i, region_delta) in data.delta_set(index.inner).enumerate() {
1348            let region_index = region_indices
1349                .get(i)
1350                .ok_or(ReadError::MalformedData(
1351                    "invalid delta sets in ItemVariationStore",
1352                ))?
1353                .get() as usize;
1354            let region = regions.get(region_index)?;
1355            let scalar = region.compute_scalar(coords);
1356            accum += region_delta as i64 * scalar.to_bits() as i64;
1357        }
1358        Ok(((accum + 0x8000) >> 16) as i32)
1359    }
1360
1361    /// Computes the delta value in floating point for the specified index and set
1362    /// of normalized variation coordinates.
1363    pub fn compute_float_delta(
1364        &self,
1365        index: DeltaSetIndex,
1366        coords: &[F2Dot14],
1367    ) -> Result<FloatItemDelta, ReadError> {
1368        if coords.is_empty() {
1369            return Ok(FloatItemDelta::ZERO);
1370        }
1371        let data = match self.item_variation_data().get(index.outer as usize) {
1372            Some(data) => data?,
1373            None => return Ok(FloatItemDelta::ZERO),
1374        };
1375        let regions = self.variation_region_list()?.variation_regions();
1376        let region_indices = data.region_indexes();
1377        // Compute deltas in 64-bit floating point.
1378        let mut accum = 0f64;
1379        for (i, region_delta) in data.delta_set(index.inner).enumerate() {
1380            let region_index = region_indices
1381                .get(i)
1382                .ok_or(ReadError::MalformedData(
1383                    "invalid delta sets in ItemVariationStore",
1384                ))?
1385                .get() as usize;
1386            let region = regions.get(region_index)?;
1387            let scalar = region.compute_scalar_f32(coords);
1388            accum += region_delta as f64 * scalar as f64;
1389        }
1390        Ok(FloatItemDelta(accum))
1391    }
1392}
1393
1394/// Floating point item delta computed by an item variation store.
1395///
1396/// These can be applied to types that implement [`FloatItemDeltaTarget`].
1397#[derive(Copy, Clone, Default, Debug)]
1398pub struct FloatItemDelta(f64);
1399
1400impl FloatItemDelta {
1401    pub const ZERO: Self = Self(0.0);
1402}
1403
1404/// Trait for applying floating point item deltas to target values.
1405pub trait FloatItemDeltaTarget {
1406    fn apply_float_delta(&self, delta: FloatItemDelta) -> f32;
1407}
1408
1409impl FloatItemDeltaTarget for Fixed {
1410    fn apply_float_delta(&self, delta: FloatItemDelta) -> f32 {
1411        const FIXED_TO_FLOAT: f64 = 1.0 / 65536.0;
1412        self.to_f32() + (delta.0 * FIXED_TO_FLOAT) as f32
1413    }
1414}
1415
1416impl FloatItemDeltaTarget for FWord {
1417    fn apply_float_delta(&self, delta: FloatItemDelta) -> f32 {
1418        self.to_i16() as f32 + delta.0 as f32
1419    }
1420}
1421
1422impl FloatItemDeltaTarget for UfWord {
1423    fn apply_float_delta(&self, delta: FloatItemDelta) -> f32 {
1424        self.to_u16() as f32 + delta.0 as f32
1425    }
1426}
1427
1428impl FloatItemDeltaTarget for F2Dot14 {
1429    fn apply_float_delta(&self, delta: FloatItemDelta) -> f32 {
1430        const F2DOT14_TO_FLOAT: f64 = 1.0 / 16384.0;
1431        self.to_f32() + (delta.0 * F2DOT14_TO_FLOAT) as f32
1432    }
1433}
1434
1435impl<'a> VariationRegion<'a> {
1436    /// Computes a scalar value for this region and the specified
1437    /// normalized variation coordinates.
1438    pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Fixed {
1439        const ZERO: Fixed = Fixed::ZERO;
1440        let mut scalar = Fixed::ONE;
1441        for (i, peak, axis_coords) in self.active_region_axes() {
1442            let peak = peak.to_fixed();
1443            let start = axis_coords.start_coord.get().to_fixed();
1444            let end = axis_coords.end_coord.get().to_fixed();
1445            if start > peak || peak > end || start < ZERO && end > ZERO {
1446                continue;
1447            }
1448            let coord = coords.get(i).map(|coord| coord.to_fixed()).unwrap_or(ZERO);
1449            if coord < start || coord > end {
1450                return ZERO;
1451            } else if coord == peak {
1452                continue;
1453            } else if coord < peak {
1454                scalar = scalar.mul_div(coord - start, peak - start);
1455            } else {
1456                scalar = scalar.mul_div(end - coord, end - peak);
1457            }
1458        }
1459        scalar
1460    }
1461
1462    /// Computes a floating point scalar value for this region and the
1463    /// specified normalized variation coordinates.
1464    pub fn compute_scalar_f32(&self, coords: &[F2Dot14]) -> f32 {
1465        let mut scalar = 1.0;
1466        for (i, peak, axis_coords) in self.active_region_axes() {
1467            let peak = peak.to_f32();
1468            let start = axis_coords.start_coord.get().to_f32();
1469            let end = axis_coords.end_coord.get().to_f32();
1470            if start > peak || peak > end || start < 0.0 && end > 0.0 {
1471                continue;
1472            }
1473            let coord = coords.get(i).map(|coord| coord.to_f32()).unwrap_or(0.0);
1474            if coord < start || coord > end {
1475                return 0.0;
1476            } else if coord == peak {
1477                continue;
1478            } else if coord < peak {
1479                scalar = (scalar * (coord - start)) / (peak - start);
1480            } else {
1481                scalar = (scalar * (end - coord)) / (end - peak);
1482            }
1483        }
1484        scalar
1485    }
1486
1487    fn active_region_axes(
1488        &self,
1489    ) -> impl Iterator<Item = (usize, F2Dot14, &'a RegionAxisCoordinates)> {
1490        self.region_axes()
1491            .iter()
1492            .enumerate()
1493            .filter_map(|(i, axis_coords)| {
1494                let peak = axis_coords.peak_coord();
1495                if peak != F2Dot14::ZERO {
1496                    Some((i, peak, axis_coords))
1497                } else {
1498                    None
1499                }
1500            })
1501    }
1502}
1503
1504impl<'a> ItemVariationData<'a> {
1505    /// Returns an iterator over the per-region delta values for the specified
1506    /// inner index.
1507    pub fn delta_set(&self, inner_index: u16) -> impl Iterator<Item = i32> + 'a + Clone {
1508        let word_delta_count = self.word_delta_count();
1509        let region_count = self.region_index_count();
1510        let bytes_per_row = Self::delta_row_len(word_delta_count, region_count);
1511        let long_words = word_delta_count & 0x8000 != 0;
1512        let word_delta_count = word_delta_count & 0x7FFF;
1513
1514        let offset = bytes_per_row * inner_index as usize;
1515        ItemDeltas {
1516            cursor: FontData::new(self.delta_sets())
1517                .slice(offset..)
1518                .unwrap_or_default()
1519                .cursor(),
1520            word_delta_count,
1521            long_words,
1522            len: region_count,
1523            pos: 0,
1524        }
1525    }
1526
1527    pub fn get_delta_row_len(&self) -> usize {
1528        let word_delta_count = self.word_delta_count();
1529        let region_count = self.region_index_count();
1530        Self::delta_row_len(word_delta_count, region_count)
1531    }
1532
1533    /// the length of one delta set
1534    pub fn delta_row_len(word_delta_count: u16, region_index_count: u16) -> usize {
1535        let region_count = region_index_count as usize;
1536        let long_words = word_delta_count & 0x8000 != 0;
1537        let (word_size, small_size) = if long_words { (4, 2) } else { (2, 1) };
1538        let long_delta_count = (word_delta_count & 0x7FFF) as usize;
1539        let short_delta_count = region_count.saturating_sub(long_delta_count);
1540        long_delta_count * word_size + short_delta_count * small_size
1541    }
1542
1543    // called from generated code: compute the length in bytes of the delta_sets data
1544    pub fn delta_sets_len(
1545        item_count: u16,
1546        word_delta_count: u16,
1547        region_index_count: u16,
1548    ) -> usize {
1549        let bytes_per_row = Self::delta_row_len(word_delta_count, region_index_count);
1550        bytes_per_row * item_count as usize
1551    }
1552}
1553
1554#[derive(Clone)]
1555struct ItemDeltas<'a> {
1556    cursor: Cursor<'a>,
1557    word_delta_count: u16,
1558    long_words: bool,
1559    len: u16,
1560    pos: u16,
1561}
1562
1563impl Iterator for ItemDeltas<'_> {
1564    type Item = i32;
1565
1566    fn next(&mut self) -> Option<Self::Item> {
1567        if self.pos >= self.len {
1568            return None;
1569        }
1570        let pos = self.pos;
1571        self.pos += 1;
1572        let value = match (pos >= self.word_delta_count, self.long_words) {
1573            (true, true) | (false, false) => self.cursor.read::<i16>().ok()? as i32,
1574            (true, false) => self.cursor.read::<i8>().ok()? as i32,
1575            (false, true) => self.cursor.read::<i32>().ok()?,
1576        };
1577        Some(value)
1578    }
1579}
1580
1581pub(crate) fn advance_delta(
1582    dsim: Option<Result<DeltaSetIndexMap, ReadError>>,
1583    ivs: Result<ItemVariationStore, ReadError>,
1584    glyph_id: GlyphId,
1585    coords: &[F2Dot14],
1586) -> Result<Fixed, ReadError> {
1587    if coords.is_empty() {
1588        return Ok(Fixed::ZERO);
1589    }
1590    let gid = glyph_id.to_u32();
1591    let ix = match dsim {
1592        Some(Ok(dsim)) => dsim.get(gid)?,
1593        _ => DeltaSetIndex {
1594            outer: 0,
1595            inner: gid as _,
1596        },
1597    };
1598    Ok(Fixed::from_i32(ivs?.compute_delta(ix, coords)?))
1599}
1600
1601pub(crate) fn item_delta(
1602    dsim: Option<Result<DeltaSetIndexMap, ReadError>>,
1603    ivs: Result<ItemVariationStore, ReadError>,
1604    glyph_id: GlyphId,
1605    coords: &[F2Dot14],
1606) -> Result<Fixed, ReadError> {
1607    if coords.is_empty() {
1608        return Ok(Fixed::ZERO);
1609    }
1610    let gid = glyph_id.to_u32();
1611    let ix = match dsim {
1612        Some(Ok(dsim)) => dsim.get(gid)?,
1613        _ => return Err(ReadError::NullOffset),
1614    };
1615    Ok(Fixed::from_i32(ivs?.compute_delta(ix, coords)?))
1616}
1617
1618#[cfg(test)]
1619mod tests {
1620    use font_test_data::bebuffer::BeBuffer;
1621
1622    use super::*;
1623    use crate::{FontRef, TableProvider};
1624
1625    #[test]
1626    fn ivs_regions() {
1627        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
1628        let hvar = font.hvar().expect("missing HVAR table");
1629        let ivs = hvar
1630            .item_variation_store()
1631            .expect("missing item variation store in HVAR");
1632        let region_list = ivs.variation_region_list().expect("missing region list!");
1633        let regions = region_list.variation_regions();
1634        let expected = &[
1635            // start_coord, peak_coord, end_coord
1636            vec![[-1.0f32, -1.0, 0.0]],
1637            vec![[0.0, 1.0, 1.0]],
1638        ][..];
1639        let region_coords = regions
1640            .iter()
1641            .map(|region| {
1642                region
1643                    .unwrap()
1644                    .region_axes()
1645                    .iter()
1646                    .map(|coords| {
1647                        [
1648                            coords.start_coord().to_f32(),
1649                            coords.peak_coord().to_f32(),
1650                            coords.end_coord().to_f32(),
1651                        ]
1652                    })
1653                    .collect::<Vec<_>>()
1654            })
1655            .collect::<Vec<_>>();
1656        assert_eq!(expected, &region_coords);
1657    }
1658
1659    // adapted from https://github.com/fonttools/fonttools/blob/f73220816264fc383b8a75f2146e8d69e455d398/Tests/ttLib/tables/TupleVariation_test.py#L492
1660    #[test]
1661    fn packed_points() {
1662        fn decode_points(bytes: &[u8]) -> Option<Vec<u16>> {
1663            let data = FontData::new(bytes);
1664            let packed = PackedPointNumbers { data };
1665            if packed.count() == 0 {
1666                None
1667            } else {
1668                Some(packed.iter().collect())
1669            }
1670        }
1671
1672        assert_eq!(decode_points(&[0]), None);
1673        // all points in glyph (in overly verbose encoding, not explicitly prohibited by spec)
1674        assert_eq!(decode_points(&[0x80, 0]), None);
1675        // 2 points; first run: [9, 9+6]
1676        assert_eq!(decode_points(&[0x02, 0x01, 0x09, 0x06]), Some(vec![9, 15]));
1677        // 2 points; first run: [0xBEEF, 0xCAFE]. (0x0C0F = 0xCAFE - 0xBEEF)
1678        assert_eq!(
1679            decode_points(&[0x02, 0x81, 0xbe, 0xef, 0x0c, 0x0f]),
1680            Some(vec![0xbeef, 0xcafe])
1681        );
1682        // 1 point; first run: [7]
1683        assert_eq!(decode_points(&[0x01, 0, 0x07]), Some(vec![7]));
1684        // 1 point; first run: [7] in overly verbose encoding
1685        assert_eq!(decode_points(&[0x01, 0x80, 0, 0x07]), Some(vec![7]));
1686        // 1 point; first run: [65535]; requires words to be treated as unsigned numbers
1687        assert_eq!(decode_points(&[0x01, 0x80, 0xff, 0xff]), Some(vec![65535]));
1688        // 4 points; first run: [7, 8]; second run: [255, 257]. 257 is stored in delta-encoded bytes (0xFF + 2).
1689        assert_eq!(
1690            decode_points(&[0x04, 1, 7, 1, 1, 0xff, 2]),
1691            Some(vec![7, 8, 263, 265])
1692        );
1693    }
1694
1695    #[test]
1696    fn packed_point_byte_len() {
1697        fn count_bytes(bytes: &[u8]) -> usize {
1698            let packed = PackedPointNumbers {
1699                data: FontData::new(bytes),
1700            };
1701            packed.total_len()
1702        }
1703
1704        static CASES: &[&[u8]] = &[
1705            &[0],
1706            &[0x80, 0],
1707            &[0x02, 0x01, 0x09, 0x06],
1708            &[0x02, 0x81, 0xbe, 0xef, 0x0c, 0x0f],
1709            &[0x01, 0, 0x07],
1710            &[0x01, 0x80, 0, 0x07],
1711            &[0x01, 0x80, 0xff, 0xff],
1712            &[0x04, 1, 7, 1, 1, 0xff, 2],
1713        ];
1714
1715        for case in CASES {
1716            assert_eq!(count_bytes(case), case.len(), "{case:?}");
1717        }
1718    }
1719
1720    // https://github.com/fonttools/fonttools/blob/c30a6355ffdf7f09d31e7719975b4b59bac410af/Tests/ttLib/tables/TupleVariation_test.py#L670
1721    #[test]
1722    fn packed_deltas() {
1723        static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]);
1724
1725        let deltas = PackedDeltas::consume_all(INPUT);
1726        assert_eq!(deltas.count, 7);
1727        assert_eq!(
1728            deltas.iter().collect::<Vec<_>>(),
1729            &[0, 0, 0, 0, 258, -127, -128]
1730        );
1731
1732        assert_eq!(
1733            PackedDeltas::consume_all(FontData::new(&[0x81]))
1734                .iter()
1735                .collect::<Vec<_>>(),
1736            &[0, 0,]
1737        );
1738    }
1739
1740    // https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas
1741    #[test]
1742    fn packed_deltas_spec() {
1743        static INPUT: FontData = FontData::new(&[
1744            0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34,
1745        ]);
1746        static EXPECTED: &[i32] = &[10, -105, 0, -58, 0, 0, 0, 0, 0, 0, 0, 0, 4130, -1228];
1747
1748        let deltas = PackedDeltas::consume_all(INPUT);
1749        assert_eq!(deltas.count, EXPECTED.len());
1750        assert_eq!(deltas.iter().collect::<Vec<_>>(), EXPECTED);
1751    }
1752
1753    #[test]
1754    fn packed_point_split() {
1755        static INPUT: FontData =
1756            FontData::new(&[2, 1, 1, 2, 1, 205, 143, 1, 8, 0, 1, 202, 59, 1, 255, 0]);
1757        let (points, data) = PackedPointNumbers::split_off_front(INPUT);
1758        assert_eq!(points.count(), 2);
1759        assert_eq!(points.iter().collect::<Vec<_>>(), &[1, 3]);
1760        assert_eq!(points.total_len(), 4);
1761        assert_eq!(data.len(), INPUT.len() - 4);
1762    }
1763
1764    #[test]
1765    fn packed_points_dont_panic() {
1766        // a single '0' byte means that there are deltas for all points
1767        static ALL_POINTS: FontData = FontData::new(&[0]);
1768        let (all_points, _) = PackedPointNumbers::split_off_front(ALL_POINTS);
1769        // in which case the iterator just keeps incrementing until u16::MAX
1770        assert_eq!(all_points.iter().count(), u16::MAX as usize);
1771    }
1772
1773    /// Test that we split properly when the coordinate boundary doesn't align
1774    /// with a packed run boundary
1775    #[test]
1776    fn packed_delta_run_crosses_coord_boundary() {
1777        // 8 deltas with values 0..=7 with a run broken after the first 6; the
1778        // coordinate boundary occurs after the first 4
1779        static INPUT: FontData = FontData::new(&[
1780            // first run: 6 deltas as bytes
1781            5,
1782            0,
1783            1,
1784            2,
1785            3,
1786            // coordinate boundary is here
1787            4,
1788            5,
1789            // second run: 2 deltas as words
1790            1 | DELTAS_ARE_WORDS,
1791            0,
1792            6,
1793            0,
1794            7,
1795        ]);
1796        let deltas = PackedDeltas::consume_all(INPUT);
1797        assert_eq!(deltas.count, 8);
1798        let x_deltas = deltas.x_deltas().collect::<Vec<_>>();
1799        let y_deltas = deltas.y_deltas().collect::<Vec<_>>();
1800        assert_eq!(x_deltas, [0, 1, 2, 3]);
1801        assert_eq!(y_deltas, [4, 5, 6, 7]);
1802    }
1803
1804    /// We don't have a reference for our float delta computation, so this is
1805    /// a sanity test to ensure that floating point deltas are within a
1806    /// reasonable margin of the same in fixed point.
1807    #[test]
1808    fn ivs_float_deltas_nearly_match_fixed_deltas() {
1809        let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
1810        let axis_count = font.fvar().unwrap().axis_count() as usize;
1811        let colr = font.colr().unwrap();
1812        let ivs = colr.item_variation_store().unwrap().unwrap();
1813        // Generate a set of coords from -1 to 1 in 0.1 increments
1814        for coord in (0..=20).map(|x| F2Dot14::from_f32((x as f32) / 10.0 - 1.0)) {
1815            // For testing purposes, just splat the coord to all axes
1816            let coords = vec![coord; axis_count];
1817            for (outer_ix, data) in ivs.item_variation_data().iter().enumerate() {
1818                let outer_ix = outer_ix as u16;
1819                let Some(Ok(data)) = data else {
1820                    continue;
1821                };
1822                for inner_ix in 0..data.item_count() {
1823                    let delta_ix = DeltaSetIndex {
1824                        outer: outer_ix,
1825                        inner: inner_ix,
1826                    };
1827                    // Check the deltas against all possible target values
1828                    let orig_delta = ivs.compute_delta(delta_ix, &coords).unwrap();
1829                    let float_delta = ivs.compute_float_delta(delta_ix, &coords).unwrap();
1830                    // For font unit types, we need to accept both rounding and
1831                    // truncation to account for the additional accumulation of
1832                    // fractional bits in floating point
1833                    assert!(
1834                        orig_delta == float_delta.0.round() as i32
1835                            || orig_delta == float_delta.0.trunc() as i32
1836                    );
1837                    // For the fixed point types, check with an epsilon
1838                    const EPSILON: f32 = 1e12;
1839                    let fixed_delta = Fixed::ZERO.apply_float_delta(float_delta);
1840                    assert!((Fixed::from_bits(orig_delta).to_f32() - fixed_delta).abs() < EPSILON);
1841                    let f2dot14_delta = F2Dot14::ZERO.apply_float_delta(float_delta);
1842                    assert!(
1843                        (F2Dot14::from_bits(orig_delta as i16).to_f32() - f2dot14_delta).abs()
1844                            < EPSILON
1845                    );
1846                }
1847            }
1848        }
1849    }
1850
1851    #[test]
1852    fn ivs_data_len_short() {
1853        let data = BeBuffer::new()
1854            .push(2u16) // item_count
1855            .push(3u16) // word_delta_count
1856            .push(5u16) // region_index_count
1857            .extend([0u16, 1, 2, 3, 4]) // region_indices
1858            .extend([1u8; 128]); // this is much more data than we need!
1859
1860        let ivs = ItemVariationData::read(data.data().into()).unwrap();
1861        let row_len = (3 * u16::RAW_BYTE_LEN) + (2 * u8::RAW_BYTE_LEN); // 3 word deltas, 2 byte deltas
1862        let expected_len = 2 * row_len;
1863        assert_eq!(ivs.delta_sets().len(), expected_len);
1864    }
1865
1866    #[test]
1867    fn ivs_data_len_long() {
1868        let data = BeBuffer::new()
1869            .push(2u16) // item_count
1870            .push(2u16 | 0x8000) // word_delta_count, long deltas
1871            .push(4u16) // region_index_count
1872            .extend([0u16, 1, 2]) // region_indices
1873            .extend([1u8; 128]); // this is much more data than we need!
1874
1875        let ivs = ItemVariationData::read(data.data().into()).unwrap();
1876        let row_len = (2 * u32::RAW_BYTE_LEN) + (2 * u16::RAW_BYTE_LEN); // 1 word (4-byte) delta, 2 short (2-byte)
1877        let expected_len = 2 * row_len;
1878        assert_eq!(ivs.delta_sets().len(), expected_len);
1879    }
1880
1881    // Add with overflow when accumulating packed point numbers
1882    // https://issues.oss-fuzz.com/issues/378159154
1883    #[test]
1884    fn packed_point_numbers_avoid_overflow() {
1885        // Lots of 1 bits triggers the behavior quite nicely
1886        let buf = vec![0xFF; 0xFFFF];
1887        let iter = PackedPointNumbersIter::new(0xFFFF, FontData::new(&buf).cursor());
1888        // Don't panic!
1889        let _ = iter.count();
1890    }
1891
1892    // Dense accumulator should match iterator
1893    #[test]
1894    fn accumulate_dense() {
1895        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
1896        let gvar = font.gvar().unwrap();
1897        let gvar_data = gvar.glyph_variation_data(GlyphId::new(1)).unwrap().unwrap();
1898        let mut count = 0;
1899        for tuple in gvar_data.tuples() {
1900            if !tuple.has_deltas_for_all_points() {
1901                continue;
1902            }
1903            let iter_deltas = tuple
1904                .deltas()
1905                .map(|delta| (delta.x_delta, delta.y_delta))
1906                .collect::<Vec<_>>();
1907            let mut delta_buf = vec![Point::broadcast(Fixed::ZERO); iter_deltas.len()];
1908            tuple
1909                .accumulate_dense_deltas(&mut delta_buf, Fixed::ONE)
1910                .unwrap();
1911            let accum_deltas = delta_buf
1912                .iter()
1913                .map(|delta| (delta.x.to_i32(), delta.y.to_i32()))
1914                .collect::<Vec<_>>();
1915            assert_eq!(iter_deltas, accum_deltas);
1916            count += iter_deltas.len();
1917        }
1918        assert!(count != 0);
1919    }
1920
1921    // Sparse accumulator should match iterator
1922    #[test]
1923    fn accumulate_sparse() {
1924        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
1925        let gvar = font.gvar().unwrap();
1926        let gvar_data = gvar.glyph_variation_data(GlyphId::new(2)).unwrap().unwrap();
1927        let mut count = 0;
1928        for tuple in gvar_data.tuples() {
1929            if tuple.has_deltas_for_all_points() {
1930                continue;
1931            }
1932            let iter_deltas = tuple.deltas().collect::<Vec<_>>();
1933            let max_modified_point = iter_deltas
1934                .iter()
1935                .max_by_key(|delta| delta.position)
1936                .unwrap()
1937                .position as usize;
1938            let mut delta_buf = vec![Point::broadcast(Fixed::ZERO); max_modified_point + 1];
1939            let mut flags = vec![PointFlags::default(); delta_buf.len()];
1940            tuple
1941                .accumulate_sparse_deltas(&mut delta_buf, &mut flags, Fixed::ONE)
1942                .unwrap();
1943            let mut accum_deltas = vec![];
1944            for (i, (delta, flag)) in delta_buf.iter().zip(flags).enumerate() {
1945                if flag.has_marker(PointMarker::HAS_DELTA) {
1946                    accum_deltas.push(GlyphDelta::new(
1947                        i as u16,
1948                        delta.x.to_i32(),
1949                        delta.y.to_i32(),
1950                    ));
1951                }
1952            }
1953            assert_eq!(iter_deltas, accum_deltas);
1954            count += iter_deltas.len();
1955        }
1956        assert!(count != 0);
1957    }
1958}