Coverage Report

Created: 2025-06-16 06:13

/src/ttf-parser/src/var_store.rs
Line
Count
Source (jump to first uncovered line)
1
//! Implementation of Item Variation Store
2
//!
3
//! <https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#item-variation-store>
4
5
use crate::parser::{FromData, LazyArray16, NumFrom, Stream};
6
use crate::NormalizedCoordinate;
7
8
#[derive(Clone, Copy, Debug)]
9
pub(crate) struct ItemVariationStore<'a> {
10
    data: &'a [u8],
11
    data_offsets: LazyArray16<'a, u32>,
12
    pub regions: VariationRegionList<'a>,
13
}
14
15
impl<'a> Default for ItemVariationStore<'a> {
16
    #[inline]
17
3.20k
    fn default() -> Self {
18
3.20k
        ItemVariationStore {
19
3.20k
            data: &[],
20
3.20k
            data_offsets: LazyArray16::new(&[]),
21
3.20k
            regions: VariationRegionList {
22
3.20k
                axis_count: 0,
23
3.20k
                regions: LazyArray16::new(&[]),
24
3.20k
            },
25
3.20k
        }
26
3.20k
    }
27
}
28
29
impl<'a> ItemVariationStore<'a> {
30
    #[inline]
31
3.40k
    pub fn parse(mut s: Stream) -> Option<ItemVariationStore> {
32
3.40k
        let data = s.tail()?;
33
34
3.40k
        let mut regions_s = s.clone();
35
3.40k
        let format = s.read::<u16>()?;
36
3.38k
        if format != 1 {
37
241
            return None;
38
3.14k
        }
39
40
3.14k
        let region_list_offset = s.read::<u32>()?;
41
3.12k
        let count = s.read::<u16>()?;
42
3.08k
        let offsets = s.read_array16::<u32>(count)?;
43
44
2.17k
        let regions = {
45
2.86k
            regions_s.advance(usize::num_from(region_list_offset));
46
            // TODO: should be the same as in `fvar`
47
2.86k
            let axis_count = regions_s.read::<u16>()?;
48
2.40k
            let count = regions_s.read::<u16>()?;
49
2.39k
            let total = count.checked_mul(axis_count)?;
50
            VariationRegionList {
51
2.35k
                axis_count,
52
2.35k
                regions: regions_s.read_array16::<RegionAxisCoordinatesRecord>(total)?,
53
            }
54
        };
55
56
2.17k
        Some(ItemVariationStore {
57
2.17k
            data,
58
2.17k
            data_offsets: offsets,
59
2.17k
            regions,
60
2.17k
        })
61
3.40k
    }
62
63
124k
    pub fn region_indices(&self, index: u16) -> Option<LazyArray16<u16>> {
64
        // Offsets in bytes from the start of the item variation store
65
        // to each item variation data subtable.
66
124k
        let offset = self.data_offsets.get(index)?;
67
122k
        let mut s = Stream::new_at(self.data, usize::num_from(offset))?;
68
121k
        s.skip::<u16>(); // item_count
69
121k
        s.skip::<u16>(); // short_delta_count
70
121k
        let count = s.read::<u16>()?;
71
121k
        s.read_array16::<u16>(count)
72
124k
    }
73
74
52
    pub fn parse_delta(
75
52
        &self,
76
52
        outer_index: u16,
77
52
        inner_index: u16,
78
52
        coordinates: &[NormalizedCoordinate],
79
52
    ) -> Option<f32> {
80
52
        let offset = self.data_offsets.get(outer_index)?;
81
42
        let mut s = Stream::new_at(self.data, usize::num_from(offset))?;
82
38
        let item_count = s.read::<u16>()?;
83
38
        let word_delta_count = s.read::<u16>()?;
84
38
        let region_index_count = s.read::<u16>()?;
85
38
        let region_indices = s.read_array16::<u16>(region_index_count)?;
86
87
38
        if inner_index >= item_count {
88
10
            return None;
89
28
        }
90
28
91
28
        let has_long_words = (word_delta_count & 0x8000) != 0;
92
28
        let word_delta_count = word_delta_count & 0x7FFF;
93
28
94
28
        // From the spec: The length of the data for each row, in bytes, is
95
28
        // regionIndexCount + (wordDeltaCount & WORD_DELTA_COUNT_MASK)
96
28
        // if the LONG_WORDS flag is not set, or 2 x that amount if the flag is set.
97
28
        let mut delta_set_len = word_delta_count + region_index_count;
98
28
        if has_long_words {
99
14
            delta_set_len *= 2;
100
14
        }
101
102
28
        s.advance(usize::from(inner_index).checked_mul(usize::from(delta_set_len))?);
103
104
28
        let mut delta = 0.0;
105
28
        let mut i = 0;
106
564k
        while i < word_delta_count {
107
564k
            let idx = region_indices.get(i)?;
108
564k
            let num = if has_long_words {
109
                // TODO: use f64?
110
261k
                s.read::<i32>()? as f32
111
            } else {
112
303k
                f32::from(s.read::<i16>()?)
113
            };
114
564k
            delta += num * self.regions.evaluate_region(idx, coordinates);
115
564k
            i += 1;
116
        }
117
118
536k
        while i < region_index_count {
119
536k
            let idx = region_indices.get(i)?;
120
536k
            let num = if has_long_words {
121
392k
                f32::from(s.read::<i16>()?)
122
            } else {
123
143k
                f32::from(s.read::<i8>()?)
124
            };
125
536k
            delta += num * self.regions.evaluate_region(idx, coordinates);
126
536k
            i += 1;
127
        }
128
129
25
        Some(delta)
130
52
    }
131
}
132
133
#[derive(Clone, Copy, Debug)]
134
pub struct VariationRegionList<'a> {
135
    axis_count: u16,
136
    regions: LazyArray16<'a, RegionAxisCoordinatesRecord>,
137
}
138
139
impl<'a> VariationRegionList<'a> {
140
    #[inline]
141
1.62M
    pub(crate) fn evaluate_region(&self, index: u16, coordinates: &[NormalizedCoordinate]) -> f32 {
142
1.62M
        let mut v = 1.0;
143
1.62M
        for (i, coord) in coordinates.iter().enumerate() {
144
448k
            let region = match self.regions.get(index * self.axis_count + i as u16) {
145
140k
                Some(r) => r,
146
308k
                None => return 0.0,
147
            };
148
149
140k
            let factor = region.evaluate_axis(coord.get());
150
140k
            if factor == 0.0 {
151
64.7k
                return 0.0;
152
75.3k
            }
153
75.3k
154
75.3k
            v *= factor;
155
        }
156
157
1.25M
        v
158
1.62M
    }
159
}
160
161
#[derive(Clone, Copy, Debug)]
162
struct RegionAxisCoordinatesRecord {
163
    start_coord: i16,
164
    peak_coord: i16,
165
    end_coord: i16,
166
}
167
168
impl RegionAxisCoordinatesRecord {
169
    #[inline]
170
140k
    pub fn evaluate_axis(&self, coord: i16) -> f32 {
171
140k
        let start = self.start_coord;
172
140k
        let peak = self.peak_coord;
173
140k
        let end = self.end_coord;
174
140k
175
140k
        if start > peak || peak > end {
176
37.7k
            return 1.0;
177
102k
        }
178
102k
179
102k
        if start < 0 && end > 0 && peak != 0 {
180
4.40k
            return 1.0;
181
97.9k
        }
182
97.9k
183
97.9k
        if peak == 0 || coord == peak {
184
2.61k
            return 1.0;
185
95.3k
        }
186
95.3k
187
95.3k
        if coord <= start || end <= coord {
188
64.7k
            return 0.0;
189
30.5k
        }
190
30.5k
191
30.5k
        if coord < peak {
192
28.9k
            f32::from(coord - start) / f32::from(peak - start)
193
        } else {
194
1.58k
            f32::from(end - coord) / f32::from(end - peak)
195
        }
196
140k
    }
197
}
198
199
impl FromData for RegionAxisCoordinatesRecord {
200
    const SIZE: usize = 6;
201
202
    #[inline]
203
140k
    fn parse(data: &[u8]) -> Option<Self> {
204
140k
        let mut s = Stream::new(data);
205
140k
        Some(RegionAxisCoordinatesRecord {
206
140k
            start_coord: s.read::<i16>()?,
207
140k
            peak_coord: s.read::<i16>()?,
208
140k
            end_coord: s.read::<i16>()?,
209
        })
210
140k
    }
211
}