canvas: Move current_default_path to script CanvasState (#38114)

This PR moves current path handling from canvas paint thread to script's
`CanvasState`. Because we used manual impl for arcto and some prechecks
for early fail before sending IPC we also needed to fix some bugs in
Path impl.

Testing: Existing WPT tests
work towards #38022
try run: https://github.com/sagudev/servo/actions/runs/16316090028

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
sagudev 2025-07-18 07:45:53 +02:00 committed by GitHub
parent bbed6cddcd
commit a070f24450
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 82 additions and 594 deletions

View file

@ -175,6 +175,9 @@ pub(crate) struct CanvasState {
#[no_trace]
missing_image_urls: DomRefCell<Vec<ServoUrl>>,
saved_states: DomRefCell<Vec<CanvasContextState>>,
/// <https://html.spec.whatwg.org/multipage/#current-default-path>
#[no_trace]
current_default_path: DomRefCell<Path>,
}
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<f32>) {
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::<bool>(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,
))
}
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-settransform-matrix>
@ -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)
}
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-ellipse>
@ -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)
}
}