use std::{io::Cursor, time::Duration};
use image::{
AnimationDecoder, DynamicImage, Frame, ImageBuffer, ImageFormat, ImageReader, Rgb, Rgba,
codecs::{gif::GifDecoder, openexr::OpenExrDecoder, png::PngDecoder, webp::WebPDecoder},
};
use imagepipe::{ImageSource, Pipeline};
use psd::Psd;
use resvg::usvg::{Options, Tree};
use crate::{app::preferences::PREFERENCES, util::Image};
pub fn decode_images<T, E>(frames: T) -> Vec<Image>
where
T: IntoIterator<Item = Result<Frame, E>>,
{
frames
.into_iter()
.filter_map(|result| match result {
Ok(frame) => Some(frame.into()),
Err(_) => None,
})
.collect()
}
pub fn load_raster(bytes: &[u8]) -> Option<Vec<Image>> {
let format = match image::guess_format(bytes) {
Ok(format) => format,
Err(_) => return None,
};
match format {
ImageFormat::Gif => {
if let Ok(decoder) = GifDecoder::new(Cursor::new(bytes)) {
return Some(decode_images(decoder.into_frames()));
}
None
}
ImageFormat::WebP => {
if let Ok(decoder) = webp_animation::Decoder::new(bytes) {
let mut time = 0;
let frames: Vec<Image> = decoder
.into_iter()
.filter_map(|frame| {
let timestamp = frame.timestamp();
let difference = timestamp - time;
let (width, height) = frame.dimensions();
let data = frame.data().to_vec();
ImageBuffer::from_raw(width, height, data).map(|image| {
time = timestamp;
let delay = Duration::from_millis(difference as u64);
Image::with_delay(DynamicImage::ImageRgba8(image), delay)
})
})
.collect();
if !frames.is_empty() {
return Some(frames);
}
}
if let Ok(decoder) = WebPDecoder::new(Cursor::new(bytes)) {
if decoder.has_animation() {
return Some(decode_images(decoder.into_frames()));
} else if let Ok(image) = DynamicImage::from_decoder(decoder) {
return Some(vec![Image::new(image)]);
}
}
None
}
ImageFormat::Png => {
if let Ok(decoder) = PngDecoder::new(Cursor::new(bytes)) {
if decoder.is_apng().unwrap_or(false) {
return Some(decode_images(decoder.apng().ok()?.into_frames()));
} else if let Ok(image) = DynamicImage::from_decoder(decoder) {
return Some(vec![Image::new(image)]);
}
}
None
}
ImageFormat::OpenExr => {
if let Ok(decoder) = OpenExrDecoder::new(Cursor::new(bytes))
&& let Ok(mut image) = DynamicImage::from_decoder(decoder)
{
match &mut image {
DynamicImage::ImageRgb32F(buffer) => {
for sample in buffer.as_flat_samples_mut().as_mut_slice() {
if sample.is_nan() {
*sample = 0.0;
}
}
}
DynamicImage::ImageRgba32F(buffer) => {
for sample in buffer.as_flat_samples_mut().as_mut_slice() {
if sample.is_nan() {
*sample = 0.0;
}
}
}
_ => (),
}
return Some(vec![Image::new(image)]);
}
None
}
format => {
let mut reader = ImageReader::with_format(Cursor::new(&bytes), format);
reader.no_limits();
match reader.decode() {
Ok(image) => Some(vec![Image::new(image)]),
Err(_) => None,
}
}
}
}
pub fn load_un_detectable_raster(bytes: &[u8]) -> Option<Vec<Image>> {
match ImageReader::with_format(Cursor::new(&bytes), ImageFormat::Tga).decode() {
Ok(image) => Some(vec![Image::new(image)]),
Err(_) => None,
}
}
pub fn load_svg(bytes: &[u8]) -> Option<Vec<Image>> {
let options = Options::default();
let tree = Tree::from_data(bytes, &options).ok()?;
let size = tree.size().to_int_size();
let min_size = PREFERENCES.lock().unwrap().min_svg_size.max(100);
let smaller_axis = size.width().min(size.height());
let size = if smaller_axis < min_size {
let scale_factor = min_size as f32 / smaller_axis as f32;
size.scale_by(scale_factor).unwrap_or(size)
} else {
size
};
let transform = resvg::tiny_skia::Transform::from_scale(
size.width() as f32 / tree.size().width(),
size.height() as f32 / tree.size().height(),
);
let mut pix_map = resvg::tiny_skia::Pixmap::new(size.width(), size.height()).unwrap();
resvg::render(&tree, transform, &mut pix_map.as_mut());
let width = pix_map.width();
let height = pix_map.height();
let data = pix_map.take();
Some(vec![Image::from(ImageBuffer::<Rgba<u8>, _>::from_raw(
width, height, data,
)?)])
}
pub fn load_psd(bytes: &[u8]) -> Option<Vec<Image>> {
let psd = match Psd::from_bytes(bytes) {
Ok(psd) => psd,
Err(_) => return None,
};
let raw = psd.rgba();
Some(vec![Image::from(
ImageBuffer::<Rgba<u8>, _>::from_raw(psd.width(), psd.height(), raw).unwrap(),
)])
}
pub fn load_raw(bytes: &[u8]) -> Option<Vec<Image>> {
let Ok(raw) = rawloader::decode(&mut Cursor::new(bytes)) else {
return None;
};
let source = ImageSource::Raw(raw);
let Ok(mut pipeline) = Pipeline::new_from_source(source) else {
return None;
};
pipeline.run(None);
let Ok(image) = pipeline.output_16bit(None) else {
return None;
};
let image = ImageBuffer::<Rgb<u16>, Vec<u16>>::from_raw(
image.width as u32,
image.height as u32,
image.data,
)?;
let image = image::DynamicImage::ImageRgb16(image);
Some(vec![Image::new(image)])
}
pub fn load_jxl(bytes: &[u8]) -> Option<Vec<Image>> {
#[cfg(feature = "jxl")]
{
use jpegxl_rs::image::ToDynamic;
let decoder = jpegxl_rs::decoder_builder()
.unpremul_alpha(true)
.build()
.ok()?;
let image = decoder.decode_to_image(bytes).ok()??;
Some(vec![Image::new(image)])
}
#[cfg(not(feature = "jxl"))]
{
let _ = bytes;
None
}
}
pub fn load_heif(bytes: &[u8]) -> Option<Vec<Image>> {
#[cfg(feature = "heif")]
{
use image::RgbaImage;
use libheif_rs::{ColorSpace, DecodingOptions, HeifContext, LibHeif, RgbChroma};
let lib_heif = LibHeif::new();
let ctx = HeifContext::read_from_bytes(bytes).ok()?;
let handle = ctx.primary_image_handle().ok()?;
let image = lib_heif
.decode(
&handle,
ColorSpace::Rgb(RgbChroma::Rgba),
DecodingOptions::new().map(|mut o| {
o.set_convert_hdr_to_8bit(true);
o
}),
)
.ok()?;
let planes = image.planes();
let interleaved_plane = planes.interleaved?;
let raw = interleaved_plane.data.to_vec();
let rgba_image =
RgbaImage::from_raw(interleaved_plane.width, interleaved_plane.height, raw)?;
Some(vec![Image::new(DynamicImage::ImageRgba8(rgba_image))])
}
#[cfg(not(feature = "heif"))]
{
let _ = bytes;
None
}
}