blp/core/decode/
direct.rs1use crate::core::image::ImageBlp;
2use crate::error::error::BlpError;
3use byteorder::{LittleEndian, ReadBytesExt};
4use image::RgbaImage;
5use std::io::{Cursor, Read};
6
7impl ImageBlp {
8 pub fn decode_direct(&mut self, buf: &[u8], mip_visible: &[bool]) -> Result<(), BlpError> {
16 if self.header_offset + self.header_length > buf.len() {
19 return Err(BlpError::new("direct.header.oob"));
20 }
21 let mut cur = Cursor::new(&buf[..]);
22 cur.set_position(self.header_offset as u64);
23
24 let mut palette = [[0u8; 3]; 256];
25 for i in 0..256 {
26 let color = cur.read_u32::<LittleEndian>()?;
27 let r = ((color >> 16) & 0xFF) as u8;
29 let g = ((color >> 8) & 0xFF) as u8;
30 let b = (color & 0xFF) as u8;
31 palette[i] = [r, g, b];
32 }
33
34 let buf_len = buf.len();
35 let alpha_bits = self.alpha_bits;
36
37 for i in 0..self.mipmaps.len() {
39 let visible = mip_visible
41 .get(i)
42 .copied()
43 .unwrap_or(true);
44 if !visible {
45 self.mipmaps[i].image = None;
46 continue;
47 }
48
49 let off = self.mipmaps[i].offset;
50 let len = self.mipmaps[i].length;
51 if len == 0 {
52 continue; }
54 if off.checked_add(len).is_none() || off + len > buf_len {
55 continue; }
57
58 cur.set_position(off as u64);
59
60 let (w, h) = (self.mipmaps[i].width, self.mipmaps[i].height);
61 let pixel_count = (w as usize) * (h as usize);
62
63 let mut indices = vec![0u8; pixel_count];
65 cur.read_exact(&mut indices)
66 .map_err(|_| BlpError::new("direct.indices.truncated"))?;
67
68 let alpha_bytes = match alpha_bits {
70 0 => 0,
71 1 => (pixel_count + 7) / 8, 4 => (pixel_count + 1) / 2, 8 => pixel_count, _ => return Err(BlpError::new("blp.version.invalid").with_arg("msg", "unsupported alpha bits")),
75 };
76 let mut alpha_raw = vec![0u8; alpha_bytes];
77 if alpha_bytes > 0 {
78 cur.read_exact(&mut alpha_raw)
79 .map_err(|_| BlpError::new("direct.alpha.truncated"))?;
80 }
81
82 let mut img = RgbaImage::new(w, h);
84 for p in 0..pixel_count {
85 let idx = indices[p] as usize;
86 let [r, g, b] = palette[idx];
87 let a = match alpha_bits {
88 0 => 255,
89 1 => {
90 let byte = alpha_raw[p / 8];
91 let bit = (byte >> (p % 8)) & 1;
92 if bit == 1 { 255 } else { 0 }
93 }
94 4 => {
95 let byte = alpha_raw[p / 2];
96 let nibble = if (p & 1) == 0 { byte & 0x0F } else { byte >> 4 };
97 (nibble << 4) | nibble
98 }
99 8 => alpha_raw[p],
100 _ => 255,
101 };
102 img.get_pixel_mut((p as u32) % w, (p as u32) / w)
103 .0 = [r, g, b, a];
104 }
105 self.mipmaps[i].image = Some(img);
106 }
107 Ok(())
108 }
109}