diff options
-rw-r--r-- | components/canvas/canvas_data.rs | 10 | ||||
-rw-r--r-- | components/canvas/raqote_backend.rs | 410 |
2 files changed, 247 insertions, 173 deletions
diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index c843ab19454..ab30dbe9741 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -358,15 +358,7 @@ pub enum Pattern<'a> { #[cfg(feature = "canvas2d-azure")] Azure(azure::azure_hl::Pattern, PhantomData<&'a ()>), #[cfg(feature = "canvas2d-raqote")] - Raqote(raqote::Source<'a>, Option<Repetition>), -} - -#[derive(Clone)] -pub enum Repetition { - Repeat, - RepeatX, - RepeatY, - NoRepeat, + Raqote(crate::raqote_backend::Pattern<'a>), } pub enum DrawSurfaceOptions { diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index 73f7762609e..ca93944e4f7 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -2,10 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::canvas_data; use crate::canvas_data::{ Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, ExtendMode, Filter, - GenericDrawTarget, GenericPathBuilder, GradientStop, GradientStops, Path, Pattern, Repetition, - SourceSurface, StrokeOptions, SurfaceFormat, + GenericDrawTarget, GenericPathBuilder, GradientStop, GradientStops, Path, SourceSurface, + StrokeOptions, SurfaceFormat, }; use crate::canvas_paint_thread::AntialiasMode; use canvas_traits::canvas::*; @@ -26,25 +27,26 @@ impl Backend for RaqoteBackend { color.as_raqote().a != 0 } - fn size_from_pattern(&self, rect: &Rect<f32>, pattern: &Pattern) -> Option<Size2D<f32>> { + fn size_from_pattern( + &self, + rect: &Rect<f32>, + pattern: &canvas_data::Pattern, + ) -> Option<Size2D<f32>> { match pattern { - Pattern::Raqote(raqote::Source::Image(image, ..), repetition) => { - if let Some(repeat) = repetition { - match repeat { - Repetition::RepeatX => { - Some(Size2D::new(rect.size.width as f32, image.height as f32)) - }, - Repetition::RepeatY => { - Some(Size2D::new(image.width as f32, rect.size.height as f32)) - }, - Repetition::Repeat => Some(rect.size), - Repetition::NoRepeat => { - Some(Size2D::new(image.width as f32, image.height as f32)) - }, - } - } else { - None - } + canvas_data::Pattern::Raqote(Pattern::Surface(pattern)) => match pattern.repeat { + Repetition::RepeatX => Some(Size2D::new( + rect.size.width as f32, + pattern.image.height as f32, + )), + Repetition::RepeatY => Some(Size2D::new( + pattern.image.width as f32, + rect.size.height as f32, + )), + Repetition::Repeat => Some(rect.size), + Repetition::NoRepeat => Some(Size2D::new( + pattern.image.width as f32, + pattern.image.height as f32, + )), }, _ => None, } @@ -60,8 +62,8 @@ impl Backend for RaqoteBackend { state: &mut CanvasPaintState<'a>, _drawtarget: &dyn GenericDrawTarget, ) { - if let Some(pattern) = style.to_raqote_source() { - state.fill_style = pattern; + if let Some(pattern) = style.to_raqote_pattern() { + state.fill_style = canvas_data::Pattern::Raqote(pattern); } } @@ -71,8 +73,8 @@ impl Backend for RaqoteBackend { state: &mut CanvasPaintState<'a>, _drawtarget: &dyn GenericDrawTarget, ) { - if let Some(pattern) = style.to_raqote_source() { - state.stroke_style = pattern; + if let Some(pattern) = style.to_raqote_pattern() { + state.stroke_style = canvas_data::Pattern::Raqote(pattern); } } @@ -98,54 +100,162 @@ impl Backend for RaqoteBackend { impl<'a> CanvasPaintState<'a> { pub fn new(_antialias: AntialiasMode) -> CanvasPaintState<'a> { - let solid_src = raqote::SolidSource::from_unpremultiplied_argb( - 255, - 0, - 0, - 0, - ); + let pattern = Pattern::Color(255, 0, 0, 0); CanvasPaintState { draw_options: DrawOptions::Raqote(raqote::DrawOptions::new()), - fill_style: Pattern::Raqote(raqote::Source::Solid(solid_src), None), - stroke_style: Pattern::Raqote(raqote::Source::Solid(solid_src), None), + fill_style: canvas_data::Pattern::Raqote(pattern.clone()), + stroke_style: canvas_data::Pattern::Raqote(pattern), stroke_opts: StrokeOptions::Raqote(Default::default(), PhantomData), transform: Transform2D::identity(), shadow_offset_x: 0.0, shadow_offset_y: 0.0, shadow_blur: 0.0, - shadow_color: Color::Raqote(raqote::SolidSource::from_unpremultiplied_argb( - 0, - 0, - 0, - 0, - )), + shadow_color: Color::Raqote(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)), } } } -impl Pattern<'_> { - pub fn is_zero_size_gradient(&self) -> bool { - match self { - Pattern::Raqote(source, _) => { - use raqote::Source::*; - - match source { - LinearGradient(g, ..) | - RadialGradient(g, ..) | - TwoCircleRadialGradient(g, ..) => g.stops.is_empty(), - _ => false, - } +#[derive(Clone)] +pub enum Pattern<'a> { + // argb + Color(u8, u8, u8, u8), + LinearGradient(LinearGradientPattern), + RadialGradient(RadialGradientPattern), + Surface(SurfacePattern<'a>), +} + +#[derive(Clone)] +pub struct LinearGradientPattern { + gradient: raqote::Gradient, + start: Point2D<f32>, + end: Point2D<f32>, +} + +impl LinearGradientPattern { + fn new(start: Point2D<f32>, end: Point2D<f32>, stops: Vec<raqote::GradientStop>) -> Self { + LinearGradientPattern { + gradient: raqote::Gradient { stops: stops }, + start: start, + end: end, + } + } +} + +#[derive(Clone)] +pub struct RadialGradientPattern { + gradient: raqote::Gradient, + center1: Point2D<f32>, + radius1: f32, + center2: Point2D<f32>, + radius2: f32, +} + +impl RadialGradientPattern { + fn new( + center1: Point2D<f32>, + radius1: f32, + center2: Point2D<f32>, + radius2: f32, + stops: Vec<raqote::GradientStop>, + ) -> Self { + RadialGradientPattern { + gradient: raqote::Gradient { stops: stops }, + center1: center1, + radius1: radius1, + center2: center2, + radius2: radius2, + } + } +} + +#[derive(Clone)] +pub struct SurfacePattern<'a> { + image: raqote::Image<'a>, + filter: raqote::FilterMode, + extend: raqote::ExtendMode, + repeat: Repetition, +} + +impl<'a> SurfacePattern<'a> { + fn new(image: raqote::Image<'a>, filter: raqote::FilterMode, repeat: Repetition) -> Self { + let extend = match repeat { + Repetition::NoRepeat => raqote::ExtendMode::Pad, + Repetition::RepeatX | Repetition::RepeatY | Repetition::Repeat => { + raqote::ExtendMode::Repeat }, + }; + SurfacePattern { + image: image, + filter: filter, + extend: extend, + repeat: repeat, + } + } +} + +#[derive(Clone)] +pub enum Repetition { + Repeat, + RepeatX, + RepeatY, + NoRepeat, +} + +impl Repetition { + fn from_xy(repeat_x: bool, repeat_y: bool) -> Self { + if repeat_x && repeat_y { + Repetition::Repeat + } else if repeat_x { + Repetition::RepeatX + } else if repeat_y { + Repetition::RepeatY + } else { + Repetition::NoRepeat } } - pub fn source(&self) -> &raqote::Source { +} + +impl canvas_data::Pattern<'_> { + pub fn source(&self) -> raqote::Source { match self { - Pattern::Raqote(source, _) => source, + canvas_data::Pattern::Raqote(pattern) => match pattern { + Pattern::Color(a, r, g, b) => raqote::Source::Solid( + raqote::SolidSource::from_unpremultiplied_argb(*a, *r, *g, *b), + ), + Pattern::LinearGradient(pattern) => raqote::Source::new_linear_gradient( + pattern.gradient.clone(), + pattern.start, + pattern.end, + raqote::Spread::Pad, + ), + Pattern::RadialGradient(pattern) => raqote::Source::new_two_circle_radial_gradient( + pattern.gradient.clone(), + pattern.center1, + pattern.radius1, + pattern.center2, + pattern.radius2, + raqote::Spread::Pad, + ), + Pattern::Surface(pattern) => raqote::Source::Image( + pattern.image, + pattern.extend, + pattern.filter, + Transform2D::identity(), + ), + }, } } - pub fn repetition(&self) -> &Option<Repetition> { + pub fn is_zero_size_gradient(&self) -> bool { match self { - Pattern::Raqote(_, repetition) => repetition, + canvas_data::Pattern::Raqote(pattern) => match pattern { + Pattern::RadialGradient(pattern) => { + let centers_equal = pattern.center1 == pattern.center2; + let radii_equal = pattern.radius1 == pattern.radius2; + centers_equal && radii_equal + }, + Pattern::LinearGradient(pattern) => pattern.start == pattern.end, + Pattern::Color(..) | Pattern::Surface(..) => false, + }, } } } @@ -226,6 +336,16 @@ impl Path { } } +fn create_gradient_stops(gradient_stops: Vec<GradientStop>) -> GradientStops { + let mut stops = gradient_stops + .into_iter() + .map(|item| item.as_raqote().clone()) + .collect::<Vec<raqote::GradientStop>>(); + // https://www.w3.org/html/test/results/2dcontext/annotated-spec/canvas.html#testrefs.2d.gradient.interpolate.overlap + stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap()); + GradientStops::Raqote(stops) +} + impl GenericDrawTarget for raqote::DrawTarget { fn clear_rect(&mut self, rect: &Rect<f32>) { let mut pb = raqote::PathBuilder::new(); @@ -237,16 +357,12 @@ impl GenericDrawTarget for raqote::DrawTarget { ); let mut options = raqote::DrawOptions::new(); options.blend_mode = raqote::BlendMode::Clear; - raqote::DrawTarget::fill( + let pattern = Pattern::Color(0, 0, 0, 0); + GenericDrawTarget::fill( self, - &pb.finish(), - &raqote::Source::Solid(raqote::SolidSource::from_unpremultiplied_argb( - 0, - 0, - 0, - 0, - )), - &options, + &Path::Raqote(pb.finish()), + canvas_data::Pattern::Raqote(pattern), + &DrawOptions::Raqote(options), ); } #[allow(unsafe_code)] @@ -262,21 +378,14 @@ impl GenericDrawTarget for raqote::DrawTarget { dt.get_data_mut().copy_from_slice(s); raqote::DrawTarget::copy_surface(self, &dt, source.to_box2d(), destination); } - // TODO(pylbrecht) - // unused code? fn create_gradient_stops( &self, gradient_stops: Vec<GradientStop>, _extend_mode: ExtendMode, ) -> GradientStops { - let mut stops = gradient_stops - .into_iter() - .map(|item| item.as_raqote().clone()) - .collect::<Vec<raqote::GradientStop>>(); - // https://www.w3.org/html/test/results/2dcontext/annotated-spec/canvas.html#testrefs.2d.gradient.interpolate.overlap - stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap()); - GradientStops::Raqote(stops) + create_gradient_stops(gradient_stops) } + fn create_path_builder(&self) -> Box<dyn GenericPathBuilder> { Box::new(PathBuilder::new()) } @@ -322,21 +431,15 @@ impl GenericDrawTarget for raqote::DrawTarget { dest.size.width as f32, dest.size.height as f32, ); - let source = raqote::Source::Image( + let pattern = Pattern::Surface(SurfacePattern::new( image, - raqote::ExtendMode::Pad, filter.to_raqote(), - raqote::Transform::create_translation(-dest.origin.x as f32, -dest.origin.y as f32) - .post_scale( - image.width as f32 / dest.size.width as f32, - image.height as f32 / dest.size.height as f32, - ), - ); - + Repetition::NoRepeat, + )); GenericDrawTarget::fill( self, &Path::Raqote(pb.finish()), - Pattern::Raqote(source, None), + canvas_data::Pattern::Raqote(pattern), draw_options, ); } @@ -351,11 +454,15 @@ impl GenericDrawTarget for raqote::DrawTarget { ) { warn!("no support for drawing shadows"); } - fn fill(&mut self, path: &Path, pattern: Pattern, draw_options: &DrawOptions) { + fn fill(&mut self, path: &Path, pattern: canvas_data::Pattern, draw_options: &DrawOptions) { match draw_options.as_raqote().blend_mode { raqote::BlendMode::Src => { self.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)); - self.fill(path.as_raqote(), pattern.source(), draw_options.as_raqote()); + self.fill( + path.as_raqote(), + &pattern.source(), + draw_options.as_raqote(), + ); }, raqote::BlendMode::SrcAtop | raqote::BlendMode::DstOut | @@ -363,7 +470,11 @@ impl GenericDrawTarget for raqote::DrawTarget { raqote::BlendMode::Xor | raqote::BlendMode::DstOver | raqote::BlendMode::SrcOver => { - self.fill(path.as_raqote(), pattern.source(), draw_options.as_raqote()); + self.fill( + path.as_raqote(), + &pattern.source(), + draw_options.as_raqote(), + ); }, raqote::BlendMode::SrcIn | raqote::BlendMode::SrcOut | @@ -372,7 +483,7 @@ impl GenericDrawTarget for raqote::DrawTarget { let mut options = draw_options.as_raqote().clone(); self.push_layer_with_blend(1., options.blend_mode); options.blend_mode = raqote::BlendMode::SrcOver; - self.fill(path.as_raqote(), pattern.source(), &options); + self.fill(path.as_raqote(), &pattern.source(), &options); self.pop_layer(); }, _ => warn!( @@ -384,7 +495,7 @@ impl GenericDrawTarget for raqote::DrawTarget { fn fill_rect( &mut self, rect: &Rect<f32>, - pattern: Pattern, + pattern: canvas_data::Pattern, draw_options: Option<&DrawOptions>, ) { let mut pb = raqote::PathBuilder::new(); @@ -431,13 +542,13 @@ impl GenericDrawTarget for raqote::DrawTarget { fn stroke( &mut self, path: &Path, - pattern: Pattern, + pattern: canvas_data::Pattern, stroke_options: &StrokeOptions, draw_options: &DrawOptions, ) { self.stroke( path.as_raqote(), - pattern.source(), + &pattern.source(), stroke_options.as_raqote(), draw_options.as_raqote(), ); @@ -446,7 +557,7 @@ impl GenericDrawTarget for raqote::DrawTarget { &mut self, start: Point2D<f32>, end: Point2D<f32>, - pattern: Pattern, + pattern: canvas_data::Pattern, stroke_options: &StrokeOptions, draw_options: &DrawOptions, ) { @@ -462,7 +573,7 @@ impl GenericDrawTarget for raqote::DrawTarget { self.stroke( &pb.finish(), - pattern.source(), + &pattern.source(), &stroke_options, draw_options.as_raqote(), ); @@ -470,7 +581,7 @@ impl GenericDrawTarget for raqote::DrawTarget { fn stroke_rect( &mut self, rect: &Rect<f32>, - pattern: Pattern, + pattern: canvas_data::Pattern, stroke_options: &StrokeOptions, draw_options: &DrawOptions, ) { @@ -484,7 +595,7 @@ impl GenericDrawTarget for raqote::DrawTarget { self.stroke( &pb.finish(), - pattern.source(), + &pattern.source(), stroke_options.as_raqote(), draw_options.as_raqote(), ); @@ -711,8 +822,8 @@ impl ToRaqoteStyle for LineCapStyle { } } -pub trait ToRaqoteSource<'a> { - fn to_raqote_source(self) -> Option<Pattern<'a>>; +pub trait ToRaqotePattern<'a> { + fn to_raqote_pattern(self) -> Option<Pattern<'a>>; } pub trait ToRaqoteGradientStop { @@ -732,88 +843,64 @@ impl ToRaqoteGradientStop for CanvasGradientStop { } } -impl<'a> ToRaqoteSource<'a> for FillOrStrokeStyle { +impl<'a> ToRaqotePattern<'_> for FillOrStrokeStyle { #[allow(unsafe_code)] - fn to_raqote_source(self) -> Option<Pattern<'a>> { + fn to_raqote_pattern(self) -> Option<Pattern<'static>> { use canvas_traits::canvas::FillOrStrokeStyle::*; match self { - Color(rgba) => Some(Pattern::Raqote(raqote::Source::Solid( - raqote::SolidSource::from_unpremultiplied_argb( - rgba.alpha, rgba.red, rgba.green, rgba.blue, - ), - ), None)), + Color(color) => Some(Pattern::Color( + color.alpha, + color.red, + color.green, + color.blue, + )), LinearGradient(style) => { - let mut stops: Vec<raqote::GradientStop> = - style.stops.into_iter().map(|s| s.to_raqote()).collect(); - // https://www.w3.org/html/test/results/2dcontext/annotated-spec/canvas.html#testrefs.2d.gradient.interpolate.overlap - stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap()); - let mut 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); - if start == end { - // TODO - // hack to make Pattern::is_zero_size_gradient() return true - gradient.stops.clear(); - } - Some(Pattern::Raqote(raqote::Source::new_linear_gradient( - gradient, - start, - end, - raqote::Spread::Pad, - ), None)) + let mut stops = style + .stops + .iter() + .map(|s| s.to_raqote()) + .collect::<Vec<raqote::GradientStop>>(); + stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap()); + Some(Pattern::LinearGradient(LinearGradientPattern::new( + start, end, stops, + ))) }, RadialGradient(style) => { - let stops = style.stops.into_iter().map(|s| s.to_raqote()).collect(); - let mut 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); - let equal_centers = center1 == center2; - let equal_radius = style.r0 == style.r1; - if equal_centers && equal_radius { - // TODO - // hack to make Pattern::is_zero_size_gradient() return true - gradient.stops.clear(); - } - Some(Pattern::Raqote(raqote::Source::new_two_circle_radial_gradient( - gradient, + let mut stops = style + .stops + .iter() + .map(|s| s.to_raqote()) + .collect::<Vec<raqote::GradientStop>>(); + stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap()); + Some(Pattern::RadialGradient(RadialGradientPattern::new( center1, style.r0 as f32, center2, style.r1 as f32, - raqote::Spread::Pad, - ), None)) + stops, + ))) }, Surface(ref style) => { - let repetition = if style.repeat_x && style.repeat_y { - Repetition::Repeat - } else if style.repeat_x { - Repetition::RepeatX - } else if style.repeat_y { - Repetition::RepeatY - } else { - Repetition::NoRepeat - }; - - let extend = if style.repeat_x || style.repeat_y { - raqote::ExtendMode::Repeat - } else { - raqote::ExtendMode::Pad - }; - + let repeat = Repetition::from_xy(style.repeat_x, style.repeat_y); let data = &style.surface_data[..]; - Some(Pattern::Raqote(raqote::Source::Image( - raqote::Image { - data: unsafe { - std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / 4) - }, - width: style.surface_size.width as i32, - height: style.surface_size.height as i32, + + let image = raqote::Image { + width: style.surface_size.width as i32, + height: style.surface_size.height as i32, + data: unsafe { + std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / 4) }, - extend, - raqote::FilterMode::Bilinear, - raqote::Transform::identity(), - ), Some(repetition))) + }; + Some(Pattern::Surface(SurfacePattern::new( + image, + raqote::FilterMode::Nearest, + repeat, + ))) }, } } @@ -831,12 +918,7 @@ impl ToRaqoteStyle for RGBA { type Target = raqote::SolidSource; fn to_raqote_style(self) -> Self::Target { - raqote::SolidSource::from_unpremultiplied_argb( - self.alpha, - self.red, - self.green, - self.blue, - ) + raqote::SolidSource::from_unpremultiplied_argb(self.alpha, self.red, self.green, self.blue) } } |