mirror of
https://github.com/servo/servo.git
synced 2025-07-30 10:40:27 +01:00
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:
commit
ae31d9d9e8
20 changed files with 81 additions and 76 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
2
components/servo/Cargo.lock
generated
2
components/servo/Cargo.lock
generated
|
@ -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
2
ports/cef/Cargo.lock
generated
|
@ -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
2
ports/gonk/Cargo.lock
generated
|
@ -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)",
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
[2d.path.arcTo.coincide.2.html]
|
||||
type: testharness
|
||||
[arcTo() draws a straight line to P1 if P1 = P2]
|
||||
expected: FAIL
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
[2d.path.arcTo.negative.html]
|
||||
type: testharness
|
||||
[arcTo() with negative radius throws an exception]
|
||||
expected: FAIL
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
[2d.path.arcTo.nonfinite.html]
|
||||
type: testharness
|
||||
[arcTo() with Infinity/NaN is ignored]
|
||||
expected: FAIL
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
[2d.path.arcTo.shape.end.html]
|
||||
type: testharness
|
||||
[arcTo() does not draw anything from P1 to P2]
|
||||
expected: FAIL
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue