diff --git a/Cargo.lock b/Cargo.lock index 64066e0f1e49..2c561e97ff52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -489,6 +489,7 @@ dependencies = [ "half", "ipc-channel", "log", + "lyon_geom 0.14.0", "num-traits", "pixels", "raqote", diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 9c48df20ef6d..3ad24bea4453 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -29,6 +29,7 @@ gleam = "0.6.7" half = "1" ipc-channel = "0.14" log = "0.4" +lyon_geom = "0.14" num-traits = "0.2" raqote = {git = "https://github.com/jrmuizel/raqote"} time = { version = "0.1.0", optional = true } diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index 362ad1c5c578..8c8e6254c0b3 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -12,8 +12,9 @@ use crate::canvas_paint_thread::AntialiasMode; use canvas_traits::canvas::*; use cssparser::RGBA; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; +use euclid::Angle; +use lyon_geom::Arc; use raqote::PathOp; -use std::f32::consts::PI; use std::marker::PhantomData; pub struct RaqoteBackend; @@ -684,25 +685,19 @@ impl GenericPathBuilder for PathBuilder { &mut self, origin: Point2D, radius: f32, - mut start_angle: f32, - mut end_angle: f32, + start_angle: f32, + end_angle: f32, anticlockwise: bool, ) { - if (!anticlockwise && start_angle > end_angle + 2. * PI) || - (anticlockwise && end_angle > start_angle + 2. * PI) - { - start_angle = start_angle % (2. * PI); - end_angle = end_angle % (2. * PI); - } - - if (anticlockwise && end_angle > 0.) || (!anticlockwise && end_angle < 0.) { - end_angle = -end_angle; - } - - self.0 - .as_mut() - .unwrap() - .arc(origin.x, origin.y, radius, start_angle, end_angle); + self.ellipse( + origin, + radius, + radius, + 0., + start_angle, + end_angle, + anticlockwise, + ); } fn bezier_curve_to( &mut self, @@ -727,77 +722,57 @@ impl GenericPathBuilder for PathBuilder { origin: Point2D, radius_x: f32, radius_y: f32, - _rotation_angle: f32, + rotation_angle: f32, start_angle: f32, - mut end_angle: f32, + end_angle: f32, anticlockwise: bool, ) { - let start_point = Point2D::new( - origin.x + start_angle.cos() * radius_x, - origin.y + end_angle.sin() * radius_y, - ); - self.line_to(start_point); - - if !anticlockwise && (end_angle < start_angle) { - let correction = ((start_angle - end_angle) / (2.0 * PI)).ceil(); - end_angle += correction * 2.0 * PI; - } else if anticlockwise && (start_angle < end_angle) { - let correction = ((end_angle - start_angle) / (2.0 * PI)).ceil(); - end_angle += correction * 2.0 * PI; - } - // Sweeping more than 2 * pi is a full circle. - if !anticlockwise && (end_angle - start_angle > 2.0 * PI) { - end_angle = start_angle + 2.0 * PI; - } else if anticlockwise && (start_angle - end_angle > 2.0 * PI) { - end_angle = start_angle - 2.0 * PI; + let mut start = Angle::radians(start_angle); + let mut end = Angle::radians(end_angle); + + // Wrap angles mod 2 * PI if necessary + if !anticlockwise && start > end + Angle::two_pi() || + anticlockwise && end > start + Angle::two_pi() + { + start = start.positive(); + end = end.positive(); } // Calculate the total arc we're going to sweep. - let mut arc_sweep_left = (end_angle - start_angle).abs(); - let sweep_direction = match anticlockwise { - true => -1.0, - false => 1.0, + let sweep = match anticlockwise { + true => { + if end - start == Angle::two_pi() { + -Angle::two_pi() + } else if end > start { + -(Angle::two_pi() - (end - start)) + } else { + -(start - end) + } + }, + false => { + if start - end == Angle::two_pi() { + Angle::two_pi() + } else if start > end { + Angle::two_pi() - (start - end) + } else { + end - start + } + }, }; - let mut current_start_angle = start_angle; - while arc_sweep_left > 0.0 { - // We guarantee here the current point is the start point of the next - // curve segment. - let current_end_angle; - if arc_sweep_left > PI / 2.0 { - current_end_angle = current_start_angle + PI / 2.0 * sweep_direction; - } else { - current_end_angle = current_start_angle + arc_sweep_left * sweep_direction; - } - let current_start_point = Point2D::new( - origin.x + current_start_angle.cos() * radius_x, - origin.y + current_start_angle.sin() * radius_y, - ); - let current_end_point = Point2D::new( - origin.x + current_end_angle.cos() * radius_x, - origin.y + current_end_angle.sin() * radius_y, - ); - // Calculate kappa constant for partial curve. The sign of angle in the - // tangent will actually ensure this is negative for a counter clockwise - // sweep, so changing signs later isn't needed. - let kappa_factor = - (4.0 / 3.0) * ((current_end_angle - current_start_angle) / 4.0).tan(); - let kappa_x = kappa_factor * radius_x; - let kappa_y = kappa_factor * radius_y; - - let tangent_start = - Point2D::new(-(current_start_angle.sin()), current_start_angle.cos()); - let mut cp1 = current_start_point; - cp1 += Point2D::new(tangent_start.x * kappa_x, tangent_start.y * kappa_y).to_vector(); - let rev_tangent_end = Point2D::new(current_end_angle.sin(), -(current_end_angle.cos())); - let mut cp2 = current_end_point; - cp2 += - Point2D::new(rev_tangent_end.x * kappa_x, rev_tangent_end.y * kappa_y).to_vector(); - - self.bezier_curve_to(&cp1, &cp2, ¤t_end_point); - - arc_sweep_left -= PI / 2.0; - current_start_angle = current_end_angle; - } + + let arc: Arc = Arc { + center: origin, + radii: Vector2D::new(radius_x, radius_y), + start_angle: start, + sweep_angle: sweep, + x_rotation: Angle::radians(rotation_angle), + }; + + self.line_to(arc.from()); + + arc.for_each_quadratic_bezier(&mut |q| { + self.quadratic_curve_to(&q.ctrl, &q.to); + }); } fn get_current_point(&mut self) -> Option> { diff --git a/tests/wpt/metadata/2dcontext/line-styles/2d.line.cap.round.html.ini b/tests/wpt/metadata/2dcontext/line-styles/2d.line.cap.round.html.ini deleted file mode 100644 index 63fd4382c1e8..000000000000 --- a/tests/wpt/metadata/2dcontext/line-styles/2d.line.cap.round.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.line.cap.round.html] - [lineCap 'round' is rendered correctly] - expected: FAIL - diff --git a/tests/wpt/metadata/2dcontext/path-objects/2d.path.arc.twopie.1.html.ini b/tests/wpt/metadata/2dcontext/path-objects/2d.path.arc.twopie.1.html.ini deleted file mode 100644 index 522695b4c569..000000000000 --- a/tests/wpt/metadata/2dcontext/path-objects/2d.path.arc.twopie.1.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arc.twopie.1.html] - [arc() draws nothing when end = start + 2pi-e and anticlockwise] - expected: FAIL - diff --git a/tests/wpt/metadata/2dcontext/path-objects/2d.path.arc.twopie.3.html.ini b/tests/wpt/metadata/2dcontext/path-objects/2d.path.arc.twopie.3.html.ini deleted file mode 100644 index c1e40e7c3d98..000000000000 --- a/tests/wpt/metadata/2dcontext/path-objects/2d.path.arc.twopie.3.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arc.twopie.3.html] - [arc() draws a full circle when end = start + 2pi+e and anticlockwise] - expected: FAIL - diff --git a/tests/wpt/metadata/2dcontext/path-objects/2d.path.arcTo.scale.html.ini b/tests/wpt/metadata/2dcontext/path-objects/2d.path.arcTo.scale.html.ini deleted file mode 100644 index 583a1a2cf0c9..000000000000 --- a/tests/wpt/metadata/2dcontext/path-objects/2d.path.arcTo.scale.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arcTo.scale.html] - [arcTo scales the curve, not just the control points] - expected: FAIL - diff --git a/tests/wpt/metadata/2dcontext/path-objects/2d.path.arcTo.shape.curve2.html.ini b/tests/wpt/metadata/2dcontext/path-objects/2d.path.arcTo.shape.curve2.html.ini deleted file mode 100644 index 3ceefeb7d1ab..000000000000 --- a/tests/wpt/metadata/2dcontext/path-objects/2d.path.arcTo.shape.curve2.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[2d.path.arcTo.shape.curve2.html] - type: testharness - [arcTo() curves in the right kind of shape] - expected: FAIL - diff --git a/tests/wpt/metadata/2dcontext/path-objects/2d.path.arcTo.transformation.html.ini b/tests/wpt/metadata/2dcontext/path-objects/2d.path.arcTo.transformation.html.ini deleted file mode 100644 index 569cfc2fb803..000000000000 --- a/tests/wpt/metadata/2dcontext/path-objects/2d.path.arcTo.transformation.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arcTo.transformation.html] - [arcTo joins up to the last subpath point correctly] - expected: FAIL - diff --git a/tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.round.html.ini b/tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.round.html.ini deleted file mode 100644 index 63fd4382c1e8..000000000000 --- a/tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.round.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.line.cap.round.html] - [lineCap 'round' is rendered correctly] - expected: FAIL - diff --git a/tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.round.worker.js.ini b/tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.round.worker.js.ini deleted file mode 100644 index 8f7f3a0f347e..000000000000 --- a/tests/wpt/metadata/offscreen-canvas/line-styles/2d.line.cap.round.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.line.cap.round.worker.html] - [lineCap 'round' is rendered correctly] - expected: FAIL - diff --git a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arc.twopie.1.html.ini b/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arc.twopie.1.html.ini deleted file mode 100644 index 522695b4c569..000000000000 --- a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arc.twopie.1.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arc.twopie.1.html] - [arc() draws nothing when end = start + 2pi-e and anticlockwise] - expected: FAIL - diff --git a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arc.twopie.1.worker.js.ini b/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arc.twopie.1.worker.js.ini deleted file mode 100644 index ba0a32aebbdd..000000000000 --- a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arc.twopie.1.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arc.twopie.1.worker.html] - [arc() draws nothing when end = start + 2pi-e and anticlockwise] - expected: FAIL - diff --git a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arc.twopie.3.html.ini b/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arc.twopie.3.html.ini deleted file mode 100644 index c1e40e7c3d98..000000000000 --- a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arc.twopie.3.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arc.twopie.3.html] - [arc() draws a full circle when end = start + 2pi+e and anticlockwise] - expected: FAIL - diff --git a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arc.twopie.3.worker.js.ini b/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arc.twopie.3.worker.js.ini deleted file mode 100644 index 07e0bb88d273..000000000000 --- a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arc.twopie.3.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arc.twopie.3.worker.html] - [arc() draws a full circle when end = start + 2pi+e and anticlockwise] - expected: FAIL - diff --git a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.scale.html.ini b/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.scale.html.ini deleted file mode 100644 index 583a1a2cf0c9..000000000000 --- a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.scale.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arcTo.scale.html] - [arcTo scales the curve, not just the control points] - expected: FAIL - diff --git a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.scale.worker.js.ini b/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.scale.worker.js.ini deleted file mode 100644 index 875612e1fa90..000000000000 --- a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.scale.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arcTo.scale.worker.html] - [arcTo scales the curve, not just the control points] - expected: FAIL - diff --git a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.shape.curve2.html.ini b/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.shape.curve2.html.ini deleted file mode 100644 index 604b5e8b6bfe..000000000000 --- a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.shape.curve2.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arcTo.shape.curve2.html] - [arcTo() curves in the right kind of shape] - expected: FAIL - diff --git a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.shape.curve2.worker.js.ini b/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.shape.curve2.worker.js.ini deleted file mode 100644 index 822ca101f8bf..000000000000 --- a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.shape.curve2.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arcTo.shape.curve2.worker.html] - [arcTo() curves in the right kind of shape] - expected: FAIL - diff --git a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.transformation.html.ini b/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.transformation.html.ini deleted file mode 100644 index 569cfc2fb803..000000000000 --- a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.transformation.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arcTo.transformation.html] - [arcTo joins up to the last subpath point correctly] - expected: FAIL - diff --git a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.transformation.worker.js.ini b/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.transformation.worker.js.ini deleted file mode 100644 index 15980b4b69d2..000000000000 --- a/tests/wpt/metadata/offscreen-canvas/path-objects/2d.path.arcTo.transformation.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.path.arcTo.transformation.worker.html] - [arcTo joins up to the last subpath point correctly] - expected: FAIL -