/src/ttf-parser/src/tables/colr.rs
Line | Count | Source |
1 | | //! A [Color Table]( |
2 | | //! https://docs.microsoft.com/en-us/typography/opentype/spec/colr) implementation. |
3 | | |
4 | | // NOTE: Parts of the implementation have been inspired by |
5 | | // [skrifa](https://github.com/googlefonts/fontations/tree/main/skrifa). |
6 | | |
7 | | #[cfg(feature = "variable-fonts")] |
8 | | use crate::NormalizedCoordinate; |
9 | | #[cfg(feature = "variable-fonts")] |
10 | | use crate::delta_set::DeltaSetIndexMap; |
11 | | use crate::parser::{F2DOT14, FromData, LazyArray16, Offset, Offset24, Offset32, Stream}; |
12 | | #[cfg(feature = "variable-fonts")] |
13 | | use crate::var_store::ItemVariationStore; |
14 | | use crate::{Fixed, LazyArray32, RectF, Transform, cpal}; |
15 | | use crate::{GlyphId, RgbaColor}; |
16 | | |
17 | | /// A [base glyph]( |
18 | | /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyph-and-layer-records). |
19 | | #[derive(Clone, Copy, Debug)] |
20 | | struct BaseGlyphRecord { |
21 | | glyph_id: GlyphId, |
22 | | first_layer_index: u16, |
23 | | num_layers: u16, |
24 | | } |
25 | | |
26 | | /// A [ClipBox](https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyphlist-layerlist-and-cliplist). |
27 | | pub type ClipBox = RectF; |
28 | | |
29 | | /// A paint. |
30 | | #[derive(Clone, Debug)] |
31 | | pub enum Paint<'a> { |
32 | | /// A paint with a solid color. |
33 | | Solid(RgbaColor), |
34 | | /// A paint with a linear gradient. |
35 | | LinearGradient(LinearGradient<'a>), |
36 | | /// A paint with a radial gradient. |
37 | | RadialGradient(RadialGradient<'a>), |
38 | | /// A paint with a sweep gradient. |
39 | | SweepGradient(SweepGradient<'a>), |
40 | | } |
41 | | |
42 | | /// A [clip record]( |
43 | | /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyphlist-layerlist-and-cliplist). |
44 | | #[derive(Clone, Copy, Debug)] |
45 | | struct ClipRecord { |
46 | | /// The first glyph ID for the range covered by this record. |
47 | | pub start_glyph_id: GlyphId, |
48 | | /// The last glyph ID, *inclusive*, for the range covered by this record. |
49 | | pub end_glyph_id: GlyphId, |
50 | | /// The offset to the clip box. |
51 | | pub clip_box_offset: Offset24, |
52 | | } |
53 | | |
54 | | impl FromData for ClipRecord { |
55 | | const SIZE: usize = 7; |
56 | | |
57 | 286k | fn parse(data: &[u8]) -> Option<Self> { |
58 | 286k | let mut s = Stream::new(data); |
59 | | Some(ClipRecord { |
60 | 286k | start_glyph_id: s.read::<GlyphId>()?, |
61 | 286k | end_glyph_id: s.read::<GlyphId>()?, |
62 | 286k | clip_box_offset: s.read::<Offset24>()?, |
63 | | }) |
64 | 286k | } |
65 | | } |
66 | | |
67 | | impl ClipRecord { |
68 | | /// Returns the glyphs range. |
69 | 267k | pub fn glyphs_range(&self) -> core::ops::RangeInclusive<GlyphId> { |
70 | 267k | self.start_glyph_id..=self.end_glyph_id |
71 | 267k | } |
72 | | } |
73 | | |
74 | | /// A [clip list]( |
75 | | /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyphlist-layerlist-and-cliplist). |
76 | | #[derive(Clone, Copy, Debug, Default)] |
77 | | struct ClipList<'a> { |
78 | | data: &'a [u8], |
79 | | records: LazyArray32<'a, ClipRecord>, |
80 | | } |
81 | | |
82 | | impl<'a> ClipList<'a> { |
83 | 18.9k | pub fn get( |
84 | 18.9k | &self, |
85 | 18.9k | index: u32, |
86 | 18.9k | #[cfg(feature = "variable-fonts")] variation_data: &VariationData, |
87 | 18.9k | #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], |
88 | 18.9k | ) -> Option<ClipBox> { |
89 | 18.9k | let record = self.records.get(index)?; |
90 | 18.9k | let offset = record.clip_box_offset.to_usize(); |
91 | 18.9k | self.data.get(offset..).and_then(|data| { |
92 | 13.1k | let mut s = Stream::new(data); |
93 | 13.1k | let format = s.read::<u8>()?; |
94 | | |
95 | | #[cfg(not(feature = "variable-fonts"))] |
96 | | let deltas = [0.0, 0.0, 0.0, 0.0]; |
97 | | #[cfg(feature = "variable-fonts")] |
98 | 12.8k | let deltas = if format == 2 { |
99 | 1.79k | let mut var_s = s.clone(); |
100 | 1.79k | var_s.advance(8); |
101 | 1.79k | let var_index_base = var_s.read::<u32>()?; |
102 | | |
103 | 885 | variation_data.read_deltas::<4>(var_index_base, coords) |
104 | | } else { |
105 | 11.1k | [0.0, 0.0, 0.0, 0.0] |
106 | | }; |
107 | | |
108 | | Some(ClipBox { |
109 | 11.9k | x_min: s.read::<i16>()? as f32 + deltas[0], |
110 | 11.7k | y_min: s.read::<i16>()? as f32 + deltas[1], |
111 | 10.6k | x_max: s.read::<i16>()? as f32 + deltas[2], |
112 | 10.0k | y_max: s.read::<i16>()? as f32 + deltas[3], |
113 | | }) |
114 | 13.1k | }) |
115 | 18.9k | } |
116 | | |
117 | | /// Returns a ClipBox by glyph ID. |
118 | | #[inline] |
119 | 23.5k | pub fn find( |
120 | 23.5k | &self, |
121 | 23.5k | glyph_id: GlyphId, |
122 | 23.5k | #[cfg(feature = "variable-fonts")] variation_data: &VariationData, |
123 | 23.5k | #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], |
124 | 23.5k | ) -> Option<ClipBox> { |
125 | 23.5k | let index = self |
126 | 23.5k | .records |
127 | 23.5k | .into_iter() |
128 | 267k | .position(|v| v.glyphs_range().contains(&glyph_id))?; |
129 | 18.9k | self.get( |
130 | 18.9k | index as u32, |
131 | | #[cfg(feature = "variable-fonts")] |
132 | 18.9k | variation_data, |
133 | | #[cfg(feature = "variable-fonts")] |
134 | 18.9k | coords, |
135 | | ) |
136 | 23.5k | } |
137 | | } |
138 | | |
139 | | impl FromData for BaseGlyphRecord { |
140 | | const SIZE: usize = 6; |
141 | | |
142 | 11.4k | fn parse(data: &[u8]) -> Option<Self> { |
143 | 11.4k | let mut s = Stream::new(data); |
144 | | Some(Self { |
145 | 11.4k | glyph_id: s.read::<GlyphId>()?, |
146 | 11.4k | first_layer_index: s.read::<u16>()?, |
147 | 11.4k | num_layers: s.read::<u16>()?, |
148 | | }) |
149 | 11.4k | } |
150 | | } |
151 | | |
152 | | /// A [layer]( |
153 | | /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyph-and-layer-records). |
154 | | #[derive(Clone, Copy, Debug)] |
155 | | struct LayerRecord { |
156 | | glyph_id: GlyphId, |
157 | | palette_index: u16, |
158 | | } |
159 | | |
160 | | impl FromData for LayerRecord { |
161 | | const SIZE: usize = 4; |
162 | | |
163 | 1.86k | fn parse(data: &[u8]) -> Option<Self> { |
164 | 1.86k | let mut s = Stream::new(data); |
165 | | Some(Self { |
166 | 1.86k | glyph_id: s.read::<GlyphId>()?, |
167 | 1.86k | palette_index: s.read::<u16>()?, |
168 | | }) |
169 | 1.86k | } |
170 | | } |
171 | | |
172 | | /// A [BaseGlyphPaintRecord]( |
173 | | /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyphlist-layerlist-and-cliplist). |
174 | | #[derive(Clone, Copy, Debug)] |
175 | | struct BaseGlyphPaintRecord { |
176 | | glyph_id: GlyphId, |
177 | | paint_table_offset: Offset32, |
178 | | } |
179 | | |
180 | | impl FromData for BaseGlyphPaintRecord { |
181 | | const SIZE: usize = 6; |
182 | | |
183 | 94.0k | fn parse(data: &[u8]) -> Option<Self> { |
184 | 94.0k | let mut s = Stream::new(data); |
185 | | Some(Self { |
186 | 94.0k | glyph_id: s.read::<GlyphId>()?, |
187 | 94.0k | paint_table_offset: s.read::<Offset32>()?, |
188 | | }) |
189 | 94.0k | } |
190 | | } |
191 | | |
192 | | /// A [gradient extend]( |
193 | | /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyphlist-layerlist-and-cliplist). |
194 | | #[derive(Clone, Copy, Debug, PartialEq)] |
195 | | pub enum GradientExtend { |
196 | | /// The `Pad` gradient extend mode. |
197 | | Pad, |
198 | | /// The `Repeat` gradient extend mode. |
199 | | Repeat, |
200 | | /// The `Reflect` gradient extend mode. |
201 | | Reflect, |
202 | | } |
203 | | |
204 | | impl FromData for GradientExtend { |
205 | | const SIZE: usize = 1; |
206 | | |
207 | 293k | fn parse(data: &[u8]) -> Option<Self> { |
208 | 293k | match data[0] { |
209 | 124k | 0 => Some(Self::Pad), |
210 | 28.1k | 1 => Some(Self::Repeat), |
211 | 58 | 2 => Some(Self::Reflect), |
212 | 141k | _ => None, |
213 | | } |
214 | 293k | } |
215 | | } |
216 | | |
217 | | /// A [color stop]( |
218 | | /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#color-references-colorstop-and-colorline). |
219 | | #[derive(Clone, Copy, Debug)] |
220 | | struct ColorStopRaw { |
221 | | stop_offset: F2DOT14, |
222 | | palette_index: u16, |
223 | | alpha: F2DOT14, |
224 | | } |
225 | | |
226 | | impl FromData for ColorStopRaw { |
227 | | const SIZE: usize = 6; |
228 | | |
229 | 2.23M | fn parse(data: &[u8]) -> Option<Self> { |
230 | 2.23M | let mut s = Stream::new(data); |
231 | | Some(Self { |
232 | 2.23M | stop_offset: s.read::<F2DOT14>()?, |
233 | 2.23M | palette_index: s.read::<u16>()?, |
234 | 2.23M | alpha: s.read::<F2DOT14>()?, |
235 | | }) |
236 | 2.23M | } |
237 | | } |
238 | | |
239 | | /// A [var color stop]( |
240 | | /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#color-references-colorstop-and-colorline). |
241 | | #[cfg(feature = "variable-fonts")] |
242 | | #[derive(Clone, Copy, Debug)] |
243 | | struct VarColorStopRaw { |
244 | | stop_offset: F2DOT14, |
245 | | palette_index: u16, |
246 | | alpha: F2DOT14, |
247 | | var_index_base: u32, |
248 | | } |
249 | | |
250 | | #[cfg(feature = "variable-fonts")] |
251 | | impl FromData for VarColorStopRaw { |
252 | | const SIZE: usize = 10; |
253 | | |
254 | 142k | fn parse(data: &[u8]) -> Option<Self> { |
255 | 142k | let mut s = Stream::new(data); |
256 | | Some(Self { |
257 | 142k | stop_offset: s.read::<F2DOT14>()?, |
258 | 142k | palette_index: s.read::<u16>()?, |
259 | 142k | alpha: s.read::<F2DOT14>()?, |
260 | 142k | var_index_base: s.read::<u32>()?, |
261 | | }) |
262 | 142k | } |
263 | | } |
264 | | |
265 | | #[derive(Clone)] |
266 | | struct NonVarColorLine<'a> { |
267 | | extend: GradientExtend, |
268 | | colors: LazyArray16<'a, ColorStopRaw>, |
269 | | palettes: cpal::Table<'a>, |
270 | | foreground_color: RgbaColor, |
271 | | } |
272 | | |
273 | | impl NonVarColorLine<'_> { |
274 | | // TODO: Color stops should be sorted, but hard to do without allocations |
275 | 2.23M | fn get(&self, palette: u16, index: u16) -> Option<ColorStop> { |
276 | 2.23M | let info = self.colors.get(index)?; |
277 | | |
278 | 2.23M | let mut color = if info.palette_index == u16::MAX { |
279 | 59.3k | self.foreground_color |
280 | | } else { |
281 | 2.18M | self.palettes.get(palette, info.palette_index)? |
282 | | }; |
283 | | |
284 | 2.19M | color.apply_alpha(info.alpha.to_f32()); |
285 | 2.19M | Some(ColorStop { |
286 | 2.19M | stop_offset: info.stop_offset.to_f32(), |
287 | 2.19M | color, |
288 | 2.19M | }) |
289 | 2.23M | } |
290 | | } |
291 | | |
292 | | #[cfg(feature = "variable-fonts")] |
293 | | impl VarColorLine<'_> { |
294 | | // TODO: Color stops should be sorted, but hard to do without allocations |
295 | 142k | fn get( |
296 | 142k | &self, |
297 | 142k | palette: u16, |
298 | 142k | index: u16, |
299 | 142k | #[cfg(feature = "variable-fonts")] variation_data: VariationData, |
300 | 142k | #[cfg(feature = "variable-fonts")] coordinates: &[NormalizedCoordinate], |
301 | 142k | ) -> Option<ColorStop> { |
302 | 142k | let info = self.colors.get(index)?; |
303 | | |
304 | 142k | let mut color = if info.palette_index == u16::MAX { |
305 | 9.67k | self.foreground_color |
306 | | } else { |
307 | 133k | self.palettes.get(palette, info.palette_index)? |
308 | | }; |
309 | | |
310 | 119k | let deltas = variation_data.read_deltas::<2>(info.var_index_base, coordinates); |
311 | 119k | let stop_offset = info.stop_offset.apply_float_delta(deltas[0]); |
312 | 119k | color.apply_alpha(info.alpha.apply_float_delta(deltas[1])); |
313 | | |
314 | 119k | Some(ColorStop { stop_offset, color }) |
315 | 142k | } |
316 | | } |
317 | | |
318 | | #[cfg(feature = "variable-fonts")] |
319 | | #[derive(Clone)] |
320 | | struct VarColorLine<'a> { |
321 | | extend: GradientExtend, |
322 | | colors: LazyArray16<'a, VarColorStopRaw>, |
323 | | palettes: cpal::Table<'a>, |
324 | | foreground_color: RgbaColor, |
325 | | } |
326 | | |
327 | | #[derive(Clone)] |
328 | | enum ColorLine<'a> { |
329 | | #[cfg(feature = "variable-fonts")] |
330 | | VarColorLine(VarColorLine<'a>), |
331 | | NonVarColorLine(NonVarColorLine<'a>), |
332 | | } |
333 | | |
334 | | /// A [gradient extend]( |
335 | | /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyphlist-layerlist-and-cliplist). |
336 | | #[derive(Clone, Copy, Debug)] |
337 | | pub struct ColorStop { |
338 | | /// The offset of the color stop. |
339 | | pub stop_offset: f32, |
340 | | /// The color of the color stop. |
341 | | pub color: RgbaColor, |
342 | | } |
343 | | |
344 | | /// A [linear gradient](https://learn.microsoft.com/en-us/typography/opentype/spec/colr#formats-4-and-5-paintlineargradient-paintvarlineargradient) |
345 | | #[derive(Clone)] |
346 | | pub struct LinearGradient<'a> { |
347 | | /// The `x0` value. |
348 | | pub x0: f32, |
349 | | /// The `y0` value. |
350 | | pub y0: f32, |
351 | | /// The `x1` value. |
352 | | pub x1: f32, |
353 | | /// The `y1` value. |
354 | | pub y1: f32, |
355 | | /// The `x2` value. |
356 | | pub x2: f32, |
357 | | /// The `y2` value. |
358 | | pub y2: f32, |
359 | | /// The extend. |
360 | | pub extend: GradientExtend, |
361 | | #[cfg(feature = "variable-fonts")] |
362 | | variation_data: VariationData<'a>, |
363 | | color_line: ColorLine<'a>, |
364 | | } |
365 | | |
366 | | impl<'a> core::fmt::Debug for LinearGradient<'a> { |
367 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
368 | 0 | f.debug_struct("LinearGradient") |
369 | 0 | .field("x0", &self.x0) |
370 | 0 | .field("y0", &self.y0) |
371 | 0 | .field("x1", &self.x1) |
372 | 0 | .field("y1", &self.y1) |
373 | 0 | .field("x2", &self.x2) |
374 | 0 | .field("y2", &self.y2) |
375 | 0 | .field("extend", &self.extend) |
376 | 0 | .field( |
377 | 0 | "stops", |
378 | 0 | &self.stops( |
379 | 0 | 0, |
380 | 0 | #[cfg(feature = "variable-fonts")] |
381 | 0 | &[], |
382 | 0 | ), |
383 | 0 | ) |
384 | 0 | .finish() |
385 | 0 | } |
386 | | } |
387 | | |
388 | | impl<'a> LinearGradient<'a> { |
389 | | /// Returns an iterator over the stops of the linear gradient. Stops need to be sorted |
390 | | /// manually by the caller. |
391 | 38.4k | pub fn stops<'b>( |
392 | 38.4k | &'b self, |
393 | 38.4k | palette: u16, |
394 | 38.4k | #[cfg(feature = "variable-fonts")] coords: &'b [NormalizedCoordinate], |
395 | 38.4k | ) -> GradientStopsIter<'a, 'b> { |
396 | 38.4k | GradientStopsIter { |
397 | 38.4k | color_line: &self.color_line, |
398 | 38.4k | palette, |
399 | 38.4k | index: 0, |
400 | 38.4k | #[cfg(feature = "variable-fonts")] |
401 | 38.4k | variation_data: self.variation_data, |
402 | 38.4k | #[cfg(feature = "variable-fonts")] |
403 | 38.4k | coords, |
404 | 38.4k | } |
405 | 38.4k | } |
406 | | } |
407 | | |
408 | | /// A [radial gradient](https://learn.microsoft.com/en-us/typography/opentype/spec/colr#formats-6-and-7-paintradialgradient-paintvarradialgradient) |
409 | | #[derive(Clone)] |
410 | | pub struct RadialGradient<'a> { |
411 | | /// The `x0` value. |
412 | | pub x0: f32, |
413 | | /// The `y0` value. |
414 | | pub y0: f32, |
415 | | /// The `r0` value. |
416 | | pub r0: f32, |
417 | | /// The `r1` value. |
418 | | pub r1: f32, |
419 | | /// The `x1` value. |
420 | | pub x1: f32, |
421 | | /// The `y1` value. |
422 | | pub y1: f32, |
423 | | /// The extend. |
424 | | pub extend: GradientExtend, |
425 | | #[cfg(feature = "variable-fonts")] |
426 | | variation_data: VariationData<'a>, |
427 | | color_line: ColorLine<'a>, |
428 | | } |
429 | | |
430 | | impl<'a> core::fmt::Debug for RadialGradient<'a> { |
431 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
432 | 0 | f.debug_struct("RadialGradient") |
433 | 0 | .field("x0", &self.x0) |
434 | 0 | .field("y0", &self.y0) |
435 | 0 | .field("r0", &self.r0) |
436 | 0 | .field("r1", &self.r1) |
437 | 0 | .field("x1", &self.x1) |
438 | 0 | .field("y1", &self.y1) |
439 | 0 | .field("extend", &self.extend) |
440 | 0 | .field( |
441 | 0 | "stops", |
442 | 0 | &self.stops( |
443 | 0 | 0, |
444 | 0 | #[cfg(feature = "variable-fonts")] |
445 | 0 | &[], |
446 | 0 | ), |
447 | 0 | ) |
448 | 0 | .finish() |
449 | 0 | } |
450 | | } |
451 | | |
452 | | impl<'a> RadialGradient<'a> { |
453 | | /// Returns an iterator over the stops of the radial gradient. Stops need to be sorted |
454 | | /// manually by the caller. |
455 | 49.9k | pub fn stops<'b>( |
456 | 49.9k | &'b self, |
457 | 49.9k | palette: u16, |
458 | 49.9k | #[cfg(feature = "variable-fonts")] coords: &'a [NormalizedCoordinate], |
459 | 49.9k | ) -> GradientStopsIter<'a, 'b> { |
460 | 49.9k | GradientStopsIter { |
461 | 49.9k | color_line: &self.color_line, |
462 | 49.9k | palette, |
463 | 49.9k | index: 0, |
464 | 49.9k | #[cfg(feature = "variable-fonts")] |
465 | 49.9k | variation_data: self.variation_data, |
466 | 49.9k | #[cfg(feature = "variable-fonts")] |
467 | 49.9k | coords, |
468 | 49.9k | } |
469 | 49.9k | } |
470 | | } |
471 | | |
472 | | /// A [sweep gradient](https://learn.microsoft.com/en-us/typography/opentype/spec/colr#formats-8-and-9-paintsweepgradient-paintvarsweepgradient) |
473 | | #[derive(Clone)] |
474 | | pub struct SweepGradient<'a> { |
475 | | /// The x of the center. |
476 | | pub center_x: f32, |
477 | | /// The y of the center. |
478 | | pub center_y: f32, |
479 | | /// The start angle. |
480 | | pub start_angle: f32, |
481 | | /// The end angle. |
482 | | pub end_angle: f32, |
483 | | /// The extend. |
484 | | pub extend: GradientExtend, |
485 | | #[cfg(feature = "variable-fonts")] |
486 | | variation_data: VariationData<'a>, |
487 | | color_line: ColorLine<'a>, |
488 | | } |
489 | | |
490 | | impl<'a> core::fmt::Debug for SweepGradient<'a> { |
491 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
492 | 0 | f.debug_struct("SweepGradient") |
493 | 0 | .field("center_x", &self.center_x) |
494 | 0 | .field("center_y", &self.center_y) |
495 | 0 | .field("start_angle", &self.start_angle) |
496 | 0 | .field("end_angle", &self.end_angle) |
497 | 0 | .field("extend", &self.extend) |
498 | 0 | .field( |
499 | 0 | "stops", |
500 | 0 | &self.stops( |
501 | 0 | 0, |
502 | 0 | #[cfg(feature = "variable-fonts")] |
503 | 0 | &[], |
504 | 0 | ), |
505 | 0 | ) |
506 | 0 | .finish() |
507 | 0 | } |
508 | | } |
509 | | |
510 | | impl<'a> SweepGradient<'a> { |
511 | | // TODO: Make API nicer so that variable coordinates don't |
512 | | // need to be passed by the caller (same for radial and linear gradient) |
513 | | /// Returns an iterator over the stops of the sweep gradient. Stops need to be sorted |
514 | | /// manually by the caller. |
515 | 21.3k | pub fn stops<'b>( |
516 | 21.3k | &'b self, |
517 | 21.3k | palette: u16, |
518 | 21.3k | #[cfg(feature = "variable-fonts")] coords: &'a [NormalizedCoordinate], |
519 | 21.3k | ) -> GradientStopsIter<'a, 'b> { |
520 | 21.3k | GradientStopsIter { |
521 | 21.3k | color_line: &self.color_line, |
522 | 21.3k | palette, |
523 | 21.3k | index: 0, |
524 | 21.3k | #[cfg(feature = "variable-fonts")] |
525 | 21.3k | variation_data: self.variation_data, |
526 | 21.3k | #[cfg(feature = "variable-fonts")] |
527 | 21.3k | coords, |
528 | 21.3k | } |
529 | 21.3k | } |
530 | | } |
531 | | |
532 | | /// An iterator over stops of a gradient. |
533 | | #[derive(Clone, Copy)] |
534 | | pub struct GradientStopsIter<'a, 'b> { |
535 | | color_line: &'b ColorLine<'a>, |
536 | | palette: u16, |
537 | | index: u16, |
538 | | #[cfg(feature = "variable-fonts")] |
539 | | variation_data: VariationData<'a>, |
540 | | #[cfg(feature = "variable-fonts")] |
541 | | coords: &'b [NormalizedCoordinate], |
542 | | } |
543 | | |
544 | | impl Iterator for GradientStopsIter<'_, '_> { |
545 | | type Item = ColorStop; |
546 | | |
547 | 2.42M | fn next(&mut self) -> Option<Self::Item> { |
548 | 2.42M | let len = match self.color_line { |
549 | | #[cfg(feature = "variable-fonts")] |
550 | 156k | ColorLine::VarColorLine(vcl) => vcl.colors.len(), |
551 | 2.27M | ColorLine::NonVarColorLine(nvcl) => nvcl.colors.len(), |
552 | | }; |
553 | | |
554 | 2.42M | if self.index == len { |
555 | 45.5k | return None; |
556 | 2.38M | } |
557 | | |
558 | 2.38M | let index = self.index; |
559 | 2.38M | self.index = self.index.checked_add(1)?; |
560 | | |
561 | 2.38M | match self.color_line { |
562 | | #[cfg(feature = "variable-fonts")] |
563 | 142k | ColorLine::VarColorLine(vcl) => { |
564 | 142k | vcl.get(self.palette, index, self.variation_data, self.coords) |
565 | | } |
566 | 2.23M | ColorLine::NonVarColorLine(nvcl) => nvcl.get(self.palette, index), |
567 | | } |
568 | 2.42M | } |
569 | | } |
570 | | |
571 | | impl core::fmt::Debug for GradientStopsIter<'_, '_> { |
572 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
573 | 0 | f.debug_list().entries(*self).finish() |
574 | 0 | } |
575 | | } |
576 | | |
577 | | /// A [composite mode](https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite) |
578 | | #[derive(Clone, Copy, PartialEq, Debug)] |
579 | | pub enum CompositeMode { |
580 | | /// The composite mode 'Clear'. |
581 | | Clear, |
582 | | /// The composite mode 'Source'. |
583 | | Source, |
584 | | /// The composite mode 'Destination'. |
585 | | Destination, |
586 | | /// The composite mode 'SourceOver'. |
587 | | SourceOver, |
588 | | /// The composite mode 'DestinationOver'. |
589 | | DestinationOver, |
590 | | /// The composite mode 'SourceIn'. |
591 | | SourceIn, |
592 | | /// The composite mode 'DestinationIn'. |
593 | | DestinationIn, |
594 | | /// The composite mode 'SourceOut'. |
595 | | SourceOut, |
596 | | /// The composite mode 'DestinationOut'. |
597 | | DestinationOut, |
598 | | /// The composite mode 'SourceAtop'. |
599 | | SourceAtop, |
600 | | /// The composite mode 'DestinationAtop'. |
601 | | DestinationAtop, |
602 | | /// The composite mode 'Xor'. |
603 | | Xor, |
604 | | /// The composite mode 'Plus'. |
605 | | Plus, |
606 | | /// The composite mode 'Screen'. |
607 | | Screen, |
608 | | /// The composite mode 'Overlay'. |
609 | | Overlay, |
610 | | /// The composite mode 'Darken'. |
611 | | Darken, |
612 | | /// The composite mode 'Lighten'. |
613 | | Lighten, |
614 | | /// The composite mode 'ColorDodge'. |
615 | | ColorDodge, |
616 | | /// The composite mode 'ColorBurn'. |
617 | | ColorBurn, |
618 | | /// The composite mode 'HardLight'. |
619 | | HardLight, |
620 | | /// The composite mode 'SoftLight'. |
621 | | SoftLight, |
622 | | /// The composite mode 'Difference'. |
623 | | Difference, |
624 | | /// The composite mode 'Exclusion'. |
625 | | Exclusion, |
626 | | /// The composite mode 'Multiply'. |
627 | | Multiply, |
628 | | /// The composite mode 'Hue'. |
629 | | Hue, |
630 | | /// The composite mode 'Saturation'. |
631 | | Saturation, |
632 | | /// The composite mode 'Color'. |
633 | | Color, |
634 | | /// The composite mode 'Luminosity'. |
635 | | Luminosity, |
636 | | } |
637 | | |
638 | | impl FromData for CompositeMode { |
639 | | const SIZE: usize = 1; |
640 | | |
641 | 1.04M | fn parse(data: &[u8]) -> Option<Self> { |
642 | 1.04M | match data[0] { |
643 | 890k | 0 => Some(Self::Clear), |
644 | 787 | 1 => Some(Self::Source), |
645 | 1.02k | 2 => Some(Self::Destination), |
646 | 136 | 3 => Some(Self::SourceOver), |
647 | 75.2k | 4 => Some(Self::DestinationOver), |
648 | 4 | 5 => Some(Self::SourceIn), |
649 | 0 | 6 => Some(Self::DestinationIn), |
650 | 19 | 7 => Some(Self::SourceOut), |
651 | 2 | 8 => Some(Self::DestinationOut), |
652 | 0 | 9 => Some(Self::SourceAtop), |
653 | 0 | 10 => Some(Self::DestinationAtop), |
654 | 0 | 11 => Some(Self::Xor), |
655 | 0 | 12 => Some(Self::Plus), |
656 | 276 | 13 => Some(Self::Screen), |
657 | 0 | 14 => Some(Self::Overlay), |
658 | 472 | 15 => Some(Self::Darken), |
659 | 31 | 16 => Some(Self::Lighten), |
660 | 583 | 17 => Some(Self::ColorDodge), |
661 | 15 | 18 => Some(Self::ColorBurn), |
662 | 0 | 19 => Some(Self::HardLight), |
663 | 0 | 20 => Some(Self::SoftLight), |
664 | 493 | 21 => Some(Self::Difference), |
665 | 239 | 22 => Some(Self::Exclusion), |
666 | 1 | 23 => Some(Self::Multiply), |
667 | 0 | 24 => Some(Self::Hue), |
668 | 143 | 25 => Some(Self::Saturation), |
669 | 315 | 26 => Some(Self::Color), |
670 | 31 | 27 => Some(Self::Luminosity), |
671 | 75.3k | _ => None, |
672 | | } |
673 | 1.04M | } |
674 | | } |
675 | | |
676 | | /// A trait for color glyph painting. |
677 | | /// |
678 | | /// See [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) for details. |
679 | | pub trait Painter<'a> { |
680 | | /// Outline a glyph and store it. |
681 | | fn outline_glyph(&mut self, glyph_id: GlyphId); |
682 | | /// Paint the stored outline using the provided color. |
683 | | fn paint(&mut self, paint: Paint<'a>); |
684 | | |
685 | | /// Push a new clip path using the currently stored outline. |
686 | | fn push_clip(&mut self); |
687 | | |
688 | | /// Push a new clip path using the clip box. |
689 | | fn push_clip_box(&mut self, clipbox: ClipBox); |
690 | | /// Pop the last clip path. |
691 | | fn pop_clip(&mut self); |
692 | | |
693 | | /// Push a new layer with the given composite mode. |
694 | | fn push_layer(&mut self, mode: CompositeMode); |
695 | | /// Pop the last layer. |
696 | | fn pop_layer(&mut self); |
697 | | /// Push a transform. |
698 | | fn push_transform(&mut self, transform: Transform); |
699 | | /// Pop the last transform. |
700 | | fn pop_transform(&mut self); |
701 | | } |
702 | | |
703 | | /// A [Color Table]( |
704 | | /// https://docs.microsoft.com/en-us/typography/opentype/spec/colr). |
705 | | /// |
706 | | /// Currently, only version 0 is supported. |
707 | | #[derive(Clone, Copy, Debug)] |
708 | | pub struct Table<'a> { |
709 | | pub(crate) palettes: cpal::Table<'a>, |
710 | | data: &'a [u8], |
711 | | version: u8, |
712 | | // v0 |
713 | | base_glyphs: LazyArray16<'a, BaseGlyphRecord>, |
714 | | layers: LazyArray16<'a, LayerRecord>, |
715 | | // v1 |
716 | | base_glyph_paints_offset: Offset32, |
717 | | base_glyph_paints: LazyArray32<'a, BaseGlyphPaintRecord>, |
718 | | layer_paint_offsets_offset: Offset32, |
719 | | layer_paint_offsets: LazyArray32<'a, Offset32>, |
720 | | clip_list_offsets_offset: Offset32, |
721 | | clip_list: ClipList<'a>, |
722 | | #[cfg(feature = "variable-fonts")] |
723 | | var_index_map: Option<DeltaSetIndexMap<'a>>, |
724 | | #[cfg(feature = "variable-fonts")] |
725 | | item_variation_store: Option<ItemVariationStore<'a>>, |
726 | | } |
727 | | |
728 | | impl<'a> Table<'a> { |
729 | | /// Parses a table from raw data. |
730 | 7.15k | pub fn parse(palettes: cpal::Table<'a>, data: &'a [u8]) -> Option<Self> { |
731 | 7.15k | let mut s = Stream::new(data); |
732 | | |
733 | 7.15k | let version = s.read::<u16>()?; |
734 | 7.14k | if version > 1 { |
735 | 7 | return None; |
736 | 7.14k | } |
737 | | |
738 | 7.14k | let num_base_glyphs = s.read::<u16>()?; |
739 | 7.13k | let base_glyphs_offset = s.read::<Offset32>()?; |
740 | 7.13k | let layers_offset = s.read::<Offset32>()?; |
741 | 7.12k | let num_layers = s.read::<u16>()?; |
742 | | |
743 | 7.11k | let base_glyphs = Stream::new_at(data, base_glyphs_offset.to_usize())? |
744 | 6.89k | .read_array16::<BaseGlyphRecord>(num_base_glyphs)?; |
745 | | |
746 | 6.83k | let layers = Stream::new_at(data, layers_offset.to_usize())? |
747 | 6.76k | .read_array16::<LayerRecord>(num_layers)?; |
748 | | |
749 | 6.67k | let mut table = Self { |
750 | 6.67k | version: version as u8, |
751 | 6.67k | data, |
752 | 6.67k | palettes, |
753 | 6.67k | base_glyphs, |
754 | 6.67k | layers, |
755 | 6.67k | base_glyph_paints_offset: Offset32(0), // the actual value doesn't matter |
756 | 6.67k | base_glyph_paints: LazyArray32::default(), |
757 | 6.67k | layer_paint_offsets_offset: Offset32(0), |
758 | 6.67k | layer_paint_offsets: LazyArray32::default(), |
759 | 6.67k | clip_list_offsets_offset: Offset32(0), |
760 | 6.67k | clip_list: ClipList::default(), |
761 | 6.67k | #[cfg(feature = "variable-fonts")] |
762 | 6.67k | item_variation_store: None, |
763 | 6.67k | #[cfg(feature = "variable-fonts")] |
764 | 6.67k | var_index_map: None, |
765 | 6.67k | }; |
766 | | |
767 | 6.67k | if version == 0 { |
768 | 100 | return Some(table); |
769 | 6.57k | } |
770 | | |
771 | 6.57k | table.base_glyph_paints_offset = s.read::<Offset32>()?; |
772 | 6.57k | let layer_list_offset = s.read::<Option<Offset32>>()?; |
773 | 6.56k | let clip_list_offset = s.read::<Option<Offset32>>()?; |
774 | | #[cfg(feature = "variable-fonts")] |
775 | 6.55k | let var_index_map_offset = s.read::<Option<Offset32>>()?; |
776 | | #[cfg(feature = "variable-fonts")] |
777 | 6.55k | let item_variation_offset = s.read::<Option<Offset32>>()?; |
778 | | |
779 | | { |
780 | 6.54k | let mut s = Stream::new_at(data, table.base_glyph_paints_offset.to_usize())?; |
781 | 6.36k | let count = s.read::<u32>()?; |
782 | 6.36k | table.base_glyph_paints = s.read_array32::<BaseGlyphPaintRecord>(count)?; |
783 | | } |
784 | | |
785 | 6.11k | if let Some(offset) = layer_list_offset { |
786 | 5.30k | table.layer_paint_offsets_offset = offset; |
787 | 5.30k | let mut s = Stream::new_at(data, offset.to_usize())?; |
788 | 5.16k | let count = s.read::<u32>()?; |
789 | 5.15k | table.layer_paint_offsets = s.read_array32::<Offset32>(count)?; |
790 | 807 | } |
791 | | |
792 | 5.78k | if let Some(offset) = clip_list_offset { |
793 | 5.49k | table.clip_list_offsets_offset = offset; |
794 | 5.49k | let clip_data = data.get(offset.to_usize()..)?; |
795 | 5.40k | let mut s = Stream::new(clip_data); |
796 | 5.40k | s.skip::<u8>(); // Format |
797 | 5.40k | let count = s.read::<u32>()?; |
798 | | table.clip_list = ClipList { |
799 | 5.39k | data: clip_data, |
800 | 5.39k | records: s.read_array32::<ClipRecord>(count)?, |
801 | | }; |
802 | 290 | } |
803 | | |
804 | | #[cfg(feature = "variable-fonts")] |
805 | | { |
806 | 5.47k | if let Some(offset) = item_variation_offset { |
807 | 444 | let item_var_data = data.get(offset.to_usize()..)?; |
808 | 339 | let s = Stream::new(item_var_data); |
809 | 339 | let var_store = ItemVariationStore::parse(s)?; |
810 | 169 | table.item_variation_store = Some(var_store); |
811 | 5.02k | } |
812 | | } |
813 | | |
814 | | #[cfg(feature = "variable-fonts")] |
815 | | { |
816 | 5.19k | if let Some(offset) = var_index_map_offset { |
817 | 4.97k | let var_index_map_data = data.get(offset.to_usize()..)?; |
818 | 4.89k | let var_index_map = DeltaSetIndexMap::new(var_index_map_data); |
819 | 4.89k | table.var_index_map = Some(var_index_map); |
820 | 228 | } |
821 | | } |
822 | | |
823 | 5.12k | Some(table) |
824 | 7.15k | } |
825 | | |
826 | | /// Returns `true` if the current table has version 0. |
827 | | /// |
828 | | /// A simple table can only emit `outline_glyph`, `paint`, `push_clip`, and |
829 | | /// `pop_clip` [`Painter`] methods. |
830 | 0 | pub fn is_simple(&self) -> bool { |
831 | 0 | self.version == 0 |
832 | 0 | } |
833 | | |
834 | 6.97k | fn get_v0(&self, glyph_id: GlyphId) -> Option<BaseGlyphRecord> { |
835 | 6.97k | self.base_glyphs |
836 | 11.4k | .binary_search_by(|base| base.glyph_id.cmp(&glyph_id)) |
837 | 6.97k | .map(|v| v.1) |
838 | 6.97k | } |
839 | | |
840 | 30.5k | fn get_v1(&self, glyph_id: GlyphId) -> Option<BaseGlyphPaintRecord> { |
841 | 30.5k | self.base_glyph_paints |
842 | 94.0k | .binary_search_by(|base| base.glyph_id.cmp(&glyph_id)) |
843 | 30.5k | .map(|v| v.1) |
844 | 30.5k | } |
845 | | |
846 | | #[cfg(feature = "variable-fonts")] |
847 | 300k | fn variation_data(&self) -> VariationData<'a> { |
848 | 300k | VariationData { |
849 | 300k | variation_store: self.item_variation_store, |
850 | 300k | delta_map: self.var_index_map, |
851 | 300k | } |
852 | 300k | } |
853 | | |
854 | | /// Whether the table contains a definition for the given glyph. |
855 | 0 | pub fn contains(&self, glyph_id: GlyphId) -> bool { |
856 | 0 | self.get_v1(glyph_id).is_some() || self.get_v0(glyph_id).is_some() |
857 | 0 | } |
858 | | |
859 | | /// Returns the clip box for a glyph. |
860 | 23.5k | pub fn clip_box( |
861 | 23.5k | &self, |
862 | 23.5k | glyph_id: GlyphId, |
863 | 23.5k | #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], |
864 | 23.5k | ) -> Option<ClipBox> { |
865 | 23.5k | self.clip_list.find( |
866 | 23.5k | glyph_id, |
867 | | #[cfg(feature = "variable-fonts")] |
868 | 23.5k | &self.variation_data(), |
869 | | #[cfg(feature = "variable-fonts")] |
870 | 23.5k | coords, |
871 | | ) |
872 | 23.5k | } |
873 | | |
874 | | // This method should only be called from outside, not from within `colr.rs`. |
875 | | // From inside, you always should call paint_impl, so that the recursion stack can |
876 | | // be passed on and any kind of recursion can be prevented. |
877 | | /// Paints the color glyph. |
878 | 5.17k | pub fn paint( |
879 | 5.17k | &self, |
880 | 5.17k | glyph_id: GlyphId, |
881 | 5.17k | palette: u16, |
882 | 5.17k | painter: &mut dyn Painter<'a>, |
883 | 5.17k | #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], |
884 | 5.17k | foreground_color: RgbaColor, |
885 | 5.17k | ) -> Option<()> { |
886 | 5.17k | let mut recursion_stack = RecursionStack { |
887 | 5.17k | stack: [0; 64], |
888 | 5.17k | len: 0, |
889 | 5.17k | visits_left: MAX_PAINT_VISITS, |
890 | 5.17k | }; |
891 | | |
892 | 5.17k | self.paint_impl( |
893 | 5.17k | glyph_id, |
894 | 5.17k | palette, |
895 | 5.17k | painter, |
896 | 5.17k | &mut recursion_stack, |
897 | | #[cfg(feature = "variable-fonts")] |
898 | 5.17k | coords, |
899 | 5.17k | foreground_color, |
900 | | ) |
901 | 5.17k | } |
902 | | |
903 | 30.5k | fn paint_impl( |
904 | 30.5k | &self, |
905 | 30.5k | glyph_id: GlyphId, |
906 | 30.5k | palette: u16, |
907 | 30.5k | painter: &mut dyn Painter<'a>, |
908 | 30.5k | recursion_stack: &mut RecursionStack, |
909 | 30.5k | #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], |
910 | 30.5k | foreground_color: RgbaColor, |
911 | 30.5k | ) -> Option<()> { |
912 | 30.5k | if let Some(base) = self.get_v1(glyph_id) { |
913 | 23.5k | self.paint_v1( |
914 | 23.5k | base, |
915 | 23.5k | palette, |
916 | 23.5k | painter, |
917 | 23.5k | recursion_stack, |
918 | | #[cfg(feature = "variable-fonts")] |
919 | 23.5k | coords, |
920 | 23.5k | foreground_color, |
921 | | ) |
922 | 6.97k | } else if let Some(base) = self.get_v0(glyph_id) { |
923 | 2.44k | self.paint_v0(base, palette, painter, foreground_color) |
924 | | } else { |
925 | 4.52k | None |
926 | | } |
927 | 30.5k | } |
928 | | |
929 | 2.44k | fn paint_v0( |
930 | 2.44k | &self, |
931 | 2.44k | base: BaseGlyphRecord, |
932 | 2.44k | palette: u16, |
933 | 2.44k | painter: &mut dyn Painter, |
934 | 2.44k | foreground_color: RgbaColor, |
935 | 2.44k | ) -> Option<()> { |
936 | 2.44k | let start = base.first_layer_index; |
937 | 2.44k | let end = start.checked_add(base.num_layers)?; |
938 | 1.99k | let layers = self.layers.slice(start..end)?; |
939 | | |
940 | 2.47k | for layer in layers { |
941 | 1.86k | painter.outline_glyph(layer.glyph_id); |
942 | 1.86k | painter.push_clip(); |
943 | 1.86k | if layer.palette_index == 0xFFFF { |
944 | 618 | // A special case. |
945 | 618 | painter.paint(Paint::Solid(foreground_color)); |
946 | 618 | } else { |
947 | 1.24k | let color = self.palettes.get(palette, layer.palette_index)?; |
948 | 542 | painter.paint(Paint::Solid(color)); |
949 | | } |
950 | 1.16k | painter.pop_clip(); |
951 | | } |
952 | | |
953 | 618 | Some(()) |
954 | 2.44k | } |
955 | | |
956 | 23.5k | fn paint_v1( |
957 | 23.5k | &self, |
958 | 23.5k | base: BaseGlyphPaintRecord, |
959 | 23.5k | palette: u16, |
960 | 23.5k | painter: &mut dyn Painter<'a>, |
961 | 23.5k | recursion_stack: &mut RecursionStack, |
962 | 23.5k | #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], |
963 | 23.5k | foreground_color: RgbaColor, |
964 | 23.5k | ) -> Option<()> { |
965 | 23.5k | let clip_box = self.clip_box( |
966 | 23.5k | base.glyph_id, |
967 | | #[cfg(feature = "variable-fonts")] |
968 | 23.5k | coords, |
969 | | ); |
970 | 23.5k | if let Some(clip_box) = clip_box { |
971 | 9.69k | painter.push_clip_box(clip_box); |
972 | 13.8k | } |
973 | | |
974 | 23.5k | self.parse_paint( |
975 | 23.5k | self.base_glyph_paints_offset.to_usize() + base.paint_table_offset.to_usize(), |
976 | 23.5k | palette, |
977 | 23.5k | painter, |
978 | 23.5k | recursion_stack, |
979 | | #[cfg(feature = "variable-fonts")] |
980 | 23.5k | coords, |
981 | 23.5k | foreground_color, |
982 | | ); |
983 | | |
984 | 23.5k | if clip_box.is_some() { |
985 | 9.69k | painter.pop_clip(); |
986 | 13.8k | } |
987 | | |
988 | 23.5k | Some(()) |
989 | 23.5k | } |
990 | | |
991 | 32.4M | fn parse_paint( |
992 | 32.4M | &self, |
993 | 32.4M | offset: usize, |
994 | 32.4M | palette: u16, |
995 | 32.4M | painter: &mut dyn Painter<'a>, |
996 | 32.4M | recursion_stack: &mut RecursionStack, |
997 | 32.4M | #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], |
998 | 32.4M | foreground_color: RgbaColor, |
999 | 32.4M | ) -> Option<()> { |
1000 | | // Charged before any other bail-out. Seeking, reading the format and the cycle check all |
1001 | | // cost real work, so a graph whose nodes mostly point back at an ancestor would otherwise |
1002 | | // be traversed for free and the budget would bound 256x more work than it claims. ~keep |
1003 | 32.4M | recursion_stack.consume_visit().ok()?; |
1004 | | |
1005 | 32.4M | let mut s = Stream::new_at(self.data, offset)?; |
1006 | 10.4M | let format = s.read::<u8>()?; |
1007 | | |
1008 | | // Cycle detected |
1009 | 10.4M | if recursion_stack.contains(offset) { |
1010 | 1.37M | return None; |
1011 | 9.04M | } |
1012 | | |
1013 | 9.04M | recursion_stack.push(offset).ok()?; |
1014 | 9.03M | let result = self.parse_paint_impl( |
1015 | 9.03M | offset, |
1016 | 9.03M | palette, |
1017 | 9.03M | painter, |
1018 | 9.03M | recursion_stack, |
1019 | 9.03M | &mut s, |
1020 | 9.03M | format, |
1021 | | #[cfg(feature = "variable-fonts")] |
1022 | 9.03M | coords, |
1023 | 9.03M | foreground_color, |
1024 | | ); |
1025 | 9.03M | recursion_stack.pop(); |
1026 | | |
1027 | 9.03M | result |
1028 | 32.4M | } |
1029 | | |
1030 | 9.03M | fn parse_paint_impl( |
1031 | 9.03M | &self, |
1032 | 9.03M | offset: usize, |
1033 | 9.03M | palette: u16, |
1034 | 9.03M | painter: &mut dyn Painter<'a>, |
1035 | 9.03M | recursion_stack: &mut RecursionStack, |
1036 | 9.03M | s: &mut Stream, |
1037 | 9.03M | format: u8, |
1038 | 9.03M | #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], |
1039 | 9.03M | foreground_color: RgbaColor, |
1040 | 9.03M | ) -> Option<()> { |
1041 | 9.03M | match format { |
1042 | | 1 => { |
1043 | | // PaintColrLayers |
1044 | 273k | let layers_count = s.read::<u8>()?; |
1045 | 273k | let first_layer_index = s.read::<u32>()?; |
1046 | | |
1047 | 30.0M | for i in 0..layers_count { |
1048 | 30.0M | let index = first_layer_index.checked_add(u32::from(i))?; |
1049 | 30.0M | let paint_offset = self.layer_paint_offsets.get(index)?; |
1050 | 30.0M | self.parse_paint( |
1051 | 30.0M | self.layer_paint_offsets_offset.to_usize() + paint_offset.to_usize(), |
1052 | 30.0M | palette, |
1053 | 30.0M | painter, |
1054 | 30.0M | recursion_stack, |
1055 | | #[cfg(feature = "variable-fonts")] |
1056 | 30.0M | coords, |
1057 | 30.0M | foreground_color, |
1058 | | ); |
1059 | | } |
1060 | | } |
1061 | | 2 => { |
1062 | | // PaintSolid |
1063 | 14.9k | let palette_index = s.read::<u16>()?; |
1064 | 11.8k | let alpha = s.read::<F2DOT14>()?; |
1065 | | |
1066 | 9.09k | let mut color = if palette_index == u16::MAX { |
1067 | 568 | foreground_color |
1068 | | } else { |
1069 | 8.52k | self.palettes.get(palette, palette_index)? |
1070 | | }; |
1071 | | |
1072 | 2.77k | color.apply_alpha(alpha.to_f32()); |
1073 | 2.77k | painter.paint(Paint::Solid(color)); |
1074 | | } |
1075 | | #[cfg(feature = "variable-fonts")] |
1076 | | 3 => { |
1077 | | // PaintVarSolid |
1078 | 72.1k | let palette_index = s.read::<u16>()?; |
1079 | 71.3k | let alpha = s.read::<F2DOT14>()?; |
1080 | 70.1k | let var_index_base = s.read::<u32>()?; |
1081 | | |
1082 | 66.8k | let deltas = self |
1083 | 66.8k | .variation_data() |
1084 | 66.8k | .read_deltas::<1>(var_index_base, coords); |
1085 | | |
1086 | 66.8k | let mut color = if palette_index == u16::MAX { |
1087 | 523 | foreground_color |
1088 | | } else { |
1089 | 66.3k | self.palettes.get(palette, palette_index)? |
1090 | | }; |
1091 | | |
1092 | 970 | color.apply_alpha(alpha.apply_float_delta(deltas[0])); |
1093 | 970 | painter.paint(Paint::Solid(color)); |
1094 | | } |
1095 | | 4 => { |
1096 | | // PaintLinearGradient |
1097 | 128k | let color_line_offset = s.read::<Offset24>()?; |
1098 | 35.9k | let color_line = |
1099 | 125k | self.parse_color_line(offset + color_line_offset.to_usize(), foreground_color)?; |
1100 | | |
1101 | 35.9k | painter.paint(Paint::LinearGradient(LinearGradient { |
1102 | 35.9k | x0: s.read::<i16>()? as f32, |
1103 | 35.9k | y0: s.read::<i16>()? as f32, |
1104 | 34.6k | x1: s.read::<i16>()? as f32, |
1105 | 34.0k | y1: s.read::<i16>()? as f32, |
1106 | 33.5k | x2: s.read::<i16>()? as f32, |
1107 | 32.5k | y2: s.read::<i16>()? as f32, |
1108 | 32.0k | extend: color_line.extend, |
1109 | | #[cfg(feature = "variable-fonts")] |
1110 | 32.0k | variation_data: self.variation_data(), |
1111 | 32.0k | color_line: ColorLine::NonVarColorLine(color_line), |
1112 | | })) |
1113 | | } |
1114 | | #[cfg(feature = "variable-fonts")] |
1115 | | 5 => { |
1116 | | // PaintVarLinearGradient |
1117 | 87.9k | let var_color_line_offset = s.read::<Offset24>()?; |
1118 | 87.4k | let color_line = self.parse_var_color_line( |
1119 | 87.4k | offset + var_color_line_offset.to_usize(), |
1120 | 87.4k | foreground_color, |
1121 | 80.5k | )?; |
1122 | 6.94k | let mut var_s = s.clone(); |
1123 | 6.94k | var_s.advance(12); |
1124 | 6.94k | let var_index_base = var_s.read::<u32>()?; |
1125 | | |
1126 | 6.38k | let deltas = self |
1127 | 6.38k | .variation_data() |
1128 | 6.38k | .read_deltas::<6>(var_index_base, coords); |
1129 | | |
1130 | 6.38k | painter.paint(Paint::LinearGradient(LinearGradient { |
1131 | 6.38k | x0: s.read::<i16>()? as f32 + deltas[0], |
1132 | 6.38k | y0: s.read::<i16>()? as f32 + deltas[1], |
1133 | 6.38k | x1: s.read::<i16>()? as f32 + deltas[2], |
1134 | 6.38k | y1: s.read::<i16>()? as f32 + deltas[3], |
1135 | 6.38k | x2: s.read::<i16>()? as f32 + deltas[4], |
1136 | 6.38k | y2: s.read::<i16>()? as f32 + deltas[5], |
1137 | 6.38k | extend: color_line.extend, |
1138 | 6.38k | variation_data: self.variation_data(), |
1139 | 6.38k | color_line: ColorLine::VarColorLine(color_line), |
1140 | | })) |
1141 | | } |
1142 | | 6 => { |
1143 | | // PaintRadialGradient |
1144 | 51.5k | let color_line_offset = s.read::<Offset24>()?; |
1145 | 24.5k | let color_line = |
1146 | 51.1k | self.parse_color_line(offset + color_line_offset.to_usize(), foreground_color)?; |
1147 | 24.5k | painter.paint(Paint::RadialGradient(RadialGradient { |
1148 | 24.5k | x0: s.read::<i16>()? as f32, |
1149 | 24.5k | y0: s.read::<i16>()? as f32, |
1150 | 23.6k | r0: s.read::<u16>()? as f32, |
1151 | 22.6k | x1: s.read::<i16>()? as f32, |
1152 | 21.8k | y1: s.read::<i16>()? as f32, |
1153 | 21.0k | r1: s.read::<u16>()? as f32, |
1154 | 20.7k | extend: color_line.extend, |
1155 | | #[cfg(feature = "variable-fonts")] |
1156 | 20.7k | variation_data: self.variation_data(), |
1157 | 20.7k | color_line: ColorLine::NonVarColorLine(color_line), |
1158 | | })) |
1159 | | } |
1160 | | #[cfg(feature = "variable-fonts")] |
1161 | | 7 => { |
1162 | | // PaintVarRadialGradient |
1163 | 67.7k | let color_line_offset = s.read::<Offset24>()?; |
1164 | 67.2k | let color_line = self.parse_var_color_line( |
1165 | 67.2k | offset + color_line_offset.to_usize(), |
1166 | 67.2k | foreground_color, |
1167 | 36.8k | )?; |
1168 | | |
1169 | 30.3k | let mut var_s = s.clone(); |
1170 | 30.3k | var_s.advance(12); |
1171 | 30.3k | let var_index_base = var_s.read::<u32>()?; |
1172 | | |
1173 | 29.2k | let deltas = self |
1174 | 29.2k | .variation_data() |
1175 | 29.2k | .read_deltas::<6>(var_index_base, coords); |
1176 | | |
1177 | 29.2k | painter.paint(Paint::RadialGradient(RadialGradient { |
1178 | 29.2k | x0: s.read::<i16>()? as f32 + deltas[0], |
1179 | 29.2k | y0: s.read::<i16>()? as f32 + deltas[1], |
1180 | 29.2k | r0: s.read::<u16>()? as f32 + deltas[2], |
1181 | 29.2k | x1: s.read::<i16>()? as f32 + deltas[3], |
1182 | 29.2k | y1: s.read::<i16>()? as f32 + deltas[4], |
1183 | 29.2k | r1: s.read::<u16>()? as f32 + deltas[5], |
1184 | 29.2k | extend: color_line.extend, |
1185 | 29.2k | variation_data: self.variation_data(), |
1186 | 29.2k | color_line: ColorLine::VarColorLine(color_line), |
1187 | | })) |
1188 | | } |
1189 | | 8 => { |
1190 | | // PaintSweepGradient |
1191 | 36.3k | let color_line_offset = s.read::<Offset24>()?; |
1192 | 21.8k | let color_line = |
1193 | 35.3k | self.parse_color_line(offset + color_line_offset.to_usize(), foreground_color)?; |
1194 | 21.8k | painter.paint(Paint::SweepGradient(SweepGradient { |
1195 | 21.8k | center_x: s.read::<i16>()? as f32, |
1196 | 21.8k | center_y: s.read::<i16>()? as f32, |
1197 | 20.8k | start_angle: s.read::<F2DOT14>()?.to_f32(), |
1198 | 19.6k | end_angle: s.read::<F2DOT14>()?.to_f32(), |
1199 | 19.2k | extend: color_line.extend, |
1200 | 19.2k | color_line: ColorLine::NonVarColorLine(color_line), |
1201 | | #[cfg(feature = "variable-fonts")] |
1202 | 19.2k | variation_data: self.variation_data(), |
1203 | | })) |
1204 | | } |
1205 | | #[cfg(feature = "variable-fonts")] |
1206 | | 9 => { |
1207 | | // PaintVarSweepGradient |
1208 | 14.6k | let color_line_offset = s.read::<Offset24>()?; |
1209 | 14.3k | let color_line = self.parse_var_color_line( |
1210 | 14.3k | offset + color_line_offset.to_usize(), |
1211 | 14.3k | foreground_color, |
1212 | 11.7k | )?; |
1213 | | |
1214 | 2.63k | let mut var_s = s.clone(); |
1215 | 2.63k | var_s.advance(8); |
1216 | 2.63k | let var_index_base = var_s.read::<u32>()?; |
1217 | | |
1218 | 2.09k | let deltas = self |
1219 | 2.09k | .variation_data() |
1220 | 2.09k | .read_deltas::<4>(var_index_base, coords); |
1221 | | |
1222 | 2.09k | painter.paint(Paint::SweepGradient(SweepGradient { |
1223 | 2.09k | center_x: s.read::<i16>()? as f32 + deltas[0], |
1224 | 2.09k | center_y: s.read::<i16>()? as f32 + deltas[1], |
1225 | 2.09k | start_angle: s.read::<F2DOT14>()?.apply_float_delta(deltas[2]), |
1226 | 2.09k | end_angle: s.read::<F2DOT14>()?.apply_float_delta(deltas[3]), |
1227 | 2.09k | extend: color_line.extend, |
1228 | 2.09k | color_line: ColorLine::VarColorLine(color_line), |
1229 | 2.09k | variation_data: self.variation_data(), |
1230 | | })) |
1231 | | } |
1232 | | 10 => { |
1233 | | // PaintGlyph |
1234 | 358k | let paint_offset = s.read::<Offset24>()?; |
1235 | 356k | let glyph_id = s.read::<GlyphId>()?; |
1236 | 354k | painter.outline_glyph(glyph_id); |
1237 | 354k | painter.push_clip(); |
1238 | | |
1239 | 354k | self.parse_paint( |
1240 | 354k | offset + paint_offset.to_usize(), |
1241 | 354k | palette, |
1242 | 354k | painter, |
1243 | 354k | recursion_stack, |
1244 | | #[cfg(feature = "variable-fonts")] |
1245 | 354k | coords, |
1246 | 354k | foreground_color, |
1247 | | ); |
1248 | | |
1249 | 354k | painter.pop_clip(); |
1250 | | } |
1251 | | 11 => { |
1252 | | // PaintColrGlyph |
1253 | 25.7k | let glyph_id = s.read::<GlyphId>()?; |
1254 | 25.3k | self.paint_impl( |
1255 | 25.3k | glyph_id, |
1256 | 25.3k | palette, |
1257 | 25.3k | painter, |
1258 | 25.3k | recursion_stack, |
1259 | | #[cfg(feature = "variable-fonts")] |
1260 | 25.3k | coords, |
1261 | 25.3k | foreground_color, |
1262 | | ); |
1263 | | } |
1264 | | 12 => { |
1265 | | // PaintTransform |
1266 | 11.6k | let paint_offset = s.read::<Offset24>()?; |
1267 | 10.4k | let ts_offset = s.read::<Offset24>()?; |
1268 | 9.17k | let mut s = Stream::new_at(self.data, offset + ts_offset.to_usize())?; |
1269 | 1.94k | let ts = Transform { |
1270 | 6.55k | a: s.read::<Fixed>().map(|n| n.0)?, |
1271 | 5.91k | b: s.read::<Fixed>().map(|n| n.0)?, |
1272 | 4.44k | c: s.read::<Fixed>().map(|n| n.0)?, |
1273 | 3.44k | d: s.read::<Fixed>().map(|n| n.0)?, |
1274 | 2.84k | e: s.read::<Fixed>().map(|n| n.0)?, |
1275 | 2.53k | f: s.read::<Fixed>().map(|n| n.0)?, |
1276 | | }; |
1277 | | |
1278 | 1.94k | painter.push_transform(ts); |
1279 | 1.94k | self.parse_paint( |
1280 | 1.94k | offset + paint_offset.to_usize(), |
1281 | 1.94k | palette, |
1282 | 1.94k | painter, |
1283 | 1.94k | recursion_stack, |
1284 | | #[cfg(feature = "variable-fonts")] |
1285 | 1.94k | coords, |
1286 | 1.94k | foreground_color, |
1287 | | ); |
1288 | 1.94k | painter.pop_transform(); |
1289 | | } |
1290 | | #[cfg(feature = "variable-fonts")] |
1291 | | 13 => { |
1292 | | // PaintVarTransform |
1293 | 12.4k | let paint_offset = s.read::<Offset24>()?; |
1294 | 11.6k | let ts_offset = s.read::<Offset24>()?; |
1295 | 9.54k | let mut s = Stream::new_at(self.data, offset + ts_offset.to_usize())?; |
1296 | | |
1297 | 6.47k | let mut var_s = s.clone(); |
1298 | 6.47k | var_s.advance(24); |
1299 | 6.47k | let var_index_base = var_s.read::<u32>()?; |
1300 | | |
1301 | 5.69k | let deltas = self |
1302 | 5.69k | .variation_data() |
1303 | 5.69k | .read_deltas::<6>(var_index_base, coords); |
1304 | | |
1305 | 5.69k | let ts = Transform { |
1306 | 5.69k | a: s.read::<Fixed>()?.apply_float_delta(deltas[0]), |
1307 | 5.69k | b: s.read::<Fixed>()?.apply_float_delta(deltas[1]), |
1308 | 5.69k | c: s.read::<Fixed>()?.apply_float_delta(deltas[2]), |
1309 | 5.69k | d: s.read::<Fixed>()?.apply_float_delta(deltas[3]), |
1310 | 5.69k | e: s.read::<Fixed>()?.apply_float_delta(deltas[4]), |
1311 | 5.69k | f: s.read::<Fixed>()?.apply_float_delta(deltas[5]), |
1312 | | }; |
1313 | | |
1314 | 5.69k | painter.push_transform(ts); |
1315 | 5.69k | self.parse_paint( |
1316 | 5.69k | offset + paint_offset.to_usize(), |
1317 | 5.69k | palette, |
1318 | 5.69k | painter, |
1319 | 5.69k | recursion_stack, |
1320 | 5.69k | coords, |
1321 | 5.69k | foreground_color, |
1322 | | ); |
1323 | 5.69k | painter.pop_transform(); |
1324 | | } |
1325 | | 14 => { |
1326 | | // PaintTranslate |
1327 | 13.8k | let paint_offset = s.read::<Offset24>()?; |
1328 | 13.5k | let tx = f32::from(s.read::<i16>()?); |
1329 | 12.4k | let ty = f32::from(s.read::<i16>()?); |
1330 | | |
1331 | 11.0k | painter.push_transform(Transform::new_translate(tx, ty)); |
1332 | 11.0k | self.parse_paint( |
1333 | 11.0k | offset + paint_offset.to_usize(), |
1334 | 11.0k | palette, |
1335 | 11.0k | painter, |
1336 | 11.0k | recursion_stack, |
1337 | | #[cfg(feature = "variable-fonts")] |
1338 | 11.0k | coords, |
1339 | 11.0k | foreground_color, |
1340 | | ); |
1341 | 11.0k | painter.pop_transform(); |
1342 | | } |
1343 | | #[cfg(feature = "variable-fonts")] |
1344 | | 15 => { |
1345 | | // PaintVarTranslate |
1346 | 7.67k | let paint_offset = s.read::<Offset24>()?; |
1347 | | |
1348 | 7.09k | let mut var_s = s.clone(); |
1349 | 7.09k | var_s.advance(4); |
1350 | 7.09k | let var_index_base = var_s.read::<u32>()?; |
1351 | | |
1352 | 4.92k | let deltas = self |
1353 | 4.92k | .variation_data() |
1354 | 4.92k | .read_deltas::<2>(var_index_base, coords); |
1355 | | |
1356 | 4.92k | let tx = f32::from(s.read::<i16>()?) + deltas[0]; |
1357 | 4.92k | let ty = f32::from(s.read::<i16>()?) + deltas[1]; |
1358 | | |
1359 | 4.92k | painter.push_transform(Transform::new_translate(tx, ty)); |
1360 | 4.92k | self.parse_paint( |
1361 | 4.92k | offset + paint_offset.to_usize(), |
1362 | 4.92k | palette, |
1363 | 4.92k | painter, |
1364 | 4.92k | recursion_stack, |
1365 | 4.92k | coords, |
1366 | 4.92k | foreground_color, |
1367 | | ); |
1368 | 4.92k | painter.pop_transform(); |
1369 | | } |
1370 | | 16 => { |
1371 | | // PaintScale |
1372 | 5.66k | let paint_offset = s.read::<Offset24>()?; |
1373 | 5.26k | let sx = s.read::<F2DOT14>()?.to_f32(); |
1374 | 4.29k | let sy = s.read::<F2DOT14>()?.to_f32(); |
1375 | | |
1376 | 3.38k | painter.push_transform(Transform::new_scale(sx, sy)); |
1377 | 3.38k | self.parse_paint( |
1378 | 3.38k | offset + paint_offset.to_usize(), |
1379 | 3.38k | palette, |
1380 | 3.38k | painter, |
1381 | 3.38k | recursion_stack, |
1382 | | #[cfg(feature = "variable-fonts")] |
1383 | 3.38k | coords, |
1384 | 3.38k | foreground_color, |
1385 | | ); |
1386 | 3.38k | painter.pop_transform(); |
1387 | | } |
1388 | | #[cfg(feature = "variable-fonts")] |
1389 | | 17 => { |
1390 | | // PaintVarScale |
1391 | 11.1k | let paint_offset = s.read::<Offset24>()?; |
1392 | | |
1393 | 9.29k | let mut var_s = s.clone(); |
1394 | 9.29k | var_s.advance(4); |
1395 | 9.29k | let var_index_base = var_s.read::<u32>()?; |
1396 | | |
1397 | 3.60k | let deltas = self |
1398 | 3.60k | .variation_data() |
1399 | 3.60k | .read_deltas::<2>(var_index_base, coords); |
1400 | | |
1401 | 3.60k | let sx = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1402 | 3.60k | let sy = s.read::<F2DOT14>()?.apply_float_delta(deltas[1]); |
1403 | | |
1404 | 3.60k | painter.push_transform(Transform::new_scale(sx, sy)); |
1405 | 3.60k | self.parse_paint( |
1406 | 3.60k | offset + paint_offset.to_usize(), |
1407 | 3.60k | palette, |
1408 | 3.60k | painter, |
1409 | 3.60k | recursion_stack, |
1410 | 3.60k | coords, |
1411 | 3.60k | foreground_color, |
1412 | | ); |
1413 | 3.60k | painter.pop_transform(); |
1414 | | } |
1415 | | 18 => { |
1416 | | // PaintScaleAroundCenter |
1417 | 20.6k | let paint_offset = s.read::<Offset24>()?; |
1418 | 19.1k | let sx = s.read::<F2DOT14>()?.to_f32(); |
1419 | 15.9k | let sy = s.read::<F2DOT14>()?.to_f32(); |
1420 | 12.3k | let center_x = f32::from(s.read::<i16>()?); |
1421 | 8.00k | let center_y = f32::from(s.read::<i16>()?); |
1422 | | |
1423 | 4.88k | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1424 | 4.88k | painter.push_transform(Transform::new_scale(sx, sy)); |
1425 | 4.88k | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1426 | 4.88k | self.parse_paint( |
1427 | 4.88k | offset + paint_offset.to_usize(), |
1428 | 4.88k | palette, |
1429 | 4.88k | painter, |
1430 | 4.88k | recursion_stack, |
1431 | | #[cfg(feature = "variable-fonts")] |
1432 | 4.88k | coords, |
1433 | 4.88k | foreground_color, |
1434 | | ); |
1435 | 4.88k | painter.pop_transform(); |
1436 | 4.88k | painter.pop_transform(); |
1437 | 4.88k | painter.pop_transform(); |
1438 | | } |
1439 | | #[cfg(feature = "variable-fonts")] |
1440 | | 19 => { |
1441 | | // PaintVarScaleAroundCenter |
1442 | 10.3k | let paint_offset = s.read::<Offset24>()?; |
1443 | | |
1444 | 9.76k | let mut var_s = s.clone(); |
1445 | 9.76k | var_s.advance(8); |
1446 | 9.76k | let var_index_base = var_s.read::<u32>()?; |
1447 | | |
1448 | 7.34k | let deltas = self |
1449 | 7.34k | .variation_data() |
1450 | 7.34k | .read_deltas::<4>(var_index_base, coords); |
1451 | | |
1452 | 7.34k | let sx = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1453 | 7.34k | let sy = s.read::<F2DOT14>()?.apply_float_delta(deltas[1]); |
1454 | 7.34k | let center_x = f32::from(s.read::<i16>()?) + deltas[2]; |
1455 | 7.34k | let center_y = f32::from(s.read::<i16>()?) + deltas[3]; |
1456 | | |
1457 | 7.34k | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1458 | 7.34k | painter.push_transform(Transform::new_scale(sx, sy)); |
1459 | 7.34k | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1460 | 7.34k | self.parse_paint( |
1461 | 7.34k | offset + paint_offset.to_usize(), |
1462 | 7.34k | palette, |
1463 | 7.34k | painter, |
1464 | 7.34k | recursion_stack, |
1465 | 7.34k | coords, |
1466 | 7.34k | foreground_color, |
1467 | | ); |
1468 | 7.34k | painter.pop_transform(); |
1469 | 7.34k | painter.pop_transform(); |
1470 | 7.34k | painter.pop_transform(); |
1471 | | } |
1472 | | 20 => { |
1473 | | // PaintScaleUniform |
1474 | 9.30k | let paint_offset = s.read::<Offset24>()?; |
1475 | 9.05k | let scale = s.read::<F2DOT14>()?.to_f32(); |
1476 | | |
1477 | 7.92k | painter.push_transform(Transform::new_scale(scale, scale)); |
1478 | 7.92k | self.parse_paint( |
1479 | 7.92k | offset + paint_offset.to_usize(), |
1480 | 7.92k | palette, |
1481 | 7.92k | painter, |
1482 | 7.92k | recursion_stack, |
1483 | | #[cfg(feature = "variable-fonts")] |
1484 | 7.92k | coords, |
1485 | 7.92k | foreground_color, |
1486 | | ); |
1487 | 7.92k | painter.pop_transform(); |
1488 | | } |
1489 | | #[cfg(feature = "variable-fonts")] |
1490 | | 21 => { |
1491 | | // PaintVarScaleUniform |
1492 | 8.30k | let paint_offset = s.read::<Offset24>()?; |
1493 | | |
1494 | 7.48k | let mut var_s = s.clone(); |
1495 | 7.48k | var_s.advance(2); |
1496 | 7.48k | let var_index_base = var_s.read::<u32>()?; |
1497 | | |
1498 | 4.71k | let deltas = self |
1499 | 4.71k | .variation_data() |
1500 | 4.71k | .read_deltas::<1>(var_index_base, coords); |
1501 | | |
1502 | 4.71k | let scale = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1503 | | |
1504 | 4.71k | painter.push_transform(Transform::new_scale(scale, scale)); |
1505 | 4.71k | self.parse_paint( |
1506 | 4.71k | offset + paint_offset.to_usize(), |
1507 | 4.71k | palette, |
1508 | 4.71k | painter, |
1509 | 4.71k | recursion_stack, |
1510 | 4.71k | coords, |
1511 | 4.71k | foreground_color, |
1512 | | ); |
1513 | 4.71k | painter.pop_transform(); |
1514 | | } |
1515 | | 22 => { |
1516 | | // PaintScaleUniformAroundCenter |
1517 | 31.2k | let paint_offset = s.read::<Offset24>()?; |
1518 | 30.0k | let scale = s.read::<F2DOT14>()?.to_f32(); |
1519 | 28.1k | let center_x = f32::from(s.read::<i16>()?); |
1520 | 25.5k | let center_y = f32::from(s.read::<i16>()?); |
1521 | | |
1522 | 21.4k | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1523 | 21.4k | painter.push_transform(Transform::new_scale(scale, scale)); |
1524 | 21.4k | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1525 | 21.4k | self.parse_paint( |
1526 | 21.4k | offset + paint_offset.to_usize(), |
1527 | 21.4k | palette, |
1528 | 21.4k | painter, |
1529 | 21.4k | recursion_stack, |
1530 | | #[cfg(feature = "variable-fonts")] |
1531 | 21.4k | coords, |
1532 | 21.4k | foreground_color, |
1533 | | ); |
1534 | 21.4k | painter.pop_transform(); |
1535 | 21.4k | painter.pop_transform(); |
1536 | 21.4k | painter.pop_transform(); |
1537 | | } |
1538 | | #[cfg(feature = "variable-fonts")] |
1539 | | 23 => { |
1540 | | // PaintVarScaleUniformAroundCenter |
1541 | 18.2k | let paint_offset = s.read::<Offset24>()?; |
1542 | | |
1543 | 17.1k | let mut var_s = s.clone(); |
1544 | 17.1k | var_s.advance(6); |
1545 | 17.1k | let var_index_base = var_s.read::<u32>()?; |
1546 | | |
1547 | 13.7k | let deltas = self |
1548 | 13.7k | .variation_data() |
1549 | 13.7k | .read_deltas::<3>(var_index_base, coords); |
1550 | | |
1551 | 13.7k | let scale = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1552 | 13.7k | let center_x = f32::from(s.read::<i16>()?) + deltas[1]; |
1553 | 13.7k | let center_y = f32::from(s.read::<i16>()?) + deltas[2]; |
1554 | | |
1555 | 13.7k | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1556 | 13.7k | painter.push_transform(Transform::new_scale(scale, scale)); |
1557 | 13.7k | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1558 | 13.7k | self.parse_paint( |
1559 | 13.7k | offset + paint_offset.to_usize(), |
1560 | 13.7k | palette, |
1561 | 13.7k | painter, |
1562 | 13.7k | recursion_stack, |
1563 | 13.7k | coords, |
1564 | 13.7k | foreground_color, |
1565 | | ); |
1566 | 13.7k | painter.pop_transform(); |
1567 | 13.7k | painter.pop_transform(); |
1568 | 13.7k | painter.pop_transform(); |
1569 | | } |
1570 | | 24 => { |
1571 | | // PaintRotate |
1572 | 7.42k | let paint_offset = s.read::<Offset24>()?; |
1573 | 6.87k | let angle = s.read::<F2DOT14>()?.to_f32(); |
1574 | | |
1575 | 5.51k | painter.push_transform(Transform::new_rotate(angle)); |
1576 | 5.51k | self.parse_paint( |
1577 | 5.51k | offset + paint_offset.to_usize(), |
1578 | 5.51k | palette, |
1579 | 5.51k | painter, |
1580 | 5.51k | recursion_stack, |
1581 | | #[cfg(feature = "variable-fonts")] |
1582 | 5.51k | coords, |
1583 | 5.51k | foreground_color, |
1584 | | ); |
1585 | 5.51k | painter.pop_transform(); |
1586 | | } |
1587 | | #[cfg(feature = "variable-fonts")] |
1588 | | 25 => { |
1589 | | // PaintVarRotate |
1590 | 17.2k | let paint_offset = s.read::<Offset24>()?; |
1591 | | |
1592 | 14.1k | let mut var_s = s.clone(); |
1593 | 14.1k | var_s.advance(2); |
1594 | 14.1k | let var_index_base = var_s.read::<u32>()?; |
1595 | | |
1596 | 6.07k | let deltas = self |
1597 | 6.07k | .variation_data() |
1598 | 6.07k | .read_deltas::<1>(var_index_base, coords); |
1599 | | |
1600 | 6.07k | let angle = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1601 | | |
1602 | 6.07k | painter.push_transform(Transform::new_rotate(angle)); |
1603 | 6.07k | self.parse_paint( |
1604 | 6.07k | offset + paint_offset.to_usize(), |
1605 | 6.07k | palette, |
1606 | 6.07k | painter, |
1607 | 6.07k | recursion_stack, |
1608 | 6.07k | coords, |
1609 | 6.07k | foreground_color, |
1610 | | ); |
1611 | 6.07k | painter.pop_transform(); |
1612 | | } |
1613 | | 26 => { |
1614 | | // PaintRotateAroundCenter |
1615 | 10.9k | let paint_offset = s.read::<Offset24>()?; |
1616 | 9.56k | let angle = s.read::<F2DOT14>()?.to_f32(); |
1617 | 7.44k | let center_x = f32::from(s.read::<i16>()?); |
1618 | 4.86k | let center_y = f32::from(s.read::<i16>()?); |
1619 | | |
1620 | 2.49k | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1621 | 2.49k | painter.push_transform(Transform::new_rotate(angle)); |
1622 | 2.49k | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1623 | 2.49k | self.parse_paint( |
1624 | 2.49k | offset + paint_offset.to_usize(), |
1625 | 2.49k | palette, |
1626 | 2.49k | painter, |
1627 | 2.49k | recursion_stack, |
1628 | | #[cfg(feature = "variable-fonts")] |
1629 | 2.49k | coords, |
1630 | 2.49k | foreground_color, |
1631 | | ); |
1632 | 2.49k | painter.pop_transform(); |
1633 | 2.49k | painter.pop_transform(); |
1634 | 2.49k | painter.pop_transform(); |
1635 | | } |
1636 | | #[cfg(feature = "variable-fonts")] |
1637 | | 27 => { |
1638 | | // PaintVarRotateAroundCenter |
1639 | 6.76k | let paint_offset = s.read::<Offset24>()?; |
1640 | | |
1641 | 6.52k | let mut var_s = s.clone(); |
1642 | 6.52k | var_s.advance(6); |
1643 | 6.52k | let var_index_base = var_s.read::<u32>()?; |
1644 | | |
1645 | 4.51k | let deltas = self |
1646 | 4.51k | .variation_data() |
1647 | 4.51k | .read_deltas::<3>(var_index_base, coords); |
1648 | | |
1649 | 4.51k | let angle = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1650 | 4.51k | let center_x = f32::from(s.read::<i16>()?) + deltas[1]; |
1651 | 4.51k | let center_y = f32::from(s.read::<i16>()?) + deltas[2]; |
1652 | | |
1653 | 4.51k | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1654 | 4.51k | painter.push_transform(Transform::new_rotate(angle)); |
1655 | 4.51k | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1656 | 4.51k | self.parse_paint( |
1657 | 4.51k | offset + paint_offset.to_usize(), |
1658 | 4.51k | palette, |
1659 | 4.51k | painter, |
1660 | 4.51k | recursion_stack, |
1661 | 4.51k | coords, |
1662 | 4.51k | foreground_color, |
1663 | | ); |
1664 | 4.51k | painter.pop_transform(); |
1665 | 4.51k | painter.pop_transform(); |
1666 | 4.51k | painter.pop_transform(); |
1667 | | } |
1668 | | 28 => { |
1669 | | // PaintSkew |
1670 | 5.80k | let paint_offset = s.read::<Offset24>()?; |
1671 | 5.43k | let skew_x = s.read::<F2DOT14>()?.to_f32(); |
1672 | 4.53k | let skew_y = s.read::<F2DOT14>()?.to_f32(); |
1673 | | |
1674 | 3.60k | painter.push_transform(Transform::new_skew(skew_x, skew_y)); |
1675 | 3.60k | self.parse_paint( |
1676 | 3.60k | offset + paint_offset.to_usize(), |
1677 | 3.60k | palette, |
1678 | 3.60k | painter, |
1679 | 3.60k | recursion_stack, |
1680 | | #[cfg(feature = "variable-fonts")] |
1681 | 3.60k | coords, |
1682 | 3.60k | foreground_color, |
1683 | | ); |
1684 | 3.60k | painter.pop_transform(); |
1685 | | } |
1686 | | #[cfg(feature = "variable-fonts")] |
1687 | | 29 => { |
1688 | | // PaintVarSkew |
1689 | 10.1k | let paint_offset = s.read::<Offset24>()?; |
1690 | | |
1691 | 9.13k | let mut var_s = s.clone(); |
1692 | 9.13k | var_s.advance(4); |
1693 | 9.13k | let var_index_base = var_s.read::<u32>()?; |
1694 | | |
1695 | 5.01k | let deltas = self |
1696 | 5.01k | .variation_data() |
1697 | 5.01k | .read_deltas::<2>(var_index_base, coords); |
1698 | | |
1699 | 5.01k | let skew_x = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1700 | 5.01k | let skew_y = s.read::<F2DOT14>()?.apply_float_delta(deltas[1]); |
1701 | | |
1702 | 5.01k | painter.push_transform(Transform::new_skew(skew_x, skew_y)); |
1703 | 5.01k | self.parse_paint( |
1704 | 5.01k | offset + paint_offset.to_usize(), |
1705 | 5.01k | palette, |
1706 | 5.01k | painter, |
1707 | 5.01k | recursion_stack, |
1708 | 5.01k | coords, |
1709 | 5.01k | foreground_color, |
1710 | | ); |
1711 | 5.01k | painter.pop_transform(); |
1712 | | } |
1713 | | 30 => { |
1714 | | // PaintSkewAroundCenter |
1715 | 16.3k | let paint_offset = s.read::<Offset24>()?; |
1716 | 14.2k | let skew_x = s.read::<F2DOT14>()?.to_f32(); |
1717 | 12.4k | let skew_y = s.read::<F2DOT14>()?.to_f32(); |
1718 | 9.84k | let center_x = f32::from(s.read::<i16>()?); |
1719 | 7.08k | let center_y = f32::from(s.read::<i16>()?); |
1720 | | |
1721 | 4.19k | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1722 | 4.19k | painter.push_transform(Transform::new_skew(skew_x, skew_y)); |
1723 | 4.19k | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1724 | 4.19k | self.parse_paint( |
1725 | 4.19k | offset + paint_offset.to_usize(), |
1726 | 4.19k | palette, |
1727 | 4.19k | painter, |
1728 | 4.19k | recursion_stack, |
1729 | | #[cfg(feature = "variable-fonts")] |
1730 | 4.19k | coords, |
1731 | 4.19k | foreground_color, |
1732 | | ); |
1733 | 4.19k | painter.pop_transform(); |
1734 | 4.19k | painter.pop_transform(); |
1735 | 4.19k | painter.pop_transform(); |
1736 | | } |
1737 | | #[cfg(feature = "variable-fonts")] |
1738 | | 31 => { |
1739 | | // PaintVarSkewAroundCenter |
1740 | 10.0k | let paint_offset = s.read::<Offset24>()?; |
1741 | | |
1742 | 9.21k | let mut var_s = s.clone(); |
1743 | 9.21k | var_s.advance(8); |
1744 | 9.21k | let var_index_base = var_s.read::<u32>()?; |
1745 | | |
1746 | 6.58k | let deltas = self |
1747 | 6.58k | .variation_data() |
1748 | 6.58k | .read_deltas::<4>(var_index_base, coords); |
1749 | | |
1750 | 6.58k | let skew_x = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1751 | 6.58k | let skew_y = s.read::<F2DOT14>()?.apply_float_delta(deltas[1]); |
1752 | 6.58k | let center_x = f32::from(s.read::<i16>()?) + deltas[2]; |
1753 | 6.58k | let center_y = f32::from(s.read::<i16>()?) + deltas[3]; |
1754 | | |
1755 | 6.58k | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1756 | 6.58k | painter.push_transform(Transform::new_skew(skew_x, skew_y)); |
1757 | 6.58k | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1758 | 6.58k | self.parse_paint( |
1759 | 6.58k | offset + paint_offset.to_usize(), |
1760 | 6.58k | palette, |
1761 | 6.58k | painter, |
1762 | 6.58k | recursion_stack, |
1763 | 6.58k | coords, |
1764 | 6.58k | foreground_color, |
1765 | | ); |
1766 | 6.58k | painter.pop_transform(); |
1767 | 6.58k | painter.pop_transform(); |
1768 | 6.58k | painter.pop_transform(); |
1769 | | } |
1770 | | 32 => { |
1771 | | // PaintComposite |
1772 | 1.04M | let source_paint_offset = s.read::<Offset24>()?; |
1773 | 1.04M | let composite_mode = s.read::<CompositeMode>()?; |
1774 | 970k | let backdrop_paint_offset = s.read::<Offset24>()?; |
1775 | | |
1776 | 968k | painter.push_layer(CompositeMode::SourceOver); |
1777 | 968k | self.parse_paint( |
1778 | 968k | offset + backdrop_paint_offset.to_usize(), |
1779 | 968k | palette, |
1780 | 968k | painter, |
1781 | 968k | recursion_stack, |
1782 | | #[cfg(feature = "variable-fonts")] |
1783 | 968k | coords, |
1784 | 968k | foreground_color, |
1785 | | ); |
1786 | 968k | painter.push_layer(composite_mode); |
1787 | 968k | self.parse_paint( |
1788 | 968k | offset + source_paint_offset.to_usize(), |
1789 | 968k | palette, |
1790 | 968k | painter, |
1791 | 968k | recursion_stack, |
1792 | | #[cfg(feature = "variable-fonts")] |
1793 | 968k | coords, |
1794 | 968k | foreground_color, |
1795 | | ); |
1796 | 968k | painter.pop_layer(); |
1797 | 968k | painter.pop_layer(); |
1798 | | } |
1799 | 6.60M | _ => {} |
1800 | | } |
1801 | | |
1802 | 8.45M | Some(()) |
1803 | 9.03M | } |
1804 | | |
1805 | 212k | fn parse_color_line( |
1806 | 212k | &self, |
1807 | 212k | offset: usize, |
1808 | 212k | foreground_color: RgbaColor, |
1809 | 212k | ) -> Option<NonVarColorLine<'a>> { |
1810 | 212k | let mut s = Stream::new_at(self.data, offset)?; |
1811 | 151k | let extend = s.read::<GradientExtend>()?; |
1812 | 103k | let count = s.read::<u16>()?; |
1813 | 102k | let colors = s.read_array16::<ColorStopRaw>(count)?; |
1814 | 82.3k | Some(NonVarColorLine { |
1815 | 82.3k | extend, |
1816 | 82.3k | colors, |
1817 | 82.3k | foreground_color, |
1818 | 82.3k | palettes: self.palettes, |
1819 | 82.3k | }) |
1820 | 212k | } |
1821 | | |
1822 | | #[cfg(feature = "variable-fonts")] |
1823 | 169k | fn parse_var_color_line( |
1824 | 169k | &self, |
1825 | 169k | offset: usize, |
1826 | 169k | foreground_color: RgbaColor, |
1827 | 169k | ) -> Option<VarColorLine<'a>> { |
1828 | 169k | let mut s = Stream::new_at(self.data, offset)?; |
1829 | 143k | let extend = s.read::<GradientExtend>()?; |
1830 | 49.0k | let count = s.read::<u16>()?; |
1831 | 48.7k | let colors = s.read_array16::<VarColorStopRaw>(count)?; |
1832 | 39.9k | Some(VarColorLine { |
1833 | 39.9k | extend, |
1834 | 39.9k | colors, |
1835 | 39.9k | foreground_color, |
1836 | 39.9k | palettes: self.palettes, |
1837 | 39.9k | }) |
1838 | 169k | } |
1839 | | } |
1840 | | |
1841 | | struct RecursionStack { |
1842 | | // The limit of 64 is chosen arbitrarily and not from the spec. But we have to stop somewhere... |
1843 | | stack: [usize; 64], |
1844 | | len: usize, |
1845 | | // `stack`/`contains` only detect a cycle on the CURRENT root-to-leaf path (an entry is popped |
1846 | | // as soon as its call returns), so a paint graph shaped as a DAG -- the same offset reachable |
1847 | | // through multiple sibling branches, none of which individually cycles -- is not caught by it. |
1848 | | // A chain of `PaintColrLayers` records where every layer slot at each level points at one |
1849 | | // shared next-level record forces `branching^depth` calls while never re-entering the active |
1850 | | // path. This budget bounds the total number of `parse_paint` calls for one top-level `paint` |
1851 | | // call, independent of the DAG's branching factor. |
1852 | | visits_left: u32, |
1853 | | } |
1854 | | |
1855 | | // Not from the spec. HarfBuzz bounds the same traversal with `HB_MAX_GRAPH_EDGE_COUNT = 2048` and |
1856 | | // renders real color fonts with it, so real paint graphs sit far below this; we keep more headroom |
1857 | | // because we count nodes rather than edges. Matches `glyf::MAX_COMPONENT_VISITS`. ~keep |
1858 | | const MAX_PAINT_VISITS: u32 = 100_000; |
1859 | | |
1860 | | impl RecursionStack { |
1861 | | #[inline] |
1862 | | pub fn is_empty(&self) -> bool { |
1863 | | self.len == 0 |
1864 | | } |
1865 | | |
1866 | | #[inline] |
1867 | 9.04M | pub fn push(&mut self, offset: usize) -> Result<(), ()> { |
1868 | 9.04M | if self.len == self.stack.len() { |
1869 | 9.33k | Err(()) |
1870 | | } else { |
1871 | 9.03M | self.stack[self.len] = offset; |
1872 | 9.03M | self.len += 1; |
1873 | 9.03M | Ok(()) |
1874 | | } |
1875 | 9.04M | } |
1876 | | |
1877 | | #[inline] |
1878 | 10.4M | pub fn contains(&self, offset: usize) -> bool { |
1879 | 10.4M | if let Some(offsets) = self.stack.get(..self.len) { |
1880 | 10.4M | return offsets.contains(&offset); |
1881 | 0 | } |
1882 | | |
1883 | 0 | false |
1884 | 10.4M | } |
1885 | | |
1886 | | #[inline] |
1887 | 9.03M | pub fn pop(&mut self) { |
1888 | 9.03M | debug_assert!(!self.is_empty()); |
1889 | 9.03M | self.len -= 1; |
1890 | 9.03M | } |
1891 | | |
1892 | | #[inline] |
1893 | 32.4M | pub fn consume_visit(&mut self) -> Result<(), ()> { |
1894 | 32.4M | match self.visits_left.checked_sub(1) { |
1895 | 32.4M | Some(left) => { |
1896 | 32.4M | self.visits_left = left; |
1897 | 32.4M | Ok(()) |
1898 | | } |
1899 | 18.7k | None => Err(()), |
1900 | | } |
1901 | 32.4M | } |
1902 | | } |
1903 | | |
1904 | | #[cfg(feature = "variable-fonts")] |
1905 | | #[derive(Clone, Copy, Debug, Default)] |
1906 | | struct VariationData<'a> { |
1907 | | variation_store: Option<ItemVariationStore<'a>>, |
1908 | | delta_map: Option<DeltaSetIndexMap<'a>>, |
1909 | | } |
1910 | | |
1911 | | #[cfg(feature = "variable-fonts")] |
1912 | | impl VariationData<'_> { |
1913 | | // Inspired from `fontations`. |
1914 | 286k | fn read_deltas<const N: usize>( |
1915 | 286k | &self, |
1916 | 286k | var_index_base: u32, |
1917 | 286k | coordinates: &[NormalizedCoordinate], |
1918 | 286k | ) -> [f32; N] { |
1919 | | const NO_VARIATION_DELTAS: u32 = 0xFFFFFFFF; |
1920 | 286k | let mut deltas = [0.0; N]; |
1921 | | |
1922 | 286k | if coordinates.is_empty() |
1923 | 0 | || self.variation_store.is_none() |
1924 | 0 | || var_index_base == NO_VARIATION_DELTAS |
1925 | | { |
1926 | 286k | return deltas; |
1927 | 0 | } |
1928 | | |
1929 | 0 | let variation_store = self.variation_store.as_ref().unwrap(); |
1930 | | |
1931 | 0 | for (i, delta) in deltas.iter_mut().enumerate() { |
1932 | | // `var_index_base` comes straight from the paint table, so this addition can |
1933 | | // overflow: an abort in debug, and a wrap to an unrelated delta index in |
1934 | | // release. `0xFFFFFFFF` is already handled above as "no deltas". ~keep |
1935 | 0 | *delta = self |
1936 | 0 | .delta_map |
1937 | 0 | .zip(var_index_base.checked_add(i as u32)) |
1938 | 0 | .and_then(|(d, index)| d.map(index)) Unexecuted instantiation: <ttf_parser::tables::colr::VariationData>::read_deltas::<1>::{closure#0}Unexecuted instantiation: <ttf_parser::tables::colr::VariationData>::read_deltas::<2>::{closure#0}Unexecuted instantiation: <ttf_parser::tables::colr::VariationData>::read_deltas::<3>::{closure#0}Unexecuted instantiation: <ttf_parser::tables::colr::VariationData>::read_deltas::<4>::{closure#0}Unexecuted instantiation: <ttf_parser::tables::colr::VariationData>::read_deltas::<6>::{closure#0} |
1939 | 0 | .and_then(|d| variation_store.parse_delta(d.0, d.1, coordinates)) Unexecuted instantiation: <ttf_parser::tables::colr::VariationData>::read_deltas::<1>::{closure#1}Unexecuted instantiation: <ttf_parser::tables::colr::VariationData>::read_deltas::<2>::{closure#1}Unexecuted instantiation: <ttf_parser::tables::colr::VariationData>::read_deltas::<3>::{closure#1}Unexecuted instantiation: <ttf_parser::tables::colr::VariationData>::read_deltas::<4>::{closure#1}Unexecuted instantiation: <ttf_parser::tables::colr::VariationData>::read_deltas::<6>::{closure#1} |
1940 | 0 | .unwrap_or(0.0); |
1941 | | } |
1942 | | |
1943 | 0 | deltas |
1944 | 286k | } <ttf_parser::tables::colr::VariationData>::read_deltas::<1> Line | Count | Source | 1914 | 77.6k | fn read_deltas<const N: usize>( | 1915 | 77.6k | &self, | 1916 | 77.6k | var_index_base: u32, | 1917 | 77.6k | coordinates: &[NormalizedCoordinate], | 1918 | 77.6k | ) -> [f32; N] { | 1919 | | const NO_VARIATION_DELTAS: u32 = 0xFFFFFFFF; | 1920 | 77.6k | let mut deltas = [0.0; N]; | 1921 | | | 1922 | 77.6k | if coordinates.is_empty() | 1923 | 0 | || self.variation_store.is_none() | 1924 | 0 | || var_index_base == NO_VARIATION_DELTAS | 1925 | | { | 1926 | 77.6k | return deltas; | 1927 | 0 | } | 1928 | | | 1929 | 0 | let variation_store = self.variation_store.as_ref().unwrap(); | 1930 | | | 1931 | 0 | for (i, delta) in deltas.iter_mut().enumerate() { | 1932 | | // `var_index_base` comes straight from the paint table, so this addition can | 1933 | | // overflow: an abort in debug, and a wrap to an unrelated delta index in | 1934 | | // release. `0xFFFFFFFF` is already handled above as "no deltas". ~keep | 1935 | 0 | *delta = self | 1936 | 0 | .delta_map | 1937 | 0 | .zip(var_index_base.checked_add(i as u32)) | 1938 | 0 | .and_then(|(d, index)| d.map(index)) | 1939 | 0 | .and_then(|d| variation_store.parse_delta(d.0, d.1, coordinates)) | 1940 | 0 | .unwrap_or(0.0); | 1941 | | } | 1942 | | | 1943 | 0 | deltas | 1944 | 77.6k | } |
<ttf_parser::tables::colr::VariationData>::read_deltas::<2> Line | Count | Source | 1914 | 132k | fn read_deltas<const N: usize>( | 1915 | 132k | &self, | 1916 | 132k | var_index_base: u32, | 1917 | 132k | coordinates: &[NormalizedCoordinate], | 1918 | 132k | ) -> [f32; N] { | 1919 | | const NO_VARIATION_DELTAS: u32 = 0xFFFFFFFF; | 1920 | 132k | let mut deltas = [0.0; N]; | 1921 | | | 1922 | 132k | if coordinates.is_empty() | 1923 | 0 | || self.variation_store.is_none() | 1924 | 0 | || var_index_base == NO_VARIATION_DELTAS | 1925 | | { | 1926 | 132k | return deltas; | 1927 | 0 | } | 1928 | | | 1929 | 0 | let variation_store = self.variation_store.as_ref().unwrap(); | 1930 | | | 1931 | 0 | for (i, delta) in deltas.iter_mut().enumerate() { | 1932 | | // `var_index_base` comes straight from the paint table, so this addition can | 1933 | | // overflow: an abort in debug, and a wrap to an unrelated delta index in | 1934 | | // release. `0xFFFFFFFF` is already handled above as "no deltas". ~keep | 1935 | 0 | *delta = self | 1936 | 0 | .delta_map | 1937 | 0 | .zip(var_index_base.checked_add(i as u32)) | 1938 | 0 | .and_then(|(d, index)| d.map(index)) | 1939 | 0 | .and_then(|d| variation_store.parse_delta(d.0, d.1, coordinates)) | 1940 | 0 | .unwrap_or(0.0); | 1941 | | } | 1942 | | | 1943 | 0 | deltas | 1944 | 132k | } |
<ttf_parser::tables::colr::VariationData>::read_deltas::<3> Line | Count | Source | 1914 | 18.2k | fn read_deltas<const N: usize>( | 1915 | 18.2k | &self, | 1916 | 18.2k | var_index_base: u32, | 1917 | 18.2k | coordinates: &[NormalizedCoordinate], | 1918 | 18.2k | ) -> [f32; N] { | 1919 | | const NO_VARIATION_DELTAS: u32 = 0xFFFFFFFF; | 1920 | 18.2k | let mut deltas = [0.0; N]; | 1921 | | | 1922 | 18.2k | if coordinates.is_empty() | 1923 | 0 | || self.variation_store.is_none() | 1924 | 0 | || var_index_base == NO_VARIATION_DELTAS | 1925 | | { | 1926 | 18.2k | return deltas; | 1927 | 0 | } | 1928 | | | 1929 | 0 | let variation_store = self.variation_store.as_ref().unwrap(); | 1930 | | | 1931 | 0 | for (i, delta) in deltas.iter_mut().enumerate() { | 1932 | | // `var_index_base` comes straight from the paint table, so this addition can | 1933 | | // overflow: an abort in debug, and a wrap to an unrelated delta index in | 1934 | | // release. `0xFFFFFFFF` is already handled above as "no deltas". ~keep | 1935 | 0 | *delta = self | 1936 | 0 | .delta_map | 1937 | 0 | .zip(var_index_base.checked_add(i as u32)) | 1938 | 0 | .and_then(|(d, index)| d.map(index)) | 1939 | 0 | .and_then(|d| variation_store.parse_delta(d.0, d.1, coordinates)) | 1940 | 0 | .unwrap_or(0.0); | 1941 | | } | 1942 | | | 1943 | 0 | deltas | 1944 | 18.2k | } |
<ttf_parser::tables::colr::VariationData>::read_deltas::<4> Line | Count | Source | 1914 | 16.9k | fn read_deltas<const N: usize>( | 1915 | 16.9k | &self, | 1916 | 16.9k | var_index_base: u32, | 1917 | 16.9k | coordinates: &[NormalizedCoordinate], | 1918 | 16.9k | ) -> [f32; N] { | 1919 | | const NO_VARIATION_DELTAS: u32 = 0xFFFFFFFF; | 1920 | 16.9k | let mut deltas = [0.0; N]; | 1921 | | | 1922 | 16.9k | if coordinates.is_empty() | 1923 | 0 | || self.variation_store.is_none() | 1924 | 0 | || var_index_base == NO_VARIATION_DELTAS | 1925 | | { | 1926 | 16.9k | return deltas; | 1927 | 0 | } | 1928 | | | 1929 | 0 | let variation_store = self.variation_store.as_ref().unwrap(); | 1930 | | | 1931 | 0 | for (i, delta) in deltas.iter_mut().enumerate() { | 1932 | | // `var_index_base` comes straight from the paint table, so this addition can | 1933 | | // overflow: an abort in debug, and a wrap to an unrelated delta index in | 1934 | | // release. `0xFFFFFFFF` is already handled above as "no deltas". ~keep | 1935 | 0 | *delta = self | 1936 | 0 | .delta_map | 1937 | 0 | .zip(var_index_base.checked_add(i as u32)) | 1938 | 0 | .and_then(|(d, index)| d.map(index)) | 1939 | 0 | .and_then(|d| variation_store.parse_delta(d.0, d.1, coordinates)) | 1940 | 0 | .unwrap_or(0.0); | 1941 | | } | 1942 | | | 1943 | 0 | deltas | 1944 | 16.9k | } |
<ttf_parser::tables::colr::VariationData>::read_deltas::<6> Line | Count | Source | 1914 | 41.2k | fn read_deltas<const N: usize>( | 1915 | 41.2k | &self, | 1916 | 41.2k | var_index_base: u32, | 1917 | 41.2k | coordinates: &[NormalizedCoordinate], | 1918 | 41.2k | ) -> [f32; N] { | 1919 | | const NO_VARIATION_DELTAS: u32 = 0xFFFFFFFF; | 1920 | 41.2k | let mut deltas = [0.0; N]; | 1921 | | | 1922 | 41.2k | if coordinates.is_empty() | 1923 | 0 | || self.variation_store.is_none() | 1924 | 0 | || var_index_base == NO_VARIATION_DELTAS | 1925 | | { | 1926 | 41.2k | return deltas; | 1927 | 0 | } | 1928 | | | 1929 | 0 | let variation_store = self.variation_store.as_ref().unwrap(); | 1930 | | | 1931 | 0 | for (i, delta) in deltas.iter_mut().enumerate() { | 1932 | | // `var_index_base` comes straight from the paint table, so this addition can | 1933 | | // overflow: an abort in debug, and a wrap to an unrelated delta index in | 1934 | | // release. `0xFFFFFFFF` is already handled above as "no deltas". ~keep | 1935 | 0 | *delta = self | 1936 | 0 | .delta_map | 1937 | 0 | .zip(var_index_base.checked_add(i as u32)) | 1938 | 0 | .and_then(|(d, index)| d.map(index)) | 1939 | 0 | .and_then(|d| variation_store.parse_delta(d.0, d.1, coordinates)) | 1940 | 0 | .unwrap_or(0.0); | 1941 | | } | 1942 | | | 1943 | 0 | deltas | 1944 | 41.2k | } |
|
1945 | | } |