#[cfg(test)]
mod tests;
mod error;
pub use error::*;
mod format;
pub use format::{D3DFormat, DataFormat, DxgiFormat, FourCC, PixelFormat, PixelFormatFlags};
mod header;
pub use header::{Caps, Caps2, Header, HeaderFlags};
mod header10;
pub use header10::{AlphaMode, D3D10ResourceDimension, Header10, MiscFlag};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
use std::io::{Read, Write};
#[derive(Clone)]
pub struct Dds {
pub header: Header,
pub header10: Option<Header10>,
pub data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct NewD3dParams {
pub height: u32,
pub width: u32,
pub depth: Option<u32>,
pub format: D3DFormat,
pub mipmap_levels: Option<u32>,
pub caps2: Option<Caps2>,
}
#[derive(Debug, Clone)]
pub struct NewDxgiParams {
pub height: u32,
pub width: u32,
pub depth: Option<u32>,
pub format: DxgiFormat,
pub mipmap_levels: Option<u32>,
pub array_layers: Option<u32>,
pub caps2: Option<Caps2>,
pub is_cubemap: bool,
pub resource_dimension: D3D10ResourceDimension,
pub alpha_mode: AlphaMode,
}
impl Dds {
const MAGIC: u32 = 0x20534444;
pub fn new_d3d(params: NewD3dParams) -> Result<Dds, Error> {
let mml = params.mipmap_levels.unwrap_or(1);
let data_size = get_array_stride(
params.width,
params.height,
params.depth,
mml,
¶ms.format,
)
.ok_or(Error::UnsupportedFormat)?;
Ok(Dds {
header: Header::new_d3d(
params.height,
params.width,
params.depth,
params.format,
params.mipmap_levels,
params.caps2,
)?,
header10: None,
data: vec![0; data_size as usize],
})
}
pub fn new_dxgi(params: NewDxgiParams) -> Result<Dds, Error> {
let arraysize = params.array_layers.unwrap_or(1);
let mml = params.mipmap_levels.unwrap_or(1);
let array_stride = get_array_stride(
params.width,
params.height,
params.depth,
mml,
¶ms.format,
)
.ok_or(Error::UnsupportedFormat)?;
let data_size = arraysize * array_stride;
let arraysize = if params.is_cubemap {
arraysize / 6
} else {
arraysize
};
let header10 = Header10::new(
params.format,
params.is_cubemap,
params.resource_dimension,
arraysize,
params.alpha_mode,
);
Ok(Dds {
header: Header::new_dxgi(
params.height,
params.width,
params.depth,
params.format,
params.mipmap_levels,
params.array_layers,
params.caps2,
)?,
header10: Some(header10),
data: vec![0; data_size as usize],
})
}
pub fn read<R: Read>(mut r: R) -> Result<Dds, Error> {
let magic = r.read_u32::<LittleEndian>()?;
if magic != Self::MAGIC {
return Err(Error::BadMagicNumber);
}
let header = Header::read(&mut r)?;
let header10 = if header.spf.fourcc == Some(FourCC(<FourCC>::DX10)) {
Some(Header10::read(&mut r)?)
} else {
None
};
let mut data: Vec<u8> = Vec::new();
r.read_to_end(&mut data)?;
Ok(Dds {
header,
header10,
data,
})
}
pub fn write<W: Write>(&self, w: &mut W) -> Result<(), Error> {
w.write_u32::<LittleEndian>(Self::MAGIC)?;
self.header.write(w)?;
if let Some(ref header10) = self.header10 {
header10.write(w)?;
}
w.write_all(&self.data)?;
Ok(())
}
pub fn get_d3d_format(&self) -> Option<D3DFormat> {
D3DFormat::try_from_pixel_format(&self.header.spf)
}
pub fn get_dxgi_format(&self) -> Option<DxgiFormat> {
if let Some(ref h10) = self.header10 {
Some(h10.dxgi_format)
} else {
DxgiFormat::try_from_pixel_format(&self.header.spf)
}
}
pub fn get_format(&self) -> Option<Box<dyn DataFormat>> {
if let Some(dxgi) = self.get_dxgi_format() {
Some(Box::new(dxgi))
} else if let Some(d3d) = self.get_d3d_format() {
Some(Box::new(d3d))
} else {
None
}
}
pub fn get_width(&self) -> u32 {
self.header.width
}
pub fn get_height(&self) -> u32 {
self.header.height
}
pub fn get_depth(&self) -> u32 {
self.header.depth.unwrap_or(1)
}
pub fn get_bits_per_pixel(&self) -> Option<u32> {
if let Some(format) = self.get_format() {
if let Some(bpp) = format.get_bits_per_pixel() {
return Some(bpp as u32);
}
}
if let Some(bpp) = self.header.spf.rgb_bit_count {
return Some(bpp);
}
None
}
pub fn get_pitch(&self) -> Option<u32> {
if let Some(format) = self.get_format() {
if let Some(pitch) = format.get_pitch(self.header.width) {
return Some(pitch);
}
}
if let Some(pitch) = self.header.pitch {
return Some(pitch);
}
if let Some(bpp) = self.get_bits_per_pixel() {
return Some((bpp * self.get_width()).div_ceil(8));
}
None
}
pub fn get_pitch_height(&self) -> u32 {
if let Some(format) = self.get_format() {
format.get_pitch_height()
} else {
1
}
}
pub fn get_main_texture_size(&self) -> Option<u32> {
get_texture_size(
self.get_pitch(),
self.header.linear_size,
self.get_pitch_height(),
self.header.height,
self.header.depth,
)
}
pub fn get_array_stride(&self) -> Result<u32, Error> {
let format = self.get_format().ok_or(Error::UnsupportedFormat)?;
get_array_stride(
self.header.width,
self.header.height,
self.header.depth,
self.get_num_mipmap_levels(),
&*format,
)
.ok_or(Error::UnsupportedFormat)
}
pub fn get_num_array_layers(&self) -> u32 {
if let Some(ref h10) = self.header10 {
if h10.misc_flag.contains(MiscFlag::TEXTURECUBE) {
h10.array_size * 6
} else {
h10.array_size
}
} else if self.header.caps2.contains(Caps2::CUBEMAP) {
6
} else {
1 }
}
pub fn get_num_mipmap_levels(&self) -> u32 {
self.header.mip_map_count.unwrap_or(1)
}
pub fn get_min_mipmap_size_in_bytes(&self) -> u32 {
if let Some(format) = self.get_format() {
if let Some(min) = format.get_minimum_mipmap_size_in_bytes() {
return min;
}
}
if let Some(bpp) = self.get_bits_per_pixel() {
bpp.div_ceil(8)
} else {
1
}
}
pub fn get_data(&self, array_layer: u32) -> Result<&[u8], Error> {
let (offset, size) = self.get_offset_and_size(array_layer)?;
let offset = offset as usize;
let size = size as usize;
self.data
.get(offset..offset + size)
.ok_or(Error::OutOfBounds)
}
pub fn get_mut_data(&mut self, array_layer: u32) -> Result<&mut [u8], Error> {
let (offset, size) = self.get_offset_and_size(array_layer)?;
let offset = offset as usize;
let size = size as usize;
self.data
.get_mut(offset..offset + size)
.ok_or(Error::OutOfBounds)
}
fn get_offset_and_size(&self, array_layer: u32) -> Result<(u32, u32), Error> {
if array_layer >= self.get_num_array_layers() {
return Err(Error::OutOfBounds);
}
let array_stride = self.get_array_stride()?;
let offset = array_layer * array_stride;
Ok((offset, array_stride))
}
}
fn get_texture_size(
pitch: Option<u32>,
linear_size: Option<u32>,
pitch_height: u32,
height: u32,
depth: Option<u32>,
) -> Option<u32> {
let depth = depth.unwrap_or(1);
if let Some(ls) = linear_size {
Some(ls)
} else if let Some(pitch) = pitch {
let row_height = height.div_ceil(pitch_height);
Some(pitch * row_height * depth)
} else {
None
}
}
fn get_array_stride(
width: u32,
height: u32,
depth: Option<u32>,
mipmap_levels: u32,
format: &dyn DataFormat,
) -> Option<u32> {
let mut stride: u32 = 0;
let mut mip_width = width;
let mut mip_height = height;
let mut mip_depth = depth.unwrap_or(1);
let pitch_height = format.get_pitch_height();
for _ in 0..mipmap_levels {
let pitch = format.get_pitch(mip_width)?;
let row_height = mip_height.div_ceil(pitch_height);
stride += pitch * row_height * mip_depth;
mip_width = (mip_width / 2).max(1);
mip_height = (mip_height / 2).max(1);
mip_depth = (mip_depth / 2).max(1);
}
Some(stride)
}
impl fmt::Debug for Dds {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Dds:")?;
if let Some(d3dformat) = self.get_d3d_format() {
writeln!(f, " Format: {:?}", d3dformat)?;
} else if let Some(dxgiformat) = self.get_dxgi_format() {
writeln!(f, " Format: {:?}", dxgiformat)?;
} else if let Some(ref fourcc) = self.header.spf.fourcc {
writeln!(f, " Format: FOURCC={:?} (Unknown)", fourcc)?;
} else {
writeln!(f, " Format UNSPECIFIED")?;
}
write!(f, "{:?}", self.header)?;
if let Some(ref h10) = self.header10 {
write!(f, "{:?}", h10)?;
}
writeln!(f, " (data elided)")?;
Ok(())
}
}