#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum I16x16Mode {
Vertical = 0,
Horizontal = 1,
Dc = 2,
Plane = 3,
}
impl I16x16Mode {
pub fn as_u8(self) -> u8 {
self as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntraChromaMode {
Dc = 0,
Horizontal = 1,
Vertical = 2,
Plane = 3,
}
impl IntraChromaMode {
pub fn as_u8(self) -> u8 {
self as u8
}
}
pub fn predict_16x16(
mode: I16x16Mode,
recon_y: &[u8],
width: usize,
height: usize,
mb_x: usize,
mb_y: usize,
) -> Option<[i32; 256]> {
predict_16x16_avail(mode, recon_y, width, height, mb_x, mb_y, None)
}
pub fn predict_16x16_avail(
mode: I16x16Mode,
recon_y: &[u8],
width: usize,
height: usize,
mb_x: usize,
mb_y: usize,
avail: Option<(bool, bool, bool)>,
) -> Option<[i32; 256]> {
let (top, left, tl) = avail.unwrap_or_else(|| availability(mb_x, mb_y));
match mode {
I16x16Mode::Vertical if !top => None,
I16x16Mode::Horizontal if !left => None,
I16x16Mode::Plane if !(top && left && tl) => None,
I16x16Mode::Vertical => Some(predict_16x16_vertical(
recon_y,
width,
height,
mb_x,
mb_y,
(top, left, tl),
)),
I16x16Mode::Horizontal => Some(predict_16x16_horizontal(
recon_y,
width,
height,
mb_x,
mb_y,
(top, left, tl),
)),
I16x16Mode::Dc => Some(predict_16x16_dc(
recon_y,
width,
height,
mb_x,
mb_y,
(top, left, tl),
)),
I16x16Mode::Plane => Some(predict_16x16_plane(
recon_y,
width,
height,
mb_x,
mb_y,
(top, left, tl),
)),
}
}
fn availability(mb_x: usize, mb_y: usize) -> (bool, bool, bool) {
(mb_y > 0, mb_x > 0, mb_y > 0 && mb_x > 0)
}
fn predict_16x16_vertical(
recon_y: &[u8],
width: usize,
_height: usize,
mb_x: usize,
mb_y: usize,
av: (bool, bool, bool),
) -> [i32; 256] {
let (top, _left, _tl) = av;
let mut out = [128i32; 256];
if !top {
return out;
}
let row_above = (mb_y * 16 - 1) * width;
for x in 0..16 {
let s = recon_y[row_above + mb_x * 16 + x] as i32;
for y in 0..16 {
out[y * 16 + x] = s;
}
}
out
}
fn predict_16x16_horizontal(
recon_y: &[u8],
width: usize,
_height: usize,
mb_x: usize,
mb_y: usize,
av: (bool, bool, bool),
) -> [i32; 256] {
let (_top, left, _tl) = av;
let mut out = [128i32; 256];
if !left {
return out;
}
for y in 0..16 {
let s = recon_y[(mb_y * 16 + y) * width + mb_x * 16 - 1] as i32;
for x in 0..16 {
out[y * 16 + x] = s;
}
}
out
}
fn predict_16x16_dc(
recon_y: &[u8],
width: usize,
_height: usize,
mb_x: usize,
mb_y: usize,
av: (bool, bool, bool),
) -> [i32; 256] {
let (top, left, _tl) = av;
let dc = if top && left {
let mut s_top = 0i32;
let mut s_left = 0i32;
let row_above = (mb_y * 16 - 1) * width;
for i in 0..16 {
s_top += recon_y[row_above + mb_x * 16 + i] as i32;
}
for j in 0..16 {
s_left += recon_y[(mb_y * 16 + j) * width + mb_x * 16 - 1] as i32;
}
(s_top + s_left + 16) >> 5
} else if left {
let mut s = 0i32;
for j in 0..16 {
s += recon_y[(mb_y * 16 + j) * width + mb_x * 16 - 1] as i32;
}
(s + 8) >> 4
} else if top {
let mut s = 0i32;
let row_above = (mb_y * 16 - 1) * width;
for i in 0..16 {
s += recon_y[row_above + mb_x * 16 + i] as i32;
}
(s + 8) >> 4
} else {
128
};
[dc; 256]
}
fn predict_16x16_plane(
recon_y: &[u8],
width: usize,
height: usize,
mb_x: usize,
mb_y: usize,
av: (bool, bool, bool),
) -> [i32; 256] {
let (top, left, tl) = av;
if !(top && left && tl) {
return predict_16x16_dc(recon_y, width, height, mb_x, mb_y, av);
}
let row_above = (mb_y * 16 - 1) * width;
let p_top = |x: i32| -> i32 {
if x == -1 {
recon_y[row_above + mb_x * 16 - 1] as i32
} else {
recon_y[row_above + mb_x * 16 + x as usize] as i32
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
recon_y[row_above + mb_x * 16 - 1] as i32
} else {
recon_y[(mb_y * 16 + y as usize) * width + mb_x * 16 - 1] as i32
}
};
let mut h = 0i32;
for xp in 0..8i32 {
h += (xp + 1) * (p_top(8 + xp) - p_top(6 - xp));
}
let mut v = 0i32;
for yp in 0..8i32 {
v += (yp + 1) * (p_left(8 + yp) - p_left(6 - yp));
}
let a = 16 * (p_left(15) + p_top(15));
let b = (5 * h + 32) >> 6;
let c = (5 * v + 32) >> 6;
let mut out = [0i32; 256];
for y in 0..16i32 {
for x in 0..16i32 {
let raw = (a + b * (x - 7) + c * (y - 7) + 16) >> 5;
out[(y * 16 + x) as usize] = raw.clamp(0, 255);
}
}
out
}
pub fn predict_chroma_8x8(
mode: IntraChromaMode,
recon: &[u8],
width: usize,
height: usize,
mb_x: usize,
mb_y: usize,
) -> Option<[i32; 64]> {
predict_chroma_8x8_avail(mode, recon, width, height, mb_x, mb_y, None)
}
pub fn predict_chroma_8x8_avail(
mode: IntraChromaMode,
recon: &[u8],
width: usize,
height: usize,
mb_x: usize,
mb_y: usize,
avail: Option<(bool, bool, bool)>,
) -> Option<[i32; 64]> {
let (top, left, tl) = avail.unwrap_or_else(|| availability(mb_x, mb_y));
match mode {
IntraChromaMode::Vertical if !top => None,
IntraChromaMode::Horizontal if !left => None,
IntraChromaMode::Plane if !(top && left && tl) => None,
IntraChromaMode::Dc => Some(predict_chroma_dc_8x8(
recon,
width,
height,
mb_x,
mb_y,
(top, left, tl),
)),
IntraChromaMode::Horizontal => Some(predict_chroma_h_8x8(
recon,
width,
height,
mb_x,
mb_y,
(top, left, tl),
)),
IntraChromaMode::Vertical => Some(predict_chroma_v_8x8(
recon,
width,
height,
mb_x,
mb_y,
(top, left, tl),
)),
IntraChromaMode::Plane => Some(predict_chroma_plane_8x8(
recon,
width,
height,
mb_x,
mb_y,
(top, left, tl),
)),
}
}
fn predict_chroma_dc_8x8(
recon: &[u8],
width: usize,
_height: usize,
mb_x: usize,
mb_y: usize,
av: (bool, bool, bool),
) -> [i32; 64] {
let (top, left, _tl) = av;
let default = 128i32;
let row_above = if top { (mb_y * 8 - 1) * width } else { 0 };
let sum_top = |x_o: usize| -> i32 {
let mut s = 0i32;
for i in 0..4 {
s += recon[row_above + mb_x * 8 + x_o + i] as i32;
}
s
};
let sum_left = |y_o: usize| -> i32 {
let mut s = 0i32;
for j in 0..4 {
s += recon[(mb_y * 8 + y_o + j) * width + mb_x * 8 - 1] as i32;
}
s
};
let dc_sub = |x_o: usize, y_o: usize| -> i32 {
if x_o == 0 && y_o == 0 {
if top && left {
(sum_top(0) + sum_left(0) + 4) >> 3
} else if top {
(sum_top(0) + 2) >> 2
} else if left {
(sum_left(0) + 2) >> 2
} else {
default
}
} else if x_o > 0 && y_o == 0 {
if top {
(sum_top(x_o) + 2) >> 2
} else if left {
(sum_left(0) + 2) >> 2
} else {
default
}
} else if x_o == 0 && y_o > 0 {
if left {
(sum_left(y_o) + 2) >> 2
} else if top {
(sum_top(0) + 2) >> 2
} else {
default
}
} else {
if top && left {
(sum_top(x_o) + sum_left(y_o) + 4) >> 3
} else if top {
(sum_top(x_o) + 2) >> 2
} else if left {
(sum_left(y_o) + 2) >> 2
} else {
default
}
}
};
let mut out = [0i32; 64];
for y_o in [0, 4] {
for x_o in [0, 4] {
let dc = dc_sub(x_o, y_o);
for yy in 0..4 {
for xx in 0..4 {
out[(y_o + yy) * 8 + (x_o + xx)] = dc;
}
}
}
}
out
}
fn predict_chroma_h_8x8(
recon: &[u8],
width: usize,
_height: usize,
mb_x: usize,
mb_y: usize,
av: (bool, bool, bool),
) -> [i32; 64] {
let (_top, left, _tl) = av;
let mut out = [128i32; 64];
if !left {
return out;
}
for y in 0..8 {
let s = recon[(mb_y * 8 + y) * width + mb_x * 8 - 1] as i32;
for x in 0..8 {
out[y * 8 + x] = s;
}
}
out
}
fn predict_chroma_v_8x8(
recon: &[u8],
width: usize,
_height: usize,
mb_x: usize,
mb_y: usize,
av: (bool, bool, bool),
) -> [i32; 64] {
let (top, _left, _tl) = av;
let mut out = [128i32; 64];
if !top {
return out;
}
let row_above = (mb_y * 8 - 1) * width;
for x in 0..8 {
let s = recon[row_above + mb_x * 8 + x] as i32;
for y in 0..8 {
out[y * 8 + x] = s;
}
}
out
}
fn predict_chroma_plane_8x8(
recon: &[u8],
width: usize,
height: usize,
mb_x: usize,
mb_y: usize,
av: (bool, bool, bool),
) -> [i32; 64] {
let (top, left, tl) = av;
if !(top && left && tl) {
return predict_chroma_dc_8x8(recon, width, height, mb_x, mb_y, av);
}
let row_above = (mb_y * 8 - 1) * width;
let p_top = |x: i32| -> i32 {
if x == -1 {
recon[row_above + mb_x * 8 - 1] as i32
} else {
recon[row_above + mb_x * 8 + x as usize] as i32
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
recon[row_above + mb_x * 8 - 1] as i32
} else {
recon[(mb_y * 8 + y as usize) * width + mb_x * 8 - 1] as i32
}
};
let mut h = 0i32;
for xp in 0..4i32 {
h += (xp + 1) * (p_top(4 + xp) - p_top(2 - xp));
}
let mut v = 0i32;
for yp in 0..4i32 {
v += (yp + 1) * (p_left(4 + yp) - p_left(2 - yp));
}
let a = 16 * (p_left(7) + p_top(7));
let b = (34 * h + 32) >> 6;
let c = (34 * v + 32) >> 6;
let mut out = [0i32; 64];
for y in 0..8i32 {
for x in 0..8i32 {
let raw = (a + b * (x - 3) + c * (y - 3) + 16) >> 5;
out[(y * 8 + x) as usize] = raw.clamp(0, 255);
}
}
out
}
pub fn predict_chroma_8x16(
mode: IntraChromaMode,
recon: &[u8],
width: usize,
height: usize,
mb_x: usize,
mb_y: usize,
) -> Option<[i32; 128]> {
let (top, left, tl) = chroma_avail_422(mb_x, mb_y);
match mode {
IntraChromaMode::Vertical if !top => None,
IntraChromaMode::Horizontal if !left => None,
IntraChromaMode::Plane if !(top && left && tl) => None,
IntraChromaMode::Dc => Some(predict_chroma_dc_8x16(recon, width, height, mb_x, mb_y)),
IntraChromaMode::Horizontal => {
Some(predict_chroma_h_8x16(recon, width, height, mb_x, mb_y))
}
IntraChromaMode::Vertical => Some(predict_chroma_v_8x16(recon, width, height, mb_x, mb_y)),
IntraChromaMode::Plane => Some(predict_chroma_plane_8x16(recon, width, height, mb_x, mb_y)),
}
}
fn chroma_avail_422(mb_x: usize, mb_y: usize) -> (bool, bool, bool) {
let top = mb_y > 0;
let left = mb_x > 0;
(top, left, top && left)
}
fn predict_chroma_dc_8x16(
recon: &[u8],
width: usize,
_height: usize,
mb_x: usize,
mb_y: usize,
) -> [i32; 128] {
let (top, left, _tl) = chroma_avail_422(mb_x, mb_y);
let default = 128i32;
let row_above = if top { (mb_y * 16 - 1) * width } else { 0 };
let sum_top = |x_o: usize| -> i32 {
let mut s = 0i32;
for i in 0..4 {
s += recon[row_above + mb_x * 8 + x_o + i] as i32;
}
s
};
let sum_left = |y_o: usize| -> i32 {
let mut s = 0i32;
for j in 0..4 {
s += recon[(mb_y * 16 + y_o + j) * width + mb_x * 8 - 1] as i32;
}
s
};
let dc_sub = |x_o: usize, y_o: usize| -> i32 {
if x_o == 0 && y_o == 0 {
if top && left {
(sum_top(0) + sum_left(0) + 4) >> 3
} else if top {
(sum_top(0) + 2) >> 2
} else if left {
(sum_left(0) + 2) >> 2
} else {
default
}
} else if x_o > 0 && y_o == 0 {
if top {
(sum_top(x_o) + 2) >> 2
} else if left {
(sum_left(0) + 2) >> 2
} else {
default
}
} else if x_o == 0 && y_o > 0 {
if left {
(sum_left(y_o) + 2) >> 2
} else if top {
(sum_top(0) + 2) >> 2
} else {
default
}
} else {
if top && left {
(sum_top(x_o) + sum_left(y_o) + 4) >> 3
} else if top {
(sum_top(x_o) + 2) >> 2
} else if left {
(sum_left(y_o) + 2) >> 2
} else {
default
}
}
};
let mut out = [0i32; 128];
for y_o in [0usize, 4, 8, 12] {
for x_o in [0usize, 4] {
let dc = dc_sub(x_o, y_o);
for yy in 0..4 {
for xx in 0..4 {
out[(y_o + yy) * 8 + (x_o + xx)] = dc;
}
}
}
}
out
}
fn predict_chroma_h_8x16(
recon: &[u8],
width: usize,
_height: usize,
mb_x: usize,
mb_y: usize,
) -> [i32; 128] {
let (_top, left, _tl) = chroma_avail_422(mb_x, mb_y);
let mut out = [128i32; 128];
if !left {
return out;
}
for y in 0..16 {
let s = recon[(mb_y * 16 + y) * width + mb_x * 8 - 1] as i32;
for x in 0..8 {
out[y * 8 + x] = s;
}
}
out
}
fn predict_chroma_v_8x16(
recon: &[u8],
width: usize,
_height: usize,
mb_x: usize,
mb_y: usize,
) -> [i32; 128] {
let (top, _left, _tl) = chroma_avail_422(mb_x, mb_y);
let mut out = [128i32; 128];
if !top {
return out;
}
let row_above = (mb_y * 16 - 1) * width;
for x in 0..8 {
let s = recon[row_above + mb_x * 8 + x] as i32;
for y in 0..16 {
out[y * 8 + x] = s;
}
}
out
}
fn predict_chroma_plane_8x16(
recon: &[u8],
width: usize,
height: usize,
mb_x: usize,
mb_y: usize,
) -> [i32; 128] {
let (top, left, tl) = chroma_avail_422(mb_x, mb_y);
if !(top && left && tl) {
return predict_chroma_dc_8x16(recon, width, height, mb_x, mb_y);
}
let row_above = (mb_y * 16 - 1) * width;
let p_top = |x: i32| -> i32 {
if x == -1 {
recon[row_above + mb_x * 8 - 1] as i32
} else {
recon[row_above + mb_x * 8 + x as usize] as i32
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
recon[row_above + mb_x * 8 - 1] as i32
} else {
recon[(mb_y * 16 + y as usize) * width + mb_x * 8 - 1] as i32
}
};
let mut h = 0i32;
for xp in 0..4i32 {
h += (xp + 1) * (p_top(4 + xp) - p_top(2 - xp));
}
let y_cf: i32 = 4;
let mut v = 0i32;
for yp in 0..=(3 + y_cf) {
v += (yp + 1) * (p_left(4 + y_cf + yp) - p_left(2 + y_cf - yp));
}
let a = 16 * (p_left(15) + p_top(7));
let b = (34 * h + 32) >> 6;
let c = (5 * v + 32) >> 6;
let mut out = [0i32; 128];
for y in 0..16i32 {
for x in 0..8i32 {
let raw = (a + b * (x - 3) + c * (y - 3 - y_cf) + 16) >> 5;
out[(y * 8 + x) as usize] = raw.clamp(0, 255);
}
}
out
}
pub fn sad_8x16(
src: &[u8],
src_stride: usize,
src_x: usize,
src_y: usize,
pred: &[i32; 128],
) -> u64 {
let mut s = 0u64;
for j in 0..16 {
for i in 0..8 {
let p = pred[j * 8 + i];
let q = src[(src_y + j) * src_stride + (src_x + i)] as i32;
s += (p - q).unsigned_abs() as u64;
}
}
s
}
pub fn sad_16x16(
src: &[u8],
src_stride: usize,
src_x: usize,
src_y: usize,
pred: &[i32; 256],
) -> u64 {
let mut s = 0u64;
for j in 0..16 {
for i in 0..16 {
let p = pred[j * 16 + i];
let q = src[(src_y + j) * src_stride + (src_x + i)] as i32;
s += (p - q).unsigned_abs() as u64;
}
}
s
}
pub fn sad_8x8(src: &[u8], src_stride: usize, src_x: usize, src_y: usize, pred: &[i32; 64]) -> u64 {
let mut s = 0u64;
for j in 0..8 {
for i in 0..8 {
let p = pred[j * 8 + i];
let q = src[(src_y + j) * src_stride + (src_x + i)] as i32;
s += (p - q).unsigned_abs() as u64;
}
}
s
}
#[cfg(test)]
mod tests {
use super::*;
fn flat_recon(width: usize, height: usize, val: u8) -> Vec<u8> {
vec![val; width * height]
}
#[test]
fn dc_with_no_neighbours_returns_128() {
let recon = flat_recon(64, 64, 200);
let pred = predict_16x16_dc(&recon, 64, 64, 0, 0, availability(0, 0));
assert!(pred.iter().all(|&p| p == 128));
}
#[test]
fn vertical_unavailable_at_top_edge() {
let recon = flat_recon(64, 64, 200);
assert!(predict_16x16(I16x16Mode::Vertical, &recon, 64, 64, 0, 0).is_none());
assert!(predict_16x16(I16x16Mode::Horizontal, &recon, 64, 64, 0, 0).is_none());
assert!(predict_16x16(I16x16Mode::Plane, &recon, 64, 64, 0, 0).is_none());
assert!(predict_16x16(I16x16Mode::Dc, &recon, 64, 64, 0, 0).is_some());
}
#[test]
fn vertical_copies_top_row() {
let mut recon = vec![0u8; 64 * 64];
for x in 0..64 {
recon[15 * 64 + x] = (x * 4) as u8;
}
let pred = predict_16x16_vertical(&recon, 64, 64, 0, 1, availability(0, 1));
for y in 0..16 {
assert_eq!(pred[y * 16 + 5], 20);
}
}
#[test]
fn horizontal_copies_left_column() {
let mut recon = vec![0u8; 64 * 64];
for y in 0..64 {
recon[y * 64 + 15] = (y * 3) as u8;
}
let pred = predict_16x16_horizontal(&recon, 64, 64, 1, 0, availability(1, 0));
for y in 0..16 {
for x in 0..16 {
assert_eq!(pred[y * 16 + x], ((y * 3) & 0xff) as i32);
}
}
}
#[test]
fn plane_constant_neighbours_returns_constant() {
let recon = flat_recon(64, 64, 100);
let pred = predict_16x16_plane(&recon, 64, 64, 1, 1, availability(1, 1));
assert!(pred.iter().all(|&p| p == 100), "got {:?}", &pred[..8]);
}
#[test]
fn chroma_dc_with_no_neighbours_returns_128() {
let recon = flat_recon(32, 32, 200);
let pred = predict_chroma_dc_8x8(&recon, 32, 32, 0, 0, availability(0, 0));
assert!(pred.iter().all(|&p| p == 128));
}
#[test]
fn chroma_h_copies_left() {
let mut recon = vec![0u8; 32 * 32];
for y in 0..32 {
recon[y * 32 + 7] = (y * 2) as u8;
}
let pred = predict_chroma_h_8x8(&recon, 32, 32, 1, 0, availability(1, 0));
for y in 0..8 {
for x in 0..8 {
assert_eq!(pred[y * 8 + x], ((y * 2) & 0xff) as i32);
}
}
}
#[test]
fn sad_zero_when_pred_matches_src() {
let src = vec![100u8; 64 * 64];
let pred = [100i32; 256];
assert_eq!(sad_16x16(&src, 64, 0, 0, &pred), 0);
}
}