use super::frame::LoopFilter;
use super::plane::Plane;
use super::tile::{FRAME_LF_COUNT, MAX_SEGMENTS};
use super::transform::TxSize;
const MI_SIZE: usize = 4;
const MAX_LOOP_FILTER: i32 = 63;
pub struct Deblock<'a> {
pub planes: &'a mut [Plane],
pub loop_filter: &'a LoopFilter,
pub bit_depth: u8,
pub num_planes: usize,
pub subsampling_x: usize,
pub subsampling_y: usize,
pub mi_rows: usize,
pub mi_cols: usize,
pub frame_width: usize,
pub frame_height: usize,
pub lf_tx_sizes: &'a [Vec<u8>],
pub delta_lfs: &'a [[i8; FRAME_LF_COUNT]],
pub delta_lf_multi: bool,
pub segment_ids: &'a [u8],
pub segment_lf: [[i32; FRAME_LF_COUNT]; MAX_SEGMENTS],
}
impl Deblock<'_> {
pub fn run(&mut self) {
let luma_vert = self.loop_filter.level.first().copied().unwrap_or(0);
let luma_horz = self.loop_filter.level.get(1).copied().unwrap_or(0);
if luma_vert == 0 && luma_horz == 0 {
return;
}
for plane in 0..self.num_planes {
if plane != 0 && self.level_for(plane) == 0 {
continue;
}
let (sub_x, sub_y) = self.plane_subsampling(plane);
for pass in 0..2 {
let mut row = 0;
while row < self.mi_rows {
let mut col = 0;
while col < self.mi_cols {
self.filter_edge(plane, pass, row, col);
col += 1 << sub_x;
}
row += 1 << sub_y;
}
}
}
}
fn plane_subsampling(&self, plane: usize) -> (usize, usize) {
if plane == 0 {
(0, 0)
} else {
(self.subsampling_x, self.subsampling_y)
}
}
fn level_for(&self, plane: usize) -> u8 {
self.loop_filter.level.get(1 + plane).copied().unwrap_or(0)
}
fn tx_size(&self, plane: usize, row: usize, col: usize) -> TxSize {
let idx = row * self.mi_cols + col;
let raw = self
.lf_tx_sizes
.get(plane)
.and_then(|g| g.get(idx))
.copied()
.unwrap_or(0);
TxSize::from_index(usize::from(raw))
}
fn filter_edge(&mut self, plane: usize, pass: usize, row: usize, col: usize) {
let (dx, dy) = if pass == 0 { (1_usize, 0) } else { (0, 1) };
let (sub_x, sub_y) = self.plane_subsampling(plane);
let x = col * MI_SIZE;
let y = row * MI_SIZE;
let row = row | sub_y;
let col = col | sub_x;
if x >= self.frame_width || y >= self.frame_height {
return;
}
if pass == 0 && x == 0 {
return;
}
if pass == 1 && y == 0 {
return;
}
let xp = x >> sub_x;
let yp = y >> sub_y;
let tx_sz = self.tx_size(plane, row >> sub_y, col >> sub_x);
let tx_w = tx_sz.width();
let tx_h = tx_sz.height();
let (prev_row, prev_col) = if pass == 0 {
(row, col.wrapping_sub(1 << sub_x))
} else {
(row.wrapping_sub(1 << sub_y), col)
};
let prev_tx_sz = self.tx_size(plane, prev_row >> sub_y, prev_col >> sub_x);
let is_tx_edge = if pass == 0 {
xp % tx_w == 0
} else {
yp % tx_h == 0
};
if !is_tx_edge {
return;
}
let filter_size = filter_size(tx_sz, prev_tx_sz, pass, plane);
let (mut lvl, mut limit, mut blimit, mut thresh) =
self.filter_strength(row, col, plane, pass);
if lvl == 0 {
let (l2, li2, bl2, th2) = self.filter_strength(prev_row, prev_col, plane, pass);
lvl = l2;
limit = li2;
blimit = bl2;
thresh = th2;
}
if lvl == 0 {
return;
}
let Some(plane_buf) = self.planes.get_mut(plane) else {
return;
};
for i in 0..MI_SIZE {
let fx = xp + dy * i;
let fy = yp + dx * i;
sample_filter(
plane_buf,
fx,
fy,
plane,
limit,
blimit,
thresh,
dx,
dy,
filter_size,
self.bit_depth,
);
}
}
fn filter_strength(
&self,
row: usize,
col: usize,
plane: usize,
pass: usize,
) -> (i32, i32, i32, i32) {
let i = if plane == 0 { pass } else { plane + 1 };
let base = self.loop_filter.level.get(i).copied().unwrap_or(0);
let deltas = row
.checked_mul(self.mi_cols)
.and_then(|at| at.checked_add(col))
.and_then(|at| self.delta_lfs.get(at))
.copied()
.unwrap_or_default();
let delta = if self.delta_lf_multi {
deltas.get(i).copied().unwrap_or(0)
} else {
deltas.first().copied().unwrap_or(0)
};
let mut lvl = (i32::from(base) + i32::from(delta)).clamp(0, MAX_LOOP_FILTER);
let segment = row
.checked_mul(self.mi_cols)
.and_then(|at| at.checked_add(col))
.and_then(|at| self.segment_ids.get(at))
.map_or(0, |&s| usize::from(s));
let offset = self
.segment_lf
.get(segment)
.and_then(|lf| lf.get(i))
.copied()
.unwrap_or(0);
lvl = (lvl + offset).clamp(0, MAX_LOOP_FILTER);
if self.loop_filter.delta_enabled {
let n_shift = lvl >> 5;
let intra_delta = self.loop_filter.ref_deltas.first().copied().unwrap_or(0);
lvl = (lvl + (intra_delta << n_shift)).clamp(0, MAX_LOOP_FILTER);
}
let sharpness = i32::from(self.loop_filter.sharpness);
let shift = if sharpness > 4 {
2
} else if sharpness > 0 {
1
} else {
0
};
let limit = if sharpness > 0 {
(lvl >> shift).clamp(1, 9 - sharpness)
} else {
(lvl >> shift).max(1)
};
let blimit = 2 * (lvl + 2) + limit;
let thresh = lvl >> 4;
(lvl, limit, blimit, thresh)
}
}
fn filter_size(tx_sz: TxSize, prev_tx_sz: TxSize, pass: usize, plane: usize) -> usize {
let base = if pass == 0 {
prev_tx_sz.width().min(tx_sz.width())
} else {
prev_tx_sz.height().min(tx_sz.height())
};
if plane == 0 {
base.min(16)
} else {
base.min(8)
}
}
fn get(plane: &Plane, x: usize, y: usize, dx: usize, dy: usize, k: isize) -> i32 {
let sx = x as isize + dx as isize * k;
let sy = y as isize + dy as isize * k;
i32::from(plane.sample_clamped(sx, sy))
}
#[allow(clippy::too_many_arguments, reason = "mirrors the spec's input list")]
fn sample_filter(
plane_buf: &mut Plane,
x: usize,
y: usize,
plane: usize,
limit: i32,
blimit: i32,
thresh: i32,
dx: usize,
dy: usize,
filter_size: usize,
bit_depth: u8,
) {
let bd = u32::from(bit_depth);
let q = |k: isize| get(plane_buf, x, y, dx, dy, k);
let p = |k: isize| get(plane_buf, x, y, dx, dy, -1 - k);
let (q0, q1, q2, q3) = (q(0), q(1), q(2), q(3));
let (p0, p1, p2, p3) = (p(0), p(1), p(2), p(3));
let filter_len = if filter_size == 4 {
4
} else if plane != 0 {
6
} else if filter_size == 8 {
8
} else {
16
};
let shift = bd - 8;
let thresh_bd = thresh << shift;
let hev_mask = ((p1 - p0).abs() > thresh_bd) || ((q1 - q0).abs() > thresh_bd);
let limit_bd = limit << shift;
let blimit_bd = blimit << shift;
let mut mask = false;
mask |= (p1 - p0).abs() > limit_bd;
mask |= (q1 - q0).abs() > limit_bd;
mask |= (p0 - q0).abs() * 2 + (p1 - q1).abs() / 2 > blimit_bd;
if filter_len >= 6 {
mask |= (p2 - p1).abs() > limit_bd;
mask |= (q2 - q1).abs() > limit_bd;
}
if filter_len >= 8 {
mask |= (p3 - p2).abs() > limit_bd;
mask |= (q3 - q2).abs() > limit_bd;
}
let filter_mask = !mask;
if !filter_mask {
return;
}
let threshold_bd = 1_i32 << shift;
let flat_mask = if filter_size >= 8 {
let mut m = false;
m |= (p1 - p0).abs() > threshold_bd;
m |= (q1 - q0).abs() > threshold_bd;
m |= (p2 - p0).abs() > threshold_bd;
m |= (q2 - q0).abs() > threshold_bd;
if filter_len >= 8 {
m |= (p3 - p0).abs() > threshold_bd;
m |= (q3 - q0).abs() > threshold_bd;
}
!m
} else {
false
};
let flat_mask2 = if filter_size >= 16 {
let (q4, q5, q6) = (q(4), q(5), q(6));
let (p4, p5, p6) = (p(4), p(5), p(6));
let mut m = false;
m |= (p6 - p0).abs() > threshold_bd;
m |= (q6 - q0).abs() > threshold_bd;
m |= (p5 - p0).abs() > threshold_bd;
m |= (q5 - q0).abs() > threshold_bd;
m |= (p4 - p0).abs() > threshold_bd;
m |= (q4 - q0).abs() > threshold_bd;
!m
} else {
false
};
if filter_size == 4 || !flat_mask {
narrow_filter(plane_buf, x, y, dx, dy, hev_mask, bd);
} else if filter_size == 8 || !flat_mask2 {
wide_filter(plane_buf, x, y, plane, dx, dy, 3);
} else {
wide_filter(plane_buf, x, y, plane, dx, dy, 4);
}
}
fn filter4_clamp(value: i32, bd: u32) -> i32 {
let lo = -(1_i32 << (bd - 1));
let hi = (1_i32 << (bd - 1)) - 1;
value.clamp(lo, hi)
}
fn round2(x: i32, n: u32) -> i32 {
if n == 0 { x } else { (x + (1 << (n - 1))) >> n }
}
fn narrow_filter(
plane_buf: &mut Plane,
x: usize,
y: usize,
dx: usize,
dy: usize,
hev_mask: bool,
bd: u32,
) {
let bias = 0x80 << (bd - 8);
let read = |buf: &Plane, k: isize| get(buf, x, y, dx, dy, k);
let q0 = read(plane_buf, 0);
let q1 = read(plane_buf, 1);
let p0 = read(plane_buf, -1);
let p1 = read(plane_buf, -2);
let ps1 = p1 - bias;
let ps0 = p0 - bias;
let qs0 = q0 - bias;
let qs1 = q1 - bias;
let mut filter = if hev_mask {
filter4_clamp(ps1 - qs1, bd)
} else {
0
};
filter = filter4_clamp(filter + 3 * (qs0 - ps0), bd);
let filter1 = filter4_clamp(filter + 4, bd) >> 3;
let filter2 = filter4_clamp(filter + 3, bd) >> 3;
let oq0 = filter4_clamp(qs0 - filter1, bd) + bias;
let op0 = filter4_clamp(ps0 + filter2, bd) + bias;
put(plane_buf, x, y, dx, dy, 0, oq0);
put(plane_buf, x, y, dx, dy, -1, op0);
if !hev_mask {
let f = round2(filter1, 1);
let oq1 = filter4_clamp(qs1 - f, bd) + bias;
let op1 = filter4_clamp(ps1 + f, bd) + bias;
put(plane_buf, x, y, dx, dy, 1, oq1);
put(plane_buf, x, y, dx, dy, -2, op1);
}
}
fn wide_filter(
plane_buf: &mut Plane,
x: usize,
y: usize,
plane: usize,
dx: usize,
dy: usize,
log2_size: u32,
) {
let n: isize = if log2_size == 4 {
6
} else if plane == 0 {
3
} else {
2
};
let n2: isize = if log2_size == 3 && plane == 0 { 0 } else { 1 };
let mut filtered = [0_i32; 12]; for i in -n..n {
let mut t = 0;
for j in -n..=n {
let pos = (i + j).clamp(-(n + 1), n);
let tap = if j.abs() <= n2 { 2 } else { 1 };
t += get(plane_buf, x, y, dx, dy, pos) * tap;
}
if let Some(cell) = filtered.get_mut((i + n) as usize) {
*cell = round2(t, log2_size);
}
}
for i in -n..n {
let v = filtered.get((i + n) as usize).copied().unwrap_or(0);
put(plane_buf, x, y, dx, dy, i, v);
}
}
fn put(plane_buf: &mut Plane, x: usize, y: usize, dx: usize, dy: usize, k: isize, value: i32) {
let sx = x as isize + dx as isize * k;
let sy = y as isize + dy as isize * k;
if sx < 0 || sy < 0 {
return;
}
plane_buf.set(sx as usize, sy as usize, value.max(0) as u16);
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::indexing_slicing,
clippy::panic,
reason = "tests operate on known-good values and assert shapes directly"
)]
mod tests {
use super::super::frame::LoopFilter;
use super::*;
fn lf(level: [u8; 4]) -> LoopFilter {
LoopFilter {
level,
sharpness: 0,
delta_enabled: true,
ref_deltas: [1, 0, 0, 0, -1, -1, -1, -1],
mode_deltas: [0, 0],
}
}
#[test]
fn a_flat_region_is_left_unchanged() {
let (w, h) = (16, 16);
let mut plane = Plane::new(w, h);
for y in 0..h {
for x in 0..w {
plane.set(x, y, 137);
}
}
let before = plane.clone();
let grid = vec![1_u8; (w / MI_SIZE) * (h / MI_SIZE)];
let mut planes = [plane];
Deblock {
planes: &mut planes,
loop_filter: &lf([20, 20, 20, 20]),
bit_depth: 8,
num_planes: 1,
subsampling_x: 0,
subsampling_y: 0,
mi_rows: h / MI_SIZE,
mi_cols: w / MI_SIZE,
frame_width: w,
frame_height: h,
lf_tx_sizes: std::slice::from_ref(&grid),
delta_lfs: &[],
delta_lf_multi: false,
segment_ids: &[],
segment_lf: [[0; FRAME_LF_COUNT]; MAX_SEGMENTS],
}
.run();
assert_eq!(planes[0].row(0), before.row(0));
assert_eq!(planes[0].row(7), before.row(7));
assert_eq!(planes[0].row(8), before.row(8));
}
#[test]
fn zero_luma_level_skips_filtering() {
let (w, h) = (16, 16);
let mut plane = Plane::new(w, h);
for y in 0..h {
for x in 0..w {
plane.set(x, y, if x < 8 { 40 } else { 200 });
}
}
let before = plane.clone();
let grid = vec![1_u8; (w / MI_SIZE) * (h / MI_SIZE)];
let mut planes = [plane];
Deblock {
planes: &mut planes,
loop_filter: &lf([0, 0, 0, 0]),
bit_depth: 8,
num_planes: 1,
subsampling_x: 0,
subsampling_y: 0,
mi_rows: h / MI_SIZE,
mi_cols: w / MI_SIZE,
frame_width: w,
frame_height: h,
lf_tx_sizes: std::slice::from_ref(&grid),
delta_lfs: &[],
delta_lf_multi: false,
segment_ids: &[],
segment_lf: [[0; FRAME_LF_COUNT]; MAX_SEGMENTS],
}
.run();
assert_eq!(planes[0].row(0), before.row(0));
}
#[test]
fn subsampled_chroma_is_filtered_on_its_own_transform_grid() {
let (w, h) = (32, 32);
let step_at = |edge: usize| {
let mut p = Plane::new(w / 2, h / 2);
for y in 0..h / 2 {
for x in 0..w / 2 {
p.set(x, y, if x < edge { 100 } else { 140 });
}
}
p
};
let luma = Plane::new(w, h);
let mut planes = [luma, step_at(8), step_at(4)];
let grid = vec![1_u8; (w / MI_SIZE) * (h / MI_SIZE)];
let grids = [grid.clone(), grid.clone(), grid];
Deblock {
planes: &mut planes,
loop_filter: &lf([32, 32, 32, 32]),
bit_depth: 8,
num_planes: 3,
subsampling_x: 1,
subsampling_y: 1,
mi_rows: h / MI_SIZE,
mi_cols: w / MI_SIZE,
frame_width: w,
frame_height: h,
lf_tx_sizes: &grids,
delta_lfs: &[],
delta_lf_multi: false,
segment_ids: &[],
segment_lf: [[0; FRAME_LF_COUNT]; MAX_SEGMENTS],
}
.run();
let u = planes[1].row(5).unwrap();
assert!(u[7] > 100 && u[8] < 140, "edge on the tx grid: {u:?}");
let v = planes[2].row(5).unwrap();
assert_eq!((v[3], v[4]), (100, 140), "edge inside a transform: {v:?}");
}
fn step_edge_filtered(deltas: [i8; FRAME_LF_COUNT], multi: bool) -> bool {
step_edge_filtered_in_segment(deltas, multi, [0; FRAME_LF_COUNT])
}
fn step_edge_filtered_in_segment(
deltas: [i8; FRAME_LF_COUNT],
multi: bool,
segment_lf: [i32; FRAME_LF_COUNT],
) -> bool {
let (w, h) = (16, 16);
let mut plane = Plane::new(w, h);
for y in 0..h {
for x in 0..w {
plane.set(x, y, if x < 8 { 100 } else { 140 });
}
}
let grid = vec![1_u8; (w / MI_SIZE) * (h / MI_SIZE)];
let delta_lfs = vec![deltas; (w / MI_SIZE) * (h / MI_SIZE)];
let segment_ids = vec![1_u8; (w / MI_SIZE) * (h / MI_SIZE)];
let mut offsets = [[0; FRAME_LF_COUNT]; MAX_SEGMENTS];
offsets[1] = segment_lf;
let loop_filter = LoopFilter {
delta_enabled: false,
..lf([32, 32, 0, 0])
};
let mut planes = [plane];
Deblock {
planes: &mut planes,
loop_filter: &loop_filter,
bit_depth: 8,
num_planes: 1,
subsampling_x: 0,
subsampling_y: 0,
mi_rows: h / MI_SIZE,
mi_cols: w / MI_SIZE,
frame_width: w,
frame_height: h,
lf_tx_sizes: std::slice::from_ref(&grid),
delta_lfs: &delta_lfs,
delta_lf_multi: multi,
segment_ids: &segment_ids,
segment_lf: offsets,
}
.run();
let row = planes[0].row(4).unwrap();
(row[7], row[8]) != (100, 140)
}
#[test]
fn a_block_delta_moves_the_filter_level() {
assert!(step_edge_filtered([0; FRAME_LF_COUNT], false));
assert!(!step_edge_filtered([-32, 0, 0, 0], false));
assert!(step_edge_filtered([63, 0, 0, 0], false));
}
#[test]
fn a_segment_offset_moves_the_filter_level_for_its_direction() {
assert!(!step_edge_filtered_in_segment(
[0; FRAME_LF_COUNT],
false,
[-32, 0, 0, 0]
));
assert!(step_edge_filtered_in_segment(
[0; FRAME_LF_COUNT],
false,
[0, -32, 0, 0]
));
assert!(!step_edge_filtered_in_segment(
[16, 0, 0, 0],
false,
[-48, 0, 0, 0]
));
}
#[test]
fn delta_lf_multi_picks_the_delta_for_the_edge_direction() {
assert!(!step_edge_filtered([-32, 0, 0, 0], true));
assert!(step_edge_filtered([0, -32, 0, 0], true));
assert!(step_edge_filtered([0, -32, -32, -32], false));
}
#[test]
fn a_step_edge_is_smoothed() {
let (w, h) = (16, 16);
let mut plane = Plane::new(w, h);
for y in 0..h {
for x in 0..w {
plane.set(x, y, if x < 8 { 100 } else { 140 });
}
}
let grid = vec![1_u8; (w / MI_SIZE) * (h / MI_SIZE)];
let mut planes = [plane];
Deblock {
planes: &mut planes,
loop_filter: &lf([32, 32, 0, 0]),
bit_depth: 8,
num_planes: 1,
subsampling_x: 0,
subsampling_y: 0,
mi_rows: h / MI_SIZE,
mi_cols: w / MI_SIZE,
frame_width: w,
frame_height: h,
lf_tx_sizes: std::slice::from_ref(&grid),
delta_lfs: &[],
delta_lf_multi: false,
segment_ids: &[],
segment_lf: [[0; FRAME_LF_COUNT]; MAX_SEGMENTS],
}
.run();
let row = planes[0].row(4).unwrap();
assert!(row[7] > 100, "left edge sample should rise, got {}", row[7]);
assert!(
row[8] < 140,
"right edge sample should fall, got {}",
row[8]
);
}
}