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<&str> for EncodedImageType {
245 fn from(mime_string: &str) -> Self {
251 if mime_string.eq_ignore_ascii_case("image/jpeg") {
252 Self::Jpeg
253 } else if mime_string.eq_ignore_ascii_case("image/webp") {
254 Self::Webp
255 } else {
256 Self::Png
257 }
258 }
259}
260
261impl EncodedImageType {
262 pub fn as_mime_type(&self) -> String {
263 match self {
264 Self::Png => "image/png",
265 Self::Jpeg => "image/jpeg",
266 Self::Webp => "image/webp",
267 }
268 .to_owned()
269 }
270}
271
272#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
275pub enum CorsStatus {
276 Safe,
278 Unsafe,
281}
282
283#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
285pub struct SharedRasterImage {
286 pub metadata: ImageMetadata,
287 pub format: PixelFormat,
288 pub id: Option<ImageKey>,
289 pub cors_status: CorsStatus,
290 #[conditional_malloc_size_of]
291 pub bytes: Arc<GenericSharedMemory>,
292 pub frames: Vec<ImageFrame>,
293 pub is_opaque: bool,
295}
296
297#[derive(Clone, MallocSizeOf)]
298pub struct RasterImage {
299 pub metadata: ImageMetadata,
300 pub format: PixelFormat,
301 pub id: Option<ImageKey>,
302 pub cors_status: CorsStatus,
303 #[conditional_malloc_size_of]
304 pub bytes: Arc<Vec<u8>>,
305 pub frames: Vec<ImageFrame>,
306 pub is_opaque: bool,
308}
309
310fn sensible_delay(delay: Duration) -> Duration {
311 if delay <= Duration::from_millis(10) {
317 Duration::from_millis(100)
318 } else {
319 delay
320 }
321}
322
323#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
324pub struct ImageFrame {
325 pub delay: Option<Duration>,
326 pub byte_range: Range<usize>,
329 pub width: u32,
330 pub height: u32,
331}
332
333impl ImageFrame {
334 pub fn delay(&self) -> Option<Duration> {
335 self.delay.map(sensible_delay)
336 }
337}
338
339pub struct ImageFrameView<'a> {
341 pub delay: Option<Duration>,
342 pub bytes: &'a [u8],
343 pub width: u32,
344 pub height: u32,
345}
346
347impl ImageFrameView<'_> {
348 pub fn delay(&self) -> Option<Duration> {
349 self.delay.map(sensible_delay)
350 }
351}
352
353impl RasterImage {
354 pub fn should_animate(&self) -> bool {
355 self.frames.len() > 1
356 }
357
358 fn frame_view<'image>(&'image self, frame: &ImageFrame) -> ImageFrameView<'image> {
359 ImageFrameView {
360 delay: frame.delay,
361 bytes: self.bytes.get(frame.byte_range.clone()).unwrap(),
362 width: frame.width,
363 height: frame.height,
364 }
365 }
366
367 pub fn frame(&self, index: usize) -> Option<ImageFrameView<'_>> {
368 self.frames.get(index).map(|frame| self.frame_view(frame))
369 }
370
371 pub fn first_frame(&self) -> ImageFrameView<'_> {
372 self.frame(0)
373 .expect("All images should have at least one frame")
374 }
375
376 pub fn as_snapshot(&self) -> Snapshot {
377 let size = Size2D::new(self.metadata.width, self.metadata.height);
378 let format = match self.format {
379 PixelFormat::BGRA8 => SnapshotPixelFormat::BGRA,
380 PixelFormat::RGBA8 => SnapshotPixelFormat::RGBA,
381 pixel_format => {
382 unimplemented!("unsupported pixel format ({pixel_format:?})");
383 },
384 };
385
386 let alpha_mode = SnapshotAlphaMode::Transparent {
387 premultiplied: true,
388 };
389
390 Snapshot::from_arc_vec(
391 size.cast(),
392 format,
393 alpha_mode,
394 self.bytes.clone(),
395 self.frames[0].byte_range.clone(),
396 )
397 }
398
399 pub fn frame_data(&self, index: usize) -> Option<&ImageFrame> {
400 self.frames.get(index)
401 }
402
403 pub fn webrender_image_descriptor_and_data_for_frame(
408 &self,
409 frame_index: usize,
410 ) -> (ImageDescriptor, GenericSharedMemory, bool) {
411 let frame = self
412 .frames
413 .get(frame_index)
414 .unwrap_or_else(|| panic!("Asked for a frame that did not exist: {frame_index:?}"));
415
416 let (format, data, should_animate) = match self.format {
417 PixelFormat::BGRA8 => (
418 WebRenderImageFormat::BGRA8,
419 GenericSharedMemory::from_arc_vec(self.bytes.clone()),
420 self.should_animate(),
421 ),
422 PixelFormat::RGBA8 => (
423 WebRenderImageFormat::RGBA8,
424 GenericSharedMemory::from_arc_vec(self.bytes.clone()),
425 self.should_animate(),
426 ),
427 PixelFormat::RGB8 => {
428 let frame_bytes = &self.bytes[frame.byte_range.clone()];
429 let mut bytes = Vec::with_capacity(frame_bytes.len() / 3 * 4);
430 for rgb in frame_bytes.chunks(3) {
431 bytes.extend_from_slice(&[rgb[2], rgb[1], rgb[0], 0xff]);
432 }
433 (
434 WebRenderImageFormat::BGRA8,
435 GenericSharedMemory::from_vec(bytes),
436 false,
440 )
441 },
442 PixelFormat::K8 | PixelFormat::KA8 => {
443 panic!("Not support by webrender yet");
444 },
445 };
446 let mut flags = ImageDescriptorFlags::ALLOW_MIPMAPS;
447 flags.set(ImageDescriptorFlags::IS_OPAQUE, self.is_opaque);
448
449 let size = DeviceIntSize::new(self.metadata.width as i32, self.metadata.height as i32);
450 let descriptor = ImageDescriptor {
451 size,
452 stride: None,
453 format,
454 offset: frame.byte_range.start as i32,
455 flags,
456 };
457 (descriptor, data, should_animate)
458 }
459
460 pub fn webrender_image_descriptor_and_offset_for_frame(&self) -> Option<ImageDescriptor> {
463 if self.format == PixelFormat::RGB8 ||
464 self.format == PixelFormat::K8 ||
465 self.format == PixelFormat::KA8
466 {
467 return None;
468 }
469 let format = match self.format {
470 PixelFormat::BGRA8 => WebRenderImageFormat::BGRA8,
471 PixelFormat::RGBA8 => WebRenderImageFormat::RGBA8,
472 PixelFormat::RGB8 => WebRenderImageFormat::BGRA8,
473 PixelFormat::KA8 | PixelFormat::K8 => {
474 error!("Pixel format currently not supported");
475 return None;
476 },
477 };
478 let mut flags = ImageDescriptorFlags::ALLOW_MIPMAPS;
479 flags.set(ImageDescriptorFlags::IS_OPAQUE, self.is_opaque);
480
481 let size = DeviceIntSize::new(self.metadata.width as i32, self.metadata.height as i32);
482 let descriptor = ImageDescriptor {
483 size,
484 stride: None,
485 format,
486 offset: 0,
487 flags,
488 };
489 Some(descriptor)
490 }
491
492 pub fn to_shared(&self) -> Arc<SharedRasterImage> {
493 Arc::new(SharedRasterImage {
494 metadata: self.metadata,
495 format: self.format,
496 id: self.id,
497 cors_status: self.cors_status,
498 bytes: Arc::new(GenericSharedMemory::from_arc_vec(self.bytes.clone())),
499 frames: self.frames.clone(),
500 is_opaque: self.is_opaque,
501 })
502 }
503}
504
505impl fmt::Debug for RasterImage {
506 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
507 write!(
508 f,
509 "Image {{ width: {}, height: {}, format: {:?}, ..., id: {:?} }}",
510 self.metadata.width, self.metadata.height, self.format, self.id
511 )
512 }
513}
514
515#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
516pub struct ImageMetadata {
517 pub width: u32,
518 pub height: u32,
519}
520
521pub fn load_from_memory(buffer: &[u8], cors_status: CorsStatus) -> Option<RasterImage> {
525 if buffer.is_empty() {
526 return None;
527 }
528
529 let image_fmt_result = detect_image_format(buffer);
530 match image_fmt_result {
531 Err(msg) => {
532 debug!("{}", msg);
533 None
534 },
535 Ok(format) => {
536 let Ok(image_decoder) = make_decoder(format, buffer) else {
537 return None;
538 };
539 match image_decoder {
540 GenericImageDecoder::Png(png_decoder) => {
541 if png_decoder.is_apng().unwrap_or_default() {
542 let Ok(apng_decoder) = png_decoder.apng() else {
543 return None;
544 };
545 decode_animated_image(cors_status, apng_decoder)
546 } else {
547 decode_static_image(cors_status, *png_decoder)
548 }
549 },
550 GenericImageDecoder::Gif(animation_decoder) => {
551 decode_animated_image(cors_status, *animation_decoder)
552 },
553 GenericImageDecoder::Webp(webp_decoder) => {
554 if webp_decoder.has_animation() {
555 decode_animated_image(cors_status, *webp_decoder)
556 } else {
557 decode_static_image(cors_status, *webp_decoder)
558 }
559 },
560 GenericImageDecoder::Bmp(image_decoder) => {
561 decode_static_image(cors_status, *image_decoder)
562 },
563 GenericImageDecoder::Jpeg(image_decoder) => {
564 decode_static_image(cors_status, *image_decoder)
565 },
566 GenericImageDecoder::Ico(image_decoder) => {
567 decode_static_image(cors_status, *image_decoder)
568 },
569 }
570 },
571 }
572}
573
574pub fn detect_image_format(buffer: &[u8]) -> Result<ImageFormat, &str> {
576 if is_gif(buffer) {
577 Ok(ImageFormat::Gif)
578 } else if is_jpeg(buffer) {
579 Ok(ImageFormat::Jpeg)
580 } else if is_png(buffer) {
581 Ok(ImageFormat::Png)
582 } else if is_webp(buffer) {
583 Ok(ImageFormat::WebP)
584 } else if is_bmp(buffer) {
585 Ok(ImageFormat::Bmp)
586 } else if is_ico(buffer) {
587 Ok(ImageFormat::Ico)
588 } else {
589 Err("Image Format Not Supported")
590 }
591}
592
593#[expect(
594 clippy::manual_checked_ops,
595 reason = "This code becomes less readable by applying the lint"
596)]
597pub fn unmultiply_inplace<const SWAP_RB: bool>(pixels: &mut [u8]) {
598 for rgba in pixels.chunks_mut(4) {
599 let a = rgba[3] as u32;
600 let mut b = rgba[2] as u32;
601 let mut g = rgba[1] as u32;
602 let mut r = rgba[0] as u32;
603
604 if a > 0 {
605 r = r * 255 / a;
606 g = g * 255 / a;
607 b = b * 255 / a;
608
609 if SWAP_RB {
610 rgba[2] = r as u8;
611 rgba[1] = g as u8;
612 rgba[0] = b as u8;
613 } else {
614 rgba[2] = b as u8;
615 rgba[1] = g as u8;
616 rgba[0] = r as u8;
617 }
618 }
619 }
620}
621
622#[repr(u8)]
623pub enum Multiply {
624 None = 0,
625 PreMultiply = 1,
626 UnMultiply = 2,
627}
628
629pub fn transform_inplace(pixels: &mut [u8], multiply: Multiply, swap_rb: bool, clear_alpha: bool) {
630 match (multiply, swap_rb, clear_alpha) {
631 (Multiply::None, true, true) => generic_transform_inplace::<0, true, true>(pixels),
632 (Multiply::None, true, false) => generic_transform_inplace::<0, true, false>(pixels),
633 (Multiply::None, false, true) => generic_transform_inplace::<0, false, true>(pixels),
634 (Multiply::None, false, false) => generic_transform_inplace::<0, false, false>(pixels),
635 (Multiply::PreMultiply, true, true) => generic_transform_inplace::<1, true, true>(pixels),
636 (Multiply::PreMultiply, true, false) => generic_transform_inplace::<1, true, false>(pixels),
637 (Multiply::PreMultiply, false, true) => generic_transform_inplace::<1, false, true>(pixels),
638 (Multiply::PreMultiply, false, false) => {
639 generic_transform_inplace::<1, false, false>(pixels)
640 },
641 (Multiply::UnMultiply, true, true) => generic_transform_inplace::<2, true, true>(pixels),
642 (Multiply::UnMultiply, true, false) => generic_transform_inplace::<2, true, false>(pixels),
643 (Multiply::UnMultiply, false, true) => generic_transform_inplace::<2, false, true>(pixels),
644 (Multiply::UnMultiply, false, false) => {
645 generic_transform_inplace::<2, false, false>(pixels)
646 },
647 }
648}
649
650#[expect(
651 clippy::manual_checked_ops,
652 reason = "This code becomes less readable by applying the lint"
653)]
654pub fn generic_transform_inplace<
655 const MULTIPLY: u8, const SWAP_RB: bool,
657 const CLEAR_ALPHA: bool,
658>(
659 pixels: &mut [u8],
660) {
661 for rgba in pixels.chunks_mut(4) {
662 match MULTIPLY {
663 1 => {
664 let a = rgba[3];
665
666 rgba[0] = multiply_u8_color(rgba[0], a);
667 rgba[1] = multiply_u8_color(rgba[1], a);
668 rgba[2] = multiply_u8_color(rgba[2], a);
669 },
670 2 => {
671 let a = rgba[3] as u32;
672
673 if a > 0 {
674 rgba[0] = (rgba[0] as u32 * 255 / a) as u8;
675 rgba[1] = (rgba[1] as u32 * 255 / a) as u8;
676 rgba[2] = (rgba[2] as u32 * 255 / a) as u8;
677 }
678 },
679 _ => {},
680 }
681 if SWAP_RB {
682 rgba.swap(0, 2);
683 }
684 if CLEAR_ALPHA {
685 rgba[3] = u8::MAX;
686 }
687 }
688}
689
690fn is_gif(buffer: &[u8]) -> bool {
691 buffer.starts_with(b"GIF87a") || buffer.starts_with(b"GIF89a")
692}
693
694fn is_jpeg(buffer: &[u8]) -> bool {
695 buffer.starts_with(&[0xff, 0xd8, 0xff])
696}
697
698fn is_png(buffer: &[u8]) -> bool {
699 buffer.starts_with(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])
700}
701
702fn is_bmp(buffer: &[u8]) -> bool {
703 buffer.starts_with(&[0x42, 0x4D])
704}
705
706fn is_ico(buffer: &[u8]) -> bool {
707 buffer.starts_with(&[0x00, 0x00, 0x01, 0x00])
708}
709
710fn is_webp(buffer: &[u8]) -> bool {
711 if !buffer.starts_with(b"RIFF") || buffer.len() < 12 {
714 return false;
715 }
716 let size: [u8; 4] = [buffer[4], buffer[5], buffer[6], buffer[7]];
717 let len: usize = u32::from_le_bytes(size) as usize;
722 buffer[8..].len() >= len && &buffer[8..12] == b"WEBP"
723}
724
725enum GenericImageDecoder<R: std::io::BufRead + std::io::Seek> {
726 Png(Box<png::PngDecoder<R>>),
727 Gif(Box<gif::GifDecoder<R>>),
728 Webp(Box<webp::WebPDecoder<R>>),
729 Jpeg(Box<jpeg::JpegDecoder<R>>),
730 Bmp(Box<bmp::BmpDecoder<R>>),
731 Ico(Box<ico::IcoDecoder<R>>),
732}
733
734fn make_decoder(
735 format: ImageFormat,
736 buffer: &[u8],
737) -> ImageResult<GenericImageDecoder<Cursor<&[u8]>>> {
738 let limits = Limits::default();
739 let reader = Cursor::new(buffer);
740 Ok(match format {
741 ImageFormat::Png => {
742 GenericImageDecoder::Png(Box::new(png::PngDecoder::with_limits(reader, limits)?))
743 },
744 ImageFormat::Gif => GenericImageDecoder::Gif(Box::new(gif::GifDecoder::new(reader)?)),
745 ImageFormat::WebP => GenericImageDecoder::Webp(Box::new(webp::WebPDecoder::new(reader)?)),
746 ImageFormat::Jpeg => GenericImageDecoder::Jpeg(Box::new(jpeg::JpegDecoder::new(reader)?)),
747 ImageFormat::Bmp => GenericImageDecoder::Bmp(Box::new(bmp::BmpDecoder::new(reader)?)),
748 ImageFormat::Ico => GenericImageDecoder::Ico(Box::new(ico::IcoDecoder::new(reader)?)),
749 _ => {
750 return Err(ImageError::Unsupported(
751 ImageFormatHint::Exact(format).into(),
752 ));
753 },
754 })
755}
756
757fn decode_static_image(
758 cors_status: CorsStatus,
759 mut image_decoder: impl ImageDecoder,
760) -> Option<RasterImage> {
761 let orientation = image_decoder.orientation();
762
763 let Ok(mut dynamic_image) = DynamicImage::from_decoder(image_decoder) else {
764 debug!("Image decoding error");
765 return None;
766 };
767
768 if let Ok(orientation) = orientation {
769 dynamic_image.apply_orientation(orientation);
770 }
771
772 let mut rgba = dynamic_image.into_rgba8();
773
774 let is_opaque = rgba8_premultiply_inplace(&mut rgba);
778
779 let frame = ImageFrame {
780 delay: None,
781 byte_range: 0..rgba.len(),
782 width: rgba.width(),
783 height: rgba.height(),
784 };
785 Some(RasterImage {
786 metadata: ImageMetadata {
787 width: rgba.width(),
788 height: rgba.height(),
789 },
790 format: PixelFormat::RGBA8,
791 frames: vec![frame],
792 bytes: Arc::new(rgba.to_vec()),
793 id: None,
794 cors_status,
795 is_opaque,
796 })
797}
798
799fn decode_animated_image<'a, T>(
800 cors_status: CorsStatus,
801 animated_image_decoder: T,
802) -> Option<RasterImage>
803where
804 T: AnimationDecoder<'a>,
805{
806 let mut width = 0;
807 let mut height = 0;
808
809 let mut frame_data = vec![];
813 let mut total_number_of_bytes = 0;
814 let mut is_opaque = true;
815 let frames: Vec<ImageFrame> = animated_image_decoder
816 .into_frames()
817 .map_while(|decoded_frame| {
818 let mut animated_frame = match decoded_frame {
819 Ok(decoded_frame) => decoded_frame,
820 Err(error) => {
821 debug!("decode Animated frame error: {error}");
822 return None;
823 },
824 };
825
826 is_opaque = rgba8_premultiply_inplace(animated_frame.buffer_mut()) && is_opaque;
830
831 let frame_start = total_number_of_bytes;
832 total_number_of_bytes += animated_frame.buffer().len();
833
834 let frame_width = animated_frame.buffer().width();
836 let frame_height = animated_frame.buffer().height();
837 width = cmp::max(width, frame_width);
838 height = cmp::max(height, frame_height);
839
840 let frame = ImageFrame {
841 byte_range: frame_start..total_number_of_bytes,
842 delay: Some(Duration::from(animated_frame.delay())),
843 width: frame_width,
844 height: frame_height,
845 };
846
847 frame_data.push(animated_frame);
848
849 Some(frame)
850 })
851 .collect();
852
853 if frames.is_empty() {
854 debug!("Animated Image decoding error");
855 return None;
856 }
857
858 let mut bytes = Vec::with_capacity(total_number_of_bytes);
860 for frame in frame_data {
861 bytes.extend_from_slice(frame.buffer());
862 }
863
864 Some(RasterImage {
865 metadata: ImageMetadata { width, height },
866 cors_status,
867 frames,
868 id: None,
869 format: PixelFormat::RGBA8,
870 bytes: Arc::new(bytes),
871 is_opaque,
872 })
873}
874
875#[cfg(test)]
876mod test {
877 use super::detect_image_format;
878
879 #[test]
880 fn test_supported_images() {
881 let gif1 = [b'G', b'I', b'F', b'8', b'7', b'a'];
882 let gif2 = [b'G', b'I', b'F', b'8', b'9', b'a'];
883 let jpeg = [0xff, 0xd8, 0xff];
884 let png = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
885 let webp = [
886 b'R', b'I', b'F', b'F', 0x04, 0x00, 0x00, 0x00, b'W', b'E', b'B', b'P',
887 ];
888 let bmp = [0x42, 0x4D];
889 let ico = [0x00, 0x00, 0x01, 0x00];
890 let junk_format = [0x01, 0x02, 0x03, 0x04, 0x05];
891
892 assert!(detect_image_format(&gif1).is_ok());
893 assert!(detect_image_format(&gif2).is_ok());
894 assert!(detect_image_format(&jpeg).is_ok());
895 assert!(detect_image_format(&png).is_ok());
896 assert!(detect_image_format(&webp).is_ok());
897 assert!(detect_image_format(&bmp).is_ok());
898 assert!(detect_image_format(&ico).is_ok());
899 assert!(detect_image_format(&junk_format).is_err());
900 }
901}