#![allow(dead_code)]
#[derive(Debug)]
pub struct EdgeSamples<'a> {
pub p3: i32,
pub p2: &'a mut i32,
pub p1: &'a mut i32,
pub p0: &'a mut i32,
pub q0: &'a mut i32,
pub q1: &'a mut i32,
pub q2: &'a mut i32,
pub q3: i32,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Plane {
Luma,
Chroma,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct FilterParams {
pub bs: u8,
pub qp_avg: i32,
pub filter_offset_a: i32,
pub filter_offset_b: i32,
pub bit_depth: u32,
}
#[derive(Debug, Copy, Clone, Default)]
pub struct BsInputs {
pub p_is_intra: bool,
pub q_is_intra: bool,
pub is_mb_edge: bool,
pub is_sp_or_si: bool,
pub either_has_nonzero_coeffs: bool,
pub different_ref_or_mv: bool,
pub mixed_mode_edge: bool,
pub vertical_edge: bool,
pub mbaff_or_field: bool,
pub both_in_frame_mbs: bool,
}
const ALPHA_PRIME: [u8; 52] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15, 17, 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, 63, 71, 80, 90, 101, 113, 127, 144, 162, 182, 203, 226, 255, 255,
];
const BETA_PRIME: [u8; 52] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18,
];
const TC0_PRIME: [[u8; 3]; 52] = [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 1, 1], [0, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 2], [1, 1, 2], [1, 1, 2], [1, 1, 2], [1, 2, 3], [1, 2, 3], [2, 2, 3], [2, 2, 4], [2, 3, 4], [2, 3, 4], [3, 3, 5], [3, 4, 6], [3, 4, 6], [4, 5, 7], [4, 5, 8], [4, 6, 9], [5, 7, 10], [6, 8, 11], [6, 8, 13], [7, 10, 14], [8, 11, 16], [9, 12, 18], [10, 13, 20], [11, 15, 23], [13, 17, 25], ];
#[inline]
fn clip3(x: i32, y: i32, z: i32) -> i32 {
if z < x {
x
} else if z > y {
y
} else {
z
}
}
#[inline]
fn clip1(v: i32, bit_depth: u32) -> i32 {
let hi = (1i32 << bit_depth) - 1;
clip3(0, hi, v)
}
pub fn alpha_from_index(index_a: i32, bit_depth: u32) -> i32 {
let idx = clip3(0, 51, index_a) as usize;
let prime = ALPHA_PRIME[idx] as i32;
prime << bit_depth.saturating_sub(8)
}
pub fn beta_from_index(index_b: i32, bit_depth: u32) -> i32 {
let idx = clip3(0, 51, index_b) as usize;
let prime = BETA_PRIME[idx] as i32;
prime << bit_depth.saturating_sub(8)
}
pub fn tc0_from(bs: u8, index_a: i32, bit_depth: u32) -> i32 {
if !(1..=3).contains(&bs) {
return 0;
}
let idx = clip3(0, 51, index_a) as usize;
let prime = TC0_PRIME[idx][(bs - 1) as usize] as i32;
prime << bit_depth.saturating_sub(8)
}
pub fn derive_boundary_strength(inputs: BsInputs) -> u8 {
let intra_or_spsi = inputs.p_is_intra || inputs.q_is_intra || inputs.is_sp_or_si;
if inputs.is_mb_edge && intra_or_spsi {
if !inputs.mbaff_or_field || inputs.both_in_frame_mbs {
return 4;
}
if inputs.vertical_edge {
return 4;
}
}
if intra_or_spsi {
if !inputs.mixed_mode_edge {
return 3;
}
if inputs.mixed_mode_edge && !inputs.vertical_edge {
return 3;
}
}
if inputs.either_has_nonzero_coeffs {
return 2;
}
if inputs.mixed_mode_edge || inputs.different_ref_or_mv {
return 1;
}
0
}
pub fn filter_edge(plane: Plane, mut samples: EdgeSamples<'_>, params: FilterParams) {
if params.bs == 0 {
return;
}
let index_a = clip3(0, 51, params.qp_avg + params.filter_offset_a);
let index_b = clip3(0, 51, params.qp_avg + params.filter_offset_b);
let alpha = alpha_from_index(index_a, params.bit_depth);
let beta = beta_from_index(index_b, params.bit_depth);
let p0 = *samples.p0;
let q0 = *samples.q0;
let p1 = *samples.p1;
let q1 = *samples.q1;
let filter_samples = params.bs != 0
&& (p0 - q0).abs() < alpha
&& (p1 - p0).abs() < beta
&& (q1 - q0).abs() < beta;
if !filter_samples {
return;
}
let chroma_style = matches!(plane, Plane::Chroma);
if params.bs < 4 {
filter_normal(&mut samples, params, index_a, beta, chroma_style);
} else {
filter_strong(&mut samples, alpha, beta, chroma_style);
}
}
fn filter_normal(
samples: &mut EdgeSamples<'_>,
params: FilterParams,
index_a: i32,
beta: i32,
chroma_style: bool,
) {
let p2 = *samples.p2;
let p1 = *samples.p1;
let p0 = *samples.p0;
let q0 = *samples.q0;
let q1 = *samples.q1;
let q2 = *samples.q2;
let tc0 = tc0_from(params.bs, index_a, params.bit_depth);
let a_p = (p2 - p0).abs();
let a_q = (q2 - q0).abs();
let tc = if !chroma_style {
tc0 + (if a_p < beta { 1 } else { 0 }) + (if a_q < beta { 1 } else { 0 })
} else {
tc0 + 1
};
let delta = clip3(-tc, tc, (((q0 - p0) << 2) + (p1 - q1) + 4) >> 3);
*samples.p0 = clip1(p0 + delta, params.bit_depth);
*samples.q0 = clip1(q0 - delta, params.bit_depth);
if !chroma_style && a_p < beta {
let step = clip3(-tc0, tc0, (p2 + ((p0 + q0 + 1) >> 1) - (p1 << 1)) >> 1);
*samples.p1 = p1 + step;
}
if !chroma_style && a_q < beta {
let step = clip3(-tc0, tc0, (q2 + ((p0 + q0 + 1) >> 1) - (q1 << 1)) >> 1);
*samples.q1 = q1 + step;
}
let _ = (p2, q2);
}
fn filter_strong(samples: &mut EdgeSamples<'_>, alpha: i32, beta: i32, chroma_style: bool) {
let p3 = samples.p3;
let p2 = *samples.p2;
let p1 = *samples.p1;
let p0 = *samples.p0;
let q0 = *samples.q0;
let q1 = *samples.q1;
let q2 = *samples.q2;
let q3 = samples.q3;
let a_p = (p2 - p0).abs();
let a_q = (q2 - q0).abs();
let strong_p = !chroma_style && a_p < beta && (p0 - q0).abs() < ((alpha >> 2) + 2);
if strong_p {
*samples.p0 = (p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3;
*samples.p1 = (p2 + p1 + p0 + q0 + 2) >> 2;
*samples.p2 = (2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3;
} else {
*samples.p0 = (2 * p1 + p0 + q1 + 2) >> 2;
let _ = (p1, p2);
}
let strong_q = !chroma_style && a_q < beta && (p0 - q0).abs() < ((alpha >> 2) + 2);
if strong_q {
*samples.q0 = (p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3;
*samples.q1 = (p0 + q0 + q1 + q2 + 2) >> 2;
*samples.q2 = (2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3;
} else {
*samples.q0 = (2 * q1 + q0 + p1 + 2) >> 2;
let _ = (q1, q2);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn run_filter(
plane: Plane,
samples: [i32; 8], params: FilterParams,
) -> [i32; 8] {
let mut p2 = samples[1];
let mut p1 = samples[2];
let mut p0 = samples[3];
let mut q0 = samples[4];
let mut q1 = samples[5];
let mut q2 = samples[6];
filter_edge(
plane,
EdgeSamples {
p3: samples[0],
p2: &mut p2,
p1: &mut p1,
p0: &mut p0,
q0: &mut q0,
q1: &mut q1,
q2: &mut q2,
q3: samples[7],
},
params,
);
[samples[0], p2, p1, p0, q0, q1, q2, samples[7]]
}
#[test]
fn alpha_table_spotcheck() {
assert_eq!(alpha_from_index(0, 8), 0);
assert_eq!(alpha_from_index(15, 8), 0);
assert_eq!(alpha_from_index(16, 8), 4);
assert_eq!(alpha_from_index(25, 8), 13);
assert_eq!(alpha_from_index(36, 8), 50);
assert_eq!(alpha_from_index(51, 8), 255);
}
#[test]
fn beta_table_spotcheck() {
assert_eq!(beta_from_index(0, 8), 0);
assert_eq!(beta_from_index(15, 8), 0);
assert_eq!(beta_from_index(16, 8), 2);
assert_eq!(beta_from_index(25, 8), 4);
assert_eq!(beta_from_index(40, 8), 13);
assert_eq!(beta_from_index(51, 8), 18);
}
#[test]
fn tc0_table_spotcheck() {
assert_eq!(tc0_from(1, 0, 8), 0);
assert_eq!(tc0_from(1, 22, 8), 0);
assert_eq!(tc0_from(1, 17, 8), 0);
assert_eq!(tc0_from(2, 17, 8), 0);
assert_eq!(tc0_from(3, 17, 8), 1);
assert_eq!(tc0_from(1, 21, 8), 0);
assert_eq!(tc0_from(2, 21, 8), 1);
assert_eq!(tc0_from(3, 21, 8), 1);
assert_eq!(tc0_from(1, 23, 8), 1);
assert_eq!(tc0_from(2, 23, 8), 1);
assert_eq!(tc0_from(3, 23, 8), 1);
assert_eq!(tc0_from(1, 24, 8), 1);
assert_eq!(tc0_from(2, 24, 8), 1);
assert_eq!(tc0_from(3, 24, 8), 1);
assert_eq!(tc0_from(1, 30, 8), 1);
assert_eq!(tc0_from(2, 30, 8), 1);
assert_eq!(tc0_from(3, 30, 8), 2);
assert_eq!(tc0_from(1, 36, 8), 2);
assert_eq!(tc0_from(2, 36, 8), 3);
assert_eq!(tc0_from(3, 36, 8), 4);
assert_eq!(tc0_from(1, 44, 8), 6);
assert_eq!(tc0_from(2, 44, 8), 8);
assert_eq!(tc0_from(3, 44, 8), 11);
assert_eq!(tc0_from(1, 51, 8), 13);
assert_eq!(tc0_from(2, 51, 8), 17);
assert_eq!(tc0_from(3, 51, 8), 25);
assert_eq!(tc0_from(0, 36, 8), 0);
assert_eq!(tc0_from(4, 36, 8), 0);
}
#[test]
fn alpha_bit_depth_scaling() {
assert_eq!(alpha_from_index(36, 10), 50 * 4);
assert_eq!(beta_from_index(36, 10), 11 * 4);
assert_eq!(tc0_from(3, 36, 10), 4 * 4);
}
#[test]
fn tc0_table_every_entry_matches_spec() {
for idx in 0..=16 {
assert_eq!(tc0_from(1, idx, 8), 0, "bS=1 idxA={}", idx);
assert_eq!(tc0_from(2, idx, 8), 0, "bS=2 idxA={}", idx);
assert_eq!(tc0_from(3, idx, 8), 0, "bS=3 idxA={}", idx);
}
let expected: [(i32, i32, i32, i32); 35] = [
(17, 0, 0, 1),
(18, 0, 0, 1),
(19, 0, 0, 1),
(20, 0, 0, 1),
(21, 0, 1, 1),
(22, 0, 1, 1),
(23, 1, 1, 1),
(24, 1, 1, 1),
(25, 1, 1, 1),
(26, 1, 1, 1),
(27, 1, 1, 2),
(28, 1, 1, 2),
(29, 1, 1, 2),
(30, 1, 1, 2),
(31, 1, 2, 3),
(32, 1, 2, 3),
(33, 2, 2, 3),
(34, 2, 2, 4),
(35, 2, 3, 4),
(36, 2, 3, 4),
(37, 3, 3, 5),
(38, 3, 4, 6),
(39, 3, 4, 6),
(40, 4, 5, 7),
(41, 4, 5, 8),
(42, 4, 6, 9),
(43, 5, 7, 10),
(44, 6, 8, 11),
(45, 6, 8, 13),
(46, 7, 10, 14),
(47, 8, 11, 16),
(48, 9, 12, 18),
(49, 10, 13, 20),
(50, 11, 15, 23),
(51, 13, 17, 25),
];
for (idx, e1, e2, e3) in expected {
assert_eq!(tc0_from(1, idx, 8), e1, "bS=1 idxA={}", idx);
assert_eq!(tc0_from(2, idx, 8), e2, "bS=2 idxA={}", idx);
assert_eq!(tc0_from(3, idx, 8), e3, "bS=3 idxA={}", idx);
}
}
#[test]
fn bs_intra_intra_mb_edge_is_4() {
let bs = derive_boundary_strength(BsInputs {
p_is_intra: true,
q_is_intra: true,
is_mb_edge: true,
..Default::default()
});
assert_eq!(bs, 4);
}
#[test]
fn bs_intra_not_mb_edge_is_3() {
let bs = derive_boundary_strength(BsInputs {
p_is_intra: true,
q_is_intra: false,
is_mb_edge: false,
..Default::default()
});
assert_eq!(bs, 3);
}
#[test]
fn bs_nonintra_nonzero_coeffs_is_2() {
let bs = derive_boundary_strength(BsInputs {
either_has_nonzero_coeffs: true,
..Default::default()
});
assert_eq!(bs, 2);
}
#[test]
fn bs_nonintra_diff_ref_is_1() {
let bs = derive_boundary_strength(BsInputs {
different_ref_or_mv: true,
..Default::default()
});
assert_eq!(bs, 1);
}
#[test]
fn bs_nonintra_same_ref_zero_mv_is_0() {
let bs = derive_boundary_strength(BsInputs::default());
assert_eq!(bs, 0);
}
#[test]
fn bs_sp_si_mb_edge_is_4() {
let bs = derive_boundary_strength(BsInputs {
is_sp_or_si: true,
is_mb_edge: true,
..Default::default()
});
assert_eq!(bs, 4);
}
#[test]
fn filter_bs0_is_noop() {
let input: [i32; 8] = [10, 20, 30, 40, 50, 60, 70, 80];
let out = run_filter(
Plane::Luma,
input,
FilterParams {
bs: 0,
qp_avg: 30,
filter_offset_a: 0,
filter_offset_b: 0,
bit_depth: 8,
},
);
assert_eq!(out, input, "bS=0 must leave all samples unchanged");
}
#[test]
fn filter_alpha_threshold_skips_filter() {
let input: [i32; 8] = [0, 0, 0, 0, 50, 50, 50, 50];
let out = run_filter(
Plane::Luma,
input,
FilterParams {
bs: 2,
qp_avg: 30,
filter_offset_a: 0,
filter_offset_b: 0,
bit_depth: 8,
},
);
assert_eq!(out, input, "|p0-q0| >= α must skip filter");
}
#[test]
fn filter_beta_threshold_skips_filter() {
let input: [i32; 8] = [0, 0, 10, 0, 0, 0, 0, 0];
let out = run_filter(
Plane::Luma,
input,
FilterParams {
bs: 2,
qp_avg: 30,
filter_offset_a: 0,
filter_offset_b: 0,
bit_depth: 8,
},
);
assert_eq!(out, input, "|p1-p0| >= β must skip filter");
}
#[test]
fn filter_bs4_luma_strong_smooths_edge() {
let input: [i32; 8] = [100, 100, 100, 100, 108, 108, 108, 108];
let out = run_filter(
Plane::Luma,
input,
FilterParams {
bs: 4,
qp_avg: 40,
filter_offset_a: 0,
filter_offset_b: 0,
bit_depth: 8,
},
);
assert_eq!(out[0], 100, "p3 is read-only");
assert_eq!(out[1], 101, "p2' per eq. 8-479");
assert_eq!(out[2], 102, "p1' per eq. 8-478");
assert_eq!(out[3], 103, "p0' per eq. 8-477");
assert_eq!(out[4], 105, "q0' per eq. 8-484");
assert_eq!(out[5], 106, "q1' per eq. 8-485");
assert_eq!(out[6], 107, "q2' per eq. 8-486");
assert_eq!(out[7], 108, "q3 is read-only");
}
#[test]
fn filter_bs1_luma_normal_updates_p0_q0() {
let input: [i32; 8] = [20, 20, 20, 20, 30, 30, 30, 30];
let out = run_filter(
Plane::Luma,
input,
FilterParams {
bs: 1,
qp_avg: 30,
filter_offset_a: 0,
filter_offset_b: 0,
bit_depth: 8,
},
);
assert_eq!(out[0], 20);
assert_eq!(out[1], 20, "p2 unchanged in normal filter");
assert_eq!(out[2], 21, "p1' per eq. 8-470");
assert_eq!(out[3], 23, "p0' per eq. 8-468");
assert_eq!(out[4], 27, "q0' per eq. 8-469");
assert_eq!(out[5], 29, "q1' per eq. 8-472");
assert_eq!(out[6], 30, "q2 unchanged in normal filter");
assert_eq!(out[7], 30);
}
#[test]
fn filter_bs1_chroma_only_updates_p0_q0() {
let input: [i32; 8] = [20, 20, 20, 20, 30, 30, 30, 30];
let out = run_filter(
Plane::Chroma,
input,
FilterParams {
bs: 1,
qp_avg: 30,
filter_offset_a: 0,
filter_offset_b: 0,
bit_depth: 8,
},
);
assert_eq!(out[0], 20, "p3 unchanged");
assert_eq!(out[1], 20, "p2 unchanged for chroma");
assert_eq!(out[2], 20, "p1 unchanged for chroma");
assert_eq!(out[3], 22, "p0' per eq. 8-468 with tC=tC0+1");
assert_eq!(out[4], 28, "q0' per eq. 8-469 with tC=tC0+1");
assert_eq!(out[5], 30, "q1 unchanged for chroma");
assert_eq!(out[6], 30, "q2 unchanged for chroma");
assert_eq!(out[7], 30, "q3 unchanged");
}
#[test]
fn filter_bs4_chroma_only_updates_p0_q0() {
let input: [i32; 8] = [100, 100, 100, 100, 108, 108, 108, 108];
let out = run_filter(
Plane::Chroma,
input,
FilterParams {
bs: 4,
qp_avg: 40,
filter_offset_a: 0,
filter_offset_b: 0,
bit_depth: 8,
},
);
assert_eq!(out[1], 100, "p2 unchanged for chroma-style strong");
assert_eq!(out[2], 100, "p1 unchanged for chroma-style strong");
assert_eq!(out[3], 102, "p0' per eq. 8-480");
assert_eq!(out[4], 106, "q0' per eq. 8-487");
assert_eq!(out[5], 108, "q1 unchanged for chroma-style strong");
assert_eq!(out[6], 108, "q2 unchanged for chroma-style strong");
}
#[test]
fn filter_clamps_to_sample_range() {
let input: [i32; 8] = [0, 0, 0, 0, 5, 5, 5, 5];
let out = run_filter(
Plane::Luma,
input,
FilterParams {
bs: 4,
qp_avg: 40,
filter_offset_a: 0,
filter_offset_b: 0,
bit_depth: 8,
},
);
for v in out.iter() {
assert!(*v >= 0 && *v <= 255, "sample {} out of 8-bit range", v);
}
}
}