1use crate::types::ImageFormat;
4
5fn rgb565(c: u16) -> [u8; 3] {
6 let (r, g, b) = ((c >> 11) & 31, (c >> 5) & 63, c & 31);
7 [
8 (r << 3 | r >> 2) as u8,
9 (g << 2 | g >> 4) as u8,
10 (b << 3 | b >> 2) as u8,
11 ]
12}
13
14fn bc1_palette(c0: u16, c1: u16, four_colour: bool) -> [[u8; 4]; 4] {
15 let (p0, p1) = (rgb565(c0), rgb565(c1));
16 let mix = |a: u16, b: u16, d: u16| -> [u8; 4] {
17 let f = |i: usize| ((a * p0[i] as u16 + b * p1[i] as u16) / d) as u8;
18 [f(0), f(1), f(2), 255]
19 };
20 let solid = |p: [u8; 3]| [p[0], p[1], p[2], 255];
21 if c0 > c1 || four_colour {
22 [solid(p0), solid(p1), mix(2, 1, 3), mix(1, 2, 3)]
23 } else {
24 [solid(p0), solid(p1), mix(1, 1, 2), [0, 0, 0, 0]]
25 }
26}
27
28fn bc3_alpha(a0: u8, a1: u8) -> [u8; 8] {
29 let (a0w, a1w) = (a0 as u16, a1 as u16);
30 let mut t = [a0, a1, 0, 0, 0, 0, 0, 0];
31 if a0 > a1 {
32 for i in 1..7u16 {
33 t[i as usize + 1] = (((7 - i) * a0w + i * a1w) / 7) as u8;
34 }
35 } else {
36 for i in 1..5u16 {
37 t[i as usize + 1] = (((5 - i) * a0w + i * a1w) / 5) as u8;
38 }
39 t[6] = 0;
40 t[7] = 255;
41 }
42 t
43}
44
45pub fn decode_rgba(format: ImageFormat, width: u32, height: u32, data: &[u8]) -> Option<Vec<u8>> {
48 let (w, h) = (width.max(1) as usize, height.max(1) as usize);
49 let mut out = vec![0u8; w * h * 4];
50 match format {
51 ImageFormat::Bgra8 => {
52 for (o, i) in out.chunks_exact_mut(4).zip(data.chunks_exact(4)) {
53 o.copy_from_slice(&[i[2], i[1], i[0], i[3]]);
54 }
55 }
56 ImageFormat::R8 => {
57 for (o, &v) in out.chunks_exact_mut(4).zip(data) {
58 o.copy_from_slice(&[v, v, v, 255]);
59 }
60 }
61 ImageFormat::A8 => {
62 for (o, &v) in out.chunks_exact_mut(4).zip(data) {
63 o.copy_from_slice(&[255, 255, 255, v]);
64 }
65 }
66 ImageFormat::Bc1 | ImageFormat::Bc3 => {
67 let bc3 = format == ImageFormat::Bc3;
68 let rows_per_band = if w * h >= PARALLEL_TEXELS {
71 8
72 } else {
73 h.div_ceil(4)
74 };
75 let mut bands: Vec<(usize, &mut [u8])> = out
76 .chunks_mut(rows_per_band * 4 * w * 4)
77 .enumerate()
78 .map(|(i, band)| (i * rows_per_band, band))
79 .collect();
80 let decoded = crate::par::map_mut(&mut bands, |(by0, band)| {
81 decode_bc_rows(data, w, h, bc3, *by0, band)
82 });
83 if decoded.contains(&None) {
84 return None;
85 }
86 }
87 ImageFormat::R32F | ImageFormat::R24G8 => return None,
88 }
89 Some(out)
90}
91
92const PARALLEL_TEXELS: usize = 512 * 512;
94
95fn decode_bc_rows(
98 data: &[u8],
99 w: usize,
100 h: usize,
101 bc3: bool,
102 by0: usize,
103 band: &mut [u8],
104) -> Option<()> {
105 let size = if bc3 { 16 } else { 8 };
106 let bw = w.div_ceil(4);
107 let band_rows = band.len() / (w * 4);
108 for by in by0..by0 + band_rows.div_ceil(4) {
109 for bx in 0..bw {
110 let mut o = (by * bw + bx) * size;
111 let blk = data.get(o..o + size)?;
112 let alpha = bc3.then(|| {
113 let bits =
114 u64::from_le_bytes([blk[2], blk[3], blk[4], blk[5], blk[6], blk[7], 0, 0]);
115 (bc3_alpha(blk[0], blk[1]), bits)
116 });
117 if bc3 {
118 o += 8;
119 }
120 let c0 = u16::from_le_bytes([data[o], data[o + 1]]);
121 let c1 = u16::from_le_bytes([data[o + 2], data[o + 3]]);
122 let bits = u32::from_le_bytes(data[o + 4..o + 8].try_into().unwrap());
123 let pal = bc1_palette(c0, c1, bc3);
124 for py in 0..4 {
125 for px in 0..4 {
126 let (x, y) = (bx * 4 + px, by * 4 + py);
127 if x >= w || y >= h {
128 continue;
129 }
130 let k = py * 4 + px;
131 let mut c = pal[((bits >> (2 * k)) & 3) as usize];
132 if let Some((table, abits)) = alpha {
133 c[3] = table[((abits >> (3 * k)) & 7) as usize];
134 }
135 let p = ((y - by0 * 4) * w + x) * 4;
136 band[p..p + 4].copy_from_slice(&c);
137 }
138 }
139 }
140 }
141 Some(())
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
151 fn banded_decode_matches_one_pass() {
152 let mut seed = 0x1234_5678u32;
153 for (format, size) in [(ImageFormat::Bc1, 8), (ImageFormat::Bc3, 16)] {
154 for (w, h) in [(1024, 1024), (1022, 1030), (600, 514), (64, 64)] {
155 let blocks = (w as usize).div_ceil(4) * (h as usize).div_ceil(4);
156 let data: Vec<u8> = (0..blocks * size)
157 .map(|_| {
158 seed = seed.wrapping_mul(1_103_515_245).wrapping_add(12_345);
159 (seed >> 16) as u8
160 })
161 .collect();
162 let (wu, hu) = (w as usize, h as usize);
163 let mut whole = vec![0u8; wu * hu * 4];
164 decode_bc_rows(&data, wu, hu, format == ImageFormat::Bc3, 0, &mut whole).unwrap();
165 assert_eq!(decode_rgba(format, w, h, &data).unwrap(), whole, "{w}x{h}");
166 assert!(decode_rgba(format, w, h, &data[..data.len() - 1]).is_none());
167 }
168 }
169 }
170}