#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate enum_primitive;
mod error;
pub use error::*;
mod format;
pub use format::{DxgiFormat, D3DFormat, DataFormat, PixelFormat, PixelFormatFlags, FourCC};
mod header;
pub use header::{Header, HeaderFlags, Caps, Caps2};
mod header10;
pub use header10::{Header10, D3D10ResourceDimension, MiscFlag, AlphaMode};
use std::io::{Read, Write};
use std::fmt;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
pub struct Dds {
pub header: Header,
pub header10: Option<Header10>,
pub data: Vec<u8>,
}
impl Dds {
const MAGIC: u32 = 0x20534444;
pub fn new_d3d(height: u32, width: u32, depth: Option<u32>, format: D3DFormat,
mipmap_levels: Option<u32>, caps2: Option<Caps2>) -> Result<Dds, Error>
{
let size = match get_texture_size(format.get_pitch(width), None,
format.get_pitch_height(),
height, depth)
{
Some(s) => s,
None => return Err(Error::UnsupportedFormat),
};
let mml = mipmap_levels.unwrap_or(1);
let min_mipmap_size = match format.get_minimum_mipmap_size_in_bytes() {
Some(mms) => mms,
None => return Err(Error::UnsupportedFormat),
};
let array_stride = get_array_stride(size, min_mipmap_size, mml);
let data_size = array_stride;
Ok(Dds {
header: Header::new_d3d(height, width, depth, format, mipmap_levels,
caps2)?,
header10: None,
data: vec![0; data_size as usize],
})
}
pub fn new_dxgi(height: u32, width: u32, depth: Option<u32>,
format: DxgiFormat,
mipmap_levels: Option<u32>, array_layers: Option<u32>,
caps2: Option<Caps2>, is_cubemap: bool,
resource_dimension: D3D10ResourceDimension,
alpha_mode: AlphaMode)
-> Result<Dds, Error>
{
let arraysize = match array_layers {
Some(s) => s,
None => 1,
};
let size = match get_texture_size(format.get_pitch(width), None,
format.get_pitch_height(),
height, depth)
{
Some(s) => s,
None => return Err(Error::UnsupportedFormat),
};
let mml = mipmap_levels.unwrap_or(1);
let min_mipmap_size = match format.get_minimum_mipmap_size_in_bytes() {
Some(mms) => mms,
None => return Err(Error::UnsupportedFormat),
};
let array_stride = get_array_stride(size, min_mipmap_size, mml);
let data_size = arraysize * array_stride;
let header10 = Header10::new(
format, is_cubemap, resource_dimension,
arraysize, alpha_mode);
Ok(Dds {
header: Header::new_dxgi(height, width, depth, format, mipmap_levels,
array_layers, caps2)?,
header10: Some(header10),
data: vec![0; data_size as usize],
})
}
pub fn read<R: Read>(r: &mut R) -> Result<Dds, Error> {
let magic = r.read_u32::<LittleEndian>()?;
if magic != Self::MAGIC {
return Err(Error::BadMagicNumber);
}
let header = Header::read(r)?;
let header10 = if header.spf.fourcc == Some(FourCC(<FourCC>::DX10)) {
Some(Header10::read(r)?)
} else {
None
};
let mut data: Vec<u8> = Vec::new();
r.read_to_end(&mut data)?;
Ok(Dds {
header: header,
header10: header10,
data: 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() + 7) / 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 size = match self.get_main_texture_size() {
Some(s) => s,
None => return Err(Error::UnsupportedFormat),
};
let mml = self.get_num_mipmap_levels();
let min_mipmap_size = self.get_min_mipmap_size_in_bytes();
Ok(get_array_stride(size, min_mipmap_size, mml))
}
pub fn get_num_array_layers(&self) -> u32 {
if let Some(ref h10) = self.header10 {
h10.array_size
} else {
1
}
}
pub fn get_num_mipmap_levels(&self) -> u32 {
if let Some(mmc) = self.header.mip_map_count {
mmc
} else {
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 + 7) / 8
} else {
1
}
}
pub fn get_data<'a>(&'a self, array_layer: u32)
-> Result<&'a[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<'a>(&'a mut self, array_layer: u32)
-> Result<&'a 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 + (pitch_height-1))/ pitch_height;
Some(pitch * row_height * depth)
}
else {
None
}
}
fn get_array_stride(texture_size: u32,
min_mipmap_size: u32,
mipmap_levels: u32)
-> u32
{
let mut stride: u32 = 0;
let mut current_mipsize: u32 = texture_size;
for _ in 0..mipmap_levels {
stride += current_mipsize;
current_mipsize /= 4;
if current_mipsize < min_mipmap_size {
current_mipsize = min_mipmap_size;
}
}
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(())
}
}