1use 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 #[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 fn Save(&self) {
112 self.canvas_state.save()
113 }
114
115 fn Restore(&self) {
117 self.canvas_state.restore()
118 }
119
120 fn Reset(&self) {
122 self.canvas_state.reset()
123 }
124
125 fn Scale(&self, x: f64, y: f64) {
127 self.canvas_state.scale(x, y)
128 }
129
130 fn Rotate(&self, angle: f64) {
132 self.canvas_state.rotate(angle)
133 }
134
135 fn Translate(&self, x: f64, y: f64) {
137 self.canvas_state.translate(x, y)
138 }
139
140 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 fn GetTransform(&self, cx: &mut JSContext) -> DomRoot<DOMMatrix> {
147 self.canvas_state.get_transform(&self.global(), cx)
148 }
149
150 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 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 fn ResetTransform(&self) {
166 self.canvas_state.reset_transform();
167 self.scale_by_device_pixel_ratio();
168 }
169
170 fn GlobalAlpha(&self) -> f64 {
172 self.canvas_state.global_alpha()
173 }
174
175 fn SetGlobalAlpha(&self, alpha: f64) {
177 self.canvas_state.set_global_alpha(alpha)
178 }
179
180 fn GlobalCompositeOperation(&self) -> DOMString {
182 self.canvas_state.global_composite_operation()
183 }
184
185 fn SetGlobalCompositeOperation(&self, op_str: DOMString) {
187 self.canvas_state.set_global_composite_operation(op_str)
188 }
189
190 fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) {
192 self.canvas_state.fill_rect(x, y, width, height)
193 }
194
195 fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) {
197 self.canvas_state.clear_rect(x, y, width, height)
198 }
199
200 fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) {
202 self.canvas_state.stroke_rect(x, y, width, height)
203 }
204
205 fn BeginPath(&self) {
207 self.canvas_state.begin_path()
208 }
209
210 fn ClosePath(&self) {
212 self.canvas_state.close_path()
213 }
214
215 fn Fill(&self, fill_rule: CanvasFillRule) {
217 self.canvas_state.fill(fill_rule)
218 }
219
220 fn Fill_(&self, path: &Path2D, fill_rule: CanvasFillRule) {
222 self.canvas_state.fill_(path.segments(), fill_rule)
223 }
224
225 fn Stroke(&self) {
227 self.canvas_state.stroke()
228 }
229
230 fn Stroke_(&self, path: &Path2D) {
232 self.canvas_state.stroke_(path.segments())
233 }
234
235 fn Clip(&self, fill_rule: CanvasFillRule) {
237 self.canvas_state.clip(fill_rule)
238 }
239
240 fn Clip_(&self, path: &Path2D, fill_rule: CanvasFillRule) {
242 self.canvas_state.clip_(path.segments(), fill_rule)
243 }
244
245 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 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 fn DrawImage(&self, image: CanvasImageSource, dx: f64, dy: f64) -> ErrorResult {
259 self.canvas_state.draw_image(None, image, dx, dy)
260 }
261
262 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 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 fn MoveTo(&self, x: f64, y: f64) {
293 self.canvas_state.move_to(x, y)
294 }
295
296 fn LineTo(&self, x: f64, y: f64) {
298 self.canvas_state.line_to(x, y)
299 }
300
301 fn Rect(&self, x: f64, y: f64, width: f64, height: f64) {
303 self.canvas_state.rect(x, y, width, height)
304 }
305
306 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 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 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 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 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 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 fn ImageSmoothingEnabled(&self) -> bool {
357 self.canvas_state.image_smoothing_enabled()
358 }
359
360 fn SetImageSmoothingEnabled(&self, value: bool) {
362 self.canvas_state.set_image_smoothing_enabled(value)
363 }
364
365 fn StrokeStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
367 self.canvas_state.stroke_style()
368 }
369
370 fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
372 self.canvas_state.set_stroke_style(None, value)
373 }
374
375 fn FillStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
377 self.canvas_state.fill_style()
378 }
379
380 fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
382 self.canvas_state.set_fill_style(None, value)
383 }
384
385 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 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 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 fn LineWidth(&self) -> f64 {
426 self.canvas_state.line_width()
427 }
428
429 fn SetLineWidth(&self, width: f64) {
431 self.canvas_state.set_line_width(width)
432 }
433
434 fn LineCap(&self) -> CanvasLineCap {
436 self.canvas_state.line_cap()
437 }
438
439 fn SetLineCap(&self, cap: CanvasLineCap) {
441 self.canvas_state.set_line_cap(cap)
442 }
443
444 fn LineJoin(&self) -> CanvasLineJoin {
446 self.canvas_state.line_join()
447 }
448
449 fn SetLineJoin(&self, join: CanvasLineJoin) {
451 self.canvas_state.set_line_join(join)
452 }
453
454 fn MiterLimit(&self) -> f64 {
456 self.canvas_state.miter_limit()
457 }
458
459 fn SetMiterLimit(&self, limit: f64) {
461 self.canvas_state.set_miter_limit(limit)
462 }
463
464 fn SetLineDash(&self, segments: Vec<f64>) {
466 self.canvas_state.set_line_dash(segments);
467 }
468
469 fn GetLineDash(&self) -> Vec<f64> {
471 self.canvas_state.line_dash()
472 }
473
474 fn LineDashOffset(&self) -> f64 {
476 self.canvas_state.line_dash_offset()
477 }
478
479 fn SetLineDashOffset(&self, offset: f64) {
481 self.canvas_state.set_line_dash_offset(offset);
482 }
483
484 fn ShadowOffsetX(&self) -> f64 {
486 self.canvas_state.shadow_offset_x()
487 }
488
489 fn SetShadowOffsetX(&self, value: f64) {
491 self.canvas_state.set_shadow_offset_x(value)
492 }
493
494 fn ShadowOffsetY(&self) -> f64 {
496 self.canvas_state.shadow_offset_y()
497 }
498
499 fn SetShadowOffsetY(&self, value: f64) {
501 self.canvas_state.set_shadow_offset_y(value)
502 }
503
504 fn ShadowBlur(&self) -> f64 {
506 self.canvas_state.shadow_blur()
507 }
508
509 fn SetShadowBlur(&self, value: f64) {
511 self.canvas_state.set_shadow_blur(value)
512 }
513
514 fn ShadowColor(&self) -> DOMString {
516 self.canvas_state.shadow_color()
517 }
518
519 fn SetShadowColor(&self, value: DOMString) {
521 self.canvas_state.set_shadow_color(None, value)
522 }
523}