Coverage Report

Created: 2026-10-01 07:20

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