Skip to content

Commit

Permalink
Auto merge of #24201 - pylbrecht:raqote, r=jdm
Browse files Browse the repository at this point in the history
Implement raqote backend for 2D canvas rendering

<!-- Please describe your changes on the following line: -->

---
<!-- 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
- [ ] These changes fix (part of) #23431

<!-- Either: -->
- [ ] 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. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/24201)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo committed Sep 17, 2019
2 parents 8bc8981 + 2a44994 commit 052d3d9
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 16 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

130 changes: 118 additions & 12 deletions components/canvas/raqote_backend.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -185,8 +186,7 @@ impl Path {
}

pub fn contains_point(&self, x: f64, y: f64, _path_transform: &Transform2D<f32>) -> bool {
let path = self.as_raqote();
path.contains_point(0.1, path.winding, x as f32, y as f32)
self.as_raqote().contains_point(0.1, x as f32, y as f32)
}

pub fn copy_to_builder(&self) -> Box<dyn GenericPathBuilder> {
Expand Down Expand Up @@ -483,16 +483,82 @@ 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!();
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();

Expand Down Expand Up @@ -561,6 +627,22 @@ pub trait ToRaqoteSource<'a> {
fn to_raqote_source(self) -> Option<raqote::Source<'a>>;
}

pub trait ToRaqoteGradientStop {
fn to_raqote(&self) -> raqote::GradientStop;
}

impl ToRaqoteGradientStop for CanvasGradientStop {
fn to_raqote(&self) -> raqote::GradientStop {
let color: u32 = (self.color.alpha << 8 * 3 |
self.color.red << 8 * 2 |
self.color.green << 8 * 1 |
self.color.blue << 8 * 0)
.into();
let position = self.offset as f32;
raqote::GradientStop { position, color }
}
}

impl<'a> ToRaqoteSource<'a> for FillOrStrokeStyle {
#[allow(unsafe_code)]
fn to_raqote_source(self) -> Option<raqote::Source<'a>> {
Expand All @@ -573,8 +655,32 @@ impl<'a> ToRaqoteSource<'a> for FillOrStrokeStyle {
b: rgba.blue,
a: rgba.alpha,
})),
LinearGradient(_) => unimplemented!(),
RadialGradient(_) => unimplemented!(),
LinearGradient(style) => {
let stops = style.stops.into_iter().map(|s| s.to_raqote()).collect();
let gradient = raqote::Gradient { stops };
let start = Point2D::new(style.x0 as f32, style.y0 as f32);
let end = Point2D::new(style.x1 as f32, style.y1 as f32);
Some(raqote::Source::new_linear_gradient(
gradient,
start,
end,
raqote::Spread::Pad,
))
},
RadialGradient(style) => {
let stops = style.stops.into_iter().map(|s| s.to_raqote()).collect();
let gradient = raqote::Gradient { stops };
let center1 = Point2D::new(style.x0 as f32, style.y0 as f32);
let center2 = Point2D::new(style.x1 as f32, style.y1 as f32);
Some(raqote::Source::new_two_circle_radial_gradient(
gradient,
center1,
style.r0 as f32,
center2,
style.r1 as f32,
raqote::Spread::Pad,
))
},
Surface(ref surface) => {
let data = &surface.surface_data[..];
Some(raqote::Source::Image(
Expand Down Expand Up @@ -645,7 +751,7 @@ impl ToRaqoteStyle for BlendingStyle {
BlendingStyle::Saturation => raqote::BlendMode::Saturation,
BlendingStyle::Color => raqote::BlendMode::Color,
BlendingStyle::Luminosity => raqote::BlendMode::Luminosity,
BlendingStyle::ColorBurn => unimplemented!("raqote doesn't support colorburn"),
BlendingStyle::ColorBurn => raqote::BlendMode::ColorBurn,
}
}
}
Expand Down

0 comments on commit 052d3d9

Please sign in to comment.