Skip to main content

read_fonts/tables/colr/
closure.rs

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