mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
canvas: Use wrapped kurbo::BezPath
for path everywhere (#37967)
This PR removes existing path(segment) abstractions in favor of `kurbo::BezPath`, well actually wrapped `kurbo::BezPath`, to ensure building of valid paths. This allows us better Path2D building in script and doing all validation and segmentation there and also allows us remove blocking is_point_in_path on Path2D as we can now do this in script. Current path is still done on canvas thread side as it will be harder to move to script (will be done as a follow up), but it now uses this new path abstraction. Using kurbo also allows us to ditch our manual svgpath parser with the one provided by kurbo. Same code is stolen from: https://github.com/servo/servo/pull/36821. Testing: Existing WPT tests Fixes: #37904 wpt run: https://github.com/sagudev/servo/actions/runs/16172191716 --------- Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
parent
d4528e84b9
commit
9b5b26386c
23 changed files with 571 additions and 1502 deletions
|
@ -4,7 +4,7 @@
|
|||
|
||||
use std::cell::RefCell;
|
||||
|
||||
use canvas_traits::canvas::PathSegment;
|
||||
use canvas_traits::canvas::Path;
|
||||
use dom_struct::dom_struct;
|
||||
use js::rust::HandleObject;
|
||||
use script_bindings::str::DOMString;
|
||||
|
@ -15,20 +15,20 @@ use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
|
|||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::script_runtime::CanGc;
|
||||
use crate::svgpath::PathParser;
|
||||
|
||||
#[dom_struct]
|
||||
pub(crate) struct Path2D {
|
||||
reflector_: Reflector,
|
||||
#[ignore_malloc_size_of = "Defined in kurbo."]
|
||||
#[no_trace]
|
||||
path: RefCell<Vec<PathSegment>>,
|
||||
path: RefCell<Path>,
|
||||
}
|
||||
|
||||
impl Path2D {
|
||||
pub(crate) fn new() -> Path2D {
|
||||
Self {
|
||||
reflector_: Reflector::new(),
|
||||
path: RefCell::new(vec![]),
|
||||
path: RefCell::new(Path::new()),
|
||||
}
|
||||
}
|
||||
pub(crate) fn new_with_path(other: &Path2D) -> Path2D {
|
||||
|
@ -37,26 +37,15 @@ impl Path2D {
|
|||
path: other.path.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_with_str(path: &str) -> Path2D {
|
||||
let mut path_segments = Vec::new();
|
||||
|
||||
for segment in PathParser::new(path) {
|
||||
if let Ok(segment) = segment {
|
||||
path_segments.push(segment);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
reflector_: Reflector::new(),
|
||||
path: RefCell::new(path_segments),
|
||||
path: RefCell::new(Path::from_svg(path)),
|
||||
}
|
||||
}
|
||||
pub(crate) fn push(&self, seg: PathSegment) {
|
||||
self.path.borrow_mut().push(seg);
|
||||
}
|
||||
pub(crate) fn segments(&self) -> Vec<PathSegment> {
|
||||
|
||||
pub(crate) fn segments(&self) -> Path {
|
||||
self.path.borrow().clone()
|
||||
}
|
||||
}
|
||||
|
@ -64,143 +53,49 @@ impl Path2D {
|
|||
impl Path2DMethods<crate::DomTypeHolder> for Path2D {
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-path2d-addpath>
|
||||
fn AddPath(&self, other: &Path2D) {
|
||||
let other = other.segments();
|
||||
// Step 7. Add all the subpaths in c to a.
|
||||
if std::ptr::eq(&self.path, &other.path) {
|
||||
// Note: this is not part of the spec, but it is a workaround to
|
||||
// avoids borrow conflict when path is same as other.path
|
||||
self.path.borrow_mut().extend_from_within(..);
|
||||
} else {
|
||||
let mut dest = self.path.borrow_mut();
|
||||
dest.extend(other.path.borrow().iter().copied());
|
||||
}
|
||||
self.path.borrow_mut().0.extend(other.0);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath>
|
||||
fn ClosePath(&self) {
|
||||
self.push(PathSegment::ClosePath);
|
||||
self.path.borrow_mut().close_path();
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto>
|
||||
fn MoveTo(&self, x: f64, y: f64) {
|
||||
// Step 1. If either of the arguments are infinite or NaN, then return.
|
||||
if !(x.is_finite() && y.is_finite()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2. Create a new subpath with the specified point as its first (and only) point.
|
||||
self.push(PathSegment::MoveTo {
|
||||
x: x as f32,
|
||||
y: y as f32,
|
||||
});
|
||||
self.path.borrow_mut().move_to(x, y);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-lineto>
|
||||
fn LineTo(&self, x: f64, y: f64) {
|
||||
// Step 1. If either of the arguments are infinite or NaN, then return.
|
||||
if !(x.is_finite() && y.is_finite()) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.push(PathSegment::LineTo {
|
||||
x: x as f32,
|
||||
y: y as f32,
|
||||
});
|
||||
self.path.borrow_mut().line_to(x, y);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-quadraticcurveto>
|
||||
fn QuadraticCurveTo(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
|
||||
// Step 1. If any of the arguments are infinite or NaN, then return.
|
||||
if !(cpx.is_finite() && cpy.is_finite() && x.is_finite() && y.is_finite()) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.push(PathSegment::Quadratic {
|
||||
cpx: cpx as f32,
|
||||
cpy: cpy as f32,
|
||||
x: x as f32,
|
||||
y: y as f32,
|
||||
});
|
||||
self.path.borrow_mut().quadratic_curve_to(cpx, cpy, x, y);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-beziercurveto>
|
||||
fn BezierCurveTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
|
||||
// Step 1. If any of the arguments are infinite or NaN, then return.
|
||||
if !(cp1x.is_finite() &&
|
||||
cp1y.is_finite() &&
|
||||
cp2x.is_finite() &&
|
||||
cp2y.is_finite() &&
|
||||
x.is_finite() &&
|
||||
y.is_finite())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.push(PathSegment::Bezier {
|
||||
cp1x: cp1x as f32,
|
||||
cp1y: cp1y as f32,
|
||||
cp2x: cp2x as f32,
|
||||
cp2y: cp2y as f32,
|
||||
x: x as f32,
|
||||
y: y as f32,
|
||||
});
|
||||
self.path
|
||||
.borrow_mut()
|
||||
.bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-arcto>
|
||||
fn ArcTo(&self, x1: f64, y1: f64, x2: f64, y2: f64, r: f64) -> Fallible<()> {
|
||||
// Step 1. If any of the arguments are infinite or NaN, then return.
|
||||
if !(x1.is_finite() && y1.is_finite() && x2.is_finite() && y2.is_finite() && r.is_finite())
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Step 3. If radius is negative, then throw an "IndexSizeError" DOMException.
|
||||
if r < 0.0 {
|
||||
return Err(Error::IndexSize);
|
||||
}
|
||||
|
||||
self.push(PathSegment::ArcTo {
|
||||
cp1x: x1 as f32,
|
||||
cp1y: y1 as f32,
|
||||
cp2x: x2 as f32,
|
||||
cp2y: y2 as f32,
|
||||
radius: r as f32,
|
||||
});
|
||||
Ok(())
|
||||
fn ArcTo(&self, x1: f64, y1: f64, x2: f64, y2: f64, radius: f64) -> Fallible<()> {
|
||||
self.path
|
||||
.borrow_mut()
|
||||
.arc_to(x1, y1, x2, y2, radius)
|
||||
.map_err(|_| Error::IndexSize)
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-rect>
|
||||
fn Rect(&self, x: f64, y: f64, w: f64, h: f64) {
|
||||
// Step 1. If any of the arguments are infinite or NaN, then return.
|
||||
if !(x.is_finite() && y.is_finite() && w.is_finite() && h.is_finite()) {
|
||||
return;
|
||||
}
|
||||
// Step 2. Create a new subpath containing just the four points
|
||||
// (x, y), (x+w, y), (x+w, y+h), (x, y+h), in that order,
|
||||
// with those four points connected by straight lines.
|
||||
self.push(PathSegment::MoveTo {
|
||||
x: x as f32,
|
||||
y: y as f32,
|
||||
});
|
||||
self.push(PathSegment::LineTo {
|
||||
x: (x + w) as f32,
|
||||
y: y as f32,
|
||||
});
|
||||
self.push(PathSegment::LineTo {
|
||||
x: (x + w) as f32,
|
||||
y: (y + h) as f32,
|
||||
});
|
||||
self.push(PathSegment::LineTo {
|
||||
x: x as f32,
|
||||
y: (y + h) as f32,
|
||||
});
|
||||
// Step 3. Mark the subpath as closed.
|
||||
self.push(PathSegment::ClosePath);
|
||||
|
||||
// Step 4. Create a new subpath with the point (x, y) as the only point in the subpath.
|
||||
self.push(PathSegment::MoveTo {
|
||||
x: x as f32,
|
||||
y: y as f32,
|
||||
});
|
||||
self.path.borrow_mut().rect(x, y, w, h);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-arc>
|
||||
|
@ -208,37 +103,15 @@ impl Path2DMethods<crate::DomTypeHolder> for Path2D {
|
|||
&self,
|
||||
x: f64,
|
||||
y: f64,
|
||||
r: f64,
|
||||
start: f64,
|
||||
end: f64,
|
||||
anticlockwise: bool,
|
||||
radius: f64,
|
||||
start_angle: f64,
|
||||
end_angle: f64,
|
||||
counterclockwise: bool,
|
||||
) -> Fallible<()> {
|
||||
// Step 1. If any of the arguments are infinite or NaN, then return.
|
||||
if !(x.is_finite() &&
|
||||
y.is_finite() &&
|
||||
r.is_finite() &&
|
||||
start.is_finite() &&
|
||||
end.is_finite())
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Step 2. If either radiusX or radiusY are negative, then throw an "IndexSizeError" DOMException.
|
||||
if r < 0.0 {
|
||||
return Err(Error::IndexSize);
|
||||
}
|
||||
|
||||
self.push(PathSegment::Ellipse {
|
||||
x: x as f32,
|
||||
y: y as f32,
|
||||
radius_x: r as f32,
|
||||
radius_y: r as f32,
|
||||
rotation: 0.,
|
||||
start_angle: start as f32,
|
||||
end_angle: end as f32,
|
||||
anticlockwise,
|
||||
});
|
||||
Ok(())
|
||||
self.path
|
||||
.borrow_mut()
|
||||
.arc(x, y, radius, start_angle, end_angle, counterclockwise)
|
||||
.map_err(|_| Error::IndexSize)
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-ellipse>
|
||||
|
@ -246,41 +119,26 @@ impl Path2DMethods<crate::DomTypeHolder> for Path2D {
|
|||
&self,
|
||||
x: f64,
|
||||
y: f64,
|
||||
rx: f64,
|
||||
ry: f64,
|
||||
rotation: f64,
|
||||
start: f64,
|
||||
end: f64,
|
||||
anticlockwise: bool,
|
||||
radius_x: f64,
|
||||
radius_y: f64,
|
||||
rotation_angle: f64,
|
||||
start_angle: f64,
|
||||
end_angle: f64,
|
||||
counterclockwise: bool,
|
||||
) -> Fallible<()> {
|
||||
// Step 1. If any of the arguments are infinite or NaN, then return.
|
||||
if !(x.is_finite() &&
|
||||
y.is_finite() &&
|
||||
rx.is_finite() &&
|
||||
ry.is_finite() &&
|
||||
rotation.is_finite() &&
|
||||
start.is_finite() &&
|
||||
end.is_finite())
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Step 2. If either radiusX or radiusY are negative, then throw an "IndexSizeError" DOMException.
|
||||
if rx < 0.0 || ry < 0.0 {
|
||||
return Err(Error::IndexSize);
|
||||
}
|
||||
|
||||
self.push(PathSegment::Ellipse {
|
||||
x: x as f32,
|
||||
y: y as f32,
|
||||
radius_x: rx as f32,
|
||||
radius_y: ry as f32,
|
||||
rotation: rotation as f32,
|
||||
start_angle: start as f32,
|
||||
end_angle: end as f32,
|
||||
anticlockwise,
|
||||
});
|
||||
Ok(())
|
||||
self.path
|
||||
.borrow_mut()
|
||||
.ellipse(
|
||||
x,
|
||||
y,
|
||||
radius_x,
|
||||
radius_y,
|
||||
rotation_angle,
|
||||
start_angle,
|
||||
end_angle,
|
||||
counterclockwise,
|
||||
)
|
||||
.map_err(|_| Error::IndexSize)
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-path2d-dev>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue