1use crate::color::Srgb;
7use crate::color::{AlphaColor, DynamicColor};
8use crate::glyph::{ColorGlyph, OutlinePath};
9use crate::kurbo::{Affine, BezPath, Point, Rect, Shape};
10use crate::math::FloatExt;
11use crate::peniko::{self, BlendMode, ColorStops, Compose, Extend, Gradient, Mix};
12use alloc::boxed::Box;
13use alloc::vec;
14use alloc::vec::Vec;
15use core::fmt::Debug;
16use peniko::{LinearGradientPosition, RadialGradientPosition, SweepGradientPosition};
17use skrifa::color::{Brush, ColorPainter, ColorStop, CompositeMode, Transform};
18use skrifa::outline::DrawSettings;
19use skrifa::raw::TableProvider;
20use skrifa::raw::types::BoundingBox;
21use skrifa::{GlyphId, MetadataProvider};
22use smallvec::SmallVec;
23
24pub trait ColrRenderer {
26 fn push_clip_layer(&mut self, clip: &BezPath);
28 fn push_blend_layer(&mut self, blend_mode: BlendMode);
30 fn fill_solid(&mut self, color: AlphaColor<Srgb>);
32 fn fill_gradient(&mut self, gradient: Gradient);
34 fn set_paint_transform(&mut self, affine: Affine);
36 fn pop_layer(&mut self);
38}
39
40pub struct ColrPainter<'a> {
42 transforms: Vec<Affine>,
43 color_glyph: Box<ColorGlyph<'a>>,
44 context_color: AlphaColor<Srgb>,
45 painter: &'a mut dyn ColrRenderer,
46 layer_count: u32,
47}
48
49impl Debug for ColrPainter<'_> {
50 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51 f.debug_struct("ColrPainter()").finish()
52 }
53}
54
55impl<'a> ColrPainter<'a> {
56 pub fn new(
58 color_glyph: Box<ColorGlyph<'a>>,
59 context_color: AlphaColor<Srgb>,
60 painter: &'a mut impl ColrRenderer,
61 ) -> Self {
62 Self {
63 transforms: vec![color_glyph.draw_transform],
64 color_glyph,
65 context_color,
66 painter,
67 layer_count: 0,
68 }
69 }
70
71 pub fn paint(&mut self) {
73 let color_glyph = self.color_glyph.skrifa_glyph.clone();
74 let location_ref = self.color_glyph.location;
75 let _ = color_glyph.paint(location_ref, self);
77
78 for _ in 0..self.layer_count {
81 self.painter.pop_layer();
82 }
83 }
84
85 fn cur_transform(&self) -> Affine {
86 self.transforms.last().copied().unwrap_or_default()
87 }
88
89 fn palette_index_to_color(&self, palette_index: u16, alpha: f32) -> Option<AlphaColor<Srgb>> {
90 if palette_index != u16::MAX {
91 let color = self
92 .color_glyph
93 .font_ref
94 .cpal()
95 .ok()?
96 .color_records_array()?
97 .ok()?[palette_index as usize];
98
99 Some(
100 AlphaColor::from_rgba8(color.red, color.green, color.blue, color.alpha)
101 .multiply_alpha(alpha),
102 )
103 } else {
104 Some(self.context_color.multiply_alpha(alpha))
105 }
106 }
107
108 fn convert_stops(&self, stops: &[ColorStop]) -> ColorStops {
109 let mut stops = stops
110 .iter()
111 .map(|s| {
112 let color = self
113 .palette_index_to_color(s.palette_index, s.alpha)
114 .unwrap_or(AlphaColor::BLACK);
115
116 peniko::ColorStop {
117 offset: s.offset,
118 color: DynamicColor::from_alpha_color(color),
119 }
120 })
121 .collect::<SmallVec<[peniko::ColorStop; 4]>>();
122
123 let first_stop = stops[0];
126 let last_stop = *stops.last().unwrap();
127
128 if first_stop.offset != 0.0 {
129 let mut new_stop = first_stop;
130 new_stop.offset = 0.0;
131 stops.insert(0, new_stop);
132 }
133
134 if last_stop.offset != 1.0 {
135 let mut new_stop = last_stop;
136 new_stop.offset = 1.0;
137 stops.push(new_stop);
138 }
139
140 while let Some(stop) = stops.get(stops.len() - 2).map(|s| s.offset) {
144 if (stop - 1.0).is_nearly_zero() {
145 stops.remove(stops.len() - 2);
146 } else {
147 break;
148 }
149 }
150
151 ColorStops(stops)
152 }
153}
154
155impl ColorPainter for ColrPainter<'_> {
156 fn push_transform(&mut self, t: Transform) {
157 let affine = Affine::new([
158 f64::from(t.xx),
159 f64::from(t.yx),
160 f64::from(t.xy),
161 f64::from(t.yy),
162 f64::from(t.dx),
163 f64::from(t.dy),
164 ]);
165 self.transforms.push(self.cur_transform() * affine);
166 }
167
168 fn pop_transform(&mut self) {
169 self.transforms.pop();
170 }
171
172 fn push_clip_glyph(&mut self, glyph_id: GlyphId) {
173 let mut outline_builder = OutlinePath::new();
174
175 let outline_glyphs = self.color_glyph.font_ref.outline_glyphs();
176 let Some(outline_glyph) = outline_glyphs.get(glyph_id) else {
177 return;
178 };
179
180 let _ = outline_glyph.draw(
181 DrawSettings::unhinted(
182 skrifa::instance::Size::unscaled(),
183 self.color_glyph.location,
184 ),
185 &mut outline_builder,
186 );
187
188 let finished = outline_builder.0;
189 let transformed = self.cur_transform() * finished;
190
191 self.painter.push_clip_layer(&transformed);
192 self.layer_count += 1;
193 }
194
195 fn push_clip_box(&mut self, clip_box: BoundingBox<f32>) {
196 let rect = Rect::new(
197 f64::from(clip_box.x_min),
198 f64::from(clip_box.y_min),
199 f64::from(clip_box.x_max),
200 f64::from(clip_box.y_max),
201 );
202 let transformed = self.cur_transform() * rect.to_path(0.1);
203
204 self.painter.push_clip_layer(&transformed);
205 self.layer_count += 1;
206 }
207
208 fn pop_clip(&mut self) {
209 self.painter.pop_layer();
210 self.layer_count -= 1;
211 }
212
213 fn fill(&mut self, brush: Brush<'_>) {
214 match brush {
215 Brush::Solid {
216 palette_index,
217 alpha,
218 } => {
219 let color = self
220 .palette_index_to_color(palette_index, alpha)
221 .unwrap_or(AlphaColor::BLACK);
222
223 self.painter.fill_solid(color);
224 }
225 Brush::LinearGradient {
226 p0,
227 p1,
228 color_stops,
229 extend,
230 } => {
231 let p0 = convert_point(p0);
232 let p1 = convert_point(p1);
233 let extend = convert_extend(extend);
234 let stops = self.convert_stops(color_stops);
235
236 if stops.len() == 1 {
237 self.painter.fill_solid(stops[0].color.to_alpha_color());
238 } else {
239 let grad = Gradient {
240 kind: LinearGradientPosition { start: p0, end: p1 }.into(),
241 stops,
242 extend,
243 ..Default::default()
244 };
245 self.painter.set_paint_transform(self.cur_transform());
246 self.painter.fill_gradient(grad);
247 }
248 }
249 Brush::RadialGradient {
250 c0,
251 r0,
252 c1,
253 r1,
254 color_stops,
255 extend,
256 } => {
257 let p0 = convert_point(c0);
260 let p1 = convert_point(c1);
261 let extend = convert_extend(extend);
262 let stops = self.convert_stops(color_stops);
263
264 if r1 <= 0.0 || stops.len() == 1 {
265 self.painter.fill_solid(stops[0].color.to_alpha_color());
266
267 return;
268 }
269
270 let grad = Gradient {
271 kind: RadialGradientPosition {
272 start_center: p0,
273 start_radius: r0,
274 end_center: p1,
275 end_radius: r1,
276 }
277 .into(),
278 stops,
279 extend,
280 ..Default::default()
281 };
282
283 self.painter.set_paint_transform(self.cur_transform());
284 self.painter.fill_gradient(grad);
285 }
286 Brush::SweepGradient {
287 c0,
288 start_angle,
289 mut end_angle,
290 color_stops,
291 extend,
292 } => {
293 let p0 = convert_point(c0);
294 let extend = convert_extend(extend);
295 let stops = self.convert_stops(color_stops);
296
297 if stops.len() == 1 {
298 self.painter.fill_solid(stops[0].color.to_alpha_color());
299
300 return;
301 }
302
303 if start_angle == end_angle {
304 match extend {
305 Extend::Pad => {
306 end_angle += 0.01;
309 }
310 _ => {
311 unreachable!()
314 }
315 }
316 }
317
318 let grad = Gradient {
321 kind: SweepGradientPosition {
322 center: Point::new(p0.x, -p0.y),
323 start_angle: start_angle.to_radians(),
324 end_angle: end_angle.to_radians(),
325 }
326 .into(),
327 stops,
328 extend,
329 ..Default::default()
330 };
331
332 let paint_transform = self.cur_transform() * Affine::scale_non_uniform(1.0, -1.0);
333
334 self.painter.set_paint_transform(paint_transform);
335 self.painter.fill_gradient(grad);
336 }
337 };
338 }
339
340 fn push_layer(&mut self, composite_mode: CompositeMode) {
341 let blend_mode = match composite_mode {
342 CompositeMode::Clear => BlendMode::new(Mix::Normal, Compose::Clear),
343 CompositeMode::Src => BlendMode::new(Mix::Normal, Compose::Copy),
344 CompositeMode::Dest => BlendMode::new(Mix::Normal, Compose::Dest),
345 CompositeMode::SrcOver => BlendMode::new(Mix::Normal, Compose::SrcOver),
346 CompositeMode::DestOver => BlendMode::new(Mix::Normal, Compose::DestOver),
347 CompositeMode::SrcIn => BlendMode::new(Mix::Normal, Compose::SrcIn),
348 CompositeMode::DestIn => BlendMode::new(Mix::Normal, Compose::DestIn),
349 CompositeMode::SrcOut => BlendMode::new(Mix::Normal, Compose::SrcOut),
350 CompositeMode::DestOut => BlendMode::new(Mix::Normal, Compose::DestOut),
351 CompositeMode::SrcAtop => BlendMode::new(Mix::Normal, Compose::SrcAtop),
352 CompositeMode::DestAtop => BlendMode::new(Mix::Normal, Compose::DestAtop),
353 CompositeMode::Xor => BlendMode::new(Mix::Normal, Compose::Xor),
354 CompositeMode::Plus => BlendMode::new(Mix::Normal, Compose::Plus),
355 CompositeMode::Screen => BlendMode::new(Mix::Screen, Compose::SrcOver),
356 CompositeMode::Overlay => BlendMode::new(Mix::Overlay, Compose::SrcOver),
357 CompositeMode::Darken => BlendMode::new(Mix::Darken, Compose::SrcOver),
358 CompositeMode::Lighten => BlendMode::new(Mix::Lighten, Compose::SrcOver),
359 CompositeMode::ColorDodge => BlendMode::new(Mix::ColorDodge, Compose::SrcOver),
360 CompositeMode::ColorBurn => BlendMode::new(Mix::ColorBurn, Compose::SrcOver),
361 CompositeMode::HardLight => BlendMode::new(Mix::HardLight, Compose::SrcOver),
362 CompositeMode::SoftLight => BlendMode::new(Mix::SoftLight, Compose::SrcOver),
363 CompositeMode::Difference => BlendMode::new(Mix::Difference, Compose::SrcOver),
364 CompositeMode::Exclusion => BlendMode::new(Mix::Exclusion, Compose::SrcOver),
365 CompositeMode::Multiply => BlendMode::new(Mix::Multiply, Compose::SrcOver),
366 CompositeMode::HslHue => BlendMode::new(Mix::Hue, Compose::SrcOver),
367 CompositeMode::HslSaturation => BlendMode::new(Mix::Saturation, Compose::SrcOver),
368 CompositeMode::HslColor => BlendMode::new(Mix::Color, Compose::SrcOver),
369 CompositeMode::HslLuminosity => BlendMode::new(Mix::Luminosity, Compose::SrcOver),
370 CompositeMode::Unknown => BlendMode::new(Mix::Normal, Compose::SrcOver),
371 };
372
373 self.painter.push_blend_layer(blend_mode);
374 self.layer_count += 1;
375 }
376
377 fn pop_layer(&mut self) {
378 self.painter.pop_layer();
379 self.layer_count -= 1;
380 }
381}
382
383fn convert_extend(extend: skrifa::color::Extend) -> Extend {
384 match extend {
385 skrifa::color::Extend::Pad => Extend::Pad,
386 skrifa::color::Extend::Repeat => Extend::Repeat,
387 skrifa::color::Extend::Reflect => Extend::Reflect,
388 skrifa::color::Extend::Unknown => Extend::Pad,
389 }
390}
391
392fn convert_point(point: skrifa::raw::types::Point<f32>) -> Point {
393 Point::new(f64::from(point.x), f64::from(point.y))
394}
395
396pub(crate) fn convert_bounding_box(rect: BoundingBox<f32>) -> Rect {
397 Rect::new(
398 f64::from(rect.x_min),
399 f64::from(rect.y_min),
400 f64::from(rect.x_max),
401 f64::from(rect.y_max),
402 )
403}