unity_asset_decode/texture/decoders/
basic.rs

1//! Basic texture format decoders
2//!
3//! This module handles uncompressed texture formats like RGBA32, RGB24, etc.
4
5use super::{Decoder, create_rgba_image, validate_dimensions};
6use crate::error::{BinaryError, Result};
7use crate::texture::formats::TextureFormat;
8use crate::texture::types::Texture2D;
9use image::RgbaImage;
10
11/// Decoder for basic uncompressed texture formats
12pub struct BasicDecoder;
13
14impl BasicDecoder {
15    /// Create a new basic decoder
16    pub fn new() -> Self {
17        Self
18    }
19
20    /// Decode RGBA32 format (R8G8B8A8)
21    fn decode_rgba32(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
22        validate_dimensions(width, height)?;
23
24        let expected_size = (width * height * 4) as usize;
25        if data.len() < expected_size {
26            return Err(BinaryError::invalid_data(format!(
27                "Insufficient data for RGBA32: expected {}, got {}",
28                expected_size,
29                data.len()
30            )));
31        }
32
33        // RGBA32 is already in the correct format
34        create_rgba_image(data[..expected_size].to_vec(), width, height)
35    }
36
37    /// Decode RGB24 format (R8G8B8)
38    fn decode_rgb24(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
39        validate_dimensions(width, height)?;
40
41        let expected_size = (width * height * 3) as usize;
42        if data.len() < expected_size {
43            return Err(BinaryError::invalid_data(format!(
44                "Insufficient data for RGB24: expected {}, got {}",
45                expected_size,
46                data.len()
47            )));
48        }
49
50        // Convert RGB24 to RGBA32 by adding alpha channel
51        let mut rgba_data = Vec::with_capacity((width * height * 4) as usize);
52        for chunk in data[..expected_size].chunks_exact(3) {
53            rgba_data.push(chunk[0]); // R
54            rgba_data.push(chunk[1]); // G
55            rgba_data.push(chunk[2]); // B
56            rgba_data.push(255); // A (fully opaque)
57        }
58
59        create_rgba_image(rgba_data, width, height)
60    }
61
62    /// Decode ARGB32 format (A8R8G8B8)
63    fn decode_argb32(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
64        validate_dimensions(width, height)?;
65
66        let expected_size = (width * height * 4) as usize;
67        if data.len() < expected_size {
68            return Err(BinaryError::invalid_data(format!(
69                "Insufficient data for ARGB32: expected {}, got {}",
70                expected_size,
71                data.len()
72            )));
73        }
74
75        // Convert ARGB32 to RGBA32 by reordering channels
76        let mut rgba_data = Vec::with_capacity(expected_size);
77        for chunk in data[..expected_size].chunks_exact(4) {
78            rgba_data.push(chunk[1]); // R (from position 1)
79            rgba_data.push(chunk[2]); // G (from position 2)
80            rgba_data.push(chunk[3]); // B (from position 3)
81            rgba_data.push(chunk[0]); // A (from position 0)
82        }
83
84        create_rgba_image(rgba_data, width, height)
85    }
86
87    /// Decode BGRA32 format (B8G8R8A8)
88    fn decode_bgra32(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
89        validate_dimensions(width, height)?;
90
91        let expected_size = (width * height * 4) as usize;
92        if data.len() < expected_size {
93            return Err(BinaryError::invalid_data(format!(
94                "Insufficient data for BGRA32: expected {}, got {}",
95                expected_size,
96                data.len()
97            )));
98        }
99
100        // Convert BGRA32 to RGBA32 by swapping R and B channels
101        let mut rgba_data = Vec::with_capacity(expected_size);
102        for chunk in data[..expected_size].chunks_exact(4) {
103            rgba_data.push(chunk[2]); // R (from B position)
104            rgba_data.push(chunk[1]); // G (unchanged)
105            rgba_data.push(chunk[0]); // B (from R position)
106            rgba_data.push(chunk[3]); // A (unchanged)
107        }
108
109        create_rgba_image(rgba_data, width, height)
110    }
111
112    /// Decode Alpha8 format (single channel alpha)
113    fn decode_alpha8(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
114        validate_dimensions(width, height)?;
115
116        let expected_size = (width * height) as usize;
117        if data.len() < expected_size {
118            return Err(BinaryError::invalid_data(format!(
119                "Insufficient data for Alpha8: expected {}, got {}",
120                expected_size,
121                data.len()
122            )));
123        }
124
125        // Convert Alpha8 to RGBA32 (white with alpha)
126        let mut rgba_data = Vec::with_capacity((width * height * 4) as usize);
127        for &alpha in &data[..expected_size] {
128            rgba_data.push(255); // R (white)
129            rgba_data.push(255); // G (white)
130            rgba_data.push(255); // B (white)
131            rgba_data.push(alpha); // A (from source)
132        }
133
134        create_rgba_image(rgba_data, width, height)
135    }
136
137    /// Decode RGBA4444 format (4 bits per channel)
138    fn decode_rgba4444(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
139        validate_dimensions(width, height)?;
140
141        let expected_size = (width * height * 2) as usize; // 2 bytes per pixel
142        if data.len() < expected_size {
143            return Err(BinaryError::invalid_data(format!(
144                "Insufficient data for RGBA4444: expected {}, got {}",
145                expected_size,
146                data.len()
147            )));
148        }
149
150        // Convert RGBA4444 to RGBA32
151        let mut rgba_data = Vec::with_capacity((width * height * 4) as usize);
152        for chunk in data[..expected_size].chunks_exact(2) {
153            let pixel = u16::from_le_bytes([chunk[0], chunk[1]]);
154
155            // Extract 4-bit channels and expand to 8-bit
156            let r = ((pixel >> 12) & 0xF) as u8;
157            let g = ((pixel >> 8) & 0xF) as u8;
158            let b = ((pixel >> 4) & 0xF) as u8;
159            let a = (pixel & 0xF) as u8;
160
161            // Expand 4-bit to 8-bit by duplicating bits
162            rgba_data.push(r << 4 | r);
163            rgba_data.push(g << 4 | g);
164            rgba_data.push(b << 4 | b);
165            rgba_data.push(a << 4 | a);
166        }
167
168        create_rgba_image(rgba_data, width, height)
169    }
170
171    /// Decode ARGB4444 format (4 bits per channel, ARGB order)
172    fn decode_argb4444(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
173        validate_dimensions(width, height)?;
174
175        let expected_size = (width * height * 2) as usize; // 2 bytes per pixel
176        if data.len() < expected_size {
177            return Err(BinaryError::invalid_data(format!(
178                "Insufficient data for ARGB4444: expected {}, got {}",
179                expected_size,
180                data.len()
181            )));
182        }
183
184        // Convert ARGB4444 to RGBA32
185        let mut rgba_data = Vec::with_capacity((width * height * 4) as usize);
186        for chunk in data[..expected_size].chunks_exact(2) {
187            let pixel = u16::from_le_bytes([chunk[0], chunk[1]]);
188
189            // Extract 4-bit channels (ARGB order)
190            let a = ((pixel >> 12) & 0xF) as u8;
191            let r = ((pixel >> 8) & 0xF) as u8;
192            let g = ((pixel >> 4) & 0xF) as u8;
193            let b = (pixel & 0xF) as u8;
194
195            // Expand 4-bit to 8-bit by duplicating bits
196            rgba_data.push(r << 4 | r); // R
197            rgba_data.push(g << 4 | g); // G
198            rgba_data.push(b << 4 | b); // B
199            rgba_data.push(a << 4 | a); // A
200        }
201
202        create_rgba_image(rgba_data, width, height)
203    }
204
205    /// Decode RGB565 format (5-6-5 bits per channel)
206    fn decode_rgb565(&self, data: &[u8], width: u32, height: u32) -> Result<RgbaImage> {
207        validate_dimensions(width, height)?;
208
209        let expected_size = (width * height * 2) as usize; // 2 bytes per pixel
210        if data.len() < expected_size {
211            return Err(BinaryError::invalid_data(format!(
212                "Insufficient data for RGB565: expected {}, got {}",
213                expected_size,
214                data.len()
215            )));
216        }
217
218        // Convert RGB565 to RGBA32
219        let mut rgba_data = Vec::with_capacity((width * height * 4) as usize);
220        for chunk in data[..expected_size].chunks_exact(2) {
221            let pixel = u16::from_le_bytes([chunk[0], chunk[1]]);
222
223            // Extract RGB channels
224            let r = ((pixel >> 11) & 0x1F) as u8;
225            let g = ((pixel >> 5) & 0x3F) as u8;
226            let b = (pixel & 0x1F) as u8;
227
228            // Expand to 8-bit
229            rgba_data.push((r << 3) | (r >> 2)); // 5-bit to 8-bit
230            rgba_data.push((g << 2) | (g >> 4)); // 6-bit to 8-bit
231            rgba_data.push((b << 3) | (b >> 2)); // 5-bit to 8-bit
232            rgba_data.push(255); // Alpha (fully opaque)
233        }
234
235        create_rgba_image(rgba_data, width, height)
236    }
237}
238
239impl Decoder for BasicDecoder {
240    fn decode(&self, texture: &Texture2D) -> Result<RgbaImage> {
241        let width = texture.width as u32;
242        let height = texture.height as u32;
243        let data = &texture.image_data;
244
245        match texture.format {
246            TextureFormat::RGBA32 => self.decode_rgba32(data, width, height),
247            TextureFormat::RGB24 => self.decode_rgb24(data, width, height),
248            TextureFormat::ARGB32 => self.decode_argb32(data, width, height),
249            TextureFormat::BGRA32 => self.decode_bgra32(data, width, height),
250            TextureFormat::Alpha8 => self.decode_alpha8(data, width, height),
251            TextureFormat::RGBA4444 => self.decode_rgba4444(data, width, height),
252            TextureFormat::ARGB4444 => self.decode_argb4444(data, width, height),
253            TextureFormat::RGB565 => self.decode_rgb565(data, width, height),
254            _ => Err(BinaryError::unsupported(format!(
255                "Format {:?} is not a basic format",
256                texture.format
257            ))),
258        }
259    }
260
261    fn can_decode(&self, format: TextureFormat) -> bool {
262        format.is_basic_format()
263    }
264
265    fn supported_formats(&self) -> Vec<TextureFormat> {
266        vec![
267            TextureFormat::Alpha8,
268            TextureFormat::RGB24,
269            TextureFormat::RGBA32,
270            TextureFormat::ARGB32,
271            TextureFormat::BGRA32,
272            TextureFormat::RGBA4444,
273            TextureFormat::ARGB4444,
274            TextureFormat::RGB565,
275        ]
276    }
277}
278
279impl Default for BasicDecoder {
280    fn default() -> Self {
281        Self::new()
282    }
283}