unity_asset_decode/texture/decoders/
compressed.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 CompressedDecoder;
14
15impl CompressedDecoder {
16 pub fn new() -> Self {
18 Self
19 }
20
21 #[cfg(feature = "texture-advanced")]
23 fn decode_dxt1(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
24 validate_dimensions(width, height)?;
25
26 let mut output = vec![0u32; (width * height) as usize];
27
28 match texture2ddecoder::decode_bc1(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
43 create_rgba_image(rgba_data, width, height)
44 }
45 Err(e) => Err(BinaryError::generic(format!("DXT1 decoding failed: {}", e))),
46 }
47 }
48
49 #[cfg(feature = "texture-advanced")]
51 fn decode_dxt5(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
52 validate_dimensions(width, height)?;
53
54 let mut output = vec![0u32; (width * height) as usize];
55
56 match texture2ddecoder::decode_bc3(data, width as usize, height as usize, &mut output) {
57 Ok(_) => {
58 let rgba_data: Vec<u8> = output
60 .iter()
61 .flat_map(|&pixel| {
62 [
63 (pixel & 0xFF) as u8, ((pixel >> 8) & 0xFF) as u8, ((pixel >> 16) & 0xFF) as u8, ((pixel >> 24) & 0xFF) as u8, ]
68 })
69 .collect();
70
71 create_rgba_image(rgba_data, width, height)
72 }
73 Err(e) => Err(BinaryError::generic(format!("DXT5 decoding failed: {}", e))),
74 }
75 }
76
77 #[cfg(feature = "texture-advanced")]
79 fn decode_bc7(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
80 validate_dimensions(width, height)?;
81
82 let mut output = vec![0u32; (width * height) as usize];
83
84 match texture2ddecoder::decode_bc7(data, width as usize, height as usize, &mut output) {
85 Ok(_) => {
86 let rgba_data: Vec<u8> = output
88 .iter()
89 .flat_map(|&pixel| {
90 [
91 (pixel & 0xFF) as u8, ((pixel >> 8) & 0xFF) as u8, ((pixel >> 16) & 0xFF) as u8, ((pixel >> 24) & 0xFF) as u8, ]
96 })
97 .collect();
98
99 create_rgba_image(rgba_data, width, height)
100 }
101 Err(e) => Err(BinaryError::generic(format!("BC7 decoding failed: {}", e))),
102 }
103 }
104
105 #[cfg(feature = "texture-advanced")]
107 fn decode_bc4(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
108 validate_dimensions(width, height)?;
109
110 let mut output = vec![0u32; (width * height) as usize];
111
112 match texture2ddecoder::decode_bc4(data, width as usize, height as usize, &mut output) {
113 Ok(_) => {
114 let rgba_data: Vec<u8> = output
116 .iter()
117 .flat_map(|&pixel| {
118 let value = (pixel & 0xFF) as u8;
119 [value, value, value, 255] })
121 .collect();
122
123 create_rgba_image(rgba_data, width, height)
124 }
125 Err(e) => Err(BinaryError::generic(format!("BC4 decoding failed: {}", e))),
126 }
127 }
128
129 #[cfg(feature = "texture-advanced")]
131 fn decode_bc5(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
132 validate_dimensions(width, height)?;
133
134 let mut output = vec![0u32; (width * height) as usize];
135
136 match texture2ddecoder::decode_bc5(data, width as usize, height as usize, &mut output) {
137 Ok(_) => {
138 let rgba_data: Vec<u8> = output
140 .iter()
141 .flat_map(|&pixel| {
142 [
143 (pixel & 0xFF) as u8, ((pixel >> 8) & 0xFF) as u8, 0, 255, ]
148 })
149 .collect();
150
151 create_rgba_image(rgba_data, width, height)
152 }
153 Err(e) => Err(BinaryError::generic(format!("BC5 decoding failed: {}", e))),
154 }
155 }
156
157 #[cfg(not(feature = "texture-advanced"))]
159 fn decode_unsupported(&self, format: TextureFormat) -> Result<RgbaImage> {
160 Err(BinaryError::unsupported(format!(
161 "Compressed format {:?} requires texture-advanced feature",
162 format
163 )))
164 }
165}
166
167impl Decoder for CompressedDecoder {
168 fn decode(&self, texture: &Texture2D) -> Result<RgbaImage> {
169 let width = texture.width as u32;
170 let height = texture.height as u32;
171 let data = &texture.image_data;
172
173 match texture.format {
174 #[cfg(feature = "texture-advanced")]
175 TextureFormat::DXT1 => self.decode_dxt1(data, width, height),
176 #[cfg(feature = "texture-advanced")]
177 TextureFormat::DXT5 => self.decode_dxt5(data, width, height),
178 #[cfg(feature = "texture-advanced")]
179 TextureFormat::BC7 => self.decode_bc7(data, width, height),
180 #[cfg(feature = "texture-advanced")]
181 TextureFormat::BC4 => self.decode_bc4(data, width, height),
182 #[cfg(feature = "texture-advanced")]
183 TextureFormat::BC5 => self.decode_bc5(data, width, height),
184
185 #[cfg(not(feature = "texture-advanced"))]
186 format if format.is_compressed_format() => self.decode_unsupported(format),
187
188 _ => Err(BinaryError::unsupported(format!(
189 "Format {:?} is not a compressed format",
190 texture.format
191 ))),
192 }
193 }
194
195 fn can_decode(&self, format: TextureFormat) -> bool {
196 #[cfg(feature = "texture-advanced")]
197 {
198 matches!(
199 format,
200 TextureFormat::DXT1
201 | TextureFormat::DXT5
202 | TextureFormat::BC4
203 | TextureFormat::BC5
204 | TextureFormat::BC7
205 )
206 }
207
208 #[cfg(not(feature = "texture-advanced"))]
209 {
210 false
211 }
212 }
213
214 fn supported_formats(&self) -> Vec<TextureFormat> {
215 #[cfg(feature = "texture-advanced")]
216 {
217 vec![
218 TextureFormat::DXT1,
219 TextureFormat::DXT5,
220 TextureFormat::BC4,
221 TextureFormat::BC5,
222 TextureFormat::BC7,
223 ]
224 }
225
226 #[cfg(not(feature = "texture-advanced"))]
227 {
228 vec![]
229 }
230 }
231}
232
233impl Default for CompressedDecoder {
234 fn default() -> Self {
235 Self::new()
236 }
237}