1use base::Epoch;
6use canvas_traits::canvas::{Canvas2dMsg, CanvasId};
7use dom_struct::dom_struct;
8use euclid::default::Size2D;
9use ipc_channel::ipc;
10use pixels::Snapshot;
11use script_bindings::inheritance::Castable;
12use servo_url::ServoUrl;
13use webrender_api::ImageKey;
14
15use crate::canvas_context::{CanvasContext, CanvasHelpers, LayoutCanvasRenderingContextHelpers};
16use crate::canvas_state::CanvasState;
17use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{
18 CanvasDirection, CanvasFillRule, CanvasImageSource, CanvasLineCap, CanvasLineJoin,
19 CanvasRenderingContext2DMethods, CanvasTextAlign, CanvasTextBaseline,
20};
21use crate::dom::bindings::codegen::Bindings::DOMMatrixBinding::DOMMatrix2DInit;
22use crate::dom::bindings::codegen::UnionTypes::{
23 HTMLCanvasElementOrOffscreenCanvas, StringOrCanvasGradientOrCanvasPattern,
24};
25use crate::dom::bindings::error::{ErrorResult, Fallible};
26use crate::dom::bindings::num::Finite;
27use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
28use crate::dom::bindings::root::{DomRoot, LayoutDom};
29use crate::dom::bindings::str::DOMString;
30use crate::dom::canvasgradient::CanvasGradient;
31use crate::dom::canvaspattern::CanvasPattern;
32use crate::dom::dommatrix::DOMMatrix;
33use crate::dom::globalscope::GlobalScope;
34use crate::dom::html::htmlcanvaselement::HTMLCanvasElement;
35use crate::dom::imagedata::ImageData;
36use crate::dom::node::{Node, NodeDamage, NodeTraits};
37use crate::dom::path2d::Path2D;
38use crate::dom::textmetrics::TextMetrics;
39use crate::script_runtime::CanGc;
40
41#[dom_struct]
43pub(crate) struct CanvasRenderingContext2D {
44 reflector_: Reflector,
45 canvas: HTMLCanvasElementOrOffscreenCanvas,
46 canvas_state: CanvasState,
47}
48
49impl CanvasRenderingContext2D {
50 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
51 pub(crate) fn new_inherited(
52 global: &GlobalScope,
53 canvas: HTMLCanvasElementOrOffscreenCanvas,
54 size: Size2D<u32>,
55 ) -> Option<CanvasRenderingContext2D> {
56 let canvas_state =
57 CanvasState::new(global, Size2D::new(size.width as u64, size.height as u64))?;
58 Some(CanvasRenderingContext2D {
59 reflector_: Reflector::new(),
60 canvas,
61 canvas_state,
62 })
63 }
64
65 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
66 pub(crate) fn new(
67 global: &GlobalScope,
68 canvas: &HTMLCanvasElement,
69 size: Size2D<u32>,
70 can_gc: CanGc,
71 ) -> Option<DomRoot<CanvasRenderingContext2D>> {
72 let boxed = Box::new(CanvasRenderingContext2D::new_inherited(
73 global,
74 HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(canvas)),
75 size,
76 )?);
77 Some(reflect_dom_object(boxed, global, can_gc))
78 }
79
80 fn reset_to_initial_state(&self) {
82 self.canvas_state.reset_to_initial_state();
83 }
84
85 pub(crate) fn set_canvas_bitmap_dimensions(&self, size: Size2D<u64>) {
87 self.canvas_state.set_bitmap_dimensions(size);
88 }
89
90 pub(crate) fn take_missing_image_urls(&self) -> Vec<ServoUrl> {
91 std::mem::take(&mut self.canvas_state.get_missing_image_urls().borrow_mut())
92 }
93
94 pub(crate) fn get_canvas_id(&self) -> CanvasId {
95 self.canvas_state.get_canvas_id()
96 }
97
98 pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
99 self.canvas_state.send_canvas_2d_msg(msg)
100 }
101}
102
103impl LayoutCanvasRenderingContextHelpers for LayoutDom<'_, CanvasRenderingContext2D> {
104 fn canvas_data_source(self) -> Option<ImageKey> {
105 let canvas_state = &self.unsafe_get().canvas_state;
106
107 if canvas_state.is_paintable() {
108 Some(canvas_state.image_key())
109 } else {
110 None
111 }
112 }
113}
114
115impl CanvasContext for CanvasRenderingContext2D {
116 type ID = CanvasId;
117
118 fn context_id(&self) -> Self::ID {
119 self.canvas_state.get_canvas_id()
120 }
121
122 fn canvas(&self) -> Option<HTMLCanvasElementOrOffscreenCanvas> {
123 Some(self.canvas.clone())
124 }
125
126 fn update_rendering(&self, canvas_epoch: Epoch) -> bool {
127 if !self.onscreen() {
128 return false;
129 }
130 self.canvas_state.update_rendering(Some(canvas_epoch))
131 }
132
133 fn resize(&self) {
134 self.set_canvas_bitmap_dimensions(self.size().cast())
135 }
136
137 fn reset_bitmap(&self) {
138 self.canvas_state.reset_bitmap()
139 }
140
141 fn get_image_data(&self) -> Option<Snapshot> {
142 if !self.canvas_state.is_paintable() {
143 return None;
144 }
145
146 let (sender, receiver) = ipc::channel().unwrap();
147 self.canvas_state
148 .send_canvas_2d_msg(Canvas2dMsg::GetImageData(None, sender));
149 Some(receiver.recv().unwrap().to_owned())
150 }
151
152 fn origin_is_clean(&self) -> bool {
153 self.canvas_state.origin_is_clean()
154 }
155
156 fn mark_as_dirty(&self) {
157 if let Some(canvas) = self.canvas.canvas() {
158 canvas.upcast::<Node>().dirty(NodeDamage::Other);
159 canvas.owner_document().add_dirty_2d_canvas(self);
160 }
161 }
162
163 fn image_key(&self) -> Option<ImageKey> {
164 Some(self.canvas_state.image_key())
165 }
166}
167
168impl CanvasRenderingContext2DMethods<crate::DomTypeHolder> for CanvasRenderingContext2D {
178 fn Canvas(&self) -> DomRoot<HTMLCanvasElement> {
180 match &self.canvas {
181 HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => canvas.clone(),
182 _ => panic!("Should not be called from offscreen canvas"),
183 }
184 }
185
186 fn Save(&self) {
188 self.canvas_state.save()
189 }
190
191 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
192 fn Restore(&self) {
194 self.canvas_state.restore()
195 }
196
197 fn Reset(&self) {
199 self.canvas_state.reset()
200 }
201
202 fn Scale(&self, x: f64, y: f64) {
204 self.canvas_state.scale(x, y)
205 }
206
207 fn Rotate(&self, angle: f64) {
209 self.canvas_state.rotate(angle)
210 }
211
212 fn Translate(&self, x: f64, y: f64) {
214 self.canvas_state.translate(x, y)
215 }
216
217 fn Transform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) {
219 self.canvas_state.transform(a, b, c, d, e, f)
220 }
221
222 fn GetTransform(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
224 self.canvas_state.get_transform(&self.global(), can_gc)
225 }
226
227 fn SetTransform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) -> ErrorResult {
229 self.canvas_state.set_transform(a, b, c, d, e, f);
230 Ok(())
231 }
232
233 fn SetTransform_(&self, transform: &DOMMatrix2DInit) -> ErrorResult {
235 self.canvas_state.set_transform_(transform)
236 }
237
238 fn ResetTransform(&self) {
240 self.canvas_state.reset_transform()
241 }
242
243 fn GlobalAlpha(&self) -> f64 {
245 self.canvas_state.global_alpha()
246 }
247
248 fn SetGlobalAlpha(&self, alpha: f64) {
250 self.canvas_state.set_global_alpha(alpha)
251 }
252
253 fn GlobalCompositeOperation(&self) -> DOMString {
255 self.canvas_state.global_composite_operation()
256 }
257
258 fn SetGlobalCompositeOperation(&self, op_str: DOMString) {
260 self.canvas_state.set_global_composite_operation(op_str)
261 }
262
263 fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) {
265 self.canvas_state.fill_rect(x, y, width, height);
266 self.mark_as_dirty();
267 }
268
269 fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) {
271 self.canvas_state.clear_rect(x, y, width, height);
272 self.mark_as_dirty();
273 }
274
275 fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) {
277 self.canvas_state.stroke_rect(x, y, width, height);
278 self.mark_as_dirty();
279 }
280
281 fn BeginPath(&self) {
283 self.canvas_state.begin_path()
284 }
285
286 fn ClosePath(&self) {
288 self.canvas_state.close_path()
289 }
290
291 fn Fill(&self, fill_rule: CanvasFillRule) {
293 self.canvas_state.fill(fill_rule);
294 self.mark_as_dirty();
295 }
296
297 fn Fill_(&self, path: &Path2D, fill_rule: CanvasFillRule) {
299 self.canvas_state.fill_(path.segments(), fill_rule);
300 self.mark_as_dirty();
301 }
302
303 fn Stroke(&self) {
305 self.canvas_state.stroke();
306 self.mark_as_dirty();
307 }
308
309 fn Stroke_(&self, path: &Path2D) {
311 self.canvas_state.stroke_(path.segments());
312 self.mark_as_dirty();
313 }
314
315 fn Clip(&self, fill_rule: CanvasFillRule) {
317 self.canvas_state.clip(fill_rule)
318 }
319
320 fn Clip_(&self, path: &Path2D, fill_rule: CanvasFillRule) {
322 self.canvas_state.clip_(path.segments(), fill_rule)
323 }
324
325 fn IsPointInPath(&self, x: f64, y: f64, fill_rule: CanvasFillRule) -> bool {
327 self.canvas_state
328 .is_point_in_path(&self.global(), x, y, fill_rule)
329 }
330
331 fn IsPointInPath_(&self, path: &Path2D, x: f64, y: f64, fill_rule: CanvasFillRule) -> bool {
333 self.canvas_state
334 .is_point_in_path_(&self.global(), path.segments(), x, y, fill_rule)
335 }
336
337 fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>) {
339 self.canvas_state.fill_text(
340 &self.global(),
341 self.canvas.canvas().as_deref(),
342 text,
343 x,
344 y,
345 max_width,
346 );
347 self.mark_as_dirty();
348 }
349
350 fn MeasureText(&self, text: DOMString, can_gc: CanGc) -> DomRoot<TextMetrics> {
352 self.canvas_state.measure_text(
353 &self.global(),
354 self.canvas.canvas().as_deref(),
355 text,
356 can_gc,
357 )
358 }
359
360 fn Font(&self) -> DOMString {
362 self.canvas_state.font()
363 }
364
365 fn SetFont(&self, value: DOMString) {
367 self.canvas_state
368 .set_font(self.canvas.canvas().as_deref(), value)
369 }
370
371 fn TextAlign(&self) -> CanvasTextAlign {
373 self.canvas_state.text_align()
374 }
375
376 fn SetTextAlign(&self, value: CanvasTextAlign) {
378 self.canvas_state.set_text_align(value)
379 }
380
381 fn TextBaseline(&self) -> CanvasTextBaseline {
383 self.canvas_state.text_baseline()
384 }
385
386 fn SetTextBaseline(&self, value: CanvasTextBaseline) {
388 self.canvas_state.set_text_baseline(value)
389 }
390
391 fn Direction(&self) -> CanvasDirection {
393 self.canvas_state.direction()
394 }
395
396 fn SetDirection(&self, value: CanvasDirection) {
398 self.canvas_state.set_direction(value)
399 }
400
401 fn DrawImage(&self, image: CanvasImageSource, dx: f64, dy: f64) -> ErrorResult {
403 self.canvas_state
404 .draw_image(self.canvas.canvas().as_deref(), image, dx, dy)
405 }
406
407 fn DrawImage_(
409 &self,
410 image: CanvasImageSource,
411 dx: f64,
412 dy: f64,
413 dw: f64,
414 dh: f64,
415 ) -> ErrorResult {
416 self.canvas_state
417 .draw_image_(self.canvas.canvas().as_deref(), image, dx, dy, dw, dh)
418 }
419
420 fn DrawImage__(
422 &self,
423 image: CanvasImageSource,
424 sx: f64,
425 sy: f64,
426 sw: f64,
427 sh: f64,
428 dx: f64,
429 dy: f64,
430 dw: f64,
431 dh: f64,
432 ) -> ErrorResult {
433 self.canvas_state.draw_image__(
434 self.canvas.canvas().as_deref(),
435 image,
436 sx,
437 sy,
438 sw,
439 sh,
440 dx,
441 dy,
442 dw,
443 dh,
444 )
445 }
446
447 fn MoveTo(&self, x: f64, y: f64) {
449 self.canvas_state.move_to(x, y)
450 }
451
452 fn LineTo(&self, x: f64, y: f64) {
454 self.canvas_state.line_to(x, y)
455 }
456
457 fn Rect(&self, x: f64, y: f64, width: f64, height: f64) {
459 self.canvas_state.rect(x, y, width, height)
460 }
461
462 fn QuadraticCurveTo(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
464 self.canvas_state.quadratic_curve_to(cpx, cpy, x, y)
465 }
466
467 fn BezierCurveTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
469 self.canvas_state
470 .bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y)
471 }
472
473 fn Arc(&self, x: f64, y: f64, r: f64, start: f64, end: f64, ccw: bool) -> ErrorResult {
475 self.canvas_state.arc(x, y, r, start, end, ccw)
476 }
477
478 fn ArcTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, r: f64) -> ErrorResult {
480 self.canvas_state.arc_to(cp1x, cp1y, cp2x, cp2y, r)
481 }
482
483 fn Ellipse(
485 &self,
486 x: f64,
487 y: f64,
488 rx: f64,
489 ry: f64,
490 rotation: f64,
491 start: f64,
492 end: f64,
493 ccw: bool,
494 ) -> ErrorResult {
495 self.canvas_state
496 .ellipse(x, y, rx, ry, rotation, start, end, ccw)
497 }
498
499 fn ImageSmoothingEnabled(&self) -> bool {
501 self.canvas_state.image_smoothing_enabled()
502 }
503
504 fn SetImageSmoothingEnabled(&self, value: bool) {
506 self.canvas_state.set_image_smoothing_enabled(value)
507 }
508
509 fn StrokeStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
511 self.canvas_state.stroke_style()
512 }
513
514 fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
516 self.canvas_state
517 .set_stroke_style(self.canvas.canvas().as_deref(), value)
518 }
519
520 fn FillStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
522 self.canvas_state.fill_style()
523 }
524
525 fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
527 self.canvas_state
528 .set_fill_style(self.canvas.canvas().as_deref(), value)
529 }
530
531 fn CreateImageData(&self, sw: i32, sh: i32, can_gc: CanGc) -> Fallible<DomRoot<ImageData>> {
533 self.canvas_state
534 .create_image_data(&self.global(), sw, sh, can_gc)
535 }
536
537 fn CreateImageData_(
539 &self,
540 imagedata: &ImageData,
541 can_gc: CanGc,
542 ) -> Fallible<DomRoot<ImageData>> {
543 self.canvas_state
544 .create_image_data_(&self.global(), imagedata, can_gc)
545 }
546
547 fn GetImageData(
549 &self,
550 sx: i32,
551 sy: i32,
552 sw: i32,
553 sh: i32,
554 can_gc: CanGc,
555 ) -> Fallible<DomRoot<ImageData>> {
556 self.canvas_state
557 .get_image_data(self.canvas.size(), &self.global(), sx, sy, sw, sh, can_gc)
558 }
559
560 fn PutImageData(&self, imagedata: &ImageData, dx: i32, dy: i32) {
562 self.canvas_state
563 .put_image_data(self.canvas.size(), imagedata, dx, dy)
564 }
565
566 #[allow(unsafe_code)]
568 fn PutImageData_(
569 &self,
570 imagedata: &ImageData,
571 dx: i32,
572 dy: i32,
573 dirty_x: i32,
574 dirty_y: i32,
575 dirty_width: i32,
576 dirty_height: i32,
577 ) {
578 self.canvas_state.put_image_data_(
579 self.canvas.size(),
580 imagedata,
581 dx,
582 dy,
583 dirty_x,
584 dirty_y,
585 dirty_width,
586 dirty_height,
587 );
588 self.mark_as_dirty();
589 }
590
591 fn CreateLinearGradient(
593 &self,
594 x0: Finite<f64>,
595 y0: Finite<f64>,
596 x1: Finite<f64>,
597 y1: Finite<f64>,
598 can_gc: CanGc,
599 ) -> DomRoot<CanvasGradient> {
600 self.canvas_state
601 .create_linear_gradient(&self.global(), x0, y0, x1, y1, can_gc)
602 }
603
604 fn CreateRadialGradient(
606 &self,
607 x0: Finite<f64>,
608 y0: Finite<f64>,
609 r0: Finite<f64>,
610 x1: Finite<f64>,
611 y1: Finite<f64>,
612 r1: Finite<f64>,
613 can_gc: CanGc,
614 ) -> Fallible<DomRoot<CanvasGradient>> {
615 self.canvas_state
616 .create_radial_gradient(&self.global(), x0, y0, r0, x1, y1, r1, can_gc)
617 }
618
619 fn CreatePattern(
621 &self,
622 image: CanvasImageSource,
623 repetition: DOMString,
624 can_gc: CanGc,
625 ) -> Fallible<Option<DomRoot<CanvasPattern>>> {
626 self.canvas_state
627 .create_pattern(&self.global(), image, repetition, can_gc)
628 }
629
630 fn LineWidth(&self) -> f64 {
632 self.canvas_state.line_width()
633 }
634
635 fn SetLineWidth(&self, width: f64) {
637 self.canvas_state.set_line_width(width)
638 }
639
640 fn LineCap(&self) -> CanvasLineCap {
642 self.canvas_state.line_cap()
643 }
644
645 fn SetLineCap(&self, cap: CanvasLineCap) {
647 self.canvas_state.set_line_cap(cap)
648 }
649
650 fn LineJoin(&self) -> CanvasLineJoin {
652 self.canvas_state.line_join()
653 }
654
655 fn SetLineJoin(&self, join: CanvasLineJoin) {
657 self.canvas_state.set_line_join(join)
658 }
659
660 fn MiterLimit(&self) -> f64 {
662 self.canvas_state.miter_limit()
663 }
664
665 fn SetMiterLimit(&self, limit: f64) {
667 self.canvas_state.set_miter_limit(limit)
668 }
669
670 fn SetLineDash(&self, segments: Vec<f64>) {
672 self.canvas_state.set_line_dash(segments);
673 }
674
675 fn GetLineDash(&self) -> Vec<f64> {
677 self.canvas_state.line_dash()
678 }
679
680 fn LineDashOffset(&self) -> f64 {
682 self.canvas_state.line_dash_offset()
683 }
684
685 fn SetLineDashOffset(&self, offset: f64) {
687 self.canvas_state.set_line_dash_offset(offset);
688 }
689
690 fn ShadowOffsetX(&self) -> f64 {
692 self.canvas_state.shadow_offset_x()
693 }
694
695 fn SetShadowOffsetX(&self, value: f64) {
697 self.canvas_state.set_shadow_offset_x(value)
698 }
699
700 fn ShadowOffsetY(&self) -> f64 {
702 self.canvas_state.shadow_offset_y()
703 }
704
705 fn SetShadowOffsetY(&self, value: f64) {
707 self.canvas_state.set_shadow_offset_y(value)
708 }
709
710 fn ShadowBlur(&self) -> f64 {
712 self.canvas_state.shadow_blur()
713 }
714
715 fn SetShadowBlur(&self, value: f64) {
717 self.canvas_state.set_shadow_blur(value)
718 }
719
720 fn ShadowColor(&self) -> DOMString {
722 self.canvas_state.shadow_color()
723 }
724
725 fn SetShadowColor(&self, value: DOMString) {
727 self.canvas_state
728 .set_shadow_color(self.canvas.canvas().as_deref(), value)
729 }
730}