Skip to main content

banished_formats/
texture.rs

1//! CPU decoding of `ImageBuffer` levels to RGBA8, for targets without BC texture support.
2
3use 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
45/// Decode one level to tightly packed RGBA8 (`width * height * 4` bytes).
46/// Returns `None` for formats that are not colour images (R32F, depth).
47pub 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            // Large levels decode in bands of block rows on several threads (native); every
69            // band writes only its own rows, so the result is the same as in one pass.
70            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
92/// Levels with at least this many texels decode on several threads.
93const PARALLEL_TEXELS: usize = 512 * 512;
94
95/// Decode the BC1/BC3 blocks of block rows `by0..` into `band`, the RGBA8 rows they cover;
96/// `None` when the data ends early.
97fn 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    /// Large levels decode in bands on several threads; the result must equal one pass over
149    /// the whole level, for sizes that do and do not fill whole blocks and bands.
150    #[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}