use core::{f32, u32, u8};
use f32_to_i32_clamped;
pub fn compress_bc2(rgba: &[[u8; 4]; 16], mask: u32, block: &mut [u8]) {
let mut tmp = [0u8; 8];
for i in 0..tmp.len() {
let alpha1 = f32::from(rgba[2 * i][3]) * (15.0 / 255.0);
let alpha2 = f32::from(rgba[2 * i + 1][3]) * (15.0 / 255.0);
let mut quant1 = f32_to_i32_clamped(alpha1, 15) as u8;
let mut quant2 = f32_to_i32_clamped(alpha2, 15) as u8;
let bit1 = 1 << (2 * i);
let bit2 = 1 << (2 * i + 1);
if (mask & bit1) == 0 {
quant1 = 0;
}
if (mask & bit2) == 0 {
quant2 = 0;
}
tmp[i] = quant1 | (quant2 << 4)
}
block.copy_from_slice(&tmp);
}
pub fn decompress_bc2(rgba: &mut [[u8; 4]; 16], bytes: &[u8]) {
assert!(bytes.len() == 8);
for i in 0..bytes.len() {
let quant = bytes[i];
let lo = quant & 0x0F;
let hi = quant & 0xF0;
rgba[2 * i][3] = lo | (lo << 4);
rgba[2 * i + 1][3] = hi | (hi << 4);
}
}
fn fix_range(min: &mut u8, max: &mut u8, steps: u8) {
if (*max - *min) < steps {
*max = (i32::from(*min) + i32::from(steps)).min(i32::from(u8::MAX)) as u8;
}
if (*max - *min) < steps {
*min = (i32::from(*max) - i32::from(steps)).max(0) as u8;
}
}
fn fit_codes(rgba: &[[u8; 4]; 16], mask: u32, codes: [u8; 8], indices: &mut [u8; 16]) -> u32 {
let mut err = 0;
for i in 0..16 {
let bit = 1 << i;
if (mask & bit) == 0 {
indices[i] = 0;
continue;
}
let value = rgba[i][3];
let mut least = u32::MAX;
let mut index = 0;
for (j, &code) in codes.iter().enumerate().take(8) {
let dist = i32::from(value) - i32::from(code);
let dist = (dist * dist) as u32;
if dist < least {
least = dist;
index = j as u8;
}
}
indices[i] = index;
err += least;
}
err
}
fn write_alpha_block(alpha0: u8, alpha1: u8, indices: &[u8; 16], block: &mut [u8]) {
let mut buf = [0u8; 8];
buf[0] = alpha0;
buf[1] = alpha1;
for i in 0..2 {
let mut value = 0u32;
for j in 0..8 {
let index = u32::from(indices[8 * i + j]);
value |= index << (3 * j);
}
let mut tmp = &mut buf[2 + i * 3..5 + i * 3];
for (j, t) in tmp.iter_mut().enumerate() {
*t = ((value >> (8 * j)) & 0xFF) as u8;
}
}
block.copy_from_slice(&buf);
}
fn write_alpha_block5(alpha0: u8, alpha1: u8, indices: &[u8; 16], block: &mut [u8]) {
if alpha0 > alpha1 {
let mut swapped = *indices;
for index in &mut swapped[..] {
*index = match *index {
0 => 1,
1 => 0,
x @ 2...5 => 7 - x,
x => x,
}
}
write_alpha_block(alpha1, alpha0, &swapped, block);
} else {
write_alpha_block(alpha0, alpha1, indices, block);
}
}
fn write_alpha_block7(alpha0: u8, alpha1: u8, indices: &[u8; 16], block: &mut [u8]) {
if alpha0 < alpha1 {
let mut swapped = *indices;
for index in &mut swapped[..] {
*index = match *index {
0 => 1,
1 => 0,
x => 9 - x,
}
}
write_alpha_block(alpha1, alpha0, &swapped, block);
} else {
write_alpha_block(alpha0, alpha1, indices, block);
}
}
pub fn compress_bc3(rgba: &[[u8; 4]; 16], mask: u32, block: &mut [u8]) {
let mut min5 = u8::MAX;
let mut max5 = 0u8;
let mut min7 = u8::MAX;
let mut max7 = 0u8;
for (i, pixel) in rgba.iter().enumerate() {
let bit = 1 << i;
if (mask & bit) == 0 {
continue;
}
let value = pixel[3];
min7 = min7.min(value);
max7 = max7.max(value);
if value != 0 {
min5 = min5.min(value);
}
if value != u8::MAX {
max5 = max5.max(value);
}
}
if min5 > max5 {
min5 = max5;
}
if min7 > max7 {
min7 = max7;
}
fix_range(&mut min5, &mut max5, 5);
fix_range(&mut min7, &mut max7, 7);
let mut codes5 = [0u8; 8];
codes5[0] = min5;
codes5[1] = max5;
for i in 1..5i32 {
codes5[1 + i as usize] = (((5 - i) * i32::from(min5) + i * i32::from(max5)) / 5) as u8;
}
codes5[6] = 0;
codes5[7] = u8::MAX;
let mut codes7 = [0u8; 8];
codes7[0] = min5;
codes7[1] = max5;
for i in 1..7i32 {
codes7[1 + i as usize] = (((7 - i) * i32::from(min7) + i * i32::from(max7)) / 7) as u8;
}
let mut indices5 = [0u8; 16];
let mut indices7 = [0u8; 16];
let err5 = fit_codes(rgba, mask, codes5, &mut indices5);
let err7 = fit_codes(rgba, mask, codes7, &mut indices7);
if err5 <= err7 {
write_alpha_block5(min5, max5, &indices5, block);
} else {
write_alpha_block7(min7, max7, &indices7, block);
}
}
pub fn decompress_bc3(rgba: &mut [[u8; 4]; 16], bytes: &[u8]) {
assert!(bytes.len() == 8);
let alpha0 = i32::from(bytes[0]);
let alpha1 = i32::from(bytes[1]);
let mut codes = [0u8; 8];
codes[0] = bytes[0];
codes[1] = bytes[1];
if alpha0 <= alpha1 {
for i in 1..5i32 {
codes[1 + i as usize] = (((5 - i) * alpha0 + i * alpha1) / 5) as u8
}
codes[6] = 0;
codes[7] = u8::MAX;
} else {
for i in 1..7i32 {
codes[1 + i as usize] = (((7 - i) * alpha0 + i * alpha1) / 7) as u8;
}
}
let mut indices = [0u8; 16];
for i in 0..2 {
let mut value = 0i32;
for j in 0..3 {
let byte = i32::from(bytes[2 + 3 * i + j]);
value |= byte << (8 * j);
}
for j in 0..8 {
let index = (value >> (3 * j)) & 0x07;
indices[8 * i + j] = index as u8;
}
}
for (pixel, &index) in rgba.iter_mut().zip(indices.iter()) {
pixel[3] = codes[index as usize];
}
}