type Rgb = [u8; 3];
fn enc565_decode(value: u16) -> Rgb {
let red = (value >> 11) & 0x1F;
let green = (value >> 5) & 0x3F;
let blue = (value) & 0x1F;
[
(red * 0xFF / 0x1F) as u8,
(green * 0xFF / 0x3F) as u8,
(blue * 0xFF / 0x1F) as u8,
]
}
fn alpha_table_dxt5(alpha0: u8, alpha1: u8) -> [u8; 8] {
let mut table = [alpha0, alpha1, 0, 0, 0, 0, 0, 0xFF];
if alpha0 > alpha1 {
for i in 2..8u16 {
table[i as usize] =
(((8 - i) * u16::from(alpha0) + (i - 1) * u16::from(alpha1)) / 7) as u8;
}
} else {
for i in 2..6u16 {
table[i as usize] =
(((6 - i) * u16::from(alpha0) + (i - 1) * u16::from(alpha1)) / 5) as u8;
}
}
table
}
fn decode_dxt_colors(source: &[u8], dest: &mut [u8], is_dxt1: bool) {
assert!(source.len() == 8 && (dest.len() == 48 || dest.len() == 64));
let pitch = dest.len() / 16;
let color0 = u16::from(source[0]) | (u16::from(source[1]) << 8);
let color1 = u16::from(source[2]) | (u16::from(source[3]) << 8);
let color_table = u32::from(source[4])
| (u32::from(source[5]) << 8)
| (u32::from(source[6]) << 16)
| (u32::from(source[7]) << 24);
let mut colors = [[0; 3]; 4];
colors[0] = enc565_decode(color0);
colors[1] = enc565_decode(color1);
if color0 > color1 || !is_dxt1 {
for i in 0..3 {
colors[2][i] = ((u16::from(colors[0][i]) * 2 + u16::from(colors[1][i]) + 1) / 3) as u8;
colors[3][i] = ((u16::from(colors[0][i]) + u16::from(colors[1][i]) * 2 + 1) / 3) as u8;
}
} else {
for i in 0..3 {
colors[2][i] = (u16::from(colors[0][i]) + u16::from(colors[1][i])).div_ceil(2) as u8;
}
}
for i in 0..16 {
dest[i * pitch..i * pitch + 3]
.copy_from_slice(&colors[(color_table >> (i * 2)) as usize & 3]);
}
}
fn decode_dxt5_block(source: &[u8], dest: &mut [u8]) {
assert!(source.len() == 16 && dest.len() == 64);
let alpha_table = source[2..8]
.iter()
.rev()
.fold(0, |t, &b| (t << 8) | u64::from(b));
let alphas = alpha_table_dxt5(source[0], source[1]);
for i in 0..16 {
dest[i * 4 + 3] = alphas[(alpha_table >> (i * 3)) as usize & 7];
}
decode_dxt_colors(&source[8..16], dest, false);
}
fn decode_dxt3_block(source: &[u8], dest: &mut [u8]) {
assert!(source.len() == 16 && dest.len() == 64);
let alpha_table = source[0..8]
.iter()
.rev()
.fold(0, |t, &b| (t << 8) | u64::from(b));
for i in 0..16 {
dest[i * 4 + 3] = ((alpha_table >> (i * 4)) as u8 & 0xF) * 0x11;
}
decode_dxt_colors(&source[8..16], dest, false);
}
pub(crate) fn decode_dxt3_row(source: &[u8], dest: &mut [u8]) {
assert!(source.len() % 16 == 0);
let block_count = source.len() / 16;
assert!(dest.len() >= block_count * 64);
let mut decoded_block = [0u8; 64];
for (x, encoded_block) in source.chunks(16).enumerate() {
decode_dxt3_block(encoded_block, &mut decoded_block);
for line in 0..4 {
let offset = (block_count * line + x) * 16;
dest[offset..offset + 16].copy_from_slice(&decoded_block[line * 16..(line + 1) * 16]);
}
}
}
pub(crate) fn decode_dxt5_row(source: &[u8], dest: &mut [u8]) {
assert!(source.len() % 16 == 0);
let block_count = source.len() / 16;
assert!(dest.len() >= block_count * 64);
let mut decoded_block = [0u8; 64];
for (x, encoded_block) in source.chunks(16).enumerate() {
decode_dxt5_block(encoded_block, &mut decoded_block);
for line in 0..4 {
let offset = (block_count * line + x) * 16;
dest[offset..offset + 16].copy_from_slice(&decoded_block[line * 16..(line + 1) * 16]);
}
}
}