1use 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 #[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 fn Save(&self) {
110 self.canvas_state.save()
111 }
112
113 fn Restore(&self) {
115 self.canvas_state.restore()
116 }
117
118 fn Reset(&self) {
120 self.canvas_state.reset()
121 }
122
123 fn Scale(&self, x: f64, y: f64) {
125 self.canvas_state.scale(x, y)
126 }
127
128 fn Rotate(&self, angle: f64) {
130 self.canvas_state.rotate(angle)
131 }
132
133 fn Translate(&self, x: f64, y: f64) {
135 self.canvas_state.translate(x, y)
136 }
137
138 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 fn GetTransform(&self, cx: &mut JSContext) -> DomRoot<DOMMatrix> {
145 self.canvas_state.get_transform(&self.global(), cx)
146 }
147
148 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 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 fn ResetTransform(&self) {
164 self.canvas_state.reset_transform();
165 self.scale_by_device_pixel_ratio();
166 }
167
168 fn GlobalAlpha(&self) -> f64 {
170 self.canvas_state.global_alpha()
171 }
172
173 fn SetGlobalAlpha(&self, alpha: f64) {
175 self.canvas_state.set_global_alpha(alpha)
176 }
177
178 fn GlobalCompositeOperation(&self) -> DOMString {
180 self.canvas_state.global_composite_operation()
181 }
182
183 fn SetGlobalCompositeOperation(&self, op_str: DOMString) {
185 self.canvas_state.set_global_composite_operation(op_str)
186 }
187
188 fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) {
190 self.canvas_state.fill_rect(x, y, width, height)
191 }
192
193 fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) {
195 self.canvas_state.clear_rect(x, y, width, height)
196 }
197
198 fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) {
200 self.canvas_state.stroke_rect(x, y, width, height)
201 }
202
203 fn BeginPath(&self) {
205 self.canvas_state.begin_path()
206 }
207
208 fn ClosePath(&self) {
210 self.canvas_state.close_path()
211 }
212
213 fn Fill(&self, fill_rule: CanvasFillRule) {
215 self.canvas_state.fill(fill_rule)
216 }
217
218 fn Fill_(&self, path: &Path2D, fill_rule: CanvasFillRule) {
220 self.canvas_state.fill_(path.segments(), fill_rule)
221 }
222
223 fn Stroke(&self) {
225 self.canvas_state.stroke()
226 }
227
228 fn Stroke_(&self, path: &Path2D) {
230 self.canvas_state.stroke_(path.segments())
231 }
232
233 fn Clip(&self, fill_rule: CanvasFillRule) {
235 self.canvas_state.clip(fill_rule)
236 }
237
238 fn Clip_(&self, path: &Path2D, fill_rule: CanvasFillRule) {
240 self.canvas_state.clip_(path.segments(), fill_rule)
241 }
242
243 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 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 fn DrawImage(&self, image: CanvasImageSource, dx: f64, dy: f64) -> ErrorResult {
257 self.canvas_state.draw_image(None, image, dx, dy)
258 }
259
260 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 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 fn MoveTo(&self, x: f64, y: f64) {
291 self.canvas_state.move_to(x, y)
292 }
293
294 fn LineTo(&self, x: f64, y: f64) {
296 self.canvas_state.line_to(x, y)
297 }
298
299 fn Rect(&self, x: f64, y: f64, width: f64, height: f64) {
301 self.canvas_state.rect(x, y, width, height)
302 }
303
304 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 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 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 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 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 fn ImageSmoothingEnabled(&self) -> bool {
343 self.canvas_state.image_smoothing_enabled()
344 }
345
346 fn SetImageSmoothingEnabled(&self, value: bool) {
348 self.canvas_state.set_image_smoothing_enabled(value)
349 }
350
351 fn StrokeStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
353 self.canvas_state.stroke_style()
354 }
355
356 fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
358 self.canvas_state.set_stroke_style(None, value)
359 }
360
361 fn FillStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
363 self.canvas_state.fill_style()
364 }
365
366 fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
368 self.canvas_state.set_fill_style(None, value)
369 }
370
371 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 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 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 fn LineWidth(&self) -> f64 {
412 self.canvas_state.line_width()
413 }
414
415 fn SetLineWidth(&self, width: f64) {
417 self.canvas_state.set_line_width(width)
418 }
419
420 fn LineCap(&self) -> CanvasLineCap {
422 self.canvas_state.line_cap()
423 }
424
425 fn SetLineCap(&self, cap: CanvasLineCap) {
427 self.canvas_state.set_line_cap(cap)
428 }
429
430 fn LineJoin(&self) -> CanvasLineJoin {
432 self.canvas_state.line_join()
433 }
434
435 fn SetLineJoin(&self, join: CanvasLineJoin) {
437 self.canvas_state.set_line_join(join)
438 }
439
440 fn MiterLimit(&self) -> f64 {
442 self.canvas_state.miter_limit()
443 }
444
445 fn SetMiterLimit(&self, limit: f64) {
447 self.canvas_state.set_miter_limit(limit)
448 }
449
450 fn SetLineDash(&self, segments: Vec<f64>) {
452 self.canvas_state.set_line_dash(segments);
453 }
454
455 fn GetLineDash(&self) -> Vec<f64> {
457 self.canvas_state.line_dash()
458 }
459
460 fn LineDashOffset(&self) -> f64 {
462 self.canvas_state.line_dash_offset()
463 }
464
465 fn SetLineDashOffset(&self, offset: f64) {
467 self.canvas_state.set_line_dash_offset(offset);
468 }
469
470 fn ShadowOffsetX(&self) -> f64 {
472 self.canvas_state.shadow_offset_x()
473 }
474
475 fn SetShadowOffsetX(&self, value: f64) {
477 self.canvas_state.set_shadow_offset_x(value)
478 }
479
480 fn ShadowOffsetY(&self) -> f64 {
482 self.canvas_state.shadow_offset_y()
483 }
484
485 fn SetShadowOffsetY(&self, value: f64) {
487 self.canvas_state.set_shadow_offset_y(value)
488 }
489
490 fn ShadowBlur(&self) -> f64 {
492 self.canvas_state.shadow_blur()
493 }
494
495 fn SetShadowBlur(&self, value: f64) {
497 self.canvas_state.set_shadow_blur(value)
498 }
499
500 fn ShadowColor(&self) -> DOMString {
502 self.canvas_state.shadow_color()
503 }
504
505 fn SetShadowColor(&self, value: DOMString) {
507 self.canvas_state.set_shadow_color(None, value)
508 }
509}