script/dom/canvas/
imagebitmaprenderingcontext.rs1use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use euclid::default::Size2D;
9use paint_api::SerializableImageData;
10use pixels::Snapshot;
11use servo_base::Epoch;
12use webrender_api::units::DeviceIntSize;
13use webrender_api::{ImageDescriptor, ImageFormat, ImageKey};
14
15use crate::canvas_context::{CanvasContext, CanvasHelpers, HTMLCanvasElementOrOffscreenCanvas};
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;
23use crate::dom::globalscope::GlobalScope;
24use crate::dom::html::htmlcanvaselement::HTMLCanvasElement;
25use crate::dom::imagebitmap::ImageBitmap;
26use crate::dom::node::node::NodeTraits;
27use crate::script_runtime::CanGc;
28
29#[dom_struct]
31pub(crate) struct ImageBitmapRenderingContext {
32 reflector_: Reflector,
33 canvas: HTMLCanvasElementOrOffscreenCanvas,
35 #[no_trace]
39 bitmap: DomRefCell<Option<Snapshot>>,
40 origin_clean: Cell<bool>,
41 #[no_trace]
42 image_key: Cell<Option<ImageKey>>,
43 image_added: Cell<bool>,
46}
47
48impl ImageBitmapRenderingContext {
49 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
51 fn new_inherited(canvas: HTMLCanvasElementOrOffscreenCanvas) -> ImageBitmapRenderingContext {
52 ImageBitmapRenderingContext {
53 reflector_: Reflector::new(),
54 canvas,
55 bitmap: DomRefCell::new(None),
56 origin_clean: Cell::new(true),
57 image_key: Cell::new(None),
58 image_added: Cell::new(false),
59 }
60 }
61
62 pub(crate) fn new(
63 global: &GlobalScope,
64 canvas: &RootedHTMLCanvasElementOrOffscreenCanvas,
65 can_gc: CanGc,
66 ) -> DomRoot<ImageBitmapRenderingContext> {
67 reflect_dom_object(
68 Box::new(ImageBitmapRenderingContext::new_inherited(
69 HTMLCanvasElementOrOffscreenCanvas::from(canvas),
70 )),
71 global,
72 can_gc,
73 )
74 }
75
76 pub(crate) fn set_image_key(&self, image_key: ImageKey) {
77 self.image_key.set(Some(image_key));
78 }
79
80 pub(crate) fn update_rendering(&self, epoch: Epoch) -> bool {
81 let Some(image_key) = self.image_key.get() else {
82 return false;
83 };
84
85 let Some(snapshot) = self.bitmap.borrow().as_ref().cloned() else {
86 return false;
87 };
88
89 let size = snapshot.size();
90 let format = match snapshot.format() {
91 pixels::SnapshotPixelFormat::RGBA => ImageFormat::RGBA8,
92 pixels::SnapshotPixelFormat::BGRA => ImageFormat::BGRA8,
93 };
94 let shared = snapshot.to_shared();
95 let descriptor = ImageDescriptor {
96 format,
97 size: DeviceIntSize::new(size.width as i32, size.height as i32),
98 stride: None,
99 offset: 0,
100 flags: webrender_api::ImageDescriptorFlags::empty(),
101 };
102 let data = SerializableImageData::Raw(shared.shared_memory());
103
104 if let HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(ref canvas) = self.canvas {
106 let canvas: &HTMLCanvasElement = canvas;
107 let doc = canvas.owner_document();
108 let paint_api = doc.window().paint_api();
109 if self.image_added.get() {
110 paint_api.update_image(image_key, descriptor, data, Some(epoch));
111 } else {
112 paint_api.add_image(image_key, descriptor, data, false);
113 self.image_added.set(true);
114 }
115 }
116 false
117 }
118
119 fn set_bitmap(&self, image_bitmap: Option<&ImageBitmap>) {
121 match image_bitmap {
122 Some(image_bitmap) => {
123 *self.bitmap.borrow_mut() = image_bitmap.bitmap_data().clone();
127
128 self.origin_clean.set(image_bitmap.origin_is_clean());
131 },
132 None => {
133 *self.bitmap.borrow_mut() = None;
141
142 self.origin_clean.set(true);
144 },
145 }
146 }
147}
148
149impl CanvasContext for ImageBitmapRenderingContext {
150 type ID = ();
151
152 fn context_id(&self) -> Self::ID {}
153
154 fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
155 Some(RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas))
156 }
157
158 fn resize(&self) {
160 }
163
164 fn reset_bitmap(&self) {
165 if self.bitmap.borrow().is_none() {
168 return;
169 }
170
171 let size = self.bitmap.borrow().as_ref().unwrap().size();
172 *self.bitmap.borrow_mut() = Some(Snapshot::cleared(size));
173 }
174
175 fn get_image_data(&self) -> Option<Snapshot> {
176 match self.bitmap.borrow().as_ref() {
177 Some(bitmap) => Some(bitmap.clone()),
178 None => {
179 let size = self.canvas.size();
180 if size.is_empty() ||
181 pixels::compute_rgba8_byte_length_if_within_limit(
182 size.width as usize,
183 size.height as usize,
184 )
185 .is_none()
186 {
187 None
188 } else {
189 Some(Snapshot::cleared(size))
190 }
191 },
192 }
193 }
194
195 fn origin_is_clean(&self) -> bool {
196 self.origin_clean.get()
197 }
198
199 fn size(&self) -> Size2D<u32> {
200 self.bitmap
201 .borrow()
202 .as_ref()
203 .map_or_else(|| self.canvas.size(), |bitmap| bitmap.size())
204 }
205
206 fn mark_as_dirty(&self) {
207 self.canvas.mark_as_dirty();
208 }
209}
210
211impl ImageBitmapRenderingContextMethods<crate::DomTypeHolder> for ImageBitmapRenderingContext {
212 fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas {
214 RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas)
215 }
216
217 fn TransferFromImageBitmap(&self, image_bitmap: Option<&ImageBitmap>) -> Fallible<()> {
219 let Some(image_bitmap) = image_bitmap else {
220 self.set_bitmap(None);
225 self.mark_as_dirty();
226
227 return Ok(());
228 };
229
230 if image_bitmap.is_detached() {
234 return Err(Error::InvalidState(None));
235 }
236
237 self.set_bitmap(Some(image_bitmap));
242 self.mark_as_dirty();
243
244 image_bitmap.Close();
248
249 Ok(())
250 }
251}