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 | fn default() -> Self { |
18 | ItemVariationStore { |
19 | data: &[], |
20 | data_offsets: LazyArray16::new(&[]), |
21 | regions: VariationRegionList { |
22 | axis_count: 0, |
23 | regions: LazyArray16::new(&[]), |
24 | }, |
25 | } |
26 | } |
27 | } |
28 | |
29 | impl<'a> ItemVariationStore<'a> { |
30 | #[inline ] |
31 | pub fn parse(mut s: Stream) -> Option<ItemVariationStore> { |
32 | let data = s.tail()?; |
33 | |
34 | let mut regions_s = s.clone(); |
35 | let format = s.read::<u16>()?; |
36 | if format != 1 { |
37 | return None; |
38 | } |
39 | |
40 | let region_list_offset = s.read::<u32>()?; |
41 | let count = s.read::<u16>()?; |
42 | let offsets = s.read_array16::<u32>(count)?; |
43 | |
44 | let regions = { |
45 | regions_s.advance(usize::num_from(region_list_offset)); |
46 | // TODO: should be the same as in `fvar` |
47 | let axis_count = regions_s.read::<u16>()?; |
48 | let count = regions_s.read::<u16>()?; |
49 | let total = count.checked_mul(axis_count)?; |
50 | VariationRegionList { |
51 | axis_count, |
52 | regions: regions_s.read_array16::<RegionAxisCoordinatesRecord>(total)?, |
53 | } |
54 | }; |
55 | |
56 | Some(ItemVariationStore { |
57 | data, |
58 | data_offsets: offsets, |
59 | regions, |
60 | }) |
61 | } |
62 | |
63 | 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 | let offset = self.data_offsets.get(index)?; |
67 | let mut s = Stream::new_at(self.data, usize::num_from(offset))?; |
68 | s.skip::<u16>(); // item_count |
69 | s.skip::<u16>(); // short_delta_count |
70 | let count = s.read::<u16>()?; |
71 | s.read_array16::<u16>(count) |
72 | } |
73 | |
74 | pub fn parse_delta( |
75 | &self, |
76 | outer_index: u16, |
77 | inner_index: u16, |
78 | coordinates: &[NormalizedCoordinate], |
79 | ) -> Option<f32> { |
80 | let offset = self.data_offsets.get(outer_index)?; |
81 | let mut s = Stream::new_at(self.data, usize::num_from(offset))?; |
82 | let item_count = s.read::<u16>()?; |
83 | let word_delta_count = s.read::<u16>()?; |
84 | let region_index_count = s.read::<u16>()?; |
85 | let region_indices = s.read_array16::<u16>(region_index_count)?; |
86 | |
87 | if inner_index >= item_count { |
88 | return None; |
89 | } |
90 | |
91 | let has_long_words = (word_delta_count & 0x8000) != 0; |
92 | let word_delta_count = word_delta_count & 0x7FFF; |
93 | |
94 | // From the spec: The length of the data for each row, in bytes, is |
95 | // regionIndexCount + (wordDeltaCount & WORD_DELTA_COUNT_MASK) |
96 | // if the LONG_WORDS flag is not set, or 2 x that amount if the flag is set. |
97 | let mut delta_set_len = word_delta_count + region_index_count; |
98 | if has_long_words { |
99 | delta_set_len *= 2; |
100 | } |
101 | |
102 | s.advance(usize::from(inner_index).checked_mul(usize::from(delta_set_len))?); |
103 | |
104 | let mut delta = 0.0; |
105 | let mut i = 0; |
106 | while i < word_delta_count { |
107 | let idx = region_indices.get(i)?; |
108 | let num = if has_long_words { |
109 | // TODO: use f64? |
110 | s.read::<i32>()? as f32 |
111 | } else { |
112 | f32::from(s.read::<i16>()?) |
113 | }; |
114 | delta += num * self.regions.evaluate_region(idx, coordinates); |
115 | i += 1; |
116 | } |
117 | |
118 | while i < region_index_count { |
119 | let idx = region_indices.get(i)?; |
120 | let num = if has_long_words { |
121 | f32::from(s.read::<i16>()?) |
122 | } else { |
123 | f32::from(s.read::<i8>()?) |
124 | }; |
125 | delta += num * self.regions.evaluate_region(idx, coordinates); |
126 | i += 1; |
127 | } |
128 | |
129 | Some(delta) |
130 | } |
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 | pub(crate) fn evaluate_region(&self, index: u16, coordinates: &[NormalizedCoordinate]) -> f32 { |
142 | let mut v: f32 = 1.0; |
143 | for (i: usize, coord: &NormalizedCoordinate) in coordinates.iter().enumerate() { |
144 | let region: RegionAxisCoordinatesRecord = match self.regions.get(index:index * self.axis_count + i as u16) { |
145 | Some(r: RegionAxisCoordinatesRecord) => r, |
146 | None => return 0.0, |
147 | }; |
148 | |
149 | let factor: f32 = region.evaluate_axis(coord.get()); |
150 | if factor == 0.0 { |
151 | return 0.0; |
152 | } |
153 | |
154 | v *= factor; |
155 | } |
156 | |
157 | v |
158 | } |
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 | pub fn evaluate_axis(&self, coord: i16) -> f32 { |
171 | let start = self.start_coord; |
172 | let peak = self.peak_coord; |
173 | let end = self.end_coord; |
174 | |
175 | if start > peak || peak > end { |
176 | return 1.0; |
177 | } |
178 | |
179 | if start < 0 && end > 0 && peak != 0 { |
180 | return 1.0; |
181 | } |
182 | |
183 | if peak == 0 || coord == peak { |
184 | return 1.0; |
185 | } |
186 | |
187 | if coord <= start || end <= coord { |
188 | return 0.0; |
189 | } |
190 | |
191 | if coord < peak { |
192 | f32::from(coord - start) / f32::from(peak - start) |
193 | } else { |
194 | f32::from(end - coord) / f32::from(end - peak) |
195 | } |
196 | } |
197 | } |
198 | |
199 | impl FromData for RegionAxisCoordinatesRecord { |
200 | const SIZE: usize = 6; |
201 | |
202 | #[inline ] |
203 | fn parse(data: &[u8]) -> Option<Self> { |
204 | let mut s: Stream<'_> = Stream::new(data); |
205 | Some(RegionAxisCoordinatesRecord { |
206 | start_coord: s.read::<i16>()?, |
207 | peak_coord: s.read::<i16>()?, |
208 | end_coord: s.read::<i16>()?, |
209 | }) |
210 | } |
211 | } |
212 | |