use std::{error, fmt};
use std::io::Read;
use byteorder::{LittleEndian, ReadBytesExt};
use crate::color::ColorType;
use crate::dxt::{DxtDecoder, DxtReader, DXTVariant};
use crate::error::{
DecodingError, ImageError, ImageFormatHint, ImageResult, UnsupportedError, UnsupportedErrorKind,
};
use crate::image::{ImageDecoder, ImageFormat};
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum DecoderError {
PixelFormatSizeInvalid(u32),
HeaderSizeInvalid(u32),
HeaderFlagsInvalid(u32),
DdsSignatureInvalid,
}
impl fmt::Display for DecoderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DecoderError::PixelFormatSizeInvalid(s) =>
f.write_fmt(format_args!("Invalid DDS PixelFormat size: {}", s)),
DecoderError::HeaderSizeInvalid(s) =>
f.write_fmt(format_args!("Invalid DDS header size: {}", s)),
DecoderError::HeaderFlagsInvalid(fs) =>
f.write_fmt(format_args!("Invalid DDS header flags: {:#010X}", fs)),
DecoderError::DdsSignatureInvalid =>
f.write_str("DDS signature not found"),
}
}
}
impl From<DecoderError> for ImageError {
fn from(e: DecoderError) -> ImageError {
ImageError::Decoding(DecodingError::new(ImageFormat::Dds.into(), e))
}
}
impl error::Error for DecoderError {}
#[derive(Debug)]
struct Header {
flags: u32,
height: u32,
width: u32,
pitch_or_linear_size: u32,
depth: u32,
mipmap_count: u32,
pixel_format: PixelFormat,
caps: u32,
caps2: u32,
}
#[derive(Debug)]
struct PixelFormat {
flags: u32,
fourcc: [u8; 4],
rgb_bit_count: u32,
r_bit_mask: u32,
g_bit_mask: u32,
b_bit_mask: u32,
a_bit_mask: u32,
}
impl PixelFormat {
fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
let size = r.read_u32::<LittleEndian>()?;
if size != 32 {
return Err(DecoderError::PixelFormatSizeInvalid(size).into());
}
Ok(Self {
flags: r.read_u32::<LittleEndian>()?,
fourcc: {
let mut v = [0; 4];
r.read_exact(&mut v)?;
v
},
rgb_bit_count: r.read_u32::<LittleEndian>()?,
r_bit_mask: r.read_u32::<LittleEndian>()?,
g_bit_mask: r.read_u32::<LittleEndian>()?,
b_bit_mask: r.read_u32::<LittleEndian>()?,
a_bit_mask: r.read_u32::<LittleEndian>()?,
})
}
}
impl Header {
fn from_reader(r: &mut dyn Read) -> ImageResult<Self> {
let size = r.read_u32::<LittleEndian>()?;
if size != 124 {
return Err(DecoderError::HeaderSizeInvalid(size).into());
}
const REQUIRED_FLAGS: u32 = 0x1 | 0x2 | 0x4 | 0x1000;
const VALID_FLAGS: u32 = 0x1 | 0x2 | 0x4 | 0x8 | 0x1000 | 0x20000 | 0x80000 | 0x800000;
let flags = r.read_u32::<LittleEndian>()?;
if flags & (REQUIRED_FLAGS | !VALID_FLAGS) != REQUIRED_FLAGS {
return Err(DecoderError::HeaderFlagsInvalid(flags).into());
}
let height = r.read_u32::<LittleEndian>()?;
let width = r.read_u32::<LittleEndian>()?;
let pitch_or_linear_size = r.read_u32::<LittleEndian>()?;
let depth = r.read_u32::<LittleEndian>()?;
let mipmap_count = r.read_u32::<LittleEndian>()?;
{
let mut skipped = [0; 4 * 11];
r.read_exact(&mut skipped)?;
}
let pixel_format = PixelFormat::from_reader(r)?;
let caps = r.read_u32::<LittleEndian>()?;
let caps2 = r.read_u32::<LittleEndian>()?;
{
let mut skipped = [0; 4 + 4 + 4];
r.read_exact(&mut skipped)?;
}
Ok(Self {
flags,
height,
width,
pitch_or_linear_size,
depth,
mipmap_count,
pixel_format,
caps,
caps2,
})
}
}
pub struct DdsDecoder<R: Read> {
inner: DxtDecoder<R>,
}
impl<R: Read> DdsDecoder<R> {
pub fn new(mut r: R) -> ImageResult<Self> {
let mut magic = [0; 4];
r.read_exact(&mut magic)?;
if magic != b"DDS "[..] {
return Err(DecoderError::DdsSignatureInvalid.into());
}
let header = Header::from_reader(&mut r)?;
if header.pixel_format.flags & 0x4 != 0 {
let variant = match &header.pixel_format.fourcc {
b"DXT1" => DXTVariant::DXT1,
b"DXT3" => DXTVariant::DXT3,
b"DXT5" => DXTVariant::DXT5,
fourcc => {
return Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Dds.into(),
UnsupportedErrorKind::GenericFeature(format!("DDS FourCC {:?}", fourcc)),
),
))
}
};
if crate::utils::check_dimension_overflow(
header.width,
header.height,
variant.color_type().bytes_per_pixel(),
) {
return Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Dds.into(),
UnsupportedErrorKind::GenericFeature(format!(
"Image dimensions ({}x{}) are too large",
header.width, header.height
)),
),
));
}
let inner = DxtDecoder::new(r, header.width, header.height, variant)?;
Ok(Self { inner })
} else {
Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Dds.into(),
UnsupportedErrorKind::Format(ImageFormatHint::Name("DDS".to_string())),
),
))
}
}
}
impl<'a, R: 'a + Read> ImageDecoder<'a> for DdsDecoder<R> {
type Reader = DxtReader<R>;
fn dimensions(&self) -> (u32, u32) {
self.inner.dimensions()
}
fn color_type(&self) -> ColorType {
self.inner.color_type()
}
fn scanline_bytes(&self) -> u64 {
self.inner.scanline_bytes()
}
fn into_reader(self) -> ImageResult<Self::Reader> {
self.inner.into_reader()
}
fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
self.inner.read_image(buf)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn dimension_overflow() {
let header = vec![
0x44, 0x44, 0x53, 0x20, 0x7C, 0x0, 0x0, 0x0, 0x7, 0x10, 0x8, 0x0, 0xFC, 0xFF, 0xFF, 0xFF, 0xFC,
0xFF, 0xFF, 0xFF, 0x0, 0xC0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x49, 0x4D,
0x41, 0x47, 0x45, 0x4D, 0x41, 0x47, 0x49, 0x43, 0x4B, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x44,
0x58, 0x54, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
];
assert!(DdsDecoder::new(&header[..]).is_err());
}
}