use super::*;
pub fn encode_bc7_mode6(pixels: [[u8; 4]; 16], out: &mut [u8]) {
let (bits6, err6) = encode_bc7_mode6_inner(&pixels);
let mut a_lo = 255u8;
let mut a_hi = 0u8;
for p in &pixels {
a_lo = a_lo.min(p[3]);
a_hi = a_hi.max(p[3]);
}
let mut best_bits = bits6;
let mut best_err = err6;
if err6 > 0 && a_hi - a_lo > 2 {
if let Some((bits5, err5)) = try_bc7_mode5(&pixels, 0) {
if err5 < best_err {
best_err = err5;
best_bits = bits5;
}
}
if best_err > 0 {
if let Some((bits4, err4)) = try_bc7_mode4(&pixels) {
if err4 < best_err {
best_err = err4;
best_bits = bits4;
}
}
}
}
if best_err > bc7_m1_min_err().max(4) && best_err <= 1024 && a_lo == 255 {
if let Some((bits1, err1)) = m1::try_bc7_mode1(&pixels, best_err) {
if err1 < best_err {
best_err = err1;
best_bits = bits1;
}
}
}
if best_err > 0 {
let (mx, mn) = channel_minmax_rgba(&pixels);
let spans: [i32; 4] = [
(mx[0] - mn[0]) as i32,
(mx[1] - mn[1]) as i32,
(mx[2] - mn[2]) as i32,
(mx[3] - mn[3]) as i32,
];
for rot in 1u8..=3 {
let c = rot as usize - 1; let rest = spans[3].max(spans[(c + 1) % 3]).max(spans[(c + 2) % 3]);
if spans[c] > 2 && spans[c] > rest {
let mut rotated = pixels;
for p in rotated.iter_mut() {
p.swap(c, 3);
}
if let Some((bits5, err5)) = try_bc7_mode5(&rotated, rot) {
if err5 < best_err {
best_err = err5;
best_bits = bits5;
}
}
}
}
}
out[..16].copy_from_slice(&best_bits);
}
pub(super) const W2: [u32; 4] = [0, 21, 43, 64];
pub(super) const W3: [u32; 8] = [0, 9, 18, 27, 37, 46, 55, 64];
#[inline]
pub(super) fn unquant7(v: u8) -> u8 {
(v << 1) | (v >> 6)
}
pub(super) fn try_bc7_mode5(pixels: &[[u8; 4]; 16], rotation: u8) -> Option<([u8; 16], i64)> {
let alpha: [u8; 16] = pixels.map(|p| p[3]);
let mut a0 = 255u8;
let mut a1 = 0u8;
for &a in &alpha {
a0 = a0.min(a);
a1 = a1.max(a);
}
let (a_ep0, a_ep1, a_idx, a_err) = fit_alpha_mode5(&alpha, a1, a0);
let (mut best_c, mut c_err) = {
let (mx, mn) = extrema_opaque(pixels);
fit_color_mode5(pixels, mx, mn)
};
{
let (mx, mn) = channel_minmax_rgb(pixels);
let cand = fit_color_mode5(pixels, mx, mn);
if cand.1 < c_err {
c_err = cand.1;
best_c = cand.0;
}
if let Some((pa, pb)) = pca_extremes_rgb(pixels) {
let cand = fit_color_mode5(pixels, pa, pb);
if cand.1 < c_err {
c_err = cand.1;
best_c = cand.0;
}
}
if let Some((e0, e1)) = ls_endpoints_mode5(pixels, &best_c.2) {
let cand = fit_color_mode5(pixels, e0, e1);
if cand.1 < c_err {
c_err = cand.1;
best_c = cand.0;
}
}
}
let (c_ep0, c_ep1, c_idx) = best_c;
let err = c_err as i64 + a_err as i64;
Some((
pack_bc7_mode5(rotation, c_ep0, c_ep1, a_ep0, a_ep1, &c_idx, &a_idx),
err,
))
}
pub(super) fn try_bc7_mode4(pixels: &[[u8; 4]; 16]) -> Option<([u8; 16], i64)> {
let (mut best_c, mut c_err) = {
let (mx, mn) = extrema_opaque(pixels);
fit_color_mode4(pixels, mx, mn)
};
{
let (mx, mn) = channel_minmax_rgb(pixels);
let cand = fit_color_mode4(pixels, mx, mn);
if cand.1 < c_err {
c_err = cand.1;
best_c = cand.0;
}
if let Some((pa, pb)) = pca_extremes_rgb(pixels) {
let cand = fit_color_mode4(pixels, pa, pb);
if cand.1 < c_err {
c_err = cand.1;
best_c = cand.0;
}
}
if let Some((e0, e1)) = ls_endpoints_mode5(pixels, &best_c.2) {
let cand = fit_color_mode4(pixels, e0, e1);
if cand.1 < c_err {
c_err = cand.1;
best_c = cand.0;
}
}
}
let (c_ep0, c_ep1, c_idx) = best_c;
let alpha: [u8; 16] = pixels.map(|p| p[3]);
let mut lo = 255u8;
let mut hi = 0u8;
for &a in &alpha {
lo = lo.min(a);
hi = hi.max(a);
}
let (mut a_ep0, mut a_ep1, mut a_idx, mut a_err) = score_alpha_mode4(&alpha, hi >> 2, lo >> 2);
if a_err > 0 {
for d0 in -2i32..=2 {
for d1 in -2i32..=2 {
if d0 == 0 && d1 == 0 {
continue;
}
let q0 = ((hi >> 2) as i32 + d0).clamp(0, 63) as u8;
let q1 = ((lo >> 2) as i32 + d1).clamp(0, 63) as u8;
let cand = score_alpha_mode4(&alpha, q0, q1);
if cand.3 < a_err {
(a_ep0, a_ep1, a_idx, a_err) = cand;
}
}
}
}
let err = c_err as i64 + a_err as i64;
Some((pack_bc7_mode4(c_ep0, c_ep1, a_ep0, a_ep1, &c_idx, &a_idx), err))
}
#[inline]
pub(super) fn unquant5(v: u8) -> u8 {
(v << 3) | (v >> 2)
}
#[inline]
pub(super) fn unquant6(v: u8) -> u8 {
(v << 2) | (v >> 4)
}
#[allow(clippy::type_complexity)]
pub(super) fn fit_color_mode4(
pixels: &[[u8; 4]; 16],
e0: [u8; 3],
e1: [u8; 3],
) -> (([u8; 3], [u8; 3], [u8; 16]), i32) {
let mut q0 = [0u8; 3];
let mut q1 = [0u8; 3];
for c in 0..3 {
q0[c] = e0[c] >> 3;
q1[c] = e1[c] >> 3;
}
let c0 = [unquant5(q0[0]), unquant5(q0[1]), unquant5(q0[2])];
let c1 = [unquant5(q1[0]), unquant5(q1[1]), unquant5(q1[2])];
let mut pal = [[0u8; 3]; 4];
for (k, &w) in W2.iter().enumerate() {
for c in 0..3 {
pal[k][c] = (((64 - w) * c0[c] as u32 + w * c1[c] as u32 + 32) / 64) as u8;
}
}
let mut idx = [0u8; 16];
let mut err = 0i32;
for (i, p) in pixels.iter().enumerate() {
let mut bi = 0u8;
let mut be = i32::MAX;
for (j, pc) in pal.iter().enumerate() {
let e = sqr_rgb([p[0], p[1], p[2]], *pc);
if e < be {
be = e;
bi = j as u8;
}
}
idx[i] = bi;
err += be;
}
if idx[0] >= 2 {
std::mem::swap(&mut q0, &mut q1);
for v in idx.iter_mut() {
*v = 3 - *v;
}
}
((q0, q1, idx), err)
}
pub(super) fn score_alpha_mode4(alpha: &[u8; 16], q0: u8, q1: u8) -> (u8, u8, [u8; 16], i32) {
let c0 = unquant6(q0);
let c1 = unquant6(q1);
let mut pal = [0u8; 8];
for (k, &w) in W3.iter().enumerate() {
pal[k] = (((64 - w) * c0 as u32 + w * c1 as u32 + 32) / 64) as u8;
}
let mut idx = [0u8; 16];
let mut err = 0i32;
for (i, &a) in alpha.iter().enumerate() {
let mut bi = 0u8;
let mut be = i32::MAX;
for (j, &p) in pal.iter().enumerate() {
let d = (p as i32 - a as i32).pow(2);
if d < be {
be = d;
bi = j as u8;
}
}
idx[i] = bi;
err += be;
}
let (mut r0, mut r1) = (q0, q1);
if idx[0] >= 4 {
std::mem::swap(&mut r0, &mut r1);
for v in idx.iter_mut() {
*v = 7 - *v;
}
}
(r0, r1, idx, err)
}
pub(super) fn pack_bc7_mode4(
c0: [u8; 3],
c1: [u8; 3],
a0: u8,
a1: u8,
c_idx: &[u8; 16],
a_idx: &[u8; 16],
) -> [u8; 16] {
let mut bw = BitWriter::default();
for _ in 0..4 {
bw.write_bits(0, 1);
}
bw.write_bits(1, 1);
bw.write_bits(0, 2); bw.write_bits(0, 1); for c in 0..3 {
bw.write_bits(c0[c] as u32, 5);
bw.write_bits(c1[c] as u32, 5);
}
bw.write_bits(a0 as u32, 6);
bw.write_bits(a1 as u32, 6);
bw.write_bits(c_idx[0] as u32, 1);
for &v in &c_idx[1..] {
bw.write_bits(v as u32, 2);
}
bw.write_bits(a_idx[0] as u32, 2);
for &v in &a_idx[1..] {
bw.write_bits(v as u32, 3);
}
bw.into_array()
}
#[allow(clippy::type_complexity)]
pub(super) fn fit_color_mode5(
pixels: &[[u8; 4]; 16],
e0: [u8; 3],
e1: [u8; 3],
) -> (([u8; 3], [u8; 3], [u8; 16]), i32) {
let mut q0 = [0u8; 3];
let mut q1 = [0u8; 3];
for c in 0..3 {
q0[c] = e0[c] >> 1;
q1[c] = e1[c] >> 1;
}
let pal = palette_mode5_color(q0, q1);
let mut idx = [0u8; 16];
let mut err = 0i32;
for (i, p) in pixels.iter().enumerate() {
let mut bi = 0u8;
let mut be = i32::MAX;
for (j, pc) in pal.iter().enumerate() {
let e = sqr_rgb([p[0], p[1], p[2]], *pc);
if e < be {
be = e;
bi = j as u8;
}
}
idx[i] = bi;
err += be;
}
if idx[0] >= 2 {
std::mem::swap(&mut q0, &mut q1);
for v in idx.iter_mut() {
*v = 3 - *v;
}
}
((q0, q1, idx), err)
}
pub(super) fn palette_mode5_color(q0: [u8; 3], q1: [u8; 3]) -> [[u8; 3]; 4] {
let c0 = [unquant7(q0[0]), unquant7(q0[1]), unquant7(q0[2])];
let c1 = [unquant7(q1[0]), unquant7(q1[1]), unquant7(q1[2])];
let mut pal = [[0u8; 3]; 4];
for (k, &w) in W2.iter().enumerate() {
for c in 0..3 {
pal[k][c] = (((64 - w) * c0[c] as u32 + w * c1[c] as u32 + 32) / 64) as u8;
}
}
pal
}
pub(super) fn ls_endpoints_mode5(pixels: &[[u8; 4]; 16], indices: &[u8; 16]) -> Option<([u8; 3], [u8; 3])> {
const WF: [f32; 4] = [0.0, 21.0 / 64.0, 43.0 / 64.0, 1.0];
let mut a00 = 0f32;
let mut a01 = 0f32;
let mut a11 = 0f32;
let mut b0 = [0f32; 3];
let mut b1 = [0f32; 3];
for (i, p) in pixels.iter().enumerate() {
let w = WF[indices[i] as usize];
let u = 1.0 - w;
a00 += u * u;
a01 += u * w;
a11 += w * w;
for c in 0..3 {
let x = p[c] as f32;
b0[c] += u * x;
b1[c] += w * x;
}
}
let det = a00 * a11 - a01 * a01;
if det.abs() < 1e-4 {
return None;
}
let mut e0 = [0u8; 3];
let mut e1 = [0u8; 3];
for c in 0..3 {
e0[c] = ((a11 * b0[c] - a01 * b1[c]) / det).round().clamp(0.0, 255.0) as u8;
e1[c] = ((a00 * b1[c] - a01 * b0[c]) / det).round().clamp(0.0, 255.0) as u8;
}
Some((e0, e1))
}
pub(super) fn fit_alpha_mode5(alpha: &[u8; 16], hi: u8, lo: u8) -> (u8, u8, [u8; 16], i32) {
let (mut e0, mut e1, mut idx, mut err) = score_alpha_mode5(alpha, hi, lo);
if err > 0 && hi != lo {
for d0 in -2i32..=2 {
for d1 in -2i32..=2 {
if d0 == 0 && d1 == 0 {
continue;
}
let c0 = (hi as i32 + d0).clamp(0, 255) as u8;
let c1 = (lo as i32 + d1).clamp(0, 255) as u8;
let cand = score_alpha_mode5(alpha, c0, c1);
if cand.3 < err {
(e0, e1, idx, err) = cand;
}
}
}
}
(e0, e1, idx, err)
}
pub(super) fn score_alpha_mode5(alpha: &[u8; 16], c0: u8, c1: u8) -> (u8, u8, [u8; 16], i32) {
let mut pal = [0u8; 4];
for (k, &w) in W2.iter().enumerate() {
pal[k] = (((64 - w) * c0 as u32 + w * c1 as u32 + 32) / 64) as u8;
}
let mut idx = [0u8; 16];
let mut err = 0i32;
for (i, &a) in alpha.iter().enumerate() {
let mut bi = 0u8;
let mut be = i32::MAX;
for (j, &p) in pal.iter().enumerate() {
let d = (p as i32 - a as i32).pow(2);
if d < be {
be = d;
bi = j as u8;
}
}
idx[i] = bi;
err += be;
}
let (mut r0, mut r1) = (c0, c1);
if idx[0] >= 2 {
std::mem::swap(&mut r0, &mut r1);
for v in idx.iter_mut() {
*v = 3 - *v;
}
}
(r0, r1, idx, err)
}
pub(super) fn pack_bc7_mode5(
rotation: u8,
c0: [u8; 3],
c1: [u8; 3],
a0: u8,
a1: u8,
c_idx: &[u8; 16],
a_idx: &[u8; 16],
) -> [u8; 16] {
let mut bw = BitWriter::default();
for _ in 0..5 {
bw.write_bits(0, 1);
}
bw.write_bits(1, 1);
bw.write_bits(rotation as u32, 2);
for c in 0..3 {
bw.write_bits(c0[c] as u32, 7);
bw.write_bits(c1[c] as u32, 7);
}
bw.write_bits(a0 as u32, 8);
bw.write_bits(a1 as u32, 8);
bw.write_bits(c_idx[0] as u32, 1);
for &v in &c_idx[1..] {
bw.write_bits(v as u32, 2);
}
bw.write_bits(a_idx[0] as u32, 1);
for &v in &a_idx[1..] {
bw.write_bits(v as u32, 2);
}
bw.into_array()
}
pub(super) fn encode_bc7_mode6_inner(pixels: &[[u8; 4]; 16]) -> ([u8; 16], i64) {
let mut best_bits = [0u8; 16];
let mut best_err = i64::MAX;
let mut best_seed = extrema_rgba(pixels);
let mut have = false;
let (seeds, n_seeds) = bc7_mode6_seeds(pixels);
for &(ep0, ep1) in &seeds[..n_seeds] {
if let Some((bits, err)) = try_bc7_mode6(pixels, ep0, ep1, false) {
if err < best_err {
best_err = err;
best_bits = bits;
best_seed = (ep0, ep1);
have = true;
}
}
}
let do_ls = rgba_span_sum(pixels) > 8;
if do_ls {
if let Some((bits, err)) = try_bc7_mode6(pixels, best_seed.0, best_seed.1, true) {
if err <= best_err {
best_bits = bits;
best_err = err;
have = true;
}
}
}
if !have {
if let Some((bits, err)) = try_bc7_mode6(pixels, best_seed.0, best_seed.1, true) {
best_bits = bits;
best_err = err;
}
}
(best_bits, best_err)
}
pub(super) fn rgba_span_sum(pixels: &[[u8; 4]; 16]) -> i32 {
let (mx, mn) = channel_minmax_rgba(pixels);
(0..4).map(|c| (mx[c] - mn[c]) as i32).sum()
}
pub(super) type Seed = ([u8; 4], [u8; 4]);
#[inline]
pub(super) fn push_seed(seeds: &mut [Seed; 5], n: &mut usize, s: Seed) {
for seed in seeds[..*n].iter() {
if *seed == s {
return;
}
}
seeds[*n] = s;
*n += 1;
}
pub(super) fn bc7_mode6_seeds(pixels: &[[u8; 4]; 16]) -> ([Seed; 5], usize) {
let mut seeds = [([0u8; 4], [0u8; 4]); 5];
let mut n = 0usize;
push_seed(&mut seeds, &mut n, extrema_rgba(pixels));
push_seed(&mut seeds, &mut n, channel_minmax_rgba(pixels));
let span = rgba_span_sum(pixels);
if span <= 16 {
return (seeds, n);
}
let (mx, mn) = extrema_rgba(pixels);
let mut mean = [0u32; 4];
for p in pixels {
for c in 0..4 {
mean[c] += p[c] as u32;
}
}
let mean = mean.map(|v| (v / 16) as u8);
push_seed(&mut seeds, &mut n, (mx, mean));
push_seed(&mut seeds, &mut n, (mean, mn));
if span > 48 {
let mut best_d = -1i32;
let mut pa = pixels[0];
let mut pb = pixels[0];
for i in 0..16 {
for j in (i + 1)..16 {
let mut d = 0i32;
for c in 0..4 {
let t = pixels[i][c] as i32 - pixels[j][c] as i32;
d += t * t;
}
if d > best_d {
best_d = d;
pa = pixels[i];
pb = pixels[j];
}
}
}
push_seed(&mut seeds, &mut n, (pa, pb));
}
(seeds, n)
}
pub(super) fn channel_minmax_rgba(pixels: &[[u8; 4]; 16]) -> ([u8; 4], [u8; 4]) {
let mut mn = [255u8; 4];
let mut mx = [0u8; 4];
for p in pixels {
for c in 0..4 {
mn[c] = mn[c].min(p[c]);
mx[c] = mx[c].max(p[c]);
}
}
(mx, mn)
}
pub(super) const W6M: [u32; 16] = [0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64];
#[inline]
pub(super) fn palette_mode6(c0: [u8; 4], c1: [u8; 4]) -> [[u8; 4]; 16] {
let mut pal = [[0u8; 4]; 16];
for (k, &w) in W6M.iter().enumerate() {
for c in 0..4 {
pal[k][c] = (((64 - w) * c0[c] as u32 + w * c1[c] as u32 + 32) / 64) as u8;
}
}
pal
}
#[inline]
pub(super) fn best_index_pal(px: &[u8; 4], pal: &[[u8; 4]; 16]) -> (u8, i32) {
let mut best_i = 0u8;
let mut best_e = i32::MAX;
for (k, p) in pal.iter().enumerate() {
let mut e = 0i32;
for c in 0..4 {
let d = p[c] as i32 - px[c] as i32;
e += d * d;
}
if e < best_e {
best_e = e;
best_i = k as u8;
}
}
(best_i, best_e)
}
#[inline]
pub(super) fn fit_indices_mode6(pixels: &[[u8; 4]; 16], pal: &[[u8; 4]; 16]) -> ([u8; 16], i64) {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
if simd::has_avx2() {
return simd::fit_indices_mode6_avx2(pixels, pal);
}
fit_indices_mode6_exhaustive(pixels, pal)
}
#[inline]
pub(super) fn fit_indices_mode6_exhaustive(pixels: &[[u8; 4]; 16], pal: &[[u8; 4]; 16]) -> ([u8; 16], i64) {
let mut indices = [0u8; 16];
let mut err = 0i64;
for (i, px) in pixels.iter().enumerate() {
let (idx, e) = best_index_pal(px, pal);
indices[i] = idx;
err += e as i64;
}
(indices, err)
}
pub(super) fn try_bc7_mode6(
pixels: &[[u8; 4]; 16],
ep0: [u8; 4],
ep1: [u8; 4],
refine: bool,
) -> Option<([u8; 16], i64)> {
let (mut q0, mut p0) = quantize_7p(ep0);
let (mut q1, mut p1) = quantize_7p(ep1);
let pal = palette_mode6(unquantize_7p(q0, p0), unquantize_7p(q1, p1));
let (mut indices, mut err) = fit_indices_mode6(pixels, &pal);
if indices[0] > 7 {
std::mem::swap(&mut q0, &mut q1);
std::mem::swap(&mut p0, &mut p1);
for idx in indices.iter_mut() {
*idx = 15 - *idx;
}
}
if refine {
if let Some((r0, r1)) = ls_endpoints_mode6(pixels, &indices) {
let (nq0, np0) = quantize_7p(r0);
let (nq1, np1) = quantize_7p(r1);
let npal = palette_mode6(unquantize_7p(nq0, np0), unquantize_7p(nq1, np1));
let (mut nidx, nerr) = fit_indices_mode6(pixels, &npal);
if nidx[0] > 7 {
for idx in nidx.iter_mut() {
*idx = 15 - *idx;
}
q0 = nq1;
p0 = np1;
q1 = nq0;
p1 = np0;
} else {
q0 = nq0;
p0 = np0;
q1 = nq1;
p1 = np1;
}
indices = nidx;
err = nerr;
}
}
Some((pack_bc7_mode6(q0, p0, q1, p1, indices), err))
}
pub(super) fn ls_endpoints_mode6(pixels: &[[u8; 4]; 16], indices: &[u8; 16]) -> Option<([u8; 4], [u8; 4])> {
const W: [f32; 16] = [
0.0, 4.0 / 64.0, 9.0 / 64.0, 13.0 / 64.0, 17.0 / 64.0, 21.0 / 64.0, 26.0 / 64.0,
30.0 / 64.0, 34.0 / 64.0, 38.0 / 64.0, 43.0 / 64.0, 47.0 / 64.0, 51.0 / 64.0, 55.0 / 64.0,
60.0 / 64.0, 1.0,
];
let mut a00 = 0.0f32;
let mut a01 = 0.0f32;
let mut a11 = 0.0f32;
let mut b0 = [0.0f32; 4];
let mut b1 = [0.0f32; 4];
for i in 0..16 {
let w = W[indices[i] as usize];
let u = 1.0 - w;
a00 += u * u;
a01 += u * w;
a11 += w * w;
for c in 0..4 {
let x = pixels[i][c] as f32;
b0[c] += u * x;
b1[c] += w * x;
}
}
let det = a00 * a11 - a01 * a01;
if det.abs() < 1e-4 {
return None;
}
let mut e0 = [0u8; 4];
let mut e1 = [0u8; 4];
for c in 0..4 {
let x0 = (a11 * b0[c] - a01 * b1[c]) / det;
let x1 = (a00 * b1[c] - a01 * b0[c]) / det;
e0[c] = x0.round().clamp(0.0, 255.0) as u8;
e1[c] = x1.round().clamp(0.0, 255.0) as u8;
}
Some((e0, e1))
}
pub(super) fn pack_bc7_mode6(q0: [u8; 4], p0: u8, q1: [u8; 4], p1: u8, indices: [u8; 16]) -> [u8; 16] {
let mut bw = BitWriter::default();
for _ in 0..6 {
bw.write_bits(0, 1);
}
bw.write_bits(1, 1);
for c in 0..4 {
bw.write_bits(q0[c] as u32, 7);
bw.write_bits(q1[c] as u32, 7);
}
bw.write_bits(p0 as u32, 1);
bw.write_bits(p1 as u32, 1);
bw.write_bits(indices[0] as u32, 3);
for i in 1..16 {
bw.write_bits(indices[i] as u32, 4);
}
bw.into_array()
}
pub(super) fn quantize_7p(c: [u8; 4]) -> ([u8; 4], u8) {
let mut best_p = 0u8;
let mut best_q = [0u8; 4];
let mut best_err = i32::MAX;
for p in 0..2u8 {
let mut q = [0u8; 4];
let mut err = 0i32;
for i in 0..4 {
q[i] = (c[i] >> 1).min(127);
let mut best_qi = q[i];
let mut best_e = i32::MAX;
for cand in q[i].saturating_sub(1)..=q[i].saturating_add(1).min(127) {
let recon = unquantize_7p_chan(cand, p);
let e = (recon as i32 - c[i] as i32).pow(2);
if e < best_e {
best_e = e;
best_qi = cand;
}
}
q[i] = best_qi;
err += best_e;
}
if err < best_err {
best_err = err;
best_p = p;
best_q = q;
}
}
(best_q, best_p)
}
pub(super) fn unquantize_7p(q: [u8; 4], p: u8) -> [u8; 4] {
[
unquantize_7p_chan(q[0], p),
unquantize_7p_chan(q[1], p),
unquantize_7p_chan(q[2], p),
unquantize_7p_chan(q[3], p),
]
}
pub(super) fn unquantize_7p_chan(q: u8, p: u8) -> u8 {
let v = ((q as u32) << 1) | (p as u32);
v as u8
}
pub(super) fn extrema_opaque(pixels: &[[u8; 4]; 16]) -> ([u8; 3], [u8; 3]) {
let mut min_l = i32::MAX;
let mut max_l = i32::MIN;
let mut min_rgb = [0u8; 3];
let mut max_rgb = [0u8; 3];
for p in pixels {
let l = p[0] as i32 * 2 + p[1] as i32 * 3 + p[2] as i32;
if l < min_l {
min_l = l;
min_rgb = [p[0], p[1], p[2]];
}
if l > max_l {
max_l = l;
max_rgb = [p[0], p[1], p[2]];
}
}
(max_rgb, min_rgb)
}
pub(super) fn extrema_rgba(pixels: &[[u8; 4]; 16]) -> ([u8; 4], [u8; 4]) {
let mut min_l = i32::MAX;
let mut max_l = i32::MIN;
let mut min_p = [0u8; 4];
let mut max_p = [255u8; 4];
for p in pixels {
let l = p[0] as i32 + p[1] as i32 + p[2] as i32 + p[3] as i32;
if l < min_l {
min_l = l;
min_p = *p;
}
if l > max_l {
max_l = l;
max_p = *p;
}
}
(max_p, min_p)
}