Skip to main content

script/dom/canvas/2d/
canvaspattern.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use dom_struct::dom_struct;
8use euclid::default::{Size2D, Transform2D};
9use js::context::JSContext;
10use pixels::{SharedSnapshot, Snapshot};
11use script_bindings::cell::DomRefCell;
12use script_bindings::reflector::{Reflector, reflect_dom_object};
13use servo_canvas_traits::canvas::{FillOrStrokeStyle, RepetitionStyle, SurfaceStyle};
14
15use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasPatternMethods;
16use crate::dom::bindings::codegen::Bindings::DOMMatrixBinding::DOMMatrix2DInit;
17use crate::dom::bindings::error::ErrorResult;
18use crate::dom::bindings::root::DomRoot;
19use crate::dom::canvasgradient::ToFillOrStrokeStyle;
20use crate::dom::dommatrixreadonly::dommatrix2dinit_to_matrix;
21use crate::dom::globalscope::GlobalScope;
22
23/// <https://html.spec.whatwg.org/multipage/#canvaspattern>
24#[dom_struct]
25pub(crate) struct CanvasPattern {
26    reflector_: Reflector,
27    #[no_trace]
28    surface_data: SharedSnapshot,
29    #[no_trace]
30    surface_size: Size2D<u32>,
31    repeat_x: bool,
32    repeat_y: bool,
33    #[no_trace]
34    transform: DomRefCell<Transform2D<f32>>,
35    origin_clean: bool,
36}
37
38impl CanvasPattern {
39    fn new_inherited(
40        surface_data: Snapshot,
41        surface_size: Size2D<u32>,
42        repeat: RepetitionStyle,
43        origin_clean: bool,
44    ) -> CanvasPattern {
45        let (x, y) = match repeat {
46            RepetitionStyle::Repeat => (true, true),
47            RepetitionStyle::RepeatX => (true, false),
48            RepetitionStyle::RepeatY => (false, true),
49            RepetitionStyle::NoRepeat => (false, false),
50        };
51
52        CanvasPattern {
53            reflector_: Reflector::new(),
54            surface_data: surface_data.to_shared(),
55            surface_size,
56            repeat_x: x,
57            repeat_y: y,
58            transform: DomRefCell::new(Transform2D::identity()),
59            origin_clean,
60        }
61    }
62    pub(crate) fn new(
63        global: &GlobalScope,
64        cx: &mut JSContext,
65        surface_data: Snapshot,
66        surface_size: Size2D<u32>,
67        repeat: RepetitionStyle,
68        origin_clean: bool,
69    ) -> DomRoot<CanvasPattern> {
70        reflect_dom_object(
71            cx,
72            Box::new(CanvasPattern::new_inherited(
73                surface_data,
74                surface_size,
75                repeat,
76                origin_clean,
77            )),
78            global,
79        )
80    }
81    pub(crate) fn origin_is_clean(&self) -> bool {
82        self.origin_clean
83    }
84}
85
86impl CanvasPatternMethods<crate::DomTypeHolder> for CanvasPattern {
87    /// <https://html.spec.whatwg.org/multipage/#dom-canvaspattern-settransform>
88    fn SetTransform(&self, transform: &DOMMatrix2DInit) -> ErrorResult {
89        // Step 1. Let matrix be the result of creating a DOMMatrix from the 2D
90        // dictionary transform.
91        let matrix = dommatrix2dinit_to_matrix(transform)?;
92
93        // Step 2. If one or more of matrix's m11 element, m12 element, m21
94        // element, m22 element, m41 element, or m42 element are infinite or
95        // NaN, then return.
96        if !matrix.m11.is_finite() ||
97            !matrix.m12.is_finite() ||
98            !matrix.m21.is_finite() ||
99            !matrix.m22.is_finite() ||
100            !matrix.m31.is_finite() ||
101            !matrix.m32.is_finite()
102        {
103            return Ok(());
104        }
105
106        // Step 3. Reset the pattern's transformation matrix to matrix.
107        *self.transform.borrow_mut() = matrix.cast();
108
109        Ok(())
110    }
111}
112
113impl ToFillOrStrokeStyle for &CanvasPattern {
114    fn to_fill_or_stroke_style(self) -> FillOrStrokeStyle {
115        FillOrStrokeStyle::Surface(SurfaceStyle::new(
116            self.surface_data.clone(),
117            self.surface_size,
118            self.repeat_x,
119            self.repeat_y,
120            *self.transform.borrow(),
121        ))
122    }
123}