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::delta_set::DeltaSetIndexMap; |
9 | use crate::parser::{FromData, LazyArray16, Offset, Offset24, Offset32, Stream, F2DOT14}; |
10 | #[cfg (feature = "variable-fonts" )] |
11 | use crate::var_store::ItemVariationStore; |
12 | #[cfg (feature = "variable-fonts" )] |
13 | use crate::NormalizedCoordinate; |
14 | use crate::{cpal, Fixed, LazyArray32, RectF, Transform}; |
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 | fn parse(data: &[u8]) -> Option<Self> { |
58 | let mut s: Stream<'_> = Stream::new(data); |
59 | Some(ClipRecord { |
60 | start_glyph_id: s.read::<GlyphId>()?, |
61 | end_glyph_id: s.read::<GlyphId>()?, |
62 | clip_box_offset: s.read::<Offset24>()?, |
63 | }) |
64 | } |
65 | } |
66 | |
67 | impl ClipRecord { |
68 | /// Returns the glyphs range. |
69 | pub fn glyphs_range(&self) -> core::ops::RangeInclusive<GlyphId> { |
70 | self.start_glyph_id..=self.end_glyph_id |
71 | } |
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 | pub fn get( |
84 | &self, |
85 | index: u32, |
86 | #[cfg (feature = "variable-fonts" )] variation_data: &VariationData, |
87 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
88 | ) -> Option<ClipBox> { |
89 | let record = self.records.get(index)?; |
90 | let offset = record.clip_box_offset.to_usize(); |
91 | self.data.get(offset..).and_then(|data| { |
92 | let mut s = Stream::new(data); |
93 | 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 | let deltas = if format == 2 { |
99 | let mut var_s = s.clone(); |
100 | var_s.advance(8); |
101 | let var_index_base = var_s.read::<u32>()?; |
102 | |
103 | variation_data.read_deltas::<4>(var_index_base, coords) |
104 | } else { |
105 | [0.0, 0.0, 0.0, 0.0] |
106 | }; |
107 | |
108 | Some(ClipBox { |
109 | x_min: s.read::<i16>()? as f32 + deltas[0], |
110 | y_min: s.read::<i16>()? as f32 + deltas[1], |
111 | x_max: s.read::<i16>()? as f32 + deltas[2], |
112 | y_max: s.read::<i16>()? as f32 + deltas[3], |
113 | }) |
114 | }) |
115 | } |
116 | |
117 | /// Returns a ClipBox by glyph ID. |
118 | #[inline ] |
119 | pub fn find( |
120 | &self, |
121 | glyph_id: GlyphId, |
122 | #[cfg (feature = "variable-fonts" )] variation_data: &VariationData, |
123 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
124 | ) -> Option<ClipBox> { |
125 | let index = self |
126 | .records |
127 | .into_iter() |
128 | .position(|v| v.glyphs_range().contains(&glyph_id))?; |
129 | self.get( |
130 | index as u32, |
131 | #[cfg (feature = "variable-fonts" )] |
132 | variation_data, |
133 | #[cfg (feature = "variable-fonts" )] |
134 | coords, |
135 | ) |
136 | } |
137 | } |
138 | |
139 | impl FromData for BaseGlyphRecord { |
140 | const SIZE: usize = 6; |
141 | |
142 | fn parse(data: &[u8]) -> Option<Self> { |
143 | let mut s: Stream<'_> = Stream::new(data); |
144 | Some(Self { |
145 | glyph_id: s.read::<GlyphId>()?, |
146 | first_layer_index: s.read::<u16>()?, |
147 | num_layers: s.read::<u16>()?, |
148 | }) |
149 | } |
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 | fn parse(data: &[u8]) -> Option<Self> { |
164 | let mut s: Stream<'_> = Stream::new(data); |
165 | Some(Self { |
166 | glyph_id: s.read::<GlyphId>()?, |
167 | palette_index: s.read::<u16>()?, |
168 | }) |
169 | } |
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 | fn parse(data: &[u8]) -> Option<Self> { |
184 | let mut s: Stream<'_> = Stream::new(data); |
185 | Some(Self { |
186 | glyph_id: s.read::<GlyphId>()?, |
187 | paint_table_offset: s.read::<Offset32>()?, |
188 | }) |
189 | } |
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 | fn parse(data: &[u8]) -> Option<Self> { |
208 | match data[0] { |
209 | 0 => Some(Self::Pad), |
210 | 1 => Some(Self::Repeat), |
211 | 2 => Some(Self::Reflect), |
212 | _ => None, |
213 | } |
214 | } |
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 | fn parse(data: &[u8]) -> Option<Self> { |
230 | let mut s: Stream<'_> = Stream::new(data); |
231 | Some(Self { |
232 | stop_offset: s.read::<F2DOT14>()?, |
233 | palette_index: s.read::<u16>()?, |
234 | alpha: s.read::<F2DOT14>()?, |
235 | }) |
236 | } |
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 | fn parse(data: &[u8]) -> Option<Self> { |
255 | let mut s = Stream::new(data); |
256 | Some(Self { |
257 | stop_offset: s.read::<F2DOT14>()?, |
258 | palette_index: s.read::<u16>()?, |
259 | alpha: s.read::<F2DOT14>()?, |
260 | var_index_base: s.read::<u32>()?, |
261 | }) |
262 | } |
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 | fn get(&self, palette: u16, index: u16) -> Option<ColorStop> { |
276 | let info: ColorStopRaw = self.colors.get(index)?; |
277 | |
278 | let mut color: RgbaColor = if info.palette_index == u16::MAX { |
279 | self.foreground_color |
280 | } else { |
281 | self.palettes.get(palette, palette_entry:info.palette_index)? |
282 | }; |
283 | |
284 | color.apply_alpha(info.alpha.to_f32()); |
285 | Some(ColorStop { |
286 | stop_offset: info.stop_offset.to_f32(), |
287 | color, |
288 | }) |
289 | } |
290 | } |
291 | |
292 | #[cfg (feature = "variable-fonts" )] |
293 | impl VarColorLine<'_> { |
294 | // TODO: Color stops should be sorted, but hard to do without allocations |
295 | fn get( |
296 | &self, |
297 | palette: u16, |
298 | index: u16, |
299 | #[cfg (feature = "variable-fonts" )] variation_data: VariationData, |
300 | #[cfg (feature = "variable-fonts" )] coordinates: &[NormalizedCoordinate], |
301 | ) -> Option<ColorStop> { |
302 | let info = self.colors.get(index)?; |
303 | |
304 | let mut color = if info.palette_index == u16::MAX { |
305 | self.foreground_color |
306 | } else { |
307 | self.palettes.get(palette, info.palette_index)? |
308 | }; |
309 | |
310 | let deltas = variation_data.read_deltas::<2>(info.var_index_base, coordinates); |
311 | let stop_offset = info.stop_offset.apply_float_delta(deltas[0]); |
312 | color.apply_alpha(info.alpha.apply_float_delta(deltas[1])); |
313 | |
314 | Some(ColorStop { stop_offset, color }) |
315 | } |
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 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
368 | f&mut DebugStruct<'_, '_>.debug_struct("LinearGradient" ) |
369 | .field("x0" , &self.x0) |
370 | .field("y0" , &self.y0) |
371 | .field("x1" , &self.x1) |
372 | .field("y1" , &self.y1) |
373 | .field("x2" , &self.x2) |
374 | .field("y2" , &self.y2) |
375 | .field("extend" , &self.extend) |
376 | .field( |
377 | name:"stops" , |
378 | &self.stops( |
379 | palette:0, |
380 | #[cfg (feature = "variable-fonts" )] |
381 | &[], |
382 | ), |
383 | ) |
384 | .finish() |
385 | } |
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 | pub fn stops<'b>( |
392 | &'b self, |
393 | palette: u16, |
394 | #[cfg (feature = "variable-fonts" )] coords: &'b [NormalizedCoordinate], |
395 | ) -> GradientStopsIter<'a, 'b> { |
396 | GradientStopsIter { |
397 | color_line: &self.color_line, |
398 | palette, |
399 | index: 0, |
400 | #[cfg (feature = "variable-fonts" )] |
401 | variation_data: self.variation_data, |
402 | #[cfg (feature = "variable-fonts" )] |
403 | coords, |
404 | } |
405 | } |
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 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
432 | f&mut DebugStruct<'_, '_>.debug_struct("RadialGradient" ) |
433 | .field("x0" , &self.x0) |
434 | .field("y0" , &self.y0) |
435 | .field("r0" , &self.r0) |
436 | .field("r1" , &self.r1) |
437 | .field("x1" , &self.x1) |
438 | .field("y1" , &self.y1) |
439 | .field("extend" , &self.extend) |
440 | .field( |
441 | name:"stops" , |
442 | &self.stops( |
443 | palette:0, |
444 | #[cfg (feature = "variable-fonts" )] |
445 | &[], |
446 | ), |
447 | ) |
448 | .finish() |
449 | } |
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 | pub fn stops<'b>( |
456 | &'b self, |
457 | palette: u16, |
458 | #[cfg (feature = "variable-fonts" )] coords: &'a [NormalizedCoordinate], |
459 | ) -> GradientStopsIter<'a, 'b> { |
460 | GradientStopsIter { |
461 | color_line: &self.color_line, |
462 | palette, |
463 | index: 0, |
464 | #[cfg (feature = "variable-fonts" )] |
465 | variation_data: self.variation_data, |
466 | #[cfg (feature = "variable-fonts" )] |
467 | coords, |
468 | } |
469 | } |
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 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
492 | f&mut DebugStruct<'_, '_>.debug_struct("SweepGradient" ) |
493 | .field("center_x" , &self.center_x) |
494 | .field("center_y" , &self.center_y) |
495 | .field("start_angle" , &self.start_angle) |
496 | .field("end_angle" , &self.end_angle) |
497 | .field("extend" , &self.extend) |
498 | .field( |
499 | name:"stops" , |
500 | &self.stops( |
501 | palette:0, |
502 | #[cfg (feature = "variable-fonts" )] |
503 | &[], |
504 | ), |
505 | ) |
506 | .finish() |
507 | } |
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 | pub fn stops<'b>( |
516 | &'b self, |
517 | palette: u16, |
518 | #[cfg (feature = "variable-fonts" )] coords: &'a [NormalizedCoordinate], |
519 | ) -> GradientStopsIter<'a, 'b> { |
520 | GradientStopsIter { |
521 | color_line: &self.color_line, |
522 | palette, |
523 | index: 0, |
524 | #[cfg (feature = "variable-fonts" )] |
525 | variation_data: self.variation_data, |
526 | #[cfg (feature = "variable-fonts" )] |
527 | coords, |
528 | } |
529 | } |
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 | fn next(&mut self) -> Option<Self::Item> { |
548 | let len = match self.color_line { |
549 | #[cfg (feature = "variable-fonts" )] |
550 | ColorLine::VarColorLine(vcl) => vcl.colors.len(), |
551 | ColorLine::NonVarColorLine(nvcl) => nvcl.colors.len(), |
552 | }; |
553 | |
554 | if self.index == len { |
555 | return None; |
556 | } |
557 | |
558 | let index = self.index; |
559 | self.index = self.index.checked_add(1)?; |
560 | |
561 | match self.color_line { |
562 | #[cfg (feature = "variable-fonts" )] |
563 | ColorLine::VarColorLine(vcl) => { |
564 | vcl.get(self.palette, index, self.variation_data, self.coords) |
565 | } |
566 | ColorLine::NonVarColorLine(nvcl) => nvcl.get(self.palette, index), |
567 | } |
568 | } |
569 | } |
570 | |
571 | impl core::fmt::Debug for GradientStopsIter<'_, '_> { |
572 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
573 | f.debug_list().entries(*self).finish() |
574 | } |
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 | fn parse(data: &[u8]) -> Option<Self> { |
642 | match data[0] { |
643 | 0 => Some(Self::Clear), |
644 | 1 => Some(Self::Source), |
645 | 2 => Some(Self::Destination), |
646 | 3 => Some(Self::SourceOver), |
647 | 4 => Some(Self::DestinationOver), |
648 | 5 => Some(Self::SourceIn), |
649 | 6 => Some(Self::DestinationIn), |
650 | 7 => Some(Self::SourceOut), |
651 | 8 => Some(Self::DestinationOut), |
652 | 9 => Some(Self::SourceAtop), |
653 | 10 => Some(Self::DestinationAtop), |
654 | 11 => Some(Self::Xor), |
655 | 12 => Some(Self::Plus), |
656 | 13 => Some(Self::Screen), |
657 | 14 => Some(Self::Overlay), |
658 | 15 => Some(Self::Darken), |
659 | 16 => Some(Self::Lighten), |
660 | 17 => Some(Self::ColorDodge), |
661 | 18 => Some(Self::ColorBurn), |
662 | 19 => Some(Self::HardLight), |
663 | 20 => Some(Self::SoftLight), |
664 | 21 => Some(Self::Difference), |
665 | 22 => Some(Self::Exclusion), |
666 | 23 => Some(Self::Multiply), |
667 | 24 => Some(Self::Hue), |
668 | 25 => Some(Self::Saturation), |
669 | 26 => Some(Self::Color), |
670 | 27 => Some(Self::Luminosity), |
671 | _ => None, |
672 | } |
673 | } |
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 | |
698 | // TODO: Unify transforms into one callback. |
699 | /// Push a translation transform. |
700 | fn push_translate(&mut self, tx: f32, ty: f32); |
701 | /// Push a scaling transform. |
702 | fn push_scale(&mut self, sx: f32, sy: f32); |
703 | /// Push a rotation transform. |
704 | fn push_rotate(&mut self, angle: f32); |
705 | /// Push a skewing transform. |
706 | fn push_skew(&mut self, skew_x: f32, skew_y: f32); |
707 | /// Push a transform. |
708 | fn push_transform(&mut self, transform: Transform); |
709 | /// Pop the last transform. |
710 | fn pop_transform(&mut self); |
711 | } |
712 | |
713 | /// A [Color Table]( |
714 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/colr). |
715 | /// |
716 | /// Currently, only version 0 is supported. |
717 | #[derive (Clone, Copy, Debug)] |
718 | pub struct Table<'a> { |
719 | pub(crate) palettes: cpal::Table<'a>, |
720 | data: &'a [u8], |
721 | version: u8, |
722 | // v0 |
723 | base_glyphs: LazyArray16<'a, BaseGlyphRecord>, |
724 | layers: LazyArray16<'a, LayerRecord>, |
725 | // v1 |
726 | base_glyph_paints_offset: Offset32, |
727 | base_glyph_paints: LazyArray32<'a, BaseGlyphPaintRecord>, |
728 | layer_paint_offsets_offset: Offset32, |
729 | layer_paint_offsets: LazyArray32<'a, Offset32>, |
730 | clip_list_offsets_offset: Offset32, |
731 | clip_list: ClipList<'a>, |
732 | #[cfg (feature = "variable-fonts" )] |
733 | var_index_map: Option<DeltaSetIndexMap<'a>>, |
734 | #[cfg (feature = "variable-fonts" )] |
735 | item_variation_store: Option<ItemVariationStore<'a>>, |
736 | } |
737 | |
738 | impl<'a> Table<'a> { |
739 | /// Parses a table from raw data. |
740 | pub fn parse(palettes: cpal::Table<'a>, data: &'a [u8]) -> Option<Self> { |
741 | let mut s = Stream::new(data); |
742 | |
743 | let version = s.read::<u16>()?; |
744 | if version > 1 { |
745 | return None; |
746 | } |
747 | |
748 | let num_base_glyphs = s.read::<u16>()?; |
749 | let base_glyphs_offset = s.read::<Offset32>()?; |
750 | let layers_offset = s.read::<Offset32>()?; |
751 | let num_layers = s.read::<u16>()?; |
752 | |
753 | let base_glyphs = Stream::new_at(data, base_glyphs_offset.to_usize())? |
754 | .read_array16::<BaseGlyphRecord>(num_base_glyphs)?; |
755 | |
756 | let layers = Stream::new_at(data, layers_offset.to_usize())? |
757 | .read_array16::<LayerRecord>(num_layers)?; |
758 | |
759 | let mut table = Self { |
760 | version: version as u8, |
761 | data, |
762 | palettes, |
763 | base_glyphs, |
764 | layers, |
765 | base_glyph_paints_offset: Offset32(0), // the actual value doesn't matter |
766 | base_glyph_paints: LazyArray32::default(), |
767 | layer_paint_offsets_offset: Offset32(0), |
768 | layer_paint_offsets: LazyArray32::default(), |
769 | clip_list_offsets_offset: Offset32(0), |
770 | clip_list: ClipList::default(), |
771 | #[cfg (feature = "variable-fonts" )] |
772 | item_variation_store: None, |
773 | #[cfg (feature = "variable-fonts" )] |
774 | var_index_map: None, |
775 | }; |
776 | |
777 | if version == 0 { |
778 | return Some(table); |
779 | } |
780 | |
781 | table.base_glyph_paints_offset = s.read::<Offset32>()?; |
782 | let layer_list_offset = s.read::<Option<Offset32>>()?; |
783 | let clip_list_offset = s.read::<Option<Offset32>>()?; |
784 | #[cfg (feature = "variable-fonts" )] |
785 | let var_index_map_offset = s.read::<Option<Offset32>>()?; |
786 | #[cfg (feature = "variable-fonts" )] |
787 | let item_variation_offset = s.read::<Option<Offset32>>()?; |
788 | |
789 | { |
790 | let mut s = Stream::new_at(data, table.base_glyph_paints_offset.to_usize())?; |
791 | let count = s.read::<u32>()?; |
792 | table.base_glyph_paints = s.read_array32::<BaseGlyphPaintRecord>(count)?; |
793 | } |
794 | |
795 | if let Some(offset) = layer_list_offset { |
796 | table.layer_paint_offsets_offset = offset; |
797 | let mut s = Stream::new_at(data, offset.to_usize())?; |
798 | let count = s.read::<u32>()?; |
799 | table.layer_paint_offsets = s.read_array32::<Offset32>(count)?; |
800 | } |
801 | |
802 | if let Some(offset) = clip_list_offset { |
803 | table.clip_list_offsets_offset = offset; |
804 | let clip_data = data.get(offset.to_usize()..)?; |
805 | let mut s = Stream::new(clip_data); |
806 | s.skip::<u8>(); // Format |
807 | let count = s.read::<u32>()?; |
808 | table.clip_list = ClipList { |
809 | data: clip_data, |
810 | records: s.read_array32::<ClipRecord>(count)?, |
811 | }; |
812 | } |
813 | |
814 | #[cfg (feature = "variable-fonts" )] |
815 | { |
816 | if let Some(offset) = item_variation_offset { |
817 | let item_var_data = data.get(offset.to_usize()..)?; |
818 | let s = Stream::new(item_var_data); |
819 | let var_store = ItemVariationStore::parse(s)?; |
820 | table.item_variation_store = Some(var_store); |
821 | } |
822 | } |
823 | |
824 | #[cfg (feature = "variable-fonts" )] |
825 | { |
826 | if let Some(offset) = var_index_map_offset { |
827 | let var_index_map_data = data.get(offset.to_usize()..)?; |
828 | let var_index_map = DeltaSetIndexMap::new(var_index_map_data); |
829 | table.var_index_map = Some(var_index_map); |
830 | } |
831 | } |
832 | |
833 | Some(table) |
834 | } |
835 | |
836 | /// Returns `true` if the current table has version 0. |
837 | /// |
838 | /// A simple table can only emit `outline_glyph` and `paint` |
839 | /// [`Painter`] methods. |
840 | pub fn is_simple(&self) -> bool { |
841 | self.version == 0 |
842 | } |
843 | |
844 | fn get_v0(&self, glyph_id: GlyphId) -> Option<BaseGlyphRecord> { |
845 | self.base_glyphs |
846 | .binary_search_by(|base| base.glyph_id.cmp(&glyph_id)) |
847 | .map(|v| v.1) |
848 | } |
849 | |
850 | fn get_v1(&self, glyph_id: GlyphId) -> Option<BaseGlyphPaintRecord> { |
851 | self.base_glyph_paints |
852 | .binary_search_by(|base| base.glyph_id.cmp(&glyph_id)) |
853 | .map(|v| v.1) |
854 | } |
855 | |
856 | #[cfg (feature = "variable-fonts" )] |
857 | fn variation_data(&self) -> VariationData<'a> { |
858 | VariationData { |
859 | variation_store: self.item_variation_store, |
860 | delta_map: self.var_index_map, |
861 | } |
862 | } |
863 | |
864 | /// Whether the table contains a definition for the given glyph. |
865 | pub fn contains(&self, glyph_id: GlyphId) -> bool { |
866 | self.get_v1(glyph_id).is_some() || self.get_v0(glyph_id).is_some() |
867 | } |
868 | |
869 | // This method should only be called from outside, not from within `colr.rs`. |
870 | // From inside, you always should call paint_impl, so that the recursion stack can |
871 | // be passed on and any kind of recursion can be prevented. |
872 | /// Paints the color glyph. |
873 | pub fn paint( |
874 | &self, |
875 | glyph_id: GlyphId, |
876 | palette: u16, |
877 | painter: &mut dyn Painter<'a>, |
878 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
879 | foreground_color: RgbaColor, |
880 | ) -> Option<()> { |
881 | let mut recursion_stack = RecursionStack { |
882 | stack: [0; 64], |
883 | len: 0, |
884 | }; |
885 | |
886 | self.paint_impl( |
887 | glyph_id, |
888 | palette, |
889 | painter, |
890 | &mut recursion_stack, |
891 | #[cfg (feature = "variable-fonts" )] |
892 | coords, |
893 | foreground_color, |
894 | ) |
895 | } |
896 | |
897 | fn paint_impl( |
898 | &self, |
899 | glyph_id: GlyphId, |
900 | palette: u16, |
901 | painter: &mut dyn Painter<'a>, |
902 | recusion_stack: &mut RecursionStack, |
903 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
904 | foreground_color: RgbaColor, |
905 | ) -> Option<()> { |
906 | if let Some(base) = self.get_v1(glyph_id) { |
907 | self.paint_v1( |
908 | base, |
909 | palette, |
910 | painter, |
911 | recusion_stack, |
912 | #[cfg (feature = "variable-fonts" )] |
913 | coords, |
914 | foreground_color, |
915 | ) |
916 | } else if let Some(base) = self.get_v0(glyph_id) { |
917 | self.paint_v0(base, palette, painter, foreground_color) |
918 | } else { |
919 | None |
920 | } |
921 | } |
922 | |
923 | fn paint_v0( |
924 | &self, |
925 | base: BaseGlyphRecord, |
926 | palette: u16, |
927 | painter: &mut dyn Painter, |
928 | foreground_color: RgbaColor, |
929 | ) -> Option<()> { |
930 | let start = base.first_layer_index; |
931 | let end = start.checked_add(base.num_layers)?; |
932 | let layers = self.layers.slice(start..end)?; |
933 | |
934 | for layer in layers { |
935 | if layer.palette_index == 0xFFFF { |
936 | // A special case. |
937 | painter.outline_glyph(layer.glyph_id); |
938 | painter.paint(Paint::Solid(foreground_color)); |
939 | } else { |
940 | let color = self.palettes.get(palette, layer.palette_index)?; |
941 | painter.outline_glyph(layer.glyph_id); |
942 | painter.paint(Paint::Solid(color)); |
943 | } |
944 | } |
945 | |
946 | Some(()) |
947 | } |
948 | |
949 | fn paint_v1( |
950 | &self, |
951 | base: BaseGlyphPaintRecord, |
952 | palette: u16, |
953 | painter: &mut dyn Painter<'a>, |
954 | recursion_stack: &mut RecursionStack, |
955 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
956 | foreground_color: RgbaColor, |
957 | ) -> Option<()> { |
958 | let clip_box = self.clip_list.find( |
959 | base.glyph_id, |
960 | #[cfg (feature = "variable-fonts" )] |
961 | &self.variation_data(), |
962 | #[cfg (feature = "variable-fonts" )] |
963 | coords, |
964 | ); |
965 | if let Some(clip_box) = clip_box { |
966 | painter.push_clip_box(clip_box); |
967 | } |
968 | |
969 | self.parse_paint( |
970 | self.base_glyph_paints_offset.to_usize() + base.paint_table_offset.to_usize(), |
971 | palette, |
972 | painter, |
973 | recursion_stack, |
974 | #[cfg (feature = "variable-fonts" )] |
975 | coords, |
976 | foreground_color, |
977 | ); |
978 | |
979 | if clip_box.is_some() { |
980 | painter.pop_clip(); |
981 | } |
982 | |
983 | Some(()) |
984 | } |
985 | |
986 | fn parse_paint( |
987 | &self, |
988 | offset: usize, |
989 | palette: u16, |
990 | painter: &mut dyn Painter<'a>, |
991 | recursion_stack: &mut RecursionStack, |
992 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
993 | foreground_color: RgbaColor, |
994 | ) -> Option<()> { |
995 | let mut s = Stream::new_at(self.data, offset)?; |
996 | let format = s.read::<u8>()?; |
997 | |
998 | // Cycle detected |
999 | if recursion_stack.contains(offset) { |
1000 | return None; |
1001 | } |
1002 | |
1003 | recursion_stack.push(offset).ok()?; |
1004 | let result = self.parse_paint_impl( |
1005 | offset, |
1006 | palette, |
1007 | painter, |
1008 | recursion_stack, |
1009 | &mut s, |
1010 | format, |
1011 | #[cfg (feature = "variable-fonts" )] |
1012 | coords, |
1013 | foreground_color, |
1014 | ); |
1015 | recursion_stack.pop(); |
1016 | |
1017 | result |
1018 | } |
1019 | |
1020 | fn parse_paint_impl( |
1021 | &self, |
1022 | offset: usize, |
1023 | palette: u16, |
1024 | painter: &mut dyn Painter<'a>, |
1025 | recursion_stack: &mut RecursionStack, |
1026 | s: &mut Stream, |
1027 | format: u8, |
1028 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
1029 | foreground_color: RgbaColor, |
1030 | ) -> Option<()> { |
1031 | match format { |
1032 | 1 => { |
1033 | // PaintColrLayers |
1034 | let layers_count = s.read::<u8>()?; |
1035 | let first_layer_index = s.read::<u32>()?; |
1036 | |
1037 | for i in 0..layers_count { |
1038 | let index = first_layer_index.checked_add(u32::from(i))?; |
1039 | let paint_offset = self.layer_paint_offsets.get(index)?; |
1040 | self.parse_paint( |
1041 | self.layer_paint_offsets_offset.to_usize() + paint_offset.to_usize(), |
1042 | palette, |
1043 | painter, |
1044 | recursion_stack, |
1045 | #[cfg (feature = "variable-fonts" )] |
1046 | coords, |
1047 | foreground_color, |
1048 | ); |
1049 | } |
1050 | } |
1051 | 2 => { |
1052 | // PaintSolid |
1053 | let palette_index = s.read::<u16>()?; |
1054 | let alpha = s.read::<F2DOT14>()?; |
1055 | |
1056 | let mut color = if palette_index == u16::MAX { |
1057 | foreground_color |
1058 | } else { |
1059 | self.palettes.get(palette, palette_index)? |
1060 | }; |
1061 | |
1062 | color.apply_alpha(alpha.to_f32()); |
1063 | painter.paint(Paint::Solid(color)); |
1064 | } |
1065 | #[cfg (feature = "variable-fonts" )] |
1066 | 3 => { |
1067 | // PaintVarSolid |
1068 | let palette_index = s.read::<u16>()?; |
1069 | let alpha = s.read::<F2DOT14>()?; |
1070 | let var_index_base = s.read::<u32>()?; |
1071 | |
1072 | let deltas = self |
1073 | .variation_data() |
1074 | .read_deltas::<1>(var_index_base, coords); |
1075 | |
1076 | let mut color = if palette_index == u16::MAX { |
1077 | foreground_color |
1078 | } else { |
1079 | self.palettes.get(palette, palette_index)? |
1080 | }; |
1081 | |
1082 | color.apply_alpha(alpha.apply_float_delta(deltas[0])); |
1083 | painter.paint(Paint::Solid(color)); |
1084 | } |
1085 | 4 => { |
1086 | // PaintLinearGradient |
1087 | let color_line_offset = s.read::<Offset24>()?; |
1088 | let color_line = |
1089 | self.parse_color_line(offset + color_line_offset.to_usize(), foreground_color)?; |
1090 | |
1091 | painter.paint(Paint::LinearGradient(LinearGradient { |
1092 | x0: s.read::<i16>()? as f32, |
1093 | y0: s.read::<i16>()? as f32, |
1094 | x1: s.read::<i16>()? as f32, |
1095 | y1: s.read::<i16>()? as f32, |
1096 | x2: s.read::<i16>()? as f32, |
1097 | y2: s.read::<i16>()? as f32, |
1098 | extend: color_line.extend, |
1099 | #[cfg (feature = "variable-fonts" )] |
1100 | variation_data: self.variation_data(), |
1101 | color_line: ColorLine::NonVarColorLine(color_line), |
1102 | })) |
1103 | } |
1104 | #[cfg (feature = "variable-fonts" )] |
1105 | 5 => { |
1106 | // PaintVarLinearGradient |
1107 | let var_color_line_offset = s.read::<Offset24>()?; |
1108 | let color_line = self.parse_var_color_line( |
1109 | offset + var_color_line_offset.to_usize(), |
1110 | foreground_color, |
1111 | )?; |
1112 | let mut var_s = s.clone(); |
1113 | var_s.advance(12); |
1114 | let var_index_base = var_s.read::<u32>()?; |
1115 | |
1116 | let deltas = self |
1117 | .variation_data() |
1118 | .read_deltas::<6>(var_index_base, coords); |
1119 | |
1120 | painter.paint(Paint::LinearGradient(LinearGradient { |
1121 | x0: s.read::<i16>()? as f32 + deltas[0], |
1122 | y0: s.read::<i16>()? as f32 + deltas[1], |
1123 | x1: s.read::<i16>()? as f32 + deltas[2], |
1124 | y1: s.read::<i16>()? as f32 + deltas[3], |
1125 | x2: s.read::<i16>()? as f32 + deltas[4], |
1126 | y2: s.read::<i16>()? as f32 + deltas[5], |
1127 | extend: color_line.extend, |
1128 | variation_data: self.variation_data(), |
1129 | color_line: ColorLine::VarColorLine(color_line), |
1130 | })) |
1131 | } |
1132 | 6 => { |
1133 | // PaintRadialGradient |
1134 | let color_line_offset = s.read::<Offset24>()?; |
1135 | let color_line = |
1136 | self.parse_color_line(offset + color_line_offset.to_usize(), foreground_color)?; |
1137 | painter.paint(Paint::RadialGradient(RadialGradient { |
1138 | x0: s.read::<i16>()? as f32, |
1139 | y0: s.read::<i16>()? as f32, |
1140 | r0: s.read::<u16>()? as f32, |
1141 | x1: s.read::<i16>()? as f32, |
1142 | y1: s.read::<i16>()? as f32, |
1143 | r1: s.read::<u16>()? as f32, |
1144 | extend: color_line.extend, |
1145 | #[cfg (feature = "variable-fonts" )] |
1146 | variation_data: self.variation_data(), |
1147 | color_line: ColorLine::NonVarColorLine(color_line), |
1148 | })) |
1149 | } |
1150 | #[cfg (feature = "variable-fonts" )] |
1151 | 7 => { |
1152 | // PaintVarRadialGradient |
1153 | let color_line_offset = s.read::<Offset24>()?; |
1154 | let color_line = self.parse_var_color_line( |
1155 | offset + color_line_offset.to_usize(), |
1156 | foreground_color, |
1157 | )?; |
1158 | |
1159 | let mut var_s = s.clone(); |
1160 | var_s.advance(12); |
1161 | let var_index_base = var_s.read::<u32>()?; |
1162 | |
1163 | let deltas = self |
1164 | .variation_data() |
1165 | .read_deltas::<6>(var_index_base, coords); |
1166 | |
1167 | painter.paint(Paint::RadialGradient(RadialGradient { |
1168 | x0: s.read::<i16>()? as f32 + deltas[0], |
1169 | y0: s.read::<i16>()? as f32 + deltas[1], |
1170 | r0: s.read::<u16>()? as f32 + deltas[2], |
1171 | x1: s.read::<i16>()? as f32 + deltas[3], |
1172 | y1: s.read::<i16>()? as f32 + deltas[4], |
1173 | r1: s.read::<u16>()? as f32 + deltas[5], |
1174 | extend: color_line.extend, |
1175 | variation_data: self.variation_data(), |
1176 | color_line: ColorLine::VarColorLine(color_line), |
1177 | })) |
1178 | } |
1179 | 8 => { |
1180 | // PaintSweepGradient |
1181 | let color_line_offset = s.read::<Offset24>()?; |
1182 | let color_line = |
1183 | self.parse_color_line(offset + color_line_offset.to_usize(), foreground_color)?; |
1184 | painter.paint(Paint::SweepGradient(SweepGradient { |
1185 | center_x: s.read::<i16>()? as f32, |
1186 | center_y: s.read::<i16>()? as f32, |
1187 | start_angle: s.read::<F2DOT14>()?.to_f32(), |
1188 | end_angle: s.read::<F2DOT14>()?.to_f32(), |
1189 | extend: color_line.extend, |
1190 | color_line: ColorLine::NonVarColorLine(color_line), |
1191 | #[cfg (feature = "variable-fonts" )] |
1192 | variation_data: self.variation_data(), |
1193 | })) |
1194 | } |
1195 | #[cfg (feature = "variable-fonts" )] |
1196 | 9 => { |
1197 | // PaintVarSweepGradient |
1198 | let color_line_offset = s.read::<Offset24>()?; |
1199 | let color_line = self.parse_var_color_line( |
1200 | offset + color_line_offset.to_usize(), |
1201 | foreground_color, |
1202 | )?; |
1203 | |
1204 | let mut var_s = s.clone(); |
1205 | var_s.advance(8); |
1206 | let var_index_base = var_s.read::<u32>()?; |
1207 | |
1208 | let deltas = self |
1209 | .variation_data() |
1210 | .read_deltas::<4>(var_index_base, coords); |
1211 | |
1212 | painter.paint(Paint::SweepGradient(SweepGradient { |
1213 | center_x: s.read::<i16>()? as f32 + deltas[0], |
1214 | center_y: s.read::<i16>()? as f32 + deltas[1], |
1215 | start_angle: s.read::<F2DOT14>()?.apply_float_delta(deltas[2]), |
1216 | end_angle: s.read::<F2DOT14>()?.apply_float_delta(deltas[3]), |
1217 | extend: color_line.extend, |
1218 | color_line: ColorLine::VarColorLine(color_line), |
1219 | variation_data: self.variation_data(), |
1220 | })) |
1221 | } |
1222 | 10 => { |
1223 | // PaintGlyph |
1224 | let paint_offset = s.read::<Offset24>()?; |
1225 | let glyph_id = s.read::<GlyphId>()?; |
1226 | painter.outline_glyph(glyph_id); |
1227 | painter.push_clip(); |
1228 | |
1229 | self.parse_paint( |
1230 | offset + paint_offset.to_usize(), |
1231 | palette, |
1232 | painter, |
1233 | recursion_stack, |
1234 | #[cfg (feature = "variable-fonts" )] |
1235 | coords, |
1236 | foreground_color, |
1237 | ); |
1238 | |
1239 | painter.pop_clip(); |
1240 | } |
1241 | 11 => { |
1242 | // PaintColrGlyph |
1243 | let glyph_id = s.read::<GlyphId>()?; |
1244 | self.paint_impl( |
1245 | glyph_id, |
1246 | palette, |
1247 | painter, |
1248 | recursion_stack, |
1249 | #[cfg (feature = "variable-fonts" )] |
1250 | coords, |
1251 | foreground_color, |
1252 | ); |
1253 | } |
1254 | 12 => { |
1255 | // PaintTransform |
1256 | let paint_offset = s.read::<Offset24>()?; |
1257 | let ts_offset = s.read::<Offset24>()?; |
1258 | let mut s = Stream::new_at(self.data, offset + ts_offset.to_usize())?; |
1259 | let ts = Transform { |
1260 | a: s.read::<Fixed>().map(|n| n.0)?, |
1261 | b: s.read::<Fixed>().map(|n| n.0)?, |
1262 | c: s.read::<Fixed>().map(|n| n.0)?, |
1263 | d: s.read::<Fixed>().map(|n| n.0)?, |
1264 | e: s.read::<Fixed>().map(|n| n.0)?, |
1265 | f: s.read::<Fixed>().map(|n| n.0)?, |
1266 | }; |
1267 | |
1268 | painter.push_transform(ts); |
1269 | self.parse_paint( |
1270 | offset + paint_offset.to_usize(), |
1271 | palette, |
1272 | painter, |
1273 | recursion_stack, |
1274 | #[cfg (feature = "variable-fonts" )] |
1275 | coords, |
1276 | foreground_color, |
1277 | ); |
1278 | painter.pop_transform(); |
1279 | } |
1280 | #[cfg (feature = "variable-fonts" )] |
1281 | 13 => { |
1282 | // PaintVarTransform |
1283 | let paint_offset = s.read::<Offset24>()?; |
1284 | let ts_offset = s.read::<Offset24>()?; |
1285 | let mut s = Stream::new_at(self.data, offset + ts_offset.to_usize())?; |
1286 | |
1287 | let mut var_s = s.clone(); |
1288 | var_s.advance(24); |
1289 | let var_index_base = var_s.read::<u32>()?; |
1290 | |
1291 | let deltas = self |
1292 | .variation_data() |
1293 | .read_deltas::<6>(var_index_base, coords); |
1294 | |
1295 | let ts = Transform { |
1296 | a: s.read::<Fixed>()?.apply_float_delta(deltas[0]), |
1297 | b: s.read::<Fixed>()?.apply_float_delta(deltas[1]), |
1298 | c: s.read::<Fixed>()?.apply_float_delta(deltas[2]), |
1299 | d: s.read::<Fixed>()?.apply_float_delta(deltas[3]), |
1300 | e: s.read::<Fixed>()?.apply_float_delta(deltas[4]), |
1301 | f: s.read::<Fixed>()?.apply_float_delta(deltas[5]), |
1302 | }; |
1303 | |
1304 | painter.push_transform(ts); |
1305 | self.parse_paint( |
1306 | offset + paint_offset.to_usize(), |
1307 | palette, |
1308 | painter, |
1309 | recursion_stack, |
1310 | coords, |
1311 | foreground_color, |
1312 | ); |
1313 | painter.pop_transform(); |
1314 | } |
1315 | 14 => { |
1316 | // PaintTranslate |
1317 | let paint_offset = s.read::<Offset24>()?; |
1318 | let tx = f32::from(s.read::<i16>()?); |
1319 | let ty = f32::from(s.read::<i16>()?); |
1320 | |
1321 | painter.push_translate(tx, ty); |
1322 | self.parse_paint( |
1323 | offset + paint_offset.to_usize(), |
1324 | palette, |
1325 | painter, |
1326 | recursion_stack, |
1327 | #[cfg (feature = "variable-fonts" )] |
1328 | coords, |
1329 | foreground_color, |
1330 | ); |
1331 | painter.pop_transform(); |
1332 | } |
1333 | #[cfg (feature = "variable-fonts" )] |
1334 | 15 => { |
1335 | // PaintVarTranslate |
1336 | let paint_offset = s.read::<Offset24>()?; |
1337 | |
1338 | let mut var_s = s.clone(); |
1339 | var_s.advance(4); |
1340 | let var_index_base = var_s.read::<u32>()?; |
1341 | |
1342 | let deltas = self |
1343 | .variation_data() |
1344 | .read_deltas::<2>(var_index_base, coords); |
1345 | |
1346 | let tx = f32::from(s.read::<i16>()?) + deltas[0]; |
1347 | let ty = f32::from(s.read::<i16>()?) + deltas[1]; |
1348 | |
1349 | painter.push_translate(tx, ty); |
1350 | self.parse_paint( |
1351 | offset + paint_offset.to_usize(), |
1352 | palette, |
1353 | painter, |
1354 | recursion_stack, |
1355 | coords, |
1356 | foreground_color, |
1357 | ); |
1358 | painter.pop_transform(); |
1359 | } |
1360 | 16 => { |
1361 | // PaintScale |
1362 | let paint_offset = s.read::<Offset24>()?; |
1363 | let sx = s.read::<F2DOT14>()?.to_f32(); |
1364 | let sy = s.read::<F2DOT14>()?.to_f32(); |
1365 | |
1366 | painter.push_scale(sx, sy); |
1367 | self.parse_paint( |
1368 | offset + paint_offset.to_usize(), |
1369 | palette, |
1370 | painter, |
1371 | recursion_stack, |
1372 | #[cfg (feature = "variable-fonts" )] |
1373 | coords, |
1374 | foreground_color, |
1375 | ); |
1376 | painter.pop_transform(); |
1377 | } |
1378 | #[cfg (feature = "variable-fonts" )] |
1379 | 17 => { |
1380 | // PaintVarScale |
1381 | let paint_offset = s.read::<Offset24>()?; |
1382 | |
1383 | let mut var_s = s.clone(); |
1384 | var_s.advance(4); |
1385 | let var_index_base = var_s.read::<u32>()?; |
1386 | |
1387 | let deltas = self |
1388 | .variation_data() |
1389 | .read_deltas::<2>(var_index_base, coords); |
1390 | |
1391 | let sx = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1392 | let sy = s.read::<F2DOT14>()?.apply_float_delta(deltas[1]); |
1393 | |
1394 | painter.push_scale(sx, sy); |
1395 | self.parse_paint( |
1396 | offset + paint_offset.to_usize(), |
1397 | palette, |
1398 | painter, |
1399 | recursion_stack, |
1400 | coords, |
1401 | foreground_color, |
1402 | ); |
1403 | painter.pop_transform(); |
1404 | } |
1405 | 18 => { |
1406 | // PaintScaleAroundCenter |
1407 | let paint_offset = s.read::<Offset24>()?; |
1408 | let sx = s.read::<F2DOT14>()?.to_f32(); |
1409 | let sy = s.read::<F2DOT14>()?.to_f32(); |
1410 | let center_x = f32::from(s.read::<i16>()?); |
1411 | let center_y = f32::from(s.read::<i16>()?); |
1412 | |
1413 | painter.push_translate(center_x, center_y); |
1414 | painter.push_scale(sx, sy); |
1415 | painter.push_translate(-center_x, -center_y); |
1416 | self.parse_paint( |
1417 | offset + paint_offset.to_usize(), |
1418 | palette, |
1419 | painter, |
1420 | recursion_stack, |
1421 | #[cfg (feature = "variable-fonts" )] |
1422 | coords, |
1423 | foreground_color, |
1424 | ); |
1425 | painter.pop_transform(); |
1426 | painter.pop_transform(); |
1427 | painter.pop_transform(); |
1428 | } |
1429 | #[cfg (feature = "variable-fonts" )] |
1430 | 19 => { |
1431 | // PaintVarScaleAroundCenter |
1432 | let paint_offset = s.read::<Offset24>()?; |
1433 | |
1434 | let mut var_s = s.clone(); |
1435 | var_s.advance(8); |
1436 | let var_index_base = var_s.read::<u32>()?; |
1437 | |
1438 | let deltas = self |
1439 | .variation_data() |
1440 | .read_deltas::<4>(var_index_base, coords); |
1441 | |
1442 | let sx = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1443 | let sy = s.read::<F2DOT14>()?.apply_float_delta(deltas[1]); |
1444 | let center_x = f32::from(s.read::<i16>()?) + deltas[2]; |
1445 | let center_y = f32::from(s.read::<i16>()?) + deltas[3]; |
1446 | |
1447 | painter.push_translate(center_x, center_y); |
1448 | painter.push_scale(sx, sy); |
1449 | painter.push_translate(-center_x, -center_y); |
1450 | self.parse_paint( |
1451 | offset + paint_offset.to_usize(), |
1452 | palette, |
1453 | painter, |
1454 | recursion_stack, |
1455 | coords, |
1456 | foreground_color, |
1457 | ); |
1458 | painter.pop_transform(); |
1459 | painter.pop_transform(); |
1460 | painter.pop_transform(); |
1461 | } |
1462 | 20 => { |
1463 | // PaintScaleUniform |
1464 | let paint_offset = s.read::<Offset24>()?; |
1465 | let scale = s.read::<F2DOT14>()?.to_f32(); |
1466 | |
1467 | painter.push_scale(scale, scale); |
1468 | self.parse_paint( |
1469 | offset + paint_offset.to_usize(), |
1470 | palette, |
1471 | painter, |
1472 | recursion_stack, |
1473 | #[cfg (feature = "variable-fonts" )] |
1474 | coords, |
1475 | foreground_color, |
1476 | ); |
1477 | painter.pop_transform(); |
1478 | } |
1479 | #[cfg (feature = "variable-fonts" )] |
1480 | 21 => { |
1481 | // PaintVarScaleUniform |
1482 | let paint_offset = s.read::<Offset24>()?; |
1483 | |
1484 | let mut var_s = s.clone(); |
1485 | var_s.advance(2); |
1486 | let var_index_base = var_s.read::<u32>()?; |
1487 | |
1488 | let deltas = self |
1489 | .variation_data() |
1490 | .read_deltas::<1>(var_index_base, coords); |
1491 | |
1492 | let scale = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1493 | |
1494 | painter.push_scale(scale, scale); |
1495 | self.parse_paint( |
1496 | offset + paint_offset.to_usize(), |
1497 | palette, |
1498 | painter, |
1499 | recursion_stack, |
1500 | coords, |
1501 | foreground_color, |
1502 | ); |
1503 | painter.pop_transform(); |
1504 | } |
1505 | 22 => { |
1506 | // PaintScaleUniformAroundCenter |
1507 | let paint_offset = s.read::<Offset24>()?; |
1508 | let scale = s.read::<F2DOT14>()?.to_f32(); |
1509 | let center_x = f32::from(s.read::<i16>()?); |
1510 | let center_y = f32::from(s.read::<i16>()?); |
1511 | |
1512 | painter.push_translate(center_x, center_y); |
1513 | painter.push_scale(scale, scale); |
1514 | painter.push_translate(-center_x, -center_y); |
1515 | self.parse_paint( |
1516 | offset + paint_offset.to_usize(), |
1517 | palette, |
1518 | painter, |
1519 | recursion_stack, |
1520 | #[cfg (feature = "variable-fonts" )] |
1521 | coords, |
1522 | foreground_color, |
1523 | ); |
1524 | painter.pop_transform(); |
1525 | painter.pop_transform(); |
1526 | painter.pop_transform(); |
1527 | } |
1528 | #[cfg (feature = "variable-fonts" )] |
1529 | 23 => { |
1530 | // PaintVarScaleUniformAroundCenter |
1531 | let paint_offset = s.read::<Offset24>()?; |
1532 | |
1533 | let mut var_s = s.clone(); |
1534 | var_s.advance(6); |
1535 | let var_index_base = var_s.read::<u32>()?; |
1536 | |
1537 | let deltas = self |
1538 | .variation_data() |
1539 | .read_deltas::<3>(var_index_base, coords); |
1540 | |
1541 | let scale = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1542 | let center_x = f32::from(s.read::<i16>()?) + deltas[1]; |
1543 | let center_y = f32::from(s.read::<i16>()?) + deltas[2]; |
1544 | |
1545 | painter.push_translate(center_x, center_y); |
1546 | painter.push_scale(scale, scale); |
1547 | painter.push_translate(-center_x, -center_y); |
1548 | self.parse_paint( |
1549 | offset + paint_offset.to_usize(), |
1550 | palette, |
1551 | painter, |
1552 | recursion_stack, |
1553 | coords, |
1554 | foreground_color, |
1555 | ); |
1556 | painter.pop_transform(); |
1557 | painter.pop_transform(); |
1558 | painter.pop_transform(); |
1559 | } |
1560 | 24 => { |
1561 | // PaintRotate |
1562 | let paint_offset = s.read::<Offset24>()?; |
1563 | let angle = s.read::<F2DOT14>()?.to_f32(); |
1564 | |
1565 | painter.push_rotate(angle); |
1566 | self.parse_paint( |
1567 | offset + paint_offset.to_usize(), |
1568 | palette, |
1569 | painter, |
1570 | recursion_stack, |
1571 | #[cfg (feature = "variable-fonts" )] |
1572 | coords, |
1573 | foreground_color, |
1574 | ); |
1575 | painter.pop_transform(); |
1576 | } |
1577 | #[cfg (feature = "variable-fonts" )] |
1578 | 25 => { |
1579 | // PaintVarRotate |
1580 | let paint_offset = s.read::<Offset24>()?; |
1581 | |
1582 | let mut var_s = s.clone(); |
1583 | var_s.advance(2); |
1584 | let var_index_base = var_s.read::<u32>()?; |
1585 | |
1586 | let deltas = self |
1587 | .variation_data() |
1588 | .read_deltas::<1>(var_index_base, coords); |
1589 | |
1590 | let angle = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1591 | |
1592 | painter.push_rotate(angle); |
1593 | self.parse_paint( |
1594 | offset + paint_offset.to_usize(), |
1595 | palette, |
1596 | painter, |
1597 | recursion_stack, |
1598 | coords, |
1599 | foreground_color, |
1600 | ); |
1601 | painter.pop_transform(); |
1602 | } |
1603 | 26 => { |
1604 | // PaintRotateAroundCenter |
1605 | let paint_offset = s.read::<Offset24>()?; |
1606 | let angle = s.read::<F2DOT14>()?.to_f32(); |
1607 | let center_x = f32::from(s.read::<i16>()?); |
1608 | let center_y = f32::from(s.read::<i16>()?); |
1609 | |
1610 | painter.push_translate(center_x, center_y); |
1611 | painter.push_rotate(angle); |
1612 | painter.push_translate(-center_x, -center_y); |
1613 | self.parse_paint( |
1614 | offset + paint_offset.to_usize(), |
1615 | palette, |
1616 | painter, |
1617 | recursion_stack, |
1618 | #[cfg (feature = "variable-fonts" )] |
1619 | coords, |
1620 | foreground_color, |
1621 | ); |
1622 | painter.pop_transform(); |
1623 | painter.pop_transform(); |
1624 | painter.pop_transform(); |
1625 | } |
1626 | #[cfg (feature = "variable-fonts" )] |
1627 | 27 => { |
1628 | // PaintVarRotateAroundCenter |
1629 | let paint_offset = s.read::<Offset24>()?; |
1630 | |
1631 | let mut var_s = s.clone(); |
1632 | var_s.advance(6); |
1633 | let var_index_base = var_s.read::<u32>()?; |
1634 | |
1635 | let deltas = self |
1636 | .variation_data() |
1637 | .read_deltas::<3>(var_index_base, coords); |
1638 | |
1639 | let angle = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1640 | let center_x = f32::from(s.read::<i16>()?) + deltas[1]; |
1641 | let center_y = f32::from(s.read::<i16>()?) + deltas[2]; |
1642 | |
1643 | painter.push_translate(center_x, center_y); |
1644 | painter.push_rotate(angle); |
1645 | painter.push_translate(-center_x, -center_y); |
1646 | self.parse_paint( |
1647 | offset + paint_offset.to_usize(), |
1648 | palette, |
1649 | painter, |
1650 | recursion_stack, |
1651 | coords, |
1652 | foreground_color, |
1653 | ); |
1654 | painter.pop_transform(); |
1655 | painter.pop_transform(); |
1656 | painter.pop_transform(); |
1657 | } |
1658 | 28 => { |
1659 | // PaintSkew |
1660 | let paint_offset = s.read::<Offset24>()?; |
1661 | let skew_x = s.read::<F2DOT14>()?.to_f32(); |
1662 | let skew_y = s.read::<F2DOT14>()?.to_f32(); |
1663 | |
1664 | painter.push_skew(skew_x, skew_y); |
1665 | self.parse_paint( |
1666 | offset + paint_offset.to_usize(), |
1667 | palette, |
1668 | painter, |
1669 | recursion_stack, |
1670 | #[cfg (feature = "variable-fonts" )] |
1671 | coords, |
1672 | foreground_color, |
1673 | ); |
1674 | painter.pop_transform(); |
1675 | } |
1676 | #[cfg (feature = "variable-fonts" )] |
1677 | 29 => { |
1678 | // PaintVarSkew |
1679 | let paint_offset = s.read::<Offset24>()?; |
1680 | |
1681 | let mut var_s = s.clone(); |
1682 | var_s.advance(4); |
1683 | let var_index_base = var_s.read::<u32>()?; |
1684 | |
1685 | let deltas = self |
1686 | .variation_data() |
1687 | .read_deltas::<2>(var_index_base, coords); |
1688 | |
1689 | let skew_x = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1690 | let skew_y = s.read::<F2DOT14>()?.apply_float_delta(deltas[1]); |
1691 | |
1692 | painter.push_skew(skew_x, skew_y); |
1693 | self.parse_paint( |
1694 | offset + paint_offset.to_usize(), |
1695 | palette, |
1696 | painter, |
1697 | recursion_stack, |
1698 | coords, |
1699 | foreground_color, |
1700 | ); |
1701 | painter.pop_transform(); |
1702 | } |
1703 | 30 => { |
1704 | // PaintSkewAroundCenter |
1705 | let paint_offset = s.read::<Offset24>()?; |
1706 | let skew_x = s.read::<F2DOT14>()?.to_f32(); |
1707 | let skew_y = s.read::<F2DOT14>()?.to_f32(); |
1708 | let center_x = f32::from(s.read::<i16>()?); |
1709 | let center_y = f32::from(s.read::<i16>()?); |
1710 | |
1711 | painter.push_translate(center_x, center_y); |
1712 | painter.push_skew(skew_x, skew_y); |
1713 | painter.push_translate(-center_x, -center_y); |
1714 | self.parse_paint( |
1715 | offset + paint_offset.to_usize(), |
1716 | palette, |
1717 | painter, |
1718 | recursion_stack, |
1719 | #[cfg (feature = "variable-fonts" )] |
1720 | coords, |
1721 | foreground_color, |
1722 | ); |
1723 | painter.pop_transform(); |
1724 | painter.pop_transform(); |
1725 | painter.pop_transform(); |
1726 | } |
1727 | #[cfg (feature = "variable-fonts" )] |
1728 | 31 => { |
1729 | // PaintVarSkewAroundCenter |
1730 | let paint_offset = s.read::<Offset24>()?; |
1731 | |
1732 | let mut var_s = s.clone(); |
1733 | var_s.advance(8); |
1734 | let var_index_base = var_s.read::<u32>()?; |
1735 | |
1736 | let deltas = self |
1737 | .variation_data() |
1738 | .read_deltas::<4>(var_index_base, coords); |
1739 | |
1740 | let skew_x = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1741 | let skew_y = s.read::<F2DOT14>()?.apply_float_delta(deltas[1]); |
1742 | let center_x = f32::from(s.read::<i16>()?) + deltas[2]; |
1743 | let center_y = f32::from(s.read::<i16>()?) + deltas[3]; |
1744 | |
1745 | painter.push_translate(center_x, center_y); |
1746 | painter.push_skew(skew_x, skew_y); |
1747 | painter.push_translate(-center_x, -center_y); |
1748 | self.parse_paint( |
1749 | offset + paint_offset.to_usize(), |
1750 | palette, |
1751 | painter, |
1752 | recursion_stack, |
1753 | coords, |
1754 | foreground_color, |
1755 | ); |
1756 | painter.pop_transform(); |
1757 | painter.pop_transform(); |
1758 | painter.pop_transform(); |
1759 | } |
1760 | 32 => { |
1761 | // PaintComposite |
1762 | let source_paint_offset = s.read::<Offset24>()?; |
1763 | let composite_mode = s.read::<CompositeMode>()?; |
1764 | let backdrop_paint_offset = s.read::<Offset24>()?; |
1765 | |
1766 | painter.push_layer(CompositeMode::SourceOver); |
1767 | self.parse_paint( |
1768 | offset + backdrop_paint_offset.to_usize(), |
1769 | palette, |
1770 | painter, |
1771 | recursion_stack, |
1772 | #[cfg (feature = "variable-fonts" )] |
1773 | coords, |
1774 | foreground_color, |
1775 | ); |
1776 | painter.push_layer(composite_mode); |
1777 | self.parse_paint( |
1778 | offset + source_paint_offset.to_usize(), |
1779 | palette, |
1780 | painter, |
1781 | recursion_stack, |
1782 | #[cfg (feature = "variable-fonts" )] |
1783 | coords, |
1784 | foreground_color, |
1785 | ); |
1786 | painter.pop_layer(); |
1787 | painter.pop_layer(); |
1788 | } |
1789 | _ => {} |
1790 | } |
1791 | |
1792 | Some(()) |
1793 | } |
1794 | |
1795 | fn parse_color_line( |
1796 | &self, |
1797 | offset: usize, |
1798 | foreground_color: RgbaColor, |
1799 | ) -> Option<NonVarColorLine<'a>> { |
1800 | let mut s = Stream::new_at(self.data, offset)?; |
1801 | let extend = s.read::<GradientExtend>()?; |
1802 | let count = s.read::<u16>()?; |
1803 | let colors = s.read_array16::<ColorStopRaw>(count)?; |
1804 | Some(NonVarColorLine { |
1805 | extend, |
1806 | colors, |
1807 | foreground_color, |
1808 | palettes: self.palettes, |
1809 | }) |
1810 | } |
1811 | |
1812 | #[cfg (feature = "variable-fonts" )] |
1813 | fn parse_var_color_line( |
1814 | &self, |
1815 | offset: usize, |
1816 | foreground_color: RgbaColor, |
1817 | ) -> Option<VarColorLine<'a>> { |
1818 | let mut s = Stream::new_at(self.data, offset)?; |
1819 | let extend = s.read::<GradientExtend>()?; |
1820 | let count = s.read::<u16>()?; |
1821 | let colors = s.read_array16::<VarColorStopRaw>(count)?; |
1822 | Some(VarColorLine { |
1823 | extend, |
1824 | colors, |
1825 | foreground_color, |
1826 | palettes: self.palettes, |
1827 | }) |
1828 | } |
1829 | } |
1830 | |
1831 | struct RecursionStack { |
1832 | // The limit of 64 is chosen arbitrarily and not from the spec. But we have to stop somewhere... |
1833 | stack: [usize; 64], |
1834 | len: usize, |
1835 | } |
1836 | |
1837 | impl RecursionStack { |
1838 | #[inline ] |
1839 | pub fn is_empty(&self) -> bool { |
1840 | self.len == 0 |
1841 | } |
1842 | |
1843 | #[inline ] |
1844 | pub fn push(&mut self, offset: usize) -> Result<(), ()> { |
1845 | if self.len == self.stack.len() { |
1846 | Err(()) |
1847 | } else { |
1848 | self.stack[self.len] = offset; |
1849 | self.len += 1; |
1850 | Ok(()) |
1851 | } |
1852 | } |
1853 | |
1854 | #[inline ] |
1855 | pub fn contains(&self, offset: usize) -> bool { |
1856 | if let Some(offsets) = self.stack.get(..self.len) { |
1857 | return offsets.contains(&offset); |
1858 | } |
1859 | |
1860 | false |
1861 | } |
1862 | |
1863 | #[inline ] |
1864 | pub fn pop(&mut self) { |
1865 | debug_assert!(!self.is_empty()); |
1866 | self.len -= 1; |
1867 | } |
1868 | } |
1869 | |
1870 | #[cfg (feature = "variable-fonts" )] |
1871 | #[derive (Clone, Copy, Debug, Default)] |
1872 | struct VariationData<'a> { |
1873 | variation_store: Option<ItemVariationStore<'a>>, |
1874 | delta_map: Option<DeltaSetIndexMap<'a>>, |
1875 | } |
1876 | |
1877 | #[cfg (feature = "variable-fonts" )] |
1878 | impl VariationData<'_> { |
1879 | // Inspired from `fontations`. |
1880 | fn read_deltas<const N: usize>( |
1881 | &self, |
1882 | var_index_base: u32, |
1883 | coordinates: &[NormalizedCoordinate], |
1884 | ) -> [f32; N] { |
1885 | const NO_VARIATION_DELTAS: u32 = 0xFFFFFFFF; |
1886 | let mut deltas = [0.0; N]; |
1887 | |
1888 | if coordinates.is_empty() |
1889 | || self.variation_store.is_none() |
1890 | || var_index_base == NO_VARIATION_DELTAS |
1891 | { |
1892 | return deltas; |
1893 | } |
1894 | |
1895 | let variation_store = self.variation_store.as_ref().unwrap(); |
1896 | |
1897 | for i in 0..N { |
1898 | deltas[i] = self |
1899 | .delta_map |
1900 | .and_then(|d| d.map(var_index_base + i as u32)) |
1901 | .and_then(|d| variation_store.parse_delta(d.0, d.1, coordinates)) |
1902 | .unwrap_or(0.0); |
1903 | } |
1904 | |
1905 | deltas |
1906 | } |
1907 | } |
1908 | |