auto merge of #5414 : mmatyas/servo/canvas_arcto, r=jdm

Based on the implementation in Firefox.

After comparing how `arcTo` works in different browsers, I've found the code in Firefox the cleanest implentation. It also performed better on some test, for example the one on [this site](http://flashcanvas.net/examples/dl.dropbox.com/u/1865210/mindcat/arcto.html). In (Linux) Firefox 36, it looks like [this](http://i59.tinypic.com/2ch5b2d.png), while in Chromium 41, [some features are missing](http://i58.tinypic.com/30u5w8z.png).
This commit is contained in:
bors-servo 2015-04-01 15:00:43 -06:00
commit ae31d9d9e8
20 changed files with 81 additions and 76 deletions

View file

@ -16,6 +16,7 @@ use util::vec::byte_swap;
use cssparser::RGBA;
use std::borrow::ToOwned;
use std::num::Float;
use std::sync::mpsc::{channel, Sender};
#[derive(Clone)]
@ -34,6 +35,7 @@ pub enum CanvasMsg {
QuadraticCurveTo(Point2D<f32>, Point2D<f32>),
BezierCurveTo(Point2D<f32>, Point2D<f32>, Point2D<f32>),
Arc(Point2D<f32>, f32, f32, f32, bool),
ArcTo(Point2D<f32>, Point2D<f32>, f32),
SetFillStyle(FillOrStrokeStyle),
SetStrokeStyle(FillOrStrokeStyle),
SetTransform(Matrix2D<f32>),
@ -230,6 +232,9 @@ impl<'a> CanvasPaintTask<'a> {
CanvasMsg::Arc(ref center, radius, start, end, ccw) => {
painter.arc(center, radius, start, end, ccw)
}
CanvasMsg::ArcTo(ref cp1, ref cp2, radius) => {
painter.arc_to(cp1, cp2, radius)
}
CanvasMsg::SetFillStyle(style) => painter.set_fill_style(style),
CanvasMsg::SetStrokeStyle(style) => painter.set_stroke_style(style),
CanvasMsg::SetTransform(ref matrix) => painter.set_transform(matrix),
@ -344,6 +349,61 @@ impl<'a> CanvasPaintTask<'a> {
self.path_builder.arc(*center, radius, start_angle, end_angle, ccw)
}
fn arc_to(&self,
cp1: &Point2D<AzFloat>,
cp2: &Point2D<AzFloat>,
radius: AzFloat) {
let cp0 = self.path_builder.get_current_point();
let cp1 = *cp1;
let cp2 = *cp2;
if (cp0.x == cp1.x && cp0.y == cp1.y) || cp1 == cp2 || radius == 0.0 {
self.line_to(&cp1);
return;
}
// if all three control points lie on a single straight line,
// connect the first two by a straight line
let direction = (cp2.x - cp1.x) * (cp0.y - cp1.y) + (cp2.y - cp1.y) * (cp1.x - cp0.x);
if direction == 0.0 {
self.line_to(&cp1);
return;
}
// otherwise, draw the Arc
let a2 = (cp0.x - cp1.x).powi(2) + (cp0.y - cp1.y).powi(2);
let b2 = (cp1.x - cp2.x).powi(2) + (cp1.y - cp2.y).powi(2);
let d = {
let c2 = (cp0.x - cp2.x).powi(2) + (cp0.y - cp2.y).powi(2);
let cosx = (a2 + b2 - c2) / (2.0 * (a2 * b2).sqrt());
let sinx = (1.0 - cosx.powi(2)).sqrt();
radius / ((1.0 - cosx) / sinx)
};
// first tangent point
let anx = (cp1.x - cp0.x) / a2.sqrt();
let any = (cp1.y - cp0.y) / a2.sqrt();
let tp1 = Point2D::<AzFloat>(cp1.x - anx * d, cp1.y - any * d);
// second tangent point
let bnx = (cp1.x - cp2.x) / b2.sqrt();
let bny = (cp1.y - cp2.y) / b2.sqrt();
let tp2 = Point2D::<AzFloat>(cp1.x - bnx * d, cp1.y - bny * d);
// arc center and angles
let anticlockwise = direction < 0.0;
let cx = tp1.x + any * radius * if anticlockwise { 1.0 } else { -1.0 };
let cy = tp1.y - anx * radius * if anticlockwise { 1.0 } else { -1.0 };
let angle_start = (tp1.y - cy).atan2(tp1.x - cx);
let angle_end = (tp2.y - cy).atan2(tp2.x - cx);
self.line_to(&tp1);
if [cx, cy, angle_start, angle_end].iter().all(|x| x.is_finite()) {
self.arc(&Point2D::<AzFloat>(cx, cy), radius,
angle_start, angle_end, anticlockwise);
}
}
fn set_fill_style(&mut self, style: FillOrStrokeStyle) {
self.fill_style = style.to_azure_pattern(&self.drawtarget)
}

View file

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![feature(collections)]
#![feature(std_misc)]
extern crate azure;
extern crate cssparser;

View file

@ -477,6 +477,19 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
Ok(())
}
fn ArcTo(self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, r: f64) -> Fallible<()> {
if !([cp1x, cp1y, cp2x, cp2y, r].iter().all(|x| x.is_finite())) {
return Ok(());
}
if r < 0.0 {
return Err(IndexSize);
}
self.renderer.send(CanvasMsg::ArcTo(Point2D(cp1x as f32, cp1y as f32),
Point2D(cp2x as f32, cp2y as f32),
r as f32)).unwrap();
Ok(())
}
// https://html.spec.whatwg.org/#dom-context-2d-imagesmoothingenabled
fn ImageSmoothingEnabled(self) -> bool {
self.image_smoothing_enabled.get()

View file

@ -145,7 +145,10 @@ interface CanvasPathMethods {
unrestricted double x,
unrestricted double y);
//void arcTo(double x1, double y1, double x2, double y2, double radius);
[Throws]
void arcTo(unrestricted double x1, unrestricted double y1,
unrestricted double x2, unrestricted double y2,
unrestricted double radius);
// NOT IMPLEMENTED [LenientFloat] void arcTo(double x1, double y1, double x2, double y2, double radiusX, double radiusY, double rotation);
//void rect(double x, double y, double w, double h);

View file

@ -31,7 +31,7 @@ source = "git+https://github.com/tomaka/android-rs-glue#5a68056599fb498b0cf3715f
[[package]]
name = "azure"
version = "0.1.0"
source = "git+https://github.com/servo/rust-azure#9aae2113fb19a34a67a82b7ec6a5e0b34e290d29"
source = "git+https://github.com/servo/rust-azure#c71473d94ae24fb195987660698207d5088a4042"
dependencies = [
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",

2
ports/cef/Cargo.lock generated
View file

@ -36,7 +36,7 @@ source = "git+https://github.com/tomaka/android-rs-glue#5a68056599fb498b0cf3715f
[[package]]
name = "azure"
version = "0.1.0"
source = "git+https://github.com/servo/rust-azure#9aae2113fb19a34a67a82b7ec6a5e0b34e290d29"
source = "git+https://github.com/servo/rust-azure#c71473d94ae24fb195987660698207d5088a4042"
dependencies = [
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",

2
ports/gonk/Cargo.lock generated
View file

@ -24,7 +24,7 @@ dependencies = [
[[package]]
name = "azure"
version = "0.1.0"
source = "git+https://github.com/servo/rust-azure#9aae2113fb19a34a67a82b7ec6a5e0b34e290d29"
source = "git+https://github.com/servo/rust-azure#c71473d94ae24fb195987660698207d5088a4042"
dependencies = [
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",

View file

@ -1,5 +0,0 @@
[2d.path.arcTo.coincide.2.html]
type: testharness
[arcTo() draws a straight line to P1 if P1 = P2]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.arcTo.collinear.1.html]
type: testharness
[arcTo() with all points on a line, and P1 between P0/P2, draws a straight line to P1]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.arcTo.collinear.2.html]
type: testharness
[arcTo() with all points on a line, and P2 between P0/P1, draws a straight line to P1]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.arcTo.collinear.3.html]
type: testharness
[arcTo() with all points on a line, and P0 between P1/P2, draws a straight line to P1]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.arcTo.ensuresubpath.1.html]
type: testharness
[If there is no subpath, the first control point is added (and nothing is drawn up to it)]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.arcTo.ensuresubpath.2.html]
type: testharness
[If there is no subpath, the first control point is added]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.arcTo.negative.html]
type: testharness
[arcTo() with negative radius throws an exception]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.arcTo.nonfinite.html]
type: testharness
[arcTo() with Infinity/NaN is ignored]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.arcTo.shape.end.html]
type: testharness
[arcTo() does not draw anything from P1 to P2]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.arcTo.zero.1.html]
type: testharness
[arcTo() with zero radius draws a straight line from P0 to P1]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.arcTo.zero.2.html]
type: testharness
[arcTo() with zero radius draws a straight line from P0 to P1, even when all points are collinear]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.stroke.prune.arc.html]
type: testharness
[Zero-length line segments from arcTo and arc are removed before stroking]
expected: FAIL

View file

@ -7083,9 +7083,6 @@
[CanvasRenderingContext2D interface: attribute direction]
expected: FAIL
[CanvasRenderingContext2D interface: operation arcTo(unrestricted double,unrestricted double,unrestricted double,unrestricted double,unrestricted double)]
expected: FAIL
[CanvasRenderingContext2D interface: operation arcTo(unrestricted double,unrestricted double,unrestricted double,unrestricted double,unrestricted double,unrestricted double,unrestricted double)]
expected: FAIL
@ -7305,15 +7302,6 @@
[CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "direction" with the proper type (69)]
expected: FAIL
[CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "arcTo" with the proper type (75)]
expected: FAIL
[CanvasRenderingContext2D interface: calling arcTo(unrestricted double,unrestricted double,unrestricted double,unrestricted double,unrestricted double) on document.createElement("canvas").getContext("2d") with too few arguments must throw TypeError]
expected: FAIL
[CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "arcTo" with the proper type (76)]
expected: FAIL
[CanvasRenderingContext2D interface: calling arcTo(unrestricted double,unrestricted double,unrestricted double,unrestricted double,unrestricted double,unrestricted double,unrestricted double) on document.createElement("canvas").getContext("2d") with too few arguments must throw TypeError]
expected: FAIL