use super::block::DecoderEtcBlock;
use super::tables::{
ETC1_COLOR_DELTA_MAX, ETC1_COLOR_DELTA_MIN, ETC1_PIXEL_COORDS, ETC1_SOLID_SELECTORS,
};
use crate::color::Color32;
use crate::uastc::tables::HAS_ETC1_BIAS;
use crate::uastc::unpack::{unpack_pixels, unpack_to_block, UnpackedUastcBlock};
use crate::uastc::UASTC_MODE_INDEX_SOLID_COLOR;
fn apply_etc1_bias(block_color: Color32, bias: u32, limit: u32, subblock: u32) -> Color32 {
const DIVS: [i32; 3] = [1, 3, 9];
let mut result = Color32::default();
let sub = subblock != 0;
for c in 0..3usize {
let delta: i32 = match bias {
2 => {
if sub {
0
} else if c == 0 {
-1
} else {
0
}
}
5 => {
if sub {
0
} else if c == 1 {
-1
} else {
0
}
}
6 => {
if sub {
0
} else if c == 2 {
-1
} else {
0
}
}
7 => {
if sub {
0
} else if c == 0 {
1
} else {
0
}
}
11 => {
if sub {
0
} else if c == 1 {
1
} else {
0
}
}
15 => {
if sub {
0
} else if c == 2 {
1
} else {
0
}
}
18 => {
if sub {
if c == 0 {
-1
} else {
0
}
} else {
0
}
}
19 => {
if sub {
if c == 1 {
-1
} else {
0
}
} else {
0
}
}
20 => {
if sub {
if c == 2 {
-1
} else {
0
}
} else {
0
}
}
21 => {
if sub {
if c == 0 {
1
} else {
0
}
} else {
0
}
}
24 => {
if sub {
if c == 1 {
1
} else {
0
}
} else {
0
}
}
8 => {
if sub {
if c == 2 {
1
} else {
0
}
} else {
0
}
}
10 => -2,
27 => {
if sub {
0
} else {
-1
}
}
28 => {
if sub {
-1
} else {
1
}
}
29 => {
if sub {
1
} else {
0
}
}
30 => {
if sub {
-1
} else {
0
}
}
31 => {
if sub {
0
} else {
1
}
}
_ => ((bias as i32 / DIVS[c]) % 3) - 1,
};
let lim = limit as i32;
let mut v = block_color[c] as i32;
if v == 0 {
if delta == -2 {
v += 3;
} else {
v += delta + 1;
}
} else if v == lim {
v += delta - 1;
} else {
v += delta;
if v < 0 || v > lim {
v = (v - delta) - delta;
}
}
result[c] = v as u8;
}
result
}
fn etc1_determine_selectors(
blk: &mut DecoderEtcBlock,
pixels: &[Color32; 16],
first_subblock: u32,
last_subblock: u32,
) {
const S_TRAN: [u8; 4] = [1, 0, 2, 3];
let mut l_bitmask = 0u16;
let mut h_bitmask = 0u16;
let flip = blk.get_flip_bit();
for subblock in first_subblock..last_subblock {
let bc = blk.get_block_colors(subblock);
let block_y: [u32; 4] = core::array::from_fn(|i| {
bc[i][0] as u32 * 54 + bc[i][1] as u32 * 183 + bc[i][2] as u32 * 19
});
let block_y01 = block_y[0] + block_y[1];
let block_y12 = block_y[1] + block_y[2];
let block_y23 = block_y[2] + block_y[3];
if flip {
let mut ofs = subblock * 2;
for y in 0..2u32 {
for x in 0..4u32 {
let c = pixels[(x + (subblock * 2 + y) * 4) as usize];
let l = c[0] as u32 * 108 + c[1] as u32 * 366 + c[2] as u32 * 38;
let t = S_TRAN[(l < block_y01) as usize
+ (l < block_y12) as usize
+ (l < block_y23) as usize] as u16;
l_bitmask |= (t & 1) << ofs;
h_bitmask |= (t >> 1) << ofs;
ofs += 4;
}
ofs = ofs + 1 - 4 * 4;
}
} else {
let mut ofs = subblock * 2 * 4;
for x in 0..2u32 {
for y in 0..4u32 {
let c = pixels[(subblock * 2 + x + y * 4) as usize];
let l = c[0] as u32 * 108 + c[1] as u32 * 366 + c[2] as u32 * 38;
let t = S_TRAN[(l < block_y01) as usize
+ (l < block_y12) as usize
+ (l < block_y23) as usize] as u16;
l_bitmask |= (t & 1) << ofs;
h_bitmask |= (t >> 1) << ofs;
ofs += 1;
}
}
}
}
blk.m_bytes[7] = l_bitmask as u8;
blk.m_bytes[6] = (l_bitmask >> 8) as u8;
blk.m_bytes[5] = h_bitmask as u8;
blk.m_bytes[4] = (h_bitmask >> 8) as u8;
}
pub(crate) fn transcode_to_etc1(
u: &UnpackedUastcBlock,
pixels: &[Color32; 16],
blk: &mut DecoderEtcBlock,
) {
if u.mode == UASTC_MODE_INDEX_SOLID_COLOR {
blk.m_bytes[3] =
((u.etc1_diff as u32) << 1 | (u.etc1_inten0 << 5) | (u.etc1_inten0 << 2)) as u8;
if u.etc1_diff {
blk.m_bytes[0] = (u.etc1_r << 3) as u8;
blk.m_bytes[1] = (u.etc1_g << 3) as u8;
blk.m_bytes[2] = (u.etc1_b << 3) as u8;
} else {
blk.m_bytes[0] = (u.etc1_r | (u.etc1_r << 4)) as u8;
blk.m_bytes[1] = (u.etc1_g | (u.etc1_g << 4)) as u8;
blk.m_bytes[2] = (u.etc1_b | (u.etc1_b << 4)) as u8;
}
blk.m_bytes[4..8].copy_from_slice(&ETC1_SOLID_SELECTORS[u.etc1_selector as usize]);
return;
}
let flip = u.etc1_flip;
let diff = u.etc1_diff;
blk.m_bytes[3] =
(flip as u32 | (diff as u32) << 1 | (u.etc1_inten0 << 5) | (u.etc1_inten1 << 2)) as u8;
let limit = if diff { 31 } else { 15 };
let mut block_colors = [Color32::default(); 2];
for subset in 0..2usize {
let mut avg = [0u32; 3];
for c in &ETC1_PIXEL_COORDS[flip as usize][subset] {
let (x, y) = (c[0] as usize, c[1] as usize);
let p = pixels[x + y * 4];
avg[0] += p.r() as u32;
avg[1] += p.g() as u32;
avg[2] += p.b() as u32;
}
block_colors[subset] = Color32::new(
(avg[0] * limit + 1020) / (8 * 255),
(avg[1] * limit + 1020) / (8 * 255),
(avg[2] * limit + 1020) / (8 * 255),
0,
);
if HAS_ETC1_BIAS[u.mode as usize] != 0 {
block_colors[subset] =
apply_etc1_bias(block_colors[subset], u.etc1_bias, limit, subset as u32);
}
}
if diff {
let mut dr = block_colors[1].r() as i32 - block_colors[0].r() as i32;
let mut dg = block_colors[1].g() as i32 - block_colors[0].g() as i32;
let mut db = block_colors[1].b() as i32 - block_colors[0].b() as i32;
dr = dr.clamp(ETC1_COLOR_DELTA_MIN, ETC1_COLOR_DELTA_MAX);
dg = dg.clamp(ETC1_COLOR_DELTA_MIN, ETC1_COLOR_DELTA_MAX);
db = db.clamp(ETC1_COLOR_DELTA_MIN, ETC1_COLOR_DELTA_MAX);
if dr < 0 {
dr += 8;
}
if dg < 0 {
dg += 8;
}
if db < 0 {
db += 8;
}
blk.m_bytes[0] = ((block_colors[0].r() as i32) << 3 | dr) as u8;
blk.m_bytes[1] = ((block_colors[0].g() as i32) << 3 | dg) as u8;
blk.m_bytes[2] = ((block_colors[0].b() as i32) << 3 | db) as u8;
} else {
blk.m_bytes[0] = block_colors[1].r() | (block_colors[0].r() << 4);
blk.m_bytes[1] = block_colors[1].g() | (block_colors[0].g() << 4);
blk.m_bytes[2] = block_colors[1].b() | (block_colors[0].b() << 4);
}
etc1_determine_selectors(blk, pixels, 0, 2);
}
pub fn transcode_uastc_to_etc1(src: &[u8; 16]) -> Option<[u8; 8]> {
let u = unpack_to_block(src, false, true)?;
let pixels = if u.mode == UASTC_MODE_INDEX_SOLID_COLOR {
[Color32::default(); 16]
} else {
unpack_pixels(u.mode, u.common_pattern, u.solid_color, &u.astc, false)
};
let mut blk = DecoderEtcBlock::new();
transcode_to_etc1(&u, &pixels, &mut blk);
Some(blk.m_bytes)
}