1use std::cell::RefCell;
6use std::collections::HashMap;
7use std::sync::Arc;
8
9use canvas_traits::canvas::{
10 CompositionOptions, CompositionOrBlending, CompositionStyle, FillOrStrokeStyle, FillRule,
11 LineOptions, Path, ShadowOptions, TextRun,
12};
13use compositing_traits::SerializableImageData;
14use euclid::default::{Point2D, Rect, Size2D, Transform2D};
15use fonts::FontIdentifier;
16use ipc_channel::ipc::IpcSharedMemory;
17use kurbo::Shape;
18use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat};
19use vello_cpu::{kurbo, peniko};
20use webrender_api::{ImageDescriptor, ImageDescriptorFlags};
21
22use crate::backend::{Convert, GenericDrawTarget};
23use crate::canvas_data::Filter;
24
25thread_local! {
26 static SHARED_FONT_CACHE: RefCell<HashMap<FontIdentifier, peniko::Font>> = RefCell::default();
28}
29
30#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
31enum State {
32 Drawing,
34 Rendered,
38}
39
40pub(crate) struct VelloCPUDrawTarget {
41 ctx: vello_cpu::RenderContext,
50 pixmap: vello_cpu::Pixmap,
51 clips: Vec<Path>,
52 state: State,
53}
54
55impl VelloCPUDrawTarget {
56 fn with_composition(
57 &mut self,
58 composition_operation: CompositionOrBlending,
59 f: impl FnOnce(&mut Self),
60 ) {
61 if composition_operation == CompositionOrBlending::Composition(CompositionStyle::SourceOver)
63 {
64 f(self);
65 return;
66 }
67 self.ctx.push_blend_layer(composition_operation.convert());
68 f(self);
69 self.ctx.pop_layer();
70 }
71
72 fn ignore_clips(&mut self, f: impl FnOnce(&mut Self)) {
73 for _ in &self.clips {
75 self.ctx.pop_layer();
76 }
77 f(self);
78 for path in &self.clips {
80 self.ctx.push_clip_layer(&path.0);
81 }
82 }
83
84 fn ensure_drawing(&mut self) {
85 match self.state {
86 State::Drawing => {},
87 State::Rendered => {
88 self.ignore_clips(|self_| {
89 self_.ctx.set_transform(kurbo::Affine::IDENTITY);
90 self_.ctx.set_paint(vello_cpu::Image {
91 source: vello_cpu::ImageSource::Pixmap(Arc::new(self_.pixmap.clone())),
92 x_extend: peniko::Extend::Pad,
93 y_extend: peniko::Extend::Pad,
94 quality: peniko::ImageQuality::Low,
95 });
96 self_.ctx.fill_rect(&kurbo::Rect::from_origin_size(
97 (0., 0.),
98 self_.size().cast(),
99 ));
100 });
101 self.state = State::Drawing;
102 },
103 }
104 }
105
106 fn pixmap(&mut self) -> &[u8] {
107 if self.state == State::Drawing {
108 self.ignore_clips(|self_| {
109 self_.ctx.flush();
110 self_.ctx.render_to_pixmap(&mut self_.pixmap);
111 self_.ctx.reset();
112 self_.state = State::Rendered;
113 });
114 }
115
116 self.pixmap.data_as_u8_slice()
117 }
118
119 fn size(&self) -> Size2D<u32> {
120 Size2D::new(self.ctx.width(), self.ctx.height()).cast()
121 }
122
123 fn is_viewport_cleared(&mut self, rect: &Rect<f32>, transform: Transform2D<f64>) -> bool {
124 let transformed_rect = transform.outer_transformed_rect(&rect.cast());
125 if transformed_rect.is_empty() {
126 return false;
127 }
128 let viewport: Rect<f64> = Rect::from_size(self.get_size().cast());
129 let Some(clip) = self.clips.iter().try_fold(viewport, |acc, e| {
130 acc.intersection(&e.0.bounding_box().into())
131 }) else {
132 return false;
134 };
135 transformed_rect.cast().contains_rect(&viewport) && clip.contains_rect(&viewport) }
138}
139
140impl GenericDrawTarget for VelloCPUDrawTarget {
141 type SourceSurface = Arc<vello_cpu::Pixmap>;
142
143 fn new(size: Size2D<u32>) -> Self {
144 let size = size.cast();
145 Self {
146 ctx: vello_cpu::RenderContext::new(size.width, size.height),
147 pixmap: vello_cpu::Pixmap::new(size.width, size.height),
148 clips: Vec::new(),
149 state: State::Rendered,
150 }
151 }
152
153 fn clear_rect(&mut self, rect: &Rect<f32>, transform: Transform2D<f64>) {
154 if self.is_viewport_cleared(rect, transform) {
157 self.ctx.reset();
158 self.clips.clear(); self.state = State::Drawing;
160 return;
161 }
162 self.ensure_drawing();
163 let rect: kurbo::Rect = rect.cast().into();
164 let mut clip_path = rect.to_path(0.1);
165 clip_path.apply_affine(transform.cast().into());
166 let blend_mode = peniko::Compose::Clear;
167 self.ctx.push_layer(
168 Some(&clip_path.to_path(0.1)),
169 Some(blend_mode.into()),
170 None,
171 None,
172 );
173 self.ctx.pop_layer();
174 }
175
176 fn copy_surface(
177 &mut self,
178 surface: Self::SourceSurface,
179 source: Rect<i32>,
180 destination: Point2D<i32>,
181 ) {
182 self.ensure_drawing();
183 let destination: kurbo::Point = destination.cast::<f64>().into();
184 let rect = kurbo::Rect::from_origin_size(destination, source.size.cast());
185 self.ctx.set_transform(kurbo::Affine::IDENTITY);
186 self.ignore_clips(|self_| {
187 self_.ctx.set_paint(vello_cpu::Image {
192 source: vello_cpu::ImageSource::Pixmap(surface),
193 x_extend: peniko::Extend::Pad,
194 y_extend: peniko::Extend::Pad,
195 quality: peniko::ImageQuality::Low,
196 });
197 self_.ctx.fill_rect(&rect);
198
199 });
201 }
202
203 fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self {
204 Self::new(size.cast())
205 }
206
207 fn draw_surface(
208 &mut self,
209 mut surface: Self::SourceSurface,
210 dest: Rect<f64>,
211 source: Rect<f64>,
212 filter: Filter,
213 composition_options: CompositionOptions,
214 transform: Transform2D<f64>,
215 ) {
216 self.ensure_drawing();
217 let scale_up = dest.size.width > source.size.width || dest.size.height > source.size.height;
218 if composition_options.alpha != 1.0 {
219 Arc::get_mut(&mut surface)
220 .expect("surface should be owned")
221 .multiply_alpha((composition_options.alpha * 255.0) as u8);
222 }
223 self.with_composition(composition_options.composition_operation, move |self_| {
224 self_.ctx.set_transform(transform.cast().into());
225 self_.ctx.set_paint(vello_cpu::Image {
226 source: vello_cpu::ImageSource::Pixmap(surface),
227 x_extend: peniko::Extend::Pad,
228 y_extend: peniko::Extend::Pad,
229 quality: if scale_up {
231 filter.convert()
232 } else {
233 peniko::ImageQuality::Low
234 },
235 });
236 self_.ctx.set_paint_transform(
237 kurbo::Affine::translate((dest.origin.x, dest.origin.y)).pre_scale_non_uniform(
238 dest.size.width / source.size.width,
239 dest.size.height / source.size.height,
240 ),
241 );
242 self_.ctx.fill_rect(&dest.into());
243 self_.ctx.reset_paint_transform();
244 })
245 }
246
247 fn draw_surface_with_shadow(
248 &self,
249 _surface: Self::SourceSurface,
250 _dest: &Point2D<f32>,
251 _shadow_options: ShadowOptions,
252 _composition_options: CompositionOptions,
253 ) {
254 log::warn!("no support for drawing shadows");
255 }
262
263 fn fill(
264 &mut self,
265 path: &Path,
266 fill_rule: FillRule,
267 style: FillOrStrokeStyle,
268 composition_options: CompositionOptions,
269 transform: Transform2D<f64>,
270 ) {
271 self.ensure_drawing();
272 self.with_composition(composition_options.composition_operation, |self_| {
273 self_.ctx.set_transform(transform.cast().into());
274 self_.ctx.set_fill_rule(fill_rule.convert());
275 self_.ctx.set_paint(paint(style, composition_options.alpha));
276 self_.ctx.fill_path(&path.0);
277 });
278 self.ctx.set_fill_rule(peniko::Fill::NonZero);
279 }
280
281 fn fill_text(
282 &mut self,
283 text_runs: Vec<TextRun>,
284 style: FillOrStrokeStyle,
285 composition_options: CompositionOptions,
286 transform: Transform2D<f64>,
287 ) {
288 self.ensure_drawing();
289 self.ctx.set_paint(paint(style, composition_options.alpha));
290 self.ctx.set_transform(transform.cast().into());
291 self.with_composition(composition_options.composition_operation, |self_| {
292 for text_run in text_runs.iter() {
293 SHARED_FONT_CACHE.with(|font_cache| {
294 let identifier = &text_run.font.identifier;
295 if !font_cache.borrow().contains_key(identifier) {
296 let Some(font_data_and_index) = text_run.font.font_data_and_index() else {
297 return;
298 };
299 let font = font_data_and_index.convert();
300 font_cache.borrow_mut().insert(identifier.clone(), font);
301 }
302
303 let font_cache = font_cache.borrow();
304 let Some(font) = font_cache.get(identifier) else {
305 return;
306 };
307 self_
308 .ctx
309 .glyph_run(font)
310 .font_size(text_run.pt_size)
311 .fill_glyphs(text_run.glyphs_and_positions.iter().map(
312 |glyph_and_position| vello_cpu::Glyph {
313 id: glyph_and_position.id,
314 x: glyph_and_position.point.x,
315 y: glyph_and_position.point.y,
316 },
317 ));
318 });
319 }
320 })
321 }
322
323 fn fill_rect(
324 &mut self,
325 rect: &Rect<f32>,
326 style: FillOrStrokeStyle,
327 composition_options: CompositionOptions,
328 transform: Transform2D<f64>,
329 ) {
330 self.ensure_drawing();
331 self.with_composition(composition_options.composition_operation, |self_| {
332 self_.ctx.set_transform(transform.cast().into());
333 self_.ctx.set_paint(paint(style, composition_options.alpha));
334 self_.ctx.fill_rect(&rect.cast().into());
335 })
336 }
337
338 fn get_size(&self) -> Size2D<i32> {
339 self.size().cast()
340 }
341
342 fn pop_clip(&mut self) {
343 if self.clips.pop().is_some() {
344 self.ctx.pop_layer();
345 }
346 }
347
348 fn push_clip(&mut self, path: &Path, fill_rule: FillRule, transform: Transform2D<f64>) {
349 self.ctx.set_transform(transform.cast().into());
350 let mut path = path.clone();
351 path.transform(transform.cast());
352 self.ctx.set_fill_rule(fill_rule.convert());
353 self.ctx.push_clip_layer(&path.0);
354 self.clips.push(path);
355 self.ctx.set_fill_rule(peniko::Fill::NonZero);
356 }
357
358 fn push_clip_rect(&mut self, rect: &Rect<i32>) {
359 let mut path = Path::new();
360 let rect = rect.cast();
361 path.rect(
362 rect.origin.x,
363 rect.origin.y,
364 rect.size.width,
365 rect.size.height,
366 );
367 self.push_clip(&path, FillRule::Nonzero, Transform2D::identity());
368 }
369
370 fn stroke(
371 &mut self,
372 path: &Path,
373 style: FillOrStrokeStyle,
374 line_options: LineOptions,
375 composition_options: CompositionOptions,
376 transform: Transform2D<f64>,
377 ) {
378 self.ensure_drawing();
379 self.with_composition(composition_options.composition_operation, |self_| {
380 self_.ctx.set_transform(transform.cast().into());
381 self_.ctx.set_paint(paint(style, composition_options.alpha));
382 self_.ctx.set_stroke(line_options.convert());
383 self_.ctx.stroke_path(&path.0);
384 })
385 }
386
387 fn stroke_rect(
388 &mut self,
389 rect: &Rect<f32>,
390 style: FillOrStrokeStyle,
391 line_options: LineOptions,
392 composition_options: CompositionOptions,
393 transform: Transform2D<f64>,
394 ) {
395 self.ensure_drawing();
396 self.with_composition(composition_options.composition_operation, |self_| {
397 self_.ctx.set_transform(transform.cast().into());
398 self_.ctx.set_paint(paint(style, composition_options.alpha));
399 self_.ctx.set_stroke(line_options.convert());
400 self_.ctx.stroke_rect(&rect.cast().into());
401 })
402 }
403
404 fn image_descriptor_and_serializable_data(
405 &mut self,
406 ) -> (ImageDescriptor, SerializableImageData) {
407 let image_desc = ImageDescriptor {
408 format: webrender_api::ImageFormat::RGBA8,
409 size: self.size().cast().cast_unit(),
410 stride: None,
411 offset: 0,
412 flags: ImageDescriptorFlags::empty(),
413 };
414 let data = SerializableImageData::Raw(IpcSharedMemory::from_bytes(self.pixmap()));
415 (image_desc, data)
416 }
417
418 fn snapshot(&mut self) -> pixels::Snapshot {
419 Snapshot::from_vec(
420 self.size().cast(),
421 SnapshotPixelFormat::RGBA,
422 SnapshotAlphaMode::Transparent {
423 premultiplied: true,
424 },
425 self.pixmap().to_vec(),
426 )
427 }
428
429 fn surface(&mut self) -> Self::SourceSurface {
430 self.pixmap(); Arc::new(vello_cpu::Pixmap::from_parts(
432 self.pixmap.clone().take(),
433 self.pixmap.width(),
434 self.pixmap.height(),
435 ))
436 }
437
438 fn create_source_surface_from_data(&self, data: Snapshot) -> Option<Self::SourceSurface> {
439 Some(snapshot_as_pixmap(data))
440 }
441}
442
443fn snapshot_as_pixmap(data: Snapshot) -> Arc<vello_cpu::Pixmap> {
444 let size = data.size().cast();
445 let (data, _, _) = data.to_vec(
446 Some(SnapshotAlphaMode::Transparent {
447 premultiplied: true,
448 }),
449 Some(SnapshotPixelFormat::RGBA),
450 );
451 Arc::new(vello_cpu::Pixmap::from_parts(
452 bytemuck::cast_vec(data),
453 size.width,
454 size.height,
455 ))
456}
457
458impl Convert<vello_cpu::PaintType> for FillOrStrokeStyle {
459 fn convert(self) -> vello_cpu::PaintType {
460 use canvas_traits::canvas::FillOrStrokeStyle::*;
461 match self {
462 Color(absolute_color) => vello_cpu::PaintType::Solid(absolute_color.convert()),
463 LinearGradient(style) => {
464 let start = kurbo::Point::new(style.x0, style.y0);
465 let end = kurbo::Point::new(style.x1, style.y1);
466 let mut gradient = peniko::Gradient::new_linear(start, end);
467 gradient.stops = style.stops.convert();
468 vello_cpu::PaintType::Gradient(gradient)
469 },
470 RadialGradient(style) => {
471 let center1 = kurbo::Point::new(style.x0, style.y0);
472 let center2 = kurbo::Point::new(style.x1, style.y1);
473 let mut gradient = peniko::Gradient::new_two_point_radial(
474 center1,
475 style.r0 as f32,
476 center2,
477 style.r1 as f32,
478 );
479 gradient.stops = style.stops.convert();
480 vello_cpu::PaintType::Gradient(gradient)
481 },
482 Surface(surface_style) => {
483 let pixmap = snapshot_as_pixmap(surface_style.surface_data.to_owned());
484 vello_cpu::PaintType::Image(vello_cpu::Image {
485 source: vello_cpu::ImageSource::Pixmap(pixmap),
486 x_extend: if surface_style.repeat_x {
487 peniko::Extend::Repeat
488 } else {
489 peniko::Extend::Pad
490 },
491 y_extend: if surface_style.repeat_y {
492 peniko::Extend::Repeat
493 } else {
494 peniko::Extend::Pad
495 },
496 quality: peniko::ImageQuality::Low,
497 })
498 },
499 }
500 }
501}
502
503fn paint(style: FillOrStrokeStyle, alpha: f64) -> vello_cpu::PaintType {
504 assert!((0.0..=1.0).contains(&alpha));
505 let paint = style.convert();
506 if alpha == 1.0 {
507 paint
508 } else {
509 match paint {
510 vello_cpu::PaintType::Solid(alpha_color) => {
511 vello_cpu::PaintType::Solid(alpha_color.multiply_alpha(alpha as f32))
512 },
513 vello_cpu::PaintType::Gradient(gradient) => {
514 vello_cpu::PaintType::Gradient(gradient.multiply_alpha(alpha as f32))
515 },
516 vello_cpu::PaintType::Image(mut image) => {
517 match &mut image.source {
518 vello_cpu::ImageSource::Pixmap(pixmap) => Arc::get_mut(pixmap)
519 .expect("pixmap should not be shared with anyone at this point")
520 .multiply_alpha((alpha * 255.0) as u8),
521 vello_cpu::ImageSource::OpaqueId(_) => unimplemented!(),
522 };
523 vello_cpu::PaintType::Image(image)
524 },
525 }
526 }
527}