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::NormalizedCoord;
6 use crate::parser::{Stream, FromData, LazyArray16, NumFrom};
7 
8 
9 #[derive(Clone, Copy)]
10 pub(crate) struct ItemVariationStore<'a> {
11     data: &'a [u8],
12     data_offsets: LazyArray16<'a, u32>,
13     pub regions: VariationRegionList<'a>,
14 }
15 
16 impl<'a> Default for ItemVariationStore<'a> {
17     #[inline]
default() -> Self18     fn default() -> Self {
19         ItemVariationStore {
20             data: &[],
21             data_offsets: LazyArray16::new(&[]),
22             regions: VariationRegionList {
23                 axis_count: 0,
24                 regions: LazyArray16::new(&[]),
25             }
26         }
27     }
28 }
29 
30 impl<'a> ItemVariationStore<'a> {
31     #[inline]
parse(mut s: Stream) -> Option<ItemVariationStore>32     pub fn parse(mut s: Stream) -> Option<ItemVariationStore> {
33         let data = s.tail()?;
34 
35         let mut regions_s = s.clone();
36         let format: u16 = s.read()?;
37         if format != 1 {
38             return None;
39         }
40 
41         let region_list_offset: u32 = s.read()?;
42         let count: u16 = s.read()?;
43         let offsets = s.read_array16::<u32>(count)?;
44 
45         let regions = {
46             regions_s.advance(usize::num_from(region_list_offset));
47             // TODO: should be the same as in `fvar`
48             let axis_count = regions_s.read::<u16>()?;
49             let count = regions_s.read::<u16>()?;
50             let total = count.checked_mul(axis_count)?;
51             VariationRegionList {
52                 axis_count,
53                 regions: regions_s.read_array16(total)?,
54             }
55         };
56 
57         Some(ItemVariationStore { data, data_offsets: offsets, regions })
58     }
59 
region_indices(&self, index: u16) -> Option<LazyArray16<u16>>60     pub fn region_indices(&self, index: u16) -> Option<LazyArray16<u16>> {
61         // Offsets in bytes from the start of the item variation store
62         // to each item variation data subtable.
63         let offset = self.data_offsets.get(index)?;
64         let mut s = Stream::new_at(self.data, usize::num_from(offset))?;
65         s.skip::<u16>(); // item_count
66         s.skip::<u16>(); // short_delta_count
67         let count: u16 = s.read()?;
68         s.read_array16(count)
69     }
70 
parse_delta( &self, outer_index: u16, inner_index: u16, coordinates: &[NormalizedCoord], ) -> Option<f32>71     pub fn parse_delta(
72         &self,
73         outer_index: u16,
74         inner_index: u16,
75         coordinates: &[NormalizedCoord],
76     ) -> Option<f32> {
77         let offset = self.data_offsets.get(outer_index)?;
78         let mut s = Stream::new_at(self.data, usize::num_from(offset))?;
79         let item_count: u16 = s.read()?;
80         let short_delta_count: u16 = s.read()?;
81         let region_index_count: u16 = s.read()?;
82         let region_indices = s.read_array16(region_index_count)?;
83 
84         if inner_index >= item_count {
85             return None;
86         }
87 
88         let delta_set_len = usize::from(short_delta_count) + usize::from(region_index_count);
89         s.advance(usize::from(inner_index).checked_mul(delta_set_len)?);
90 
91         let mut delta = 0.0;
92         let mut i = 0;
93         while i < short_delta_count {
94             let idx = region_indices.get(i)?;
95             delta += f32::from(s.read::<i16>()?) * self.regions.evaluate_region(idx, coordinates);
96             i += 1;
97         }
98 
99         while i < region_index_count {
100             let idx = region_indices.get(i)?;
101             delta += f32::from(s.read::<i8>()?) * self.regions.evaluate_region(idx, coordinates);
102             i += 1;
103         }
104 
105         Some(delta)
106     }
107 }
108 
109 
110 #[derive(Clone, Copy)]
111 pub struct VariationRegionList<'a> {
112     axis_count: u16,
113     regions: LazyArray16<'a, RegionAxisCoordinatesRecord>,
114 }
115 
116 impl<'a> VariationRegionList<'a> {
117     #[inline]
evaluate_region( &self, index: u16, coordinates: &[NormalizedCoord], ) -> f32118     pub(crate) fn evaluate_region(
119         &self,
120         index: u16,
121         coordinates: &[NormalizedCoord],
122     ) -> f32 {
123         let mut v = 1.0;
124         for (i, coord) in coordinates.iter().enumerate() {
125             let region = match self.regions.get(index * self.axis_count + i as u16) {
126                 Some(r) => r,
127                 None => return 0.0,
128             };
129 
130             let factor = region.evaluate_axis(coord.get());
131             if factor == 0.0 {
132                 return 0.0;
133             }
134 
135             v *= factor;
136         }
137 
138         v
139     }
140 }
141 
142 
143 #[derive(Clone, Copy)]
144 struct RegionAxisCoordinatesRecord {
145     start_coord: i16,
146     peak_coord: i16,
147     end_coord: i16,
148 }
149 
150 impl RegionAxisCoordinatesRecord {
151     #[inline]
evaluate_axis(&self, coord: i16) -> f32152     pub fn evaluate_axis(&self, coord: i16) -> f32 {
153         let start = self.start_coord;
154         let peak = self.peak_coord;
155         let end = self.end_coord;
156 
157         if start > peak || peak > end {
158             return 1.0;
159         }
160 
161         if start < 0 && end > 0 && peak != 0 {
162             return 1.0;
163         }
164 
165         if peak == 0 || coord == peak {
166             return 1.0;
167         }
168 
169         if coord <= start || end <= coord {
170             return 0.0;
171         }
172 
173         if coord < peak {
174             f32::from(coord - start) / f32::from(peak - start)
175         } else {
176             f32::from(end - coord) / f32::from(end - peak)
177         }
178     }
179 }
180 
181 impl FromData for RegionAxisCoordinatesRecord {
182     const SIZE: usize = 6;
183 
184     #[inline]
parse(data: &[u8]) -> Option<Self>185     fn parse(data: &[u8]) -> Option<Self> {
186         let mut s = Stream::new(data);
187         Some(RegionAxisCoordinatesRecord {
188             start_coord: s.read()?,
189             peak_coord: s.read()?,
190             end_coord: s.read()?,
191         })
192     }
193 }
194