unity_asset_decode/texture/
formats.rs

1//! Texture format definitions
2//!
3//! This module defines Unity texture formats and their capabilities.
4//! Inspired by UnityPy/enums/TextureFormat.py
5
6use serde::{Deserialize, Serialize};
7
8/// Unity texture formats
9///
10/// This enum represents all texture formats supported by Unity.
11/// Values match Unity's internal TextureFormat enum.
12#[allow(non_camel_case_types)]
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
14#[repr(i32)]
15pub enum TextureFormat {
16    // Basic formats
17    Alpha8 = 1,
18    ARGB4444 = 2,
19    RGB24 = 3,
20    RGBA32 = 4,
21    ARGB32 = 5,
22    RGB565 = 7,
23    R16 = 9,
24
25    // Compressed formats
26    DXT1 = 10,
27    DXT5 = 12,
28    RGBA4444 = 13,
29    BGRA32 = 14,
30
31    // HDR formats
32    RHalf = 15,
33    RGHalf = 16,
34    RGBAHalf = 17,
35    RFloat = 18,
36    RGFloat = 19,
37    RGBAFloat = 20,
38
39    // Special formats
40    YUY2 = 21,
41    RGB9e5Float = 22,
42
43    // BC formats
44    BC6H = 24,
45    BC7 = 25,
46    BC4 = 26,
47    BC5 = 27,
48
49    // Crunched formats
50    DXT1Crunched = 28,
51    DXT5Crunched = 29,
52
53    // Mobile formats
54    PVRTC_RGB2 = 30,
55    PVRTC_RGBA2 = 31,
56    PVRTC_RGB4 = 32,
57    PVRTC_RGBA4 = 33,
58    ETC_RGB4 = 34,
59
60    // ETC2/EAC formats
61    EAC_R = 41,
62    EAC_R_SIGNED = 42,
63    EAC_RG = 43,
64    EAC_RG_SIGNED = 44,
65    ETC2_RGB = 45,
66    ETC2_RGBA1 = 46,
67    ETC2_RGBA8 = 47,
68
69    // ASTC formats
70    ASTC_RGB_4x4 = 48,
71    ASTC_RGB_5x5 = 49,
72    ASTC_RGB_6x6 = 50,
73    ASTC_RGB_8x8 = 51,
74    ASTC_RGB_10x10 = 52,
75    ASTC_RGB_12x12 = 53,
76    ASTC_RGBA_4x4 = 54,
77    ASTC_RGBA_5x5 = 55,
78    ASTC_RGBA_6x6 = 56,
79    ASTC_RGBA_8x8 = 57,
80    ASTC_RGBA_10x10 = 58,
81    ASTC_RGBA_12x12 = 59,
82
83    // More Crunched formats (Unity 2017.3+)
84    ETC_RGB4Crunched = 64,
85    ETC2_RGBA8Crunched = 65,
86
87    // Unknown format
88    #[default]
89    Unknown = -1,
90}
91
92impl From<i32> for TextureFormat {
93    fn from(value: i32) -> Self {
94        match value {
95            1 => TextureFormat::Alpha8,
96            2 => TextureFormat::ARGB4444,
97            3 => TextureFormat::RGB24,
98            4 => TextureFormat::RGBA32,
99            5 => TextureFormat::ARGB32,
100            7 => TextureFormat::RGB565,
101            9 => TextureFormat::R16,
102            10 => TextureFormat::DXT1,
103            12 => TextureFormat::DXT5,
104            13 => TextureFormat::RGBA4444,
105            14 => TextureFormat::BGRA32,
106            15 => TextureFormat::RHalf,
107            16 => TextureFormat::RGHalf,
108            17 => TextureFormat::RGBAHalf,
109            18 => TextureFormat::RFloat,
110            19 => TextureFormat::RGFloat,
111            20 => TextureFormat::RGBAFloat,
112            21 => TextureFormat::YUY2,
113            22 => TextureFormat::RGB9e5Float,
114            24 => TextureFormat::BC6H,
115            25 => TextureFormat::BC7,
116            26 => TextureFormat::BC4,
117            27 => TextureFormat::BC5,
118            28 => TextureFormat::DXT1Crunched,
119            29 => TextureFormat::DXT5Crunched,
120            30 => TextureFormat::PVRTC_RGB2,
121            31 => TextureFormat::PVRTC_RGBA2,
122            32 => TextureFormat::PVRTC_RGB4,
123            33 => TextureFormat::PVRTC_RGBA4,
124            34 => TextureFormat::ETC_RGB4,
125            41 => TextureFormat::EAC_R,
126            42 => TextureFormat::EAC_R_SIGNED,
127            43 => TextureFormat::EAC_RG,
128            44 => TextureFormat::EAC_RG_SIGNED,
129            45 => TextureFormat::ETC2_RGB,
130            46 => TextureFormat::ETC2_RGBA1,
131            47 => TextureFormat::ETC2_RGBA8,
132            48 => TextureFormat::ASTC_RGB_4x4,
133            49 => TextureFormat::ASTC_RGB_5x5,
134            50 => TextureFormat::ASTC_RGB_6x6,
135            51 => TextureFormat::ASTC_RGB_8x8,
136            52 => TextureFormat::ASTC_RGB_10x10,
137            53 => TextureFormat::ASTC_RGB_12x12,
138            54 => TextureFormat::ASTC_RGBA_4x4,
139            55 => TextureFormat::ASTC_RGBA_5x5,
140            56 => TextureFormat::ASTC_RGBA_6x6,
141            57 => TextureFormat::ASTC_RGBA_8x8,
142            58 => TextureFormat::ASTC_RGBA_10x10,
143            59 => TextureFormat::ASTC_RGBA_12x12,
144            64 => TextureFormat::ETC_RGB4Crunched,
145            65 => TextureFormat::ETC2_RGBA8Crunched,
146            _ => TextureFormat::Unknown,
147        }
148    }
149}
150
151/// Texture format capabilities and metadata
152#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct TextureFormatInfo {
154    pub name: String,
155    pub bits_per_pixel: u32,
156    pub block_size: (u32, u32), // (width, height) in pixels
157    pub compressed: bool,
158    pub has_alpha: bool,
159    pub supported: bool,
160}
161
162impl Default for TextureFormatInfo {
163    fn default() -> Self {
164        Self {
165            name: "Unknown".to_string(),
166            bits_per_pixel: 0,
167            block_size: (1, 1),
168            compressed: false,
169            has_alpha: false,
170            supported: false,
171        }
172    }
173}
174
175impl TextureFormat {
176    /// Get format information
177    pub fn info(&self) -> TextureFormatInfo {
178        match self {
179            TextureFormat::Alpha8 => TextureFormatInfo {
180                name: "Alpha8".to_string(),
181                bits_per_pixel: 8,
182                block_size: (1, 1),
183                compressed: false,
184                has_alpha: true,
185                supported: true,
186            },
187            TextureFormat::RGB24 => TextureFormatInfo {
188                name: "RGB24".to_string(),
189                bits_per_pixel: 24,
190                block_size: (1, 1),
191                compressed: false,
192                has_alpha: false,
193                supported: true,
194            },
195            TextureFormat::RGBA32 => TextureFormatInfo {
196                name: "RGBA32".to_string(),
197                bits_per_pixel: 32,
198                block_size: (1, 1),
199                compressed: false,
200                has_alpha: true,
201                supported: true,
202            },
203            TextureFormat::ARGB32 => TextureFormatInfo {
204                name: "ARGB32".to_string(),
205                bits_per_pixel: 32,
206                block_size: (1, 1),
207                compressed: false,
208                has_alpha: true,
209                supported: true,
210            },
211            TextureFormat::BGRA32 => TextureFormatInfo {
212                name: "BGRA32".to_string(),
213                bits_per_pixel: 32,
214                block_size: (1, 1),
215                compressed: false,
216                has_alpha: true,
217                supported: true,
218            },
219            TextureFormat::RGBA4444 => TextureFormatInfo {
220                name: "RGBA4444".to_string(),
221                bits_per_pixel: 16,
222                block_size: (1, 1),
223                compressed: false,
224                has_alpha: true,
225                supported: true,
226            },
227            TextureFormat::ARGB4444 => TextureFormatInfo {
228                name: "ARGB4444".to_string(),
229                bits_per_pixel: 16,
230                block_size: (1, 1),
231                compressed: false,
232                has_alpha: true,
233                supported: true,
234            },
235            TextureFormat::RGB565 => TextureFormatInfo {
236                name: "RGB565".to_string(),
237                bits_per_pixel: 16,
238                block_size: (1, 1),
239                compressed: false,
240                has_alpha: false,
241                supported: true,
242            },
243            TextureFormat::DXT1 => TextureFormatInfo {
244                name: "DXT1".to_string(),
245                bits_per_pixel: 4,
246                block_size: (4, 4),
247                compressed: true,
248                has_alpha: false,
249                supported: true,
250            },
251            TextureFormat::DXT5 => TextureFormatInfo {
252                name: "DXT5".to_string(),
253                bits_per_pixel: 8,
254                block_size: (4, 4),
255                compressed: true,
256                has_alpha: true,
257                supported: true,
258            },
259            TextureFormat::ETC2_RGB => TextureFormatInfo {
260                name: "ETC2_RGB".to_string(),
261                bits_per_pixel: 4,
262                block_size: (4, 4),
263                compressed: true,
264                has_alpha: false,
265                supported: true,
266            },
267            TextureFormat::ETC2_RGBA8 => TextureFormatInfo {
268                name: "ETC2_RGBA8".to_string(),
269                bits_per_pixel: 8,
270                block_size: (4, 4),
271                compressed: true,
272                has_alpha: true,
273                supported: true,
274            },
275            TextureFormat::ASTC_RGBA_4x4 => TextureFormatInfo {
276                name: "ASTC_RGBA_4x4".to_string(),
277                bits_per_pixel: 8,
278                block_size: (4, 4),
279                compressed: true,
280                has_alpha: true,
281                supported: true,
282            },
283            _ => TextureFormatInfo::default(),
284        }
285    }
286
287    /// Check if format is supported for decoding
288    pub fn is_supported(&self) -> bool {
289        self.info().supported
290    }
291
292    /// Get expected data size for given dimensions
293    pub fn calculate_data_size(&self, width: u32, height: u32) -> u32 {
294        let info = self.info();
295        if info.compressed {
296            let blocks_x = width.div_ceil(info.block_size.0);
297            let blocks_y = height.div_ceil(info.block_size.1);
298            // For compressed formats, calculate bytes per block
299            let bytes_per_block = match self {
300                TextureFormat::DXT1 => 8,
301                TextureFormat::DXT5 => 16,
302                TextureFormat::BC7 => 16,
303                TextureFormat::ETC2_RGB => 8,
304                TextureFormat::ETC2_RGBA8 => 16,
305                TextureFormat::ASTC_RGBA_4x4 => 16,
306                _ => info.bits_per_pixel / 8,
307            };
308            blocks_x * blocks_y * bytes_per_block
309        } else {
310            width * height * (info.bits_per_pixel / 8)
311        }
312    }
313
314    /// Check if format uses Crunch compression
315    pub fn is_crunch_compressed(&self) -> bool {
316        matches!(
317            self,
318            TextureFormat::DXT1Crunched
319                | TextureFormat::DXT5Crunched
320                | TextureFormat::ETC_RGB4Crunched
321                | TextureFormat::ETC2_RGBA8Crunched
322        )
323    }
324
325    /// Check if format is a basic uncompressed format
326    pub fn is_basic_format(&self) -> bool {
327        matches!(
328            self,
329            TextureFormat::Alpha8
330                | TextureFormat::RGB24
331                | TextureFormat::RGBA32
332                | TextureFormat::ARGB32
333                | TextureFormat::BGRA32
334                | TextureFormat::RGBA4444
335                | TextureFormat::ARGB4444
336                | TextureFormat::RGB565
337                | TextureFormat::R16
338        )
339    }
340
341    /// Check if format is a compressed format
342    pub fn is_compressed_format(&self) -> bool {
343        matches!(
344            self,
345            TextureFormat::DXT1
346                | TextureFormat::DXT5
347                | TextureFormat::BC4
348                | TextureFormat::BC5
349                | TextureFormat::BC6H
350                | TextureFormat::BC7
351        )
352    }
353
354    /// Check if format is a mobile-specific format
355    pub fn is_mobile_format(&self) -> bool {
356        matches!(
357            self,
358            TextureFormat::PVRTC_RGB2
359                | TextureFormat::PVRTC_RGBA2
360                | TextureFormat::PVRTC_RGB4
361                | TextureFormat::PVRTC_RGBA4
362                | TextureFormat::ETC_RGB4
363                | TextureFormat::ETC2_RGB
364                | TextureFormat::ETC2_RGBA1
365                | TextureFormat::ETC2_RGBA8
366                | TextureFormat::EAC_R
367                | TextureFormat::EAC_R_SIGNED
368                | TextureFormat::EAC_RG
369                | TextureFormat::EAC_RG_SIGNED
370                | TextureFormat::ASTC_RGB_4x4
371                | TextureFormat::ASTC_RGB_5x5
372                | TextureFormat::ASTC_RGB_6x6
373                | TextureFormat::ASTC_RGB_8x8
374                | TextureFormat::ASTC_RGB_10x10
375                | TextureFormat::ASTC_RGB_12x12
376                | TextureFormat::ASTC_RGBA_4x4
377                | TextureFormat::ASTC_RGBA_5x5
378                | TextureFormat::ASTC_RGBA_6x6
379                | TextureFormat::ASTC_RGBA_8x8
380                | TextureFormat::ASTC_RGBA_10x10
381                | TextureFormat::ASTC_RGBA_12x12
382        )
383    }
384}