use crate::{
blockdepth::block_depth, div_round_up, height_in_blocks, width_in_gobs, BlockHeight,
SwizzleError, GOB_HEIGHT_IN_BYTES, GOB_SIZE_IN_BYTES, GOB_WIDTH_IN_BYTES,
};
use alloc::{vec, vec::Vec};
pub fn swizzle_block_linear(
width: u32,
height: u32,
depth: u32,
source: &[u8],
block_height: BlockHeight,
bytes_per_pixel: u32,
) -> Result<Vec<u8>, SwizzleError> {
let mut destination =
vec![0u8; swizzled_mip_size(width, height, depth, block_height, bytes_per_pixel)];
let expected_size = deswizzled_mip_size(width, height, depth, bytes_per_pixel);
if source.len() < expected_size {
return Err(SwizzleError::NotEnoughData {
actual_size: source.len(),
expected_size,
});
}
let block_depth = block_depth(depth);
swizzle_inner::<false>(
width,
height,
depth,
source,
&mut destination,
block_height,
block_depth,
bytes_per_pixel,
);
Ok(destination)
}
pub fn deswizzle_block_linear(
width: u32,
height: u32,
depth: u32,
source: &[u8],
block_height: BlockHeight,
bytes_per_pixel: u32,
) -> Result<Vec<u8>, SwizzleError> {
let mut destination = vec![0u8; deswizzled_mip_size(width, height, depth, bytes_per_pixel)];
let expected_size = swizzled_mip_size(width, height, depth, block_height, bytes_per_pixel);
if source.len() < expected_size {
return Err(SwizzleError::NotEnoughData {
actual_size: source.len(),
expected_size,
});
}
let block_depth = block_depth(depth);
swizzle_inner::<true>(
width,
height,
depth,
source,
&mut destination,
block_height,
block_depth,
bytes_per_pixel,
);
Ok(destination)
}
pub(crate) fn swizzle_inner<const DESWIZZLE: bool>(
width: u32,
height: u32,
depth: u32,
source: &[u8],
destination: &mut [u8],
block_height: BlockHeight,
block_depth: u32,
bytes_per_pixel: u32,
) {
let block_height = block_height as u32;
let width_in_gobs = width_in_gobs(width, bytes_per_pixel);
let slice_size = slice_size(block_height, block_depth, width_in_gobs, height);
let block_width = 1;
let block_size_in_bytes = GOB_SIZE_IN_BYTES * block_width * block_height * block_depth;
let block_height_in_bytes = GOB_HEIGHT_IN_BYTES * block_height;
for z0 in 0..depth {
let offset_z = gob_address_z(z0, block_height, block_depth, slice_size as u32);
for y0 in (0..height).step_by(GOB_HEIGHT_IN_BYTES as usize) {
let offset_y = gob_address_y(
y0,
block_height_in_bytes,
block_size_in_bytes,
width_in_gobs,
);
for x0 in (0..(width * bytes_per_pixel)).step_by(GOB_WIDTH_IN_BYTES as usize) {
let offset_x = gob_address_x(x0, block_size_in_bytes);
let gob_address = offset_z as usize + offset_y as usize + offset_x as usize;
if x0 + GOB_WIDTH_IN_BYTES < width * bytes_per_pixel
&& y0 + GOB_HEIGHT_IN_BYTES < height
{
let linear_offset = (z0 * width * height * bytes_per_pixel)
+ (y0 * width * bytes_per_pixel)
+ x0;
if DESWIZZLE {
deswizzle_complete_gob(
&mut destination[linear_offset as usize..],
&source[gob_address..],
width as usize * bytes_per_pixel as usize,
);
} else {
swizzle_complete_gob(
&mut destination[gob_address..],
&source[linear_offset as usize..],
width as usize * bytes_per_pixel as usize,
);
}
} else {
swizzle_deswizzle_gob::<DESWIZZLE>(
destination,
source,
x0,
y0,
z0,
width,
height,
bytes_per_pixel,
gob_address,
);
}
}
}
}
}
fn swizzle_deswizzle_gob<const DESWIZZLE: bool>(
destination: &mut [u8],
source: &[u8],
x0: u32,
y0: u32,
z0: u32,
width: u32,
height: u32,
bytes_per_pixel: u32,
gob_address: usize,
) {
for y in 0..GOB_HEIGHT_IN_BYTES {
for x in 0..GOB_WIDTH_IN_BYTES {
if y0 + y < height && x0 + x < width * bytes_per_pixel {
let swizzled_offset = gob_address + gob_offset(x, y) as usize;
let linear_offset = (z0 * width * height * bytes_per_pixel)
+ ((y0 + y) * width * bytes_per_pixel)
+ x0
+ x;
if DESWIZZLE {
destination[linear_offset as usize] = source[swizzled_offset];
} else {
destination[swizzled_offset] = source[linear_offset as usize];
}
}
}
}
}
fn slice_size(block_height: u32, block_depth: u32, width_in_gobs: u32, height: u32) -> usize {
let rob_size = GOB_SIZE_IN_BYTES * block_height * block_depth * width_in_gobs;
div_round_up(height, block_height * GOB_HEIGHT_IN_BYTES) as usize * rob_size as usize
}
fn gob_address_z(z: u32, block_height: u32, block_depth: u32, slice_size: u32) -> u32 {
(z / block_depth * slice_size) + ((z & (block_depth - 1)) * GOB_SIZE_IN_BYTES * block_height)
}
fn gob_address_y(
y: u32,
block_height_in_bytes: u32,
block_size_in_bytes: u32,
image_width_in_gobs: u32,
) -> u32 {
let block_y = y / block_height_in_bytes;
let block_inner_row = y % block_height_in_bytes / GOB_HEIGHT_IN_BYTES;
block_y * block_size_in_bytes * image_width_in_gobs + block_inner_row * GOB_SIZE_IN_BYTES
}
fn gob_address_x(x: u32, block_size_in_bytes: u32) -> u32 {
let block_x = x / GOB_WIDTH_IN_BYTES;
block_x * block_size_in_bytes
}
fn gob_offset(x: u32, y: u32) -> u32 {
((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 + (y % 2) * 16 + (x % 16)
}
const GOB_ROW_OFFSETS: [usize; GOB_HEIGHT_IN_BYTES as usize] = [0, 16, 64, 80, 128, 144, 192, 208];
fn deswizzle_complete_gob(dst: &mut [u8], src: &[u8], row_size_in_bytes: usize) {
for (i, offset) in GOB_ROW_OFFSETS.iter().enumerate() {
deswizzle_gob_row(dst, row_size_in_bytes * i, src, *offset);
}
}
fn deswizzle_gob_row(dst: &mut [u8], dst_offset: usize, src: &[u8], src_offset: usize) {
let dst = &mut dst[dst_offset..];
let src = &src[src_offset..];
dst[48..64].copy_from_slice(&src[288..304]);
dst[32..48].copy_from_slice(&src[256..272]);
dst[16..32].copy_from_slice(&src[32..48]);
dst[0..16].copy_from_slice(&src[0..16]);
}
fn swizzle_complete_gob(dst: &mut [u8], src: &[u8], row_size_in_bytes: usize) {
for (i, offset) in GOB_ROW_OFFSETS.iter().enumerate() {
swizzle_gob_row(dst, *offset, src, row_size_in_bytes * i);
}
}
fn swizzle_gob_row(dst: &mut [u8], dst_offset: usize, src: &[u8], src_offset: usize) {
let dst = &mut dst[dst_offset..];
let src = &src[src_offset..];
dst[288..304].copy_from_slice(&src[48..64]);
dst[256..272].copy_from_slice(&src[32..48]);
dst[32..48].copy_from_slice(&src[16..32]);
dst[0..16].copy_from_slice(&src[0..16]);
}
pub const fn swizzled_mip_size(
width: u32,
height: u32,
depth: u32,
block_height: BlockHeight,
bytes_per_pixel: u32,
) -> usize {
let width_in_gobs = width_in_gobs(width, bytes_per_pixel) as usize;
let height_in_blocks = height_in_blocks(height, block_height as u32);
let height_in_gobs = height_in_blocks as usize * block_height as usize;
let depth_in_gobs = depth.next_multiple_of(block_depth(depth));
let num_gobs = width_in_gobs * height_in_gobs * depth_in_gobs as usize;
num_gobs * GOB_SIZE_IN_BYTES as usize
}
pub const fn deswizzled_mip_size(
width: u32,
height: u32,
depth: u32,
bytes_per_pixel: u32,
) -> usize {
width as usize * height as usize * depth as usize * bytes_per_pixel as usize
}
#[cfg(test)]
mod tests {
use super::*;
use rand::{rngs::StdRng, Rng, SeedableRng};
#[test]
fn swizzle_deswizzle_bytes_per_pixel() {
let width = 312;
let height = 575;
let block_height = BlockHeight::Eight;
let bytes_per_pixel = 12;
let deswizzled_size = deswizzled_mip_size(width, height, 1, bytes_per_pixel);
let seed = [13u8; 32];
let mut rng: StdRng = SeedableRng::from_seed(seed);
let input: Vec<_> = (0..deswizzled_size)
.map(|_| rng.gen_range::<u8, _>(0..=255))
.collect();
let swizzled =
swizzle_block_linear(width, height, 1, &input, block_height, bytes_per_pixel).unwrap();
let deswizzled =
deswizzle_block_linear(width, height, 1, &swizzled, block_height, bytes_per_pixel)
.unwrap();
assert_eq!(input, deswizzled);
}
#[test]
fn swizzle_empty() {
let result = swizzle_block_linear(32, 32, 1, &[], BlockHeight::Sixteen, 4);
assert_eq!(
result,
Err(SwizzleError::NotEnoughData {
actual_size: 0,
expected_size: 4096
})
);
}
#[test]
fn deswizzle_empty() {
let result = deswizzle_block_linear(32, 32, 1, &[], BlockHeight::Sixteen, 4);
assert_eq!(
result,
Err(SwizzleError::NotEnoughData {
actual_size: 0,
expected_size: 16384
})
);
}
#[test]
fn swizzle_bc7_64_64_not_enough_data() {
let result = swizzle_block_linear(
64 / 4,
64 / 4,
1,
&vec![0u8; 64 * 64 - 1],
BlockHeight::Sixteen,
16,
);
assert_eq!(
result,
Err(SwizzleError::NotEnoughData {
actual_size: 4095,
expected_size: 4096
})
);
}
#[test]
fn deswizzle_bc7_64_64_not_enough_data() {
let result =
deswizzle_block_linear(64 / 4, 64 / 4, 1, &[0u8; 64 * 64], BlockHeight::Sixteen, 16);
assert_eq!(
result,
Err(SwizzleError::NotEnoughData {
actual_size: 4096,
expected_size: 32768
})
);
}
#[test]
fn swizzle_deswizzle_bc7_64_64() {
let swizzled = include_bytes!("../block_linear/64_bc7_tiled.bin");
let deswizzled =
deswizzle_block_linear(64 / 4, 64 / 4, 1, swizzled, BlockHeight::Two, 16).unwrap();
let new_swizzled =
swizzle_block_linear(64 / 4, 64 / 4, 1, &deswizzled, BlockHeight::Two, 16).unwrap();
assert_eq!(swizzled, &new_swizzled[..]);
}
#[test]
fn deswizzle_bc7_64_64() {
let input = include_bytes!("../block_linear/64_bc7_tiled.bin");
let expected = include_bytes!("../block_linear/64_bc7.bin");
let actual =
deswizzle_block_linear(64 / 4, 64 / 4, 1, input, BlockHeight::Two, 16).unwrap();
assert_eq!(expected, &actual[..]);
}
#[test]
fn deswizzle_bc1_128_128() {
let input = include_bytes!("../block_linear/128_bc1_tiled.bin");
let expected = include_bytes!("../block_linear/128_bc1.bin");
let actual =
deswizzle_block_linear(128 / 4, 128 / 4, 1, input, BlockHeight::Four, 8).unwrap();
assert_eq!(expected, &actual[..]);
}
#[test]
fn deswizzle_bc3_128_128() {
let input = include_bytes!("../block_linear/128_bc3_tiled.bin");
let expected = include_bytes!("../block_linear/128_bc3.bin");
let actual =
deswizzle_block_linear(128 / 4, 128 / 4, 1, input, BlockHeight::Four, 16).unwrap();
assert_eq!(expected, &actual[..]);
}
#[test]
fn deswizzle_rgba_f32_128_128() {
let input = include_bytes!("../block_linear/128_rgbaf32_tiled.bin");
let expected = include_bytes!("../block_linear/128_rgbaf32.bin");
let actual = deswizzle_block_linear(128, 128, 1, input, BlockHeight::Sixteen, 16).unwrap();
assert_eq!(expected, &actual[..]);
}
#[test]
fn deswizzle_bc7_128_128() {
let input = include_bytes!("../block_linear/128_bc7_tiled.bin");
let expected = include_bytes!("../block_linear/128_bc7.bin");
let actual =
deswizzle_block_linear(128 / 4, 128 / 4, 1, input, BlockHeight::Four, 16).unwrap();
assert_eq!(expected, &actual[..]);
}
#[test]
fn deswizzle_bc7_256_256() {
let input = include_bytes!("../block_linear/256_bc7_tiled.bin");
let expected = include_bytes!("../block_linear/256_bc7.bin");
let actual =
deswizzle_block_linear(256 / 4, 256 / 4, 1, input, BlockHeight::Eight, 16).unwrap();
assert_eq!(expected, &actual[..]);
}
#[test]
fn deswizzle_bc7_320_320() {
let input = include_bytes!("../block_linear/320_bc7_tiled.bin");
let expected = include_bytes!("../block_linear/320_bc7.bin");
let actual =
deswizzle_block_linear(320 / 4, 320 / 4, 1, input, BlockHeight::Eight, 16).unwrap();
assert_eq!(expected, &actual[..]);
}
#[test]
fn deswizzle_bc7_512_512() {
let input = include_bytes!("../block_linear/512_bc7_tiled.bin");
let expected = include_bytes!("../block_linear/512_bc7.bin");
let actual =
deswizzle_block_linear(512 / 4, 512 / 4, 1, input, BlockHeight::Sixteen, 16).unwrap();
assert_eq!(expected, &actual[..]);
}
#[test]
fn deswizzle_bc7_1024_1024() {
let input = include_bytes!("../block_linear/1024_bc7_tiled.bin");
let expected = include_bytes!("../block_linear/1024_bc7.bin");
let actual =
deswizzle_block_linear(1024 / 4, 1024 / 4, 1, input, BlockHeight::Sixteen, 16).unwrap();
assert_eq!(expected, &actual[..]);
}
#[test]
fn deswizzle_rgba_16_16_16() {
let input = include_bytes!("../block_linear/16_16_16_rgba_tiled.bin");
let expected = include_bytes!("../block_linear/16_16_16_rgba.bin");
let actual = deswizzle_block_linear(16, 16, 16, input, BlockHeight::One, 4).unwrap();
assert_eq!(expected, &actual[..]);
}
}