use super::etc1s::{Endpoint, Etc1sTranscoder, Selector};
use super::etc1s_bc1::convert_etc1s_to_dxt1;
use alloc::vec::Vec;
#[inline]
fn conv_dxt1_to_fxt1_sels(sels: u32) -> u8 {
const TBL: [u8; 16] = [0, 3, 1, 2, 12, 15, 13, 14, 4, 7, 5, 6, 8, 11, 9, 10];
(TBL[(sels & 15) as usize] as u32 | ((TBL[(sels >> 4) as usize] as u32) << 4)) as u8
}
#[derive(Clone, Copy, Default)]
pub struct Fxt1Block {
sels: [u8; 8],
rgb: [u8; 12],
alpha: u8,
glsb: u8,
mode: u8,
}
impl Fxt1Block {
pub fn to_bytes(self) -> [u8; 16] {
let mut hi: u64 = 0;
let mut shift = 0u32;
for &c in &self.rgb {
hi |= ((c & 0x1F) as u64) << shift;
shift += 5;
}
hi |= ((self.alpha & 1) as u64) << 60;
hi |= ((self.glsb & 3) as u64) << 61;
hi |= ((self.mode & 1) as u64) << 63;
let mut out = [0u8; 16];
out[0..8].copy_from_slice(&self.sels);
out[8..16].copy_from_slice(&hi.to_le_bytes());
out
}
#[inline]
fn set_rgb(&mut self, idx: usize, r: u8, g: u8, b: u8) {
self.rgb[idx * 3] = b;
self.rgb[idx * 3 + 1] = g;
self.rgb[idx * 3 + 2] = r;
}
}
pub fn convert_etc1s_to_fxt1(
block: &mut Fxt1Block,
ep: &Endpoint,
sel: &Selector,
fxt1_subblock: u32,
) {
let blk = convert_etc1s_to_dxt1(ep, sel, false);
let l = (blk[0] as u32) | ((blk[1] as u32) << 8); let h = (blk[2] as u32) | ((blk[3] as u32) << 8);
let mut c0 = [(l >> 11) & 31, (l >> 5) & 63, l & 31]; let mut c1 = [(h >> 11) & 31, (h >> 5) & 63, h & 31];
let mut g0 = c0[1] & 1;
let mut g1 = c1[1] & 1;
c0[1] >>= 1;
c1[1] >>= 1;
let mut s = [
conv_dxt1_to_fxt1_sels(blk[4] as u32),
conv_dxt1_to_fxt1_sels(blk[5] as u32),
conv_dxt1_to_fxt1_sels(blk[6] as u32),
conv_dxt1_to_fxt1_sels(blk[7] as u32),
];
if ((s[0] >> 1) & 1) as u32 != (g0 ^ g1) {
core::mem::swap(&mut c0, &mut c1);
core::mem::swap(&mut g0, &mut g1);
for b in s.iter_mut() {
*b ^= 0xFF;
}
}
let (r0, g0c, b0) = (c0[0] as u8, c0[1] as u8, c0[2] as u8);
let (r1, g1c, b1) = (c1[0] as u8, c1[1] as u8, c1[2] as u8);
if fxt1_subblock == 0 {
block.mode = 1;
block.alpha = 0;
block.glsb = (g1 | (g1 << 1)) as u8;
block.set_rgb(0, r0, g0c, b0);
block.set_rgb(1, r1, g1c, b1);
block.set_rgb(2, r0, g0c, b0);
block.set_rgb(3, r1, g1c, b1);
block.sels[0] = s[0];
block.sels[1] = s[1];
block.sels[2] = s[2];
block.sels[3] = s[3];
const BORDER_DUP: [u8; 4] = [0, 85, 170, 255];
block.sels[4] = BORDER_DUP[(s[0] >> 6) as usize];
block.sels[5] = BORDER_DUP[(s[1] >> 6) as usize];
block.sels[6] = BORDER_DUP[(s[2] >> 6) as usize];
block.sels[7] = BORDER_DUP[(s[3] >> 6) as usize];
} else {
block.glsb = ((block.glsb & 1) as u32 | (g1 << 1)) as u8;
block.set_rgb(2, r0, g0c, b0);
block.set_rgb(3, r1, g1c, b1);
block.sels[4] = s[0];
block.sels[5] = s[1];
block.sels[6] = s[2];
block.sels[7] = s[3];
}
}
impl Etc1sTranscoder {
#[allow(clippy::too_many_arguments)]
pub fn transcode_slice_fxt1(
&self,
slice_data: &[u8],
num_blocks_x: u32,
num_blocks_y: u32,
orig_width: u32,
orig_height: u32,
video: Option<&mut Vec<u32>>,
out: &mut [u8],
) -> Option<()> {
let indices = self.decode_slice_indices(slice_data, num_blocks_x, num_blocks_y, video)?;
let fxt1_blocks_x = orig_width.div_ceil(8);
let fxt1_blocks_y = orig_height.div_ceil(4);
let mut blocks = vec![Fxt1Block::default(); (fxt1_blocks_x * fxt1_blocks_y) as usize];
for block_y in 0..num_blocks_y {
for block_x in 0..num_blocks_x {
let (ei, si) = indices[(block_x + block_y * num_blocks_x) as usize];
let fxt1_block_x = block_x >> 1;
let fxt1_subblock = block_x & 1;
let dst = (fxt1_block_x + block_y * fxt1_blocks_x) as usize;
convert_etc1s_to_fxt1(
&mut blocks[dst],
&self.endpoints[ei as usize],
&self.selectors[si as usize],
fxt1_subblock,
);
}
}
let total_4x4 = (num_blocks_x * num_blocks_y) as usize;
if out.len() != total_4x4 * 16 {
return None;
}
out.fill(0);
for (b, blk) in blocks.iter().enumerate() {
out[b * 16..b * 16 + 16].copy_from_slice(&blk.to_bytes());
}
Some(())
}
}