mirror of
https://github.com/servo/servo.git
synced 2025-07-16 11:53:39 +01:00
canvas: Join Path
and PathBuilder
into Path
(#37842)
Dealing with both path and builder makes code complicated and this is backend internal specific anyway (we often do conversions between the two just so we get specific functionality). Now I joined both into single abstractions `Path`, that has all what one needs and let raqote backend deal with path/pathbuilder coversions. Motivation: Simplification and in Vello there is only [BezPath](https://docs.rs/kurbo/0.11.2/kurbo/struct.BezPath.html) so we use it as both path and pathbuilder. Reviewable per commit. Testing: Existing WPT tests --------- Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
parent
940eff9497
commit
b57c9acc65
3 changed files with 197 additions and 230 deletions
|
@ -5,14 +5,14 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use canvas_traits::canvas::{
|
use canvas_traits::canvas::{
|
||||||
CompositionOrBlending, FillOrStrokeStyle, LineCapStyle, LineJoinStyle,
|
CompositionOrBlending, FillOrStrokeStyle, LineCapStyle, LineJoinStyle, PathSegment,
|
||||||
};
|
};
|
||||||
use euclid::Angle;
|
use euclid::Angle;
|
||||||
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
|
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
|
||||||
use lyon_geom::Arc;
|
use lyon_geom::Arc;
|
||||||
use style::color::AbsoluteColor;
|
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 {
|
pub(crate) trait Backend: Clone + Sized {
|
||||||
type Pattern<'a>: PatternHelpers + Clone;
|
type Pattern<'a>: PatternHelpers + Clone;
|
||||||
|
@ -21,9 +21,8 @@ pub(crate) trait Backend: Clone + Sized {
|
||||||
type DrawOptions: DrawOptionsHelpers + Clone;
|
type DrawOptions: DrawOptionsHelpers + Clone;
|
||||||
type CompositionOp;
|
type CompositionOp;
|
||||||
type DrawTarget: GenericDrawTarget<Self>;
|
type DrawTarget: GenericDrawTarget<Self>;
|
||||||
type PathBuilder: GenericPathBuilder<Self>;
|
type Path: GenericPath<Self> + Clone;
|
||||||
type SourceSurface;
|
type SourceSurface;
|
||||||
type Path: PathHelpers<Self> + Clone;
|
|
||||||
type GradientStop;
|
type GradientStop;
|
||||||
type GradientStops;
|
type GradientStops;
|
||||||
|
|
||||||
|
@ -61,7 +60,6 @@ pub(crate) trait GenericDrawTarget<B: Backend> {
|
||||||
source: Rect<i32>,
|
source: Rect<i32>,
|
||||||
destination: Point2D<i32>,
|
destination: Point2D<i32>,
|
||||||
);
|
);
|
||||||
fn create_path_builder(&self) -> B::PathBuilder;
|
|
||||||
fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self;
|
fn create_similar_draw_target(&self, size: &Size2D<i32>) -> Self;
|
||||||
fn create_source_surface_from_data(&self, data: &[u8]) -> Option<B::SourceSurface>;
|
fn create_source_surface_from_data(&self, data: &[u8]) -> Option<B::SourceSurface>;
|
||||||
fn draw_surface(
|
fn draw_surface(
|
||||||
|
@ -118,8 +116,10 @@ pub(crate) trait GenericDrawTarget<B: Backend> {
|
||||||
fn bytes(&self) -> Cow<[u8]>;
|
fn bytes(&self) -> Cow<[u8]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generic PathBuilder that abstracts the interface for azure's and raqote's PathBuilder.
|
/// A generic Path that abstracts the interface for raqote's PathBuilder/Path.
|
||||||
pub(crate) trait GenericPathBuilder<B: Backend> {
|
pub(crate) trait GenericPath<B: Backend<Path = Self>> {
|
||||||
|
fn new() -> Self;
|
||||||
|
fn transform(&mut self, transform: &Transform2D<f32>);
|
||||||
fn arc(
|
fn arc(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: Point2D<f32>,
|
origin: Point2D<f32>,
|
||||||
|
@ -237,7 +237,76 @@ pub(crate) trait GenericPathBuilder<B: Backend> {
|
||||||
self.quadratic_curve_to(&q.ctrl, &q.to);
|
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<f32>) -> bool;
|
||||||
|
fn add_segments(&mut self, path: &[PathSegment]) {
|
||||||
|
let mut build_ref = PathBuilderRef::<B> {
|
||||||
|
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 {
|
pub(crate) trait PatternHelpers {
|
||||||
|
@ -257,11 +326,3 @@ pub(crate) trait StrokeOptionsHelpers {
|
||||||
pub(crate) trait DrawOptionsHelpers {
|
pub(crate) trait DrawOptionsHelpers {
|
||||||
fn set_alpha(&mut self, val: f32);
|
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,85 +26,14 @@ use unicode_script::Script;
|
||||||
use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey};
|
use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey};
|
||||||
|
|
||||||
use crate::backend::{
|
use crate::backend::{
|
||||||
Backend, DrawOptionsHelpers as _, GenericDrawTarget as _, GenericPathBuilder, PathHelpers,
|
Backend, DrawOptionsHelpers as _, GenericDrawTarget as _, GenericPath, PatternHelpers,
|
||||||
PatternHelpers, StrokeOptionsHelpers as _,
|
StrokeOptionsHelpers as _,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Asserts on WR texture cache update for zero sized image with raw data.
|
// 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
|
// https://github.com/servo/webrender/blob/main/webrender/src/texture_cache.rs#L1475
|
||||||
const MIN_WR_IMAGE_SIZE: Size2D<u64> = Size2D::new(1, 1);
|
const MIN_WR_IMAGE_SIZE: Size2D<u64> = Size2D::new(1, 1);
|
||||||
|
|
||||||
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(),
|
|
||||||
};
|
|
||||||
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 canvas data stores a state machine for the current status of
|
||||||
/// the path data and any relevant transformations that are
|
/// the path data and any relevant transformations that are
|
||||||
/// applied to it. The Azure drawing API expects the path to be in
|
/// applied to it. The Azure drawing API expects the path to be in
|
||||||
|
@ -115,35 +44,31 @@ fn to_path<B: Backend>(path: &[PathSegment], mut builder: B::PathBuilder) -> B::
|
||||||
/// with the correct transform applied.
|
/// with the correct transform applied.
|
||||||
/// TODO: De-abstract now that Azure is removed?
|
/// TODO: De-abstract now that Azure is removed?
|
||||||
enum PathState<B: Backend> {
|
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(B::PathBuilder, Option<Transform2D<f32>>),
|
|
||||||
/// Path builder in device-space.
|
|
||||||
DeviceSpacePathBuilder(B::PathBuilder),
|
|
||||||
/// Path in user-space. If a transform has been applied but
|
/// Path in user-space. If a transform has been applied but
|
||||||
/// but no further path operations have occurred, it is stored
|
/// but no further path operations have occurred, it is stored
|
||||||
/// in the optional field.
|
/// in the optional field.
|
||||||
UserSpacePath(B::Path, Option<Transform2D<f32>>),
|
UserSpacePath(B::Path, Option<Transform2D<f32>>),
|
||||||
|
/// Path in device-space.
|
||||||
|
DeviceSpacePath(B::Path),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper around a stored PathBuilder and an optional transformation that should be
|
/// 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.
|
/// applied to any points to ensure they are in the matching device space.
|
||||||
struct PathBuilderRef<'a, B: Backend> {
|
pub(crate) struct PathBuilderRef<'a, B: Backend> {
|
||||||
builder: &'a mut B::PathBuilder,
|
pub(crate) builder: &'a mut B::Path,
|
||||||
transform: Transform2D<f32>,
|
pub(crate) transform: Transform2D<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: Backend> PathBuilderRef<'_, B> {
|
impl<B: Backend> PathBuilderRef<'_, B> {
|
||||||
/// <https://html.spec.whatwg.org/multipage#ensure-there-is-a-subpath>
|
/// <https://html.spec.whatwg.org/multipage#ensure-there-is-a-subpath>
|
||||||
fn ensure_there_is_a_subpath(&mut self, point: &Point2D<f32>) {
|
pub(crate) fn ensure_there_is_a_subpath(&mut self, point: &Point2D<f32>) {
|
||||||
if self.builder.get_current_point().is_none() {
|
if self.builder.get_current_point().is_none() {
|
||||||
self.builder.move_to(*point);
|
self.builder.move_to(*point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://html.spec.whatwg.org/multipage#dom-context-2d-lineto>
|
/// <https://html.spec.whatwg.org/multipage#dom-context-2d-lineto>
|
||||||
fn line_to(&mut self, pt: &Point2D<f32>) {
|
pub(crate) fn line_to(&mut self, pt: &Point2D<f32>) {
|
||||||
// 2. If the object's path has no subpaths, then ensure there is a subpath for (x, y).
|
// 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);
|
self.ensure_there_is_a_subpath(pt);
|
||||||
|
|
||||||
|
@ -151,12 +76,12 @@ impl<B: Backend> PathBuilderRef<'_, B> {
|
||||||
self.builder.line_to(pt);
|
self.builder.line_to(pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_to(&mut self, pt: &Point2D<f32>) {
|
pub(crate) fn move_to(&mut self, pt: &Point2D<f32>) {
|
||||||
let pt = self.transform.transform_point(*pt);
|
let pt = self.transform.transform_point(*pt);
|
||||||
self.builder.move_to(pt);
|
self.builder.move_to(pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rect(&mut self, rect: &Rect<f32>) {
|
pub(crate) fn rect(&mut self, rect: &Rect<f32>) {
|
||||||
let (first, second, third, fourth) = (
|
let (first, second, third, fourth) = (
|
||||||
Point2D::new(rect.origin.x, rect.origin.y),
|
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),
|
||||||
|
@ -175,7 +100,7 @@ impl<B: Backend> PathBuilderRef<'_, B> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://html.spec.whatwg.org/multipage#dom-context-2d-quadraticcurveto>
|
/// <https://html.spec.whatwg.org/multipage#dom-context-2d-quadraticcurveto>
|
||||||
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>) {
|
||||||
// 2. Ensure there is a subpath for (cpx, cpy).
|
// 2. Ensure there is a subpath for (cpx, cpy).
|
||||||
self.ensure_there_is_a_subpath(cp);
|
self.ensure_there_is_a_subpath(cp);
|
||||||
|
|
||||||
|
@ -186,7 +111,12 @@ impl<B: Backend> PathBuilderRef<'_, B> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://html.spec.whatwg.org/multipage#dom-context-2d-beziercurveto>
|
/// <https://html.spec.whatwg.org/multipage#dom-context-2d-beziercurveto>
|
||||||
fn bezier_curve_to(&mut self, cp1: &Point2D<f32>, cp2: &Point2D<f32>, endpoint: &Point2D<f32>) {
|
pub(crate) fn bezier_curve_to(
|
||||||
|
&mut self,
|
||||||
|
cp1: &Point2D<f32>,
|
||||||
|
cp2: &Point2D<f32>,
|
||||||
|
endpoint: &Point2D<f32>,
|
||||||
|
) {
|
||||||
// 2. Ensure there is a subpath for (cp1x, cp1y).
|
// 2. Ensure there is a subpath for (cp1x, cp1y).
|
||||||
self.ensure_there_is_a_subpath(cp1);
|
self.ensure_there_is_a_subpath(cp1);
|
||||||
|
|
||||||
|
@ -197,7 +127,7 @@ impl<B: Backend> PathBuilderRef<'_, B> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arc(
|
pub(crate) fn arc(
|
||||||
&mut self,
|
&mut self,
|
||||||
center: &Point2D<f32>,
|
center: &Point2D<f32>,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
|
@ -211,7 +141,7 @@ impl<B: Backend> PathBuilderRef<'_, B> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://html.spec.whatwg.org/multipage#dom-context-2d-arcto>
|
/// <https://html.spec.whatwg.org/multipage#dom-context-2d-arcto>
|
||||||
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) {
|
||||||
let cp0 = if let (Some(inverse), Some(point)) =
|
let cp0 = if let (Some(inverse), Some(point)) =
|
||||||
(self.transform.inverse(), self.builder.get_current_point())
|
(self.transform.inverse(), self.builder.get_current_point())
|
||||||
{
|
{
|
||||||
|
@ -300,7 +230,7 @@ impl<B: Backend> PathBuilderRef<'_, B> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn svg_arc(
|
pub(crate) fn svg_arc(
|
||||||
&mut self,
|
&mut self,
|
||||||
radius_x: f32,
|
radius_x: f32,
|
||||||
radius_y: f32,
|
radius_y: f32,
|
||||||
|
@ -320,7 +250,7 @@ impl<B: Backend> PathBuilderRef<'_, B> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close(&mut self) {
|
pub(crate) fn close(&mut self) {
|
||||||
self.builder.close();
|
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
|
/// Turn the [`Self::path_state`] into a user-space path, returning `None` if the
|
||||||
/// path transformation matrix is uninvertible.
|
/// path transformation matrix is uninvertible.
|
||||||
fn ensure_path(&mut self) -> Option<&B::Path> {
|
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() {
|
if self.path_state.is_none() {
|
||||||
self.path_state = Some(PathState::UserSpacePathBuilder(
|
self.path_state = Some(PathState::UserSpacePath(B::Path::new(), None));
|
||||||
self.drawtarget.create_path_builder(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a user-space builder exists, create a finished path from it.
|
// If a user-space path exists, create a device-space path based on it if
|
||||||
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
|
|
||||||
// any transform is present.
|
// any transform is present.
|
||||||
let new_state = match *self.path_state.as_ref().unwrap() {
|
let new_state = match *self.path_state.as_ref().unwrap() {
|
||||||
PathState::UserSpacePath(ref path, Some(ref transform)) => {
|
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::UserSpacePath(..) | PathState::DeviceSpacePath(..) => None,
|
||||||
PathState::UserSpacePathBuilder(..) |
|
|
||||||
PathState::DeviceSpacePathBuilder(..) => None,
|
|
||||||
};
|
};
|
||||||
if let Some(builder) = new_state {
|
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.
|
// finished path by inverting the initial transformation.
|
||||||
let new_state = match *self.path_state.as_mut().unwrap() {
|
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 inverse = self.drawtarget.get_transform().inverse()?;
|
||||||
let mut builder = builder.finish().transformed_copy_to_builder(&inverse);
|
let mut path = builder.clone();
|
||||||
Some(builder.finish())
|
path.transform(&inverse);
|
||||||
|
Some(path)
|
||||||
},
|
},
|
||||||
PathState::UserSpacePathBuilder(..) | PathState::UserSpacePath(..) => None,
|
PathState::UserSpacePath(..) => None,
|
||||||
};
|
};
|
||||||
if let Some(path) = new_state {
|
if let Some(path) = new_state {
|
||||||
self.path_state = Some(PathState::UserSpacePath(path, None));
|
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() {
|
if self.state.fill_style.is_zero_size_gradient() {
|
||||||
return; // Paint nothing if gradient size is zero.
|
return; // Paint nothing if gradient size is zero.
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = to_path::<B>(path, self.drawtarget.create_path_builder());
|
let mut path = B::Path::new();
|
||||||
|
path.add_segments(path_segments);
|
||||||
|
|
||||||
self.drawtarget
|
self.drawtarget
|
||||||
.fill(&path, &self.state.fill_style, &self.state.draw_options);
|
.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() {
|
if self.state.stroke_style.is_zero_size_gradient() {
|
||||||
return; // Paint nothing if gradient size is zero.
|
return; // Paint nothing if gradient size is zero.
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = to_path::<B>(path, self.drawtarget.create_path_builder());
|
let mut path = B::Path::new();
|
||||||
|
path.add_segments(path_segments);
|
||||||
|
|
||||||
self.drawtarget.stroke(
|
self.drawtarget.stroke(
|
||||||
&path,
|
&path,
|
||||||
|
@ -934,8 +853,9 @@ impl<'a, B: Backend> CanvasData<'a, B> {
|
||||||
self.drawtarget.push_clip(&path);
|
self.drawtarget.push_clip(&path);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clip_path(&mut self, path: &[PathSegment]) {
|
pub(crate) fn clip_path(&mut self, path_segments: &[PathSegment]) {
|
||||||
let path = to_path::<B>(path, self.drawtarget.create_path_builder());
|
let mut path = B::Path::new();
|
||||||
|
path.add_segments(path_segments);
|
||||||
self.drawtarget.push_clip(&path);
|
self.drawtarget.push_clip(&path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -960,7 +880,7 @@ impl<'a, B: Backend> CanvasData<'a, B> {
|
||||||
|
|
||||||
pub(crate) fn is_point_in_path_(
|
pub(crate) fn is_point_in_path_(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: &[PathSegment],
|
path_segments: &[PathSegment],
|
||||||
x: f64,
|
x: f64,
|
||||||
y: f64,
|
y: f64,
|
||||||
_fill_rule: FillRule,
|
_fill_rule: FillRule,
|
||||||
|
@ -970,11 +890,9 @@ impl<'a, B: Backend> CanvasData<'a, B> {
|
||||||
Some(PathState::UserSpacePath(_, Some(transform))) => transform,
|
Some(PathState::UserSpacePath(_, Some(transform))) => transform,
|
||||||
Some(_) | None => &self.drawtarget.get_transform(),
|
Some(_) | None => &self.drawtarget.get_transform(),
|
||||||
};
|
};
|
||||||
let result = to_path::<B>(path, self.drawtarget.create_path_builder()).contains_point(
|
let mut path = B::Path::new();
|
||||||
x,
|
path.add_segments(path_segments);
|
||||||
y,
|
let result = path.contains_point(x, y, path_transform);
|
||||||
path_transform,
|
|
||||||
);
|
|
||||||
chan.send(result).unwrap();
|
chan.send(result).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -988,10 +906,7 @@ impl<'a, B: Backend> CanvasData<'a, B> {
|
||||||
|
|
||||||
fn path_builder(&mut self) -> PathBuilderRef<B> {
|
fn path_builder(&mut self) -> PathBuilderRef<B> {
|
||||||
if self.path_state.is_none() {
|
if self.path_state.is_none() {
|
||||||
self.path_state = Some(PathState::UserSpacePathBuilder(
|
self.path_state = Some(PathState::UserSpacePath(B::Path::new(), None));
|
||||||
self.drawtarget.create_path_builder(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rust is not pleased by returning a reference to a builder in some branches
|
// 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.
|
// matches works around the resulting borrow errors.
|
||||||
let new_state = {
|
let new_state = {
|
||||||
match *self.path_state.as_mut().unwrap() {
|
match *self.path_state.as_mut().unwrap() {
|
||||||
PathState::UserSpacePathBuilder(_, None) | PathState::DeviceSpacePathBuilder(_) => {
|
PathState::DeviceSpacePath(_) => None,
|
||||||
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)) => {
|
PathState::UserSpacePath(ref path, None) => {
|
||||||
let path = builder.finish();
|
Some(PathState::UserSpacePath(path.clone(), None))
|
||||||
Some(PathState::DeviceSpacePathBuilder(
|
|
||||||
path.transformed_copy_to_builder(transform),
|
|
||||||
))
|
|
||||||
},
|
},
|
||||||
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 {
|
match new_state {
|
||||||
|
@ -1022,13 +930,13 @@ impl<'a, B: Backend> CanvasData<'a, B> {
|
||||||
Some(state) => self.path_state = Some(state),
|
Some(state) => self.path_state = Some(state),
|
||||||
// There's an existing builder value that can be returned immediately.
|
// There's an existing builder value that can be returned immediately.
|
||||||
None => match *self.path_state.as_mut().unwrap() {
|
None => match *self.path_state.as_mut().unwrap() {
|
||||||
PathState::UserSpacePathBuilder(ref mut builder, None) => {
|
PathState::UserSpacePath(ref mut builder, None) => {
|
||||||
return PathBuilderRef {
|
return PathBuilderRef {
|
||||||
builder,
|
builder,
|
||||||
transform: Transform2D::identity(),
|
transform: Transform2D::identity(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
PathState::DeviceSpacePathBuilder(ref mut builder) => {
|
PathState::DeviceSpacePath(ref mut builder) => {
|
||||||
return PathBuilderRef {
|
return PathBuilderRef {
|
||||||
builder,
|
builder,
|
||||||
transform: self.drawtarget.get_transform(),
|
transform: self.drawtarget.get_transform(),
|
||||||
|
@ -1039,15 +947,15 @@ impl<'a, B: Backend> CanvasData<'a, B> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match *self.path_state.as_mut().unwrap() {
|
match *self.path_state.as_mut().unwrap() {
|
||||||
PathState::UserSpacePathBuilder(ref mut builder, None) => PathBuilderRef {
|
PathState::UserSpacePath(ref mut builder, None) => PathBuilderRef {
|
||||||
builder,
|
builder,
|
||||||
transform: Transform2D::identity(),
|
transform: Transform2D::identity(),
|
||||||
},
|
},
|
||||||
PathState::DeviceSpacePathBuilder(ref mut builder) => PathBuilderRef {
|
PathState::DeviceSpacePath(ref mut builder) => PathBuilderRef {
|
||||||
builder,
|
builder,
|
||||||
transform: self.drawtarget.get_transform(),
|
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
|
// If there is an in-progress path, store the existing transformation required
|
||||||
// to move between device and user space.
|
// to move between device and user space.
|
||||||
match self.path_state.as_mut() {
|
match self.path_state.as_mut() {
|
||||||
None | Some(PathState::DeviceSpacePathBuilder(..)) => (),
|
None | Some(PathState::DeviceSpacePath(..)) => (),
|
||||||
Some(PathState::UserSpacePathBuilder(_, transform)) |
|
|
||||||
Some(PathState::UserSpacePath(_, transform)) => {
|
Some(PathState::UserSpacePath(_, transform)) => {
|
||||||
if transform.is_none() {
|
if transform.is_none() {
|
||||||
*transform = Some(self.drawtarget.get_transform());
|
*transform = Some(self.drawtarget.get_transform());
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::RefCell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use canvas_traits::canvas::*;
|
use canvas_traits::canvas::*;
|
||||||
|
@ -17,8 +17,8 @@ use raqote::PathOp;
|
||||||
use style::color::AbsoluteColor;
|
use style::color::AbsoluteColor;
|
||||||
|
|
||||||
use crate::backend::{
|
use crate::backend::{
|
||||||
Backend, DrawOptionsHelpers, GenericDrawTarget, GenericPathBuilder, PathHelpers,
|
Backend, DrawOptionsHelpers, GenericDrawTarget, GenericPath, PatternHelpers,
|
||||||
PatternHelpers, StrokeOptionsHelpers,
|
StrokeOptionsHelpers,
|
||||||
};
|
};
|
||||||
use crate::canvas_data::{CanvasPaintState, Filter, TextRun};
|
use crate::canvas_data::{CanvasPaintState, Filter, TextRun};
|
||||||
|
|
||||||
|
@ -40,9 +40,8 @@ impl Backend for RaqoteBackend {
|
||||||
type DrawOptions = raqote::DrawOptions;
|
type DrawOptions = raqote::DrawOptions;
|
||||||
type CompositionOp = raqote::BlendMode;
|
type CompositionOp = raqote::BlendMode;
|
||||||
type DrawTarget = raqote::DrawTarget;
|
type DrawTarget = raqote::DrawTarget;
|
||||||
type PathBuilder = PathBuilder;
|
|
||||||
type SourceSurface = Vec<u8>; // TODO: See if we can avoid the alloc (probably?)
|
type SourceSurface = Vec<u8>; // TODO: See if we can avoid the alloc (probably?)
|
||||||
type Path = raqote::Path;
|
type Path = Path;
|
||||||
type GradientStop = raqote::GradientStop;
|
type GradientStop = raqote::GradientStop;
|
||||||
type GradientStops = Vec<raqote::GradientStop>;
|
type GradientStops = Vec<raqote::GradientStop>;
|
||||||
|
|
||||||
|
@ -326,24 +325,6 @@ impl DrawOptionsHelpers for raqote::DrawOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn copy_to_builder(&self) -> PathBuilder {
|
|
||||||
PathBuilder(Some(raqote::PathBuilder::from(self.clone())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_gradient_stops(gradient_stops: Vec<CanvasGradientStop>) -> Vec<raqote::GradientStop> {
|
fn create_gradient_stops(gradient_stops: Vec<CanvasGradientStop>) -> Vec<raqote::GradientStop> {
|
||||||
let mut stops = gradient_stops
|
let mut stops = gradient_stops
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -366,7 +347,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
|
||||||
let mut options = raqote::DrawOptions::new();
|
let mut options = raqote::DrawOptions::new();
|
||||||
options.blend_mode = raqote::BlendMode::Clear;
|
options.blend_mode = raqote::BlendMode::Clear;
|
||||||
let pattern = Pattern::Color(0, 0, 0, 0);
|
let pattern = Pattern::Color(0, 0, 0, 0);
|
||||||
<Self as GenericDrawTarget<RaqoteBackend>>::fill(self, &pb.finish(), &pattern, &options);
|
<Self as GenericDrawTarget<RaqoteBackend>>::fill(self, &pb.into(), &pattern, &options);
|
||||||
}
|
}
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn copy_surface(
|
fn copy_surface(
|
||||||
|
@ -382,9 +363,6 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
|
||||||
raqote::DrawTarget::copy_surface(self, &dt, source.to_box2d(), destination);
|
raqote::DrawTarget::copy_surface(self, &dt, source.to_box2d(), destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_path_builder(&self) -> <RaqoteBackend as Backend>::PathBuilder {
|
|
||||||
PathBuilder::new()
|
|
||||||
}
|
|
||||||
fn create_similar_draw_target(
|
fn create_similar_draw_target(
|
||||||
&self,
|
&self,
|
||||||
size: &Size2D<i32>,
|
size: &Size2D<i32>,
|
||||||
|
@ -440,12 +418,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
|
||||||
dest.size.height as f32,
|
dest.size.height as f32,
|
||||||
);
|
);
|
||||||
|
|
||||||
<Self as GenericDrawTarget<RaqoteBackend>>::fill(
|
<Self as GenericDrawTarget<RaqoteBackend>>::fill(self, &pb.into(), &pattern, draw_options);
|
||||||
self,
|
|
||||||
&pb.finish(),
|
|
||||||
&pattern,
|
|
||||||
draw_options,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
fn draw_surface_with_shadow(
|
fn draw_surface_with_shadow(
|
||||||
&self,
|
&self,
|
||||||
|
@ -464,10 +437,11 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
|
||||||
pattern: &<RaqoteBackend as Backend>::Pattern<'_>,
|
pattern: &<RaqoteBackend as Backend>::Pattern<'_>,
|
||||||
draw_options: &<RaqoteBackend as Backend>::DrawOptions,
|
draw_options: &<RaqoteBackend as Backend>::DrawOptions,
|
||||||
) {
|
) {
|
||||||
|
let path = path.into();
|
||||||
match draw_options.blend_mode {
|
match draw_options.blend_mode {
|
||||||
raqote::BlendMode::Src => {
|
raqote::BlendMode::Src => {
|
||||||
self.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0));
|
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::Clear |
|
||||||
raqote::BlendMode::SrcAtop |
|
raqote::BlendMode::SrcAtop |
|
||||||
|
@ -476,7 +450,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
|
||||||
raqote::BlendMode::Xor |
|
raqote::BlendMode::Xor |
|
||||||
raqote::BlendMode::DstOver |
|
raqote::BlendMode::DstOver |
|
||||||
raqote::BlendMode::SrcOver => {
|
raqote::BlendMode::SrcOver => {
|
||||||
self.fill(path, &source(pattern), draw_options);
|
self.fill(&path, &source(pattern), draw_options);
|
||||||
},
|
},
|
||||||
raqote::BlendMode::SrcIn |
|
raqote::BlendMode::SrcIn |
|
||||||
raqote::BlendMode::SrcOut |
|
raqote::BlendMode::SrcOut |
|
||||||
|
@ -485,7 +459,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
|
||||||
let mut options = *draw_options;
|
let mut options = *draw_options;
|
||||||
self.push_layer_with_blend(1., options.blend_mode);
|
self.push_layer_with_blend(1., options.blend_mode);
|
||||||
options.blend_mode = raqote::BlendMode::SrcOver;
|
options.blend_mode = raqote::BlendMode::SrcOver;
|
||||||
self.fill(path, &source(pattern), &options);
|
self.fill(&path, &source(pattern), &options);
|
||||||
self.pop_layer();
|
self.pop_layer();
|
||||||
},
|
},
|
||||||
_ => warn!("unrecognized blend mode: {:?}", draw_options.blend_mode),
|
_ => warn!("unrecognized blend mode: {:?}", draw_options.blend_mode),
|
||||||
|
@ -566,7 +540,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
|
||||||
rect.size.height,
|
rect.size.height,
|
||||||
);
|
);
|
||||||
|
|
||||||
<Self as GenericDrawTarget<RaqoteBackend>>::fill(self, &pb.finish(), pattern, draw_options);
|
<Self as GenericDrawTarget<RaqoteBackend>>::fill(self, &pb.into(), pattern, draw_options);
|
||||||
}
|
}
|
||||||
fn get_size(&self) -> Size2D<i32> {
|
fn get_size(&self) -> Size2D<i32> {
|
||||||
Size2D::new(self.width(), self.height())
|
Size2D::new(self.width(), self.height())
|
||||||
|
@ -578,7 +552,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
|
||||||
self.pop_clip();
|
self.pop_clip();
|
||||||
}
|
}
|
||||||
fn push_clip(&mut self, path: &<RaqoteBackend as Backend>::Path) {
|
fn push_clip(&mut self, path: &<RaqoteBackend as Backend>::Path) {
|
||||||
self.push_clip(path);
|
self.push_clip(&path.into());
|
||||||
}
|
}
|
||||||
fn set_transform(&mut self, matrix: &Transform2D<f32>) {
|
fn set_transform(&mut self, matrix: &Transform2D<f32>) {
|
||||||
self.set_transform(matrix);
|
self.set_transform(matrix);
|
||||||
|
@ -593,7 +567,7 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
|
||||||
stroke_options: &<RaqoteBackend as Backend>::StrokeOptions,
|
stroke_options: &<RaqoteBackend as Backend>::StrokeOptions,
|
||||||
draw_options: &<RaqoteBackend as Backend>::DrawOptions,
|
draw_options: &<RaqoteBackend as Backend>::DrawOptions,
|
||||||
) {
|
) {
|
||||||
self.stroke(path, &source(pattern), stroke_options, draw_options);
|
self.stroke(&path.into(), &source(pattern), stroke_options, draw_options);
|
||||||
}
|
}
|
||||||
fn stroke_rect(
|
fn stroke_rect(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -630,22 +604,47 @@ impl Filter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct PathBuilder(Option<raqote::PathBuilder>);
|
pub(crate) struct Path(Cell<raqote::PathBuilder>);
|
||||||
|
|
||||||
impl PathBuilder {
|
impl From<raqote::PathBuilder> for Path {
|
||||||
fn new() -> PathBuilder {
|
fn from(path_builder: raqote::PathBuilder) -> Self {
|
||||||
PathBuilder(Some(raqote::PathBuilder::new()))
|
Self(Cell::new(path_builder))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenericPathBuilder<RaqoteBackend> 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<RaqoteBackend> for Path {
|
||||||
|
fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D<f32>) -> 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(
|
fn bezier_curve_to(
|
||||||
&mut self,
|
&mut self,
|
||||||
control_point1: &Point2D<f32>,
|
control_point1: &Point2D<f32>,
|
||||||
control_point2: &Point2D<f32>,
|
control_point2: &Point2D<f32>,
|
||||||
control_point3: &Point2D<f32>,
|
control_point3: &Point2D<f32>,
|
||||||
) {
|
) {
|
||||||
self.0.as_mut().unwrap().cubic_to(
|
self.0.get_mut().cubic_to(
|
||||||
control_point1.x,
|
control_point1.x,
|
||||||
control_point1.y,
|
control_point1.y,
|
||||||
control_point2.x,
|
control_point2.x,
|
||||||
|
@ -656,12 +655,11 @@ impl GenericPathBuilder<RaqoteBackend> for PathBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close(&mut self) {
|
fn close(&mut self) {
|
||||||
self.0.as_mut().unwrap().close();
|
self.0.get_mut().close();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_current_point(&mut self) -> Option<Point2D<f32>> {
|
fn get_current_point(&mut self) -> Option<Point2D<f32>> {
|
||||||
let path = self.finish();
|
let path: raqote::Path = (&*self).into();
|
||||||
self.0 = Some(path.clone().into());
|
|
||||||
|
|
||||||
path.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::MoveTo(point) | PathOp::LineTo(point) => Some(Point2D::new(point.x, point.y)),
|
||||||
|
@ -672,21 +670,22 @@ impl GenericPathBuilder<RaqoteBackend> for PathBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_to(&mut self, point: Point2D<f32>) {
|
fn line_to(&mut self, point: Point2D<f32>) {
|
||||||
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<f32>) {
|
fn move_to(&mut self, point: Point2D<f32>) {
|
||||||
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<f32>, end_point: &Point2D<f32>) {
|
fn quadratic_curve_to(&mut self, control_point: &Point2D<f32>, end_point: &Point2D<f32>) {
|
||||||
self.0.as_mut().unwrap().quad_to(
|
self.0
|
||||||
control_point.x,
|
.get_mut()
|
||||||
control_point.y,
|
.quad_to(control_point.x, control_point.y, end_point.x, end_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<f32>) {
|
||||||
|
let path: raqote::Path = (&*self).into();
|
||||||
|
self.0.set(path.transform(transform).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue