#[inline]
pub(crate) fn predict_planar(above: &[u16], left: &[u16], n: usize) -> [u16; 256] {
let mut pred = [0u16; 256];
let top_right = above[n] as i32;
let bottom_left = left[n] as i32;
let log2 = n.trailing_zeros();
for (row, &left) in left[..n].iter().enumerate() {
let lr = left as i32;
let v_w = (n - 1 - row) as i32;
let v_b = (row + 1) as i32 * bottom_left;
let arow = &above[..n];
let prow = &mut pred[row * n..row * n + n];
for (col, (prow, &arow)) in prow[..n].iter_mut().zip(arow.iter()).enumerate() {
let h = (n - 1 - col) as i32 * lr + (col + 1) as i32 * top_right;
let v = v_w * arow as i32 + v_b;
*prow = ((h + v + n as i32) >> (log2 + 1)) as u16;
}
}
pred
}
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) fn predict_dc(
above: &[u16],
left: &[u16],
n: usize,
filter_boundary: bool,
) -> [u16; 256] {
let mut sum = 0i32;
for (&above, &left) in above[..n].iter().zip(left[..n].iter()) {
sum += above as i32 + left as i32;
}
let log2 = n.trailing_zeros();
let dc = (sum + n as i32) >> (log2 + 1);
let mut pred = [0u16; 256];
for v in pred[..n * n].iter_mut() {
*v = 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 (pred, &above) in pred[1..n].iter_mut().zip(above[1..n].iter()) {
*pred = ((above as i32 + 3 * dc + 2) >> 2) as u16;
}
for (y, &left) in (1..n).zip(left[1..n].iter()) {
pred[y * n] = ((left as i32 + 3 * dc + 2) >> 2) as u16;
}
}
pred
}
pub(crate) fn predict_angular(
corner: u16,
above: &[u16],
left: &[u16],
n: usize,
mode: u8,
filter_boundary: bool,
max_val: i32,
) -> [u16; 256] {
let angle = INTRA_PRED_ANGLE[mode as usize];
let mut pred = [0u16; 256];
let vertical = mode >= 18;
let (main, side): (&[u16], &[u16]) = if vertical {
(above, left)
} else {
(left, above)
};
const OFF: usize = 32;
let mut r = [0i32; OFF + 64 + 1];
r[OFF] = corner as i32;
r[OFF + 1..=OFF + 2 * n]
.iter_mut()
.zip(main[..2 * n].iter())
.for_each(|(dst, &src)| *dst = src as i32);
if angle < 0 {
let inv = INV_ANGLE[mode as usize];
let last = (n as i32 * angle) >> 5; let mut x = -1;
while x >= last {
let idx = (x * inv + 128) >> 8; r[(OFF as i32 + x) as usize] = if idx <= 0 {
corner as i32
} else {
side[(idx - 1) as usize] as i32
};
x -= 1;
}
}
for p_outer in 0..n {
let pos = (p_outer as i32 + 1) * angle;
let i_idx = pos >> 5;
let i_fact = pos & 31;
for p_inner in 0..n {
let base = OFF as i32 + p_inner as i32 + i_idx + 1;
let a = r[base as usize];
let b = r[(base + 1) as usize];
let val = (((32 - i_fact) * a + i_fact * b + 16) >> 5) as u16;
if vertical {
pred[p_outer * n + p_inner] = val; } else {
pred[p_inner * n + p_outer] = val; }
}
}
if filter_boundary && n < 32 {
let max = max_val;
if mode == 26 {
for y in 0..n {
let v = above[0] as i32 + ((left[y] as i32 - corner as i32) >> 1);
pred[y * n] = v.clamp(0, max) as u16;
}
} else if mode == 10 {
for (pred, &above) in pred[..n].iter_mut().zip(above[..n].iter()) {
let v = left[0] as i32 + ((above as i32 - corner as i32) >> 1);
*pred = v.clamp(0, max) as u16;
}
}
}
pred
}
pub(crate) fn predict_chroma_tb(
mode: u8,
corner: u16,
above: &[u16],
left: &[u16],
n: usize,
max_val: i32,
) -> [u16; 256] {
match mode {
PLANAR => predict_planar(above, left, n),
DC => predict_dc(above, left, n, false),
_ => predict_angular(corner, above, left, n, mode, false, max_val),
}
}
pub(crate) fn filter_references(
corner: u16,
above: &[u16],
left: &[u16],
n: usize,
) -> ([u16; 33], [u16; 33]) {
let mut fa = [0u16; 33];
let mut fl = [0u16; 33];
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; 33],
mut left: [u16; 33],
avail_corner: bool,
avail_above: &[bool; 33],
avail_left: &[bool; 33],
ext: usize,
neutral: u16,
) -> (u16, [u16; 33], [u16; 33]) {
let any = avail_corner
|| avail_above[..=ext].iter().any(|&a| a)
|| avail_left[..=ext].iter().any(|&a| a);
if !any {
return (neutral, [neutral; 33], [neutral; 33]);
}
let total = (ext + 1) + 1 + (ext + 1);
const MAXT: usize = 67;
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; 33], [u16; 33]), (u16, [u16; 33], [u16; 33])) {
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 = 33;
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; 33], [u16; 33]) {
let LumaRefGeometry {
stride,
block_row,
block_col,
height,
n,
ctu,
ctus_x,
neutral,
} = geo;
let width = stride;
let ext = 2 * n; const MAXE: usize = 33; 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, n, 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, n, 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, n, 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; 33], [neutral; 33]);
}
let total = (ext + 1) + 1 + (ext + 1);
const MAXT: usize = 67;
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)
}
pub(crate) fn compute_residual_i32(orig: &[u16], pred: &[u16], n: usize) -> [i32; 256] {
let mut res = [0i32; 256];
for (r, (&o, &p)) in res[..n * n].iter_mut().zip(orig.iter().zip(pred.iter())) {
*r = o as i32 - p as i32;
}
res
}
pub(crate) fn reconstruct(pred: &[u16], residual: &[i32], n: usize, max_val: u16) -> [u16; 256] {
let mut out = [0u16; 256];
let mx = max_val as i32;
for (o, (&p, &r)) in out[..n * n]
.iter_mut()
.zip(pred.iter().zip(residual.iter()))
{
*o = (p as i32 + r).clamp(0, mx) as u16;
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[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 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));
}
#[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); }
#[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));
}
}