script/dom/canvas/
imagebitmaprenderingcontext.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use euclid::default::Size2D;
9use pixels::Snapshot;
10
11use crate::canvas_context::{CanvasContext, CanvasHelpers, HTMLCanvasElementOrOffscreenCanvas};
12use crate::dom::bindings::cell::DomRefCell;
13use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::ImageBitmapMethods;
14use crate::dom::bindings::codegen::Bindings::ImageBitmapRenderingContextBinding::ImageBitmapRenderingContextMethods;
15use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas;
16use crate::dom::bindings::error::{Error, Fallible};
17use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
18use crate::dom::bindings::root::DomRoot;
19use crate::dom::globalscope::GlobalScope;
20use crate::dom::imagebitmap::ImageBitmap;
21use crate::script_runtime::CanGc;
22
23/// <https://html.spec.whatwg.org/multipage/#imagebitmaprenderingcontext>
24#[dom_struct]
25pub(crate) struct ImageBitmapRenderingContext {
26    reflector_: Reflector,
27    /// <https://html.spec.whatwg.org/multipage/#dom-imagebitmaprenderingcontext-canvas>
28    canvas: HTMLCanvasElementOrOffscreenCanvas,
29    /// Represents both the [output bitmap] and the [bitmap mode] of the context.
30    /// <https://html.spec.whatwg.org/multipage/#concept-imagebitmaprenderingcontext-output-bitmap>
31    /// <https://html.spec.whatwg.org/multipage/#concept-imagebitmaprenderingcontext-bitmap-mode>
32    #[no_trace]
33    bitmap: DomRefCell<Option<Snapshot>>,
34    origin_clean: Cell<bool>,
35}
36
37impl ImageBitmapRenderingContext {
38    /// <https://html.spec.whatwg.org/multipage/#imagebitmaprenderingcontext-creation-algorithm>
39    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
40    fn new_inherited(canvas: HTMLCanvasElementOrOffscreenCanvas) -> ImageBitmapRenderingContext {
41        ImageBitmapRenderingContext {
42            reflector_: Reflector::new(),
43            canvas,
44            bitmap: DomRefCell::new(None),
45            origin_clean: Cell::new(true),
46        }
47    }
48
49    pub(crate) fn new(
50        global: &GlobalScope,
51        canvas: &RootedHTMLCanvasElementOrOffscreenCanvas,
52        can_gc: CanGc,
53    ) -> DomRoot<ImageBitmapRenderingContext> {
54        reflect_dom_object(
55            Box::new(ImageBitmapRenderingContext::new_inherited(
56                HTMLCanvasElementOrOffscreenCanvas::from(canvas),
57            )),
58            global,
59            can_gc,
60        )
61    }
62
63    /// <https://html.spec.whatwg.org/multipage/#set-an-imagebitmaprenderingcontext's-output-bitmap>
64    fn set_bitmap(&self, image_bitmap: Option<&ImageBitmap>) {
65        match image_bitmap {
66            Some(image_bitmap) => {
67                // Step 2.1. Set context's bitmap mode to valid.
68                // Step 2.2. Set context's output bitmap to refer to the same
69                // underlying bitmap data as bitmap, without making a copy.
70                *self.bitmap.borrow_mut() = image_bitmap.bitmap_data().clone();
71
72                // The origin-clean flag of bitmap is included in the bitmap
73                // data to be referenced by context's output bitmap.
74                self.origin_clean.set(image_bitmap.origin_is_clean());
75            },
76            None => {
77                // Step 1.1. Set context's bitmap mode to blank.
78                // Step 1.2. Let canvas be the canvas element to which context is bound.
79                // Step 1.3. Set context's output bitmap to be transparent black
80                // with a natural width equal to the numeric value of canvas's
81                // width attribute and a natural height equal to the numeric
82                // value of canvas's height attribute, those values being
83                // interpreted in CSS pixels.
84                *self.bitmap.borrow_mut() = None;
85
86                // Step 1.4. Set the output bitmap's origin-clean flag to true.
87                self.origin_clean.set(true);
88            },
89        }
90    }
91}
92
93impl CanvasContext for ImageBitmapRenderingContext {
94    type ID = ();
95
96    fn context_id(&self) -> Self::ID {}
97
98    fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
99        Some(RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas))
100    }
101
102    /// <https://html.spec.whatwg.org/multipage/#the-canvas-element:concept-canvas-bitmaprenderer>
103    fn resize(&self) {
104        // The absence of the bitmap is the context's blank bitmap mode so the
105        // steps to set output bitmap could be omitted.
106    }
107
108    fn reset_bitmap(&self) {
109        // The newly created bitmap should be of the same dimensions as the
110        // previous bitmap if the context's bitmap mode is valid.
111        if self.bitmap.borrow().is_none() {
112            return;
113        }
114
115        let size = self.bitmap.borrow().as_ref().unwrap().size();
116        *self.bitmap.borrow_mut() = Some(Snapshot::cleared(size));
117    }
118
119    fn get_image_data(&self) -> Option<Snapshot> {
120        match self.bitmap.borrow().as_ref() {
121            Some(bitmap) => Some(bitmap.clone()),
122            None => {
123                let size = self.canvas.size();
124                if size.is_empty() ||
125                    pixels::compute_rgba8_byte_length_if_within_limit(
126                        size.width as usize,
127                        size.height as usize,
128                    )
129                    .is_none()
130                {
131                    None
132                } else {
133                    Some(Snapshot::cleared(size))
134                }
135            },
136        }
137    }
138
139    fn origin_is_clean(&self) -> bool {
140        self.origin_clean.get()
141    }
142
143    fn size(&self) -> Size2D<u32> {
144        self.bitmap
145            .borrow()
146            .as_ref()
147            .map_or_else(|| self.canvas.size(), |bitmap| bitmap.size())
148    }
149
150    fn mark_as_dirty(&self) {}
151}
152
153impl ImageBitmapRenderingContextMethods<crate::DomTypeHolder> for ImageBitmapRenderingContext {
154    /// <https://html.spec.whatwg.org/multipage/#dom-imagebitmaprenderingcontext-canvas>
155    fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas {
156        RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas)
157    }
158
159    /// <https://html.spec.whatwg.org/multipage/#dom-imagebitmaprenderingcontext-transferfromimagebitmap>
160    fn TransferFromImageBitmap(&self, image_bitmap: Option<&ImageBitmap>) -> Fallible<()> {
161        let Some(image_bitmap) = image_bitmap else {
162            // Step 2. If bitmap is null, then run the steps to set an
163            // ImageBitmapRenderingContext's output bitmap, with
164            // bitmapContext as the context argument and no bitmap argument,
165            // then return.
166            self.set_bitmap(None);
167
168            return Ok(());
169        };
170
171        // Step 3. If the value of bitmap's [[Detached]] internal slot
172        // is set to true, then throw an "InvalidStateError"
173        // DOMException.
174        if image_bitmap.is_detached() {
175            return Err(Error::InvalidState(None));
176        }
177
178        // Step 4. Run the steps to set an ImageBitmapRenderingContext's
179        // output bitmap, with the context argument equal to
180        // bitmapContext, and the bitmap argument referring to bitmap's
181        // underlying bitmap data.
182        self.set_bitmap(Some(image_bitmap));
183
184        // Step 5. Set the value of bitmap's [[Detached]] internal slot
185        // to true.
186        // Step 6. Unset bitmap's bitmap data.
187        image_bitmap.Close();
188
189        Ok(())
190    }
191}