skrifa/color/
instance.rs

1//! COLR table instance.
2
3use read_fonts::{
4    tables::{
5        colr::*,
6        variations::{
7            DeltaSetIndex, DeltaSetIndexMap, FloatItemDelta, FloatItemDeltaTarget,
8            ItemVariationStore,
9        },
10    },
11    types::{BoundingBox, F2Dot14, GlyphId16, Point},
12    ReadError,
13};
14
15use core::ops::{Deref, Range};
16
17/// Unique paint identifier used for detecting cycles in the paint graph.
18pub type PaintId = usize;
19
20/// Combination of a `COLR` table and a location in variation space for
21/// resolving paints.
22///
23/// See [`resolve_paint`], [`ColorStops::resolve`] and [`resolve_clip_box`].
24#[derive(Clone)]
25pub struct ColrInstance<'a> {
26    colr: Colr<'a>,
27    index_map: Option<DeltaSetIndexMap<'a>>,
28    var_store: Option<ItemVariationStore<'a>>,
29    coords: &'a [F2Dot14],
30}
31
32impl<'a> ColrInstance<'a> {
33    /// Creates a new instance for the given `COLR` table and normalized variation
34    /// coordinates.
35    pub fn new(colr: Colr<'a>, coords: &'a [F2Dot14]) -> Self {
36        let index_map = colr.var_index_map().and_then(|res| res.ok());
37        let var_store = colr.item_variation_store().and_then(|res| res.ok());
38        Self {
39            colr,
40            coords,
41            index_map,
42            var_store,
43        }
44    }
45
46    /// Computes a sequence of N variation deltas starting at the given
47    /// `var_base` index.
48    fn var_deltas<const N: usize>(&self, var_index_base: u32) -> [FloatItemDelta; N] {
49        // Magic value that indicates deltas should not be applied.
50        const NO_VARIATION_DELTAS: u32 = 0xFFFFFFFF;
51        // Note: FreeType never returns an error for these lookups, so
52        // we do the same and just `unwrap_or_default` on var store
53        // errors.
54        // See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/fc01e7dd/src/sfnt/ttcolr.c#L574>
55        let mut deltas = [FloatItemDelta::ZERO; N];
56        if self.coords.is_empty()
57            || self.var_store.is_none()
58            || var_index_base == NO_VARIATION_DELTAS
59        {
60            return deltas;
61        }
62        let var_store = self.var_store.as_ref().unwrap();
63        if let Some(index_map) = self.index_map.as_ref() {
64            for (i, delta) in deltas.iter_mut().enumerate() {
65                let var_index = var_index_base + i as u32;
66                if let Ok(delta_ix) = index_map.get(var_index) {
67                    *delta = var_store
68                        .compute_float_delta(delta_ix, self.coords)
69                        .unwrap_or_default();
70                }
71            }
72        } else {
73            for (i, delta) in deltas.iter_mut().enumerate() {
74                let var_index = var_index_base + i as u32;
75                // If we don't have a var index map, use our index as the inner
76                // component and set the outer to 0.
77                let delta_ix = DeltaSetIndex {
78                    outer: 0,
79                    inner: var_index as u16,
80                };
81                *delta = var_store
82                    .compute_float_delta(delta_ix, self.coords)
83                    .unwrap_or_default();
84            }
85        }
86        deltas
87    }
88}
89
90impl<'a> Deref for ColrInstance<'a> {
91    type Target = Colr<'a>;
92
93    fn deref(&self) -> &Self::Target {
94        &self.colr
95    }
96}
97
98/// Resolves a clip box, applying variation deltas using the given
99/// instance.
100pub fn resolve_clip_box(instance: &ColrInstance, clip_box: &ClipBox) -> BoundingBox<f32> {
101    match clip_box {
102        ClipBox::Format1(cbox) => BoundingBox {
103            x_min: cbox.x_min().to_i16() as f32,
104            y_min: cbox.y_min().to_i16() as f32,
105            x_max: cbox.x_max().to_i16() as f32,
106            y_max: cbox.y_max().to_i16() as f32,
107        },
108        ClipBox::Format2(cbox) => {
109            let deltas = instance.var_deltas::<4>(cbox.var_index_base());
110            BoundingBox {
111                x_min: cbox.x_min().apply_float_delta(deltas[0]),
112                y_min: cbox.y_min().apply_float_delta(deltas[1]),
113                x_max: cbox.x_max().apply_float_delta(deltas[2]),
114                y_max: cbox.y_max().apply_float_delta(deltas[3]),
115            }
116        }
117    }
118}
119
120/// Simplified version of a [`ColorStop`] or [`VarColorStop`] with applied
121/// variation deltas.
122#[derive(Clone, Debug)]
123pub struct ResolvedColorStop {
124    pub offset: f32,
125    pub palette_index: u16,
126    pub alpha: f32,
127}
128
129/// Collection of [`ColorStop`] or [`VarColorStop`].
130// Note: only one of these fields is used at any given time, but this structure
131// was chosen over the obvious enum approach for simplicity in generating a
132// single concrete type for the `impl Iterator` return type of the `resolve`
133// method.
134#[derive(Clone)]
135pub struct ColorStops<'a> {
136    stops: &'a [ColorStop],
137    var_stops: &'a [VarColorStop],
138}
139
140impl ColorStops<'_> {
141    pub fn len(&self) -> usize {
142        self.stops.len() + self.var_stops.len()
143    }
144
145    pub fn is_empty(&self) -> bool {
146        self.stops.is_empty() && self.var_stops.is_empty()
147    }
148}
149
150impl<'a> From<ColorLine<'a>> for ColorStops<'a> {
151    fn from(value: ColorLine<'a>) -> Self {
152        Self {
153            stops: value.color_stops(),
154            var_stops: &[],
155        }
156    }
157}
158
159impl<'a> From<VarColorLine<'a>> for ColorStops<'a> {
160    fn from(value: VarColorLine<'a>) -> Self {
161        Self {
162            stops: &[],
163            var_stops: value.color_stops(),
164        }
165    }
166}
167
168impl<'a> ColorStops<'a> {
169    /// Returns an iterator yielding resolved color stops with variation deltas
170    /// applied.
171    pub fn resolve(
172        &self,
173        instance: &'a ColrInstance<'a>,
174    ) -> impl Iterator<Item = ResolvedColorStop> + 'a {
175        self.stops
176            .iter()
177            .map(|stop| ResolvedColorStop {
178                offset: stop.stop_offset().to_f32(),
179                palette_index: stop.palette_index(),
180                alpha: stop.alpha().to_f32(),
181            })
182            .chain(self.var_stops.iter().map(|stop| {
183                let deltas = instance.var_deltas::<2>(stop.var_index_base());
184                ResolvedColorStop {
185                    offset: stop.stop_offset().apply_float_delta(deltas[0]),
186                    palette_index: stop.palette_index(),
187                    alpha: stop.alpha().apply_float_delta(deltas[1]),
188                }
189            }))
190    }
191}
192
193/// Simplified version of `Paint` with applied variation deltas.
194///
195/// These are constructed with the [`resolve_paint`] function.
196///
197/// This is roughly equivalent to FreeType's
198/// [`FT_COLR_Paint`](https://freetype.org/freetype2/docs/reference/ft2-layer_management.html#ft_colr_paint)
199/// type.
200pub enum ResolvedPaint<'a> {
201    ColrLayers {
202        range: Range<usize>,
203    },
204    Solid {
205        palette_index: u16,
206        alpha: f32,
207    },
208    LinearGradient {
209        x0: f32,
210        y0: f32,
211        x1: f32,
212        y1: f32,
213        x2: f32,
214        y2: f32,
215        color_stops: ColorStops<'a>,
216        extend: Extend,
217    },
218    RadialGradient {
219        x0: f32,
220        y0: f32,
221        radius0: f32,
222        x1: f32,
223        y1: f32,
224        radius1: f32,
225        color_stops: ColorStops<'a>,
226        extend: Extend,
227    },
228    SweepGradient {
229        center_x: f32,
230        center_y: f32,
231        start_angle: f32,
232        end_angle: f32,
233        color_stops: ColorStops<'a>,
234        extend: Extend,
235    },
236    Glyph {
237        glyph_id: GlyphId16,
238        paint: Paint<'a>,
239    },
240    ColrGlyph {
241        glyph_id: GlyphId16,
242    },
243    Transform {
244        xx: f32,
245        yx: f32,
246        xy: f32,
247        yy: f32,
248        dx: f32,
249        dy: f32,
250        paint: Paint<'a>,
251    },
252    Translate {
253        dx: f32,
254        dy: f32,
255        paint: Paint<'a>,
256    },
257    Scale {
258        scale_x: f32,
259        scale_y: f32,
260        around_center: Option<Point<f32>>,
261        paint: Paint<'a>,
262    },
263    Rotate {
264        angle: f32,
265        around_center: Option<Point<f32>>,
266        paint: Paint<'a>,
267    },
268    Skew {
269        x_skew_angle: f32,
270        y_skew_angle: f32,
271        around_center: Option<Point<f32>>,
272        paint: Paint<'a>,
273    },
274    Composite {
275        source_paint: Paint<'a>,
276        mode: CompositeMode,
277        backdrop_paint: Paint<'a>,
278    },
279}
280
281/// Resolves this paint with the given instance.
282///
283/// Resolving means that all numeric values are converted to 32-bit floating
284/// point, variation deltas are applied (also computed fully in floating
285/// point), and the various transform paints are collapsed into a single value
286/// for their category (transform, translate, scale, rotate and skew).
287///
288/// This provides a simpler type for consumers that are more interested
289/// in extracting the semantics of the graph rather than working with the
290/// raw encoded structures.
291pub fn resolve_paint<'a>(
292    instance: &ColrInstance<'a>,
293    paint: &Paint<'a>,
294) -> Result<ResolvedPaint<'a>, ReadError> {
295    Ok(match paint {
296        Paint::ColrLayers(layers) => {
297            let start = layers.first_layer_index() as usize;
298            ResolvedPaint::ColrLayers {
299                range: start..start + layers.num_layers() as usize,
300            }
301        }
302        Paint::Solid(solid) => ResolvedPaint::Solid {
303            palette_index: solid.palette_index(),
304            alpha: solid.alpha().to_f32(),
305        },
306        Paint::VarSolid(solid) => {
307            let deltas = instance.var_deltas::<1>(solid.var_index_base());
308            ResolvedPaint::Solid {
309                palette_index: solid.palette_index(),
310                alpha: solid.alpha().apply_float_delta(deltas[0]),
311            }
312        }
313        Paint::LinearGradient(gradient) => {
314            let color_line = gradient.color_line()?;
315            let extend = color_line.extend();
316            ResolvedPaint::LinearGradient {
317                x0: gradient.x0().to_i16() as f32,
318                y0: gradient.y0().to_i16() as f32,
319                x1: gradient.x1().to_i16() as f32,
320                y1: gradient.y1().to_i16() as f32,
321                x2: gradient.x2().to_i16() as f32,
322                y2: gradient.y2().to_i16() as f32,
323                color_stops: color_line.into(),
324                extend,
325            }
326        }
327        Paint::VarLinearGradient(gradient) => {
328            let color_line = gradient.color_line()?;
329            let extend = color_line.extend();
330            let deltas = instance.var_deltas::<6>(gradient.var_index_base());
331            ResolvedPaint::LinearGradient {
332                x0: gradient.x0().apply_float_delta(deltas[0]),
333                y0: gradient.y0().apply_float_delta(deltas[1]),
334                x1: gradient.x1().apply_float_delta(deltas[2]),
335                y1: gradient.y1().apply_float_delta(deltas[3]),
336                x2: gradient.x2().apply_float_delta(deltas[4]),
337                y2: gradient.y2().apply_float_delta(deltas[5]),
338                color_stops: color_line.into(),
339                extend,
340            }
341        }
342        Paint::RadialGradient(gradient) => {
343            let color_line = gradient.color_line()?;
344            let extend = color_line.extend();
345            ResolvedPaint::RadialGradient {
346                x0: gradient.x0().to_i16() as f32,
347                y0: gradient.y0().to_i16() as f32,
348                radius0: gradient.radius0().to_u16() as f32,
349                x1: gradient.x1().to_i16() as f32,
350                y1: gradient.y1().to_i16() as f32,
351                radius1: gradient.radius1().to_u16() as f32,
352                color_stops: color_line.into(),
353                extend,
354            }
355        }
356        Paint::VarRadialGradient(gradient) => {
357            let color_line = gradient.color_line()?;
358            let extend = color_line.extend();
359            let deltas = instance.var_deltas::<6>(gradient.var_index_base());
360            ResolvedPaint::RadialGradient {
361                x0: gradient.x0().apply_float_delta(deltas[0]),
362                y0: gradient.y0().apply_float_delta(deltas[1]),
363                radius0: gradient.radius0().apply_float_delta(deltas[2]),
364                x1: gradient.x1().apply_float_delta(deltas[3]),
365                y1: gradient.y1().apply_float_delta(deltas[4]),
366                radius1: gradient.radius1().apply_float_delta(deltas[5]),
367                color_stops: color_line.into(),
368                extend,
369            }
370        }
371        Paint::SweepGradient(gradient) => {
372            let color_line = gradient.color_line()?;
373            let extend = color_line.extend();
374            ResolvedPaint::SweepGradient {
375                center_x: gradient.center_x().to_i16() as f32,
376                center_y: gradient.center_y().to_i16() as f32,
377                start_angle: gradient.start_angle().to_f32(),
378                end_angle: gradient.end_angle().to_f32(),
379                color_stops: color_line.into(),
380                extend,
381            }
382        }
383        Paint::VarSweepGradient(gradient) => {
384            let color_line = gradient.color_line()?;
385            let extend = color_line.extend();
386            let deltas = instance.var_deltas::<4>(gradient.var_index_base());
387            ResolvedPaint::SweepGradient {
388                center_x: gradient.center_x().apply_float_delta(deltas[0]),
389                center_y: gradient.center_y().apply_float_delta(deltas[1]),
390                start_angle: gradient.start_angle().apply_float_delta(deltas[2]),
391                end_angle: gradient.end_angle().apply_float_delta(deltas[3]),
392                color_stops: color_line.into(),
393                extend,
394            }
395        }
396        Paint::Glyph(glyph) => ResolvedPaint::Glyph {
397            glyph_id: glyph.glyph_id(),
398            paint: glyph.paint()?,
399        },
400        Paint::ColrGlyph(glyph) => ResolvedPaint::ColrGlyph {
401            glyph_id: glyph.glyph_id(),
402        },
403        Paint::Transform(transform) => {
404            let affine = transform.transform()?;
405            let paint = transform.paint()?;
406            ResolvedPaint::Transform {
407                xx: affine.xx().to_f32(),
408                yx: affine.yx().to_f32(),
409                xy: affine.xy().to_f32(),
410                yy: affine.yy().to_f32(),
411                dx: affine.dx().to_f32(),
412                dy: affine.dy().to_f32(),
413                paint,
414            }
415        }
416        Paint::VarTransform(transform) => {
417            let affine = transform.transform()?;
418            let paint = transform.paint()?;
419            let deltas = instance.var_deltas::<6>(affine.var_index_base());
420            ResolvedPaint::Transform {
421                xx: affine.xx().apply_float_delta(deltas[0]),
422                yx: affine.yx().apply_float_delta(deltas[1]),
423                xy: affine.xy().apply_float_delta(deltas[2]),
424                yy: affine.yy().apply_float_delta(deltas[3]),
425                dx: affine.dx().apply_float_delta(deltas[4]),
426                dy: affine.dy().apply_float_delta(deltas[5]),
427                paint,
428            }
429        }
430        Paint::Translate(transform) => ResolvedPaint::Translate {
431            dx: transform.dx().to_i16() as f32,
432            dy: transform.dy().to_i16() as f32,
433            paint: transform.paint()?,
434        },
435        Paint::VarTranslate(transform) => {
436            let deltas = instance.var_deltas::<2>(transform.var_index_base());
437            ResolvedPaint::Translate {
438                dx: transform.dx().apply_float_delta(deltas[0]),
439                dy: transform.dy().apply_float_delta(deltas[1]),
440                paint: transform.paint()?,
441            }
442        }
443        Paint::Scale(transform) => ResolvedPaint::Scale {
444            scale_x: transform.scale_x().to_f32(),
445            scale_y: transform.scale_y().to_f32(),
446            around_center: None,
447            paint: transform.paint()?,
448        },
449        Paint::VarScale(transform) => {
450            let deltas = instance.var_deltas::<2>(transform.var_index_base());
451            ResolvedPaint::Scale {
452                scale_x: transform.scale_x().apply_float_delta(deltas[0]),
453                scale_y: transform.scale_y().apply_float_delta(deltas[1]),
454                around_center: None,
455                paint: transform.paint()?,
456            }
457        }
458        Paint::ScaleAroundCenter(transform) => ResolvedPaint::Scale {
459            scale_x: transform.scale_x().to_f32(),
460            scale_y: transform.scale_y().to_f32(),
461            around_center: Some(Point::new(
462                transform.center_x().to_i16() as f32,
463                transform.center_y().to_i16() as f32,
464            )),
465            paint: transform.paint()?,
466        },
467        Paint::VarScaleAroundCenter(transform) => {
468            let deltas = instance.var_deltas::<4>(transform.var_index_base());
469            ResolvedPaint::Scale {
470                scale_x: transform.scale_x().apply_float_delta(deltas[0]),
471                scale_y: transform.scale_y().apply_float_delta(deltas[1]),
472                around_center: Some(Point::new(
473                    transform.center_x().apply_float_delta(deltas[2]),
474                    transform.center_y().apply_float_delta(deltas[3]),
475                )),
476                paint: transform.paint()?,
477            }
478        }
479        Paint::ScaleUniform(transform) => {
480            let scale = transform.scale().to_f32();
481            ResolvedPaint::Scale {
482                scale_x: scale,
483                scale_y: scale,
484                around_center: None,
485                paint: transform.paint()?,
486            }
487        }
488        Paint::VarScaleUniform(transform) => {
489            let deltas = instance.var_deltas::<1>(transform.var_index_base());
490            let scale = transform.scale().apply_float_delta(deltas[0]);
491            ResolvedPaint::Scale {
492                scale_x: scale,
493                scale_y: scale,
494                around_center: None,
495                paint: transform.paint()?,
496            }
497        }
498        Paint::ScaleUniformAroundCenter(transform) => {
499            let scale = transform.scale().to_f32();
500            ResolvedPaint::Scale {
501                scale_x: scale,
502                scale_y: scale,
503                around_center: Some(Point::new(
504                    transform.center_x().to_i16() as f32,
505                    transform.center_y().to_i16() as f32,
506                )),
507                paint: transform.paint()?,
508            }
509        }
510        Paint::VarScaleUniformAroundCenter(transform) => {
511            let deltas = instance.var_deltas::<3>(transform.var_index_base());
512            let scale = transform.scale().apply_float_delta(deltas[0]);
513            ResolvedPaint::Scale {
514                scale_x: scale,
515                scale_y: scale,
516                around_center: Some(Point::new(
517                    transform.center_x().apply_float_delta(deltas[1]),
518                    transform.center_y().apply_float_delta(deltas[2]),
519                )),
520                paint: transform.paint()?,
521            }
522        }
523        Paint::Rotate(transform) => ResolvedPaint::Rotate {
524            angle: transform.angle().to_f32(),
525            around_center: None,
526            paint: transform.paint()?,
527        },
528        Paint::VarRotate(transform) => {
529            let deltas = instance.var_deltas::<1>(transform.var_index_base());
530            ResolvedPaint::Rotate {
531                angle: transform.angle().apply_float_delta(deltas[0]),
532                around_center: None,
533                paint: transform.paint()?,
534            }
535        }
536        Paint::RotateAroundCenter(transform) => ResolvedPaint::Rotate {
537            angle: transform.angle().to_f32(),
538            around_center: Some(Point::new(
539                transform.center_x().to_i16() as f32,
540                transform.center_y().to_i16() as f32,
541            )),
542            paint: transform.paint()?,
543        },
544        Paint::VarRotateAroundCenter(transform) => {
545            let deltas = instance.var_deltas::<3>(transform.var_index_base());
546            ResolvedPaint::Rotate {
547                angle: transform.angle().apply_float_delta(deltas[0]),
548                around_center: Some(Point::new(
549                    transform.center_x().apply_float_delta(deltas[1]),
550                    transform.center_y().apply_float_delta(deltas[2]),
551                )),
552                paint: transform.paint()?,
553            }
554        }
555        Paint::Skew(transform) => ResolvedPaint::Skew {
556            x_skew_angle: transform.x_skew_angle().to_f32(),
557            y_skew_angle: transform.y_skew_angle().to_f32(),
558            around_center: None,
559            paint: transform.paint()?,
560        },
561        Paint::VarSkew(transform) => {
562            let deltas = instance.var_deltas::<2>(transform.var_index_base());
563            ResolvedPaint::Skew {
564                x_skew_angle: transform.x_skew_angle().apply_float_delta(deltas[0]),
565                y_skew_angle: transform.y_skew_angle().apply_float_delta(deltas[1]),
566                around_center: None,
567                paint: transform.paint()?,
568            }
569        }
570        Paint::SkewAroundCenter(transform) => ResolvedPaint::Skew {
571            x_skew_angle: transform.x_skew_angle().to_f32(),
572            y_skew_angle: transform.y_skew_angle().to_f32(),
573            around_center: Some(Point::new(
574                transform.center_x().to_i16() as f32,
575                transform.center_y().to_i16() as f32,
576            )),
577            paint: transform.paint()?,
578        },
579        Paint::VarSkewAroundCenter(transform) => {
580            let deltas = instance.var_deltas::<4>(transform.var_index_base());
581            ResolvedPaint::Skew {
582                x_skew_angle: transform.x_skew_angle().apply_float_delta(deltas[0]),
583                y_skew_angle: transform.y_skew_angle().apply_float_delta(deltas[1]),
584                around_center: Some(Point::new(
585                    transform.center_x().apply_float_delta(deltas[2]),
586                    transform.center_y().apply_float_delta(deltas[3]),
587                )),
588                paint: transform.paint()?,
589            }
590        }
591        Paint::Composite(composite) => ResolvedPaint::Composite {
592            source_paint: composite.source_paint()?,
593            mode: composite.composite_mode(),
594            backdrop_paint: composite.backdrop_paint()?,
595        },
596    })
597}