1use dom_struct::dom_struct;
6use euclid::default::Size2D;
7use js::context::{JSContext, NoGC};
8use pixels::Snapshot;
9use script_bindings::reflector::{AssociatedMemory, Reflector, reflect_dom_object};
10use servo_base::{Epoch, generic_channel};
11use servo_canvas_traits::canvas::{CanvasCommand, CanvasId};
12use webrender_api::ImageKey;
13
14use super::canvas_state::CanvasState;
15use crate::canvas_context::{CanvasContext, CanvasHelpers, HTMLCanvasElementOrOffscreenCanvas};
16use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{
17 CanvasColorType, CanvasDirection, CanvasFillRule, CanvasImageSource, CanvasLineCap,
18 CanvasLineJoin, CanvasRenderingContext2DMethods, CanvasRenderingContext2DSettings,
19 CanvasTextAlign, CanvasTextBaseline, PredefinedColorSpace,
20};
21use crate::dom::bindings::codegen::Bindings::DOMMatrixBinding::DOMMatrix2DInit;
22use crate::dom::bindings::codegen::UnionTypes::{
23 HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas,
24 StringOrCanvasGradientOrCanvasPattern,
25 UnrestrictedDoubleOrDOMPointInitOrUnrestrictedDoubleOrDOMPointInitSequence,
26};
27use crate::dom::bindings::error::{ErrorResult, Fallible};
28use crate::dom::bindings::num::Finite;
29use crate::dom::bindings::reflector::DomGlobal;
30use crate::dom::bindings::root::{Dom, 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::globalscope::GlobalScope;
36use crate::dom::html::htmlcanvaselement::HTMLCanvasElement;
37use crate::dom::imagedata::ImageData;
38use crate::dom::path2d::Path2D;
39use crate::dom::textmetrics::TextMetrics;
40
41#[dom_struct]
43pub(crate) struct CanvasRenderingContext2D {
44 reflector_: Reflector<AssociatedMemory>,
45 canvas: HTMLCanvasElementOrOffscreenCanvas,
46 canvas_state: CanvasState,
47 alpha: bool,
49 desynchronized: bool,
51 color_space: PredefinedColorSpace,
53 color_type: CanvasColorType,
55 will_read_frequently: bool,
57}
58
59impl CanvasRenderingContext2D {
60 const RGBA8_BYTES_PER_PIXEL: usize = 4;
61 const ASSOCIATED_MEMORY_BUFFER_COUNT: usize = 4;
77
78 fn associated_memory_size(size: Size2D<u64>) -> usize {
79 (size.width as usize)
80 .saturating_mul(size.height as usize)
81 .saturating_mul(Self::RGBA8_BYTES_PER_PIXEL)
82 .saturating_mul(Self::ASSOCIATED_MEMORY_BUFFER_COUNT)
83 }
84
85 pub(crate) fn update_associated_memory_size(&self) {
86 self.reflector_.update_memory_size(
87 self,
88 Self::associated_memory_size(self.canvas_state.bitmap_dimensions()),
89 );
90 }
91
92 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
94 pub(crate) fn new_inherited(
95 global: &GlobalScope,
96 canvas: HTMLCanvasElementOrOffscreenCanvas,
97 size: Size2D<u32>,
98 settings: &CanvasRenderingContext2DSettings,
99 ) -> Option<CanvasRenderingContext2D> {
100 let canvas_state =
101 CanvasState::new(global, Size2D::new(size.width as u64, size.height as u64))?;
102 Some(CanvasRenderingContext2D {
103 reflector_: Reflector::new(),
104 canvas,
105 canvas_state,
106 alpha: settings.alpha,
108 desynchronized: settings.desynchronized,
110 color_space: settings.colorSpace,
112 color_type: settings.colorType,
114 will_read_frequently: settings.willReadFrequently,
116 })
117 }
118
119 pub(crate) fn new(
120 cx: &mut JSContext,
121 global: &GlobalScope,
122 canvas: &HTMLCanvasElement,
123 size: Size2D<u32>,
124 settings: &CanvasRenderingContext2DSettings,
125 ) -> Option<DomRoot<CanvasRenderingContext2D>> {
126 CanvasRenderingContext2D::new_inherited(
127 global,
128 HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(Dom::from_ref(canvas)),
129 size,
130 settings,
131 )
132 .map(|context| {
133 let context = reflect_dom_object(cx, Box::new(context), global);
134 context.update_associated_memory_size();
135 context
136 })
137 }
138
139 pub(crate) fn send_canvas_command_immediate(&self, msg: CanvasCommand) {
140 self.canvas_state.send_canvas_command_immediate(msg)
141 }
142
143 pub(crate) fn set_image_key(&self, image_key: ImageKey) {
144 self.canvas_state.set_image_key(image_key);
145 }
146
147 pub(crate) fn update_rendering(&self, canvas_epoch: Epoch) -> bool {
148 if !self.onscreen() {
149 return false;
150 }
151 self.canvas_state.update_rendering(Some(canvas_epoch))
152 }
153}
154
155impl CanvasContext for CanvasRenderingContext2D {
156 type ID = CanvasId;
157
158 fn context_id(&self) -> Self::ID {
159 self.canvas_state.get_canvas_id()
160 }
161
162 fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
163 Some(RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas))
164 }
165
166 fn resize(&self) {
167 self.canvas_state.set_bitmap_dimensions(self.size().cast());
168 self.update_associated_memory_size();
169 }
170
171 fn reset_bitmap(&self) {
172 self.canvas_state.reset_bitmap()
173 }
174
175 fn get_image_data(&self) -> Option<Snapshot> {
176 if !self.canvas_state.is_paintable() {
177 return None;
178 }
179
180 let (sender, receiver) = generic_channel::channel().unwrap();
181 self.canvas_state
182 .send_canvas_command_immediate(CanvasCommand::GetImageData(None, sender));
183 Some(receiver.recv().unwrap().to_owned())
184 }
185
186 fn origin_is_clean(&self) -> bool {
187 self.canvas_state.origin_is_clean()
188 }
189
190 fn mark_as_dirty(&self) {
191 self.canvas.mark_as_dirty();
192 }
193}
194
195impl CanvasRenderingContext2DMethods<crate::DomTypeHolder> for CanvasRenderingContext2D {
205 fn GetContextAttributes(&self) -> CanvasRenderingContext2DSettings {
207 CanvasRenderingContext2DSettings {
211 alpha: self.alpha,
212 desynchronized: self.desynchronized,
213 colorSpace: self.color_space,
214 colorType: self.color_type,
215 willReadFrequently: self.will_read_frequently,
216 }
217 }
218
219 fn Canvas(&self) -> DomRoot<HTMLCanvasElement> {
221 match &self.canvas {
222 HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => canvas.as_rooted(),
223 _ => panic!("Should not be called from offscreen canvas"),
224 }
225 }
226
227 fn Save(&self) {
229 self.canvas_state.save()
230 }
231
232 fn Restore(&self) {
234 self.canvas_state.restore()
235 }
236
237 fn Reset(&self) {
239 self.canvas_state.reset()
240 }
241
242 fn Scale(&self, x: f64, y: f64) {
244 self.canvas_state.scale(x, y)
245 }
246
247 fn Rotate(&self, angle: f64) {
249 self.canvas_state.rotate(angle)
250 }
251
252 fn Translate(&self, x: f64, y: f64) {
254 self.canvas_state.translate(x, y)
255 }
256
257 fn Transform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) {
259 self.canvas_state.transform(a, b, c, d, e, f)
260 }
261
262 fn GetTransform(&self, cx: &mut JSContext) -> DomRoot<DOMMatrix> {
264 self.canvas_state.get_transform(&self.global(), cx)
265 }
266
267 fn SetTransform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) -> ErrorResult {
269 self.canvas_state.set_transform(a, b, c, d, e, f);
270 Ok(())
271 }
272
273 fn SetTransform_(&self, transform: &DOMMatrix2DInit) -> ErrorResult {
275 self.canvas_state.set_transform_(transform)
276 }
277
278 fn ResetTransform(&self) {
280 self.canvas_state.reset_transform()
281 }
282
283 fn GlobalAlpha(&self) -> f64 {
285 self.canvas_state.global_alpha()
286 }
287
288 fn SetGlobalAlpha(&self, alpha: f64) {
290 self.canvas_state.set_global_alpha(alpha)
291 }
292
293 fn GlobalCompositeOperation(&self) -> DOMString {
295 self.canvas_state.global_composite_operation()
296 }
297
298 fn SetGlobalCompositeOperation(&self, op_str: DOMString) {
300 self.canvas_state.set_global_composite_operation(op_str)
301 }
302
303 fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) {
305 self.canvas_state.fill_rect(x, y, width, height);
306 self.mark_as_dirty();
307 }
308
309 fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) {
311 self.canvas_state.clear_rect(x, y, width, height);
312 self.mark_as_dirty();
313 }
314
315 fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) {
317 self.canvas_state.stroke_rect(x, y, width, height);
318 self.mark_as_dirty();
319 }
320
321 fn BeginPath(&self) {
323 self.canvas_state.begin_path()
324 }
325
326 fn ClosePath(&self) {
328 self.canvas_state.close_path()
329 }
330
331 fn Fill(&self, fill_rule: CanvasFillRule) {
333 self.canvas_state.fill(fill_rule);
334 self.mark_as_dirty();
335 }
336
337 fn Fill_(&self, path: &Path2D, fill_rule: CanvasFillRule) {
339 self.canvas_state.fill_(path.segments(), fill_rule);
340 self.mark_as_dirty();
341 }
342
343 fn Stroke(&self) {
345 self.canvas_state.stroke();
346 self.mark_as_dirty();
347 }
348
349 fn Stroke_(&self, path: &Path2D) {
351 self.canvas_state.stroke_(path.segments());
352 self.mark_as_dirty();
353 }
354
355 fn Clip(&self, fill_rule: CanvasFillRule) {
357 self.canvas_state.clip(fill_rule)
358 }
359
360 fn Clip_(&self, path: &Path2D, fill_rule: CanvasFillRule) {
362 self.canvas_state.clip_(path.segments(), fill_rule)
363 }
364
365 fn IsPointInPath(&self, x: f64, y: f64, fill_rule: CanvasFillRule) -> bool {
367 self.canvas_state
368 .is_point_in_path(&self.global(), x, y, fill_rule)
369 }
370
371 fn IsPointInPath_(&self, path: &Path2D, x: f64, y: f64, fill_rule: CanvasFillRule) -> bool {
373 self.canvas_state
374 .is_point_in_path_(&self.global(), path.segments(), x, y, fill_rule)
375 }
376
377 fn FillText(
379 &self,
380 cx: &mut JSContext,
381 text: DOMString,
382 x: f64,
383 y: f64,
384 max_width: Option<f64>,
385 ) {
386 self.canvas_state.fill_text(
387 cx,
388 &self.global(),
389 self.canvas.canvas().as_deref(),
390 text,
391 x,
392 y,
393 max_width,
394 );
395 self.mark_as_dirty();
396 }
397
398 fn StrokeText(
400 &self,
401 cx: &mut JSContext,
402 text: DOMString,
403 x: f64,
404 y: f64,
405 max_width: Option<f64>,
406 ) {
407 self.canvas_state.stroke_text(
408 cx,
409 &self.global(),
410 self.canvas.canvas().as_deref(),
411 text,
412 x,
413 y,
414 max_width,
415 );
416 self.mark_as_dirty();
417 }
418
419 fn MeasureText(&self, cx: &mut JSContext, text: DOMString) -> DomRoot<TextMetrics> {
421 self.canvas_state
422 .measure_text(&self.global(), self.canvas.canvas().as_deref(), text, cx)
423 }
424
425 fn Font(&self) -> DOMString {
427 self.canvas_state.font()
428 }
429
430 fn SetFont(&self, cx: &mut JSContext, value: DOMString) {
432 self.canvas_state
433 .set_font(cx, self.canvas.canvas().as_deref(), value)
434 }
435
436 fn TextAlign(&self) -> CanvasTextAlign {
438 self.canvas_state.text_align()
439 }
440
441 fn SetTextAlign(&self, value: CanvasTextAlign) {
443 self.canvas_state.set_text_align(value)
444 }
445
446 fn TextBaseline(&self) -> CanvasTextBaseline {
448 self.canvas_state.text_baseline()
449 }
450
451 fn SetTextBaseline(&self, value: CanvasTextBaseline) {
453 self.canvas_state.set_text_baseline(value)
454 }
455
456 fn Direction(&self) -> CanvasDirection {
458 self.canvas_state.direction()
459 }
460
461 fn SetDirection(&self, value: CanvasDirection) {
463 self.canvas_state.set_direction(value)
464 }
465
466 fn DrawImage(&self, image: CanvasImageSource, dx: f64, dy: f64) -> ErrorResult {
468 self.canvas_state
469 .draw_image(self.canvas.canvas().as_deref(), image, dx, dy)
470 }
471
472 fn DrawImage_(
474 &self,
475 image: CanvasImageSource,
476 dx: f64,
477 dy: f64,
478 dw: f64,
479 dh: f64,
480 ) -> ErrorResult {
481 self.canvas_state
482 .draw_image_(self.canvas.canvas().as_deref(), image, dx, dy, dw, dh)
483 }
484
485 fn DrawImage__(
487 &self,
488 image: CanvasImageSource,
489 sx: f64,
490 sy: f64,
491 sw: f64,
492 sh: f64,
493 dx: f64,
494 dy: f64,
495 dw: f64,
496 dh: f64,
497 ) -> ErrorResult {
498 self.canvas_state.draw_image__(
499 self.canvas.canvas().as_deref(),
500 image,
501 sx,
502 sy,
503 sw,
504 sh,
505 dx,
506 dy,
507 dw,
508 dh,
509 )
510 }
511
512 fn MoveTo(&self, x: f64, y: f64) {
514 self.canvas_state.move_to(x, y)
515 }
516
517 fn LineTo(&self, x: f64, y: f64) {
519 self.canvas_state.line_to(x, y)
520 }
521
522 fn Rect(&self, x: f64, y: f64, width: f64, height: f64) {
524 self.canvas_state.rect(x, y, width, height)
525 }
526
527 fn RoundRect(
529 &self,
530 x: f64,
531 y: f64,
532 width: f64,
533 height: f64,
534 radii: UnrestrictedDoubleOrDOMPointInitOrUnrestrictedDoubleOrDOMPointInitSequence,
535 ) -> ErrorResult {
536 self.canvas_state.round_rect(x, y, width, height, radii)
537 }
538
539 fn QuadraticCurveTo(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
541 self.canvas_state.quadratic_curve_to(cpx, cpy, x, y)
542 }
543
544 fn BezierCurveTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
546 self.canvas_state
547 .bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y)
548 }
549
550 fn Arc(&self, x: f64, y: f64, r: f64, start: f64, end: f64, ccw: bool) -> ErrorResult {
552 self.canvas_state.arc(x, y, r, start, end, ccw)
553 }
554
555 fn ArcTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, r: f64) -> ErrorResult {
557 self.canvas_state.arc_to(cp1x, cp1y, cp2x, cp2y, r)
558 }
559
560 fn Ellipse(
562 &self,
563 x: f64,
564 y: f64,
565 rx: f64,
566 ry: f64,
567 rotation: f64,
568 start: f64,
569 end: f64,
570 ccw: bool,
571 ) -> ErrorResult {
572 self.canvas_state
573 .ellipse(x, y, rx, ry, rotation, start, end, ccw)
574 }
575
576 fn ImageSmoothingEnabled(&self) -> bool {
578 self.canvas_state.image_smoothing_enabled()
579 }
580
581 fn SetImageSmoothingEnabled(&self, value: bool) {
583 self.canvas_state.set_image_smoothing_enabled(value)
584 }
585
586 fn StrokeStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
588 self.canvas_state.stroke_style()
589 }
590
591 fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
593 self.canvas_state
594 .set_stroke_style(self.canvas.canvas().as_deref(), value)
595 }
596
597 fn FillStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
599 self.canvas_state.fill_style()
600 }
601
602 fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
604 self.canvas_state
605 .set_fill_style(self.canvas.canvas().as_deref(), value)
606 }
607
608 fn CreateImageData(
610 &self,
611 cx: &mut JSContext,
612 sw: i32,
613 sh: i32,
614 ) -> Fallible<DomRoot<ImageData>> {
615 self.canvas_state
616 .create_image_data(cx, &self.global(), sw, sh)
617 }
618
619 fn CreateImageData_(
621 &self,
622 cx: &mut JSContext,
623 imagedata: &ImageData,
624 ) -> Fallible<DomRoot<ImageData>> {
625 self.canvas_state
626 .create_image_data_(cx, &self.global(), imagedata)
627 }
628
629 fn GetImageData(
631 &self,
632 cx: &mut JSContext,
633 sx: i32,
634 sy: i32,
635 sw: i32,
636 sh: i32,
637 ) -> Fallible<DomRoot<ImageData>> {
638 self.canvas_state
639 .get_image_data(cx, self.canvas.size(), &self.global(), sx, sy, sw, sh)
640 }
641
642 fn PutImageData(&self, no_gc: &NoGC, imagedata: &ImageData, dx: i32, dy: i32) {
644 self.canvas_state
645 .put_image_data(no_gc, self.canvas.size(), imagedata, dx, dy);
646 self.mark_as_dirty();
647 }
648
649 fn PutImageData_(
651 &self,
652 no_gc: &NoGC,
653 imagedata: &ImageData,
654 dx: i32,
655 dy: i32,
656 dirty_x: i32,
657 dirty_y: i32,
658 dirty_width: i32,
659 dirty_height: i32,
660 ) {
661 self.canvas_state.put_image_data_(
662 no_gc,
663 self.canvas.size(),
664 imagedata,
665 dx,
666 dy,
667 dirty_x,
668 dirty_y,
669 dirty_width,
670 dirty_height,
671 );
672 self.mark_as_dirty();
673 }
674
675 fn CreateLinearGradient(
677 &self,
678 cx: &mut JSContext,
679 x0: Finite<f64>,
680 y0: Finite<f64>,
681 x1: Finite<f64>,
682 y1: Finite<f64>,
683 ) -> DomRoot<CanvasGradient> {
684 self.canvas_state
685 .create_linear_gradient(&self.global(), cx, x0, y0, x1, y1)
686 }
687
688 fn CreateRadialGradient(
690 &self,
691 cx: &mut JSContext,
692 x0: Finite<f64>,
693 y0: Finite<f64>,
694 r0: Finite<f64>,
695 x1: Finite<f64>,
696 y1: Finite<f64>,
697 r1: Finite<f64>,
698 ) -> Fallible<DomRoot<CanvasGradient>> {
699 self.canvas_state
700 .create_radial_gradient(&self.global(), cx, x0, y0, r0, x1, y1, r1)
701 }
702
703 fn CreatePattern(
705 &self,
706 cx: &mut JSContext,
707 image: CanvasImageSource,
708 repetition: DOMString,
709 ) -> Fallible<Option<DomRoot<CanvasPattern>>> {
710 self.canvas_state
711 .create_pattern(&self.global(), cx, image, repetition)
712 }
713
714 fn LineWidth(&self) -> f64 {
716 self.canvas_state.line_width()
717 }
718
719 fn SetLineWidth(&self, width: f64) {
721 self.canvas_state.set_line_width(width)
722 }
723
724 fn LineCap(&self) -> CanvasLineCap {
726 self.canvas_state.line_cap()
727 }
728
729 fn SetLineCap(&self, cap: CanvasLineCap) {
731 self.canvas_state.set_line_cap(cap)
732 }
733
734 fn LineJoin(&self) -> CanvasLineJoin {
736 self.canvas_state.line_join()
737 }
738
739 fn SetLineJoin(&self, join: CanvasLineJoin) {
741 self.canvas_state.set_line_join(join)
742 }
743
744 fn MiterLimit(&self) -> f64 {
746 self.canvas_state.miter_limit()
747 }
748
749 fn SetMiterLimit(&self, limit: f64) {
751 self.canvas_state.set_miter_limit(limit)
752 }
753
754 fn SetLineDash(&self, segments: Vec<f64>) {
756 self.canvas_state.set_line_dash(segments);
757 }
758
759 fn GetLineDash(&self) -> Vec<f64> {
761 self.canvas_state.line_dash()
762 }
763
764 fn LineDashOffset(&self) -> f64 {
766 self.canvas_state.line_dash_offset()
767 }
768
769 fn SetLineDashOffset(&self, offset: f64) {
771 self.canvas_state.set_line_dash_offset(offset);
772 }
773
774 fn ShadowOffsetX(&self) -> f64 {
776 self.canvas_state.shadow_offset_x()
777 }
778
779 fn SetShadowOffsetX(&self, value: f64) {
781 self.canvas_state.set_shadow_offset_x(value)
782 }
783
784 fn ShadowOffsetY(&self) -> f64 {
786 self.canvas_state.shadow_offset_y()
787 }
788
789 fn SetShadowOffsetY(&self, value: f64) {
791 self.canvas_state.set_shadow_offset_y(value)
792 }
793
794 fn ShadowBlur(&self) -> f64 {
796 self.canvas_state.shadow_blur()
797 }
798
799 fn SetShadowBlur(&self, value: f64) {
801 self.canvas_state.set_shadow_blur(value)
802 }
803
804 fn ShadowColor(&self) -> DOMString {
806 self.canvas_state.shadow_color()
807 }
808
809 fn SetShadowColor(&self, value: DOMString) {
811 self.canvas_state
812 .set_shadow_color(self.canvas.canvas().as_deref(), value)
813 }
814}