diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index ec8433f44f7..0e76a2c5668 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -15,7 +15,6 @@ use fonts::{ ByteIndex, FontBaseline, FontContext, FontGroup, FontMetrics, FontRef, GlyphInfo, GlyphStore, LAST_RESORT_GLYPH_ADVANCE, ShapingFlags, ShapingOptions, }; -use ipc_channel::ipc::IpcSender; use log::warn; use pixels::Snapshot; use range::Range; @@ -34,180 +33,6 @@ use crate::backend::{ // https://github.com/servo/webrender/blob/main/webrender/src/texture_cache.rs#L1475 const MIN_WR_IMAGE_SIZE: Size2D = Size2D::new(1, 1); -/// 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. -pub(crate) struct PathBuilderRef<'a> { - pub(crate) builder: &'a mut Path, - pub(crate) transform: Transform2D, -} - -impl PathBuilderRef<'_> { - /// - pub(crate) fn line_to(&mut self, pt: &Point2D) { - let pt = self.transform.transform_point(*pt).cast(); - self.builder.line_to(pt.x, pt.y); - } - - pub(crate) fn move_to(&mut self, pt: &Point2D) { - let pt = self.transform.transform_point(*pt).cast(); - self.builder.move_to(pt.x, pt.y); - } - - pub(crate) fn rect(&mut self, rect: &Rect) { - let (first, second, third, fourth) = ( - Point2D::new(rect.origin.x, rect.origin.y), - Point2D::new(rect.origin.x + rect.size.width, rect.origin.y), - Point2D::new( - rect.origin.x + rect.size.width, - rect.origin.y + rect.size.height, - ), - Point2D::new(rect.origin.x, rect.origin.y + rect.size.height), - ); - self.move_to(&first); - self.line_to(&second); - self.line_to(&third); - self.line_to(&fourth); - self.close(); - self.move_to(&first); - } - - /// - pub(crate) fn quadratic_curve_to(&mut self, cp: &Point2D, endpoint: &Point2D) { - let cp = self.transform.transform_point(*cp).cast(); - let endpoint = self.transform.transform_point(*endpoint).cast(); - self.builder - .quadratic_curve_to(cp.x, cp.y, endpoint.x, endpoint.y) - } - - /// - pub(crate) fn bezier_curve_to( - &mut self, - cp1: &Point2D, - cp2: &Point2D, - endpoint: &Point2D, - ) { - let cp1 = self.transform.transform_point(*cp1).cast(); - let cp2 = self.transform.transform_point(*cp2).cast(); - let endpoint = self.transform.transform_point(*endpoint).cast(); - self.builder - .bezier_curve_to(cp1.x, cp1.y, cp2.x, cp2.y, endpoint.x, endpoint.y) - } - - pub(crate) fn arc( - &mut self, - center: &Point2D, - radius: f32, - start_angle: f32, - end_angle: f32, - ccw: bool, - ) { - let center = self.transform.transform_point(*center).cast(); - let _ = self.builder.arc( - center.x, - center.y, - radius as f64, - start_angle as f64, - end_angle as f64, - ccw, - ); - } - - /// - pub(crate) fn arc_to(&mut self, cp1: &Point2D, cp2: &Point2D, radius: f32) { - let cp0 = if let (Some(inverse), Some(point)) = - (self.transform.inverse(), self.builder.last_point()) - { - inverse.transform_point(Point2D::new(point.x, point.y).cast()) - } else { - *cp1 - }; - - // 2. Ensure there is a subpath for (x1, y1) is done by one of self.line_to calls - - if (cp0.x == cp1.x && cp0.y == cp1.y) || cp1 == cp2 || radius == 0.0 { - self.line_to(cp1); - return; - } - - // if all three control points lie on a single straight line, - // connect the first two by a straight line - let direction = (cp2.x - cp1.x) * (cp0.y - cp1.y) + (cp2.y - cp1.y) * (cp1.x - cp0.x); - if direction == 0.0 { - self.line_to(cp1); - return; - } - - // otherwise, draw the Arc - let a2 = (cp0.x - cp1.x).powi(2) + (cp0.y - cp1.y).powi(2); - let b2 = (cp1.x - cp2.x).powi(2) + (cp1.y - cp2.y).powi(2); - let d = { - let c2 = (cp0.x - cp2.x).powi(2) + (cp0.y - cp2.y).powi(2); - let cosx = (a2 + b2 - c2) / (2.0 * (a2 * b2).sqrt()); - let sinx = (1.0 - cosx.powi(2)).sqrt(); - radius / ((1.0 - cosx) / sinx) - }; - - // first tangent point - let anx = (cp1.x - cp0.x) / a2.sqrt(); - let any = (cp1.y - cp0.y) / a2.sqrt(); - let tp1 = Point2D::new(cp1.x - anx * d, cp1.y - any * d); - - // second tangent point - let bnx = (cp1.x - cp2.x) / b2.sqrt(); - let bny = (cp1.y - cp2.y) / b2.sqrt(); - let tp2 = Point2D::new(cp1.x - bnx * d, cp1.y - bny * d); - - // arc center and angles - let anticlockwise = direction < 0.0; - let cx = tp1.x + any * radius * if anticlockwise { 1.0 } else { -1.0 }; - let cy = tp1.y - anx * radius * if anticlockwise { 1.0 } else { -1.0 }; - let angle_start = (tp1.y - cy).atan2(tp1.x - cx); - let angle_end = (tp2.y - cy).atan2(tp2.x - cx); - - self.line_to(&self.transform.transform_point(tp1)); - if [cx, cy, angle_start, angle_end] - .iter() - .all(|x| x.is_finite()) - { - self.arc( - &self.transform.transform_point(Point2D::new(cx, cy)), - radius, - angle_start, - angle_end, - anticlockwise, - ); - } - } - - #[allow(clippy::too_many_arguments)] - pub(crate) fn ellipse( - &mut self, - center: &Point2D, - radius_x: f32, - radius_y: f32, - rotation_angle: f32, - start_angle: f32, - end_angle: f32, - ccw: bool, - ) { - let center = self.transform.transform_point(*center).cast(); - let _ = self.builder.ellipse( - center.x, - center.y, - radius_x as f64, - radius_y as f64, - rotation_angle as f64, - start_angle as f64, - end_angle as f64, - ccw, - ); - } - - pub(crate) fn close(&mut self) { - self.builder.close_path(); - } -} - #[derive(Default)] struct UnshapedTextRun<'a> { font: Option, @@ -287,8 +112,6 @@ pub(crate) enum Filter { pub(crate) struct CanvasData<'a, B: Backend> { backend: B, drawtarget: B::DrawTarget, - /// - path_state: Path, state: CanvasPaintState<'a, B>, saved_states: Vec>, compositor_api: CrossProcessCompositorApi, @@ -312,7 +135,6 @@ impl<'a, B: Backend> CanvasData<'a, B> { state: backend.new_paint_state(), backend, drawtarget: draw_target, - path_state: Path::new(), saved_states: vec![], compositor_api, image_key, @@ -689,61 +511,18 @@ impl<'a, B: Backend> CanvasData<'a, B> { } } - pub(crate) fn begin_path(&mut self) { - // Erase any traces of previous paths that existed before this. - self.path_state = Path::new(); - } - - pub(crate) fn close_path(&mut self) { - self.path_builder().close(); - } - - pub(crate) fn fill(&mut self) { - if self.state.fill_style.is_zero_size_gradient() { - return; // Paint nothing if gradient size is zero. - } - - let path = self.path_state.clone(); - - self.maybe_bound_shape_with_pattern( - self.state.fill_style.clone(), - &path.bounding_box(), - |self_| { - self_.drawtarget.fill( - &path, - &self_.state.fill_style, - &self_.state.draw_options.clone(), - ); - }, - ) - } - pub(crate) fn fill_path(&mut self, path: &Path) { if self.state.fill_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } - self.drawtarget - .fill(path, &self.state.fill_style, &self.state.draw_options); - } - - pub(crate) fn stroke(&mut self) { - if self.state.stroke_style.is_zero_size_gradient() { - return; // Paint nothing if gradient size is zero. - } - - let path = self.path_state.clone(); - self.maybe_bound_shape_with_pattern( - self.state.stroke_style.clone(), + self.state.fill_style.clone(), &path.bounding_box(), |self_| { - self_.drawtarget.stroke( - &path, - &self_.state.stroke_style, - &self_.state.stroke_opts, - &self_.state.draw_options, - ); + self_ + .drawtarget + .fill(path, &self_.state.fill_style, &self_.state.draw_options) }, ) } @@ -767,98 +546,10 @@ impl<'a, B: Backend> CanvasData<'a, B> { ) } - pub(crate) fn clip(&mut self) { - let path = &self.path_state; - - self.drawtarget.push_clip(path); - } - pub(crate) fn clip_path(&mut self, path: &Path) { self.drawtarget.push_clip(path); } - pub(crate) fn is_point_in_path( - &mut self, - x: f64, - y: f64, - fill_rule: FillRule, - chan: IpcSender, - ) { - let mut path = self.path_state.clone(); - path.transform(self.get_transform().cast()); - chan.send(path.is_point_in_path(x, y, fill_rule)).unwrap(); - } - - pub(crate) fn move_to(&mut self, point: &Point2D) { - self.path_builder().move_to(point); - } - - pub(crate) fn line_to(&mut self, point: &Point2D) { - self.path_builder().line_to(point); - } - - fn path_builder(&mut self) -> PathBuilderRef { - PathBuilderRef { - builder: &mut self.path_state, - transform: Transform2D::identity(), - } - } - - pub(crate) fn rect(&mut self, rect: &Rect) { - self.path_builder().rect(rect); - } - - pub(crate) fn quadratic_curve_to(&mut self, cp: &Point2D, endpoint: &Point2D) { - self.path_builder().quadratic_curve_to(cp, endpoint); - } - - pub(crate) fn bezier_curve_to( - &mut self, - cp1: &Point2D, - cp2: &Point2D, - endpoint: &Point2D, - ) { - self.path_builder().bezier_curve_to(cp1, cp2, endpoint); - } - - pub(crate) fn arc( - &mut self, - center: &Point2D, - radius: f32, - start_angle: f32, - end_angle: f32, - ccw: bool, - ) { - self.path_builder() - .arc(center, radius, start_angle, end_angle, ccw); - } - - pub(crate) fn arc_to(&mut self, cp1: &Point2D, cp2: &Point2D, radius: f32) { - self.path_builder().arc_to(cp1, cp2, radius); - } - - #[allow(clippy::too_many_arguments)] - pub(crate) fn ellipse( - &mut self, - center: &Point2D, - radius_x: f32, - radius_y: f32, - rotation_angle: f32, - start_angle: f32, - end_angle: f32, - ccw: bool, - ) { - self.path_builder().ellipse( - center, - radius_x, - radius_y, - rotation_angle, - start_angle, - end_angle, - ccw, - ); - } - pub(crate) fn set_fill_style(&mut self, style: FillOrStrokeStyle) { self.backend .set_fill_style(style, &mut self.state, &self.drawtarget); @@ -898,12 +589,8 @@ impl<'a, B: Backend> CanvasData<'a, B> { } pub(crate) fn set_transform(&mut self, transform: &Transform2D) { - self.path_state.transform(self.get_transform().cast()); self.state.transform = *transform; self.drawtarget.set_transform(transform); - if let Some(inverse) = transform.inverse() { - self.path_state.transform(inverse.cast()); - } } pub(crate) fn set_global_alpha(&mut self, alpha: f32) { @@ -925,9 +612,6 @@ impl<'a, B: Backend> CanvasData<'a, B> { .backend .create_drawtarget(Size2D::new(size.width, size.height)); - // Step 2. Empty the list of subpaths in context's current default path. - self.path_state = Path::new(); - // Step 3. Clear the context's drawing state stack. self.saved_states.clear(); diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index 1429385ad28..c6dca6a877c 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -11,7 +11,7 @@ use canvas_traits::ConstellationCanvasMsg; use canvas_traits::canvas::*; use compositing_traits::CrossProcessCompositorApi; use crossbeam_channel::{Sender, select, unbounded}; -use euclid::default::{Point2D, Rect, Size2D, Transform2D}; +use euclid::default::{Rect, Size2D, Transform2D}; use fonts::{FontContext, SystemFontServiceProxy}; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; @@ -148,29 +148,15 @@ impl<'a> CanvasPaintThread<'a> { self.canvas(canvas_id).stroke_rect(&rect); }, Canvas2dMsg::ClearRect(ref rect) => self.canvas(canvas_id).clear_rect(rect), - Canvas2dMsg::BeginPath => self.canvas(canvas_id).begin_path(), - Canvas2dMsg::ClosePath => self.canvas(canvas_id).close_path(), - Canvas2dMsg::Fill(style) => { - self.canvas(canvas_id).set_fill_style(style); - self.canvas(canvas_id).fill(); - }, Canvas2dMsg::FillPath(style, path) => { self.canvas(canvas_id).set_fill_style(style); self.canvas(canvas_id).fill_path(&path); }, - Canvas2dMsg::Stroke(style) => { - self.canvas(canvas_id).set_stroke_style(style); - self.canvas(canvas_id).stroke(); - }, Canvas2dMsg::StrokePath(style, path) => { self.canvas(canvas_id).set_stroke_style(style); self.canvas(canvas_id).stroke_path(&path); }, - Canvas2dMsg::Clip => self.canvas(canvas_id).clip(), Canvas2dMsg::ClipPath(path) => self.canvas(canvas_id).clip_path(&path), - Canvas2dMsg::IsPointInCurrentPath(x, y, fill_rule, chan) => self - .canvas(canvas_id) - .is_point_in_path(x, y, fill_rule, chan), Canvas2dMsg::DrawImage(snapshot, dest_rect, source_rect, smoothing_enabled) => { self.canvas(canvas_id).draw_image( snapshot.to_owned(), @@ -199,24 +185,6 @@ impl<'a> CanvasPaintThread<'a> { smoothing, ); }, - Canvas2dMsg::MoveTo(ref point) => self.canvas(canvas_id).move_to(point), - Canvas2dMsg::LineTo(ref point) => self.canvas(canvas_id).line_to(point), - Canvas2dMsg::Rect(ref rect) => self.canvas(canvas_id).rect(rect), - Canvas2dMsg::QuadraticCurveTo(ref cp, ref pt) => { - self.canvas(canvas_id).quadratic_curve_to(cp, pt) - }, - Canvas2dMsg::BezierCurveTo(ref cp1, ref cp2, ref pt) => { - self.canvas(canvas_id).bezier_curve_to(cp1, cp2, pt) - }, - Canvas2dMsg::Arc(ref center, radius, start, end, ccw) => { - self.canvas(canvas_id).arc(center, radius, start, end, ccw) - }, - Canvas2dMsg::ArcTo(ref cp1, ref cp2, radius) => { - self.canvas(canvas_id).arc_to(cp1, cp2, radius) - }, - Canvas2dMsg::Ellipse(ref center, radius_x, radius_y, rotation, start, end, ccw) => self - .canvas(canvas_id) - .ellipse(center, radius_x, radius_y, rotation, start, end, ccw), Canvas2dMsg::MeasureText(text, sender) => { let metrics = self.canvas(canvas_id).measure_text(text); sender.send(metrics).unwrap(); @@ -284,12 +252,6 @@ impl Canvas<'_> { } } - 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, is_rtl: bool) { match self { Canvas::Raqote(canvas_data) => canvas_data.fill_text(text, x, y, max_width, is_rtl), @@ -314,48 +276,18 @@ impl Canvas<'_> { } } - 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: &Path) { 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: &Path) { 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) { - match self { - Canvas::Raqote(canvas_data) => canvas_data.is_point_in_path(x, y, fill_rule, chan), - } - } - fn clear_rect(&mut self, rect: &Rect) { match self { Canvas::Raqote(canvas_data) => canvas_data.clear_rect(rect), @@ -386,66 +318,6 @@ impl Canvas<'_> { } } - fn move_to(&mut self, point: &Point2D) { - match self { - Canvas::Raqote(canvas_data) => canvas_data.move_to(point), - } - } - - fn line_to(&mut self, point: &Point2D) { - match self { - Canvas::Raqote(canvas_data) => canvas_data.line_to(point), - } - } - - fn rect(&mut self, rect: &Rect) { - match self { - Canvas::Raqote(canvas_data) => canvas_data.rect(rect), - } - } - - fn quadratic_curve_to(&mut self, cp: &Point2D, pt: &Point2D) { - match self { - Canvas::Raqote(canvas_data) => canvas_data.quadratic_curve_to(cp, pt), - } - } - - fn bezier_curve_to(&mut self, cp1: &Point2D, cp2: &Point2D, pt: &Point2D) { - match self { - Canvas::Raqote(canvas_data) => canvas_data.bezier_curve_to(cp1, cp2, pt), - } - } - - fn arc(&mut self, center: &Point2D, 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, cp2: &Point2D, 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, - 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(), diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index 82fac313525..2721aad1f38 100644 --- a/components/script/canvas_state.rs +++ b/components/script/canvas_state.rs @@ -175,6 +175,9 @@ pub(crate) struct CanvasState { #[no_trace] missing_image_urls: DomRefCell>, saved_states: DomRefCell>, + /// + #[no_trace] + current_default_path: DomRefCell, } impl CanvasState { @@ -212,6 +215,7 @@ impl CanvasState { saved_states: DomRefCell::new(Vec::new()), image_key, origin, + current_default_path: DomRefCell::new(Path::new()), } } @@ -288,6 +292,7 @@ impl CanvasState { } pub(crate) fn reset_to_initial_state(&self) { + *self.current_default_path.borrow_mut() = Path::new(); self.saved_states.borrow_mut().clear(); *self.state.borrow_mut() = CanvasContextState::new(); } @@ -941,8 +946,18 @@ impl CanvasState { (source_rect, dest_rect) } - fn update_transform(&self) { - self.send_canvas_2d_msg(Canvas2dMsg::SetTransform(self.state.borrow().transform)) + fn update_transform(&self, transform: Transform2D) { + let mut state = self.state.borrow_mut(); + self.current_default_path + .borrow_mut() + .transform(state.transform.cast()); + state.transform = transform; + self.send_canvas_2d_msg(Canvas2dMsg::SetTransform(state.transform)); + if let Some(inverse) = transform.inverse() { + self.current_default_path + .borrow_mut() + .transform(inverse.cast()); + } } // https://html.spec.whatwg.org/multipage/#dom-context-2d-fillrect @@ -1830,14 +1845,13 @@ impl CanvasState { // https://html.spec.whatwg.org/multipage/#dom-context-2d-beginpath pub(crate) fn begin_path(&self) { - self.send_canvas_2d_msg(Canvas2dMsg::BeginPath); + *self.current_default_path.borrow_mut() = Path::new(); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-fill - pub(crate) fn fill(&self, _fill_rule: CanvasFillRule) { - // TODO: Process fill rule - let style = self.state.borrow().fill_style.to_fill_or_stroke_style(); - self.send_canvas_2d_msg(Canvas2dMsg::Fill(style)); + pub(crate) fn fill(&self, fill_rule: CanvasFillRule) { + let path = self.current_default_path.borrow().clone(); + self.fill_(path, fill_rule); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-fill @@ -1849,8 +1863,8 @@ impl CanvasState { // https://html.spec.whatwg.org/multipage/#dom-context-2d-stroke pub(crate) fn stroke(&self) { - let style = self.state.borrow().stroke_style.to_fill_or_stroke_style(); - self.send_canvas_2d_msg(Canvas2dMsg::Stroke(style)); + let path = self.current_default_path.borrow().clone(); + self.stroke_(path); } pub(crate) fn stroke_(&self, path: Path) { @@ -1859,9 +1873,9 @@ impl CanvasState { } // https://html.spec.whatwg.org/multipage/#dom-context-2d-clip - pub(crate) fn clip(&self, _fill_rule: CanvasFillRule) { - // TODO: Process fill rule - self.send_canvas_2d_msg(Canvas2dMsg::Clip); + pub(crate) fn clip(&self, fill_rule: CanvasFillRule) { + let path = self.current_default_path.borrow().clone(); + self.clip_(path, fill_rule); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-clip @@ -1878,18 +1892,9 @@ impl CanvasState { y: f64, fill_rule: CanvasFillRule, ) -> bool { - if !(x.is_finite() && y.is_finite()) { - return false; - } - - let fill_rule = match fill_rule { - CanvasFillRule::Nonzero => FillRule::Nonzero, - CanvasFillRule::Evenodd => FillRule::Evenodd, - }; - let (sender, receiver) = - profiled_ipc::channel::(global.time_profiler_chan().clone()).unwrap(); - self.send_canvas_2d_msg(Canvas2dMsg::IsPointInCurrentPath(x, y, fill_rule, sender)); - receiver.recv().unwrap() + let mut path = self.current_default_path.borrow().clone(); + path.transform(self.state.borrow().transform.cast()); + self.is_point_in_path_(global, path, x, y, fill_rule) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-ispointinpath @@ -1915,8 +1920,7 @@ impl CanvasState { } let transform = self.state.borrow().transform; - self.state.borrow_mut().transform = transform.pre_scale(x as f32, y as f32); - self.update_transform() + self.update_transform(transform.pre_scale(x as f32, y as f32)) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-rotate @@ -1927,10 +1931,10 @@ impl CanvasState { let (sin, cos) = (angle.sin(), angle.cos()); let transform = self.state.borrow().transform; - self.state.borrow_mut().transform = + self.update_transform( Transform2D::new(cos as f32, sin as f32, -sin as f32, cos as f32, 0.0, 0.0) - .then(&transform); - self.update_transform() + .then(&transform), + ) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-translate @@ -1940,8 +1944,7 @@ impl CanvasState { } let transform = self.state.borrow().transform; - self.state.borrow_mut().transform = transform.pre_translate(vec2(x as f32, y as f32)); - self.update_transform() + self.update_transform(transform.pre_translate(vec2(x as f32, y as f32))) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-transform @@ -1957,10 +1960,10 @@ impl CanvasState { } let transform = self.state.borrow().transform; - self.state.borrow_mut().transform = + self.update_transform( Transform2D::new(a as f32, b as f32, c as f32, d as f32, e as f32, f as f32) - .then(&transform); - self.update_transform() + .then(&transform), + ) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-gettransform @@ -1983,9 +1986,9 @@ impl CanvasState { } // Step 2. Reset the current transformation matrix to the matrix described by: - self.state.borrow_mut().transform = - Transform2D::new(a as f32, b as f32, c as f32, d as f32, e as f32, f as f32); - self.update_transform() + self.update_transform(Transform2D::new( + a as f32, b as f32, c as f32, d as f32, e as f32, f as f32, + )) } /// @@ -2008,58 +2011,42 @@ impl CanvasState { } // Step 3. Reset the current transformation matrix to matrix. - self.state.borrow_mut().transform = matrix.cast(); - self.update_transform(); + self.update_transform(matrix.cast()); Ok(()) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-resettransform pub(crate) fn reset_transform(&self) { - self.state.borrow_mut().transform = Transform2D::identity(); - self.update_transform() + self.update_transform(Transform2D::identity()) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath pub(crate) fn close_path(&self) { - self.send_canvas_2d_msg(Canvas2dMsg::ClosePath); + self.current_default_path.borrow_mut().close_path(); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto pub(crate) fn move_to(&self, x: f64, y: f64) { - if !(x.is_finite() && y.is_finite()) { - return; - } - self.send_canvas_2d_msg(Canvas2dMsg::MoveTo(Point2D::new(x as f32, y as f32))); + self.current_default_path.borrow_mut().move_to(x, y); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-lineto pub(crate) fn line_to(&self, x: f64, y: f64) { - if !(x.is_finite() && y.is_finite()) { - return; - } - self.send_canvas_2d_msg(Canvas2dMsg::LineTo(Point2D::new(x as f32, y as f32))); + self.current_default_path.borrow_mut().line_to(x, y); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-rect pub(crate) fn rect(&self, x: f64, y: f64, width: f64, height: f64) { - if [x, y, width, height].iter().all(|val| val.is_finite()) { - let rect = Rect::new( - Point2D::new(x as f32, y as f32), - Size2D::new(width as f32, height as f32), - ); - self.send_canvas_2d_msg(Canvas2dMsg::Rect(rect)); - } + self.current_default_path + .borrow_mut() + .rect(x, y, width, height); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-quadraticcurveto pub(crate) fn quadratic_curve_to(&self, cpx: f64, cpy: f64, x: f64, y: f64) { - if !(cpx.is_finite() && cpy.is_finite() && x.is_finite() && y.is_finite()) { - return; - } - self.send_canvas_2d_msg(Canvas2dMsg::QuadraticCurveTo( - Point2D::new(cpx as f32, cpy as f32), - Point2D::new(x as f32, y as f32), - )); + self.current_default_path + .borrow_mut() + .quadratic_curve_to(cpx, cpy, x, y); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-beziercurveto @@ -2072,20 +2059,9 @@ impl CanvasState { x: f64, y: f64, ) { - if !(cp1x.is_finite() && - cp1y.is_finite() && - cp2x.is_finite() && - cp2y.is_finite() && - x.is_finite() && - y.is_finite()) - { - return; - } - self.send_canvas_2d_msg(Canvas2dMsg::BezierCurveTo( - Point2D::new(cp1x as f32, cp1y as f32), - Point2D::new(cp2x as f32, cp2y as f32), - Point2D::new(x as f32, y as f32), - )); + self.current_default_path + .borrow_mut() + .bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-arc @@ -2098,39 +2074,18 @@ impl CanvasState { end: f64, ccw: bool, ) -> ErrorResult { - if !([x, y, r, start, end].iter().all(|x| x.is_finite())) { - return Ok(()); - } - - if r < 0.0 { - return Err(Error::IndexSize); - } - - self.send_canvas_2d_msg(Canvas2dMsg::Arc( - Point2D::new(x as f32, y as f32), - r as f32, - start as f32, - end as f32, - ccw, - )); - Ok(()) + self.current_default_path + .borrow_mut() + .arc(x, y, r, start, end, ccw) + .map_err(|_| Error::IndexSize) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-arcto pub(crate) fn arc_to(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, r: f64) -> ErrorResult { - if !([cp1x, cp1y, cp2x, cp2y, r].iter().all(|x| x.is_finite())) { - return Ok(()); - } - if r < 0.0 { - return Err(Error::IndexSize); - } - - self.send_canvas_2d_msg(Canvas2dMsg::ArcTo( - Point2D::new(cp1x as f32, cp1y as f32), - Point2D::new(cp2x as f32, cp2y as f32), - r as f32, - )); - Ok(()) + self.current_default_path + .borrow_mut() + .arc_to(cp1x, cp1y, cp2x, cp2y, r) + .map_err(|_| Error::IndexSize) } /// @@ -2146,26 +2101,10 @@ impl CanvasState { end: f64, ccw: bool, ) -> ErrorResult { - if !([x, y, rx, ry, rotation, start, end] - .iter() - .all(|x| x.is_finite())) - { - return Ok(()); - } - if rx < 0.0 || ry < 0.0 { - return Err(Error::IndexSize); - } - - self.send_canvas_2d_msg(Canvas2dMsg::Ellipse( - Point2D::new(x as f32, y as f32), - rx as f32, - ry as f32, - rotation as f32, - start as f32, - end as f32, - ccw, - )); - Ok(()) + self.current_default_path + .borrow_mut() + .ellipse(x, y, rx, ry, rotation, start, end, ccw) + .map_err(|_| Error::IndexSize) } } diff --git a/components/shared/canvas/canvas.rs b/components/shared/canvas/canvas.rs index 03db7aa2823..8df9ba163b9 100644 --- a/components/shared/canvas/canvas.rs +++ b/components/shared/canvas/canvas.rs @@ -10,6 +10,7 @@ use euclid::approxeq::ApproxEq; use euclid::default::{Point2D, Rect, Size2D, Transform2D}; use ipc_channel::ipc::IpcSender; use kurbo::{Affine, BezPath, ParamCurveNearest as _, PathEl, Point, Shape, Triangle}; +use malloc_size_of::MallocSizeOf; use malloc_size_of_derive::MallocSizeOf; use pixels::IpcSnapshot; use serde::{Deserialize, Serialize}; @@ -20,6 +21,12 @@ use style::properties::style_structs::Font as FontStyleStruct; #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Path(pub BezPath); +impl MallocSizeOf for Path { + fn size_of(&self, _ops: &mut malloc_size_of::MallocSizeOfOps) -> usize { + std::mem::size_of_val(self.0.elements()) + } +} + pub struct IndexSizeError; impl Path { @@ -146,7 +153,7 @@ impl Path { self.ensure_there_is_a_subpath(x1, y1); // Step 3. If either radius is negative, then throw an "IndexSizeError" DOMException. - if radius.is_sign_negative() { + if radius < 0.0 { return Err(IndexSizeError); } @@ -166,7 +173,7 @@ impl Path { // all lie on a single straight line, then add the point (x1, y1) to the subpath, // and connect that point to the previous point (x0, y0) by a straight line. let direction = Triangle::from_coords((x0, y0), (x1, y1), (x2, y2)).area(); - if direction == 0.0 { + if direction.approx_eq(&0.0) { self.0.line_to((x1, y1)); return Ok(()); } @@ -209,7 +216,7 @@ impl Path { let angle_start = (tp1.y - cy).atan2(tp1.x - cx); let angle_end = (tp2.y - cy).atan2(tp2.x - cx); - self.0.line_to((tp1.x, tp2.x)); + self.0.line_to((tp1.x, tp1.y)); self.arc(cx, cy, radius, angle_start, angle_end, anticlockwise) } @@ -280,7 +287,7 @@ impl Path { } // Step 2. If either radiusX or radiusY are negative, then throw an "IndexSizeError" DOMException. - if radius_x.is_sign_negative() || radius_y.is_sign_negative() { + if radius_x < 0.0 || radius_y < 0.0 { return Err(IndexSizeError); } @@ -419,34 +426,20 @@ pub enum CanvasMsg { #[derive(Debug, Deserialize, Serialize)] pub enum Canvas2dMsg { - Arc(Point2D, f32, f32, f32, bool), - ArcTo(Point2D, Point2D, f32), DrawImage(IpcSnapshot, Rect, Rect, bool), DrawEmptyImage(Size2D, Rect, Rect), DrawImageInOther(CanvasId, Size2D, Rect, Rect, bool), - BeginPath, - BezierCurveTo(Point2D, Point2D, Point2D), ClearRect(Rect), - Clip, ClipPath(Path), - ClosePath, - Ellipse(Point2D, f32, f32, f32, f32, f32, bool), - Fill(FillOrStrokeStyle), FillPath(FillOrStrokeStyle, Path), FillText(String, f64, f64, Option, FillOrStrokeStyle, bool), FillRect(Rect, FillOrStrokeStyle), GetImageData(Rect, Size2D, IpcSender), - IsPointInCurrentPath(f64, f64, FillRule, IpcSender), - LineTo(Point2D), - MoveTo(Point2D), MeasureText(String, IpcSender), PutImageData(Rect, IpcSnapshot), - QuadraticCurveTo(Point2D, Point2D), - Rect(Rect), RestoreContext, SaveContext, StrokeRect(Rect, FillOrStrokeStyle), - Stroke(FillOrStrokeStyle), StrokePath(FillOrStrokeStyle, Path), SetLineWidth(f32), SetLineCap(LineCapStyle),