#[inline]
pub(crate) fn predict_planar(above: &[u16], left: &[u16], n: usize) -> Vec<u16> {
let mut pred = vec![0u16; n * n];
let top_right = above[n] as i32;
let bottom_left = left[n] as i32;
let log2 = n.trailing_zeros();
for row in 0..n {
for col in 0..n {
let h = (n - 1 - col) as i32 * left[row] as i32 + (col + 1) as i32 * top_right;
let v = (n - 1 - row) as i32 * above[col] as i32 + (row + 1) as i32 * bottom_left;
pred[row * n + col] = ((h + v + n as i32) >> (log2 + 1)) as u16;
}
}
pred
}
pub(crate) fn filter_references(
corner: u16,
above: &[u16],
left: &[u16],
n: usize,
) -> (Vec<u16>, Vec<u16>) {
let mut fa = above.to_vec();
let mut fl = left.to_vec();
let ext = above.len() - 1; 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 {
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 {
fl[y] = ((left[y - 1] as i32 + 2 * left[y] as i32 + left[y + 1] as i32 + 2) >> 2) as u16;
}
let _ = n;
(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)]
pub(crate) fn get_reference_samples_chroma(
plane: &[u16],
stride: usize,
block_row: usize,
block_col: usize,
chroma_h: usize,
n: usize,
sub_w: usize,
sub_h: usize,
luma_w: usize,
luma_h: usize,
luma_ctus_x: usize,
cur_luma_row: usize,
cur_luma_col: usize,
neutral: u16,
) -> (u16, Vec<u16>, Vec<u16>) {
let width = stride;
let ext = 2 * n;
const MAXE: usize = 17;
let mut above = vec![0u16; ext + 1];
let mut left = vec![0u16; ext + 1];
let mut avail_above = [false; MAXE];
let mut avail_left = [false; MAXE];
let mut 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) {
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 avail(nr, nc, block_row) {
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 avail(nr, nc, block_row) {
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, vec![neutral; ext + 1], vec![neutral; ext + 1]);
}
let total = (ext + 1) + 1 + (ext + 1);
const MAXT: usize = 35;
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)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn get_reference_samples(
plane: &[u16],
stride: usize,
block_row: usize,
block_col: usize,
height: usize,
n: usize,
ctu: usize,
ctus_x: usize,
neutral: u16,
) -> (u16, Vec<u16>, Vec<u16>) {
let width = stride;
let ext = 2 * n; const MAXE: usize = 17; let mut above = vec![0u16; ext + 1]; let mut left = vec![0u16; ext + 1]; 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, vec![neutral; ext + 1], vec![neutral; ext + 1]);
}
let total = (ext + 1) + 1 + (ext + 1);
const MAXT: usize = 35;
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(orig: &[u16], pred: &[u16], n: usize) -> Vec<f32> {
debug_assert_eq!(orig.len(), n * n);
debug_assert_eq!(pred.len(), n * n);
orig.iter()
.zip(pred.iter())
.map(|(&o, &p)| o as f32 - p as f32)
.collect()
}
pub(crate) fn reconstruct(pred: &[u16], residual: &[f32], n: usize, max_val: u16) -> Vec<u16> {
let _ = n;
pred.iter()
.zip(residual.iter())
.map(|(&p, &r)| (p as f32 + r).round().clamp(0.0, max_val as f32) as u16)
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[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(&pixels, &pred, 8);
assert!(res.iter().all(|&r| r == 0.0));
}
}