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)] |
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 short_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 delta_set_len = usize::from(short_delta_count) + usize::from(region_index_count); |
92 | s.advance(usize::from(inner_index).checked_mul(delta_set_len)?); |
93 | |
94 | let mut delta = 0.0; |
95 | let mut i = 0; |
96 | while i < short_delta_count { |
97 | let idx = region_indices.get(i)?; |
98 | delta += f32::from(s.read::<i16>()?) * self.regions.evaluate_region(idx, coordinates); |
99 | i += 1; |
100 | } |
101 | |
102 | while i < region_index_count { |
103 | let idx = region_indices.get(i)?; |
104 | delta += f32::from(s.read::<i8>()?) * self.regions.evaluate_region(idx, coordinates); |
105 | i += 1; |
106 | } |
107 | |
108 | Some(delta) |
109 | } |
110 | } |
111 | |
112 | #[derive (Clone, Copy)] |
113 | pub struct VariationRegionList<'a> { |
114 | axis_count: u16, |
115 | regions: LazyArray16<'a, RegionAxisCoordinatesRecord>, |
116 | } |
117 | |
118 | impl<'a> VariationRegionList<'a> { |
119 | #[inline ] |
120 | pub(crate) fn evaluate_region(&self, index: u16, coordinates: &[NormalizedCoordinate]) -> f32 { |
121 | let mut v: f32 = 1.0; |
122 | for (i: usize, coord: &NormalizedCoordinate) in coordinates.iter().enumerate() { |
123 | let region: RegionAxisCoordinatesRecord = match self.regions.get(index:index * self.axis_count + i as u16) { |
124 | Some(r: RegionAxisCoordinatesRecord) => r, |
125 | None => return 0.0, |
126 | }; |
127 | |
128 | let factor: f32 = region.evaluate_axis(coord:coord.get()); |
129 | if factor == 0.0 { |
130 | return 0.0; |
131 | } |
132 | |
133 | v *= factor; |
134 | } |
135 | |
136 | v |
137 | } |
138 | } |
139 | |
140 | #[derive (Clone, Copy)] |
141 | struct RegionAxisCoordinatesRecord { |
142 | start_coord: i16, |
143 | peak_coord: i16, |
144 | end_coord: i16, |
145 | } |
146 | |
147 | impl RegionAxisCoordinatesRecord { |
148 | #[inline ] |
149 | pub fn evaluate_axis(&self, coord: i16) -> f32 { |
150 | let start = self.start_coord; |
151 | let peak = self.peak_coord; |
152 | let end = self.end_coord; |
153 | |
154 | if start > peak || peak > end { |
155 | return 1.0; |
156 | } |
157 | |
158 | if start < 0 && end > 0 && peak != 0 { |
159 | return 1.0; |
160 | } |
161 | |
162 | if peak == 0 || coord == peak { |
163 | return 1.0; |
164 | } |
165 | |
166 | if coord <= start || end <= coord { |
167 | return 0.0; |
168 | } |
169 | |
170 | if coord < peak { |
171 | f32::from(coord - start) / f32::from(peak - start) |
172 | } else { |
173 | f32::from(end - coord) / f32::from(end - peak) |
174 | } |
175 | } |
176 | } |
177 | |
178 | impl FromData for RegionAxisCoordinatesRecord { |
179 | const SIZE: usize = 6; |
180 | |
181 | #[inline ] |
182 | fn parse(data: &[u8]) -> Option<Self> { |
183 | let mut s: Stream<'_> = Stream::new(data); |
184 | Some(RegionAxisCoordinatesRecord { |
185 | start_coord: s.read::<i16>()?, |
186 | peak_coord: s.read::<i16>()?, |
187 | end_coord: s.read::<i16>()?, |
188 | }) |
189 | } |
190 | } |
191 | |