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