use super::direct::*;
use super::header::*;
use super::jpeg::*;
pub use super::version::BlpVersion;
pub const BLP_MAX_WIDTH: u32 = 65535;
pub const BLP_MAX_HEIGHT: u32 = 65535;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BlpImage {
pub header: BlpHeader,
pub content: BlpContent,
}
impl BlpImage {
pub fn image_count(&self) -> usize {
match &self.content {
BlpContent::Dxt1(v) => v.images.len(),
BlpContent::Dxt3(v) => v.images.len(),
BlpContent::Dxt5(v) => v.images.len(),
BlpContent::Raw1(v) => v.images.len(),
BlpContent::Raw3(v) => v.images.len(),
BlpContent::Jpeg(v) => v.images.len(),
}
}
pub fn content_jpeg(&self) -> Option<&BlpJpeg> {
self.content.jpeg()
}
pub fn content_raw1(&self) -> Option<&BlpRaw1> {
self.content.raw1()
}
pub fn content_raw3(&self) -> Option<&BlpRaw3> {
self.content.raw3()
}
pub fn content_dxt1(&self) -> Option<&BlpDxtn> {
self.content.dxt1()
}
pub fn content_dxt3(&self) -> Option<&BlpDxtn> {
self.content.dxt3()
}
pub fn content_dxt5(&self) -> Option<&BlpDxtn> {
self.content.dxt5()
}
pub fn compression_type(&self) -> CompressionType {
match &self.content {
BlpContent::Jpeg(_) => CompressionType::Jpeg,
BlpContent::Raw1(_) => CompressionType::Raw1,
BlpContent::Raw3(_) => CompressionType::Raw3,
BlpContent::Dxt1(_) => CompressionType::Dxt1,
BlpContent::Dxt3(_) => CompressionType::Dxt3,
BlpContent::Dxt5(_) => CompressionType::Dxt5,
}
}
pub fn alpha_bit_depth(&self) -> u8 {
self.header.alpha_bits() as u8
}
pub fn best_mipmap_for_size(&self, target_size: u32) -> usize {
let image_count = self.image_count();
if image_count == 0 {
return 0;
}
let mut best_level = 0;
let mut best_diff = u32::MAX;
for level in 0..image_count {
let (width, height) = self.header.mipmap_size(level);
let size = width.max(height);
let diff = size.abs_diff(target_size);
if diff < best_diff {
best_diff = diff;
best_level = level;
}
}
best_level
}
pub fn mipmap_info(&self) -> Vec<MipMapInfo> {
let mut info = Vec::new();
for level in 0..self.image_count() {
let (width, height) = self.header.mipmap_size(level);
let data_size = match &self.content {
BlpContent::Jpeg(jpeg) => jpeg.images.get(level).map(|img| img.len()).unwrap_or(0),
BlpContent::Raw1(raw) => raw
.images
.get(level)
.map(|img| img.indexed_rgb.len() + img.indexed_alpha.len())
.unwrap_or(0),
BlpContent::Raw3(raw) => raw
.images
.get(level)
.map(|img| img.pixels.len() * 4)
.unwrap_or(0),
BlpContent::Dxt1(dxt) => dxt
.images
.get(level)
.map(|img| img.content.len())
.unwrap_or(0),
BlpContent::Dxt3(dxt) => dxt
.images
.get(level)
.map(|img| img.content.len())
.unwrap_or(0),
BlpContent::Dxt5(dxt) => dxt
.images
.get(level)
.map(|img| img.content.len())
.unwrap_or(0),
};
info.push(MipMapInfo {
level,
width,
height,
data_size,
pixel_count: width * height,
});
}
info
}
pub fn estimated_file_size(&self) -> usize {
let header_size = BlpHeader::size(self.header.version);
let content_size = match &self.content {
BlpContent::Jpeg(jpeg) => {
jpeg.header.len() + jpeg.images.iter().map(|img| img.len()).sum::<usize>()
}
BlpContent::Raw1(raw) => {
raw.cmap.len() * 4 + raw.images.iter().map(|img| {
img.indexed_rgb.len() + img.indexed_alpha.len()
}).sum::<usize>()
}
BlpContent::Raw3(raw) => raw
.images
.iter()
.map(|img| img.pixels.len() * 4)
.sum::<usize>(),
BlpContent::Dxt1(dxt) => dxt
.images
.iter()
.map(|img| img.content.len())
.sum::<usize>(),
BlpContent::Dxt3(dxt) => dxt
.images
.iter()
.map(|img| img.content.len())
.sum::<usize>(),
BlpContent::Dxt5(dxt) => dxt
.images
.iter()
.map(|img| img.content.len())
.sum::<usize>(),
};
header_size + content_size
}
pub fn compression_ratio(&self) -> f32 {
let uncompressed_size = self
.mipmap_info()
.iter()
.map(|info| info.width * info.height * 4) .sum::<u32>() as f32;
let compressed_size = self.estimated_file_size() as f32;
if compressed_size > 0.0 {
uncompressed_size / compressed_size
} else {
1.0
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MipMapInfo {
pub level: usize,
pub width: u32,
pub height: u32,
pub data_size: usize,
pub pixel_count: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CompressionType {
Jpeg,
Raw1,
Raw3,
Dxt1,
Dxt3,
Dxt5,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BlpContent {
Jpeg(BlpJpeg),
Raw1(BlpRaw1),
Raw3(BlpRaw3),
Dxt1(BlpDxtn),
Dxt3(BlpDxtn),
Dxt5(BlpDxtn),
}
impl BlpContent {
pub fn tag(&self) -> BlpContentTag {
match self {
BlpContent::Jpeg { .. } => BlpContentTag::Jpeg,
BlpContent::Raw1 { .. } => BlpContentTag::Direct,
BlpContent::Raw3 { .. } => BlpContentTag::Direct,
BlpContent::Dxt1 { .. } => BlpContentTag::Direct,
BlpContent::Dxt3 { .. } => BlpContentTag::Direct,
BlpContent::Dxt5 { .. } => BlpContentTag::Direct,
}
}
pub fn jpeg(&self) -> Option<&BlpJpeg> {
match self {
BlpContent::Jpeg(v) => Some(v),
_ => None,
}
}
pub fn raw1(&self) -> Option<&BlpRaw1> {
match self {
BlpContent::Raw1(v) => Some(v),
_ => None,
}
}
pub fn raw3(&self) -> Option<&BlpRaw3> {
match self {
BlpContent::Raw3(v) => Some(v),
_ => None,
}
}
pub fn dxt1(&self) -> Option<&BlpDxtn> {
match self {
BlpContent::Dxt1(v) => Some(v),
_ => None,
}
}
pub fn dxt3(&self) -> Option<&BlpDxtn> {
match self {
BlpContent::Dxt3(v) => Some(v),
_ => None,
}
}
pub fn dxt5(&self) -> Option<&BlpDxtn> {
match self {
BlpContent::Dxt5(v) => Some(v),
_ => None,
}
}
}