unity_asset_decode/texture/decoders/
crunch.rs1use super::{Decoder, create_rgba_image, validate_dimensions};
7use crate::error::{BinaryError, Result};
8use crate::texture::formats::TextureFormat;
9use crate::texture::types::Texture2D;
10use image::RgbaImage;
11
12pub struct CrunchDecoder;
14
15impl CrunchDecoder {
16 pub fn new() -> Self {
18 Self
19 }
20
21 #[cfg(feature = "texture-advanced")]
23 fn decompress_crunch(&self, data: &[u8], width: u32, height: u32) -> Result<Vec<u8>> {
24 validate_dimensions(width, height)?;
25
26 let mut output = vec![0u32; (width * height) as usize];
27
28 match texture2ddecoder::decode_crunch(data, width as usize, height as usize, &mut output) {
29 Ok(_) => {
30 let rgba_data: Vec<u8> = output
32 .iter()
33 .flat_map(|&pixel| {
34 [
35 (pixel & 0xFF) as u8, ((pixel >> 8) & 0xFF) as u8, ((pixel >> 16) & 0xFF) as u8, ((pixel >> 24) & 0xFF) as u8, ]
40 })
41 .collect();
42 Ok(rgba_data)
43 }
44 Err(e) => Err(BinaryError::generic(format!(
45 "Crunch decompression failed: {}",
46 e
47 ))),
48 }
49 }
50
51 #[cfg(feature = "texture-advanced")]
53 fn decode_dxt1_crunched(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
54 let rgba_data = self.decompress_crunch(data, width, height)?;
55 create_rgba_image(rgba_data, width, height)
56 }
57
58 #[cfg(feature = "texture-advanced")]
60 fn decode_dxt5_crunched(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
61 let rgba_data = self.decompress_crunch(data, width, height)?;
62 create_rgba_image(rgba_data, width, height)
63 }
64
65 #[cfg(feature = "texture-advanced")]
67 fn decode_etc_rgb4_crunched(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
68 let rgba_data = self.decompress_crunch(data, width, height)?;
69 create_rgba_image(rgba_data, width, height)
70 }
71
72 #[cfg(feature = "texture-advanced")]
74 fn decode_etc2_rgba8_crunched(
75 &self,
76 data: &[u8],
77 width: u32,
78 height: u32,
79 ) -> Result<RgbaImage> {
80 let rgba_data = self.decompress_crunch(data, width, height)?;
81 create_rgba_image(rgba_data, width, height)
82 }
83
84 #[cfg(not(feature = "texture-advanced"))]
86 fn decode_unsupported(&self, format: TextureFormat) -> Result<RgbaImage> {
87 Err(BinaryError::unsupported(format!(
88 "Crunch format {:?} requires texture-advanced feature",
89 format
90 )))
91 }
92}
93
94impl Decoder for CrunchDecoder {
95 fn decode(&self, texture: &Texture2D) -> Result<RgbaImage> {
96 let width = texture.width as u32;
97 let height = texture.height as u32;
98 let data = &texture.image_data;
99
100 match texture.format {
101 #[cfg(feature = "texture-advanced")]
102 TextureFormat::DXT1Crunched => self.decode_dxt1_crunched(data, width, height),
103 #[cfg(feature = "texture-advanced")]
104 TextureFormat::DXT5Crunched => self.decode_dxt5_crunched(data, width, height),
105 #[cfg(feature = "texture-advanced")]
106 TextureFormat::ETC_RGB4Crunched => self.decode_etc_rgb4_crunched(data, width, height),
107 #[cfg(feature = "texture-advanced")]
108 TextureFormat::ETC2_RGBA8Crunched => {
109 self.decode_etc2_rgba8_crunched(data, width, height)
110 }
111
112 #[cfg(not(feature = "texture-advanced"))]
113 format if format.is_crunch_compressed() => self.decode_unsupported(format),
114
115 _ => Err(BinaryError::unsupported(format!(
116 "Format {:?} is not a Crunch format",
117 texture.format
118 ))),
119 }
120 }
121
122 fn can_decode(&self, format: TextureFormat) -> bool {
123 #[cfg(feature = "texture-advanced")]
124 {
125 format.is_crunch_compressed()
126 }
127
128 #[cfg(not(feature = "texture-advanced"))]
129 {
130 false
131 }
132 }
133
134 fn supported_formats(&self) -> Vec<TextureFormat> {
135 #[cfg(feature = "texture-advanced")]
136 {
137 vec![
138 TextureFormat::DXT1Crunched,
139 TextureFormat::DXT5Crunched,
140 TextureFormat::ETC_RGB4Crunched,
141 TextureFormat::ETC2_RGBA8Crunched,
142 ]
143 }
144
145 #[cfg(not(feature = "texture-advanced"))]
146 {
147 vec![]
148 }
149 }
150}
151
152impl Default for CrunchDecoder {
153 fn default() -> Self {
154 Self::new()
155 }
156}