pub const DEFAULT_QMAT: [u8; 64] = [4u8; 64];
pub const PERCEPTUAL_LUMA_QMAT: [u8; 64] = [
2, 2, 2, 2, 3, 5, 6, 8, 2, 2, 2, 2, 3, 7, 8, 7, 2, 2, 2, 3, 5, 7, 9, 7, 2, 2, 3, 4, 6, 11, 10, 8, 2, 3, 5, 7, 9, 14, 13, 10, 3, 4, 7, 8, 10, 13, 14, 12, 6, 8, 10, 11, 13, 15, 15, 13, 9, 12, 12, 12, 14, 13, 13, 12, ];
pub const PERCEPTUAL_CHROMA_QMAT: [u8; 64] = [
2, 2, 3, 6, 12, 12, 12, 12, 2, 3, 3, 8, 12, 12, 12, 12, 3, 3, 7, 12, 12, 12, 12, 12, 6, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, ];
pub const SIGNATURE_PROXY_LUMA_QMAT: [u8; 64] = [
4, 7, 9, 11, 13, 14, 15, 63, 7, 7, 11, 12, 14, 15, 63, 63, 9, 11, 13, 14, 15, 63, 63, 63, 11, 11, 13, 14, 63, 63, 63, 63, 11, 13, 14, 63, 63, 63, 63, 63, 13, 14, 63, 63, 63, 63, 63, 63, 13, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, ];
pub const SIGNATURE_PROXY_CHROMA_QMAT: [u8; 64] = [
4, 7, 9, 11, 13, 14, 63, 63, 7, 7, 11, 12, 14, 63, 63, 63, 9, 11, 13, 14, 63, 63, 63, 63, 11, 11, 13, 14, 63, 63, 63, 63, 11, 13, 14, 63, 63, 63, 63, 63, 13, 14, 63, 63, 63, 63, 63, 63, 13, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, ];
pub const SIGNATURE_LT_QMAT: [u8; 64] = [
4, 5, 6, 7, 9, 11, 13, 15, 5, 5, 7, 8, 11, 13, 15, 17, 6, 7, 9, 11, 13, 15, 15, 17, 7, 7, 9, 11, 13, 15, 17, 19, 7, 9, 11, 13, 14, 16, 19, 23, 9, 11, 13, 14, 16, 19, 23, 29, 9, 11, 13, 15, 17, 21, 28, 35, 11, 13, 16, 17, 21, 28, 35, 41, ];
pub const SIGNATURE_STANDARD_QMAT: [u8; 64] = [
4, 4, 5, 5, 6, 7, 7, 9, 4, 4, 5, 6, 7, 7, 9, 9, 5, 5, 6, 7, 7, 9, 9, 10, 5, 5, 6, 7, 7, 9, 9, 10, 5, 6, 7, 7, 8, 9, 10, 12, 6, 7, 7, 8, 9, 10, 12, 15, 6, 7, 7, 9, 10, 11, 14, 17, 7, 7, 9, 10, 11, 14, 17, 21, ];
pub const SIGNATURE_HQ_QMAT: [u8; 64] = [
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 5, 5, 4, 4, 4, 4, 4, 5, 5, 6, 4, 4, 4, 4, 5, 5, 6, 7, 4, 4, 4, 4, 5, 6, 7, 7, ];
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct QuantMatrices {
pub luma: [u8; 64],
pub chroma: [u8; 64],
}
impl QuantMatrices {
pub const fn flat() -> Self {
Self {
luma: DEFAULT_QMAT,
chroma: DEFAULT_QMAT,
}
}
pub const fn perceptual() -> Self {
Self {
luma: PERCEPTUAL_LUMA_QMAT,
chroma: PERCEPTUAL_CHROMA_QMAT,
}
}
pub fn perceptual_for_profile(profile: crate::frame::Profile) -> Self {
let bn = profile.default_quant_index() as u32; let mut luma = [0u8; 64];
let mut chroma = [0u8; 64];
for k in 0..64 {
let lw = ((8 - bn) * 4 + bn * PERCEPTUAL_LUMA_QMAT[k] as u32 + 4) / 8;
let cw = ((8 - bn) * 4 + bn * PERCEPTUAL_CHROMA_QMAT[k] as u32 + 4) / 8;
luma[k] = lw.clamp(2, 63) as u8;
chroma[k] = cw.clamp(2, 63) as u8;
}
Self { luma, chroma }
}
pub fn signature_for_profile(profile: crate::frame::Profile) -> Self {
use crate::frame::Profile;
match profile {
Profile::Proxy => Self {
luma: SIGNATURE_PROXY_LUMA_QMAT,
chroma: SIGNATURE_PROXY_CHROMA_QMAT,
},
Profile::Lt => Self {
luma: SIGNATURE_LT_QMAT,
chroma: SIGNATURE_LT_QMAT,
},
Profile::Standard => Self {
luma: SIGNATURE_STANDARD_QMAT,
chroma: SIGNATURE_STANDARD_QMAT,
},
Profile::Hq | Profile::Prores4444 | Profile::Prores4444Xq => Self {
luma: SIGNATURE_HQ_QMAT,
chroma: SIGNATURE_HQ_QMAT,
},
}
}
pub fn from_header(header: &crate::frame::FrameHeader) -> Self {
Self {
luma: header.luma_qmat,
chroma: header.chroma_qmat,
}
}
pub fn is_default(&self) -> bool {
self.luma == DEFAULT_QMAT && self.chroma == DEFAULT_QMAT
}
pub fn wire_flags(&self) -> (bool, bool) {
let load_luma = self.luma != DEFAULT_QMAT;
let load_chroma = self.chroma != self.luma;
(load_luma, load_chroma)
}
pub fn weights_valid(&self) -> bool {
self.luma.iter().all(|w| (2..=63).contains(w))
&& self.chroma.iter().all(|w| (2..=63).contains(w))
}
}
impl Default for QuantMatrices {
fn default() -> Self {
Self::flat()
}
}
pub const BLOCK_SCAN_PROGRESSIVE: [u8; 64] = [
0, 1, 4, 5, 16, 17, 21, 22, 2, 3, 6, 7, 18, 20, 23, 28, 8, 9, 12, 13, 19, 24, 27, 29, 10, 11, 14, 15, 25, 26, 30, 31, 32, 33, 37, 38, 45, 46, 53, 54, 34, 36, 39, 44, 47, 52, 55, 60, 35, 40, 43, 48, 51, 56, 59, 61, 41, 42, 49, 50, 57, 58, 62, 63, ];
pub const BLOCK_SCAN_INTERLACED: [u8; 64] = [
0, 2, 8, 10, 32, 34, 35, 41, 1, 3, 9, 11, 33, 36, 40, 42, 4, 6, 12, 14, 37, 39, 43, 49, 5, 7, 13, 15, 38, 44, 48, 50, 16, 18, 19, 25, 45, 47, 51, 57, 17, 20, 24, 26, 46, 52, 56, 58, 21, 23, 27, 30, 53, 55, 59, 62, 22, 28, 29, 31, 54, 60, 61, 63, ];
pub const PROGRESSIVE_INV_SCAN: [u8; 64] = invert_scan(&BLOCK_SCAN_PROGRESSIVE);
pub const INTERLACED_INV_SCAN: [u8; 64] = invert_scan(&BLOCK_SCAN_INTERLACED);
const fn invert_scan(scan: &[u8; 64]) -> [u8; 64] {
let mut out = [0u8; 64];
let mut i = 0;
while i < 64 {
out[scan[i] as usize] = i as u8;
i += 1;
}
out
}
pub fn qscale(quantization_index: u8) -> i32 {
let i = quantization_index as i32;
if i <= 128 {
i
} else {
128 + 4 * (i - 128)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wire_flags_all_four_combinations() {
let default = DEFAULT_QMAT;
let mut custom_a = DEFAULT_QMAT;
custom_a[7] = 9;
let mut custom_b = DEFAULT_QMAT;
custom_b[63] = 40;
assert_ne!(custom_a, default);
assert_ne!(custom_b, default);
assert_ne!(custom_a, custom_b);
assert_eq!(
QuantMatrices {
luma: default,
chroma: default
}
.wire_flags(),
(false, false)
);
assert_eq!(
QuantMatrices {
luma: default,
chroma: custom_b
}
.wire_flags(),
(false, true)
);
assert_eq!(
QuantMatrices {
luma: custom_a,
chroma: custom_a
}
.wire_flags(),
(true, false)
);
assert_eq!(
QuantMatrices {
luma: custom_a,
chroma: custom_b
}
.wire_flags(),
(true, true)
);
assert_eq!(
QuantMatrices {
luma: custom_a,
chroma: default
}
.wire_flags(),
(true, true)
);
}
#[test]
fn wire_flags_flat_and_perceptual() {
assert_eq!(QuantMatrices::flat().wire_flags(), (false, false));
assert_eq!(QuantMatrices::perceptual().wire_flags(), (true, true));
for profile in [
crate::frame::Profile::Proxy,
crate::frame::Profile::Lt,
crate::frame::Profile::Standard,
crate::frame::Profile::Hq,
crate::frame::Profile::Prores4444,
crate::frame::Profile::Prores4444Xq,
] {
assert_eq!(
QuantMatrices::perceptual_for_profile(profile).wire_flags(),
(true, true),
"profile {profile:?} should carry both custom tables",
);
}
}
#[test]
fn progressive_scan_is_permutation() {
let mut seen = [false; 64];
for &v in &BLOCK_SCAN_PROGRESSIVE {
assert!(!seen[v as usize], "duplicate {v}");
seen[v as usize] = true;
}
assert!(seen.iter().all(|&b| b));
}
#[test]
fn interlaced_scan_is_permutation() {
let mut seen = [false; 64];
for &v in &BLOCK_SCAN_INTERLACED {
assert!(!seen[v as usize], "duplicate {v}");
seen[v as usize] = true;
}
assert!(seen.iter().all(|&b| b));
}
#[test]
fn inverse_progressive_scan_roundtrips() {
for k in 0..64 {
let nat = PROGRESSIVE_INV_SCAN[k] as usize;
assert_eq!(BLOCK_SCAN_PROGRESSIVE[nat], k as u8);
}
}
#[test]
fn perceptual_matrices_in_valid_weight_range() {
let m = QuantMatrices::perceptual();
assert!(m.weights_valid());
assert_eq!(m.luma[0], 2);
assert_eq!(m.chroma[0], 2);
assert!(m.luma[63] > 4);
}
#[test]
fn perceptual_matrices_match_jpeg_k1_k2_normalised_dc2() {
assert_eq!(PERCEPTUAL_LUMA_QMAT[0], 2);
assert_eq!(PERCEPTUAL_LUMA_QMAT[1], 2);
assert_eq!(PERCEPTUAL_LUMA_QMAT[7], 8);
assert_eq!(PERCEPTUAL_LUMA_QMAT[7 * 8], 9);
assert_eq!(PERCEPTUAL_CHROMA_QMAT[0], 2);
assert_eq!(PERCEPTUAL_CHROMA_QMAT[3], 6);
assert_eq!(PERCEPTUAL_CHROMA_QMAT[3 * 8 + 3], 12);
}
#[test]
fn quant_matrices_default_is_flat() {
assert_eq!(QuantMatrices::default(), QuantMatrices::flat());
assert!(QuantMatrices::default().is_default());
assert!(!QuantMatrices::perceptual().is_default());
}
#[test]
fn quant_matrices_weights_valid_rejects_out_of_range() {
let mut bad = QuantMatrices::flat();
bad.luma[0] = 1; assert!(!bad.weights_valid());
bad.luma[0] = 64; assert!(!bad.weights_valid());
bad.luma[0] = 4;
bad.chroma[5] = 0;
assert!(!bad.weights_valid());
}
#[test]
fn perceptual_for_profile_proxy_equals_perceptual() {
let p = QuantMatrices::perceptual_for_profile(crate::frame::Profile::Proxy);
assert_eq!(p, QuantMatrices::perceptual());
}
#[test]
fn perceptual_for_profile_weights_in_valid_range_for_all_profiles() {
use crate::frame::Profile;
for &profile in &[
Profile::Proxy,
Profile::Lt,
Profile::Standard,
Profile::Hq,
Profile::Prores4444,
Profile::Prores4444Xq,
] {
let m = QuantMatrices::perceptual_for_profile(profile);
assert!(
m.weights_valid(),
"weights out of 2..=63 for profile {profile:?}: luma {:?} chroma {:?}",
&m.luma[..],
&m.chroma[..],
);
assert!(m.luma[0] <= 4, "DC luma should not exceed 4");
assert!(m.chroma[0] <= 4, "DC chroma should not exceed 4");
}
}
#[test]
fn perceptual_for_profile_hf_weight_monotonic_in_quality_tier() {
use crate::frame::Profile;
let xq = QuantMatrices::perceptual_for_profile(Profile::Prores4444Xq).luma[63];
let hq = QuantMatrices::perceptual_for_profile(Profile::Hq).luma[63];
let std = QuantMatrices::perceptual_for_profile(Profile::Standard).luma[63];
let lt = QuantMatrices::perceptual_for_profile(Profile::Lt).luma[63];
let proxy = QuantMatrices::perceptual_for_profile(Profile::Proxy).luma[63];
assert!(xq <= hq, "XQ {xq} > HQ {hq} (HF should grow toward Proxy)");
assert!(hq <= std, "HQ {hq} > Standard {std}");
assert!(std <= lt, "Standard {std} > LT {lt}");
assert!(lt <= proxy, "LT {lt} > Proxy {proxy}");
assert_eq!(proxy, PERCEPTUAL_LUMA_QMAT[63]);
}
#[test]
fn perceptual_for_profile_xq_pulls_toward_flat() {
let xq = QuantMatrices::perceptual_for_profile(crate::frame::Profile::Prores4444Xq);
let flat = QuantMatrices::flat();
let perc = QuantMatrices::perceptual();
let dist_flat: u32 = xq
.luma
.iter()
.zip(flat.luma.iter())
.map(|(a, b)| a.abs_diff(*b) as u32)
.sum();
let dist_perc: u32 = xq
.luma
.iter()
.zip(perc.luma.iter())
.map(|(a, b)| a.abs_diff(*b) as u32)
.sum();
assert!(
dist_flat < dist_perc,
"XQ matrix distance to flat ({dist_flat}) must be < distance to perceptual ({dist_perc})",
);
}
#[test]
fn perceptual_for_profile_not_default_for_any_profile() {
use crate::frame::Profile;
for &profile in &[
Profile::Proxy,
Profile::Lt,
Profile::Standard,
Profile::Hq,
Profile::Prores4444,
Profile::Prores4444Xq,
] {
let m = QuantMatrices::perceptual_for_profile(profile);
assert!(
!m.is_default(),
"profile {profile:?} blended matrix collapsed to flat default — \
encoder would silently emit load_*_qmat = 0",
);
}
}
#[test]
fn signature_matrices_all_in_valid_range() {
for m in [
&SIGNATURE_PROXY_LUMA_QMAT,
&SIGNATURE_PROXY_CHROMA_QMAT,
&SIGNATURE_LT_QMAT,
&SIGNATURE_STANDARD_QMAT,
&SIGNATURE_HQ_QMAT,
] {
assert!(
m.iter().all(|&w| (2..=63).contains(&w)),
"weight out of range"
);
assert_eq!(m[0], 4, "DC weight is 4");
}
}
#[test]
fn signature_matrices_are_low_to_high_frequency_monotone() {
for m in [
&SIGNATURE_PROXY_LUMA_QMAT,
&SIGNATURE_LT_QMAT,
&SIGNATURE_STANDARD_QMAT,
&SIGNATURE_HQ_QMAT,
] {
for v in 0..8 {
for u in 0..7 {
assert!(m[v * 8 + u] <= m[v * 8 + u + 1], "row {v} not monotone");
}
}
for u in 0..8 {
for v in 0..7 {
assert!(m[v * 8 + u] <= m[(v + 1) * 8 + u], "col {u} not monotone");
}
}
}
}
#[test]
fn signature_for_profile_wire_flags() {
use crate::frame::Profile;
assert_eq!(
QuantMatrices::signature_for_profile(Profile::Proxy).wire_flags(),
(true, true)
);
for p in [
Profile::Lt,
Profile::Standard,
Profile::Hq,
Profile::Prores4444,
Profile::Prores4444Xq,
] {
let qm = QuantMatrices::signature_for_profile(p);
assert_eq!(qm.luma, qm.chroma, "{p:?} chroma should equal luma");
assert_eq!(qm.wire_flags(), (true, false), "{p:?} flags");
assert!(!qm.is_default(), "{p:?} differs from all-4s default");
}
}
#[test]
fn signature_hq_family_shares_one_matrix() {
use crate::frame::Profile;
let hq = QuantMatrices::signature_for_profile(Profile::Hq);
assert_eq!(
hq,
QuantMatrices::signature_for_profile(Profile::Prores4444)
);
assert_eq!(
hq,
QuantMatrices::signature_for_profile(Profile::Prores4444Xq)
);
}
#[test]
fn from_header_recovers_matrix_pair() {
use crate::frame::{parse_frame_header, write_frame_with_meta, ChromaFormat, FrameMeta};
let qm = QuantMatrices::signature_for_profile(crate::frame::Profile::Proxy);
let (load_luma, load_chroma) = qm.wire_flags();
let mut buf = Vec::new();
write_frame_with_meta(
&mut buf,
0,
64,
64,
ChromaFormat::Y422,
0,
&qm.luma,
&qm.chroma,
load_luma,
load_chroma,
0,
FrameMeta::default(),
);
let (fh, _) = parse_frame_header(&buf[8..]).unwrap();
let recovered = QuantMatrices::from_header(&fh);
assert_eq!(recovered, qm);
assert_ne!(recovered.luma, recovered.chroma, "proxy chroma differs");
}
#[test]
fn qscale_table15_samples() {
assert_eq!(qscale(1), 1);
assert_eq!(qscale(2), 2);
assert_eq!(qscale(126), 126);
assert_eq!(qscale(127), 127);
assert_eq!(qscale(128), 128);
assert_eq!(qscale(129), 132);
assert_eq!(qscale(130), 136);
assert_eq!(qscale(223), 508);
assert_eq!(qscale(224), 512);
}
}