diff --git a/components/canvas/backend.rs b/components/canvas/backend.rs index 512d5a30e34..01bcc6d26f5 100644 --- a/components/canvas/backend.rs +++ b/components/canvas/backend.rs @@ -5,14 +5,14 @@ use std::borrow::Cow; use canvas_traits::canvas::{ - CompositionOrBlending, FillOrStrokeStyle, LineCapStyle, LineJoinStyle, + CompositionOrBlending, FillOrStrokeStyle, LineCapStyle, LineJoinStyle, PathSegment, }; use euclid::Angle; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use lyon_geom::Arc; use style::color::AbsoluteColor; -use crate::canvas_data::{CanvasPaintState, Filter, TextRun}; +use crate::canvas_data::{CanvasPaintState, Filter, PathBuilderRef, TextRun}; pub(crate) trait Backend: Clone + Sized { type Pattern<'a>: PatternHelpers + Clone; @@ -21,9 +21,8 @@ pub(crate) trait Backend: Clone + Sized { type DrawOptions: DrawOptionsHelpers + Clone; type CompositionOp; type DrawTarget: GenericDrawTarget; - type PathBuilder: GenericPathBuilder; + type Path: GenericPath + Clone; type SourceSurface; - type Path: PathHelpers + Clone; type GradientStop; type GradientStops; @@ -61,7 +60,6 @@ pub(crate) trait GenericDrawTarget { source: Rect, destination: Point2D, ); - fn create_path_builder(&self) -> B::PathBuilder; fn create_similar_draw_target(&self, size: &Size2D) -> Self; fn create_source_surface_from_data(&self, data: &[u8]) -> Option; fn draw_surface( @@ -118,8 +116,10 @@ pub(crate) trait GenericDrawTarget { fn bytes(&self) -> Cow<[u8]>; } -/// A generic PathBuilder that abstracts the interface for azure's and raqote's PathBuilder. -pub(crate) trait GenericPathBuilder { +/// A generic Path that abstracts the interface for raqote's PathBuilder/Path. +pub(crate) trait GenericPath> { + fn new() -> Self; + fn transform(&mut self, transform: &Transform2D); fn arc( &mut self, origin: Point2D, @@ -237,7 +237,76 @@ pub(crate) trait GenericPathBuilder { self.quadratic_curve_to(&q.ctrl, &q.to); }); } - fn finish(&mut self) -> B::Path; + fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D) -> bool; + fn add_segments(&mut self, path: &[PathSegment]) { + let mut build_ref = PathBuilderRef:: { + builder: self, + transform: Transform2D::identity(), + }; + for &seg in path { + match seg { + PathSegment::ClosePath => build_ref.close(), + PathSegment::MoveTo { x, y } => build_ref.move_to(&Point2D::new(x, y)), + PathSegment::LineTo { x, y } => build_ref.line_to(&Point2D::new(x, y)), + PathSegment::Quadratic { cpx, cpy, x, y } => { + build_ref.quadratic_curve_to(&Point2D::new(cpx, cpy), &Point2D::new(x, y)) + }, + PathSegment::Bezier { + cp1x, + cp1y, + cp2x, + cp2y, + x, + y, + } => build_ref.bezier_curve_to( + &Point2D::new(cp1x, cp1y), + &Point2D::new(cp2x, cp2y), + &Point2D::new(x, y), + ), + PathSegment::ArcTo { + cp1x, + cp1y, + cp2x, + cp2y, + radius, + } => build_ref.arc_to(&Point2D::new(cp1x, cp1y), &Point2D::new(cp2x, cp2y), radius), + PathSegment::Ellipse { + x, + y, + radius_x, + radius_y, + rotation, + start_angle, + end_angle, + anticlockwise, + } => build_ref.ellipse( + &Point2D::new(x, y), + radius_x, + radius_y, + rotation, + start_angle, + end_angle, + anticlockwise, + ), + PathSegment::SvgArc { + radius_x, + radius_y, + rotation, + large_arc, + sweep, + x, + y, + } => build_ref.svg_arc( + radius_x, + radius_y, + rotation, + large_arc, + sweep, + &Point2D::new(x, y), + ), + } + } + } } pub(crate) trait PatternHelpers { @@ -257,11 +326,3 @@ pub(crate) trait StrokeOptionsHelpers { pub(crate) trait DrawOptionsHelpers { fn set_alpha(&mut self, val: f32); } - -pub(crate) trait PathHelpers { - fn transformed_copy_to_builder(&self, transform: &Transform2D) -> B::PathBuilder; - - fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D) -> bool; - - fn copy_to_builder(&self) -> B::PathBuilder; -} diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 978fb44c3bd..923b719599a 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -26,85 +26,14 @@ use unicode_script::Script; use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey}; use crate::backend::{ - Backend, DrawOptionsHelpers as _, GenericDrawTarget as _, GenericPathBuilder, PathHelpers, - PatternHelpers, StrokeOptionsHelpers as _, + Backend, DrawOptionsHelpers as _, GenericDrawTarget as _, GenericPath, 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 = Size2D::new(1, 1); -fn to_path(path: &[PathSegment], mut builder: B::PathBuilder) -> B::Path { - let mut build_ref = PathBuilderRef:: { - builder: &mut builder, - transform: Transform2D::identity(), - }; - for &seg in path { - match seg { - PathSegment::ClosePath => build_ref.close(), - PathSegment::MoveTo { x, y } => build_ref.move_to(&Point2D::new(x, y)), - PathSegment::LineTo { x, y } => build_ref.line_to(&Point2D::new(x, y)), - PathSegment::Quadratic { cpx, cpy, x, y } => { - build_ref.quadratic_curve_to(&Point2D::new(cpx, cpy), &Point2D::new(x, y)) - }, - PathSegment::Bezier { - cp1x, - cp1y, - cp2x, - cp2y, - x, - y, - } => build_ref.bezier_curve_to( - &Point2D::new(cp1x, cp1y), - &Point2D::new(cp2x, cp2y), - &Point2D::new(x, y), - ), - PathSegment::ArcTo { - cp1x, - cp1y, - cp2x, - cp2y, - radius, - } => build_ref.arc_to(&Point2D::new(cp1x, cp1y), &Point2D::new(cp2x, cp2y), radius), - PathSegment::Ellipse { - x, - y, - radius_x, - radius_y, - rotation, - start_angle, - end_angle, - anticlockwise, - } => build_ref.ellipse( - &Point2D::new(x, y), - radius_x, - radius_y, - rotation, - start_angle, - end_angle, - anticlockwise, - ), - PathSegment::SvgArc { - radius_x, - radius_y, - rotation, - large_arc, - sweep, - x, - y, - } => build_ref.svg_arc( - radius_x, - radius_y, - rotation, - large_arc, - sweep, - &Point2D::new(x, y), - ), - } - } - builder.finish() -} - /// The canvas data stores a state machine for the current status of /// the path data and any relevant transformations that are /// applied to it. The Azure drawing API expects the path to be in @@ -115,35 +44,31 @@ fn to_path(path: &[PathSegment], mut builder: B::PathBuilder) -> B:: /// with the correct transform applied. /// TODO: De-abstract now that Azure is removed? enum PathState { - /// 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(B::PathBuilder, Option>), - /// Path builder in device-space. - 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(B::Path, Option>), + /// Path in device-space. + DeviceSpacePath(B::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, B: Backend> { - builder: &'a mut B::PathBuilder, - transform: Transform2D, +pub(crate) struct PathBuilderRef<'a, B: Backend> { + pub(crate) builder: &'a mut B::Path, + pub(crate) transform: Transform2D, } impl PathBuilderRef<'_, B> { /// - fn ensure_there_is_a_subpath(&mut self, point: &Point2D) { + pub(crate) fn ensure_there_is_a_subpath(&mut self, point: &Point2D) { if self.builder.get_current_point().is_none() { self.builder.move_to(*point); } } /// - fn line_to(&mut self, pt: &Point2D) { + pub(crate) fn line_to(&mut self, pt: &Point2D) { // 2. If the object's path has no subpaths, then ensure there is a subpath for (x, y). self.ensure_there_is_a_subpath(pt); @@ -151,12 +76,12 @@ impl PathBuilderRef<'_, B> { self.builder.line_to(pt); } - fn move_to(&mut self, pt: &Point2D) { + pub(crate) fn move_to(&mut self, pt: &Point2D) { let pt = self.transform.transform_point(*pt); self.builder.move_to(pt); } - fn rect(&mut self, rect: &Rect) { + 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), @@ -175,7 +100,7 @@ impl PathBuilderRef<'_, B> { } /// - fn quadratic_curve_to(&mut self, cp: &Point2D, endpoint: &Point2D) { + pub(crate) fn quadratic_curve_to(&mut self, cp: &Point2D, endpoint: &Point2D) { // 2. Ensure there is a subpath for (cpx, cpy). self.ensure_there_is_a_subpath(cp); @@ -186,7 +111,12 @@ impl PathBuilderRef<'_, B> { } /// - fn bezier_curve_to(&mut self, cp1: &Point2D, cp2: &Point2D, endpoint: &Point2D) { + pub(crate) fn bezier_curve_to( + &mut self, + cp1: &Point2D, + cp2: &Point2D, + endpoint: &Point2D, + ) { // 2. Ensure there is a subpath for (cp1x, cp1y). self.ensure_there_is_a_subpath(cp1); @@ -197,7 +127,7 @@ impl PathBuilderRef<'_, B> { ) } - fn arc( + pub(crate) fn arc( &mut self, center: &Point2D, radius: f32, @@ -211,7 +141,7 @@ impl PathBuilderRef<'_, B> { } /// - fn arc_to(&mut self, cp1: &Point2D, cp2: &Point2D, radius: f32) { + 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.get_current_point()) { @@ -300,7 +230,7 @@ impl PathBuilderRef<'_, B> { ); } - fn svg_arc( + pub(crate) fn svg_arc( &mut self, radius_x: f32, radius_y: f32, @@ -320,7 +250,7 @@ impl PathBuilderRef<'_, B> { ); } - fn close(&mut self) { + pub(crate) fn close(&mut self) { self.builder.close(); } } @@ -814,48 +744,35 @@ impl<'a, B: Backend> CanvasData<'a, B> { /// Turn the [`Self::path_state`] into a user-space path, returning `None` if the /// path transformation matrix is uninvertible. fn ensure_path(&mut self) -> Option<&B::Path> { - // If there's no record of any path yet, create a new builder in user-space. + // If there's no record of any path yet, create a new path in user-space. if self.path_state.is_none() { - self.path_state = Some(PathState::UserSpacePathBuilder( - self.drawtarget.create_path_builder(), - None, - )); + self.path_state = Some(PathState::UserSpacePath(B::Path::new(), None)); } - // If a user-space builder exists, create a finished path from it. - let new_state = match *self.path_state.as_mut().unwrap() { - PathState::UserSpacePathBuilder(ref mut builder, ref mut transform) => { - Some((builder.finish(), transform.take())) - }, - PathState::DeviceSpacePathBuilder(..) | PathState::UserSpacePath(..) => None, - }; - if let Some((path, transform)) = new_state { - self.path_state = Some(PathState::UserSpacePath(path, transform)); - } - - // If a user-space path exists, create a device-space builder based on it if + // If a user-space path exists, create a device-space path based on it if // any transform is present. let new_state = match *self.path_state.as_ref().unwrap() { PathState::UserSpacePath(ref path, Some(ref transform)) => { - Some(path.transformed_copy_to_builder(transform)) + let mut path = path.clone(); + path.transform(transform); + Some(path) }, - PathState::UserSpacePath(..) | - PathState::UserSpacePathBuilder(..) | - PathState::DeviceSpacePathBuilder(..) => None, + PathState::UserSpacePath(..) | PathState::DeviceSpacePath(..) => None, }; if let Some(builder) = new_state { - self.path_state = Some(PathState::DeviceSpacePathBuilder(builder)); + self.path_state = Some(PathState::DeviceSpacePath(builder)); } - // If a device-space builder is present, create a user-space path from its + // If a device-space path is present, create a user-space path from its // finished path by inverting the initial transformation. let new_state = match *self.path_state.as_mut().unwrap() { - PathState::DeviceSpacePathBuilder(ref mut builder) => { + PathState::DeviceSpacePath(ref mut builder) => { let inverse = self.drawtarget.get_transform().inverse()?; - let mut builder = builder.finish().transformed_copy_to_builder(&inverse); - Some(builder.finish()) + let mut path = builder.clone(); + path.transform(&inverse); + Some(path) }, - PathState::UserSpacePathBuilder(..) | PathState::UserSpacePath(..) => None, + PathState::UserSpacePath(..) => None, }; if let Some(path) = new_state { self.path_state = Some(PathState::UserSpacePath(path, None)); @@ -883,12 +800,13 @@ impl<'a, B: Backend> CanvasData<'a, B> { ); } - pub(crate) fn fill_path(&mut self, path: &[PathSegment]) { + pub(crate) fn fill_path(&mut self, path_segments: &[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 mut path = B::Path::new(); + path.add_segments(path_segments); self.drawtarget .fill(&path, &self.state.fill_style, &self.state.draw_options); @@ -911,12 +829,13 @@ impl<'a, B: Backend> CanvasData<'a, B> { ); } - pub(crate) fn stroke_path(&mut self, path: &[PathSegment]) { + pub(crate) fn stroke_path(&mut self, path_segments: &[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 mut path = B::Path::new(); + path.add_segments(path_segments); self.drawtarget.stroke( &path, @@ -934,8 +853,9 @@ impl<'a, B: Backend> CanvasData<'a, B> { self.drawtarget.push_clip(&path); } - pub(crate) 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_segments: &[PathSegment]) { + let mut path = B::Path::new(); + path.add_segments(path_segments); self.drawtarget.push_clip(&path); } @@ -960,7 +880,7 @@ impl<'a, B: Backend> CanvasData<'a, B> { pub(crate) fn is_point_in_path_( &mut self, - path: &[PathSegment], + path_segments: &[PathSegment], x: f64, y: f64, _fill_rule: FillRule, @@ -970,11 +890,9 @@ impl<'a, B: Backend> CanvasData<'a, B> { Some(PathState::UserSpacePath(_, Some(transform))) => transform, Some(_) | None => &self.drawtarget.get_transform(), }; - let result = to_path::(path, self.drawtarget.create_path_builder()).contains_point( - x, - y, - path_transform, - ); + let mut path = B::Path::new(); + path.add_segments(path_segments); + let result = path.contains_point(x, y, path_transform); chan.send(result).unwrap(); } @@ -988,10 +906,7 @@ impl<'a, B: Backend> CanvasData<'a, B> { fn path_builder(&mut self) -> PathBuilderRef { if self.path_state.is_none() { - self.path_state = Some(PathState::UserSpacePathBuilder( - self.drawtarget.create_path_builder(), - None, - )); + self.path_state = Some(PathState::UserSpacePath(B::Path::new(), None)); } // Rust is not pleased by returning a reference to a builder in some branches @@ -999,22 +914,15 @@ impl<'a, B: Backend> CanvasData<'a, B> { // matches works around the resulting borrow errors. let new_state = { match *self.path_state.as_mut().unwrap() { - PathState::UserSpacePathBuilder(_, None) | PathState::DeviceSpacePathBuilder(_) => { - None + PathState::DeviceSpacePath(_) => None, + PathState::UserSpacePath(ref path, Some(ref transform)) => { + let mut path = path.clone(); + path.transform(transform); + Some(PathState::DeviceSpacePath(path)) }, - PathState::UserSpacePathBuilder(ref mut builder, Some(ref transform)) => { - let path = builder.finish(); - Some(PathState::DeviceSpacePathBuilder( - path.transformed_copy_to_builder(transform), - )) + PathState::UserSpacePath(ref path, None) => { + Some(PathState::UserSpacePath(path.clone(), None)) }, - PathState::UserSpacePath(ref path, Some(ref transform)) => Some( - PathState::DeviceSpacePathBuilder(path.transformed_copy_to_builder(transform)), - ), - PathState::UserSpacePath(ref path, None) => Some(PathState::UserSpacePathBuilder( - path.copy_to_builder(), - None, - )), } }; match new_state { @@ -1022,13 +930,13 @@ impl<'a, B: Backend> CanvasData<'a, B> { Some(state) => self.path_state = Some(state), // There's an existing builder value that can be returned immediately. None => match *self.path_state.as_mut().unwrap() { - PathState::UserSpacePathBuilder(ref mut builder, None) => { + PathState::UserSpacePath(ref mut builder, None) => { return PathBuilderRef { builder, transform: Transform2D::identity(), }; }, - PathState::DeviceSpacePathBuilder(ref mut builder) => { + PathState::DeviceSpacePath(ref mut builder) => { return PathBuilderRef { builder, transform: self.drawtarget.get_transform(), @@ -1039,15 +947,15 @@ impl<'a, B: Backend> CanvasData<'a, B> { } match *self.path_state.as_mut().unwrap() { - PathState::UserSpacePathBuilder(ref mut builder, None) => PathBuilderRef { + PathState::UserSpacePath(ref mut builder, None) => PathBuilderRef { builder, transform: Transform2D::identity(), }, - PathState::DeviceSpacePathBuilder(ref mut builder) => PathBuilderRef { + PathState::DeviceSpacePath(ref mut builder) => PathBuilderRef { builder, transform: self.drawtarget.get_transform(), }, - PathState::UserSpacePathBuilder(..) | PathState::UserSpacePath(..) => unreachable!(), + PathState::UserSpacePath(..) => unreachable!(), } } @@ -1154,8 +1062,7 @@ impl<'a, B: Backend> CanvasData<'a, B> { // 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() { - None | Some(PathState::DeviceSpacePathBuilder(..)) => (), - Some(PathState::UserSpacePathBuilder(_, transform)) | + None | Some(PathState::DeviceSpacePath(..)) => (), Some(PathState::UserSpacePath(_, transform)) => { if transform.is_none() { *transform = Some(self.drawtarget.get_transform()); diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index c86609d98e6..45cf461b753 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::borrow::Cow; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::collections::HashMap; use canvas_traits::canvas::*; @@ -17,8 +17,8 @@ use raqote::PathOp; use style::color::AbsoluteColor; use crate::backend::{ - Backend, DrawOptionsHelpers, GenericDrawTarget, GenericPathBuilder, PathHelpers, - PatternHelpers, StrokeOptionsHelpers, + Backend, DrawOptionsHelpers, GenericDrawTarget, GenericPath, PatternHelpers, + StrokeOptionsHelpers, }; use crate::canvas_data::{CanvasPaintState, Filter, TextRun}; @@ -40,9 +40,8 @@ impl Backend for RaqoteBackend { type DrawOptions = raqote::DrawOptions; type CompositionOp = raqote::BlendMode; type DrawTarget = raqote::DrawTarget; - type PathBuilder = PathBuilder; type SourceSurface = Vec; // TODO: See if we can avoid the alloc (probably?) - type Path = raqote::Path; + type Path = Path; type GradientStop = raqote::GradientStop; type GradientStops = Vec; @@ -326,24 +325,6 @@ impl DrawOptionsHelpers for raqote::DrawOptions { } } -impl PathHelpers for raqote::Path { - fn transformed_copy_to_builder(&self, transform: &Transform2D) -> PathBuilder { - PathBuilder(Some(raqote::PathBuilder::from( - self.clone().transform(transform), - ))) - } - - fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D) -> bool { - self.clone() - .transform(path_transform) - .contains_point(0.1, x as f32, y as f32) - } - - fn copy_to_builder(&self) -> PathBuilder { - PathBuilder(Some(raqote::PathBuilder::from(self.clone()))) - } -} - fn create_gradient_stops(gradient_stops: Vec) -> Vec { let mut stops = gradient_stops .into_iter() @@ -366,7 +347,7 @@ 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); - >::fill(self, &pb.finish(), &pattern, &options); + >::fill(self, &pb.into(), &pattern, &options); } #[allow(unsafe_code)] fn copy_surface( @@ -382,9 +363,6 @@ impl GenericDrawTarget for raqote::DrawTarget { raqote::DrawTarget::copy_surface(self, &dt, source.to_box2d(), destination); } - fn create_path_builder(&self) -> ::PathBuilder { - PathBuilder::new() - } fn create_similar_draw_target( &self, size: &Size2D, @@ -440,12 +418,7 @@ impl GenericDrawTarget for raqote::DrawTarget { dest.size.height as f32, ); - >::fill( - self, - &pb.finish(), - &pattern, - draw_options, - ); + >::fill(self, &pb.into(), &pattern, draw_options); } fn draw_surface_with_shadow( &self, @@ -464,10 +437,11 @@ impl GenericDrawTarget for raqote::DrawTarget { pattern: &::Pattern<'_>, draw_options: &::DrawOptions, ) { + let path = path.into(); match draw_options.blend_mode { raqote::BlendMode::Src => { self.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)); - self.fill(path, &source(pattern), draw_options); + self.fill(&path, &source(pattern), draw_options); }, raqote::BlendMode::Clear | raqote::BlendMode::SrcAtop | @@ -476,7 +450,7 @@ impl GenericDrawTarget for raqote::DrawTarget { raqote::BlendMode::Xor | raqote::BlendMode::DstOver | raqote::BlendMode::SrcOver => { - self.fill(path, &source(pattern), draw_options); + self.fill(&path, &source(pattern), draw_options); }, raqote::BlendMode::SrcIn | raqote::BlendMode::SrcOut | @@ -485,7 +459,7 @@ impl GenericDrawTarget for raqote::DrawTarget { let mut options = *draw_options; self.push_layer_with_blend(1., options.blend_mode); options.blend_mode = raqote::BlendMode::SrcOver; - self.fill(path, &source(pattern), &options); + self.fill(&path, &source(pattern), &options); self.pop_layer(); }, _ => warn!("unrecognized blend mode: {:?}", draw_options.blend_mode), @@ -566,7 +540,7 @@ impl GenericDrawTarget for raqote::DrawTarget { rect.size.height, ); - >::fill(self, &pb.finish(), pattern, draw_options); + >::fill(self, &pb.into(), pattern, draw_options); } fn get_size(&self) -> Size2D { Size2D::new(self.width(), self.height()) @@ -578,7 +552,7 @@ impl GenericDrawTarget for raqote::DrawTarget { self.pop_clip(); } fn push_clip(&mut self, path: &::Path) { - self.push_clip(path); + self.push_clip(&path.into()); } fn set_transform(&mut self, matrix: &Transform2D) { self.set_transform(matrix); @@ -593,7 +567,7 @@ impl GenericDrawTarget for raqote::DrawTarget { stroke_options: &::StrokeOptions, draw_options: &::DrawOptions, ) { - self.stroke(path, &source(pattern), stroke_options, draw_options); + self.stroke(&path.into(), &source(pattern), stroke_options, draw_options); } fn stroke_rect( &mut self, @@ -630,22 +604,47 @@ impl Filter { } } -pub(crate) struct PathBuilder(Option); +pub(crate) struct Path(Cell); -impl PathBuilder { - fn new() -> PathBuilder { - PathBuilder(Some(raqote::PathBuilder::new())) +impl From for Path { + fn from(path_builder: raqote::PathBuilder) -> Self { + Self(Cell::new(path_builder)) } } -impl GenericPathBuilder for PathBuilder { +impl From<&Path> for raqote::Path { + fn from(path: &Path) -> Self { + path.clone().0.into_inner().finish() + } +} + +impl Clone for Path { + fn clone(&self) -> Self { + let path_builder = self.0.replace(raqote::PathBuilder::new()); + let path = path_builder.finish(); + self.0.set(path.clone().into()); + Self(Cell::new(path.into())) + } +} + +impl GenericPath for Path { + fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D) -> bool { + let path: raqote::Path = self.into(); + path.transform(path_transform) + .contains_point(0.01, x as f32, y as f32) + } + + fn new() -> Self { + Self(Cell::new(raqote::PathBuilder::new())) + } + fn bezier_curve_to( &mut self, control_point1: &Point2D, control_point2: &Point2D, control_point3: &Point2D, ) { - self.0.as_mut().unwrap().cubic_to( + self.0.get_mut().cubic_to( control_point1.x, control_point1.y, control_point2.x, @@ -656,12 +655,11 @@ impl GenericPathBuilder for PathBuilder { } fn close(&mut self) { - self.0.as_mut().unwrap().close(); + self.0.get_mut().close(); } fn get_current_point(&mut self) -> Option> { - let path = self.finish(); - self.0 = Some(path.clone().into()); + let path: raqote::Path = (&*self).into(); path.ops.iter().last().and_then(|op| match op { PathOp::MoveTo(point) | PathOp::LineTo(point) => Some(Point2D::new(point.x, point.y)), @@ -672,21 +670,22 @@ impl GenericPathBuilder for PathBuilder { } fn line_to(&mut self, point: Point2D) { - self.0.as_mut().unwrap().line_to(point.x, point.y); + self.0.get_mut().line_to(point.x, point.y); } + fn move_to(&mut self, point: Point2D) { - self.0.as_mut().unwrap().move_to(point.x, point.y); + self.0.get_mut().move_to(point.x, point.y); } + fn quadratic_curve_to(&mut self, control_point: &Point2D, end_point: &Point2D) { - self.0.as_mut().unwrap().quad_to( - control_point.x, - control_point.y, - end_point.x, - end_point.y, - ); + self.0 + .get_mut() + .quad_to(control_point.x, control_point.y, end_point.x, end_point.y); } - fn finish(&mut self) -> raqote::Path { - self.0.take().unwrap().finish() + + fn transform(&mut self, transform: &Transform2D) { + let path: raqote::Path = (&*self).into(); + self.0.set(path.transform(transform).into()); } }