#[inline]
pub(crate) fn predict_planar_into(above: &[u16], left: &[u16], n: usize, pred: &mut [u16]) {
debug_assert!(pred.len() >= n * n);
let top_right = above[n] as i32;
let bottom_left = left[n] as i32;
let log2 = n.trailing_zeros();
for (row, (&left, pred_row)) in left[..n]
.iter()
.zip(pred[..n * n].chunks_exact_mut(n))
.enumerate()
{
let lr = left as i32;
let v_w = (n - 1 - row) as i32;
let v_b = (row + 1) as i32 * bottom_left;
for (col, (dst, &top)) in pred_row.iter_mut().zip(&above[..n]).enumerate() {
let h = (n - 1 - col) as i32 * lr + (col + 1) as i32 * top_right;
let v = v_w * top as i32 + v_b;
*dst = ((h + v + n as i32) >> (log2 + 1)) as u16;
}
}
}
pub(crate) fn should_filter_refs(mode: u8, n: usize) -> bool {
if mode == DC || n == 4 {
return false;
}
let dist = if mode == PLANAR {
10
} else {
(mode as i32 - 26).abs().min((mode as i32 - 10).abs())
};
let thresh = match n {
8 => 7,
16 => 1,
_ => 0, };
dist > thresh
}
static INTRA_PRED_ANGLE: [i32; 35] = [
0, 0, 32, 26, 21, 17, 13, 9, 5, 2, 0, -2, -5, -9, -13, -17, -21, -26, -32, -26, -21, -17, -13,
-9, -5, -2, 0, 2, 5, 9, 13, 17, 21, 26, 32,
];
static INV_ANGLE: [i32; 35] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4096, -1638, -910, -630, -482, -390, -315, -256, -315, -390,
-482, -630, -910, -1638, -4096, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
pub(crate) const PLANAR: u8 = 0;
pub(crate) const DC: u8 = 1;
pub(crate) struct AngularScratch {
refs: [i32; 193],
indices: [i32; 32],
fractions: [i32; 32],
}
impl AngularScratch {
pub(crate) const fn new() -> Self {
Self {
refs: [0; 193],
indices: [0; 32],
fractions: [0; 32],
}
}
}
#[inline]
pub(crate) fn predict_dc_into(
above: &[u16],
left: &[u16],
n: usize,
filter_boundary: bool,
pred: &mut [u16],
) {
debug_assert!(pred.len() >= n * n);
let sum = above[..n]
.iter()
.zip(&left[..n])
.fold(0i32, |sum, (&above, &left)| {
sum + above as i32 + left as i32
});
let log2 = n.trailing_zeros();
let dc = (sum + n as i32) >> (log2 + 1);
pred[..n * n].fill(dc as u16);
if filter_boundary && n < 32 {
pred[0] = ((left[0] as i32 + 2 * dc + above[0] as i32 + 2) >> 2) as u16;
for (dst, &above) in pred[1..n].iter_mut().zip(&above[1..n]) {
*dst = ((above as i32 + 3 * dc + 2) >> 2) as u16;
}
for (row, &left) in pred[..n * n].chunks_exact_mut(n).skip(1).zip(&left[1..n]) {
row[0] = ((left as i32 + 3 * dc + 2) >> 2) as u16;
}
}
}
#[inline]
#[allow(clippy::too_many_arguments)]
pub(crate) fn predict_angular_into(
corner: u16,
above: &[u16],
left: &[u16],
n: usize,
mode: u8,
filter_boundary: bool,
max_val: i32,
pred: &mut [u16],
scratch: &mut AngularScratch,
) {
match n {
4 => predict_angular_n::<4>(
corner,
above,
left,
mode,
filter_boundary,
max_val,
pred,
scratch,
),
8 => predict_angular_n::<8>(
corner,
above,
left,
mode,
filter_boundary,
max_val,
pred,
scratch,
),
16 => predict_angular_n::<16>(
corner,
above,
left,
mode,
filter_boundary,
max_val,
pred,
scratch,
),
32 => predict_angular_n::<32>(
corner,
above,
left,
mode,
filter_boundary,
max_val,
pred,
scratch,
),
_ => panic!("unsupported intra prediction size {n}"),
}
}
#[inline]
#[allow(clippy::too_many_arguments)]
fn predict_angular_n<const N: usize>(
corner: u16,
above: &[u16],
left: &[u16],
mode: u8,
filter_boundary: bool,
max_val: i32,
pred: &mut [u16],
scratch: &mut AngularScratch,
) {
debug_assert!(pred.len() >= N * N);
let pred = &mut pred[..N * N];
let angle = INTRA_PRED_ANGLE[mode as usize];
let vertical = mode >= 18;
if angle == 0 {
if vertical {
for (row, &left) in pred.as_chunks_mut::<N>().0.iter_mut().zip(&left[..N]) {
row.copy_from_slice(&above[..N]);
if filter_boundary && N < 32 {
let value = above[0] as i32 + ((left as i32 - corner as i32) >> 1);
row[0] = value.clamp(0, max_val) as u16;
}
}
} else if filter_boundary && N < 32 {
for (dst, &above) in pred[..N].iter_mut().zip(&above[..N]) {
let value = left[0] as i32 + ((above as i32 - corner as i32) >> 1);
*dst = value.clamp(0, max_val) as u16;
}
for (row, &sample) in pred
.as_chunks_mut::<N>()
.0
.iter_mut()
.skip(1)
.zip(&left[1..N])
{
row.fill(sample);
}
} else {
for (row, &sample) in pred.as_chunks_mut::<N>().0.iter_mut().zip(&left[..N]) {
row.fill(sample);
}
}
} else {
let (main, side): (&[u16], &[u16]) = if vertical {
(above, left)
} else {
(left, above)
};
if angle > 0 {
if angle == 32 {
for (offset, row) in pred.as_chunks_mut::<N>().0.iter_mut().enumerate() {
row.copy_from_slice(&main[offset + 1..offset + N + 1]);
}
return;
}
if vertical {
for (outer, row) in pred.as_chunks_mut::<N>().0.iter_mut().enumerate() {
let pos = (outer as i32 + 1) * angle;
let index = (pos >> 5) as usize;
let fraction = pos & 31;
if fraction == 0 {
row.copy_from_slice(&main[index..index + N]);
} else {
for (dst, pair) in row
.iter_mut()
.zip(main[index..index + N + 1].array_windows::<2>())
{
*dst = (((32 - fraction) * pair[0] as i32
+ fraction * pair[1] as i32
+ 16)
>> 5) as u16;
}
}
}
} else {
for (outer, (index, fraction)) in scratch.indices[..N]
.iter_mut()
.zip(scratch.fractions[..N].iter_mut())
.enumerate()
{
let pos = (outer as i32 + 1) * angle;
*index = pos >> 5;
*fraction = pos & 31;
}
for (inner, row) in pred.as_chunks_mut::<N>().0.iter_mut().enumerate() {
for (dst, (&index, &fraction)) in row
.iter_mut()
.zip(scratch.indices[..N].iter().zip(&scratch.fractions[..N]))
{
let base = inner + index as usize;
let a = main[base] as i32;
*dst = if fraction == 0 {
a as u16
} else {
(((32 - fraction) * a + fraction * main[base + 1] as i32 + 16) >> 5)
as u16
};
}
}
}
} else {
const OFF: usize = 64;
scratch.refs[OFF] = corner as i32;
for (dst, &src) in scratch.refs[OFF + 1..OFF + 1 + N]
.iter_mut()
.zip(&main[..N])
{
*dst = src as i32;
}
let inv = INV_ANGLE[mode as usize];
let last = (N as i32 * angle) >> 5;
for x in (last..=-1).rev() {
let index = (x * inv + 128) >> 8;
scratch.refs[(OFF as i32 + x) as usize] = if index <= 0 {
corner as i32
} else {
side[(index - 1) as usize] as i32
};
}
if vertical {
for (outer, row) in pred.as_chunks_mut::<N>().0.iter_mut().enumerate() {
let pos = (outer as i32 + 1) * angle;
let index = pos >> 5;
let fraction = pos & 31;
let start = (OFF as i32 + index + 1) as usize;
if fraction == 0 {
for (dst, &src) in row.iter_mut().zip(&scratch.refs[start..start + N]) {
*dst = src as u16;
}
} else {
for (dst, pair) in row
.iter_mut()
.zip(scratch.refs[start..start + N + 1].array_windows::<2>())
{
*dst =
(((32 - fraction) * pair[0] + fraction * pair[1] + 16) >> 5) as u16;
}
}
}
} else {
for (outer, (index, fraction)) in scratch.indices[..N]
.iter_mut()
.zip(scratch.fractions[..N].iter_mut())
.enumerate()
{
let pos = (outer as i32 + 1) * angle;
*index = pos >> 5;
*fraction = pos & 31;
}
for (inner, row) in pred.as_chunks_mut::<N>().0.iter_mut().enumerate() {
for (dst, (&index, &fraction)) in row
.iter_mut()
.zip(scratch.indices[..N].iter().zip(&scratch.fractions[..N]))
{
let base = (OFF as i32 + inner as i32 + index + 1) as usize;
let a = scratch.refs[base];
*dst = if fraction == 0 {
a as u16
} else {
(((32 - fraction) * a + fraction * scratch.refs[base + 1] + 16) >> 5)
as u16
};
}
}
}
}
}
}
#[inline]
#[allow(clippy::too_many_arguments)]
pub(crate) fn predict_chroma_tb_into(
mode: u8,
corner: u16,
above: &[u16],
left: &[u16],
n: usize,
max_val: i32,
pred: &mut [u16],
scratch: &mut AngularScratch,
) {
match mode {
PLANAR => predict_planar_into(above, left, n, pred),
DC => predict_dc_into(above, left, n, false, pred),
_ => predict_angular_into(corner, above, left, n, mode, false, max_val, pred, scratch),
}
}
pub(crate) fn filter_references(
corner: u16,
above: &[u16],
left: &[u16],
n: usize,
) -> ([u16; 65], [u16; 65]) {
let mut fa = [0u16; 65];
let mut fl = [0u16; 65];
let ext = 2 * n; fa[..=ext].copy_from_slice(&above[..=ext]);
fl[..=ext].copy_from_slice(&left[..=ext]);
if ext >= 1 {
fa[0] = ((corner as i32 + 2 * above[0] as i32 + above[1] as i32 + 2) >> 2) as u16;
}
for x in 1..ext - 1 {
fa[x] = ((above[x - 1] as i32 + 2 * above[x] as i32 + above[x + 1] as i32 + 2) >> 2) as u16;
}
if ext >= 1 {
fl[0] = ((corner as i32 + 2 * left[0] as i32 + left[1] as i32 + 2) >> 2) as u16;
}
for y in 1..ext - 1 {
fl[y] = ((left[y - 1] as i32 + 2 * left[y] as i32 + left[y + 1] as i32 + 2) >> 2) as u16;
}
(fa, fl)
}
fn decode_order(r: usize, c: usize, blk: usize, ctu: usize, ctus_x: usize) -> i64 {
let ctu_r = r / ctu;
let ctu_c = c / ctu;
let ctu_idx = ctu_r * ctus_x + ctu_c;
let sub_r = (r % ctu) / blk;
let sub_c = (c % ctu) / blk;
let grid = ctu / blk; let mut z: u64 = 0;
let mut bit = 0;
let mut sr = sub_r as u64;
let mut sc = sub_c as u64;
let mut g = grid;
while g > 1 {
z |= (sc & 1) << (2 * bit);
z |= (sr & 1) << (2 * bit + 1);
sr >>= 1;
sc >>= 1;
bit += 1;
g >>= 1;
}
let cells = (grid * grid) as i64;
(ctu_idx as i64) * cells + z as i64
}
#[allow(clippy::too_many_arguments)]
fn is_available(
nr: i64,
nc: i64,
block_row: usize,
block_col: usize,
blk: usize,
ctu: usize,
ctus_x: usize,
width: usize,
height: usize,
) -> bool {
if nr < 0 || nc < 0 || nr as usize >= height || nc as usize >= width {
return false;
}
let cur = decode_order(block_row, block_col, blk, ctu, ctus_x);
let nb = decode_order(nr as usize, nc as usize, blk, ctu, ctus_x);
nb < cur
}
pub(crate) fn luma_decode_order(r: usize, c: usize, ctus_x: usize) -> i64 {
decode_order(r, c, 8, 64, ctus_x)
}
#[allow(clippy::too_many_arguments)]
fn substitute_refs(
mut corner: u16,
mut above: [u16; 65],
mut left: [u16; 65],
avail_corner: bool,
avail_above: &[bool; 65],
avail_left: &[bool; 65],
ext: usize,
neutral: u16,
) -> (u16, [u16; 65], [u16; 65]) {
let any = avail_corner
|| avail_above[..=ext].iter().any(|&a| a)
|| avail_left[..=ext].iter().any(|&a| a);
if !any {
return (neutral, [neutral; 65], [neutral; 65]);
}
let total = (ext + 1) + 1 + (ext + 1);
const MAXT: usize = 131;
let mut vals = [0u16; MAXT];
let mut av = [false; MAXT];
for i in 0..=ext {
vals[i] = left[ext - i];
av[i] = avail_left[ext - i];
}
vals[ext + 1] = corner;
av[ext + 1] = avail_corner;
for i in 0..=ext {
vals[(ext + 2) + i] = above[i];
av[(ext + 2) + i] = avail_above[i];
}
let first = av[..total].iter().position(|&a| a).unwrap();
let firstval = vals[first];
for k in 0..first {
vals[k] = firstval;
av[k] = true;
}
for k in 1..total {
if !av[k] {
vals[k] = vals[k - 1];
av[k] = true;
}
}
for i in 0..=ext {
left[ext - i] = vals[i];
}
corner = vals[ext + 1];
for i in 0..=ext {
above[i] = vals[(ext + 2) + i];
}
(corner, above, left)
}
#[derive(Clone, Copy)]
pub(crate) struct ChromaRefGeometry {
pub(crate) stride: usize,
pub(crate) block_row: usize,
pub(crate) block_col: usize,
pub(crate) chroma_h: usize,
pub(crate) n: usize,
pub(crate) sub_w: usize,
pub(crate) sub_h: usize,
pub(crate) luma_w: usize,
pub(crate) luma_h: usize,
pub(crate) luma_ctus_x: usize,
pub(crate) cur_luma_row: usize,
pub(crate) cur_luma_col: usize,
pub(crate) neutral: u16,
}
#[allow(clippy::type_complexity)]
pub(crate) fn get_reference_samples_chroma_pair(
plane_cb: &[u16],
plane_cr: &[u16],
geo: ChromaRefGeometry,
) -> ((u16, [u16; 65], [u16; 65]), (u16, [u16; 65], [u16; 65])) {
let ChromaRefGeometry {
stride,
block_row,
block_col,
chroma_h,
n,
sub_w,
sub_h,
luma_w,
luma_h,
luma_ctus_x,
cur_luma_row,
cur_luma_col,
neutral,
} = geo;
let width = stride;
let ext = 2 * n;
const MAXE: usize = 65;
let mut cb_above = [0u16; MAXE];
let mut cb_left = [0u16; MAXE];
let mut cr_above = [0u16; MAXE];
let mut cr_left = [0u16; MAXE];
let mut avail_above = [false; MAXE];
let mut avail_left = [false; MAXE];
let mut cb_corner = 0u16;
let mut cr_corner = 0u16;
let mut avail_corner = false;
let cur_luma = luma_decode_order(cur_luma_row, cur_luma_col, luma_ctus_x);
let avail = |nr: i64, nc: i64, block_row: usize| -> bool {
if nr < 0 || nc < 0 || nr as usize >= chroma_h || nc as usize >= width {
return false;
}
let lr = (nr as usize) * sub_h;
let lc = (nc as usize) * sub_w;
if lr >= luma_h || lc >= luma_w {
return false;
}
let nb_luma = luma_decode_order(lr, lc, luma_ctus_x);
if nb_luma < cur_luma {
return true;
}
nb_luma == cur_luma && (nr as usize) < block_row
};
{
let nr = block_row as i64 - 1;
let nc = block_col as i64 - 1;
if avail(nr, nc, block_row) {
let idx = (nr as usize) * stride + nc as usize;
cb_corner = plane_cb[idx];
cr_corner = plane_cr[idx];
avail_corner = true;
}
}
{
let nr = block_row as i64 - 1;
for i in 0..=ext {
let nc = block_col as i64 + i as i64;
if avail(nr, nc, block_row) {
let idx = (nr as usize) * stride + nc as usize;
cb_above[i] = plane_cb[idx];
cr_above[i] = plane_cr[idx];
avail_above[i] = true;
}
}
}
{
let nc = block_col as i64 - 1;
for i in 0..=ext {
let nr = block_row as i64 + i as i64;
if avail(nr, nc, block_row) {
let idx = (nr as usize) * stride + nc as usize;
cb_left[i] = plane_cb[idx];
cr_left[i] = plane_cr[idx];
avail_left[i] = true;
}
}
}
let cb = substitute_refs(
cb_corner,
cb_above,
cb_left,
avail_corner,
&avail_above,
&avail_left,
ext,
neutral,
);
let cr = substitute_refs(
cr_corner,
cr_above,
cr_left,
avail_corner,
&avail_above,
&avail_left,
ext,
neutral,
);
(cb, cr)
}
#[derive(Clone, Copy)]
pub(crate) struct LumaRefGeometry {
pub(crate) stride: usize,
pub(crate) block_row: usize,
pub(crate) block_col: usize,
pub(crate) height: usize,
pub(crate) n: usize,
pub(crate) ctu: usize,
pub(crate) ctus_x: usize,
pub(crate) neutral: u16,
}
pub(crate) fn get_reference_samples(
plane: &[u16],
geo: LumaRefGeometry,
) -> (u16, [u16; 65], [u16; 65]) {
let LumaRefGeometry {
stride,
block_row,
block_col,
height,
n,
ctu,
ctus_x,
neutral,
} = geo;
let width = stride;
let ext = 2 * n; const MAXE: usize = 65; let mut above = [0u16; MAXE]; let mut left = [0u16; MAXE]; let mut avail_above = [false; MAXE];
let mut avail_left = [false; MAXE];
let mut corner = 0u16;
let mut avail_corner = false;
{
let nr = block_row as i64 - 1;
let nc = block_col as i64 - 1;
if is_available(nr, nc, block_row, block_col, 8, ctu, ctus_x, width, height) {
corner = plane[(nr as usize) * stride + nc as usize];
avail_corner = true;
}
}
{
let nr = block_row as i64 - 1;
for i in 0..=ext {
let nc = block_col as i64 + i as i64;
if is_available(nr, nc, block_row, block_col, 8, ctu, ctus_x, width, height) {
above[i] = plane[(nr as usize) * stride + nc as usize];
avail_above[i] = true;
}
}
}
{
let nc = block_col as i64 - 1;
for i in 0..=ext {
let nr = block_row as i64 + i as i64;
if is_available(nr, nc, block_row, block_col, 8, ctu, ctus_x, width, height) {
left[i] = plane[(nr as usize) * stride + nc as usize];
avail_left[i] = true;
}
}
}
let any = avail_corner || avail_above.iter().any(|&a| a) || avail_left.iter().any(|&a| a);
if !any {
return (neutral, [neutral; 65], [neutral; 65]);
}
let total = (ext + 1) + 1 + (ext + 1);
const MAXT: usize = 131;
let mut vals = [0u16; MAXT];
let mut av = [false; MAXT];
for i in 0..=ext {
vals[i] = left[ext - i];
av[i] = avail_left[ext - i];
}
vals[ext + 1] = corner;
av[ext + 1] = avail_corner;
for i in 0..=ext {
vals[(ext + 2) + i] = above[i];
av[(ext + 2) + i] = avail_above[i];
}
let first = av[..total].iter().position(|&a| a).unwrap();
let firstval = vals[first];
for k in 0..first {
vals[k] = firstval;
av[k] = true;
}
for k in 1..total {
if !av[k] {
vals[k] = vals[k - 1];
av[k] = true;
}
}
for i in 0..=ext {
left[ext - i] = vals[i];
}
corner = vals[ext + 1];
for i in 0..=ext {
above[i] = vals[(ext + 2) + i];
}
(corner, above, left)
}
#[inline]
pub(crate) fn compute_residual_i32_into(
orig: &[u16],
pred: &[u16],
n: usize,
residual: &mut [i32],
) {
let len = n * n;
debug_assert!(orig.len() >= len && pred.len() >= len && residual.len() >= len);
for (dst, (&orig, &pred)) in residual[..len]
.iter_mut()
.zip(orig[..len].iter().zip(&pred[..len]))
{
*dst = orig as i32 - pred as i32;
}
}
#[inline]
pub(crate) fn reconstruct_into(
pred: &[u16],
residual: &[i32],
n: usize,
max_val: u16,
out: &mut [u16],
) {
let len = n * n;
debug_assert!(pred.len() >= len && residual.len() >= len && out.len() >= len);
let max_val = max_val as i32;
for (dst, (&pred, &residual)) in out[..len]
.iter_mut()
.zip(pred[..len].iter().zip(&residual[..len]))
{
*dst = (pred as i32 + residual).clamp(0, max_val) as u16;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[inline]
pub(crate) fn predict_angular(
corner: u16,
above: &[u16],
left: &[u16],
n: usize,
mode: u8,
filter_boundary: bool,
max_val: i32,
) -> [u16; 1024] {
let mut pred = [0u16; 1024];
let mut scratch = AngularScratch::new();
predict_angular_into(
corner,
above,
left,
n,
mode,
filter_boundary,
max_val,
&mut pred,
&mut scratch,
);
pred
}
#[test]
fn angular_mode18_negative_diagonal() {
let mut above = [0u16; 33];
let mut left = [0u16; 33];
for i in 0..8 {
above[i] = (10 * (i + 1)) as u16; left[i] = (50 + 10 * i) as u16; }
let corner = 5;
let p = predict_angular(corner, &above, &left, 4, 18, false, 255);
let expect = [
5, 10, 20, 30, 50, 5, 10, 20, 60, 50, 5, 10, 70, 60, 50, 5, ];
assert_eq!(&p[..16], &expect, "mode-18 negative diagonal");
}
#[test]
fn planar_and_dc_32_flat_are_flat() {
let above = [777u16; 65];
let left = [777u16; 65];
let planar = predict_planar(&above, &left, 32);
let dc = predict_dc(&above, &left, 32, false);
assert!(planar[..1024].iter().all(|&v| v == 777));
assert!(dc[..1024].iter().all(|&v| v == 777));
}
#[test]
fn dc_of_flat_is_flat() {
let above = [100u16; 33];
let left = [100u16; 33];
let p = predict_dc(&above, &left, 8, false);
assert!(p[..64].iter().all(|&v| v == 100));
}
#[inline]
pub(crate) fn predict_planar(above: &[u16], left: &[u16], n: usize) -> [u16; 1024] {
let mut pred = [0u16; 1024];
predict_planar_into(above, left, n, &mut pred);
pred
}
#[inline]
pub(crate) fn predict_dc(
above: &[u16],
left: &[u16],
n: usize,
filter_boundary: bool,
) -> [u16; 1024] {
let mut pred = [0u16; 1024];
predict_dc_into(above, left, n, filter_boundary, &mut pred);
pred
}
#[test]
fn dc_value_is_average() {
let mut above = [0u16; 33];
let mut left = [0u16; 33];
for i in 0..8 {
above[i] = 80;
left[i] = 120;
}
let p = predict_dc(&above, &left, 8, false);
assert_eq!(p[63], 100); }
#[test]
fn vertical_mode_copies_above_row() {
let mut above = [0u16; 33];
let left = [50u16; 33];
for i in 0..8 {
above[i] = (10 * i) as u16;
}
let p = predict_angular(50, &above, &left, 8, 26, false, 255);
for r in 0..8 {
for c in 0..8 {
assert_eq!(p[r * 8 + c], above[c], "row {r} col {c}");
}
}
}
#[test]
fn horizontal_mode_copies_left_col() {
let above = [50u16; 33];
let mut left = [0u16; 33];
for i in 0..8 {
left[i] = (10 * i) as u16;
}
let p = predict_angular(50, &above, &left, 8, 10, false, 255);
for r in 0..8 {
for c in 0..8 {
assert_eq!(p[r * 8 + c], left[r], "row {r} col {c}");
}
}
}
#[test]
fn angular_45_diagonal_shifts() {
let mut above = [0u16; 33];
let left = [0u16; 33];
for i in 0..16 {
above[i] = (i + 1) as u16;
}
let p = predict_angular(0, &above, &left, 8, 34, false, 255);
for y in 0..8 {
for x in 0..8 {
assert_eq!(p[y * 8 + x], above[x + y + 1], "y{y} x{x}");
}
}
}
#[test]
fn ref_filter_decision() {
assert!(!should_filter_refs(DC, 8)); assert!(!should_filter_refs(0, 4)); assert!(should_filter_refs(PLANAR, 8)); assert!(!should_filter_refs(26, 8)); assert!(!should_filter_refs(10, 16)); assert!(should_filter_refs(2, 8)); assert!(should_filter_refs(18, 16)); }
#[test]
fn planar_corners() {
let mut above = vec![0u16; 9];
let mut left = vec![0u16; 9];
above[8] = 255; left[8] = 255; let pred = predict_planar(&above, &left, 8);
assert!(pred[7] > 100); assert!(pred[8 * 7] > 100); }
pub(crate) fn compute_residual_i32(orig: &[u16], pred: &[u16], n: usize) -> [i32; 1024] {
let mut residual = [0i32; 1024];
compute_residual_i32_into(orig, pred, n, &mut residual);
residual
}
#[test]
fn residual_zero_when_perfect() {
let pixels = vec![128u16; 64];
let pred = vec![128u16; 64];
let res = compute_residual_i32(&pixels, &pred, 8);
assert!(res.iter().all(|&r| r == 0));
}
}