#![allow(clippy::approx_constant)]
#![allow(clippy::excessive_precision)]
#[rustfmt::skip]
const AFV4X4_BASIS_TRANSPOSE: [[f32; 16]; 16] = [
[0.2500000000000000, 0.8769029297991420, 0.0000000000000000, 0.0000000000000000,
0.0000000000000000, -0.4105377591765233, 0.0000000000000000, 0.0000000000000000,
0.0000000000000000, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000,
0.0000000000000000, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000],
[0.2500000000000000, 0.2206518106944235, 0.0000000000000000, 0.0000000000000000,
-0.7071067811865474, 0.6235485373547691, 0.0000000000000000, 0.0000000000000000,
0.0000000000000000, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000,
0.0000000000000000, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000],
[0.2500000000000000, -0.1014005039375376, 0.4067007583026075, -0.2125574805828875,
0.0000000000000000, -0.0643507165794627, -0.4517556589999482, -0.3046847507248690,
0.3017929516615495, 0.4082482904638627, 0.1747866975480809, -0.2110560104933578,
-0.1426608480880726, -0.1381354035075859, -0.1743760259965107, 0.1135498731499434],
[0.2500000000000000, -0.1014005039375375, 0.4444481661973445, 0.3085497062849767,
0.0000000000000000, -0.0643507165794627, 0.1585450355184006, 0.5112616136591823,
0.2579236279634118, 0.0000000000000000, 0.0812611176717539, 0.1856718091610980,
-0.3416446842253372, 0.3302282550303788, 0.0702790691196284, -0.0741750459581035],
[0.2500000000000000, 0.2206518106944236, 0.0000000000000000, 0.0000000000000000,
0.7071067811865476, 0.6235485373547694, 0.0000000000000000, 0.0000000000000000,
0.0000000000000000, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000,
0.0000000000000000, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000],
[0.2500000000000000, -0.1014005039375378, 0.0000000000000000, 0.4706702258572536,
0.0000000000000000, -0.0643507165794628, -0.0403851516082220, 0.0000000000000000,
0.1627234014286620, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000,
0.7367497537172237, 0.0875511500058708, -0.2921026642334881, 0.1940289303259434],
[0.2500000000000000, -0.1014005039375377, 0.1957439937204294, -0.1621205195722993,
0.0000000000000000, -0.0643507165794628, 0.0074182263792424, -0.2904801297289980,
0.0952002265347504, 0.0000000000000000, -0.3675398009862027, 0.4921585901373873,
0.2462710772207515, -0.0794670660590957, 0.3623817333531167, -0.4351904965232280],
[0.2500000000000000, -0.1014005039375376, 0.2929100136981264, 0.0000000000000000,
0.0000000000000000, -0.0643507165794627, 0.3935103426921017, -0.0657870154914280,
0.0000000000000000, -0.4082482904638628, -0.3078822139579090, -0.3852501370925192,
-0.0857401903551931, -0.4613374887461511, 0.0000000000000000, 0.2191868483885747],
[0.2500000000000000, -0.1014005039375376, -0.4067007583026072, -0.2125574805828705,
0.0000000000000000, -0.0643507165794627, -0.4517556589999464, 0.3046847507248840,
0.3017929516615503, -0.4082482904638635, -0.1747866975480813, 0.2110560104933581,
-0.1426608480880734, -0.1381354035075829, -0.1743760259965108, 0.1135498731499426],
[0.2500000000000000, -0.1014005039375377, -0.1957439937204287, -0.1621205195722833,
0.0000000000000000, -0.0643507165794628, 0.0074182263792444, 0.2904801297290076,
0.0952002265347505, 0.0000000000000000, 0.3675398009862011, -0.4921585901373891,
0.2462710772207514, -0.0794670660591026, 0.3623817333531165, -0.4351904965232251],
[0.2500000000000000, -0.1014005039375375, 0.0000000000000000, -0.4706702258572528,
0.0000000000000000, -0.0643507165794627, 0.1107416575309343, 0.0000000000000000,
-0.1627234014286617, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000,
0.1488339922711357, 0.4972464710953509, 0.2921026642334879, 0.5550443808910661],
[0.2500000000000000, -0.1014005039375377, 0.1137907446044809, -0.1464291867126764,
0.0000000000000000, -0.0643507165794628, 0.0829816309488205, -0.2388977352334460,
-0.3531238544981630, -0.4082482904638630, 0.4826689115059883, 0.1741941265991622,
-0.0476868035022925, 0.1253805944856366, -0.4326608024727445, -0.2546827712406646],
[0.2500000000000000, -0.1014005039375377, -0.4444481661973438, 0.3085497062849487,
0.0000000000000000, -0.0643507165794628, 0.1585450355183970, -0.5112616136592012,
0.2579236279634129, 0.0000000000000000, -0.0812611176717504, -0.1856718091610990,
-0.3416446842253373, 0.3302282550303805, 0.0702790691196282, -0.0741750459581023],
[0.2500000000000000, -0.1014005039375376, -0.2929100136981264, 0.0000000000000000,
0.0000000000000000, -0.0643507165794627, 0.3935103426921022, 0.0657870154914254,
0.0000000000000000, 0.4082482904638634, 0.3078822139579031, 0.3852501370925211,
-0.0857401903551927, -0.4613374887461554, 0.0000000000000000, 0.2191868483885793],
[0.2500000000000000, -0.1014005039375377, -0.1137907446044814, -0.1464291867126654,
0.0000000000000000, -0.0643507165794628, 0.0829816309488214, 0.2388977352334547,
-0.3531238544981624, 0.4082482904638636, -0.4826689115059846, -0.1741941265991693,
-0.0476868035022926, 0.1253805944856419, -0.4326608024727457, -0.2546827712406567],
[0.2500000000000000, -0.1014005039375374, 0.0000000000000000, 0.4251149611657548,
0.0000000000000000, -0.0643507165794626, -0.4517556589999480, 0.0000000000000000,
-0.6035859033230976, 0.0000000000000000, 0.0000000000000000, 0.0000000000000000,
-0.1426608480880724, -0.1381354035075845, 0.3487520519930227, 0.1135498731499429],
];
#[inline(always)]
fn afv_dct_4x4(pixels: &[f32; 16], coeffs: &mut [f32; 16]) {
*coeffs = [0.0; 16];
for i in 0..16 {
let p = pixels[i];
let row = &AFV4X4_BASIS_TRANSPOSE[i];
for j in 0..16 {
coeffs[j] += p * row[j];
}
}
}
#[inline(always)]
fn afv_idct_4x4(coeffs: &[f32; 16], pixels: &mut [f32; 16]) {
for i in 0..16 {
let mut sum = 0.0f32;
for j in 0..16 {
sum += coeffs[j] * AFV4X4_BASIS_TRANSPOSE[i][j];
}
pixels[i] = sum;
}
}
#[inline(always)]
fn dct_4x4_simple(pixels: &[f32; 16], coeffs: &mut [f32; 16]) {
super::dct::dct_4x4(pixels, coeffs);
}
#[inline(always)]
fn dct_4x8_simple(pixels: &[f32; 32], coeffs: &mut [f32; 32]) {
super::dct::dct_4x8(pixels, coeffs);
}
#[allow(dead_code)]
pub const RAW_STRATEGY_AFV0: u8 = 12;
#[allow(dead_code)]
pub const RAW_STRATEGY_AFV1: u8 = 13;
#[allow(dead_code)]
pub const RAW_STRATEGY_AFV2: u8 = 14;
#[allow(dead_code)]
pub const RAW_STRATEGY_AFV3: u8 = 15;
#[allow(dead_code)]
pub const STRATEGY_CODE_AFV0: u8 = 14;
#[allow(dead_code)]
pub const STRATEGY_CODE_AFV1: u8 = 15;
#[allow(dead_code)]
pub const STRATEGY_CODE_AFV2: u8 = 16;
#[allow(dead_code)]
pub const STRATEGY_CODE_AFV3: u8 = 17;
#[allow(dead_code)]
pub fn afv_kind_from_strategy(raw_strategy: u8) -> Option<usize> {
match raw_strategy {
RAW_STRATEGY_AFV0 => Some(0),
RAW_STRATEGY_AFV1 => Some(1),
RAW_STRATEGY_AFV2 => Some(2),
RAW_STRATEGY_AFV3 => Some(3),
_ => None,
}
}
#[inline(always)]
pub fn afv_transform_from_pixels(pixels: &[f32], afv_kind: usize, coefficients: &mut [f32; 64]) {
let afv_x = afv_kind & 1;
let afv_y = afv_kind / 2;
let mut block_4x4 = [0.0f32; 16];
for iy in 0..4 {
for ix in 0..4 {
let src_y = if afv_y == 1 { 3 - iy } else { iy };
let src_x = if afv_x == 1 { 3 - ix } else { ix };
block_4x4[src_y * 4 + src_x] = pixels[(iy + 4 * afv_y) * 8 + ix + 4 * afv_x];
}
}
let mut afv_coeffs = [0.0f32; 16];
afv_dct_4x4(&block_4x4, &mut afv_coeffs);
for iy in 0..4 {
for ix in 0..4 {
coefficients[iy * 2 * 8 + ix * 2] = afv_coeffs[iy * 4 + ix];
}
}
let mut dct4_pixels = [0.0f32; 16];
for iy in 0..4 {
for ix in 0..4 {
dct4_pixels[iy * 4 + ix] = pixels[(iy + afv_y * 4) * 8 + ix + (1 - afv_x) * 4];
}
}
let mut dct4_coeffs = [0.0f32; 16];
dct_4x4_simple(&dct4_pixels, &mut dct4_coeffs);
for iy in 0..4 {
for ix in 0..4 {
coefficients[iy * 2 * 8 + ix * 2 + 1] = dct4_coeffs[iy * 4 + ix];
}
}
let mut dct4x8_pixels = [0.0f32; 32];
for iy in 0..4 {
for ix in 0..8 {
dct4x8_pixels[iy * 8 + ix] = pixels[(iy + (1 - afv_y) * 4) * 8 + ix];
}
}
let mut dct4x8_coeffs = [0.0f32; 32];
dct_4x8_simple(&dct4x8_pixels, &mut dct4x8_coeffs);
for iy in 0..4 {
for ix in 0..8 {
coefficients[(1 + iy * 2) * 8 + ix] = dct4x8_coeffs[iy * 8 + ix];
}
}
let block00 = coefficients[0] * 0.25;
let block01 = coefficients[1];
let block10 = coefficients[8];
coefficients[0] = (block00 + block01 + 2.0 * block10) * 0.25;
coefficients[1] = (block00 - block01) * 0.5;
coefficients[8] = (block00 + block01 - 2.0 * block10) * 0.25;
}
pub fn dc_from_afv(coefficients: &[f32; 64]) -> f32 {
coefficients[0]
}
#[inline(always)]
pub fn inverse_afv_transform(coefficients: &[f32; 64], afv_kind: usize, pixels: &mut [f32; 64]) {
let afv_x = afv_kind & 1;
let afv_y = afv_kind / 2;
let block00 = coefficients[0];
let block01 = coefficients[1];
let block10 = coefficients[8];
let dcs: [f32; 3] = [
(block00 + block10 + block01) * 4.0, block00 + block10 - block01, block00 - block10, ];
let mut afv_coeff = [0.0f32; 16];
for iy in 0..4 {
for ix in 0..4 {
afv_coeff[iy * 4 + ix] = if ix == 0 && iy == 0 {
dcs[0]
} else {
coefficients[iy * 2 * 8 + ix * 2]
};
}
}
let mut afv_pixels = [0.0f32; 16];
afv_idct_4x4(&afv_coeff, &mut afv_pixels);
for iy in 0..4 {
let block_y = if afv_y == 1 { 3 - iy } else { iy };
for ix in 0..4 {
let block_x = if afv_x == 1 { 3 - ix } else { ix };
pixels[(iy + afv_y * 4) * 8 + afv_x * 4 + ix] = afv_pixels[block_y * 4 + block_x];
}
}
let mut dct4_coeff = [0.0f32; 16];
for iy in 0..4 {
for ix in 0..4 {
dct4_coeff[iy * 4 + ix] = if ix == 0 && iy == 0 {
dcs[1]
} else {
coefficients[iy * 2 * 8 + ix * 2 + 1]
};
}
}
let mut dct4_pixels = [0.0f32; 16];
super::dct::idct_4x4(&dct4_coeff, &mut dct4_pixels);
for iy in 0..4 {
for ix in 0..4 {
pixels[(iy + afv_y * 4) * 8 + (1 - afv_x) * 4 + ix] = dct4_pixels[iy * 4 + ix];
}
}
let mut dct4x8_coeff = [0.0f32; 32];
for iy in 0..4 {
for ix in 0..8 {
dct4x8_coeff[iy * 8 + ix] = if ix == 0 && iy == 0 {
dcs[2]
} else {
coefficients[(1 + iy * 2) * 8 + ix]
};
}
}
let mut dct4x8_pixels = [0.0f32; 32];
super::dct::idct_4x8(&dct4x8_coeff, &mut dct4x8_pixels);
for iy in 0..4 {
for ix in 0..8 {
pixels[(iy + (1 - afv_y) * 4) * 8 + ix] = dct4x8_pixels[iy * 8 + ix];
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_afv_dct_4x4_constant() {
let pixels = [1.0f32; 16];
let mut coeffs = [0.0f32; 16];
afv_dct_4x4(&pixels, &mut coeffs);
let dc_mag = coeffs[0].abs();
for i in 1..16 {
assert!(
coeffs[i].abs() < dc_mag,
"AC coeffs[{}] = {} should be smaller than DC = {}",
i,
coeffs[i],
coeffs[0]
);
}
assert!(coeffs[0] > 1.0, "DC = {} should be positive", coeffs[0]);
}
#[test]
fn test_afv_transform_from_pixels_constant() {
let pixels = [1.0f32; 64];
let mut coeffs = [0.0f32; 64];
afv_transform_from_pixels(&pixels, 0, &mut coeffs);
let dc = dc_from_afv(&coeffs);
assert!(dc > 0.0, "DC = {}", dc);
}
#[test]
fn test_afv_kind_from_strategy() {
assert_eq!(afv_kind_from_strategy(RAW_STRATEGY_AFV0), Some(0));
assert_eq!(afv_kind_from_strategy(RAW_STRATEGY_AFV1), Some(1));
assert_eq!(afv_kind_from_strategy(RAW_STRATEGY_AFV2), Some(2));
assert_eq!(afv_kind_from_strategy(RAW_STRATEGY_AFV3), Some(3));
assert_eq!(afv_kind_from_strategy(0), None);
}
#[test]
fn test_afv_4x4_roundtrip() {
let pixels = [
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
];
let mut coeffs = [0.0f32; 16];
afv_dct_4x4(&pixels, &mut coeffs);
let mut recovered = [0.0f32; 16];
afv_idct_4x4(&coeffs, &mut recovered);
let max_error: f32 = pixels
.iter()
.zip(recovered.iter())
.map(|(a, b)| (a - b).abs())
.fold(0.0f32, |a, b| a.max(b));
assert!(
max_error < 1e-4,
"AFV 4x4 roundtrip error {} should be < 1e-4",
max_error
);
}
#[test]
fn test_afv_full_roundtrip() {
let mut pixels = [0.0f32; 64];
for (i, px) in pixels.iter_mut().enumerate() {
*px = (i as f32) + 1.0;
}
for afv_kind in 0..4 {
let mut coeffs = [0.0f32; 64];
afv_transform_from_pixels(&pixels, afv_kind, &mut coeffs);
let mut recovered = [0.0f32; 64];
inverse_afv_transform(&coeffs, afv_kind, &mut recovered);
let max_error: f32 = pixels
.iter()
.zip(recovered.iter())
.map(|(a, b)| (a - b).abs())
.fold(0.0f32, |a, b| a.max(b));
assert!(
max_error < 1.0,
"AFV{} full roundtrip error {} should be < 1.0",
afv_kind,
max_error
);
}
}
#[test]
fn test_afv_regions_isolated() {
let afv_kind = 0;
let mut pixels1 = [0.0f32; 64];
for i in 0..4 {
for j in 0..4 {
pixels1[i * 8 + j] = (i * 4 + j + 1) as f32;
}
}
let mut coeffs1 = [0.0f32; 64];
afv_transform_from_pixels(&pixels1, afv_kind, &mut coeffs1);
let mut recov1 = [0.0f32; 64];
inverse_afv_transform(&coeffs1, afv_kind, &mut recov1);
let err1: f32 = pixels1
.iter()
.zip(recov1.iter())
.map(|(a, b)| (a - b).abs())
.fold(0.0, f32::max);
eprintln!("AFV corner only - max error: {}", err1);
let mut pixels2 = [0.0f32; 64];
for i in 0..4 {
for j in 4..8 {
pixels2[i * 8 + j] = (i * 4 + (j - 4) + 1) as f32;
}
}
let mut coeffs2 = [0.0f32; 64];
afv_transform_from_pixels(&pixels2, afv_kind, &mut coeffs2);
let mut recov2 = [0.0f32; 64];
inverse_afv_transform(&coeffs2, afv_kind, &mut recov2);
let err2: f32 = pixels2
.iter()
.zip(recov2.iter())
.map(|(a, b)| (a - b).abs())
.fold(0.0, f32::max);
eprintln!("DCT4 corner only - max error: {}", err2);
let mut pixels3 = [0.0f32; 64];
for i in 4..8 {
for j in 0..8 {
pixels3[i * 8 + j] = ((i - 4) * 8 + j + 1) as f32;
}
}
let mut coeffs3 = [0.0f32; 64];
afv_transform_from_pixels(&pixels3, afv_kind, &mut coeffs3);
let mut recov3 = [0.0f32; 64];
inverse_afv_transform(&coeffs3, afv_kind, &mut recov3);
let err3: f32 = pixels3
.iter()
.zip(recov3.iter())
.map(|(a, b)| (a - b).abs())
.fold(0.0, f32::max);
eprintln!("DCT4x8 only - max error: {}", err3);
assert!(err1 < 1e-4, "AFV corner roundtrip error {} too large", err1);
assert!(
err2 < 1e-4,
"DCT4 corner roundtrip error {} too large",
err2
);
assert!(err3 < 1e-4, "DCT4x8 roundtrip error {} too large", err3);
}
}