mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
canvas: Add CanvasPattern 'setTranform(transform)' method (#37731)
Follow the HTML canvas specification and add missing 'setTransform(transform)' method to CanvasPattern interface. https://html.spec.whatwg.org/multipage/#dom-canvaspattern-settransform Testing: Improvements in the tests - html/canvas/element/fill-and-stroke-styles/2d.pattern.transform* - html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.transform* Signed-off-by: Andrei Volykhin <andrei.volykhin@gmail.com>
This commit is contained in:
parent
5286869b96
commit
3c16db2642
12 changed files with 65 additions and 51 deletions
|
@ -121,17 +121,6 @@ pub enum Pattern<'a> {
|
||||||
Surface(SurfacePattern<'a>),
|
Surface(SurfacePattern<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pattern<'_> {
|
|
||||||
fn set_transform(&mut self, transform: Transform2D<f32>) {
|
|
||||||
match self {
|
|
||||||
Pattern::Surface(pattern) => pattern.set_transform(transform),
|
|
||||||
Pattern::LinearGradient(..) | Pattern::RadialGradient(..) | Pattern::Color(..) => {
|
|
||||||
warn!("transform not supported")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LinearGradientPattern {
|
pub struct LinearGradientPattern {
|
||||||
gradient: raqote::Gradient,
|
gradient: raqote::Gradient,
|
||||||
|
@ -186,7 +175,12 @@ pub struct SurfacePattern<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SurfacePattern<'a> {
|
impl<'a> SurfacePattern<'a> {
|
||||||
fn new(image: raqote::Image<'a>, filter: raqote::FilterMode, repeat: Repetition) -> Self {
|
fn new(
|
||||||
|
image: raqote::Image<'a>,
|
||||||
|
filter: raqote::FilterMode,
|
||||||
|
repeat: Repetition,
|
||||||
|
transform: Transform2D<f32>,
|
||||||
|
) -> Self {
|
||||||
let extend = match repeat {
|
let extend = match repeat {
|
||||||
Repetition::NoRepeat => raqote::ExtendMode::Pad,
|
Repetition::NoRepeat => raqote::ExtendMode::Pad,
|
||||||
Repetition::RepeatX | Repetition::RepeatY | Repetition::Repeat => {
|
Repetition::RepeatX | Repetition::RepeatY | Repetition::Repeat => {
|
||||||
|
@ -198,12 +192,9 @@ impl<'a> SurfacePattern<'a> {
|
||||||
filter,
|
filter,
|
||||||
extend,
|
extend,
|
||||||
repeat,
|
repeat,
|
||||||
transform: Transform2D::identity(),
|
transform,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn set_transform(&mut self, transform: Transform2D<f32>) {
|
|
||||||
self.transform = transform;
|
|
||||||
}
|
|
||||||
pub fn size(&self) -> Size2D<f32> {
|
pub fn size(&self) -> Size2D<f32> {
|
||||||
Size2D::new(self.image.width as f32, self.image.height as f32)
|
Size2D::new(self.image.width as f32, self.image.height as f32)
|
||||||
}
|
}
|
||||||
|
@ -427,18 +418,19 @@ impl GenericDrawTarget<RaqoteBackend> for raqote::DrawTarget {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut pattern = Pattern::Surface(SurfacePattern::new(
|
|
||||||
image,
|
|
||||||
filter.to_raqote(),
|
|
||||||
Repetition::NoRepeat,
|
|
||||||
));
|
|
||||||
let transform =
|
let transform =
|
||||||
raqote::Transform::translation(-dest.origin.x as f32, -dest.origin.y as f32)
|
raqote::Transform::translation(-dest.origin.x as f32, -dest.origin.y as f32)
|
||||||
.then_scale(
|
.then_scale(
|
||||||
image.width as f32 / dest.size.width as f32,
|
image.width as f32 / dest.size.width as f32,
|
||||||
image.height as f32 / dest.size.height as f32,
|
image.height as f32 / dest.size.height as f32,
|
||||||
);
|
);
|
||||||
pattern.set_transform(transform);
|
|
||||||
|
let pattern = Pattern::Surface(SurfacePattern::new(
|
||||||
|
image,
|
||||||
|
filter.to_raqote(),
|
||||||
|
Repetition::NoRepeat,
|
||||||
|
transform,
|
||||||
|
));
|
||||||
|
|
||||||
let mut pb = raqote::PathBuilder::new();
|
let mut pb = raqote::PathBuilder::new();
|
||||||
pb.rect(
|
pb.rect(
|
||||||
|
@ -809,6 +801,7 @@ impl ToRaqotePattern<'_> for FillOrStrokeStyle {
|
||||||
image,
|
image,
|
||||||
raqote::FilterMode::Nearest,
|
raqote::FilterMode::Nearest,
|
||||||
repeat,
|
repeat,
|
||||||
|
style.transform,
|
||||||
)))
|
)))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,20 @@
|
||||||
|
|
||||||
use canvas_traits::canvas::{FillOrStrokeStyle, RepetitionStyle, SurfaceStyle};
|
use canvas_traits::canvas::{FillOrStrokeStyle, RepetitionStyle, SurfaceStyle};
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use euclid::default::Size2D;
|
use euclid::default::{Size2D, Transform2D};
|
||||||
|
|
||||||
|
use crate::dom::bindings::cell::DomRefCell;
|
||||||
|
use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasPatternMethods;
|
||||||
|
use crate::dom::bindings::codegen::Bindings::DOMMatrixBinding::DOMMatrix2DInit;
|
||||||
|
use crate::dom::bindings::error::ErrorResult;
|
||||||
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
|
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||||
use crate::dom::bindings::root::DomRoot;
|
use crate::dom::bindings::root::DomRoot;
|
||||||
use crate::dom::canvasgradient::ToFillOrStrokeStyle;
|
use crate::dom::canvasgradient::ToFillOrStrokeStyle;
|
||||||
|
use crate::dom::dommatrixreadonly::dommatrix2dinit_to_matrix;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::script_runtime::CanGc;
|
use crate::script_runtime::CanGc;
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#canvaspattern
|
/// <https://html.spec.whatwg.org/multipage/#canvaspattern>
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub(crate) struct CanvasPattern {
|
pub(crate) struct CanvasPattern {
|
||||||
reflector_: Reflector,
|
reflector_: Reflector,
|
||||||
|
@ -21,6 +26,8 @@ pub(crate) struct CanvasPattern {
|
||||||
surface_size: Size2D<u32>,
|
surface_size: Size2D<u32>,
|
||||||
repeat_x: bool,
|
repeat_x: bool,
|
||||||
repeat_y: bool,
|
repeat_y: bool,
|
||||||
|
#[no_trace]
|
||||||
|
transform: DomRefCell<Transform2D<f32>>,
|
||||||
origin_clean: bool,
|
origin_clean: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +51,7 @@ impl CanvasPattern {
|
||||||
surface_size,
|
surface_size,
|
||||||
repeat_x: x,
|
repeat_x: x,
|
||||||
repeat_y: y,
|
repeat_y: y,
|
||||||
|
transform: DomRefCell::new(Transform2D::identity()),
|
||||||
origin_clean,
|
origin_clean,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,6 +79,40 @@ impl CanvasPattern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CanvasPatternMethods<crate::DomTypeHolder> for CanvasPattern {
|
||||||
|
/// <https://html.spec.whatwg.org/multipage/#dom-canvaspattern-settransform>
|
||||||
|
fn SetTransform(&self, transform: &DOMMatrix2DInit) -> ErrorResult {
|
||||||
|
// Step 1. Let matrix be the result of creating a DOMMatrix from the 2D
|
||||||
|
// dictionary transform.
|
||||||
|
let matrix = dommatrix2dinit_to_matrix(transform)?;
|
||||||
|
|
||||||
|
// Step 2. If one or more of matrix's m11 element, m12 element, m21
|
||||||
|
// element, m22 element, m41 element, or m42 element are infinite or
|
||||||
|
// NaN, then return.
|
||||||
|
if !matrix.m11.is_finite() ||
|
||||||
|
!matrix.m12.is_finite() ||
|
||||||
|
!matrix.m21.is_finite() ||
|
||||||
|
!matrix.m22.is_finite() ||
|
||||||
|
!matrix.m41.is_finite() ||
|
||||||
|
!matrix.m42.is_finite()
|
||||||
|
{
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3. Reset the pattern's transformation matrix to matrix.
|
||||||
|
*self.transform.borrow_mut() = Transform2D::new(
|
||||||
|
matrix.m11 as f32,
|
||||||
|
matrix.m12 as f32,
|
||||||
|
matrix.m21 as f32,
|
||||||
|
matrix.m22 as f32,
|
||||||
|
matrix.m41 as f32,
|
||||||
|
matrix.m42 as f32,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToFillOrStrokeStyle for &CanvasPattern {
|
impl ToFillOrStrokeStyle for &CanvasPattern {
|
||||||
fn to_fill_or_stroke_style(self) -> FillOrStrokeStyle {
|
fn to_fill_or_stroke_style(self) -> FillOrStrokeStyle {
|
||||||
FillOrStrokeStyle::Surface(SurfaceStyle::new(
|
FillOrStrokeStyle::Surface(SurfaceStyle::new(
|
||||||
|
@ -78,6 +120,7 @@ impl ToFillOrStrokeStyle for &CanvasPattern {
|
||||||
self.surface_size,
|
self.surface_size,
|
||||||
self.repeat_x,
|
self.repeat_x,
|
||||||
self.repeat_y,
|
self.repeat_y,
|
||||||
|
*self.transform.borrow(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,7 +254,8 @@ interface CanvasGradient {
|
||||||
[Exposed=(Window, PaintWorklet, Worker)]
|
[Exposed=(Window, PaintWorklet, Worker)]
|
||||||
interface CanvasPattern {
|
interface CanvasPattern {
|
||||||
// opaque object
|
// opaque object
|
||||||
//undefined setTransform(optional DOMMatrix2DInit transform = {});
|
[Throws]
|
||||||
|
undefined setTransform(optional DOMMatrix2DInit transform = {});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Float16Array
|
// TODO: Float16Array
|
||||||
|
|
|
@ -214,6 +214,7 @@ pub struct SurfaceStyle {
|
||||||
pub surface_size: Size2D<u32>,
|
pub surface_size: Size2D<u32>,
|
||||||
pub repeat_x: bool,
|
pub repeat_x: bool,
|
||||||
pub repeat_y: bool,
|
pub repeat_y: bool,
|
||||||
|
pub transform: Transform2D<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SurfaceStyle {
|
impl SurfaceStyle {
|
||||||
|
@ -222,12 +223,14 @@ impl SurfaceStyle {
|
||||||
surface_size: Size2D<u32>,
|
surface_size: Size2D<u32>,
|
||||||
repeat_x: bool,
|
repeat_x: bool,
|
||||||
repeat_y: bool,
|
repeat_y: bool,
|
||||||
|
transform: Transform2D<f32>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
surface_data: ByteBuf::from(surface_data),
|
surface_data: ByteBuf::from(surface_data),
|
||||||
surface_size,
|
surface_size,
|
||||||
repeat_x,
|
repeat_x,
|
||||||
repeat_y,
|
repeat_y,
|
||||||
|
transform,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.pattern.transform.identity.html]
|
|
||||||
[Canvas test: 2d.pattern.transform.identity]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[2d.pattern.transform.infinity.html]
|
|
||||||
[Canvas test: 2d.pattern.transform.infinity]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[2d.pattern.transform.identity.html]
|
|
||||||
[OffscreenCanvas test: 2d.pattern.transform.identity]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[2d.pattern.transform.identity.worker.html]
|
|
||||||
[2d]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[2d.pattern.transform.infinity.html]
|
|
||||||
[OffscreenCanvas test: 2d.pattern.transform.infinity]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[2d.pattern.transform.infinity.worker.html]
|
|
||||||
[2d]
|
|
||||||
expected: FAIL
|
|
|
@ -1,7 +1,4 @@
|
||||||
[idlharness.any.worker.html]
|
[idlharness.any.worker.html]
|
||||||
[CanvasPattern interface: operation setTransform(optional DOMMatrix2DInit)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Path2D interface: operation roundRect(unrestricted double, unrestricted double, unrestricted double, unrestricted double, optional (unrestricted double or DOMPointInit or sequence<(unrestricted double or DOMPointInit)>))]
|
[Path2D interface: operation roundRect(unrestricted double, unrestricted double, unrestricted double, unrestricted double, optional (unrestricted double or DOMPointInit or sequence<(unrestricted double or DOMPointInit)>))]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -4343,9 +4343,6 @@
|
||||||
[CanvasRenderingContext2D interface: calling roundRect(unrestricted double, unrestricted double, unrestricted double, unrestricted double, optional (unrestricted double or DOMPointInit or sequence<(unrestricted double or DOMPointInit)>)) on document.createElement("canvas").getContext("2d") with too few arguments must throw TypeError]
|
[CanvasRenderingContext2D interface: calling roundRect(unrestricted double, unrestricted double, unrestricted double, unrestricted double, optional (unrestricted double or DOMPointInit or sequence<(unrestricted double or DOMPointInit)>)) on document.createElement("canvas").getContext("2d") with too few arguments must throw TypeError]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[CanvasPattern interface: operation setTransform(optional DOMMatrix2DInit)]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Path2D interface: operation roundRect(unrestricted double, unrestricted double, unrestricted double, unrestricted double, optional (unrestricted double or DOMPointInit or sequence<(unrestricted double or DOMPointInit)>))]
|
[Path2D interface: operation roundRect(unrestricted double, unrestricted double, unrestricted double, unrestricted double, optional (unrestricted double or DOMPointInit or sequence<(unrestricted double or DOMPointInit)>))]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue