unity_asset_decode/texture/decoders/
mod.rs1mod basic;
7mod compressed;
8mod crunch;
9mod mobile;
10
11pub use basic::BasicDecoder;
12pub use compressed::CompressedDecoder;
13pub use crunch::CrunchDecoder;
14pub use mobile::MobileDecoder;
15
16use super::formats::TextureFormat;
17use super::types::Texture2D;
18use crate::error::{BinaryError, Result};
19use image::RgbaImage;
20
21pub struct TextureDecoder {
26 basic: BasicDecoder,
27 compressed: CompressedDecoder,
28 mobile: MobileDecoder,
29 crunch: CrunchDecoder,
30}
31
32impl TextureDecoder {
33 pub fn new() -> Self {
35 Self {
36 basic: BasicDecoder::new(),
37 compressed: CompressedDecoder::new(),
38 mobile: MobileDecoder::new(),
39 crunch: CrunchDecoder::new(),
40 }
41 }
42
43 pub fn decode(&self, texture: &Texture2D) -> Result<RgbaImage> {
48 texture
50 .validate()
51 .map_err(|e| BinaryError::invalid_data(&e))?;
52
53 if texture.format.is_crunch_compressed() {
55 return self.crunch.decode(texture);
56 }
57
58 if texture.format.is_basic_format() {
60 self.basic.decode(texture)
61 } else if texture.format.is_compressed_format() {
62 self.compressed.decode(texture)
63 } else if texture.format.is_mobile_format() {
64 self.mobile.decode(texture)
65 } else {
66 Err(BinaryError::unsupported(format!(
67 "Unsupported texture format: {:?}",
68 texture.format
69 )))
70 }
71 }
72
73 pub fn can_decode(&self, format: TextureFormat) -> bool {
75 format.is_basic_format()
76 || format.is_compressed_format()
77 || format.is_mobile_format()
78 || format.is_crunch_compressed()
79 }
80
81 pub fn supported_formats(&self) -> Vec<TextureFormat> {
83 vec![
84 TextureFormat::Alpha8,
86 TextureFormat::RGB24,
87 TextureFormat::RGBA32,
88 TextureFormat::ARGB32,
89 TextureFormat::BGRA32,
90 TextureFormat::RGBA4444,
91 TextureFormat::ARGB4444,
92 TextureFormat::RGB565,
93 #[cfg(feature = "texture-advanced")]
95 TextureFormat::DXT1,
96 #[cfg(feature = "texture-advanced")]
97 TextureFormat::DXT5,
98 #[cfg(feature = "texture-advanced")]
99 TextureFormat::BC7,
100 #[cfg(feature = "texture-advanced")]
102 TextureFormat::ETC2_RGB,
103 #[cfg(feature = "texture-advanced")]
104 TextureFormat::ETC2_RGBA8,
105 #[cfg(feature = "texture-advanced")]
106 TextureFormat::ASTC_RGBA_4x4,
107 #[cfg(feature = "texture-advanced")]
109 TextureFormat::DXT1Crunched,
110 #[cfg(feature = "texture-advanced")]
111 TextureFormat::DXT5Crunched,
112 ]
113 }
114}
115
116impl Default for TextureDecoder {
117 fn default() -> Self {
118 Self::new()
119 }
120}
121
122pub trait Decoder {
126 fn decode(&self, texture: &Texture2D) -> Result<RgbaImage>;
128
129 fn can_decode(&self, format: TextureFormat) -> bool;
131
132 fn supported_formats(&self) -> Vec<TextureFormat>;
134}
135
136pub(crate) fn create_rgba_image(data: Vec<u8>, width: u32, height: u32) -> Result<RgbaImage> {
138 if data.len() != (width * height * 4) as usize {
139 return Err(BinaryError::invalid_data(format!(
140 "Invalid data size: expected {}, got {}",
141 width * height * 4,
142 data.len()
143 )));
144 }
145
146 RgbaImage::from_raw(width, height, data)
147 .ok_or_else(|| BinaryError::invalid_data("Failed to create RGBA image from raw data"))
148}
149
150pub(crate) fn validate_dimensions(width: u32, height: u32) -> Result<()> {
152 if width == 0 || height == 0 {
153 return Err(BinaryError::invalid_data("Invalid texture dimensions"));
154 }
155
156 if width > 16384 || height > 16384 {
158 return Err(BinaryError::invalid_data("Texture dimensions too large"));
159 }
160
161 Ok(())
162}