aboutsummaryrefslogtreecommitdiffstats
path: root/components/canvas
diff options
context:
space:
mode:
authorpylbrecht <palbrecht@mailbox.org>2019-12-13 22:15:19 +0100
committerpylbrecht <palbrecht@mailbox.org>2019-12-17 19:52:02 +0100
commita50aef6f00d3ba1cb45a0eb5d0c7fafbbe7bb1fb (patch)
tree81612221ad623a0a3c9da163a97e6421286f401f /components/canvas
parent1aecf40922337f7bd514ecd00b2ce9c43f3877d7 (diff)
downloadservo-a50aef6f00d3ba1cb45a0eb5d0c7fafbbe7bb1fb.tar.gz
servo-a50aef6f00d3ba1cb45a0eb5d0c7fafbbe7bb1fb.zip
Add a layer to store Pattern related information
Some information of `canvas_data::Pattern` was lost by converting it to `raqote::Source` due to the fact that Raqote did not store this information (e.g. linear gradient's start/end points). We introduce another layer to keep this information for later use (like in `is_zero_size_gradient()`).
Diffstat (limited to 'components/canvas')
-rw-r--r--components/canvas/canvas_data.rs10
-rw-r--r--components/canvas/raqote_backend.rs410
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)
}
}