1use std::borrow::Cow;
6use std::vec::Vec;
7
8use dom_struct::dom_struct;
9use euclid::default::{Rect, Size2D};
10use js::context::{JSContext, NoGC};
11use js::gc::CustomAutoRooterGuard;
12use js::jsapi::JSObject;
13use js::rust::HandleObject;
14use js::typedarray::{ClampedU8, HeapUint8ClampedArray, TypedArray, Uint8ClampedArray};
15use pixels::{Snapshot, SnapshotAlphaMode, SnapshotPixelFormat};
16use rustc_hash::FxHashMap;
17use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
18use script_bindings::trace::RootedTraceableBox;
19#[cfg(feature = "webgl")]
20use servo_base::generic_channel::GenericSharedMemory;
21use servo_base::id::{ImageDataId, ImageDataIndex};
22use servo_constellation_traits::SerializableImageData;
23
24use crate::dom::bindings::buffer_source::{
25 HeapBufferSource, create_buffer_source, create_heap_buffer_source_with_length,
26};
27use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::{
28 ImageDataMethods, ImageDataPixelFormat, ImageDataSettings, PredefinedColorSpace,
29};
30use crate::dom::bindings::error::{Error, Fallible};
31use crate::dom::bindings::root::DomRoot;
32use crate::dom::bindings::serializable::Serializable;
33use crate::dom::bindings::structuredclone::StructuredData;
34use crate::dom::globalscope::GlobalScope;
35
36#[dom_struct]
37pub(crate) struct ImageData {
38 reflector_: Reflector,
39 width: u32,
40 height: u32,
41 #[ignore_malloc_size_of = "mozjs"]
43 data: HeapBufferSource<ClampedU8>,
44 pixel_format: ImageDataPixelFormat,
45 color_space: PredefinedColorSpace,
46}
47
48impl ImageData {
49 pub(crate) fn new(
50 cx: &mut JSContext,
51 global: &GlobalScope,
52 width: u32,
53 height: u32,
54 mut data: Option<Vec<u8>>,
55 ) -> Fallible<DomRoot<ImageData>> {
56 let len =
57 pixels::compute_rgba8_byte_length_if_within_limit(width as usize, height as usize)
58 .ok_or(Error::Range(
59 c"The requested image size exceeds the supported range".to_owned(),
60 ))?;
61
62 let settings = ImageDataSettings {
63 colorSpace: Some(PredefinedColorSpace::Srgb),
64 pixelFormat: ImageDataPixelFormat::Rgba_unorm8,
65 };
66
67 if let Some(ref mut d) = data {
68 d.resize(len as usize, 0);
69
70 rooted!(&in(cx) let mut js_object = std::ptr::null_mut::<JSObject>());
71 let _buffer_source =
72 create_buffer_source::<ClampedU8>(cx, &d[..], js_object.handle_mut())
73 .map_err(|_| Error::JSFailed)?;
74 auto_root!(&in(cx) let data = TypedArray::<ClampedU8, *mut JSObject>::from(js_object.get()).map_err(|_| Error::JSFailed)?);
75
76 Self::Constructor_(cx, global, None, data, width, Some(height), &settings)
77 } else {
78 Self::Constructor(cx, global, None, width, height, &settings)
79 }
80 }
81
82 #[allow(clippy::too_many_arguments)]
83 fn initialize(
85 cx: &mut JSContext,
86 pixels_per_row: u32,
87 rows: u32,
88 settings: &ImageDataSettings,
89 source: Option<CustomAutoRooterGuard<Uint8ClampedArray>>,
90 default_color_space: Option<PredefinedColorSpace>,
91 global: &GlobalScope,
92 proto: Option<HandleObject>,
93 ) -> Fallible<DomRoot<ImageData>> {
94 let data = if let Some(source) = source {
96 if !matches!(settings.pixelFormat, ImageDataPixelFormat::Rgba_unorm8) {
101 return Err(Error::InvalidState(None));
103 }
104 HeapBufferSource::<ClampedU8>::from_view(cx, source)
106 } else {
107 match settings.pixelFormat {
109 ImageDataPixelFormat::Rgba_unorm8 => {
110 create_heap_buffer_source_with_length(cx, 4 * rows * pixels_per_row)?
118 },
119 }
126 };
127 let width = pixels_per_row;
129 let height = rows;
131 let pixel_format = settings.pixelFormat;
133 let color_space = settings
136 .colorSpace
137 .or(default_color_space)
140 .unwrap_or(PredefinedColorSpace::Srgb);
142
143 Ok(reflect_dom_object_with_proto(
144 cx,
145 Box::new(ImageData {
146 reflector_: Reflector::new(),
147 width,
148 height,
149 data: *data.into_box(),
150 pixel_format,
151 color_space,
152 }),
153 global,
154 proto,
155 ))
156 }
157
158 pub(crate) fn is_detached(&self, cx: &mut JSContext) -> bool {
159 self.data.is_detached_buffer(cx)
160 }
161
162 pub(crate) fn get_size(&self) -> Size2D<u32> {
163 Size2D::new(self.Width(), self.Height())
164 }
165
166 pub(crate) fn as_slice<'a>(&'a self, no_gc: &'a NoGC) -> &'a [u8] {
168 assert!(self.data.is_initialized());
169 let internal_data = self
170 .data
171 .get_typed_array()
172 .expect("Failed to get Data from ImageData.");
173 internal_data.as_slice_safe(no_gc).unwrap_or(&[])
174 }
175
176 pub(crate) fn get_rect<'a>(&'a self, no_gc: &'a NoGC, rect: Rect<u32>) -> Cow<'a, [u8]> {
178 pixels::rgba8_get_rect(self.as_slice(no_gc), self.get_size().to_u32(), rect)
179 }
180
181 pub(crate) fn get_snapshot_rect(&self, no_gc: &NoGC, rect: Rect<u32>) -> Snapshot {
182 Snapshot::from_vec(
183 rect.size,
184 SnapshotPixelFormat::RGBA,
185 SnapshotAlphaMode::Transparent {
186 premultiplied: false,
187 },
188 self.get_rect(no_gc, rect).into_owned(),
189 )
190 }
191
192 pub(crate) fn get_snapshot(&self, no_gc: &NoGC) -> Snapshot {
193 Snapshot::from_vec(
194 self.get_size(),
195 SnapshotPixelFormat::RGBA,
196 SnapshotAlphaMode::Transparent {
197 premultiplied: false,
198 },
199 self.as_slice(no_gc).to_vec(),
200 )
201 }
202
203 #[cfg(feature = "webgl")]
204 pub(crate) fn to_shared_memory(&self, no_gc: &NoGC) -> GenericSharedMemory {
205 GenericSharedMemory::from_bytes(self.as_slice(no_gc))
207 }
208
209 pub(crate) fn to_vec(&self, no_gc: &NoGC) -> Vec<u8> {
210 self.as_slice(no_gc).to_vec()
212 }
213}
214
215impl Serializable for ImageData {
216 type Index = ImageDataIndex;
217 type Data = SerializableImageData;
218
219 fn serialize(&self, no_gc: &NoGC) -> Result<(ImageDataId, Self::Data), ()> {
221 let data = self.to_vec(no_gc);
223
224 let serialized = SerializableImageData {
231 data,
232 width: self.width,
233 height: self.height,
234 };
235 Ok((ImageDataId::new(), serialized))
236 }
237
238 fn deserialize(
240 cx: &mut JSContext,
241 owner: &GlobalScope,
242 serialized: Self::Data,
243 ) -> Result<DomRoot<Self>, ()> {
244 ImageData::new(
250 cx,
251 owner,
252 serialized.width,
253 serialized.height,
254 Some(serialized.data),
255 )
256 .map_err(|_| ())
257 }
258
259 fn serialized_storage<'a>(
260 reader: StructuredData<'a, '_>,
261 ) -> &'a mut Option<FxHashMap<ImageDataId, Self::Data>> {
262 match reader {
263 StructuredData::Reader(r) => &mut r.image_data,
264 StructuredData::Writer(w) => &mut w.image_data,
265 }
266 }
267}
268
269impl ImageDataMethods<crate::DomTypeHolder> for ImageData {
270 fn Constructor(
272 cx: &mut JSContext,
273 global: &GlobalScope,
274 proto: Option<HandleObject>,
275 sw: u32,
276 sh: u32,
277 settings: &ImageDataSettings,
278 ) -> Fallible<DomRoot<Self>> {
279 if sw == 0 || sh == 0 {
281 return Err(Error::IndexSize(None));
282 }
283
284 pixels::compute_rgba8_byte_length_if_within_limit(sw as usize, sh as usize)
287 .ok_or(Error::IndexSize(None))?;
288
289 Self::initialize(cx, sw, sh, settings, None, None, global, proto)
292 }
293
294 fn Constructor_(
296 cx: &mut JSContext,
297 global: &GlobalScope,
298 proto: Option<HandleObject>,
299 data: CustomAutoRooterGuard<Uint8ClampedArray>,
300 sw: u32,
301 sh: Option<u32>,
302 settings: &ImageDataSettings,
303 ) -> Fallible<DomRoot<Self>> {
304 let bytes_per_pixel = match settings.pixelFormat {
306 ImageDataPixelFormat::Rgba_unorm8 => 4,
307 };
308 let length = data.len();
310 if length == 0 {
311 return Err(Error::InvalidState(None));
312 }
313 if !length.is_multiple_of(bytes_per_pixel) {
316 return Err(Error::InvalidState(None));
317 }
318 let length = length / bytes_per_pixel;
320 if sw == 0 || !length.is_multiple_of(sw as usize) {
322 return Err(Error::IndexSize(None));
323 }
324 let height = length / sw as usize;
326 if sh.is_some_and(|x| height != x as usize) {
328 return Err(Error::IndexSize(None));
329 }
330 Self::initialize(
332 cx,
333 sw,
334 height as u32,
335 settings,
336 Some(data),
337 None,
338 global,
339 proto,
340 )
341 }
342
343 fn Width(&self) -> u32 {
345 self.width
346 }
347
348 fn Height(&self) -> u32 {
350 self.height
351 }
352
353 fn GetData(&self) -> Fallible<RootedTraceableBox<HeapUint8ClampedArray>> {
355 self.data.get_typed_array().map_err(|_| Error::JSFailed)
356 }
357
358 fn PixelFormat(&self) -> ImageDataPixelFormat {
360 self.pixel_format
361 }
362
363 fn ColorSpace(&self) -> PredefinedColorSpace {
365 self.color_space
366 }
367}