#![allow(dead_code)]
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::{BufRead, BufReader, Cursor, Seek};
use std::path::Path;
mod container;
mod formats;
mod util;
pub use container::{
atc::AtcCompression, dds::DdsCompression, heif::Compression, pkm::PkmCompression,
pvrtc::PvrtcCompression,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CompressionFamily {
BlockCompression,
Etc,
Eac,
Pvrtc,
Astc,
Atc,
Uncompressed,
}
use {
container::heif::{self},
formats::*,
};
#[derive(Debug)]
pub enum ImageError {
NotSupported,
CorruptedImage,
IoError(std::io::Error),
}
impl Error for ImageError {}
impl fmt::Display for ImageError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::ImageError::*;
match self {
NotSupported => f.write_str("Could not decode image"),
CorruptedImage => f.write_str("Hit end of file before finding size"),
IoError(error) => error.fmt(f),
}
}
}
impl From<std::io::Error> for ImageError {
fn from(err: std::io::Error) -> ImageError {
ImageError::IoError(err)
}
}
pub type ImageResult<T> = Result<T, ImageError>;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ImageType {
#[cfg(feature = "aesprite")]
Aseprite,
#[cfg(feature = "astc")]
Astc,
#[cfg(feature = "atc")]
Atc(AtcCompression),
#[cfg(feature = "bmp")]
Bmp,
#[cfg(feature = "dds")]
Dds(DdsCompression),
#[cfg(feature = "eac")]
Eac(PkmCompression),
#[cfg(feature = "etc2")]
Etc2(PkmCompression),
#[cfg(feature = "exr")]
Exr,
#[cfg(feature = "farbfeld")]
Farbfeld,
#[cfg(feature = "gif")]
Gif,
#[cfg(feature = "hdr")]
Hdr,
#[cfg(feature = "heif")]
Heif(Compression),
#[cfg(feature = "ico")]
Ico,
#[cfg(feature = "ilbm")]
Ilbm,
#[cfg(feature = "jpeg")]
Jpeg,
#[cfg(feature = "jxl")]
Jxl,
#[cfg(feature = "ktx2")]
Ktx2,
#[cfg(feature = "png")]
Png,
#[cfg(feature = "pnm")]
Pnm,
#[cfg(feature = "pvrtc")]
Pvrtc(PvrtcCompression),
#[cfg(feature = "psd")]
Psd,
#[cfg(feature = "qoi")]
Qoi,
#[cfg(feature = "tga")]
Tga,
#[cfg(feature = "tiff")]
Tiff,
#[cfg(feature = "vtf")]
Vtf,
#[cfg(feature = "webp")]
Webp,
}
impl ImageType {
pub fn compression_family(&self) -> Option<CompressionFamily> {
match self {
#[cfg(feature = "dds")]
ImageType::Dds(compression) => match compression {
DdsCompression::Bc1
| DdsCompression::Bc2
| DdsCompression::Bc3
| DdsCompression::Bc4
| DdsCompression::Bc5
| DdsCompression::Bc6h
| DdsCompression::Bc7 => Some(CompressionFamily::BlockCompression),
DdsCompression::Rgba32 | DdsCompression::Rgb24 => {
Some(CompressionFamily::Uncompressed)
}
DdsCompression::Unknown => None,
},
#[cfg(feature = "etc2")]
ImageType::Etc2(compression) => match compression {
PkmCompression::Etc1
| PkmCompression::Etc2
| PkmCompression::Etc2A1
| PkmCompression::Etc2A8 => Some(CompressionFamily::Etc),
PkmCompression::EacR
| PkmCompression::EacRg
| PkmCompression::EacRSigned
| PkmCompression::EacRgSigned => Some(CompressionFamily::Eac),
PkmCompression::Unknown => None,
},
#[cfg(feature = "eac")]
ImageType::Eac(compression) => match compression {
PkmCompression::EacR
| PkmCompression::EacRg
| PkmCompression::EacRSigned
| PkmCompression::EacRgSigned => Some(CompressionFamily::Eac),
PkmCompression::Etc1
| PkmCompression::Etc2
| PkmCompression::Etc2A1
| PkmCompression::Etc2A8 => Some(CompressionFamily::Etc),
PkmCompression::Unknown => None,
},
#[cfg(feature = "pvrtc")]
ImageType::Pvrtc(compression) => match compression {
PvrtcCompression::Pvrtc2BppRgb
| PvrtcCompression::Pvrtc2BppRgba
| PvrtcCompression::Pvrtc4BppRgb
| PvrtcCompression::Pvrtc4BppRgba => Some(CompressionFamily::Pvrtc),
PvrtcCompression::Etc2Rgb
| PvrtcCompression::Etc2Rgba
| PvrtcCompression::Etc2RgbA1 => Some(CompressionFamily::Etc),
PvrtcCompression::EacR11 | PvrtcCompression::EacRg11 => {
Some(CompressionFamily::Eac)
}
PvrtcCompression::Unknown => None,
},
#[cfg(feature = "atc")]
ImageType::Atc(_) => Some(CompressionFamily::Atc),
#[cfg(feature = "astc")]
ImageType::Astc => Some(CompressionFamily::Astc),
_ => None,
}
}
pub fn is_block_compressed(&self) -> bool {
matches!(
self.compression_family(),
Some(CompressionFamily::BlockCompression)
)
}
pub fn container_format(&self) -> Option<&'static str> {
match self {
#[cfg(feature = "dds")]
ImageType::Dds(_) => Some("DDS"),
#[cfg(feature = "etc2")]
ImageType::Etc2(_) => Some("PKM"),
#[cfg(feature = "eac")]
ImageType::Eac(_) => Some("PKM"),
#[cfg(feature = "pvrtc")]
ImageType::Pvrtc(_) => Some("PowerVR"),
#[cfg(feature = "atc")]
ImageType::Atc(_) => Some("PKM"),
#[cfg(feature = "astc")]
ImageType::Astc => Some("ASTC"),
#[cfg(feature = "heif")]
ImageType::Heif(_) => Some("HEIF"),
#[cfg(feature = "ktx2")]
ImageType::Ktx2 => Some("KTX2"),
_ => None,
}
}
pub fn is_multi_compression_container(&self) -> bool {
match self {
#[cfg(feature = "dds")]
ImageType::Dds(_) => true,
#[cfg(feature = "pvrtc")]
ImageType::Pvrtc(_) => true,
#[cfg(feature = "ktx2")]
ImageType::Ktx2 => true,
_ => false,
}
}
pub fn reader_size<R: BufRead + Seek>(&self, reader: &mut R) -> ImageResult<ImageSize> {
match self {
#[cfg(feature = "aesprite")]
ImageType::Aseprite => aesprite::size(reader),
#[cfg(feature = "astc")]
ImageType::Astc => astc::size(reader),
#[cfg(feature = "atc")]
ImageType::Atc(..) => container::atc::size(reader),
#[cfg(feature = "bmp")]
ImageType::Bmp => bmp::size(reader),
#[cfg(feature = "dds")]
ImageType::Dds(..) => container::dds::size(reader),
#[cfg(feature = "eac")]
ImageType::Eac(..) => container::pkm::size(reader),
#[cfg(feature = "etc2")]
ImageType::Etc2(..) => container::pkm::size(reader),
#[cfg(feature = "exr")]
ImageType::Exr => exr::size(reader),
#[cfg(feature = "farbfeld")]
ImageType::Farbfeld => farbfeld::size(reader),
#[cfg(feature = "gif")]
ImageType::Gif => gif::size(reader),
#[cfg(feature = "hdr")]
ImageType::Hdr => hdr::size(reader),
#[cfg(feature = "ico")]
ImageType::Ico => ico::size(reader),
#[cfg(feature = "ilbm")]
ImageType::Ilbm => ilbm::size(reader),
#[cfg(feature = "jpeg")]
ImageType::Jpeg => jpeg::size(reader),
#[cfg(feature = "jxl")]
ImageType::Jxl => jxl::size(reader),
#[cfg(feature = "ktx2")]
ImageType::Ktx2 => ktx2::size(reader),
#[cfg(feature = "png")]
ImageType::Png => png::size(reader),
#[cfg(feature = "pnm")]
ImageType::Pnm => pnm::size(reader),
#[cfg(feature = "pvrtc")]
ImageType::Pvrtc(..) => container::pvrtc::size(reader),
#[cfg(feature = "psd")]
ImageType::Psd => psd::size(reader),
#[cfg(feature = "qoi")]
ImageType::Qoi => qoi::size(reader),
#[cfg(feature = "tga")]
ImageType::Tga => tga::size(reader),
#[cfg(feature = "tiff")]
ImageType::Tiff => tiff::size(reader),
#[cfg(feature = "vtf")]
ImageType::Vtf => vtf::size(reader),
#[cfg(feature = "webp")]
ImageType::Webp => webp::size(reader),
#[cfg(feature = "heif")]
ImageType::Heif(..) => heif::size(reader),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ImageSize {
pub width: usize,
pub height: usize,
}
impl Ord for ImageSize {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(self.width * self.height).cmp(&(other.width * other.height))
}
}
impl PartialOrd for ImageSize {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
pub fn image_type(header: &[u8]) -> ImageResult<ImageType> {
formats::image_type(&mut Cursor::new(header))
}
pub fn size<P: AsRef<Path>>(path: P) -> ImageResult<ImageSize> {
let file = File::open(path)?;
let reader = BufReader::new(file);
reader_size(reader)
}
pub fn blob_size(data: &[u8]) -> ImageResult<ImageSize> {
let reader = Cursor::new(data);
reader_size(reader)
}
pub fn reader_size<R: BufRead + Seek>(mut reader: R) -> ImageResult<ImageSize> {
reader_type(&mut reader)?.reader_size(&mut reader)
}
pub fn reader_type<R: BufRead + Seek>(mut reader: R) -> ImageResult<ImageType> {
formats::image_type(&mut reader)
}