mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Implement "repeat-x" and "repeat-y" for images
This commit is contained in:
parent
25d036f05c
commit
1aecf40922
2 changed files with 77 additions and 48 deletions
|
@ -358,7 +358,15 @@ pub enum Pattern<'a> {
|
||||||
#[cfg(feature = "canvas2d-azure")]
|
#[cfg(feature = "canvas2d-azure")]
|
||||||
Azure(azure::azure_hl::Pattern, PhantomData<&'a ()>),
|
Azure(azure::azure_hl::Pattern, PhantomData<&'a ()>),
|
||||||
#[cfg(feature = "canvas2d-raqote")]
|
#[cfg(feature = "canvas2d-raqote")]
|
||||||
Raqote(raqote::Source<'a>),
|
Raqote(raqote::Source<'a>, Option<Repetition>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum Repetition {
|
||||||
|
Repeat,
|
||||||
|
RepeatX,
|
||||||
|
RepeatY,
|
||||||
|
NoRepeat,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum DrawSurfaceOptions {
|
pub enum DrawSurfaceOptions {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use crate::canvas_data::{
|
use crate::canvas_data::{
|
||||||
Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, ExtendMode, Filter,
|
Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, ExtendMode, Filter,
|
||||||
GenericDrawTarget, GenericPathBuilder, GradientStop, GradientStops, Path, Pattern,
|
GenericDrawTarget, GenericPathBuilder, GradientStop, GradientStops, Path, Pattern, Repetition,
|
||||||
SourceSurface, StrokeOptions, SurfaceFormat,
|
SourceSurface, StrokeOptions, SurfaceFormat,
|
||||||
};
|
};
|
||||||
use crate::canvas_paint_thread::AntialiasMode;
|
use crate::canvas_paint_thread::AntialiasMode;
|
||||||
|
@ -28,9 +28,23 @@ impl Backend for RaqoteBackend {
|
||||||
|
|
||||||
fn size_from_pattern(&self, rect: &Rect<f32>, pattern: &Pattern) -> Option<Size2D<f32>> {
|
fn size_from_pattern(&self, rect: &Rect<f32>, pattern: &Pattern) -> Option<Size2D<f32>> {
|
||||||
match pattern {
|
match pattern {
|
||||||
Pattern::Raqote(raqote::Source::Image(image, extend, ..)) => match extend {
|
Pattern::Raqote(raqote::Source::Image(image, ..), repetition) => {
|
||||||
raqote::ExtendMode::Repeat => Some(rect.size),
|
if let Some(repeat) = repetition {
|
||||||
_ => Some(Size2D::new(image.width as f32, image.height as f32)),
|
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
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -46,8 +60,8 @@ impl Backend for RaqoteBackend {
|
||||||
state: &mut CanvasPaintState<'a>,
|
state: &mut CanvasPaintState<'a>,
|
||||||
_drawtarget: &dyn GenericDrawTarget,
|
_drawtarget: &dyn GenericDrawTarget,
|
||||||
) {
|
) {
|
||||||
if let Some(source) = style.to_raqote_source() {
|
if let Some(pattern) = style.to_raqote_source() {
|
||||||
state.fill_style = Pattern::Raqote(source);
|
state.fill_style = pattern;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +72,7 @@ impl Backend for RaqoteBackend {
|
||||||
_drawtarget: &dyn GenericDrawTarget,
|
_drawtarget: &dyn GenericDrawTarget,
|
||||||
) {
|
) {
|
||||||
if let Some(pattern) = style.to_raqote_source() {
|
if let Some(pattern) = style.to_raqote_source() {
|
||||||
state.stroke_style = Pattern::Raqote(pattern)
|
state.stroke_style = pattern;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,8 +106,8 @@ impl<'a> CanvasPaintState<'a> {
|
||||||
);
|
);
|
||||||
CanvasPaintState {
|
CanvasPaintState {
|
||||||
draw_options: DrawOptions::Raqote(raqote::DrawOptions::new()),
|
draw_options: DrawOptions::Raqote(raqote::DrawOptions::new()),
|
||||||
fill_style: Pattern::Raqote(raqote::Source::Solid(solid_src)),
|
fill_style: Pattern::Raqote(raqote::Source::Solid(solid_src), None),
|
||||||
stroke_style: Pattern::Raqote(raqote::Source::Solid(solid_src)),
|
stroke_style: Pattern::Raqote(raqote::Source::Solid(solid_src), None),
|
||||||
stroke_opts: StrokeOptions::Raqote(Default::default(), PhantomData),
|
stroke_opts: StrokeOptions::Raqote(Default::default(), PhantomData),
|
||||||
transform: Transform2D::identity(),
|
transform: Transform2D::identity(),
|
||||||
shadow_offset_x: 0.0,
|
shadow_offset_x: 0.0,
|
||||||
|
@ -112,10 +126,10 @@ impl<'a> CanvasPaintState<'a> {
|
||||||
impl Pattern<'_> {
|
impl Pattern<'_> {
|
||||||
pub fn is_zero_size_gradient(&self) -> bool {
|
pub fn is_zero_size_gradient(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Pattern::Raqote(p) => {
|
Pattern::Raqote(source, _) => {
|
||||||
use raqote::Source::*;
|
use raqote::Source::*;
|
||||||
|
|
||||||
match p {
|
match source {
|
||||||
LinearGradient(g, ..) |
|
LinearGradient(g, ..) |
|
||||||
RadialGradient(g, ..) |
|
RadialGradient(g, ..) |
|
||||||
TwoCircleRadialGradient(g, ..) => g.stops.is_empty(),
|
TwoCircleRadialGradient(g, ..) => g.stops.is_empty(),
|
||||||
|
@ -124,9 +138,14 @@ impl Pattern<'_> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn as_raqote(&self) -> &raqote::Source {
|
pub fn source(&self) -> &raqote::Source {
|
||||||
match self {
|
match self {
|
||||||
Pattern::Raqote(p) => p,
|
Pattern::Raqote(source, _) => source,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn repetition(&self) -> &Option<Repetition> {
|
||||||
|
match self {
|
||||||
|
Pattern::Raqote(_, repetition) => repetition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,7 +336,7 @@ impl GenericDrawTarget for raqote::DrawTarget {
|
||||||
GenericDrawTarget::fill(
|
GenericDrawTarget::fill(
|
||||||
self,
|
self,
|
||||||
&Path::Raqote(pb.finish()),
|
&Path::Raqote(pb.finish()),
|
||||||
Pattern::Raqote(source),
|
Pattern::Raqote(source, None),
|
||||||
draw_options,
|
draw_options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -336,11 +355,7 @@ impl GenericDrawTarget for raqote::DrawTarget {
|
||||||
match draw_options.as_raqote().blend_mode {
|
match draw_options.as_raqote().blend_mode {
|
||||||
raqote::BlendMode::Src => {
|
raqote::BlendMode::Src => {
|
||||||
self.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0));
|
self.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0));
|
||||||
self.fill(
|
self.fill(path.as_raqote(), pattern.source(), draw_options.as_raqote());
|
||||||
path.as_raqote(),
|
|
||||||
pattern.as_raqote(),
|
|
||||||
draw_options.as_raqote(),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
raqote::BlendMode::SrcAtop |
|
raqote::BlendMode::SrcAtop |
|
||||||
raqote::BlendMode::DstOut |
|
raqote::BlendMode::DstOut |
|
||||||
|
@ -348,11 +363,7 @@ impl GenericDrawTarget for raqote::DrawTarget {
|
||||||
raqote::BlendMode::Xor |
|
raqote::BlendMode::Xor |
|
||||||
raqote::BlendMode::DstOver |
|
raqote::BlendMode::DstOver |
|
||||||
raqote::BlendMode::SrcOver => {
|
raqote::BlendMode::SrcOver => {
|
||||||
self.fill(
|
self.fill(path.as_raqote(), pattern.source(), draw_options.as_raqote());
|
||||||
path.as_raqote(),
|
|
||||||
pattern.as_raqote(),
|
|
||||||
draw_options.as_raqote(),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
raqote::BlendMode::SrcIn |
|
raqote::BlendMode::SrcIn |
|
||||||
raqote::BlendMode::SrcOut |
|
raqote::BlendMode::SrcOut |
|
||||||
|
@ -361,7 +372,7 @@ impl GenericDrawTarget for raqote::DrawTarget {
|
||||||
let mut options = draw_options.as_raqote().clone();
|
let mut options = draw_options.as_raqote().clone();
|
||||||
self.push_layer_with_blend(1., options.blend_mode);
|
self.push_layer_with_blend(1., options.blend_mode);
|
||||||
options.blend_mode = raqote::BlendMode::SrcOver;
|
options.blend_mode = raqote::BlendMode::SrcOver;
|
||||||
self.fill(path.as_raqote(), pattern.as_raqote(), &options);
|
self.fill(path.as_raqote(), pattern.source(), &options);
|
||||||
self.pop_layer();
|
self.pop_layer();
|
||||||
},
|
},
|
||||||
_ => warn!(
|
_ => warn!(
|
||||||
|
@ -426,7 +437,7 @@ impl GenericDrawTarget for raqote::DrawTarget {
|
||||||
) {
|
) {
|
||||||
self.stroke(
|
self.stroke(
|
||||||
path.as_raqote(),
|
path.as_raqote(),
|
||||||
pattern.as_raqote(),
|
pattern.source(),
|
||||||
stroke_options.as_raqote(),
|
stroke_options.as_raqote(),
|
||||||
draw_options.as_raqote(),
|
draw_options.as_raqote(),
|
||||||
);
|
);
|
||||||
|
@ -451,7 +462,7 @@ impl GenericDrawTarget for raqote::DrawTarget {
|
||||||
|
|
||||||
self.stroke(
|
self.stroke(
|
||||||
&pb.finish(),
|
&pb.finish(),
|
||||||
pattern.as_raqote(),
|
pattern.source(),
|
||||||
&stroke_options,
|
&stroke_options,
|
||||||
draw_options.as_raqote(),
|
draw_options.as_raqote(),
|
||||||
);
|
);
|
||||||
|
@ -473,7 +484,7 @@ impl GenericDrawTarget for raqote::DrawTarget {
|
||||||
|
|
||||||
self.stroke(
|
self.stroke(
|
||||||
&pb.finish(),
|
&pb.finish(),
|
||||||
pattern.as_raqote(),
|
pattern.source(),
|
||||||
stroke_options.as_raqote(),
|
stroke_options.as_raqote(),
|
||||||
draw_options.as_raqote(),
|
draw_options.as_raqote(),
|
||||||
);
|
);
|
||||||
|
@ -701,7 +712,7 @@ impl ToRaqoteStyle for LineCapStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ToRaqoteSource<'a> {
|
pub trait ToRaqoteSource<'a> {
|
||||||
fn to_raqote_source(self) -> Option<raqote::Source<'a>>;
|
fn to_raqote_source(self) -> Option<Pattern<'a>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ToRaqoteGradientStop {
|
pub trait ToRaqoteGradientStop {
|
||||||
|
@ -723,16 +734,15 @@ impl ToRaqoteGradientStop for CanvasGradientStop {
|
||||||
|
|
||||||
impl<'a> ToRaqoteSource<'a> for FillOrStrokeStyle {
|
impl<'a> ToRaqoteSource<'a> for FillOrStrokeStyle {
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn to_raqote_source(self) -> Option<raqote::Source<'a>> {
|
fn to_raqote_source(self) -> Option<Pattern<'a>> {
|
||||||
use canvas_traits::canvas::FillOrStrokeStyle::*;
|
use canvas_traits::canvas::FillOrStrokeStyle::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Color(rgba) => Some(raqote::Source::Solid(raqote::SolidSource::from_unpremultiplied_argb(
|
Color(rgba) => Some(Pattern::Raqote(raqote::Source::Solid(
|
||||||
rgba.alpha,
|
raqote::SolidSource::from_unpremultiplied_argb(
|
||||||
rgba.red,
|
rgba.alpha, rgba.red, rgba.green, rgba.blue,
|
||||||
rgba.green,
|
),
|
||||||
rgba.blue,
|
), None)),
|
||||||
))),
|
|
||||||
LinearGradient(style) => {
|
LinearGradient(style) => {
|
||||||
let mut stops: Vec<raqote::GradientStop> =
|
let mut stops: Vec<raqote::GradientStop> =
|
||||||
style.stops.into_iter().map(|s| s.to_raqote()).collect();
|
style.stops.into_iter().map(|s| s.to_raqote()).collect();
|
||||||
|
@ -746,12 +756,12 @@ impl<'a> ToRaqoteSource<'a> for FillOrStrokeStyle {
|
||||||
// hack to make Pattern::is_zero_size_gradient() return true
|
// hack to make Pattern::is_zero_size_gradient() return true
|
||||||
gradient.stops.clear();
|
gradient.stops.clear();
|
||||||
}
|
}
|
||||||
Some(raqote::Source::new_linear_gradient(
|
Some(Pattern::Raqote(raqote::Source::new_linear_gradient(
|
||||||
gradient,
|
gradient,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
raqote::Spread::Pad,
|
raqote::Spread::Pad,
|
||||||
))
|
), None))
|
||||||
},
|
},
|
||||||
RadialGradient(style) => {
|
RadialGradient(style) => {
|
||||||
let stops = style.stops.into_iter().map(|s| s.to_raqote()).collect();
|
let stops = style.stops.into_iter().map(|s| s.to_raqote()).collect();
|
||||||
|
@ -765,34 +775,45 @@ impl<'a> ToRaqoteSource<'a> for FillOrStrokeStyle {
|
||||||
// hack to make Pattern::is_zero_size_gradient() return true
|
// hack to make Pattern::is_zero_size_gradient() return true
|
||||||
gradient.stops.clear();
|
gradient.stops.clear();
|
||||||
}
|
}
|
||||||
Some(raqote::Source::new_two_circle_radial_gradient(
|
Some(Pattern::Raqote(raqote::Source::new_two_circle_radial_gradient(
|
||||||
gradient,
|
gradient,
|
||||||
center1,
|
center1,
|
||||||
style.r0 as f32,
|
style.r0 as f32,
|
||||||
center2,
|
center2,
|
||||||
style.r1 as f32,
|
style.r1 as f32,
|
||||||
raqote::Spread::Pad,
|
raqote::Spread::Pad,
|
||||||
))
|
), None))
|
||||||
},
|
},
|
||||||
Surface(ref surface) => {
|
Surface(ref style) => {
|
||||||
let data = &surface.surface_data[..];
|
let repetition = if style.repeat_x && style.repeat_y {
|
||||||
let extend = if surface.repeat_x || surface.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
|
raqote::ExtendMode::Repeat
|
||||||
} else {
|
} else {
|
||||||
raqote::ExtendMode::Pad
|
raqote::ExtendMode::Pad
|
||||||
};
|
};
|
||||||
Some(raqote::Source::Image(
|
|
||||||
|
let data = &style.surface_data[..];
|
||||||
|
Some(Pattern::Raqote(raqote::Source::Image(
|
||||||
raqote::Image {
|
raqote::Image {
|
||||||
data: unsafe {
|
data: unsafe {
|
||||||
std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / 4)
|
std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / 4)
|
||||||
},
|
},
|
||||||
width: surface.surface_size.width as i32,
|
width: style.surface_size.width as i32,
|
||||||
height: surface.surface_size.height as i32,
|
height: style.surface_size.height as i32,
|
||||||
},
|
},
|
||||||
extend,
|
extend,
|
||||||
raqote::FilterMode::Bilinear,
|
raqote::FilterMode::Bilinear,
|
||||||
raqote::Transform::identity(),
|
raqote::Transform::identity(),
|
||||||
))
|
), Some(repetition)))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue