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(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>) {
379 self.canvas_state.fill_text(
380 &self.global(),
381 self.canvas.canvas().as_deref(),
382 text,
383 x,
384 y,
385 max_width,
386 );
387 self.mark_as_dirty();
388 }
389
390 fn StrokeText(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>) {
392 self.canvas_state.stroke_text(
393 &self.global(),
394 self.canvas.canvas().as_deref(),
395 text,
396 x,
397 y,
398 max_width,
399 );
400 self.mark_as_dirty();
401 }
402
403 fn MeasureText(&self, cx: &mut JSContext, text: DOMString) -> DomRoot<TextMetrics> {
405 self.canvas_state
406 .measure_text(&self.global(), self.canvas.canvas().as_deref(), text, cx)
407 }
408
409 fn Font(&self) -> DOMString {
411 self.canvas_state.font()
412 }
413
414 fn SetFont(&self, value: DOMString) {
416 self.canvas_state
417 .set_font(self.canvas.canvas().as_deref(), value)
418 }
419
420 fn TextAlign(&self) -> CanvasTextAlign {
422 self.canvas_state.text_align()
423 }
424
425 fn SetTextAlign(&self, value: CanvasTextAlign) {
427 self.canvas_state.set_text_align(value)
428 }
429
430 fn TextBaseline(&self) -> CanvasTextBaseline {
432 self.canvas_state.text_baseline()
433 }
434
435 fn SetTextBaseline(&self, value: CanvasTextBaseline) {
437 self.canvas_state.set_text_baseline(value)
438 }
439
440 fn Direction(&self) -> CanvasDirection {
442 self.canvas_state.direction()
443 }
444
445 fn SetDirection(&self, value: CanvasDirection) {
447 self.canvas_state.set_direction(value)
448 }
449
450 fn DrawImage(&self, image: CanvasImageSource, dx: f64, dy: f64) -> ErrorResult {
452 self.canvas_state
453 .draw_image(self.canvas.canvas().as_deref(), image, dx, dy)
454 }
455
456 fn DrawImage_(
458 &self,
459 image: CanvasImageSource,
460 dx: f64,
461 dy: f64,
462 dw: f64,
463 dh: f64,
464 ) -> ErrorResult {
465 self.canvas_state
466 .draw_image_(self.canvas.canvas().as_deref(), image, dx, dy, dw, dh)
467 }
468
469 fn DrawImage__(
471 &self,
472 image: CanvasImageSource,
473 sx: f64,
474 sy: f64,
475 sw: f64,
476 sh: f64,
477 dx: f64,
478 dy: f64,
479 dw: f64,
480 dh: f64,
481 ) -> ErrorResult {
482 self.canvas_state.draw_image__(
483 self.canvas.canvas().as_deref(),
484 image,
485 sx,
486 sy,
487 sw,
488 sh,
489 dx,
490 dy,
491 dw,
492 dh,
493 )
494 }
495
496 fn MoveTo(&self, x: f64, y: f64) {
498 self.canvas_state.move_to(x, y)
499 }
500
501 fn LineTo(&self, x: f64, y: f64) {
503 self.canvas_state.line_to(x, y)
504 }
505
506 fn Rect(&self, x: f64, y: f64, width: f64, height: f64) {
508 self.canvas_state.rect(x, y, width, height)
509 }
510
511 fn RoundRect(
513 &self,
514 x: f64,
515 y: f64,
516 width: f64,
517 height: f64,
518 radii: UnrestrictedDoubleOrDOMPointInitOrUnrestrictedDoubleOrDOMPointInitSequence,
519 ) -> ErrorResult {
520 self.canvas_state.round_rect(x, y, width, height, radii)
521 }
522
523 fn QuadraticCurveTo(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
525 self.canvas_state.quadratic_curve_to(cpx, cpy, x, y)
526 }
527
528 fn BezierCurveTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
530 self.canvas_state
531 .bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y)
532 }
533
534 fn Arc(&self, x: f64, y: f64, r: f64, start: f64, end: f64, ccw: bool) -> ErrorResult {
536 self.canvas_state.arc(x, y, r, start, end, ccw)
537 }
538
539 fn ArcTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, r: f64) -> ErrorResult {
541 self.canvas_state.arc_to(cp1x, cp1y, cp2x, cp2y, r)
542 }
543
544 fn Ellipse(
546 &self,
547 x: f64,
548 y: f64,
549 rx: f64,
550 ry: f64,
551 rotation: f64,
552 start: f64,
553 end: f64,
554 ccw: bool,
555 ) -> ErrorResult {
556 self.canvas_state
557 .ellipse(x, y, rx, ry, rotation, start, end, ccw)
558 }
559
560 fn ImageSmoothingEnabled(&self) -> bool {
562 self.canvas_state.image_smoothing_enabled()
563 }
564
565 fn SetImageSmoothingEnabled(&self, value: bool) {
567 self.canvas_state.set_image_smoothing_enabled(value)
568 }
569
570 fn StrokeStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
572 self.canvas_state.stroke_style()
573 }
574
575 fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
577 self.canvas_state
578 .set_stroke_style(self.canvas.canvas().as_deref(), value)
579 }
580
581 fn FillStyle(&self) -> StringOrCanvasGradientOrCanvasPattern {
583 self.canvas_state.fill_style()
584 }
585
586 fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
588 self.canvas_state
589 .set_fill_style(self.canvas.canvas().as_deref(), value)
590 }
591
592 fn CreateImageData(
594 &self,
595 cx: &mut JSContext,
596 sw: i32,
597 sh: i32,
598 ) -> Fallible<DomRoot<ImageData>> {
599 self.canvas_state
600 .create_image_data(cx, &self.global(), sw, sh)
601 }
602
603 fn CreateImageData_(
605 &self,
606 cx: &mut JSContext,
607 imagedata: &ImageData,
608 ) -> Fallible<DomRoot<ImageData>> {
609 self.canvas_state
610 .create_image_data_(cx, &self.global(), imagedata)
611 }
612
613 fn GetImageData(
615 &self,
616 cx: &mut JSContext,
617 sx: i32,
618 sy: i32,
619 sw: i32,
620 sh: i32,
621 ) -> Fallible<DomRoot<ImageData>> {
622 self.canvas_state
623 .get_image_data(cx, self.canvas.size(), &self.global(), sx, sy, sw, sh)
624 }
625
626 fn PutImageData(&self, no_gc: &NoGC, imagedata: &ImageData, dx: i32, dy: i32) {
628 self.canvas_state
629 .put_image_data(no_gc, self.canvas.size(), imagedata, dx, dy);
630 self.mark_as_dirty();
631 }
632
633 fn PutImageData_(
635 &self,
636 no_gc: &NoGC,
637 imagedata: &ImageData,
638 dx: i32,
639 dy: i32,
640 dirty_x: i32,
641 dirty_y: i32,
642 dirty_width: i32,
643 dirty_height: i32,
644 ) {
645 self.canvas_state.put_image_data_(
646 no_gc,
647 self.canvas.size(),
648 imagedata,
649 dx,
650 dy,
651 dirty_x,
652 dirty_y,
653 dirty_width,
654 dirty_height,
655 );
656 self.mark_as_dirty();
657 }
658
659 fn CreateLinearGradient(
661 &self,
662 cx: &mut JSContext,
663 x0: Finite<f64>,
664 y0: Finite<f64>,
665 x1: Finite<f64>,
666 y1: Finite<f64>,
667 ) -> DomRoot<CanvasGradient> {
668 self.canvas_state
669 .create_linear_gradient(&self.global(), cx, x0, y0, x1, y1)
670 }
671
672 fn CreateRadialGradient(
674 &self,
675 cx: &mut JSContext,
676 x0: Finite<f64>,
677 y0: Finite<f64>,
678 r0: Finite<f64>,
679 x1: Finite<f64>,
680 y1: Finite<f64>,
681 r1: Finite<f64>,
682 ) -> Fallible<DomRoot<CanvasGradient>> {
683 self.canvas_state
684 .create_radial_gradient(&self.global(), cx, x0, y0, r0, x1, y1, r1)
685 }
686
687 fn CreatePattern(
689 &self,
690 cx: &mut JSContext,
691 image: CanvasImageSource,
692 repetition: DOMString,
693 ) -> Fallible<Option<DomRoot<CanvasPattern>>> {
694 self.canvas_state
695 .create_pattern(&self.global(), cx, image, repetition)
696 }
697
698 fn LineWidth(&self) -> f64 {
700 self.canvas_state.line_width()
701 }
702
703 fn SetLineWidth(&self, width: f64) {
705 self.canvas_state.set_line_width(width)
706 }
707
708 fn LineCap(&self) -> CanvasLineCap {
710 self.canvas_state.line_cap()
711 }
712
713 fn SetLineCap(&self, cap: CanvasLineCap) {
715 self.canvas_state.set_line_cap(cap)
716 }
717
718 fn LineJoin(&self) -> CanvasLineJoin {
720 self.canvas_state.line_join()
721 }
722
723 fn SetLineJoin(&self, join: CanvasLineJoin) {
725 self.canvas_state.set_line_join(join)
726 }
727
728 fn MiterLimit(&self) -> f64 {
730 self.canvas_state.miter_limit()
731 }
732
733 fn SetMiterLimit(&self, limit: f64) {
735 self.canvas_state.set_miter_limit(limit)
736 }
737
738 fn SetLineDash(&self, segments: Vec<f64>) {
740 self.canvas_state.set_line_dash(segments);
741 }
742
743 fn GetLineDash(&self) -> Vec<f64> {
745 self.canvas_state.line_dash()
746 }
747
748 fn LineDashOffset(&self) -> f64 {
750 self.canvas_state.line_dash_offset()
751 }
752
753 fn SetLineDashOffset(&self, offset: f64) {
755 self.canvas_state.set_line_dash_offset(offset);
756 }
757
758 fn ShadowOffsetX(&self) -> f64 {
760 self.canvas_state.shadow_offset_x()
761 }
762
763 fn SetShadowOffsetX(&self, value: f64) {
765 self.canvas_state.set_shadow_offset_x(value)
766 }
767
768 fn ShadowOffsetY(&self) -> f64 {
770 self.canvas_state.shadow_offset_y()
771 }
772
773 fn SetShadowOffsetY(&self, value: f64) {
775 self.canvas_state.set_shadow_offset_y(value)
776 }
777
778 fn ShadowBlur(&self) -> f64 {
780 self.canvas_state.shadow_blur()
781 }
782
783 fn SetShadowBlur(&self, value: f64) {
785 self.canvas_state.set_shadow_blur(value)
786 }
787
788 fn ShadowColor(&self) -> DOMString {
790 self.canvas_state.shadow_color()
791 }
792
793 fn SetShadowColor(&self, value: DOMString) {
795 self.canvas_state
796 .set_shadow_color(self.canvas.canvas().as_deref(), value)
797 }
798}