read_fonts/tables/colr/
closure.rs

1//! computing closure for the colr table
2
3use font_types::{GlyphId, GlyphId16};
4
5use crate::{collections::IntSet, tables::variations::NO_VARIATION_INDEX, ResolveOffset};
6
7use super::{
8    Clip, ClipBox, ClipBoxFormat2, ClipList, ColorLine, ColorStop, Colr, Paint, PaintColrGlyph,
9    PaintColrLayers, PaintComposite, PaintGlyph, PaintLinearGradient, PaintRadialGradient,
10    PaintRotate, PaintRotateAroundCenter, PaintScale, PaintScaleAroundCenter, PaintScaleUniform,
11    PaintScaleUniformAroundCenter, PaintSkew, PaintSkewAroundCenter, PaintSolid,
12    PaintSweepGradient, PaintTransform, PaintTranslate, PaintVarLinearGradient,
13    PaintVarRadialGradient, PaintVarRotate, PaintVarRotateAroundCenter, PaintVarScale,
14    PaintVarScaleAroundCenter, PaintVarScaleUniform, PaintVarScaleUniformAroundCenter,
15    PaintVarSkew, PaintVarSkewAroundCenter, PaintVarSolid, PaintVarSweepGradient,
16    PaintVarTransform, PaintVarTranslate, VarAffine2x3, VarColorLine, VarColorStop,
17};
18
19impl Colr<'_> {
20    //Collect the transitive closure of V0 palette indices needed for all of the input glyphs set
21    //It's similar to closure glyphs but in a separate fn, because v1 closure might adds more v0 glyphs, so this fn needs to be called after v1 closure
22    pub fn v0_closure_palette_indices(
23        &self,
24        glyph_set: &IntSet<GlyphId>,
25        palette_indices: &mut IntSet<u16>,
26    ) {
27        let Some(Ok(records)) = self.base_glyph_records() else {
28            return;
29        };
30        for glyph_id in glyph_set.iter() {
31            let Ok(glyph_id) = glyph_id.try_into() else {
32                continue;
33            };
34            let record = match records.binary_search_by(|rec| rec.glyph_id().cmp(&glyph_id)) {
35                Ok(idx) => records[idx],
36                _ => continue,
37            };
38            let start = record.first_layer_index() as usize;
39            let end = start + record.num_layers() as usize;
40            for layer_index in start..=end {
41                if let Ok((_gid, palette_id)) = self.v0_layer(layer_index) {
42                    palette_indices.insert(palette_id);
43                }
44            }
45        }
46    }
47
48    /// Collect the transitive closure of v1 glyphs,layer/paletted indices and variation/delta set indices for COLRv1
49    pub fn v1_closure(
50        &self,
51        glyph_set: &mut IntSet<GlyphId>,
52        layer_indices: &mut IntSet<u32>,
53        palette_indices: &mut IntSet<u16>,
54        variation_indices: &mut IntSet<u32>,
55        delta_set_indices: &mut IntSet<u32>,
56    ) {
57        if self.version() < 1 {
58            return;
59        }
60
61        let mut c =
62            Colrv1ClosureContext::new(layer_indices, palette_indices, variation_indices, self);
63        if let Some(Ok(base_glyph_list)) = self.base_glyph_list() {
64            let base_glyph_records = base_glyph_list.base_glyph_paint_records();
65            let offset_data = base_glyph_list.offset_data();
66            for paint_record in base_glyph_records {
67                let gid = paint_record.glyph_id();
68                if !glyph_set.contains(GlyphId::from(gid)) {
69                    continue;
70                }
71                if let Ok(paint) = paint_record.paint(offset_data) {
72                    c.dispatch(&paint);
73                }
74            }
75
76            glyph_set.union(&c.glyph_set);
77        }
78
79        if let Some(Ok(clip_list)) = self.clip_list() {
80            c.glyph_set.union(glyph_set);
81            for clip_record in clip_list.clips() {
82                clip_record.v1_closure(&mut c, &clip_list);
83            }
84        }
85
86        //when a DeltaSetIndexMap is included, collected variation indices are actually delta set indices, we need to map them into variation indices
87        if let Some(Ok(var_index_map)) = self.var_index_map() {
88            delta_set_indices.extend(variation_indices.iter());
89            variation_indices.clear();
90            for idx in delta_set_indices.iter() {
91                if let Ok(var_idx) = var_index_map.get(idx) {
92                    let var_idx = ((var_idx.outer as u32) << 16) + var_idx.inner as u32;
93                    variation_indices.insert(var_idx);
94                }
95            }
96        }
97    }
98
99    /// Collect the transitive closure of V0 glyphs needed for all of the input glyphs set
100    pub fn v0_closure_glyphs(
101        &self,
102        glyph_set: &IntSet<GlyphId>,
103        glyphset_colrv0: &mut IntSet<GlyphId>,
104    ) {
105        glyphset_colrv0.union(glyph_set);
106        let Some(Ok(records)) = self.base_glyph_records() else {
107            return;
108        };
109        for glyph_id in glyph_set.iter() {
110            let Ok(glyph_id) = glyph_id.try_into() else {
111                continue;
112            };
113            let record = match records.binary_search_by(|rec| rec.glyph_id().cmp(&glyph_id)) {
114                Ok(idx) => records[idx],
115                _ => continue,
116            };
117            let start = record.first_layer_index() as usize;
118            let end = start + record.num_layers() as usize;
119            for layer_index in start..=end {
120                if let Ok((gid, _palette_id)) = self.v0_layer(layer_index) {
121                    glyphset_colrv0.insert(GlyphId::from(gid));
122                }
123            }
124        }
125    }
126}
127
128struct Colrv1ClosureContext<'a> {
129    glyph_set: IntSet<GlyphId>,
130    layer_indices: &'a mut IntSet<u32>,
131    palette_indices: &'a mut IntSet<u16>,
132    variation_indices: &'a mut IntSet<u32>,
133    colr: &'a Colr<'a>,
134    nesting_level_left: u8,
135    visited_paints: IntSet<u32>,
136    colr_head: usize,
137}
138
139impl<'a> Colrv1ClosureContext<'a> {
140    pub fn new(
141        layer_indices: &'a mut IntSet<u32>,
142        palette_indices: &'a mut IntSet<u16>,
143        variation_indices: &'a mut IntSet<u32>,
144        colr: &'a Colr,
145    ) -> Self {
146        let colr_head = colr.offset_data().as_bytes().as_ptr() as usize;
147        Self {
148            glyph_set: IntSet::empty(),
149            layer_indices,
150            palette_indices,
151            variation_indices,
152            colr,
153            nesting_level_left: 64,
154            visited_paints: IntSet::empty(),
155            colr_head,
156        }
157    }
158
159    fn dispatch(&mut self, paint: &Paint) {
160        if self.nesting_level_left == 0 {
161            return;
162        }
163
164        if self.paint_visited(paint) {
165            return;
166        }
167        self.nesting_level_left -= 1;
168        paint.v1_closure(self);
169        self.nesting_level_left += 1;
170    }
171
172    fn paint_visited(&mut self, paint: &Paint) -> bool {
173        let offset = (paint.offset_data().as_bytes().as_ptr() as usize - self.colr_head) as u32;
174        if self.visited_paints.contains(offset) {
175            return true;
176        }
177
178        self.visited_paints.insert(offset);
179        false
180    }
181
182    fn add_layer_indices(&mut self, first_layer_index: u32, last_layer_index: u32) {
183        self.layer_indices
184            .insert_range(first_layer_index..=last_layer_index);
185    }
186
187    fn add_palette_index(&mut self, palette_index: u16) {
188        self.palette_indices.insert(palette_index);
189    }
190
191    fn add_variation_indices(&mut self, var_index_base: u32, num_vars: u8) {
192        if num_vars == 0 || var_index_base == NO_VARIATION_INDEX {
193            return;
194        }
195
196        let last_var_index = var_index_base + num_vars as u32 - 1;
197        self.variation_indices
198            .insert_range(var_index_base..=last_var_index);
199    }
200
201    fn add_glyph_id(&mut self, gid: GlyphId16) {
202        self.glyph_set.insert(GlyphId::from(gid));
203    }
204}
205
206impl ColorStop {
207    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
208        c.add_palette_index(self.palette_index());
209    }
210}
211
212impl VarColorStop {
213    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
214        c.add_palette_index(self.palette_index());
215        c.add_variation_indices(self.var_index_base(), 2);
216    }
217}
218
219impl ColorLine<'_> {
220    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
221        for colorstop in self.color_stops() {
222            colorstop.v1_closure(c);
223        }
224    }
225}
226
227impl VarColorLine<'_> {
228    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
229        for var_colorstop in self.color_stops() {
230            var_colorstop.v1_closure(c);
231        }
232    }
233}
234
235impl Paint<'_> {
236    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
237        match self {
238            Self::ColrLayers(item) => item.v1_closure(c),
239            Self::Solid(item) => item.v1_closure(c),
240            Self::VarSolid(item) => item.v1_closure(c),
241            Self::LinearGradient(item) => item.v1_closure(c),
242            Self::VarLinearGradient(item) => item.v1_closure(c),
243            Self::RadialGradient(item) => item.v1_closure(c),
244            Self::VarRadialGradient(item) => item.v1_closure(c),
245            Self::SweepGradient(item) => item.v1_closure(c),
246            Self::VarSweepGradient(item) => item.v1_closure(c),
247            Self::Glyph(item) => item.v1_closure(c),
248            Self::ColrGlyph(item) => item.v1_closure(c),
249            Self::Transform(item) => item.v1_closure(c),
250            Self::VarTransform(item) => item.v1_closure(c),
251            Self::Translate(item) => item.v1_closure(c),
252            Self::VarTranslate(item) => item.v1_closure(c),
253            Self::Scale(item) => item.v1_closure(c),
254            Self::VarScale(item) => item.v1_closure(c),
255            Self::ScaleAroundCenter(item) => item.v1_closure(c),
256            Self::VarScaleAroundCenter(item) => item.v1_closure(c),
257            Self::ScaleUniform(item) => item.v1_closure(c),
258            Self::VarScaleUniform(item) => item.v1_closure(c),
259            Self::ScaleUniformAroundCenter(item) => item.v1_closure(c),
260            Self::VarScaleUniformAroundCenter(item) => item.v1_closure(c),
261            Self::Rotate(item) => item.v1_closure(c),
262            Self::VarRotate(item) => item.v1_closure(c),
263            Self::RotateAroundCenter(item) => item.v1_closure(c),
264            Self::VarRotateAroundCenter(item) => item.v1_closure(c),
265            Self::Skew(item) => item.v1_closure(c),
266            Self::VarSkew(item) => item.v1_closure(c),
267            Self::SkewAroundCenter(item) => item.v1_closure(c),
268            Self::VarSkewAroundCenter(item) => item.v1_closure(c),
269            Self::Composite(item) => item.v1_closure(c),
270        }
271    }
272}
273
274impl PaintColrLayers<'_> {
275    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
276        let num_layers = self.num_layers();
277        if num_layers == 0 {
278            return;
279        }
280
281        let Some(Ok(layer_list)) = c.colr.layer_list() else {
282            return;
283        };
284        let first_layer_index = self.first_layer_index();
285        let last_layer_index = first_layer_index + num_layers as u32 - 1;
286        c.add_layer_indices(first_layer_index, last_layer_index);
287
288        let offset_data = layer_list.offset_data();
289        for layer_index in first_layer_index..=last_layer_index {
290            if let Some(paint_offset) = layer_list.paint_offsets().get(layer_index as usize) {
291                if let Ok(paint) = paint_offset.get().resolve::<Paint>(offset_data) {
292                    c.dispatch(&paint);
293                }
294            }
295        }
296    }
297}
298
299impl PaintSolid<'_> {
300    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
301        c.add_palette_index(self.palette_index());
302    }
303}
304
305impl PaintVarSolid<'_> {
306    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
307        c.add_palette_index(self.palette_index());
308        c.add_variation_indices(self.var_index_base(), 1);
309    }
310}
311
312impl PaintLinearGradient<'_> {
313    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
314        if let Ok(colorline) = self.color_line() {
315            colorline.v1_closure(c);
316        }
317    }
318}
319
320impl PaintVarLinearGradient<'_> {
321    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
322        if let Ok(var_colorline) = self.color_line() {
323            var_colorline.v1_closure(c);
324        }
325        c.add_variation_indices(self.var_index_base(), 6);
326    }
327}
328
329impl PaintRadialGradient<'_> {
330    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
331        if let Ok(colorline) = self.color_line() {
332            colorline.v1_closure(c);
333        }
334    }
335}
336
337impl PaintVarRadialGradient<'_> {
338    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
339        if let Ok(var_colorline) = self.color_line() {
340            var_colorline.v1_closure(c);
341        }
342        c.add_variation_indices(self.var_index_base(), 6);
343    }
344}
345
346impl PaintSweepGradient<'_> {
347    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
348        if let Ok(colorline) = self.color_line() {
349            colorline.v1_closure(c);
350        }
351    }
352}
353
354impl PaintVarSweepGradient<'_> {
355    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
356        if let Ok(var_colorline) = self.color_line() {
357            var_colorline.v1_closure(c);
358        }
359        c.add_variation_indices(self.var_index_base(), 4);
360    }
361}
362
363impl PaintGlyph<'_> {
364    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
365        c.add_glyph_id(self.glyph_id());
366        if let Ok(paint) = self.paint() {
367            c.dispatch(&paint);
368        }
369    }
370}
371
372impl PaintColrGlyph<'_> {
373    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
374        let glyph_id = self.glyph_id();
375        let Some(Ok(list)) = c.colr.base_glyph_list() else {
376            return;
377        };
378        let records = list.base_glyph_paint_records();
379        let record = match records.binary_search_by(|rec| rec.glyph_id().cmp(&glyph_id)) {
380            Ok(ix) => &records[ix],
381            _ => return,
382        };
383        if let Ok(paint) = record.paint(list.offset_data()) {
384            c.add_glyph_id(glyph_id);
385            c.dispatch(&paint);
386        }
387    }
388}
389
390impl PaintTransform<'_> {
391    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
392        if let Ok(paint) = self.paint() {
393            c.dispatch(&paint);
394        }
395    }
396}
397
398impl VarAffine2x3<'_> {
399    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
400        c.add_variation_indices(self.var_index_base(), 6);
401    }
402}
403
404impl PaintVarTransform<'_> {
405    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
406        if let Ok(paint) = self.paint() {
407            c.dispatch(&paint);
408            if let Ok(affine2x3) = self.transform() {
409                affine2x3.v1_closure(c);
410            }
411        }
412    }
413}
414
415impl PaintTranslate<'_> {
416    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
417        if let Ok(paint) = self.paint() {
418            c.dispatch(&paint);
419        }
420    }
421}
422
423impl PaintVarTranslate<'_> {
424    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
425        if let Ok(paint) = self.paint() {
426            c.dispatch(&paint);
427            c.add_variation_indices(self.var_index_base(), 2);
428        }
429    }
430}
431
432impl PaintScale<'_> {
433    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
434        if let Ok(paint) = self.paint() {
435            c.dispatch(&paint);
436        }
437    }
438}
439
440impl PaintVarScale<'_> {
441    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
442        if let Ok(paint) = self.paint() {
443            c.dispatch(&paint);
444            c.add_variation_indices(self.var_index_base(), 2);
445        }
446    }
447}
448
449impl PaintScaleAroundCenter<'_> {
450    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
451        if let Ok(paint) = self.paint() {
452            c.dispatch(&paint);
453        }
454    }
455}
456
457impl PaintVarScaleAroundCenter<'_> {
458    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
459        if let Ok(paint) = self.paint() {
460            c.dispatch(&paint);
461            c.add_variation_indices(self.var_index_base(), 4);
462        }
463    }
464}
465
466impl PaintScaleUniform<'_> {
467    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
468        if let Ok(paint) = self.paint() {
469            c.dispatch(&paint);
470        }
471    }
472}
473
474impl PaintVarScaleUniform<'_> {
475    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
476        if let Ok(paint) = self.paint() {
477            c.dispatch(&paint);
478            c.add_variation_indices(self.var_index_base(), 1);
479        }
480    }
481}
482
483impl PaintScaleUniformAroundCenter<'_> {
484    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
485        if let Ok(paint) = self.paint() {
486            c.dispatch(&paint);
487        }
488    }
489}
490
491impl PaintVarScaleUniformAroundCenter<'_> {
492    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
493        if let Ok(paint) = self.paint() {
494            c.dispatch(&paint);
495            c.add_variation_indices(self.var_index_base(), 3);
496        }
497    }
498}
499
500impl PaintRotate<'_> {
501    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
502        if let Ok(paint) = self.paint() {
503            c.dispatch(&paint);
504        }
505    }
506}
507
508impl PaintVarRotate<'_> {
509    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
510        if let Ok(paint) = self.paint() {
511            c.dispatch(&paint);
512            c.add_variation_indices(self.var_index_base(), 1);
513        }
514    }
515}
516
517impl PaintRotateAroundCenter<'_> {
518    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
519        if let Ok(paint) = self.paint() {
520            c.dispatch(&paint);
521        }
522    }
523}
524
525impl PaintVarRotateAroundCenter<'_> {
526    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
527        if let Ok(paint) = self.paint() {
528            c.dispatch(&paint);
529            c.add_variation_indices(self.var_index_base(), 3);
530        }
531    }
532}
533
534impl PaintSkew<'_> {
535    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
536        if let Ok(paint) = self.paint() {
537            c.dispatch(&paint);
538        }
539    }
540}
541
542impl PaintVarSkew<'_> {
543    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
544        if let Ok(paint) = self.paint() {
545            c.dispatch(&paint);
546            c.add_variation_indices(self.var_index_base(), 2);
547        }
548    }
549}
550
551impl PaintSkewAroundCenter<'_> {
552    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
553        if let Ok(paint) = self.paint() {
554            c.dispatch(&paint);
555        }
556    }
557}
558
559impl PaintVarSkewAroundCenter<'_> {
560    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
561        if let Ok(paint) = self.paint() {
562            c.dispatch(&paint);
563            c.add_variation_indices(self.var_index_base(), 4);
564        }
565    }
566}
567
568impl PaintComposite<'_> {
569    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
570        if let Ok(source_paint) = self.source_paint() {
571            c.dispatch(&source_paint);
572        }
573
574        if let Ok(backdrop_paint) = self.backdrop_paint() {
575            c.dispatch(&backdrop_paint);
576        }
577    }
578}
579
580impl Clip {
581    fn v1_closure(&self, c: &mut Colrv1ClosureContext, clip_list: &ClipList) {
582        let Ok(clip_box) = self.clip_box(clip_list.offset_data()) else {
583            return;
584        };
585        //TODO: replace below code when we have intersects(Range) available for int-set
586        let mut included_gids = IntSet::empty();
587        let start_id = GlyphId::from(self.start_glyph_id());
588        let end_id = GlyphId::from(self.end_glyph_id());
589        included_gids.insert_range(start_id..=end_id);
590        included_gids.intersect(&c.glyph_set);
591
592        if included_gids.is_empty() {
593            return;
594        }
595        clip_box.v1_closure(c);
596    }
597}
598
599impl ClipBox<'_> {
600    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
601        if let Self::Format2(item) = self {
602            item.v1_closure(c)
603        }
604    }
605}
606
607impl ClipBoxFormat2<'_> {
608    fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
609        c.add_variation_indices(self.var_index_base(), 4);
610    }
611}
612
613#[cfg(test)]
614mod tests {
615    use super::*;
616    use crate::{FontRef, GlyphId, TableProvider};
617
618    #[test]
619    fn test_colr_v0_closure() {
620        let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
621        let colr = font.colr().unwrap();
622
623        let mut input_glyph_set = IntSet::empty();
624        input_glyph_set.insert(GlyphId::new(168));
625
626        let mut glyph_set_colred = IntSet::empty();
627
628        colr.v0_closure_glyphs(&input_glyph_set, &mut glyph_set_colred);
629        assert_eq!(glyph_set_colred.len(), 9);
630        assert!(glyph_set_colred.contains(GlyphId::new(5)));
631        assert!(glyph_set_colred.contains(GlyphId::new(168)));
632        assert!(glyph_set_colred.contains(GlyphId::new(170)));
633        assert!(glyph_set_colred.contains(GlyphId::new(171)));
634        assert!(glyph_set_colred.contains(GlyphId::new(172)));
635        assert!(glyph_set_colred.contains(GlyphId::new(173)));
636        assert!(glyph_set_colred.contains(GlyphId::new(174)));
637        assert!(glyph_set_colred.contains(GlyphId::new(175)));
638        assert!(glyph_set_colred.contains(GlyphId::new(176)));
639
640        let mut palette_indices = IntSet::empty();
641        colr.v0_closure_palette_indices(&glyph_set_colred, &mut palette_indices);
642        assert_eq!(palette_indices.len(), 8);
643        assert!(palette_indices.contains(0));
644        assert!(palette_indices.contains(1));
645        assert!(palette_indices.contains(2));
646        assert!(palette_indices.contains(3));
647        assert!(palette_indices.contains(4));
648        assert!(palette_indices.contains(5));
649        assert!(palette_indices.contains(6));
650        assert!(palette_indices.contains(10));
651    }
652
653    #[test]
654    fn test_colr_v0_closure_not_found() {
655        let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
656        let colr = font.colr().unwrap();
657
658        let mut input_glyph_set = IntSet::empty();
659        input_glyph_set.insert(GlyphId::new(8));
660
661        let mut glyph_set_colred = IntSet::empty();
662
663        colr.v0_closure_glyphs(&input_glyph_set, &mut glyph_set_colred);
664        assert_eq!(glyph_set_colred.len(), 1);
665        assert!(glyph_set_colred.contains(GlyphId::new(8)));
666    }
667
668    #[test]
669    fn test_colr_v1_closure_no_var() {
670        let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
671        let colr = font.colr().unwrap();
672
673        let mut glyph_set = IntSet::empty();
674        glyph_set.insert(GlyphId::new(220));
675        glyph_set.insert(GlyphId::new(120));
676
677        let mut layer_indices = IntSet::empty();
678        let mut palette_indices = IntSet::empty();
679        let mut variation_indices = IntSet::empty();
680        let mut delta_set_indices = IntSet::empty();
681
682        colr.v1_closure(
683            &mut glyph_set,
684            &mut layer_indices,
685            &mut palette_indices,
686            &mut variation_indices,
687            &mut delta_set_indices,
688        );
689
690        assert_eq!(glyph_set.len(), 6);
691        assert!(glyph_set.contains(GlyphId::new(6)));
692        assert!(glyph_set.contains(GlyphId::new(7)));
693        assert!(glyph_set.contains(GlyphId::new(220)));
694        assert!(glyph_set.contains(GlyphId::new(3)));
695        assert!(glyph_set.contains(GlyphId::new(2)));
696        assert!(glyph_set.contains(GlyphId::new(120)));
697
698        assert_eq!(palette_indices.len(), 5);
699        assert!(palette_indices.contains(0));
700        assert!(palette_indices.contains(4));
701        assert!(palette_indices.contains(10));
702        assert!(palette_indices.contains(11));
703        assert!(palette_indices.contains(12));
704
705        assert_eq!(layer_indices.len(), 2);
706        assert!(layer_indices.contains(0));
707        assert!(layer_indices.contains(1));
708
709        assert!(variation_indices.is_empty());
710        assert!(delta_set_indices.is_empty());
711    }
712
713    #[test]
714    fn test_colr_v1_closure_w_var() {
715        let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
716        let colr = font.colr().unwrap();
717
718        let mut glyph_set = IntSet::empty();
719        glyph_set.insert(GlyphId::new(109));
720
721        let mut layer_indices = IntSet::empty();
722        let mut palette_indices = IntSet::empty();
723        let mut variation_indices = IntSet::empty();
724        let mut delta_set_indices = IntSet::empty();
725
726        colr.v1_closure(
727            &mut glyph_set,
728            &mut layer_indices,
729            &mut palette_indices,
730            &mut variation_indices,
731            &mut delta_set_indices,
732        );
733
734        assert_eq!(glyph_set.len(), 2);
735        assert!(glyph_set.contains(GlyphId::new(3)));
736        assert!(glyph_set.contains(GlyphId::new(109)));
737
738        assert_eq!(palette_indices.len(), 2);
739        assert!(palette_indices.contains(1));
740        assert!(palette_indices.contains(4));
741
742        assert!(layer_indices.is_empty());
743
744        assert_eq!(delta_set_indices.len(), 6);
745        assert!(delta_set_indices.contains(51));
746        assert!(delta_set_indices.contains(52));
747        assert!(delta_set_indices.contains(53));
748        assert!(delta_set_indices.contains(54));
749        assert!(delta_set_indices.contains(55));
750        assert!(delta_set_indices.contains(56));
751
752        assert_eq!(variation_indices.len(), 6);
753        assert!(variation_indices.contains(0x160000_u32));
754        assert!(variation_indices.contains(0x170000_u32));
755        assert!(variation_indices.contains(0x180000_u32));
756        assert!(variation_indices.contains(0x190000_u32));
757        assert!(variation_indices.contains(0x1A0000_u32));
758        assert!(variation_indices.contains(0x1B0000_u32));
759    }
760}