1use std::cell::RefCell;
6use std::collections::HashMap;
7use std::sync::Arc;
8
9use euclid::default::{Point2D, Rect, Size2D, Transform2D};
10use fonts::FontIdentifier;
11use kurbo::Shape;
12use paint_api::SerializableImageData;
13use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat};
14use servo_base::generic_channel::GenericSharedMemory;
15use servo_canvas_traits::canvas::{
16 CompositionOptions, CompositionOrBlending, CompositionStyle, FillOrStrokeStyle, FillRule,
17 LineOptions, Path, ShadowOptions, TextRun,
18};
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::FontData>> = 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, kurbo::Affine)>,
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, affine) in &self.clips {
80 self.ctx.set_transform(*affine);
81 self.ctx.push_clip_layer(&path.0);
82 }
83 }
84
85 fn ensure_drawing(&mut self) {
86 match self.state {
87 State::Drawing => {},
88 State::Rendered => {
89 self.ignore_clips(|self_| {
90 self_.ctx.set_transform(kurbo::Affine::IDENTITY);
91 self_.ctx.set_paint(vello_cpu::Image {
92 image: vello_cpu::ImageSource::Pixmap(Arc::new(self_.pixmap.clone())),
93 sampler: peniko::ImageSampler {
94 x_extend: peniko::Extend::Pad,
95 y_extend: peniko::Extend::Pad,
96 quality: peniko::ImageQuality::Low,
97 alpha: 1.0,
98 },
99 });
100 self_.ctx.fill_rect(&kurbo::Rect::from_origin_size(
101 (0., 0.),
102 self_.size().cast(),
103 ));
104 });
105 self.state = State::Drawing;
106 },
107 }
108 }
109
110 fn pixmap(&mut self) -> &[u8] {
111 if self.state == State::Drawing {
112 self.ignore_clips(|self_| {
113 self_.ctx.flush();
114 self_.ctx.render_to_pixmap(&mut self_.pixmap);
115 self_.ctx.reset();
116 self_.state = State::Rendered;
117 });
118 }
119
120 self.pixmap.data_as_u8_slice()
121 }
122
123 fn size(&self) -> Size2D<u32> {
124 Size2D::new(self.ctx.width(), self.ctx.height()).cast()
125 }
126
127 fn is_viewport_cleared(&mut self, rect: &Rect<f32>, transform: Transform2D<f64>) -> bool {
128 let transformed_rect = transform.outer_transformed_rect(&rect.cast());
129 if transformed_rect.is_empty() {
130 return false;
131 }
132 let viewport: Rect<f64> = Rect::from_size(self.get_size().cast());
133 let Some(clip) = self.clips.iter().try_fold(viewport, |acc, (path, affine)| {
134 let mut bez = path.0.clone();
135 bez.apply_affine(*affine);
136 acc.intersection(&bez.bounding_box().into())
137 }) else {
138 return false;
140 };
141 transformed_rect.cast().contains_rect(&viewport) && clip.contains_rect(&viewport) }
144}
145
146impl GenericDrawTarget for VelloCPUDrawTarget {
147 type SourceSurface = Arc<vello_cpu::Pixmap>;
148
149 fn new(size: Size2D<u32>) -> Self {
150 let size = size.cast();
151 Self {
152 ctx: vello_cpu::RenderContext::new(size.width, size.height),
153 pixmap: vello_cpu::Pixmap::new(size.width, size.height),
154 clips: Vec::new(),
155 state: State::Rendered,
156 }
157 }
158
159 fn clear_rect(&mut self, rect: &Rect<f32>, transform: Transform2D<f64>) {
160 if self.is_viewport_cleared(rect, transform) {
163 self.ctx.reset();
164 self.clips.clear(); self.state = State::Drawing;
166 return;
167 }
168 self.ensure_drawing();
169 let rect: kurbo::Rect = rect.cast().into();
170 let mut clip_path = rect.to_path(0.1);
171 clip_path.apply_affine(transform.cast().into());
172 let blend_mode = peniko::Compose::Clear;
173 self.ctx.push_layer(
174 Some(&clip_path.to_path(0.1)),
175 Some(blend_mode.into()),
176 None,
177 None,
178 );
179 self.ctx.pop_layer();
180 }
181
182 fn copy_surface(
183 &mut self,
184 surface: Self::SourceSurface,
185 source: Rect<i32>,
186 destination: Point2D<i32>,
187 ) {
188 self.ensure_drawing();
189 let destination: kurbo::Point = destination.cast::<f64>().into();
190 let rect = kurbo::Rect::from_origin_size(destination, source.size.cast());
191 self.ctx.set_transform(kurbo::Affine::IDENTITY);
192 self.ignore_clips(|self_| {
193 self_.ctx.set_paint(vello_cpu::Image {
198 image: vello_cpu::ImageSource::Pixmap(surface),
199 sampler: peniko::ImageSampler {
200 x_extend: peniko::Extend::Pad,
201 y_extend: peniko::Extend::Pad,
202 quality: peniko::ImageQuality::Low,
203 alpha: 1.0,
204 },
205 });
206 self_.ctx.fill_rect(&rect);
207
208 });
210 }
211
212 fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self {
213 Self::new(size.cast())
214 }
215
216 fn draw_surface(
217 &mut self,
218 mut surface: Self::SourceSurface,
219 dest: Rect<f64>,
220 source: Rect<f64>,
221 filter: Filter,
222 composition_options: CompositionOptions,
223 transform: Transform2D<f64>,
224 ) {
225 self.ensure_drawing();
226 let scale_up = dest.size.width > source.size.width || dest.size.height > source.size.height;
227 if composition_options.alpha != 1.0 {
228 Arc::get_mut(&mut surface)
229 .expect("surface should be owned")
230 .multiply_alpha((composition_options.alpha * 255.0) as u8);
231 }
232 self.with_composition(composition_options.composition_operation, move |self_| {
233 self_.ctx.set_transform(transform.cast().into());
234 self_.ctx.set_paint(vello_cpu::Image {
235 image: vello_cpu::ImageSource::Pixmap(surface),
236 sampler: peniko::ImageSampler {
237 x_extend: peniko::Extend::Pad,
238 y_extend: peniko::Extend::Pad,
239 quality: if scale_up {
241 filter.convert()
242 } else {
243 peniko::ImageQuality::Low
244 },
245 alpha: 1.0,
246 },
247 });
248 self_.ctx.set_paint_transform(
249 kurbo::Affine::translate((dest.origin.x, dest.origin.y)).pre_scale_non_uniform(
250 dest.size.width / source.size.width,
251 dest.size.height / source.size.height,
252 ),
253 );
254 self_.ctx.fill_rect(&dest.into());
255 self_.ctx.reset_paint_transform();
256 })
257 }
258
259 fn draw_surface_with_shadow(
260 &self,
261 _surface: Self::SourceSurface,
262 _dest: &Point2D<f32>,
263 _shadow_options: ShadowOptions,
264 _composition_options: CompositionOptions,
265 ) {
266 log::warn!("no support for drawing shadows");
267 }
274
275 fn fill(
276 &mut self,
277 path: &Path,
278 fill_rule: FillRule,
279 style: FillOrStrokeStyle,
280 composition_options: CompositionOptions,
281 transform: Transform2D<f64>,
282 ) {
283 self.ensure_drawing();
284 self.with_composition(composition_options.composition_operation, |self_| {
285 self_.ctx.set_transform(transform.cast().into());
286 self_.ctx.set_fill_rule(fill_rule.convert());
287 self_.ctx.set_paint(paint(style, composition_options.alpha));
288 self_.ctx.fill_path(&path.0);
289 });
290 self.ctx.set_fill_rule(peniko::Fill::NonZero);
291 }
292
293 fn fill_text(
294 &mut self,
295 text_runs: Vec<TextRun>,
296 style: FillOrStrokeStyle,
297 composition_options: CompositionOptions,
298 transform: Transform2D<f64>,
299 ) {
300 self.ensure_drawing();
301 self.ctx.set_paint(paint(style, composition_options.alpha));
302 self.ctx.set_transform(transform.cast().into());
303 self.with_composition(composition_options.composition_operation, |self_| {
304 for text_run in text_runs.iter() {
305 SHARED_FONT_CACHE.with(|font_cache| {
306 let identifier = &text_run.font.identifier;
307 if !font_cache.borrow().contains_key(identifier) {
308 let Some(font_data_and_index) = text_run.font.font_data_and_index() else {
309 return;
310 };
311 let font = font_data_and_index.convert();
312 font_cache.borrow_mut().insert(identifier.clone(), font);
313 }
314
315 let font_cache = font_cache.borrow();
316 let Some(font) = font_cache.get(identifier) else {
317 return;
318 };
319 self_
320 .ctx
321 .glyph_run(font)
322 .font_size(text_run.pt_size)
323 .fill_glyphs(text_run.glyphs_and_positions.iter().map(
324 |glyph_and_position| vello_cpu::Glyph {
325 id: glyph_and_position.id,
326 x: glyph_and_position.point.x,
327 y: glyph_and_position.point.y,
328 },
329 ));
330 });
331 }
332 })
333 }
334
335 fn fill_rect(
336 &mut self,
337 rect: &Rect<f32>,
338 style: FillOrStrokeStyle,
339 composition_options: CompositionOptions,
340 transform: Transform2D<f64>,
341 ) {
342 self.ensure_drawing();
343 self.with_composition(composition_options.composition_operation, |self_| {
344 self_.ctx.set_transform(transform.cast().into());
345 self_.ctx.set_paint(paint(style, composition_options.alpha));
346 self_.ctx.fill_rect(&rect.cast().into());
347 })
348 }
349
350 fn get_size(&self) -> Size2D<i32> {
351 self.size().cast()
352 }
353
354 fn pop_clip(&mut self) {
355 if self.clips.pop().is_some() {
356 self.ctx.pop_layer();
357 }
358 }
359
360 fn push_clip(&mut self, path: &Path, fill_rule: FillRule, transform: Transform2D<f64>) {
361 let affine = transform.cast().into();
362 self.ctx.set_transform(affine);
363 self.ctx.set_fill_rule(fill_rule.convert());
364 self.ctx.push_clip_layer(&path.0);
365 self.clips.push((path.clone(), affine));
366 self.ctx.set_fill_rule(peniko::Fill::NonZero);
367 }
368
369 fn push_clip_rect(&mut self, rect: &Rect<i32>) {
370 let mut path = Path::new();
371 let rect = rect.cast();
372 path.rect(
373 rect.origin.x,
374 rect.origin.y,
375 rect.size.width,
376 rect.size.height,
377 );
378 self.push_clip(&path, FillRule::Nonzero, Transform2D::identity());
379 }
380
381 fn stroke(
382 &mut self,
383 path: &Path,
384 style: FillOrStrokeStyle,
385 line_options: LineOptions,
386 composition_options: CompositionOptions,
387 transform: Transform2D<f64>,
388 ) {
389 self.ensure_drawing();
390 self.with_composition(composition_options.composition_operation, |self_| {
391 self_.ctx.set_transform(transform.cast().into());
392 self_.ctx.set_paint(paint(style, composition_options.alpha));
393 self_.ctx.set_stroke(line_options.convert());
394 self_.ctx.stroke_path(&path.0);
395 })
396 }
397
398 fn stroke_text(
399 &mut self,
400 text_runs: Vec<TextRun>,
401 style: FillOrStrokeStyle,
402 line_options: LineOptions,
403 composition_options: CompositionOptions,
404 transform: Transform2D<f64>,
405 ) {
406 self.ensure_drawing();
407 self.ctx.set_paint(paint(style, composition_options.alpha));
408 self.ctx.set_stroke(line_options.convert());
409 self.ctx.set_transform(transform.cast().into());
410 self.with_composition(composition_options.composition_operation, |self_| {
411 for text_run in text_runs.iter() {
412 SHARED_FONT_CACHE.with(|font_cache| {
413 let identifier = &text_run.font.identifier;
414 if !font_cache.borrow().contains_key(identifier) {
415 let Some(font_data_and_index) = text_run.font.font_data_and_index() else {
416 return;
417 };
418 let font = font_data_and_index.convert();
419 font_cache.borrow_mut().insert(identifier.clone(), font);
420 }
421
422 let font_cache = font_cache.borrow();
423 let Some(font) = font_cache.get(identifier) else {
424 return;
425 };
426 self_
427 .ctx
428 .glyph_run(font)
429 .font_size(text_run.pt_size)
430 .stroke_glyphs(text_run.glyphs_and_positions.iter().map(
431 |glyph_and_position| vello_cpu::Glyph {
432 id: glyph_and_position.id,
433 x: glyph_and_position.point.x,
434 y: glyph_and_position.point.y,
435 },
436 ));
437 });
438 }
439 })
440 }
441
442 fn stroke_rect(
443 &mut self,
444 rect: &Rect<f32>,
445 style: FillOrStrokeStyle,
446 line_options: LineOptions,
447 composition_options: CompositionOptions,
448 transform: Transform2D<f64>,
449 ) {
450 self.ensure_drawing();
451 self.with_composition(composition_options.composition_operation, |self_| {
452 self_.ctx.set_transform(transform.cast().into());
453 self_.ctx.set_paint(paint(style, composition_options.alpha));
454 self_.ctx.set_stroke(line_options.convert());
455 self_.ctx.stroke_rect(&rect.cast().into());
456 })
457 }
458
459 fn image_descriptor_and_serializable_data(
460 &mut self,
461 ) -> (ImageDescriptor, SerializableImageData) {
462 let image_desc = ImageDescriptor {
463 format: webrender_api::ImageFormat::RGBA8,
464 size: self.size().cast().cast_unit(),
465 stride: None,
466 offset: 0,
467 flags: ImageDescriptorFlags::empty(),
468 };
469 let data = SerializableImageData::Raw(GenericSharedMemory::from_bytes(self.pixmap()));
470 (image_desc, data)
471 }
472
473 fn snapshot(&mut self) -> pixels::Snapshot {
474 Snapshot::from_vec(
475 self.size().cast(),
476 SnapshotPixelFormat::RGBA,
477 SnapshotAlphaMode::Transparent {
478 premultiplied: true,
479 },
480 self.pixmap().to_vec(),
481 )
482 }
483
484 fn surface(&mut self) -> Self::SourceSurface {
485 self.pixmap(); Arc::new(vello_cpu::Pixmap::from_parts(
487 self.pixmap.clone().take(),
488 self.pixmap.width(),
489 self.pixmap.height(),
490 ))
491 }
492
493 fn create_source_surface_from_data(&self, data: Snapshot) -> Option<Self::SourceSurface> {
494 Some(snapshot_as_pixmap(data))
495 }
496}
497
498fn snapshot_as_pixmap(mut snapshot: Snapshot) -> Arc<vello_cpu::Pixmap> {
499 let size = snapshot.size().cast();
500 snapshot.transform(
501 SnapshotAlphaMode::Transparent {
502 premultiplied: true,
503 },
504 SnapshotPixelFormat::RGBA,
505 );
506
507 Arc::new(vello_cpu::Pixmap::from_parts(
508 bytemuck::cast_vec(snapshot.into()),
509 size.width,
510 size.height,
511 ))
512}
513
514impl Convert<vello_cpu::PaintType> for FillOrStrokeStyle {
515 fn convert(self) -> vello_cpu::PaintType {
516 use servo_canvas_traits::canvas::FillOrStrokeStyle::*;
517 match self {
518 Color(absolute_color) => vello_cpu::PaintType::Solid(absolute_color.convert()),
519 LinearGradient(style) => {
520 let start = kurbo::Point::new(style.x0, style.y0);
521 let end = kurbo::Point::new(style.x1, style.y1);
522 let mut gradient = peniko::Gradient::new_linear(start, end);
523 gradient.stops = style.stops.convert();
524 vello_cpu::PaintType::Gradient(gradient)
525 },
526 RadialGradient(style) => {
527 let center1 = kurbo::Point::new(style.x0, style.y0);
528 let center2 = kurbo::Point::new(style.x1, style.y1);
529 let mut gradient = peniko::Gradient::new_two_point_radial(
530 center1,
531 style.r0 as f32,
532 center2,
533 style.r1 as f32,
534 );
535 gradient.stops = style.stops.convert();
536 vello_cpu::PaintType::Gradient(gradient)
537 },
538 Surface(surface_style) => {
539 let pixmap = snapshot_as_pixmap(surface_style.surface_data.to_owned());
540 vello_cpu::PaintType::Image(vello_cpu::Image {
541 image: vello_cpu::ImageSource::Pixmap(pixmap),
542 sampler: peniko::ImageSampler {
543 x_extend: if surface_style.repeat_x {
544 peniko::Extend::Repeat
545 } else {
546 peniko::Extend::Pad
547 },
548 y_extend: if surface_style.repeat_y {
549 peniko::Extend::Repeat
550 } else {
551 peniko::Extend::Pad
552 },
553 quality: peniko::ImageQuality::Low,
554 alpha: 1.0,
555 },
556 })
557 },
558 }
559 }
560}
561
562fn paint(style: FillOrStrokeStyle, alpha: f64) -> vello_cpu::PaintType {
563 assert!((0.0..=1.0).contains(&alpha));
564 let paint = style.convert();
565 if alpha == 1.0 {
566 paint
567 } else {
568 match paint {
569 vello_cpu::PaintType::Solid(alpha_color) => {
570 vello_cpu::PaintType::Solid(alpha_color.multiply_alpha(alpha as f32))
571 },
572 vello_cpu::PaintType::Gradient(gradient) => {
573 vello_cpu::PaintType::Gradient(gradient.multiply_alpha(alpha as f32))
574 },
575 vello_cpu::PaintType::Image(mut image) => {
576 match &mut image.image {
577 vello_cpu::ImageSource::Pixmap(pixmap) => Arc::get_mut(pixmap)
578 .expect("pixmap should not be shared with anyone at this point")
579 .multiply_alpha((alpha * 255.0) as u8),
580 vello_cpu::ImageSource::OpaqueId(_) => unimplemented!(),
581 };
582 vello_cpu::PaintType::Image(image)
583 },
584 }
585 }
586}