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