#![allow(dead_code)]
#[inline]
fn clip1(x: i32, bit_depth: u32) -> i32 {
let max = (1i32 << bit_depth) - 1;
if x < 0 {
0
} else if x > max {
max
} else {
x
}
}
#[inline]
fn dc_default(bit_depth: u32) -> i32 {
1 << (bit_depth - 1)
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Intra4x4Mode {
Vertical,
Horizontal,
Dc,
DiagonalDownLeft,
DiagonalDownRight,
VerticalRight,
HorizontalDown,
VerticalLeft,
HorizontalUp,
}
impl Intra4x4Mode {
pub fn from_index(idx: u8) -> Option<Self> {
Some(match idx {
0 => Self::Vertical,
1 => Self::Horizontal,
2 => Self::Dc,
3 => Self::DiagonalDownLeft,
4 => Self::DiagonalDownRight,
5 => Self::VerticalRight,
6 => Self::HorizontalDown,
7 => Self::VerticalLeft,
8 => Self::HorizontalUp,
_ => return None,
})
}
pub fn as_index(self) -> u8 {
match self {
Self::Vertical => 0,
Self::Horizontal => 1,
Self::Dc => 2,
Self::DiagonalDownLeft => 3,
Self::DiagonalDownRight => 4,
Self::VerticalRight => 5,
Self::HorizontalDown => 6,
Self::VerticalLeft => 7,
Self::HorizontalUp => 8,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Intra8x8Mode {
Vertical,
Horizontal,
Dc,
DiagonalDownLeft,
DiagonalDownRight,
VerticalRight,
HorizontalDown,
VerticalLeft,
HorizontalUp,
}
impl Intra8x8Mode {
pub fn from_index(idx: u8) -> Option<Self> {
Some(match idx {
0 => Self::Vertical,
1 => Self::Horizontal,
2 => Self::Dc,
3 => Self::DiagonalDownLeft,
4 => Self::DiagonalDownRight,
5 => Self::VerticalRight,
6 => Self::HorizontalDown,
7 => Self::VerticalLeft,
8 => Self::HorizontalUp,
_ => return None,
})
}
pub fn as_index(self) -> u8 {
match self {
Self::Vertical => 0,
Self::Horizontal => 1,
Self::Dc => 2,
Self::DiagonalDownLeft => 3,
Self::DiagonalDownRight => 4,
Self::VerticalRight => 5,
Self::HorizontalDown => 6,
Self::VerticalLeft => 7,
Self::HorizontalUp => 8,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Intra16x16Mode {
Vertical,
Horizontal,
Dc,
Plane,
}
impl Intra16x16Mode {
pub fn from_index(idx: u8) -> Option<Self> {
Some(match idx {
0 => Self::Vertical,
1 => Self::Horizontal,
2 => Self::Dc,
3 => Self::Plane,
_ => return None,
})
}
pub fn as_index(self) -> u8 {
match self {
Self::Vertical => 0,
Self::Horizontal => 1,
Self::Dc => 2,
Self::Plane => 3,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum IntraChromaMode {
Dc,
Horizontal,
Vertical,
Plane,
}
impl IntraChromaMode {
pub fn from_index(idx: u8) -> Option<Self> {
Some(match idx {
0 => Self::Dc,
1 => Self::Horizontal,
2 => Self::Vertical,
3 => Self::Plane,
_ => return None,
})
}
pub fn as_index(self) -> u8 {
match self {
Self::Dc => 0,
Self::Horizontal => 1,
Self::Vertical => 2,
Self::Plane => 3,
}
}
}
#[derive(Debug, Copy, Clone, Default)]
pub struct Neighbour4x4Availability {
pub top_left: bool,
pub top: bool,
pub top_right: bool,
pub left: bool,
}
pub fn intra_4x4_mode_permitted(mode: Intra4x4Mode, av: &Neighbour4x4Availability) -> bool {
match mode {
Intra4x4Mode::Dc => true,
Intra4x4Mode::Vertical | Intra4x4Mode::DiagonalDownLeft | Intra4x4Mode::VerticalLeft => {
av.top
}
Intra4x4Mode::Horizontal | Intra4x4Mode::HorizontalUp => av.left,
Intra4x4Mode::DiagonalDownRight
| Intra4x4Mode::VerticalRight
| Intra4x4Mode::HorizontalDown => av.top && av.left && av.top_left,
}
}
pub fn intra_8x8_mode_permitted(mode: Intra8x8Mode, av: &Neighbour4x4Availability) -> bool {
match mode {
Intra8x8Mode::Dc => true,
Intra8x8Mode::Vertical | Intra8x8Mode::DiagonalDownLeft | Intra8x8Mode::VerticalLeft => {
av.top
}
Intra8x8Mode::Horizontal | Intra8x8Mode::HorizontalUp => av.left,
Intra8x8Mode::DiagonalDownRight
| Intra8x8Mode::VerticalRight
| Intra8x8Mode::HorizontalDown => av.top && av.left,
}
}
pub fn intra_16x16_mode_permitted(mode: Intra16x16Mode, av: &Neighbour4x4Availability) -> bool {
match mode {
Intra16x16Mode::Dc => true,
Intra16x16Mode::Vertical => av.top,
Intra16x16Mode::Horizontal => av.left,
Intra16x16Mode::Plane => av.top && av.left && av.top_left,
}
}
pub fn intra_chroma_mode_permitted(mode: IntraChromaMode, av: &Neighbour4x4Availability) -> bool {
match mode {
IntraChromaMode::Dc => true,
IntraChromaMode::Horizontal => av.left,
IntraChromaMode::Vertical => av.top,
IntraChromaMode::Plane => av.top && av.left && av.top_left,
}
}
#[derive(Debug, Copy, Clone)]
pub struct Samples4x4 {
pub top_left: i32,
pub top: [i32; 4],
pub top_right: [i32; 4],
pub left: [i32; 4],
pub availability: Neighbour4x4Availability,
}
#[derive(Debug, Copy, Clone)]
pub struct Samples8x8 {
pub top_left: i32,
pub top: [i32; 8],
pub top_right: [i32; 8],
pub left: [i32; 8],
pub availability: Neighbour4x4Availability,
}
#[derive(Debug, Copy, Clone)]
pub struct Samples16x16 {
pub top_left: i32,
pub top: [i32; 16],
pub left: [i32; 16],
pub availability: Neighbour4x4Availability,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ChromaArrayType {
Yuv420,
Yuv422,
}
impl ChromaArrayType {
pub fn width(self) -> usize {
8
}
pub fn height(self) -> usize {
match self {
Self::Yuv420 => 8,
Self::Yuv422 => 16,
}
}
}
#[derive(Debug, Clone)]
pub struct SamplesChroma {
pub top_left: i32,
pub top: Vec<i32>, pub left: Vec<i32>, pub availability: Neighbour4x4Availability,
}
fn top_row_4x4(s: &Samples4x4) -> [i32; 8] {
let mut p = [0i32; 8];
p[0..4].copy_from_slice(&s.top);
if s.availability.top_right {
p[4..8].copy_from_slice(&s.top_right);
} else if s.availability.top {
for slot in &mut p[4..8] {
*slot = s.top[3];
}
}
p
}
pub fn predict_4x4(mode: Intra4x4Mode, samples: &Samples4x4, bit_depth: u32, out: &mut [i32; 16]) {
match mode {
Intra4x4Mode::Vertical => predict_4x4_vertical(samples, out),
Intra4x4Mode::Horizontal => predict_4x4_horizontal(samples, out),
Intra4x4Mode::Dc => predict_4x4_dc(samples, bit_depth, out),
Intra4x4Mode::DiagonalDownLeft => predict_4x4_ddl(samples, out),
Intra4x4Mode::DiagonalDownRight => predict_4x4_ddr(samples, out),
Intra4x4Mode::VerticalRight => predict_4x4_vr(samples, out),
Intra4x4Mode::HorizontalDown => predict_4x4_hd(samples, out),
Intra4x4Mode::VerticalLeft => predict_4x4_vl(samples, out),
Intra4x4Mode::HorizontalUp => predict_4x4_hu(samples, out),
}
}
#[inline]
fn at4(x: usize, y: usize) -> usize {
y * 4 + x
}
fn predict_4x4_vertical(s: &Samples4x4, out: &mut [i32; 16]) {
for y in 0..4 {
for x in 0..4 {
out[at4(x, y)] = s.top[x];
}
}
}
fn predict_4x4_horizontal(s: &Samples4x4, out: &mut [i32; 16]) {
for y in 0..4 {
for x in 0..4 {
out[at4(x, y)] = s.left[y];
}
}
}
fn predict_4x4_dc(s: &Samples4x4, bit_depth: u32, out: &mut [i32; 16]) {
let dc = if s.availability.top && s.availability.left {
(s.top[0]
+ s.top[1]
+ s.top[2]
+ s.top[3]
+ s.left[0]
+ s.left[1]
+ s.left[2]
+ s.left[3]
+ 4)
>> 3
} else if s.availability.left {
(s.left[0] + s.left[1] + s.left[2] + s.left[3] + 2) >> 2
} else if s.availability.top {
(s.top[0] + s.top[1] + s.top[2] + s.top[3] + 2) >> 2
} else {
dc_default(bit_depth)
};
for sample in out.iter_mut() {
*sample = dc;
}
}
fn predict_4x4_ddl(s: &Samples4x4, out: &mut [i32; 16]) {
let p = top_row_4x4(s);
for y in 0..4 {
for x in 0..4 {
let v = if x == 3 && y == 3 {
(p[6] + 3 * p[7] + 2) >> 2
} else {
(p[x + y] + 2 * p[x + y + 1] + p[x + y + 2] + 2) >> 2
};
out[at4(x, y)] = v;
}
}
}
fn predict_4x4_ddr(s: &Samples4x4, out: &mut [i32; 16]) {
let tl = s.top_left;
let t = &s.top;
let l = &s.left;
let p_top = |x: i32| -> i32 {
if x == -1 {
tl
} else {
t[x as usize]
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
tl
} else {
l[y as usize]
}
};
for y in 0..4i32 {
for x in 0..4i32 {
let v = if x > y {
(p_top(x - y - 2) + 2 * p_top(x - y - 1) + p_top(x - y) + 2) >> 2
} else if x < y {
(p_left(y - x - 2) + 2 * p_left(y - x - 1) + p_left(y - x) + 2) >> 2
} else {
(p_top(0) + 2 * tl + p_left(0) + 2) >> 2
};
out[at4(x as usize, y as usize)] = v;
}
}
}
fn predict_4x4_vr(s: &Samples4x4, out: &mut [i32; 16]) {
let tl = s.top_left;
let t = &s.top;
let l = &s.left;
let p_top = |x: i32| -> i32 {
if x == -1 {
tl
} else {
t[x as usize]
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
tl
} else {
l[y as usize]
}
};
for y in 0..4i32 {
for x in 0..4i32 {
let z_vr = 2 * x - y;
let v = match z_vr {
0 | 2 | 4 | 6 => {
let xp = x - (y >> 1) - 1;
(p_top(xp) + p_top(xp + 1) + 1) >> 1
}
1 | 3 | 5 => {
let xp = x - (y >> 1) - 2;
(p_top(xp) + 2 * p_top(xp + 1) + p_top(xp + 2) + 2) >> 2
}
-1 => {
(p_left(0) + 2 * tl + p_top(0) + 2) >> 2
}
_ => {
(p_left(y - 1) + 2 * p_left(y - 2) + p_left(y - 3) + 2) >> 2
}
};
out[at4(x as usize, y as usize)] = v;
}
}
}
fn predict_4x4_hd(s: &Samples4x4, out: &mut [i32; 16]) {
let tl = s.top_left;
let t = &s.top;
let l = &s.left;
let p_top = |x: i32| -> i32 {
if x == -1 {
tl
} else {
t[x as usize]
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
tl
} else {
l[y as usize]
}
};
for y in 0..4i32 {
for x in 0..4i32 {
let z_hd = 2 * y - x;
let v = match z_hd {
0 | 2 | 4 | 6 => {
let yp = y - (x >> 1) - 1;
(p_left(yp) + p_left(yp + 1) + 1) >> 1
}
1 | 3 | 5 => {
let yp = y - (x >> 1) - 2;
(p_left(yp) + 2 * p_left(yp + 1) + p_left(yp + 2) + 2) >> 2
}
-1 => {
(p_left(0) + 2 * tl + p_top(0) + 2) >> 2
}
_ => {
(p_top(x - 1) + 2 * p_top(x - 2) + p_top(x - 3) + 2) >> 2
}
};
out[at4(x as usize, y as usize)] = v;
}
}
}
fn predict_4x4_vl(s: &Samples4x4, out: &mut [i32; 16]) {
let p = top_row_4x4(s);
for y in 0..4i32 {
for x in 0..4i32 {
let v = if y == 0 || y == 2 {
let xp = (x + (y >> 1)) as usize;
(p[xp] + p[xp + 1] + 1) >> 1
} else {
let xp = (x + (y >> 1)) as usize;
(p[xp] + 2 * p[xp + 1] + p[xp + 2] + 2) >> 2
};
out[at4(x as usize, y as usize)] = v;
}
}
}
fn predict_4x4_hu(s: &Samples4x4, out: &mut [i32; 16]) {
let l = &s.left;
for y in 0..4i32 {
for x in 0..4i32 {
let z_hu = x + 2 * y;
let v = match z_hu {
0 | 2 | 4 => {
let yp = (y + (x >> 1)) as usize;
(l[yp] + l[yp + 1] + 1) >> 1
}
1 | 3 => {
let yp = (y + (x >> 1)) as usize;
let p2 = if yp + 2 < 4 { l[yp + 2] } else { l[3] };
let p1 = if yp + 1 < 4 { l[yp + 1] } else { l[3] };
(l[yp] + 2 * p1 + p2 + 2) >> 2
}
5 => {
(l[2] + 3 * l[3] + 2) >> 2
}
_ => {
l[3]
}
};
out[at4(x as usize, y as usize)] = v;
}
}
}
fn top_row_8x8_raw(s: &Samples8x8) -> [i32; 16] {
let mut p = [0i32; 16];
p[0..8].copy_from_slice(&s.top);
if s.availability.top_right {
p[8..16].copy_from_slice(&s.top_right);
} else if s.availability.top {
for slot in &mut p[8..16] {
*slot = s.top[7];
}
}
p
}
pub fn filter_samples_8x8(raw: &Samples8x8, _bit_depth: u32) -> Samples8x8 {
let raw_top = top_row_8x8_raw(raw);
let tl_avail = raw.availability.top_left;
let top_avail = raw.availability.top;
let left_avail = raw.availability.left;
let mut filt_top = [0i32; 16];
let mut filt_top_right = [0i32; 8];
if top_avail {
filt_top[0] = if tl_avail {
(raw.top_left + 2 * raw_top[0] + raw_top[1] + 2) >> 2
} else {
(3 * raw_top[0] + raw_top[1] + 2) >> 2
};
for x in 1..15 {
filt_top[x] = (raw_top[x - 1] + 2 * raw_top[x] + raw_top[x + 1] + 2) >> 2;
}
filt_top[15] = (raw_top[14] + 3 * raw_top[15] + 2) >> 2;
}
let mut filt_top_8 = [0i32; 8];
filt_top_8.copy_from_slice(&filt_top[0..8]);
filt_top_right.copy_from_slice(&filt_top[8..16]);
let filt_tl = if tl_avail {
if !top_avail || !left_avail {
if top_avail {
(3 * raw.top_left + raw_top[0] + 2) >> 2
} else if left_avail {
(3 * raw.top_left + raw.left[0] + 2) >> 2
} else {
raw.top_left
}
} else {
(raw_top[0] + 2 * raw.top_left + raw.left[0] + 2) >> 2
}
} else {
raw.top_left
};
let mut filt_left = [0i32; 8];
if left_avail {
filt_left[0] = if tl_avail {
(raw.top_left + 2 * raw.left[0] + raw.left[1] + 2) >> 2
} else {
(3 * raw.left[0] + raw.left[1] + 2) >> 2
};
#[allow(clippy::needless_range_loop)] for y in 1..7 {
filt_left[y] = (raw.left[y - 1] + 2 * raw.left[y] + raw.left[y + 1] + 2) >> 2;
}
filt_left[7] = (raw.left[6] + 3 * raw.left[7] + 2) >> 2;
}
Samples8x8 {
top_left: filt_tl,
top: filt_top_8,
top_right: filt_top_right,
left: filt_left,
availability: raw.availability,
}
}
fn filtered_top_16(s: &Samples8x8) -> [i32; 16] {
let mut p = [0i32; 16];
p[0..8].copy_from_slice(&s.top);
p[8..16].copy_from_slice(&s.top_right);
p
}
pub fn predict_8x8(mode: Intra8x8Mode, samples: &Samples8x8, bit_depth: u32, out: &mut [i32; 64]) {
match mode {
Intra8x8Mode::Vertical => predict_8x8_vertical(samples, out),
Intra8x8Mode::Horizontal => predict_8x8_horizontal(samples, out),
Intra8x8Mode::Dc => predict_8x8_dc(samples, bit_depth, out),
Intra8x8Mode::DiagonalDownLeft => predict_8x8_ddl(samples, out),
Intra8x8Mode::DiagonalDownRight => predict_8x8_ddr(samples, out),
Intra8x8Mode::VerticalRight => predict_8x8_vr(samples, out),
Intra8x8Mode::HorizontalDown => predict_8x8_hd(samples, out),
Intra8x8Mode::VerticalLeft => predict_8x8_vl(samples, out),
Intra8x8Mode::HorizontalUp => predict_8x8_hu(samples, out),
}
}
#[inline]
fn at8(x: usize, y: usize) -> usize {
y * 8 + x
}
fn predict_8x8_vertical(s: &Samples8x8, out: &mut [i32; 64]) {
for y in 0..8 {
for x in 0..8 {
out[at8(x, y)] = s.top[x];
}
}
}
fn predict_8x8_horizontal(s: &Samples8x8, out: &mut [i32; 64]) {
for y in 0..8 {
for x in 0..8 {
out[at8(x, y)] = s.left[y];
}
}
}
fn predict_8x8_dc(s: &Samples8x8, bit_depth: u32, out: &mut [i32; 64]) {
let dc = if s.availability.top && s.availability.left {
let sum_top: i32 = s.top.iter().sum();
let sum_left: i32 = s.left.iter().sum();
(sum_top + sum_left + 8) >> 4
} else if s.availability.left {
let sum_left: i32 = s.left.iter().sum();
(sum_left + 4) >> 3
} else if s.availability.top {
let sum_top: i32 = s.top.iter().sum();
(sum_top + 4) >> 3
} else {
dc_default(bit_depth)
};
for sample in out.iter_mut() {
*sample = dc;
}
}
fn predict_8x8_ddl(s: &Samples8x8, out: &mut [i32; 64]) {
let p = filtered_top_16(s);
for y in 0..8 {
for x in 0..8 {
let v = if x == 7 && y == 7 {
(p[14] + 3 * p[15] + 2) >> 2
} else {
(p[x + y] + 2 * p[x + y + 1] + p[x + y + 2] + 2) >> 2
};
out[at8(x, y)] = v;
}
}
}
fn predict_8x8_ddr(s: &Samples8x8, out: &mut [i32; 64]) {
let tl = s.top_left;
let t = &s.top;
let l = &s.left;
let p_top = |x: i32| -> i32 {
if x == -1 {
tl
} else {
t[x as usize]
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
tl
} else {
l[y as usize]
}
};
for y in 0..8i32 {
for x in 0..8i32 {
let v = if x > y {
(p_top(x - y - 2) + 2 * p_top(x - y - 1) + p_top(x - y) + 2) >> 2
} else if x < y {
(p_left(y - x - 2) + 2 * p_left(y - x - 1) + p_left(y - x) + 2) >> 2
} else {
(p_top(0) + 2 * tl + p_left(0) + 2) >> 2
};
out[at8(x as usize, y as usize)] = v;
}
}
}
fn predict_8x8_vr(s: &Samples8x8, out: &mut [i32; 64]) {
let tl = s.top_left;
let p = filtered_top_16(s);
let l = &s.left;
let p_top = |x: i32| -> i32 {
if x == -1 {
tl
} else {
p[x as usize]
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
tl
} else {
l[y as usize]
}
};
for y in 0..8i32 {
for x in 0..8i32 {
let z_vr = 2 * x - y;
let v = match z_vr {
0 | 2 | 4 | 6 | 8 | 10 | 12 | 14 => {
let xp = x - (y >> 1) - 1;
(p_top(xp) + p_top(xp + 1) + 1) >> 1
}
1 | 3 | 5 | 7 | 9 | 11 | 13 => {
let xp = x - (y >> 1) - 2;
(p_top(xp) + 2 * p_top(xp + 1) + p_top(xp + 2) + 2) >> 2
}
-1 => {
(p_left(0) + 2 * tl + p_top(0) + 2) >> 2
}
_ => {
(p_left(y - 2 * x - 1) + 2 * p_left(y - 2 * x - 2) + p_left(y - 2 * x - 3) + 2)
>> 2
}
};
out[at8(x as usize, y as usize)] = v;
}
}
}
fn predict_8x8_hd(s: &Samples8x8, out: &mut [i32; 64]) {
let tl = s.top_left;
let t = &s.top;
let l = &s.left;
let p_top = |x: i32| -> i32 {
if x == -1 {
tl
} else {
t[x as usize]
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
tl
} else {
l[y as usize]
}
};
for y in 0..8i32 {
for x in 0..8i32 {
let z_hd = 2 * y - x;
let v = match z_hd {
0 | 2 | 4 | 6 | 8 | 10 | 12 | 14 => {
let yp = y - (x >> 1) - 1;
(p_left(yp) + p_left(yp + 1) + 1) >> 1
}
1 | 3 | 5 | 7 | 9 | 11 | 13 => {
let yp = y - (x >> 1) - 2;
(p_left(yp) + 2 * p_left(yp + 1) + p_left(yp + 2) + 2) >> 2
}
-1 => {
(p_left(0) + 2 * tl + p_top(0) + 2) >> 2
}
_ => {
(p_top(x - 2 * y - 1) + 2 * p_top(x - 2 * y - 2) + p_top(x - 2 * y - 3) + 2)
>> 2
}
};
out[at8(x as usize, y as usize)] = v;
}
}
}
fn predict_8x8_vl(s: &Samples8x8, out: &mut [i32; 64]) {
let p = filtered_top_16(s);
for y in 0..8 {
for x in 0..8 {
let v = if y == 0 || y == 2 || y == 4 || y == 6 {
let xp = x + (y >> 1);
(p[xp] + p[xp + 1] + 1) >> 1
} else {
let xp = x + (y >> 1);
(p[xp] + 2 * p[xp + 1] + p[xp + 2] + 2) >> 2
};
out[at8(x, y)] = v;
}
}
}
fn predict_8x8_hu(s: &Samples8x8, out: &mut [i32; 64]) {
let l = &s.left;
for y in 0..8i32 {
for x in 0..8i32 {
let z_hu = x + 2 * y;
let v = match z_hu {
0 | 2 | 4 | 6 | 8 | 10 | 12 => {
let yp = (y + (x >> 1)) as usize;
(l[yp] + l[yp + 1] + 1) >> 1
}
1 | 3 | 5 | 7 | 9 | 11 => {
let yp = (y + (x >> 1)) as usize;
let p1 = if yp + 1 < 8 { l[yp + 1] } else { l[7] };
let p2 = if yp + 2 < 8 { l[yp + 2] } else { l[7] };
(l[yp] + 2 * p1 + p2 + 2) >> 2
}
13 => {
(l[6] + 3 * l[7] + 2) >> 2
}
_ => {
l[7]
}
};
out[at8(x as usize, y as usize)] = v;
}
}
}
pub fn predict_16x16(
mode: Intra16x16Mode,
samples: &Samples16x16,
bit_depth: u32,
out: &mut [i32; 256],
) {
match mode {
Intra16x16Mode::Vertical => predict_16x16_vertical(samples, out),
Intra16x16Mode::Horizontal => predict_16x16_horizontal(samples, out),
Intra16x16Mode::Dc => predict_16x16_dc(samples, bit_depth, out),
Intra16x16Mode::Plane => predict_16x16_plane(samples, bit_depth, out),
}
}
#[inline]
fn at16(x: usize, y: usize) -> usize {
y * 16 + x
}
fn predict_16x16_vertical(s: &Samples16x16, out: &mut [i32; 256]) {
for y in 0..16 {
for x in 0..16 {
out[at16(x, y)] = s.top[x];
}
}
}
fn predict_16x16_horizontal(s: &Samples16x16, out: &mut [i32; 256]) {
for y in 0..16 {
for x in 0..16 {
out[at16(x, y)] = s.left[y];
}
}
}
fn predict_16x16_dc(s: &Samples16x16, bit_depth: u32, out: &mut [i32; 256]) {
let dc = if s.availability.top && s.availability.left {
let sum_top: i32 = s.top.iter().sum();
let sum_left: i32 = s.left.iter().sum();
(sum_top + sum_left + 16) >> 5
} else if s.availability.left {
let sum_left: i32 = s.left.iter().sum();
(sum_left + 8) >> 4
} else if s.availability.top {
let sum_top: i32 = s.top.iter().sum();
(sum_top + 8) >> 4
} else {
dc_default(bit_depth)
};
for sample in out.iter_mut() {
*sample = dc;
}
}
fn predict_16x16_plane(s: &Samples16x16, bit_depth: u32, out: &mut [i32; 256]) {
let p_top = |x: i32| -> i32 {
if x == -1 {
s.top_left
} else {
s.top[x as usize]
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
s.top_left
} else {
s.left[y as usize]
}
};
let mut h: i32 = 0;
for xp in 0..8i32 {
h += (xp + 1) * (p_top(8 + xp) - p_top(6 - xp));
}
let mut v: i32 = 0;
for yp in 0..8i32 {
v += (yp + 1) * (p_left(8 + yp) - p_left(6 - yp));
}
let a = 16 * (s.left[15] + s.top[15]);
let b = (5 * h + 32) >> 6;
let c = (5 * v + 32) >> 6;
for y in 0..16i32 {
for x in 0..16i32 {
let raw = (a + b * (x - 7) + c * (y - 7) + 16) >> 5;
out[at16(x as usize, y as usize)] = clip1(raw, bit_depth);
}
}
}
pub fn predict_chroma(
mode: IntraChromaMode,
samples: &SamplesChroma,
chroma_type: ChromaArrayType,
bit_depth: u32,
out: &mut [i32],
) {
let w = chroma_type.width();
let h = chroma_type.height();
assert_eq!(out.len(), w * h, "chroma output buffer size mismatch");
assert_eq!(samples.top.len(), w, "chroma top row length mismatch");
assert_eq!(samples.left.len(), h, "chroma left column length mismatch");
match mode {
IntraChromaMode::Dc => predict_chroma_dc(samples, chroma_type, bit_depth, out),
IntraChromaMode::Horizontal => predict_chroma_horizontal(samples, chroma_type, out),
IntraChromaMode::Vertical => predict_chroma_vertical(samples, chroma_type, out),
IntraChromaMode::Plane => predict_chroma_plane(samples, chroma_type, bit_depth, out),
}
}
fn predict_chroma_dc(
s: &SamplesChroma,
chroma_type: ChromaArrayType,
bit_depth: u32,
out: &mut [i32],
) {
let w = chroma_type.width();
let h = chroma_type.height();
let default_dc = dc_default(bit_depth);
let mut y_o = 0;
while y_o < h {
let mut x_o = 0;
while x_o < w {
let dc = chroma_dc_subblock(s, x_o, y_o, w, h, default_dc);
for yy in 0..4 {
for xx in 0..4 {
out[(y_o + yy) * w + (x_o + xx)] = dc;
}
}
x_o += 4;
}
y_o += 4;
}
}
fn chroma_dc_subblock(
s: &SamplesChroma,
x_o: usize,
y_o: usize,
_w: usize,
_h: usize,
default_dc: i32,
) -> i32 {
let top_avail = s.availability.top;
let left_avail = s.availability.left;
let sum_top = || -> i32 { s.top[x_o] + s.top[x_o + 1] + s.top[x_o + 2] + s.top[x_o + 3] };
let sum_left = || -> i32 { s.left[y_o] + s.left[y_o + 1] + s.left[y_o + 2] + s.left[y_o + 3] };
if x_o == 0 && y_o == 0 {
if top_avail && left_avail {
(sum_top() + sum_left() + 4) >> 3
} else if top_avail {
(sum_top() + 2) >> 2
} else if left_avail {
(sum_left() + 2) >> 2
} else {
default_dc
}
} else if x_o > 0 && y_o == 0 {
if top_avail {
(sum_top() + 2) >> 2
} else if left_avail {
(sum_left() + 2) >> 2
} else {
default_dc
}
} else if x_o == 0 && y_o > 0 {
if left_avail {
(sum_left() + 2) >> 2
} else if top_avail {
(sum_top() + 2) >> 2
} else {
default_dc
}
} else {
if top_avail && left_avail {
(sum_top() + sum_left() + 4) >> 3
} else if top_avail {
(sum_top() + 2) >> 2
} else if left_avail {
(sum_left() + 2) >> 2
} else {
default_dc
}
}
}
fn predict_chroma_horizontal(s: &SamplesChroma, chroma_type: ChromaArrayType, out: &mut [i32]) {
let w = chroma_type.width();
let h = chroma_type.height();
for y in 0..h {
for x in 0..w {
out[y * w + x] = s.left[y];
}
}
}
fn predict_chroma_vertical(s: &SamplesChroma, chroma_type: ChromaArrayType, out: &mut [i32]) {
let w = chroma_type.width();
let h = chroma_type.height();
for y in 0..h {
for x in 0..w {
out[y * w + x] = s.top[x];
}
}
}
fn predict_chroma_plane(
s: &SamplesChroma,
chroma_type: ChromaArrayType,
bit_depth: u32,
out: &mut [i32],
) {
let w = chroma_type.width();
let h = chroma_type.height();
let is_yuv422 = chroma_type == ChromaArrayType::Yuv422;
let x_cf: i32 = 0;
let y_cf: i32 = if is_yuv422 { 4 } else { 0 };
let p_top = |x: i32| -> i32 {
if x == -1 {
s.top_left
} else {
s.top[x as usize]
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
s.top_left
} else {
s.left[y as usize]
}
};
let mut h_sum: i32 = 0;
for xp in 0..=(3 + x_cf) {
h_sum += (xp + 1) * (p_top(4 + x_cf + xp) - p_top(2 + x_cf - xp));
}
let mut v_sum: i32 = 0;
for yp in 0..=(3 + y_cf) {
v_sum += (yp + 1) * (p_left(4 + y_cf + yp) - p_left(2 + y_cf - yp));
}
let a = 16 * (s.left[h - 1] + s.top[w - 1]);
let b = (34 * h_sum + 32) >> 6;
let c_coef = if is_yuv422 { 5 } else { 34 };
let c = (c_coef * v_sum + 32) >> 6;
for y in 0..h {
for x in 0..w {
let xi = x as i32;
let yi = y as i32;
let raw = (a + b * (xi - 3 - x_cf) + c * (yi - 3 - y_cf) + 16) >> 5;
out[y * w + x] = clip1(raw, bit_depth);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn all_avail() -> Neighbour4x4Availability {
Neighbour4x4Availability {
top_left: true,
top: true,
top_right: true,
left: true,
}
}
fn no_avail() -> Neighbour4x4Availability {
Neighbour4x4Availability::default()
}
#[test]
fn intra4x4_mode_index_roundtrip() {
for i in 0..=8u8 {
let m = Intra4x4Mode::from_index(i).unwrap();
assert_eq!(m.as_index(), i);
}
assert!(Intra4x4Mode::from_index(9).is_none());
}
#[test]
fn intra8x8_mode_index_roundtrip() {
for i in 0..=8u8 {
let m = Intra8x8Mode::from_index(i).unwrap();
assert_eq!(m.as_index(), i);
}
}
#[test]
fn intra16x16_mode_index_roundtrip() {
for i in 0..=3u8 {
let m = Intra16x16Mode::from_index(i).unwrap();
assert_eq!(m.as_index(), i);
}
assert!(Intra16x16Mode::from_index(4).is_none());
}
#[test]
fn intra_chroma_mode_index_roundtrip() {
for i in 0..=3u8 {
let m = IntraChromaMode::from_index(i).unwrap();
assert_eq!(m.as_index(), i);
}
}
fn mk_4x4(top: [i32; 4], top_right: [i32; 4], left: [i32; 4], tl: i32) -> Samples4x4 {
Samples4x4 {
top_left: tl,
top,
top_right,
left,
availability: all_avail(),
}
}
#[test]
fn intra_4x4_dc_all_neighbours() {
let s = mk_4x4([10, 20, 30, 40], [50, 60, 70, 80], [5, 15, 25, 35], 0);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::Dc, &s, 8, &mut out);
for v in out.iter() {
assert_eq!(*v, 23);
}
}
#[test]
fn intra_4x4_dc_no_neighbours() {
let mut s = mk_4x4([0; 4], [0; 4], [0; 4], 0);
s.availability = no_avail();
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::Dc, &s, 8, &mut out);
for v in out.iter() {
assert_eq!(*v, 128);
}
}
#[test]
fn intra_4x4_dc_only_left() {
let mut s = mk_4x4([0; 4], [0; 4], [4, 8, 12, 16], 0);
s.availability = Neighbour4x4Availability {
top_left: false,
top: false,
top_right: false,
left: true,
};
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::Dc, &s, 8, &mut out);
for v in out.iter() {
assert_eq!(*v, 10);
}
}
#[test]
fn intra_4x4_dc_only_top() {
let mut s = mk_4x4([1, 3, 5, 7], [0; 4], [0; 4], 0);
s.availability = Neighbour4x4Availability {
top_left: false,
top: true,
top_right: false,
left: false,
};
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::Dc, &s, 8, &mut out);
for v in out.iter() {
assert_eq!(*v, 4);
}
}
#[test]
fn intra_4x4_vertical() {
let s = mk_4x4([11, 22, 33, 44], [0; 4], [0; 4], 0);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::Vertical, &s, 8, &mut out);
for y in 0..4 {
for x in 0..4 {
assert_eq!(out[at4(x, y)], s.top[x]);
}
}
}
#[test]
fn intra_4x4_horizontal() {
let s = mk_4x4([0; 4], [0; 4], [7, 14, 21, 28], 0);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::Horizontal, &s, 8, &mut out);
for y in 0..4 {
for x in 0..4 {
assert_eq!(out[at4(x, y)], s.left[y]);
}
}
}
#[test]
fn intra_4x4_ddl_hand() {
let s = mk_4x4([0, 1, 2, 3], [4, 5, 6, 7], [0; 4], 0);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::DiagonalDownLeft, &s, 8, &mut out);
assert_eq!(out[at4(0, 0)], 1);
assert_eq!(out[at4(3, 3)], 7);
assert_eq!(out[at4(1, 0)], 2);
assert_eq!(out[at4(0, 1)], 2);
}
#[test]
fn intra_4x4_ddl_top_right_substitution() {
let mut s = mk_4x4([10, 20, 30, 40], [99; 4], [0; 4], 0);
s.availability.top_right = false;
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::DiagonalDownLeft, &s, 8, &mut out);
assert_eq!(out[at4(3, 0)], 40);
}
#[test]
fn intra_4x4_ddr_hand() {
let s = mk_4x4([10, 20, 30, 40], [0; 4], [50, 60, 70, 80], 100);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::DiagonalDownRight, &s, 8, &mut out);
assert_eq!(out[at4(0, 0)], 65);
assert_eq!(out[at4(1, 0)], 35);
assert_eq!(out[at4(0, 1)], 65);
}
#[test]
fn intra_4x4_vr_hand() {
let s = mk_4x4([10, 20, 30, 40], [0; 4], [60, 70, 80, 90], 50);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::VerticalRight, &s, 8, &mut out);
assert_eq!(out[at4(0, 0)], 30);
assert_eq!(out[at4(0, 1)], 43);
}
#[test]
fn intra_4x4_hd_hand() {
let s = mk_4x4([10, 20, 30, 40], [0; 4], [60, 70, 80, 90], 50);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::HorizontalDown, &s, 8, &mut out);
assert_eq!(out[at4(0, 0)], 55);
assert_eq!(out[at4(1, 0)], 43);
}
#[test]
fn intra_4x4_vl_hand() {
let s = mk_4x4([10, 20, 30, 40], [50, 60, 70, 80], [0; 4], 0);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::VerticalLeft, &s, 8, &mut out);
assert_eq!(out[at4(0, 0)], 15);
assert_eq!(out[at4(0, 1)], 20);
}
#[test]
fn intra_4x4_hu_hand() {
let s = mk_4x4([0; 4], [0; 4], [10, 20, 30, 40], 0);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::HorizontalUp, &s, 8, &mut out);
assert_eq!(out[at4(0, 0)], 15);
assert_eq!(out[at4(1, 0)], 20);
assert_eq!(out[at4(3, 3)], 40);
assert_eq!(out[at4(1, 2)], 38);
}
#[test]
fn intra_4x4_ddl_all_samples() {
let s = mk_4x4([8, 9, 10, 11], [12, 13, 14, 15], [0; 4], 0);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::DiagonalDownLeft, &s, 8, &mut out);
let p = [8, 9, 10, 11, 12, 13, 14, 15];
for y in 0..4 {
for x in 0..4 {
let expected = if x == 3 && y == 3 {
(p[6] + 3 * p[7] + 2) >> 2
} else {
(p[x + y] + 2 * p[x + y + 1] + p[x + y + 2] + 2) >> 2
};
assert_eq!(out[at4(x, y)], expected, "DDL mismatch at ({}, {})", x, y);
}
}
}
#[test]
fn intra_4x4_ddr_all_samples() {
let s = mk_4x4([10, 20, 30, 40], [0; 4], [50, 60, 70, 80], 5);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::DiagonalDownRight, &s, 8, &mut out);
let p_top = [10, 20, 30, 40];
let p_left = [50, 60, 70, 80];
let tl = 5;
let fetch_top = |x: i32| -> i32 {
if x == -1 {
tl
} else {
p_top[x as usize]
}
};
let fetch_left = |y: i32| -> i32 {
if y == -1 {
tl
} else {
p_left[y as usize]
}
};
for y in 0..4i32 {
for x in 0..4i32 {
let expected = if x > y {
(fetch_top(x - y - 2) + 2 * fetch_top(x - y - 1) + fetch_top(x - y) + 2) >> 2
} else if x < y {
(fetch_left(y - x - 2) + 2 * fetch_left(y - x - 1) + fetch_left(y - x) + 2) >> 2
} else {
(fetch_top(0) + 2 * tl + fetch_left(0) + 2) >> 2
};
assert_eq!(
out[at4(x as usize, y as usize)],
expected,
"DDR mismatch at ({}, {})",
x,
y
);
}
}
}
#[test]
fn intra_4x4_vr_all_samples() {
let s = mk_4x4([100, 110, 120, 130], [0; 4], [50, 60, 70, 80], 5);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::VerticalRight, &s, 8, &mut out);
let tl = 5;
let top = [100, 110, 120, 130];
let left = [50, 60, 70, 80];
let p_top = |x: i32| -> i32 {
if x == -1 {
tl
} else {
top[x as usize]
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
tl
} else {
left[y as usize]
}
};
for y in 0..4i32 {
for x in 0..4i32 {
let z = 2 * x - y;
let expected = match z {
0 | 2 | 4 | 6 => {
let xp = x - (y >> 1) - 1;
(p_top(xp) + p_top(xp + 1) + 1) >> 1
}
1 | 3 | 5 => {
let xp = x - (y >> 1) - 2;
(p_top(xp) + 2 * p_top(xp + 1) + p_top(xp + 2) + 2) >> 2
}
-1 => (p_left(0) + 2 * tl + p_top(0) + 2) >> 2,
_ => {
(p_left(y - 1) + 2 * p_left(y - 2) + p_left(y - 3) + 2) >> 2
}
};
assert_eq!(
out[at4(x as usize, y as usize)],
expected,
"VR mismatch at ({}, {})",
x,
y
);
}
}
}
#[test]
fn intra_4x4_hd_all_samples() {
let s = mk_4x4([100, 110, 120, 130], [0; 4], [50, 60, 70, 80], 5);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::HorizontalDown, &s, 8, &mut out);
let tl = 5;
let top = [100, 110, 120, 130];
let left = [50, 60, 70, 80];
let p_top = |x: i32| -> i32 {
if x == -1 {
tl
} else {
top[x as usize]
}
};
let p_left = |y: i32| -> i32 {
if y == -1 {
tl
} else {
left[y as usize]
}
};
for y in 0..4i32 {
for x in 0..4i32 {
let z = 2 * y - x;
let expected = match z {
0 | 2 | 4 | 6 => {
let yp = y - (x >> 1) - 1;
(p_left(yp) + p_left(yp + 1) + 1) >> 1
}
1 | 3 | 5 => {
let yp = y - (x >> 1) - 2;
(p_left(yp) + 2 * p_left(yp + 1) + p_left(yp + 2) + 2) >> 2
}
-1 => (p_left(0) + 2 * tl + p_top(0) + 2) >> 2,
_ => {
(p_top(x - 1) + 2 * p_top(x - 2) + p_top(x - 3) + 2) >> 2
}
};
assert_eq!(
out[at4(x as usize, y as usize)],
expected,
"HD mismatch at ({}, {})",
x,
y
);
}
}
}
#[test]
fn intra_4x4_vl_all_samples() {
let s = mk_4x4([8, 9, 10, 11], [12, 13, 14, 15], [0; 4], 0);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::VerticalLeft, &s, 8, &mut out);
let p = [8, 9, 10, 11, 12, 13, 14, 15];
for y in 0..4i32 {
for x in 0..4i32 {
let xp = (x + (y >> 1)) as usize;
let expected = if y == 0 || y == 2 {
(p[xp] + p[xp + 1] + 1) >> 1
} else {
(p[xp] + 2 * p[xp + 1] + p[xp + 2] + 2) >> 2
};
assert_eq!(
out[at4(x as usize, y as usize)],
expected,
"VL mismatch at ({}, {})",
x,
y
);
}
}
}
#[test]
fn intra_4x4_hu_all_samples() {
let s = mk_4x4([0; 4], [0; 4], [50, 60, 70, 80], 0);
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::HorizontalUp, &s, 8, &mut out);
let l = [50, 60, 70, 80];
for y in 0..4i32 {
for x in 0..4i32 {
let z_hu = x + 2 * y;
let expected = match z_hu {
0 | 2 | 4 => {
let yp = (y + (x >> 1)) as usize;
(l[yp] + l[yp + 1] + 1) >> 1
}
1 | 3 => {
let yp = (y + (x >> 1)) as usize;
let p1 = if yp + 1 < 4 { l[yp + 1] } else { l[3] };
let p2 = if yp + 2 < 4 { l[yp + 2] } else { l[3] };
(l[yp] + 2 * p1 + p2 + 2) >> 2
}
5 => (l[2] + 3 * l[3] + 2) >> 2,
_ => l[3],
};
assert_eq!(
out[at4(x as usize, y as usize)],
expected,
"HU mismatch at ({}, {})",
x,
y
);
}
}
}
#[test]
fn intra_4x4_ddl_top_row_substitution_applied() {
let mut s = mk_4x4([10, 10, 10, 10], [99; 4], [0; 4], 0);
s.availability.top_right = false;
let mut out = [0i32; 16];
predict_4x4(Intra4x4Mode::DiagonalDownLeft, &s, 8, &mut out);
for v in out.iter() {
assert_eq!(*v, 10);
}
}
fn mk_8x8(top: [i32; 8], top_right: [i32; 8], left: [i32; 8], tl: i32) -> Samples8x8 {
Samples8x8 {
top_left: tl,
top,
top_right,
left,
availability: all_avail(),
}
}
#[test]
fn intra_8x8_filter_basic() {
let raw = mk_8x8([100; 8], [100; 8], [100; 8], 100);
let filtered = filter_samples_8x8(&raw, 8);
for v in filtered.top.iter() {
assert_eq!(*v, 100);
}
for v in filtered.top_right.iter() {
assert_eq!(*v, 100);
}
for v in filtered.left.iter() {
assert_eq!(*v, 100);
}
assert_eq!(filtered.top_left, 100);
}
#[test]
fn intra_8x8_filter_known() {
let raw = mk_8x8(
[0, 4, 8, 12, 16, 20, 24, 28],
[32, 36, 40, 44, 48, 52, 56, 60],
[100; 8],
50,
);
let filtered = filter_samples_8x8(&raw, 8);
assert_eq!(filtered.top[0], 14);
assert_eq!(filtered.top[1], 4);
assert_eq!(filtered.top_right[7], 59);
assert_eq!(filtered.left[0], 88);
assert_eq!(filtered.left[7], 100);
assert_eq!(filtered.top_left, 50);
}
#[test]
fn intra_8x8_dc_all() {
let raw = mk_8x8([100; 8], [100; 8], [100; 8], 100);
let filtered = filter_samples_8x8(&raw, 8);
let mut out = [0i32; 64];
predict_8x8(Intra8x8Mode::Dc, &filtered, 8, &mut out);
for v in out.iter() {
assert_eq!(*v, 100);
}
}
#[test]
fn intra_8x8_dc_no_neighbours() {
let mut raw = mk_8x8([0; 8], [0; 8], [0; 8], 0);
raw.availability = no_avail();
let filtered = filter_samples_8x8(&raw, 8);
let mut out = [0i32; 64];
predict_8x8(Intra8x8Mode::Dc, &filtered, 8, &mut out);
for v in out.iter() {
assert_eq!(*v, 128);
}
}
#[test]
fn intra_8x8_vertical() {
let raw = mk_8x8([1, 2, 3, 4, 5, 6, 7, 8], [9; 8], [0; 8], 0);
let filtered = filter_samples_8x8(&raw, 8);
let mut out = [0i32; 64];
predict_8x8(Intra8x8Mode::Vertical, &filtered, 8, &mut out);
for y in 0..8 {
for x in 0..8 {
assert_eq!(out[at8(x, y)], filtered.top[x]);
}
}
}
#[test]
fn intra_8x8_horizontal() {
let raw = mk_8x8([0; 8], [0; 8], [10, 20, 30, 40, 50, 60, 70, 80], 0);
let filtered = filter_samples_8x8(&raw, 8);
let mut out = [0i32; 64];
predict_8x8(Intra8x8Mode::Horizontal, &filtered, 8, &mut out);
for y in 0..8 {
for x in 0..8 {
assert_eq!(out[at8(x, y)], filtered.left[y]);
}
}
}
#[test]
fn intra_8x8_ddl_constant() {
let raw = mk_8x8([77; 8], [77; 8], [77; 8], 77);
let filtered = filter_samples_8x8(&raw, 8);
let mut out = [0i32; 64];
predict_8x8(Intra8x8Mode::DiagonalDownLeft, &filtered, 8, &mut out);
for v in out.iter() {
assert_eq!(*v, 77);
}
}
#[test]
fn intra_8x8_ddr_constant() {
let raw = mk_8x8([42; 8], [42; 8], [42; 8], 42);
let filtered = filter_samples_8x8(&raw, 8);
let mut out = [0i32; 64];
predict_8x8(Intra8x8Mode::DiagonalDownRight, &filtered, 8, &mut out);
for v in out.iter() {
assert_eq!(*v, 42);
}
}
fn mk_16x16(top: [i32; 16], left: [i32; 16], tl: i32) -> Samples16x16 {
Samples16x16 {
top_left: tl,
top,
left,
availability: all_avail(),
}
}
#[test]
fn intra_16x16_vertical() {
let mut top = [0i32; 16];
for (i, t) in top.iter_mut().enumerate() {
*t = i as i32 * 2;
}
let s = mk_16x16(top, [0; 16], 0);
let mut out = [0i32; 256];
predict_16x16(Intra16x16Mode::Vertical, &s, 8, &mut out);
for y in 0..16 {
for x in 0..16 {
assert_eq!(out[at16(x, y)], top[x]);
}
}
}
#[test]
fn intra_16x16_horizontal() {
let mut left = [0i32; 16];
for (i, l) in left.iter_mut().enumerate() {
*l = i as i32 + 5;
}
let s = mk_16x16([0; 16], left, 0);
let mut out = [0i32; 256];
predict_16x16(Intra16x16Mode::Horizontal, &s, 8, &mut out);
for y in 0..16 {
for x in 0..16 {
assert_eq!(out[at16(x, y)], left[y]);
}
}
}
#[test]
fn intra_16x16_dc_all_100() {
let s = mk_16x16([100; 16], [100; 16], 100);
let mut out = [0i32; 256];
predict_16x16(Intra16x16Mode::Dc, &s, 8, &mut out);
for v in out.iter() {
assert_eq!(*v, 100);
}
}
#[test]
fn intra_16x16_dc_no_neighbours() {
let mut s = mk_16x16([0; 16], [0; 16], 0);
s.availability = no_avail();
let mut out = [0i32; 256];
predict_16x16(Intra16x16Mode::Dc, &s, 8, &mut out);
for v in out.iter() {
assert_eq!(*v, 128);
}
}
#[test]
fn intra_16x16_plane_constant() {
let s = mk_16x16([80; 16], [80; 16], 80);
let mut out = [0i32; 256];
predict_16x16(Intra16x16Mode::Plane, &s, 8, &mut out);
for v in out.iter() {
assert_eq!(*v, 80);
}
}
#[test]
fn intra_16x16_plane_hand() {
let mut top = [0i32; 16];
let mut left = [0i32; 16];
for i in 0..16 {
top[i] = i as i32;
left[i] = i as i32;
}
let s = mk_16x16(top, left, 0);
let mut out = [0i32; 256];
predict_16x16(Intra16x16Mode::Plane, &s, 8, &mut out);
assert_eq!(out[at16(7, 7)], 15);
}
fn mk_chroma_420(top: Vec<i32>, left: Vec<i32>, tl: i32) -> SamplesChroma {
SamplesChroma {
top_left: tl,
top,
left,
availability: all_avail(),
}
}
#[test]
fn intra_chroma_dc_420_all_avail() {
let s = mk_chroma_420(
vec![10, 20, 30, 40, 50, 60, 70, 80],
vec![5, 15, 25, 35, 45, 55, 65, 75],
0,
);
let mut out = vec![0i32; 64];
predict_chroma(
IntraChromaMode::Dc,
&s,
ChromaArrayType::Yuv420,
8,
&mut out,
);
for y in 0..4 {
for x in 0..4 {
assert_eq!(out[y * 8 + x], 23, "TL subblock at ({},{})", x, y);
}
}
for y in 0..4 {
for x in 4..8 {
assert_eq!(out[y * 8 + x], 65, "TR subblock at ({},{})", x, y);
}
}
for y in 4..8 {
for x in 0..4 {
assert_eq!(out[y * 8 + x], 60, "BL subblock at ({},{})", x, y);
}
}
for y in 4..8 {
for x in 4..8 {
assert_eq!(out[y * 8 + x], 63, "BR subblock at ({},{})", x, y);
}
}
}
#[test]
fn intra_chroma_dc_420_no_neighbours() {
let mut s = mk_chroma_420(vec![0; 8], vec![0; 8], 0);
s.availability = no_avail();
let mut out = vec![0i32; 64];
predict_chroma(
IntraChromaMode::Dc,
&s,
ChromaArrayType::Yuv420,
8,
&mut out,
);
for v in out.iter() {
assert_eq!(*v, 128);
}
}
#[test]
fn intra_chroma_vertical_420() {
let s = mk_chroma_420(vec![1, 2, 3, 4, 5, 6, 7, 8], vec![0; 8], 0);
let mut out = vec![0i32; 64];
predict_chroma(
IntraChromaMode::Vertical,
&s,
ChromaArrayType::Yuv420,
8,
&mut out,
);
for y in 0..8 {
for x in 0..8 {
assert_eq!(out[y * 8 + x], s.top[x]);
}
}
}
#[test]
fn intra_chroma_horizontal_420() {
let s = mk_chroma_420(vec![0; 8], vec![10, 20, 30, 40, 50, 60, 70, 80], 0);
let mut out = vec![0i32; 64];
predict_chroma(
IntraChromaMode::Horizontal,
&s,
ChromaArrayType::Yuv420,
8,
&mut out,
);
for y in 0..8 {
for x in 0..8 {
assert_eq!(out[y * 8 + x], s.left[y]);
}
}
}
#[test]
fn intra_chroma_plane_420_constant() {
let s = mk_chroma_420(vec![60; 8], vec![60; 8], 60);
let mut out = vec![0i32; 64];
predict_chroma(
IntraChromaMode::Plane,
&s,
ChromaArrayType::Yuv420,
8,
&mut out,
);
for v in out.iter() {
assert_eq!(*v, 60);
}
}
#[test]
fn intra_chroma_plane_420_hand() {
let s = mk_chroma_420(
vec![0, 2, 4, 6, 8, 10, 12, 14],
vec![0, 2, 4, 6, 8, 10, 12, 14],
0,
);
let mut out = vec![0i32; 64];
predict_chroma(
IntraChromaMode::Plane,
&s,
ChromaArrayType::Yuv420,
8,
&mut out,
);
assert_eq!(out[3 * 8 + 3], 14);
}
#[test]
fn intra_chroma_vertical_422() {
let s = SamplesChroma {
top_left: 0,
top: vec![1, 2, 3, 4, 5, 6, 7, 8],
left: vec![0; 16],
availability: all_avail(),
};
let mut out = vec![0i32; 128];
predict_chroma(
IntraChromaMode::Vertical,
&s,
ChromaArrayType::Yuv422,
8,
&mut out,
);
for y in 0..16 {
for x in 0..8 {
assert_eq!(out[y * 8 + x], s.top[x]);
}
}
}
#[test]
fn intra_chroma_plane_422_constant() {
let s = SamplesChroma {
top_left: 60,
top: vec![60; 8],
left: vec![60; 16],
availability: all_avail(),
};
let mut out = vec![0i32; 128];
predict_chroma(
IntraChromaMode::Plane,
&s,
ChromaArrayType::Yuv422,
8,
&mut out,
);
for v in out.iter() {
assert_eq!(*v, 60);
}
}
#[test]
fn clip1_boundary() {
assert_eq!(clip1(-1, 8), 0);
assert_eq!(clip1(0, 8), 0);
assert_eq!(clip1(255, 8), 255);
assert_eq!(clip1(256, 8), 255);
assert_eq!(clip1(1023, 10), 1023);
assert_eq!(clip1(1024, 10), 1023);
}
}