mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-29 12:37:08 -06:00
150 lines
6.4 KiB
Rust
150 lines
6.4 KiB
Rust
//! Deblocking filter (8.7.2) over a whole reconstructed picture, mirrored from the filmcraft-hevc
|
|
//! decoder: all vertical edges on the 8x8 grid first, then all horizontal edges. One slice, one QP,
|
|
//! uni-directional prediction from a single reference picture, offsets 0.
|
|
|
|
use crate::frame::{Blk, E_EDGE_H, E_EDGE_V, F_CBF, F_INTRA};
|
|
use crate::tables::{BETA_TABLE, TC_TABLE, qpc_420};
|
|
|
|
#[inline]
|
|
fn tc_beta(q: i32, bs: i32, bd: u32) -> (i32, i32) {
|
|
let qb = q.clamp(0, 51);
|
|
let qt = (q + 2 * (bs - 1)).clamp(0, 53);
|
|
((TC_TABLE[qt as usize] as i32) << (bd - 8), (BETA_TABLE[qb as usize] as i32) << (bd - 8))
|
|
}
|
|
|
|
/// Boundary strength (8.7.2.4) between 4x4 blocks p and q on a transform / prediction edge.
|
|
fn boundary_strength(p: &Blk, q: &Blk) -> i32 {
|
|
if (p.flags | q.flags) & F_INTRA != 0 {
|
|
return 2;
|
|
}
|
|
if (p.flags | q.flags) & F_CBF != 0 {
|
|
return 1;
|
|
}
|
|
let far = (p.mv[0] as i32 - q.mv[0] as i32).abs() >= 4 || (p.mv[1] as i32 - q.mv[1] as i32).abs() >= 4;
|
|
far as i32
|
|
}
|
|
|
|
/// Deblock the planes in place. `qp` is the slice QpY (without QpBdOffset).
|
|
pub fn deblock(planes: &mut [Vec<u16>; 3], blk: &[Blk], w: usize, h: usize, qp: i32, bd: u32) {
|
|
let (w4, h4) = (w / 4, h / 4);
|
|
let cw = w / 2;
|
|
// chroma tc (bS 2 only; QpC from the averaged luma QP, no offsets)
|
|
let qt = (qpc_420(qp) + 2).clamp(0, 53);
|
|
let tc_c = (TC_TABLE[qt as usize] as i32) << (bd - 8);
|
|
// vertical edges
|
|
for by in 0..h4 {
|
|
for bx in (2..w4).step_by(2) {
|
|
let qi = by * w4 + bx;
|
|
if blk[qi].flags & E_EDGE_V == 0 {
|
|
continue;
|
|
}
|
|
let bs = boundary_strength(&blk[qi - 1], &blk[qi]);
|
|
if bs == 0 {
|
|
continue;
|
|
}
|
|
let (x, y) = (bx * 4, by * 4);
|
|
let (tc, beta) = tc_beta(qp, bs, bd);
|
|
filter_luma(&mut planes[0], y * w + x, 1, w, tc, beta, bd);
|
|
// chroma: 8x8 chroma grid (16 luma samples), one 4-sample chroma segment per 8 luma rows
|
|
if bs == 2 && bx % 4 == 0 && by % 2 == 0 {
|
|
for c in 1..3 {
|
|
filter_chroma(&mut planes[c], (y / 2) * cw + x / 2, 1, cw, tc_c, bd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// horizontal edges
|
|
for by in (2..h4).step_by(2) {
|
|
for bx in 0..w4 {
|
|
let qi = by * w4 + bx;
|
|
if blk[qi].flags & E_EDGE_H == 0 {
|
|
continue;
|
|
}
|
|
let bs = boundary_strength(&blk[qi - w4], &blk[qi]);
|
|
if bs == 0 {
|
|
continue;
|
|
}
|
|
let (x, y) = (bx * 4, by * 4);
|
|
let (tc, beta) = tc_beta(qp, bs, bd);
|
|
filter_luma(&mut planes[0], y * w + x, w, 1, tc, beta, bd);
|
|
if bs == 2 && by % 4 == 0 && bx % 2 == 0 {
|
|
for c in 1..3 {
|
|
filter_chroma(&mut planes[c], (y / 2) * cw + x / 2, cw, 1, tc_c, bd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Filter one 4-sample luma edge segment. `o` = offset of q0 of the first line, `step` = distance
|
|
/// between samples across the edge, `along` = distance between lines.
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn filter_luma(s: &mut [u16], o: usize, step: usize, along: usize, tc: i32, beta: i32, bd: u32) {
|
|
let px = |s: &[u16], k: usize, i: usize| s[o + k * along - (i + 1) * step] as i32;
|
|
let qx = |s: &[u16], k: usize, i: usize| s[o + k * along + i * step] as i32;
|
|
let dp0 = (px(s, 0, 2) - 2 * px(s, 0, 1) + px(s, 0, 0)).abs();
|
|
let dp3 = (px(s, 3, 2) - 2 * px(s, 3, 1) + px(s, 3, 0)).abs();
|
|
let dq0 = (qx(s, 0, 2) - 2 * qx(s, 0, 1) + qx(s, 0, 0)).abs();
|
|
let dq3 = (qx(s, 3, 2) - 2 * qx(s, 3, 1) + qx(s, 3, 0)).abs();
|
|
let (dpq0, dpq3) = (dp0 + dq0, dp3 + dq3);
|
|
let (dp, dq) = (dp0 + dp3, dq0 + dq3);
|
|
let d = dpq0 + dpq3;
|
|
if d >= beta {
|
|
return;
|
|
}
|
|
let dsam = |s: &[u16], k: usize, dpq: i32| -> bool {
|
|
dpq < (beta >> 2)
|
|
&& (px(s, k, 3) - px(s, k, 0)).abs() + (qx(s, k, 0) - qx(s, k, 3)).abs() < (beta >> 3)
|
|
&& (px(s, k, 0) - qx(s, k, 0)).abs() < ((5 * tc + 1) >> 1)
|
|
};
|
|
let strong = dsam(s, 0, 2 * dpq0) && dsam(s, 3, 2 * dpq3);
|
|
let dep = dp < ((beta + (beta >> 1)) >> 3);
|
|
let deq = dq < ((beta + (beta >> 1)) >> 3);
|
|
let max = (1i32 << bd) - 1;
|
|
for k in 0..4 {
|
|
let base = o + k * along;
|
|
let p = |i: usize| s[base - (i + 1) * step] as i32;
|
|
let q = |i: usize| s[base + i * step] as i32;
|
|
let (p0, p1, p2, p3) = (p(0), p(1), p(2), p(3));
|
|
let (q0, q1, q2, q3) = (q(0), q(1), q(2), q(3));
|
|
if strong {
|
|
let t2 = 2 * tc;
|
|
s[base - step] = ((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3).clamp(p0 - t2, p0 + t2) as u16;
|
|
s[base - 2 * step] = ((p2 + p1 + p0 + q0 + 2) >> 2).clamp(p1 - t2, p1 + t2) as u16;
|
|
s[base - 3 * step] = ((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3).clamp(p2 - t2, p2 + t2) as u16;
|
|
s[base] = ((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3).clamp(q0 - t2, q0 + t2) as u16;
|
|
s[base + step] = ((p0 + q0 + q1 + q2 + 2) >> 2).clamp(q1 - t2, q1 + t2) as u16;
|
|
s[base + 2 * step] = ((p0 + q0 + q1 + 3 * q2 + 2 * q3 + 4) >> 3).clamp(q2 - t2, q2 + t2) as u16;
|
|
} else {
|
|
let mut delta = (9 * (q0 - p0) - 3 * (q1 - p1) + 8) >> 4;
|
|
if delta.abs() >= tc * 10 {
|
|
continue;
|
|
}
|
|
delta = delta.clamp(-tc, tc);
|
|
s[base - step] = (p0 + delta).clamp(0, max) as u16;
|
|
s[base] = (q0 - delta).clamp(0, max) as u16;
|
|
if dep {
|
|
let dp = ((((p2 + p0 + 1) >> 1) - p1 + delta) >> 1).clamp(-(tc >> 1), tc >> 1);
|
|
s[base - 2 * step] = (p1 + dp).clamp(0, max) as u16;
|
|
}
|
|
if deq {
|
|
let dq = ((((q2 + q0 + 1) >> 1) - q1 - delta) >> 1).clamp(-(tc >> 1), tc >> 1);
|
|
s[base + step] = (q1 + dq).clamp(0, max) as u16;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Filter one 4-sample chroma edge segment.
|
|
fn filter_chroma(s: &mut [u16], o: usize, step: usize, along: usize, tc: i32, bd: u32) {
|
|
let max = (1i32 << bd) - 1;
|
|
for k in 0..4 {
|
|
let base = o + k * along;
|
|
let (p0, p1) = (s[base - step] as i32, s[base - 2 * step] as i32);
|
|
let (q0, q1) = (s[base] as i32, s[base + step] as i32);
|
|
let delta = ((((q0 - p0) << 2) + p1 - q1 + 4) >> 3).clamp(-tc, tc);
|
|
s[base - step] = (p0 + delta).clamp(0, max) as u16;
|
|
s[base] = (q0 - delta).clamp(0, max) as u16;
|
|
}
|
|
}
|