/rust/registry/src/index.crates.io-1949cf8c6b5b557f/read-fonts-0.46.0/src/tables/variations.rs
Line | Count | Source |
1 | | //! OpenType font variations common tables. |
2 | | |
3 | | include!("../../generated/generated_variations.rs"); |
4 | | |
5 | | use super::{ |
6 | | glyf::{PointCoord, PointFlags, PointMarker}, |
7 | | gvar::GlyphDelta, |
8 | | }; |
9 | | |
10 | | pub const NO_VARIATION_INDEX: u32 = 0xFFFFFFFF; |
11 | | /// Outer and inner indices for reading from an [ItemVariationStore]. |
12 | | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] |
13 | | pub struct DeltaSetIndex { |
14 | | /// Outer delta set index. |
15 | | pub outer: u16, |
16 | | /// Inner delta set index. |
17 | | pub inner: u16, |
18 | | } |
19 | | |
20 | | impl DeltaSetIndex { |
21 | | pub const NO_VARIATION_INDEX: Self = Self { |
22 | | outer: (NO_VARIATION_INDEX >> 16) as u16, |
23 | | inner: (NO_VARIATION_INDEX & 0xFFFF) as u16, |
24 | | }; |
25 | | } |
26 | | |
27 | | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] |
28 | | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
29 | | pub struct TupleIndex(u16); |
30 | | |
31 | | impl TupleIndex { |
32 | | /// Flag indicating that this tuple variation header includes an embedded |
33 | | /// peak tuple record, immediately after the tupleIndex field. |
34 | | /// |
35 | | /// If set, the low 12 bits of the tupleIndex value are ignored. |
36 | | /// |
37 | | /// Note that this must always be set within the 'cvar' table. |
38 | | pub const EMBEDDED_PEAK_TUPLE: u16 = 0x8000; |
39 | | |
40 | | /// Flag indicating that this tuple variation table applies to an |
41 | | /// intermediate region within the variation space. |
42 | | /// |
43 | | /// If set, the header includes the two intermediate-region, start and end |
44 | | /// tuple records, immediately after the peak tuple record (if present). |
45 | | pub const INTERMEDIATE_REGION: u16 = 0x4000; |
46 | | /// Flag indicating that the serialized data for this tuple variation table |
47 | | /// includes packed “point” number data. |
48 | | /// |
49 | | /// If set, this tuple variation table uses that number data; if clear, |
50 | | /// this tuple variation table uses shared number data found at the start |
51 | | /// of the serialized data for this glyph variation data or 'cvar' table. |
52 | | pub const PRIVATE_POINT_NUMBERS: u16 = 0x2000; |
53 | | //0x1000 Reserved Reserved for future use — set to 0. |
54 | | // |
55 | | /// Mask for the low 12 bits to give the shared tuple records index. |
56 | | pub const TUPLE_INDEX_MASK: u16 = 0x0FFF; |
57 | | |
58 | | #[inline(always)] |
59 | 45.2k | fn tuple_len(self, axis_count: u16, flag: usize) -> usize { |
60 | 45.2k | if flag == 0 { |
61 | 21.7k | self.embedded_peak_tuple() as usize * axis_count as usize |
62 | | } else { |
63 | 23.4k | self.intermediate_region() as usize * axis_count as usize |
64 | | } |
65 | 45.2k | } |
66 | | |
67 | 0 | pub fn bits(self) -> u16 { |
68 | 0 | self.0 |
69 | 0 | } |
70 | | |
71 | 0 | pub fn from_bits(bits: u16) -> Self { |
72 | 0 | TupleIndex(bits) |
73 | 0 | } |
74 | | |
75 | | /// `true` if the header includes an embedded peak tuple. |
76 | 81.7k | pub fn embedded_peak_tuple(self) -> bool { |
77 | 81.7k | (self.0 & Self::EMBEDDED_PEAK_TUPLE) != 0 |
78 | 81.7k | } |
79 | | |
80 | | /// `true` if the header includes the two intermediate region tuple records. |
81 | 77.3k | pub fn intermediate_region(self) -> bool { |
82 | 77.3k | (self.0 & Self::INTERMEDIATE_REGION) != 0 |
83 | 77.3k | } |
84 | | |
85 | | /// `true` if the data for this table includes packed point number data. |
86 | 3.44k | pub fn private_point_numbers(self) -> bool { |
87 | 3.44k | (self.0 & Self::PRIVATE_POINT_NUMBERS) != 0 |
88 | 3.44k | } |
89 | | |
90 | 26.7k | pub fn tuple_records_index(self) -> Option<u16> { |
91 | 26.7k | (!self.embedded_peak_tuple()).then_some(self.0 & Self::TUPLE_INDEX_MASK) |
92 | 26.7k | } |
93 | | } |
94 | | |
95 | | impl types::Scalar for TupleIndex { |
96 | | type Raw = <u16 as types::Scalar>::Raw; |
97 | 0 | fn to_raw(self) -> Self::Raw { |
98 | 0 | self.0.to_raw() |
99 | 0 | } |
100 | 135k | fn from_raw(raw: Self::Raw) -> Self { |
101 | 135k | let t = <u16>::from_raw(raw); |
102 | 135k | Self(t) |
103 | 135k | } |
104 | | } |
105 | | |
106 | | /// The 'tupleVariationCount' field of the [Tuple Variation Store Header][header] |
107 | | /// |
108 | | /// The high 4 bits are flags, and the low 12 bits are the number of tuple |
109 | | /// variation tables for this glyph. The count can be any number between 1 and 4095. |
110 | | /// |
111 | | /// [header]: https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuple-variation-store-header |
112 | | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] |
113 | | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
114 | | pub struct TupleVariationCount(u16); |
115 | | |
116 | | impl TupleVariationCount { |
117 | | /// Flag indicating that some or all tuple variation tables reference a |
118 | | /// shared set of “point” numbers. |
119 | | /// |
120 | | /// These shared numbers are represented as packed point number data at the |
121 | | /// start of the serialized data. |
122 | | pub const SHARED_POINT_NUMBERS: u16 = 0x8000; |
123 | | |
124 | | /// Mask for the low 12 bits to give the shared tuple records index. |
125 | | pub const COUNT_MASK: u16 = 0x0FFF; |
126 | | |
127 | 0 | pub fn bits(self) -> u16 { |
128 | 0 | self.0 |
129 | 0 | } |
130 | | |
131 | 0 | pub fn from_bits(bits: u16) -> Self { |
132 | 0 | Self(bits) |
133 | 0 | } |
134 | | |
135 | | /// `true` if any tables reference a shared set of point numbers |
136 | 2.51k | pub fn shared_point_numbers(self) -> bool { |
137 | 2.51k | (self.0 & Self::SHARED_POINT_NUMBERS) != 0 |
138 | 2.51k | } |
139 | | |
140 | 2.51k | pub fn count(self) -> u16 { |
141 | 2.51k | self.0 & Self::COUNT_MASK |
142 | 2.51k | } |
143 | | } |
144 | | |
145 | | impl types::Scalar for TupleVariationCount { |
146 | | type Raw = <u16 as types::Scalar>::Raw; |
147 | 0 | fn to_raw(self) -> Self::Raw { |
148 | 0 | self.0.to_raw() |
149 | 0 | } |
150 | 2.68k | fn from_raw(raw: Self::Raw) -> Self { |
151 | 2.68k | let t = <u16>::from_raw(raw); |
152 | 2.68k | Self(t) |
153 | 2.68k | } |
154 | | } |
155 | | |
156 | | impl<'a> TupleVariationHeader<'a> { |
157 | | /// Peak tuple record for this tuple variation table — optional, |
158 | | /// determined by flags in the tupleIndex value. Note that this |
159 | | /// must always be included in the 'cvar' table. |
160 | | #[inline(always)] |
161 | 6.13k | pub fn peak_tuple(&self) -> Option<Tuple<'a>> { |
162 | 6.13k | self.tuple_index().embedded_peak_tuple().then(|| { |
163 | 6.13k | let range = self.peak_tuple_byte_range(); |
164 | 6.13k | Tuple { |
165 | 6.13k | values: self.offset_data().read_array(range).unwrap(), |
166 | 6.13k | } |
167 | 6.13k | }) |
168 | 6.13k | } |
169 | | |
170 | | /// Intermediate start tuple record for this tuple variation table |
171 | | /// — optional, determined by flags in the tupleIndex value. |
172 | | #[inline(always)] |
173 | 0 | pub fn intermediate_start_tuple(&self) -> Option<Tuple<'a>> { |
174 | 0 | self.tuple_index().intermediate_region().then(|| { |
175 | 0 | let range = self.intermediate_start_tuple_byte_range(); |
176 | 0 | Tuple { |
177 | 0 | values: self.offset_data().read_array(range).unwrap(), |
178 | 0 | } |
179 | 0 | }) |
180 | 0 | } |
181 | | |
182 | | /// Intermediate end tuple record for this tuple variation table |
183 | | /// — optional, determined by flags in the tupleIndex value. |
184 | | #[inline(always)] |
185 | 0 | pub fn intermediate_end_tuple(&self) -> Option<Tuple<'a>> { |
186 | 0 | self.tuple_index().intermediate_region().then(|| { |
187 | 0 | let range = self.intermediate_end_tuple_byte_range(); |
188 | 0 | Tuple { |
189 | 0 | values: self.offset_data().read_array(range).unwrap(), |
190 | 0 | } |
191 | 0 | }) |
192 | 0 | } |
193 | | |
194 | | /// Intermediate tuple records for this tuple variation table |
195 | | /// — optional, determined by flags in the tupleIndex value. |
196 | | #[inline(always)] |
197 | 26.7k | pub fn intermediate_tuples(&self) -> Option<(Tuple<'a>, Tuple<'a>)> { |
198 | 26.7k | self.tuple_index().intermediate_region().then(|| { |
199 | 7.82k | let start_range = self.intermediate_start_tuple_byte_range(); |
200 | 7.82k | let end_range = self.intermediate_end_tuple_byte_range(); |
201 | 7.82k | ( |
202 | 7.82k | Tuple { |
203 | 7.82k | values: self.offset_data().read_array(start_range).unwrap(), |
204 | 7.82k | }, |
205 | 7.82k | Tuple { |
206 | 7.82k | values: self.offset_data().read_array(end_range).unwrap(), |
207 | 7.82k | }, |
208 | 7.82k | ) |
209 | 7.82k | }) |
210 | 26.7k | } |
211 | | |
212 | | /// Compute the actual length of this table in bytes |
213 | | #[inline(always)] |
214 | 27.1k | fn byte_len(&self, axis_count: u16) -> usize { |
215 | | const FIXED_LEN: usize = u16::RAW_BYTE_LEN + TupleIndex::RAW_BYTE_LEN; |
216 | 27.1k | let tuple_byte_len = F2Dot14::RAW_BYTE_LEN * axis_count as usize; |
217 | 27.1k | let index = self.tuple_index(); |
218 | | FIXED_LEN |
219 | 27.1k | + if index.embedded_peak_tuple() { |
220 | 6.32k | tuple_byte_len |
221 | | } else { |
222 | 20.7k | Default::default() |
223 | | } |
224 | 27.1k | + if index.intermediate_region() { |
225 | 8.16k | tuple_byte_len * 2 |
226 | | } else { |
227 | 18.9k | Default::default() |
228 | | } |
229 | 27.1k | } |
230 | | } |
231 | | |
232 | | impl Tuple<'_> { |
233 | 10.0k | pub fn len(&self) -> usize { |
234 | 10.0k | self.values().len() |
235 | 10.0k | } |
236 | | |
237 | 0 | pub fn is_empty(&self) -> bool { |
238 | 0 | self.values.is_empty() |
239 | 0 | } |
240 | | |
241 | | #[inline(always)] |
242 | 26.3k | pub fn get(&self, idx: usize) -> Option<F2Dot14> { |
243 | 26.3k | self.values.get(idx).map(BigEndian::get) |
244 | 26.3k | } |
245 | | } |
246 | | |
247 | | //FIXME: add an #[extra_traits(..)] attribute! |
248 | | #[allow(clippy::derivable_impls)] |
249 | | impl Default for Tuple<'_> { |
250 | 0 | fn default() -> Self { |
251 | 0 | Self { |
252 | 0 | values: Default::default(), |
253 | 0 | } |
254 | 0 | } |
255 | | } |
256 | | |
257 | | /// [Packed "Point" Numbers](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-point-numbers) |
258 | | #[derive(Clone, Default, Debug)] |
259 | | pub struct PackedPointNumbers<'a> { |
260 | | data: FontData<'a>, |
261 | | } |
262 | | |
263 | | impl<'a> PackedPointNumbers<'a> { |
264 | | /// read point numbers off the front of this data, returning the remaining data |
265 | 2.96k | pub fn split_off_front(data: FontData<'a>) -> (Self, FontData<'a>) { |
266 | 2.96k | let this = PackedPointNumbers { data }; |
267 | 2.96k | let total_len = this.total_len(); |
268 | 2.96k | let remainder = data.split_off(total_len).unwrap_or_default(); |
269 | 2.96k | (this, remainder) |
270 | 2.96k | } |
271 | | |
272 | | /// The number of points in this set |
273 | 2.94k | pub fn count(&self) -> u16 { |
274 | 2.94k | self.count_and_count_bytes().0 |
275 | 2.94k | } |
276 | | |
277 | | /// compute the count, and the number of bytes used to store it |
278 | 8.13k | fn count_and_count_bytes(&self) -> (u16, usize) { |
279 | 8.13k | match self.data.read_at::<u8>(0).unwrap_or(0) { |
280 | 2.92k | 0 => (0, 1), |
281 | 5.20k | count @ 1..=127 => (count as u16, 1), |
282 | | _ => { |
283 | | // "If the high bit of the first byte is set, then a second byte is used. |
284 | | // The count is read from interpreting the two bytes as a big-endian |
285 | | // uint16 value with the high-order bit masked out." |
286 | | |
287 | 586 | let count = self.data.read_at::<u16>(0).unwrap_or_default() & 0x7FFF; |
288 | | // a weird case where I'm following fonttools: if the 'use words' bit |
289 | | // is set, but the total count is still 0, treat it like 0 first byte |
290 | 586 | if count == 0 { |
291 | 206 | (0, 2) |
292 | | } else { |
293 | 380 | (count & 0x7FFF, 2) |
294 | | } |
295 | | } |
296 | | } |
297 | 8.13k | } |
298 | | |
299 | | /// the number of bytes to encode the packed point numbers |
300 | | #[inline(never)] |
301 | 2.96k | fn total_len(&self) -> usize { |
302 | 2.96k | let (n_points, mut n_bytes) = self.count_and_count_bytes(); |
303 | 2.96k | if n_points == 0 { |
304 | 1.44k | return n_bytes; |
305 | 1.51k | } |
306 | 1.51k | let mut cursor = self.data.cursor(); |
307 | 1.51k | cursor.advance_by(n_bytes); |
308 | | |
309 | 1.51k | let mut n_seen = 0; |
310 | 5.96k | while n_seen < n_points { |
311 | 4.66k | let Some((count, two_bytes)) = read_control_byte(&mut cursor) else { |
312 | 206 | return n_bytes; |
313 | | }; |
314 | 4.45k | let word_size = 1 + usize::from(two_bytes); |
315 | 4.45k | let run_size = word_size * count as usize; |
316 | 4.45k | n_bytes += run_size + 1; // plus the control byte; |
317 | 4.45k | cursor.advance_by(run_size); |
318 | 4.45k | n_seen += count as u16; |
319 | | } |
320 | | |
321 | 1.30k | n_bytes |
322 | 2.96k | } |
323 | | |
324 | | /// Iterate over the packed points |
325 | 2.23k | pub fn iter(&self) -> PackedPointNumbersIter<'a> { |
326 | 2.23k | let (count, n_bytes) = self.count_and_count_bytes(); |
327 | 2.23k | let mut cursor = self.data.cursor(); |
328 | 2.23k | cursor.advance_by(n_bytes); |
329 | 2.23k | PackedPointNumbersIter::new(count, cursor) |
330 | 2.23k | } |
331 | | } |
332 | | |
333 | | /// An iterator over the packed point numbers data. |
334 | | #[derive(Clone, Debug)] |
335 | | pub struct PackedPointNumbersIter<'a> { |
336 | | count: u16, |
337 | | seen: u16, |
338 | | last_val: u16, |
339 | | current_run: PointRunIter<'a>, |
340 | | } |
341 | | |
342 | | impl<'a> PackedPointNumbersIter<'a> { |
343 | 2.23k | fn new(count: u16, cursor: Cursor<'a>) -> Self { |
344 | 2.23k | PackedPointNumbersIter { |
345 | 2.23k | count, |
346 | 2.23k | seen: 0, |
347 | 2.23k | last_val: 0, |
348 | 2.23k | current_run: PointRunIter { |
349 | 2.23k | remaining: 0, |
350 | 2.23k | two_bytes: false, |
351 | 2.23k | cursor, |
352 | 2.23k | }, |
353 | 2.23k | } |
354 | 2.23k | } |
355 | | } |
356 | | |
357 | | /// Implements the logic for iterating over the individual runs |
358 | | #[derive(Clone, Debug)] |
359 | | struct PointRunIter<'a> { |
360 | | remaining: u8, |
361 | | two_bytes: bool, |
362 | | cursor: Cursor<'a>, |
363 | | } |
364 | | |
365 | | impl Iterator for PointRunIter<'_> { |
366 | | type Item = u16; |
367 | | |
368 | 25.9k | fn next(&mut self) -> Option<Self::Item> { |
369 | | // if no items remain in this run, start the next one. |
370 | 29.9k | while self.remaining == 0 { |
371 | 4.09k | (self.remaining, self.two_bytes) = read_control_byte(&mut self.cursor)?; |
372 | | } |
373 | | |
374 | 25.8k | self.remaining -= 1; |
375 | 25.8k | if self.two_bytes { |
376 | 1.34k | self.cursor.read().ok() |
377 | | } else { |
378 | 24.5k | self.cursor.read::<u8>().ok().map(|v| v as u16) |
379 | | } |
380 | 25.9k | } |
381 | | } |
382 | | |
383 | | /// returns the count and the 'uses_two_bytes' flag from the control byte |
384 | 8.75k | fn read_control_byte(cursor: &mut Cursor) -> Option<(u8, bool)> { |
385 | 8.75k | let control: u8 = cursor.read().ok()?; |
386 | 8.49k | let two_bytes = (control & 0x80) != 0; |
387 | 8.49k | let count = (control & 0x7F) + 1; |
388 | 8.49k | Some((count, two_bytes)) |
389 | 8.75k | } |
390 | | |
391 | | impl Iterator for PackedPointNumbersIter<'_> { |
392 | | type Item = u16; |
393 | | |
394 | 99.2k | fn next(&mut self) -> Option<Self::Item> { |
395 | | // if our count is zero, we keep incrementing forever |
396 | 99.2k | if self.count == 0 { |
397 | 72.7k | let result = self.last_val; |
398 | 72.7k | self.last_val = self.last_val.checked_add(1)?; |
399 | 72.7k | return Some(result); |
400 | 26.4k | } |
401 | | |
402 | 26.4k | if self.count == self.seen { |
403 | 576 | return None; |
404 | 25.9k | } |
405 | 25.9k | self.seen += 1; |
406 | 25.9k | self.last_val = self.last_val.checked_add(self.current_run.next()?)?; |
407 | 25.4k | Some(self.last_val) |
408 | 99.2k | } |
409 | | |
410 | 0 | fn size_hint(&self) -> (usize, Option<usize>) { |
411 | 0 | (self.count as usize, Some(self.count as usize)) |
412 | 0 | } |
413 | | } |
414 | | |
415 | | // completely unnecessary? |
416 | | impl ExactSizeIterator for PackedPointNumbersIter<'_> {} |
417 | | |
418 | | /// [Packed Deltas](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas) |
419 | | #[derive(Clone, Debug)] |
420 | | pub struct PackedDeltas<'a> { |
421 | | data: FontData<'a>, |
422 | | // How many values we expect |
423 | | count: Option<usize>, |
424 | | } |
425 | | |
426 | | impl<'a> PackedDeltas<'a> { |
427 | 308 | pub(crate) fn new(data: FontData<'a>, count: usize) -> Self { |
428 | 308 | Self { |
429 | 308 | data, |
430 | 308 | count: Some(count), |
431 | 308 | } |
432 | 308 | } |
433 | | |
434 | | /// NOTE: this is unbounded, and assumes all of data is deltas. |
435 | | #[doc(hidden)] // used by tests in write-fonts |
436 | 553 | pub fn consume_all(data: FontData<'a>) -> Self { |
437 | 553 | Self { data, count: None } |
438 | 553 | } |
439 | | |
440 | 0 | pub fn count(&self) -> Option<usize> { |
441 | 0 | self.count |
442 | 0 | } |
443 | | |
444 | 1.72k | pub fn count_or_compute(&self) -> usize { |
445 | 1.72k | self.count.unwrap_or_else(|| count_all_deltas(self.data)) |
446 | 1.72k | } |
447 | | |
448 | 0 | pub fn iter(&self) -> DeltaRunIter<'a> { |
449 | 0 | DeltaRunIter::new(self.data.cursor(), self.count) |
450 | 0 | } |
451 | | |
452 | 0 | pub fn fetcher(&self) -> PackedDeltaFetcher<'a> { |
453 | 0 | PackedDeltaFetcher::new(self.data.as_bytes(), self.count) |
454 | 0 | } |
455 | | |
456 | 861 | fn x_deltas(&self) -> DeltaRunIter<'a> { |
457 | 861 | let count = self.count_or_compute() / 2; |
458 | 861 | DeltaRunIter::new(self.data.cursor(), Some(count)) |
459 | 861 | } |
460 | | |
461 | 861 | fn y_deltas(&self) -> DeltaRunIter<'a> { |
462 | 861 | let count = self.count_or_compute(); |
463 | 861 | DeltaRunIter::new(self.data.cursor(), Some(count)).skip_fast(count / 2) |
464 | 861 | } |
465 | | } |
466 | | |
467 | | /// Flag indicating that this run contains no data, |
468 | | /// and that the deltas for this run are all zero. |
469 | | const DELTAS_ARE_ZERO: u8 = 0x80; |
470 | | /// Flag indicating the data type for delta values in the run. |
471 | | const DELTAS_ARE_WORDS: u8 = 0x40; |
472 | | /// Mask for the low 6 bits to provide the number of delta values in the run, minus one. |
473 | | const DELTA_RUN_COUNT_MASK: u8 = 0x3F; |
474 | | |
475 | | /// The type of values for a given delta run (influences the number of bytes per delta) |
476 | | /// |
477 | | /// The variants are intentionally set to the byte size of the type to allow usage |
478 | | /// as a multiplier when computing offsets. |
479 | | #[derive(Clone, Copy, Debug, PartialEq)] |
480 | | pub enum DeltaRunType { |
481 | | Zero = 0, |
482 | | I8 = 1, |
483 | | I16 = 2, |
484 | | I32 = 4, |
485 | | } |
486 | | |
487 | | impl DeltaRunType { |
488 | | /// The run type for a given control byte |
489 | 26.4k | pub fn new(control: u8) -> Self { |
490 | | // if the top two bits of the control byte (DELTAS_ARE_ZERO and DELTAS_ARE_WORDS) are both set, |
491 | | // then the following values are 32-bit. |
492 | | // <https://github.com/harfbuzz/boring-expansion-spec/blob/main/VARC.md#tuplevalues> |
493 | 26.4k | let are_zero = (control & DELTAS_ARE_ZERO) != 0; |
494 | 26.4k | let are_words = (control & DELTAS_ARE_WORDS) != 0; |
495 | 26.4k | match (are_zero, are_words) { |
496 | 13.0k | (false, false) => Self::I8, |
497 | 1.71k | (false, true) => Self::I16, |
498 | 10.3k | (true, false) => Self::Zero, |
499 | 1.34k | (true, true) => Self::I32, |
500 | | } |
501 | 26.4k | } |
502 | | } |
503 | | |
504 | | /// Implements the logic for iterating over the individual runs |
505 | | #[derive(Clone, Debug)] |
506 | | pub struct DeltaRunIter<'a> { |
507 | | limit: Option<usize>, // when None, consume all available data |
508 | | remaining_in_run: u8, |
509 | | value_type: DeltaRunType, |
510 | | cursor: Cursor<'a>, |
511 | | } |
512 | | |
513 | | /// A decoding helper that adds packed deltas directly to an output slice. |
514 | | pub struct PackedDeltaFetcher<'a> { |
515 | | data: &'a [u8], |
516 | | pos: usize, |
517 | | end: usize, |
518 | | run_count: usize, |
519 | | value_type: DeltaRunType, |
520 | | remaining_total: Option<usize>, |
521 | | } |
522 | | |
523 | | impl<'a> PackedDeltaFetcher<'a> { |
524 | 0 | fn new(data: &'a [u8], count: Option<usize>) -> Self { |
525 | 0 | Self { |
526 | 0 | data, |
527 | 0 | pos: 0, |
528 | 0 | end: data.len(), |
529 | 0 | run_count: 0, |
530 | 0 | value_type: DeltaRunType::I8, |
531 | 0 | remaining_total: count, |
532 | 0 | } |
533 | 0 | } |
534 | | |
535 | | #[inline(always)] |
536 | 0 | fn ensure_run(&mut self) -> Result<(), ReadError> { |
537 | 0 | if self.run_count > 0 { |
538 | 0 | return Ok(()); |
539 | 0 | } |
540 | 0 | if self.pos >= self.end { |
541 | 0 | return Err(ReadError::OutOfBounds); |
542 | 0 | } |
543 | 0 | let control = self.data[self.pos]; |
544 | 0 | self.pos += 1; |
545 | 0 | self.run_count = (control & DELTA_RUN_COUNT_MASK) as usize + 1; |
546 | 0 | self.value_type = DeltaRunType::new(control); |
547 | 0 | let width = self.value_type as usize; |
548 | 0 | let needed = self.run_count * width; |
549 | 0 | if self.pos + needed > self.end { |
550 | 0 | return Err(ReadError::OutOfBounds); |
551 | 0 | } |
552 | 0 | Ok(()) |
553 | 0 | } |
554 | | |
555 | 0 | pub fn skip(&mut self, mut n: usize) -> Result<(), ReadError> { |
556 | 0 | if let Some(remaining_total) = self.remaining_total { |
557 | 0 | if n > remaining_total { |
558 | 0 | return Err(ReadError::OutOfBounds); |
559 | 0 | } |
560 | 0 | self.remaining_total = Some(remaining_total - n); |
561 | 0 | } |
562 | 0 | while n > 0 { |
563 | 0 | self.ensure_run()?; |
564 | 0 | let take = n.min(self.run_count); |
565 | 0 | let width = self.value_type as usize; |
566 | 0 | self.pos += take * width; |
567 | 0 | self.run_count -= take; |
568 | 0 | n -= take; |
569 | | } |
570 | 0 | Ok(()) |
571 | 0 | } |
572 | | |
573 | 0 | pub fn add_to_f32_scaled(&mut self, out: &mut [f32], scale: f32) -> Result<(), ReadError> { |
574 | 0 | self.add_to_scaled(out, scale, |delta| delta as f32) |
575 | 0 | } |
576 | | |
577 | | /// Adds packed deltas without losing precision from 32-bit integer values. |
578 | 0 | pub fn add_to_f64_scaled(&mut self, out: &mut [f64], scale: f64) -> Result<(), ReadError> { |
579 | 0 | self.add_to_scaled(out, scale, |delta| delta as f64) |
580 | 0 | } |
581 | | |
582 | 0 | fn add_to_scaled<T>( |
583 | 0 | &mut self, |
584 | 0 | out: &mut [T], |
585 | 0 | scale: T, |
586 | 0 | convert: impl Fn(i32) -> T, |
587 | 0 | ) -> Result<(), ReadError> |
588 | 0 | where |
589 | 0 | T: Copy + core::ops::AddAssign + core::ops::Mul<Output = T>, |
590 | | { |
591 | 0 | let mut remaining = out.len(); |
592 | 0 | if let Some(remaining_total) = self.remaining_total { |
593 | 0 | if remaining > remaining_total { |
594 | 0 | return Err(ReadError::OutOfBounds); |
595 | 0 | } |
596 | 0 | self.remaining_total = Some(remaining_total - remaining); |
597 | 0 | } |
598 | 0 | let mut idx = 0usize; |
599 | 0 | while remaining > 0 { |
600 | 0 | self.ensure_run()?; |
601 | 0 | let take = remaining.min(self.run_count); |
602 | 0 | match self.value_type { |
603 | 0 | DeltaRunType::Zero => { |
604 | 0 | // nothing to add |
605 | 0 | idx += take; |
606 | 0 | } |
607 | | DeltaRunType::I8 => { |
608 | 0 | let bytes = &self.data[self.pos..self.pos + take]; |
609 | 0 | for &b in bytes { |
610 | 0 | out[idx] += convert(b as i8 as i32) * scale; |
611 | 0 | idx += 1; |
612 | 0 | } |
613 | 0 | self.pos += take; |
614 | | } |
615 | | DeltaRunType::I16 => { |
616 | 0 | let bytes = &self.data[self.pos..self.pos + take * 2]; |
617 | 0 | for chunk in bytes.chunks_exact(2) { |
618 | 0 | let delta = i16::from_be_bytes([chunk[0], chunk[1]]) as i32; |
619 | 0 | out[idx] += convert(delta) * scale; |
620 | 0 | idx += 1; |
621 | 0 | } |
622 | 0 | self.pos += take * 2; |
623 | | } |
624 | | DeltaRunType::I32 => { |
625 | 0 | let bytes = &self.data[self.pos..self.pos + take * 4]; |
626 | 0 | for chunk in bytes.chunks_exact(4) { |
627 | 0 | let delta = i32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); |
628 | 0 | out[idx] += convert(delta) * scale; |
629 | 0 | idx += 1; |
630 | 0 | } |
631 | 0 | self.pos += take * 4; |
632 | | } |
633 | | } |
634 | 0 | self.run_count -= take; |
635 | 0 | remaining -= take; |
636 | | } |
637 | 0 | Ok(()) |
638 | 0 | } Unexecuted instantiation: <read_fonts::tables::variations::PackedDeltaFetcher>::add_to_scaled::<f64, <read_fonts::tables::variations::PackedDeltaFetcher>::add_to_f64_scaled::{closure#0}>Unexecuted instantiation: <read_fonts::tables::variations::PackedDeltaFetcher>::add_to_scaled::<f32, <read_fonts::tables::variations::PackedDeltaFetcher>::add_to_f32_scaled::{closure#0}> |
639 | | } |
640 | | |
641 | | /// Counts the number of deltas available in the given data, avoiding |
642 | | /// excessive reads. |
643 | 1.10k | fn count_all_deltas(data: FontData) -> usize { |
644 | 1.10k | let mut count = 0; |
645 | 1.10k | let mut offset = 0; |
646 | 12.1k | while let Ok(control) = data.read_at::<u8>(offset) { |
647 | 11.0k | let run_count = (control & DELTA_RUN_COUNT_MASK) as usize + 1; |
648 | 11.0k | count += run_count; |
649 | 11.0k | offset += run_count * DeltaRunType::new(control) as usize + 1; |
650 | 11.0k | } |
651 | 1.10k | count |
652 | 1.10k | } |
653 | | |
654 | | impl<'a> DeltaRunIter<'a> { |
655 | 1.72k | fn new(cursor: Cursor<'a>, limit: Option<usize>) -> Self { |
656 | 1.72k | DeltaRunIter { |
657 | 1.72k | limit, |
658 | 1.72k | remaining_in_run: 0, |
659 | 1.72k | value_type: DeltaRunType::I8, |
660 | 1.72k | cursor, |
661 | 1.72k | } |
662 | 1.72k | } |
663 | | |
664 | 0 | pub(crate) fn end(mut self) -> Cursor<'a> { |
665 | 0 | if let Some(limit) = self.limit { |
666 | 0 | return self.skip_fast(limit).cursor; |
667 | 0 | } |
668 | | // No limit: jump over runs without decoding values. |
669 | 0 | if self.remaining_in_run != 0 { |
670 | 0 | if self.value_type != DeltaRunType::Zero { |
671 | 0 | self.cursor |
672 | 0 | .advance_by(self.remaining_in_run as usize * self.value_type as usize); |
673 | 0 | } |
674 | 0 | self.remaining_in_run = 0; |
675 | 0 | } |
676 | 0 | while self.read_next_control().is_some() { |
677 | 0 | if self.value_type != DeltaRunType::Zero { |
678 | 0 | self.cursor |
679 | 0 | .advance_by(self.remaining_in_run as usize * self.value_type as usize); |
680 | 0 | } |
681 | 0 | self.remaining_in_run = 0; |
682 | | } |
683 | 0 | self.cursor |
684 | 0 | } |
685 | | |
686 | | /// Skips `n` deltas without reading the actual delta values. |
687 | | #[inline(always)] |
688 | 861 | pub fn skip_fast(mut self, n: usize) -> Self { |
689 | 861 | let mut wanted = n; |
690 | 861 | let mut remaining = self.remaining_in_run as usize; |
691 | 861 | let mut value_type = self.value_type; |
692 | | loop { |
693 | 4.78k | if wanted > remaining { |
694 | | // Consume the rest of this run and move to the next. |
695 | 4.09k | self.cursor.advance_by(remaining * value_type as usize); |
696 | 4.09k | wanted -= remaining; |
697 | 4.09k | if self.read_next_control().is_none() { |
698 | 165 | self.limit = Some(0); |
699 | 165 | break; |
700 | 3.92k | } |
701 | 3.92k | remaining = self.remaining_in_run as usize; |
702 | 3.92k | value_type = self.value_type; |
703 | 3.92k | continue; |
704 | 696 | } |
705 | 696 | let consumed = wanted; |
706 | 696 | self.remaining_in_run -= consumed as u8; |
707 | 696 | self.cursor.advance_by(consumed * value_type as usize); |
708 | 696 | if let Some(limit) = self.limit.as_mut() { |
709 | 696 | *limit = limit.saturating_sub(n); |
710 | 696 | } |
711 | 696 | break; |
712 | | } |
713 | 861 | self |
714 | 861 | } |
715 | | |
716 | | #[inline(always)] |
717 | 9.46k | fn read_next_control(&mut self) -> Option<()> { |
718 | 9.46k | self.remaining_in_run = 0; |
719 | 9.46k | let control: u8 = self.cursor.read().ok()?; |
720 | 9.16k | self.value_type = DeltaRunType::new(control); |
721 | 9.16k | self.remaining_in_run = (control & DELTA_RUN_COUNT_MASK) + 1; |
722 | 9.16k | Some(()) |
723 | 9.46k | } |
724 | | } |
725 | | |
726 | | impl Iterator for DeltaRunIter<'_> { |
727 | | type Item = i32; |
728 | | |
729 | | #[inline(always)] |
730 | 147k | fn next(&mut self) -> Option<Self::Item> { |
731 | 147k | if let Some(limit) = self.limit { |
732 | 147k | if limit == 0 { |
733 | 187 | return None; |
734 | 147k | } |
735 | 147k | self.limit = Some(limit - 1); |
736 | 0 | } |
737 | 147k | if self.remaining_in_run == 0 { |
738 | 5.37k | self.read_next_control()?; |
739 | 141k | } |
740 | 146k | self.remaining_in_run -= 1; |
741 | 146k | match self.value_type { |
742 | 128k | DeltaRunType::Zero => Some(0), |
743 | 7.65k | DeltaRunType::I8 => self.cursor.read::<i8>().ok().map(|v| v as i32), |
744 | 6.92k | DeltaRunType::I16 => self.cursor.read::<i16>().ok().map(|v| v as i32), |
745 | 4.30k | DeltaRunType::I32 => self.cursor.read::<i32>().ok(), |
746 | | } |
747 | 147k | } |
748 | | } |
749 | | |
750 | | /// A helper type for iterating over [`TupleVariationHeader`]s. |
751 | | pub struct TupleVariationHeaderIter<'a> { |
752 | | data: FontData<'a>, |
753 | | n_headers: usize, |
754 | | current: usize, |
755 | | axis_count: u16, |
756 | | } |
757 | | |
758 | | impl<'a> TupleVariationHeaderIter<'a> { |
759 | 2.51k | pub(crate) fn new(data: FontData<'a>, n_headers: usize, axis_count: u16) -> Self { |
760 | 2.51k | Self { |
761 | 2.51k | data, |
762 | 2.51k | n_headers, |
763 | 2.51k | current: 0, |
764 | 2.51k | axis_count, |
765 | 2.51k | } |
766 | 2.51k | } |
767 | | } |
768 | | |
769 | | impl<'a> Iterator for TupleVariationHeaderIter<'a> { |
770 | | type Item = Result<TupleVariationHeader<'a>, ReadError>; |
771 | | |
772 | | #[inline(always)] |
773 | 27.4k | fn next(&mut self) -> Option<Self::Item> { |
774 | 27.4k | if self.current == self.n_headers { |
775 | 140 | return None; |
776 | 27.3k | } |
777 | 27.3k | self.current += 1; |
778 | 27.3k | let next = TupleVariationHeader::read(self.data, self.axis_count); |
779 | | |
780 | 27.3k | let next_len = next |
781 | 27.3k | .as_ref() |
782 | 27.3k | .map(|table| table.byte_len(self.axis_count)) |
783 | 27.3k | .unwrap_or(0); |
784 | 27.3k | self.data = self.data.split_off(next_len)?; |
785 | 26.9k | Some(next) |
786 | 27.4k | } |
787 | | } |
788 | | |
789 | | #[derive(Clone)] |
790 | | pub struct TupleVariationData<'a, T> { |
791 | | pub(crate) axis_count: u16, |
792 | | pub(crate) shared_tuples: Option<ComputedArray<'a, Tuple<'a>>>, |
793 | | pub(crate) shared_point_numbers: Option<PackedPointNumbers<'a>>, |
794 | | pub(crate) tuple_count: TupleVariationCount, |
795 | | // the data for all the tuple variation headers |
796 | | pub(crate) header_data: FontData<'a>, |
797 | | // the data for all the tuple bodies |
798 | | pub(crate) serialized_data: FontData<'a>, |
799 | | pub(crate) _marker: std::marker::PhantomData<fn() -> T>, |
800 | | } |
801 | | |
802 | | impl<'a, T> TupleVariationData<'a, T> |
803 | | where |
804 | | T: TupleDelta, |
805 | | { |
806 | 0 | pub fn tuples(&self) -> TupleVariationIter<'a, T> { |
807 | 0 | TupleVariationIter { |
808 | 0 | current: 0, |
809 | 0 | parent: self.clone(), |
810 | 0 | header_iter: TupleVariationHeaderIter::new( |
811 | 0 | self.header_data, |
812 | 0 | self.tuple_count.count() as usize, |
813 | 0 | self.axis_count, |
814 | 0 | ), |
815 | 0 | serialized_data: self.serialized_data, |
816 | 0 | _marker: std::marker::PhantomData, |
817 | 0 | } |
818 | 0 | } |
819 | | |
820 | | /// Returns an iterator over all of the pairs of (variation tuple, scalar) |
821 | | /// for this glyph that are active for the given set of normalized |
822 | | /// coordinates. |
823 | 0 | pub fn active_tuples_at<'b>( |
824 | 0 | &self, |
825 | 0 | coords: &'b [F2Dot14], |
826 | 0 | ) -> impl Iterator<Item = (TupleVariation<'a, T>, Fixed)> + 'b |
827 | 0 | where |
828 | 0 | 'a: 'b, |
829 | | { |
830 | 0 | self.active_tuples_at_with_scalars(coords, &[]) |
831 | 0 | } |
832 | | |
833 | | /// Returns an iterator over the tuples that apply at `coords`, reusing |
834 | | /// `scalars`. |
835 | | /// |
836 | | /// `scalars` holds one entry per shared tuple, from |
837 | | /// [`Gvar::compute_scalars`]. A tuple that names a shared peak and no |
838 | | /// range of its own has the same scalar everywhere, so it is read from |
839 | | /// there. A tuple with its own range, or one `scalars` does not reach, is |
840 | | /// computed here. |
841 | | /// |
842 | | /// [`Gvar::compute_scalars`]: crate::tables::gvar::Gvar::compute_scalars |
843 | 2.51k | pub fn active_tuples_at_with_scalars<'b>( |
844 | 2.51k | &self, |
845 | 2.51k | coords: &'b [F2Dot14], |
846 | 2.51k | scalars: &'b [Fixed], |
847 | 2.51k | ) -> impl Iterator<Item = (TupleVariation<'a, T>, Fixed)> + 'b |
848 | 2.51k | where |
849 | 2.51k | 'a: 'b, |
850 | | { |
851 | 2.51k | ActiveTupleVariationIter { |
852 | 2.51k | coords, |
853 | 2.51k | scalars, |
854 | 2.51k | parent: self.clone(), |
855 | 2.51k | header_iter: TupleVariationHeaderIter::new( |
856 | 2.51k | self.header_data, |
857 | 2.51k | self.tuple_count.count() as usize, |
858 | 2.51k | self.axis_count, |
859 | 2.51k | ), |
860 | 2.51k | serialized_data: self.serialized_data, |
861 | 2.51k | data_offset: 0, |
862 | 2.51k | _marker: std::marker::PhantomData, |
863 | 2.51k | } |
864 | 2.51k | } Unexecuted instantiation: <read_fonts::tables::variations::TupleVariationData<read_fonts::tables::cvar::CvtDelta>>::active_tuples_at_with_scalars <read_fonts::tables::variations::TupleVariationData<read_fonts::tables::gvar::GlyphDelta>>::active_tuples_at_with_scalars Line | Count | Source | 843 | 2.51k | pub fn active_tuples_at_with_scalars<'b>( | 844 | 2.51k | &self, | 845 | 2.51k | coords: &'b [F2Dot14], | 846 | 2.51k | scalars: &'b [Fixed], | 847 | 2.51k | ) -> impl Iterator<Item = (TupleVariation<'a, T>, Fixed)> + 'b | 848 | 2.51k | where | 849 | 2.51k | 'a: 'b, | 850 | | { | 851 | 2.51k | ActiveTupleVariationIter { | 852 | 2.51k | coords, | 853 | 2.51k | scalars, | 854 | 2.51k | parent: self.clone(), | 855 | 2.51k | header_iter: TupleVariationHeaderIter::new( | 856 | 2.51k | self.header_data, | 857 | 2.51k | self.tuple_count.count() as usize, | 858 | 2.51k | self.axis_count, | 859 | 2.51k | ), | 860 | 2.51k | serialized_data: self.serialized_data, | 861 | 2.51k | data_offset: 0, | 862 | 2.51k | _marker: std::marker::PhantomData, | 863 | 2.51k | } | 864 | 2.51k | } |
|
865 | | |
866 | 0 | pub(crate) fn tuple_count(&self) -> usize { |
867 | 0 | self.tuple_count.count() as usize |
868 | 0 | } |
869 | | } |
870 | | |
871 | | /// An iterator over the [`TupleVariation`]s for a specific glyph. |
872 | | pub struct TupleVariationIter<'a, T> { |
873 | | current: usize, |
874 | | parent: TupleVariationData<'a, T>, |
875 | | header_iter: TupleVariationHeaderIter<'a>, |
876 | | serialized_data: FontData<'a>, |
877 | | _marker: std::marker::PhantomData<fn() -> T>, |
878 | | } |
879 | | |
880 | | impl<'a, T> TupleVariationIter<'a, T> |
881 | | where |
882 | | T: TupleDelta, |
883 | | { |
884 | | #[inline(always)] |
885 | 0 | fn next_tuple(&mut self) -> Option<TupleVariation<'a, T>> { |
886 | 0 | if self.parent.tuple_count() == self.current { |
887 | 0 | return None; |
888 | 0 | } |
889 | 0 | self.current += 1; |
890 | | |
891 | | // FIXME: is it okay to discard an error here? |
892 | 0 | let header = self.header_iter.next()?.ok()?; |
893 | 0 | let data_len = header.variation_data_size() as usize; |
894 | 0 | let var_data = self.serialized_data.take_up_to(data_len)?; |
895 | | |
896 | 0 | Some(TupleVariation { |
897 | 0 | axis_count: self.parent.axis_count, |
898 | 0 | header, |
899 | 0 | shared_tuples: self.parent.shared_tuples.clone(), |
900 | 0 | serialized_data: var_data, |
901 | 0 | shared_point_numbers: self.parent.shared_point_numbers.clone(), |
902 | 0 | _marker: std::marker::PhantomData, |
903 | 0 | }) |
904 | 0 | } |
905 | | } |
906 | | |
907 | | impl<'a, T> Iterator for TupleVariationIter<'a, T> |
908 | | where |
909 | | T: TupleDelta, |
910 | | { |
911 | | type Item = TupleVariation<'a, T>; |
912 | | |
913 | | #[inline(always)] |
914 | 0 | fn next(&mut self) -> Option<Self::Item> { |
915 | 0 | self.next_tuple() |
916 | 0 | } |
917 | | } |
918 | | |
919 | | /// An iterator over the active [`TupleVariation`]s for a specific glyph |
920 | | /// for a given set of coordinates. |
921 | | struct ActiveTupleVariationIter<'a, 'b, T> { |
922 | | coords: &'b [F2Dot14], |
923 | | scalars: &'b [Fixed], |
924 | | parent: TupleVariationData<'a, T>, |
925 | | header_iter: TupleVariationHeaderIter<'a>, |
926 | | serialized_data: FontData<'a>, |
927 | | data_offset: usize, |
928 | | _marker: std::marker::PhantomData<fn() -> T>, |
929 | | } |
930 | | |
931 | | impl<'a, T> Iterator for ActiveTupleVariationIter<'a, '_, T> |
932 | | where |
933 | | T: TupleDelta, |
934 | | { |
935 | | type Item = (TupleVariation<'a, T>, Fixed); |
936 | | |
937 | | #[inline(always)] |
938 | 3.69k | fn next(&mut self) -> Option<Self::Item> { |
939 | | loop { |
940 | 27.4k | let header = self.header_iter.next()?.ok()?; |
941 | 26.7k | let data_len = header.variation_data_size() as usize; |
942 | 26.7k | let data_start = self.data_offset; |
943 | 26.7k | let data_end = data_start.checked_add(data_len)?; |
944 | 26.7k | self.data_offset = data_end; |
945 | 26.7k | if let Some(scalar) = compute_scalar( |
946 | 26.7k | &header, |
947 | 26.7k | self.parent.axis_count as usize, |
948 | 26.7k | &self.parent.shared_tuples, |
949 | 26.7k | self.coords, |
950 | 26.7k | self.scalars, |
951 | 26.7k | ) { |
952 | 2.98k | let var_data = self.serialized_data.slice(data_start..data_end)?; |
953 | 2.08k | return Some(( |
954 | 2.08k | TupleVariation { |
955 | 2.08k | axis_count: self.parent.axis_count, |
956 | 2.08k | header, |
957 | 2.08k | shared_tuples: self.parent.shared_tuples.clone(), |
958 | 2.08k | serialized_data: var_data, |
959 | 2.08k | shared_point_numbers: self.parent.shared_point_numbers.clone(), |
960 | 2.08k | _marker: std::marker::PhantomData, |
961 | 2.08k | }, |
962 | 2.08k | scalar, |
963 | 2.08k | )); |
964 | 23.7k | } |
965 | | } |
966 | 3.69k | } Unexecuted instantiation: <read_fonts::tables::variations::ActiveTupleVariationIter<read_fonts::tables::cvar::CvtDelta> as core::iter::traits::iterator::Iterator>::next <read_fonts::tables::variations::ActiveTupleVariationIter<read_fonts::tables::gvar::GlyphDelta> as core::iter::traits::iterator::Iterator>::next Line | Count | Source | 938 | 3.69k | fn next(&mut self) -> Option<Self::Item> { | 939 | | loop { | 940 | 27.4k | let header = self.header_iter.next()?.ok()?; | 941 | 26.7k | let data_len = header.variation_data_size() as usize; | 942 | 26.7k | let data_start = self.data_offset; | 943 | 26.7k | let data_end = data_start.checked_add(data_len)?; | 944 | 26.7k | self.data_offset = data_end; | 945 | 26.7k | if let Some(scalar) = compute_scalar( | 946 | 26.7k | &header, | 947 | 26.7k | self.parent.axis_count as usize, | 948 | 26.7k | &self.parent.shared_tuples, | 949 | 26.7k | self.coords, | 950 | 26.7k | self.scalars, | 951 | 26.7k | ) { | 952 | 2.98k | let var_data = self.serialized_data.slice(data_start..data_end)?; | 953 | 2.08k | return Some(( | 954 | 2.08k | TupleVariation { | 955 | 2.08k | axis_count: self.parent.axis_count, | 956 | 2.08k | header, | 957 | 2.08k | shared_tuples: self.parent.shared_tuples.clone(), | 958 | 2.08k | serialized_data: var_data, | 959 | 2.08k | shared_point_numbers: self.parent.shared_point_numbers.clone(), | 960 | 2.08k | _marker: std::marker::PhantomData, | 961 | 2.08k | }, | 962 | 2.08k | scalar, | 963 | 2.08k | )); | 964 | 23.7k | } | 965 | | } | 966 | 3.69k | } |
|
967 | | } |
968 | | |
969 | | /// Marks a shared tuple scalar that was never computed. |
970 | | /// |
971 | | /// A glyph names shared tuples by index and need not name them all, so a |
972 | | /// slice of scalars can carry gaps. Variation store regions are dense and |
973 | | /// need no such mark. |
974 | | /// |
975 | | /// No computed scalar can equal this: a scalar is a product of ratios, each |
976 | | /// held between zero and one. |
977 | | pub const NOT_COMPUTED: Fixed = Fixed::MAX; |
978 | | |
979 | | /// A single set of tuple variation data |
980 | | #[derive(Clone)] |
981 | | pub struct TupleVariation<'a, T> { |
982 | | axis_count: u16, |
983 | | header: TupleVariationHeader<'a>, |
984 | | shared_tuples: Option<ComputedArray<'a, Tuple<'a>>>, |
985 | | serialized_data: FontData<'a>, |
986 | | shared_point_numbers: Option<PackedPointNumbers<'a>>, |
987 | | _marker: std::marker::PhantomData<fn() -> T>, |
988 | | } |
989 | | |
990 | | impl<'a, T> TupleVariation<'a, T> |
991 | | where |
992 | | T: TupleDelta, |
993 | | { |
994 | | /// Returns true if this tuple provides deltas for all points in a glyph. |
995 | 1.36k | pub fn has_deltas_for_all_points(&self) -> bool { |
996 | 1.36k | if self.header.tuple_index().private_point_numbers() { |
997 | 890 | PackedPointNumbers { |
998 | 890 | data: self.serialized_data, |
999 | 890 | } |
1000 | 890 | .count() |
1001 | 890 | == 0 |
1002 | 470 | } else if let Some(shared) = &self.shared_point_numbers { |
1003 | 335 | shared.count() == 0 |
1004 | | } else { |
1005 | 135 | false |
1006 | | } |
1007 | 1.36k | } |
1008 | | |
1009 | 0 | pub fn point_numbers(&self) -> PackedPointNumbersIter<'a> { |
1010 | 0 | let (point_numbers, _) = self.point_numbers_and_packed_deltas(); |
1011 | 0 | point_numbers.iter() |
1012 | 0 | } |
1013 | | |
1014 | | /// Returns the 'peak' tuple for this variation |
1015 | 0 | pub fn peak(&self) -> Tuple<'a> { |
1016 | 0 | self.header |
1017 | 0 | .tuple_index() |
1018 | 0 | .tuple_records_index() |
1019 | 0 | .and_then(|idx| self.shared_tuples.as_ref()?.get(idx as usize).ok()) |
1020 | 0 | .or_else(|| self.header.peak_tuple()) |
1021 | 0 | .unwrap_or_default() |
1022 | 0 | } |
1023 | | |
1024 | 0 | pub fn intermediate_start(&self) -> Option<Tuple<'a>> { |
1025 | 0 | self.header.intermediate_start_tuple() |
1026 | 0 | } |
1027 | | |
1028 | 0 | pub fn intermediate_end(&self) -> Option<Tuple<'a>> { |
1029 | 0 | self.header.intermediate_end_tuple() |
1030 | 0 | } |
1031 | | |
1032 | | /// Compute the fixed point scalar for this tuple at the given location in |
1033 | | /// variation space. |
1034 | | /// |
1035 | | /// The `coords` slice must be of lesser or equal length to the number of |
1036 | | /// axes. If it is less, missing (trailing) axes will be assumed to have |
1037 | | /// zero values. |
1038 | | /// |
1039 | | /// Returns `None` if this tuple is not applicable at the provided |
1040 | | /// coordinates (e.g. if the resulting scalar is zero). |
1041 | 0 | pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Option<Fixed> { |
1042 | 0 | compute_scalar( |
1043 | 0 | &self.header, |
1044 | 0 | self.axis_count as usize, |
1045 | 0 | &self.shared_tuples, |
1046 | 0 | coords, |
1047 | 0 | &[], |
1048 | | ) |
1049 | 0 | } |
1050 | | |
1051 | | /// Compute the floating point scalar for this tuple at the given location |
1052 | | /// in variation space. |
1053 | | /// |
1054 | | /// The `coords` slice must be of lesser or equal length to the number of |
1055 | | /// axes. If it is less, missing (trailing) axes will be assumed to have |
1056 | | /// zero values. |
1057 | | /// |
1058 | | /// Returns `None` if this tuple is not applicable at the provided |
1059 | | /// coordinates (e.g. if the resulting scalar is zero). |
1060 | 0 | pub fn compute_scalar_f32(&self, coords: &[F2Dot14]) -> Option<f32> { |
1061 | 0 | let mut scalar = 1.0; |
1062 | 0 | let peak = self.peak(); |
1063 | 0 | let inter_start = self.header.intermediate_start_tuple(); |
1064 | 0 | let inter_end = self.header.intermediate_end_tuple(); |
1065 | 0 | if peak.len() != self.axis_count as usize { |
1066 | 0 | return None; |
1067 | 0 | } |
1068 | 0 | for i in 0..self.axis_count { |
1069 | 0 | let i = i as usize; |
1070 | 0 | let coord = coords.get(i).copied().unwrap_or_default().to_bits() as i32; |
1071 | 0 | let peak = peak.get(i).unwrap_or_default().to_bits() as i32; |
1072 | 0 | if peak == 0 || peak == coord { |
1073 | 0 | continue; |
1074 | 0 | } |
1075 | 0 | if coord == 0 { |
1076 | 0 | return None; |
1077 | 0 | } |
1078 | 0 | if let (Some(inter_start), Some(inter_end)) = (&inter_start, &inter_end) { |
1079 | 0 | let start = inter_start.get(i).unwrap_or_default().to_bits() as i32; |
1080 | 0 | let end = inter_end.get(i).unwrap_or_default().to_bits() as i32; |
1081 | 0 | if start > peak || peak > end || (start < 0 && end > 0 && peak != 0) { |
1082 | 0 | continue; |
1083 | 0 | } |
1084 | 0 | if coord < start || coord > end { |
1085 | 0 | return None; |
1086 | 0 | } |
1087 | 0 | if coord < peak { |
1088 | 0 | if peak != start { |
1089 | 0 | scalar *= (coord - start) as f32 / (peak - start) as f32; |
1090 | 0 | } |
1091 | 0 | } else if peak != end { |
1092 | 0 | scalar *= (end - coord) as f32 / (end - peak) as f32; |
1093 | 0 | } |
1094 | | } else { |
1095 | 0 | if coord < peak.min(0) || coord > peak.max(0) { |
1096 | 0 | return None; |
1097 | 0 | } |
1098 | 0 | scalar *= coord as f32 / peak as f32; |
1099 | | } |
1100 | | } |
1101 | 0 | Some(scalar) |
1102 | 0 | } |
1103 | | |
1104 | | /// Iterate over the deltas for this tuple. |
1105 | | /// |
1106 | | /// This does not account for scaling. Returns only explicitly encoded |
1107 | | /// deltas, e.g. an omission by IUP will not be present. |
1108 | 861 | pub fn deltas(&self) -> TupleDeltaIter<'a, T> { |
1109 | 861 | let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas(); |
1110 | 861 | let count = point_numbers.count() as usize; |
1111 | 861 | let packed_deltas = if count == 0 { |
1112 | 553 | PackedDeltas::consume_all(packed_deltas) |
1113 | | } else { |
1114 | 308 | PackedDeltas::new(packed_deltas, if T::is_point() { count * 2 } else { count }) |
1115 | | }; |
1116 | 861 | TupleDeltaIter::new(&point_numbers, packed_deltas) |
1117 | 861 | } Unexecuted instantiation: <read_fonts::tables::variations::TupleVariation<read_fonts::tables::cvar::CvtDelta>>::deltas <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::deltas Line | Count | Source | 1108 | 861 | pub fn deltas(&self) -> TupleDeltaIter<'a, T> { | 1109 | 861 | let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas(); | 1110 | 861 | let count = point_numbers.count() as usize; | 1111 | 861 | let packed_deltas = if count == 0 { | 1112 | 553 | PackedDeltas::consume_all(packed_deltas) | 1113 | | } else { | 1114 | 308 | PackedDeltas::new(packed_deltas, if T::is_point() { count * 2 } else { count }) | 1115 | | }; | 1116 | 861 | TupleDeltaIter::new(&point_numbers, packed_deltas) | 1117 | 861 | } |
|
1118 | | |
1119 | 2.08k | fn point_numbers_and_packed_deltas(&self) -> (PackedPointNumbers<'a>, FontData<'a>) { |
1120 | 2.08k | if self.header.tuple_index().private_point_numbers() { |
1121 | 1.25k | PackedPointNumbers::split_off_front(self.serialized_data) |
1122 | | } else { |
1123 | 834 | ( |
1124 | 834 | self.shared_point_numbers.clone().unwrap_or_default(), |
1125 | 834 | self.serialized_data, |
1126 | 834 | ) |
1127 | | } |
1128 | 2.08k | } Unexecuted instantiation: <read_fonts::tables::variations::TupleVariation<read_fonts::tables::cvar::CvtDelta>>::point_numbers_and_packed_deltas <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::point_numbers_and_packed_deltas Line | Count | Source | 1119 | 2.08k | fn point_numbers_and_packed_deltas(&self) -> (PackedPointNumbers<'a>, FontData<'a>) { | 1120 | 2.08k | if self.header.tuple_index().private_point_numbers() { | 1121 | 1.25k | PackedPointNumbers::split_off_front(self.serialized_data) | 1122 | | } else { | 1123 | 834 | ( | 1124 | 834 | self.shared_point_numbers.clone().unwrap_or_default(), | 1125 | 834 | self.serialized_data, | 1126 | 834 | ) | 1127 | | } | 1128 | 2.08k | } |
|
1129 | | } |
1130 | | |
1131 | | impl TupleVariation<'_, GlyphDelta> { |
1132 | | /// Reads the set of deltas from this tuple variation. |
1133 | | /// |
1134 | | /// This is significantly faster than using the [`Self::deltas`] |
1135 | | /// method but requires preallocated memory to store deltas and |
1136 | | /// flags. |
1137 | | /// |
1138 | | /// This method should only be used when the tuple variation is dense, |
1139 | | /// that is, [`Self::has_deltas_for_all_points`] returns true. |
1140 | | /// |
1141 | | /// The size of `deltas` must be the same as the target value set to |
1142 | | /// which the variation is applied. For simple outlines, this is |
1143 | | /// `num_points + 4` and for composites it is `num_components + 4` |
1144 | | /// (where the `+ 4` is to accommodate phantom points). |
1145 | | /// |
1146 | | /// The `deltas` slice will not be zeroed before accumulation and each |
1147 | | /// delta will be multiplied by the given `scalar`. |
1148 | 370 | pub fn accumulate_dense_deltas<D: PointCoord>( |
1149 | 370 | &self, |
1150 | 370 | deltas: &mut [Point<D>], |
1151 | 370 | scalar: Fixed, |
1152 | 370 | ) -> Result<(), ReadError> { |
1153 | 370 | let (_, packed_deltas) = self.point_numbers_and_packed_deltas(); |
1154 | 370 | let mut cursor = packed_deltas.cursor(); |
1155 | 370 | if scalar == Fixed::ONE { |
1156 | | // scalar of 1.0 is common so avoid the costly conversions and |
1157 | | // multiplications per coord |
1158 | 549 | read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { |
1159 | 549 | delta.x += D::from_i32(new_delta); |
1160 | 549 | })?; Unexecuted instantiation: <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas::<font_types::fixed::Fixed>::{closure#0}<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas::<f32>::{closure#0}Line | Count | Source | 1158 | 549 | read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { | 1159 | 549 | delta.x += D::from_i32(new_delta); | 1160 | 549 | })?; |
|
1161 | 281 | read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { |
1162 | 281 | delta.y += D::from_i32(new_delta); |
1163 | 281 | })?; Unexecuted instantiation: <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas::<font_types::fixed::Fixed>::{closure#1}<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas::<f32>::{closure#1}Line | Count | Source | 1161 | 281 | read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { | 1162 | 281 | delta.y += D::from_i32(new_delta); | 1163 | 281 | })?; |
|
1164 | | } else { |
1165 | 69 | read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { |
1166 | 61 | delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar); |
1167 | 61 | })?; Unexecuted instantiation: <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas::<font_types::fixed::Fixed>::{closure#2}<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas::<f32>::{closure#2}Line | Count | Source | 1165 | 61 | read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { | 1166 | 61 | delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar); | 1167 | 61 | })?; |
|
1168 | 30 | read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { |
1169 | 30 | delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar); |
1170 | 30 | })?; Unexecuted instantiation: <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas::<font_types::fixed::Fixed>::{closure#3}<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas::<f32>::{closure#3}Line | Count | Source | 1168 | 30 | read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { | 1169 | 30 | delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar); | 1170 | 30 | })?; |
|
1171 | | } |
1172 | 44 | Ok(()) |
1173 | 370 | } Unexecuted instantiation: <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas::<font_types::fixed::Fixed> <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas::<f32> Line | Count | Source | 1148 | 370 | pub fn accumulate_dense_deltas<D: PointCoord>( | 1149 | 370 | &self, | 1150 | 370 | deltas: &mut [Point<D>], | 1151 | 370 | scalar: Fixed, | 1152 | 370 | ) -> Result<(), ReadError> { | 1153 | 370 | let (_, packed_deltas) = self.point_numbers_and_packed_deltas(); | 1154 | 370 | let mut cursor = packed_deltas.cursor(); | 1155 | 370 | if scalar == Fixed::ONE { | 1156 | | // scalar of 1.0 is common so avoid the costly conversions and | 1157 | | // multiplications per coord | 1158 | 301 | read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { | 1159 | | delta.x += D::from_i32(new_delta); | 1160 | 201 | })?; | 1161 | 100 | read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { | 1162 | | delta.y += D::from_i32(new_delta); | 1163 | 61 | })?; | 1164 | | } else { | 1165 | 69 | read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { | 1166 | | delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar); | 1167 | 60 | })?; | 1168 | 9 | read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { | 1169 | | delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar); | 1170 | 4 | })?; | 1171 | | } | 1172 | 44 | Ok(()) | 1173 | 370 | } |
|
1174 | | |
1175 | | /// Reads the set of deltas from this tuple variation. |
1176 | | /// |
1177 | | /// This is significantly faster than using the [`Self::deltas`] |
1178 | | /// method but requires preallocated memory to store deltas and |
1179 | | /// flags. |
1180 | | /// |
1181 | | /// This method should only be used when the tuple variation is sparse, |
1182 | | /// that is, [`Self::has_deltas_for_all_points`] returns false. |
1183 | | /// |
1184 | | /// The size of `deltas` must be the same as the target value set to |
1185 | | /// which the variation is applied. For simple outlines, this is |
1186 | | /// `num_points + 4` and for composites it is `num_components + 4` |
1187 | | /// (where the `+ 4` is to accommodate phantom points). |
1188 | | /// |
1189 | | /// The `deltas` and `flags` slices must be the same size. Modifications |
1190 | | /// to `deltas` will be sparse and for each entry that is modified, the |
1191 | | /// [PointMarker::HAS_DELTA] marker will be set for the corresponding |
1192 | | /// entry in the `flags` slice. |
1193 | | /// |
1194 | | /// The `deltas` slice will not be zeroed before accumulation and each |
1195 | | /// delta will be multiplied by the given `scalar`. |
1196 | 858 | pub fn accumulate_sparse_deltas<D: PointCoord>( |
1197 | 858 | &self, |
1198 | 858 | deltas: &mut [Point<D>], |
1199 | 858 | flags: &mut [PointFlags], |
1200 | 858 | scalar: Fixed, |
1201 | 858 | ) -> Result<(), ReadError> { |
1202 | 858 | let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas(); |
1203 | 858 | let mut cursor = packed_deltas.cursor(); |
1204 | 858 | let count = point_numbers.count() as usize; |
1205 | 858 | if scalar == Fixed::ONE { |
1206 | | // scalar of 1.0 is common so avoid the costly conversions and |
1207 | | // multiplications per coord |
1208 | 16.5k | read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { |
1209 | 16.5k | if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) { |
1210 | 2.16k | delta.x += D::from_i32(new_delta); |
1211 | 2.16k | flag.set_marker(PointMarker::HAS_DELTA); |
1212 | 14.3k | } |
1213 | 16.5k | })?; Unexecuted instantiation: <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas::<font_types::fixed::Fixed>::{closure#0}<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas::<f32>::{closure#0}Line | Count | Source | 1208 | 16.5k | read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { | 1209 | 16.5k | if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) { | 1210 | 2.16k | delta.x += D::from_i32(new_delta); | 1211 | 2.16k | flag.set_marker(PointMarker::HAS_DELTA); | 1212 | 14.3k | } | 1213 | 16.5k | })?; |
|
1214 | 5.21k | read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { |
1215 | 5.21k | if let Some(delta) = deltas.get_mut(ix) { |
1216 | 640 | delta.y += D::from_i32(new_delta); |
1217 | 4.57k | } |
1218 | 5.21k | })?; Unexecuted instantiation: <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas::<font_types::fixed::Fixed>::{closure#1}<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas::<f32>::{closure#1}Line | Count | Source | 1214 | 5.21k | read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { | 1215 | 5.21k | if let Some(delta) = deltas.get_mut(ix) { | 1216 | 640 | delta.y += D::from_i32(new_delta); | 1217 | 4.57k | } | 1218 | 5.21k | })?; |
|
1219 | | } else { |
1220 | 1.40k | read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { |
1221 | 1.40k | if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) { |
1222 | 48 | delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar); |
1223 | 48 | flag.set_marker(PointMarker::HAS_DELTA); |
1224 | 1.35k | } |
1225 | 1.40k | })?; Unexecuted instantiation: <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas::<font_types::fixed::Fixed>::{closure#2}<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas::<f32>::{closure#2}Line | Count | Source | 1220 | 1.40k | read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { | 1221 | 1.40k | if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) { | 1222 | 48 | delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar); | 1223 | 48 | flag.set_marker(PointMarker::HAS_DELTA); | 1224 | 1.35k | } | 1225 | 1.40k | })?; |
|
1226 | 434 | read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { |
1227 | 434 | if let Some(delta) = deltas.get_mut(ix) { |
1228 | 26 | delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar); |
1229 | 408 | } |
1230 | 434 | })?; Unexecuted instantiation: <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas::<font_types::fixed::Fixed>::{closure#3}<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas::<f32>::{closure#3}Line | Count | Source | 1226 | 434 | read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { | 1227 | 434 | if let Some(delta) = deltas.get_mut(ix) { | 1228 | 26 | delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar); | 1229 | 408 | } | 1230 | 434 | })?; |
|
1231 | | } |
1232 | 273 | Ok(()) |
1233 | 858 | } Unexecuted instantiation: <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas::<font_types::fixed::Fixed> <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas::<f32> Line | Count | Source | 1196 | 858 | pub fn accumulate_sparse_deltas<D: PointCoord>( | 1197 | 858 | &self, | 1198 | 858 | deltas: &mut [Point<D>], | 1199 | 858 | flags: &mut [PointFlags], | 1200 | 858 | scalar: Fixed, | 1201 | 858 | ) -> Result<(), ReadError> { | 1202 | 858 | let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas(); | 1203 | 858 | let mut cursor = packed_deltas.cursor(); | 1204 | 858 | let count = point_numbers.count() as usize; | 1205 | 858 | if scalar == Fixed::ONE { | 1206 | | // scalar of 1.0 is common so avoid the costly conversions and | 1207 | | // multiplications per coord | 1208 | 664 | read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { | 1209 | | if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) { | 1210 | | delta.x += D::from_i32(new_delta); | 1211 | | flag.set_marker(PointMarker::HAS_DELTA); | 1212 | | } | 1213 | 249 | })?; | 1214 | 415 | read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { | 1215 | | if let Some(delta) = deltas.get_mut(ix) { | 1216 | | delta.y += D::from_i32(new_delta); | 1217 | | } | 1218 | 224 | })?; | 1219 | | } else { | 1220 | 194 | read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { | 1221 | | if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) { | 1222 | | delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar); | 1223 | | flag.set_marker(PointMarker::HAS_DELTA); | 1224 | | } | 1225 | 98 | })?; | 1226 | 96 | read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { | 1227 | | if let Some(delta) = deltas.get_mut(ix) { | 1228 | | delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar); | 1229 | | } | 1230 | 14 | })?; | 1231 | | } | 1232 | 273 | Ok(()) | 1233 | 858 | } |
|
1234 | | } |
1235 | | |
1236 | | /// This is basically a manually applied loop unswitching optimization |
1237 | | /// for reading deltas. It reads each typed run into a slice for processing |
1238 | | /// instead of handling each delta individually with all the necessary |
1239 | | /// branching that implies. |
1240 | 479 | fn read_dense_deltas<T>( |
1241 | 479 | cursor: &mut Cursor, |
1242 | 479 | deltas: &mut [T], |
1243 | 479 | mut f: impl FnMut(&mut T, i32), |
1244 | 479 | ) -> Result<(), ReadError> { |
1245 | 479 | let count = deltas.len(); |
1246 | 479 | let mut cur = 0; |
1247 | 1.27k | while cur < count { |
1248 | 1.12k | let control: u8 = cursor.read()?; |
1249 | 1.02k | let value_type = DeltaRunType::new(control); |
1250 | 1.02k | let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize; |
1251 | 1.02k | let dest = deltas |
1252 | 1.02k | .get_mut(cur..cur + run_count) |
1253 | 1.02k | .ok_or(ReadError::OutOfBounds)?; |
1254 | 827 | match value_type { |
1255 | 26 | DeltaRunType::Zero => {} |
1256 | | DeltaRunType::I8 => { |
1257 | 751 | let packed_deltas = cursor.read_array::<i8>(run_count)?; |
1258 | 856 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { |
1259 | 856 | f(delta, *new_delta as i32); |
1260 | 856 | } |
1261 | | } |
1262 | | DeltaRunType::I16 => { |
1263 | 42 | let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?; |
1264 | 46 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { |
1265 | 46 | f(delta, new_delta.get() as i32); |
1266 | 46 | } |
1267 | | } |
1268 | | DeltaRunType::I32 => { |
1269 | 8 | let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?; |
1270 | 19 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { |
1271 | 19 | f(delta, new_delta.get()); |
1272 | 19 | } |
1273 | | } |
1274 | | } |
1275 | 798 | cur += run_count; |
1276 | | } |
1277 | 153 | Ok(()) |
1278 | 479 | } Unexecuted instantiation: read_fonts::tables::variations::read_dense_deltas::<font_types::point::Point<font_types::fixed::Fixed>, <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas<font_types::fixed::Fixed>::{closure#0}>Unexecuted instantiation: read_fonts::tables::variations::read_dense_deltas::<font_types::point::Point<font_types::fixed::Fixed>, <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas<font_types::fixed::Fixed>::{closure#2}>Unexecuted instantiation: read_fonts::tables::variations::read_dense_deltas::<font_types::point::Point<font_types::fixed::Fixed>, <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas<font_types::fixed::Fixed>::{closure#3}>Unexecuted instantiation: read_fonts::tables::variations::read_dense_deltas::<font_types::point::Point<font_types::fixed::Fixed>, <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas<font_types::fixed::Fixed>::{closure#1}>read_fonts::tables::variations::read_dense_deltas::<font_types::point::Point<f32>, <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas<f32>::{closure#0}>Line | Count | Source | 1240 | 301 | fn read_dense_deltas<T>( | 1241 | 301 | cursor: &mut Cursor, | 1242 | 301 | deltas: &mut [T], | 1243 | 301 | mut f: impl FnMut(&mut T, i32), | 1244 | 301 | ) -> Result<(), ReadError> { | 1245 | 301 | let count = deltas.len(); | 1246 | 301 | let mut cur = 0; | 1247 | 763 | while cur < count { | 1248 | 663 | let control: u8 = cursor.read()?; | 1249 | 588 | let value_type = DeltaRunType::new(control); | 1250 | 588 | let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize; | 1251 | 588 | let dest = deltas | 1252 | 588 | .get_mut(cur..cur + run_count) | 1253 | 588 | .ok_or(ReadError::OutOfBounds)?; | 1254 | 485 | match value_type { | 1255 | 20 | DeltaRunType::Zero => {} | 1256 | | DeltaRunType::I8 => { | 1257 | 438 | let packed_deltas = cursor.read_array::<i8>(run_count)?; | 1258 | 512 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { | 1259 | 512 | f(delta, *new_delta as i32); | 1260 | 512 | } | 1261 | | } | 1262 | | DeltaRunType::I16 => { | 1263 | 24 | let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?; | 1264 | 28 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { | 1265 | 28 | f(delta, new_delta.get() as i32); | 1266 | 28 | } | 1267 | | } | 1268 | | DeltaRunType::I32 => { | 1269 | 3 | let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?; | 1270 | 9 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { | 1271 | 9 | f(delta, new_delta.get()); | 1272 | 9 | } | 1273 | | } | 1274 | | } | 1275 | 462 | cur += run_count; | 1276 | | } | 1277 | 100 | Ok(()) | 1278 | 301 | } |
read_fonts::tables::variations::read_dense_deltas::<font_types::point::Point<f32>, <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas<f32>::{closure#2}>Line | Count | Source | 1240 | 69 | fn read_dense_deltas<T>( | 1241 | 69 | cursor: &mut Cursor, | 1242 | 69 | deltas: &mut [T], | 1243 | 69 | mut f: impl FnMut(&mut T, i32), | 1244 | 69 | ) -> Result<(), ReadError> { | 1245 | 69 | let count = deltas.len(); | 1246 | 69 | let mut cur = 0; | 1247 | 122 | while cur < count { | 1248 | 113 | let control: u8 = cursor.read()?; | 1249 | 86 | let value_type = DeltaRunType::new(control); | 1250 | 86 | let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize; | 1251 | 86 | let dest = deltas | 1252 | 86 | .get_mut(cur..cur + run_count) | 1253 | 86 | .ok_or(ReadError::OutOfBounds)?; | 1254 | 59 | match value_type { | 1255 | 0 | DeltaRunType::Zero => {} | 1256 | | DeltaRunType::I8 => { | 1257 | 59 | let packed_deltas = cursor.read_array::<i8>(run_count)?; | 1258 | 61 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { | 1259 | 61 | f(delta, *new_delta as i32); | 1260 | 61 | } | 1261 | | } | 1262 | | DeltaRunType::I16 => { | 1263 | 0 | let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?; | 1264 | 0 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { | 1265 | 0 | f(delta, new_delta.get() as i32); | 1266 | 0 | } | 1267 | | } | 1268 | | DeltaRunType::I32 => { | 1269 | 0 | let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?; | 1270 | 0 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { | 1271 | 0 | f(delta, new_delta.get()); | 1272 | 0 | } | 1273 | | } | 1274 | | } | 1275 | 53 | cur += run_count; | 1276 | | } | 1277 | 9 | Ok(()) | 1278 | 69 | } |
read_fonts::tables::variations::read_dense_deltas::<font_types::point::Point<f32>, <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas<f32>::{closure#3}>Line | Count | Source | 1240 | 9 | fn read_dense_deltas<T>( | 1241 | 9 | cursor: &mut Cursor, | 1242 | 9 | deltas: &mut [T], | 1243 | 9 | mut f: impl FnMut(&mut T, i32), | 1244 | 9 | ) -> Result<(), ReadError> { | 1245 | 9 | let count = deltas.len(); | 1246 | 9 | let mut cur = 0; | 1247 | 33 | while cur < count { | 1248 | 28 | let control: u8 = cursor.read()?; | 1249 | 28 | let value_type = DeltaRunType::new(control); | 1250 | 28 | let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize; | 1251 | 28 | let dest = deltas | 1252 | 28 | .get_mut(cur..cur + run_count) | 1253 | 28 | .ok_or(ReadError::OutOfBounds)?; | 1254 | 24 | match value_type { | 1255 | 0 | DeltaRunType::Zero => {} | 1256 | | DeltaRunType::I8 => { | 1257 | 24 | let packed_deltas = cursor.read_array::<i8>(run_count)?; | 1258 | 30 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { | 1259 | 30 | f(delta, *new_delta as i32); | 1260 | 30 | } | 1261 | | } | 1262 | | DeltaRunType::I16 => { | 1263 | 0 | let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?; | 1264 | 0 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { | 1265 | 0 | f(delta, new_delta.get() as i32); | 1266 | 0 | } | 1267 | | } | 1268 | | DeltaRunType::I32 => { | 1269 | 0 | let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?; | 1270 | 0 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { | 1271 | 0 | f(delta, new_delta.get()); | 1272 | 0 | } | 1273 | | } | 1274 | | } | 1275 | 24 | cur += run_count; | 1276 | | } | 1277 | 5 | Ok(()) | 1278 | 9 | } |
read_fonts::tables::variations::read_dense_deltas::<font_types::point::Point<f32>, <read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_dense_deltas<f32>::{closure#1}>Line | Count | Source | 1240 | 100 | fn read_dense_deltas<T>( | 1241 | 100 | cursor: &mut Cursor, | 1242 | 100 | deltas: &mut [T], | 1243 | 100 | mut f: impl FnMut(&mut T, i32), | 1244 | 100 | ) -> Result<(), ReadError> { | 1245 | 100 | let count = deltas.len(); | 1246 | 100 | let mut cur = 0; | 1247 | 359 | while cur < count { | 1248 | 320 | let control: u8 = cursor.read()?; | 1249 | 320 | let value_type = DeltaRunType::new(control); | 1250 | 320 | let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize; | 1251 | 320 | let dest = deltas | 1252 | 320 | .get_mut(cur..cur + run_count) | 1253 | 320 | .ok_or(ReadError::OutOfBounds)?; | 1254 | 259 | match value_type { | 1255 | 6 | DeltaRunType::Zero => {} | 1256 | | DeltaRunType::I8 => { | 1257 | 230 | let packed_deltas = cursor.read_array::<i8>(run_count)?; | 1258 | 253 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { | 1259 | 253 | f(delta, *new_delta as i32); | 1260 | 253 | } | 1261 | | } | 1262 | | DeltaRunType::I16 => { | 1263 | 18 | let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?; | 1264 | 18 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { | 1265 | 18 | f(delta, new_delta.get() as i32); | 1266 | 18 | } | 1267 | | } | 1268 | | DeltaRunType::I32 => { | 1269 | 5 | let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?; | 1270 | 10 | for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { | 1271 | 10 | f(delta, new_delta.get()); | 1272 | 10 | } | 1273 | | } | 1274 | | } | 1275 | 259 | cur += run_count; | 1276 | | } | 1277 | 39 | Ok(()) | 1278 | 100 | } |
|
1279 | | |
1280 | | /// See [read_dense_deltas] docs. |
1281 | 1.36k | fn read_sparse_deltas( |
1282 | 1.36k | cursor: &mut Cursor, |
1283 | 1.36k | point_numbers: &PackedPointNumbers, |
1284 | 1.36k | count: usize, |
1285 | 1.36k | mut f: impl FnMut(usize, i32), |
1286 | 1.36k | ) -> Result<(), ReadError> { |
1287 | 1.36k | let mut cur = 0; |
1288 | 23.5k | let mut points_iter = point_numbers.iter().map(|ix| ix as usize); Unexecuted instantiation: read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<font_types::fixed::Fixed>::{closure#0}>::{closure#0}Unexecuted instantiation: read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<font_types::fixed::Fixed>::{closure#2}>::{closure#0}Unexecuted instantiation: read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<font_types::fixed::Fixed>::{closure#3}>::{closure#0}Unexecuted instantiation: read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<font_types::fixed::Fixed>::{closure#1}>::{closure#0}read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<f32>::{closure#0}>::{closure#0}Line | Count | Source | 1288 | 16.5k | let mut points_iter = point_numbers.iter().map(|ix| ix as usize); |
read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<f32>::{closure#2}>::{closure#0}Line | Count | Source | 1288 | 1.40k | let mut points_iter = point_numbers.iter().map(|ix| ix as usize); |
read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<f32>::{closure#3}>::{closure#0}Line | Count | Source | 1288 | 434 | let mut points_iter = point_numbers.iter().map(|ix| ix as usize); |
read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<f32>::{closure#1}>::{closure#0}Line | Count | Source | 1288 | 5.21k | let mut points_iter = point_numbers.iter().map(|ix| ix as usize); |
|
1289 | 6.11k | while cur < count { |
1290 | 5.33k | let control: u8 = cursor.read()?; |
1291 | 5.23k | let value_type = DeltaRunType::new(control); |
1292 | 5.23k | let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize; |
1293 | 5.23k | match value_type { |
1294 | | DeltaRunType::Zero => { |
1295 | 899 | for _ in 0..run_count { |
1296 | 8.76k | let point_ix = points_iter.next().ok_or(ReadError::OutOfBounds)?; |
1297 | 8.61k | f(point_ix, 0); |
1298 | | } |
1299 | | } |
1300 | | DeltaRunType::I8 => { |
1301 | 3.60k | let packed_deltas = cursor.read_array::<i8>(run_count)?; |
1302 | 10.8k | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { |
1303 | 10.8k | f(point_ix, *new_delta as i32); |
1304 | 10.8k | } |
1305 | | } |
1306 | | DeltaRunType::I16 => { |
1307 | 454 | let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?; |
1308 | 2.78k | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { |
1309 | 2.78k | f(point_ix, new_delta.get() as i32); |
1310 | 2.78k | } |
1311 | | } |
1312 | | DeltaRunType::I32 => { |
1313 | 279 | let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?; |
1314 | 1.32k | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { |
1315 | 1.32k | f(point_ix, new_delta.get()); |
1316 | 1.32k | } |
1317 | | } |
1318 | | } |
1319 | 4.74k | cur += run_count; |
1320 | | } |
1321 | 784 | Ok(()) |
1322 | 1.36k | } Unexecuted instantiation: read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<font_types::fixed::Fixed>::{closure#0}>Unexecuted instantiation: read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<font_types::fixed::Fixed>::{closure#2}>Unexecuted instantiation: read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<font_types::fixed::Fixed>::{closure#3}>Unexecuted instantiation: read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<font_types::fixed::Fixed>::{closure#1}>read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<f32>::{closure#0}>Line | Count | Source | 1281 | 664 | fn read_sparse_deltas( | 1282 | 664 | cursor: &mut Cursor, | 1283 | 664 | point_numbers: &PackedPointNumbers, | 1284 | 664 | count: usize, | 1285 | 664 | mut f: impl FnMut(usize, i32), | 1286 | 664 | ) -> Result<(), ReadError> { | 1287 | 664 | let mut cur = 0; | 1288 | 664 | let mut points_iter = point_numbers.iter().map(|ix| ix as usize); | 1289 | 4.09k | while cur < count { | 1290 | 3.67k | let control: u8 = cursor.read()?; | 1291 | 3.63k | let value_type = DeltaRunType::new(control); | 1292 | 3.63k | let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize; | 1293 | 3.63k | match value_type { | 1294 | | DeltaRunType::Zero => { | 1295 | 696 | for _ in 0..run_count { | 1296 | 5.66k | let point_ix = points_iter.next().ok_or(ReadError::OutOfBounds)?; | 1297 | 5.60k | f(point_ix, 0); | 1298 | | } | 1299 | | } | 1300 | | DeltaRunType::I8 => { | 1301 | 2.55k | let packed_deltas = cursor.read_array::<i8>(run_count)?; | 1302 | 8.32k | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { | 1303 | 8.32k | f(point_ix, *new_delta as i32); | 1304 | 8.32k | } | 1305 | | } | 1306 | | DeltaRunType::I16 => { | 1307 | 251 | let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?; | 1308 | 2.01k | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { | 1309 | 2.01k | f(point_ix, new_delta.get() as i32); | 1310 | 2.01k | } | 1311 | | } | 1312 | | DeltaRunType::I32 => { | 1313 | 128 | let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?; | 1314 | 561 | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { | 1315 | 561 | f(point_ix, new_delta.get()); | 1316 | 561 | } | 1317 | | } | 1318 | | } | 1319 | 3.43k | cur += run_count; | 1320 | | } | 1321 | 415 | Ok(()) | 1322 | 664 | } |
read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<f32>::{closure#2}>Line | Count | Source | 1281 | 194 | fn read_sparse_deltas( | 1282 | 194 | cursor: &mut Cursor, | 1283 | 194 | point_numbers: &PackedPointNumbers, | 1284 | 194 | count: usize, | 1285 | 194 | mut f: impl FnMut(usize, i32), | 1286 | 194 | ) -> Result<(), ReadError> { | 1287 | 194 | let mut cur = 0; | 1288 | 194 | let mut points_iter = point_numbers.iter().map(|ix| ix as usize); | 1289 | 323 | while cur < count { | 1290 | 227 | let control: u8 = cursor.read()?; | 1291 | 190 | let value_type = DeltaRunType::new(control); | 1292 | 190 | let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize; | 1293 | 190 | match value_type { | 1294 | | DeltaRunType::Zero => { | 1295 | 25 | for _ in 0..run_count { | 1296 | 290 | let point_ix = points_iter.next().ok_or(ReadError::OutOfBounds)?; | 1297 | 267 | f(point_ix, 0); | 1298 | | } | 1299 | | } | 1300 | | DeltaRunType::I8 => { | 1301 | 106 | let packed_deltas = cursor.read_array::<i8>(run_count)?; | 1302 | 603 | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { | 1303 | 603 | f(point_ix, *new_delta as i32); | 1304 | 603 | } | 1305 | | } | 1306 | | DeltaRunType::I16 => { | 1307 | 22 | let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?; | 1308 | 118 | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { | 1309 | 118 | f(point_ix, new_delta.get() as i32); | 1310 | 118 | } | 1311 | | } | 1312 | | DeltaRunType::I32 => { | 1313 | 37 | let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?; | 1314 | 418 | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { | 1315 | 418 | f(point_ix, new_delta.get()); | 1316 | 418 | } | 1317 | | } | 1318 | | } | 1319 | 129 | cur += run_count; | 1320 | | } | 1321 | 96 | Ok(()) | 1322 | 194 | } |
read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<f32>::{closure#3}>Line | Count | Source | 1281 | 96 | fn read_sparse_deltas( | 1282 | 96 | cursor: &mut Cursor, | 1283 | 96 | point_numbers: &PackedPointNumbers, | 1284 | 96 | count: usize, | 1285 | 96 | mut f: impl FnMut(usize, i32), | 1286 | 96 | ) -> Result<(), ReadError> { | 1287 | 96 | let mut cur = 0; | 1288 | 96 | let mut points_iter = point_numbers.iter().map(|ix| ix as usize); | 1289 | 174 | while cur < count { | 1290 | 92 | let control: u8 = cursor.read()?; | 1291 | 92 | let value_type = DeltaRunType::new(control); | 1292 | 92 | let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize; | 1293 | 92 | match value_type { | 1294 | | DeltaRunType::Zero => { | 1295 | 4 | for _ in 0..run_count { | 1296 | 130 | let point_ix = points_iter.next().ok_or(ReadError::OutOfBounds)?; | 1297 | 126 | f(point_ix, 0); | 1298 | | } | 1299 | | } | 1300 | | DeltaRunType::I8 => { | 1301 | 65 | let packed_deltas = cursor.read_array::<i8>(run_count)?; | 1302 | 246 | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { | 1303 | 246 | f(point_ix, *new_delta as i32); | 1304 | 246 | } | 1305 | | } | 1306 | | DeltaRunType::I16 => { | 1307 | 18 | let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?; | 1308 | 60 | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { | 1309 | 60 | f(point_ix, new_delta.get() as i32); | 1310 | 60 | } | 1311 | | } | 1312 | | DeltaRunType::I32 => { | 1313 | 5 | let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?; | 1314 | 2 | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { | 1315 | 2 | f(point_ix, new_delta.get()); | 1316 | 2 | } | 1317 | | } | 1318 | | } | 1319 | 78 | cur += run_count; | 1320 | | } | 1321 | 82 | Ok(()) | 1322 | 96 | } |
read_fonts::tables::variations::read_sparse_deltas::<<read_fonts::tables::variations::TupleVariation<read_fonts::tables::gvar::GlyphDelta>>::accumulate_sparse_deltas<f32>::{closure#1}>Line | Count | Source | 1281 | 415 | fn read_sparse_deltas( | 1282 | 415 | cursor: &mut Cursor, | 1283 | 415 | point_numbers: &PackedPointNumbers, | 1284 | 415 | count: usize, | 1285 | 415 | mut f: impl FnMut(usize, i32), | 1286 | 415 | ) -> Result<(), ReadError> { | 1287 | 415 | let mut cur = 0; | 1288 | 415 | let mut points_iter = point_numbers.iter().map(|ix| ix as usize); | 1289 | 1.52k | while cur < count { | 1290 | 1.33k | let control: u8 = cursor.read()?; | 1291 | 1.31k | let value_type = DeltaRunType::new(control); | 1292 | 1.31k | let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize; | 1293 | 1.31k | match value_type { | 1294 | | DeltaRunType::Zero => { | 1295 | 174 | for _ in 0..run_count { | 1296 | 2.68k | let point_ix = points_iter.next().ok_or(ReadError::OutOfBounds)?; | 1297 | 2.62k | f(point_ix, 0); | 1298 | | } | 1299 | | } | 1300 | | DeltaRunType::I8 => { | 1301 | 873 | let packed_deltas = cursor.read_array::<i8>(run_count)?; | 1302 | 1.65k | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { | 1303 | 1.65k | f(point_ix, *new_delta as i32); | 1304 | 1.65k | } | 1305 | | } | 1306 | | DeltaRunType::I16 => { | 1307 | 163 | let packed_deltas = cursor.read_array::<BigEndian<i16>>(run_count)?; | 1308 | 597 | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { | 1309 | 597 | f(point_ix, new_delta.get() as i32); | 1310 | 597 | } | 1311 | | } | 1312 | | DeltaRunType::I32 => { | 1313 | 109 | let packed_deltas = cursor.read_array::<BigEndian<i32>>(run_count)?; | 1314 | 341 | for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { | 1315 | 341 | f(point_ix, new_delta.get()); | 1316 | 341 | } | 1317 | | } | 1318 | | } | 1319 | 1.11k | cur += run_count; | 1320 | | } | 1321 | 191 | Ok(()) | 1322 | 415 | } |
|
1323 | | |
1324 | | /// Compute the fixed point scalar for this tuple at the given location in |
1325 | | /// variation space. |
1326 | | /// |
1327 | | /// The `coords` slice must be of lesser or equal length to the number of |
1328 | | /// axes. If it is less, missing (trailing) axes will be assumed to have |
1329 | | /// zero values. |
1330 | | /// |
1331 | | /// Returns `None` if this tuple is not applicable at the provided |
1332 | | /// coordinates (e.g. if the resulting scalar is zero). |
1333 | | #[inline(always)] |
1334 | 26.7k | fn compute_scalar<'a>( |
1335 | 26.7k | header: &TupleVariationHeader, |
1336 | 26.7k | axis_count: usize, |
1337 | 26.7k | shared_tuples: &Option<ComputedArray<'a, Tuple<'a>>>, |
1338 | 26.7k | coords: &[F2Dot14], |
1339 | 26.7k | scalars: &[Fixed], |
1340 | 26.7k | ) -> Option<Fixed> { |
1341 | 26.7k | let tuple_idx = header.tuple_index(); |
1342 | 26.7k | let intermediate = header.intermediate_tuples(); |
1343 | 26.7k | let peak = if let Some(shared_index) = tuple_idx.tuple_records_index() { |
1344 | | // A shared peak with no range of its own has the same scalar |
1345 | | // wherever it is named, so a cached value applies. |
1346 | 20.6k | if intermediate.is_none() { |
1347 | 16.7k | match scalars.get(shared_index as usize) { |
1348 | 3.67k | Some(scalar) if *scalar != NOT_COMPUTED => { |
1349 | 3.67k | return (*scalar != Fixed::ZERO).then_some(*scalar); |
1350 | | } |
1351 | 13.0k | _ => {} |
1352 | | } |
1353 | 3.86k | } |
1354 | 16.9k | shared_tuples.as_ref()?.get(shared_index as usize).ok()? |
1355 | | } else { |
1356 | 6.13k | header.peak_tuple()? |
1357 | | }; |
1358 | 6.17k | if peak.len() != axis_count { |
1359 | 0 | return None; |
1360 | 6.17k | } |
1361 | 6.17k | scalar_for(&peak, intermediate, coords) |
1362 | 26.7k | } |
1363 | | |
1364 | | /// Computes the scalar for a peak at `coords`, interpolating over the tuple's |
1365 | | /// range if it states one. |
1366 | | /// |
1367 | | /// `None` where the tuple does not apply, which a scalar of zero also |
1368 | | /// expresses. |
1369 | 10.0k | pub(crate) fn scalar_for( |
1370 | 10.0k | peak: &Tuple, |
1371 | 10.0k | intermediate: Option<(Tuple, Tuple)>, |
1372 | 10.0k | coords: &[F2Dot14], |
1373 | 10.0k | ) -> Option<Fixed> { |
1374 | 10.0k | let mut scalar = Fixed::ONE; |
1375 | 21.5k | for (i, peak) in peak |
1376 | 10.0k | .values |
1377 | 10.0k | .iter() |
1378 | 10.0k | .enumerate() |
1379 | 26.3k | .filter(|(_, peak)| peak.get() != F2Dot14::ZERO) |
1380 | | { |
1381 | 21.5k | let coord = coords.get(i).copied().unwrap_or_default(); |
1382 | 21.5k | if coord == F2Dot14::ZERO { |
1383 | 3.60k | return None; |
1384 | 17.9k | } |
1385 | 17.9k | let peak = peak.get(); |
1386 | 17.9k | if peak == coord { |
1387 | 38 | continue; |
1388 | 17.8k | } |
1389 | 17.8k | if let Some((inter_start, inter_end)) = &intermediate { |
1390 | 13.1k | let start = inter_start.get(i).unwrap_or_default(); |
1391 | 13.1k | let end = inter_end.get(i).unwrap_or_default(); |
1392 | | // A region that does not enclose its own peak, or that straddles |
1393 | | // the default, is invalid and contributes a factor of one. This |
1394 | | // mirrors the OpenType algorithm and the sibling scalar |
1395 | | // computations (`compute_scalar_f32`, `VariationRegion::compute_scalar`); |
1396 | | // without it a malformed region interpolates where it should be |
1397 | | // skipped. |
1398 | 13.1k | if start > peak || peak > end || (start < F2Dot14::ZERO && end > F2Dot14::ZERO) { |
1399 | 11.5k | continue; |
1400 | 1.61k | } |
1401 | 1.61k | if coord <= start || coord >= end { |
1402 | 512 | return None; |
1403 | 1.10k | } |
1404 | 1.10k | let coord = coord.to_fixed(); |
1405 | 1.10k | let peak = peak.to_fixed(); |
1406 | 1.10k | if coord < peak { |
1407 | 456 | let start = start.to_fixed(); |
1408 | 456 | scalar = scalar.mul_div(coord - start, peak - start); |
1409 | 644 | } else { |
1410 | 644 | let end = end.to_fixed(); |
1411 | 644 | scalar = scalar.mul_div(end - coord, end - peak); |
1412 | 644 | } |
1413 | | } else { |
1414 | 4.70k | if coord < peak.min(F2Dot14::ZERO) || coord > peak.max(F2Dot14::ZERO) { |
1415 | 2.48k | return None; |
1416 | 2.21k | } |
1417 | 2.21k | let coord = coord.to_fixed(); |
1418 | 2.21k | let peak = peak.to_fixed(); |
1419 | 2.21k | scalar = scalar.mul_div(coord, peak); |
1420 | | } |
1421 | | } |
1422 | 3.40k | (scalar != Fixed::ZERO).then_some(scalar) |
1423 | 10.0k | } |
1424 | | |
1425 | | #[derive(Clone, Debug)] |
1426 | | enum TupleDeltaValues<'a> { |
1427 | | // Point deltas have separate runs for x and y coordinates. |
1428 | | Points(DeltaRunIter<'a>, DeltaRunIter<'a>), |
1429 | | Scalars(DeltaRunIter<'a>), |
1430 | | } |
1431 | | |
1432 | | /// An iterator over the deltas for a glyph. |
1433 | | #[derive(Clone, Debug)] |
1434 | | pub struct TupleDeltaIter<'a, T> { |
1435 | | pub cur: usize, |
1436 | | // if None all points get deltas, if Some specifies subset of points that do |
1437 | | points: Option<PackedPointNumbersIter<'a>>, |
1438 | | next_point: usize, |
1439 | | values: TupleDeltaValues<'a>, |
1440 | | _marker: std::marker::PhantomData<fn() -> T>, |
1441 | | } |
1442 | | |
1443 | | impl<'a, T> TupleDeltaIter<'a, T> |
1444 | | where |
1445 | | T: TupleDelta, |
1446 | | { |
1447 | 861 | fn new(points: &PackedPointNumbers<'a>, deltas: PackedDeltas<'a>) -> TupleDeltaIter<'a, T> { |
1448 | 861 | let mut points = points.iter(); |
1449 | 861 | let next_point = points.next(); |
1450 | 861 | let values = if T::is_point() { |
1451 | 861 | TupleDeltaValues::Points(deltas.x_deltas(), deltas.y_deltas()) |
1452 | | } else { |
1453 | 0 | TupleDeltaValues::Scalars(deltas.iter()) |
1454 | | }; |
1455 | | TupleDeltaIter { |
1456 | | cur: 0, |
1457 | 861 | points: next_point.map(|_| points), |
1458 | 861 | next_point: next_point.unwrap_or_default() as usize, |
1459 | 861 | values, |
1460 | 861 | _marker: std::marker::PhantomData, |
1461 | | } |
1462 | 861 | } Unexecuted instantiation: <read_fonts::tables::variations::TupleDeltaIter<read_fonts::tables::cvar::CvtDelta>>::new <read_fonts::tables::variations::TupleDeltaIter<read_fonts::tables::gvar::GlyphDelta>>::new Line | Count | Source | 1447 | 861 | fn new(points: &PackedPointNumbers<'a>, deltas: PackedDeltas<'a>) -> TupleDeltaIter<'a, T> { | 1448 | 861 | let mut points = points.iter(); | 1449 | 861 | let next_point = points.next(); | 1450 | 861 | let values = if T::is_point() { | 1451 | 861 | TupleDeltaValues::Points(deltas.x_deltas(), deltas.y_deltas()) | 1452 | | } else { | 1453 | 0 | TupleDeltaValues::Scalars(deltas.iter()) | 1454 | | }; | 1455 | | TupleDeltaIter { | 1456 | | cur: 0, | 1457 | 861 | points: next_point.map(|_| points), | 1458 | 861 | next_point: next_point.unwrap_or_default() as usize, | 1459 | 861 | values, | 1460 | 861 | _marker: std::marker::PhantomData, | 1461 | | } | 1462 | 861 | } |
|
1463 | | } |
1464 | | |
1465 | | /// Trait for deltas that are computed in a tuple variation store. |
1466 | | pub trait TupleDelta: Sized + Copy + 'static { |
1467 | | /// Returns true if the delta is a point and requires reading two values |
1468 | | /// from the packed delta stream. |
1469 | | fn is_point() -> bool; |
1470 | | |
1471 | | /// Creates a new delta for the given position and coordinates. If |
1472 | | /// the delta is not a point, the y value will always be zero. |
1473 | | fn new(position: u16, x: i32, y: i32) -> Self; |
1474 | | } |
1475 | | |
1476 | | impl<T> Iterator for TupleDeltaIter<'_, T> |
1477 | | where |
1478 | | T: TupleDelta, |
1479 | | { |
1480 | | type Item = T; |
1481 | | |
1482 | 73.9k | fn next(&mut self) -> Option<Self::Item> { |
1483 | 73.0k | let (position, dx, dy) = loop { |
1484 | 3.38M | let position = if let Some(points) = &mut self.points { |
1485 | | // if we have points then result is sparse; only some points have deltas |
1486 | 3.38M | if self.cur > self.next_point { |
1487 | 74.0k | self.next_point = points.next()? as usize; |
1488 | 3.31M | } |
1489 | 3.38M | self.next_point |
1490 | | } else { |
1491 | | // no points, every point has a delta. Just take the next one. |
1492 | 81 | self.cur |
1493 | | }; |
1494 | 3.38M | if position == self.cur { |
1495 | 73.8k | let (dx, dy) = match &mut self.values { |
1496 | 73.8k | TupleDeltaValues::Points(x, y) => (x.next()?, y.next()?), |
1497 | 0 | TupleDeltaValues::Scalars(scalars) => (scalars.next()?, 0), |
1498 | | }; |
1499 | 73.0k | break (position, dx, dy); |
1500 | 3.31M | } |
1501 | 3.31M | self.cur += 1; |
1502 | | }; |
1503 | 73.0k | self.cur += 1; |
1504 | 73.0k | Some(T::new(position as u16, dx, dy)) |
1505 | 73.9k | } Unexecuted instantiation: <read_fonts::tables::variations::TupleDeltaIter<read_fonts::tables::cvar::CvtDelta> as core::iter::traits::iterator::Iterator>::next <read_fonts::tables::variations::TupleDeltaIter<read_fonts::tables::gvar::GlyphDelta> as core::iter::traits::iterator::Iterator>::next Line | Count | Source | 1482 | 73.9k | fn next(&mut self) -> Option<Self::Item> { | 1483 | 73.0k | let (position, dx, dy) = loop { | 1484 | 3.38M | let position = if let Some(points) = &mut self.points { | 1485 | | // if we have points then result is sparse; only some points have deltas | 1486 | 3.38M | if self.cur > self.next_point { | 1487 | 74.0k | self.next_point = points.next()? as usize; | 1488 | 3.31M | } | 1489 | 3.38M | self.next_point | 1490 | | } else { | 1491 | | // no points, every point has a delta. Just take the next one. | 1492 | 81 | self.cur | 1493 | | }; | 1494 | 3.38M | if position == self.cur { | 1495 | 73.8k | let (dx, dy) = match &mut self.values { | 1496 | 73.8k | TupleDeltaValues::Points(x, y) => (x.next()?, y.next()?), | 1497 | 0 | TupleDeltaValues::Scalars(scalars) => (scalars.next()?, 0), | 1498 | | }; | 1499 | 73.0k | break (position, dx, dy); | 1500 | 3.31M | } | 1501 | 3.31M | self.cur += 1; | 1502 | | }; | 1503 | 73.0k | self.cur += 1; | 1504 | 73.0k | Some(T::new(position as u16, dx, dy)) | 1505 | 73.9k | } |
|
1506 | | } |
1507 | | |
1508 | | impl EntryFormat { |
1509 | 1.84k | pub fn entry_size(self) -> u8 { |
1510 | 1.84k | ((self.bits() & Self::MAP_ENTRY_SIZE_MASK.bits()) >> 4) + 1 |
1511 | 1.84k | } |
1512 | | |
1513 | 260 | pub fn bit_count(self) -> u8 { |
1514 | 260 | (self.bits() & Self::INNER_INDEX_BIT_COUNT_MASK.bits()) + 1 |
1515 | 260 | } |
1516 | | |
1517 | | // called from codegen |
1518 | 1.01k | pub(crate) fn map_size(self, map_count: impl Into<u32>) -> usize { |
1519 | 1.01k | self.entry_size() as usize * map_count.into() as usize |
1520 | 1.01k | } <read_fonts::tables::variations::EntryFormat>::map_size::<u32> Line | Count | Source | 1518 | 340 | pub(crate) fn map_size(self, map_count: impl Into<u32>) -> usize { | 1519 | 340 | self.entry_size() as usize * map_count.into() as usize | 1520 | 340 | } |
<read_fonts::tables::variations::EntryFormat>::map_size::<u16> Line | Count | Source | 1518 | 673 | pub(crate) fn map_size(self, map_count: impl Into<u32>) -> usize { | 1519 | 673 | self.entry_size() as usize * map_count.into() as usize | 1520 | 673 | } |
|
1521 | | } |
1522 | | |
1523 | | impl DeltaSetIndexMap<'_> { |
1524 | | /// Returns the delta set index for the specified value. |
1525 | 1.01k | pub fn get(&self, index: u32) -> Result<DeltaSetIndex, ReadError> { |
1526 | 1.01k | let (entry_format, map_count, data) = match self { |
1527 | 673 | Self::Format0(fmt) => (fmt.entry_format(), fmt.map_count() as u32, fmt.map_data()), |
1528 | 340 | Self::Format1(fmt) => (fmt.entry_format(), fmt.map_count(), fmt.map_data()), |
1529 | | }; |
1530 | 1.01k | if map_count == 0 { |
1531 | 180 | return Ok(DeltaSetIndex { |
1532 | 180 | outer: (index >> 16) as u16, |
1533 | 180 | inner: index as u16, |
1534 | 180 | }); |
1535 | 833 | } |
1536 | 833 | let entry_size = entry_format.entry_size(); |
1537 | 833 | let data = FontData::new(data); |
1538 | | // "if an index into the mapping array is used that is greater than or equal to |
1539 | | // mapCount, then the last logical entry of the mapping array is used." |
1540 | | // https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats |
1541 | | // #associating-target-items-to-variation-data |
1542 | 833 | let index = index.min(map_count.saturating_sub(1)); |
1543 | 833 | let offset = index as usize * entry_size as usize; |
1544 | 833 | let entry = match entry_size { |
1545 | 175 | 1 => data.read_at::<u8>(offset)? as u32, |
1546 | 110 | 2 => data.read_at::<u16>(offset)? as u32, |
1547 | 401 | 3 => data.read_at::<Uint24>(offset)?.into(), |
1548 | 147 | 4 => data.read_at::<u32>(offset)?, |
1549 | | _ => { |
1550 | 0 | return Err(ReadError::MalformedData( |
1551 | 0 | "invalid entry size in DeltaSetIndexMap", |
1552 | 0 | )) |
1553 | | } |
1554 | | }; |
1555 | 260 | let bit_count = entry_format.bit_count(); |
1556 | 260 | Ok(DeltaSetIndex { |
1557 | 260 | outer: (entry >> bit_count) as u16, |
1558 | 260 | inner: (entry & ((1 << bit_count) - 1)) as u16, |
1559 | 260 | }) |
1560 | 1.01k | } |
1561 | | } |
1562 | | |
1563 | | impl ItemVariationStore<'_> { |
1564 | | /// Computes the delta value for the specified index and set of |
1565 | | /// normalized variation coordinates. |
1566 | | /// |
1567 | | /// Each region's scalar is computed in 16.16 and multiplied by its raw |
1568 | | /// delta, and the products are summed exactly -- 48.16 holds the largest |
1569 | | /// possible sum with room to spare. Use [`F48Dot16::to_i32`] for the |
1570 | | /// classic integer delta, or apply the value unrounded to targets that |
1571 | | /// take fractional deltas. |
1572 | 22 | pub fn compute_delta(&self, index: DeltaSetIndex, coords: &[F2Dot14]) -> Option<F48Dot16> { |
1573 | 22 | self.compute_delta_with_scalars(index, coords, &[]) |
1574 | 22 | } |
1575 | | |
1576 | | /// Computes the delta for `index` at `coords`, reusing `scalars`. |
1577 | | /// |
1578 | | /// `scalars` holds one entry per variation region, from |
1579 | | /// [`compute_scalars`](Self::compute_scalars). It may be empty or cover |
1580 | | /// only some of them; the rest are computed here. |
1581 | 1.09k | pub fn compute_delta_with_scalars( |
1582 | 1.09k | &self, |
1583 | 1.09k | index: DeltaSetIndex, |
1584 | 1.09k | coords: &[F2Dot14], |
1585 | 1.09k | scalars: &[Fixed], |
1586 | 1.09k | ) -> Option<F48Dot16> { |
1587 | 1.09k | if coords.is_empty() || index == DeltaSetIndex::NO_VARIATION_INDEX { |
1588 | 3 | return Some(F48Dot16::ZERO); |
1589 | 1.08k | } |
1590 | 1.08k | let data = match self.item_variation_data().get(index.outer as usize) { |
1591 | 1.06k | Some(data) => data.ok()?, |
1592 | 22 | None => return Some(F48Dot16::ZERO), |
1593 | | }; |
1594 | 97 | let region_indices = data.region_indexes(); |
1595 | | // Read only when a scalar has to be computed. A run that finds all |
1596 | | // of them in `scalars` never touches the region list. |
1597 | 97 | let mut regions = None; |
1598 | | // Compute deltas with 64-bit precision. |
1599 | | // See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/7ab541a2/src/truetype/ttgxvar.c#L1094> |
1600 | 97 | let mut accum = F48Dot16::ZERO; |
1601 | | // The deltas and the region indices are parallel arrays sized by the |
1602 | | // same header field, so they are walked together. |
1603 | 97 | for (region_index, region_delta) in region_indices.iter().zip(data.delta_set(index.inner)) { |
1604 | 0 | let region_index = region_index.get() as usize; |
1605 | 0 | let scalar = match scalars.get(region_index) { |
1606 | 0 | Some(scalar) => *scalar, |
1607 | | None => { |
1608 | 0 | let regions = match ®ions { |
1609 | 0 | Some(regions) => regions, |
1610 | | None => { |
1611 | 0 | regions.insert(self.variation_region_list().ok()?.variation_regions()) |
1612 | | } |
1613 | | }; |
1614 | 0 | let region = regions.get(region_index).ok()?; |
1615 | | // A zero delta cannot contribute at any location. Still |
1616 | | // resolve its region so malformed indices fail as before. |
1617 | 0 | if region_delta == 0 { |
1618 | 0 | continue; |
1619 | 0 | } |
1620 | 0 | region.compute_scalar(coords) |
1621 | | } |
1622 | | }; |
1623 | | // The sum cannot overflow, even for hostile data: a scalar is a |
1624 | | // product of ratios that the range guards keep at most one, so |
1625 | | // each term is under 2^47, and at most 2^16 - 1 regions bounds |
1626 | | // the total below 2^63. |
1627 | 0 | accum += scalar.mul_i32(region_delta); |
1628 | | } |
1629 | 97 | Some(accum) |
1630 | 1.09k | } |
1631 | | |
1632 | | /// Computes the scalar for each variation region at `coords`, in store |
1633 | | /// order, and returns how many were written. |
1634 | | /// |
1635 | | /// A region that does not apply at `coords` is written as zero, which is |
1636 | | /// the value it contributes. |
1637 | | /// |
1638 | | /// `out` may be shorter than the store has regions, and an unreadable |
1639 | | /// store writes nothing. |
1640 | | /// [`compute_delta_with_scalars`](Self::compute_delta_with_scalars) |
1641 | | /// computes whatever is missing. |
1642 | | /// |
1643 | | /// Pass back only the entries this wrote. Regions are dense, so the |
1644 | | /// length of the slice is what identifies them. |
1645 | 65 | pub fn compute_scalars(&self, coords: &[F2Dot14], out: &mut [Fixed]) -> usize { |
1646 | 65 | let Ok(list) = self.variation_region_list() else { |
1647 | 31 | return 0; |
1648 | | }; |
1649 | 34 | let regions = list.variation_regions(); |
1650 | 34 | let count = out.len().min(regions.len()); |
1651 | 34 | for (i, out) in out[..count].iter_mut().enumerate() { |
1652 | | // An unreadable region ends the run. Entries already written |
1653 | | // stay valid; the caller computes the rest. |
1654 | 12 | let Ok(region) = regions.get(i) else { |
1655 | 0 | return i; |
1656 | | }; |
1657 | 12 | *out = region.compute_scalar(coords); |
1658 | | } |
1659 | 34 | count |
1660 | 65 | } |
1661 | | } |
1662 | | |
1663 | | impl<'a> VariationRegion<'a> { |
1664 | | /// Computes a scalar value for this region and the specified |
1665 | | /// normalized variation coordinates. |
1666 | 12 | pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Fixed { |
1667 | | const ZERO: Fixed = Fixed::ZERO; |
1668 | 12 | let mut scalar = Fixed::ONE; |
1669 | 20 | for (i, peak, axis_coords) in self.active_region_axes() { |
1670 | | // A coordinate pinned to the peak contributes a factor of one, |
1671 | | // whatever the rest of the axis says: if the axis is invalid or |
1672 | | // the peak sits at an edge, the outcome is `continue` on every |
1673 | | // path below. Checking it first, in raw 2.14, skips the start |
1674 | | // and end reads and their conversions for the common case of an |
1675 | | // instance sitting on a region's corner. |
1676 | 20 | let raw_coord = coords.get(i).copied().unwrap_or_default(); |
1677 | 20 | if raw_coord == peak { |
1678 | 0 | continue; |
1679 | 20 | } |
1680 | 20 | let peak = peak.to_fixed(); |
1681 | 20 | let start = axis_coords.start_coord.get().to_fixed(); |
1682 | 20 | let end = axis_coords.end_coord.get().to_fixed(); |
1683 | 20 | if start > peak || peak > end || start < ZERO && end > ZERO { |
1684 | 12 | continue; |
1685 | 8 | } |
1686 | 8 | let coord = raw_coord.to_fixed(); |
1687 | 8 | if coord < start || coord > end { |
1688 | 5 | return ZERO; |
1689 | 3 | } else if coord < peak { |
1690 | 2 | scalar = scalar.mul_div(coord - start, peak - start); |
1691 | 2 | } else { |
1692 | 1 | scalar = scalar.mul_div(end - coord, end - peak); |
1693 | 1 | } |
1694 | | } |
1695 | 7 | scalar |
1696 | 12 | } |
1697 | | |
1698 | 12 | fn active_region_axes( |
1699 | 12 | &self, |
1700 | 12 | ) -> impl Iterator<Item = (usize, F2Dot14, &'a RegionAxisCoordinates)> { |
1701 | 12 | self.region_axes() |
1702 | 12 | .iter() |
1703 | 12 | .enumerate() |
1704 | 22 | .filter_map(|(i, axis_coords)| { |
1705 | 22 | let peak = axis_coords.peak_coord(); |
1706 | 22 | if peak != F2Dot14::ZERO { |
1707 | 20 | Some((i, peak, axis_coords)) |
1708 | | } else { |
1709 | 2 | None |
1710 | | } |
1711 | 22 | }) |
1712 | 12 | } |
1713 | | } |
1714 | | |
1715 | | impl<'a> ItemVariationData<'a> { |
1716 | | /// Returns an iterator over the per-region delta values for the specified |
1717 | | /// inner index. |
1718 | 97 | pub fn delta_set(&self, inner_index: u16) -> impl Iterator<Item = i32> + 'a + Clone { |
1719 | 97 | let word_delta_count = self.word_delta_count(); |
1720 | 97 | let region_count = self.region_index_count(); |
1721 | 97 | let bytes_per_row = Self::delta_row_len(word_delta_count, region_count); |
1722 | 97 | let long_words = word_delta_count & 0x8000 != 0; |
1723 | 97 | let word_delta_count = word_delta_count & 0x7FFF; |
1724 | | |
1725 | 97 | let offset = bytes_per_row * inner_index as usize; |
1726 | 97 | ItemDeltas { |
1727 | 97 | bytes: self.delta_sets().get(offset..).unwrap_or_default().iter(), |
1728 | 97 | word_delta_count, |
1729 | 97 | long_words, |
1730 | 97 | len: region_count, |
1731 | 97 | pos: 0, |
1732 | 97 | } |
1733 | 97 | } |
1734 | | |
1735 | 0 | pub fn get_delta_row_len(&self) -> usize { |
1736 | 0 | let word_delta_count = self.word_delta_count(); |
1737 | 0 | let region_count = self.region_index_count(); |
1738 | 0 | Self::delta_row_len(word_delta_count, region_count) |
1739 | 0 | } |
1740 | | |
1741 | | /// the length of one delta set |
1742 | 194 | pub fn delta_row_len(word_delta_count: u16, region_index_count: u16) -> usize { |
1743 | 194 | let region_count = region_index_count as usize; |
1744 | 194 | let long_words = word_delta_count & 0x8000 != 0; |
1745 | 194 | let (word_size, small_size) = if long_words { (4, 2) } else { (2, 1) }; |
1746 | 194 | let long_delta_count = (word_delta_count & 0x7FFF) as usize; |
1747 | 194 | let short_delta_count = region_count.saturating_sub(long_delta_count); |
1748 | 194 | long_delta_count * word_size + short_delta_count * small_size |
1749 | 194 | } |
1750 | | |
1751 | | // called from generated code: compute the length in bytes of the delta_sets data |
1752 | 97 | pub fn delta_sets_len( |
1753 | 97 | item_count: u16, |
1754 | 97 | word_delta_count: u16, |
1755 | 97 | region_index_count: u16, |
1756 | 97 | ) -> usize { |
1757 | 97 | let bytes_per_row = Self::delta_row_len(word_delta_count, region_index_count); |
1758 | 97 | bytes_per_row * item_count as usize |
1759 | 97 | } |
1760 | | } |
1761 | | |
1762 | | #[derive(Clone)] |
1763 | | struct ItemDeltas<'a> { |
1764 | | bytes: core::slice::Iter<'a, u8>, |
1765 | | word_delta_count: u16, |
1766 | | long_words: bool, |
1767 | | len: u16, |
1768 | | pos: u16, |
1769 | | } |
1770 | | |
1771 | | impl Iterator for ItemDeltas<'_> { |
1772 | | type Item = i32; |
1773 | | |
1774 | 48 | fn next(&mut self) -> Option<Self::Item> { |
1775 | 48 | if self.pos >= self.len { |
1776 | 0 | return None; |
1777 | 48 | } |
1778 | 48 | let pos = self.pos; |
1779 | 48 | self.pos += 1; |
1780 | 48 | let mut byte = || self.bytes.next().copied(); |
1781 | 48 | let value = match (pos >= self.word_delta_count, self.long_words) { |
1782 | 22 | (true, true) | (false, false) => i16::from_be_bytes([byte()?, byte()?]) as i32, |
1783 | 26 | (true, false) => byte()? as i8 as i32, |
1784 | 0 | (false, true) => i32::from_be_bytes([byte()?, byte()?, byte()?, byte()?]), |
1785 | | }; |
1786 | 0 | Some(value) |
1787 | 48 | } |
1788 | | } |
1789 | | |
1790 | | /// The delta for a glyph's advance, reusing `scalars`. |
1791 | | /// |
1792 | | /// Keeps every bit the variation store computed. Rounding it to a whole |
1793 | | /// design unit is left to a caller, and implementations differ on how. |
1794 | 3.03k | pub(crate) fn advance_delta_with_scalars( |
1795 | 3.03k | dsim: Option<Result<DeltaSetIndexMap, ReadError>>, |
1796 | 3.03k | ivs: Result<ItemVariationStore, ReadError>, |
1797 | 3.03k | glyph_id: GlyphId, |
1798 | 3.03k | coords: &[F2Dot14], |
1799 | 3.03k | scalars: &[Fixed], |
1800 | 3.03k | ) -> Option<F48Dot16> { |
1801 | 3.03k | if coords.is_empty() { |
1802 | 0 | return Some(F48Dot16::ZERO); |
1803 | 3.03k | } |
1804 | 3.03k | let gid = glyph_id.to_u32(); |
1805 | 2.46k | let ix = match dsim { |
1806 | 1.01k | Some(Ok(dsim)) => dsim.get(gid).ok()?, |
1807 | | // Implicit mappings use outer index zero; an oversized glyph ID |
1808 | | // cannot address an inner row and must not wrap to a different glyph. |
1809 | 2.02k | _ if gid > u16::MAX as u32 => return Some(F48Dot16::ZERO), |
1810 | 2.02k | _ => DeltaSetIndex { |
1811 | 2.02k | outer: 0, |
1812 | 2.02k | inner: gid as _, |
1813 | 2.02k | }, |
1814 | | }; |
1815 | 2.46k | ivs.ok()?.compute_delta_with_scalars(ix, coords, scalars) |
1816 | 3.03k | } |
1817 | | |
1818 | | /// The delta for an item, reusing `scalars`. |
1819 | | /// |
1820 | | /// As [`advance_delta_with_scalars`], but for mappings that require an index |
1821 | | /// map instead of falling back to the glyph id. |
1822 | 0 | pub(crate) fn item_delta_with_scalars( |
1823 | 0 | dsim: Option<Result<DeltaSetIndexMap, ReadError>>, |
1824 | 0 | ivs: Result<ItemVariationStore, ReadError>, |
1825 | 0 | glyph_id: GlyphId, |
1826 | 0 | coords: &[F2Dot14], |
1827 | 0 | scalars: &[Fixed], |
1828 | 0 | ) -> Option<F48Dot16> { |
1829 | 0 | if coords.is_empty() { |
1830 | 0 | return Some(F48Dot16::ZERO); |
1831 | 0 | } |
1832 | 0 | let gid = glyph_id.to_u32(); |
1833 | 0 | let ix = match dsim { |
1834 | 0 | Some(Ok(dsim)) => dsim.get(gid).ok()?, |
1835 | 0 | _ => return None, |
1836 | | }; |
1837 | 0 | ivs.ok()?.compute_delta_with_scalars(ix, coords, scalars) |
1838 | 0 | } |
1839 | | |
1840 | | #[cfg(test)] |
1841 | | mod tests { |
1842 | | use font_test_data::bebuffer::BeBuffer; |
1843 | | |
1844 | | use super::*; |
1845 | | use crate::{FontRef, TableProvider}; |
1846 | | |
1847 | | #[test] |
1848 | | fn zero_deltas_still_validate_their_regions() { |
1849 | | let store_bytes = |indices: [u16; 2]| { |
1850 | | BeBuffer::new() |
1851 | | .push(1u16) // ItemVariationStore format |
1852 | | .push(12u32) // region list offset |
1853 | | .push(1u16) // variation data count |
1854 | | .push(28u32) // variation data offset |
1855 | | .push(1u16) // axis count |
1856 | | .push(2u16) // region count |
1857 | | .extend([F2Dot14::ZERO, F2Dot14::ONE, F2Dot14::ONE]) |
1858 | | .extend([F2Dot14::NEG_ONE, F2Dot14::NEG_ONE, F2Dot14::ZERO]) |
1859 | | .push(2u16) // item count |
1860 | | .push(2u16) // word delta count |
1861 | | .push(2u16) // region index count |
1862 | | .extend(indices) |
1863 | | .extend([0i16, 120, -40, 0]) |
1864 | | }; |
1865 | | let bytes = store_bytes([0, 1]); |
1866 | | let store = ItemVariationStore::read(FontData::new(&bytes)).unwrap(); |
1867 | | for coord in [-1.0, -0.25, 0.0, 0.25, 1.0] { |
1868 | | let coords = [F2Dot14::from_f32(coord)]; |
1869 | | let mut scalars = [Fixed::ZERO; 2]; |
1870 | | assert_eq!(store.compute_scalars(&coords, &mut scalars), 2); |
1871 | | for (inner, expected) in [120.0 * (-coord).max(0.0), -40.0 * coord.max(0.0)] |
1872 | | .into_iter() |
1873 | | .enumerate() |
1874 | | { |
1875 | | let index = DeltaSetIndex { |
1876 | | outer: 0, |
1877 | | inner: inner as u16, |
1878 | | }; |
1879 | | let expected = Some(F48Dot16::from_f64(expected as f64)); |
1880 | | assert_eq!(store.compute_delta(index, &coords), expected); |
1881 | | for count in 0..=2 { |
1882 | | assert_eq!( |
1883 | | store.compute_delta_with_scalars(index, &coords, &scalars[..count]), |
1884 | | expected |
1885 | | ); |
1886 | | } |
1887 | | } |
1888 | | } |
1889 | | // Both the leading and trailing zero must reject an invalid region. |
1890 | | for (indices, inner) in [([2, 1], 0), ([0, 2], 1)] { |
1891 | | let bytes = store_bytes(indices); |
1892 | | let store = ItemVariationStore::read(FontData::new(&bytes)).unwrap(); |
1893 | | assert_eq!( |
1894 | | store.compute_delta(DeltaSetIndex { outer: 0, inner }, &[F2Dot14::ONE]), |
1895 | | None |
1896 | | ); |
1897 | | } |
1898 | | } |
1899 | | |
1900 | | #[test] |
1901 | | fn implicit_advance_indices_do_not_truncate_glyph_ids() { |
1902 | | let bytes = BeBuffer::new() |
1903 | | .push(1u16) // ItemVariationStore format |
1904 | | .push(12u32) // region list offset |
1905 | | .push(1u16) // variation data count |
1906 | | .push(22u32) // variation data offset |
1907 | | .push(1u16) // axis count |
1908 | | .push(1u16) // region count |
1909 | | .extend([F2Dot14::ZERO, F2Dot14::ONE, F2Dot14::ONE]) |
1910 | | .push(2u16) // item count |
1911 | | .push(1u16) // word delta count |
1912 | | .push(1u16) // region index count |
1913 | | .push(0u16) // region index |
1914 | | .extend([10i16, 100i16]); |
1915 | | let store = ItemVariationStore::read(FontData::new(&bytes)).unwrap(); |
1916 | | let mapping = DeltaSetIndexMap::read(FontData::new(&[0, 0, 0, 1, 0])).unwrap(); |
1917 | | let coords = [F2Dot14::from_f32(0.75)]; |
1918 | | let mut scalars = [Fixed::ZERO; 32]; |
1919 | | let scalar_count = store.compute_scalars(&coords, &mut scalars); |
1920 | | for scalars in [&[][..], &scalars[..scalar_count]] { |
1921 | | let delta = advance_delta_with_scalars( |
1922 | | None, |
1923 | | Ok(store.clone()), |
1924 | | GlyphId::new(1), |
1925 | | &coords, |
1926 | | scalars, |
1927 | | ); |
1928 | | assert!(delta.is_some_and(|delta| delta != F48Dot16::ZERO)); |
1929 | | for gid in [0xFFFF, 0x10000, 0x10001, 0xFFFF0001] { |
1930 | | assert_eq!( |
1931 | | advance_delta_with_scalars( |
1932 | | None, |
1933 | | Ok(store.clone()), |
1934 | | GlyphId::new(gid), |
1935 | | &coords, |
1936 | | scalars, |
1937 | | ), |
1938 | | Some(F48Dot16::ZERO), |
1939 | | "implicit advance mapping wrapped glyph ID {gid}" |
1940 | | ); |
1941 | | } |
1942 | | // Explicit mappings keep their last-entry repetition for high IDs. |
1943 | | for gid in [0x10000, 0x10001] { |
1944 | | assert_eq!( |
1945 | | advance_delta_with_scalars( |
1946 | | Some(Ok(mapping.clone())), |
1947 | | Ok(store.clone()), |
1948 | | GlyphId::new(gid), |
1949 | | &coords, |
1950 | | scalars, |
1951 | | ), |
1952 | | Some(F48Dot16::from_f64(7.5)) |
1953 | | ); |
1954 | | } |
1955 | | } |
1956 | | } |
1957 | | |
1958 | | #[test] |
1959 | | fn ivs_regions() { |
1960 | | let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap(); |
1961 | | let hvar = font.hvar().expect("missing HVAR table"); |
1962 | | let ivs = hvar |
1963 | | .item_variation_store() |
1964 | | .expect("missing item variation store in HVAR"); |
1965 | | let region_list = ivs.variation_region_list().expect("missing region list!"); |
1966 | | let regions = region_list.variation_regions(); |
1967 | | let expected = &[ |
1968 | | // start_coord, peak_coord, end_coord |
1969 | | vec![[-1.0f32, -1.0, 0.0]], |
1970 | | vec![[0.0, 1.0, 1.0]], |
1971 | | ][..]; |
1972 | | let region_coords = regions |
1973 | | .iter() |
1974 | | .map(|region| { |
1975 | | region |
1976 | | .unwrap() |
1977 | | .region_axes() |
1978 | | .iter() |
1979 | | .map(|coords| { |
1980 | | [ |
1981 | | coords.start_coord().to_f32(), |
1982 | | coords.peak_coord().to_f32(), |
1983 | | coords.end_coord().to_f32(), |
1984 | | ] |
1985 | | }) |
1986 | | .collect::<Vec<_>>() |
1987 | | }) |
1988 | | .collect::<Vec<_>>(); |
1989 | | assert_eq!(expected, ®ion_coords); |
1990 | | } |
1991 | | |
1992 | | // A gvar/cvar intermediate region that does not enclose its own peak, or |
1993 | | // that straddles the default, must be ignored (contribute a factor of one) |
1994 | | // rather than interpolated. This matches the OpenType algorithm and the |
1995 | | // sibling `compute_scalar_f32` / `VariationRegion::compute_scalar`, which |
1996 | | // already apply the guard. |
1997 | | #[test] |
1998 | | fn scalar_for_ignores_invalid_intermediate_region() { |
1999 | | fn tuple(values: &[BigEndian<F2Dot14>]) -> Tuple<'_> { |
2000 | | Tuple { values } |
2001 | | } |
2002 | | let f = F2Dot14::from_f32; |
2003 | | |
2004 | | // start > peak: the coord sits inside (start, end) but the region does |
2005 | | // not contain the peak, so the axis is skipped. Before the guard this |
2006 | | // interpolated to (end - coord) / (end - peak) = 0.2. |
2007 | | let peak: [BigEndian<F2Dot14>; 1] = [f(0.5).into()]; |
2008 | | let start: [BigEndian<F2Dot14>; 1] = [f(0.8).into()]; |
2009 | | let end: [BigEndian<F2Dot14>; 1] = [f(1.0).into()]; |
2010 | | let coords = [f(0.9)]; |
2011 | | assert_eq!( |
2012 | | scalar_for(&tuple(&peak), Some((tuple(&start), tuple(&end))), &coords), |
2013 | | Some(Fixed::ONE) |
2014 | | ); |
2015 | | |
2016 | | // peak > end. |
2017 | | let peak2: [BigEndian<F2Dot14>; 1] = [f(1.0).into()]; |
2018 | | let start2: [BigEndian<F2Dot14>; 1] = [f(0.0).into()]; |
2019 | | let end2: [BigEndian<F2Dot14>; 1] = [f(0.5).into()]; |
2020 | | let coords2 = [f(0.25)]; |
2021 | | assert_eq!( |
2022 | | scalar_for( |
2023 | | &tuple(&peak2), |
2024 | | Some((tuple(&start2), tuple(&end2))), |
2025 | | &coords2 |
2026 | | ), |
2027 | | Some(Fixed::ONE) |
2028 | | ); |
2029 | | |
2030 | | // Region straddling the default (start < 0 < end). |
2031 | | let start3: [BigEndian<F2Dot14>; 1] = [f(-0.5).into()]; |
2032 | | let end3: [BigEndian<F2Dot14>; 1] = [f(1.0).into()]; |
2033 | | let coords3 = [f(0.25)]; |
2034 | | assert_eq!( |
2035 | | scalar_for( |
2036 | | &tuple(&peak), |
2037 | | Some((tuple(&start3), tuple(&end3))), |
2038 | | &coords3 |
2039 | | ), |
2040 | | Some(Fixed::ONE) |
2041 | | ); |
2042 | | |
2043 | | // A well-formed region (start <= peak <= end) still interpolates. |
2044 | | let start4: [BigEndian<F2Dot14>; 1] = [f(0.0).into()]; |
2045 | | let end4: [BigEndian<F2Dot14>; 1] = [f(1.0).into()]; |
2046 | | let coords4 = [f(0.25)]; |
2047 | | let scalar = scalar_for( |
2048 | | &tuple(&peak), |
2049 | | Some((tuple(&start4), tuple(&end4))), |
2050 | | &coords4, |
2051 | | ) |
2052 | | .unwrap(); |
2053 | | assert!((scalar.to_f32() - 0.5).abs() < 1e-4); |
2054 | | } |
2055 | | |
2056 | | // adapted from https://github.com/fonttools/fonttools/blob/f73220816264fc383b8a75f2146e8d69e455d398/Tests/ttLib/tables/TupleVariation_test.py#L492 |
2057 | | #[test] |
2058 | | fn packed_points() { |
2059 | | fn decode_points(bytes: &[u8]) -> Option<Vec<u16>> { |
2060 | | let data = FontData::new(bytes); |
2061 | | let packed = PackedPointNumbers { data }; |
2062 | | if packed.count() == 0 { |
2063 | | None |
2064 | | } else { |
2065 | | Some(packed.iter().collect()) |
2066 | | } |
2067 | | } |
2068 | | |
2069 | | assert_eq!(decode_points(&[0]), None); |
2070 | | // all points in glyph (in overly verbose encoding, not explicitly prohibited by spec) |
2071 | | assert_eq!(decode_points(&[0x80, 0]), None); |
2072 | | // 2 points; first run: [9, 9+6] |
2073 | | assert_eq!(decode_points(&[0x02, 0x01, 0x09, 0x06]), Some(vec![9, 15])); |
2074 | | // 2 points; first run: [0xBEEF, 0xCAFE]. (0x0C0F = 0xCAFE - 0xBEEF) |
2075 | | assert_eq!( |
2076 | | decode_points(&[0x02, 0x81, 0xbe, 0xef, 0x0c, 0x0f]), |
2077 | | Some(vec![0xbeef, 0xcafe]) |
2078 | | ); |
2079 | | // 1 point; first run: [7] |
2080 | | assert_eq!(decode_points(&[0x01, 0, 0x07]), Some(vec![7])); |
2081 | | // 1 point; first run: [7] in overly verbose encoding |
2082 | | assert_eq!(decode_points(&[0x01, 0x80, 0, 0x07]), Some(vec![7])); |
2083 | | // 1 point; first run: [65535]; requires words to be treated as unsigned numbers |
2084 | | assert_eq!(decode_points(&[0x01, 0x80, 0xff, 0xff]), Some(vec![65535])); |
2085 | | // 4 points; first run: [7, 8]; second run: [255, 257]. 257 is stored in delta-encoded bytes (0xFF + 2). |
2086 | | assert_eq!( |
2087 | | decode_points(&[0x04, 1, 7, 1, 1, 0xff, 2]), |
2088 | | Some(vec![7, 8, 263, 265]) |
2089 | | ); |
2090 | | } |
2091 | | |
2092 | | #[test] |
2093 | | fn packed_point_byte_len() { |
2094 | | fn count_bytes(bytes: &[u8]) -> usize { |
2095 | | let packed = PackedPointNumbers { |
2096 | | data: FontData::new(bytes), |
2097 | | }; |
2098 | | packed.total_len() |
2099 | | } |
2100 | | |
2101 | | static CASES: &[&[u8]] = &[ |
2102 | | &[0], |
2103 | | &[0x80, 0], |
2104 | | &[0x02, 0x01, 0x09, 0x06], |
2105 | | &[0x02, 0x81, 0xbe, 0xef, 0x0c, 0x0f], |
2106 | | &[0x01, 0, 0x07], |
2107 | | &[0x01, 0x80, 0, 0x07], |
2108 | | &[0x01, 0x80, 0xff, 0xff], |
2109 | | &[0x04, 1, 7, 1, 1, 0xff, 2], |
2110 | | ]; |
2111 | | |
2112 | | for case in CASES { |
2113 | | assert_eq!(count_bytes(case), case.len(), "{case:?}"); |
2114 | | } |
2115 | | } |
2116 | | |
2117 | | // https://github.com/fonttools/fonttools/blob/c30a6355ffdf7f09d31e7719975b4b59bac410af/Tests/ttLib/tables/TupleVariation_test.py#L670 |
2118 | | #[test] |
2119 | | fn packed_deltas() { |
2120 | | static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]); |
2121 | | |
2122 | | let deltas = PackedDeltas::consume_all(INPUT); |
2123 | | assert_eq!(deltas.count_or_compute(), 7); |
2124 | | assert_eq!( |
2125 | | deltas.iter().collect::<Vec<_>>(), |
2126 | | &[0, 0, 0, 0, 258, -127, -128] |
2127 | | ); |
2128 | | |
2129 | | assert_eq!( |
2130 | | PackedDeltas::consume_all(FontData::new(&[0x81])) |
2131 | | .iter() |
2132 | | .collect::<Vec<_>>(), |
2133 | | &[0, 0,] |
2134 | | ); |
2135 | | } |
2136 | | |
2137 | | // https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas |
2138 | | #[test] |
2139 | | fn packed_deltas_spec() { |
2140 | | static INPUT: FontData = FontData::new(&[ |
2141 | | 0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34, |
2142 | | ]); |
2143 | | static EXPECTED: &[i32] = &[10, -105, 0, -58, 0, 0, 0, 0, 0, 0, 0, 0, 4130, -1228]; |
2144 | | |
2145 | | let deltas = PackedDeltas::consume_all(INPUT); |
2146 | | assert_eq!(deltas.count_or_compute(), EXPECTED.len()); |
2147 | | assert_eq!(deltas.iter().collect::<Vec<_>>(), EXPECTED); |
2148 | | } |
2149 | | |
2150 | | #[test] |
2151 | | fn packed_delta_fetcher_skip_matches_iterator_suffix() { |
2152 | | static INPUT: FontData = FontData::new(&[ |
2153 | | 0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34, |
2154 | | ]); |
2155 | | let deltas = PackedDeltas::consume_all(INPUT); |
2156 | | let expected = deltas.iter().collect::<Vec<_>>(); |
2157 | | |
2158 | | for skip in 0..=expected.len() { |
2159 | | let mut fetcher = deltas.fetcher(); |
2160 | | fetcher.skip(skip).unwrap(); |
2161 | | let mut out = vec![0.0; expected.len() - skip]; |
2162 | | fetcher.add_to_f32_scaled(&mut out, 1.0).unwrap(); |
2163 | | let got = out.into_iter().map(|v| v as i32).collect::<Vec<_>>(); |
2164 | | assert_eq!(&got[..], &expected[skip..], "skip={skip}"); |
2165 | | } |
2166 | | |
2167 | | let mut fetcher = deltas.fetcher(); |
2168 | | assert!(matches!( |
2169 | | fetcher.skip(expected.len() + 1), |
2170 | | Err(ReadError::OutOfBounds) |
2171 | | )); |
2172 | | } |
2173 | | |
2174 | | #[test] |
2175 | | fn packed_delta_fetcher_scaled_add_and_exhaustion() { |
2176 | | static INPUT: FontData = FontData::new(&[ |
2177 | | 0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34, |
2178 | | ]); |
2179 | | // First four deltas are [10, -105, 0, -58]. |
2180 | | let deltas = PackedDeltas::new(INPUT, 4); |
2181 | | let mut fetcher = deltas.fetcher(); |
2182 | | let mut out = [1.0f32; 4]; |
2183 | | fetcher.add_to_f32_scaled(&mut out, 0.5).unwrap(); |
2184 | | assert_eq!(out, [6.0, -51.5, 1.0, -28.0]); |
2185 | | |
2186 | | // Bounded fetcher should now be exhausted. |
2187 | | let mut extra = [0.0f32; 1]; |
2188 | | assert!(matches!( |
2189 | | fetcher.add_to_f32_scaled(&mut extra, 1.0), |
2190 | | Err(ReadError::OutOfBounds) |
2191 | | )); |
2192 | | } |
2193 | | |
2194 | | #[test] |
2195 | | fn packed_delta_fetcher_f64_preserves_i32_residuals() { |
2196 | | static INPUT: FontData = |
2197 | | FontData::new(&[0xC1, 0x01, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00]); |
2198 | | let mut fetcher = PackedDeltas::new(INPUT, 2).fetcher(); |
2199 | | let mut out = [0.0f64]; |
2200 | | fetcher.add_to_f64_scaled(&mut out, 0.5).unwrap(); |
2201 | | fetcher.add_to_f64_scaled(&mut out, 0.5).unwrap(); |
2202 | | assert_eq!(out, [0.5]); |
2203 | | assert!(matches!( |
2204 | | fetcher.add_to_f64_scaled(&mut out, 1.0), |
2205 | | Err(ReadError::OutOfBounds) |
2206 | | )); |
2207 | | } |
2208 | | |
2209 | | #[test] |
2210 | | fn packed_delta_fetcher_skip_then_add_bounded() { |
2211 | | static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]); |
2212 | | // Full decoded stream: [0, 0, 0, 0, 258, -127, -128] |
2213 | | let deltas = PackedDeltas::new(INPUT, 7); |
2214 | | let mut fetcher = deltas.fetcher(); |
2215 | | fetcher.skip(3).unwrap(); |
2216 | | let mut out = [0.0f32; 4]; |
2217 | | fetcher.add_to_f32_scaled(&mut out, 1.0).unwrap(); |
2218 | | assert_eq!(out, [0.0, 258.0, -127.0, -128.0]); |
2219 | | } |
2220 | | |
2221 | | #[test] |
2222 | | fn delta_run_iter_end_exhausts_unbounded_data() { |
2223 | | static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]); |
2224 | | let deltas = PackedDeltas::consume_all(INPUT); |
2225 | | let end = deltas.iter().end(); |
2226 | | assert_eq!(end.remaining_bytes(), 0); |
2227 | | } |
2228 | | |
2229 | | #[test] |
2230 | | fn delta_run_iter_end_respects_bounded_count() { |
2231 | | static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]); |
2232 | | // Count is exactly the first run only (4 zeros), so end() should not consume past |
2233 | | // the run header byte. |
2234 | | let deltas = PackedDeltas::new(INPUT, 4); |
2235 | | let end = deltas.iter().end(); |
2236 | | assert_eq!(end.remaining_bytes(), INPUT.len() - 1); |
2237 | | |
2238 | | let end_via_skip = deltas.iter().skip_fast(4).cursor; |
2239 | | assert_eq!(end_via_skip.remaining_bytes(), INPUT.len() - 1); |
2240 | | } |
2241 | | |
2242 | | #[test] |
2243 | | fn delta_run_iter_end_matches_manual_iteration_for_bounded_data() { |
2244 | | static INPUT: FontData = FontData::new(&[ |
2245 | | 0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34, |
2246 | | ]); |
2247 | | let deltas = PackedDeltas::new(INPUT, 6); |
2248 | | |
2249 | | let iter_collected = deltas.iter().collect::<Vec<_>>(); |
2250 | | assert_eq!(iter_collected.len(), 6); |
2251 | | |
2252 | | let end = deltas.iter().end(); |
2253 | | let end_via_skip = deltas.iter().skip_fast(6).cursor; |
2254 | | assert_eq!(end.remaining_bytes(), end_via_skip.remaining_bytes()); |
2255 | | } |
2256 | | |
2257 | | fn lcg_next(state: &mut u32) -> u32 { |
2258 | | *state = state.wrapping_mul(1664525).wrapping_add(1013904223); |
2259 | | *state |
2260 | | } |
2261 | | |
2262 | | fn generated_delta_stream(seed: u32) -> (Vec<u8>, Vec<i32>) { |
2263 | | let mut state = seed; |
2264 | | let mut bytes = Vec::new(); |
2265 | | let mut expected = Vec::new(); |
2266 | | let run_count = (lcg_next(&mut state) % 6 + 1) as usize; |
2267 | | for _ in 0..run_count { |
2268 | | let run_type = (lcg_next(&mut state) % 4) as usize; |
2269 | | let len = (lcg_next(&mut state) % 8 + 1) as usize; |
2270 | | let control = match run_type { |
2271 | | 0 => (len - 1) as u8, // i8 |
2272 | | 1 => 0x40 | (len - 1) as u8, // i16 |
2273 | | 2 => 0x80 | (len - 1) as u8, // zero |
2274 | | _ => 0xC0 | (len - 1) as u8, // i32 |
2275 | | }; |
2276 | | bytes.push(control); |
2277 | | match run_type { |
2278 | | 0 => { |
2279 | | for _ in 0..len { |
2280 | | let v = ((lcg_next(&mut state) % 255) as i32 - 127) as i8; |
2281 | | bytes.push(v as u8); |
2282 | | expected.push(v as i32); |
2283 | | } |
2284 | | } |
2285 | | 1 => { |
2286 | | for _ in 0..len { |
2287 | | let v = ((lcg_next(&mut state) % 65535) as i32 - 32767) as i16; |
2288 | | bytes.extend(v.to_be_bytes()); |
2289 | | expected.push(v as i32); |
2290 | | } |
2291 | | } |
2292 | | 2 => { |
2293 | | expected.resize(expected.len() + len, 0); |
2294 | | } |
2295 | | _ => { |
2296 | | for _ in 0..len { |
2297 | | let v = (lcg_next(&mut state) % 2_000_001) as i32 - 1_000_000; |
2298 | | bytes.extend(v.to_be_bytes()); |
2299 | | expected.push(v); |
2300 | | } |
2301 | | } |
2302 | | } |
2303 | | } |
2304 | | (bytes, expected) |
2305 | | } |
2306 | | |
2307 | | #[test] |
2308 | | fn generated_packed_deltas_iter_matches_expected() { |
2309 | | for seed in 1..=64 { |
2310 | | let (bytes, expected) = generated_delta_stream(seed); |
2311 | | let data = FontData::new(&bytes); |
2312 | | let deltas = PackedDeltas::consume_all(data); |
2313 | | assert_eq!(deltas.count_or_compute(), expected.len(), "seed={seed}"); |
2314 | | assert_eq!(deltas.iter().collect::<Vec<_>>(), expected, "seed={seed}"); |
2315 | | } |
2316 | | } |
2317 | | |
2318 | | #[test] |
2319 | | fn generated_fetcher_skip_scaled_matches_expected() { |
2320 | | for seed in 1..=64 { |
2321 | | let (bytes, expected) = generated_delta_stream(seed); |
2322 | | let data = FontData::new(&bytes); |
2323 | | let deltas = PackedDeltas::new(data, expected.len()); |
2324 | | let mut fetcher = deltas.fetcher(); |
2325 | | let skip = (seed as usize * 7) % (expected.len() + 1); |
2326 | | fetcher.skip(skip).unwrap(); |
2327 | | |
2328 | | let scale = if seed % 2 == 0 { 0.25 } else { -0.5 }; |
2329 | | let mut out = vec![10.0f32; expected.len() - skip]; |
2330 | | fetcher.add_to_f32_scaled(&mut out, scale).unwrap(); |
2331 | | for (i, got) in out.iter().copied().enumerate() { |
2332 | | let want = 10.0 + expected[skip + i] as f32 * scale; |
2333 | | assert!( |
2334 | | (got - want).abs() <= 1e-6, |
2335 | | "seed={seed} i={i} got={got} want={want}" |
2336 | | ); |
2337 | | } |
2338 | | |
2339 | | // Bounded fetcher should be exhausted after consuming all remaining entries. |
2340 | | let mut extra = [0.0f32; 1]; |
2341 | | assert!(matches!( |
2342 | | fetcher.add_to_f32_scaled(&mut extra, 1.0), |
2343 | | Err(ReadError::OutOfBounds) |
2344 | | )); |
2345 | | } |
2346 | | } |
2347 | | |
2348 | | #[test] |
2349 | | fn packed_point_split() { |
2350 | | static INPUT: FontData = |
2351 | | FontData::new(&[2, 1, 1, 2, 1, 205, 143, 1, 8, 0, 1, 202, 59, 1, 255, 0]); |
2352 | | let (points, data) = PackedPointNumbers::split_off_front(INPUT); |
2353 | | assert_eq!(points.count(), 2); |
2354 | | assert_eq!(points.iter().collect::<Vec<_>>(), &[1, 3]); |
2355 | | assert_eq!(points.total_len(), 4); |
2356 | | assert_eq!(data.len(), INPUT.len() - 4); |
2357 | | } |
2358 | | |
2359 | | #[test] |
2360 | | fn packed_points_dont_panic() { |
2361 | | // a single '0' byte means that there are deltas for all points |
2362 | | static ALL_POINTS: FontData = FontData::new(&[0]); |
2363 | | let (all_points, _) = PackedPointNumbers::split_off_front(ALL_POINTS); |
2364 | | // in which case the iterator just keeps incrementing until u16::MAX |
2365 | | assert_eq!(all_points.iter().count(), u16::MAX as usize); |
2366 | | } |
2367 | | |
2368 | | /// Test that we split properly when the coordinate boundary doesn't align |
2369 | | /// with a packed run boundary |
2370 | | #[test] |
2371 | | fn packed_delta_run_crosses_coord_boundary() { |
2372 | | // 8 deltas with values 0..=7 with a run broken after the first 6; the |
2373 | | // coordinate boundary occurs after the first 4 |
2374 | | static INPUT: FontData = FontData::new(&[ |
2375 | | // first run: 6 deltas as bytes |
2376 | | 5, |
2377 | | 0, |
2378 | | 1, |
2379 | | 2, |
2380 | | 3, |
2381 | | // coordinate boundary is here |
2382 | | 4, |
2383 | | 5, |
2384 | | // second run: 2 deltas as words |
2385 | | 1 | DELTAS_ARE_WORDS, |
2386 | | 0, |
2387 | | 6, |
2388 | | 0, |
2389 | | 7, |
2390 | | ]); |
2391 | | let deltas = PackedDeltas::consume_all(INPUT); |
2392 | | assert_eq!(deltas.count_or_compute(), 8); |
2393 | | let x_deltas = deltas.x_deltas().collect::<Vec<_>>(); |
2394 | | let y_deltas = deltas.y_deltas().collect::<Vec<_>>(); |
2395 | | assert_eq!(x_deltas, [0, 1, 2, 3]); |
2396 | | assert_eq!(y_deltas, [4, 5, 6, 7]); |
2397 | | } |
2398 | | |
2399 | | /// We don't have a reference for our float delta computation, so this is |
2400 | | /// a sanity test to ensure that floating point deltas are within a |
2401 | | /// reasonable margin of the same in fixed point. |
2402 | | #[test] |
2403 | | fn ivs_float_deltas_nearly_match_fixed_deltas() { |
2404 | | let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap(); |
2405 | | let axis_count = font.fvar().unwrap().axis_count() as usize; |
2406 | | let colr = font.colr().unwrap(); |
2407 | | let ivs = colr.item_variation_store().unwrap().unwrap(); |
2408 | | // Generate a set of coords from -1 to 1 in 0.1 increments |
2409 | | for coord in (0..=20).map(|x| F2Dot14::from_f32((x as f32) / 10.0 - 1.0)) { |
2410 | | // For testing purposes, just splat the coord to all axes |
2411 | | let coords = vec![coord; axis_count]; |
2412 | | for (outer_ix, data) in ivs.item_variation_data().iter().enumerate() { |
2413 | | let outer_ix = outer_ix as u16; |
2414 | | let Some(Ok(data)) = data else { |
2415 | | continue; |
2416 | | }; |
2417 | | for inner_ix in 0..data.item_count() { |
2418 | | let delta_ix = DeltaSetIndex { |
2419 | | outer: outer_ix, |
2420 | | inner: inner_ix, |
2421 | | }; |
2422 | | // Check the deltas against all possible target values |
2423 | | let delta = ivs.compute_delta(delta_ix, &coords).unwrap(); |
2424 | | let orig_delta = delta.to_i32(); |
2425 | | let float_delta = delta.to_f64(); |
2426 | | |
2427 | | // For font unit types, we need to accept both rounding and |
2428 | | // truncation to account for the additional accumulation of |
2429 | | // fractional bits in floating point |
2430 | | assert!( |
2431 | | orig_delta == float_delta.round() as i32 |
2432 | | || orig_delta == float_delta.trunc() as i32 |
2433 | | ); |
2434 | | // For the fixed point types, check with an epsilon |
2435 | | const EPSILON: f32 = 1e12; |
2436 | | let fixed_delta = Fixed::ZERO.apply_delta(delta); |
2437 | | assert!((Fixed::from_bits(orig_delta).to_f32() - fixed_delta).abs() < EPSILON); |
2438 | | let f2dot14_delta = F2Dot14::ZERO.apply_delta(delta); |
2439 | | assert!( |
2440 | | (F2Dot14::from_bits(orig_delta as i16).to_f32() - f2dot14_delta).abs() |
2441 | | < EPSILON |
2442 | | ); |
2443 | | } |
2444 | | } |
2445 | | } |
2446 | | } |
2447 | | |
2448 | | #[test] |
2449 | | fn ivs_data_len_short() { |
2450 | | let data = BeBuffer::new() |
2451 | | .push(2u16) // item_count |
2452 | | .push(3u16) // word_delta_count |
2453 | | .push(5u16) // region_index_count |
2454 | | .extend([0u16, 1, 2, 3, 4]) // region_indices |
2455 | | .extend([1u8; 128]); // this is much more data than we need! |
2456 | | |
2457 | | let ivs = ItemVariationData::read(data.data().into()).unwrap(); |
2458 | | let row_len = (3 * u16::RAW_BYTE_LEN) + (2 * u8::RAW_BYTE_LEN); // 3 word deltas, 2 byte deltas |
2459 | | let expected_len = 2 * row_len; |
2460 | | assert_eq!(ivs.delta_sets().len(), expected_len); |
2461 | | } |
2462 | | |
2463 | | #[test] |
2464 | | fn ivs_data_len_long() { |
2465 | | let data = BeBuffer::new() |
2466 | | .push(2u16) // item_count |
2467 | | .push(2u16 | 0x8000) // word_delta_count, long deltas |
2468 | | .push(4u16) // region_index_count |
2469 | | .extend([0u16, 1, 2]) // region_indices |
2470 | | .extend([1u8; 128]); // this is much more data than we need! |
2471 | | |
2472 | | let ivs = ItemVariationData::read(data.data().into()).unwrap(); |
2473 | | let row_len = (2 * u32::RAW_BYTE_LEN) + (2 * u16::RAW_BYTE_LEN); // 1 word (4-byte) delta, 2 short (2-byte) |
2474 | | let expected_len = 2 * row_len; |
2475 | | assert_eq!(ivs.delta_sets().len(), expected_len); |
2476 | | } |
2477 | | |
2478 | | // Add with overflow when accumulating packed point numbers |
2479 | | // https://issues.oss-fuzz.com/issues/378159154 |
2480 | | #[test] |
2481 | | fn packed_point_numbers_avoid_overflow() { |
2482 | | // Lots of 1 bits triggers the behavior quite nicely |
2483 | | let buf = vec![0xFF; 0xFFFF]; |
2484 | | let iter = PackedPointNumbersIter::new(0xFFFF, FontData::new(&buf).cursor()); |
2485 | | // Don't panic! |
2486 | | let _ = iter.count(); |
2487 | | } |
2488 | | |
2489 | | // Dense accumulator should match iterator |
2490 | | #[test] |
2491 | | fn accumulate_dense() { |
2492 | | let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap(); |
2493 | | let gvar = font.gvar().unwrap(); |
2494 | | let gvar_data = gvar.glyph_variation_data(GlyphId::new(1)).unwrap().unwrap(); |
2495 | | let mut count = 0; |
2496 | | for tuple in gvar_data.tuples() { |
2497 | | if !tuple.has_deltas_for_all_points() { |
2498 | | continue; |
2499 | | } |
2500 | | let iter_deltas = tuple |
2501 | | .deltas() |
2502 | | .map(|delta| (delta.x_delta, delta.y_delta)) |
2503 | | .collect::<Vec<_>>(); |
2504 | | let mut delta_buf = vec![Point::broadcast(Fixed::ZERO); iter_deltas.len()]; |
2505 | | tuple |
2506 | | .accumulate_dense_deltas(&mut delta_buf, Fixed::ONE) |
2507 | | .unwrap(); |
2508 | | let accum_deltas = delta_buf |
2509 | | .iter() |
2510 | | .map(|delta| (delta.x.to_i32(), delta.y.to_i32())) |
2511 | | .collect::<Vec<_>>(); |
2512 | | assert_eq!(iter_deltas, accum_deltas); |
2513 | | count += iter_deltas.len(); |
2514 | | } |
2515 | | assert!(count != 0); |
2516 | | } |
2517 | | |
2518 | | // Sparse accumulator should match iterator |
2519 | | #[test] |
2520 | | fn accumulate_sparse() { |
2521 | | let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap(); |
2522 | | let gvar = font.gvar().unwrap(); |
2523 | | let gvar_data = gvar.glyph_variation_data(GlyphId::new(2)).unwrap().unwrap(); |
2524 | | let mut count = 0; |
2525 | | for tuple in gvar_data.tuples() { |
2526 | | if tuple.has_deltas_for_all_points() { |
2527 | | continue; |
2528 | | } |
2529 | | let iter_deltas = tuple.deltas().collect::<Vec<_>>(); |
2530 | | let max_modified_point = iter_deltas |
2531 | | .iter() |
2532 | | .max_by_key(|delta| delta.position) |
2533 | | .unwrap() |
2534 | | .position as usize; |
2535 | | let mut delta_buf = vec![Point::broadcast(Fixed::ZERO); max_modified_point + 1]; |
2536 | | let mut flags = vec![PointFlags::default(); delta_buf.len()]; |
2537 | | tuple |
2538 | | .accumulate_sparse_deltas(&mut delta_buf, &mut flags, Fixed::ONE) |
2539 | | .unwrap(); |
2540 | | let mut accum_deltas = vec![]; |
2541 | | for (i, (delta, flag)) in delta_buf.iter().zip(flags).enumerate() { |
2542 | | if flag.has_marker(PointMarker::HAS_DELTA) { |
2543 | | accum_deltas.push(GlyphDelta::new( |
2544 | | i as u16, |
2545 | | delta.x.to_i32(), |
2546 | | delta.y.to_i32(), |
2547 | | )); |
2548 | | } |
2549 | | } |
2550 | | assert_eq!(iter_deltas, accum_deltas); |
2551 | | count += iter_deltas.len(); |
2552 | | } |
2553 | | assert!(count != 0); |
2554 | | } |
2555 | | |
2556 | | #[test] |
2557 | | fn delta_set_index_map_empty_is_identity() { |
2558 | | let data = BeBuffer::new() |
2559 | | .push(0u8) // format 0 |
2560 | | .push(EntryFormat::empty()) |
2561 | | .push(0u16); // map_count |
2562 | | let map = DeltaSetIndexMap::read(data.data().into()).unwrap(); |
2563 | | assert_eq!( |
2564 | | map.get(0x0001_0002).unwrap(), |
2565 | | DeltaSetIndex { outer: 1, inner: 2 } |
2566 | | ); |
2567 | | } |
2568 | | } |