mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Auto merge of #25801 - pylbrecht:arc.refactor, r=jdm
Refactor CanvasRenderingContext2D.arc() and .ellipse() <!-- Please describe your changes on the following line: --> Refactor `arc()` and `ellipse()` to make use of `lyon_geom::Arc` for approximating an arc with quadratic bezier curves. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix part of #25331 <!-- Either: --> - [x] There are tests for these changes <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
commit
ad9bfc2a62
21 changed files with 55 additions and 151 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -489,6 +489,7 @@ dependencies = [
|
||||||
"half",
|
"half",
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
"log",
|
"log",
|
||||||
|
"lyon_geom 0.14.0",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"pixels",
|
"pixels",
|
||||||
"raqote",
|
"raqote",
|
||||||
|
|
|
@ -29,6 +29,7 @@ gleam = "0.6.7"
|
||||||
half = "1"
|
half = "1"
|
||||||
ipc-channel = "0.14"
|
ipc-channel = "0.14"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
lyon_geom = "0.14"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
raqote = {git = "https://github.com/jrmuizel/raqote"}
|
raqote = {git = "https://github.com/jrmuizel/raqote"}
|
||||||
time = { version = "0.1.0", optional = true }
|
time = { version = "0.1.0", optional = true }
|
||||||
|
|
|
@ -12,8 +12,9 @@ use crate::canvas_paint_thread::AntialiasMode;
|
||||||
use canvas_traits::canvas::*;
|
use canvas_traits::canvas::*;
|
||||||
use cssparser::RGBA;
|
use cssparser::RGBA;
|
||||||
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
|
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
|
||||||
|
use euclid::Angle;
|
||||||
|
use lyon_geom::Arc;
|
||||||
use raqote::PathOp;
|
use raqote::PathOp;
|
||||||
use std::f32::consts::PI;
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
pub struct RaqoteBackend;
|
pub struct RaqoteBackend;
|
||||||
|
@ -684,25 +685,19 @@ impl GenericPathBuilder for PathBuilder {
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: Point2D<f32>,
|
origin: Point2D<f32>,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
mut start_angle: f32,
|
start_angle: f32,
|
||||||
mut end_angle: f32,
|
end_angle: f32,
|
||||||
anticlockwise: bool,
|
anticlockwise: bool,
|
||||||
) {
|
) {
|
||||||
if (!anticlockwise && start_angle > end_angle + 2. * PI) ||
|
self.ellipse(
|
||||||
(anticlockwise && end_angle > start_angle + 2. * PI)
|
origin,
|
||||||
{
|
radius,
|
||||||
start_angle = start_angle % (2. * PI);
|
radius,
|
||||||
end_angle = end_angle % (2. * PI);
|
0.,
|
||||||
}
|
start_angle,
|
||||||
|
end_angle,
|
||||||
if (anticlockwise && end_angle > 0.) || (!anticlockwise && end_angle < 0.) {
|
anticlockwise,
|
||||||
end_angle = -end_angle;
|
);
|
||||||
}
|
|
||||||
|
|
||||||
self.0
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.arc(origin.x, origin.y, radius, start_angle, end_angle);
|
|
||||||
}
|
}
|
||||||
fn bezier_curve_to(
|
fn bezier_curve_to(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -727,77 +722,57 @@ impl GenericPathBuilder for PathBuilder {
|
||||||
origin: Point2D<f32>,
|
origin: Point2D<f32>,
|
||||||
radius_x: f32,
|
radius_x: f32,
|
||||||
radius_y: f32,
|
radius_y: f32,
|
||||||
_rotation_angle: f32,
|
rotation_angle: f32,
|
||||||
start_angle: f32,
|
start_angle: f32,
|
||||||
mut end_angle: f32,
|
end_angle: f32,
|
||||||
anticlockwise: bool,
|
anticlockwise: bool,
|
||||||
) {
|
) {
|
||||||
let start_point = Point2D::new(
|
let mut start = Angle::radians(start_angle);
|
||||||
origin.x + start_angle.cos() * radius_x,
|
let mut end = Angle::radians(end_angle);
|
||||||
origin.y + end_angle.sin() * radius_y,
|
|
||||||
);
|
|
||||||
self.line_to(start_point);
|
|
||||||
|
|
||||||
if !anticlockwise && (end_angle < start_angle) {
|
// Wrap angles mod 2 * PI if necessary
|
||||||
let correction = ((start_angle - end_angle) / (2.0 * PI)).ceil();
|
if !anticlockwise && start > end + Angle::two_pi() ||
|
||||||
end_angle += correction * 2.0 * PI;
|
anticlockwise && end > start + Angle::two_pi()
|
||||||
} else if anticlockwise && (start_angle < end_angle) {
|
{
|
||||||
let correction = ((end_angle - start_angle) / (2.0 * PI)).ceil();
|
start = start.positive();
|
||||||
end_angle += correction * 2.0 * PI;
|
end = end.positive();
|
||||||
}
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the total arc we're going to sweep.
|
// Calculate the total arc we're going to sweep.
|
||||||
let mut arc_sweep_left = (end_angle - start_angle).abs();
|
let sweep = match anticlockwise {
|
||||||
let sweep_direction = match anticlockwise {
|
true => {
|
||||||
true => -1.0,
|
if end - start == Angle::two_pi() {
|
||||||
false => 1.0,
|
-Angle::two_pi()
|
||||||
};
|
} else if end > start {
|
||||||
let mut current_start_angle = start_angle;
|
-(Angle::two_pi() - (end - start))
|
||||||
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 {
|
} else {
|
||||||
current_end_angle = current_start_angle + arc_sweep_left * sweep_direction;
|
-(start - end)
|
||||||
}
|
}
|
||||||
let current_start_point = Point2D::new(
|
},
|
||||||
origin.x + current_start_angle.cos() * radius_x,
|
false => {
|
||||||
origin.y + current_start_angle.sin() * radius_y,
|
if start - end == Angle::two_pi() {
|
||||||
);
|
Angle::two_pi()
|
||||||
let current_end_point = Point2D::new(
|
} else if start > end {
|
||||||
origin.x + current_end_angle.cos() * radius_x,
|
Angle::two_pi() - (start - end)
|
||||||
origin.y + current_end_angle.sin() * radius_y,
|
} else {
|
||||||
);
|
end - start
|
||||||
// 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<f32> = 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<Point2D<f32>> {
|
fn get_current_point(&mut self) -> Option<Point2D<f32>> {
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.line.cap.round.html]
|
|
||||||
[lineCap 'round' is rendered correctly]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arc.twopie.1.html]
|
|
||||||
[arc() draws nothing when end = start + 2pi-e and anticlockwise]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arc.twopie.3.html]
|
|
||||||
[arc() draws a full circle when end = start + 2pi+e and anticlockwise]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arcTo.scale.html]
|
|
||||||
[arcTo scales the curve, not just the control points]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[2d.path.arcTo.shape.curve2.html]
|
|
||||||
type: testharness
|
|
||||||
[arcTo() curves in the right kind of shape]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arcTo.transformation.html]
|
|
||||||
[arcTo joins up to the last subpath point correctly]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.line.cap.round.html]
|
|
||||||
[lineCap 'round' is rendered correctly]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.line.cap.round.worker.html]
|
|
||||||
[lineCap 'round' is rendered correctly]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arc.twopie.1.html]
|
|
||||||
[arc() draws nothing when end = start + 2pi-e and anticlockwise]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arc.twopie.1.worker.html]
|
|
||||||
[arc() draws nothing when end = start + 2pi-e and anticlockwise]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arc.twopie.3.html]
|
|
||||||
[arc() draws a full circle when end = start + 2pi+e and anticlockwise]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arcTo.scale.html]
|
|
||||||
[arcTo scales the curve, not just the control points]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arcTo.scale.worker.html]
|
|
||||||
[arcTo scales the curve, not just the control points]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arcTo.shape.curve2.html]
|
|
||||||
[arcTo() curves in the right kind of shape]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arcTo.shape.curve2.worker.html]
|
|
||||||
[arcTo() curves in the right kind of shape]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arcTo.transformation.html]
|
|
||||||
[arcTo joins up to the last subpath point correctly]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.path.arcTo.transformation.worker.html]
|
|
||||||
[arcTo joins up to the last subpath point correctly]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue