1mod snapshot;
6
7use std::borrow::Cow;
8use std::io::Cursor;
9use std::ops::Range;
10use std::sync::Arc;
11use std::time::Duration;
12use std::{cmp, fmt, vec};
13
14use euclid::default::{Point2D, Rect, Size2D};
15use image::codecs::{bmp, gif, ico, jpeg, png, webp};
16use image::error::ImageFormatHint;
17use image::imageops::{self, FilterType};
18use image::{
19 AnimationDecoder, DynamicImage, ImageBuffer, ImageDecoder, ImageError, ImageFormat,
20 ImageResult, Limits, Rgba,
21};
22use log::{debug, error};
23use malloc_size_of_derive::MallocSizeOf;
24use serde::{Deserialize, Serialize};
25use servo_base::generic_channel::GenericSharedMemory;
26pub use snapshot::*;
27use webrender_api::units::DeviceIntSize;
28use webrender_api::{
29 ImageDescriptor, ImageDescriptorFlags, ImageFormat as WebRenderImageFormat, ImageKey,
30};
31
32#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
33pub enum FilterQuality {
34 None,
36 Low,
38 Medium,
40 High,
42}
43
44#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
45pub enum PixelFormat {
46 K8,
48 KA8,
50 RGB8,
52 RGBA8,
54 BGRA8,
56}
57
58pub fn compute_rgba8_byte_length_if_within_limit(width: usize, height: usize) -> Option<usize> {
61 const MAX_IMAGE_BYTE_LENGTH: usize = 2147483647;
63
64 4usize
67 .checked_mul(width)
68 .and_then(|v| v.checked_mul(height))
69 .filter(|v| *v <= MAX_IMAGE_BYTE_LENGTH)
70}
71
72pub fn copy_rgba8_image(
74 src_size: Size2D<u32>,
75 src_rect: Rect<u32>,
76 src_pixels: &[u8],
77 dest_size: Size2D<u32>,
78 dest_rect: Rect<u32>,
79 dest_pixels: &mut [u8],
80) {
81 assert!(!src_rect.is_empty());
82 assert!(!dest_rect.is_empty());
83 assert!(Rect::from_size(src_size).contains_rect(&src_rect));
84 assert!(Rect::from_size(dest_size).contains_rect(&dest_rect));
85 assert!(src_rect.size == dest_rect.size);
86 assert_eq!(src_pixels.len() % 4, 0);
87 assert_eq!(dest_pixels.len() % 4, 0);
88
89 if src_size == dest_size && src_rect == dest_rect {
90 dest_pixels.copy_from_slice(src_pixels);
91 return;
92 }
93
94 let src_first_column_start = src_rect.origin.x as usize * 4;
95 let src_row_length = src_size.width as usize * 4;
96 let src_first_row_start = src_rect.origin.y as usize * src_row_length;
97
98 let dest_first_column_start = dest_rect.origin.x as usize * 4;
99 let dest_row_length = dest_size.width as usize * 4;
100 let dest_first_row_start = dest_rect.origin.y as usize * dest_row_length;
101
102 let (chunk_length, chunk_count) = (
103 src_rect.size.width as usize * 4,
104 src_rect.size.height as usize,
105 );
106
107 for i in 0..chunk_count {
108 let src = &src_pixels[src_first_row_start + i * src_row_length..][src_first_column_start..]
109 [..chunk_length];
110 let dest = &mut dest_pixels[dest_first_row_start + i * dest_row_length..]
111 [dest_first_column_start..][..chunk_length];
112 dest.copy_from_slice(src);
113 }
114}
115
116pub fn scale_rgba8_image(
118 size: Size2D<u32>,
119 pixels: &[u8],
120 required_size: Size2D<u32>,
121 quality: FilterQuality,
122) -> Option<Vec<u8>> {
123 let filter = match quality {
124 FilterQuality::None => FilterType::Nearest,
125 FilterQuality::Low => FilterType::Triangle,
126 FilterQuality::Medium => FilterType::CatmullRom,
127 FilterQuality::High => FilterType::Lanczos3,
128 };
129
130 let buffer: ImageBuffer<Rgba<u8>, &[u8]> =
131 ImageBuffer::from_raw(size.width, size.height, pixels)?;
132
133 let scaled_buffer =
134 imageops::resize(&buffer, required_size.width, required_size.height, filter);
135
136 Some(scaled_buffer.into_vec())
137}
138
139pub fn flip_y_rgba8_image_inplace(size: Size2D<u32>, pixels: &mut [u8]) {
141 assert_eq!(pixels.len() % 4, 0);
142
143 let row_length = size.width as usize * 4;
144 let half_height = (size.height / 2) as usize;
145
146 let (left, right) = pixels.split_at_mut(pixels.len() - row_length * half_height);
147
148 for i in 0..half_height {
149 let top = &mut left[i * row_length..][..row_length];
150 let bottom = &mut right[(half_height - i - 1) * row_length..][..row_length];
151 top.swap_with_slice(bottom);
152 }
153}
154
155pub fn rgba8_get_rect(pixels: &[u8], size: Size2D<u32>, rect: Rect<u32>) -> Cow<'_, [u8]> {
156 assert!(!rect.is_empty());
157 assert!(Rect::from_size(size).contains_rect(&rect));
158 assert_eq!(pixels.len() % 4, 0);
159 assert_eq!(size.area() as usize, pixels.len() / 4);
160 let area = rect.size.area() as usize;
161 let first_column_start = rect.origin.x as usize * 4;
162 let row_length = size.width as usize * 4;
163 let first_row_start = rect.origin.y as usize * row_length;
164 if rect.origin.x == 0 && rect.size.width == size.width || rect.size.height == 1 {
165 let start = first_column_start + first_row_start;
166 return Cow::Borrowed(&pixels[start..start + area * 4]);
167 }
168 let mut data = Vec::with_capacity(area * 4);
169 for row in pixels[first_row_start..]
170 .chunks(row_length)
171 .take(rect.size.height as usize)
172 {
173 data.extend_from_slice(&row[first_column_start..][..rect.size.width as usize * 4]);
174 }
175 data.into()
176}
177
178pub fn rgba8_byte_swap_colors_inplace(pixels: &mut [u8]) {
180 assert!(pixels.len().is_multiple_of(4));
181 for rgba in pixels.chunks_mut(4) {
182 rgba.swap(0, 2);
183 }
184}
185
186pub fn rgba8_byte_swap_and_premultiply_inplace(pixels: &mut [u8]) {
187 assert!(pixels.len().is_multiple_of(4));
188 for rgba in pixels.chunks_mut(4) {
189 let b = rgba[0];
190 rgba[0] = multiply_u8_color(rgba[2], rgba[3]);
191 rgba[1] = multiply_u8_color(rgba[1], rgba[3]);
192 rgba[2] = multiply_u8_color(b, rgba[3]);
193 }
194}
195
196pub fn rgba8_premultiply_inplace(pixels: &mut [u8]) -> bool {
198 assert!(pixels.len().is_multiple_of(4));
199 let mut is_opaque = true;
200 for rgba in pixels.chunks_mut(4) {
201 rgba[0] = multiply_u8_color(rgba[0], rgba[3]);
202 rgba[1] = multiply_u8_color(rgba[1], rgba[3]);
203 rgba[2] = multiply_u8_color(rgba[2], rgba[3]);
204 is_opaque = is_opaque && rgba[3] == 255;
205 }
206 is_opaque
207}
208
209#[inline(always)]
213pub fn multiply_u8_color(a: u8, b: u8) -> u8 {
214 let c = a as u32 * b as u32 + 128;
215 ((c + (c >> 8)) >> 8) as u8
216}
217
218pub fn clip(
219 mut origin: Point2D<i32>,
220 mut size: Size2D<u32>,
221 surface: Size2D<u32>,
222) -> Option<Rect<u32>> {
223 if origin.x < 0 {
224 size.width = size.width.saturating_sub(-origin.x as u32);
225 origin.x = 0;
226 }
227 if origin.y < 0 {
228 size.height = size.height.saturating_sub(-origin.y as u32);
229 origin.y = 0;
230 }
231 let origin = Point2D::new(origin.x as u32, origin.y as u32);
232 Rect::new(origin, size)
233 .intersection(&Rect::from_size(surface))
234 .filter(|rect| !rect.is_empty())
235}
236
237#[derive(PartialEq)]
238pub enum EncodedImageType {
239 Png,
240 Jpeg,
241 Webp,
242}
243
244impl From<String> for EncodedImageType {
245 fn from(mime_type: String) -> Self {
251 let mime = mime_type.to_lowercase();
252 if mime == "image/jpeg" {
253 Self::Jpeg
254 } else if mime == "image/webp" {
255 Self::Webp
256 } else {
257 Self::Png
258 }
259 }
260}
261
262impl EncodedImageType {
263 pub fn as_mime_type(&self) -> String {
264 match self {
265 Self::Png => "image/png",
266 Self::Jpeg => "image/jpeg",
267 Self::Webp => "image/webp",
268 }
269 .to_owned()
270 }
271}
272
273#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
276pub enum CorsStatus {
277 Safe,
279 Unsafe,
282}
283
284#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
286pub struct SharedRasterImage {
287 pub metadata: ImageMetadata,
288 pub format: PixelFormat,
289 pub id: Option<ImageKey>,
290 pub cors_status: CorsStatus,
291 #[conditional_malloc_size_of]
292 pub bytes: Arc<GenericSharedMemory>,
293 pub frames: Vec<ImageFrame>,
294 pub is_opaque: bool,
296}
297
298#[derive(Clone, MallocSizeOf)]
299pub struct RasterImage {
300 pub metadata: ImageMetadata,
301 pub format: PixelFormat,
302 pub id: Option<ImageKey>,
303 pub cors_status: CorsStatus,
304 #[conditional_malloc_size_of]
305 pub bytes: Arc<Vec<u8>>,
306 pub frames: Vec<ImageFrame>,
307 pub is_opaque: bool,
309}
310
311fn sensible_delay(delay: Duration) -> Duration {
312 if delay <= Duration::from_millis(10) {
318 Duration::from_millis(100)
319 } else {
320 delay
321 }
322}
323
324#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
325pub struct ImageFrame {
326 pub delay: Option<Duration>,
327 pub byte_range: Range<usize>,
330 pub width: u32,
331 pub height: u32,
332}
333
334impl ImageFrame {
335 pub fn delay(&self) -> Option<Duration> {
336 self.delay.map(sensible_delay)
337 }
338}
339
340pub struct ImageFrameView<'a> {
342 pub delay: Option<Duration>,
343 pub bytes: &'a [u8],
344 pub width: u32,
345 pub height: u32,
346}
347
348impl ImageFrameView<'_> {
349 pub fn delay(&self) -> Option<Duration> {
350 self.delay.map(sensible_delay)
351 }
352}
353
354impl RasterImage {
355 pub fn should_animate(&self) -> bool {
356 self.frames.len() > 1
357 }
358
359 fn frame_view<'image>(&'image self, frame: &ImageFrame) -> ImageFrameView<'image> {
360 ImageFrameView {
361 delay: frame.delay,
362 bytes: self.bytes.get(frame.byte_range.clone()).unwrap(),
363 width: frame.width,
364 height: frame.height,
365 }
366 }
367
368 pub fn frame(&self, index: usize) -> Option<ImageFrameView<'_>> {
369 self.frames.get(index).map(|frame| self.frame_view(frame))
370 }
371
372 pub fn first_frame(&self) -> ImageFrameView<'_> {
373 self.frame(0)
374 .expect("All images should have at least one frame")
375 }
376
377 pub fn as_snapshot(&self) -> Snapshot {
378 let size = Size2D::new(self.metadata.width, self.metadata.height);
379 let format = match self.format {
380 PixelFormat::BGRA8 => SnapshotPixelFormat::BGRA,
381 PixelFormat::RGBA8 => SnapshotPixelFormat::RGBA,
382 pixel_format => {
383 unimplemented!("unsupported pixel format ({pixel_format:?})");
384 },
385 };
386
387 let alpha_mode = SnapshotAlphaMode::Transparent {
388 premultiplied: true,
389 };
390
391 Snapshot::from_arc_vec(
392 size.cast(),
393 format,
394 alpha_mode,
395 self.bytes.clone(),
396 self.frames[0].byte_range.clone(),
397 )
398 }
399
400 pub fn frame_data(&self, index: usize) -> Option<&ImageFrame> {
401 self.frames.get(index)
402 }
403
404 pub fn webrender_image_descriptor_and_data_for_frame(
405 &self,
406 frame_index: usize,
407 ) -> (ImageDescriptor, GenericSharedMemory) {
408 let frame = self
409 .frames
410 .get(frame_index)
411 .unwrap_or_else(|| panic!("Asked for a frame that did not exist: {frame_index:?}"));
412
413 let (format, data) = match self.format {
414 PixelFormat::BGRA8 => (
415 WebRenderImageFormat::BGRA8,
416 GenericSharedMemory::from_bytes(&self.bytes),
417 ),
418 PixelFormat::RGBA8 => (
419 WebRenderImageFormat::RGBA8,
420 GenericSharedMemory::from_bytes(&self.bytes),
421 ),
422 PixelFormat::RGB8 => {
423 let frame_bytes = &self.bytes[frame.byte_range.clone()];
424 let mut bytes = Vec::with_capacity(frame_bytes.len() / 3 * 4);
425 for rgb in frame_bytes.chunks(3) {
426 bytes.extend_from_slice(&[rgb[2], rgb[1], rgb[0], 0xff]);
427 }
428 (
429 WebRenderImageFormat::BGRA8,
430 GenericSharedMemory::from_bytes(&bytes),
431 )
432 },
433 PixelFormat::K8 | PixelFormat::KA8 => {
434 panic!("Not support by webrender yet");
435 },
436 };
437 let mut flags = ImageDescriptorFlags::ALLOW_MIPMAPS;
438 flags.set(ImageDescriptorFlags::IS_OPAQUE, self.is_opaque);
439
440 let size = DeviceIntSize::new(self.metadata.width as i32, self.metadata.height as i32);
441 let descriptor = ImageDescriptor {
442 size,
443 stride: None,
444 format,
445 offset: frame.byte_range.start as i32,
446 flags,
447 };
448 (descriptor, data)
449 }
450
451 pub fn webrender_image_descriptor_and_offset_for_frame(&self) -> Option<ImageDescriptor> {
454 if self.format == PixelFormat::RGB8 ||
455 self.format == PixelFormat::K8 ||
456 self.format == PixelFormat::KA8
457 {
458 return None;
459 }
460 let format = match self.format {
461 PixelFormat::BGRA8 => WebRenderImageFormat::BGRA8,
462 PixelFormat::RGBA8 => WebRenderImageFormat::RGBA8,
463 PixelFormat::RGB8 => WebRenderImageFormat::BGRA8,
464 PixelFormat::KA8 | PixelFormat::K8 => {
465 error!("Pixel format currently not supported");
466 return None;
467 },
468 };
469 let mut flags = ImageDescriptorFlags::ALLOW_MIPMAPS;
470 flags.set(ImageDescriptorFlags::IS_OPAQUE, self.is_opaque);
471
472 let size = DeviceIntSize::new(self.metadata.width as i32, self.metadata.height as i32);
473 let descriptor = ImageDescriptor {
474 size,
475 stride: None,
476 format,
477 offset: 0,
478 flags,
479 };
480 Some(descriptor)
481 }
482
483 pub fn to_shared(&self) -> Arc<SharedRasterImage> {
484 Arc::new(SharedRasterImage {
485 metadata: self.metadata,
486 format: self.format,
487 id: self.id,
488 cors_status: self.cors_status,
489 bytes: Arc::new(GenericSharedMemory::from_bytes(&self.bytes)),
490 frames: self.frames.clone(),
491 is_opaque: self.is_opaque,
492 })
493 }
494}
495
496impl fmt::Debug for RasterImage {
497 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
498 write!(
499 f,
500 "Image {{ width: {}, height: {}, format: {:?}, ..., id: {:?} }}",
501 self.metadata.width, self.metadata.height, self.format, self.id
502 )
503 }
504}
505
506#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
507pub struct ImageMetadata {
508 pub width: u32,
509 pub height: u32,
510}
511
512pub fn load_from_memory(buffer: &[u8], cors_status: CorsStatus) -> Option<RasterImage> {
516 if buffer.is_empty() {
517 return None;
518 }
519
520 let image_fmt_result = detect_image_format(buffer);
521 match image_fmt_result {
522 Err(msg) => {
523 debug!("{}", msg);
524 None
525 },
526 Ok(format) => {
527 let Ok(image_decoder) = make_decoder(format, buffer) else {
528 return None;
529 };
530 match image_decoder {
531 GenericImageDecoder::Png(png_decoder) => {
532 if png_decoder.is_apng().unwrap_or_default() {
533 let Ok(apng_decoder) = png_decoder.apng() else {
534 return None;
535 };
536 decode_animated_image(cors_status, apng_decoder)
537 } else {
538 decode_static_image(cors_status, *png_decoder)
539 }
540 },
541 GenericImageDecoder::Gif(animation_decoder) => {
542 decode_animated_image(cors_status, *animation_decoder)
543 },
544 GenericImageDecoder::Webp(webp_decoder) => {
545 if webp_decoder.has_animation() {
546 decode_animated_image(cors_status, *webp_decoder)
547 } else {
548 decode_static_image(cors_status, *webp_decoder)
549 }
550 },
551 GenericImageDecoder::Bmp(image_decoder) => {
552 decode_static_image(cors_status, *image_decoder)
553 },
554 GenericImageDecoder::Jpeg(image_decoder) => {
555 decode_static_image(cors_status, *image_decoder)
556 },
557 GenericImageDecoder::Ico(image_decoder) => {
558 decode_static_image(cors_status, *image_decoder)
559 },
560 }
561 },
562 }
563}
564
565pub fn detect_image_format(buffer: &[u8]) -> Result<ImageFormat, &str> {
567 if is_gif(buffer) {
568 Ok(ImageFormat::Gif)
569 } else if is_jpeg(buffer) {
570 Ok(ImageFormat::Jpeg)
571 } else if is_png(buffer) {
572 Ok(ImageFormat::Png)
573 } else if is_webp(buffer) {
574 Ok(ImageFormat::WebP)
575 } else if is_bmp(buffer) {
576 Ok(ImageFormat::Bmp)
577 } else if is_ico(buffer) {
578 Ok(ImageFormat::Ico)
579 } else {
580 Err("Image Format Not Supported")
581 }
582}
583
584#[expect(
585 clippy::manual_checked_ops,
586 reason = "This code becomes less readable by applying the lint"
587)]
588pub fn unmultiply_inplace<const SWAP_RB: bool>(pixels: &mut [u8]) {
589 for rgba in pixels.chunks_mut(4) {
590 let a = rgba[3] as u32;
591 let mut b = rgba[2] as u32;
592 let mut g = rgba[1] as u32;
593 let mut r = rgba[0] as u32;
594
595 if a > 0 {
596 r = r * 255 / a;
597 g = g * 255 / a;
598 b = b * 255 / a;
599
600 if SWAP_RB {
601 rgba[2] = r as u8;
602 rgba[1] = g as u8;
603 rgba[0] = b as u8;
604 } else {
605 rgba[2] = b as u8;
606 rgba[1] = g as u8;
607 rgba[0] = r as u8;
608 }
609 }
610 }
611}
612
613#[repr(u8)]
614pub enum Multiply {
615 None = 0,
616 PreMultiply = 1,
617 UnMultiply = 2,
618}
619
620pub fn transform_inplace(pixels: &mut [u8], multiply: Multiply, swap_rb: bool, clear_alpha: bool) {
621 match (multiply, swap_rb, clear_alpha) {
622 (Multiply::None, true, true) => generic_transform_inplace::<0, true, true>(pixels),
623 (Multiply::None, true, false) => generic_transform_inplace::<0, true, false>(pixels),
624 (Multiply::None, false, true) => generic_transform_inplace::<0, false, true>(pixels),
625 (Multiply::None, false, false) => generic_transform_inplace::<0, false, false>(pixels),
626 (Multiply::PreMultiply, true, true) => generic_transform_inplace::<1, true, true>(pixels),
627 (Multiply::PreMultiply, true, false) => generic_transform_inplace::<1, true, false>(pixels),
628 (Multiply::PreMultiply, false, true) => generic_transform_inplace::<1, false, true>(pixels),
629 (Multiply::PreMultiply, false, false) => {
630 generic_transform_inplace::<1, false, false>(pixels)
631 },
632 (Multiply::UnMultiply, true, true) => generic_transform_inplace::<2, true, true>(pixels),
633 (Multiply::UnMultiply, true, false) => generic_transform_inplace::<2, true, false>(pixels),
634 (Multiply::UnMultiply, false, true) => generic_transform_inplace::<2, false, true>(pixels),
635 (Multiply::UnMultiply, false, false) => {
636 generic_transform_inplace::<2, false, false>(pixels)
637 },
638 }
639}
640
641#[expect(
642 clippy::manual_checked_ops,
643 reason = "This code becomes less readable by applying the lint"
644)]
645pub fn generic_transform_inplace<
646 const MULTIPLY: u8, const SWAP_RB: bool,
648 const CLEAR_ALPHA: bool,
649>(
650 pixels: &mut [u8],
651) {
652 for rgba in pixels.chunks_mut(4) {
653 match MULTIPLY {
654 1 => {
655 let a = rgba[3];
656
657 rgba[0] = multiply_u8_color(rgba[0], a);
658 rgba[1] = multiply_u8_color(rgba[1], a);
659 rgba[2] = multiply_u8_color(rgba[2], a);
660 },
661 2 => {
662 let a = rgba[3] as u32;
663
664 if a > 0 {
665 rgba[0] = (rgba[0] as u32 * 255 / a) as u8;
666 rgba[1] = (rgba[1] as u32 * 255 / a) as u8;
667 rgba[2] = (rgba[2] as u32 * 255 / a) as u8;
668 }
669 },
670 _ => {},
671 }
672 if SWAP_RB {
673 rgba.swap(0, 2);
674 }
675 if CLEAR_ALPHA {
676 rgba[3] = u8::MAX;
677 }
678 }
679}
680
681fn is_gif(buffer: &[u8]) -> bool {
682 buffer.starts_with(b"GIF87a") || buffer.starts_with(b"GIF89a")
683}
684
685fn is_jpeg(buffer: &[u8]) -> bool {
686 buffer.starts_with(&[0xff, 0xd8, 0xff])
687}
688
689fn is_png(buffer: &[u8]) -> bool {
690 buffer.starts_with(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])
691}
692
693fn is_bmp(buffer: &[u8]) -> bool {
694 buffer.starts_with(&[0x42, 0x4D])
695}
696
697fn is_ico(buffer: &[u8]) -> bool {
698 buffer.starts_with(&[0x00, 0x00, 0x01, 0x00])
699}
700
701fn is_webp(buffer: &[u8]) -> bool {
702 if !buffer.starts_with(b"RIFF") || buffer.len() < 12 {
705 return false;
706 }
707 let size: [u8; 4] = [buffer[4], buffer[5], buffer[6], buffer[7]];
708 let len: usize = u32::from_le_bytes(size) as usize;
713 buffer[8..].len() >= len && &buffer[8..12] == b"WEBP"
714}
715
716enum GenericImageDecoder<R: std::io::BufRead + std::io::Seek> {
717 Png(Box<png::PngDecoder<R>>),
718 Gif(Box<gif::GifDecoder<R>>),
719 Webp(Box<webp::WebPDecoder<R>>),
720 Jpeg(Box<jpeg::JpegDecoder<R>>),
721 Bmp(Box<bmp::BmpDecoder<R>>),
722 Ico(Box<ico::IcoDecoder<R>>),
723}
724
725fn make_decoder(
726 format: ImageFormat,
727 buffer: &[u8],
728) -> ImageResult<GenericImageDecoder<Cursor<&[u8]>>> {
729 let limits = Limits::default();
730 let reader = Cursor::new(buffer);
731 Ok(match format {
732 ImageFormat::Png => {
733 GenericImageDecoder::Png(Box::new(png::PngDecoder::with_limits(reader, limits)?))
734 },
735 ImageFormat::Gif => GenericImageDecoder::Gif(Box::new(gif::GifDecoder::new(reader)?)),
736 ImageFormat::WebP => GenericImageDecoder::Webp(Box::new(webp::WebPDecoder::new(reader)?)),
737 ImageFormat::Jpeg => GenericImageDecoder::Jpeg(Box::new(jpeg::JpegDecoder::new(reader)?)),
738 ImageFormat::Bmp => GenericImageDecoder::Bmp(Box::new(bmp::BmpDecoder::new(reader)?)),
739 ImageFormat::Ico => GenericImageDecoder::Ico(Box::new(ico::IcoDecoder::new(reader)?)),
740 _ => {
741 return Err(ImageError::Unsupported(
742 ImageFormatHint::Exact(format).into(),
743 ));
744 },
745 })
746}
747
748fn decode_static_image(
749 cors_status: CorsStatus,
750 mut image_decoder: impl ImageDecoder,
751) -> Option<RasterImage> {
752 let orientation = image_decoder.orientation();
753
754 let Ok(mut dynamic_image) = DynamicImage::from_decoder(image_decoder) else {
755 debug!("Image decoding error");
756 return None;
757 };
758
759 if let Ok(orientation) = orientation {
760 dynamic_image.apply_orientation(orientation);
761 }
762
763 let mut rgba = dynamic_image.into_rgba8();
764
765 let is_opaque = rgba8_premultiply_inplace(&mut rgba);
769
770 let frame = ImageFrame {
771 delay: None,
772 byte_range: 0..rgba.len(),
773 width: rgba.width(),
774 height: rgba.height(),
775 };
776 Some(RasterImage {
777 metadata: ImageMetadata {
778 width: rgba.width(),
779 height: rgba.height(),
780 },
781 format: PixelFormat::RGBA8,
782 frames: vec![frame],
783 bytes: Arc::new(rgba.to_vec()),
784 id: None,
785 cors_status,
786 is_opaque,
787 })
788}
789
790fn decode_animated_image<'a, T>(
791 cors_status: CorsStatus,
792 animated_image_decoder: T,
793) -> Option<RasterImage>
794where
795 T: AnimationDecoder<'a>,
796{
797 let mut width = 0;
798 let mut height = 0;
799
800 let mut frame_data = vec![];
804 let mut total_number_of_bytes = 0;
805 let mut is_opaque = true;
806 let frames: Vec<ImageFrame> = animated_image_decoder
807 .into_frames()
808 .map_while(|decoded_frame| {
809 let mut animated_frame = match decoded_frame {
810 Ok(decoded_frame) => decoded_frame,
811 Err(error) => {
812 debug!("decode Animated frame error: {error}");
813 return None;
814 },
815 };
816
817 is_opaque = rgba8_premultiply_inplace(animated_frame.buffer_mut()) && is_opaque;
821
822 let frame_start = total_number_of_bytes;
823 total_number_of_bytes += animated_frame.buffer().len();
824
825 let frame_width = animated_frame.buffer().width();
827 let frame_height = animated_frame.buffer().height();
828 width = cmp::max(width, frame_width);
829 height = cmp::max(height, frame_height);
830
831 let frame = ImageFrame {
832 byte_range: frame_start..total_number_of_bytes,
833 delay: Some(Duration::from(animated_frame.delay())),
834 width: frame_width,
835 height: frame_height,
836 };
837
838 frame_data.push(animated_frame);
839
840 Some(frame)
841 })
842 .collect();
843
844 if frames.is_empty() {
845 debug!("Animated Image decoding error");
846 return None;
847 }
848
849 let mut bytes = Vec::with_capacity(total_number_of_bytes);
851 for frame in frame_data {
852 bytes.extend_from_slice(frame.buffer());
853 }
854
855 Some(RasterImage {
856 metadata: ImageMetadata { width, height },
857 cors_status,
858 frames,
859 id: None,
860 format: PixelFormat::RGBA8,
861 bytes: Arc::new(bytes),
862 is_opaque,
863 })
864}
865
866#[cfg(test)]
867mod test {
868 use super::detect_image_format;
869
870 #[test]
871 fn test_supported_images() {
872 let gif1 = [b'G', b'I', b'F', b'8', b'7', b'a'];
873 let gif2 = [b'G', b'I', b'F', b'8', b'9', b'a'];
874 let jpeg = [0xff, 0xd8, 0xff];
875 let png = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
876 let webp = [
877 b'R', b'I', b'F', b'F', 0x04, 0x00, 0x00, 0x00, b'W', b'E', b'B', b'P',
878 ];
879 let bmp = [0x42, 0x4D];
880 let ico = [0x00, 0x00, 0x01, 0x00];
881 let junk_format = [0x01, 0x02, 0x03, 0x04, 0x05];
882
883 assert!(detect_image_format(&gif1).is_ok());
884 assert!(detect_image_format(&gif2).is_ok());
885 assert!(detect_image_format(&jpeg).is_ok());
886 assert!(detect_image_format(&png).is_ok());
887 assert!(detect_image_format(&webp).is_ok());
888 assert!(detect_image_format(&bmp).is_ok());
889 assert!(detect_image_format(&ico).is_ok());
890 assert!(detect_image_format(&junk_format).is_err());
891 }
892}