1use std::borrow::Cow;
6use std::vec::Vec;
7
8use dom_struct::dom_struct;
9use euclid::default::{Rect, Size2D};
10use ipc_channel::ipc::IpcSharedMemory;
11use js::gc::CustomAutoRooterGuard;
12use js::jsapi::JSObject;
13use js::rust::HandleObject;
14use js::typedarray::{ClampedU8, Uint8ClampedArray};
15use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat};
16
17use crate::dom::bindings::buffer_source::{
18 HeapBufferSource, create_buffer_source, create_heap_buffer_source_with_length,
19};
20use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{
21 ImageDataMethods, ImageDataPixelFormat, ImageDataSettings, PredefinedColorSpace,
22};
23use crate::dom::bindings::error::{Error, Fallible};
24use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
25use crate::dom::bindings::root::DomRoot;
26use crate::dom::globalscope::GlobalScope;
27use crate::script_runtime::{CanGc, JSContext};
28
29#[dom_struct]
30pub(crate) struct ImageData {
31 reflector_: Reflector,
32 width: u32,
33 height: u32,
34 #[ignore_malloc_size_of = "mozjs"]
36 data: HeapBufferSource<ClampedU8>,
37 pixel_format: ImageDataPixelFormat,
38 color_space: PredefinedColorSpace,
39}
40
41impl ImageData {
42 #[allow(unsafe_code)]
43 pub(crate) fn new(
44 global: &GlobalScope,
45 width: u32,
46 height: u32,
47 mut data: Option<Vec<u8>>,
48 can_gc: CanGc,
49 ) -> Fallible<DomRoot<ImageData>> {
50 let len =
51 pixels::compute_rgba8_byte_length_if_within_limit(width as usize, height as usize)
52 .ok_or(Error::Range(
53 "The requested image size exceeds the supported range".to_owned(),
54 ))?;
55
56 let settings = ImageDataSettings {
57 colorSpace: Some(PredefinedColorSpace::Srgb),
58 pixelFormat: ImageDataPixelFormat::Rgba_unorm8,
59 };
60
61 if let Some(ref mut d) = data {
62 d.resize(len as usize, 0);
63
64 let cx = GlobalScope::get_cx();
65 rooted!(in (*cx) let mut js_object = std::ptr::null_mut::<JSObject>());
66 auto_root!(in(*cx) let data = create_buffer_source::<ClampedU8>(cx, &d[..], js_object.handle_mut(), can_gc)
67 .map_err(|_| Error::JSFailed)?);
68
69 Self::Constructor_(global, None, can_gc, data, width, Some(height), &settings)
70 } else {
71 Self::Constructor(global, None, can_gc, width, height, &settings)
72 }
73 }
74
75 #[allow(clippy::too_many_arguments)]
76 fn initialize(
78 pixels_per_row: u32,
79 rows: u32,
80 settings: &ImageDataSettings,
81 source: Option<CustomAutoRooterGuard<Uint8ClampedArray>>,
82 default_color_space: Option<PredefinedColorSpace>,
83 global: &GlobalScope,
84 proto: Option<HandleObject>,
85 can_gc: CanGc,
86 ) -> Fallible<DomRoot<ImageData>> {
87 let data = if let Some(source) = source {
89 if !matches!(settings.pixelFormat, ImageDataPixelFormat::Rgba_unorm8) {
94 return Err(Error::InvalidState);
96 }
97 HeapBufferSource::<ClampedU8>::from_view(source)
99 } else {
100 match settings.pixelFormat {
102 ImageDataPixelFormat::Rgba_unorm8 => {
103 create_heap_buffer_source_with_length(
111 GlobalScope::get_cx(),
112 4 * rows * pixels_per_row,
113 can_gc,
114 )?
115 },
116 }
123 };
124 let width = pixels_per_row;
126 let height = rows;
128 let pixel_format = settings.pixelFormat;
130 let color_space = settings
133 .colorSpace
134 .or(default_color_space)
137 .unwrap_or(PredefinedColorSpace::Srgb);
139
140 Ok(reflect_dom_object_with_proto(
141 Box::new(ImageData {
142 reflector_: Reflector::new(),
143 width,
144 height,
145 data,
146 pixel_format,
147 color_space,
148 }),
149 global,
150 proto,
151 can_gc,
152 ))
153 }
154
155 pub(crate) fn is_detached(&self) -> bool {
156 self.data.is_detached_buffer(GlobalScope::get_cx())
157 }
158
159 pub(crate) fn get_size(&self) -> Size2D<u32> {
160 Size2D::new(self.Width(), self.Height())
161 }
162
163 #[allow(unsafe_code)]
165 pub(crate) unsafe fn as_slice(&self) -> &[u8] {
166 assert!(self.data.is_initialized());
167 let internal_data = self
168 .data
169 .get_typed_array()
170 .expect("Failed to get Data from ImageData.");
171 unsafe {
177 let ptr: *const [u8] = internal_data.as_slice() as *const _;
178 &*ptr
179 }
180 }
181
182 #[allow(unsafe_code)]
184 pub(crate) unsafe fn get_rect(&self, rect: Rect<u32>) -> Cow<'_, [u8]> {
185 pixels::rgba8_get_rect(unsafe { self.as_slice() }, self.get_size().to_u32(), rect)
186 }
187
188 #[allow(unsafe_code)]
189 pub(crate) fn get_snapshot_rect(&self, rect: Rect<u32>) -> Snapshot {
190 Snapshot::from_vec(
191 rect.size,
192 SnapshotPixelFormat::RGBA,
193 SnapshotAlphaMode::Transparent {
194 premultiplied: false,
195 },
196 unsafe { self.get_rect(rect).into_owned() },
197 )
198 }
199
200 #[allow(unsafe_code)]
201 pub(crate) fn to_shared_memory(&self) -> IpcSharedMemory {
202 IpcSharedMemory::from_bytes(unsafe { self.as_slice() })
204 }
205
206 #[allow(unsafe_code)]
207 pub(crate) fn to_vec(&self) -> Vec<u8> {
208 unsafe { self.as_slice() }.to_vec()
210 }
211}
212
213impl ImageDataMethods<crate::DomTypeHolder> for ImageData {
214 fn Constructor(
216 global: &GlobalScope,
217 proto: Option<HandleObject>,
218 can_gc: CanGc,
219 sw: u32,
220 sh: u32,
221 settings: &ImageDataSettings,
222 ) -> Fallible<DomRoot<Self>> {
223 if sw == 0 || sh == 0 {
225 return Err(Error::IndexSize);
226 }
227
228 pixels::compute_rgba8_byte_length_if_within_limit(sw as usize, sh as usize)
231 .ok_or(Error::IndexSize)?;
232
233 Self::initialize(sw, sh, settings, None, None, global, proto, can_gc)
236 }
237
238 fn Constructor_(
240 global: &GlobalScope,
241 proto: Option<HandleObject>,
242 can_gc: CanGc,
243 data: CustomAutoRooterGuard<Uint8ClampedArray>,
244 sw: u32,
245 sh: Option<u32>,
246 settings: &ImageDataSettings,
247 ) -> Fallible<DomRoot<Self>> {
248 let bytes_per_pixel = match settings.pixelFormat {
250 ImageDataPixelFormat::Rgba_unorm8 => 4,
251 };
252 let length = data.len();
254 if length == 0 {
255 return Err(Error::InvalidState);
256 }
257 if length % bytes_per_pixel != 0 {
260 return Err(Error::InvalidState);
261 }
262 let length = length / bytes_per_pixel;
264 if sw == 0 || length % sw as usize != 0 {
266 return Err(Error::IndexSize);
267 }
268 let height = length / sw as usize;
270 if sh.is_some_and(|x| height != x as usize) {
272 return Err(Error::IndexSize);
273 }
274 Self::initialize(
276 sw,
277 height as u32,
278 settings,
279 Some(data),
280 None,
281 global,
282 proto,
283 can_gc,
284 )
285 }
286
287 fn Width(&self) -> u32 {
289 self.width
290 }
291
292 fn Height(&self) -> u32 {
294 self.height
295 }
296
297 fn GetData(&self, _: JSContext) -> Fallible<Uint8ClampedArray> {
299 self.data.get_typed_array().map_err(|_| Error::JSFailed)
300 }
301
302 fn PixelFormat(&self) -> ImageDataPixelFormat {
304 self.pixel_format
305 }
306
307 fn ColorSpace(&self) -> PredefinedColorSpace {
309 self.color_space
310 }
311}