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<'_> = 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: VarColorStopRaw = self.colors.get(index)?; |
303 | |
304 | let mut color: RgbaColor = if info.palette_index == u16::MAX { |
305 | self.foreground_color |
306 | } else { |
307 | self.palettes.get(palette, palette_entry:info.palette_index)? |
308 | }; |
309 | |
310 | let deltas: [f32; 2] = variation_data.read_deltas::<2>(info.var_index_base, coordinates); |
311 | let stop_offset: f32 = 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 | /// 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 | pub fn parse(palettes: cpal::Table<'a>, data: &'a [u8]) -> Option<Self> { |
731 | let mut s = Stream::new(data); |
732 | |
733 | let version = s.read::<u16>()?; |
734 | if version > 1 { |
735 | return None; |
736 | } |
737 | |
738 | let num_base_glyphs = s.read::<u16>()?; |
739 | let base_glyphs_offset = s.read::<Offset32>()?; |
740 | let layers_offset = s.read::<Offset32>()?; |
741 | let num_layers = s.read::<u16>()?; |
742 | |
743 | let base_glyphs = Stream::new_at(data, base_glyphs_offset.to_usize())? |
744 | .read_array16::<BaseGlyphRecord>(num_base_glyphs)?; |
745 | |
746 | let layers = Stream::new_at(data, layers_offset.to_usize())? |
747 | .read_array16::<LayerRecord>(num_layers)?; |
748 | |
749 | let mut table = Self { |
750 | version: version as u8, |
751 | data, |
752 | palettes, |
753 | base_glyphs, |
754 | layers, |
755 | base_glyph_paints_offset: Offset32(0), // the actual value doesn't matter |
756 | base_glyph_paints: LazyArray32::default(), |
757 | layer_paint_offsets_offset: Offset32(0), |
758 | layer_paint_offsets: LazyArray32::default(), |
759 | clip_list_offsets_offset: Offset32(0), |
760 | clip_list: ClipList::default(), |
761 | #[cfg (feature = "variable-fonts" )] |
762 | item_variation_store: None, |
763 | #[cfg (feature = "variable-fonts" )] |
764 | var_index_map: None, |
765 | }; |
766 | |
767 | if version == 0 { |
768 | return Some(table); |
769 | } |
770 | |
771 | table.base_glyph_paints_offset = s.read::<Offset32>()?; |
772 | let layer_list_offset = s.read::<Option<Offset32>>()?; |
773 | let clip_list_offset = s.read::<Option<Offset32>>()?; |
774 | #[cfg (feature = "variable-fonts" )] |
775 | let var_index_map_offset = s.read::<Option<Offset32>>()?; |
776 | #[cfg (feature = "variable-fonts" )] |
777 | let item_variation_offset = s.read::<Option<Offset32>>()?; |
778 | |
779 | { |
780 | let mut s = Stream::new_at(data, table.base_glyph_paints_offset.to_usize())?; |
781 | let count = s.read::<u32>()?; |
782 | table.base_glyph_paints = s.read_array32::<BaseGlyphPaintRecord>(count)?; |
783 | } |
784 | |
785 | if let Some(offset) = layer_list_offset { |
786 | table.layer_paint_offsets_offset = offset; |
787 | let mut s = Stream::new_at(data, offset.to_usize())?; |
788 | let count = s.read::<u32>()?; |
789 | table.layer_paint_offsets = s.read_array32::<Offset32>(count)?; |
790 | } |
791 | |
792 | if let Some(offset) = clip_list_offset { |
793 | table.clip_list_offsets_offset = offset; |
794 | let clip_data = data.get(offset.to_usize()..)?; |
795 | let mut s = Stream::new(clip_data); |
796 | s.skip::<u8>(); // Format |
797 | let count = s.read::<u32>()?; |
798 | table.clip_list = ClipList { |
799 | data: clip_data, |
800 | records: s.read_array32::<ClipRecord>(count)?, |
801 | }; |
802 | } |
803 | |
804 | #[cfg (feature = "variable-fonts" )] |
805 | { |
806 | if let Some(offset) = item_variation_offset { |
807 | let item_var_data = data.get(offset.to_usize()..)?; |
808 | let s = Stream::new(item_var_data); |
809 | let var_store = ItemVariationStore::parse(s)?; |
810 | table.item_variation_store = Some(var_store); |
811 | } |
812 | } |
813 | |
814 | #[cfg (feature = "variable-fonts" )] |
815 | { |
816 | if let Some(offset) = var_index_map_offset { |
817 | let var_index_map_data = data.get(offset.to_usize()..)?; |
818 | let var_index_map = DeltaSetIndexMap::new(var_index_map_data); |
819 | table.var_index_map = Some(var_index_map); |
820 | } |
821 | } |
822 | |
823 | Some(table) |
824 | } |
825 | |
826 | /// Returns `true` if the current table has version 0. |
827 | /// |
828 | /// A simple table can only emit `outline_glyph` and `paint` |
829 | /// [`Painter`] methods. |
830 | pub fn is_simple(&self) -> bool { |
831 | self.version == 0 |
832 | } |
833 | |
834 | fn get_v0(&self, glyph_id: GlyphId) -> Option<BaseGlyphRecord> { |
835 | self.base_glyphs |
836 | .binary_search_by(|base| base.glyph_id.cmp(&glyph_id)) |
837 | .map(|v| v.1) |
838 | } |
839 | |
840 | fn get_v1(&self, glyph_id: GlyphId) -> Option<BaseGlyphPaintRecord> { |
841 | self.base_glyph_paints |
842 | .binary_search_by(|base| base.glyph_id.cmp(&glyph_id)) |
843 | .map(|v| v.1) |
844 | } |
845 | |
846 | #[cfg (feature = "variable-fonts" )] |
847 | fn variation_data(&self) -> VariationData<'a> { |
848 | VariationData { |
849 | variation_store: self.item_variation_store, |
850 | delta_map: self.var_index_map, |
851 | } |
852 | } |
853 | |
854 | /// Whether the table contains a definition for the given glyph. |
855 | pub fn contains(&self, glyph_id: GlyphId) -> bool { |
856 | self.get_v1(glyph_id).is_some() || self.get_v0(glyph_id).is_some() |
857 | } |
858 | |
859 | /// Returns the clip box for a glyph. |
860 | pub fn clip_box( |
861 | &self, |
862 | glyph_id: GlyphId, |
863 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
864 | ) -> Option<ClipBox> { |
865 | self.clip_list.find( |
866 | glyph_id, |
867 | #[cfg (feature = "variable-fonts" )] |
868 | &self.variation_data(), |
869 | #[cfg (feature = "variable-fonts" )] |
870 | coords, |
871 | ) |
872 | } |
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 | pub fn paint( |
879 | &self, |
880 | glyph_id: GlyphId, |
881 | palette: u16, |
882 | painter: &mut dyn Painter<'a>, |
883 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
884 | foreground_color: RgbaColor, |
885 | ) -> Option<()> { |
886 | let mut recursion_stack = RecursionStack { |
887 | stack: [0; 64], |
888 | len: 0, |
889 | }; |
890 | |
891 | self.paint_impl( |
892 | glyph_id, |
893 | palette, |
894 | painter, |
895 | &mut recursion_stack, |
896 | #[cfg (feature = "variable-fonts" )] |
897 | coords, |
898 | foreground_color, |
899 | ) |
900 | } |
901 | |
902 | fn paint_impl( |
903 | &self, |
904 | glyph_id: GlyphId, |
905 | palette: u16, |
906 | painter: &mut dyn Painter<'a>, |
907 | recursion_stack: &mut RecursionStack, |
908 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
909 | foreground_color: RgbaColor, |
910 | ) -> Option<()> { |
911 | if let Some(base) = self.get_v1(glyph_id) { |
912 | self.paint_v1( |
913 | base, |
914 | palette, |
915 | painter, |
916 | recursion_stack, |
917 | #[cfg (feature = "variable-fonts" )] |
918 | coords, |
919 | foreground_color, |
920 | ) |
921 | } else if let Some(base) = self.get_v0(glyph_id) { |
922 | self.paint_v0(base, palette, painter, foreground_color) |
923 | } else { |
924 | None |
925 | } |
926 | } |
927 | |
928 | fn paint_v0( |
929 | &self, |
930 | base: BaseGlyphRecord, |
931 | palette: u16, |
932 | painter: &mut dyn Painter, |
933 | foreground_color: RgbaColor, |
934 | ) -> Option<()> { |
935 | let start = base.first_layer_index; |
936 | let end = start.checked_add(base.num_layers)?; |
937 | let layers = self.layers.slice(start..end)?; |
938 | |
939 | for layer in layers { |
940 | if layer.palette_index == 0xFFFF { |
941 | // A special case. |
942 | painter.outline_glyph(layer.glyph_id); |
943 | painter.paint(Paint::Solid(foreground_color)); |
944 | } else { |
945 | let color = self.palettes.get(palette, layer.palette_index)?; |
946 | painter.outline_glyph(layer.glyph_id); |
947 | painter.paint(Paint::Solid(color)); |
948 | } |
949 | } |
950 | |
951 | Some(()) |
952 | } |
953 | |
954 | fn paint_v1( |
955 | &self, |
956 | base: BaseGlyphPaintRecord, |
957 | palette: u16, |
958 | painter: &mut dyn Painter<'a>, |
959 | recursion_stack: &mut RecursionStack, |
960 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
961 | foreground_color: RgbaColor, |
962 | ) -> Option<()> { |
963 | let clip_box = self.clip_box( |
964 | base.glyph_id, |
965 | #[cfg (feature = "variable-fonts" )] |
966 | coords, |
967 | ); |
968 | if let Some(clip_box) = clip_box { |
969 | painter.push_clip_box(clip_box); |
970 | } |
971 | |
972 | self.parse_paint( |
973 | self.base_glyph_paints_offset.to_usize() + base.paint_table_offset.to_usize(), |
974 | palette, |
975 | painter, |
976 | recursion_stack, |
977 | #[cfg (feature = "variable-fonts" )] |
978 | coords, |
979 | foreground_color, |
980 | ); |
981 | |
982 | if clip_box.is_some() { |
983 | painter.pop_clip(); |
984 | } |
985 | |
986 | Some(()) |
987 | } |
988 | |
989 | fn parse_paint( |
990 | &self, |
991 | offset: usize, |
992 | palette: u16, |
993 | painter: &mut dyn Painter<'a>, |
994 | recursion_stack: &mut RecursionStack, |
995 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
996 | foreground_color: RgbaColor, |
997 | ) -> Option<()> { |
998 | let mut s = Stream::new_at(self.data, offset)?; |
999 | let format = s.read::<u8>()?; |
1000 | |
1001 | // Cycle detected |
1002 | if recursion_stack.contains(offset) { |
1003 | return None; |
1004 | } |
1005 | |
1006 | recursion_stack.push(offset).ok()?; |
1007 | let result = self.parse_paint_impl( |
1008 | offset, |
1009 | palette, |
1010 | painter, |
1011 | recursion_stack, |
1012 | &mut s, |
1013 | format, |
1014 | #[cfg (feature = "variable-fonts" )] |
1015 | coords, |
1016 | foreground_color, |
1017 | ); |
1018 | recursion_stack.pop(); |
1019 | |
1020 | result |
1021 | } |
1022 | |
1023 | fn parse_paint_impl( |
1024 | &self, |
1025 | offset: usize, |
1026 | palette: u16, |
1027 | painter: &mut dyn Painter<'a>, |
1028 | recursion_stack: &mut RecursionStack, |
1029 | s: &mut Stream, |
1030 | format: u8, |
1031 | #[cfg (feature = "variable-fonts" )] coords: &[NormalizedCoordinate], |
1032 | foreground_color: RgbaColor, |
1033 | ) -> Option<()> { |
1034 | match format { |
1035 | 1 => { |
1036 | // PaintColrLayers |
1037 | let layers_count = s.read::<u8>()?; |
1038 | let first_layer_index = s.read::<u32>()?; |
1039 | |
1040 | for i in 0..layers_count { |
1041 | let index = first_layer_index.checked_add(u32::from(i))?; |
1042 | let paint_offset = self.layer_paint_offsets.get(index)?; |
1043 | self.parse_paint( |
1044 | self.layer_paint_offsets_offset.to_usize() + paint_offset.to_usize(), |
1045 | palette, |
1046 | painter, |
1047 | recursion_stack, |
1048 | #[cfg (feature = "variable-fonts" )] |
1049 | coords, |
1050 | foreground_color, |
1051 | ); |
1052 | } |
1053 | } |
1054 | 2 => { |
1055 | // PaintSolid |
1056 | let palette_index = s.read::<u16>()?; |
1057 | let alpha = s.read::<F2DOT14>()?; |
1058 | |
1059 | let mut color = if palette_index == u16::MAX { |
1060 | foreground_color |
1061 | } else { |
1062 | self.palettes.get(palette, palette_index)? |
1063 | }; |
1064 | |
1065 | color.apply_alpha(alpha.to_f32()); |
1066 | painter.paint(Paint::Solid(color)); |
1067 | } |
1068 | #[cfg (feature = "variable-fonts" )] |
1069 | 3 => { |
1070 | // PaintVarSolid |
1071 | let palette_index = s.read::<u16>()?; |
1072 | let alpha = s.read::<F2DOT14>()?; |
1073 | let var_index_base = s.read::<u32>()?; |
1074 | |
1075 | let deltas = self |
1076 | .variation_data() |
1077 | .read_deltas::<1>(var_index_base, coords); |
1078 | |
1079 | let mut color = if palette_index == u16::MAX { |
1080 | foreground_color |
1081 | } else { |
1082 | self.palettes.get(palette, palette_index)? |
1083 | }; |
1084 | |
1085 | color.apply_alpha(alpha.apply_float_delta(deltas[0])); |
1086 | painter.paint(Paint::Solid(color)); |
1087 | } |
1088 | 4 => { |
1089 | // PaintLinearGradient |
1090 | let color_line_offset = s.read::<Offset24>()?; |
1091 | let color_line = |
1092 | self.parse_color_line(offset + color_line_offset.to_usize(), foreground_color)?; |
1093 | |
1094 | painter.paint(Paint::LinearGradient(LinearGradient { |
1095 | x0: s.read::<i16>()? as f32, |
1096 | y0: s.read::<i16>()? as f32, |
1097 | x1: s.read::<i16>()? as f32, |
1098 | y1: s.read::<i16>()? as f32, |
1099 | x2: s.read::<i16>()? as f32, |
1100 | y2: s.read::<i16>()? as f32, |
1101 | extend: color_line.extend, |
1102 | #[cfg (feature = "variable-fonts" )] |
1103 | variation_data: self.variation_data(), |
1104 | color_line: ColorLine::NonVarColorLine(color_line), |
1105 | })) |
1106 | } |
1107 | #[cfg (feature = "variable-fonts" )] |
1108 | 5 => { |
1109 | // PaintVarLinearGradient |
1110 | let var_color_line_offset = s.read::<Offset24>()?; |
1111 | let color_line = self.parse_var_color_line( |
1112 | offset + var_color_line_offset.to_usize(), |
1113 | foreground_color, |
1114 | )?; |
1115 | let mut var_s = s.clone(); |
1116 | var_s.advance(12); |
1117 | let var_index_base = var_s.read::<u32>()?; |
1118 | |
1119 | let deltas = self |
1120 | .variation_data() |
1121 | .read_deltas::<6>(var_index_base, coords); |
1122 | |
1123 | painter.paint(Paint::LinearGradient(LinearGradient { |
1124 | x0: s.read::<i16>()? as f32 + deltas[0], |
1125 | y0: s.read::<i16>()? as f32 + deltas[1], |
1126 | x1: s.read::<i16>()? as f32 + deltas[2], |
1127 | y1: s.read::<i16>()? as f32 + deltas[3], |
1128 | x2: s.read::<i16>()? as f32 + deltas[4], |
1129 | y2: s.read::<i16>()? as f32 + deltas[5], |
1130 | extend: color_line.extend, |
1131 | variation_data: self.variation_data(), |
1132 | color_line: ColorLine::VarColorLine(color_line), |
1133 | })) |
1134 | } |
1135 | 6 => { |
1136 | // PaintRadialGradient |
1137 | let color_line_offset = s.read::<Offset24>()?; |
1138 | let color_line = |
1139 | self.parse_color_line(offset + color_line_offset.to_usize(), foreground_color)?; |
1140 | painter.paint(Paint::RadialGradient(RadialGradient { |
1141 | x0: s.read::<i16>()? as f32, |
1142 | y0: s.read::<i16>()? as f32, |
1143 | r0: s.read::<u16>()? as f32, |
1144 | x1: s.read::<i16>()? as f32, |
1145 | y1: s.read::<i16>()? as f32, |
1146 | r1: s.read::<u16>()? as f32, |
1147 | extend: color_line.extend, |
1148 | #[cfg (feature = "variable-fonts" )] |
1149 | variation_data: self.variation_data(), |
1150 | color_line: ColorLine::NonVarColorLine(color_line), |
1151 | })) |
1152 | } |
1153 | #[cfg (feature = "variable-fonts" )] |
1154 | 7 => { |
1155 | // PaintVarRadialGradient |
1156 | let color_line_offset = s.read::<Offset24>()?; |
1157 | let color_line = self.parse_var_color_line( |
1158 | offset + color_line_offset.to_usize(), |
1159 | foreground_color, |
1160 | )?; |
1161 | |
1162 | let mut var_s = s.clone(); |
1163 | var_s.advance(12); |
1164 | let var_index_base = var_s.read::<u32>()?; |
1165 | |
1166 | let deltas = self |
1167 | .variation_data() |
1168 | .read_deltas::<6>(var_index_base, coords); |
1169 | |
1170 | painter.paint(Paint::RadialGradient(RadialGradient { |
1171 | x0: s.read::<i16>()? as f32 + deltas[0], |
1172 | y0: s.read::<i16>()? as f32 + deltas[1], |
1173 | r0: s.read::<u16>()? as f32 + deltas[2], |
1174 | x1: s.read::<i16>()? as f32 + deltas[3], |
1175 | y1: s.read::<i16>()? as f32 + deltas[4], |
1176 | r1: s.read::<u16>()? as f32 + deltas[5], |
1177 | extend: color_line.extend, |
1178 | variation_data: self.variation_data(), |
1179 | color_line: ColorLine::VarColorLine(color_line), |
1180 | })) |
1181 | } |
1182 | 8 => { |
1183 | // PaintSweepGradient |
1184 | let color_line_offset = s.read::<Offset24>()?; |
1185 | let color_line = |
1186 | self.parse_color_line(offset + color_line_offset.to_usize(), foreground_color)?; |
1187 | painter.paint(Paint::SweepGradient(SweepGradient { |
1188 | center_x: s.read::<i16>()? as f32, |
1189 | center_y: s.read::<i16>()? as f32, |
1190 | start_angle: s.read::<F2DOT14>()?.to_f32(), |
1191 | end_angle: s.read::<F2DOT14>()?.to_f32(), |
1192 | extend: color_line.extend, |
1193 | color_line: ColorLine::NonVarColorLine(color_line), |
1194 | #[cfg (feature = "variable-fonts" )] |
1195 | variation_data: self.variation_data(), |
1196 | })) |
1197 | } |
1198 | #[cfg (feature = "variable-fonts" )] |
1199 | 9 => { |
1200 | // PaintVarSweepGradient |
1201 | let color_line_offset = s.read::<Offset24>()?; |
1202 | let color_line = self.parse_var_color_line( |
1203 | offset + color_line_offset.to_usize(), |
1204 | foreground_color, |
1205 | )?; |
1206 | |
1207 | let mut var_s = s.clone(); |
1208 | var_s.advance(8); |
1209 | let var_index_base = var_s.read::<u32>()?; |
1210 | |
1211 | let deltas = self |
1212 | .variation_data() |
1213 | .read_deltas::<4>(var_index_base, coords); |
1214 | |
1215 | painter.paint(Paint::SweepGradient(SweepGradient { |
1216 | center_x: s.read::<i16>()? as f32 + deltas[0], |
1217 | center_y: s.read::<i16>()? as f32 + deltas[1], |
1218 | start_angle: s.read::<F2DOT14>()?.apply_float_delta(deltas[2]), |
1219 | end_angle: s.read::<F2DOT14>()?.apply_float_delta(deltas[3]), |
1220 | extend: color_line.extend, |
1221 | color_line: ColorLine::VarColorLine(color_line), |
1222 | variation_data: self.variation_data(), |
1223 | })) |
1224 | } |
1225 | 10 => { |
1226 | // PaintGlyph |
1227 | let paint_offset = s.read::<Offset24>()?; |
1228 | let glyph_id = s.read::<GlyphId>()?; |
1229 | painter.outline_glyph(glyph_id); |
1230 | painter.push_clip(); |
1231 | |
1232 | self.parse_paint( |
1233 | offset + paint_offset.to_usize(), |
1234 | palette, |
1235 | painter, |
1236 | recursion_stack, |
1237 | #[cfg (feature = "variable-fonts" )] |
1238 | coords, |
1239 | foreground_color, |
1240 | ); |
1241 | |
1242 | painter.pop_clip(); |
1243 | } |
1244 | 11 => { |
1245 | // PaintColrGlyph |
1246 | let glyph_id = s.read::<GlyphId>()?; |
1247 | self.paint_impl( |
1248 | glyph_id, |
1249 | palette, |
1250 | painter, |
1251 | recursion_stack, |
1252 | #[cfg (feature = "variable-fonts" )] |
1253 | coords, |
1254 | foreground_color, |
1255 | ); |
1256 | } |
1257 | 12 => { |
1258 | // PaintTransform |
1259 | let paint_offset = s.read::<Offset24>()?; |
1260 | let ts_offset = s.read::<Offset24>()?; |
1261 | let mut s = Stream::new_at(self.data, offset + ts_offset.to_usize())?; |
1262 | let ts = Transform { |
1263 | a: s.read::<Fixed>().map(|n| n.0)?, |
1264 | b: s.read::<Fixed>().map(|n| n.0)?, |
1265 | c: s.read::<Fixed>().map(|n| n.0)?, |
1266 | d: s.read::<Fixed>().map(|n| n.0)?, |
1267 | e: s.read::<Fixed>().map(|n| n.0)?, |
1268 | f: s.read::<Fixed>().map(|n| n.0)?, |
1269 | }; |
1270 | |
1271 | painter.push_transform(ts); |
1272 | self.parse_paint( |
1273 | offset + paint_offset.to_usize(), |
1274 | palette, |
1275 | painter, |
1276 | recursion_stack, |
1277 | #[cfg (feature = "variable-fonts" )] |
1278 | coords, |
1279 | foreground_color, |
1280 | ); |
1281 | painter.pop_transform(); |
1282 | } |
1283 | #[cfg (feature = "variable-fonts" )] |
1284 | 13 => { |
1285 | // PaintVarTransform |
1286 | let paint_offset = s.read::<Offset24>()?; |
1287 | let ts_offset = s.read::<Offset24>()?; |
1288 | let mut s = Stream::new_at(self.data, offset + ts_offset.to_usize())?; |
1289 | |
1290 | let mut var_s = s.clone(); |
1291 | var_s.advance(24); |
1292 | let var_index_base = var_s.read::<u32>()?; |
1293 | |
1294 | let deltas = self |
1295 | .variation_data() |
1296 | .read_deltas::<6>(var_index_base, coords); |
1297 | |
1298 | let ts = Transform { |
1299 | a: s.read::<Fixed>()?.apply_float_delta(deltas[0]), |
1300 | b: s.read::<Fixed>()?.apply_float_delta(deltas[1]), |
1301 | c: s.read::<Fixed>()?.apply_float_delta(deltas[2]), |
1302 | d: s.read::<Fixed>()?.apply_float_delta(deltas[3]), |
1303 | e: s.read::<Fixed>()?.apply_float_delta(deltas[4]), |
1304 | f: s.read::<Fixed>()?.apply_float_delta(deltas[5]), |
1305 | }; |
1306 | |
1307 | painter.push_transform(ts); |
1308 | self.parse_paint( |
1309 | offset + paint_offset.to_usize(), |
1310 | palette, |
1311 | painter, |
1312 | recursion_stack, |
1313 | coords, |
1314 | foreground_color, |
1315 | ); |
1316 | painter.pop_transform(); |
1317 | } |
1318 | 14 => { |
1319 | // PaintTranslate |
1320 | let paint_offset = s.read::<Offset24>()?; |
1321 | let tx = f32::from(s.read::<i16>()?); |
1322 | let ty = f32::from(s.read::<i16>()?); |
1323 | |
1324 | painter.push_transform(Transform::new_translate(tx, ty)); |
1325 | self.parse_paint( |
1326 | offset + paint_offset.to_usize(), |
1327 | palette, |
1328 | painter, |
1329 | recursion_stack, |
1330 | #[cfg (feature = "variable-fonts" )] |
1331 | coords, |
1332 | foreground_color, |
1333 | ); |
1334 | painter.pop_transform(); |
1335 | } |
1336 | #[cfg (feature = "variable-fonts" )] |
1337 | 15 => { |
1338 | // PaintVarTranslate |
1339 | let paint_offset = s.read::<Offset24>()?; |
1340 | |
1341 | let mut var_s = s.clone(); |
1342 | var_s.advance(4); |
1343 | let var_index_base = var_s.read::<u32>()?; |
1344 | |
1345 | let deltas = self |
1346 | .variation_data() |
1347 | .read_deltas::<2>(var_index_base, coords); |
1348 | |
1349 | let tx = f32::from(s.read::<i16>()?) + deltas[0]; |
1350 | let ty = f32::from(s.read::<i16>()?) + deltas[1]; |
1351 | |
1352 | painter.push_transform(Transform::new_translate(tx, ty)); |
1353 | self.parse_paint( |
1354 | offset + paint_offset.to_usize(), |
1355 | palette, |
1356 | painter, |
1357 | recursion_stack, |
1358 | coords, |
1359 | foreground_color, |
1360 | ); |
1361 | painter.pop_transform(); |
1362 | } |
1363 | 16 => { |
1364 | // PaintScale |
1365 | let paint_offset = s.read::<Offset24>()?; |
1366 | let sx = s.read::<F2DOT14>()?.to_f32(); |
1367 | let sy = s.read::<F2DOT14>()?.to_f32(); |
1368 | |
1369 | painter.push_transform(Transform::new_scale(sx, sy)); |
1370 | self.parse_paint( |
1371 | offset + paint_offset.to_usize(), |
1372 | palette, |
1373 | painter, |
1374 | recursion_stack, |
1375 | #[cfg (feature = "variable-fonts" )] |
1376 | coords, |
1377 | foreground_color, |
1378 | ); |
1379 | painter.pop_transform(); |
1380 | } |
1381 | #[cfg (feature = "variable-fonts" )] |
1382 | 17 => { |
1383 | // PaintVarScale |
1384 | let paint_offset = s.read::<Offset24>()?; |
1385 | |
1386 | let mut var_s = s.clone(); |
1387 | var_s.advance(4); |
1388 | let var_index_base = var_s.read::<u32>()?; |
1389 | |
1390 | let deltas = self |
1391 | .variation_data() |
1392 | .read_deltas::<2>(var_index_base, coords); |
1393 | |
1394 | let sx = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1395 | let sy = s.read::<F2DOT14>()?.apply_float_delta(deltas[1]); |
1396 | |
1397 | painter.push_transform(Transform::new_scale(sx, sy)); |
1398 | self.parse_paint( |
1399 | offset + paint_offset.to_usize(), |
1400 | palette, |
1401 | painter, |
1402 | recursion_stack, |
1403 | coords, |
1404 | foreground_color, |
1405 | ); |
1406 | painter.pop_transform(); |
1407 | } |
1408 | 18 => { |
1409 | // PaintScaleAroundCenter |
1410 | let paint_offset = s.read::<Offset24>()?; |
1411 | let sx = s.read::<F2DOT14>()?.to_f32(); |
1412 | let sy = s.read::<F2DOT14>()?.to_f32(); |
1413 | let center_x = f32::from(s.read::<i16>()?); |
1414 | let center_y = f32::from(s.read::<i16>()?); |
1415 | |
1416 | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1417 | painter.push_transform(Transform::new_scale(sx, sy)); |
1418 | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1419 | self.parse_paint( |
1420 | offset + paint_offset.to_usize(), |
1421 | palette, |
1422 | painter, |
1423 | recursion_stack, |
1424 | #[cfg (feature = "variable-fonts" )] |
1425 | coords, |
1426 | foreground_color, |
1427 | ); |
1428 | painter.pop_transform(); |
1429 | painter.pop_transform(); |
1430 | painter.pop_transform(); |
1431 | } |
1432 | #[cfg (feature = "variable-fonts" )] |
1433 | 19 => { |
1434 | // PaintVarScaleAroundCenter |
1435 | let paint_offset = s.read::<Offset24>()?; |
1436 | |
1437 | let mut var_s = s.clone(); |
1438 | var_s.advance(8); |
1439 | let var_index_base = var_s.read::<u32>()?; |
1440 | |
1441 | let deltas = self |
1442 | .variation_data() |
1443 | .read_deltas::<4>(var_index_base, coords); |
1444 | |
1445 | let sx = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1446 | let sy = s.read::<F2DOT14>()?.apply_float_delta(deltas[1]); |
1447 | let center_x = f32::from(s.read::<i16>()?) + deltas[2]; |
1448 | let center_y = f32::from(s.read::<i16>()?) + deltas[3]; |
1449 | |
1450 | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1451 | painter.push_transform(Transform::new_scale(sx, sy)); |
1452 | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1453 | self.parse_paint( |
1454 | offset + paint_offset.to_usize(), |
1455 | palette, |
1456 | painter, |
1457 | recursion_stack, |
1458 | coords, |
1459 | foreground_color, |
1460 | ); |
1461 | painter.pop_transform(); |
1462 | painter.pop_transform(); |
1463 | painter.pop_transform(); |
1464 | } |
1465 | 20 => { |
1466 | // PaintScaleUniform |
1467 | let paint_offset = s.read::<Offset24>()?; |
1468 | let scale = s.read::<F2DOT14>()?.to_f32(); |
1469 | |
1470 | painter.push_transform(Transform::new_scale(scale, scale)); |
1471 | self.parse_paint( |
1472 | offset + paint_offset.to_usize(), |
1473 | palette, |
1474 | painter, |
1475 | recursion_stack, |
1476 | #[cfg (feature = "variable-fonts" )] |
1477 | coords, |
1478 | foreground_color, |
1479 | ); |
1480 | painter.pop_transform(); |
1481 | } |
1482 | #[cfg (feature = "variable-fonts" )] |
1483 | 21 => { |
1484 | // PaintVarScaleUniform |
1485 | let paint_offset = s.read::<Offset24>()?; |
1486 | |
1487 | let mut var_s = s.clone(); |
1488 | var_s.advance(2); |
1489 | let var_index_base = var_s.read::<u32>()?; |
1490 | |
1491 | let deltas = self |
1492 | .variation_data() |
1493 | .read_deltas::<1>(var_index_base, coords); |
1494 | |
1495 | let scale = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1496 | |
1497 | painter.push_transform(Transform::new_scale(scale, scale)); |
1498 | self.parse_paint( |
1499 | offset + paint_offset.to_usize(), |
1500 | palette, |
1501 | painter, |
1502 | recursion_stack, |
1503 | coords, |
1504 | foreground_color, |
1505 | ); |
1506 | painter.pop_transform(); |
1507 | } |
1508 | 22 => { |
1509 | // PaintScaleUniformAroundCenter |
1510 | let paint_offset = s.read::<Offset24>()?; |
1511 | let scale = s.read::<F2DOT14>()?.to_f32(); |
1512 | let center_x = f32::from(s.read::<i16>()?); |
1513 | let center_y = f32::from(s.read::<i16>()?); |
1514 | |
1515 | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1516 | painter.push_transform(Transform::new_scale(scale, scale)); |
1517 | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1518 | self.parse_paint( |
1519 | offset + paint_offset.to_usize(), |
1520 | palette, |
1521 | painter, |
1522 | recursion_stack, |
1523 | #[cfg (feature = "variable-fonts" )] |
1524 | coords, |
1525 | foreground_color, |
1526 | ); |
1527 | painter.pop_transform(); |
1528 | painter.pop_transform(); |
1529 | painter.pop_transform(); |
1530 | } |
1531 | #[cfg (feature = "variable-fonts" )] |
1532 | 23 => { |
1533 | // PaintVarScaleUniformAroundCenter |
1534 | let paint_offset = s.read::<Offset24>()?; |
1535 | |
1536 | let mut var_s = s.clone(); |
1537 | var_s.advance(6); |
1538 | let var_index_base = var_s.read::<u32>()?; |
1539 | |
1540 | let deltas = self |
1541 | .variation_data() |
1542 | .read_deltas::<3>(var_index_base, coords); |
1543 | |
1544 | let scale = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1545 | let center_x = f32::from(s.read::<i16>()?) + deltas[1]; |
1546 | let center_y = f32::from(s.read::<i16>()?) + deltas[2]; |
1547 | |
1548 | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1549 | painter.push_transform(Transform::new_scale(scale, scale)); |
1550 | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1551 | self.parse_paint( |
1552 | offset + paint_offset.to_usize(), |
1553 | palette, |
1554 | painter, |
1555 | recursion_stack, |
1556 | coords, |
1557 | foreground_color, |
1558 | ); |
1559 | painter.pop_transform(); |
1560 | painter.pop_transform(); |
1561 | painter.pop_transform(); |
1562 | } |
1563 | 24 => { |
1564 | // PaintRotate |
1565 | let paint_offset = s.read::<Offset24>()?; |
1566 | let angle = s.read::<F2DOT14>()?.to_f32(); |
1567 | |
1568 | painter.push_transform(Transform::new_rotate(angle)); |
1569 | self.parse_paint( |
1570 | offset + paint_offset.to_usize(), |
1571 | palette, |
1572 | painter, |
1573 | recursion_stack, |
1574 | #[cfg (feature = "variable-fonts" )] |
1575 | coords, |
1576 | foreground_color, |
1577 | ); |
1578 | painter.pop_transform(); |
1579 | } |
1580 | #[cfg (feature = "variable-fonts" )] |
1581 | 25 => { |
1582 | // PaintVarRotate |
1583 | let paint_offset = s.read::<Offset24>()?; |
1584 | |
1585 | let mut var_s = s.clone(); |
1586 | var_s.advance(2); |
1587 | let var_index_base = var_s.read::<u32>()?; |
1588 | |
1589 | let deltas = self |
1590 | .variation_data() |
1591 | .read_deltas::<1>(var_index_base, coords); |
1592 | |
1593 | let angle = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1594 | |
1595 | painter.push_transform(Transform::new_rotate(angle)); |
1596 | self.parse_paint( |
1597 | offset + paint_offset.to_usize(), |
1598 | palette, |
1599 | painter, |
1600 | recursion_stack, |
1601 | coords, |
1602 | foreground_color, |
1603 | ); |
1604 | painter.pop_transform(); |
1605 | } |
1606 | 26 => { |
1607 | // PaintRotateAroundCenter |
1608 | let paint_offset = s.read::<Offset24>()?; |
1609 | let angle = s.read::<F2DOT14>()?.to_f32(); |
1610 | let center_x = f32::from(s.read::<i16>()?); |
1611 | let center_y = f32::from(s.read::<i16>()?); |
1612 | |
1613 | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1614 | painter.push_transform(Transform::new_rotate(angle)); |
1615 | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1616 | self.parse_paint( |
1617 | offset + paint_offset.to_usize(), |
1618 | palette, |
1619 | painter, |
1620 | recursion_stack, |
1621 | #[cfg (feature = "variable-fonts" )] |
1622 | coords, |
1623 | foreground_color, |
1624 | ); |
1625 | painter.pop_transform(); |
1626 | painter.pop_transform(); |
1627 | painter.pop_transform(); |
1628 | } |
1629 | #[cfg (feature = "variable-fonts" )] |
1630 | 27 => { |
1631 | // PaintVarRotateAroundCenter |
1632 | let paint_offset = s.read::<Offset24>()?; |
1633 | |
1634 | let mut var_s = s.clone(); |
1635 | var_s.advance(6); |
1636 | let var_index_base = var_s.read::<u32>()?; |
1637 | |
1638 | let deltas = self |
1639 | .variation_data() |
1640 | .read_deltas::<3>(var_index_base, coords); |
1641 | |
1642 | let angle = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1643 | let center_x = f32::from(s.read::<i16>()?) + deltas[1]; |
1644 | let center_y = f32::from(s.read::<i16>()?) + deltas[2]; |
1645 | |
1646 | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1647 | painter.push_transform(Transform::new_rotate(angle)); |
1648 | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1649 | self.parse_paint( |
1650 | offset + paint_offset.to_usize(), |
1651 | palette, |
1652 | painter, |
1653 | recursion_stack, |
1654 | coords, |
1655 | foreground_color, |
1656 | ); |
1657 | painter.pop_transform(); |
1658 | painter.pop_transform(); |
1659 | painter.pop_transform(); |
1660 | } |
1661 | 28 => { |
1662 | // PaintSkew |
1663 | let paint_offset = s.read::<Offset24>()?; |
1664 | let skew_x = s.read::<F2DOT14>()?.to_f32(); |
1665 | let skew_y = s.read::<F2DOT14>()?.to_f32(); |
1666 | |
1667 | painter.push_transform(Transform::new_skew(skew_x, skew_y)); |
1668 | self.parse_paint( |
1669 | offset + paint_offset.to_usize(), |
1670 | palette, |
1671 | painter, |
1672 | recursion_stack, |
1673 | #[cfg (feature = "variable-fonts" )] |
1674 | coords, |
1675 | foreground_color, |
1676 | ); |
1677 | painter.pop_transform(); |
1678 | } |
1679 | #[cfg (feature = "variable-fonts" )] |
1680 | 29 => { |
1681 | // PaintVarSkew |
1682 | let paint_offset = s.read::<Offset24>()?; |
1683 | |
1684 | let mut var_s = s.clone(); |
1685 | var_s.advance(4); |
1686 | let var_index_base = var_s.read::<u32>()?; |
1687 | |
1688 | let deltas = self |
1689 | .variation_data() |
1690 | .read_deltas::<2>(var_index_base, coords); |
1691 | |
1692 | let skew_x = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1693 | let skew_y = s.read::<F2DOT14>()?.apply_float_delta(deltas[1]); |
1694 | |
1695 | painter.push_transform(Transform::new_skew(skew_x, skew_y)); |
1696 | self.parse_paint( |
1697 | offset + paint_offset.to_usize(), |
1698 | palette, |
1699 | painter, |
1700 | recursion_stack, |
1701 | coords, |
1702 | foreground_color, |
1703 | ); |
1704 | painter.pop_transform(); |
1705 | } |
1706 | 30 => { |
1707 | // PaintSkewAroundCenter |
1708 | let paint_offset = s.read::<Offset24>()?; |
1709 | let skew_x = s.read::<F2DOT14>()?.to_f32(); |
1710 | let skew_y = s.read::<F2DOT14>()?.to_f32(); |
1711 | let center_x = f32::from(s.read::<i16>()?); |
1712 | let center_y = f32::from(s.read::<i16>()?); |
1713 | |
1714 | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1715 | painter.push_transform(Transform::new_skew(skew_x, skew_y)); |
1716 | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1717 | self.parse_paint( |
1718 | offset + paint_offset.to_usize(), |
1719 | palette, |
1720 | painter, |
1721 | recursion_stack, |
1722 | #[cfg (feature = "variable-fonts" )] |
1723 | coords, |
1724 | foreground_color, |
1725 | ); |
1726 | painter.pop_transform(); |
1727 | painter.pop_transform(); |
1728 | painter.pop_transform(); |
1729 | } |
1730 | #[cfg (feature = "variable-fonts" )] |
1731 | 31 => { |
1732 | // PaintVarSkewAroundCenter |
1733 | let paint_offset = s.read::<Offset24>()?; |
1734 | |
1735 | let mut var_s = s.clone(); |
1736 | var_s.advance(8); |
1737 | let var_index_base = var_s.read::<u32>()?; |
1738 | |
1739 | let deltas = self |
1740 | .variation_data() |
1741 | .read_deltas::<4>(var_index_base, coords); |
1742 | |
1743 | let skew_x = s.read::<F2DOT14>()?.apply_float_delta(deltas[0]); |
1744 | let skew_y = s.read::<F2DOT14>()?.apply_float_delta(deltas[1]); |
1745 | let center_x = f32::from(s.read::<i16>()?) + deltas[2]; |
1746 | let center_y = f32::from(s.read::<i16>()?) + deltas[3]; |
1747 | |
1748 | painter.push_transform(Transform::new_translate(center_x, center_y)); |
1749 | painter.push_transform(Transform::new_skew(skew_x, skew_y)); |
1750 | painter.push_transform(Transform::new_translate(-center_x, -center_y)); |
1751 | self.parse_paint( |
1752 | offset + paint_offset.to_usize(), |
1753 | palette, |
1754 | painter, |
1755 | recursion_stack, |
1756 | coords, |
1757 | foreground_color, |
1758 | ); |
1759 | painter.pop_transform(); |
1760 | painter.pop_transform(); |
1761 | painter.pop_transform(); |
1762 | } |
1763 | 32 => { |
1764 | // PaintComposite |
1765 | let source_paint_offset = s.read::<Offset24>()?; |
1766 | let composite_mode = s.read::<CompositeMode>()?; |
1767 | let backdrop_paint_offset = s.read::<Offset24>()?; |
1768 | |
1769 | painter.push_layer(CompositeMode::SourceOver); |
1770 | self.parse_paint( |
1771 | offset + backdrop_paint_offset.to_usize(), |
1772 | palette, |
1773 | painter, |
1774 | recursion_stack, |
1775 | #[cfg (feature = "variable-fonts" )] |
1776 | coords, |
1777 | foreground_color, |
1778 | ); |
1779 | painter.push_layer(composite_mode); |
1780 | self.parse_paint( |
1781 | offset + source_paint_offset.to_usize(), |
1782 | palette, |
1783 | painter, |
1784 | recursion_stack, |
1785 | #[cfg (feature = "variable-fonts" )] |
1786 | coords, |
1787 | foreground_color, |
1788 | ); |
1789 | painter.pop_layer(); |
1790 | painter.pop_layer(); |
1791 | } |
1792 | _ => {} |
1793 | } |
1794 | |
1795 | Some(()) |
1796 | } |
1797 | |
1798 | fn parse_color_line( |
1799 | &self, |
1800 | offset: usize, |
1801 | foreground_color: RgbaColor, |
1802 | ) -> Option<NonVarColorLine<'a>> { |
1803 | let mut s = Stream::new_at(self.data, offset)?; |
1804 | let extend = s.read::<GradientExtend>()?; |
1805 | let count = s.read::<u16>()?; |
1806 | let colors = s.read_array16::<ColorStopRaw>(count)?; |
1807 | Some(NonVarColorLine { |
1808 | extend, |
1809 | colors, |
1810 | foreground_color, |
1811 | palettes: self.palettes, |
1812 | }) |
1813 | } |
1814 | |
1815 | #[cfg (feature = "variable-fonts" )] |
1816 | fn parse_var_color_line( |
1817 | &self, |
1818 | offset: usize, |
1819 | foreground_color: RgbaColor, |
1820 | ) -> Option<VarColorLine<'a>> { |
1821 | let mut s = Stream::new_at(self.data, offset)?; |
1822 | let extend = s.read::<GradientExtend>()?; |
1823 | let count = s.read::<u16>()?; |
1824 | let colors = s.read_array16::<VarColorStopRaw>(count)?; |
1825 | Some(VarColorLine { |
1826 | extend, |
1827 | colors, |
1828 | foreground_color, |
1829 | palettes: self.palettes, |
1830 | }) |
1831 | } |
1832 | } |
1833 | |
1834 | struct RecursionStack { |
1835 | // The limit of 64 is chosen arbitrarily and not from the spec. But we have to stop somewhere... |
1836 | stack: [usize; 64], |
1837 | len: usize, |
1838 | } |
1839 | |
1840 | impl RecursionStack { |
1841 | #[inline ] |
1842 | pub fn is_empty(&self) -> bool { |
1843 | self.len == 0 |
1844 | } |
1845 | |
1846 | #[inline ] |
1847 | pub fn push(&mut self, offset: usize) -> Result<(), ()> { |
1848 | if self.len == self.stack.len() { |
1849 | Err(()) |
1850 | } else { |
1851 | self.stack[self.len] = offset; |
1852 | self.len += 1; |
1853 | Ok(()) |
1854 | } |
1855 | } |
1856 | |
1857 | #[inline ] |
1858 | pub fn contains(&self, offset: usize) -> bool { |
1859 | if let Some(offsets) = self.stack.get(..self.len) { |
1860 | return offsets.contains(&offset); |
1861 | } |
1862 | |
1863 | false |
1864 | } |
1865 | |
1866 | #[inline ] |
1867 | pub fn pop(&mut self) { |
1868 | debug_assert!(!self.is_empty()); |
1869 | self.len -= 1; |
1870 | } |
1871 | } |
1872 | |
1873 | #[cfg (feature = "variable-fonts" )] |
1874 | #[derive (Clone, Copy, Debug, Default)] |
1875 | struct VariationData<'a> { |
1876 | variation_store: Option<ItemVariationStore<'a>>, |
1877 | delta_map: Option<DeltaSetIndexMap<'a>>, |
1878 | } |
1879 | |
1880 | #[cfg (feature = "variable-fonts" )] |
1881 | impl VariationData<'_> { |
1882 | // Inspired from `fontations`. |
1883 | fn read_deltas<const N: usize>( |
1884 | &self, |
1885 | var_index_base: u32, |
1886 | coordinates: &[NormalizedCoordinate], |
1887 | ) -> [f32; N] { |
1888 | const NO_VARIATION_DELTAS: u32 = 0xFFFFFFFF; |
1889 | let mut deltas = [0.0; N]; |
1890 | |
1891 | if coordinates.is_empty() |
1892 | || self.variation_store.is_none() |
1893 | || var_index_base == NO_VARIATION_DELTAS |
1894 | { |
1895 | return deltas; |
1896 | } |
1897 | |
1898 | let variation_store = self.variation_store.as_ref().unwrap(); |
1899 | |
1900 | for (i, delta) in deltas.iter_mut().enumerate() { |
1901 | *delta = self |
1902 | .delta_map |
1903 | .and_then(|d| d.map(var_index_base + i as u32)) |
1904 | .and_then(|d| variation_store.parse_delta(d.0, d.1, coordinates)) |
1905 | .unwrap_or(0.0); |
1906 | } |
1907 | |
1908 | deltas |
1909 | } |
1910 | } |
1911 | |