use std::error::Error as StdError;
use std::fs::File;
use std::io::{Read,Write};
use std::mem;
use std::cmp::max;
use std::path::Path;
use std::fmt::{self, Display, Formatter};
use std::io::BufReader;
use std::borrow::{Borrow};
macro_rules! from_c_str{
($c_string: expr) => (
{
use std::ffi::CStr;
use std::str;
str::from_utf8(CStr::from_ptr($c_string).to_bytes()).unwrap()
}
);
}
#[derive(Debug,Clone,Copy)]
enum Caps{
Complex = 0x8,
Mipmap = 0x400000,
Texture = 0x1000,
}
#[derive(Debug,Clone,Copy)]
enum Caps2{
Cubemap = 0x200,
CubemapPosX = 0x400,
CubemapNegX = 0x800,
CubemapPosY = 0x1000,
CubemapNegY = 0x2000,
CubemapPosZ = 0x4000,
CubemapNegZ = 0x8000,
Volume = 0x200000,
}
#[derive(Debug,Clone,Copy)]
#[repr(u32)]
enum PixelFormatFlags{
AlphaPixels = 0x1,
Alpha = 0x2,
FourCC = 0x4,
RGB = 0x40,
YUV = 0x200,
Luminance = 0x20000,
BumpDuDv = 0x00080000,
}
#[derive(Debug,Clone)]
struct PixelFormat{
size: u32,
flags: u32,
fourcc: u32,
bitcount: u32,
r_bitmask: u32,
g_bitmask: u32,
b_bitmask: u32,
a_bitmask: u32,
}
#[derive(Debug,Clone,Copy)]
#[repr(u32)]
enum Flags{
Caps = 0x1,
Height = 0x2,
Width = 0x4,
Pitch = 0x8,
PixelFormat = 0x1000,
MipmapCount = 0x20000,
LinearSize = 0x80000,
Depth = 0x800000,
}
pub enum Format{
Gray,
RGB,
RGBA,
}
pub enum Type{
UnsignedByte,
Float,
}
#[derive(Debug,Clone)]
struct Header{
header_size: u32,
flags: u32,
height: u32,
width: u32,
linear_size_or_pitch: u32,
depth: u32,
mipmapcount: u32,
reserved: [u32;11],
pixel_format: PixelFormat,
caps: u32,
caps2: u32,
caps3: u32,
caps4: u32,
reserved2: u32,
}
#[derive(Debug,Clone,Copy,PartialEq)]
#[allow(non_camel_case_types)]
#[repr(u32)]
pub enum DXGIFormat {
UNKNOWN = 0,
R32G32B32A32_TYPELESS = 1,
R32G32B32A32_FLOAT = 2,
R32G32B32A32_UINT = 3,
R32G32B32A32_SINT = 4,
R32G32B32_TYPELESS = 5,
R32G32B32_FLOAT = 6,
R32G32B32_UINT = 7,
R32G32B32_SINT = 8,
R16G16B16A16_TYPELESS = 9,
R16G16B16A16_FLOAT = 10,
R16G16B16A16_UNORM = 11,
R16G16B16A16_UINT = 12,
R16G16B16A16_SNORM = 13,
R16G16B16A16_SINT = 14,
R32G32_TYPELESS = 15,
R32G32_FLOAT = 16,
R32G32_UINT = 17,
R32G32_SINT = 18,
R32G8X24_TYPELESS = 19,
D32_FLOAT_S8X24_UINT = 20,
R32_FLOAT_X8X24_TYPELESS = 21,
X32_TYPELESS_G8X24_UINT = 22,
R10G10B10A2_TYPELESS = 23,
R10G10B10A2_UNORM = 24,
R10G10B10A2_UINT = 25,
R11G11B10_FLOAT = 26,
R8G8B8A8_TYPELESS = 27,
R8G8B8A8_UNORM = 28,
R8G8B8A8_UNORM_SRGB = 29,
R8G8B8A8_UINT = 30,
R8G8B8A8_SNORM = 31,
R8G8B8A8_SINT = 32,
R16G16_TYPELESS = 33,
R16G16_FLOAT = 34,
R16G16_UNORM = 35,
R16G16_UINT = 36,
R16G16_SNORM = 37,
R16G16_SINT = 38,
R32_TYPELESS = 39,
D32_FLOAT = 40,
R32_FLOAT = 41,
R32_UINT = 42,
R32_SINT = 43,
R24G8_TYPELESS = 44,
D24_UNORM_S8_UINT = 45,
R24_UNORM_X8_TYPELESS = 46,
X24_TYPELESS_G8_UINT = 47,
R8G8_TYPELESS = 48,
R8G8_UNORM = 49,
R8G8_UINT = 50,
R8G8_SNORM = 51,
R8G8_SINT = 52,
R16_TYPELESS = 53,
R16_FLOAT = 54,
D16_UNORM = 55,
R16_UNORM = 56,
R16_UINT = 57,
R16_SNORM = 58,
R16_SINT = 59,
R8_TYPELESS = 60,
R8_UNORM = 61,
R8_UINT = 62,
R8_SNORM = 63,
R8_SINT = 64,
A8_UNORM = 65,
R1_UNORM = 66,
R9G9B9E5_SHAREDEXP = 67,
R8G8_B8G8_UNORM = 68,
G8R8_G8B8_UNORM = 69,
BC1_TYPELESS = 70,
BC1_UNORM = 71,
BC1_UNORM_SRGB = 72,
BC2_TYPELESS = 73,
BC2_UNORM = 74,
BC2_UNORM_SRGB = 75,
BC3_TYPELESS = 76,
BC3_UNORM = 77,
BC3_UNORM_SRGB = 78,
BC4_TYPELESS = 79,
BC4_UNORM = 80,
BC4_SNORM = 81,
BC5_TYPELESS = 82,
BC5_UNORM = 83,
BC5_SNORM = 84,
B5G6R5_UNORM = 85,
B5G5R5A1_UNORM = 86,
B8G8R8A8_UNORM = 87,
B8G8R8X8_UNORM = 88,
R10G10B10_XR_BIAS_A2_UNORM = 89,
B8G8R8A8_TYPELESS = 90,
B8G8R8A8_UNORM_SRGB = 91,
B8G8R8X8_TYPELESS = 92,
B8G8R8X8_UNORM_SRGB = 93,
BC6H_TYPELESS = 94,
BC6H_UF16 = 95,
BC6H_SF16 = 96,
BC7_TYPELESS = 97,
BC7_UNORM = 98,
BC7_UNORM_SRGB = 99,
AYUV = 100,
Y410 = 101,
Y416 = 102,
NV12 = 103,
P010 = 104,
P016 = 105,
I420_OPAQUE = 106,
YUY2 = 107,
Y210 = 108,
Y216 = 109,
NV11 = 110,
AI44 = 111,
IA44 = 112,
P8 = 113,
A8P8 = 114,
B4G4R4A4_UNORM = 115,
P208 = 130,
V208 = 131,
V408 = 132,
FORCE_UINT = 0xffffffff
}
impl DXGIFormat{
pub fn bpp(self) -> Option<usize> {
use DXGIFormat::*;
match self {
UNKNOWN => None,
R32G32B32A32_TYPELESS => Some(128),
R32G32B32A32_FLOAT => Some(128),
R32G32B32A32_UINT => Some(128),
R32G32B32A32_SINT => Some(128),
R32G32B32_TYPELESS => Some(32*3),
R32G32B32_FLOAT => Some(32*3),
R32G32B32_UINT => Some(32*3),
R32G32B32_SINT => Some(32*3),
R16G16B16A16_TYPELESS => Some(64),
R16G16B16A16_FLOAT => Some(64),
R16G16B16A16_UNORM => Some(64),
R16G16B16A16_UINT => Some(64),
R16G16B16A16_SNORM => Some(64),
R16G16B16A16_SINT => Some(64),
R32G32_TYPELESS => Some(64),
R32G32_FLOAT => Some(64),
R32G32_UINT => Some(64),
R32G32_SINT => Some(64),
R32G8X24_TYPELESS => Some(64),
D32_FLOAT_S8X24_UINT => Some(64),
R32_FLOAT_X8X24_TYPELESS => Some(64),
X32_TYPELESS_G8X24_UINT => Some(64),
R10G10B10A2_TYPELESS => Some(64),
R10G10B10A2_UNORM => Some(64),
R10G10B10A2_UINT => Some(64),
R11G11B10_FLOAT => Some(32),
R8G8B8A8_TYPELESS => Some(32),
R8G8B8A8_UNORM => Some(32),
R8G8B8A8_UNORM_SRGB => Some(32),
R8G8B8A8_UINT => Some(32),
R8G8B8A8_SNORM => Some(32),
R8G8B8A8_SINT => Some(32),
R16G16_TYPELESS => Some(32),
R16G16_FLOAT => Some(32),
R16G16_UNORM => Some(32),
R16G16_UINT => Some(32),
R16G16_SNORM => Some(32),
R16G16_SINT => Some(32),
R32_TYPELESS => Some(32),
D32_FLOAT => Some(32),
R32_FLOAT => Some(32),
R32_UINT => Some(32),
R32_SINT => Some(32),
R24G8_TYPELESS => Some(32),
D24_UNORM_S8_UINT => Some(32),
R24_UNORM_X8_TYPELESS => Some(32),
X24_TYPELESS_G8_UINT => Some(32),
R8G8_TYPELESS => Some(16),
R8G8_UNORM => Some(16),
R8G8_UINT => Some(16),
R8G8_SNORM => Some(16),
R8G8_SINT => Some(16),
R16_TYPELESS => Some(16),
R16_FLOAT => Some(16),
D16_UNORM => Some(16),
R16_UNORM => Some(16),
R16_UINT => Some(16),
R16_SNORM => Some(16),
R16_SINT => Some(16),
R8_TYPELESS => Some(8),
R8_UNORM => Some(8),
R8_UINT => Some(8),
R8_SNORM => Some(8),
R8_SINT => Some(8),
A8_UNORM => Some(8),
R1_UNORM => Some(8),
R9G9B9E5_SHAREDEXP => Some(32),
R8G8_B8G8_UNORM => Some(32),
G8R8_G8B8_UNORM => Some(32),
BC1_TYPELESS => None,
BC1_UNORM => None,
BC1_UNORM_SRGB => None,
BC2_TYPELESS => None,
BC2_UNORM => None,
BC2_UNORM_SRGB => None,
BC3_TYPELESS => None,
BC3_UNORM => None,
BC3_UNORM_SRGB => None,
BC4_TYPELESS => None,
BC4_UNORM => None,
BC4_SNORM => None,
BC5_TYPELESS => None,
BC5_UNORM => None,
BC5_SNORM => None,
B5G6R5_UNORM => Some(16),
B5G5R5A1_UNORM => Some(16),
B8G8R8A8_UNORM => Some(32),
B8G8R8X8_UNORM => Some(32),
R10G10B10_XR_BIAS_A2_UNORM => Some(32),
B8G8R8A8_TYPELESS => Some(32),
B8G8R8A8_UNORM_SRGB => Some(32),
B8G8R8X8_TYPELESS => Some(32),
B8G8R8X8_UNORM_SRGB => Some(32),
BC6H_TYPELESS => None,
BC6H_UF16 => None,
BC6H_SF16 => None,
BC7_TYPELESS => None,
BC7_UNORM => None,
BC7_UNORM_SRGB => None,
AYUV => unimplemented!(),
Y410 => unimplemented!(),
Y416 => unimplemented!(),
NV12 => unimplemented!(),
P010 => unimplemented!(),
P016 => unimplemented!(),
I420_OPAQUE => unimplemented!(),
YUY2 => unimplemented!(),
Y210 => unimplemented!(),
Y216 => unimplemented!(),
NV11 => unimplemented!(),
AI44 => unimplemented!(),
IA44 => unimplemented!(),
P8 => Some(8),
A8P8 => Some(16),
B4G4R4A4_UNORM => Some(16),
P208 => unimplemented!(),
V208 => unimplemented!(),
V408 => unimplemented!(),
FORCE_UINT => None,
}
}
}
#[derive(Debug,Clone)]
enum D3D10ResourceDimension {
UNKNOWN = 0,
BUFFER = 1,
TEXTURE1D = 2,
TEXTURE2D = 3,
TEXTURE3D = 4
}
#[derive(Debug,Clone)]
struct HeaderDXT10{
dxgi_format: DXGIFormat,
resource_dimension: u32,
misc_flag: u32,
array_size: u32,
misc_flags2: u32,
}
pub enum SliceOrVec<'a>{
Slice(&'a [u8]),
Vec(Vec<u8>),
}
impl<'a> Borrow<[u8]> for SliceOrVec<'a>{
fn borrow(&self) -> &[u8]{
match self{
SliceOrVec::Slice(s) => s,
SliceOrVec::Vec(v) => &v
}
}
}
#[derive(Clone,Debug)]
pub struct Error(String);
impl StdError for Error{
}
impl Display for Error{
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result{
self.0.fmt(fmt)
}
}
impl From<String> for Error{
fn from(s: String) -> Error{
Error(s)
}
}
pub struct Builder{
header: Header,
header_dxt10: Option<HeaderDXT10>,
}
impl Builder{
pub fn new(w: usize, h: usize, format: Format, ty: Type) -> Builder{
let bpc = match ty{
Type::UnsignedByte => 8,
Type::Float => 32,
};
let channels = match format {
Format::Gray => 1,
Format::RGB => 3,
Format::RGBA => 4,
};
let bpp = channels * bpc;
let pitch = (w * bpp + 7) / 8;
let pixel_flags = match format {
Format::Gray => PixelFormatFlags::Luminance as u32 | PixelFormatFlags::FourCC as u32,
Format::RGB => PixelFormatFlags::FourCC as u32,
Format::RGBA => PixelFormatFlags::FourCC as u32,
};
let mask = match format{
Format::RGBA => (0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000),
_ => (0xFF0000, 0x00FF00, 0x0000FF, 0x0),
};
let dxgi_format = match format{
Format::Gray => match ty{
Type::UnsignedByte => DXGIFormat::R8_UNORM,
Type::Float => DXGIFormat::R32_FLOAT,
},
Format::RGB => match ty{
Type::UnsignedByte => panic!("RGB8 not supported by dds"),
Type::Float => DXGIFormat::R32G32B32_FLOAT,
},
Format::RGBA => match ty{
Type::UnsignedByte => DXGIFormat::R8G8B8A8_UNORM,
Type::Float => DXGIFormat::R32G32B32A32_FLOAT,
},
};
Builder{
header: Header{
header_size: 124,
flags: Flags::Caps as u32 |
Flags::Height as u32 |
Flags::Width as u32 |
Flags::Pitch as u32 |
Flags::PixelFormat as u32,
height: h as u32,
width: w as u32,
linear_size_or_pitch: pitch as u32,
depth: 0,
mipmapcount: 1,
reserved: [0u32;11],
pixel_format: PixelFormat{
size: 32,
flags: pixel_flags,
fourcc: fourcc_from_str("DX10"),
bitcount: bpp as u32,
r_bitmask: mask.0,
g_bitmask: mask.1,
b_bitmask: mask.2,
a_bitmask: mask.3,
},
caps: Caps::Texture as u32,
caps2: 0x0,
caps3: 0x0,
caps4: 0x0,
reserved2: 0x0,
},
header_dxt10: Some(HeaderDXT10{
dxgi_format,
resource_dimension: D3D10ResourceDimension::TEXTURE2D as u32,
misc_flag: 0,
array_size: 1,
misc_flags2: 0,
}),
}
}
pub fn has_mipmaps(&mut self, mipcount: usize) -> &mut Builder{
self.header.mipmapcount = mipcount as u32;
if mipcount > 1{
self.header.flags |= Flags::MipmapCount as u32;
self.header.caps |= Caps::Complex as u32 | Caps::Mipmap as u32;
}
self
}
pub fn is_cubemap_allfaces(&mut self) -> &mut Builder{
self.header.caps |= Caps::Complex as u32;
self.header.caps2 |= Caps2::Cubemap as u32 |
Caps2::CubemapPosX as u32 |
Caps2::CubemapNegX as u32 |
Caps2::CubemapPosY as u32 |
Caps2::CubemapNegY as u32 |
Caps2::CubemapPosZ as u32 |
Caps2::CubemapNegZ as u32;
self.header_dxt10.as_mut().unwrap().misc_flag = 0x4;
self
}
pub fn create<S: Borrow<[u8]>>(&mut self, data: S) -> Result<Dds<S>,Error>{
let dds = Dds{
header: self.header.clone(),
header_dxt10: self.header_dxt10.clone(),
data: data
};
let linear_size = dds.try_linear_size()?;
if linear_size != dds.data.borrow().len(){
Err(Error(format!("Expected {} bytes but passed {}", linear_size, dds.data.borrow().len())))
}else{
Ok(dds)
}
}
}
#[derive(Clone)]
pub struct Dds<S = Vec<u8>>{
header: Header,
header_dxt10: Option<HeaderDXT10>,
data: S,
}
fn fourcc_to_str(fourcc: u32) -> String{
let a = fourcc & 0x000000FF;
let b = (fourcc >> 8) & 0x000000FF;
let c = (fourcc >> 16) & 0x000000FF;
let d = (fourcc >> 24) & 0x000000FF;
let c_str: &[i8] = &[a as i8, b as i8, c as i8, d as i8, 0i8];
unsafe{
from_c_str!(c_str.as_ptr()).to_string()
}
}
fn fourcc_from_str(fourcc: &str) -> u32{
let mut bytes = fourcc.bytes();
let a = bytes.next().unwrap() as u32;
let b = bytes.next().unwrap() as u32;
let c = bytes.next().unwrap() as u32;
let d = bytes.next().unwrap() as u32;
(((((d << 8) | c) << 8) | b) << 8) | a
}
impl Dds{
pub fn load<P: AsRef<Path>>(path: P) -> Result<Dds,Error>{
let file = File::open(&path)
.map_err(|err| err.description().to_string())?;
Dds::read(BufReader::new(file), path.as_ref().display())
}
pub fn from_bytes(bytes: &[u8]) -> Result<Dds,Error>{
Dds::read(bytes, "Dds from bytes")
}
fn read<R: Read, P: Display>(mut file: R, path: P) -> Result<Dds,Error> {
let mut filecode = [0u8; 4];
file.read_exact(&mut filecode)
.map_err(|_| format!("Couldn't read header for {}", path))?;
unsafe{
if filecode[0..3] != ['D' as u8, 'D' as u8, 'S' as u8]{
return Err(Error(format!("{} is not a correct dds file", path)));
}
let mut header_buffer = [0u8; 124];
file.read_exact(&mut header_buffer)
.map_err(|_| format!("Couldn't read header for {}", path))?;
let header: Header = mem::transmute(header_buffer);
let header_dxt10 = if header.pixel_format.fourcc == fourcc_from_str("DX10"){
let mut header_buffer = [0u8; 20];
file
.read_exact(&mut header_buffer)
.map_err(|_| format!("Couldn't read dxt10 header for {}", path))?;
Some(mem::transmute(header_buffer))
}else{
None
};
let mut dds = Dds{
header: header,
header_dxt10: header_dxt10,
data: vec![ ],
};
let bufsize = dds.try_linear_size()?;
let bytesread = file
.read_to_end(&mut dds.data)
.map_err(|err| format!("Error reading data from {}: {}", path, err.description()))?;
if bytesread != bufsize as usize{
return Err(Error(format!("Couldn't read expected size for {}, header specifies {} but only read {}", path, bufsize, bytesread)));
}
Ok(dds)
}
}
}
impl<S: Borrow<[u8]>> Dds<S>{
pub fn mipmap(&self, level: usize) -> Option<Dds<SliceOrVec>>{
if level>=self.mipmap_count(){
return None;
}
let mut header = self.header.clone();
let header_dxt10 = self.header_dxt10.clone();
header.mipmapcount = 1;
header.flags &= !(Flags::MipmapCount as u32);
header.caps &= !(Caps::Mipmap as u32);
if !self.is_cubemap_allfaces() {
header.caps &= !(Caps::Complex as u32);
}
let mut w;
let mut h;
let mut linear_size_or_pitch;
let data;
if self.is_compressed(){
if !self.is_cubemap_allfaces(){
let blocksize = if self.fourcc_str() == "DXT1" {8} else {16};
w = self.width();
h = self.height();
linear_size_or_pitch = self.header.linear_size_or_pitch as usize;
let mut offset = 0;
let mut size = ((w+3)/4) * ((h+3)/4) * blocksize;
for _ in 0..level{
offset += size;
w /= 2;
h /= 2;
linear_size_or_pitch /= 2;
size = ((w+3)/4) * ((h+3)/4) * blocksize;
}
data = SliceOrVec::Slice(&self.data.borrow()[offset..offset+size]);
}else{
return None;
}
}else{
if !self.is_cubemap_allfaces(){
h = self.height();
w = self.width();
linear_size_or_pitch = self.pitch();
let mut offset = 0;
let mut size = linear_size_or_pitch * h;
for _ in 0..level{
offset += size;
h /= 2;
linear_size_or_pitch /= 2;
w /= 2;
size = linear_size_or_pitch * h;
}
data = SliceOrVec::Slice(&self.data.borrow()[offset..offset+size]);
}else{
let face_size = {
let mut pitch = self.pitch();
let mut h = self.height();
let mut face_size = 0;
for _level in 0..self.mipmap_count(){
face_size += pitch * h;
pitch /= 2;
h /= 2;
}
face_size
};
let mut v = vec![];
h = self.height();
w = self.width();
linear_size_or_pitch = self.pitch();
for face in 0..6{
let mut offset = face_size * face;
h = self.height();
w = self.width();
linear_size_or_pitch = self.pitch();
let mut size = linear_size_or_pitch * h;
for _ in 0..level{
offset += size;
h /= 2;
linear_size_or_pitch /= 2;
w /= 2;
size = linear_size_or_pitch * h;
}
v.extend(&self.data.borrow()[offset..offset+size]);
}
data = SliceOrVec::Vec(v);
}
};
header.width = w as u32;
header.height = h as u32;
header.linear_size_or_pitch = linear_size_or_pitch as u32;
Some(Dds{
header,
header_dxt10,
data,
})
}
pub fn mipmap_face(&self, level: usize, face: usize) -> Result<Dds<&[u8]>, Error>{
if self.is_compressed(){
Err(Error("Compressed cubemaps not supported yet".to_string()))
}else if !self.is_cubemap_allfaces(){
Err(Error("Dds is not a cubemap".to_string()))
}else{
let mut pitch = self.pitch();
let mut h = self.height();
let mut face_size = 0;
for _level in 0..self.mipmap_count(){
face_size += pitch * h;
pitch /= 2;
h /= 2;
}
let mut offset = face_size * face;
let mut pitch = self.pitch();
let mut h = self.height();
let mut w = self.width();
let mut size = pitch * h;
for _ in 0..level{
offset += size;
pitch /= 2;
w /= 2;
h /= 2;
size = pitch * h;
}
let data = &self.data.borrow()[offset..offset+size];
let mut header = self.header.clone();
let header_dxt10 = self.header_dxt10.clone();
header.height = h as u32;
header.width = w as u32;
header.mipmapcount = 1;
header.linear_size_or_pitch = pitch as u32;
header.flags &= !(Flags::MipmapCount as u32);
header.caps &= !(Caps::Mipmap as u32);
header.caps2 &= !(Caps2::Cubemap as u32);
header.caps2 &= !(Caps2::CubemapPosX as u32);
header.caps2 &= !(Caps2::CubemapNegX as u32);
header.caps2 &= !(Caps2::CubemapPosY as u32);
header.caps2 &= !(Caps2::CubemapNegY as u32);
header.caps2 &= !(Caps2::CubemapPosZ as u32);
header.caps2 &= !(Caps2::CubemapNegZ as u32);
Ok(Dds{
header,
header_dxt10,
data,
})
}
}
pub fn face(&self, face: usize) -> Result<Dds<&[u8]>, Error>{
if self.is_compressed(){
Err(Error("Compressed cubemaps not supported yet".to_string()))
}else if !self.is_cubemap_allfaces(){
Err(Error("Dds is not a cubemap".to_string()))
}else{
let mut pitch = self.pitch();
let mut h = self.height();
let mut face_size = 0;
for _level in 0..self.mipmap_count(){
face_size += pitch * h;
pitch /= 2;
h /= 2;
}
let offset = face_size * face;
let data = &self.data.borrow()[offset..offset+face_size];
let mut header = self.header.clone();
let header_dxt10 = self.header_dxt10.clone();
header.caps2 &= !(Caps2::Cubemap as u32);
if face != 0 {
header.caps2 &= !(Caps2::CubemapPosX as u32);
}
if face != 1 {
header.caps2 &= !(Caps2::CubemapNegX as u32);
}
if face != 2 {
header.caps2 &= !(Caps2::CubemapPosY as u32);
}
if face != 3 {
header.caps2 &= !(Caps2::CubemapNegY as u32);
}
if face != 4 {
header.caps2 &= !(Caps2::CubemapPosZ as u32);
}
if face != 5 {
header.caps2 &= !(Caps2::CubemapNegZ as u32);
}
Ok(Dds{
header,
header_dxt10,
data,
})
}
}
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(),Error>{
let mut file = File::create(path)
.map_err(|err| err.description().to_string())?;
file
.write_all(&['D' as u8,'D' as u8,'S' as u8, ' ' as u8])
.map_err(|err| err.description().to_string())?;
unsafe{
let header: &[u8;124] = mem::transmute(&self.header);
file.write_all(header)
.map_err(|err| err.description().to_string())?;
if let Some(ref header_dxt) = self.header_dxt10{
let header_dxt: &[u8;20] = mem::transmute(header_dxt);
file.write_all(header_dxt)
.map_err(|err| err.description().to_string())?;
}
}
file
.write_all(self.data.borrow())
.map_err(|err| err.description().to_string())?;
Ok(())
}
pub fn data(&self) -> &[u8]{
self.data.borrow()
}
}
impl<S> Dds<S>{
pub fn width(&self) -> usize{
self.header.width as usize
}
pub fn height(&self) -> usize{
self.header.height as usize
}
pub fn mipmap_count(&self) -> usize{
if self.header.caps as usize & Caps::Mipmap as usize == Caps::Mipmap as usize {
self.header.mipmapcount as usize
}else{
1
}
}
pub fn fourcc(&self) -> u32{
self.header.pixel_format.fourcc
}
pub fn fourcc_str(&self) -> String{
fourcc_to_str(self.fourcc())
}
pub fn bpp(&self) -> Result<usize, Error>{
if self.header.pixel_format.flags & PixelFormatFlags::FourCC as u32 != 0 {
match self.header.pixel_format.fourcc {
36 => Ok(64),
110 => Ok(64),
111 => Ok(16),
112 => Ok(32),
113 => Ok(64),
114 => Ok(32),
115 => Ok(64),
116 => Ok(128),
_fourcc => {
let fourcc_str = self.fourcc_str();
match fourcc_str.as_str() {
"DXT1" | "DXT3" | "DXT5" | "DXT2" | "DXT4" | "ATI1" | "BC4U" | "BC4S" | "ATI2" | "BC5U" | "BC5S"
=> Err(Error::from(format!("Compressed format doesn't have bpp"))),
"RGBG" => Ok(32),
"GRGB" => Ok(32),
"YUY2" => Err(Error::from(format!("YUV format doesn't have bpp"))),
"DX10" => self.header_dxt10.as_ref().unwrap().dxgi_format.bpp()
.ok_or_else(|| Error::from(format!(
"DX10 format {:?} doesn't have bpp",
self.header_dxt10.as_ref().unwrap().dxgi_format))),
_ => Err(Error::from(format!(
"Pixel format flags contain FourCC but actual fourCC {} is not known",
fourcc_str.as_str()
)))
}
}
}
}else{
Ok(self.header.pixel_format.bitcount as usize)
}
}
pub fn r_bitmask(&self) -> u32{
self.header.pixel_format.r_bitmask
}
pub fn g_bitmask(&self) -> u32{
self.header.pixel_format.g_bitmask
}
pub fn b_bitmask(&self) -> u32{
self.header.pixel_format.b_bitmask
}
pub fn a_bitmask(&self) -> u32{
self.header.pixel_format.a_bitmask
}
pub fn linear_size(&self) -> usize{
self.try_linear_size().unwrap()
}
fn try_linear_size(&self) -> Result<usize, Error> {
if self.is_compressed(){
if self.mipmap_count() > 1 {
let faces = if self.is_cubemap_allfaces(){
6
}else{
1
};
let blocksize = if self.fourcc_str() == "DXT1" {8} else {16};
let mut w = self.width();
let mut h = self.height();
let mut total_size = 0;
let mut size = ((w+3)/4) * ((h+3)/4) * blocksize * faces;
for _ in 0..self.mipmap_count(){
total_size += size;
w /= 2;
h /= 2;
size = ((w+3)/4) * ((h+3)/4) * blocksize * faces;
}
Ok(total_size)
} else {
Ok(self.header.linear_size_or_pitch as usize)
}
}else{
let faces = if self.is_cubemap_allfaces(){
6
}else{
1
};
let mut h = self.height();
let bpp = self.bpp()?;
let mut pitch = self.pitch() / (bpp / 8);
let mut total_size = 0;
for _ in 0..self.mipmap_count(){
total_size += pitch * h * faces;
h /= 2;
pitch /= 2;
}
Ok(total_size * (bpp / 8))
}
}
pub fn pitch(&self) -> usize{
if self.is_compressed(){
let blocksize = if self.fourcc_str() == "DXT1" {8} else {16};
max((self.header.width as usize + 3) / 4, 1) * blocksize
}else{
self.header.linear_size_or_pitch as usize
}
}
pub fn is_compressed(&self) -> bool{
let fourcc_str = self.fourcc_str();
&fourcc_str == "DXT1"
|| &fourcc_str == "DXT3"
|| &fourcc_str == "DXT5"
|| &fourcc_str == "DXT2"
|| &fourcc_str == "DXT4"
|| &fourcc_str == "ATI1"
|| &fourcc_str == "BC4U"
|| &fourcc_str == "BC4S"
|| &fourcc_str == "ATI2"
|| &fourcc_str == "BC5U"
|| &fourcc_str == "BC5S"
|| (&fourcc_str == "DX10" && {
let header_dxt10 = self.header_dxt10.as_ref().unwrap();
header_dxt10.dxgi_format == DXGIFormat::BC1_TYPELESS
|| header_dxt10.dxgi_format == DXGIFormat::BC1_UNORM
|| header_dxt10.dxgi_format == DXGIFormat::BC1_UNORM_SRGB
|| header_dxt10.dxgi_format == DXGIFormat::BC2_TYPELESS
|| header_dxt10.dxgi_format == DXGIFormat::BC2_UNORM
|| header_dxt10.dxgi_format == DXGIFormat::BC2_UNORM_SRGB
|| header_dxt10.dxgi_format == DXGIFormat::BC3_TYPELESS
|| header_dxt10.dxgi_format == DXGIFormat::BC3_UNORM
|| header_dxt10.dxgi_format == DXGIFormat::BC3_UNORM_SRGB
|| header_dxt10.dxgi_format == DXGIFormat::BC4_TYPELESS
|| header_dxt10.dxgi_format == DXGIFormat::BC4_UNORM
|| header_dxt10.dxgi_format == DXGIFormat::BC4_SNORM
|| header_dxt10.dxgi_format == DXGIFormat::BC5_TYPELESS
|| header_dxt10.dxgi_format == DXGIFormat::BC5_UNORM
|| header_dxt10.dxgi_format == DXGIFormat::BC5_SNORM
|| header_dxt10.dxgi_format == DXGIFormat::BC6H_SF16
|| header_dxt10.dxgi_format == DXGIFormat::BC6H_UF16
|| header_dxt10.dxgi_format == DXGIFormat::BC6H_TYPELESS
|| header_dxt10.dxgi_format == DXGIFormat::BC7_UNORM
|| header_dxt10.dxgi_format == DXGIFormat::BC7_UNORM_SRGB
|| header_dxt10.dxgi_format == DXGIFormat::BC7_TYPELESS
})
}
pub fn format(&self) -> Result<DXGIFormat, Error>{
if self.header.pixel_format.flags & PixelFormatFlags::RGB as u32 != 0 {
match self.header.pixel_format.bitcount {
32 if self.header.pixel_format.r_bitmask == 0x000000ff
&& self.header.pixel_format.g_bitmask == 0x0000ff00
&& self.header.pixel_format.b_bitmask == 0x00ff0000
&& self.header.pixel_format.a_bitmask == 0xff000000 => Ok(DXGIFormat::R8G8B8A8_UNORM),
32 if self.header.pixel_format.r_bitmask == 0x00ff0000
&& self.header.pixel_format.g_bitmask == 0x0000ff00
&& self.header.pixel_format.b_bitmask == 0x000000ff
&& self.header.pixel_format.a_bitmask == 0xff000000 => Ok(DXGIFormat::B8G8R8A8_UNORM),
32 if self.header.pixel_format.r_bitmask == 0x00ff0000
&& self.header.pixel_format.g_bitmask == 0x0000ff00
&& self.header.pixel_format.b_bitmask == 0x000000ff
&& self.header.pixel_format.a_bitmask == 0x00000000 => Ok(DXGIFormat::B8G8R8X8_UNORM),
32 if self.header.pixel_format.r_bitmask == 0x0000ffff
&& self.header.pixel_format.g_bitmask == 0xffff0000
&& self.header.pixel_format.b_bitmask == 0x00000000
&& self.header.pixel_format.a_bitmask == 0x00000000 => Ok(DXGIFormat::R16G16_UNORM),
32 if self.header.pixel_format.r_bitmask == 0xffffffff
&& self.header.pixel_format.g_bitmask == 0x00000000
&& self.header.pixel_format.b_bitmask == 0x00000000
&& self.header.pixel_format.a_bitmask == 0x00000000 => Ok(DXGIFormat::R32_FLOAT),
24 => Err(Error::from(format!("Pixel format bitcount specifies 24 which is not valid"))),
16 if self.header.pixel_format.r_bitmask == 0x7c00
&& self.header.pixel_format.g_bitmask == 0x03e0
&& self.header.pixel_format.b_bitmask == 0x001f
&& self.header.pixel_format.a_bitmask == 0x8000 => Ok(DXGIFormat::B5G5R5A1_UNORM),
16 if self.header.pixel_format.r_bitmask == 0xf800
&& self.header.pixel_format.g_bitmask == 0x07e0
&& self.header.pixel_format.b_bitmask == 0x001f
&& self.header.pixel_format.a_bitmask == 0x0000 => Ok(DXGIFormat::B5G6R5_UNORM),
16 if self.header.pixel_format.r_bitmask == 0x0f00
&& self.header.pixel_format.g_bitmask == 0x00f0
&& self.header.pixel_format.b_bitmask == 0x000f
&& self.header.pixel_format.a_bitmask == 0xf000 => Ok(DXGIFormat::B4G4R4A4_UNORM),
bitcount => Err(Error::from(format!(
"RB Pixel format bitcount specifies {} with color masks {:x},{:x},{:x},{:x} which is not valid",
bitcount,
self.header.pixel_format.r_bitmask,
self.header.pixel_format.g_bitmask,
self.header.pixel_format.b_bitmask,
self.header.pixel_format.a_bitmask
)))
}
}else if self.header.pixel_format.flags & PixelFormatFlags::Luminance as u32 != 0{
match self.header.pixel_format.bitcount {
8 if self.header.pixel_format.r_bitmask == 0x000000ff
&& self.header.pixel_format.g_bitmask == 0x00000000
&& self.header.pixel_format.b_bitmask == 0x00000000
&& self.header.pixel_format.a_bitmask == 0x00000000 => Ok(DXGIFormat::R8_UNORM),
8 if self.header.pixel_format.r_bitmask == 0x000000ff
&& self.header.pixel_format.g_bitmask == 0x00000000
&& self.header.pixel_format.b_bitmask == 0x00000000
&& self.header.pixel_format.a_bitmask == 0x0000ff00 => Ok(DXGIFormat::R8G8_UNORM),
16 if self.header.pixel_format.r_bitmask == 0x0000ffff
&& self.header.pixel_format.g_bitmask == 0x00000000
&& self.header.pixel_format.b_bitmask == 0x00000000
&& self.header.pixel_format.a_bitmask == 0x00000000 => Ok(DXGIFormat::R16_UNORM),
16 if self.header.pixel_format.r_bitmask == 0x000000ff
&& self.header.pixel_format.g_bitmask == 0x00000000
&& self.header.pixel_format.b_bitmask == 0x00000000
&& self.header.pixel_format.a_bitmask == 0x0000ff00 => Ok(DXGIFormat::R8G8_UNORM),
bitcount => Err(Error::from(format!(
"Luminance Pixel format bitcount specifies {} with color masks {:x},{:x},{:x},{:x} which is not valid",
bitcount,
self.header.pixel_format.r_bitmask,
self.header.pixel_format.g_bitmask,
self.header.pixel_format.b_bitmask,
self.header.pixel_format.a_bitmask
)))
}
}else if self.header.pixel_format.flags & PixelFormatFlags::Alpha as u32 != 0
|| self.header.pixel_format.bitcount == 8
{
Ok(DXGIFormat::A8_UNORM)
}else if self.header.pixel_format.flags & PixelFormatFlags::BumpDuDv as u32 != 0 {
match self.header.pixel_format.bitcount {
16 if self.header.pixel_format.r_bitmask == 0x00ff
&& self.header.pixel_format.g_bitmask == 0xff00
&& self.header.pixel_format.b_bitmask == 0x0000
&& self.header.pixel_format.a_bitmask == 0x0000 => Ok(DXGIFormat::R8G8_UNORM),
32 if self.header.pixel_format.r_bitmask == 0x000000ff
&& self.header.pixel_format.g_bitmask == 0x0000ff00
&& self.header.pixel_format.b_bitmask == 0x00ff0000
&& self.header.pixel_format.a_bitmask == 0xff000000 => Ok(DXGIFormat::R8G8B8A8_SNORM),
32 if self.header.pixel_format.r_bitmask == 0x0000ffff
&& self.header.pixel_format.g_bitmask == 0xffff0000
&& self.header.pixel_format.b_bitmask == 0x00000000
&& self.header.pixel_format.a_bitmask == 0x00000000 => Ok(DXGIFormat::R16G16_SNORM),
bitcount => Err(Error::from(format!(
"BumpDuDv Pixel format bitcount specifies {} with color masks {:x},{:x},{:x},{:x} which is not valid",
bitcount,
self.header.pixel_format.r_bitmask,
self.header.pixel_format.g_bitmask,
self.header.pixel_format.b_bitmask,
self.header.pixel_format.a_bitmask
)))
}
}else if self.header.pixel_format.flags & PixelFormatFlags::FourCC as u32 != 0 {
match self.header.pixel_format.fourcc {
36 => Ok(DXGIFormat::R16G16B16A16_UNORM),
110 => Ok(DXGIFormat::R16G16B16A16_SNORM),
111 => Ok(DXGIFormat::R16_FLOAT),
112 => Ok(DXGIFormat::R16G16_FLOAT),
113 => Ok(DXGIFormat::R16G16B16A16_FLOAT),
114 => Ok(DXGIFormat::R32_FLOAT),
115 => Ok(DXGIFormat::R32G32_FLOAT),
116 => Ok(DXGIFormat::R32G32B32A32_FLOAT),
fourcc => {
let fourcc_str = self.fourcc_str();
match fourcc_str.as_str() {
"DXT1" => Ok(DXGIFormat::BC1_UNORM),
"DXT3" => Ok(DXGIFormat::BC2_UNORM),
"DXT5" => Ok(DXGIFormat::BC3_UNORM),
"DXT2" => Ok(DXGIFormat::BC2_UNORM),
"DXT4" => Ok(DXGIFormat::BC3_UNORM),
"ATI1" | "BC4U" => Ok(DXGIFormat::BC4_UNORM),
"BC4S" => Ok(DXGIFormat::BC4_SNORM),
"ATI2" | "BC5U" => Ok(DXGIFormat::BC5_UNORM),
"BC5S" => Ok(DXGIFormat::BC5_SNORM),
"RGBG" => Ok(DXGIFormat::R8G8_B8G8_UNORM),
"GRGB" => Ok(DXGIFormat::G8R8_G8B8_UNORM),
"YUY2" => Ok(DXGIFormat::YUY2),
"DX10" => {
if let Some(header_dx10) = self.header_dxt10.as_ref(){
Ok(header_dx10.dxgi_format)
}else{
Err(Error::from(format!(
"Pixel format flags contain FourCC DX10 but there's no DX10 header present in the file {} is not known",
fourcc
)))
}
}
_ => Err(Error::from(format!(
"Pixel format flags contain FourCC but actual fourCC {} is not known",
fourcc
)))
}
}
}
}else{
Err(Error::from(format!("Unkown format")))
}
}
pub fn is_cubemap(&self) -> bool{
self.header.caps2 & Caps2::Cubemap as u32 != 0
}
pub fn is_cubemap_allfaces(&self) -> bool{
self.header.caps2 & Caps2::CubemapPosX as u32 != 0 &&
self.header.caps2 & Caps2::CubemapNegX as u32 != 0 &&
self.header.caps2 & Caps2::CubemapPosY as u32 != 0 &&
self.header.caps2 & Caps2::CubemapNegY as u32 != 0 &&
self.header.caps2 & Caps2::CubemapPosZ as u32 != 0 &&
self.header.caps2 & Caps2::CubemapNegZ as u32 != 0
}
}