use super::tables::INTEN_TABLES;
use crate::color::Color32;
#[inline]
fn clamp255(v: i32) -> u8 {
v.clamp(0, 255) as u8
}
fn unpack_delta3(packed: u16) -> (i32, i32, i32) {
let mut r = ((packed >> 6) & 7) as i32;
let mut g = ((packed >> 3) & 7) as i32;
let mut b = (packed & 7) as i32;
if r >= 4 {
r -= 8;
}
if g >= 4 {
g -= 8;
}
if b >= 4 {
b -= 8;
}
(r, g, b)
}
fn unpack_color5(packed: u16, scaled: bool, alpha: u32) -> Color32 {
let mut b = (packed & 31) as u32;
let mut g = ((packed >> 5) & 31) as u32;
let mut r = ((packed >> 10) & 31) as u32;
if scaled {
b = (b << 3) | (b >> 2);
g = (g << 3) | (g >> 2);
r = (r << 3) | (r >> 2);
}
Color32::new(r, g, b, alpha)
}
fn unpack_color5_delta(packed5: u16, delta3: u16, scaled: bool, alpha: u32) -> Color32 {
let (dr, dg, db) = unpack_delta3(delta3);
let mut r = ((packed5 >> 10) & 31) as i32 + dr;
let mut g = ((packed5 >> 5) & 31) as i32 + dg;
let mut b = (packed5 & 31) as i32 + db;
if ((r | g | b) as u32) > 31 {
r = r.clamp(0, 31);
g = g.clamp(0, 31);
b = b.clamp(0, 31);
}
if scaled {
b = (b << 3) | (b >> 2);
g = (g << 3) | (g >> 2);
r = (r << 3) | (r >> 2);
}
Color32::new(r as u32, g as u32, b as u32, alpha.min(255))
}
fn unpack_color4(packed: u16, scaled: bool, alpha: u32) -> Color32 {
let mut b = (packed & 15) as u32;
let mut g = ((packed >> 4) & 15) as u32;
let mut r = ((packed >> 8) & 15) as u32;
if scaled {
b = (b << 4) | b;
g = (g << 4) | g;
r = (r << 4) | r;
}
Color32::new(r, g, b, alpha.min(255))
}
#[derive(Clone, Copy, Default)]
pub struct DecoderEtcBlock {
pub m_bytes: [u8; 8],
}
impl DecoderEtcBlock {
pub fn new() -> Self {
Self { m_bytes: [0; 8] }
}
fn set_byte_bits(&mut self, ofs: u32, num: u32, bits: u32) {
let byte_ofs = (7 - (ofs >> 3)) as usize;
let byte_bit_ofs = ofs & 7;
let mask = (1u32 << num) - 1;
self.m_bytes[byte_ofs] &= !((mask << byte_bit_ofs) as u8);
self.m_bytes[byte_ofs] |= (bits << byte_bit_ofs) as u8;
}
pub fn set_flip_bit(&mut self, flip: bool) {
self.m_bytes[3] &= !1;
self.m_bytes[3] |= flip as u8;
}
pub fn set_diff_bit(&mut self, diff: bool) {
self.m_bytes[3] &= !2;
self.m_bytes[3] |= (diff as u8) << 1;
}
pub fn set_inten_table(&mut self, subblock: u32, t: u32) {
let ofs = if subblock != 0 { 2 } else { 5 };
self.m_bytes[3] &= !((7 << ofs) as u8);
self.m_bytes[3] |= (t << ofs) as u8;
}
pub fn set_base5_color(&mut self, c: u16) {
let c = c as u32;
self.set_byte_bits(59, 5, (c >> 10) & 31);
self.set_byte_bits(51, 5, (c >> 5) & 31);
self.set_byte_bits(43, 5, c & 31);
}
pub fn pack_color5_unscaled(color5: Color32) -> u16 {
(color5.b() as u16) | ((color5.g() as u16) << 5) | ((color5.r() as u16) << 10)
}
pub fn get_block_color5(
base_color5: Color32,
inten_table: u32,
index: usize,
) -> (u32, u32, u32) {
let br = ((base_color5.r() as u32) << 3) | ((base_color5.r() as u32) >> 2);
let bg = ((base_color5.g() as u32) << 3) | ((base_color5.g() as u32) >> 2);
let bb = ((base_color5.b() as u32) << 3) | ((base_color5.b() as u32) >> 2);
let inten = &INTEN_TABLES[inten_table as usize];
(
clamp255(br as i32 + inten[index]) as u32,
clamp255(bg as i32 + inten[index]) as u32,
clamp255(bb as i32 + inten[index]) as u32,
)
}
pub fn get_block_color5_r(base_color5: Color32, inten_table: u32, index: usize) -> u32 {
let br = ((base_color5.r() as u32) << 3) | ((base_color5.r() as u32) >> 2);
clamp255(br as i32 + INTEN_TABLES[inten_table as usize][index]) as u32
}
pub fn get_block_colors5_g(base_color5: Color32, inten_table: u32) -> [i32; 4] {
let g = ((base_color5.g() as i32) << 3) | ((base_color5.g() as i32) >> 2);
let inten = &INTEN_TABLES[inten_table as usize];
core::array::from_fn(|i| clamp255(g + inten[i]) as i32)
}
pub fn get_block_colors5(base_color5: Color32, inten_table: u32) -> [Color32; 4] {
let r = ((base_color5.r() as u32) << 3) | ((base_color5.r() as u32) >> 2);
let g = ((base_color5.g() as u32) << 3) | ((base_color5.g() as u32) >> 2);
let b = ((base_color5.b() as u32) << 3) | ((base_color5.b() as u32) >> 2);
let inten = &INTEN_TABLES[inten_table as usize];
let mut out = [Color32::default(); 4];
for (i, c) in out.iter_mut().enumerate() {
*c = Color32::new(
clamp255(r as i32 + inten[i]) as u32,
clamp255(g as i32 + inten[i]) as u32,
clamp255(b as i32 + inten[i]) as u32,
255,
);
}
out
}
fn get_byte_bits(&self, ofs: u32, num: u32) -> u32 {
let byte_ofs = (7 - (ofs >> 3)) as usize;
let byte_bit_ofs = ofs & 7;
((self.m_bytes[byte_ofs] as u32) >> byte_bit_ofs) & ((1 << num) - 1)
}
fn get_base5_color(&self) -> u16 {
let r = self.get_byte_bits(59, 5);
let g = self.get_byte_bits(51, 5);
let b = self.get_byte_bits(43, 5);
(b | (g << 5) | (r << 10)) as u16
}
fn get_base4_color(&self, idx: u32) -> u16 {
let (r, g, b) = if idx != 0 {
(
self.get_byte_bits(56, 4),
self.get_byte_bits(48, 4),
self.get_byte_bits(40, 4),
)
} else {
(
self.get_byte_bits(60, 4),
self.get_byte_bits(52, 4),
self.get_byte_bits(44, 4),
)
};
(b | (g << 4) | (r << 8)) as u16
}
fn get_delta3_color(&self) -> u16 {
let r = self.get_byte_bits(56, 3);
let g = self.get_byte_bits(48, 3);
let b = self.get_byte_bits(40, 3);
(b | (g << 3) | (r << 6)) as u16
}
pub fn get_flip_bit(&self) -> bool {
self.m_bytes[3] & 1 != 0
}
pub fn get_diff_bit(&self) -> bool {
self.m_bytes[3] & 2 != 0
}
fn get_inten_table(&self, subblock: u32) -> u32 {
let ofs = if subblock != 0 { 2 } else { 5 };
((self.m_bytes[3] as u32) >> ofs) & 7
}
pub fn get_block_colors(&self, subblock: u32) -> [Color32; 4] {
let base = if self.get_diff_bit() {
if subblock != 0 {
unpack_color5_delta(self.get_base5_color(), self.get_delta3_color(), true, 255)
} else {
unpack_color5(self.get_base5_color(), true, 255)
}
} else {
unpack_color4(self.get_base4_color(subblock), true, 255)
};
let inten = &INTEN_TABLES[self.get_inten_table(subblock) as usize];
let mut out = [Color32::default(); 4];
for (i, c) in out.iter_mut().enumerate() {
*c = Color32::new(
clamp255(base.r() as i32 + inten[i]) as u32,
clamp255(base.g() as i32 + inten[i]) as u32,
clamp255(base.b() as i32 + inten[i]) as u32,
255,
);
}
out
}
}