use crate::error::*;
use crate::{D3DFormat, DataFormat, DxgiFormat, PixelFormat};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
use std::io::{Read, Write};
#[derive(Clone)]
pub struct Header {
size: u32,
flags: HeaderFlags,
pub height: u32,
pub width: u32,
pub pitch: Option<u32>,
pub linear_size: Option<u32>,
pub depth: Option<u32>,
pub mip_map_count: Option<u32>,
reserved1: [u32; 11],
pub spf: PixelFormat,
pub caps: Caps,
pub caps2: Caps2,
caps3: u32,
caps4: u32,
reserved2: u32,
}
impl Default for Header {
fn default() -> Header {
Header {
size: 124, flags: HeaderFlags::CAPS
| HeaderFlags::HEIGHT
| HeaderFlags::WIDTH
| HeaderFlags::PIXELFORMAT,
height: 0,
width: 0,
pitch: None,
linear_size: None,
depth: None,
mip_map_count: None,
reserved1: [0; 11],
spf: Default::default(),
caps: Caps::TEXTURE,
caps2: Caps2::empty(),
caps3: 0,
caps4: 0,
reserved2: 0,
}
}
}
impl Header {
pub fn new_d3d(
height: u32,
width: u32,
depth: Option<u32>,
format: D3DFormat,
mipmap_levels: Option<u32>,
caps2: Option<Caps2>,
) -> Result<Header, Error> {
let mut header: Header = Header {
height,
width,
mip_map_count: mipmap_levels,
depth,
spf: From::from(format),
..Default::default()
};
if let Some(mml) = mipmap_levels {
if mml > 1 {
header.flags.insert(HeaderFlags::MIPMAPCOUNT);
header.caps.insert(Caps::COMPLEX | Caps::MIPMAP);
}
}
if let Some(d) = depth {
if d > 1 {
header.caps.insert(Caps::COMPLEX);
header.flags |= HeaderFlags::DEPTH;
}
}
if let Some(c2) = caps2 {
header.caps2 = c2;
}
let compressed: bool = format.get_block_size().is_some();
let pitch: u32 = match format.get_pitch(width) {
Some(pitch) => pitch,
None => return Err(Error::UnsupportedFormat),
};
let depth = depth.unwrap_or(1);
if compressed {
header.flags |= HeaderFlags::LINEARSIZE;
let pitch_height = format.get_pitch_height();
let raw_height = (height + (pitch_height - 1)) / pitch_height;
header.linear_size = Some(pitch * raw_height * depth);
} else {
header.flags |= HeaderFlags::PITCH;
header.pitch = Some(pitch);
}
Ok(header)
}
pub fn new_dxgi(
height: u32,
width: u32,
depth: Option<u32>,
format: DxgiFormat,
mipmap_levels: Option<u32>,
array_layers: Option<u32>,
caps2: Option<Caps2>,
) -> Result<Header, Error> {
let mut header: Header = Header {
height,
width,
mip_map_count: mipmap_levels,
depth,
spf: From::from(format),
..Default::default()
};
if let Some(mml) = mipmap_levels {
if mml > 1 {
header.flags.insert(HeaderFlags::MIPMAPCOUNT);
header.caps.insert(Caps::COMPLEX | Caps::MIPMAP);
}
}
if let Some(d) = depth {
if d > 1 {
header.caps.insert(Caps::COMPLEX);
header.flags |= HeaderFlags::DEPTH;
}
}
if let Some(al) = array_layers {
if al > 1 {
header.caps.insert(Caps::COMPLEX);
}
}
if let Some(c2) = caps2 {
header.caps2 = c2;
}
let compressed: bool = format.get_block_size().is_some();
let pitch: u32 = match format.get_pitch(width) {
Some(pitch) => pitch,
None => return Err(Error::UnsupportedFormat),
};
let depth = depth.unwrap_or(1);
if compressed {
header.flags |= HeaderFlags::LINEARSIZE;
let pitch_height = format.get_pitch_height();
let raw_height = (height + (pitch_height - 1)) / pitch_height;
header.linear_size = Some(pitch * raw_height * depth);
} else {
header.flags |= HeaderFlags::PITCH;
header.pitch = Some(pitch);
}
Ok(header)
}
pub fn read<R: Read>(mut r: R) -> Result<Header, Error> {
let size = r.read_u32::<LittleEndian>()?;
if size != 124 {
return Err(Error::InvalidField("Header struct size".to_owned()));
}
let flags = HeaderFlags::from_bits_truncate(r.read_u32::<LittleEndian>()?);
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 mip_map_count = r.read_u32::<LittleEndian>()?;
let mut reserved1 = [0_u32; 11];
r.read_u32_into::<LittleEndian>(&mut reserved1)?;
let spf = PixelFormat::read(&mut r)?;
let caps = r.read_u32::<LittleEndian>()?;
let caps2 = r.read_u32::<LittleEndian>()?;
let caps3 = r.read_u32::<LittleEndian>()?;
let caps4 = r.read_u32::<LittleEndian>()?;
let reserved2 = r.read_u32::<LittleEndian>()?;
Ok(Header {
size,
flags,
height,
width,
pitch: if flags.contains(HeaderFlags::PITCH) {
Some(pitch_or_linear_size)
} else {
None
},
linear_size: if flags.contains(HeaderFlags::LINEARSIZE) {
Some(pitch_or_linear_size)
} else {
None
},
depth: if flags.contains(HeaderFlags::DEPTH) {
Some(depth)
} else {
None
},
mip_map_count: if flags.contains(HeaderFlags::MIPMAPCOUNT) {
Some(mip_map_count)
} else {
None
},
reserved1,
spf,
caps: Caps::from_bits_truncate(caps),
caps2: Caps2::from_bits_truncate(caps2),
caps3,
caps4,
reserved2,
})
}
pub fn write<W: Write>(&self, w: &mut W) -> Result<(), Error> {
w.write_u32::<LittleEndian>(self.size)?;
w.write_u32::<LittleEndian>(self.flags.bits())?;
w.write_u32::<LittleEndian>(self.height)?;
w.write_u32::<LittleEndian>(self.width)?;
if let Some(pitch) = self.pitch {
w.write_u32::<LittleEndian>(pitch)?;
} else if let Some(ls) = self.linear_size {
w.write_u32::<LittleEndian>(ls)?;
} else {
w.write_u32::<LittleEndian>(0)?;
}
w.write_u32::<LittleEndian>(self.depth.unwrap_or(0))?;
w.write_u32::<LittleEndian>(self.mip_map_count.unwrap_or(0))?;
for u in &self.reserved1 {
w.write_u32::<LittleEndian>(*u)?;
}
self.spf.write(w)?;
w.write_u32::<LittleEndian>(self.caps.bits())?;
w.write_u32::<LittleEndian>(self.caps2.bits())?;
w.write_u32::<LittleEndian>(self.caps3)?;
w.write_u32::<LittleEndian>(self.caps4)?;
w.write_u32::<LittleEndian>(self.reserved2)?;
Ok(())
}
}
impl fmt::Debug for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, " Header:")?;
writeln!(f, " flags: {:?}", self.flags)?;
writeln!(
f,
" height: {:?}, width: {:?}, depth: {:?}",
self.height, self.width, self.depth
)?;
writeln!(
f,
" pitch: {:?} linear_size: {:?}",
self.pitch, self.linear_size
)?;
writeln!(f, " mipmap_count: {:?}", self.mip_map_count)?;
writeln!(f, " caps: {:?}, caps2 {:?}", self.caps, self.caps2)?;
write!(f, "{:?}", self.spf)?;
Ok(())
}
}
bitflags! {
pub struct HeaderFlags: u32 {
const CAPS = 0x1;
const HEIGHT = 0x2;
const WIDTH = 0x4;
const PITCH = 0x8;
const PIXELFORMAT = 0x1000;
const MIPMAPCOUNT = 0x20000;
const LINEARSIZE = 0x80000;
const DEPTH = 0x800000;
}
}
bitflags! {
pub struct Caps: u32 {
const COMPLEX = 0x8;
const MIPMAP = 0x400000;
const TEXTURE = 0x1000;
}
}
bitflags! {
pub struct Caps2: u32 {
const CUBEMAP = 0x200;
const CUBEMAP_POSITIVEX = 0x400;
const CUBEMAP_NEGATIVEX = 0x800;
const CUBEMAP_POSITIVEY = 0x1000;
const CUBEMAP_NEGATIVEY = 0x2000;
const CUBEMAP_POSITIVEZ = 0x4000;
const CUBEMAP_NEGATIVEZ = 0x8000;
const VOLUME = 0x200000;
}
}