#![allow(unused_imports, unused_variables, non_camel_case_types)]
use std::io::Cursor;
use std::path::Path;
use zune_core::bytestream::{ZByteReader, ZReaderTrait};
use zune_core::log::trace;
use zune_core::options::{DecoderOptions, EncoderOptions};
use crate::codecs;
use crate::codecs::ImageFormat::JPEG_XL;
use crate::errors::ImgEncodeErrors::ImageEncodeErrors;
use crate::errors::{ImageErrors, ImgEncodeErrors};
use crate::image::Image;
use crate::traits::{DecoderTrait, EncoderTrait};
pub mod bmp;
mod exr;
pub mod farbfeld;
pub mod hdr;
pub mod jpeg;
pub mod jpeg_xl;
pub mod png;
pub mod ppm;
pub mod psd;
pub mod qoi;
pub(crate) fn create_options_for_encoder(
options: Option<EncoderOptions>, image: &Image
) -> EncoderOptions {
let start_options = if let Some(configured_opts) = options {
configured_opts
} else {
EncoderOptions::default()
};
let (width, height) = image.dimensions();
start_options
.set_width(width)
.set_height(height)
.set_depth(image.depth())
.set_colorspace(image.colorspace())
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ImageFormat {
JPEG,
PNG,
PPM,
PSD,
Farbfeld,
QOI,
JPEG_XL,
HDR,
BMP,
Unknown
}
impl ImageFormat {
pub fn has_encoder(self) -> bool {
self.get_encoder().is_some()
}
pub fn has_decoder(self) -> bool {
#[cfg(feature = "jpeg-xl")]
{
if self == JPEG_XL {
return true;
}
}
return self.get_decoder::<&[u8]>(&[]).is_ok();
}
pub fn get_decoder<'a, T>(&self, data: T) -> Result<Box<dyn DecoderTrait<T> + 'a>, ImageErrors>
where
T: ZReaderTrait + 'a
{
self.get_decoder_with_options(data, DecoderOptions::default())
}
pub fn get_decoder_with_options<'a, T>(
&self, data: T, options: DecoderOptions
) -> Result<Box<dyn DecoderTrait<T> + 'a>, ImageErrors>
where
T: ZReaderTrait + 'a
{
match self {
ImageFormat::JPEG => {
#[cfg(feature = "jpeg")]
{
Ok(Box::new(zune_jpeg::JpegDecoder::new_with_options(
data, options
)))
}
#[cfg(not(feature = "jpeg"))]
{
Err(ImageErrors::ImageDecoderNotIncluded(*self))
}
}
ImageFormat::PNG => {
#[cfg(feature = "png")]
{
Ok(Box::new(zune_png::PngDecoder::new_with_options(
data, options
)))
}
#[cfg(not(feature = "png"))]
{
Err(ImageErrors::ImageDecoderNotIncluded(*self))
}
}
ImageFormat::PPM => {
#[cfg(feature = "ppm")]
{
Ok(Box::new(zune_ppm::PPMDecoder::new_with_options(
data, options
)))
}
#[cfg(not(feature = "ppm"))]
{
Err(ImageErrors::ImageDecoderNotIncluded(*self))
}
}
ImageFormat::PSD => {
#[cfg(feature = "ppm")]
{
Ok(Box::new(zune_psd::PSDDecoder::new_with_options(
data, options
)))
}
#[cfg(not(feature = "ppm"))]
{
Err(ImageErrors::ImageDecoderNotIncluded(*self))
}
}
ImageFormat::Farbfeld => {
#[cfg(feature = "farbfeld")]
{
Ok(Box::new(zune_farbfeld::FarbFeldDecoder::new_with_options(
data, options
)))
}
#[cfg(not(feature = "farbfeld"))]
{
Err(ImageErrors::ImageDecoderNotIncluded(*self))
}
}
ImageFormat::QOI => {
#[cfg(feature = "qoi")]
{
Ok(Box::new(zune_qoi::QoiDecoder::new_with_options(
data, options
)))
}
#[cfg(not(feature = "qoi"))]
{
Err(ImageErrors::ImageDecoderNotIncluded(*self))
}
}
ImageFormat::HDR => {
#[cfg(feature = "hdr")]
{
Ok(Box::new(zune_hdr::HdrDecoder::new_with_options(
data, options
)))
}
#[cfg(not(feature = "hdr"))]
{
Err(ImageErrors::ImageDecoderNotIncluded(*self))
}
}
ImageFormat::BMP => {
#[cfg(feature = "bmp")]
{
Ok(Box::new(zune_bmp::BmpDecoder::new_with_options(
data, options
)))
}
#[cfg(not(feature = "bmp"))]
{
Err(ImageErrors::ImageDecoderNotIncluded(*self))
}
}
ImageFormat::JPEG_XL => {
#[cfg(feature = "jpeg-xl")]
{
let reader = ZByteReader::new(data);
Ok(Box::new(codecs::jpeg_xl::JxlDecoder::try_new(
reader, options
)?))
}
#[cfg(not(feature = "jpeg-xl"))]
{
Err(ImageErrors::ImageDecoderNotIncluded(*self))
}
}
ImageFormat::Unknown => Err(ImageErrors::ImageDecoderNotImplemented(*self))
}
}
pub fn get_encoder(&self) -> Option<Box<dyn EncoderTrait>> {
self.get_encoder_with_options(EncoderOptions::default())
}
pub fn get_encoder_with_options(
&self, options: EncoderOptions
) -> Option<Box<dyn EncoderTrait>> {
match self {
Self::PPM => {
#[cfg(feature = "ppm")]
{
Some(Box::new(crate::codecs::ppm::PPMEncoder::new_with_options(
options
)))
}
#[cfg(not(feature = "ppm"))]
{
None
}
}
Self::QOI => {
#[cfg(feature = "qoi")]
{
Some(Box::new(crate::codecs::qoi::QoiEncoder::new_with_options(
options
)))
}
#[cfg(not(feature = "qoi"))]
{
None
}
}
Self::JPEG => {
#[cfg(feature = "jpeg")]
{
Some(Box::new(
crate::codecs::jpeg::JpegEncoder::new_with_options(options)
))
}
#[cfg(not(feature = "jpeg"))]
{
None
}
}
Self::JPEG_XL => {
#[cfg(feature = "jpeg-xl")]
{
Some(Box::new(
crate::codecs::jpeg_xl::JxlEncoder::new_with_options(options)
))
}
#[cfg(not(feature = "jpeg-xl"))]
{
None
}
}
Self::HDR => {
#[cfg(feature = "hdr")]
{
Some(Box::new(crate::codecs::hdr::HdrEncoder::new_with_options(
options
)))
}
#[cfg(not(feature = "hdr"))]
{
None
}
}
Self::PNG => {
#[cfg(feature = "png")]
{
Some(Box::new(codecs::png::PngEncoder::new_with_options(options)))
}
#[cfg(not(feature = "png"))]
{
None
}
}
_ => None
}
}
pub fn guess_format<T>(bytes: T) -> Option<(ImageFormat, T)>
where
T: ZReaderTrait
{
guess_format(bytes)
}
pub fn get_encoder_for_extension<P: AsRef<str>>(
extension: P
) -> Option<(ImageFormat, Box<dyn EncoderTrait>)> {
match extension.as_ref() {
"qoi" => {
#[cfg(feature = "qoi")]
{
Some((ImageFormat::QOI, ImageFormat::QOI.get_encoder().unwrap()))
}
#[cfg(not(feature = "qoi"))]
{
None
}
}
"ppm" | "pam" | "pgm" | "pbm" | "pfm" => {
#[cfg(feature = "ppm")]
{
Some((ImageFormat::PPM, ImageFormat::PPM.get_encoder().unwrap()))
}
#[cfg(not(feature = "ppm"))]
{
None
}
}
"jpeg" | "jpg" => {
#[cfg(feature = "jpeg")]
{
Some((ImageFormat::JPEG, ImageFormat::JPEG.get_encoder().unwrap()))
}
#[cfg(not(feature = "jpeg"))]
{
None
}
}
"jxl" => {
#[cfg(feature = "jpeg-xl")]
{
Some((
ImageFormat::JPEG_XL,
ImageFormat::JPEG_XL.get_encoder().unwrap()
))
}
#[cfg(not(feature = "jpeg-xl"))]
{
None
}
}
"ff" => {
#[cfg(feature = "farbfeld")]
{
Some((
ImageFormat::Farbfeld,
ImageFormat::Farbfeld.get_encoder().unwrap()
))
}
#[cfg(not(feature = "farbfeld"))]
{
None
}
}
"hdr" => {
#[cfg(feature = "hdr")]
{
Some((ImageFormat::HDR, ImageFormat::HDR.get_encoder().unwrap()))
}
#[cfg(not(feature = "hdr"))]
{
None
}
}
"png" => {
#[cfg(feature = "png")]
{
Some((ImageFormat::PNG, ImageFormat::PNG.get_encoder().unwrap()))
}
#[cfg(not(feature = "png"))]
{
None
}
}
_ => None
}
}
}
impl Image {
pub fn save<P: AsRef<Path>>(&self, file: P) -> Result<(), ImageErrors> {
return if let Some(ext) = file.as_ref().extension() {
if let Some((format, _)) = ImageFormat::get_encoder_for_extension(ext.to_str().unwrap())
{
self.save_to(file, format)
} else {
let msg = format!("No encoder for extension {ext:?}");
Err(ImageErrors::EncodeErrors(ImgEncodeErrors::Generic(msg)))
}
} else {
let msg = format!("No extension for file {:?}", file.as_ref());
Err(ImageErrors::EncodeErrors(ImgEncodeErrors::Generic(msg)))
};
}
pub fn save_to<P: AsRef<Path>>(&self, file: P, format: ImageFormat) -> Result<(), ImageErrors> {
let contents = self.write_to_vec(format)?;
std::fs::write(file, contents)?;
Ok(())
}
pub fn write_to_vec(&self, format: ImageFormat) -> Result<Vec<u8>, ImageErrors> {
if let Some(mut encoder) = format.get_encoder() {
encoder.encode(self)
} else {
Err(ImageErrors::EncodeErrors(
crate::errors::ImgEncodeErrors::NoEncoderForFormat(format)
))
}
}
pub fn open<P: AsRef<Path>>(file: P) -> Result<Image, ImageErrors> {
Self::open_with_options(file, DecoderOptions::default())
}
pub fn open_with_options<P: AsRef<Path>>(
file: P, options: DecoderOptions
) -> Result<Image, ImageErrors> {
let file = std::fs::read(file)?;
Self::read(file, options)
}
pub fn read<T>(src: T, options: DecoderOptions) -> Result<Image, ImageErrors>
where
T: ZReaderTrait
{
let decoder = ImageFormat::guess_format(src);
if let Some(format) = decoder {
let mut image_decoder = format.0.get_decoder_with_options(format.1, options)?;
let mut image = image_decoder.decode()?;
image.metadata.format = Some(format.0);
Ok(image)
} else {
Err(ImageErrors::ImageDecoderNotImplemented(
ImageFormat::Unknown
))
}
}
}
pub fn guess_format<T>(bytes: T) -> Option<(ImageFormat, T)>
where
T: ZReaderTrait
{
let reader = ZByteReader::new(bytes);
let magic_bytes: Vec<(&[u8], ImageFormat)> = vec![
(&[137, 80, 78, 71, 13, 10, 26, 10], ImageFormat::PNG),
(&[0xff, 0xd8], ImageFormat::JPEG),
(b"P5", ImageFormat::PPM),
(b"P6", ImageFormat::PPM),
(b"P7", ImageFormat::PPM),
(b"Pf", ImageFormat::PPM),
(b"PF", ImageFormat::PPM),
(b"8BPS", ImageFormat::PSD),
(b"farbfeld", ImageFormat::Farbfeld),
(b"qoif", ImageFormat::QOI),
(b"#?RADIANCE\n", ImageFormat::HDR),
(b"#?RGBE\n", ImageFormat::HDR),
(
&[
0x00, 0x00, 0x00, 0x0C, 0x4A, 0x58, 0x4C, 0x20, 0x0D, 0x0A, 0x87, 0x0A
],
ImageFormat::JPEG_XL
),
(&[0xFF, 0x0A], ImageFormat::JPEG_XL),
];
for (magic, decoder) in magic_bytes {
if (reader.has(magic.len())) && reader.peek_at(0, magic.len()).unwrap().starts_with(magic) {
return Some((decoder, reader.consume()));
}
}
#[cfg(feature = "bmp")]
{
if reader.has(16) {
let reference = reader.peek_at(0, 16).unwrap();
if zune_bmp::probe_bmp(reference) {
return Some((ImageFormat::BMP, reader.consume()));
}
}
}
None
}