1use super::{traversal::ColorStopVec, Brush, Error};
4use crate::{
5 model::font::ColrTables,
6 tables::{
7 colr::*,
8 variations::{DeltaSetIndex, NO_VARIATION_INDEX},
9 },
10 types::{BoundingBox, F2Dot14, F48Dot16, FWord, Fixed, GlyphId, GlyphId16, Matrix, Point},
11};
12use core::ops::{Deref, Range};
13
14#[cfg(feature = "libm")]
15#[allow(unused_imports)]
16use core_maths::CoreFloat;
17
18pub(super) type PaintId = usize;
20
21#[derive(Clone, Copy)]
26pub(super) struct ColrInstance<'a> {
27 tables: &'a ColrTables<'a>,
28 coords: &'a [F2Dot14],
29 scalars: &'a [Fixed],
32}
33
34impl<'a> ColrInstance<'a> {
35 pub(super) fn new(
39 tables: &'a ColrTables<'a>,
40 coords: &'a [F2Dot14],
41 scalars: &'a [Fixed],
42 ) -> Self {
43 Self {
44 tables,
45 coords,
46 scalars,
47 }
48 }
49
50 fn var_deltas<const N: usize>(&self, var_index_base: u32) -> [F48Dot16; N] {
53 let mut deltas = [F48Dot16::ZERO; N];
57 let Some(var_store) = self.tables.var_store.as_ref() else {
58 return deltas;
59 };
60 if self.coords.is_empty() || var_index_base == NO_VARIATION_INDEX {
61 return deltas;
62 }
63 let actual_count = ((u32::MAX - var_index_base) as usize).min(N);
65 if let Some(index_map) = self.tables.index_map.as_ref() {
66 for (i, delta) in deltas.iter_mut().enumerate().take(actual_count) {
67 let var_index = var_index_base + i as u32;
68 if let Ok(delta_ix) = index_map.get(var_index) {
69 *delta = var_store
70 .compute_delta_with_scalars(delta_ix, self.coords, self.scalars)
71 .unwrap_or_default();
72 }
73 }
74 } else {
75 for (i, delta) in deltas.iter_mut().enumerate().take(actual_count) {
76 let var_index = var_index_base + i as u32;
77 let delta_ix = DeltaSetIndex {
81 outer: (var_index >> 16) as u16,
82 inner: var_index as u16,
83 };
84 *delta = var_store
85 .compute_delta_with_scalars(delta_ix, self.coords, self.scalars)
86 .unwrap_or_default();
87 }
88 }
89 deltas
90 }
91}
92
93impl<'a> Deref for ColrInstance<'a> {
94 type Target = Colr<'a>;
95
96 fn deref(&self) -> &Self::Target {
97 &self.tables.colr
98 }
99}
100
101pub(super) fn resolve_clip_box(instance: &ColrInstance, clip_box: &ClipBox) -> BoundingBox<f32> {
103 match clip_box {
104 ClipBox::Format1(clip_box) => BoundingBox {
105 x_min: clip_box.x_min().to_i16() as f32,
106 y_min: clip_box.y_min().to_i16() as f32,
107 x_max: clip_box.x_max().to_i16() as f32,
108 y_max: clip_box.y_max().to_i16() as f32,
109 },
110 ClipBox::Format2(clip_box) => {
111 let deltas = instance.var_deltas::<4>(clip_box.var_index_base());
112 BoundingBox {
113 x_min: clip_box.x_min().apply_delta(deltas[0]),
114 y_min: clip_box.y_min().apply_delta(deltas[1]),
115 x_max: clip_box.x_max().apply_delta(deltas[2]),
116 y_max: clip_box.y_max().apply_delta(deltas[3]),
117 }
118 }
119 }
120}
121
122pub(super) fn resolve_clip_box_for_extents(
126 instance: &ColrInstance,
127 clip_box: &ClipBox,
128) -> BoundingBox<f32> {
129 match clip_box {
130 ClipBox::Format1(_) => resolve_clip_box(instance, clip_box),
131 ClipBox::Format2(clip_box) => {
132 let deltas = instance.var_deltas::<4>(clip_box.var_index_base());
133 let edge =
134 |value: FWord, delta: F48Dot16| value.to_i16() as f32 + delta.to_f32().round();
135 BoundingBox {
136 x_min: edge(clip_box.x_min(), deltas[0]),
137 y_min: edge(clip_box.y_min(), deltas[1]),
138 x_max: edge(clip_box.x_max(), deltas[2]),
139 y_max: edge(clip_box.y_max(), deltas[3]),
140 }
141 }
142 }
143}
144
145#[derive(Clone, Debug)]
147pub(super) struct ResolvedColorStop {
148 pub offset: f32,
149 pub palette_index: u16,
150 pub alpha: f32,
151}
152
153#[derive(Clone)]
158pub(super) struct ColorStops<'a> {
159 stops: &'a [ColorStop],
160 var_stops: &'a [VarColorStop],
161}
162
163impl<'a> From<ColorLine<'a>> for ColorStops<'a> {
164 fn from(value: ColorLine<'a>) -> Self {
165 Self {
166 stops: value.color_stops(),
167 var_stops: &[],
168 }
169 }
170}
171
172impl<'a> From<VarColorLine<'a>> for ColorStops<'a> {
173 fn from(value: VarColorLine<'a>) -> Self {
174 Self {
175 stops: &[],
176 var_stops: value.color_stops(),
177 }
178 }
179}
180
181impl<'a> ColorStops<'a> {
182 pub(super) fn resolve(
185 &self,
186 instance: &'a ColrInstance<'a>,
187 ) -> impl Iterator<Item = ResolvedColorStop> + 'a {
188 self.stops
189 .iter()
190 .map(|stop| ResolvedColorStop {
191 offset: stop.stop_offset().to_f32(),
192 palette_index: stop.palette_index(),
193 alpha: stop.alpha().to_f32(),
194 })
195 .chain(self.var_stops.iter().map(|stop| {
196 let deltas = instance.var_deltas::<2>(stop.var_index_base());
197 ResolvedColorStop {
198 offset: stop.stop_offset().apply_delta(deltas[0]),
199 palette_index: stop.palette_index(),
200 alpha: stop.alpha().apply_delta(deltas[1]),
201 }
202 }))
203 }
204}
205
206pub(super) enum MaybeBrush<'a> {
209 Some(Brush<'a>),
210 NonRendering,
212 None,
214}
215
216pub(super) enum ResolvedPaint<'a> {
221 ColrLayers {
222 range: Range<usize>,
223 },
224 Solid {
225 palette_index: u16,
226 alpha: f32,
227 },
228 LinearGradient {
229 x0: f32,
230 y0: f32,
231 x1: f32,
232 y1: f32,
233 x2: f32,
234 y2: f32,
235 color_stops: ColorStops<'a>,
236 extend: Extend,
237 },
238 RadialGradient {
239 x0: f32,
240 y0: f32,
241 radius0: f32,
242 x1: f32,
243 y1: f32,
244 radius1: f32,
245 color_stops: ColorStops<'a>,
246 extend: Extend,
247 },
248 SweepGradient {
249 center_x: f32,
250 center_y: f32,
251 start_angle: f32,
252 end_angle: f32,
253 color_stops: ColorStops<'a>,
254 extend: Extend,
255 },
256 Glyph {
257 glyph: GlyphId,
258 paint: Paint<'a>,
259 },
260 ColrGlyph {
261 glyph: GlyphId16,
262 },
263 Transform {
264 xx: f32,
265 yx: f32,
266 xy: f32,
267 yy: f32,
268 dx: f32,
269 dy: f32,
270 paint: Paint<'a>,
271 },
272 Translate {
273 dx: f32,
274 dy: f32,
275 paint: Paint<'a>,
276 },
277 Scale {
278 scale_x: f32,
279 scale_y: f32,
280 around_center: Option<Point<f32>>,
281 paint: Paint<'a>,
282 },
283 Rotate {
284 angle: f32,
285 around_center: Option<Point<f32>>,
286 paint: Paint<'a>,
287 },
288 Skew {
289 x_skew_angle: f32,
290 y_skew_angle: f32,
291 around_center: Option<Point<f32>>,
292 paint: Paint<'a>,
293 },
294 Composite {
295 source_paint: Paint<'a>,
296 mode: CompositeMode,
297 backdrop_paint: Paint<'a>,
298 },
299}
300
301impl<'a> ResolvedPaint<'a> {
302 pub(super) fn as_transform(&self) -> Option<(Matrix<f32>, Paint<'a>)> {
303 match self {
304 ResolvedPaint::Rotate {
305 angle,
306 around_center,
307 paint,
308 } => {
309 let sin_v = (angle * 180.0).to_radians().sin();
310 let cos_v = (angle * 180.0).to_radians().cos();
311 let mut out_transform = Matrix {
312 xx: cos_v,
313 xy: -sin_v,
314 yx: sin_v,
315 yy: cos_v,
316 ..Default::default()
317 };
318
319 fn scalar_dot_product(a: f32, b: f32, c: f32, d: f32) -> f32 {
320 a * b + c * d
321 }
322
323 if let Some(center) = around_center {
324 out_transform.dx = scalar_dot_product(sin_v, center.y, 1.0 - cos_v, center.x);
325 out_transform.dy = scalar_dot_product(-sin_v, center.x, 1.0 - cos_v, center.y);
326 }
327 Some((out_transform, paint.clone()))
328 }
329 ResolvedPaint::Scale {
330 scale_x,
331 scale_y,
332 around_center,
333 paint,
334 } => {
335 let mut out_transform = Matrix {
336 xx: *scale_x,
337 yy: *scale_y,
338 ..Matrix::default()
339 };
340
341 if let Some(center) = around_center {
342 out_transform.dx = center.x - scale_x * center.x;
343 out_transform.dy = center.y - scale_y * center.y;
344 }
345 Some((out_transform, paint.clone()))
346 }
347 ResolvedPaint::Skew {
348 x_skew_angle,
349 y_skew_angle,
350 around_center,
351 paint,
352 } => {
353 let tan_x = (x_skew_angle * 180.0).to_radians().tan();
354 let tan_y = (y_skew_angle * 180.0).to_radians().tan();
355 let mut out_transform = Matrix {
356 xy: -tan_x,
357 yx: tan_y,
358 ..Matrix::default()
359 };
360
361 if let Some(center) = around_center {
362 out_transform.dx = tan_x * center.y;
363 out_transform.dy = -tan_y * center.x;
364 }
365 Some((out_transform, paint.clone()))
366 }
367 ResolvedPaint::Transform {
368 xx,
369 yx,
370 xy,
371 yy,
372 dx,
373 dy,
374 paint,
375 } => Some((
376 Matrix {
377 xx: *xx,
378 yx: *yx,
379 xy: *xy,
380 yy: *yy,
381 dx: *dx,
382 dy: *dy,
383 },
384 paint.clone(),
385 )),
386 ResolvedPaint::Translate { dx, dy, paint, .. } => Some((
387 Matrix {
388 dx: *dx,
389 dy: *dy,
390 ..Default::default()
391 },
392 paint.clone(),
393 )),
394 _ => None,
395 }
396 }
397
398 pub(super) fn as_brush<'s>(
399 &self,
400 instance: &ColrInstance,
401 resolved_stops: &'s mut ColorStopVec,
402 ) -> Result<MaybeBrush<'s>, Error> {
403 match self {
404 ResolvedPaint::Solid {
405 palette_index,
406 alpha,
407 } => Ok(MaybeBrush::Some(Brush::Solid {
408 palette_index: *palette_index,
409 alpha: *alpha,
410 })),
411 ResolvedPaint::LinearGradient {
412 x0,
413 y0,
414 x1,
415 y1,
416 x2,
417 y2,
418 color_stops,
419 extend,
420 } => {
421 let mut p0 = Point::new(*x0, *y0);
422 let p1 = Point::new(*x1, *y1);
423 let p2 = Point::new(*x2, *y2);
424
425 let dot_product = |a: Point<f32>, b: Point<f32>| -> f32 { a.x * b.x + a.y * b.y };
426 let cross_product = |a: Point<f32>, b: Point<f32>| -> f32 { a.x * b.y - a.y * b.x };
427 let project_onto = |vector: Point<f32>, point: Point<f32>| -> Point<f32> {
428 let length = (point.x * point.x + point.y * point.y).sqrt();
429 if length == 0.0 {
430 return Point::default();
431 }
432 let mut point_normalized = point / length;
433 point_normalized *= dot_product(vector, point) / length;
434 point_normalized
435 };
436
437 make_sorted_resolved_stops(color_stops, instance, resolved_stops);
438
439 if p1 == p0 || p2 == p0 || cross_product(p1 - p0, p2 - p0) == 0.0 {
444 if let Some(stop) = resolved_stops.first() {
445 return Ok(MaybeBrush::Some(Brush::Solid {
446 palette_index: stop.palette_index,
447 alpha: stop.alpha,
448 }));
449 };
450 return Ok(MaybeBrush::NonRendering);
451 }
452
453 let mut perpendicular_to_p2 = p2 - p0;
459 perpendicular_to_p2 = Point::new(perpendicular_to_p2.y, -perpendicular_to_p2.x);
460 let mut p3 = p0 + project_onto(p1 - p0, perpendicular_to_p2);
461
462 match (
463 resolved_stops.first().cloned(),
464 resolved_stops.last().cloned(),
465 ) {
466 (None, _) | (_, None) => {}
467 (Some(first_stop), Some(last_stop)) => {
468 let mut color_stop_range = last_stop.offset - first_stop.offset;
469
470 if color_stop_range == 0.0 && extend != &Extend::Pad {
472 return Ok(MaybeBrush::NonRendering);
473 }
474
475 if color_stop_range == 0.0 && extend == &Extend::Pad {
483 let mut extra_stop = last_stop;
484 extra_stop.offset += 1.0;
485 resolved_stops.push(extra_stop);
486
487 color_stop_range = 1.0;
488 }
489
490 debug_assert!(color_stop_range != 0.0);
491
492 if color_stop_range != 1.0 || first_stop.offset != 0.0 {
493 let p0_p3 = p3 - p0;
494 let p0_offset = p0_p3 * first_stop.offset;
495 let p3_offset = p0_p3 * last_stop.offset;
496
497 p3 = p0 + p3_offset;
498 p0 += p0_offset;
499
500 let scale_factor = 1.0 / color_stop_range;
501 let start_offset = first_stop.offset;
502
503 for stop in resolved_stops.iter_mut() {
504 stop.offset = (stop.offset - start_offset) * scale_factor;
505 }
506 }
507
508 return Ok(MaybeBrush::Some(Brush::LinearGradient {
509 p0,
510 p1: p3,
511 color_stops: resolved_stops.as_slice(),
512 extend: *extend,
513 }));
514 }
515 }
516
517 Ok(MaybeBrush::NonRendering)
518 }
519 ResolvedPaint::RadialGradient {
520 x0,
521 y0,
522 radius0,
523 x1,
524 y1,
525 radius1,
526 color_stops,
527 extend,
528 } => {
529 let mut c0 = Point::new(*x0, *y0);
530 let mut c1 = Point::new(*x1, *y1);
531 let mut radius0 = *radius0;
532 let mut radius1 = *radius1;
533
534 make_sorted_resolved_stops(color_stops, instance, resolved_stops);
535
536 match (
537 resolved_stops.first().cloned(),
538 resolved_stops.last().cloned(),
539 ) {
540 (None, _) | (_, None) => {}
541 (Some(first_stop), Some(last_stop)) => {
542 let mut color_stop_range = last_stop.offset - first_stop.offset;
543 if color_stop_range == 0.0 && extend != &Extend::Pad {
545 return Ok(MaybeBrush::NonRendering);
546 }
547
548 if color_stop_range == 0.0 && extend == &Extend::Pad {
552 let mut extra_stop = last_stop;
553 extra_stop.offset += 1.0;
554 resolved_stops.push(extra_stop);
555 color_stop_range = 1.0;
556 }
557
558 debug_assert!(color_stop_range != 0.0);
559
560 if color_stop_range != 1.0 || first_stop.offset != 0.0 {
565 let c0_to_c1 = c1 - c0;
566 let radius_diff = radius1 - radius0;
567 let scale_factor = 1.0 / color_stop_range;
568
569 let c0_offset = c0_to_c1 * first_stop.offset;
570 let c1_offset = c0_to_c1 * last_stop.offset;
571 let stops_start_offset = first_stop.offset;
572
573 c1 = c0 + c1_offset;
575 c0 += c0_offset;
576 radius1 = radius0 + radius_diff * last_stop.offset;
577 radius0 += radius_diff * first_stop.offset;
578
579 for stop in resolved_stops.iter_mut() {
580 stop.offset = (stop.offset - stops_start_offset) * scale_factor;
581 }
582 }
583
584 return Ok(MaybeBrush::Some(Brush::RadialGradient {
585 c0,
586 r0: radius0,
587 c1,
588 r1: radius1,
589 color_stops: resolved_stops.as_slice(),
590 extend: *extend,
591 }));
592 }
593 }
594 Ok(MaybeBrush::NonRendering)
595 }
596 ResolvedPaint::SweepGradient {
597 center_x,
598 center_y,
599 start_angle,
600 end_angle,
601 color_stops,
602 extend,
603 } => {
604 let sweep_angle_to_degrees = |angle| angle * 180.0 + 180.0;
607
608 let start_angle = sweep_angle_to_degrees(start_angle);
609 let end_angle = sweep_angle_to_degrees(end_angle);
610
611 let sector_angle = end_angle - start_angle;
614
615 make_sorted_resolved_stops(color_stops, instance, resolved_stops);
616 if resolved_stops.is_empty() {
617 return Ok(MaybeBrush::NonRendering);
618 }
619
620 match (
621 resolved_stops.first().cloned(),
622 resolved_stops.last().cloned(),
623 ) {
624 (None, _) | (_, None) => {}
625 (Some(first_stop), Some(last_stop)) => {
626 let mut color_stop_range = last_stop.offset - first_stop.offset;
627
628 let mut start_angle_scaled = start_angle + sector_angle * first_stop.offset;
629 let mut end_angle_scaled = start_angle + sector_angle * last_stop.offset;
630
631 let start_offset = first_stop.offset;
632
633 if color_stop_range == 0.0 && extend != &Extend::Pad {
635 return Ok(MaybeBrush::NonRendering);
636 }
637
638 if color_stop_range == 0.0 && extend == &Extend::Pad {
645 let mut offset_last = last_stop;
646 offset_last.offset += 1.0;
647 resolved_stops.push(offset_last);
648 color_stop_range = 1.0;
649 }
650
651 debug_assert!(color_stop_range != 0.0);
652
653 let scale_factor = 1.0 / color_stop_range;
654
655 for shift_stop in resolved_stops.iter_mut() {
656 shift_stop.offset = (shift_stop.offset - start_offset) * scale_factor;
657 }
658
659 start_angle_scaled = 360.0 - start_angle_scaled;
668 end_angle_scaled = 360.0 - end_angle_scaled;
669
670 if start_angle_scaled >= end_angle_scaled {
671 (start_angle_scaled, end_angle_scaled) =
672 (end_angle_scaled, start_angle_scaled);
673 resolved_stops.reverse();
674 for stop in resolved_stops.iter_mut() {
675 stop.offset = 1.0 - stop.offset;
676 }
677 }
678
679 if start_angle_scaled == end_angle_scaled && extend != &Extend::Pad {
683 return Ok(MaybeBrush::NonRendering);
684 }
685
686 return Ok(MaybeBrush::Some(Brush::SweepGradient {
687 c0: Point::new(*center_x, *center_y),
688 start_angle: start_angle_scaled,
689 end_angle: end_angle_scaled,
690 color_stops: resolved_stops.as_slice(),
691 extend: *extend,
692 }));
693 }
694 }
695 Ok(MaybeBrush::NonRendering)
696 }
697 _ => Ok(MaybeBrush::None),
698 }
699 }
700}
701
702fn make_sorted_resolved_stops(
703 stops: &ColorStops,
704 instance: &ColrInstance,
705 out_stops: &mut ColorStopVec,
706) {
707 let color_stop_iter = stops.resolve(instance).map(|stop| stop.into());
708 out_stops.clear();
709 for stop in color_stop_iter {
710 out_stops.push(stop);
711 }
712 out_stops.sort_by(|a, b| {
713 a.offset
714 .partial_cmp(&b.offset)
715 .unwrap_or(core::cmp::Ordering::Equal)
716 });
717}
718
719pub(super) fn resolve_paint<'a>(
730 instance: &ColrInstance<'a>,
731 paint: &Paint<'a>,
732) -> Result<ResolvedPaint<'a>, Error> {
733 Ok(match paint {
734 Paint::ColrLayers(layers) => {
735 let start = layers.first_layer_index() as usize;
738 let end = start
739 .checked_add(layers.num_layers() as usize)
740 .ok_or(Error::Malformed)?;
741 ResolvedPaint::ColrLayers { range: start..end }
742 }
743 Paint::Solid(solid) => ResolvedPaint::Solid {
744 palette_index: solid.palette_index(),
745 alpha: solid.alpha().to_f32(),
746 },
747 Paint::VarSolid(solid) => {
748 let deltas = instance.var_deltas::<1>(solid.var_index_base());
749 ResolvedPaint::Solid {
750 palette_index: solid.palette_index(),
751 alpha: solid.alpha().apply_delta(deltas[0]),
752 }
753 }
754 Paint::LinearGradient(gradient) => {
755 let color_line = gradient.color_line().map_err(|_| Error::Malformed)?;
756 let extend = color_line.extend();
757 ResolvedPaint::LinearGradient {
758 x0: gradient.x0().to_i16() as f32,
759 y0: gradient.y0().to_i16() as f32,
760 x1: gradient.x1().to_i16() as f32,
761 y1: gradient.y1().to_i16() as f32,
762 x2: gradient.x2().to_i16() as f32,
763 y2: gradient.y2().to_i16() as f32,
764 color_stops: color_line.into(),
765 extend,
766 }
767 }
768 Paint::VarLinearGradient(gradient) => {
769 let color_line = gradient.color_line().map_err(|_| Error::Malformed)?;
770 let extend = color_line.extend();
771 let deltas = instance.var_deltas::<6>(gradient.var_index_base());
772 ResolvedPaint::LinearGradient {
773 x0: gradient.x0().apply_delta(deltas[0]),
774 y0: gradient.y0().apply_delta(deltas[1]),
775 x1: gradient.x1().apply_delta(deltas[2]),
776 y1: gradient.y1().apply_delta(deltas[3]),
777 x2: gradient.x2().apply_delta(deltas[4]),
778 y2: gradient.y2().apply_delta(deltas[5]),
779 color_stops: color_line.into(),
780 extend,
781 }
782 }
783 Paint::RadialGradient(gradient) => {
784 let color_line = gradient.color_line().map_err(|_| Error::Malformed)?;
785 let extend = color_line.extend();
786 ResolvedPaint::RadialGradient {
787 x0: gradient.x0().to_i16() as f32,
788 y0: gradient.y0().to_i16() as f32,
789 radius0: gradient.radius0().to_u16() as f32,
790 x1: gradient.x1().to_i16() as f32,
791 y1: gradient.y1().to_i16() as f32,
792 radius1: gradient.radius1().to_u16() as f32,
793 color_stops: color_line.into(),
794 extend,
795 }
796 }
797 Paint::VarRadialGradient(gradient) => {
798 let color_line = gradient.color_line().map_err(|_| Error::Malformed)?;
799 let extend = color_line.extend();
800 let deltas = instance.var_deltas::<6>(gradient.var_index_base());
801 ResolvedPaint::RadialGradient {
802 x0: gradient.x0().apply_delta(deltas[0]),
803 y0: gradient.y0().apply_delta(deltas[1]),
804 radius0: gradient.radius0().apply_delta(deltas[2]),
805 x1: gradient.x1().apply_delta(deltas[3]),
806 y1: gradient.y1().apply_delta(deltas[4]),
807 radius1: gradient.radius1().apply_delta(deltas[5]),
808 color_stops: color_line.into(),
809 extend,
810 }
811 }
812 Paint::SweepGradient(gradient) => {
813 let color_line = gradient.color_line().map_err(|_| Error::Malformed)?;
814 let extend = color_line.extend();
815 ResolvedPaint::SweepGradient {
816 center_x: gradient.center_x().to_i16() as f32,
817 center_y: gradient.center_y().to_i16() as f32,
818 start_angle: gradient.start_angle().to_f32(),
819 end_angle: gradient.end_angle().to_f32(),
820 color_stops: color_line.into(),
821 extend,
822 }
823 }
824 Paint::VarSweepGradient(gradient) => {
825 let color_line = gradient.color_line().map_err(|_| Error::Malformed)?;
826 let extend = color_line.extend();
827 let deltas = instance.var_deltas::<4>(gradient.var_index_base());
828 ResolvedPaint::SweepGradient {
829 center_x: gradient.center_x().apply_delta(deltas[0]),
830 center_y: gradient.center_y().apply_delta(deltas[1]),
831 start_angle: gradient.start_angle().apply_delta(deltas[2]),
832 end_angle: gradient.end_angle().apply_delta(deltas[3]),
833 color_stops: color_line.into(),
834 extend,
835 }
836 }
837 Paint::Glyph(glyph) => ResolvedPaint::Glyph {
838 glyph: glyph.glyph_id().into(),
839 paint: glyph.paint().map_err(|_| Error::Malformed)?,
840 },
841 Paint::Glyph2(glyph) => ResolvedPaint::Glyph {
842 glyph: glyph.glyph_id().into(),
843 paint: glyph.paint().map_err(|_| Error::Malformed)?,
844 },
845 Paint::ColrGlyph(glyph) => ResolvedPaint::ColrGlyph {
846 glyph: glyph.glyph_id(),
847 },
848 Paint::Transform(transform) => {
849 let affine = transform.transform().map_err(|_| Error::Malformed)?;
850 let paint = transform.paint().map_err(|_| Error::Malformed)?;
851 ResolvedPaint::Transform {
852 xx: affine.xx().to_f32(),
853 yx: affine.yx().to_f32(),
854 xy: affine.xy().to_f32(),
855 yy: affine.yy().to_f32(),
856 dx: affine.dx().to_f32(),
857 dy: affine.dy().to_f32(),
858 paint,
859 }
860 }
861 Paint::VarTransform(transform) => {
862 let affine = transform.transform().map_err(|_| Error::Malformed)?;
863 let paint = transform.paint().map_err(|_| Error::Malformed)?;
864 let deltas = instance.var_deltas::<6>(affine.var_index_base());
865 ResolvedPaint::Transform {
866 xx: affine.xx().apply_delta(deltas[0]),
867 yx: affine.yx().apply_delta(deltas[1]),
868 xy: affine.xy().apply_delta(deltas[2]),
869 yy: affine.yy().apply_delta(deltas[3]),
870 dx: affine.dx().apply_delta(deltas[4]),
871 dy: affine.dy().apply_delta(deltas[5]),
872 paint,
873 }
874 }
875 Paint::Translate(transform) => ResolvedPaint::Translate {
876 dx: transform.dx().to_i16() as f32,
877 dy: transform.dy().to_i16() as f32,
878 paint: transform.paint().map_err(|_| Error::Malformed)?,
879 },
880 Paint::VarTranslate(transform) => {
881 let deltas = instance.var_deltas::<2>(transform.var_index_base());
882 ResolvedPaint::Translate {
883 dx: transform.dx().apply_delta(deltas[0]),
884 dy: transform.dy().apply_delta(deltas[1]),
885 paint: transform.paint().map_err(|_| Error::Malformed)?,
886 }
887 }
888 Paint::Scale(transform) => ResolvedPaint::Scale {
889 scale_x: transform.scale_x().to_f32(),
890 scale_y: transform.scale_y().to_f32(),
891 around_center: None,
892 paint: transform.paint().map_err(|_| Error::Malformed)?,
893 },
894 Paint::VarScale(transform) => {
895 let deltas = instance.var_deltas::<2>(transform.var_index_base());
896 ResolvedPaint::Scale {
897 scale_x: transform.scale_x().apply_delta(deltas[0]),
898 scale_y: transform.scale_y().apply_delta(deltas[1]),
899 around_center: None,
900 paint: transform.paint().map_err(|_| Error::Malformed)?,
901 }
902 }
903 Paint::ScaleAroundCenter(transform) => ResolvedPaint::Scale {
904 scale_x: transform.scale_x().to_f32(),
905 scale_y: transform.scale_y().to_f32(),
906 around_center: Some(Point::new(
907 transform.center_x().to_i16() as f32,
908 transform.center_y().to_i16() as f32,
909 )),
910 paint: transform.paint().map_err(|_| Error::Malformed)?,
911 },
912 Paint::VarScaleAroundCenter(transform) => {
913 let deltas = instance.var_deltas::<4>(transform.var_index_base());
914 ResolvedPaint::Scale {
915 scale_x: transform.scale_x().apply_delta(deltas[0]),
916 scale_y: transform.scale_y().apply_delta(deltas[1]),
917 around_center: Some(Point::new(
918 transform.center_x().apply_delta(deltas[2]),
919 transform.center_y().apply_delta(deltas[3]),
920 )),
921 paint: transform.paint().map_err(|_| Error::Malformed)?,
922 }
923 }
924 Paint::ScaleUniform(transform) => {
925 let scale = transform.scale().to_f32();
926 ResolvedPaint::Scale {
927 scale_x: scale,
928 scale_y: scale,
929 around_center: None,
930 paint: transform.paint().map_err(|_| Error::Malformed)?,
931 }
932 }
933 Paint::VarScaleUniform(transform) => {
934 let deltas = instance.var_deltas::<1>(transform.var_index_base());
935 let scale = transform.scale().apply_delta(deltas[0]);
936 ResolvedPaint::Scale {
937 scale_x: scale,
938 scale_y: scale,
939 around_center: None,
940 paint: transform.paint().map_err(|_| Error::Malformed)?,
941 }
942 }
943 Paint::ScaleUniformAroundCenter(transform) => {
944 let scale = transform.scale().to_f32();
945 ResolvedPaint::Scale {
946 scale_x: scale,
947 scale_y: scale,
948 around_center: Some(Point::new(
949 transform.center_x().to_i16() as f32,
950 transform.center_y().to_i16() as f32,
951 )),
952 paint: transform.paint().map_err(|_| Error::Malformed)?,
953 }
954 }
955 Paint::VarScaleUniformAroundCenter(transform) => {
956 let deltas = instance.var_deltas::<3>(transform.var_index_base());
957 let scale = transform.scale().apply_delta(deltas[0]);
958 ResolvedPaint::Scale {
959 scale_x: scale,
960 scale_y: scale,
961 around_center: Some(Point::new(
962 transform.center_x().apply_delta(deltas[1]),
963 transform.center_y().apply_delta(deltas[2]),
964 )),
965 paint: transform.paint().map_err(|_| Error::Malformed)?,
966 }
967 }
968 Paint::Rotate(transform) => ResolvedPaint::Rotate {
969 angle: transform.angle().to_f32(),
970 around_center: None,
971 paint: transform.paint().map_err(|_| Error::Malformed)?,
972 },
973 Paint::VarRotate(transform) => {
974 let deltas = instance.var_deltas::<1>(transform.var_index_base());
975 ResolvedPaint::Rotate {
976 angle: transform.angle().apply_delta(deltas[0]),
977 around_center: None,
978 paint: transform.paint().map_err(|_| Error::Malformed)?,
979 }
980 }
981 Paint::RotateAroundCenter(transform) => ResolvedPaint::Rotate {
982 angle: transform.angle().to_f32(),
983 around_center: Some(Point::new(
984 transform.center_x().to_i16() as f32,
985 transform.center_y().to_i16() as f32,
986 )),
987 paint: transform.paint().map_err(|_| Error::Malformed)?,
988 },
989 Paint::VarRotateAroundCenter(transform) => {
990 let deltas = instance.var_deltas::<3>(transform.var_index_base());
991 ResolvedPaint::Rotate {
992 angle: transform.angle().apply_delta(deltas[0]),
993 around_center: Some(Point::new(
994 transform.center_x().apply_delta(deltas[1]),
995 transform.center_y().apply_delta(deltas[2]),
996 )),
997 paint: transform.paint().map_err(|_| Error::Malformed)?,
998 }
999 }
1000 Paint::Skew(transform) => ResolvedPaint::Skew {
1001 x_skew_angle: transform.x_skew_angle().to_f32(),
1002 y_skew_angle: transform.y_skew_angle().to_f32(),
1003 around_center: None,
1004 paint: transform.paint().map_err(|_| Error::Malformed)?,
1005 },
1006 Paint::VarSkew(transform) => {
1007 let deltas = instance.var_deltas::<2>(transform.var_index_base());
1008 ResolvedPaint::Skew {
1009 x_skew_angle: transform.x_skew_angle().apply_delta(deltas[0]),
1010 y_skew_angle: transform.y_skew_angle().apply_delta(deltas[1]),
1011 around_center: None,
1012 paint: transform.paint().map_err(|_| Error::Malformed)?,
1013 }
1014 }
1015 Paint::SkewAroundCenter(transform) => ResolvedPaint::Skew {
1016 x_skew_angle: transform.x_skew_angle().to_f32(),
1017 y_skew_angle: transform.y_skew_angle().to_f32(),
1018 around_center: Some(Point::new(
1019 transform.center_x().to_i16() as f32,
1020 transform.center_y().to_i16() as f32,
1021 )),
1022 paint: transform.paint().map_err(|_| Error::Malformed)?,
1023 },
1024 Paint::VarSkewAroundCenter(transform) => {
1025 let deltas = instance.var_deltas::<4>(transform.var_index_base());
1026 ResolvedPaint::Skew {
1027 x_skew_angle: transform.x_skew_angle().apply_delta(deltas[0]),
1028 y_skew_angle: transform.y_skew_angle().apply_delta(deltas[1]),
1029 around_center: Some(Point::new(
1030 transform.center_x().apply_delta(deltas[2]),
1031 transform.center_y().apply_delta(deltas[3]),
1032 )),
1033 paint: transform.paint().map_err(|_| Error::Malformed)?,
1034 }
1035 }
1036 Paint::Composite(composite) => ResolvedPaint::Composite {
1037 source_paint: composite.source_paint().map_err(|_| Error::Malformed)?,
1038 mode: composite.composite_mode(),
1039 backdrop_paint: composite.backdrop_paint().map_err(|_| Error::Malformed)?,
1040 },
1041 })
1042}
1043
1044#[cfg(test)]
1045mod tests {
1046 use super::*;
1047 use crate::{FontRef, TableProvider};
1048
1049 #[test]
1053 fn var_delta_index_overflow() {
1054 let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap();
1055 let coords = &[F2Dot14::from_f32(0.5)];
1056 let tables = ColrTables::new(font.colr().unwrap());
1057 let instance = ColrInstance::new(&tables, coords, &[]);
1058 let _: [F48Dot16; 4] = instance.var_deltas(0xFFFFFFFE);
1060 }
1061
1062 #[test]
1068 fn cached_scalars_give_the_same_deltas() {
1069 use crate::model::{Font, NormalizedCoord};
1070 let font = Font::new(font_test_data::COLRV0V1_VARIABLE, 0).unwrap();
1071 let axes = font.axes().len() as usize;
1072 for value in [-1.0, -0.3, 0.6, 1.0] {
1073 let varied = font
1074 .instance_builder()
1075 .normalized_coords(core::iter::repeat_n(NormalizedCoord::from_f32(value), axes))
1076 .build();
1077 let tables = varied.colr_tables().unwrap();
1078 let scalars = varied.colr_scalars();
1079 assert!(!scalars.is_empty());
1080 let cached = ColrInstance::new(tables, varied.normalized_coords(), scalars);
1081 let computed = ColrInstance::new(tables, varied.normalized_coords(), &[]);
1082 for base in 0..512 {
1083 assert_eq!(
1084 cached.var_deltas::<4>(base),
1085 computed.var_deltas::<4>(base),
1086 "{value} {base}"
1087 );
1088 }
1089 }
1090 assert!(font.colr_scalars().is_empty());
1091 }
1092
1093 #[test]
1094 fn var_deltas_implicit_mapping_uses_outer_index() {
1095 use crate::FontRead;
1096 use font_test_data::bebuffer::BeBuffer;
1097
1098 let buf = BeBuffer::new()
1099 .push(1u16) .push(0u16) .push(0u32) .push(0u32) .push(0u16) .push(0u32) .push(0u32) .push(0u32) .push(0u32) .push(34u32) .push(1u16) .push(16u32) .push(2u16) .extend([26u32, 36]) .extend([1u16, 1])
1117 .extend([F2Dot14::ZERO, F2Dot14::ONE, F2Dot14::ONE])
1118 .extend([1u16, 1, 1, 0])
1120 .push(100i16)
1121 .extend([1u16, 1, 1, 0])
1123 .push(200i16);
1124 let colr = Colr::read(buf.data().into()).unwrap();
1125 let tables = ColrTables::new(colr);
1126 let coords = &[F2Dot14::ONE];
1127 let instance = ColrInstance::new(&tables, coords, &[]);
1128 assert!(tables.index_map.is_none());
1129 assert_eq!(
1130 instance.var_deltas::<1>(0x0000_0000),
1131 [F48Dot16::from_i32(100)]
1132 );
1133 assert_eq!(
1134 instance.var_deltas::<1>(0x0001_0000),
1135 [F48Dot16::from_i32(200)]
1136 );
1137 }
1138}