1mod dxtn;
2pub mod error;
3mod jpeg;
4mod mipmap;
5mod palette;
6mod raw1;
7mod raw3;
8
9use crate::types::*;
10pub use ::image::imageops::FilterType;
11use ::image::DynamicImage;
12use dxtn::*;
13pub use error::Error;
14use jpeg::*;
15use raw1::*;
16use raw3::*;
17use std::fmt;
18pub use texpresso::Algorithm as DxtAlgorithm;
19
20pub fn blp_to_image(image: &BlpImage, mipmap_level: usize) -> Result<DynamicImage, Error> {
22 match &image.content {
23 BlpContent::Raw1(content) => raw1_to_image(&image.header, content, mipmap_level),
24 BlpContent::Raw3(content) => raw3_to_image(&image.header, content, mipmap_level),
25 BlpContent::Jpeg(content) => jpeg_to_image(content, mipmap_level),
26 BlpContent::Dxt1(content) => dxtn_to_image(&image.header, content, mipmap_level),
27 BlpContent::Dxt3(content) => dxtn_to_image(&image.header, content, mipmap_level),
28 BlpContent::Dxt5(content) => dxtn_to_image(&image.header, content, mipmap_level),
29 }
30}
31
32#[derive(Clone, PartialEq, Eq)]
35pub enum BlpTarget {
36 Blp0(BlpOldFormat),
39 Blp1(BlpOldFormat),
42 Blp2(Blp2Format),
45}
46
47impl Default for BlpTarget {
48 fn default() -> Self {
49 BlpTarget::Blp1(Default::default())
50 }
51}
52
53impl fmt::Display for BlpTarget {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 BlpTarget::Blp0(format) => write!(f, "BLP0 {}", format),
57 BlpTarget::Blp1(format) => write!(f, "BLP1 {}", format),
58 BlpTarget::Blp2(format) => write!(f, "BLP2 {}", format),
59 }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
65pub enum BlpOldFormat {
66 Raw1 { alpha_bits: AlphaBits },
68 Jpeg { has_alpha: bool },
70}
71
72impl Default for BlpOldFormat {
73 fn default() -> Self {
74 BlpOldFormat::Jpeg { has_alpha: true }
75 }
76}
77
78impl fmt::Display for BlpOldFormat {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 match self {
81 BlpOldFormat::Raw1 { alpha_bits } => write!(f, "Palleted image with {}", alpha_bits),
82 BlpOldFormat::Jpeg { has_alpha } => {
83 if *has_alpha {
84 write!(f, "Jpeg image with alpha")
85 } else {
86 write!(f, "Jpeg image without alpha")
87 }
88 }
89 }
90 }
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
95pub enum AlphaBits {
96 NoAlpha,
98 Bit1,
100 Bit4,
102 #[default]
104 Bit8,
105}
106
107impl fmt::Display for AlphaBits {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 match self {
110 AlphaBits::NoAlpha => write!(f, "no alpha"),
111 AlphaBits::Bit1 => write!(f, "1 bit alpha"),
112 AlphaBits::Bit4 => write!(f, "4 bits alpha"),
113 AlphaBits::Bit8 => write!(f, "8 bits alpha"),
114 }
115 }
116}
117
118impl From<AlphaBits> for u32 {
119 fn from(value: AlphaBits) -> u32 {
120 match value {
121 AlphaBits::NoAlpha => 0,
122 AlphaBits::Bit1 => 1,
123 AlphaBits::Bit4 => 4,
124 AlphaBits::Bit8 => 8,
125 }
126 }
127}
128
129impl From<AlphaBits> for u8 {
130 fn from(value: AlphaBits) -> u8 {
131 match value {
132 AlphaBits::NoAlpha => 0,
133 AlphaBits::Bit1 => 1,
134 AlphaBits::Bit4 => 4,
135 AlphaBits::Bit8 => 8,
136 }
137 }
138}
139
140#[derive(Clone, PartialEq, Eq)]
142pub enum Blp2Format {
143 Raw1 { alpha_bits: AlphaBits },
145 Raw3,
147 Jpeg { has_alpha: bool },
149 Dxt1 {
151 has_alpha: bool,
152 compress_algorithm: DxtAlgorithm,
154 },
155 Dxt3 {
157 has_alpha: bool,
158 compress_algorithm: DxtAlgorithm,
160 },
161 Dxt5 {
163 has_alpha: bool,
164 compress_algorithm: DxtAlgorithm,
166 },
167}
168
169impl Default for Blp2Format {
170 fn default() -> Self {
171 Blp2Format::Dxt5 {
172 has_alpha: true,
173 compress_algorithm: Default::default(),
174 }
175 }
176}
177
178impl fmt::Display for Blp2Format {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 match self {
181 Blp2Format::Raw1 { alpha_bits } => write!(f, "Palleted image with {}", alpha_bits),
182 Blp2Format::Raw3 => write!(f, "RGBA raw data"),
183 Blp2Format::Jpeg { has_alpha } => {
184 if *has_alpha {
185 write!(f, "Jpeg image with alpha")
186 } else {
187 write!(f, "Jpeg image without alpha")
188 }
189 }
190 Blp2Format::Dxt1 {
191 has_alpha,
192 compress_algorithm,
193 } => {
194 let compress_str = match *compress_algorithm {
195 DxtAlgorithm::RangeFit => "fast/low quality",
196 DxtAlgorithm::ClusterFit => "slow/high quality",
197 DxtAlgorithm::IterativeClusterFit => "very slow/best quality",
198 };
199 if *has_alpha {
200 write!(f, "DXT1 image with alpha and compression {}", compress_str)
201 } else {
202 write!(
203 f,
204 "DXT1 image without alpha and compression {}",
205 compress_str
206 )
207 }
208 }
209 Blp2Format::Dxt3 {
210 has_alpha,
211 compress_algorithm,
212 } => {
213 let compress_str = match *compress_algorithm {
214 DxtAlgorithm::RangeFit => "fast/low quality",
215 DxtAlgorithm::ClusterFit => "slow/high quality",
216 DxtAlgorithm::IterativeClusterFit => "very slow/best quality",
217 };
218 if *has_alpha {
219 write!(f, "DXT3 image with alpha and compression {}", compress_str)
220 } else {
221 write!(
222 f,
223 "DXT3 image without alpha and compression {}",
224 compress_str
225 )
226 }
227 }
228 Blp2Format::Dxt5 {
229 has_alpha,
230 compress_algorithm,
231 } => {
232 let compress_str = match *compress_algorithm {
233 DxtAlgorithm::RangeFit => "fast/low quality",
234 DxtAlgorithm::ClusterFit => "slow/high quality",
235 DxtAlgorithm::IterativeClusterFit => "very slow/best quality",
236 };
237 if *has_alpha {
238 write!(f, "DXT5 image with alpha and compression {}", compress_str)
239 } else {
240 write!(
241 f,
242 "DXT5 image without alpha and compression {}",
243 compress_str
244 )
245 }
246 }
247 }
248 }
249}
250
251pub fn image_to_blp(
253 image: DynamicImage,
254 make_mipmaps: bool,
255 target: BlpTarget,
256 mipmap_filter: FilterType,
257) -> Result<BlpImage, Error> {
258 if image.width() > BLP_MAX_WIDTH {
259 return Err(Error::WidthTooLarge(image.width()));
260 }
261 if image.height() > BLP_MAX_HEIGHT {
262 return Err(Error::HeightTooLarge(image.height()));
263 }
264
265 match target {
266 BlpTarget::Blp0(format) => match format {
267 BlpOldFormat::Raw1 { alpha_bits } => {
268 let header = BlpHeader {
269 version: BlpVersion::Blp0,
270 content: BlpContentTag::Direct,
271 flags: BlpFlags::Old {
272 alpha_bits: alpha_bits.into(),
273 extra: 4,
274 has_mipmaps: if make_mipmaps { 1 } else { 0 },
275 },
276 width: image.width(),
277 height: image.height(),
278 mipmap_locator: MipmapLocator::External,
279 };
280 let blp_raw1 =
281 image_to_raw1(image, alpha_bits.into(), make_mipmaps, mipmap_filter)?;
282 Ok(BlpImage {
283 header,
284 content: BlpContent::Raw1(blp_raw1),
285 })
286 }
287 BlpOldFormat::Jpeg { has_alpha } => {
288 let alpha_bits = if has_alpha { 8 } else { 0 };
289 let blp_jpeg = image_to_jpeg(&image, make_mipmaps, alpha_bits, mipmap_filter)?;
290 Ok(BlpImage {
291 header: BlpHeader {
292 version: BlpVersion::Blp0,
293 content: BlpContentTag::Jpeg,
294 flags: BlpFlags::Old {
295 alpha_bits: alpha_bits as u32,
296 extra: 5,
297 has_mipmaps: if make_mipmaps { 1 } else { 0 },
298 },
299 width: image.width(),
300 height: image.height(),
301 mipmap_locator: MipmapLocator::External,
302 },
303 content: BlpContent::Jpeg(blp_jpeg),
304 })
305 }
306 },
307 BlpTarget::Blp1(format) => match format {
308 BlpOldFormat::Raw1 { alpha_bits } => {
309 let width = image.width();
310 let height = image.height();
311 let blp_raw1 =
312 image_to_raw1(image, alpha_bits.into(), make_mipmaps, mipmap_filter)?;
313 let header = BlpHeader {
314 version: BlpVersion::Blp1,
315 content: BlpContentTag::Direct,
316 flags: BlpFlags::Old {
317 alpha_bits: alpha_bits.into(),
318 extra: 4,
319 has_mipmaps: if make_mipmaps { 1 } else { 0 },
320 },
321 width,
322 height,
323 mipmap_locator: blp_raw1.mipmap_locator(BlpVersion::Blp1),
324 };
325 Ok(BlpImage {
326 header,
327 content: BlpContent::Raw1(blp_raw1),
328 })
329 }
330 BlpOldFormat::Jpeg { has_alpha } => {
331 let alpha_bits = if has_alpha { 8 } else { 0 };
332 let blp_jpeg = image_to_jpeg(&image, make_mipmaps, alpha_bits, mipmap_filter)?;
333 Ok(BlpImage {
334 header: BlpHeader {
335 version: BlpVersion::Blp1,
336 content: BlpContentTag::Jpeg,
337 flags: BlpFlags::Old {
338 alpha_bits: alpha_bits as u32,
339 extra: 5,
340 has_mipmaps: if make_mipmaps { 1 } else { 0 },
341 },
342 width: image.width(),
343 height: image.height(),
344 mipmap_locator: blp_jpeg.mipmap_locator(BlpVersion::Blp1),
345 },
346 content: BlpContent::Jpeg(blp_jpeg),
347 })
348 }
349 },
350 BlpTarget::Blp2(format) => match format {
351 Blp2Format::Raw1 { alpha_bits } => {
352 let width = image.width();
353 let height = image.height();
354 let blp_raw1 =
355 image_to_raw1(image, alpha_bits.into(), make_mipmaps, mipmap_filter)?;
356 let header = BlpHeader {
357 version: BlpVersion::Blp2,
358 content: BlpContentTag::Direct,
359 flags: BlpFlags::Blp2 {
360 compression: Compression::Raw1,
361 alpha_bits: alpha_bits.into(),
362 alpha_type: 0,
363 has_mipmaps: if make_mipmaps { 1 } else { 0 },
364 },
365 width,
366 height,
367 mipmap_locator: blp_raw1.mipmap_locator(BlpVersion::Blp2),
368 };
369 Ok(BlpImage {
370 header,
371 content: BlpContent::Raw1(blp_raw1),
372 })
373 }
374 Blp2Format::Raw3 => {
375 let width = image.width();
376 let height = image.height();
377 let blp_raw3 = image_to_raw3(image, make_mipmaps, mipmap_filter)?;
378 Ok(BlpImage {
379 header: BlpHeader {
380 version: BlpVersion::Blp2,
381 content: BlpContentTag::Direct,
382 flags: BlpFlags::Blp2 {
383 compression: Compression::Raw3,
384 alpha_bits: 8,
385 alpha_type: 0,
386 has_mipmaps: if make_mipmaps { 1 } else { 0 },
387 },
388 width,
389 height,
390 mipmap_locator: blp_raw3.mipmap_locator(BlpVersion::Blp2),
391 },
392 content: BlpContent::Raw3(blp_raw3),
393 })
394 }
395 Blp2Format::Jpeg { has_alpha } => {
396 let alpha_bits = if has_alpha { 8 } else { 0 };
397 let blp_jpeg = image_to_jpeg(&image, make_mipmaps, alpha_bits, mipmap_filter)?;
398 Ok(BlpImage {
399 header: BlpHeader {
400 version: BlpVersion::Blp2,
401 content: BlpContentTag::Jpeg,
402 flags: BlpFlags::Blp2 {
403 compression: Compression::Jpeg,
404 alpha_bits: 8,
405 alpha_type: 0,
406 has_mipmaps: if make_mipmaps { 1 } else { 0 },
407 },
408 width: image.width(),
409 height: image.height(),
410 mipmap_locator: blp_jpeg.mipmap_locator(BlpVersion::Blp2),
411 },
412 content: BlpContent::Jpeg(blp_jpeg),
413 })
414 }
415 Blp2Format::Dxt1 {
416 has_alpha,
417 compress_algorithm,
418 } => {
419 let width = image.width();
420 let height = image.height();
421 let alpha_bits = if has_alpha { 1 } else { 0 };
422 let blp_dxtn = image_to_dxtn(
423 image,
424 DxtnFormat::Dxt1,
425 make_mipmaps,
426 mipmap_filter,
427 compress_algorithm,
428 )?;
429 Ok(BlpImage {
430 header: BlpHeader {
431 version: BlpVersion::Blp2,
432 content: BlpContentTag::Direct,
433 flags: BlpFlags::Blp2 {
434 compression: Compression::Dxtc,
435 alpha_bits,
436 alpha_type: 0,
437 has_mipmaps: if make_mipmaps { 1 } else { 0 },
438 },
439 width,
440 height,
441 mipmap_locator: blp_dxtn.mipmap_locator(BlpVersion::Blp2),
442 },
443 content: BlpContent::Dxt1(blp_dxtn),
444 })
445 }
446 Blp2Format::Dxt3 {
447 has_alpha,
448 compress_algorithm,
449 } => {
450 let width = image.width();
451 let height = image.height();
452 let alpha_bits = if has_alpha { 8 } else { 0 };
453 let blp_dxtn = image_to_dxtn(
454 image,
455 DxtnFormat::Dxt3,
456 make_mipmaps,
457 mipmap_filter,
458 compress_algorithm,
459 )?;
460 Ok(BlpImage {
461 header: BlpHeader {
462 version: BlpVersion::Blp2,
463 content: BlpContentTag::Direct,
464 flags: BlpFlags::Blp2 {
465 compression: Compression::Dxtc,
466 alpha_bits,
467 alpha_type: 1,
468 has_mipmaps: if make_mipmaps { 1 } else { 0 },
469 },
470 width,
471 height,
472 mipmap_locator: blp_dxtn.mipmap_locator(BlpVersion::Blp2),
473 },
474 content: BlpContent::Dxt3(blp_dxtn),
475 })
476 }
477 Blp2Format::Dxt5 {
478 has_alpha,
479 compress_algorithm,
480 } => {
481 let width = image.width();
482 let height = image.height();
483 let alpha_bits = if has_alpha { 8 } else { 0 };
484 let blp_dxtn = image_to_dxtn(
485 image,
486 DxtnFormat::Dxt5,
487 make_mipmaps,
488 mipmap_filter,
489 compress_algorithm,
490 )?;
491 Ok(BlpImage {
492 header: BlpHeader {
493 version: BlpVersion::Blp2,
494 content: BlpContentTag::Direct,
495 flags: BlpFlags::Blp2 {
496 compression: Compression::Dxtc,
497 alpha_bits,
498 alpha_type: 7,
499 has_mipmaps: if make_mipmaps { 1 } else { 0 },
500 },
501 width,
502 height,
503 mipmap_locator: blp_dxtn.mipmap_locator(BlpVersion::Blp2),
504 },
505 content: BlpContent::Dxt5(blp_dxtn),
506 })
507 }
508 },
509 }
510}