diff options
Diffstat (limited to 'components')
-rw-r--r-- | components/canvas/backend.rs | 192 | ||||
-rw-r--r-- | components/canvas/canvas_data.rs | 494 | ||||
-rw-r--r-- | components/canvas/canvas_paint_thread.rs | 359 | ||||
-rw-r--r-- | components/canvas/lib.rs | 1 | ||||
-rw-r--r-- | components/canvas/raqote_backend.rs | 471 | ||||
-rw-r--r-- | components/script/canvas_context.rs | 182 | ||||
-rw-r--r-- | components/script/canvas_state.rs | 13 | ||||
-rw-r--r-- | components/script/dom/htmlcanvaselement.rs | 99 | ||||
-rw-r--r-- | components/script/dom/offscreencanvas.rs | 58 | ||||
-rw-r--r-- | components/script/dom/offscreencanvasrenderingcontext2d.rs | 29 | ||||
-rw-r--r-- | components/script/script_module.rs | 5 |
11 files changed, 1134 insertions, 769 deletions
diff --git a/components/canvas/backend.rs b/components/canvas/backend.rs new file mode 100644 index 00000000000..c83eebe3bee --- /dev/null +++ b/components/canvas/backend.rs @@ -0,0 +1,192 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use canvas_traits::canvas::{FillOrStrokeStyle, LineCapStyle, LineJoinStyle}; +use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; +use style::color::AbsoluteColor; + +use crate::canvas_data::{CanvasPaintState, Filter, TextRun}; + +pub(crate) trait Backend: Clone + Sized { + type Pattern<'a>: PatternHelpers + Clone; + type StrokeOptions: StrokeOptionsHelpers + Clone; + type Color: Clone; + type DrawOptions: DrawOptionsHelpers + Clone; + type CompositionOp; + type CompositionOrBlending; + type DrawTarget: GenericDrawTarget<Self>; + type PathBuilder: GenericPathBuilder<Self>; + type SourceSurface; + type Path: PathHelpers<Self> + Clone; + type GradientStop; + type GradientStops; + + fn get_composition_op(&self, opts: &Self::DrawOptions) -> Self::CompositionOp; + fn need_to_draw_shadow(&self, color: &Self::Color) -> bool; + fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_, Self>); + fn set_fill_style( + &mut self, + style: FillOrStrokeStyle, + state: &mut CanvasPaintState<'_, Self>, + drawtarget: &Self::DrawTarget, + ); + fn set_stroke_style( + &mut self, + style: FillOrStrokeStyle, + state: &mut CanvasPaintState<'_, Self>, + drawtarget: &Self::DrawTarget, + ); + fn set_global_composition( + &mut self, + op: Self::CompositionOrBlending, + state: &mut CanvasPaintState<'_, Self>, + ); + fn create_drawtarget(&self, size: Size2D<u64>) -> Self::DrawTarget; + fn new_paint_state<'a>(&self) -> CanvasPaintState<'a, Self>; +} + +// This defines required methods for a DrawTarget (currently only implemented for raqote). The +// prototypes are derived from the now-removed Azure backend's methods. +pub(crate) trait GenericDrawTarget<B: Backend> { + fn clear_rect(&mut self, rect: &Rect<f32>); + fn copy_surface( + &mut self, + surface: B::SourceSurface, + source: Rect<i32>, + destination: Point2D<i32>, + ); + fn create_path_builder(&self) -> B::PathBuilder; + fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self; + fn create_source_surface_from_data(&self, data: &[u8]) -> Option<B::SourceSurface>; + fn draw_surface( + &mut self, + surface: B::SourceSurface, + dest: Rect<f64>, + source: Rect<f64>, + filter: Filter, + draw_options: &B::DrawOptions, + ); + fn draw_surface_with_shadow( + &self, + surface: B::SourceSurface, + dest: &Point2D<f32>, + color: &B::Color, + offset: &Vector2D<f32>, + sigma: f32, + operator: B::CompositionOp, + ); + fn fill(&mut self, path: &B::Path, pattern: B::Pattern<'_>, draw_options: &B::DrawOptions); + fn fill_text( + &mut self, + text_runs: Vec<TextRun>, + start: Point2D<f32>, + pattern: &B::Pattern<'_>, + draw_options: &B::DrawOptions, + ); + fn fill_rect( + &mut self, + rect: &Rect<f32>, + pattern: B::Pattern<'_>, + draw_options: Option<&B::DrawOptions>, + ); + fn get_size(&self) -> Size2D<i32>; + fn get_transform(&self) -> Transform2D<f32>; + fn pop_clip(&mut self); + fn push_clip(&mut self, path: &B::Path); + fn set_transform(&mut self, matrix: &Transform2D<f32>); + fn snapshot(&self) -> B::SourceSurface; + fn stroke( + &mut self, + path: &B::Path, + pattern: B::Pattern<'_>, + stroke_options: &B::StrokeOptions, + draw_options: &B::DrawOptions, + ); + fn stroke_line( + &mut self, + start: Point2D<f32>, + end: Point2D<f32>, + pattern: B::Pattern<'_>, + stroke_options: &B::StrokeOptions, + draw_options: &B::DrawOptions, + ); + fn stroke_rect( + &mut self, + rect: &Rect<f32>, + pattern: B::Pattern<'_>, + stroke_options: &B::StrokeOptions, + draw_options: &B::DrawOptions, + ); + fn snapshot_data(&self) -> &[u8]; +} + +/// A generic PathBuilder that abstracts the interface for azure's and raqote's PathBuilder. +pub(crate) trait GenericPathBuilder<B: Backend> { + fn arc( + &mut self, + origin: Point2D<f32>, + radius: f32, + start_angle: f32, + end_angle: f32, + anticlockwise: bool, + ); + fn bezier_curve_to( + &mut self, + control_point1: &Point2D<f32>, + control_point2: &Point2D<f32>, + control_point3: &Point2D<f32>, + ); + fn close(&mut self); + #[allow(clippy::too_many_arguments)] + fn ellipse( + &mut self, + origin: Point2D<f32>, + radius_x: f32, + radius_y: f32, + rotation_angle: f32, + start_angle: f32, + end_angle: f32, + anticlockwise: bool, + ); + fn get_current_point(&mut self) -> Option<Point2D<f32>>; + fn line_to(&mut self, point: Point2D<f32>); + fn move_to(&mut self, point: Point2D<f32>); + fn quadratic_curve_to(&mut self, control_point: &Point2D<f32>, end_point: &Point2D<f32>); + fn svg_arc( + &mut self, + radius_x: f32, + radius_y: f32, + rotation_angle: f32, + large_arc: bool, + sweep: bool, + end_point: Point2D<f32>, + ); + fn finish(&mut self) -> B::Path; +} + +pub(crate) trait PatternHelpers { + fn is_zero_size_gradient(&self) -> bool; + fn draw_rect(&self, rect: &Rect<f32>) -> Rect<f32>; +} + +pub(crate) trait StrokeOptionsHelpers { + fn set_line_width(&mut self, _val: f32); + fn set_miter_limit(&mut self, _val: f32); + fn set_line_join(&mut self, val: LineJoinStyle); + fn set_line_cap(&mut self, val: LineCapStyle); + fn set_line_dash(&mut self, items: Vec<f32>); + fn set_line_dash_offset(&mut self, offset: f32); +} + +pub(crate) trait DrawOptionsHelpers { + fn set_alpha(&mut self, val: f32); +} + +pub(crate) trait PathHelpers<B: Backend> { + fn transformed_copy_to_builder(&self, transform: &Transform2D<f32>) -> B::PathBuilder; + + fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D<f32>) -> bool; + + fn copy_to_builder(&self) -> B::PathBuilder; +} diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 2667b7f6b44..7d8b4a9cdbc 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::marker::PhantomData; use std::mem; use std::sync::Arc; @@ -16,7 +17,6 @@ use fonts::{ }; use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; use log::warn; -use num_traits::ToPrimitive; use range::Range; use servo_arc::Arc as ServoArc; use snapshot::Snapshot; @@ -26,14 +26,17 @@ use unicode_script::Script; use webrender_api::units::RectExt as RectExt_; use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey}; -use crate::raqote_backend::Repetition; +use crate::backend::{ + Backend, DrawOptionsHelpers as _, GenericDrawTarget as _, GenericPathBuilder, PathHelpers, + PatternHelpers, StrokeOptionsHelpers as _, +}; // Asserts on WR texture cache update for zero sized image with raw data. // https://github.com/servo/webrender/blob/main/webrender/src/texture_cache.rs#L1475 const MIN_WR_IMAGE_SIZE: Size2D<u64> = Size2D::new(1, 1); -fn to_path(path: &[PathSegment], mut builder: Box<dyn GenericPathBuilder>) -> Path { - let mut build_ref = PathBuilderRef { +fn to_path<B: Backend>(path: &[PathSegment], mut builder: B::PathBuilder) -> B::Path { + let mut build_ref = PathBuilderRef::<B> { builder: &mut builder, transform: Transform2D::identity(), }; @@ -112,20 +115,20 @@ fn to_path(path: &[PathSegment], mut builder: Box<dyn GenericPathBuilder>) -> Pa /// draw the path, we convert it back to userspace and draw it /// with the correct transform applied. /// TODO: De-abstract now that Azure is removed? -enum PathState { +enum PathState<B: Backend> { /// Path builder in user-space. If a transform has been applied /// but no further path operations have occurred, it is stored /// in the optional field. - UserSpacePathBuilder(Box<dyn GenericPathBuilder>, Option<Transform2D<f32>>), + UserSpacePathBuilder(B::PathBuilder, Option<Transform2D<f32>>), /// Path builder in device-space. - DeviceSpacePathBuilder(Box<dyn GenericPathBuilder>), + DeviceSpacePathBuilder(B::PathBuilder), /// Path in user-space. If a transform has been applied but /// but no further path operations have occurred, it is stored /// in the optional field. - UserSpacePath(Path, Option<Transform2D<f32>>), + UserSpacePath(B::Path, Option<Transform2D<f32>>), } -impl PathState { +impl<B: Backend> PathState<B> { fn is_path(&self) -> bool { match *self { PathState::UserSpacePath(..) => true, @@ -133,7 +136,7 @@ impl PathState { } } - fn path(&self) -> &Path { + fn path(&self) -> &B::Path { match *self { PathState::UserSpacePath(ref p, _) => p, PathState::UserSpacePathBuilder(..) | PathState::DeviceSpacePathBuilder(..) => { @@ -143,84 +146,14 @@ impl PathState { } } -pub trait Backend { - fn get_composition_op(&self, opts: &DrawOptions) -> CompositionOp; - fn need_to_draw_shadow(&self, color: &Color) -> bool; - fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_>); - fn set_fill_style( - &mut self, - style: FillOrStrokeStyle, - state: &mut CanvasPaintState<'_>, - drawtarget: &dyn GenericDrawTarget, - ); - fn set_stroke_style( - &mut self, - style: FillOrStrokeStyle, - state: &mut CanvasPaintState<'_>, - drawtarget: &dyn GenericDrawTarget, - ); - fn set_global_composition( - &mut self, - op: CompositionOrBlending, - state: &mut CanvasPaintState<'_>, - ); - fn create_drawtarget(&self, size: Size2D<u64>) -> Box<dyn GenericDrawTarget>; - fn recreate_paint_state<'a>(&self, state: &CanvasPaintState<'a>) -> CanvasPaintState<'a>; -} - -/// A generic PathBuilder that abstracts the interface for azure's and raqote's PathBuilder. -/// TODO: De-abstract now that Azure is removed? -pub trait GenericPathBuilder { - fn arc( - &mut self, - origin: Point2D<f32>, - radius: f32, - start_angle: f32, - end_angle: f32, - anticlockwise: bool, - ); - fn bezier_curve_to( - &mut self, - control_point1: &Point2D<f32>, - control_point2: &Point2D<f32>, - control_point3: &Point2D<f32>, - ); - fn close(&mut self); - #[allow(clippy::too_many_arguments)] - fn ellipse( - &mut self, - origin: Point2D<f32>, - radius_x: f32, - radius_y: f32, - rotation_angle: f32, - start_angle: f32, - end_angle: f32, - anticlockwise: bool, - ); - fn get_current_point(&mut self) -> Option<Point2D<f32>>; - fn line_to(&mut self, point: Point2D<f32>); - fn move_to(&mut self, point: Point2D<f32>); - fn quadratic_curve_to(&mut self, control_point: &Point2D<f32>, end_point: &Point2D<f32>); - fn svg_arc( - &mut self, - radius_x: f32, - radius_y: f32, - rotation_angle: f32, - large_arc: bool, - sweep: bool, - end_point: Point2D<f32>, - ); - fn finish(&mut self) -> Path; -} - /// A wrapper around a stored PathBuilder and an optional transformation that should be /// applied to any points to ensure they are in the matching device space. -struct PathBuilderRef<'a> { - builder: &'a mut Box<dyn GenericPathBuilder>, +struct PathBuilderRef<'a, B: Backend> { + builder: &'a mut B::PathBuilder, transform: Transform2D<f32>, } -impl PathBuilderRef<'_> { +impl<B: Backend> PathBuilderRef<'_, B> { fn line_to(&mut self, pt: &Point2D<f32>) { let pt = self.transform.transform_point(*pt); self.builder.line_to(pt); @@ -341,7 +274,7 @@ impl PathBuilderRef<'_> { } #[allow(clippy::too_many_arguments)] - pub fn ellipse( + pub(crate) fn ellipse( &mut self, center: &Point2D<f32>, radius_x: f32, @@ -430,9 +363,9 @@ impl UnshapedTextRun<'_> { } } -pub struct TextRun { - pub font: FontRef, - pub glyphs: Arc<GlyphStore>, +pub(crate) struct TextRun { + pub(crate) font: FontRef, + pub(crate) glyphs: Arc<GlyphStore>, } impl TextRun { @@ -458,149 +391,31 @@ impl TextRun { } } -// This defines required methods for a DrawTarget (currently only implemented for raqote). The -// prototypes are derived from the now-removed Azure backend's methods. -pub trait GenericDrawTarget { - fn clear_rect(&mut self, rect: &Rect<f32>); - fn copy_surface( - &mut self, - surface: SourceSurface, - source: Rect<i32>, - destination: Point2D<i32>, - ); - fn create_gradient_stops(&self, gradient_stops: Vec<GradientStop>) -> GradientStops; - fn create_path_builder(&self) -> Box<dyn GenericPathBuilder>; - fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Box<dyn GenericDrawTarget>; - fn create_source_surface_from_data(&self, data: &[u8]) -> Option<SourceSurface>; - fn draw_surface( - &mut self, - surface: SourceSurface, - dest: Rect<f64>, - source: Rect<f64>, - filter: Filter, - draw_options: &DrawOptions, - ); - fn draw_surface_with_shadow( - &self, - surface: SourceSurface, - dest: &Point2D<f32>, - color: &Color, - offset: &Vector2D<f32>, - sigma: f32, - operator: CompositionOp, - ); - fn fill(&mut self, path: &Path, pattern: Pattern, draw_options: &DrawOptions); - fn fill_text( - &mut self, - text_runs: Vec<TextRun>, - start: Point2D<f32>, - pattern: &Pattern, - draw_options: &DrawOptions, - ); - fn fill_rect(&mut self, rect: &Rect<f32>, pattern: Pattern, draw_options: Option<&DrawOptions>); - fn get_size(&self) -> Size2D<i32>; - fn get_transform(&self) -> Transform2D<f32>; - fn pop_clip(&mut self); - fn push_clip(&mut self, path: &Path); - fn set_transform(&mut self, matrix: &Transform2D<f32>); - fn snapshot(&self) -> SourceSurface; - fn stroke( - &mut self, - path: &Path, - pattern: Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, - ); - fn stroke_line( - &mut self, - start: Point2D<f32>, - end: Point2D<f32>, - pattern: Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, - ); - fn stroke_rect( - &mut self, - rect: &Rect<f32>, - pattern: Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, - ); - fn snapshot_data(&self) -> &[u8]; -} - -pub enum GradientStop { - Raqote(raqote::GradientStop), -} - -pub enum GradientStops { - Raqote(Vec<raqote::GradientStop>), -} - -#[derive(Clone)] -pub enum Color { - Raqote(raqote::SolidSource), -} - -#[derive(Clone)] -pub enum CompositionOp { - Raqote(raqote::BlendMode), -} - -#[derive(Clone)] -pub enum SourceSurface { - Raqote(Vec<u8>), // TODO: See if we can avoid the alloc (probably?) -} - -#[derive(Clone)] -pub enum Path { - Raqote(raqote::Path), -} - -#[derive(Clone)] -pub enum Pattern<'a> { - Raqote(crate::raqote_backend::Pattern<'a>), -} - -#[derive(Clone)] -pub enum DrawOptions { - Raqote(raqote::DrawOptions), -} - -#[derive(Clone)] -pub enum StrokeOptions { - Raqote(raqote::StrokeStyle), -} - #[derive(Clone, Copy)] -pub enum Filter { +pub(crate) enum Filter { Bilinear, Nearest, } -pub struct CanvasData<'a> { - backend: Box<dyn Backend>, - drawtarget: Box<dyn GenericDrawTarget>, - path_state: Option<PathState>, - state: CanvasPaintState<'a>, - saved_states: Vec<CanvasPaintState<'a>>, +pub(crate) struct CanvasData<'a, B: Backend> { + backend: B, + drawtarget: B::DrawTarget, + path_state: Option<PathState<B>>, + state: CanvasPaintState<'a, B>, + saved_states: Vec<CanvasPaintState<'a, B>>, compositor_api: CrossProcessCompositorApi, image_key: ImageKey, font_context: Arc<FontContext>, } -fn create_backend() -> Box<dyn Backend> { - Box::new(crate::raqote_backend::RaqoteBackend) -} - -impl<'a> CanvasData<'a> { - pub fn new( +impl<'a, B: Backend> CanvasData<'a, B> { + pub(crate) fn new( size: Size2D<u64>, compositor_api: CrossProcessCompositorApi, font_context: Arc<FontContext>, - ) -> CanvasData<'a> { + backend: B, + ) -> CanvasData<'a, B> { let size = size.max(MIN_WR_IMAGE_SIZE); - let backend = create_backend(); let draw_target = backend.create_drawtarget(size); let image_key = compositor_api.generate_image_key().unwrap(); let descriptor = ImageDescriptor { @@ -614,10 +429,10 @@ impl<'a> CanvasData<'a> { SerializableImageData::Raw(IpcSharedMemory::from_bytes(draw_target.snapshot_data())); compositor_api.update_images(vec![ImageUpdate::AddImage(image_key, descriptor, data)]); CanvasData { + state: backend.new_paint_state(), backend, drawtarget: draw_target, path_state: None, - state: CanvasPaintState::default(), saved_states: vec![], compositor_api, image_key, @@ -625,11 +440,11 @@ impl<'a> CanvasData<'a> { } } - pub fn image_key(&self) -> ImageKey { + pub(crate) fn image_key(&self) -> ImageKey { self.image_key } - pub fn draw_image( + pub(crate) fn draw_image( &mut self, image_data: &[u8], image_size: Size2D<u64>, @@ -648,8 +463,8 @@ impl<'a> CanvasData<'a> { }; let draw_options = self.state.draw_options.clone(); - let writer = |draw_target: &mut dyn GenericDrawTarget| { - write_image( + let writer = |draw_target: &mut B::DrawTarget| { + write_image::<B>( draw_target, image_data, source_rect.size, @@ -669,15 +484,15 @@ impl<'a> CanvasData<'a> { // TODO(pylbrecht) pass another closure for raqote self.draw_with_shadow(&rect, writer); } else { - writer(&mut *self.drawtarget); + writer(&mut self.drawtarget); } } - pub fn save_context_state(&mut self) { + pub(crate) fn save_context_state(&mut self) { self.saved_states.push(self.state.clone()); } - pub fn restore_context_state(&mut self) { + pub(crate) fn restore_context_state(&mut self) { if let Some(state) = self.saved_states.pop() { let _ = mem::replace(&mut self.state, state); self.drawtarget.set_transform(&self.state.transform); @@ -685,7 +500,7 @@ impl<'a> CanvasData<'a> { } } - pub fn fill_text_with_size( + pub(crate) fn fill_text_with_size( &mut self, text: String, x: f64, @@ -764,7 +579,7 @@ impl<'a> CanvasData<'a> { } /// <https://html.spec.whatwg.org/multipage/#text-preparation-algorithm> - pub fn fill_text( + pub(crate) fn fill_text( &mut self, text: String, x: f64, @@ -782,7 +597,7 @@ impl<'a> CanvasData<'a> { /// <https://html.spec.whatwg.org/multipage/#text-preparation-algorithm> /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-measuretext> - pub fn measure_text(&mut self, text: String) -> TextMetrics { + pub(crate) fn measure_text(&mut self, text: String) -> TextMetrics { // > Step 2: Replace all ASCII whitespace in text with U+0020 SPACE characters. let text = replace_ascii_whitespace(text); let Some(ref font_style) = self.state.font_style else { @@ -934,49 +749,15 @@ impl<'a> CanvasData<'a> { point2(x + anchor_x, y + anchor_y) } - pub fn fill_rect(&mut self, rect: &Rect<f32>) { + pub(crate) fn fill_rect(&mut self, rect: &Rect<f32>) { if self.state.fill_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } - let draw_rect = match &self.state.fill_style { - Pattern::Raqote(pattern) => match pattern { - crate::raqote_backend::Pattern::Surface(pattern) => { - let pattern_rect = Rect::new(Point2D::origin(), pattern.size()); - let mut draw_rect = rect.intersection(&pattern_rect).unwrap_or(Rect::zero()); - - match pattern.repetition() { - Repetition::NoRepeat => { - draw_rect.size.width = - draw_rect.size.width.min(pattern_rect.size.width); - draw_rect.size.height = - draw_rect.size.height.min(pattern_rect.size.height); - }, - Repetition::RepeatX => { - draw_rect.size.width = rect.size.width; - draw_rect.size.height = - draw_rect.size.height.min(pattern_rect.size.height); - }, - Repetition::RepeatY => { - draw_rect.size.height = rect.size.height; - draw_rect.size.width = - draw_rect.size.width.min(pattern_rect.size.width); - }, - Repetition::Repeat => { - draw_rect = *rect; - }, - } - - draw_rect - }, - crate::raqote_backend::Pattern::Color(..) | - crate::raqote_backend::Pattern::LinearGradient(..) | - crate::raqote_backend::Pattern::RadialGradient(..) => *rect, - }, - }; + let draw_rect = self.state.fill_style.draw_rect(rect); if self.need_to_draw_shadow() { - self.draw_with_shadow(&draw_rect, |new_draw_target: &mut dyn GenericDrawTarget| { + self.draw_with_shadow(&draw_rect, |new_draw_target: &mut B::DrawTarget| { new_draw_target.fill_rect( &draw_rect, self.state.fill_style.clone(), @@ -992,17 +773,17 @@ impl<'a> CanvasData<'a> { } } - pub fn clear_rect(&mut self, rect: &Rect<f32>) { + pub(crate) fn clear_rect(&mut self, rect: &Rect<f32>) { self.drawtarget.clear_rect(rect); } - pub fn stroke_rect(&mut self, rect: &Rect<f32>) { + pub(crate) fn stroke_rect(&mut self, rect: &Rect<f32>) { if self.state.stroke_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } if self.need_to_draw_shadow() { - self.draw_with_shadow(rect, |new_draw_target: &mut dyn GenericDrawTarget| { + self.draw_with_shadow(rect, |new_draw_target: &mut B::DrawTarget| { new_draw_target.stroke_rect( rect, self.state.stroke_style.clone(), @@ -1030,12 +811,12 @@ impl<'a> CanvasData<'a> { } } - pub fn begin_path(&mut self) { + pub(crate) fn begin_path(&mut self) { // Erase any traces of previous paths that existed before this. self.path_state = None; } - pub fn close_path(&mut self) { + pub(crate) fn close_path(&mut self) { self.path_builder().close(); } @@ -1097,14 +878,14 @@ impl<'a> CanvasData<'a> { assert!(self.path_state.as_ref().unwrap().is_path()) } - fn path(&self) -> &Path { + fn path(&self) -> &B::Path { self.path_state .as_ref() .expect("Should have called ensure_path()") .path() } - pub fn fill(&mut self) { + pub(crate) fn fill(&mut self) { if self.state.fill_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } @@ -1113,16 +894,16 @@ impl<'a> CanvasData<'a> { self.drawtarget.fill( &self.path().clone(), self.state.fill_style.clone(), - &self.state.draw_options, + &self.state.draw_options.clone(), ); } - pub fn fill_path(&mut self, path: &[PathSegment]) { + pub(crate) fn fill_path(&mut self, path: &[PathSegment]) { if self.state.fill_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } - let path = to_path(path, self.drawtarget.create_path_builder()); + let path = to_path::<B>(path, self.drawtarget.create_path_builder()); self.drawtarget.fill( &path, @@ -1131,7 +912,7 @@ impl<'a> CanvasData<'a> { ); } - pub fn stroke(&mut self) { + pub(crate) fn stroke(&mut self) { if self.state.stroke_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } @@ -1145,12 +926,12 @@ impl<'a> CanvasData<'a> { ); } - pub fn stroke_path(&mut self, path: &[PathSegment]) { + pub(crate) fn stroke_path(&mut self, path: &[PathSegment]) { if self.state.stroke_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } - let path = to_path(path, self.drawtarget.create_path_builder()); + let path = to_path::<B>(path, self.drawtarget.create_path_builder()); self.drawtarget.stroke( &path, @@ -1160,18 +941,18 @@ impl<'a> CanvasData<'a> { ); } - pub fn clip(&mut self) { + pub(crate) fn clip(&mut self) { self.ensure_path(); let path = self.path().clone(); self.drawtarget.push_clip(&path); } - pub fn clip_path(&mut self, path: &[PathSegment]) { - let path = to_path(path, self.drawtarget.create_path_builder()); + pub(crate) fn clip_path(&mut self, path: &[PathSegment]) { + let path = to_path::<B>(path, self.drawtarget.create_path_builder()); self.drawtarget.push_clip(&path); } - pub fn is_point_in_path( + pub(crate) fn is_point_in_path( &mut self, x: f64, y: f64, @@ -1190,7 +971,7 @@ impl<'a> CanvasData<'a> { chan.send(result).unwrap(); } - pub fn is_point_in_path_( + pub(crate) fn is_point_in_path_( &mut self, path: &[PathSegment], x: f64, @@ -1202,7 +983,7 @@ impl<'a> CanvasData<'a> { Some(PathState::UserSpacePath(_, Some(transform))) => transform, Some(_) | None => &self.drawtarget.get_transform(), }; - let result = to_path(path, self.drawtarget.create_path_builder()).contains_point( + let result = to_path::<B>(path, self.drawtarget.create_path_builder()).contains_point( x, y, path_transform, @@ -1210,15 +991,15 @@ impl<'a> CanvasData<'a> { chan.send(result).unwrap(); } - pub fn move_to(&mut self, point: &Point2D<f32>) { + pub(crate) fn move_to(&mut self, point: &Point2D<f32>) { self.path_builder().move_to(point); } - pub fn line_to(&mut self, point: &Point2D<f32>) { + pub(crate) fn line_to(&mut self, point: &Point2D<f32>) { self.path_builder().line_to(point); } - fn path_builder(&mut self) -> PathBuilderRef { + fn path_builder(&mut self) -> PathBuilderRef<B> { if self.path_state.is_none() { self.path_state = Some(PathState::UserSpacePathBuilder( self.drawtarget.create_path_builder(), @@ -1283,18 +1064,18 @@ impl<'a> CanvasData<'a> { } } - pub fn rect(&mut self, rect: &Rect<f32>) { + pub(crate) fn rect(&mut self, rect: &Rect<f32>) { self.path_builder().rect(rect); } - pub fn quadratic_curve_to(&mut self, cp: &Point2D<f32>, endpoint: &Point2D<f32>) { + pub(crate) fn quadratic_curve_to(&mut self, cp: &Point2D<f32>, endpoint: &Point2D<f32>) { if self.path_state.is_none() { self.move_to(cp); } self.path_builder().quadratic_curve_to(cp, endpoint); } - pub fn bezier_curve_to( + pub(crate) fn bezier_curve_to( &mut self, cp1: &Point2D<f32>, cp2: &Point2D<f32>, @@ -1306,7 +1087,7 @@ impl<'a> CanvasData<'a> { self.path_builder().bezier_curve_to(cp1, cp2, endpoint); } - pub fn arc( + pub(crate) fn arc( &mut self, center: &Point2D<f32>, radius: f32, @@ -1318,12 +1099,12 @@ impl<'a> CanvasData<'a> { .arc(center, radius, start_angle, end_angle, ccw); } - pub fn arc_to(&mut self, cp1: &Point2D<f32>, cp2: &Point2D<f32>, radius: f32) { + pub(crate) fn arc_to(&mut self, cp1: &Point2D<f32>, cp2: &Point2D<f32>, radius: f32) { self.path_builder().arc_to(cp1, cp2, radius); } #[allow(clippy::too_many_arguments)] - pub fn ellipse( + pub(crate) fn ellipse( &mut self, center: &Point2D<f32>, radius_x: f32, @@ -1344,45 +1125,45 @@ impl<'a> CanvasData<'a> { ); } - pub fn set_fill_style(&mut self, style: FillOrStrokeStyle) { + pub(crate) fn set_fill_style(&mut self, style: FillOrStrokeStyle) { self.backend - .set_fill_style(style, &mut self.state, &*self.drawtarget); + .set_fill_style(style, &mut self.state, &self.drawtarget); } - pub fn set_stroke_style(&mut self, style: FillOrStrokeStyle) { + pub(crate) fn set_stroke_style(&mut self, style: FillOrStrokeStyle) { self.backend - .set_stroke_style(style, &mut self.state, &*self.drawtarget); + .set_stroke_style(style, &mut self.state, &self.drawtarget); } - pub fn set_line_width(&mut self, width: f32) { + pub(crate) fn set_line_width(&mut self, width: f32) { self.state.stroke_opts.set_line_width(width); } - pub fn set_line_cap(&mut self, cap: LineCapStyle) { + pub(crate) fn set_line_cap(&mut self, cap: LineCapStyle) { self.state.stroke_opts.set_line_cap(cap); } - pub fn set_line_join(&mut self, join: LineJoinStyle) { + pub(crate) fn set_line_join(&mut self, join: LineJoinStyle) { self.state.stroke_opts.set_line_join(join); } - pub fn set_miter_limit(&mut self, limit: f32) { + pub(crate) fn set_miter_limit(&mut self, limit: f32) { self.state.stroke_opts.set_miter_limit(limit); } - pub fn set_line_dash(&mut self, items: Vec<f32>) { + pub(crate) fn set_line_dash(&mut self, items: Vec<f32>) { self.state.stroke_opts.set_line_dash(items); } - pub fn set_line_dash_offset(&mut self, offset: f32) { + pub(crate) fn set_line_dash_offset(&mut self, offset: f32) { self.state.stroke_opts.set_line_dash_offset(offset); } - pub fn get_transform(&self) -> Transform2D<f32> { + pub(crate) fn get_transform(&self) -> Transform2D<f32> { self.drawtarget.get_transform() } - pub fn set_transform(&mut self, transform: &Transform2D<f32>) { + pub(crate) fn set_transform(&mut self, transform: &Transform2D<f32>) { // If there is an in-progress path, store the existing transformation required // to move between device and user space. match self.path_state.as_mut() { @@ -1398,32 +1179,28 @@ impl<'a> CanvasData<'a> { self.drawtarget.set_transform(transform) } - pub fn set_global_alpha(&mut self, alpha: f32) { + pub(crate) fn set_global_alpha(&mut self, alpha: f32) { self.state.draw_options.set_alpha(alpha); } - pub fn set_global_composition(&mut self, op: CompositionOrBlending) { + pub(crate) fn set_global_composition(&mut self, op: B::CompositionOrBlending) { self.backend.set_global_composition(op, &mut self.state); } - pub fn recreate(&mut self, size: Option<Size2D<u64>>) { + pub(crate) fn recreate(&mut self, size: Option<Size2D<u64>>) { let size = size .unwrap_or_else(|| self.drawtarget.get_size().to_u64()) .max(MIN_WR_IMAGE_SIZE); self.drawtarget = self .backend .create_drawtarget(Size2D::new(size.width, size.height)); - self.state = self.backend.recreate_paint_state(&self.state); + self.state = self.backend.new_paint_state(); self.saved_states.clear(); self.update_image_rendering(); } - pub fn snapshot(&self) { - self.drawtarget.snapshot_data(); - } - /// Update image in WebRender - pub fn update_image_rendering(&mut self) { + pub(crate) fn update_image_rendering(&mut self) { let descriptor = ImageDescriptor { size: self.drawtarget.get_size().cast_unit(), stride: None, @@ -1444,7 +1221,7 @@ impl<'a> CanvasData<'a> { } // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata - pub fn put_image_data(&mut self, mut imagedata: Vec<u8>, rect: Rect<u64>) { + pub(crate) fn put_image_data(&mut self, mut imagedata: Vec<u8>, rect: Rect<u64>) { assert_eq!(imagedata.len() % 4, 0); assert_eq!(rect.size.area() as usize, imagedata.len() / 4); pixels::rgba8_byte_swap_and_premultiply_inplace(&mut imagedata); @@ -1459,31 +1236,31 @@ impl<'a> CanvasData<'a> { ); } - pub fn set_shadow_offset_x(&mut self, value: f64) { + pub(crate) fn set_shadow_offset_x(&mut self, value: f64) { self.state.shadow_offset_x = value; } - pub fn set_shadow_offset_y(&mut self, value: f64) { + pub(crate) fn set_shadow_offset_y(&mut self, value: f64) { self.state.shadow_offset_y = value; } - pub fn set_shadow_blur(&mut self, value: f64) { + pub(crate) fn set_shadow_blur(&mut self, value: f64) { self.state.shadow_blur = value; } - pub fn set_shadow_color(&mut self, value: AbsoluteColor) { + pub(crate) fn set_shadow_color(&mut self, value: AbsoluteColor) { self.backend.set_shadow_color(value, &mut self.state); } - pub fn set_font(&mut self, font_style: FontStyleStruct) { + pub(crate) fn set_font(&mut self, font_style: FontStyleStruct) { self.state.font_style = Some(ServoArc::new(font_style)) } - pub fn set_text_align(&mut self, text_align: TextAlign) { + pub(crate) fn set_text_align(&mut self, text_align: TextAlign) { self.state.text_align = text_align; } - pub fn set_text_baseline(&mut self, text_baseline: TextBaseline) { + pub(crate) fn set_text_baseline(&mut self, text_baseline: TextBaseline) { self.state.text_baseline = text_baseline; } @@ -1495,7 +1272,7 @@ impl<'a> CanvasData<'a> { self.state.shadow_blur != 0.0f64) } - fn create_draw_target_for_shadow(&self, source_rect: &Rect<f32>) -> Box<dyn GenericDrawTarget> { + fn create_draw_target_for_shadow(&self, source_rect: &Rect<f32>) -> B::DrawTarget { let mut draw_target = self.drawtarget.create_similar_draw_target(&Size2D::new( source_rect.size.width as i32, source_rect.size.height as i32, @@ -1509,11 +1286,11 @@ impl<'a> CanvasData<'a> { fn draw_with_shadow<F>(&self, rect: &Rect<f32>, draw_shadow_source: F) where - F: FnOnce(&mut dyn GenericDrawTarget), + F: FnOnce(&mut B::DrawTarget), { let shadow_src_rect = self.state.transform.outer_transformed_rect(rect); let mut new_draw_target = self.create_draw_target_for_shadow(&shadow_src_rect); - draw_shadow_source(&mut *new_draw_target); + draw_shadow_source(&mut new_draw_target); self.drawtarget.draw_surface_with_shadow( new_draw_target.snapshot(), &Point2D::new(shadow_src_rect.origin.x, shadow_src_rect.origin.y), @@ -1531,7 +1308,7 @@ impl<'a> CanvasData<'a> { /// canvas_size: The size of the canvas we're reading from /// read_rect: The area of the canvas we want to read from #[allow(unsafe_code)] - pub fn read_pixels( + pub(crate) fn read_pixels( &self, read_rect: Option<Rect<u64>>, canvas_size: Option<Size2D<u64>>, @@ -1564,7 +1341,7 @@ impl<'a> CanvasData<'a> { } } -impl Drop for CanvasData<'_> { +impl<B: Backend> Drop for CanvasData<'_, B> { fn drop(&mut self) { self.compositor_api .update_images(vec![ImageUpdate::DeleteImage(self.image_key)]); @@ -1575,20 +1352,21 @@ const HANGING_BASELINE_DEFAULT: f32 = 0.8; const IDEOGRAPHIC_BASELINE_DEFAULT: f32 = 0.5; #[derive(Clone)] -pub struct CanvasPaintState<'a> { - pub draw_options: DrawOptions, - pub fill_style: Pattern<'a>, - pub stroke_style: Pattern<'a>, - pub stroke_opts: StrokeOptions, +pub(crate) struct CanvasPaintState<'a, B: Backend> { + pub(crate) draw_options: B::DrawOptions, + pub(crate) fill_style: B::Pattern<'a>, + pub(crate) stroke_style: B::Pattern<'a>, + pub(crate) stroke_opts: B::StrokeOptions, /// The current 2D transform matrix. - pub transform: Transform2D<f32>, - pub shadow_offset_x: f64, - pub shadow_offset_y: f64, - pub shadow_blur: f64, - pub shadow_color: Color, - pub font_style: Option<ServoArc<FontStyleStruct>>, - pub text_align: TextAlign, - pub text_baseline: TextBaseline, + pub(crate) transform: Transform2D<f32>, + pub(crate) shadow_offset_x: f64, + pub(crate) shadow_offset_y: f64, + pub(crate) shadow_blur: f64, + pub(crate) shadow_color: B::Color, + pub(crate) font_style: Option<ServoArc<FontStyleStruct>>, + pub(crate) text_align: TextAlign, + pub(crate) text_baseline: TextBaseline, + pub(crate) _backend: PhantomData<B>, } /// It writes an image to the destination target @@ -1598,14 +1376,14 @@ pub struct CanvasPaintState<'a> { /// dest_rect: Area of the destination target where the pixels will be copied /// smoothing_enabled: It determines if smoothing is applied to the image result /// premultiply: Determines whenever the image data should be premultiplied or not -fn write_image( - draw_target: &mut dyn GenericDrawTarget, +fn write_image<B: Backend>( + draw_target: &mut B::DrawTarget, mut image_data: Vec<u8>, image_size: Size2D<f64>, dest_rect: Rect<f64>, smoothing_enabled: bool, premultiply: bool, - draw_options: &DrawOptions, + draw_options: &B::DrawOptions, ) { if image_data.is_empty() { return; @@ -1634,25 +1412,11 @@ fn write_image( draw_target.draw_surface(source_surface, dest_rect, image_rect, filter, draw_options); } -pub trait RectToi32 { - fn to_i32(&self) -> Rect<i32>; +pub(crate) trait RectToi32 { fn ceil(&self) -> Rect<f64>; } impl RectToi32 for Rect<f64> { - fn to_i32(&self) -> Rect<i32> { - Rect::new( - Point2D::new( - self.origin.x.to_i32().unwrap(), - self.origin.y.to_i32().unwrap(), - ), - Size2D::new( - self.size.width.to_i32().unwrap(), - self.size.height.to_i32().unwrap(), - ), - ) - } - fn ceil(&self) -> Rect<f64> { Rect::new( Point2D::new(self.origin.x.ceil(), self.origin.y.ceil()), @@ -1661,22 +1425,6 @@ impl RectToi32 for Rect<f64> { } } -pub trait RectExt { - fn to_u64(&self) -> Rect<u64>; -} - -impl RectExt for Rect<f64> { - fn to_u64(&self) -> Rect<u64> { - self.cast() - } -} - -impl RectExt for Rect<u32> { - fn to_u64(&self) -> Rect<u64> { - self.cast() - } -} - fn replace_ascii_whitespace(text: String) -> String { text.chars() .map(|c| match c { diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index bb940d7ef81..82a221d560d 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -11,18 +11,21 @@ use canvas_traits::ConstellationCanvasMsg; use canvas_traits::canvas::*; use compositing_traits::CrossProcessCompositorApi; use crossbeam_channel::{Sender, select, unbounded}; -use euclid::default::Size2D; +use euclid::default::{Point2D, Rect, Size2D, Transform2D}; use fonts::{FontContext, SystemFontServiceProxy}; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use log::warn; use net_traits::ResourceThreads; +use style::color::AbsoluteColor; +use style::properties::style_structs::Font as FontStyleStruct; use webrender_api::ImageKey; use crate::canvas_data::*; +use crate::raqote_backend::RaqoteBackend; pub struct CanvasPaintThread<'a> { - canvases: HashMap<CanvasId, CanvasData<'a>>, + canvases: HashMap<CanvasId, Canvas<'a>>, next_canvas_id: CanvasId, compositor_api: CrossProcessCompositorApi, font_context: Arc<FontContext>, @@ -113,10 +116,14 @@ impl<'a> CanvasPaintThread<'a> { let canvas_id = self.next_canvas_id; self.next_canvas_id.0 += 1; - let canvas_data = - CanvasData::new(size, self.compositor_api.clone(), self.font_context.clone()); + let canvas_data = CanvasData::new( + size, + self.compositor_api.clone(), + self.font_context.clone(), + RaqoteBackend, + ); let image_key = canvas_data.image_key(); - self.canvases.insert(canvas_id, canvas_data); + self.canvases.insert(canvas_id, Canvas::Raqote(canvas_data)); (canvas_id, image_key) } @@ -276,7 +283,347 @@ impl<'a> CanvasPaintThread<'a> { } } - fn canvas(&mut self, canvas_id: CanvasId) -> &mut CanvasData<'a> { + fn canvas(&mut self, canvas_id: CanvasId) -> &mut Canvas<'a> { self.canvases.get_mut(&canvas_id).expect("Bogus canvas id") } } + +enum Canvas<'a> { + Raqote(CanvasData<'a, RaqoteBackend>), +} + +impl Canvas<'_> { + fn set_fill_style(&mut self, style: FillOrStrokeStyle) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_fill_style(style), + } + } + + fn fill(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.fill(), + } + } + + fn fill_text(&mut self, text: String, x: f64, y: f64, max_width: Option<f64>, is_rtl: bool) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.fill_text(text, x, y, max_width, is_rtl), + } + } + + fn fill_rect(&mut self, rect: &Rect<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.fill_rect(rect), + } + } + + fn set_stroke_style(&mut self, style: FillOrStrokeStyle) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_stroke_style(style), + } + } + + fn stroke_rect(&mut self, rect: &Rect<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.stroke_rect(rect), + } + } + + fn begin_path(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.begin_path(), + } + } + + fn close_path(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.close_path(), + } + } + + fn fill_path(&mut self, path: &[PathSegment]) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.fill_path(path), + } + } + + fn stroke(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.stroke(), + } + } + + fn stroke_path(&mut self, path: &[PathSegment]) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.stroke_path(path), + } + } + + fn clip(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.clip(), + } + } + + fn is_point_in_path(&mut self, x: f64, y: f64, fill_rule: FillRule, chan: IpcSender<bool>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.is_point_in_path(x, y, fill_rule, chan), + } + } + + fn is_point_in_path_( + &mut self, + path: &[PathSegment], + x: f64, + y: f64, + fill_rule: FillRule, + chan: IpcSender<bool>, + ) { + match self { + Canvas::Raqote(canvas_data) => { + canvas_data.is_point_in_path_(path, x, y, fill_rule, chan) + }, + } + } + + fn clear_rect(&mut self, rect: &Rect<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.clear_rect(rect), + } + } + + fn draw_image( + &mut self, + data: &[u8], + size: Size2D<u64>, + dest_rect: Rect<f64>, + source_rect: Rect<f64>, + smoothing_enabled: bool, + is_premultiplied: bool, + ) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.draw_image( + data, + size, + dest_rect, + source_rect, + smoothing_enabled, + is_premultiplied, + ), + } + } + + fn read_pixels( + &mut self, + read_rect: Option<Rect<u64>>, + canvas_size: Option<Size2D<u64>>, + ) -> snapshot::Snapshot { + match self { + Canvas::Raqote(canvas_data) => canvas_data.read_pixels(read_rect, canvas_size), + } + } + + fn move_to(&mut self, point: &Point2D<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.move_to(point), + } + } + + fn line_to(&mut self, point: &Point2D<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.line_to(point), + } + } + + fn rect(&mut self, rect: &Rect<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.rect(rect), + } + } + + fn quadratic_curve_to(&mut self, cp: &Point2D<f32>, pt: &Point2D<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.quadratic_curve_to(cp, pt), + } + } + + fn bezier_curve_to(&mut self, cp1: &Point2D<f32>, cp2: &Point2D<f32>, pt: &Point2D<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.bezier_curve_to(cp1, cp2, pt), + } + } + + fn arc(&mut self, center: &Point2D<f32>, radius: f32, start: f32, end: f32, ccw: bool) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.arc(center, radius, start, end, ccw), + } + } + + fn arc_to(&mut self, cp1: &Point2D<f32>, cp2: &Point2D<f32>, radius: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.arc_to(cp1, cp2, radius), + } + } + + #[allow(clippy::too_many_arguments)] + fn ellipse( + &mut self, + center: &Point2D<f32>, + radius_x: f32, + radius_y: f32, + rotation: f32, + start: f32, + end: f32, + ccw: bool, + ) { + match self { + Canvas::Raqote(canvas_data) => { + canvas_data.ellipse(center, radius_x, radius_y, rotation, start, end, ccw) + }, + } + } + + fn restore_context_state(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.restore_context_state(), + } + } + + fn save_context_state(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.save_context_state(), + } + } + + fn set_line_width(&mut self, width: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_width(width), + } + } + + fn set_line_cap(&mut self, cap: LineCapStyle) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_cap(cap), + } + } + + fn set_line_join(&mut self, join: LineJoinStyle) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_join(join), + } + } + + fn set_miter_limit(&mut self, limit: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_miter_limit(limit), + } + } + + fn set_line_dash(&mut self, items: Vec<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_dash(items), + } + } + + fn set_line_dash_offset(&mut self, offset: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_dash_offset(offset), + } + } + + fn set_transform(&mut self, matrix: &Transform2D<f32>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_transform(matrix), + } + } + + fn set_global_alpha(&mut self, alpha: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_global_alpha(alpha), + } + } + + fn set_global_composition(&mut self, op: CompositionOrBlending) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_global_composition(op), + } + } + + fn set_shadow_offset_x(&mut self, value: f64) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_shadow_offset_x(value), + } + } + + fn set_shadow_offset_y(&mut self, value: f64) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_shadow_offset_y(value), + } + } + + fn set_shadow_blur(&mut self, value: f64) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_shadow_blur(value), + } + } + + fn set_shadow_color(&mut self, color: AbsoluteColor) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_shadow_color(color), + } + } + + fn set_font(&mut self, font_style: FontStyleStruct) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_font(font_style), + } + } + + fn set_text_align(&mut self, text_align: TextAlign) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_text_align(text_align), + } + } + + fn set_text_baseline(&mut self, text_baseline: TextBaseline) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_text_baseline(text_baseline), + } + } + + fn measure_text(&mut self, text: String) -> TextMetrics { + match self { + Canvas::Raqote(canvas_data) => canvas_data.measure_text(text), + } + } + + fn clip_path(&mut self, path: &[PathSegment]) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.clip_path(path), + } + } + + fn get_transform(&self) -> Transform2D<f32> { + match self { + Canvas::Raqote(canvas_data) => canvas_data.get_transform(), + } + } + + fn put_image_data(&mut self, unwrap: Vec<u8>, rect: Rect<u64>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.put_image_data(unwrap, rect), + } + } + + fn update_image_rendering(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.update_image_rendering(), + } + } + + fn recreate(&mut self, size: Option<Size2D<u64>>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.recreate(size), + } + } +} diff --git a/components/canvas/lib.rs b/components/canvas/lib.rs index 86c291fdc87..91ab58b0e8b 100644 --- a/components/canvas/lib.rs +++ b/components/canvas/lib.rs @@ -4,6 +4,7 @@ #![deny(unsafe_code)] +mod backend; mod raqote_backend; pub mod canvas_data; diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index 12137e41f41..e88de701336 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -17,10 +17,11 @@ use range::Range; use raqote::PathOp; use style::color::AbsoluteColor; -use crate::canvas_data::{ - self, Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, Filter, GenericDrawTarget, - GenericPathBuilder, GradientStop, GradientStops, Path, SourceSurface, StrokeOptions, TextRun, +use crate::backend::{ + Backend, DrawOptionsHelpers, GenericDrawTarget, GenericPathBuilder, PathHelpers, + PatternHelpers, StrokeOptionsHelpers, }; +use crate::canvas_data::{CanvasPaintState, Filter, TextRun}; thread_local! { /// The shared font cache used by all canvases that render on a thread. It would be nicer @@ -30,80 +31,85 @@ thread_local! { static SHARED_FONT_CACHE: RefCell<HashMap<FontIdentifier, Font>> = RefCell::default(); } -#[derive(Default)] -pub struct RaqoteBackend; +#[derive(Clone, Default)] +pub(crate) struct RaqoteBackend; impl Backend for RaqoteBackend { - fn get_composition_op(&self, opts: &DrawOptions) -> CompositionOp { - CompositionOp::Raqote(opts.as_raqote().blend_mode) + type Pattern<'a> = Pattern<'a>; + type StrokeOptions = raqote::StrokeStyle; + type Color = raqote::SolidSource; + type DrawOptions = raqote::DrawOptions; + type CompositionOp = raqote::BlendMode; + type CompositionOrBlending = CompositionOrBlending; + type DrawTarget = raqote::DrawTarget; + type PathBuilder = PathBuilder; + type SourceSurface = Vec<u8>; // TODO: See if we can avoid the alloc (probably?) + type Path = raqote::Path; + type GradientStop = raqote::GradientStop; + type GradientStops = Vec<raqote::GradientStop>; + + fn get_composition_op(&self, opts: &Self::DrawOptions) -> Self::CompositionOp { + opts.blend_mode } - fn need_to_draw_shadow(&self, color: &Color) -> bool { - color.as_raqote().a != 0 + fn need_to_draw_shadow(&self, color: &Self::Color) -> bool { + color.a != 0 } - fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_>) { - state.shadow_color = Color::Raqote(color.to_raqote_style()); + fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_, Self>) { + state.shadow_color = color.to_raqote_style(); } fn set_fill_style( &mut self, style: FillOrStrokeStyle, - state: &mut CanvasPaintState<'_>, - _drawtarget: &dyn GenericDrawTarget, + state: &mut CanvasPaintState<'_, Self>, + _drawtarget: &Self::DrawTarget, ) { if let Some(pattern) = style.to_raqote_pattern() { - state.fill_style = canvas_data::Pattern::Raqote(pattern); + state.fill_style = pattern; } } fn set_stroke_style( &mut self, style: FillOrStrokeStyle, - state: &mut CanvasPaintState<'_>, - _drawtarget: &dyn GenericDrawTarget, + state: &mut CanvasPaintState<'_, Self>, + _drawtarget: &Self::DrawTarget, ) { if let Some(pattern) = style.to_raqote_pattern() { - state.stroke_style = canvas_data::Pattern::Raqote(pattern); + state.stroke_style = pattern; } } fn set_global_composition( &mut self, - op: CompositionOrBlending, - state: &mut CanvasPaintState<'_>, + op: Self::CompositionOrBlending, + state: &mut CanvasPaintState<'_, Self>, ) { - state.draw_options.as_raqote_mut().blend_mode = op.to_raqote_style(); + state.draw_options.blend_mode = op.to_raqote_style(); } - fn create_drawtarget(&self, size: Size2D<u64>) -> Box<dyn GenericDrawTarget> { - Box::new(raqote::DrawTarget::new( - size.width as i32, - size.height as i32, - )) + fn create_drawtarget(&self, size: Size2D<u64>) -> Self::DrawTarget { + raqote::DrawTarget::new(size.width as i32, size.height as i32) } - fn recreate_paint_state<'a>(&self, _state: &CanvasPaintState<'a>) -> CanvasPaintState<'a> { - CanvasPaintState::default() - } -} - -impl Default for CanvasPaintState<'_> { - fn default() -> Self { + fn new_paint_state<'a>(&self) -> CanvasPaintState<'a, Self> { let pattern = Pattern::Color(255, 0, 0, 0); CanvasPaintState { - draw_options: DrawOptions::Raqote(raqote::DrawOptions::new()), - fill_style: canvas_data::Pattern::Raqote(pattern.clone()), - stroke_style: canvas_data::Pattern::Raqote(pattern), - stroke_opts: StrokeOptions::Raqote(Default::default()), + draw_options: raqote::DrawOptions::new(), + fill_style: pattern.clone(), + stroke_style: pattern, + stroke_opts: Default::default(), transform: Transform2D::identity(), shadow_offset_x: 0.0, shadow_offset_y: 0.0, shadow_blur: 0.0, - shadow_color: Color::Raqote(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)), + shadow_color: raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0), font_style: None, text_align: TextAlign::default(), text_baseline: TextBaseline::default(), + _backend: std::marker::PhantomData, } } } @@ -230,136 +236,122 @@ impl Repetition { } } -impl canvas_data::Pattern<'_> { - pub fn source(&self) -> raqote::Source { +pub fn source<'a>(pattern: &Pattern<'a>) -> raqote::Source<'a> { + match pattern { + Pattern::Color(a, r, g, b) => raqote::Source::Solid( + raqote::SolidSource::from_unpremultiplied_argb(*a, *r, *g, *b), + ), + Pattern::LinearGradient(pattern) => raqote::Source::new_linear_gradient( + pattern.gradient.clone(), + pattern.start, + pattern.end, + raqote::Spread::Pad, + ), + Pattern::RadialGradient(pattern) => raqote::Source::new_two_circle_radial_gradient( + pattern.gradient.clone(), + pattern.center1, + pattern.radius1, + pattern.center2, + pattern.radius2, + raqote::Spread::Pad, + ), + Pattern::Surface(pattern) => raqote::Source::Image( + pattern.image, + pattern.extend, + pattern.filter, + pattern.transform, + ), + } +} + +impl PatternHelpers for Pattern<'_> { + fn is_zero_size_gradient(&self) -> bool { match self { - canvas_data::Pattern::Raqote(pattern) => match pattern { - Pattern::Color(a, r, g, b) => raqote::Source::Solid( - raqote::SolidSource::from_unpremultiplied_argb(*a, *r, *g, *b), - ), - Pattern::LinearGradient(pattern) => raqote::Source::new_linear_gradient( - pattern.gradient.clone(), - pattern.start, - pattern.end, - raqote::Spread::Pad, - ), - Pattern::RadialGradient(pattern) => raqote::Source::new_two_circle_radial_gradient( - pattern.gradient.clone(), - pattern.center1, - pattern.radius1, - pattern.center2, - pattern.radius2, - raqote::Spread::Pad, - ), - Pattern::Surface(pattern) => raqote::Source::Image( - pattern.image, - pattern.extend, - pattern.filter, - pattern.transform, - ), + Pattern::RadialGradient(pattern) => { + let centers_equal = pattern.center1 == pattern.center2; + let radii_equal = pattern.radius1 == pattern.radius2; + (centers_equal && radii_equal) || pattern.gradient.stops.is_empty() + }, + Pattern::LinearGradient(pattern) => { + (pattern.start == pattern.end) || pattern.gradient.stops.is_empty() }, + Pattern::Color(..) | Pattern::Surface(..) => false, } } - pub fn is_zero_size_gradient(&self) -> bool { + + fn draw_rect(&self, rect: &Rect<f32>) -> Rect<f32> { match self { - canvas_data::Pattern::Raqote(pattern) => match pattern { - Pattern::RadialGradient(pattern) => { - let centers_equal = pattern.center1 == pattern.center2; - let radii_equal = pattern.radius1 == pattern.radius2; - (centers_equal && radii_equal) || pattern.gradient.stops.is_empty() - }, - Pattern::LinearGradient(pattern) => { - (pattern.start == pattern.end) || pattern.gradient.stops.is_empty() - }, - Pattern::Color(..) | Pattern::Surface(..) => false, + Pattern::Surface(pattern) => { + let pattern_rect = Rect::new(Point2D::origin(), pattern.size()); + let mut draw_rect = rect.intersection(&pattern_rect).unwrap_or(Rect::zero()); + + match pattern.repetition() { + Repetition::NoRepeat => { + draw_rect.size.width = draw_rect.size.width.min(pattern_rect.size.width); + draw_rect.size.height = draw_rect.size.height.min(pattern_rect.size.height); + }, + Repetition::RepeatX => { + draw_rect.size.width = rect.size.width; + draw_rect.size.height = draw_rect.size.height.min(pattern_rect.size.height); + }, + Repetition::RepeatY => { + draw_rect.size.height = rect.size.height; + draw_rect.size.width = draw_rect.size.width.min(pattern_rect.size.width); + }, + Repetition::Repeat => { + draw_rect = *rect; + }, + } + + draw_rect }, + Pattern::Color(..) | Pattern::LinearGradient(..) | Pattern::RadialGradient(..) => *rect, } } } -impl StrokeOptions { - pub fn set_line_width(&mut self, _val: f32) { - match self { - StrokeOptions::Raqote(options) => options.width = _val, - } +impl StrokeOptionsHelpers for raqote::StrokeStyle { + fn set_line_width(&mut self, _val: f32) { + self.width = _val; } - pub fn set_miter_limit(&mut self, _val: f32) { - match self { - StrokeOptions::Raqote(options) => options.miter_limit = _val, - } + fn set_miter_limit(&mut self, _val: f32) { + self.miter_limit = _val; } - pub fn set_line_join(&mut self, val: LineJoinStyle) { - match self { - StrokeOptions::Raqote(options) => options.join = val.to_raqote_style(), - } + fn set_line_join(&mut self, val: LineJoinStyle) { + self.join = val.to_raqote_style(); } - pub fn set_line_cap(&mut self, val: LineCapStyle) { - match self { - StrokeOptions::Raqote(options) => options.cap = val.to_raqote_style(), - } + fn set_line_cap(&mut self, val: LineCapStyle) { + self.cap = val.to_raqote_style(); } - pub fn set_line_dash(&mut self, items: Vec<f32>) { - match self { - StrokeOptions::Raqote(options) => options.dash_array = items, - } + fn set_line_dash(&mut self, items: Vec<f32>) { + self.dash_array = items; } - pub fn set_line_dash_offset(&mut self, offset: f32) { - match self { - StrokeOptions::Raqote(options) => options.dash_offset = offset, - } - } - pub fn as_raqote(&self) -> &raqote::StrokeStyle { - match self { - StrokeOptions::Raqote(options) => options, - } + fn set_line_dash_offset(&mut self, offset: f32) { + self.dash_offset = offset; } } -impl DrawOptions { - pub fn set_alpha(&mut self, val: f32) { - match self { - DrawOptions::Raqote(draw_options) => draw_options.alpha = val, - } - } - pub fn as_raqote(&self) -> &raqote::DrawOptions { - match self { - DrawOptions::Raqote(options) => options, - } - } - fn as_raqote_mut(&mut self) -> &mut raqote::DrawOptions { - match self { - DrawOptions::Raqote(options) => options, - } +impl DrawOptionsHelpers for raqote::DrawOptions { + fn set_alpha(&mut self, val: f32) { + self.alpha = val; } } -impl Path { - pub fn transformed_copy_to_builder( - &self, - transform: &Transform2D<f32>, - ) -> Box<dyn GenericPathBuilder> { - Box::new(PathBuilder(Some(raqote::PathBuilder::from( - self.as_raqote().clone().transform(transform), - )))) +impl PathHelpers<RaqoteBackend> for raqote::Path { + fn transformed_copy_to_builder(&self, transform: &Transform2D<f32>) -> PathBuilder { + PathBuilder(Some(raqote::PathBuilder::from( + self.clone().transform(transform), + ))) } - pub fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D<f32>) -> bool { - self.as_raqote() - .clone() + fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D<f32>) -> bool { + self.clone() .transform(path_transform) .contains_point(0.1, x as f32, y as f32) } - pub fn copy_to_builder(&self) -> Box<dyn GenericPathBuilder> { - Box::new(PathBuilder(Some(raqote::PathBuilder::from( - self.as_raqote().clone(), - )))) - } - - pub fn as_raqote(&self) -> &raqote::Path { - match self { - Path::Raqote(p) => p, - } + fn copy_to_builder(&self) -> PathBuilder { + PathBuilder(Some(raqote::PathBuilder::from(self.clone()))) } } @@ -373,7 +365,7 @@ fn create_gradient_stops(gradient_stops: Vec<CanvasGradientStop>) -> Vec<raqote: stops } -impl GenericDrawTarget for raqote::DrawTarget { +impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget { fn clear_rect(&mut self, rect: &Rect<f32>) { let mut pb = raqote::PathBuilder::new(); pb.rect( @@ -385,59 +377,47 @@ impl GenericDrawTarget for raqote::DrawTarget { let mut options = raqote::DrawOptions::new(); options.blend_mode = raqote::BlendMode::Clear; let pattern = Pattern::Color(0, 0, 0, 0); - GenericDrawTarget::fill( - self, - &Path::Raqote(pb.finish()), - canvas_data::Pattern::Raqote(pattern), - &DrawOptions::Raqote(options), - ); + <Self as GenericDrawTarget<RaqoteBackend>>::fill(self, &pb.finish(), pattern, &options); } #[allow(unsafe_code)] fn copy_surface( &mut self, - surface: SourceSurface, + surface: <RaqoteBackend as Backend>::SourceSurface, source: Rect<i32>, destination: Point2D<i32>, ) { let mut dt = raqote::DrawTarget::new(source.size.width, source.size.height); - let data = surface.as_raqote(); + let data = surface; let s = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / 4) }; dt.get_data_mut().copy_from_slice(s); raqote::DrawTarget::copy_surface(self, &dt, source.to_box2d(), destination); } - // TODO(pylbrecht) - // Somehow a duplicate of `create_gradient_stops()` with different types. - // It feels cumbersome to convert GradientStop back and forth just to use - // `create_gradient_stops()`, so I'll leave this here for now. - fn create_gradient_stops(&self, gradient_stops: Vec<GradientStop>) -> GradientStops { - let mut stops = gradient_stops - .into_iter() - .map(|item| *item.as_raqote()) - .collect::<Vec<raqote::GradientStop>>(); - // https://www.w3.org/html/test/results/2dcontext/annotated-spec/canvas.html#testrefs.2d.gradient.interpolate.overlap - stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap()); - GradientStops::Raqote(stops) - } - fn create_path_builder(&self) -> Box<dyn GenericPathBuilder> { - Box::new(PathBuilder::new()) + fn create_path_builder(&self) -> <RaqoteBackend as Backend>::PathBuilder { + PathBuilder::new() } - fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Box<dyn GenericDrawTarget> { - Box::new(raqote::DrawTarget::new(size.width, size.height)) + fn create_similar_draw_target( + &self, + size: &Size2D<i32>, + ) -> <RaqoteBackend as Backend>::DrawTarget { + raqote::DrawTarget::new(size.width, size.height) } - fn create_source_surface_from_data(&self, data: &[u8]) -> Option<SourceSurface> { - Some(SourceSurface::Raqote(data.to_vec())) + fn create_source_surface_from_data( + &self, + data: &[u8], + ) -> Option<<RaqoteBackend as Backend>::SourceSurface> { + Some(data.to_vec()) } #[allow(unsafe_code)] fn draw_surface( &mut self, - surface: SourceSurface, + surface: <RaqoteBackend as Backend>::SourceSurface, dest: Rect<f64>, source: Rect<f64>, filter: Filter, - draw_options: &DrawOptions, + draw_options: &<RaqoteBackend as Backend>::DrawOptions, ) { - let surface_data = surface.as_raqote(); + let surface_data = surface; let image = raqote::Image { width: source.size.width as i32, height: source.size.height as i32, @@ -470,33 +450,29 @@ impl GenericDrawTarget for raqote::DrawTarget { dest.size.height as f32, ); - GenericDrawTarget::fill( - self, - &Path::Raqote(pb.finish()), - canvas_data::Pattern::Raqote(pattern), - draw_options, - ); + <Self as GenericDrawTarget<RaqoteBackend>>::fill(self, &pb.finish(), pattern, draw_options); } fn draw_surface_with_shadow( &self, - _surface: SourceSurface, + _surface: <RaqoteBackend as Backend>::SourceSurface, _dest: &Point2D<f32>, - _color: &Color, + _color: &<RaqoteBackend as Backend>::Color, _offset: &Vector2D<f32>, _sigma: f32, - _operator: CompositionOp, + _operator: <RaqoteBackend as Backend>::CompositionOp, ) { warn!("no support for drawing shadows"); } - fn fill(&mut self, path: &Path, pattern: canvas_data::Pattern, draw_options: &DrawOptions) { - match draw_options.as_raqote().blend_mode { + fn fill( + &mut self, + path: &<RaqoteBackend as Backend>::Path, + pattern: <RaqoteBackend as Backend>::Pattern<'_>, + draw_options: &<RaqoteBackend as Backend>::DrawOptions, + ) { + match draw_options.blend_mode { raqote::BlendMode::Src => { self.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)); - self.fill( - path.as_raqote(), - &pattern.source(), - draw_options.as_raqote(), - ); + self.fill(path, &source(&pattern), draw_options); }, raqote::BlendMode::Clear | raqote::BlendMode::SrcAtop | @@ -505,26 +481,19 @@ impl GenericDrawTarget for raqote::DrawTarget { raqote::BlendMode::Xor | raqote::BlendMode::DstOver | raqote::BlendMode::SrcOver => { - self.fill( - path.as_raqote(), - &pattern.source(), - draw_options.as_raqote(), - ); + self.fill(path, &source(&pattern), draw_options); }, raqote::BlendMode::SrcIn | raqote::BlendMode::SrcOut | raqote::BlendMode::DstIn | raqote::BlendMode::DstAtop => { - let mut options = *draw_options.as_raqote(); + let mut options = *draw_options; self.push_layer_with_blend(1., options.blend_mode); options.blend_mode = raqote::BlendMode::SrcOver; - self.fill(path.as_raqote(), &pattern.source(), &options); + self.fill(path, &source(&pattern), &options); self.pop_layer(); }, - _ => warn!( - "unrecognized blend mode: {:?}", - draw_options.as_raqote().blend_mode - ), + _ => warn!("unrecognized blend mode: {:?}", draw_options.blend_mode), } } @@ -532,8 +501,8 @@ impl GenericDrawTarget for raqote::DrawTarget { &mut self, text_runs: Vec<TextRun>, start: Point2D<f32>, - pattern: &canvas_data::Pattern, - draw_options: &DrawOptions, + pattern: &<RaqoteBackend as Backend>::Pattern<'_>, + draw_options: &<RaqoteBackend as Backend>::DrawOptions, ) { let mut advance = 0.; for run in text_runs.iter() { @@ -581,8 +550,8 @@ impl GenericDrawTarget for raqote::DrawTarget { run.font.descriptor.pt_size.to_f32_px(), &ids, &positions, - &pattern.source(), - draw_options.as_raqote(), + &source(pattern), + draw_options, ); }) } @@ -591,8 +560,8 @@ impl GenericDrawTarget for raqote::DrawTarget { fn fill_rect( &mut self, rect: &Rect<f32>, - pattern: canvas_data::Pattern, - draw_options: Option<&DrawOptions>, + pattern: <RaqoteBackend as Backend>::Pattern<'_>, + draw_options: Option<&<RaqoteBackend as Backend>::DrawOptions>, ) { let mut pb = raqote::PathBuilder::new(); pb.rect( @@ -602,16 +571,16 @@ impl GenericDrawTarget for raqote::DrawTarget { rect.size.height, ); let draw_options = if let Some(options) = draw_options { - *options.as_raqote() + *options } else { raqote::DrawOptions::new() }; - GenericDrawTarget::fill( + <Self as GenericDrawTarget<RaqoteBackend>>::fill( self, - &Path::Raqote(pb.finish()), + &pb.finish(), pattern, - &DrawOptions::Raqote(draw_options), + &draw_options, ); } fn get_size(&self) -> Size2D<i32> { @@ -623,41 +592,36 @@ impl GenericDrawTarget for raqote::DrawTarget { fn pop_clip(&mut self) { self.pop_clip(); } - fn push_clip(&mut self, path: &Path) { - self.push_clip(path.as_raqote()); + fn push_clip(&mut self, path: &<RaqoteBackend as Backend>::Path) { + self.push_clip(path); } fn set_transform(&mut self, matrix: &Transform2D<f32>) { self.set_transform(matrix); } - fn snapshot(&self) -> SourceSurface { - SourceSurface::Raqote(self.snapshot_data().to_vec()) + fn snapshot(&self) -> <RaqoteBackend as Backend>::SourceSurface { + self.snapshot_data().to_vec() } fn stroke( &mut self, - path: &Path, - pattern: canvas_data::Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, + path: &<RaqoteBackend as Backend>::Path, + pattern: Pattern<'_>, + stroke_options: &<RaqoteBackend as Backend>::StrokeOptions, + draw_options: &<RaqoteBackend as Backend>::DrawOptions, ) { - self.stroke( - path.as_raqote(), - &pattern.source(), - stroke_options.as_raqote(), - draw_options.as_raqote(), - ); + self.stroke(path, &source(&pattern), stroke_options, draw_options); } fn stroke_line( &mut self, start: Point2D<f32>, end: Point2D<f32>, - pattern: canvas_data::Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, + pattern: <RaqoteBackend as Backend>::Pattern<'_>, + stroke_options: &<RaqoteBackend as Backend>::StrokeOptions, + draw_options: &<RaqoteBackend as Backend>::DrawOptions, ) { let mut pb = raqote::PathBuilder::new(); pb.move_to(start.x, start.y); pb.line_to(end.x, end.y); - let mut stroke_options = stroke_options.as_raqote().clone(); + let mut stroke_options = stroke_options.clone(); let cap = match stroke_options.join { raqote::LineJoin::Round => raqote::LineCap::Round, _ => raqote::LineCap::Butt, @@ -666,17 +630,17 @@ impl GenericDrawTarget for raqote::DrawTarget { self.stroke( &pb.finish(), - &pattern.source(), + &source(&pattern), &stroke_options, - draw_options.as_raqote(), + draw_options, ); } fn stroke_rect( &mut self, rect: &Rect<f32>, - pattern: canvas_data::Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, + pattern: <RaqoteBackend as Backend>::Pattern<'_>, + stroke_options: &<RaqoteBackend as Backend>::StrokeOptions, + draw_options: &<RaqoteBackend as Backend>::DrawOptions, ) { let mut pb = raqote::PathBuilder::new(); pb.rect( @@ -688,9 +652,9 @@ impl GenericDrawTarget for raqote::DrawTarget { self.stroke( &pb.finish(), - &pattern.source(), - stroke_options.as_raqote(), - draw_options.as_raqote(), + &source(&pattern), + stroke_options, + draw_options, ); } #[allow(unsafe_code)] @@ -709,7 +673,7 @@ impl Filter { } } -struct PathBuilder(Option<raqote::PathBuilder>); +pub(crate) struct PathBuilder(Option<raqote::PathBuilder>); impl PathBuilder { fn new() -> PathBuilder { @@ -717,7 +681,7 @@ impl PathBuilder { } } -impl GenericPathBuilder for PathBuilder { +impl GenericPathBuilder<RaqoteBackend> for PathBuilder { fn arc( &mut self, origin: Point2D<f32>, @@ -726,7 +690,8 @@ impl GenericPathBuilder for PathBuilder { end_angle: f32, anticlockwise: bool, ) { - self.ellipse( + <PathBuilder as GenericPathBuilder<RaqoteBackend>>::ellipse( + self, origin, radius, radius, @@ -840,9 +805,9 @@ impl GenericPathBuilder for PathBuilder { fn get_current_point(&mut self) -> Option<Point2D<f32>> { let path = self.finish(); - self.0 = Some(path.as_raqote().clone().into()); + self.0 = Some(path.clone().into()); - path.as_raqote().ops.iter().last().and_then(|op| match op { + path.ops.iter().last().and_then(|op| match op { PathOp::MoveTo(point) | PathOp::LineTo(point) => Some(Point2D::new(point.x, point.y)), PathOp::CubicTo(_, _, point) => Some(Point2D::new(point.x, point.y)), PathOp::QuadTo(_, point) => Some(Point2D::new(point.x, point.y)), @@ -864,8 +829,8 @@ impl GenericPathBuilder for PathBuilder { end_point.y, ); } - fn finish(&mut self) -> Path { - Path::Raqote(self.0.take().unwrap().finish()) + fn finish(&mut self) -> raqote::Path { + self.0.take().unwrap().finish() } } @@ -977,14 +942,6 @@ impl ToRaqotePattern<'_> for FillOrStrokeStyle { } } -impl Color { - fn as_raqote(&self) -> &raqote::SolidSource { - match self { - Color::Raqote(s) => s, - } - } -} - impl ToRaqoteStyle for AbsoluteColor { type Target = raqote::SolidSource; @@ -1054,19 +1011,3 @@ impl ToRaqoteStyle for CompositionStyle { } } } - -impl SourceSurface { - fn as_raqote(&self) -> &Vec<u8> { - match self { - SourceSurface::Raqote(s) => s, - } - } -} - -impl GradientStop { - fn as_raqote(&self) -> &raqote::GradientStop { - match self { - GradientStop::Raqote(s) => s, - } - } -} diff --git a/components/script/canvas_context.rs b/components/script/canvas_context.rs index d49d31997e1..d85877c0f41 100644 --- a/components/script/canvas_context.rs +++ b/components/script/canvas_context.rs @@ -5,6 +5,7 @@ //! Common interfaces for Canvas Contexts use euclid::default::Size2D; +use script_bindings::root::Dom; use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource}; use snapshot::Snapshot; @@ -12,6 +13,10 @@ use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanva use crate::dom::bindings::inheritance::Castable; use crate::dom::htmlcanvaselement::HTMLCanvasElement; use crate::dom::node::{Node, NodeDamage}; +use crate::dom::types::{ + CanvasRenderingContext2D, GPUCanvasContext, OffscreenCanvas, OffscreenCanvasRenderingContext2D, + WebGL2RenderingContext, WebGLRenderingContext, +}; pub(crate) trait LayoutCanvasRenderingContextHelpers { fn canvas_data_source(self) -> HTMLCanvasDataSource; @@ -85,3 +90,180 @@ impl CanvasHelpers for HTMLCanvasElementOrOffscreenCanvas { } } } + +/// Non rooted variant of [`crate::dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::RenderingContext`] +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +#[derive(Clone, JSTraceable, MallocSizeOf)] +pub(crate) enum RenderingContext { + Placeholder(Dom<OffscreenCanvas>), + Context2d(Dom<CanvasRenderingContext2D>), + WebGL(Dom<WebGLRenderingContext>), + WebGL2(Dom<WebGL2RenderingContext>), + #[cfg(feature = "webgpu")] + WebGPU(Dom<GPUCanvasContext>), +} + +impl CanvasContext for RenderingContext { + type ID = (); + + fn context_id(&self) -> Self::ID {} + + fn canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas { + match self { + RenderingContext::Placeholder(context) => (*context.context().unwrap()).canvas(), + RenderingContext::Context2d(context) => context.canvas(), + RenderingContext::WebGL(context) => context.canvas(), + RenderingContext::WebGL2(context) => context.canvas(), + #[cfg(feature = "webgpu")] + RenderingContext::WebGPU(context) => context.canvas(), + } + } + + fn resize(&self) { + match self { + RenderingContext::Placeholder(context) => (*context.context().unwrap()).resize(), + RenderingContext::Context2d(context) => context.resize(), + RenderingContext::WebGL(context) => context.resize(), + RenderingContext::WebGL2(context) => context.resize(), + #[cfg(feature = "webgpu")] + RenderingContext::WebGPU(context) => context.resize(), + } + } + + fn get_image_data(&self) -> Option<Snapshot> { + match self { + RenderingContext::Placeholder(context) => { + (*context.context().unwrap()).get_image_data() + }, + RenderingContext::Context2d(context) => context.get_image_data(), + RenderingContext::WebGL(context) => context.get_image_data(), + RenderingContext::WebGL2(context) => context.get_image_data(), + #[cfg(feature = "webgpu")] + RenderingContext::WebGPU(context) => context.get_image_data(), + } + } + + fn origin_is_clean(&self) -> bool { + match self { + RenderingContext::Placeholder(context) => { + (*context.context().unwrap()).origin_is_clean() + }, + RenderingContext::Context2d(context) => context.origin_is_clean(), + RenderingContext::WebGL(context) => context.origin_is_clean(), + RenderingContext::WebGL2(context) => context.origin_is_clean(), + #[cfg(feature = "webgpu")] + RenderingContext::WebGPU(context) => context.origin_is_clean(), + } + } + + fn size(&self) -> Size2D<u64> { + match self { + RenderingContext::Placeholder(context) => (*context.context().unwrap()).size(), + RenderingContext::Context2d(context) => context.size(), + RenderingContext::WebGL(context) => context.size(), + RenderingContext::WebGL2(context) => context.size(), + #[cfg(feature = "webgpu")] + RenderingContext::WebGPU(context) => context.size(), + } + } + + fn mark_as_dirty(&self) { + match self { + RenderingContext::Placeholder(context) => (*context.context().unwrap()).mark_as_dirty(), + RenderingContext::Context2d(context) => context.mark_as_dirty(), + RenderingContext::WebGL(context) => context.mark_as_dirty(), + RenderingContext::WebGL2(context) => context.mark_as_dirty(), + #[cfg(feature = "webgpu")] + RenderingContext::WebGPU(context) => context.mark_as_dirty(), + } + } + + fn update_rendering(&self) { + match self { + RenderingContext::Placeholder(context) => { + (*context.context().unwrap()).update_rendering() + }, + RenderingContext::Context2d(context) => context.update_rendering(), + RenderingContext::WebGL(context) => context.update_rendering(), + RenderingContext::WebGL2(context) => context.update_rendering(), + #[cfg(feature = "webgpu")] + RenderingContext::WebGPU(context) => context.update_rendering(), + } + } + + fn onscreen(&self) -> bool { + match self { + RenderingContext::Placeholder(context) => (*context.context().unwrap()).onscreen(), + RenderingContext::Context2d(context) => context.onscreen(), + RenderingContext::WebGL(context) => context.onscreen(), + RenderingContext::WebGL2(context) => context.onscreen(), + #[cfg(feature = "webgpu")] + RenderingContext::WebGPU(context) => context.onscreen(), + } + } +} + +/// Non rooted variant of [`crate::dom::bindings::codegen::Bindings::OffscreenCanvasBinding::OffscreenRenderingContext`] +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +#[derive(Clone, JSTraceable, MallocSizeOf)] +pub(crate) enum OffscreenRenderingContext { + Context2d(Dom<OffscreenCanvasRenderingContext2D>), + //WebGL(Dom<WebGLRenderingContext>), + //WebGL2(Dom<WebGL2RenderingContext>), + //#[cfg(feature = "webgpu")] + //WebGPU(Dom<GPUCanvasContext>), +} + +impl CanvasContext for OffscreenRenderingContext { + type ID = (); + + fn context_id(&self) -> Self::ID {} + + fn canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas { + match self { + OffscreenRenderingContext::Context2d(context) => context.canvas(), + } + } + + fn resize(&self) { + match self { + OffscreenRenderingContext::Context2d(context) => context.resize(), + } + } + + fn get_image_data(&self) -> Option<Snapshot> { + match self { + OffscreenRenderingContext::Context2d(context) => context.get_image_data(), + } + } + + fn origin_is_clean(&self) -> bool { + match self { + OffscreenRenderingContext::Context2d(context) => context.origin_is_clean(), + } + } + + fn size(&self) -> Size2D<u64> { + match self { + OffscreenRenderingContext::Context2d(context) => context.size(), + } + } + + fn mark_as_dirty(&self) { + match self { + OffscreenRenderingContext::Context2d(context) => context.mark_as_dirty(), + } + } + + fn update_rendering(&self) { + match self { + OffscreenRenderingContext::Context2d(context) => context.update_rendering(), + } + } + + fn onscreen(&self) -> bool { + match self { + OffscreenRenderingContext::Context2d(context) => context.onscreen(), + } + } +} diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index e9892818e92..dabe6a5728b 100644 --- a/components/script/canvas_state.rs +++ b/components/script/canvas_state.rs @@ -36,6 +36,7 @@ use style_traits::{CssWriter, ParsingMode}; use url::Url; use webrender_api::ImageKey; +use crate::canvas_context::{OffscreenRenderingContext, RenderingContext}; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{ CanvasDirection, CanvasFillRule, CanvasImageSource, CanvasLineCap, CanvasLineJoin, @@ -52,10 +53,10 @@ use crate::dom::canvaspattern::CanvasPattern; use crate::dom::dommatrix::DOMMatrix; use crate::dom::element::{Element, cors_setting_for_element}; use crate::dom::globalscope::GlobalScope; -use crate::dom::htmlcanvaselement::{CanvasContext, HTMLCanvasElement}; +use crate::dom::htmlcanvaselement::HTMLCanvasElement; use crate::dom::imagedata::ImageData; use crate::dom::node::{Node, NodeTraits}; -use crate::dom::offscreencanvas::{OffscreenCanvas, OffscreenCanvasContext}; +use crate::dom::offscreencanvas::OffscreenCanvas; use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; use crate::dom::textmetrics::TextMetrics; use crate::script_runtime::CanGc; @@ -522,7 +523,7 @@ impl CanvasState { if let Some(context) = canvas.context() { match *context { - OffscreenCanvasContext::OffscreenContext2d(ref context) => { + OffscreenRenderingContext::Context2d(ref context) => { context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther( self.get_canvas_id(), image_size, @@ -577,7 +578,7 @@ impl CanvasState { if let Some(context) = canvas.context() { match *context { - CanvasContext::Context2d(ref context) => { + RenderingContext::Context2d(ref context) => { context.send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther( self.get_canvas_id(), image_size, @@ -586,12 +587,12 @@ impl CanvasState { smoothing_enabled, )); }, - CanvasContext::Placeholder(ref context) => { + RenderingContext::Placeholder(ref context) => { let Some(context) = context.context() else { return Err(Error::InvalidState); }; match *context { - OffscreenCanvasContext::OffscreenContext2d(ref context) => context + OffscreenRenderingContext::Context2d(ref context) => context .send_canvas_2d_msg(Canvas2dMsg::DrawImageInOther( self.get_canvas_id(), image_size, diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index bb27d28cea8..cc6df183f42 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -27,14 +27,13 @@ use servo_media::streams::registry::MediaStreamId; use snapshot::Snapshot; use style::attr::AttrValue; -use crate::canvas_context::CanvasContext as _; pub(crate) use crate::canvas_context::*; use crate::conversions::Convert; use crate::dom::attr::Attr; use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::cell::{DomRefCell, Ref, ref_filter_map}; use crate::dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::{ - BlobCallback, HTMLCanvasElementMethods, RenderingContext, + BlobCallback, HTMLCanvasElementMethods, RenderingContext as RootedRenderingContext, }; use crate::dom::bindings::codegen::Bindings::MediaStreamBinding::MediaStreamMethods; use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes; @@ -104,21 +103,10 @@ impl EncodedImageType { } } -#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] -#[derive(Clone, JSTraceable, MallocSizeOf)] -pub(crate) enum CanvasContext { - Placeholder(Dom<OffscreenCanvas>), - Context2d(Dom<CanvasRenderingContext2D>), - WebGL(Dom<WebGLRenderingContext>), - WebGL2(Dom<WebGL2RenderingContext>), - #[cfg(feature = "webgpu")] - WebGPU(Dom<GPUCanvasContext>), -} - #[dom_struct] pub(crate) struct HTMLCanvasElement { htmlelement: HTMLElement, - context: DomRefCell<Option<CanvasContext>>, + context: DomRefCell<Option<RenderingContext>>, // This id and hashmap are used to keep track of ongoing toBlob() calls. callback_id: Cell<u32>, #[ignore_malloc_size_of = "not implemented for webidl callbacks"] @@ -159,14 +147,7 @@ impl HTMLCanvasElement { fn recreate_contexts_after_resize(&self) { if let Some(ref context) = *self.context.borrow() { - match *context { - CanvasContext::Context2d(ref context) => context.resize(), - CanvasContext::WebGL(ref context) => context.resize(), - CanvasContext::WebGL2(ref context) => context.resize(), - #[cfg(feature = "webgpu")] - CanvasContext::WebGPU(ref context) => context.resize(), - CanvasContext::Placeholder(ref context) => context.resize(self.get_size().cast()), - } + context.resize() } } @@ -176,23 +157,14 @@ impl HTMLCanvasElement { pub(crate) fn origin_is_clean(&self) -> bool { match *self.context.borrow() { - Some(CanvasContext::Context2d(ref context)) => context.origin_is_clean(), + Some(ref context) => context.origin_is_clean(), _ => true, } } pub(crate) fn mark_as_dirty(&self) { if let Some(ref context) = *self.context.borrow() { - match *context { - CanvasContext::Context2d(ref context) => context.mark_as_dirty(), - CanvasContext::WebGL(ref context) => context.mark_as_dirty(), - CanvasContext::WebGL2(ref context) => context.mark_as_dirty(), - #[cfg(feature = "webgpu")] - CanvasContext::WebGPU(ref context) => context.mark_as_dirty(), - CanvasContext::Placeholder(ref _context) => { - // TODO: Should this be marked as dirty? - }, - } + context.mark_as_dirty() } } @@ -222,12 +194,14 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> { fn data(self) -> HTMLCanvasData { let source = unsafe { match self.unsafe_get().context.borrow_for_layout().as_ref() { - Some(CanvasContext::Context2d(context)) => context.to_layout().canvas_data_source(), - Some(CanvasContext::WebGL(context)) => context.to_layout().canvas_data_source(), - Some(CanvasContext::WebGL2(context)) => context.to_layout().canvas_data_source(), + Some(RenderingContext::Context2d(context)) => { + context.to_layout().canvas_data_source() + }, + Some(RenderingContext::WebGL(context)) => context.to_layout().canvas_data_source(), + Some(RenderingContext::WebGL2(context)) => context.to_layout().canvas_data_source(), #[cfg(feature = "webgpu")] - Some(CanvasContext::WebGPU(context)) => context.to_layout().canvas_data_source(), - Some(CanvasContext::Placeholder(_)) | None => HTMLCanvasDataSource::Empty, + Some(RenderingContext::WebGPU(context)) => context.to_layout().canvas_data_source(), + Some(RenderingContext::Placeholder(_)) | None => HTMLCanvasDataSource::Empty, } }; @@ -246,14 +220,14 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> { } impl HTMLCanvasElement { - pub(crate) fn context(&self) -> Option<Ref<CanvasContext>> { + pub(crate) fn context(&self) -> Option<Ref<RenderingContext>> { ref_filter_map(self.context.borrow(), |ctx| ctx.as_ref()) } fn get_or_init_2d_context(&self, can_gc: CanGc) -> Option<DomRoot<CanvasRenderingContext2D>> { if let Some(ctx) = self.context() { return match *ctx { - CanvasContext::Context2d(ref ctx) => Some(DomRoot::from_ref(ctx)), + RenderingContext::Context2d(ref ctx) => Some(DomRoot::from_ref(ctx)), _ => None, }; } @@ -261,7 +235,7 @@ impl HTMLCanvasElement { let window = self.owner_window(); let size = self.get_size(); let context = CanvasRenderingContext2D::new(window.as_global_scope(), self, size, can_gc); - *self.context.borrow_mut() = Some(CanvasContext::Context2d(Dom::from_ref(&*context))); + *self.context.borrow_mut() = Some(RenderingContext::Context2d(Dom::from_ref(&*context))); Some(context) } @@ -273,7 +247,7 @@ impl HTMLCanvasElement { ) -> Option<DomRoot<WebGLRenderingContext>> { if let Some(ctx) = self.context() { return match *ctx { - CanvasContext::WebGL(ref ctx) => Some(DomRoot::from_ref(ctx)), + RenderingContext::WebGL(ref ctx) => Some(DomRoot::from_ref(ctx)), _ => None, }; } @@ -289,7 +263,7 @@ impl HTMLCanvasElement { attrs, can_gc, )?; - *self.context.borrow_mut() = Some(CanvasContext::WebGL(Dom::from_ref(&*context))); + *self.context.borrow_mut() = Some(RenderingContext::WebGL(Dom::from_ref(&*context))); Some(context) } @@ -305,7 +279,7 @@ impl HTMLCanvasElement { } if let Some(ctx) = self.context() { return match *ctx { - CanvasContext::WebGL2(ref ctx) => Some(DomRoot::from_ref(ctx)), + RenderingContext::WebGL2(ref ctx) => Some(DomRoot::from_ref(ctx)), _ => None, }; } @@ -314,7 +288,7 @@ impl HTMLCanvasElement { let attrs = Self::get_gl_attributes(cx, options)?; let canvas = HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(self)); let context = WebGL2RenderingContext::new(&window, &canvas, size, attrs, can_gc)?; - *self.context.borrow_mut() = Some(CanvasContext::WebGL2(Dom::from_ref(&*context))); + *self.context.borrow_mut() = Some(RenderingContext::WebGL2(Dom::from_ref(&*context))); Some(context) } @@ -327,7 +301,7 @@ impl HTMLCanvasElement { fn get_or_init_webgpu_context(&self, can_gc: CanGc) -> Option<DomRoot<GPUCanvasContext>> { if let Some(ctx) = self.context() { return match *ctx { - CanvasContext::WebGPU(ref ctx) => Some(DomRoot::from_ref(ctx)), + RenderingContext::WebGPU(ref ctx) => Some(DomRoot::from_ref(ctx)), _ => None, }; } @@ -341,7 +315,8 @@ impl HTMLCanvasElement { .expect("Failed to get WebGPU channel") .map(|channel| { let context = GPUCanvasContext::new(&global_scope, self, channel, can_gc); - *self.context.borrow_mut() = Some(CanvasContext::WebGPU(Dom::from_ref(&*context))); + *self.context.borrow_mut() = + Some(RenderingContext::WebGPU(Dom::from_ref(&*context))); context }) } @@ -349,8 +324,8 @@ impl HTMLCanvasElement { /// Gets the base WebGLRenderingContext for WebGL or WebGL 2, if exists. pub(crate) fn get_base_webgl_context(&self) -> Option<DomRoot<WebGLRenderingContext>> { match *self.context.borrow() { - Some(CanvasContext::WebGL(ref context)) => Some(DomRoot::from_ref(context)), - Some(CanvasContext::WebGL2(ref context)) => Some(context.base_context()), + Some(RenderingContext::WebGL(ref context)) => Some(DomRoot::from_ref(context)), + Some(RenderingContext::WebGL2(ref context)) => Some(context.base_context()), _ => None, } } @@ -378,12 +353,7 @@ impl HTMLCanvasElement { pub(crate) fn get_image_data(&self) -> Option<Snapshot> { match self.context.borrow().as_ref() { - Some(CanvasContext::Context2d(context)) => context.get_image_data(), - Some(CanvasContext::WebGL(context)) => context.get_image_data(), - Some(CanvasContext::WebGL2(context)) => context.get_image_data(), - #[cfg(feature = "webgpu")] - Some(CanvasContext::WebGPU(context)) => context.get_image_data(), - Some(CanvasContext::Placeholder(context)) => context.get_image_data(), + Some(context) => context.get_image_data(), None => { let size = self.get_size(); if size.width == 0 || size.height == 0 { @@ -466,7 +436,7 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement { // is set to placeholder, the user agent must throw an "InvalidStateError" DOMException and leave the // attribute's value unchanged. fn SetWidth(&self, value: u32, can_gc: CanGc) -> Fallible<()> { - if let Some(CanvasContext::Placeholder(_)) = *self.context.borrow() { + if let Some(RenderingContext::Placeholder(_)) = *self.context.borrow() { return Err(Error::InvalidState); } @@ -485,7 +455,7 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement { // https://html.spec.whatwg.org/multipage/#dom-canvas-height fn SetHeight(&self, value: u32, can_gc: CanGc) -> Fallible<()> { - if let Some(CanvasContext::Placeholder(_)) = *self.context.borrow() { + if let Some(RenderingContext::Placeholder(_)) = *self.context.borrow() { return Err(Error::InvalidState); } @@ -506,26 +476,26 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement { id: DOMString, options: HandleValue, can_gc: CanGc, - ) -> Fallible<Option<RenderingContext>> { + ) -> Fallible<Option<RootedRenderingContext>> { // Always throw an InvalidState exception when the canvas is in Placeholder mode (See table in the spec). - if let Some(CanvasContext::Placeholder(_)) = *self.context.borrow() { + if let Some(RenderingContext::Placeholder(_)) = *self.context.borrow() { return Err(Error::InvalidState); } Ok(match &*id { "2d" => self .get_or_init_2d_context(can_gc) - .map(RenderingContext::CanvasRenderingContext2D), + .map(RootedRenderingContext::CanvasRenderingContext2D), "webgl" | "experimental-webgl" => self .get_or_init_webgl_context(cx, options, can_gc) - .map(RenderingContext::WebGLRenderingContext), + .map(RootedRenderingContext::WebGLRenderingContext), "webgl2" | "experimental-webgl2" => self .get_or_init_webgl2_context(cx, options, can_gc) - .map(RenderingContext::WebGL2RenderingContext), + .map(RootedRenderingContext::WebGL2RenderingContext), #[cfg(feature = "webgpu")] "webgpu" => self .get_or_init_webgpu_context(can_gc) - .map(RenderingContext::GPUCanvasContext), + .map(RootedRenderingContext::GPUCanvasContext), _ => None, }) } @@ -672,7 +642,8 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement { can_gc, ); // Step 4. Set this canvas element's context mode to placeholder. - *self.context.borrow_mut() = Some(CanvasContext::Placeholder(offscreen_canvas.as_traced())); + *self.context.borrow_mut() = + Some(RenderingContext::Placeholder(offscreen_canvas.as_traced())); // Step 5. Return offscreenCanvas. Ok(offscreen_canvas) diff --git a/components/script/dom/offscreencanvas.rs b/components/script/dom/offscreencanvas.rs index aabe5955e12..9947d35f4e0 100644 --- a/components/script/dom/offscreencanvas.rs +++ b/components/script/dom/offscreencanvas.rs @@ -9,9 +9,10 @@ use euclid::default::Size2D; use js::rust::{HandleObject, HandleValue}; use snapshot::Snapshot; +use crate::canvas_context::{CanvasContext, OffscreenRenderingContext}; use crate::dom::bindings::cell::{DomRefCell, Ref, ref_filter_map}; use crate::dom::bindings::codegen::Bindings::OffscreenCanvasBinding::{ - OffscreenCanvasMethods, OffscreenRenderingContext, + OffscreenCanvasMethods, OffscreenRenderingContext as RootedOffscreenRenderingContext, }; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_proto}; @@ -23,20 +24,12 @@ use crate::dom::htmlcanvaselement::HTMLCanvasElement; use crate::dom::offscreencanvasrenderingcontext2d::OffscreenCanvasRenderingContext2D; use crate::script_runtime::{CanGc, JSContext}; -#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] -#[derive(Clone, JSTraceable, MallocSizeOf)] -pub(crate) enum OffscreenCanvasContext { - OffscreenContext2d(Dom<OffscreenCanvasRenderingContext2D>), - //WebGL(Dom<WebGLRenderingContext>), - //WebGL2(Dom<WebGL2RenderingContext>), -} - #[dom_struct] pub(crate) struct OffscreenCanvas { eventtarget: EventTarget, width: Cell<u64>, height: Cell<u64>, - context: DomRefCell<Option<OffscreenCanvasContext>>, + context: DomRefCell<Option<OffscreenRenderingContext>>, placeholder: Option<Dom<HTMLCanvasElement>>, } @@ -77,20 +70,18 @@ impl OffscreenCanvas { pub(crate) fn origin_is_clean(&self) -> bool { match *self.context.borrow() { - Some(OffscreenCanvasContext::OffscreenContext2d(ref context)) => { - context.origin_is_clean() - }, + Some(ref context) => context.origin_is_clean(), _ => true, } } - pub(crate) fn context(&self) -> Option<Ref<OffscreenCanvasContext>> { + pub(crate) fn context(&self) -> Option<Ref<OffscreenRenderingContext>> { ref_filter_map(self.context.borrow(), |ctx| ctx.as_ref()) } pub(crate) fn get_image_data(&self) -> Option<Snapshot> { match self.context.borrow().as_ref() { - Some(OffscreenCanvasContext::OffscreenContext2d(context)) => context.get_image_data(), + Some(context) => context.get_image_data(), None => { let size = self.get_size(); if size.width == 0 || size.height == 0 { @@ -108,13 +99,13 @@ impl OffscreenCanvas { ) -> Option<DomRoot<OffscreenCanvasRenderingContext2D>> { if let Some(ctx) = self.context() { return match *ctx { - OffscreenCanvasContext::OffscreenContext2d(ref ctx) => Some(DomRoot::from_ref(ctx)), + OffscreenRenderingContext::Context2d(ref ctx) => Some(DomRoot::from_ref(ctx)), }; } let context = OffscreenCanvasRenderingContext2D::new(&self.global(), self, can_gc); - *self.context.borrow_mut() = Some(OffscreenCanvasContext::OffscreenContext2d( - Dom::from_ref(&*context), - )); + *self.context.borrow_mut() = Some(OffscreenRenderingContext::Context2d(Dom::from_ref( + &*context, + ))); Some(context) } @@ -125,19 +116,6 @@ impl OffscreenCanvas { pub(crate) fn placeholder(&self) -> Option<&HTMLCanvasElement> { self.placeholder.as_deref() } - - pub(crate) fn resize(&self, size: Size2D<u64>) { - self.width.set(size.width); - self.height.set(size.height); - - if let Some(canvas_context) = self.context() { - match &*canvas_context { - OffscreenCanvasContext::OffscreenContext2d(rendering_context) => { - rendering_context.set_canvas_bitmap_dimensions(self.get_size()); - }, - } - } - } } impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas { @@ -160,11 +138,11 @@ impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas { id: DOMString, _options: HandleValue, can_gc: CanGc, - ) -> Fallible<Option<OffscreenRenderingContext>> { + ) -> Fallible<Option<RootedOffscreenRenderingContext>> { match &*id { "2d" => Ok(self .get_or_init_2d_context(can_gc) - .map(OffscreenRenderingContext::OffscreenCanvasRenderingContext2D)), + .map(RootedOffscreenRenderingContext::OffscreenCanvasRenderingContext2D)), /*"webgl" | "experimental-webgl" => self .get_or_init_webgl_context(cx, options) .map(OffscreenRenderingContext::WebGLRenderingContext), @@ -187,11 +165,7 @@ impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas { self.width.set(value); if let Some(canvas_context) = self.context() { - match &*canvas_context { - OffscreenCanvasContext::OffscreenContext2d(rendering_context) => { - rendering_context.set_canvas_bitmap_dimensions(self.get_size()); - }, - } + canvas_context.resize(); } if let Some(canvas) = &self.placeholder { @@ -209,11 +183,7 @@ impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas { self.height.set(value); if let Some(canvas_context) = self.context() { - match &*canvas_context { - OffscreenCanvasContext::OffscreenContext2d(rendering_context) => { - rendering_context.set_canvas_bitmap_dimensions(self.get_size()); - }, - } + canvas_context.resize(); } if let Some(canvas) = &self.placeholder { diff --git a/components/script/dom/offscreencanvasrenderingcontext2d.rs b/components/script/dom/offscreencanvasrenderingcontext2d.rs index b2d0f3201ca..d7ca0e9dc4d 100644 --- a/components/script/dom/offscreencanvasrenderingcontext2d.rs +++ b/components/script/dom/offscreencanvasrenderingcontext2d.rs @@ -3,11 +3,10 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::codegen::GenericBindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2D_Binding::CanvasRenderingContext2DMethods; -use crate::canvas_context::CanvasContext as _; +use crate::canvas_context::CanvasContext; use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas; use canvas_traits::canvas::Canvas2dMsg; use dom_struct::dom_struct; -use euclid::default::Size2D; use snapshot::Snapshot; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{ @@ -64,21 +63,33 @@ impl OffscreenCanvasRenderingContext2D { reflect_dom_object(boxed, global, can_gc) } - pub(crate) fn set_canvas_bitmap_dimensions(&self, size: Size2D<u64>) { - self.context.set_canvas_bitmap_dimensions(size.cast()); - } - pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) { self.context.send_canvas_2d_msg(msg) } +} - pub(crate) fn origin_is_clean(&self) -> bool { - self.context.origin_is_clean() +impl CanvasContext for OffscreenCanvasRenderingContext2D { + type ID = <CanvasRenderingContext2D as CanvasContext>::ID; + + fn context_id(&self) -> Self::ID { + self.context.context_id() + } + + fn canvas(&self) -> HTMLCanvasElementOrOffscreenCanvas { + self.context.canvas() + } + + fn resize(&self) { + self.context.resize() } - pub(crate) fn get_image_data(&self) -> Option<Snapshot> { + fn get_image_data(&self) -> Option<Snapshot> { self.context.get_image_data() } + + fn origin_is_clean(&self) -> bool { + self.context.origin_is_clean() + } } impl OffscreenCanvasRenderingContext2DMethods<crate::DomTypeHolder> diff --git a/components/script/script_module.rs b/components/script/script_module.rs index c7697adeea6..0aa35a2eda8 100644 --- a/components/script/script_module.rs +++ b/components/script/script_module.rs @@ -1369,7 +1369,7 @@ pub(crate) unsafe extern "C" fn host_import_module_dynamically( true } -#[derive(Clone, JSTraceable, MallocSizeOf)] +#[derive(Clone, Debug, JSTraceable, MallocSizeOf)] /// <https://html.spec.whatwg.org/multipage/#script-fetch-options> pub(crate) struct ScriptFetchOptions { #[no_trace] @@ -1763,7 +1763,8 @@ fn fetch_single_module_script( .mode(mode) .insecure_requests_policy(global.insecure_requests_policy()) .has_trustworthy_ancestor_origin(global.has_trustworthy_ancestor_origin()) - .policy_container(global.policy_container().to_owned()); + .policy_container(global.policy_container().to_owned()) + .cryptographic_nonce_metadata(options.cryptographic_nonce.clone()); let context = Arc::new(Mutex::new(ModuleContext { owner, |