use bytemuck::Pod;
use crate::{error::SurfaceError, mip_size, rgba::convert::snorm8_to_unorm8};
use super::{Bc1, Bc2, Bc3, Bc4, Bc4S, Bc5, Bc5S, Bc6, Bc7, BLOCK_HEIGHT, BLOCK_WIDTH, CHANNELS};
pub trait BcnDecode<Pixel> {
type CompressedBlock;
fn decompress_block(block: &Self::CompressedBlock) -> [[Pixel; BLOCK_WIDTH]; BLOCK_HEIGHT];
}
pub trait ReadBlock {
const SIZE_IN_BYTES: usize;
fn read_block(data: &[u8], offset: usize) -> Self;
}
impl ReadBlock for [u8; 8] {
const SIZE_IN_BYTES: usize = 8;
fn read_block(data: &[u8], offset: usize) -> Self {
data[offset..offset + 8].try_into().unwrap()
}
}
impl ReadBlock for [u8; 16] {
const SIZE_IN_BYTES: usize = 16;
fn read_block(data: &[u8], offset: usize) -> Self {
data[offset..offset + 16].try_into().unwrap()
}
}
impl BcnDecode<[u8; 4]> for Bc1 {
type CompressedBlock = [u8; 8];
fn decompress_block(block: &[u8; 8]) -> [[[u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] {
let mut decompressed = [[[0u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT];
bcdec_rs::bc1(
block,
bytemuck::cast_slice_mut(&mut decompressed),
BLOCK_WIDTH * CHANNELS,
);
decompressed
}
}
impl BcnDecode<[u8; 4]> for Bc2 {
type CompressedBlock = [u8; 16];
fn decompress_block(block: &[u8; 16]) -> [[[u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] {
let mut decompressed = [[[0u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT];
bcdec_rs::bc2(
block,
bytemuck::cast_slice_mut(&mut decompressed),
BLOCK_WIDTH * CHANNELS,
);
decompressed
}
}
impl BcnDecode<[u8; 4]> for Bc3 {
type CompressedBlock = [u8; 16];
fn decompress_block(block: &[u8; 16]) -> [[[u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] {
let mut decompressed = [[[0u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT];
bcdec_rs::bc3(
block,
bytemuck::cast_slice_mut(&mut decompressed),
BLOCK_WIDTH * CHANNELS,
);
decompressed
}
}
impl BcnDecode<[u8; 4]> for Bc4 {
type CompressedBlock = [u8; 8];
fn decompress_block(block: &[u8; 8]) -> [[[u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] {
let mut decompressed_r = [[0u8; BLOCK_WIDTH]; BLOCK_HEIGHT];
bcdec_rs::bc4(
block,
bytemuck::cast_slice_mut(&mut decompressed_r),
BLOCK_WIDTH,
false,
);
let mut decompressed = [[[0u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT];
for y in 0..BLOCK_HEIGHT {
for x in 0..BLOCK_WIDTH {
let r = decompressed_r[y][x];
decompressed[y][x] = [r, r, r, 255u8];
}
}
decompressed
}
}
impl BcnDecode<[u8; 4]> for Bc4S {
type CompressedBlock = [u8; 8];
fn decompress_block(block: &[u8; 8]) -> [[[u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] {
let mut decompressed_r = [[0; BLOCK_WIDTH]; BLOCK_HEIGHT];
bcdec_rs::bc4(
block,
bytemuck::cast_slice_mut(&mut decompressed_r),
BLOCK_WIDTH,
true,
);
let mut decompressed = [[[0u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT];
for y in 0..BLOCK_HEIGHT {
for x in 0..BLOCK_WIDTH {
let r = snorm8_to_unorm8(decompressed_r[y][x]);
decompressed[y][x] = [r, r, r, 255u8];
}
}
decompressed
}
}
impl BcnDecode<[f32; 4]> for Bc4S {
type CompressedBlock = [u8; 8];
fn decompress_block(block: &[u8; 8]) -> [[[f32; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] {
let mut decompressed_r = [[0.0; BLOCK_WIDTH]; BLOCK_HEIGHT];
bcdec_rs::bc4_float(
block,
bytemuck::cast_slice_mut(&mut decompressed_r),
BLOCK_WIDTH,
true,
);
let mut decompressed = [[[0.0; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT];
for y in 0..BLOCK_HEIGHT {
for x in 0..BLOCK_WIDTH {
let r = decompressed_r[y][x];
decompressed[y][x] = [r, r, r, 1.0];
}
}
decompressed
}
}
impl BcnDecode<[u8; 4]> for Bc5 {
type CompressedBlock = [u8; 16];
fn decompress_block(block: &[u8; 16]) -> [[[u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] {
let mut decompressed_rg = [[[0u8; 2]; BLOCK_WIDTH]; BLOCK_HEIGHT];
bcdec_rs::bc5(
block,
bytemuck::cast_slice_mut(&mut decompressed_rg),
BLOCK_WIDTH * 2,
false,
);
let mut decompressed = [[[0u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT];
for y in 0..BLOCK_HEIGHT {
for x in 0..BLOCK_HEIGHT {
let [r, g] = decompressed_rg[y][x];
decompressed[y][x] = [r, g, 0u8, 255u8];
}
}
decompressed
}
}
impl BcnDecode<[u8; 4]> for Bc5S {
type CompressedBlock = [u8; 16];
fn decompress_block(block: &[u8; 16]) -> [[[u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] {
let mut decompressed_rg = [[[0u8; 2]; BLOCK_WIDTH]; BLOCK_HEIGHT];
bcdec_rs::bc5(
block,
bytemuck::cast_slice_mut(&mut decompressed_rg),
BLOCK_WIDTH * 2,
true,
);
let mut decompressed = [[[0u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT];
for y in 0..BLOCK_HEIGHT {
for x in 0..BLOCK_HEIGHT {
let [r, g] = decompressed_rg[y][x];
decompressed[y][x] = [
snorm8_to_unorm8(r),
snorm8_to_unorm8(g),
snorm8_to_unorm8(0u8),
255u8,
];
}
}
decompressed
}
}
impl BcnDecode<[f32; 4]> for Bc5S {
type CompressedBlock = [u8; 16];
fn decompress_block(block: &[u8; 16]) -> [[[f32; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] {
let mut decompressed_rg = [[[0.0; 2]; BLOCK_WIDTH]; BLOCK_HEIGHT];
bcdec_rs::bc5_float(
block,
bytemuck::cast_slice_mut(&mut decompressed_rg),
BLOCK_WIDTH * 2,
true,
);
let mut decompressed = [[[0.0; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT];
for y in 0..BLOCK_HEIGHT {
for x in 0..BLOCK_HEIGHT {
let [r, g] = decompressed_rg[y][x];
decompressed[y][x] = [r, g, 0.5, 1.0];
}
}
decompressed
}
}
impl BcnDecode<[f32; 4]> for Bc6 {
type CompressedBlock = [u8; 16];
fn decompress_block(block: &[u8; 16]) -> [[[f32; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] {
let mut decompressed_rgb = [[[0.0; 3]; BLOCK_WIDTH]; BLOCK_HEIGHT];
bcdec_rs::bc6h_float(
block,
bytemuck::cast_slice_mut(&mut decompressed_rgb),
BLOCK_WIDTH * 3,
false,
);
let mut decompressed = [[[0.0; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT];
for y in 0..BLOCK_HEIGHT {
for x in 0..BLOCK_HEIGHT {
let [r, g, b] = decompressed_rgb[y][x];
decompressed[y][x] = [r, g, b, 1.0];
}
}
decompressed
}
}
impl BcnDecode<[u8; 4]> for Bc6 {
type CompressedBlock = [u8; 16];
fn decompress_block(block: &[u8; 16]) -> [[[u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] {
let decompressed: [[[f32; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] = Bc6::decompress_block(block);
let float_to_u8 = |x: f32| (x * 255.0) as u8;
decompressed.map(|row| row.map(|pixel| pixel.map(float_to_u8)))
}
}
impl BcnDecode<[u8; 4]> for Bc7 {
type CompressedBlock = [u8; 16];
fn decompress_block(block: &[u8; 16]) -> [[[u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT] {
let mut decompressed = [[[0u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT];
bcdec_rs::bc7(
block,
bytemuck::cast_slice_mut(&mut decompressed),
BLOCK_WIDTH * CHANNELS,
);
decompressed
}
}
pub fn decode_bcn<F, T>(width: u32, height: u32, data: &[u8]) -> Result<Vec<T>, SurfaceError>
where
T: Copy + Default + Pod,
F: BcnDecode<[T; 4]>,
F::CompressedBlock: ReadBlock,
{
let expected_size = mip_size(
width as usize,
height as usize,
1,
BLOCK_WIDTH,
BLOCK_HEIGHT,
1,
F::CompressedBlock::SIZE_IN_BYTES,
)
.ok_or(SurfaceError::PixelCountWouldOverflow {
width,
height,
depth: 1,
})?;
if data.len() < expected_size {
return Err(SurfaceError::NotEnoughData {
expected: expected_size,
actual: data.len(),
});
}
let mut rgba = vec![T::default(); width as usize * height as usize * CHANNELS];
let mut block_start = 0;
for y in (0..height).step_by(BLOCK_HEIGHT) {
for x in (0..width).step_by(BLOCK_WIDTH) {
let block = F::CompressedBlock::read_block(data, block_start);
let decompressed_block = F::decompress_block(&block);
put_rgba_block(
&mut rgba,
decompressed_block,
x as usize,
y as usize,
width as usize,
height as usize,
);
block_start += F::CompressedBlock::SIZE_IN_BYTES;
}
}
Ok(rgba)
}
fn put_rgba_block<T: Pod>(
surface: &mut [T],
pixels: [[[T; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT],
x: usize,
y: usize,
width: usize,
height: usize,
) {
let elements_per_row = CHANNELS * BLOCK_WIDTH.min(width - x);
for (row, row_pixels) in pixels.iter().enumerate().take(BLOCK_HEIGHT.min(height - y)) {
let surface_index = ((y + row) * width + x) * CHANNELS;
surface[surface_index..surface_index + elements_per_row]
.copy_from_slice(&bytemuck::cast_slice(row_pixels)[..elements_per_row]);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn put_rgba_block_4x4() {
let mut surface = vec![0u8; 4 * 4 * 4];
put_rgba_block(
&mut surface,
[[[1u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT],
0,
0,
4,
4,
);
assert_eq!(vec![1u8; 4 * 4 * 4], surface);
}
#[test]
fn put_rgba_block_5x5() {
let mut surface = vec![0u8; 5 * 5 * 4];
put_rgba_block(
&mut surface,
[[[1u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT],
0,
0,
5,
5,
);
put_rgba_block(
&mut surface,
[[[2u8; 4]; BLOCK_WIDTH]; BLOCK_HEIGHT],
1,
1,
5,
5,
);
assert_eq!(
[
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
]
.into_iter()
.flatten()
.collect::<Vec<_>>(),
surface
);
}
}