diff --git a/components/canvas/vello_backend.rs b/components/canvas/vello_backend.rs index 326539829bc..5c8f745c0b3 100644 --- a/components/canvas/vello_backend.rs +++ b/components/canvas/vello_backend.rs @@ -17,7 +17,8 @@ use std::num::NonZeroUsize; use std::rc::Rc; use canvas_traits::canvas::{ - CompositionOptions, FillOrStrokeStyle, FillRule, LineOptions, Path, ShadowOptions, + CompositionOptions, CompositionOrBlending, CompositionStyle, FillOrStrokeStyle, FillRule, + LineOptions, Path, ShadowOptions, }; use compositing_traits::SerializableImageData; use euclid::default::{Point2D, Rect, Size2D, Transform2D}; @@ -134,9 +135,19 @@ impl VelloDrawTarget { } } - fn with_draw_options(&mut self, draw_options: &CompositionOptions, f: F) { + fn with_composition( + &mut self, + composition_operation: CompositionOrBlending, + f: F, + ) { + // Fast-path for default and most common composition operation + if composition_operation == CompositionOrBlending::Composition(CompositionStyle::SourceOver) + { + f(self); + return; + } self.scene.push_layer( - draw_options.composition_operation.convert(), + composition_operation.convert(), 1.0, kurbo::Affine::IDENTITY, &kurbo::Rect::ZERO.with_size(self.size.cast()), @@ -304,7 +315,7 @@ impl GenericDrawTarget for VelloDrawTarget { self.ensure_drawing(); let scale_up = dest.size.width > source.size.width || dest.size.height > source.size.height; let shape: kurbo::Rect = dest.into(); - self.with_draw_options(&composition_options, move |self_| { + self.with_composition(composition_options.composition_operation, move |self_| { self_.scene.fill( peniko::Fill::NonZero, transform.cast().into(), @@ -359,7 +370,7 @@ impl GenericDrawTarget for VelloDrawTarget { transform: Transform2D, ) { self.ensure_drawing(); - self.with_draw_options(&composition_options, |self_| { + self.with_composition(composition_options.composition_operation, |self_| { self_.scene.fill( fill_rule.convert(), transform.cast().into(), @@ -381,7 +392,7 @@ impl GenericDrawTarget for VelloDrawTarget { self.ensure_drawing(); let pattern = convert_to_brush(style, composition_options); let transform = transform.cast().into(); - self.with_draw_options(&composition_options, |self_| { + self.with_composition(composition_options.composition_operation, |self_| { let mut advance = 0.; for run in text_runs.iter() { let glyphs = &run.glyphs; @@ -443,7 +454,7 @@ impl GenericDrawTarget for VelloDrawTarget { let pattern = convert_to_brush(style, composition_options); let transform = transform.cast().into(); let rect: kurbo::Rect = rect.cast().into(); - self.with_draw_options(&composition_options, |self_| { + self.with_composition(composition_options.composition_operation, |self_| { self_ .scene .fill(peniko::Fill::NonZero, transform, &pattern, None, &rect); @@ -489,7 +500,7 @@ impl GenericDrawTarget for VelloDrawTarget { transform: Transform2D, ) { self.ensure_drawing(); - self.with_draw_options(&composition_options, |self_| { + self.with_composition(composition_options.composition_operation, |self_| { self_.scene.stroke( &line_options.convert(), transform.cast().into(), @@ -510,7 +521,7 @@ impl GenericDrawTarget for VelloDrawTarget { ) { self.ensure_drawing(); let rect: kurbo::Rect = rect.cast().into(); - self.with_draw_options(&composition_options, |self_| { + self.with_composition(composition_options.composition_operation, |self_| { self_.scene.stroke( &line_options.convert(), transform.cast().into(), diff --git a/components/canvas/vello_cpu_backend.rs b/components/canvas/vello_cpu_backend.rs index f1f45389970..6074e85a935 100644 --- a/components/canvas/vello_cpu_backend.rs +++ b/components/canvas/vello_cpu_backend.rs @@ -7,7 +7,8 @@ use std::collections::HashMap; use std::sync::Arc; use canvas_traits::canvas::{ - CompositionOptions, FillOrStrokeStyle, FillRule, LineOptions, Path, ShadowOptions, + CompositionOptions, CompositionOrBlending, CompositionStyle, FillOrStrokeStyle, FillRule, + LineOptions, Path, ShadowOptions, }; use compositing_traits::SerializableImageData; use euclid::default::{Point2D, Rect, Size2D, Transform2D}; @@ -58,15 +59,16 @@ pub(crate) struct VelloCPUDrawTarget { impl VelloCPUDrawTarget { fn with_composition( &mut self, - composition_options: &CompositionOptions, + composition_operation: CompositionOrBlending, f: impl FnOnce(&mut Self), ) { - self.ctx.push_layer( - None, - Some(composition_options.composition_operation.convert()), - Some(composition_options.alpha as f32), - None, - ); + // Fast-path for default and most common composition operation + if composition_operation == CompositionOrBlending::Composition(CompositionStyle::SourceOver) + { + f(self); + return; + } + self.ctx.push_blend_layer(composition_operation.convert()); f(self); self.ctx.pop_layer(); } @@ -210,7 +212,7 @@ impl GenericDrawTarget for VelloCPUDrawTarget { fn draw_surface( &mut self, - surface: Self::SourceSurface, + mut surface: Self::SourceSurface, dest: Rect, source: Rect, filter: Filter, @@ -219,7 +221,12 @@ impl GenericDrawTarget for VelloCPUDrawTarget { ) { self.ensure_drawing(); let scale_up = dest.size.width > source.size.width || dest.size.height > source.size.height; - self.with_composition(&composition_options, move |self_| { + if composition_options.alpha != 1.0 { + Arc::get_mut(&mut surface) + .expect("surface should be owned") + .multiply_alpha((composition_options.alpha * 255.0) as u8); + } + self.with_composition(composition_options.composition_operation, move |self_| { self_.ctx.set_transform(transform.cast().into()); self_.ctx.set_paint(vello_cpu::Image { source: vello_cpu::ImageSource::Pixmap(surface), @@ -268,11 +275,10 @@ impl GenericDrawTarget for VelloCPUDrawTarget { transform: Transform2D, ) { self.ensure_drawing(); - let paint: vello_cpu::PaintType = style.convert(); - self.with_composition(&composition_options, |self_| { + self.with_composition(composition_options.composition_operation, |self_| { self_.ctx.set_transform(transform.cast().into()); self_.ctx.set_fill_rule(fill_rule.convert()); - self_.ctx.set_paint(paint); + self_.ctx.set_paint(paint(style, composition_options.alpha)); self_.ctx.fill_path(&path.0); }); self.ctx.set_fill_rule(peniko::Fill::NonZero); @@ -287,10 +293,9 @@ impl GenericDrawTarget for VelloCPUDrawTarget { transform: Transform2D, ) { self.ensure_drawing(); - let style: vello_cpu::PaintType = style.convert(); - self.ctx.set_paint(style); + self.ctx.set_paint(paint(style, composition_options.alpha)); self.ctx.set_transform(transform.cast().into()); - self.with_composition(&composition_options, |self_| { + self.with_composition(composition_options.composition_operation, |self_| { let mut advance = 0.; for run in text_runs.iter() { let glyphs = &run.glyphs; @@ -346,10 +351,9 @@ impl GenericDrawTarget for VelloCPUDrawTarget { transform: Transform2D, ) { self.ensure_drawing(); - let paint: vello_cpu::PaintType = style.convert(); - self.with_composition(&composition_options, |self_| { + self.with_composition(composition_options.composition_operation, |self_| { self_.ctx.set_transform(transform.cast().into()); - self_.ctx.set_paint(paint); + self_.ctx.set_paint(paint(style, composition_options.alpha)); self_.ctx.fill_rect(&rect.cast().into()); }) } @@ -395,10 +399,9 @@ impl GenericDrawTarget for VelloCPUDrawTarget { transform: Transform2D, ) { self.ensure_drawing(); - let paint: vello_cpu::PaintType = style.convert(); - self.with_composition(&composition_options, |self_| { + self.with_composition(composition_options.composition_operation, |self_| { self_.ctx.set_transform(transform.cast().into()); - self_.ctx.set_paint(paint); + self_.ctx.set_paint(paint(style, composition_options.alpha)); self_.ctx.set_stroke(line_options.convert()); self_.ctx.stroke_path(&path.0); }) @@ -413,10 +416,9 @@ impl GenericDrawTarget for VelloCPUDrawTarget { transform: Transform2D, ) { self.ensure_drawing(); - let paint: vello_cpu::PaintType = style.convert(); - self.with_composition(&composition_options, |self_| { + self.with_composition(composition_options.composition_operation, |self_| { self_.ctx.set_transform(transform.cast().into()); - self_.ctx.set_paint(paint); + self_.ctx.set_paint(paint(style, composition_options.alpha)); self_.ctx.set_stroke(line_options.convert()); self_.ctx.stroke_rect(&rect.cast().into()); }) @@ -520,3 +522,29 @@ impl Convert for FillOrStrokeStyle { } } } + +fn paint(style: FillOrStrokeStyle, alpha: f64) -> vello_cpu::PaintType { + assert!((0.0..=1.0).contains(&alpha)); + let paint = style.convert(); + if alpha == 1.0 { + paint + } else { + match paint { + vello_cpu::PaintType::Solid(alpha_color) => { + vello_cpu::PaintType::Solid(alpha_color.multiply_alpha(alpha as f32)) + }, + vello_cpu::PaintType::Gradient(gradient) => { + vello_cpu::PaintType::Gradient(gradient.multiply_alpha(alpha as f32)) + }, + vello_cpu::PaintType::Image(mut image) => { + match &mut image.source { + vello_cpu::ImageSource::Pixmap(pixmap) => Arc::get_mut(pixmap) + .expect("pixmap should not be shared with anyone at this point") + .multiply_alpha((alpha * 255.0) as u8), + vello_cpu::ImageSource::OpaqueId(_) => unimplemented!(), + }; + vello_cpu::PaintType::Image(image) + }, + } + } +}