use crate::error::Error;
use crate::header::Caps2;
use crate::header10::MiscFlag;
use crate::Dds;
use std::ops::Range;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum CubemapFace {
PositiveX = 0,
NegativeX = 1,
PositiveY = 2,
NegativeY = 3,
PositiveZ = 4,
NegativeZ = 5,
}
impl CubemapFace {
pub const ALL: [CubemapFace; 6] = [
CubemapFace::PositiveX,
CubemapFace::NegativeX,
CubemapFace::PositiveY,
CubemapFace::NegativeY,
CubemapFace::PositiveZ,
CubemapFace::NegativeZ,
];
pub fn from_index(index: u32) -> Result<CubemapFace, Error> {
match index {
0 => Ok(CubemapFace::PositiveX),
1 => Ok(CubemapFace::NegativeX),
2 => Ok(CubemapFace::PositiveY),
3 => Ok(CubemapFace::NegativeY),
4 => Ok(CubemapFace::PositiveZ),
5 => Ok(CubemapFace::NegativeZ),
_ => Err(Error::OutOfBounds),
}
}
pub fn index(self) -> u32 {
self as u32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct SubresourceId {
pub mip: u32,
pub layer: u32,
pub face: u32,
}
impl SubresourceId {
pub const fn new(mip: u32, layer: u32, face: u32) -> Self {
Self { mip, layer, face }
}
pub const fn mip_layer(mip: u32, layer: u32) -> Self {
Self {
mip,
layer,
face: 0,
}
}
pub const fn cubemap(mip: u32, layer: u32, face: CubemapFace) -> Self {
Self {
mip,
layer,
face: face as u32,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct SurfaceView<'a> {
pub id: SubresourceId,
pub width: u32,
pub height: u32,
pub depth: u32,
pub data: &'a [u8],
}
impl Dds {
pub fn is_cubemap(&self) -> bool {
if let Some(ref h10) = self.header10 {
h10.misc_flag.contains(MiscFlag::TEXTURECUBE)
} else {
self.header.caps2.contains(Caps2::CUBEMAP)
}
}
pub fn subresource_layer_count(&self) -> u32 {
if self.is_cubemap() {
self.cube_count()
} else if let Some(ref h10) = self.header10 {
h10.array_size.max(1)
} else {
1
}
}
pub fn subresource_face_count(&self) -> u32 {
if self.is_cubemap() {
6
} else {
1
}
}
pub fn cube_count(&self) -> u32 {
if !self.is_cubemap() {
return 0;
}
if let Some(ref h10) = self.header10 {
h10.array_size.max(1)
} else {
1
}
}
pub fn physical_slice_count(&self) -> u32 {
if self.is_cubemap() {
self.cube_count().saturating_mul(6)
} else if let Some(ref h10) = self.header10 {
h10.array_size.max(1)
} else {
1
}
}
pub fn mip_dimensions(&self, mip: u32) -> Result<(u32, u32, u32), Error> {
if mip >= self.get_num_mipmap_levels() {
return Err(Error::OutOfBounds);
}
let width = (self.header.width >> mip).max(1);
let height = (self.header.height >> mip).max(1);
let depth = (self.header.depth.unwrap_or(1) >> mip).max(1);
Ok((width, height, depth))
}
pub fn subresource_range(&self, id: SubresourceId) -> Result<Range<usize>, Error> {
let (offset, size) = self.subresource_offset_and_size(id)?;
let start = offset as usize;
let end = start
.checked_add(size as usize)
.ok_or(Error::OutOfBounds)?;
if end > self.data.len() {
return Err(Error::TruncatedData);
}
Ok(start..end)
}
pub fn surface(&self, id: SubresourceId) -> Result<SurfaceView<'_>, Error> {
let range = self.subresource_range(id)?;
let (width, height, depth) = self.mip_dimensions(id.mip)?;
Ok(SurfaceView {
id,
width,
height,
depth,
data: &self.data[range],
})
}
pub fn surface_mut(&mut self, id: SubresourceId) -> Result<SurfaceViewMut<'_>, Error> {
let range = self.subresource_range(id)?;
let (width, height, depth) = self.mip_dimensions(id.mip)?;
Ok(SurfaceViewMut {
id,
width,
height,
depth,
data: &mut self.data[range],
})
}
fn subresource_offset_and_size(&self, id: SubresourceId) -> Result<(u32, u32), Error> {
self.validate_subresource_id(id)?;
let physical = self.physical_slice_index(id)?;
let array_stride = self.get_array_stride()?;
let (mip_offset, mip_size) = self.mip_offset_and_size_in_chain(id.mip)?;
let offset = physical
.checked_mul(array_stride)
.and_then(|base| base.checked_add(mip_offset))
.ok_or(Error::OutOfBounds)?;
Ok((offset, mip_size))
}
fn validate_subresource_id(&self, id: SubresourceId) -> Result<(), Error> {
if id.mip >= self.get_num_mipmap_levels() {
return Err(Error::OutOfBounds);
}
if id.layer >= self.subresource_layer_count() {
return Err(Error::OutOfBounds);
}
if self.is_cubemap() {
if id.face >= 6 {
return Err(Error::OutOfBounds);
}
} else if id.face != 0 {
return Err(Error::OutOfBounds);
}
Ok(())
}
fn physical_slice_index(&self, id: SubresourceId) -> Result<u32, Error> {
if self.is_cubemap() {
id.layer
.checked_mul(6)
.and_then(|base| base.checked_add(id.face))
.ok_or(Error::OutOfBounds)
} else {
Ok(id.layer)
}
}
fn mip_offset_and_size_in_chain(&self, mip: u32) -> Result<(u32, u32), Error> {
let levels = self.get_num_mipmap_levels();
if mip >= levels {
return Err(Error::OutOfBounds);
}
let mut current = self
.get_main_texture_size()
.ok_or(Error::UnsupportedFormat)?;
let min_size = self.get_min_mipmap_size_in_bytes();
let mut offset = 0_u32;
for level in 0..levels {
if level == mip {
return Ok((offset, current));
}
offset = offset.checked_add(current).ok_or(Error::OutOfBounds)?;
current /= 4;
if current < min_size {
current = min_size;
}
}
Err(Error::OutOfBounds)
}
}
#[derive(Debug)]
pub struct SurfaceViewMut<'a> {
pub id: SubresourceId,
pub width: u32,
pub height: u32,
pub depth: u32,
pub data: &'a mut [u8],
}