use crate::intops::{iclip, imin};
use crate::levels::{BlockSize, InterIntraPredMode, N_BS_SIZES};
use crate::tables::BLOCK_DIMENSIONS;
const BS_64X64: usize = BlockSize::Bs64x64 as u8 as usize;
const WEDGE_OFFSETS_LEN: usize = N_BS_SIZES - BS_64X64 - 6;
const II_NONDC_OFFSETS_LEN: usize = N_BS_SIZES - BS_64X64;
const WEDGE_444_LEN: usize = 68 * (64 + 32 + 16 + 8) * (64 + 32 + 16 + 8);
const WEDGE_422_LEN: usize = 68 * (32 + 16 + 8 + 4) * (64 + 32 + 16 + 8);
const WEDGE_420_LEN: usize = 68 * (32 + 16 + 8 + 4) * (32 + 16 + 8 + 4);
const WEDGE_TMVP_LEN: usize = 68 * (8 + 4 + 2 + 1) * (8 + 4 + 2 + 1);
const II_DC_LEN: usize = 64 * 64;
const II_NONDC_LEN: usize = (64 + 32 + 16 + 8 + 4) * (64 + 32 + 16 + 8 + 4) * 3;
pub struct Masks {
wedge_offsets: [u8; WEDGE_OFFSETS_LEN],
ii_nondc_offsets: [u16; II_NONDC_OFFSETS_LEN],
wedge_444: Vec<u8>,
wedge_422: Vec<u8>,
wedge_420: Vec<u8>,
wedge_tmvp: Vec<u8>,
ii_dc: Vec<u8>,
ii_nondc: Vec<u8>,
}
impl Masks {
pub fn ii_mask(&self, bs: usize, bw4: usize, bh4: usize, ii_mode: InterIntraPredMode) -> &[u8] {
if ii_mode == InterIntraPredMode::DcPred {
&self.ii_dc
} else {
let off = self.ii_nondc_offsets[bs - BS_64X64] as usize * 0x60
+ 16 * bw4 * bh4 * (ii_mode as usize - 1);
&self.ii_nondc[off..]
}
}
pub fn wedge_mask(
&self,
bs: usize,
bw4: usize,
bh4: usize,
widx: usize,
ss_idx: usize,
) -> &[u8] {
let off =
(self.wedge_offsets[bs - BS_64X64] as usize * 0x1100 + 16 * bw4 * bh4 * widx) >> ss_idx;
self.wedge_buf(ss_idx, off)
}
pub fn wedge_tmvp(&self, bs: usize, bw4: usize, bh4: usize, widx: usize) -> &[u8] {
let off = self.wedge_offsets[bs - BS_64X64] as usize * 68 + (bw4 * bh4 / 4) * widx;
&self.wedge_tmvp[off..]
}
fn wedge_buf(&self, ss_idx: usize, off: usize) -> &[u8] {
match ss_idx {
0 => &self.wedge_444[off..],
1 => &self.wedge_422[off..],
2 => &self.wedge_420[off..],
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
enum WedgeDirection {
Wedge0 = 0,
Wedge14 = 1,
Wedge27 = 2,
Wedge45 = 3,
Wedge63 = 4,
Wedge90 = 5,
Wedge117 = 6,
Wedge135 = 7,
Wedge153 = 8,
Wedge166 = 9,
Wedge180 = 10,
Wedge194 = 11,
Wedge207 = 12,
Wedge225 = 13,
Wedge243 = 14,
Wedge270 = 15,
Wedge297 = 16,
Wedge315 = 17,
Wedge333 = 18,
Wedge346 = 19,
}
const N_WEDGE_DIRECTIONS: usize = 20;
struct WedgeCodeType {
direction: WedgeDirection,
x_offset: u8,
y_offset: u8,
}
use WedgeDirection::*;
static WEDGE_CODEBOOK_16: [WedgeCodeType; 68] = [
WedgeCodeType {
direction: Wedge0,
x_offset: 5,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge0,
x_offset: 6,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge0,
x_offset: 7,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge14,
x_offset: 4,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge14,
x_offset: 5,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge14,
x_offset: 6,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge14,
x_offset: 7,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge27,
x_offset: 4,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge27,
x_offset: 5,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge27,
x_offset: 6,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge27,
x_offset: 7,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge45,
x_offset: 4,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge45,
x_offset: 5,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge45,
x_offset: 6,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge45,
x_offset: 7,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge63,
x_offset: 4,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge63,
x_offset: 4,
y_offset: 3,
},
WedgeCodeType {
direction: Wedge63,
x_offset: 4,
y_offset: 2,
},
WedgeCodeType {
direction: Wedge63,
x_offset: 4,
y_offset: 1,
},
WedgeCodeType {
direction: Wedge90,
x_offset: 4,
y_offset: 3,
},
WedgeCodeType {
direction: Wedge90,
x_offset: 4,
y_offset: 2,
},
WedgeCodeType {
direction: Wedge90,
x_offset: 4,
y_offset: 1,
},
WedgeCodeType {
direction: Wedge117,
x_offset: 4,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge117,
x_offset: 4,
y_offset: 3,
},
WedgeCodeType {
direction: Wedge117,
x_offset: 4,
y_offset: 2,
},
WedgeCodeType {
direction: Wedge117,
x_offset: 4,
y_offset: 1,
},
WedgeCodeType {
direction: Wedge135,
x_offset: 4,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge135,
x_offset: 3,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge135,
x_offset: 2,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge135,
x_offset: 1,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge153,
x_offset: 4,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge153,
x_offset: 3,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge153,
x_offset: 2,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge153,
x_offset: 1,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge166,
x_offset: 4,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge166,
x_offset: 3,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge166,
x_offset: 2,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge166,
x_offset: 1,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge180,
x_offset: 3,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge180,
x_offset: 2,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge180,
x_offset: 1,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge194,
x_offset: 3,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge194,
x_offset: 2,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge194,
x_offset: 1,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge207,
x_offset: 3,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge207,
x_offset: 2,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge207,
x_offset: 1,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge225,
x_offset: 3,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge225,
x_offset: 2,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge225,
x_offset: 1,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge243,
x_offset: 4,
y_offset: 5,
},
WedgeCodeType {
direction: Wedge243,
x_offset: 4,
y_offset: 6,
},
WedgeCodeType {
direction: Wedge243,
x_offset: 4,
y_offset: 7,
},
WedgeCodeType {
direction: Wedge270,
x_offset: 4,
y_offset: 5,
},
WedgeCodeType {
direction: Wedge270,
x_offset: 4,
y_offset: 6,
},
WedgeCodeType {
direction: Wedge270,
x_offset: 4,
y_offset: 7,
},
WedgeCodeType {
direction: Wedge297,
x_offset: 4,
y_offset: 5,
},
WedgeCodeType {
direction: Wedge297,
x_offset: 4,
y_offset: 6,
},
WedgeCodeType {
direction: Wedge297,
x_offset: 4,
y_offset: 7,
},
WedgeCodeType {
direction: Wedge315,
x_offset: 5,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge315,
x_offset: 6,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge315,
x_offset: 7,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge333,
x_offset: 5,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge333,
x_offset: 6,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge333,
x_offset: 7,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge346,
x_offset: 5,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge346,
x_offset: 6,
y_offset: 4,
},
WedgeCodeType {
direction: Wedge346,
x_offset: 7,
y_offset: 4,
},
];
fn copy2d(dst: &mut [u8], src: &[u8], w8: usize, h8: usize, x_off: usize, y_off: usize) {
let src_start = (64 - y_off * h8) * 128 + (64 - x_off * w8);
let row_len = w8 * 8;
for y in 0..h8 * 8 {
let s = src_start + y * 128;
let d = y * row_len;
dst[d..d + row_len].copy_from_slice(&src[s..s + row_len]);
}
}
fn subsample_420(dst: &mut [u8], src: &[u8], w8: usize, h8: usize) {
let stride = w8 * 8;
for y in 0..h8 * 4 {
for x in 0..w8 * 4 {
dst[y * w8 * 4 + x] = ((src[y * 2 * stride + x * 2] as u16
+ src[y * 2 * stride + x * 2 + 1] as u16
+ src[(y * 2 + 1) * stride + x * 2] as u16
+ src[(y * 2 + 1) * stride + x * 2 + 1] as u16
+ 2)
>> 2) as u8;
}
}
}
fn subsample_422(dst: &mut [u8], src: &[u8], w8: usize, h8: usize) {
let stride = w8 * 8;
for y in 0..h8 * 8 {
for x in 0..w8 * 4 {
dst[y * w8 * 4 + x] =
((src[y * stride + x * 2] as u16 + src[y * stride + x * 2 + 1] as u16 + 1) >> 1)
as u8;
}
}
}
fn fill_tmvp(dst: &mut [u8], src: &[u8], w8: usize, h8: usize) {
let stride = w8 * 8;
for y in 0..h8 {
for x in 0..w8 {
let mut score = [0i32; 2];
for yy in y * 8..y * 8 + 8 {
for xx in x * 8..x * 8 + 8 {
score[0] += (src[yy * stride + xx] < 4) as i32;
score[1] += (src[yy * stride + xx] > 60) as i32;
}
}
dst[y * w8 + x] = if score[0] >= 60 {
0
} else if score[1] >= 60 {
1
} else {
2
};
}
}
}
fn gen_master(master: &mut [u8; 128 * 128], mul: i32, wd: WedgeDirection) {
static COS_LUT: [i8; N_WEDGE_DIRECTIONS] = [
4, 4, 4, 2, 2, 0, -2, -2, -4, -4, -4, -4, -4, -2, -2, 0, 2, 2, 4, 4,
];
static SIN_LUT: [i8; N_WEDGE_DIRECTIONS] = [
0, -1, -2, -2, -4, -4, -4, -2, -2, -1, 0, 1, 2, 2, 4, 4, 4, 2, 2, 1,
];
static WEIGHT: [i8; 29] = [
8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
];
let idx = wd as usize;
let s = SIN_LUT[idx] as i32 * mul;
let c = COS_LUT[idx] as i32 * mul;
for y in 0..128 {
let dy = (2 * y as i32 - 127) * s;
for x in 0..128 {
let d = iclip((2 * x as i32 - 127) * c + dy, -28, 28);
master[y * 128 + x] = (4 * if d >= 0 {
16 - WEIGHT[d as usize] as i32
} else {
WEIGHT[(-d) as usize] as i32
}) as u8;
}
}
}
fn build_nondc_ii_masks(mask_v: &mut [u8], w: usize, h: usize, step: usize) {
static II_WEIGHTS_1D: [u8; 64] = [
60, 56, 52, 48, 45, 42, 39, 37, 34, 32, 30, 28, 26, 24, 22, 21, 19, 18, 17, 16, 15, 14, 13,
12, 11, 10, 10, 9, 8, 8, 7, 7, 6, 6, 6, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
];
let area = w * h;
for y in 0..h {
let off = y * w;
let wt = II_WEIGHTS_1D[y * step];
for x in 0..w {
mask_v[off + x] = wt;
mask_v[area + off + x] = II_WEIGHTS_1D[x * step];
mask_v[2 * area + off + x] = II_WEIGHTS_1D[imin(x as i32, y as i32) as usize * step];
}
}
}
const fn bs(b: BlockSize) -> usize {
b as i8 as u8 as usize
}
fn init_wedge_masks(masks: &mut Masks) {
let mut o: usize = 0;
for bsi in BS_64X64..N_BS_SIZES {
let b_dim = &BLOCK_DIMENSIONS[bsi];
if b_dim[0] == 1 || b_dim[1] == 1 {
continue;
}
masks.wedge_offsets[bsi - BS_64X64] = o as u8;
o += (b_dim[0] as usize * b_dim[1] as usize) >> 2;
}
debug_assert_eq!(o * 0x1100, WEDGE_444_LEN);
debug_assert!(o < 256);
let mut master = [0u8; 128 * 128];
let mut wd = N_WEDGE_DIRECTIONS;
macro_rules! fill {
($masks:expr, $master:expr, $cb:expr, $w8:expr, $h8:expr, $bs_variant:expr, $widx:expr) => {{
let bsi = bs($bs_variant);
let bw4 = $w8 * 2;
let bh4 = $h8 * 2;
let w8: usize = $w8;
let h8: usize = $h8;
let widx: usize = $widx;
{
let off_444 = ($masks.wedge_offsets[bsi - BS_64X64] as usize * 0x1100
+ 16 * bw4 * bh4 * widx)
>> 0;
let dst = &mut $masks.wedge_444[off_444..];
copy2d(
dst,
$master,
w8,
h8,
$cb.x_offset as usize,
$cb.y_offset as usize,
);
}
let wm_len = w8 * 8 * h8 * 8;
let off_444 = ($masks.wedge_offsets[bsi - BS_64X64] as usize * 0x1100
+ 16 * bw4 * bh4 * widx)
>> 0;
let mut wm_tmp = vec![0u8; wm_len];
wm_tmp.copy_from_slice(&$masks.wedge_444[off_444..off_444 + wm_len]);
{
let off_422 = ($masks.wedge_offsets[bsi - BS_64X64] as usize * 0x1100
+ 16 * bw4 * bh4 * widx)
>> 1;
let dst = &mut $masks.wedge_422[off_422..];
subsample_422(dst, &wm_tmp, w8, h8);
}
{
let off_420 = ($masks.wedge_offsets[bsi - BS_64X64] as usize * 0x1100
+ 16 * bw4 * bh4 * widx)
>> 2;
let dst = &mut $masks.wedge_420[off_420..];
subsample_420(dst, &wm_tmp, w8, h8);
}
{
let off_tmvp =
$masks.wedge_offsets[bsi - BS_64X64] as usize * 68 + (bw4 * bh4 / 4) * widx;
let dst = &mut $masks.wedge_tmvp[off_tmvp..];
fill_tmvp(dst, &wm_tmp, w8, h8);
}
}};
}
for widx in 0..68 {
let cb = &WEDGE_CODEBOOK_16[widx];
if cb.direction as usize != wd {
gen_master(&mut master, 2, cb.direction);
wd = cb.direction as usize;
}
fill!(masks, &master, cb, 1, 1, BlockSize::Bs8x8, widx);
fill!(masks, &master, cb, 1, 2, BlockSize::Bs8x16, widx);
fill!(masks, &master, cb, 2, 1, BlockSize::Bs16x8, widx);
fill!(masks, &master, cb, 2, 2, BlockSize::Bs16x16, widx);
}
for widx in 0..68 {
let cb = &WEDGE_CODEBOOK_16[widx];
if cb.direction as usize != wd {
gen_master(&mut master, 1, cb.direction);
wd = cb.direction as usize;
}
fill!(masks, &master, cb, 1, 4, BlockSize::Bs8x32, widx);
fill!(masks, &master, cb, 1, 8, BlockSize::Bs8x64, widx);
fill!(masks, &master, cb, 2, 4, BlockSize::Bs16x32, widx);
fill!(masks, &master, cb, 2, 8, BlockSize::Bs16x64, widx);
fill!(masks, &master, cb, 4, 1, BlockSize::Bs32x8, widx);
fill!(masks, &master, cb, 4, 2, BlockSize::Bs32x16, widx);
fill!(masks, &master, cb, 4, 4, BlockSize::Bs32x32, widx);
fill!(masks, &master, cb, 4, 8, BlockSize::Bs32x64, widx);
fill!(masks, &master, cb, 8, 1, BlockSize::Bs64x8, widx);
fill!(masks, &master, cb, 8, 2, BlockSize::Bs64x16, widx);
fill!(masks, &master, cb, 8, 4, BlockSize::Bs64x32, widx);
fill!(masks, &master, cb, 8, 8, BlockSize::Bs64x64, widx);
}
}
fn init_ii_masks(masks: &mut Masks) {
for v in masks.ii_dc.iter_mut() {
*v = 32;
}
let mut o: usize = 0;
for bsi in BS_64X64..N_BS_SIZES {
let b_dim = &BLOCK_DIMENSIONS[bsi];
masks.ii_nondc_offsets[bsi - BS_64X64] = o as u16;
o += (b_dim[0] as usize * b_dim[1] as usize) >> 1;
}
debug_assert_eq!(o * 0x60 + 0x30, II_NONDC_LEN);
debug_assert!(o < u16::MAX as usize);
macro_rules! fill_ii {
($masks:expr, $w:expr, $h:expr, $step:expr) => {{
let bsi = bs(block_size_for_dims($w, $h));
let base = $masks.ii_nondc_offsets[bsi - BS_64X64] as usize * 0x60;
let dst = &mut $masks.ii_nondc[base..];
build_nondc_ii_masks(dst, $w, $h, $step);
}};
}
fill_ii!(masks, 4, 4, 16);
fill_ii!(masks, 4, 8, 8);
fill_ii!(masks, 4, 16, 4);
fill_ii!(masks, 4, 32, 2);
fill_ii!(masks, 4, 64, 1);
fill_ii!(masks, 8, 4, 8);
fill_ii!(masks, 8, 8, 8);
fill_ii!(masks, 8, 16, 4);
fill_ii!(masks, 8, 32, 2);
fill_ii!(masks, 8, 64, 1);
fill_ii!(masks, 16, 4, 4);
fill_ii!(masks, 16, 8, 4);
fill_ii!(masks, 16, 16, 4);
fill_ii!(masks, 16, 32, 2);
fill_ii!(masks, 16, 64, 1);
fill_ii!(masks, 32, 4, 2);
fill_ii!(masks, 32, 8, 2);
fill_ii!(masks, 32, 16, 2);
fill_ii!(masks, 32, 32, 2);
fill_ii!(masks, 32, 64, 1);
fill_ii!(masks, 64, 4, 1);
fill_ii!(masks, 64, 8, 1);
fill_ii!(masks, 64, 16, 1);
fill_ii!(masks, 64, 32, 1);
fill_ii!(masks, 64, 64, 1);
}
const fn block_size_for_dims(w: usize, h: usize) -> BlockSize {
match (w, h) {
(4, 4) => BlockSize::Bs4x4,
(4, 8) => BlockSize::Bs4x8,
(4, 16) => BlockSize::Bs4x16,
(4, 32) => BlockSize::Bs4x32,
(4, 64) => BlockSize::Bs4x64,
(8, 4) => BlockSize::Bs8x4,
(8, 8) => BlockSize::Bs8x8,
(8, 16) => BlockSize::Bs8x16,
(8, 32) => BlockSize::Bs8x32,
(8, 64) => BlockSize::Bs8x64,
(16, 4) => BlockSize::Bs16x4,
(16, 8) => BlockSize::Bs16x8,
(16, 16) => BlockSize::Bs16x16,
(16, 32) => BlockSize::Bs16x32,
(16, 64) => BlockSize::Bs16x64,
(32, 4) => BlockSize::Bs32x4,
(32, 8) => BlockSize::Bs32x8,
(32, 16) => BlockSize::Bs32x16,
(32, 32) => BlockSize::Bs32x32,
(32, 64) => BlockSize::Bs32x64,
(64, 4) => BlockSize::Bs64x4,
(64, 8) => BlockSize::Bs64x8,
(64, 16) => BlockSize::Bs64x16,
(64, 32) => BlockSize::Bs64x32,
(64, 64) => BlockSize::Bs64x64,
_ => panic!(),
}
}
pub fn init_masks() -> Box<Masks> {
let mut masks = Box::new(Masks {
wedge_offsets: [0u8; WEDGE_OFFSETS_LEN],
ii_nondc_offsets: [0u16; II_NONDC_OFFSETS_LEN],
wedge_444: vec![0u8; WEDGE_444_LEN],
wedge_422: vec![0u8; WEDGE_422_LEN],
wedge_420: vec![0u8; WEDGE_420_LEN],
wedge_tmvp: vec![0u8; WEDGE_TMVP_LEN],
ii_dc: vec![0u8; II_DC_LEN],
ii_nondc: vec![0u8; II_NONDC_LEN],
});
init_wedge_masks(&mut masks);
init_ii_masks(&mut masks);
masks
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_array_size_assertions() {
let mut wedge_o: usize = 0;
for bsi in BS_64X64..N_BS_SIZES {
let b_dim = &BLOCK_DIMENSIONS[bsi];
if b_dim[0] == 1 || b_dim[1] == 1 {
continue;
}
wedge_o += (b_dim[0] as usize * b_dim[1] as usize) >> 2;
}
assert_eq!(wedge_o * 0x1100, WEDGE_444_LEN);
assert!(wedge_o < 256);
let mut ii_o: usize = 0;
for bsi in BS_64X64..N_BS_SIZES {
let b_dim = &BLOCK_DIMENSIONS[bsi];
ii_o += (b_dim[0] as usize * b_dim[1] as usize) >> 1;
}
assert_eq!(ii_o * 0x60 + 0x30, II_NONDC_LEN);
assert!(ii_o < u16::MAX as usize);
}
#[test]
fn test_init_masks_does_not_panic() {
let _masks = init_masks();
}
#[test]
fn test_gen_master_center() {
let mut master = [0u8; 128 * 128];
gen_master(&mut master, 2, WedgeDirection::Wedge90);
let val = master[64 * 128 + 0];
assert_eq!(val, 16);
let val = master[0 * 128 + 0];
assert_eq!(val, 64);
}
#[test]
fn test_ii_dc_mask_all_32() {
let masks = init_masks();
assert!(masks.ii_dc.iter().all(|&v| v == 32));
}
#[test]
fn test_wedge_offsets_monotonic() {
let masks = init_masks();
let mut prev = 0u8;
for &off in &masks.wedge_offsets {
if off > 0 {
assert!(off >= prev, "wedge offsets should be non-decreasing");
prev = off;
}
}
}
#[test]
fn test_wedge_mask_nonzero() {
let masks = init_masks();
let bsi = bs(BlockSize::Bs8x8);
let wm = masks.wedge_mask(bsi, 2, 2, 0, 0);
let sum: u32 = wm[..64].iter().map(|&v| v as u32).sum();
assert!(sum > 0, "8x8 wedge mask should contain non-zero values");
}
#[test]
fn test_ii_nondc_mask_range() {
let masks = init_masks();
for &v in &masks.ii_nondc {
assert!(v <= 64, "ii_nondc values should be <= 64, got {}", v);
}
}
#[test]
fn test_wedge_444_value_range() {
let masks = init_masks();
for &v in &masks.wedge_444 {
assert!(v <= 64, "wedge_444 values should be <= 64, got {}", v);
}
}
#[test]
fn test_wedge_tmvp_values() {
let masks = init_masks();
for &v in &masks.wedge_tmvp {
assert!(v <= 2, "wedge_tmvp values should be 0, 1, or 2, got {}", v);
}
}
#[test]
fn test_subsample_422_basic() {
let mut src = [0u8; 64];
for i in 0..64 {
src[i] = (i * 2) as u8;
}
let mut dst = [0u8; 32];
subsample_422(&mut dst, &src, 1, 1);
assert_eq!(dst[0], 1);
}
#[test]
fn test_subsample_420_basic() {
let src = [32u8; 64];
let mut dst = [0u8; 16];
subsample_420(&mut dst, &src, 1, 1);
assert!(dst.iter().all(|&v| v == 32));
}
#[test]
fn test_block_size_for_dims() {
assert_eq!(block_size_for_dims(8, 8), BlockSize::Bs8x8);
assert_eq!(block_size_for_dims(64, 64), BlockSize::Bs64x64);
assert_eq!(block_size_for_dims(16, 32), BlockSize::Bs16x32);
assert_eq!(block_size_for_dims(4, 4), BlockSize::Bs4x4);
}
}