1//! Implementation of Item Variation Store
2//!
3//! <https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#item-variation-store>
4
5use crate::parser::{FromData, LazyArray16, NumFrom, Stream};
6use crate::NormalizedCoordinate;
7
8#[derive(Clone, Copy)]
9pub(crate) struct ItemVariationStore<'a> {
10 data: &'a [u8],
11 data_offsets: LazyArray16<'a, u32>,
12 pub regions: VariationRegionList<'a>,
13}
14
15impl<'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
29impl<'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)]
113pub struct VariationRegionList<'a> {
114 axis_count: u16,
115 regions: LazyArray16<'a, RegionAxisCoordinatesRecord>,
116}
117
118impl<'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)]
141struct RegionAxisCoordinatesRecord {
142 start_coord: i16,
143 peak_coord: i16,
144 end_coord: i16,
145}
146
147impl 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
178impl 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