1use 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 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 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 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 let Some(last_var_index) = compute_inclusive_end(var_index_base, num_vars as u32) else {
243 return;
244 };
245 self.variation_indices
246 .insert_range(var_index_base..=last_var_index);
247 }
248
249 fn add_glyph_id(&mut self, gid: GlyphId16) {
250 self.glyph_set.insert(GlyphId::from(gid));
251 }
252}
253
254impl ColorStop {
255 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
256 c.add_palette_index(self.palette_index());
257 }
258}
259
260impl VarColorStop {
261 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
262 c.add_palette_index(self.palette_index());
263 c.add_variation_indices(self.var_index_base(), 2);
264 }
265}
266
267impl ColorLine<'_> {
268 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
269 for colorstop in self.color_stops() {
270 colorstop.v1_closure(c);
271 }
272 }
273}
274
275impl VarColorLine<'_> {
276 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
277 for var_colorstop in self.color_stops() {
278 var_colorstop.v1_closure(c);
279 }
280 }
281}
282
283impl Paint<'_> {
284 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
285 match self {
286 Self::ColrLayers(item) => item.v1_closure(c),
287 Self::Solid(item) => item.v1_closure(c),
288 Self::VarSolid(item) => item.v1_closure(c),
289 Self::LinearGradient(item) => item.v1_closure(c),
290 Self::VarLinearGradient(item) => item.v1_closure(c),
291 Self::RadialGradient(item) => item.v1_closure(c),
292 Self::VarRadialGradient(item) => item.v1_closure(c),
293 Self::SweepGradient(item) => item.v1_closure(c),
294 Self::VarSweepGradient(item) => item.v1_closure(c),
295 Self::Glyph(item) => item.v1_closure(c),
296 Self::ColrGlyph(item) => item.v1_closure(c),
297 Self::Transform(item) => item.v1_closure(c),
298 Self::VarTransform(item) => item.v1_closure(c),
299 Self::Translate(item) => item.v1_closure(c),
300 Self::VarTranslate(item) => item.v1_closure(c),
301 Self::Scale(item) => item.v1_closure(c),
302 Self::VarScale(item) => item.v1_closure(c),
303 Self::ScaleAroundCenter(item) => item.v1_closure(c),
304 Self::VarScaleAroundCenter(item) => item.v1_closure(c),
305 Self::ScaleUniform(item) => item.v1_closure(c),
306 Self::VarScaleUniform(item) => item.v1_closure(c),
307 Self::ScaleUniformAroundCenter(item) => item.v1_closure(c),
308 Self::VarScaleUniformAroundCenter(item) => item.v1_closure(c),
309 Self::Rotate(item) => item.v1_closure(c),
310 Self::VarRotate(item) => item.v1_closure(c),
311 Self::RotateAroundCenter(item) => item.v1_closure(c),
312 Self::VarRotateAroundCenter(item) => item.v1_closure(c),
313 Self::Skew(item) => item.v1_closure(c),
314 Self::VarSkew(item) => item.v1_closure(c),
315 Self::SkewAroundCenter(item) => item.v1_closure(c),
316 Self::VarSkewAroundCenter(item) => item.v1_closure(c),
317 Self::Composite(item) => item.v1_closure(c),
318 }
319 }
320}
321
322impl PaintColrLayers<'_> {
323 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
324 let num_layers = self.num_layers();
325 if num_layers == 0 {
326 return;
327 }
328
329 let Some(Ok(layer_list)) = c.colr.layer_list() else {
330 return;
331 };
332
333 let first_layer_index = self.first_layer_index();
334 let Some(last_layer_index) = compute_inclusive_end(first_layer_index, num_layers as u32)
335 else {
336 return;
337 };
338
339 let offset_data = layer_list.offset_data();
340 let paint_offsets = layer_list.paint_offsets();
341 for layer_index in first_layer_index..=last_layer_index {
342 let Some(paint_offset) = paint_offsets.get(layer_index as usize) else {
343 break;
344 };
345 if let Ok(paint) = paint_offset.get().resolve::<Paint>(offset_data) {
346 c.add_layer_index(layer_index);
347 c.dispatch(&paint);
348 }
349 }
350 }
351}
352
353impl PaintSolid<'_> {
354 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
355 c.add_palette_index(self.palette_index());
356 }
357}
358
359impl PaintVarSolid<'_> {
360 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
361 c.add_palette_index(self.palette_index());
362 c.add_variation_indices(self.var_index_base(), 1);
363 }
364}
365
366impl PaintLinearGradient<'_> {
367 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
368 if let Ok(colorline) = self.color_line() {
369 colorline.v1_closure(c);
370 }
371 }
372}
373
374impl PaintVarLinearGradient<'_> {
375 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
376 if let Ok(var_colorline) = self.color_line() {
377 var_colorline.v1_closure(c);
378 c.add_variation_indices(self.var_index_base(), 6);
379 }
380 }
381}
382
383impl PaintRadialGradient<'_> {
384 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
385 if let Ok(colorline) = self.color_line() {
386 colorline.v1_closure(c);
387 }
388 }
389}
390
391impl PaintVarRadialGradient<'_> {
392 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
393 if let Ok(var_colorline) = self.color_line() {
394 var_colorline.v1_closure(c);
395 c.add_variation_indices(self.var_index_base(), 6);
396 }
397 }
398}
399
400impl PaintSweepGradient<'_> {
401 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
402 if let Ok(colorline) = self.color_line() {
403 colorline.v1_closure(c);
404 }
405 }
406}
407
408impl PaintVarSweepGradient<'_> {
409 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
410 if let Ok(var_colorline) = self.color_line() {
411 var_colorline.v1_closure(c);
412 c.add_variation_indices(self.var_index_base(), 4);
413 }
414 }
415}
416
417impl PaintGlyph<'_> {
418 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
419 if let Ok(paint) = self.paint() {
420 c.add_glyph_id(self.glyph_id());
421 c.dispatch(&paint);
422 }
423 }
424}
425
426impl PaintColrGlyph<'_> {
427 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
428 let glyph_id = self.glyph_id();
429 let Some(Ok(list)) = c.colr.base_glyph_list() else {
430 return;
431 };
432 let records = list.base_glyph_paint_records();
433 let record = match records.binary_search_by(|rec| rec.glyph_id().cmp(&glyph_id)) {
434 Ok(ix) => &records[ix],
435 _ => return,
436 };
437 if let Ok(paint) = record.paint(list.offset_data()) {
438 c.add_glyph_id(glyph_id);
439 c.dispatch(&paint);
440 }
441 }
442}
443
444impl PaintTransform<'_> {
445 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
446 if let Ok(paint) = self.paint() {
447 c.dispatch(&paint);
448 }
449 }
450}
451
452impl VarAffine2x3<'_> {
453 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
454 c.add_variation_indices(self.var_index_base(), 6);
455 }
456}
457
458impl PaintVarTransform<'_> {
459 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
460 if let Ok(paint) = self.paint() {
461 if let Ok(affine2x3) = self.transform() {
462 c.dispatch(&paint);
463 affine2x3.v1_closure(c);
464 }
465 }
466 }
467}
468
469impl PaintTranslate<'_> {
470 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
471 if let Ok(paint) = self.paint() {
472 c.dispatch(&paint);
473 }
474 }
475}
476
477impl PaintVarTranslate<'_> {
478 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
479 if let Ok(paint) = self.paint() {
480 c.dispatch(&paint);
481 c.add_variation_indices(self.var_index_base(), 2);
482 }
483 }
484}
485
486impl PaintScale<'_> {
487 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
488 if let Ok(paint) = self.paint() {
489 c.dispatch(&paint);
490 }
491 }
492}
493
494impl PaintVarScale<'_> {
495 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
496 if let Ok(paint) = self.paint() {
497 c.dispatch(&paint);
498 c.add_variation_indices(self.var_index_base(), 2);
499 }
500 }
501}
502
503impl PaintScaleAroundCenter<'_> {
504 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
505 if let Ok(paint) = self.paint() {
506 c.dispatch(&paint);
507 }
508 }
509}
510
511impl PaintVarScaleAroundCenter<'_> {
512 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
513 if let Ok(paint) = self.paint() {
514 c.dispatch(&paint);
515 c.add_variation_indices(self.var_index_base(), 4);
516 }
517 }
518}
519
520impl PaintScaleUniform<'_> {
521 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
522 if let Ok(paint) = self.paint() {
523 c.dispatch(&paint);
524 }
525 }
526}
527
528impl PaintVarScaleUniform<'_> {
529 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
530 if let Ok(paint) = self.paint() {
531 c.dispatch(&paint);
532 c.add_variation_indices(self.var_index_base(), 1);
533 }
534 }
535}
536
537impl PaintScaleUniformAroundCenter<'_> {
538 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
539 if let Ok(paint) = self.paint() {
540 c.dispatch(&paint);
541 }
542 }
543}
544
545impl PaintVarScaleUniformAroundCenter<'_> {
546 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
547 if let Ok(paint) = self.paint() {
548 c.dispatch(&paint);
549 c.add_variation_indices(self.var_index_base(), 3);
550 }
551 }
552}
553
554impl PaintRotate<'_> {
555 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
556 if let Ok(paint) = self.paint() {
557 c.dispatch(&paint);
558 }
559 }
560}
561
562impl PaintVarRotate<'_> {
563 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
564 if let Ok(paint) = self.paint() {
565 c.dispatch(&paint);
566 c.add_variation_indices(self.var_index_base(), 1);
567 }
568 }
569}
570
571impl PaintRotateAroundCenter<'_> {
572 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
573 if let Ok(paint) = self.paint() {
574 c.dispatch(&paint);
575 }
576 }
577}
578
579impl PaintVarRotateAroundCenter<'_> {
580 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
581 if let Ok(paint) = self.paint() {
582 c.dispatch(&paint);
583 c.add_variation_indices(self.var_index_base(), 3);
584 }
585 }
586}
587
588impl PaintSkew<'_> {
589 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
590 if let Ok(paint) = self.paint() {
591 c.dispatch(&paint);
592 }
593 }
594}
595
596impl PaintVarSkew<'_> {
597 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
598 if let Ok(paint) = self.paint() {
599 c.dispatch(&paint);
600 c.add_variation_indices(self.var_index_base(), 2);
601 }
602 }
603}
604
605impl PaintSkewAroundCenter<'_> {
606 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
607 if let Ok(paint) = self.paint() {
608 c.dispatch(&paint);
609 }
610 }
611}
612
613impl PaintVarSkewAroundCenter<'_> {
614 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
615 if let Ok(paint) = self.paint() {
616 c.dispatch(&paint);
617 c.add_variation_indices(self.var_index_base(), 4);
618 }
619 }
620}
621
622impl PaintComposite<'_> {
623 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
624 if let Ok(source_paint) = self.source_paint() {
625 c.dispatch(&source_paint);
626 }
627
628 if let Ok(backdrop_paint) = self.backdrop_paint() {
629 c.dispatch(&backdrop_paint);
630 }
631 }
632}
633
634impl Clip {
635 fn v1_closure(&self, c: &mut Colrv1ClosureContext, clip_list: &ClipList) {
636 let Ok(clip_box) = self.clip_box(clip_list.offset_data()) else {
637 return;
638 };
639 let start_id = GlyphId::from(self.start_glyph_id());
640 let end_id = GlyphId::from(self.end_glyph_id());
641 if c.glyph_set.intersects_range(start_id..=end_id) {
642 clip_box.v1_closure(c);
643 }
644 }
645}
646
647impl ClipBox<'_> {
648 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
649 if let Self::Format2(item) = self {
650 item.v1_closure(c)
651 }
652 }
653}
654
655impl ClipBoxFormat2<'_> {
656 fn v1_closure(&self, c: &mut Colrv1ClosureContext) {
657 c.add_variation_indices(self.var_index_base(), 4);
658 }
659}
660
661fn compute_inclusive_end(start: u32, len: u32) -> Option<u32> {
664 if len == 0 {
665 return None;
666 }
667 start.checked_add(len).and_then(|v| v.checked_sub(1))
668}
669
670#[cfg(test)]
671mod tests {
672 use super::*;
673 use crate::{FontRef, GlyphId, TableProvider};
674
675 #[test]
676 fn test_colr_v0_closure() {
677 let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
678 let colr = font.colr().unwrap();
679
680 let mut input_glyph_set = IntSet::empty();
681 input_glyph_set.insert(GlyphId::new(168));
682
683 let mut glyph_set_colred = IntSet::empty();
684
685 colr.v0_closure_glyphs(&input_glyph_set, &mut glyph_set_colred);
686 assert_eq!(glyph_set_colred.len(), 9);
687 assert!(glyph_set_colred.contains(GlyphId::new(5)));
688 assert!(glyph_set_colred.contains(GlyphId::new(168)));
689 assert!(glyph_set_colred.contains(GlyphId::new(170)));
690 assert!(glyph_set_colred.contains(GlyphId::new(171)));
691 assert!(glyph_set_colred.contains(GlyphId::new(172)));
692 assert!(glyph_set_colred.contains(GlyphId::new(173)));
693 assert!(glyph_set_colred.contains(GlyphId::new(174)));
694 assert!(glyph_set_colred.contains(GlyphId::new(175)));
695 assert!(glyph_set_colred.contains(GlyphId::new(176)));
696
697 let mut palette_indices = IntSet::empty();
698 colr.v0_closure_palette_indices(&glyph_set_colred, &mut palette_indices);
699 assert_eq!(palette_indices.len(), 8);
700 assert!(palette_indices.contains(0));
701 assert!(palette_indices.contains(1));
702 assert!(palette_indices.contains(2));
703 assert!(palette_indices.contains(3));
704 assert!(palette_indices.contains(4));
705 assert!(palette_indices.contains(5));
706 assert!(palette_indices.contains(6));
707 assert!(palette_indices.contains(10));
708 }
709
710 #[test]
711 fn test_colr_v0_closure_not_found() {
712 let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
713 let colr = font.colr().unwrap();
714
715 let mut input_glyph_set = IntSet::empty();
716 input_glyph_set.insert(GlyphId::new(8));
717
718 let mut glyph_set_colred = IntSet::empty();
719
720 colr.v0_closure_glyphs(&input_glyph_set, &mut glyph_set_colred);
721 assert_eq!(glyph_set_colred.len(), 1);
722 assert!(glyph_set_colred.contains(GlyphId::new(8)));
723 }
724
725 #[test]
726 fn test_colr_v1_closure_no_var() {
727 let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
728 let colr = font.colr().unwrap();
729
730 let mut glyph_set = IntSet::empty();
731 glyph_set.insert(GlyphId::new(220));
732 glyph_set.insert(GlyphId::new(120));
733
734 let mut layer_indices = IntSet::empty();
735 let mut palette_indices = IntSet::empty();
736 let mut variation_indices = IntSet::empty();
737
738 colr.v1_closure(
739 &mut glyph_set,
740 &mut layer_indices,
741 &mut palette_indices,
742 &mut variation_indices,
743 );
744
745 assert_eq!(glyph_set.len(), 6);
746 assert!(glyph_set.contains(GlyphId::new(6)));
747 assert!(glyph_set.contains(GlyphId::new(7)));
748 assert!(glyph_set.contains(GlyphId::new(220)));
749 assert!(glyph_set.contains(GlyphId::new(3)));
750 assert!(glyph_set.contains(GlyphId::new(2)));
751 assert!(glyph_set.contains(GlyphId::new(120)));
752
753 assert_eq!(palette_indices.len(), 5);
754 assert!(palette_indices.contains(0));
755 assert!(palette_indices.contains(4));
756 assert!(palette_indices.contains(10));
757 assert!(palette_indices.contains(11));
758 assert!(palette_indices.contains(12));
759
760 assert_eq!(layer_indices.len(), 2);
761 assert!(layer_indices.contains(0));
762 assert!(layer_indices.contains(1));
763
764 assert!(variation_indices.is_empty());
765 }
766
767 #[test]
768 fn test_colr_v1_closure_w_var() {
769 let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
770 let colr = font.colr().unwrap();
771
772 let mut glyph_set = IntSet::empty();
773 glyph_set.insert(GlyphId::new(109));
774
775 let mut layer_indices = IntSet::empty();
776 let mut palette_indices = IntSet::empty();
777 let mut variation_indices = IntSet::empty();
778
779 colr.v1_closure(
780 &mut glyph_set,
781 &mut layer_indices,
782 &mut palette_indices,
783 &mut variation_indices,
784 );
785
786 assert_eq!(glyph_set.len(), 2);
787 assert!(glyph_set.contains(GlyphId::new(3)));
788 assert!(glyph_set.contains(GlyphId::new(109)));
789
790 assert_eq!(palette_indices.len(), 2);
791 assert!(palette_indices.contains(1));
792 assert!(palette_indices.contains(4));
793
794 assert!(layer_indices.is_empty());
795
796 assert_eq!(variation_indices.len(), 6);
797 assert!(variation_indices.contains(51));
798 assert!(variation_indices.contains(52));
799 assert!(variation_indices.contains(53));
800 assert!(variation_indices.contains(54));
801 assert!(variation_indices.contains(55));
802 assert!(variation_indices.contains(56));
803 }
804
805 #[test]
806 fn test_compute_inclusive_end() {
807 assert_eq!(compute_inclusive_end(0, 0), None);
808 assert_eq!(compute_inclusive_end(0, 1), Some(0));
809 assert_eq!(compute_inclusive_end(0, 2), Some(1));
810 assert_eq!(compute_inclusive_end(1, 1), Some(1));
811 assert_eq!(compute_inclusive_end(1, 2), Some(2));
812 assert_eq!(compute_inclusive_end(u32::MAX, 0), None);
813 assert_eq!(compute_inclusive_end(u32::MAX, 1), None);
814 assert_eq!(compute_inclusive_end(u32::MAX - 1, 1), Some(u32::MAX - 1));
815 assert_eq!(compute_inclusive_end(u32::MAX - 1, 3), None);
816 }
817}