use super::read_write::{
for_each_block_rect_untyped, for_each_block_untyped, general_process_blocks, PixelRange,
};
use super::{Args, Decoder, DecoderSet, RArgs};
use crate::{Channels, ColorFormat, NormConvert, WithPrecision};
fn decode_astc_block<const PIXELS: usize, T: Default + Copy>(
block_size: (u8, u8),
) -> impl Fn([u8; 16]) -> [[T; 4]; PIXELS]
where
u8: NormConvert<T>,
{
fn decode_into<T>(bytes: &[u8; 16], footprint: astc_decode::Footprint, out: &mut [[T; 4]])
where
u8: NormConvert<T>,
{
debug_assert_eq!(
footprint.block_width() as usize * footprint.block_height() as usize,
out.len()
);
let width = footprint.block_width() as usize;
astc_decode::astc_decode_block(bytes, footprint, move |x, y, [r, g, b, a]| {
out[y as usize * width + x as usize] = [
NormConvert::to(r),
NormConvert::to(g),
NormConvert::to(b),
NormConvert::to(a),
];
});
}
debug_assert_eq!(PIXELS, block_size.0 as usize * block_size.1 as usize);
let footprint = astc_decode::Footprint::new(block_size.0 as u32, block_size.1 as u32);
move |bytes| {
let mut block = [[T::default(); 4]; PIXELS];
decode_into(&bytes, footprint, &mut block);
block
}
}
macro_rules! astc_decoder {
($out:ty, $block_w:literal, $block_h:literal) => {{
const BLOCK_WIDTH: u8 = $block_w;
const BLOCK_HEIGHT: u8 = $block_h;
const BLOCK_PIXELS: usize = BLOCK_WIDTH as usize * BLOCK_HEIGHT as usize;
const BYTES_PER_BLOCK: usize = 16;
const CHANNELS: usize = 4;
type OutPixel = [$out; CHANNELS];
fn process_blocks(
encoded_blocks: &[u8],
decoded: &mut [u8],
stride: usize,
range: PixelRange,
) {
let f = decode_astc_block::<BLOCK_PIXELS, $out>((BLOCK_WIDTH, BLOCK_HEIGHT));
general_process_blocks::<
BLOCK_WIDTH,
BLOCK_HEIGHT,
BLOCK_PIXELS,
BYTES_PER_BLOCK,
OutPixel,
>(encoded_blocks, decoded, stride, range, f)
}
const NATIVE_COLOR: ColorFormat =
ColorFormat::new(Channels::Rgba, <$out as WithPrecision>::PRECISION);
Decoder::new_with_all_channels(
NATIVE_COLOR,
|Args(r, out, context)| {
for_each_block_untyped::<BLOCK_WIDTH, BLOCK_HEIGHT, BYTES_PER_BLOCK, OutPixel>(
r,
out,
context,
NATIVE_COLOR,
process_blocks,
)
},
|RArgs(r, out, offset, context)| {
for_each_block_rect_untyped::<BLOCK_WIDTH, BLOCK_HEIGHT, BYTES_PER_BLOCK>(
r,
out,
offset,
context,
NATIVE_COLOR,
process_blocks,
)
},
)
}};
}
macro_rules! astc {
($w:literal, $h:literal) => {
DecoderSet::new(&[
astc_decoder!(u8, $w, $h),
astc_decoder!(u16, $w, $h),
astc_decoder!(f32, $w, $h),
])
};
}
pub(crate) const ASTC_4X4_UNORM: DecoderSet = astc!(4, 4);
pub(crate) const ASTC_5X4_UNORM: DecoderSet = astc!(5, 4);
pub(crate) const ASTC_5X5_UNORM: DecoderSet = astc!(5, 5);
pub(crate) const ASTC_6X5_UNORM: DecoderSet = astc!(6, 5);
pub(crate) const ASTC_6X6_UNORM: DecoderSet = astc!(6, 6);
pub(crate) const ASTC_8X5_UNORM: DecoderSet = astc!(8, 5);
pub(crate) const ASTC_8X6_UNORM: DecoderSet = astc!(8, 6);
pub(crate) const ASTC_8X8_UNORM: DecoderSet = astc!(8, 8);
pub(crate) const ASTC_10X5_UNORM: DecoderSet = astc!(10, 5);
pub(crate) const ASTC_10X6_UNORM: DecoderSet = astc!(10, 6);
pub(crate) const ASTC_10X8_UNORM: DecoderSet = astc!(10, 8);
pub(crate) const ASTC_10X10_UNORM: DecoderSet = astc!(10, 10);
pub(crate) const ASTC_12X10_UNORM: DecoderSet = astc!(12, 10);
pub(crate) const ASTC_12X12_UNORM: DecoderSet = astc!(12, 12);