pub(crate) static DST4: [[i32; 4]; 4] = [
[29, 55, 74, 84],
[74, 74, 0, -74],
[84, -29, -74, 55],
[55, -84, 74, -29],
];
static DEQUANT_SCALE: [i64; 6] = [40, 45, 51, 57, 64, 72];
pub(crate) trait Coeff: Copy + Default + Send + Sync + 'static {
fn from_i32(v: i32) -> Self;
fn to_i32(self) -> i32;
}
impl Coeff for i32 {
#[inline(always)]
fn from_i32(v: i32) -> Self {
v
}
#[inline(always)]
fn to_i32(self) -> i32 {
self
}
}
impl Coeff for i16 {
#[inline(always)]
fn from_i32(v: i32) -> Self {
v as i16
}
#[inline(always)]
fn to_i32(self) -> i32 {
self as i32
}
}
#[inline(always)]
fn idct_odd_8(c: [i32; 8]) -> [i32; 4] {
[
89 * c[1] + 75 * c[3] + 50 * c[5] + 18 * c[7],
75 * c[1] + -18 * c[3] + -89 * c[5] + -50 * c[7],
50 * c[1] + -89 * c[3] + 18 * c[5] + 75 * c[7],
18 * c[1] + -50 * c[3] + 75 * c[5] + -89 * c[7],
]
}
#[inline(always)]
fn idct_odd_16(c: [i32; 16]) -> [i32; 8] {
[
90 * c[1]
+ 87 * c[3]
+ 80 * c[5]
+ 70 * c[7]
+ 57 * c[9]
+ 43 * c[11]
+ 25 * c[13]
+ 9 * c[15],
87 * c[1]
+ 57 * c[3]
+ 9 * c[5]
+ -43 * c[7]
+ -80 * c[9]
+ -90 * c[11]
+ -70 * c[13]
+ -25 * c[15],
80 * c[1]
+ 9 * c[3]
+ -70 * c[5]
+ -87 * c[7]
+ -25 * c[9]
+ 57 * c[11]
+ 90 * c[13]
+ 43 * c[15],
70 * c[1]
+ -43 * c[3]
+ -87 * c[5]
+ 9 * c[7]
+ 90 * c[9]
+ 25 * c[11]
+ -80 * c[13]
+ -57 * c[15],
57 * c[1]
+ -80 * c[3]
+ -25 * c[5]
+ 90 * c[7]
+ -9 * c[9]
+ -87 * c[11]
+ 43 * c[13]
+ 70 * c[15],
43 * c[1]
+ -90 * c[3]
+ 57 * c[5]
+ 25 * c[7]
+ -87 * c[9]
+ 70 * c[11]
+ 9 * c[13]
+ -80 * c[15],
25 * c[1]
+ -70 * c[3]
+ 90 * c[5]
+ -80 * c[7]
+ 43 * c[9]
+ 9 * c[11]
+ -57 * c[13]
+ 87 * c[15],
9 * c[1]
+ -25 * c[3]
+ 43 * c[5]
+ -57 * c[7]
+ 70 * c[9]
+ -80 * c[11]
+ 87 * c[13]
+ -90 * c[15],
]
}
#[inline(always)]
fn idct_odd_32(c: [i32; 32]) -> [i32; 16] {
[
90 * c[1]
+ 90 * c[3]
+ 88 * c[5]
+ 85 * c[7]
+ 82 * c[9]
+ 78 * c[11]
+ 73 * c[13]
+ 67 * c[15]
+ 61 * c[17]
+ 54 * c[19]
+ 46 * c[21]
+ 38 * c[23]
+ 31 * c[25]
+ 22 * c[27]
+ 13 * c[29]
+ 4 * c[31],
90 * c[1]
+ 82 * c[3]
+ 67 * c[5]
+ 46 * c[7]
+ 22 * c[9]
+ -4 * c[11]
+ -31 * c[13]
+ -54 * c[15]
+ -73 * c[17]
+ -85 * c[19]
+ -90 * c[21]
+ -88 * c[23]
+ -78 * c[25]
+ -61 * c[27]
+ -38 * c[29]
+ -13 * c[31],
88 * c[1]
+ 67 * c[3]
+ 31 * c[5]
+ -13 * c[7]
+ -54 * c[9]
+ -82 * c[11]
+ -90 * c[13]
+ -78 * c[15]
+ -46 * c[17]
+ -4 * c[19]
+ 38 * c[21]
+ 73 * c[23]
+ 90 * c[25]
+ 85 * c[27]
+ 61 * c[29]
+ 22 * c[31],
85 * c[1]
+ 46 * c[3]
+ -13 * c[5]
+ -67 * c[7]
+ -90 * c[9]
+ -73 * c[11]
+ -22 * c[13]
+ 38 * c[15]
+ 82 * c[17]
+ 88 * c[19]
+ 54 * c[21]
+ -4 * c[23]
+ -61 * c[25]
+ -90 * c[27]
+ -78 * c[29]
+ -31 * c[31],
82 * c[1]
+ 22 * c[3]
+ -54 * c[5]
+ -90 * c[7]
+ -61 * c[9]
+ 13 * c[11]
+ 78 * c[13]
+ 85 * c[15]
+ 31 * c[17]
+ -46 * c[19]
+ -90 * c[21]
+ -67 * c[23]
+ 4 * c[25]
+ 73 * c[27]
+ 88 * c[29]
+ 38 * c[31],
78 * c[1]
+ -4 * c[3]
+ -82 * c[5]
+ -73 * c[7]
+ 13 * c[9]
+ 85 * c[11]
+ 67 * c[13]
+ -22 * c[15]
+ -88 * c[17]
+ -61 * c[19]
+ 31 * c[21]
+ 90 * c[23]
+ 54 * c[25]
+ -38 * c[27]
+ -90 * c[29]
+ -46 * c[31],
73 * c[1]
+ -31 * c[3]
+ -90 * c[5]
+ -22 * c[7]
+ 78 * c[9]
+ 67 * c[11]
+ -38 * c[13]
+ -90 * c[15]
+ -13 * c[17]
+ 82 * c[19]
+ 61 * c[21]
+ -46 * c[23]
+ -88 * c[25]
+ -4 * c[27]
+ 85 * c[29]
+ 54 * c[31],
67 * c[1]
+ -54 * c[3]
+ -78 * c[5]
+ 38 * c[7]
+ 85 * c[9]
+ -22 * c[11]
+ -90 * c[13]
+ 4 * c[15]
+ 90 * c[17]
+ 13 * c[19]
+ -88 * c[21]
+ -31 * c[23]
+ 82 * c[25]
+ 46 * c[27]
+ -73 * c[29]
+ -61 * c[31],
61 * c[1]
+ -73 * c[3]
+ -46 * c[5]
+ 82 * c[7]
+ 31 * c[9]
+ -88 * c[11]
+ -13 * c[13]
+ 90 * c[15]
+ -4 * c[17]
+ -90 * c[19]
+ 22 * c[21]
+ 85 * c[23]
+ -38 * c[25]
+ -78 * c[27]
+ 54 * c[29]
+ 67 * c[31],
54 * c[1]
+ -85 * c[3]
+ -4 * c[5]
+ 88 * c[7]
+ -46 * c[9]
+ -61 * c[11]
+ 82 * c[13]
+ 13 * c[15]
+ -90 * c[17]
+ 38 * c[19]
+ 67 * c[21]
+ -78 * c[23]
+ -22 * c[25]
+ 90 * c[27]
+ -31 * c[29]
+ -73 * c[31],
46 * c[1]
+ -90 * c[3]
+ 38 * c[5]
+ 54 * c[7]
+ -90 * c[9]
+ 31 * c[11]
+ 61 * c[13]
+ -88 * c[15]
+ 22 * c[17]
+ 67 * c[19]
+ -85 * c[21]
+ 13 * c[23]
+ 73 * c[25]
+ -82 * c[27]
+ 4 * c[29]
+ 78 * c[31],
38 * c[1]
+ -88 * c[3]
+ 73 * c[5]
+ -4 * c[7]
+ -67 * c[9]
+ 90 * c[11]
+ -46 * c[13]
+ -31 * c[15]
+ 85 * c[17]
+ -78 * c[19]
+ 13 * c[21]
+ 61 * c[23]
+ -90 * c[25]
+ 54 * c[27]
+ 22 * c[29]
+ -82 * c[31],
31 * c[1]
+ -78 * c[3]
+ 90 * c[5]
+ -61 * c[7]
+ 4 * c[9]
+ 54 * c[11]
+ -88 * c[13]
+ 82 * c[15]
+ -38 * c[17]
+ -22 * c[19]
+ 73 * c[21]
+ -90 * c[23]
+ 67 * c[25]
+ -13 * c[27]
+ -46 * c[29]
+ 85 * c[31],
22 * c[1]
+ -61 * c[3]
+ 85 * c[5]
+ -90 * c[7]
+ 73 * c[9]
+ -38 * c[11]
+ -4 * c[13]
+ 46 * c[15]
+ -78 * c[17]
+ 90 * c[19]
+ -82 * c[21]
+ 54 * c[23]
+ -13 * c[25]
+ -31 * c[27]
+ 67 * c[29]
+ -88 * c[31],
13 * c[1]
+ -38 * c[3]
+ 61 * c[5]
+ -78 * c[7]
+ 88 * c[9]
+ -90 * c[11]
+ 85 * c[13]
+ -73 * c[15]
+ 54 * c[17]
+ -31 * c[19]
+ 4 * c[21]
+ 22 * c[23]
+ -46 * c[25]
+ 67 * c[27]
+ -82 * c[29]
+ 90 * c[31],
4 * c[1]
+ -13 * c[3]
+ 22 * c[5]
+ -31 * c[7]
+ 38 * c[9]
+ -46 * c[11]
+ 54 * c[13]
+ -61 * c[15]
+ 67 * c[17]
+ -73 * c[19]
+ 78 * c[21]
+ -82 * c[23]
+ 85 * c[25]
+ -88 * c[27]
+ 90 * c[29]
+ -90 * c[31],
]
}
#[inline(always)]
fn idct_raw_4(c: [i32; 4]) -> [i32; 4] {
let e0 = 64 * (c[0] + c[2]);
let e1 = 64 * (c[0] - c[2]);
let o0 = 83 * c[1] + 36 * c[3];
let o1 = 36 * c[1] - 83 * c[3];
[e0 + o0, e1 + o1, e1 - o1, e0 - o0]
}
#[inline(always)]
fn idct_raw_8(c: [i32; 8]) -> [i32; 8] {
let ee = idct_raw_4([c[0], c[2], c[4], c[6]]);
let oo = idct_odd_8(c);
let mut out = [0i32; 8];
for (k, (&ee, &oo)) in ee.iter().zip(oo.iter()).enumerate() {
out[k] = ee + oo;
out[7 - k] = ee - oo;
}
out
}
#[inline(always)]
fn idct_raw_16(c: [i32; 16]) -> [i32; 16] {
let ee = idct_raw_8(std::array::from_fn(|j| c[2 * j]));
let oo = idct_odd_16(c);
let mut out = [0i32; 16];
for (k, (&ee, &oo)) in ee.iter().zip(oo.iter()).enumerate() {
out[k] = ee + oo;
out[15 - k] = ee - oo;
}
out
}
#[inline(always)]
fn idct_raw_32(c: [i32; 32]) -> [i32; 32] {
let ee = idct_raw_16(std::array::from_fn(|j| c[2 * j]));
let oo = idct_odd_32(c);
let mut out = [0i32; 32];
for (k, (&ee, &oo)) in ee.iter().zip(oo.iter()).enumerate() {
out[k] = ee + oo;
out[31 - k] = ee - oo;
}
out
}
#[inline(always)]
fn idct_raw<const N: usize>(c: [i32; N]) -> [i32; N] {
debug_assert!(N == 4 || N == 8 || N == 16 || N == 32);
match N {
4 => {
let src = [c[0], c[1], c[2], c[3]];
let r = idct_raw_4(src);
std::array::from_fn(|i| r[i])
}
8 => {
let src = [c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]];
let r = idct_raw_8(src);
std::array::from_fn(|i| r[i])
}
16 => {
let src = [
c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], c[10], c[11], c[12],
c[13], c[14], c[15],
];
let r = idct_raw_16(src);
std::array::from_fn(|i| r[i])
}
32 => {
let src = [
c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], c[10], c[11], c[12],
c[13], c[14], c[15], c[16], c[17], c[18], c[19], c[20], c[21], c[22], c[23], c[24],
c[25], c[26], c[27], c[28], c[29], c[30], c[31],
];
let r = idct_raw_32(src);
std::array::from_fn(|i| r[i])
}
_ => unreachable!(),
}
}
#[inline]
fn inv_dct_n_into<const N: usize, S: Coeff>(coeff: &[S], bit_depth: u8, nx: usize, out: &mut [S]) {
debug_assert!(N == 4 || N == 8 || N == 16 || N == 32);
debug_assert!(coeff.len() >= N * N);
debug_assert!(out.len() >= N * N);
let shift1 = 7i32;
let add1 = 1i32 << (shift1 - 1);
let shift2 = 20 - bit_depth as i32;
let add2 = 1i32 << (shift2 - 1);
let mut tmp = [0i16; 32 * 32];
let nx = nx.min(N);
for c in 0..nx {
let col: [i32; N] = std::array::from_fn(|k| coeff[k * N + c].to_i32());
let raw = idct_raw::<N>(col);
for (m, &raw) in raw.iter().enumerate() {
tmp[m * N + c] = ((raw + add1) >> shift1).clamp(-32768, 32767) as i16;
}
}
for (tmp_row, out_row) in tmp
.as_chunks::<N>()
.0
.iter()
.zip(out.as_chunks_mut::<N>().0.iter_mut())
{
let row: [i32; N] = std::array::from_fn(|k| tmp_row[k] as i32);
let raw = idct_raw::<N>(row);
for (dst, &raw) in out_row.iter_mut().zip(raw.iter()) {
*dst = S::from_i32((raw + add2) >> shift2);
}
}
}
#[inline]
fn inv_transform_n_into<const N: usize, S: Coeff>(
coeff: &[S],
t: &[[i32; N]; N],
bit_depth: u8,
nx: usize,
out: &mut [S],
) {
let bd = bit_depth as i32;
let shift1 = 7i32;
let add1 = 1i32 << (shift1 - 1);
let shift2 = 20 - bd;
let add2 = 1i32 << (shift2 - 1);
let mut tmp = [0i16; 32 * 32];
let mut acc = [0i32; N];
let nx = nx.min(N);
for c in 0..nx {
acc[..N].fill(0);
for k in 0..N {
let ck = coeff[k * N + c].to_i32();
if ck == 0 {
continue; }
let trow = &t[k];
for (acc, &tm) in acc[..N].iter_mut().zip(trow.iter()) {
*acc += tm * ck;
}
}
for (m, &acc) in acc[..N].iter().enumerate() {
tmp[m * N + c] = ((acc + add1) >> shift1).clamp(-32768, 32767) as i16;
}
}
for (rowv, out_row) in tmp
.as_chunks::<N>()
.0
.iter()
.zip(out.as_chunks_mut::<N>().0.iter_mut())
{
acc[..N].fill(0);
for (&rk, trow) in rowv.iter().zip(t.iter()) {
if rk == 0 {
continue;
}
let rk = rk as i32;
for (acc, &tm) in acc[..N].iter_mut().zip(trow.iter()) {
*acc += tm * rk;
}
}
for (dst, &acc) in out_row.iter_mut().zip(acc[..N].iter()) {
*dst = S::from_i32((acc + add2) >> shift2);
}
}
}
pub(crate) fn dequantize_into<S: Coeff>(
levels: &[i32],
n: usize,
qp: u8,
bit_depth: u8,
out: &mut [S],
) {
let log2n = (n as u32).trailing_zeros() as i64;
let bd = bit_depth as i64;
let bd_shift = (bd + log2n - 5).max(1);
let add = 1i64 << (bd_shift - 1);
let qp_bd_offset = 6 * (bd - 8);
let qp_scaled = (qp as i64) + qp_bd_offset;
let scale = DEQUANT_SCALE[(qp_scaled % 6) as usize];
let per = 1i64 << (qp_scaled / 6);
let factor = scale * per * 16;
for (o, &l) in out[..n * n].iter_mut().zip(levels.iter()) {
*o = S::from_i32(((l as i64 * factor + add) >> bd_shift).clamp(-32768, 32767) as i32);
}
}
type InvTransformFn = fn(&[i32], usize, u8, usize, &mut [i32]);
type InvTransform4Fn = fn(&[i32], u8, &mut [i32]);
type InvTransformFn16 = fn(&[i16], usize, u8, usize, &mut [i16]);
type InvTransform4Fn16 = fn(&[i16], u8, &mut [i16]);
static INV_TRANSFORM: std::sync::OnceLock<InvTransformFn> = std::sync::OnceLock::new();
static INV_TRANSFORM_DST4: std::sync::OnceLock<InvTransform4Fn> = std::sync::OnceLock::new();
static INV_TRANSFORM16: std::sync::OnceLock<InvTransformFn16> = std::sync::OnceLock::new();
static INV_TRANSFORM_DST4_16: std::sync::OnceLock<InvTransform4Fn16> = std::sync::OnceLock::new();
#[inline]
fn resolve_inv_transform() -> InvTransformFn {
*INV_TRANSFORM.get_or_init(|| {
let mut _f: InvTransformFn = inv_transform_into_scalar;
#[cfg(all(feature = "neon", target_arch = "aarch64"))]
{
_f = crate::neon::inv_transform_into_neon;
}
#[cfg(all(feature = "sse", any(target_arch = "x86", target_arch = "x86_64")))]
{
if std::is_x86_feature_detected!("sse4.1") {
_f = crate::sse::inv_transform_into_sse41;
}
}
_f
})
}
#[inline]
fn resolve_inv_transform_dst4() -> InvTransform4Fn {
*INV_TRANSFORM_DST4.get_or_init(|| {
let mut _f: InvTransform4Fn = inv_transform_dst_into_scalar;
#[cfg(all(feature = "neon", target_arch = "aarch64"))]
{
_f = crate::neon::inv_transform_dst_into_neon;
}
#[cfg(all(feature = "sse", any(target_arch = "x86", target_arch = "x86_64")))]
{
if std::is_x86_feature_detected!("sse4.1") {
_f = crate::sse::inv_transform_dst_into_sse41;
}
}
_f
})
}
#[inline]
fn resolve_inv_transform16() -> InvTransformFn16 {
*INV_TRANSFORM16.get_or_init(|| {
let mut _f: InvTransformFn16 = inv_transform_into_scalar16;
#[cfg(all(feature = "neon", target_arch = "aarch64"))]
{
_f = crate::neon::inv_transform_into_neon16;
}
#[cfg(all(feature = "sse", any(target_arch = "x86", target_arch = "x86_64")))]
{
if std::is_x86_feature_detected!("sse4.1") {
_f = crate::sse::inv_transform_into_sse41_16;
}
}
_f
})
}
#[inline]
fn resolve_inv_transform_dst4_16() -> InvTransform4Fn16 {
*INV_TRANSFORM_DST4_16.get_or_init(|| {
let mut _f: InvTransform4Fn16 = inv_transform_dst_into_scalar16;
#[cfg(all(feature = "neon", target_arch = "aarch64"))]
{
_f = crate::neon::inv_transform_dst_into_neon16;
}
#[cfg(all(feature = "sse", any(target_arch = "x86", target_arch = "x86_64")))]
{
if std::is_x86_feature_detected!("sse4.1") {
_f = crate::sse::inv_transform_dst_into_sse41_16;
}
}
_f
})
}
pub(crate) fn inv_transform_into_scalar(
coeff: &[i32],
n: usize,
bit_depth: u8,
nx: usize,
out: &mut [i32],
) {
match n {
4 => inv_dct_n_into::<4, i32>(coeff, bit_depth, nx, out),
8 => inv_dct_n_into::<8, i32>(coeff, bit_depth, nx, out),
16 => inv_dct_n_into::<16, i32>(coeff, bit_depth, nx, out),
32 => inv_dct_n_into::<32, i32>(coeff, bit_depth, nx, out),
_ => panic!("unsupported transform size {n}"),
}
}
pub(crate) fn inv_transform_dst_into_scalar(coeff: &[i32], bit_depth: u8, out: &mut [i32]) {
inv_transform_n_into::<4, i32>(coeff, &DST4, bit_depth, 4, out);
}
pub(crate) fn inv_transform_into_scalar16(
coeff: &[i16],
n: usize,
bit_depth: u8,
nx: usize,
out: &mut [i16],
) {
match n {
4 => inv_dct_n_into::<4, i16>(coeff, bit_depth, nx, out),
8 => inv_dct_n_into::<8, i16>(coeff, bit_depth, nx, out),
16 => inv_dct_n_into::<16, i16>(coeff, bit_depth, nx, out),
32 => inv_dct_n_into::<32, i16>(coeff, bit_depth, nx, out),
_ => panic!("unsupported transform size {n}"),
}
}
pub(crate) fn inv_transform_dst_into_scalar16(coeff: &[i16], bit_depth: u8, out: &mut [i16]) {
inv_transform_n_into::<4, i16>(coeff, &DST4, bit_depth, 4, out);
}
pub(crate) fn inv_transform_into(
coeff: &[i32],
n: usize,
bit_depth: u8,
nx: usize,
out: &mut [i32],
) {
resolve_inv_transform()(coeff, n, bit_depth, nx, out);
}
pub(crate) fn inv_transform_dst_into(coeff: &[i32], bit_depth: u8, out: &mut [i32]) {
resolve_inv_transform_dst4()(coeff, bit_depth, out);
}
pub(crate) fn inv_transform_into16(
coeff: &[i16],
n: usize,
bit_depth: u8,
nx: usize,
out: &mut [i16],
) {
resolve_inv_transform16()(coeff, n, bit_depth, nx, out);
}
pub(crate) fn inv_transform_dst_into16(coeff: &[i16], bit_depth: u8, out: &mut [i16]) {
resolve_inv_transform_dst4_16()(coeff, bit_depth, out);
}