use crate::util::FastRound;
fn dc_tx_scale(w: usize, h: usize) -> i32 {
let s = (w.trailing_zeros() + h.trailing_zeros()) as i32;
if s <= 8 { 0 } else { (s - 7) / 2 }
}
fn dc_tx_index(w: usize, h: usize) -> usize {
use crate::av2::itx::tx_size::*;
match (w, h) {
(4, 4) => TX_4X4,
(8, 8) => TX_8X8,
(16, 16) => TX_16X16,
(32, 32) => TX_32X32,
(64, 64) => TX_64X64,
(4, 8) => RTX_4X8,
(8, 4) => RTX_8X4,
(8, 16) => RTX_8X16,
(16, 8) => RTX_16X8,
(16, 32) => RTX_16X32,
(32, 16) => RTX_32X16,
(32, 64) => RTX_32X64,
(64, 32) => RTX_64X32,
(4, 16) => RTX_4X16,
(16, 4) => RTX_16X4,
(8, 32) => RTX_8X32,
(32, 8) => RTX_32X8,
(16, 64) => RTX_16X64,
(64, 16) => RTX_64X16,
(4, 32) => RTX_4X32,
(32, 4) => RTX_32X4,
(8, 64) => RTX_8X64,
(64, 8) => RTX_64X8,
(4, 64) => RTX_4X64,
(64, 4) => RTX_64X4,
_ => unreachable!("unsupported chroma/DC tx {w}x{h}"),
}
}
pub(crate) fn reconstruct_chroma(
pred: f32,
lev: &[f32],
qstep: i32,
scan: &[u16],
w: usize,
h: usize,
bd: i32,
) -> Vec<f32> {
let (cw, ch) = (w.min(32), h.min(32));
let txs = dc_tx_scale(w, h);
let tx = dc_tx_index(w, h);
let mut coeff = vec![0i32; cw * ch];
let wscan = scan.get(1).is_some_and(|&v| v as usize == cw);
let sh = (cw as u32).trailing_zeros();
let smask = cw - 1;
for (&l, &rc) in lev.iter().zip(scan.iter()) {
if l != 0.0 {
let rc = rc as usize;
let (row, col) = if wscan {
(rc >> sh, rc & smask)
} else {
(rc & 31, rc >> 5)
};
let li = l as i64;
let mag = (li.abs() * qstep as i64) & 0xffffff;
let rounded = (mag + (1 << 2)) >> 3; let dqmag = (rounded >> txs) as i32;
coeff[col * ch + row] = if li < 0 { -dqmag } else { dqmag };
}
}
let p = pred.fast_round() as i32;
let mut out = vec![0f32; w * h];
crate::av2::itx::inv_txfm_recon_f32(&mut out, &coeff, 0, tx, bd, |_| p);
out
}
pub(crate) fn reconstruct_chroma_pred(
pred: &[i32],
lev: &[f32],
qstep: i32,
scan: &[u16],
w: usize,
h: usize,
bd: i32,
) -> Vec<f32> {
let (cw, ch) = (w.min(32), h.min(32));
let txs = dc_tx_scale(w, h);
let tx = dc_tx_index(w, h);
let mut coeff = vec![0i32; cw * ch];
let wscan = scan.get(1).is_some_and(|&v| v as usize == cw);
let sh = (cw as u32).trailing_zeros();
let smask = cw - 1;
for (&l, &rc) in lev.iter().zip(scan.iter()) {
if l != 0.0 {
let rc = rc as usize;
let (row, col) = if wscan {
(rc >> sh, rc & smask)
} else {
(rc & 31, rc >> 5)
};
let li = l as i64;
let mag = (li.abs() * qstep as i64) & 0xffffff;
let rounded = (mag + (1 << 2)) >> 3;
let dqmag = (rounded >> txs) as i32;
coeff[col * ch + row] = if li < 0 { -dqmag } else { dqmag };
}
}
let mut out = vec![0f32; w * h];
crate::av2::itx::inv_txfm_recon_f32(&mut out, &coeff, 0, tx, bd, |i| pred[i]);
out
}
pub(crate) fn reconstruct_chroma_cfl(
pred: &[i32],
lev: &[f32],
qstep: i32,
scan: &[u16],
w: usize,
h: usize,
bd: i32,
) -> Vec<f32> {
let (cw, ch) = (w.min(32), h.min(32));
let txs = dc_tx_scale(w, h);
let tx = dc_tx_index(w, h);
let mut coeff = vec![0i32; cw * ch];
let wscan = scan.get(1).is_some_and(|&v| v as usize == cw);
let sh = (cw as u32).trailing_zeros();
let smask = cw - 1;
for (&l, &rc) in lev.iter().zip(scan.iter()) {
if l != 0.0 {
let rc = rc as usize;
let (row, col) = if wscan {
(rc >> sh, rc & smask)
} else {
(rc & 31, rc >> 5)
};
let li = l as i64;
let mag = (li.abs() * qstep as i64) & 0xffffff;
let rounded = (mag + (1 << 2)) >> 3;
let dqmag = (rounded >> txs) as i32;
coeff[col * ch + row] = if li < 0 { -dqmag } else { dqmag };
}
}
let mut out = vec![0f32; w * h];
crate::av2::itx::inv_txfm_recon_f32(&mut out, &coeff, 0, tx, bd, |i| pred[i]);
out
}
#[rustfmt::skip]
static AVM_ADST16_INV: [i16; 256] = [
8, 17, 25, 33, 41, 48, 55, 62, 67, 73, 77, 81, 84, 87, 88, 89,
25, 48, 67, 81, 88, 88, 81, 67, 48, 25, 0, -25, -48, -67, -81, -88,
41, 73, 88, 84, 62, 25, -17, -55, -81, -89, -77, -48, -8, 33, 67, 87,
55, 87, 81, 41, -17, -67, -89, -73, -25, 33, 77, 88, 62, 8, -48, -84,
67, 88, 48, -25, -81, -81, -25, 48, 88, 67, 0, -67, -88, -48, 25, 81,
77, 77, 0, -77, -77, 0, 77, 77, 0, -77, -77, 0, 77, 77, 0, -77,
84, 55, -48, -87, -8, 81, 62, -41, -88, -17, 77, 67, -33, -89, -25, 73,
88, 25, -81, -48, 67, 67, -48, -81, 25, 88, 0, -88, -25, 81, 48, -67,
89, -8, -88, 17, 87, -25, -84, 33, 81, -41, -77, 48, 73, -55, -67, 62,
87, -41, -67, 73, 33, -88, 8, 84, -48, -62, 77, 25, -89, 17, 81, -55,
81, -67, -25, 88, -48, -48, 88, -25, -67, 81, 0, -81, 67, 25, -88, 48,
73, -84, 25, 55, -89, 48, 33, -87, 67, 8, -77, 81, -17, -62, 88, -41,
62, -89, 67, -8, -55, 88, -73, 17, 48, -87, 77, -25, -41, 84, -81, 33,
48, -81, 88, -67, 25, 25, -67, 88, -81, 48, 0, -48, 81, -88, 67, -25,
33, -62, 81, -89, 84, -67, 41, -8, -25, 55, -77, 88, -87, 73, -48, 17,
17, -33, 48, -62, 73, -81, 87, -89, 88, -84, 77, -67, 55, -41, 25, -8,
];
#[rustfmt::skip]
static AVM_DCT16_INV: [i16; 256] = [
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
90, 87, 80, 70, 57, 43, 26, 9, -9, -26, -43, -57, -70, -80, -87, -90,
89, 75, 50, 18, -18, -50, -75, -89, -89, -75, -50, -18, 18, 50, 75, 89,
87, 57, 9, -43, -80, -90, -70, -26, 26, 70, 90, 80, 43, -9, -57, -87,
83, 35, -35, -83, -83, -35, 35, 83, 83, 35, -35, -83, -83, -35, 35, 83,
80, 9, -70, -87, -26, 57, 90, 43, -43, -90, -57, 26, 87, 70, -9, -80,
75, -18, -89, -50, 50, 89, 18, -75, -75, 18, 89, 50, -50, -89, -18, 75,
70, -43, -87, 9, 90, 26, -80, -57, 57, 80, -26, -90, -9, 87, 43, -70,
64, -64, -64, 64, 64, -64, -64, 64, 64, -64, -64, 64, 64, -64, -64, 64,
57, -80, -26, 90, -9, -87, 43, 70, -70, -43, 87, 9, -90, 26, 80, -57,
50, -89, 18, 75, -75, -18, 89, -50, -50, 89, -18, -75, 75, 18, -89, 50,
43, -90, 57, 26, -87, 70, 9, -80, 80, -9, -70, 87, -26, -57, 90, -43,
35, -83, 83, -35, -35, 83, -83, 35, 35, -83, 83, -35, -35, 83, -83, 35,
26, -70, 90, -80, 43, 9, -57, 87, -87, 57, -9, -43, 80, -90, 70, -26,
18, -50, 75, -89, 89, -75, 50, -18, -18, 50, -75, 89, -89, 75, -50, 18,
9, -26, 43, -57, 70, -80, 87, -90, 90, -87, 80, -70, 57, -43, 26, -9,
];
#[inline]
fn adst16_pass(
src: &[i32; 256],
dst: &mut [i32; 256],
mat: &[i16; 256],
shift: i32,
cmin: i32,
cmax: i32,
) {
let add = 1i32 << (shift - 1);
for i in 0..16 {
let s = &src[i * 16..i * 16 + 16];
for j in 0..16 {
let mut sum = 0i32;
for k in 0..16 {
sum += s[k] * mat[k * 16 + j] as i32;
}
dst[j * 16 + i] = ((sum + add) >> shift).clamp(cmin, cmax);
}
}
}
pub(crate) fn reconstruct_luma16_adst(
pred: &[f32],
lev: &[f32],
qstep: i32,
scan: &[u16],
row_adst: bool,
col_adst: bool,
bd: i32,
) -> [f32; 256] {
let wscan = scan.get(1).is_some_and(|&v| v as usize == 16);
let dq_min = -(1i32 << (7 + bd));
let dq_max = (1i32 << (7 + bd)) - 1;
let mut block = [0i32; 256];
for (&l, &rc) in lev[..256].iter().zip(scan[..256].iter()) {
if l != 0.0 {
let rc = rc as usize;
let pos = if wscan {
rc
} else {
(rc >> 5) + (rc & 31) * 16
};
let li = l as i64;
let mag = (li.abs() * qstep as i64) & 0xffffff;
let dqmag = ((mag + (1 << 2)) >> 3) as i32;
block[pos] = (if li < 0 { -dqmag } else { dqmag }).clamp(dq_min, dq_max);
}
}
let row_mat = if row_adst {
&AVM_ADST16_INV
} else {
&AVM_DCT16_INV
};
let col_mat = if col_adst {
&AVM_ADST16_INV
} else {
&AVM_DCT16_INV
};
let rng = 1i32 << (bd + 7);
let col_rng = 1i32 << bd;
let mut tmp = [0i32; 256];
let mut res = [0i32; 256];
adst16_pass(&block, &mut tmp, row_mat, 6, -rng, rng - 1);
adst16_pass(&tmp, &mut res, col_mat, 13, -col_rng, col_rng - 1);
let pmax = (1i32 << bd) - 1;
let mut out = [0f32; 256];
for (o, (&p, &r)) in out.iter_mut().zip(pred.iter().zip(res.iter())) {
*o = ((p + 0.5) as i32 + r).clamp(0, pmax) as f32;
}
out
}
pub(crate) fn reconstruct_luma16(
pred: &[f32],
lev: &[f32],
qstep: i32,
scan: &[u16],
bd: i32,
) -> [f32; 256] {
let wscan = scan.get(1).is_some_and(|&v| v as usize == 16);
let mut coeff = [0i32; 256];
for (&l, &rc) in lev[..256].iter().zip(scan[..256].iter()) {
if l != 0.0 {
let rc = rc as usize;
let (row, col) = if wscan {
(rc >> 4, rc & 15)
} else {
(rc & 31, rc >> 5)
};
let li = l as i64;
let mag = (li.abs() * qstep as i64) & 0xffffff;
let rounded = (mag + (1 << 2)) >> 3; let dqmag = rounded as i32; coeff[col * 16 + row] = if li < 0 { -dqmag } else { dqmag };
}
}
let mut out = [0f32; 256];
crate::av2::itx::inv_txfm_recon_f32(
&mut out,
&coeff,
0,
crate::av2::itx::tx_size::TX_16X16,
bd,
|i| (pred[i] + 0.5) as i32,
);
out
}
pub(crate) fn reconstruct_luma_64x16(
pred: &[f32],
lev: &[f32],
qstep: i32,
scan: &[u16],
bd: i32,
) -> [f32; 1024] {
let h = 16usize;
let mut coeff = [0i32; 512];
for (&l, &rc) in lev[..512].iter().zip(scan[..512].iter()) {
if l != 0.0 {
let hf = rc as usize >> 5; let wf = rc as usize & 31; let li = l as i64;
let mag = (li.abs() * qstep as i64) & 0xffffff;
let rounded = (mag + (1 << 2)) >> 3;
let dqmag = (rounded >> 1) as i32; coeff[wf * h + hf] = if li < 0 { -dqmag } else { dqmag };
}
}
let mut out = [0f32; 1024];
crate::av2::itx::inv_txfm_recon_f32(
&mut out,
&coeff,
0,
crate::av2::itx::tx_size::RTX_64X16,
bd,
|i| (pred[i] + 0.5) as i32,
);
out
}
pub(crate) fn reconstruct_luma_16x64(
pred: &[f32],
lev: &[f32],
qstep: i32,
scan: &[u16],
bd: i32,
) -> [f32; 1024] {
let ch = 32usize;
let mut coeff = [0i32; 512];
for (&l, &rc) in lev[..512].iter().zip(scan[..512].iter()) {
if l != 0.0 {
let (row, col) = (rc as usize >> 4, rc as usize & 15);
let li = l as i64;
let mag = (li.abs() * qstep as i64) & 0xffffff;
let rounded = (mag + (1 << 2)) >> 3;
let dqmag = (rounded >> 1) as i32; coeff[col * ch + row] = if li < 0 { -dqmag } else { dqmag };
}
}
let mut out = [0f32; 1024];
crate::av2::itx::inv_txfm_recon_f32(
&mut out,
&coeff,
0,
crate::av2::itx::tx_size::RTX_16X64,
bd,
|i| (pred[i] + 0.5) as i32,
);
out
}
pub(crate) fn reconstruct_luma(
pred: &[f32],
lev: &[f32],
qstep: i32,
scan: &[u16],
bd: i32,
) -> [f32; 1024] {
let mut coeff = [0i32; 1024];
for (&l, &rc) in lev[..1024].iter().zip(scan[..1024].iter()) {
if l != 0.0 {
let (col, row) = (rc as usize >> 5, rc as usize & 31);
let li = l as i64;
let mag = (li.abs() * qstep as i64) & 0xffffff;
let rounded = (mag + (1 << 2)) >> 3; let dqmag = (rounded >> 1) as i32; coeff[col * 32 + row] = if li < 0 { -dqmag } else { dqmag };
}
}
let mut out = [0f32; 1024];
crate::av2::itx::inv_txfm_recon_f32(
&mut out,
&coeff,
0,
crate::av2::itx::tx_size::TX_32X32,
bd,
|i| (pred[i] + 0.5) as i32,
);
out
}