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