mod blockheight;
pub mod ffi;
pub use blockheight::*;
const GOB_WIDTH_IN_BYTES: usize = 64;
const GOB_HEIGHT_IN_BYTES: usize = 8;
const GOB_SIZE_IN_BYTES: usize = GOB_WIDTH_IN_BYTES * GOB_HEIGHT_IN_BYTES;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum BlockHeight {
One = 1,
Two = 2,
Four = 4,
Eight = 8,
Sixteen = 16,
ThirtyTwo = 32,
}
#[derive(Debug)]
pub enum SwizzleError {
NotEnoughData {
expected_size: usize,
actual_size: usize,
},
}
impl BlockHeight {
pub fn new(value: usize) -> Option<Self> {
match value {
1 => Some(BlockHeight::One),
2 => Some(BlockHeight::Two),
4 => Some(BlockHeight::Four),
8 => Some(BlockHeight::Eight),
16 => Some(BlockHeight::Sixteen),
32 => Some(BlockHeight::ThirtyTwo),
_ => None,
}
}
}
fn gob_address_z(z: usize) -> usize {
z * 512
}
fn gob_address_x(x: usize, block_size_in_bytes: usize) -> usize {
let block_x = x / GOB_WIDTH_IN_BYTES;
block_x * block_size_in_bytes
}
fn gob_address_y(
y: usize,
block_height_in_bytes: usize,
block_size_in_bytes: usize,
image_width_in_gobs: usize,
) -> usize {
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_offset(x: usize, y: usize) -> usize {
((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 + (y % 2) * 16 + (x % 16)
}
fn deswizzle_complete_gob(dst: &mut [u8], src: &[u8], row_size_in_bytes: usize) {
deswizzle_gob_row(dst, row_size_in_bytes * 0, src, 0);
deswizzle_gob_row(dst, row_size_in_bytes * 1, src, 16);
deswizzle_gob_row(dst, row_size_in_bytes * 2, src, 64);
deswizzle_gob_row(dst, row_size_in_bytes * 3, src, 80);
deswizzle_gob_row(dst, row_size_in_bytes * 4, src, 128);
deswizzle_gob_row(dst, row_size_in_bytes * 5, src, 144);
deswizzle_gob_row(dst, row_size_in_bytes * 6, src, 192);
deswizzle_gob_row(dst, row_size_in_bytes * 7, src, 208);
}
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) {
swizzle_gob_row(dst, 0, src, row_size_in_bytes * 0);
swizzle_gob_row(dst, 16, src, row_size_in_bytes * 1);
swizzle_gob_row(dst, 64, src, row_size_in_bytes * 2);
swizzle_gob_row(dst, 80, src, row_size_in_bytes * 3);
swizzle_gob_row(dst, 128, src, row_size_in_bytes * 4);
swizzle_gob_row(dst, 144, src, row_size_in_bytes * 5);
swizzle_gob_row(dst, 192, src, row_size_in_bytes * 6);
swizzle_gob_row(dst, 208, src, row_size_in_bytes * 7);
}
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_surface_size(
width: usize,
height: usize,
depth: usize,
block_height: BlockHeight,
bytes_per_pixel: usize,
) -> usize {
let width_in_gobs = width_in_gobs(width, bytes_per_pixel);
let height_in_blocks = height_in_blocks(height, block_height as usize);
width_in_gobs * height_in_blocks * block_height as usize * GOB_SIZE_IN_BYTES * depth
}
const fn height_in_blocks(height: usize, block_height: usize) -> usize {
div_round_up(height, block_height * GOB_HEIGHT_IN_BYTES)
}
pub const fn deswizzled_surface_size(
width: usize,
height: usize,
depth: usize,
bytes_per_pixel: usize,
) -> usize {
width * height * depth * bytes_per_pixel
}
pub const fn div_round_up(x: usize, d: usize) -> usize {
(x + d - 1) / d
}
const fn width_in_gobs(width: usize, bytes_per_pixel: usize) -> usize {
div_round_up(width * bytes_per_pixel, GOB_WIDTH_IN_BYTES)
}
pub fn swizzle_block_linear(
width: usize,
height: usize,
depth: usize,
source: &[u8],
block_height: BlockHeight,
bytes_per_pixel: usize,
) -> Result<Vec<u8>, SwizzleError> {
let mut destination =
vec![0u8; swizzled_surface_size(width, height, depth, block_height, bytes_per_pixel)];
let expected_size = deswizzled_surface_size(width, height, depth, bytes_per_pixel);
if source.len() < expected_size {
return Err(SwizzleError::NotEnoughData {
actual_size: source.len(),
expected_size,
});
}
swizzle_inner(
width,
height,
depth,
source,
&mut destination,
block_height as usize,
depth,
bytes_per_pixel,
false,
);
Ok(destination)
}
pub fn deswizzle_block_linear(
width: usize,
height: usize,
depth: usize,
source: &[u8],
block_height: BlockHeight,
bytes_per_pixel: usize,
) -> Result<Vec<u8>, SwizzleError> {
let mut destination = vec![0u8; deswizzled_surface_size(width, height, depth, bytes_per_pixel)];
let expected_size = swizzled_surface_size(width, height, depth, block_height, bytes_per_pixel);
if source.len() < expected_size {
return Err(SwizzleError::NotEnoughData {
actual_size: source.len(),
expected_size,
});
}
swizzle_inner(
width,
height,
depth,
source,
&mut destination,
block_height as usize,
depth,
bytes_per_pixel,
true,
);
Ok(destination)
}
fn swizzle_inner(
width: usize,
height: usize,
depth: usize,
source: &[u8],
destination: &mut [u8],
block_height: usize,
block_depth: usize,
bytes_per_pixel: usize,
deswizzle: bool,
) {
let image_width_in_gobs = width_in_gobs(width, bytes_per_pixel);
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);
for y0 in (0..height).step_by(GOB_HEIGHT_IN_BYTES) {
let offset_y = gob_address_y(
y0,
block_height_in_bytes,
block_size_in_bytes,
image_width_in_gobs,
);
for x0 in (0..(width * bytes_per_pixel)).step_by(GOB_WIDTH_IN_BYTES) {
let offset_x = gob_address_x(x0, block_size_in_bytes);
let gob_address = offset_z + offset_y + offset_x;
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..],
&source[gob_address..],
width * bytes_per_pixel,
);
} else {
swizzle_complete_gob(
&mut destination[gob_address..],
&source[linear_offset..],
width * bytes_per_pixel,
);
}
} else {
swizzle_deswizzle_gob(
destination,
source,
x0,
y0,
z0,
width,
height,
bytes_per_pixel,
gob_address,
deswizzle,
);
}
}
}
}
}
fn swizzle_deswizzle_gob(
destination: &mut [u8],
source: &[u8],
x0: usize,
y0: usize,
z0: usize,
width: usize,
height: usize,
bytes_per_pixel: usize,
gob_address: usize,
deswizzle: bool,
) {
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);
let linear_offset = (z0 * width * height * bytes_per_pixel)
+ ((y0 + y) * width * bytes_per_pixel)
+ x0
+ x;
if deswizzle {
destination[linear_offset] = source[swizzled_offset];
} else {
destination[swizzled_offset] = source[linear_offset];
}
}
}
}
}
#[cfg(test)]
mod tests {
use rand::{rngs::StdRng, Rng, SeedableRng};
use super::*;
#[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_surface_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!(matches!(
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!(matches!(
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!(matches!(
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!(matches!(
result,
Err(SwizzleError::NotEnoughData {
actual_size: 4096,
expected_size: 32768
})
));
}
#[test]
fn swizzle_deswizzle_bc7_64_64() {
let swizzled = include_bytes!("../../swizzle_data/64_bc7_swizzled.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!("../../swizzle_data/64_bc7_swizzled.bin");
let expected = include_bytes!("../../swizzle_data/64_bc7_deswizzled.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!("../../swizzle_data/128_bc1_swizzled.bin");
let expected = include_bytes!("../../swizzle_data/128_bc1_deswizzled.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!("../../swizzle_data/128_bc3_swizzled.bin");
let expected = include_bytes!("../../swizzle_data/128_bc3_deswizzled.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!("../../swizzle_data/128_rgbaf32_swizzled.bin");
let expected = include_bytes!("../../swizzle_data/128_rgbaf32_deswizzled.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!("../../swizzle_data/128_bc7_swizzled.bin");
let expected = include_bytes!("../../swizzle_data/128_bc7_deswizzled.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!("../../swizzle_data/256_bc7_swizzled.bin");
let expected = include_bytes!("../../swizzle_data/256_bc7_deswizzled.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!("../../swizzle_data/320_bc7_swizzled.bin");
let expected = include_bytes!("../../swizzle_data/320_bc7_deswizzled.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!("../../swizzle_data/512_bc7_swizzled.bin");
let expected = include_bytes!("../../swizzle_data/512_bc7_deswizzled.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!("../../swizzle_data/1024_bc7_swizzled.bin");
let expected = include_bytes!("../../swizzle_data/1024_bc7_deswizzled.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!("../../swizzle_data/16_16_16_rgba_swizzled.bin");
let expected = include_bytes!("../../swizzle_data/16_16_16_rgba_deswizzled.bin");
let actual = deswizzle_block_linear(16, 16, 16, input, BlockHeight::One, 4).unwrap();
assert_eq!(expected, &actual[..]);
}
#[test]
fn width_in_gobs_block16() {
assert_eq!(20, width_in_gobs(320 / 4, 16));
}
#[test]
fn deswizzled_surface_sizes() {
assert_eq!(3145728, deswizzled_surface_size(512, 512, 3, 4));
}
#[test]
fn surface_sizes_block4() {
assert_eq!(
1048576,
swizzled_surface_size(512, 512, 1, BlockHeight::Sixteen, 4)
);
}
#[test]
fn surface_sizes_3d() {
assert_eq!(
16384,
swizzled_surface_size(16, 16, 16, BlockHeight::One, 4)
);
}
#[test]
fn surface_sizes_block16() {
assert_eq!(
163840,
swizzled_surface_size(320 / 4, 320 / 4, 1, BlockHeight::Sixteen, 16)
);
assert_eq!(
40960,
swizzled_surface_size(160 / 4, 160 / 4, 1, BlockHeight::Four, 16)
);
assert_eq!(
1024,
swizzled_surface_size(32 / 4, 32 / 4, 1, BlockHeight::One, 16)
);
}
}