use core::{mem, u8};
use byteorder::{ByteOrder, LittleEndian};
use f32_to_i32_clamped;
use math::Vec3;
fn pack_565(colour: &Vec3) -> u16 {
let r = f32_to_i32_clamped(31.0 * colour.x(), 31) as u8;
let g = f32_to_i32_clamped(63.0 * colour.y(), 63) as u8;
let b = f32_to_i32_clamped(31.0 * colour.z(), 31) as u8;
LittleEndian::read_u16(&[(g << 5) | b, (r << 3) | (g >> 3)])
}
fn write_block(a: u16, b: u16, indices: &[u8; 16], block: &mut [u8]) {
LittleEndian::write_u16(&mut &mut block[0..2], a);
LittleEndian::write_u16(&mut &mut block[2..4], b);
let mut packed = [0u8; 4];
for i in 0..packed.len() {
packed[i] = ((indices[4 * i + 3] & 0x03) << 6)
| ((indices[4 * i + 2] & 0x03) << 4)
| ((indices[4 * i + 1] & 0x03) << 2)
| (indices[4 * i] & 0x03);
}
block[4..].copy_from_slice(&packed);
}
pub fn write3(start: &Vec3, end: &Vec3, indices: &[u8; 16], block: &mut [u8]) {
let mut a = pack_565(start);
let mut b = pack_565(end);
let mut remapped = *indices;
if a > b {
mem::swap(&mut a, &mut b);
for index in &mut remapped[..] {
*index = match *index {
0 => 1,
1 => 0,
x => x,
};
}
}
write_block(a, b, &remapped, block);
}
pub fn write4(start: &Vec3, end: &Vec3, indices: &[u8; 16], block: &mut [u8]) {
let mut a = pack_565(start);
let mut b = pack_565(end);
let mut remapped = [0u8; 16];
if a < b {
mem::swap(&mut a, &mut b);
for (mut remapped, index) in remapped.iter_mut().zip(indices) {
*remapped = (index ^ 0x01) & 0x03;
}
} else if a > b {
remapped = *indices;
}
write_block(a, b, &remapped, block);
}
fn unpack_565(packed: &[u8]) -> [u8; 4] {
assert!(packed.len() == 2);
let value: u16 = u16::from(packed[0]) | (u16::from(packed[1]) << 8);
let r = ((value >> 11) & 0x1F) as u8;
let g = ((value >> 5) & 0x3F) as u8;
let b = (value & 0x1F) as u8;
let r = (r << 3) | (r >> 2);
let g = (g << 2) | (g >> 4);
let b = (b << 3) | (b >> 2);
[r, g, b, 255u8]
}
pub fn decompress(bytes: &[u8], is_bc1: bool) -> [[u8; 4]; 16] {
assert!(bytes.len() == 8);
let mut codes = [0u8; 16];
let a = LittleEndian::read_u16(&bytes[0..2]);
let b = LittleEndian::read_u16(&bytes[2..4]);
codes[0..4].copy_from_slice(&unpack_565(&bytes[0..2]));
codes[4..8].copy_from_slice(&unpack_565(&bytes[2..4]));
for i in 0..4 {
let c = u32::from(codes[i]);
let d = u32::from(codes[4 + i]);
if is_bc1 && (a <= b) {
codes[8 + i] = ((c + d) / 2) as u8;
codes[12 + i] = 0;
} else {
codes[8 + i] = ((2 * c + d) / 3) as u8;
codes[12 + i] = ((c + 2 * d) / 3) as u8;
}
}
codes[8 + 3] = u8::MAX;
codes[12 + 3] = if is_bc1 && (a <= b) { 0u8 } else { u8::MAX };
let mut indices = [0u8; 16];
for i in 0..4 {
let ind = &mut indices[4 * i..4 * i + 4];
let packed = bytes[4 + i];
ind[0] = packed & 0x03;
ind[1] = (packed >> 2) & 0x03;
ind[2] = (packed >> 4) & 0x03;
ind[3] = (packed >> 6) & 0x03;
}
let mut rgba = [[0u8; 4]; 16];
for i in 0..rgba.len() {
let offset = 4 * indices[i] as usize;
let length = rgba[i].len();
rgba[i].copy_from_slice(&codes[offset..(offset + length)])
}
rgba
}