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