use super::{Decoder, create_rgba_image, validate_dimensions};
use crate::error::{BinaryError, Result};
use crate::texture::formats::TextureFormat;
use crate::texture::types::Texture2D;
use image::RgbaImage;
pub struct CrunchDecoder;
impl CrunchDecoder {
pub fn new() -> Self {
Self
}
#[cfg(feature = "texture-advanced")]
fn decompress_crunch(&self, data: &[u8], width: u32, height: u32) -> Result<Vec<u8>> {
validate_dimensions(width, height)?;
let mut output = vec![0u32; (width * height) as usize];
match texture2ddecoder::decode_crunch(data, width as usize, height as usize, &mut output) {
Ok(_) => {
let rgba_data: Vec<u8> = output
.iter()
.flat_map(|&pixel| {
[
(pixel & 0xFF) as u8, ((pixel >> 8) & 0xFF) as u8, ((pixel >> 16) & 0xFF) as u8, ((pixel >> 24) & 0xFF) as u8, ]
})
.collect();
Ok(rgba_data)
}
Err(e) => Err(BinaryError::generic(format!(
"Crunch decompression failed: {}",
e
))),
}
}
#[cfg(feature = "texture-advanced")]
fn decode_dxt1_crunched(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
let rgba_data = self.decompress_crunch(data, width, height)?;
create_rgba_image(rgba_data, width, height)
}
#[cfg(feature = "texture-advanced")]
fn decode_dxt5_crunched(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
let rgba_data = self.decompress_crunch(data, width, height)?;
create_rgba_image(rgba_data, width, height)
}
#[cfg(feature = "texture-advanced")]
fn decode_etc_rgb4_crunched(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
let rgba_data = self.decompress_crunch(data, width, height)?;
create_rgba_image(rgba_data, width, height)
}
#[cfg(feature = "texture-advanced")]
fn decode_etc2_rgba8_crunched(
&self,
data: &[u8],
width: u32,
height: u32,
) -> Result<RgbaImage> {
let rgba_data = self.decompress_crunch(data, width, height)?;
create_rgba_image(rgba_data, width, height)
}
#[cfg(not(feature = "texture-advanced"))]
fn decode_unsupported(&self, format: TextureFormat) -> Result<RgbaImage> {
Err(BinaryError::unsupported(format!(
"Crunch format {:?} requires texture-advanced feature",
format
)))
}
}
impl Decoder for CrunchDecoder {
fn decode(&self, texture: &Texture2D) -> Result<RgbaImage> {
let width = texture.width as u32;
let height = texture.height as u32;
let data = &texture.image_data;
match texture.format {
#[cfg(feature = "texture-advanced")]
TextureFormat::DXT1Crunched => self.decode_dxt1_crunched(data, width, height),
#[cfg(feature = "texture-advanced")]
TextureFormat::DXT5Crunched => self.decode_dxt5_crunched(data, width, height),
#[cfg(feature = "texture-advanced")]
TextureFormat::ETC_RGB4Crunched => self.decode_etc_rgb4_crunched(data, width, height),
#[cfg(feature = "texture-advanced")]
TextureFormat::ETC2_RGBA8Crunched => {
self.decode_etc2_rgba8_crunched(data, width, height)
}
#[cfg(not(feature = "texture-advanced"))]
format if format.is_crunch_compressed() => self.decode_unsupported(format),
_ => Err(BinaryError::unsupported(format!(
"Format {:?} is not a Crunch format",
texture.format
))),
}
}
fn can_decode(&self, format: TextureFormat) -> bool {
#[cfg(feature = "texture-advanced")]
{
format.is_crunch_compressed()
}
#[cfg(not(feature = "texture-advanced"))]
{
false
}
}
fn supported_formats(&self) -> Vec<TextureFormat> {
#[cfg(feature = "texture-advanced")]
{
vec![
TextureFormat::DXT1Crunched,
TextureFormat::DXT5Crunched,
TextureFormat::ETC_RGB4Crunched,
TextureFormat::ETC2_RGBA8Crunched,
]
}
#[cfg(not(feature = "texture-advanced"))]
{
vec![]
}
}
}
impl Default for CrunchDecoder {
fn default() -> Self {
Self::new()
}
}