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