use super::etc1s::{Endpoint, Etc1sTranscoder, Selector};
use crate::etc::block::DecoderEtcBlock;
use crate::tables::atc_55::G_ETC1S_TO_ATC_55;
use crate::tables::atc_56::G_ETC1S_TO_ATC_56;
use crate::tables::dxt5a::G_ETC1_G_TO_DXT5A;
use alloc::vec::Vec;
const NUM_RANGES: usize = 6;
const NUM_MAPPINGS: usize = 10;
const ATC_IDENTITY_SELECTOR_MAPPING_INDEX: usize = 6;
static ATC_SELECTOR_RANGES: [[u32; 2]; NUM_RANGES] =
[[0, 3], [1, 3], [0, 2], [1, 2], [2, 3], [0, 1]];
static ATC_SELECTOR_MAPPINGS: [[u8; 4]; NUM_MAPPINGS] = [
[0, 0, 1, 1],
[0, 0, 1, 2],
[0, 0, 1, 3],
[0, 0, 2, 3],
[0, 1, 1, 1],
[0, 1, 2, 2],
[0, 1, 2, 3], [0, 2, 3, 3],
[1, 2, 2, 2],
[1, 2, 3, 3],
];
fn set_low_color(out: &mut [u8; 8], r: u32, g: u32, b: u32) {
let x = (r << 10) | (g << 5) | b;
out[0] = (x & 0xFF) as u8;
out[1] = ((x >> 8) & 0xFF) as u8;
}
fn set_high_color(out: &mut [u8; 8], r: u32, g: u32, b: u32) {
let x = (r << 11) | (g << 5) | b;
out[2] = (x & 0xFF) as u8;
out[3] = ((x >> 8) & 0xFF) as u8;
}
fn atc_match_equals_1(i: u32, size1: u32) -> (u32, u32) {
let mut best_lo = 0u32;
let mut best_hi = 0u32;
let mut lowest_e = 256i32;
let i = i as i32;
for lo in 0..32u32 {
let lo_e = ((lo << 3) | (lo >> 2)) as i32;
for hi in 0..size1 {
let hi_e = if size1 == 32 {
((hi << 3) | (hi >> 2)) as i32
} else {
((hi << 2) | (hi >> 4)) as i32
};
let e = ((lo_e * 5 + hi_e * 3) / 8 - i).abs();
if e < lowest_e {
best_lo = lo;
best_hi = hi;
lowest_e = e;
}
}
}
(best_lo, best_hi)
}
pub fn convert_etc1s_to_atc(ep: &Endpoint, sel: &Selector) -> [u8; 8] {
let low_selector = sel.lo_selector as u32;
let high_selector = sel.hi_selector as u32;
let inten_table = ep.inten5 as u32;
let mut out = [0u8; 8];
if low_selector == high_selector {
let (r, g, b) =
DecoderEtcBlock::get_block_color5(ep.color5, inten_table, low_selector as usize);
let (r_lo, r_hi) = atc_match_equals_1(r, 32);
let (g_lo, g_hi) = atc_match_equals_1(g, 64);
let (b_lo, b_hi) = atc_match_equals_1(b, 32);
set_low_color(&mut out, r_lo, g_lo, b_lo);
set_high_color(&mut out, r_hi, g_hi, b_hi);
out[4] = 0x55;
out[5] = 0x55;
out[6] = 0x55;
out[7] = 0x55;
return out;
}
if inten_table >= 7 && sel.num_unique_selectors == 2 && low_selector == 0 && high_selector == 3
{
let bc = DecoderEtcBlock::get_block_colors5(ep.color5, inten_table);
let (r0, g0, b0) = (bc[0].r() as u32, bc[0].g() as u32, bc[0].b() as u32);
let (r1, g1, b1) = (bc[3].r() as u32, bc[3].g() as u32, bc[3].b() as u32);
set_low_color(
&mut out,
atc_match3(r0, 32),
atc_match3(g0, 32),
atc_match3(b0, 32),
);
set_high_color(
&mut out,
atc_match3(r1, 32),
atc_match3(g1, 64),
atc_match3(b1, 32),
);
out[4] = sel.selectors[0];
out[5] = sel.selectors[1];
out[6] = sel.selectors[2];
out[7] = sel.selectors[3];
return out;
}
let srt = atc_selector_range_index(low_selector, high_selector);
let it = inten_table as usize;
let base_r =
(it * 32 + ep.color5.r() as usize) * (NUM_RANGES * NUM_MAPPINGS) + srt * NUM_MAPPINGS;
let base_g =
(it * 32 + ep.color5.g() as usize) * (NUM_RANGES * NUM_MAPPINGS) + srt * NUM_MAPPINGS;
let base_b =
(it * 32 + ep.color5.b() as usize) * (NUM_RANGES * NUM_MAPPINGS) + srt * NUM_MAPPINGS;
let mut best_err = u32::MAX;
let mut best_mapping = 0usize;
for m in 0..NUM_MAPPINGS {
let total = G_ETC1S_TO_ATC_55[base_r + m].m_err as u32
+ G_ETC1S_TO_ATC_56[base_g + m].m_err as u32
+ G_ETC1S_TO_ATC_55[base_b + m].m_err as u32;
if total < best_err {
best_err = total;
best_mapping = m;
}
}
let tr = G_ETC1S_TO_ATC_55[base_r + best_mapping];
let tg = G_ETC1S_TO_ATC_56[base_g + best_mapping];
let tb = G_ETC1S_TO_ATC_55[base_b + best_mapping];
set_low_color(&mut out, tr.m_lo as u32, tg.m_lo as u32, tb.m_lo as u32);
set_high_color(&mut out, tr.m_hi as u32, tg.m_hi as u32, tb.m_hi as u32);
if best_mapping == ATC_IDENTITY_SELECTOR_MAPPING_INDEX {
out[4] = sel.selectors[0];
out[5] = sel.selectors[1];
out[6] = sel.selectors[2];
out[7] = sel.selectors[3];
} else {
let xlat = &ATC_SELECTOR_MAPPINGS[best_mapping];
for (i, &sel_bits) in sel.selectors.iter().enumerate() {
let sel_bits = sel_bits as u32;
let mut atc_sels = 0u32;
for x in 0..4u32 {
let x_shift = x * 2;
atc_sels |= (xlat[((sel_bits >> x_shift) & 3) as usize] as u32) << x_shift;
}
out[4 + i] = atc_sels as u8;
}
}
out
}
fn atc_selector_range_index(low: u32, high: u32) -> usize {
for (i, r) in ATC_SELECTOR_RANGES.iter().enumerate() {
if low == r[0] && high == r[1] {
return i;
}
}
0
}
fn atc_match3(i: u32, size1: u32) -> u32 {
let mut best_hi = 0u32;
let mut lowest_e = 256i32;
let i = i as i32;
for hi in 0..size1 {
let hi_e = if size1 == 32 {
((hi << 3) | (hi >> 2)) as i32
} else {
((hi << 2) | (hi >> 4)) as i32
};
let e = (hi_e - i).abs();
if e < lowest_e {
best_hi = hi;
lowest_e = e;
}
}
best_hi
}
static DXT5A_SELECTOR_RANGES: [[u32; 2]; 4] = [[0, 3], [1, 3], [0, 2], [1, 2]];
fn dxt5a_set_selector(sels: &mut [u8; 6], x: u32, y: u32, val: u32) {
let selector_index = y * 4 + x;
let bit_index = selector_index * 3;
let byte_index = (bit_index >> 3) as usize;
let bit_ofs = bit_index & 7;
let mut v = sels[byte_index] as u32;
if byte_index < 5 {
v |= (sels[byte_index + 1] as u32) << 8;
}
v &= !(7 << bit_ofs);
v |= val << bit_ofs;
sels[byte_index] = v as u8;
if byte_index < 5 {
sels[byte_index + 1] = (v >> 8) as u8;
}
}
fn convert_etc1s_to_dxt5a(ep: &Endpoint, sel: &Selector) -> [u8; 8] {
let low_selector = sel.lo_selector as u32;
let high_selector = sel.hi_selector as u32;
let inten_table = ep.inten5 as u32;
let mut out = [0u8; 8];
if low_selector == high_selector {
let r = DecoderEtcBlock::get_block_color5_r(ep.color5, inten_table, low_selector as usize);
out[0] = r as u8; out[1] = r as u8; return out;
}
let mut sels = [0u8; 6];
if sel.num_unique_selectors == 2 {
let bc = DecoderEtcBlock::get_block_colors5(ep.color5, inten_table);
let r0 = bc[low_selector as usize].r() as u32;
let r1 = bc[high_selector as usize].r() as u32;
out[0] = r0 as u8;
out[1] = r1 as u8;
for y in 0..4u32 {
for x in 0..4u32 {
let s = sel.get_selector(x, y);
let ds = if s == high_selector { 1 } else { 0 };
dxt5a_set_selector(&mut sels, x, y, ds);
}
}
out[2..8].copy_from_slice(&sels);
return out;
}
let mut srt = 0usize;
for (i, r) in DXT5A_SELECTOR_RANGES.iter().enumerate() {
if low_selector == r[0] && high_selector == r[1] {
srt = i;
break;
}
}
let entry = G_ETC1_G_TO_DXT5A[(ep.color5.r() as usize + inten_table as usize * 32) * 4 + srt];
out[0] = entry.m_lo;
out[1] = entry.m_hi;
for y in 0..4u32 {
for x in 0..4u32 {
let s = sel.get_selector(x, y);
let ds = (entry.m_trans as u32 >> (s * 3)) & 7;
dxt5a_set_selector(&mut sels, x, y, ds);
}
}
out[2..8].copy_from_slice(&sels);
out
}
const OPAQUE_DXT5A: [u8; 8] = [255, 255, 0, 0, 0, 0, 0, 0];
impl Etc1sTranscoder {
pub fn transcode_slice_atc(
&self,
slice_data: &[u8],
num_blocks_x: u32,
num_blocks_y: 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)?;
if out.len() != indices.len() * 8 {
return None;
}
out.fill(0);
for (b, &(ei, si)) in indices.iter().enumerate() {
let block =
convert_etc1s_to_atc(&self.endpoints[ei as usize], &self.selectors[si as usize]);
out[b * 8..b * 8 + 8].copy_from_slice(&block);
}
Some(())
}
pub fn transcode_slice_atc_rgba_opaque(
&self,
rgb_slice: &[u8],
num_blocks_x: u32,
num_blocks_y: u32,
video: Option<&mut Vec<u32>>,
out: &mut [u8],
) -> Option<()> {
let rgb = self.decode_slice_indices(rgb_slice, num_blocks_x, num_blocks_y, video)?;
if out.len() != rgb.len() * 16 {
return None;
}
out.fill(0);
for (b, &(ei, si)) in rgb.iter().enumerate() {
out[b * 16..b * 16 + 8].copy_from_slice(&OPAQUE_DXT5A);
let color =
convert_etc1s_to_atc(&self.endpoints[ei as usize], &self.selectors[si as usize]);
out[b * 16 + 8..b * 16 + 16].copy_from_slice(&color);
}
Some(())
}
pub fn transcode_image_atc_rgba(
&self,
rgb_slice: &[u8],
alpha_slice: &[u8],
num_blocks_x: u32,
num_blocks_y: u32,
video: Option<(&mut Vec<u32>, &mut Vec<u32>)>,
out: &mut [u8],
) -> Option<()> {
let (vc, va) = match video {
Some((c, a)) => (Some(c), Some(a)),
None => (None, None),
};
let rgb = self.decode_slice_indices(rgb_slice, num_blocks_x, num_blocks_y, vc)?;
let alpha = self.decode_slice_indices(alpha_slice, num_blocks_x, num_blocks_y, va)?;
if out.len() != rgb.len() * 16 {
return None;
}
out.fill(0);
for (b, (&(rei, rsi), &(aei, asi))) in rgb.iter().zip(alpha.iter()).enumerate() {
let a = convert_etc1s_to_dxt5a(
&self.endpoints[aei as usize],
&self.selectors[asi as usize],
);
let color =
convert_etc1s_to_atc(&self.endpoints[rei as usize], &self.selectors[rsi as usize]);
out[b * 16..b * 16 + 8].copy_from_slice(&a);
out[b * 16 + 8..b * 16 + 16].copy_from_slice(&color);
}
Some(())
}
}