use std::io::Cursor;
use std::sync::Arc;
use std::time::Duration;
use std::{cmp, fmt, vec};
use image::codecs::{bmp, gif, ico, jpeg, png, webp};
use image::error::ImageFormatHint;
use image::metadata::LoopCount;
use image::{
AnimationDecoder, DynamicImage, ImageDecoder, ImageError, ImageFormat, ImageResult, Limits,
};
use log::debug;
use crate::{
CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage, Repeat,
rgba8_premultiply_inplace,
};
enum GenericImageDecoder<'a> {
Apng(Box<png::ApngDecoder<Cursor<&'a [u8]>>>),
Png(Box<png::PngDecoder<Cursor<&'a [u8]>>>),
Gif(Box<gif::GifDecoder<Cursor<&'a [u8]>>>),
Webp(Box<webp::WebPDecoder<Cursor<&'a [u8]>>>),
Jpeg(Box<jpeg::JpegDecoder<Cursor<&'a [u8]>>>),
Bmp(Box<bmp::BmpDecoder<Cursor<&'a [u8]>>>),
Ico(Box<ico::IcoDecoder<Cursor<&'a [u8]>>>),
}
impl<'a> std::fmt::Debug for GenericImageDecoder<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Apng(_) => f.debug_tuple("Apng").finish(),
Self::Png(_) => f.debug_tuple("Png").finish(),
Self::Gif(_) => f.debug_tuple("Gif").finish(),
Self::Webp(_) => f.debug_tuple("Webp").finish(),
Self::Jpeg(_) => f.debug_tuple("Jpeg").finish(),
Self::Bmp(_) => f.debug_tuple("Bmp").finish(),
Self::Ico(_) => f.debug_tuple("Ico").finish(),
}
}
}
impl<'a> image::ImageDecoder for GenericImageDecoder<'a> {
fn dimensions(&self) -> (u32, u32) {
match self {
GenericImageDecoder::Apng(_) => {
unreachable!("Animated image should never go into non-animated values")
},
GenericImageDecoder::Png(d) => d.dimensions(),
GenericImageDecoder::Gif(d) => d.dimensions(),
GenericImageDecoder::Webp(d) => d.dimensions(),
GenericImageDecoder::Jpeg(d) => d.dimensions(),
GenericImageDecoder::Bmp(d) => d.dimensions(),
GenericImageDecoder::Ico(d) => d.dimensions(),
}
}
fn color_type(&self) -> image::ColorType {
match self {
GenericImageDecoder::Apng(_) => {
unreachable!("Animated image should never go into non-animated values")
},
GenericImageDecoder::Png(d) => d.color_type(),
GenericImageDecoder::Gif(d) => d.color_type(),
GenericImageDecoder::Webp(d) => d.color_type(),
GenericImageDecoder::Jpeg(d) => d.color_type(),
GenericImageDecoder::Bmp(d) => d.color_type(),
GenericImageDecoder::Ico(d) => d.color_type(),
}
}
fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
where
Self: Sized,
{
match self {
GenericImageDecoder::Apng(_) => {
unreachable!("Animated image should never go into non-animated values")
},
GenericImageDecoder::Png(d) => d.read_image(buf),
GenericImageDecoder::Gif(d) => d.read_image(buf),
GenericImageDecoder::Webp(d) => d.read_image(buf),
GenericImageDecoder::Jpeg(d) => d.read_image(buf),
GenericImageDecoder::Bmp(d) => d.read_image(buf),
GenericImageDecoder::Ico(d) => d.read_image(buf),
}
}
fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
match *self {
GenericImageDecoder::Apng(_) => {
unreachable!("Animated image should never go into non-animated values")
},
GenericImageDecoder::Png(d) => d.read_image_boxed(buf),
GenericImageDecoder::Gif(d) => d.read_image_boxed(buf),
GenericImageDecoder::Webp(d) => d.read_image_boxed(buf),
GenericImageDecoder::Jpeg(d) => d.read_image_boxed(buf),
GenericImageDecoder::Bmp(d) => d.read_image_boxed(buf),
GenericImageDecoder::Ico(d) => d.read_image_boxed(buf),
}
}
fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
match self {
GenericImageDecoder::Apng(_) => {
unreachable!("Animated image should never go into non-animated values")
},
GenericImageDecoder::Png(d) => d.icc_profile(),
GenericImageDecoder::Gif(d) => d.icc_profile(),
GenericImageDecoder::Webp(d) => d.icc_profile(),
GenericImageDecoder::Jpeg(d) => d.icc_profile(),
GenericImageDecoder::Bmp(d) => d.icc_profile(),
GenericImageDecoder::Ico(d) => d.icc_profile(),
}
}
fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
match self {
GenericImageDecoder::Apng(_) => {
unreachable!("Animated image should never go into non-animated values")
},
GenericImageDecoder::Png(d) => d.exif_metadata(),
GenericImageDecoder::Gif(d) => d.exif_metadata(),
GenericImageDecoder::Webp(d) => d.exif_metadata(),
GenericImageDecoder::Jpeg(d) => d.exif_metadata(),
GenericImageDecoder::Bmp(d) => d.exif_metadata(),
GenericImageDecoder::Ico(d) => d.exif_metadata(),
}
}
fn xmp_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
match self {
GenericImageDecoder::Apng(_) => {
unreachable!("Animated image should never go into non-animated values")
},
GenericImageDecoder::Png(d) => d.xmp_metadata(),
GenericImageDecoder::Gif(d) => d.xmp_metadata(),
GenericImageDecoder::Webp(d) => d.xmp_metadata(),
GenericImageDecoder::Jpeg(d) => d.xmp_metadata(),
GenericImageDecoder::Bmp(d) => d.xmp_metadata(),
GenericImageDecoder::Ico(d) => d.xmp_metadata(),
}
}
fn iptc_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
match self {
GenericImageDecoder::Apng(_) => {
unreachable!("Animated image should never go into non-animated values")
},
GenericImageDecoder::Png(d) => d.iptc_metadata(),
GenericImageDecoder::Gif(d) => d.iptc_metadata(),
GenericImageDecoder::Webp(d) => d.iptc_metadata(),
GenericImageDecoder::Jpeg(d) => d.iptc_metadata(),
GenericImageDecoder::Bmp(d) => d.iptc_metadata(),
GenericImageDecoder::Ico(d) => d.iptc_metadata(),
}
}
}
impl<'a> AnimationDecoder<'a> for GenericImageDecoder<'a> {
fn into_frames(self) -> image::Frames<'a> {
match self {
GenericImageDecoder::Apng(decoder) => decoder.into_frames(),
GenericImageDecoder::Gif(decoder) => decoder.into_frames(),
GenericImageDecoder::Webp(decoder) => decoder.into_frames(),
_ => unreachable!("Should never decode these images with animated decoder"),
}
}
fn loop_count(&self) -> LoopCount {
match self {
GenericImageDecoder::Apng(decoder) => decoder.loop_count(),
GenericImageDecoder::Gif(decoder) => decoder.loop_count(),
GenericImageDecoder::Webp(decoder) => decoder.loop_count(),
_ => unreachable!("Should never decode these images with animated decoder"),
}
}
}
#[derive(Debug)]
pub(crate) struct DefaultImageDecoder<'a> {
decoder: GenericImageDecoder<'a>,
}
pub(crate) trait ServoImageDecoder<'a>: Sized + std::fmt::Debug {
fn make_decoder(format: ImageFormat, buffer: &'a [u8]) -> ImageResult<Self>;
fn is_animated(&self) -> bool;
fn decoder(self) -> impl ImageDecoder;
fn animated_decoder(self) -> impl AnimationDecoder<'a>;
}
impl<'a> ServoImageDecoder<'a> for DefaultImageDecoder<'a> {
fn make_decoder(format: ImageFormat, buffer: &'a [u8]) -> ImageResult<Self> {
let reader = Cursor::new(buffer);
let decoder = match format {
ImageFormat::Png => {
let limits = Limits::default();
let png_decoder = png::PngDecoder::with_limits(reader, limits)?;
if png_decoder.is_apng().unwrap_or_default() {
let decoder = png_decoder.apng()?;
GenericImageDecoder::Apng(Box::new(decoder))
} else {
GenericImageDecoder::Png(Box::new(png_decoder))
}
},
ImageFormat::Gif => GenericImageDecoder::Gif(Box::new(gif::GifDecoder::new(reader)?)),
ImageFormat::WebP => {
GenericImageDecoder::Webp(Box::new(webp::WebPDecoder::new(reader)?))
},
ImageFormat::Jpeg => {
GenericImageDecoder::Jpeg(Box::new(jpeg::JpegDecoder::new(reader)?))
},
ImageFormat::Bmp => GenericImageDecoder::Bmp(Box::new(bmp::BmpDecoder::new(reader)?)),
ImageFormat::Ico => GenericImageDecoder::Ico(Box::new(ico::IcoDecoder::new(reader)?)),
_ => {
return Err(ImageError::Unsupported(
ImageFormatHint::Exact(format).into(),
));
},
};
Ok(DefaultImageDecoder { decoder })
}
fn is_animated(&self) -> bool {
match &self.decoder {
GenericImageDecoder::Apng(_) | GenericImageDecoder::Gif(_) => true,
GenericImageDecoder::Webp(decoder) => decoder.has_animation(),
GenericImageDecoder::Png(_) |
GenericImageDecoder::Jpeg(_) |
GenericImageDecoder::Bmp(_) |
GenericImageDecoder::Ico(_) => false,
}
}
fn decoder(self) -> impl ImageDecoder {
self.decoder
}
fn animated_decoder(self) -> impl AnimationDecoder<'a> {
self.decoder
}
}
pub(crate) fn decode_static_image(
cors_status: CorsStatus,
mut image_decoder: impl ImageDecoder,
) -> Option<RasterImage> {
let orientation = image_decoder.orientation();
let Ok(mut dynamic_image) = DynamicImage::from_decoder(image_decoder) else {
debug!("Image decoding error");
return None;
};
if let Ok(orientation) = orientation {
dynamic_image.apply_orientation(orientation);
}
let mut rgba = dynamic_image.into_rgba8();
let is_opaque = rgba8_premultiply_inplace(&mut rgba);
let frame = ImageFrame {
delay: None,
byte_range: 0..rgba.len(),
width: rgba.width(),
height: rgba.height(),
};
Some(RasterImage {
metadata: ImageMetadata {
width: rgba.width(),
height: rgba.height(),
},
format: PixelFormat::RGBA8,
frames: vec![frame],
bytes: Arc::new(rgba.into_vec()),
id: None,
cors_status,
is_opaque,
loop_count: None,
})
}
pub(crate) fn decode_animated_image<'a, T>(
cors_status: CorsStatus,
animated_image_decoder: T,
) -> Option<RasterImage>
where
T: AnimationDecoder<'a>,
{
let mut width = 0;
let mut height = 0;
let mut frame_data = vec![];
let mut total_number_of_bytes = 0;
let mut is_opaque = true;
let loop_count = match animated_image_decoder.loop_count() {
LoopCount::Finite(repeat_time) => Repeat::Finite(repeat_time),
LoopCount::Infinite => Repeat::Infinite,
};
let frames: Vec<ImageFrame> = animated_image_decoder
.into_frames()
.map_while(|decoded_frame| {
let mut animated_frame = match decoded_frame {
Ok(decoded_frame) => decoded_frame,
Err(error) => {
debug!("decode Animated frame error: {error}");
return None;
},
};
is_opaque = rgba8_premultiply_inplace(animated_frame.buffer_mut()) && is_opaque;
let frame_start = total_number_of_bytes;
total_number_of_bytes += animated_frame.buffer().len();
let frame_width = animated_frame.buffer().width();
let frame_height = animated_frame.buffer().height();
width = cmp::max(width, frame_width);
height = cmp::max(height, frame_height);
let frame = ImageFrame {
byte_range: frame_start..total_number_of_bytes,
delay: Some(Duration::from(animated_frame.delay())),
width: frame_width,
height: frame_height,
};
frame_data.push(animated_frame);
Some(frame)
})
.collect();
if frames.is_empty() {
debug!("Animated Image decoding error");
return None;
}
let mut bytes = Vec::with_capacity(total_number_of_bytes);
for frame in frame_data {
bytes.extend_from_slice(frame.buffer());
}
Some(RasterImage {
metadata: ImageMetadata { width, height },
cors_status,
frames,
id: None,
format: PixelFormat::RGBA8,
bytes: Arc::new(bytes),
is_opaque,
loop_count: Some(loop_count),
})
}