1use super::direct::*;
2use super::header::*;
3use super::jpeg::*;
4pub use super::version::BlpVersion;
5
6pub const BLP_MAX_WIDTH: u32 = 65535;
9pub const BLP_MAX_HEIGHT: u32 = 65535;
12
13#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct BlpImage {
18 pub header: BlpHeader,
20 pub content: BlpContent,
22}
23
24impl BlpImage {
25 pub fn image_count(&self) -> usize {
27 match &self.content {
28 BlpContent::Dxt1(v) => v.images.len(),
29 BlpContent::Dxt3(v) => v.images.len(),
30 BlpContent::Dxt5(v) => v.images.len(),
31 BlpContent::Raw1(v) => v.images.len(),
32 BlpContent::Raw3(v) => v.images.len(),
33 BlpContent::Jpeg(v) => v.images.len(),
34 }
35 }
36
37 pub fn content_jpeg(&self) -> Option<&BlpJpeg> {
39 self.content.jpeg()
40 }
41
42 pub fn content_raw1(&self) -> Option<&BlpRaw1> {
44 self.content.raw1()
45 }
46
47 pub fn content_raw3(&self) -> Option<&BlpRaw3> {
49 self.content.raw3()
50 }
51
52 pub fn content_dxt1(&self) -> Option<&BlpDxtn> {
54 self.content.dxt1()
55 }
56
57 pub fn content_dxt3(&self) -> Option<&BlpDxtn> {
59 self.content.dxt3()
60 }
61
62 pub fn content_dxt5(&self) -> Option<&BlpDxtn> {
64 self.content.dxt5()
65 }
66
67 pub fn compression_type(&self) -> CompressionType {
69 match &self.content {
70 BlpContent::Jpeg(_) => CompressionType::Jpeg,
71 BlpContent::Raw1(_) => CompressionType::Raw1,
72 BlpContent::Raw3(_) => CompressionType::Raw3,
73 BlpContent::Dxt1(_) => CompressionType::Dxt1,
74 BlpContent::Dxt3(_) => CompressionType::Dxt3,
75 BlpContent::Dxt5(_) => CompressionType::Dxt5,
76 }
77 }
78
79 pub fn alpha_bit_depth(&self) -> u8 {
81 self.header.alpha_bits() as u8
82 }
83
84 pub fn best_mipmap_for_size(&self, target_size: u32) -> usize {
87 let image_count = self.image_count();
88 if image_count == 0 {
89 return 0;
90 }
91
92 let mut best_level = 0;
93 let mut best_diff = u32::MAX;
94
95 for level in 0..image_count {
96 let (width, height) = self.header.mipmap_size(level);
97 let size = width.max(height);
98 let diff = size.abs_diff(target_size);
99
100 if diff < best_diff {
101 best_diff = diff;
102 best_level = level;
103 }
104 }
105
106 best_level
107 }
108
109 pub fn mipmap_info(&self) -> Vec<MipMapInfo> {
111 let mut info = Vec::new();
112
113 for level in 0..self.image_count() {
114 let (width, height) = self.header.mipmap_size(level);
115 let data_size = match &self.content {
116 BlpContent::Jpeg(jpeg) => jpeg.images.get(level).map(|img| img.len()).unwrap_or(0),
117 BlpContent::Raw1(raw) => raw
118 .images
119 .get(level)
120 .map(|img| img.indexed_rgb.len() + img.indexed_alpha.len())
121 .unwrap_or(0),
122 BlpContent::Raw3(raw) => raw
123 .images
124 .get(level)
125 .map(|img| img.pixels.len() * 4)
126 .unwrap_or(0),
127 BlpContent::Dxt1(dxt) => dxt
128 .images
129 .get(level)
130 .map(|img| img.content.len())
131 .unwrap_or(0),
132 BlpContent::Dxt3(dxt) => dxt
133 .images
134 .get(level)
135 .map(|img| img.content.len())
136 .unwrap_or(0),
137 BlpContent::Dxt5(dxt) => dxt
138 .images
139 .get(level)
140 .map(|img| img.content.len())
141 .unwrap_or(0),
142 };
143
144 info.push(MipMapInfo {
145 level,
146 width,
147 height,
148 data_size,
149 pixel_count: width * height,
150 });
151 }
152
153 info
154 }
155
156 pub fn estimated_file_size(&self) -> usize {
158 let header_size = BlpHeader::size(self.header.version);
159 let content_size = match &self.content {
160 BlpContent::Jpeg(jpeg) => {
161 jpeg.header.len() + jpeg.images.iter().map(|img| img.len()).sum::<usize>()
162 }
163 BlpContent::Raw1(raw) => {
164 raw.cmap.len() * 4 + raw.images.iter().map(|img| {
166 img.indexed_rgb.len() + img.indexed_alpha.len()
167 }).sum::<usize>()
168 }
169 BlpContent::Raw3(raw) => raw
170 .images
171 .iter()
172 .map(|img| img.pixels.len() * 4)
173 .sum::<usize>(),
174 BlpContent::Dxt1(dxt) => dxt
175 .images
176 .iter()
177 .map(|img| img.content.len())
178 .sum::<usize>(),
179 BlpContent::Dxt3(dxt) => dxt
180 .images
181 .iter()
182 .map(|img| img.content.len())
183 .sum::<usize>(),
184 BlpContent::Dxt5(dxt) => dxt
185 .images
186 .iter()
187 .map(|img| img.content.len())
188 .sum::<usize>(),
189 };
190
191 header_size + content_size
192 }
193
194 pub fn compression_ratio(&self) -> f32 {
196 let uncompressed_size = self
197 .mipmap_info()
198 .iter()
199 .map(|info| info.width * info.height * 4) .sum::<u32>() as f32;
201
202 let compressed_size = self.estimated_file_size() as f32;
203
204 if compressed_size > 0.0 {
205 uncompressed_size / compressed_size
206 } else {
207 1.0
208 }
209 }
210}
211
212#[derive(Debug, Clone, PartialEq, Eq)]
214pub struct MipMapInfo {
215 pub level: usize,
217 pub width: u32,
219 pub height: u32,
221 pub data_size: usize,
223 pub pixel_count: u32,
225}
226
227#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
229pub enum CompressionType {
230 Jpeg,
232 Raw1,
234 Raw3,
236 Dxt1,
238 Dxt3,
240 Dxt5,
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
246pub enum BlpContent {
247 Jpeg(BlpJpeg),
249 Raw1(BlpRaw1),
251 Raw3(BlpRaw3),
253 Dxt1(BlpDxtn),
255 Dxt3(BlpDxtn),
257 Dxt5(BlpDxtn),
259}
260
261impl BlpContent {
262 pub fn tag(&self) -> BlpContentTag {
264 match self {
265 BlpContent::Jpeg { .. } => BlpContentTag::Jpeg,
266 BlpContent::Raw1 { .. } => BlpContentTag::Direct,
267 BlpContent::Raw3 { .. } => BlpContentTag::Direct,
268 BlpContent::Dxt1 { .. } => BlpContentTag::Direct,
269 BlpContent::Dxt3 { .. } => BlpContentTag::Direct,
270 BlpContent::Dxt5 { .. } => BlpContentTag::Direct,
271 }
272 }
273
274 pub fn jpeg(&self) -> Option<&BlpJpeg> {
276 match self {
277 BlpContent::Jpeg(v) => Some(v),
278 _ => None,
279 }
280 }
281
282 pub fn raw1(&self) -> Option<&BlpRaw1> {
284 match self {
285 BlpContent::Raw1(v) => Some(v),
286 _ => None,
287 }
288 }
289
290 pub fn raw3(&self) -> Option<&BlpRaw3> {
292 match self {
293 BlpContent::Raw3(v) => Some(v),
294 _ => None,
295 }
296 }
297
298 pub fn dxt1(&self) -> Option<&BlpDxtn> {
300 match self {
301 BlpContent::Dxt1(v) => Some(v),
302 _ => None,
303 }
304 }
305
306 pub fn dxt3(&self) -> Option<&BlpDxtn> {
308 match self {
309 BlpContent::Dxt3(v) => Some(v),
310 _ => None,
311 }
312 }
313
314 pub fn dxt5(&self) -> Option<&BlpDxtn> {
316 match self {
317 BlpContent::Dxt5(v) => Some(v),
318 _ => None,
319 }
320 }
321}