use crate::{
ffi::{self, prelude::*},
DDSMetaData, Result, DDS_FLAGS, DXGI_FORMAT, TEX_ALPHA_MODE, TEX_DIMENSION, TEX_MISC_FLAG,
TEX_MISC_FLAG2, TGA_FLAGS,
};
use core::ptr;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(C)]
pub struct TexMetadata {
pub width: usize,
pub height: usize,
pub depth: usize,
pub array_size: usize,
pub mip_levels: usize,
pub misc_flags: u32,
pub misc_flags2: u32,
pub format: DXGI_FORMAT,
pub dimension: TEX_DIMENSION,
}
impl TexMetadata {
#[must_use]
pub fn compute_index(&self, mip: usize, item: usize, slice: usize) -> Option<usize> {
let result =
unsafe { ffi::DirectXTexFFI_TexMetadata_ComputIndex(self.into(), mip, item, slice) };
(result != usize::MAX).then_some(result)
}
#[must_use]
pub fn is_cubemap(&self) -> bool {
(self.misc_flags & TEX_MISC_FLAG::TEX_MISC_TEXTURECUBE.bits()) != 0
}
#[must_use]
pub fn is_pm_alpha(&self) -> bool {
(self.misc_flags2 & TEX_MISC_FLAG2::TEX_MISC2_ALPHA_MODE_MASK.bits())
== TEX_ALPHA_MODE::TEX_ALPHA_MODE_PREMULTIPLIED.bits()
}
pub fn set_alpha_mode(&mut self, mode: TEX_ALPHA_MODE) {
self.misc_flags2 =
(self.misc_flags2 & !TEX_MISC_FLAG2::TEX_MISC2_ALPHA_MODE_MASK.bits()) | mode.bits();
}
#[must_use]
pub fn get_alpha_mode(&self) -> TEX_ALPHA_MODE {
(self.misc_flags2 & TEX_MISC_FLAG2::TEX_MISC2_ALPHA_MODE_MASK.bits()).into()
}
#[must_use]
pub fn is_volumemap(&self) -> bool {
self.dimension == TEX_DIMENSION::TEX_DIMENSION_TEXTURE3D
}
pub fn from_dds(
source: &[u8],
flags: DDS_FLAGS,
dd_pixel_format: Option<&mut DDSMetaData>,
) -> Result<Self> {
let mut result = Self::default();
let hr = unsafe {
ffi::DirectXTexFFI_GetMetadataFromDDSMemoryEx(
source.as_ffi_ptr(),
source.len(),
flags,
(&mut result).into(),
dd_pixel_format.into_ffi_ptr(),
)
};
hr.success(result)
}
pub fn from_hdr(source: &[u8]) -> Result<Self> {
let mut result = Self::default();
let hr = unsafe {
ffi::DirectXTexFFI_GetMetadataFromHDRMemory(
source.as_ffi_ptr(),
source.len(),
(&mut result).into(),
)
};
hr.success(result)
}
pub fn from_tga(source: &[u8], flags: TGA_FLAGS) -> Result<Self> {
let mut result = Self::default();
let hr = unsafe {
ffi::DirectXTexFFI_GetMetadataFromTGAMemory(
source.as_ffi_ptr(),
source.len(),
flags,
(&mut result).into(),
)
};
hr.success(result)
}
pub fn encode_dds_header(&self, flags: DDS_FLAGS) -> Result<Box<[u8]>> {
let mut required = 0;
let hr = unsafe {
ffi::DirectXTexFFI_EncodeDDSHeader(
self.into(),
flags,
ptr::null_mut(),
0,
(&mut required).into(),
)
};
hr.success(())?;
let mut v = Vec::new();
v.resize_with(required, Default::default);
let hr = unsafe {
ffi::DirectXTexFFI_EncodeDDSHeader(
self.into(),
flags,
v.as_mut_ffi_ptr(),
v.len(),
(&mut required).into(),
)
};
hr.success(()).map(|()| v.into_boxed_slice())
}
}
#[cfg(test)]
mod tests {
use crate::{ffi, TexMetadata, DXGI_FORMAT, TEX_ALPHA_MODE, TEX_DIMENSION};
use core::mem;
use std::fs;
#[test]
fn verify_layout() {
assert_eq!(mem::size_of::<TexMetadata>(), unsafe {
ffi::DirectXTexFFI_TexMetadata_Sizeof()
});
assert_eq!(mem::align_of::<TexMetadata>(), unsafe {
ffi::DirectXTexFFI_TexMetadata_Alignof()
});
}
#[test]
fn from_dds() {
let file = fs::read("data/ferris_wheel.dds").unwrap();
let (tex, dds) = {
let mut dds = Default::default();
let tex = TexMetadata::from_dds(&file, Default::default(), Some(&mut dds)).unwrap();
(tex, dds)
};
assert_eq!(tex.width, 720);
assert_eq!(tex.height, 1280);
assert_eq!(tex.depth, 1);
assert_eq!(tex.array_size, 1);
assert_eq!(tex.mip_levels, 11);
assert_eq!(tex.misc_flags, 0);
assert_eq!(tex.misc_flags2, 0);
assert_eq!(tex.format, DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_UNORM);
assert_eq!(tex.dimension, TEX_DIMENSION::TEX_DIMENSION_TEXTURE2D);
assert_eq!(tex.get_alpha_mode(), TEX_ALPHA_MODE::TEX_ALPHA_MODE_UNKNOWN);
assert_eq!(dds.size, 32);
assert_eq!(dds.flags, 65);
assert_eq!(dds.four_cc, 0);
assert_eq!(dds.rgb_bit_count, 32);
assert_eq!(dds.r_bit_mask, 0x000000FF);
assert_eq!(dds.g_bit_mask, 0x0000FF00);
assert_eq!(dds.b_bit_mask, 0x00FF0000);
assert_eq!(dds.a_bit_mask, 0xFF000000);
}
#[test]
fn from_hdr() {
let file = fs::read("data/ferris_wheel.hdr").unwrap();
let tex = TexMetadata::from_hdr(&file).unwrap();
assert_eq!(tex.width, 720);
assert_eq!(tex.height, 1280);
assert_eq!(tex.depth, 1);
assert_eq!(tex.array_size, 1);
assert_eq!(tex.mip_levels, 1);
assert_eq!(tex.misc_flags, 0);
assert_eq!(tex.misc_flags2, 3);
assert_eq!(tex.format, DXGI_FORMAT::DXGI_FORMAT_R32G32B32A32_FLOAT);
assert_eq!(tex.dimension, TEX_DIMENSION::TEX_DIMENSION_TEXTURE2D);
assert_eq!(tex.get_alpha_mode(), TEX_ALPHA_MODE::TEX_ALPHA_MODE_OPAQUE);
}
#[test]
fn from_tga() {
let file = fs::read("data/ferris_wheel.tga").unwrap();
let tex = TexMetadata::from_tga(&file, Default::default()).unwrap();
assert_eq!(tex.width, 720);
assert_eq!(tex.height, 1280);
assert_eq!(tex.depth, 1);
assert_eq!(tex.array_size, 1);
assert_eq!(tex.mip_levels, 1);
assert_eq!(tex.misc_flags, 0);
assert_eq!(tex.misc_flags2, 0);
assert_eq!(tex.format, DXGI_FORMAT::DXGI_FORMAT_R8G8B8A8_UNORM);
assert_eq!(tex.dimension, TEX_DIMENSION::TEX_DIMENSION_TEXTURE2D);
assert_eq!(tex.get_alpha_mode(), TEX_ALPHA_MODE::TEX_ALPHA_MODE_UNKNOWN);
}
#[test]
fn encode_dds_header() {
let file = fs::read("data/ferris_wheel.dds").unwrap();
let tex = TexMetadata::from_dds(&file, Default::default(), None).unwrap();
let header = tex.encode_dds_header(Default::default()).unwrap();
let len = header.len();
assert_eq!(&header[..], &file[..len]);
}
}