diff --git a/Cargo.lock b/Cargo.lock index 1c2114cbbdf..9f550c216a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -405,7 +405,7 @@ dependencies = [ "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "offscreen_gl_context 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", "pixels 0.0.1", - "raqote 0.6.2-alpha.0 (git+https://github.com/jrmuizel/raqote)", + "raqote 0.6.5-alpha.0 (git+https://github.com/jrmuizel/raqote)", "serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", "servo_config 0.0.1", "sparkle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3688,8 +3688,8 @@ dependencies = [ [[package]] name = "raqote" -version = "0.6.2-alpha.0" -source = "git+https://github.com/jrmuizel/raqote#b1437ce88d27d376520485a1f8d60c5a480be5c1" +version = "0.6.5-alpha.0" +source = "git+https://github.com/jrmuizel/raqote#c9d6d9c65ffac5fe91e2699f9854b3fbaa3c85c2" dependencies = [ "euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "font-kit 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -6170,7 +6170,7 @@ dependencies = [ "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" "checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -"checksum raqote 0.6.2-alpha.0 (git+https://github.com/jrmuizel/raqote)" = "" +"checksum raqote 0.6.5-alpha.0 (git+https://github.com/jrmuizel/raqote)" = "" "checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" "checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" "checksum rayon_croissant 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5b725e815f3aa08718063883a75003336889debafe2f8fa67fbe91563ddc4efa" diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index bd25b5fb9de..b8fcd592de0 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; @@ -185,8 +186,7 @@ impl Path { } pub fn contains_point(&self, x: f64, y: f64, _path_transform: &Transform2D) -> 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 { @@ -483,16 +483,82 @@ impl GenericPathBuilder for PathBuilder { } fn ellipse( &mut self, - _origin: Point2D, - _radius_x: f32, - _radius_y: f32, + origin: Point2D, + 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, ¤t_end_point); + + arc_sweep_left -= PI / 2.0; + current_start_angle = current_end_angle; + } } + fn get_current_point(&mut self) -> Point2D { let path = self.finish(); @@ -561,6 +627,22 @@ pub trait ToRaqoteSource<'a> { fn to_raqote_source(self) -> Option>; } +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 as u32) << 8 * 3 | + (self.color.red as u32) << 8 * 2 | + (self.color.green as u32) << 8 * 1 | + (self.color.blue as u32) << 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> { @@ -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( @@ -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, } } }