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