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