pub use super::locator::MipmapLocator;
pub use super::version::BlpVersion;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BlpContentTag {
Jpeg,
Direct,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnknownContent(u32);
impl fmt::Display for UnknownContent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unknown content field value: {}", self.0)
}
}
impl TryFrom<u32> for BlpContentTag {
type Error = UnknownContent;
fn try_from(val: u32) -> Result<BlpContentTag, Self::Error> {
match val {
0 => Ok(BlpContentTag::Jpeg),
1 => Ok(BlpContentTag::Direct),
_ => Err(UnknownContent(val)),
}
}
}
impl From<BlpContentTag> for u32 {
fn from(val: BlpContentTag) -> u32 {
match val {
BlpContentTag::Jpeg => 0,
BlpContentTag::Direct => 1,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BlpHeader {
pub version: BlpVersion,
pub content: BlpContentTag,
pub flags: BlpFlags,
pub width: u32,
pub height: u32,
pub mipmap_locator: MipmapLocator,
}
impl BlpHeader {
pub fn mipmaps_count(&self) -> usize {
if self.has_mipmaps() {
let width_n = (self.width as f32).log2() as usize;
let height_n = (self.height as f32).log2() as usize;
width_n.max(height_n)
} else {
0
}
}
pub fn has_mipmaps(&self) -> bool {
self.flags.has_mipmaps()
}
pub fn mipmap_size(&self, i: usize) -> (u32, u32) {
if i == 0 {
(self.width, self.height)
} else {
((self.width >> i).max(1), (self.height >> i).max(1))
}
}
pub fn mipmap_pixels(&self, i: usize) -> u32 {
let (w, h) = self.mipmap_size(i);
w * h
}
pub fn alpha_bits(&self) -> u32 {
self.flags.alpha_bits()
}
pub fn internal_mipmaps(&self) -> Option<([u32; 16], [u32; 16])> {
match self.mipmap_locator {
MipmapLocator::Internal { offsets, sizes } => Some((offsets, sizes)),
MipmapLocator::External => None,
}
}
pub fn size(version: BlpVersion) -> usize {
4 + 4 + 4 + 4 + 4 + if version < BlpVersion::Blp2 {8} else {0} + if version > BlpVersion::Blp0 {16*4*2} else {0} }
}
impl Default for BlpHeader {
fn default() -> Self {
BlpHeader {
version: BlpVersion::Blp1,
content: BlpContentTag::Jpeg,
flags: Default::default(),
width: 1,
height: 1,
mipmap_locator: Default::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Compression {
Jpeg, Raw1,
Raw3,
Dxtc,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnknownCompression(u8);
impl fmt::Display for UnknownCompression {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unknown compression field value: {}", self.0)
}
}
impl TryFrom<u8> for Compression {
type Error = UnknownCompression;
fn try_from(val: u8) -> Result<Compression, Self::Error> {
match val {
0 => Ok(Compression::Jpeg),
1 => Ok(Compression::Raw1),
2 => Ok(Compression::Dxtc),
3 => Ok(Compression::Raw3),
_ => Err(UnknownCompression(val)),
}
}
}
impl From<Compression> for u8 {
fn from(val: Compression) -> u8 {
match val {
Compression::Jpeg => 0,
Compression::Raw1 => 1,
Compression::Dxtc => 2,
Compression::Raw3 => 3,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BlpFlags {
Blp2 {
compression: Compression,
alpha_bits: u8, alpha_type: u8, has_mipmaps: u8,
},
Old {
alpha_bits: u32,
extra: u32, has_mipmaps: u32, },
}
impl Default for BlpFlags {
fn default() -> Self {
BlpFlags::Old {
alpha_bits: 8,
extra: 8,
has_mipmaps: 1,
}
}
}
impl BlpFlags {
pub fn has_mipmaps(&self) -> bool {
match self {
BlpFlags::Blp2 { has_mipmaps, .. } => *has_mipmaps != 0,
BlpFlags::Old { has_mipmaps, .. } => *has_mipmaps != 0,
}
}
pub fn alpha_bits(&self) -> u32 {
match self {
BlpFlags::Blp2 { compression, .. } if *compression == Compression::Raw3 => 4,
BlpFlags::Blp2 { alpha_bits, .. } => *alpha_bits as u32,
BlpFlags::Old { alpha_bits, .. } => *alpha_bits,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mipmap_count() {
let header = BlpHeader {
width: 512,
height: 256,
version: BlpVersion::Blp0,
..Default::default()
};
assert_eq!(header.mipmaps_count(), 9);
let header = BlpHeader {
width: 512,
height: 256,
version: BlpVersion::Blp1,
..Default::default()
};
assert_eq!(header.mipmaps_count(), 9);
let header = BlpHeader {
width: 1,
height: 4,
..Default::default()
};
assert_eq!(header.mipmaps_count(), 2);
let header = BlpHeader {
width: 4,
height: 7,
..Default::default()
};
assert_eq!(header.mipmaps_count(), 2);
let header = BlpHeader {
width: 768,
height: 128,
..Default::default()
};
assert_eq!(header.mipmaps_count(), 9);
}
}