aboutsummaryrefslogtreecommitdiffstats
path: root/components/canvas/raqote_backend.rs
diff options
context:
space:
mode:
authorpylbrecht <palbrecht@mailbox.org>2019-09-12 11:47:50 +0200
committerpylbrecht <palbrecht@mailbox.org>2019-09-17 11:48:44 +0200
commit818b5d4150bebcb46a366304ea7271aeaa0a4344 (patch)
tree50c588f4fef4ef4689aa4fc88dc9b36c778adc6b /components/canvas/raqote_backend.rs
parent8bc8981ae5ccc54caf443663ddb51ab9053d3ad4 (diff)
downloadservo-818b5d4150bebcb46a366304ea7271aeaa0a4344.tar.gz
servo-818b5d4150bebcb46a366304ea7271aeaa0a4344.zip
Add naive implementation for ellipse()
rust-azure's ellipse() C++ implementation copy/pasted and kind of ported to Rust. Obviously needs refactor to turn it into idiomatic Rust.
Diffstat (limited to 'components/canvas/raqote_backend.rs')
-rw-r--r--components/canvas/raqote_backend.rs143
1 files changed, 136 insertions, 7 deletions
diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs
index bd25b5fb9de..17472b648da 100644
--- a/components/canvas/raqote_backend.rs
+++ b/components/canvas/raqote_backend.rs
@@ -12,6 +12,7 @@ use canvas_traits::canvas::*;
use cssparser::RGBA;
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
use raqote::PathOp;
+use std::f32::consts::PI;
use std::marker::PhantomData;
pub struct RaqoteBackend;
@@ -483,16 +484,144 @@ impl GenericPathBuilder for PathBuilder {
}
fn ellipse(
&mut self,
- _origin: Point2D<f32>,
- _radius_x: f32,
- _radius_y: f32,
+ origin: Point2D<f32>,
+ radius_x: f32,
+ radius_y: f32,
_rotation_angle: f32,
- _start_angle: f32,
- _end_angle: f32,
- _anticlockwise: bool,
+ start_angle: f32,
+ mut end_angle: f32,
+ anticlockwise: bool,
) {
- unimplemented!();
+ /*
+ * Point startPoint(aOrigin.x + cosf(aStartAngle) * aRadius.width,
+ * aOrigin.y + sinf(aStartAngle) * aRadius.height);
+ * aSink->LineTo(startPoint);
+ * // Clockwise we always sweep from the smaller to the larger angle, ccw
+ * // it's vice versa.
+ * if (!aAntiClockwise && (aEndAngle < aStartAngle)) {
+ * Float correction = Float(ceil((aStartAngle - aEndAngle) / (2.0f * M_PI)));
+ * aEndAngle += float(correction * 2.0f * M_PI);
+ * } else if (aAntiClockwise && (aStartAngle < aEndAngle)) {
+ * Float correction = (Float)ceil((aEndAngle - aStartAngle) / (2.0f * M_PI));
+ * aStartAngle += float(correction * 2.0f * M_PI);
+ * }
+ * // Sweeping more than 2 * pi is a full circle.
+ * if (!aAntiClockwise && (aEndAngle - aStartAngle > 2 * M_PI)) {
+ * aEndAngle = float(aStartAngle + 2.0f * M_PI);
+ * } else if (aAntiClockwise && (aStartAngle - aEndAngle > 2.0f * M_PI)) {
+ * aEndAngle = float(aStartAngle - 2.0f * M_PI);
+ * }
+ *
+ * // Calculate the total arc we're going to sweep.
+ * Float arcSweepLeft = fabs(aEndAngle - aStartAngle);
+ *
+ * Float sweepDirection = aAntiClockwise ? -1.0f : 1.0f;
+ *
+ * Float currentStartAngle = aStartAngle;
+ *
+ * while (arcSweepLeft > 0) {
+ * // We guarantee here the current point is the start point of the next
+ * // curve segment.
+ * Float currentEndAngle;
+ *
+ * if (arcSweepLeft > M_PI / 2.0f) {
+ * currentEndAngle = Float(currentStartAngle + M_PI / 2.0f * sweepDirection);
+ * } else {
+ * currentEndAngle = currentStartAngle + arcSweepLeft * sweepDirection;
+ * }
+ * Point currentStartPoint(aOrigin.x + cosf(currentStartAngle) * aRadius.width,
+ * aOrigin.y + sinf(currentStartAngle) * aRadius.height);
+ * Point currentEndPoint(aOrigin.x + cosf(currentEndAngle) * aRadius.width,
+ * aOrigin.y + sinf(currentEndAngle) * aRadius.height);
+ *
+ * // 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.
+ * Float kappaFactor = (4.0f / 3.0f) * tan((currentEndAngle - currentStartAngle) / 4.0f);
+ * Float kappaX = kappaFactor * aRadius.width;
+ * Float kappaY = kappaFactor * aRadius.height;
+ *
+ * Point tangentStart(-sin(currentStartAngle), cos(currentStartAngle));
+ * Point cp1 = currentStartPoint;
+ * cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY);
+ *
+ * Point revTangentEnd(sin(currentEndAngle), -cos(currentEndAngle));
+ * Point cp2 = currentEndPoint;
+ * cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY);
+ *
+ * aSink->BezierTo(cp1, cp2, currentEndPoint);
+ *
+ * arcSweepLeft -= Float(M_PI / 2.0f);
+ * currentStartAngle = currentEndAngle;
+ */
+ 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;
+ }
+
+ // 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 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, &current_end_point);
+
+ arc_sweep_left -= PI / 2.0;
+ current_start_angle = current_end_angle;
+ }
}
+
fn get_current_point(&mut self) -> Point2D<f32> {
let path = self.finish();