#![allow(
clippy::cast_possible_truncation,
reason = "border/dimension values are provably in range; casts reproduce the \
reference decoder's byte handling"
)]
use crate::{Codec, Dimensions, Error, Image, Metadata, PixelLayout, Result};
use crate::lossy::decode::{FilterHeader, MbData, SegmentHeader};
use crate::lossy::loop_filter::{self, FInfo};
use crate::lossy::predict;
use crate::lossy::prelude::*;
use crate::lossy::{idct, yuv};
pub(crate) struct Planes {
pub(crate) y: Vec<u8>,
pub(crate) u: Vec<u8>,
pub(crate) v: Vec<u8>,
pub(crate) y_stride: usize,
pub(crate) uv_stride: usize,
}
impl Planes {
pub(crate) fn new(mb_w: usize, mb_h: usize) -> Self {
let y_stride = 1 + mb_w * 16 + 4;
let uv_stride = 1 + mb_w * 8;
let mut y = vec![0u8; y_stride * (1 + mb_h * 16)];
let mut u = vec![0u8; uv_stride * (1 + mb_h * 8)];
let mut v = vec![0u8; uv_stride * (1 + mb_h * 8)];
fill_borders(&mut y, y_stride, 1 + mb_h * 16);
fill_borders(&mut u, uv_stride, 1 + mb_h * 8);
fill_borders(&mut v, uv_stride, 1 + mb_h * 8);
Self {
y,
u,
v,
y_stride,
uv_stride,
}
}
}
fn fill_borders(plane: &mut [u8], stride: usize, rows: usize) {
plane[0..stride].fill(127);
for row in 1..rows {
plane[row * stride] = 129;
}
}
#[cfg(feature = "oracle")]
fn crop(plane: &[u8], stride: usize, w: usize, h: usize) -> Vec<u8> {
let mut out = Vec::with_capacity(w * h);
for y in 0..h {
let base = (y + 1) * stride + 1;
out.extend_from_slice(&plane[base..base + w]);
}
out
}
#[cfg(feature = "oracle")]
impl Planes {
pub(crate) fn crop_y(&self, w: usize, h: usize) -> Vec<u8> {
crop(&self.y, self.y_stride, w, h)
}
pub(crate) fn crop_u(&self, w: usize, h: usize) -> Vec<u8> {
crop(&self.u, self.uv_stride, w, h)
}
pub(crate) fn crop_v(&self, w: usize, h: usize) -> Vec<u8> {
crop(&self.v, self.uv_stride, w, h)
}
}
pub(crate) fn reconstruct_mb(
planes: &mut Planes,
block: &MbData,
mb_x: usize,
mb_y: usize,
mb_w: usize,
) {
let y_off = (mb_y * 16 + 1) * planes.y_stride + (mb_x * 16 + 1);
let uv_off = (mb_y * 8 + 1) * planes.uv_stride + (mb_x * 8 + 1);
reconstruct_mb_at(
planes,
block,
y_off,
uv_off,
mb_y > 0,
mb_x > 0,
mb_x == mb_w - 1,
);
}
pub(crate) fn reconstruct_mb_at(
planes: &mut Planes,
block: &MbData,
y_off: usize,
uv_off: usize,
has_top: bool,
has_left: bool,
is_rightmost: bool,
) {
if block.is_i4x4 {
reconstruct_luma_i4x4(planes, block, y_off, has_top, is_rightmost);
} else {
reconstruct_luma_16(planes, block, y_off, has_top, has_left);
}
reconstruct_chroma(planes, block, uv_off, has_top, has_left);
}
fn add_residual(plane: &mut [u8], coeffs: &[i16; 384], idx: usize, sub: usize, stride: usize) {
let block = &coeffs[idx * 16..idx * 16 + 16];
if block[1..].iter().any(|&c| c != 0) {
idct::transform_one(block, plane, sub, stride);
} else if block[0] != 0 {
idct::transform_dc(block[0], plane, sub, stride);
}
}
fn reconstruct_luma_16(
planes: &mut Planes,
block: &MbData,
y_off: usize,
has_top: bool,
has_left: bool,
) {
let stride = planes.y_stride;
predict::predict_luma16(
&mut planes.y,
y_off,
stride,
block.imodes[0],
has_top,
has_left,
);
for n in 0..16 {
let sub = y_off + (n % 4) * 4 + (n / 4) * 4 * stride;
add_residual(&mut planes.y, &block.coeffs, n, sub, stride);
}
}
fn reconstruct_luma_i4x4(
planes: &mut Planes,
block: &MbData,
y_off: usize,
has_top: bool,
is_rightmost: bool,
) {
let stride = planes.y_stride;
fill_top_right_lane(&mut planes.y, y_off, stride, has_top, is_rightmost);
for n in 0..16 {
let sub = y_off + (n % 4) * 4 + (n / 4) * 4 * stride;
predict::predict_luma4(&mut planes.y, sub, stride, block.imodes[n]);
add_residual(&mut planes.y, &block.coeffs, n, sub, stride);
}
}
pub(crate) fn fill_top_right_lane(
y: &mut [u8],
y_off: usize,
stride: usize,
has_top: bool,
is_rightmost: bool,
) {
let tr = y_off - stride + 16;
if !has_top {
} else if is_rightmost {
let last = y[y_off - stride + 15];
y[tr..tr + 4].fill(last);
}
let lane = [y[tr], y[tr + 1], y[tr + 2], y[tr + 3]];
for r in [3usize, 7, 11] {
let dst = y_off + r * stride + 16;
y[dst..dst + 4].copy_from_slice(&lane);
}
}
fn reconstruct_chroma(
planes: &mut Planes,
block: &MbData,
uv_off: usize,
has_top: bool,
has_left: bool,
) {
let stride = planes.uv_stride;
predict::predict_chroma8(
&mut planes.u,
uv_off,
stride,
block.uvmode,
has_top,
has_left,
);
predict::predict_chroma8(
&mut planes.v,
uv_off,
stride,
block.uvmode,
has_top,
has_left,
);
for n in 0..4 {
let sub = uv_off + (n % 2) * 4 + (n / 2) * 4 * stride;
add_residual(&mut planes.u, &block.coeffs, 16 + n, sub, stride);
add_residual(&mut planes.v, &block.coeffs, 20 + n, sub, stride);
}
}
pub(crate) fn compute_fstrengths(
segment: &SegmentHeader,
filter: &FilterHeader,
) -> [[FInfo; 2]; 4] {
let mut table = [[FInfo::default(); 2]; 4];
for (s, seg) in table.iter_mut().enumerate() {
let base = if segment.use_segment {
let mut b = segment.filter_strength[s];
if !segment.absolute_delta {
b += filter.level;
}
b
} else {
filter.level
};
for (i4x4, info) in seg.iter_mut().enumerate() {
*info = strength_for(filter, base, i4x4 == 1);
}
}
table
}
fn strength_for(filter: &FilterHeader, base: i32, i4x4: bool) -> FInfo {
let mut level = base;
if filter.use_lf_delta {
level += filter.ref_lf_delta[0]; if i4x4 {
level += filter.mode_lf_delta[0]; }
}
let level = level.clamp(0, 63);
if level == 0 {
return FInfo::default();
}
let mut inner = level;
if filter.sharpness > 0 {
inner >>= if filter.sharpness > 4 { 2 } else { 1 };
let cap = 9 - filter.sharpness;
if inner > cap {
inner = cap;
}
}
if inner < 1 {
inner = 1;
}
FInfo {
f_limit: 2 * level + inner,
f_ilevel: inner,
f_inner: i4x4,
hev_thresh: if level >= 40 {
2
} else {
i32::from(level >= 15)
},
}
}
pub(crate) fn resolve_finfo(table: [[FInfo; 2]; 4], block: &MbData, use_skip: bool) -> FInfo {
let mut skip = use_skip && block.skip;
if !skip {
skip = (block.non_zero_y | block.non_zero_uv) == 0;
}
let mut info = table[usize::from(block.segment)][usize::from(block.is_i4x4)];
info.f_inner = block.is_i4x4 || !skip;
info
}
pub(crate) fn filter_mb_row(
planes: &mut Planes,
finfo_row: &[FInfo],
mb_y: usize,
filter_type: u8,
) {
if filter_type == 0 {
return;
}
for (mb_x, &info) in finfo_row.iter().enumerate() {
let (do_left, do_top) = (mb_x > 0, mb_y > 0);
let y_off = (mb_y * 16 + 1) * planes.y_stride + (mb_x * 16 + 1);
let uv_off = (mb_y * 8 + 1) * planes.uv_stride + (mb_x * 8 + 1);
if filter_type == 1 {
loop_filter::filter_mb_simple(
&mut planes.y,
y_off,
planes.y_stride,
do_left,
do_top,
info,
);
} else {
loop_filter::filter_mb_normal(
&mut planes.y,
y_off,
planes.y_stride,
&mut planes.u,
&mut planes.v,
uv_off,
planes.uv_stride,
do_left,
do_top,
info,
);
}
}
}
pub(crate) fn filter_frame(
planes: &mut Planes,
finfo: &[FInfo],
mb_w: usize,
mb_h: usize,
filter_type: u8,
) {
for mb_y in 0..mb_h {
filter_mb_row(
planes,
&finfo[mb_y * mb_w..(mb_y + 1) * mb_w],
mb_y,
filter_type,
);
}
}
pub(crate) fn to_image(planes: &Planes, width: usize, height: usize) -> Result<Image> {
let y0 = planes.y_stride + 1;
let uv0 = planes.uv_stride + 1;
let rgba = yuv::yuv420_to_rgba(
&yuv::Yuv420Ref {
y: &planes.y[y0..],
y_stride: planes.y_stride,
u: &planes.u[uv0..],
v: &planes.v[uv0..],
uv_stride: planes.uv_stride,
},
width,
height,
);
let dims = Dimensions::new(
u32::try_from(width).unwrap_or(0),
u32::try_from(height).unwrap_or(0),
)
.map_err(|_| Error::InvalidBitstream {
codec: Codec::Lossy,
})?;
Ok(Image::from_parts(
dims,
PixelLayout::Rgba8,
rgba,
false,
Metadata::none(),
))
}
#[cfg(test)]
mod tests {
#![allow(
clippy::doc_markdown,
clippy::inconsistent_struct_constructor,
reason = "these KATs construct FInfo / header literals and cite informal \
RFC 6386 / libwebp identifiers in comments; field order and \
bare names mirror the reference for readability"
)]
use super::{Planes, compute_fstrengths, fill_top_right_lane, filter_frame, resolve_finfo};
use crate::lossy::decode::{FilterHeader, MbData, SegmentHeader};
use crate::lossy::loop_filter::FInfo;
fn segment(
use_segment: bool,
absolute_delta: bool,
filter_strength: [i32; 4],
) -> SegmentHeader {
SegmentHeader {
use_segment,
update_map: false,
absolute_delta,
quantizer: [0; 4],
filter_strength,
}
}
fn mb(segment: u8, is_i4x4: bool, skip: bool, non_zero_y: u32, non_zero_uv: u32) -> MbData {
MbData {
segment,
is_i4x4,
skip,
non_zero_y,
non_zero_uv,
..MbData::default()
}
}
fn assert_finfo(info: FInfo, f_limit: i32, f_ilevel: i32, f_inner: bool, hev_thresh: i32) {
assert_eq!(info.f_limit, f_limit, "f_limit");
assert_eq!(info.f_ilevel, f_ilevel, "f_ilevel");
assert_eq!(info.f_inner, f_inner, "f_inner");
assert_eq!(info.hev_thresh, hev_thresh, "hev_thresh");
}
#[test]
fn fstrengths_plain_level_no_sharpness_no_deltas() {
let seg = segment(false, false, [0; 4]);
let filter = FilterHeader {
level: 20,
..Default::default()
};
let table = compute_fstrengths(&seg, &filter);
for lane in &table {
assert_finfo(lane[0], 60, 20, false, 1); assert_finfo(lane[1], 60, 20, true, 1); }
}
#[test]
fn fstrengths_segment_absolute_uses_filter_strength_directly() {
let seg = segment(true, true, [7, 20, 40, 3]);
let filter = FilterHeader {
level: 10,
..Default::default()
};
let table = compute_fstrengths(&seg, &filter);
let expect = [(21, 7, 0), (60, 20, 1), (120, 40, 2), (9, 3, 0)];
for (s, &(fl, il, hev)) in expect.iter().enumerate() {
assert_finfo(table[s][0], fl, il, false, hev);
assert_finfo(table[s][1], fl, il, true, hev);
}
}
#[test]
fn fstrengths_segment_delta_adds_base_level() {
let seg = segment(true, false, [7, 20, 40, 3]);
let filter = FilterHeader {
level: 10,
..Default::default()
};
let table = compute_fstrengths(&seg, &filter);
let expect = [(51, 17, 1), (90, 30, 1), (150, 50, 2), (39, 13, 0)];
for (s, &(fl, il, hev)) in expect.iter().enumerate() {
assert_finfo(table[s][0], fl, il, false, hev);
assert_finfo(table[s][1], fl, il, true, hev);
}
}
#[test]
fn fstrengths_lf_delta_adds_ref_then_mode_on_i4x4() {
let seg = segment(false, false, [0; 4]);
let filter = FilterHeader {
level: 20,
use_lf_delta: true,
ref_lf_delta: [5, 0, 0, 0],
mode_lf_delta: [8, 0, 0, 0],
..Default::default()
};
let table = compute_fstrengths(&seg, &filter);
for lane in &table {
assert_finfo(lane[0], 75, 25, false, 1);
assert_finfo(lane[1], 99, 33, true, 1);
}
}
#[test]
fn fstrengths_sharpness_shifts_and_caps_inner_level() {
let inner = |level: i32, sharpness: i32| {
let seg = segment(false, false, [0; 4]);
let filter = FilterHeader {
level,
sharpness,
..Default::default()
};
compute_fstrengths(&seg, &filter)[0][0]
};
assert_finfo(inner(40, 1), 88, 8, false, 2);
assert_finfo(inner(40, 5), 84, 4, false, 2);
assert_finfo(inner(6, 3), 15, 3, false, 0);
assert_finfo(inner(1, 1), 3, 1, false, 0);
assert_finfo(inner(63, 7), 128, 2, false, 2);
}
#[test]
fn fstrengths_level_clamped_to_zero_disables_filtering() {
let seg = segment(false, false, [0; 4]);
let filter = FilterHeader {
level: 3,
use_lf_delta: true,
ref_lf_delta: [-10, 0, 0, 0],
mode_lf_delta: [20, 0, 0, 0],
..Default::default()
};
let table = compute_fstrengths(&seg, &filter);
for lane in &table {
assert_finfo(lane[0], 0, 0, false, 0);
assert_finfo(lane[1], 39, 13, true, 0);
}
}
fn indexed_table() -> [[FInfo; 2]; 4] {
let mut table = [[FInfo::default(); 2]; 4];
for (s, lane) in table.iter_mut().enumerate() {
for (i, cell) in lane.iter_mut().enumerate() {
*cell = FInfo {
f_limit: 100 + 10 * i32::try_from(s).unwrap() + i32::try_from(i).unwrap(),
f_ilevel: 7,
f_inner: false,
hev_thresh: 1,
};
}
}
table
}
#[test]
fn resolve_finfo_residual_present_sets_inner_and_indexes_segment() {
let table = indexed_table();
let block = mb(2, false, false, 0x5, 0);
let info = resolve_finfo(table, &block, true);
assert_finfo(info, 120, 7, true, 1);
}
#[test]
fn resolve_finfo_explicit_skip_clears_inner() {
let table = indexed_table();
let block = mb(1, false, true, 0, 0);
let info = resolve_finfo(table, &block, true);
assert_finfo(info, 110, 7, false, 1);
}
#[test]
fn resolve_finfo_i4x4_forces_inner_even_when_skipped() {
let table = indexed_table();
let block = mb(3, true, true, 0, 0);
let info = resolve_finfo(table, &block, true);
assert_finfo(info, 131, 7, true, 1);
}
#[test]
fn resolve_finfo_use_skip_false_ignores_block_skip_but_checks_residual() {
let table = indexed_table();
let block = mb(0, false, true, 0, 0);
let info = resolve_finfo(table, &block, false);
assert_finfo(info, 100, 7, false, 1);
}
#[test]
fn resolve_finfo_chroma_residual_alone_sets_inner() {
let table = indexed_table();
let block = mb(1, false, true, 0, 0x2);
let info = resolve_finfo(table, &block, false);
assert_finfo(info, 110, 7, true, 1);
}
#[test]
fn resolve_finfo_i4x4_no_residual_indexes_i4x4_lane() {
let table = indexed_table();
let block = mb(2, true, false, 0, 0);
let info = resolve_finfo(table, &block, true);
assert_finfo(info, 121, 7, true, 1);
}
fn stepped_left_edge_planes() -> Planes {
let mut planes = Planes::new(2, 1);
let stride = planes.y_stride; for row in 1..17 {
for col in 1..stride {
planes.y[row * stride + col] = if col < 17 { 128 } else { 134 };
}
}
planes
}
#[test]
fn filter_frame_simple_smooths_left_mb_edge() {
let mut planes = stepped_left_edge_planes();
let stride = planes.y_stride;
let info = FInfo {
f_limit: 20,
f_ilevel: 9,
f_inner: false,
hev_thresh: 0,
};
let finfo = vec![info; 2];
filter_frame(&mut planes, &finfo, 2, 1, 1);
for r in 1..17 {
assert_eq!(planes.y[r * stride + 15], 128, "p1 untouched, row {r}");
assert_eq!(planes.y[r * stride + 16], 129, "p0 filtered, row {r}");
assert_eq!(planes.y[r * stride + 17], 132, "q0 filtered, row {r}");
assert_eq!(planes.y[r * stride + 18], 134, "q1 untouched, row {r}");
assert_eq!(planes.y[r * stride + 5], 128, "mb_x=0 interior, row {r}");
assert_eq!(planes.y[r * stride + 30], 134, "mb_x=1 interior, row {r}");
}
}
#[test]
fn filter_frame_simple_smooths_top_mb_edge() {
let mut planes = Planes::new(1, 2);
let stride = planes.y_stride; for row in 1..33 {
for col in 1..stride {
planes.y[row * stride + col] = if row < 17 { 128 } else { 134 };
}
}
let info = FInfo {
f_limit: 20,
f_ilevel: 9,
f_inner: false,
hev_thresh: 0,
};
let finfo = vec![info; 2];
filter_frame(&mut planes, &finfo, 1, 2, 1);
for c in 1..17 {
assert_eq!(planes.y[15 * stride + c], 128, "p1 untouched, col {c}");
assert_eq!(planes.y[16 * stride + c], 129, "p0 filtered, col {c}");
assert_eq!(planes.y[17 * stride + c], 132, "q0 filtered, col {c}");
assert_eq!(planes.y[18 * stride + c], 134, "q1 untouched, col {c}");
}
}
#[test]
fn filter_frame_type0_leaves_plane_untouched() {
let mut planes = stepped_left_edge_planes();
let before = planes.y.clone();
let info = FInfo {
f_limit: 20,
f_ilevel: 9,
f_inner: false,
hev_thresh: 0,
};
let finfo = vec![info; 2];
filter_frame(&mut planes, &finfo, 2, 1, 0);
assert_eq!(planes.y, before, "filter_type 0 must be a no-op");
}
#[test]
fn filter_frame_zero_flimit_is_a_per_mb_noop() {
let mut planes = stepped_left_edge_planes();
let before = planes.y.clone();
let finfo = vec![FInfo::default(); 2]; filter_frame(&mut planes, &finfo, 2, 1, 1);
assert_eq!(planes.y, before, "f_limit==0 must disable filtering");
}
#[test]
fn resolve_finfo_equal_nonzero_luma_and_chroma_uses_or_not_xor() {
let table = indexed_table();
let block = mb(0, false, false, 0x5, 0x5);
let info = resolve_finfo(table, &block, true);
assert_finfo(info, 100, 7, true, 1);
}
#[test]
fn fstrengths_sharpness_shift_selector_branches_strictly_above_four() {
let seg = segment(false, false, [0; 4]);
let filter = FilterHeader {
level: 12,
sharpness: 4,
..Default::default()
};
let info = compute_fstrengths(&seg, &filter)[0][0];
assert_finfo(info, 29, 5, false, 0);
}
#[test]
fn fill_top_right_lane_replicates_above_right_samples() {
let mut planes = Planes::new(2, 2);
let stride = planes.y_stride; let y_off = 630usize;
let tr = y_off - stride + 16; for (i, v) in [90u8, 91, 92, 100, 101, 102, 103].into_iter().enumerate() {
planes.y[tr - 3 + i] = v; }
fill_top_right_lane(&mut planes.y, y_off, stride, true, false);
let lane = [100u8, 101, 102, 103]; for r in [3usize, 7, 11] {
let dst = y_off + r * stride + 16;
assert_eq!(&planes.y[dst..dst + 4], &lane, "row {r} replica");
}
}
#[test]
fn fill_top_right_lane_rightmost_fills_from_own_last_top_sample() {
let mut planes = Planes::new(2, 2);
let stride = planes.y_stride; let y_off = 630usize;
let tr = y_off - stride + 16; planes.y[y_off - stride + 15] = 150; for (i, v) in [10u8, 11, 12, 13].into_iter().enumerate() {
planes.y[tr + i] = v; }
fill_top_right_lane(&mut planes.y, y_off, stride, true, true);
assert_eq!(
&planes.y[tr..tr + 4],
&[150u8; 4],
"lane filled from last top"
);
for r in [3usize, 7, 11] {
let dst = y_off + r * stride + 16;
assert_eq!(&planes.y[dst..dst + 4], &[150u8; 4], "row {r} replica");
}
}
#[cfg(feature = "oracle")]
#[test]
fn crop_extracts_exact_top_left_region_from_each_plane() {
let mut planes = Planes::new(1, 1);
let ys = planes.y_stride; let us = planes.uv_stride; for (r, base) in [10u8, 20].into_iter().enumerate() {
let row = (r + 1) * ys;
planes.y[row + 1] = base;
planes.y[row + 2] = base + 1;
planes.y[row + 3] = base + 2;
}
assert_eq!(planes.crop_y(3, 2), vec![10, 11, 12, 20, 21, 22]);
for (r, base) in [30u8, 40].into_iter().enumerate() {
let row = (r + 1) * us;
planes.u[row + 1] = base;
planes.u[row + 2] = base + 1;
}
assert_eq!(planes.crop_u(2, 2), vec![30, 31, 40, 41]);
for (r, base) in [50u8, 60].into_iter().enumerate() {
let row = (r + 1) * us;
planes.v[row + 1] = base;
planes.v[row + 2] = base + 1;
}
assert_eq!(planes.crop_v(2, 2), vec![50, 51, 60, 61]);
}
}