Skip to main content

script/dom/canvas/2d/
paintrenderingcontext2d.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
5use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use euclid::{Scale, Size2D};
9use js::context::JSContext;
10use script_bindings::reflector::{Reflector, reflect_dom_object};
11use servo_url::ServoUrl;
12use style_traits::CSSPixel;
13use webrender_api::ImageKey;
14use webrender_api::units::DevicePixel;
15
16use super::canvas_state::CanvasState;
17use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{
18    CanvasFillRule, CanvasImageSource, CanvasLineCap, CanvasLineJoin,
19};
20use crate::dom::bindings::codegen::Bindings::DOMMatrixBinding::DOMMatrix2DInit;
21use crate::dom::bindings::codegen::Bindings::PaintRenderingContext2DBinding::PaintRenderingContext2DMethods;
22use crate::dom::bindings::codegen::UnionTypes::{
23    StringOrCanvasGradientOrCanvasPattern,
24    UnrestrictedDoubleOrDOMPointInitOrUnrestrictedDoubleOrDOMPointInitSequence,
25};
26use crate::dom::bindings::error::{ErrorResult, Fallible};
27use crate::dom::bindings::inheritance::Castable;
28use crate::dom::bindings::num::Finite;
29use crate::dom::bindings::reflector::DomGlobal as _;
30use crate::dom::bindings::root::DomRoot;
31use crate::dom::bindings::str::DOMString;
32use crate::dom::canvasgradient::CanvasGradient;
33use crate::dom::canvaspattern::CanvasPattern;
34use crate::dom::dommatrix::DOMMatrix;
35use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
36use crate::dom::path2d::Path2D;
37
38#[dom_struct]
39pub(crate) struct PaintRenderingContext2D {
40    reflector_: Reflector,
41    canvas_state: CanvasState,
42    #[no_trace]
43    device_pixel_ratio: Cell<Scale<f32, CSSPixel, DevicePixel>>,
44    /// An [`ImageKey`] used to store the results of this [`PaintWorkletGlobalScope`].
45    #[no_trace]
46    image_key: ImageKey,
47}
48
49impl PaintRenderingContext2D {
50    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
51    fn new_inherited(global: &PaintWorkletGlobalScope) -> Option<PaintRenderingContext2D> {
52        let canvas_state = CanvasState::new(global.upcast(), Size2D::zero())?;
53        let image_cache = global.image_cache();
54        let image_key = image_cache.get_image_key()?;
55        canvas_state.set_image_key(image_key);
56
57        Some(PaintRenderingContext2D {
58            reflector_: Reflector::new(),
59            canvas_state,
60            device_pixel_ratio: Cell::new(Scale::new(1.0)),
61            image_key,
62        })
63    }
64
65    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
66    pub(crate) fn new(
67        cx: &mut JSContext,
68        global: &PaintWorkletGlobalScope,
69    ) -> Option<DomRoot<PaintRenderingContext2D>> {
70        Some(reflect_dom_object(
71            cx,
72            Box::new(PaintRenderingContext2D::new_inherited(global)?),
73            global,
74        ))
75    }
76
77    pub(crate) fn update_rendering(&self) -> bool {
78        self.canvas_state.update_rendering(None)
79    }
80
81    pub(crate) fn image_key(&self) -> ImageKey {
82        self.image_key
83    }
84
85    pub(crate) fn take_missing_image_urls(&self) -> Vec<ServoUrl> {
86        std::mem::take(&mut self.canvas_state.get_missing_image_urls().borrow_mut())
87    }
88
89    pub(crate) fn set_bitmap_dimensions(
90        &self,
91        size: Size2D<f32, CSSPixel>,
92        device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
93    ) {
94        let size = size * device_pixel_ratio;
95        self.device_pixel_ratio.set(device_pixel_ratio);
96        self.canvas_state
97            .set_bitmap_dimensions(size.to_untyped().to_u64());
98        self.scale_by_device_pixel_ratio();
99    }
100
101    fn scale_by_device_pixel_ratio(&self) {
102        let device_pixel_ratio = self.device_pixel_ratio.get().get() as f64;
103        if device_pixel_ratio != 1.0 {
104            self.Scale(device_pixel_ratio, device_pixel_ratio);
105        }
106    }
107}
108
109impl PaintRenderingContext2DMethods<crate::DomTypeHolder> for PaintRenderingContext2D {
110    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-save>
111    fn Save(&self) {
112        self.canvas_state.save()
113    }
114
115    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-restore>
116    fn Restore(&self) {
117        self.canvas_state.restore()
118    }
119
120    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-reset>
121    fn Reset(&self) {
122        self.canvas_state.reset()
123    }
124
125    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-scale>
126    fn Scale(&self, x: f64, y: f64) {
127        self.canvas_state.scale(x, y)
128    }
129
130    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-rotate>
131    fn Rotate(&self, angle: f64) {
132        self.canvas_state.rotate(angle)
133    }
134
135    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-translate>
136    fn Translate(&self, x: f64, y: f64) {
137        self.canvas_state.translate(x, y)
138    }
139
140    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-transform>
141    fn Transform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) {
142        self.canvas_state.transform(a, b, c, d, e, f)
143    }
144
145    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-gettransform>
146    fn GetTransform(&self, cx: &mut JSContext) -> DomRoot<DOMMatrix> {
147        self.canvas_state.get_transform(&self.global(), cx)
148    }
149
150    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-settransform>
151    fn SetTransform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) -> ErrorResult {
152        self.canvas_state.set_transform(a, b, c, d, e, f);
153        self.scale_by_device_pixel_ratio();
154        Ok(())
155    }
156
157    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-settransform-matrix>
158    fn SetTransform_(&self, transform: &DOMMatrix2DInit) -> ErrorResult {
159        self.canvas_state.set_transform_(transform)?;
160        self.scale_by_device_pixel_ratio();
161        Ok(())
162    }
163
164    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-resettransform>
165    fn ResetTransform(&self) {
166        self.canvas_state.reset_transform();
167        self.scale_by_device_pixel_ratio();
168    }
169
170    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha>
171    fn GlobalAlpha(&self) -> f64 {
172        self.canvas_state.global_alpha()
173    }
174
175    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha>
176    fn SetGlobalAlpha(&self, alpha: f64) {
177        self.canvas_state.set_global_alpha(alpha)
178    }
179
180    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation>
181    fn GlobalCompositeOperation(&self) -> DOMString {
182        self.canvas_state.global_composite_operation()
183    }
184
185    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation>
186    fn SetGlobalCompositeOperation(&self, op_str: DOMString) {
187        self.canvas_state.set_global_composite_operation(op_str)
188    }
189
190    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-fillrect>
191    fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) {
192        self.canvas_state.fill_rect(x, y, width, height)
193    }
194
195    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-clearrect>
196    fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) {
197        self.canvas_state.clear_rect(x, y, width, height)
198    }
199
200    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-strokerect>
201    fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) {
202        self.canvas_state.stroke_rect(x, y, width, height)
203    }
204
205    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-beginpath>
206    fn BeginPath(&self) {
207        self.canvas_state.begin_path()
208    }
209
210    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath>
211    fn ClosePath(&self) {
212        self.canvas_state.close_path()
213    }
214
215    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-fill>
216    fn Fill(&self, fill_rule: CanvasFillRule) {
217        self.canvas_state.fill(fill_rule)
218    }
219
220    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-fill>
221    fn Fill_(&self, path: &Path2D, fill_rule: CanvasFillRule) {
222        self.canvas_state.fill_(path.segments(), fill_rule)
223    }
224
225    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-stroke>
226    fn Stroke(&self) {
227        self.canvas_state.stroke()
228    }
229
230    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-stroke>
231    fn Stroke_(&self, path: &Path2D) {
232        self.canvas_state.stroke_(path.segments())
233    }
234
235    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-clip>
236    fn Clip(&self, fill_rule: CanvasFillRule) {
237        self.canvas_state.clip(fill_rule)
238    }
239
240    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-clip>
241    fn Clip_(&self, path: &Path2D, fill_rule: CanvasFillRule) {
242        self.canvas_state.clip_(path.segments(), fill_rule)
243    }
244
245    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-ispointinpath>
246    fn IsPointInPath(&self, x: f64, y: f64, fill_rule: CanvasFillRule) -> bool {
247        self.canvas_state
248            .is_point_in_path(&self.global(), x, y, fill_rule)
249    }
250
251    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-ispointinpath>
252    fn IsPointInPath_(&self, path: &Path2D, x: f64, y: f64, fill_rule: CanvasFillRule) -> bool {
253        self.canvas_state
254            .is_point_in_path_(&self.global(), path.segments(), x, y, fill_rule)
255    }
256
257    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage>
258    fn DrawImage(&self, image: CanvasImageSource, dx: f64, dy: f64) -> ErrorResult {
259        self.canvas_state.draw_image(None, image, dx, dy)
260    }
261
262    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage>
263    fn DrawImage_(
264        &self,
265        image: CanvasImageSource,
266        dx: f64,
267        dy: f64,
268        dw: f64,
269        dh: f64,
270    ) -> ErrorResult {
271        self.canvas_state.draw_image_(None, image, dx, dy, dw, dh)
272    }
273
274    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage>
275    fn DrawImage__(
276        &self,
277        image: CanvasImageSource,
278        sx: f64,
279        sy: f64,
280        sw: f64,
281        sh: f64,
282        dx: f64,
283        dy: f64,
284        dw: f64,
285        dh: f64,
286    ) -> ErrorResult {
287        self.canvas_state
288            .draw_image__(None, image, sx, sy, sw, sh, dx, dy, dw, dh)
289    }
290
291    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto>
292    fn MoveTo(&self, x: f64, y: f64) {
293        self.canvas_state.move_to(x, y)
294    }
295
296    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-lineto>
297    fn LineTo(&self, x: f64, y: f64) {
298        self.canvas_state.line_to(x, y)
299    }
300
301    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-rect>
302    fn Rect(&self, x: f64, y: f64, width: f64, height: f64) {
303        self.canvas_state.rect(x, y, width, height)
304    }
305
306    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-roundrect>
307    fn RoundRect(
308        &self,
309        x: f64,
310        y: f64,
311        width: f64,
312        height: f64,
313        radii: UnrestrictedDoubleOrDOMPointInitOrUnrestrictedDoubleOrDOMPointInitSequence,
314    ) -> ErrorResult {
315        self.canvas_state.round_rect(x, y, width, height, radii)
316    }
317
318    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-quadraticcurveto>
319    fn QuadraticCurveTo(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
320        self.canvas_state.quadratic_curve_to(cpx, cpy, x, y)
321    }
322
323    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-beziercurveto>
324    fn BezierCurveTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
325        self.canvas_state
326            .bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y)
327    }
328
329    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-arc>
330    fn Arc(&self, x: f64, y: f64, r: f64, start: f64, end: f64, ccw: bool) -> ErrorResult {
331        self.canvas_state.arc(x, y, r, start, end, ccw)
332    }
333
334    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-arcto>
335    fn ArcTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, r: f64) -> ErrorResult {
336        self.canvas_state.arc_to(cp1x, cp1y, cp2x, cp2y, r)
337    }
338
339    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-ellipse>
340    fn Ellipse(
341        &self,
342        x: f64,
343        y: f64,
344        rx: f64,
345        ry: f64,
346        rotation: f64,
347        start: f64,
348        end: f64,
349        ccw: bool,
350    ) -> ErrorResult {
351        self.canvas_state
352            .ellipse(x, y, rx, ry, rotation, start, end, ccw)
353    }
354
355    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled>
356    fn ImageSmoothingEnabled(&self) -> bool {
357        self.canvas_state.image_smoothing_enabled()
358    }
359
360    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled>
361    fn SetImageSmoothingEnabled(&self, value: bool) {
362        self.canvas_state.set_image_smoothing_enabled(value)
363    }
364
365    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle>
366    fn StrokeStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
367        self.canvas_state.stroke_style()
368    }
369
370    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle>
371    fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
372        self.canvas_state.set_stroke_style(None, value)
373    }
374
375    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle>
376    fn FillStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
377        self.canvas_state.fill_style()
378    }
379
380    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle>
381    fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
382        self.canvas_state.set_fill_style(None, value)
383    }
384
385    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-createlineargradient>
386    fn CreateLinearGradient(
387        &self,
388        cx: &mut JSContext,
389        x0: Finite<f64>,
390        y0: Finite<f64>,
391        x1: Finite<f64>,
392        y1: Finite<f64>,
393    ) -> DomRoot<CanvasGradient> {
394        self.canvas_state
395            .create_linear_gradient(&self.global(), cx, x0, y0, x1, y1)
396    }
397
398    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-createradialgradient>
399    fn CreateRadialGradient(
400        &self,
401        cx: &mut JSContext,
402        x0: Finite<f64>,
403        y0: Finite<f64>,
404        r0: Finite<f64>,
405        x1: Finite<f64>,
406        y1: Finite<f64>,
407        r1: Finite<f64>,
408    ) -> Fallible<DomRoot<CanvasGradient>> {
409        self.canvas_state
410            .create_radial_gradient(&self.global(), cx, x0, y0, r0, x1, y1, r1)
411    }
412
413    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-createpattern>
414    fn CreatePattern(
415        &self,
416        cx: &mut JSContext,
417        image: CanvasImageSource,
418        repetition: DOMString,
419    ) -> Fallible<Option<DomRoot<CanvasPattern>>> {
420        self.canvas_state
421            .create_pattern(&self.global(), cx, image, repetition)
422    }
423
424    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth>
425    fn LineWidth(&self) -> f64 {
426        self.canvas_state.line_width()
427    }
428
429    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth>
430    fn SetLineWidth(&self, width: f64) {
431        self.canvas_state.set_line_width(width)
432    }
433
434    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap>
435    fn LineCap(&self) -> CanvasLineCap {
436        self.canvas_state.line_cap()
437    }
438
439    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap>
440    fn SetLineCap(&self, cap: CanvasLineCap) {
441        self.canvas_state.set_line_cap(cap)
442    }
443
444    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin>
445    fn LineJoin(&self) -> CanvasLineJoin {
446        self.canvas_state.line_join()
447    }
448
449    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin>
450    fn SetLineJoin(&self, join: CanvasLineJoin) {
451        self.canvas_state.set_line_join(join)
452    }
453
454    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit>
455    fn MiterLimit(&self) -> f64 {
456        self.canvas_state.miter_limit()
457    }
458
459    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit>
460    fn SetMiterLimit(&self, limit: f64) {
461        self.canvas_state.set_miter_limit(limit)
462    }
463
464    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-setlinedash>
465    fn SetLineDash(&self, segments: Vec<f64>) {
466        self.canvas_state.set_line_dash(segments);
467    }
468
469    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-getlinedash>
470    fn GetLineDash(&self) -> Vec<f64> {
471        self.canvas_state.line_dash()
472    }
473
474    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-linedashoffset>
475    fn LineDashOffset(&self) -> f64 {
476        self.canvas_state.line_dash_offset()
477    }
478
479    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-linedashoffset>
480    fn SetLineDashOffset(&self, offset: f64) {
481        self.canvas_state.set_line_dash_offset(offset);
482    }
483
484    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx>
485    fn ShadowOffsetX(&self) -> f64 {
486        self.canvas_state.shadow_offset_x()
487    }
488
489    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx>
490    fn SetShadowOffsetX(&self, value: f64) {
491        self.canvas_state.set_shadow_offset_x(value)
492    }
493
494    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety>
495    fn ShadowOffsetY(&self) -> f64 {
496        self.canvas_state.shadow_offset_y()
497    }
498
499    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety>
500    fn SetShadowOffsetY(&self, value: f64) {
501        self.canvas_state.set_shadow_offset_y(value)
502    }
503
504    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur>
505    fn ShadowBlur(&self) -> f64 {
506        self.canvas_state.shadow_blur()
507    }
508
509    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur>
510    fn SetShadowBlur(&self, value: f64) {
511        self.canvas_state.set_shadow_blur(value)
512    }
513
514    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor>
515    fn ShadowColor(&self) -> DOMString {
516        self.canvas_state.shadow_color()
517    }
518
519    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor>
520    fn SetShadowColor(&self, value: DOMString) {
521        self.canvas_state.set_shadow_color(None, value)
522    }
523}