use crate::cdf;
use crate::quant::{ac_q, dc_q, dequant, quantize};
use crate::transform::{TxSize, TxType, forward_transform_2d, inverse_transform_2d};
use gamut_bitstream::SymbolEncoder;
use gamut_color::{Planar8, clip_pixel};
use gamut_dsp::round2_signed;
const NUM_BASE_LEVELS: i32 = 2;
const COEFF_BASE_PLUS_RANGE: i32 = 14;
const DC_PRED: u8 = 0;
const V_PRED: u8 = 1;
const H_PRED: u8 = 2;
const D45_PRED: u8 = 3;
const D203_PRED: u8 = 7;
const D67_PRED: u8 = 8;
const D135_PRED: u8 = 4;
const D113_PRED: u8 = 5;
const D157_PRED: u8 = 6;
const SMOOTH_PRED: u8 = 9;
const SMOOTH_V_PRED: u8 = 10;
const SMOOTH_H_PRED: u8 = 11;
const PAETH_PRED: u8 = 12;
const UV_CFL_PRED: u8 = 13;
const LUMA_MODES: [u8; 13] = [
DC_PRED,
V_PRED,
H_PRED,
D45_PRED,
D135_PRED,
D113_PRED,
D157_PRED,
D203_PRED,
D67_PRED,
SMOOTH_PRED,
SMOOTH_V_PRED,
SMOOTH_H_PRED,
PAETH_PRED,
];
const DIRECTIONAL_MODES: [u8; 8] = [
V_PRED, H_PRED, D45_PRED, D135_PRED, D113_PRED, D157_PRED, D203_PRED, D67_PRED,
];
const FILTER_INTRA_MODE_TO_INTRA_DIR: [u8; 5] = [DC_PRED, V_PRED, H_PRED, D157_PRED, DC_PRED];
const fn is_directional(mode: u8) -> bool {
V_PRED <= mode && mode <= D67_PRED
}
struct PaletteBlock {
colors: Vec<u8>,
index_map: Vec<u8>,
}
fn palette_color_cdf(n: usize, ctx: usize) -> &'static [u16] {
match n {
2 => &cdf::PALETTE_SIZE_2_Y_COLOR[ctx],
3 => &cdf::PALETTE_SIZE_3_Y_COLOR[ctx],
4 => &cdf::PALETTE_SIZE_4_Y_COLOR[ctx],
5 => &cdf::PALETTE_SIZE_5_Y_COLOR[ctx],
6 => &cdf::PALETTE_SIZE_6_Y_COLOR[ctx],
7 => &cdf::PALETTE_SIZE_7_Y_COLOR[ctx],
_ => &cdf::PALETTE_SIZE_8_Y_COLOR[ctx],
}
}
fn ceil_log2(x: usize) -> u32 {
if x < 2 { 0 } else { (x - 1).ilog2() + 1 }
}
fn palette_color_context(
color_map: &[u8],
bw: usize,
r: usize,
c: usize,
n: usize,
) -> ([usize; 8], usize) {
let mut scores = [0i32; 8];
let mut order = [0usize; 8];
for (i, o) in order.iter_mut().enumerate() {
*o = i;
}
if c > 0 {
scores[color_map[r * bw + (c - 1)] as usize] += 2;
}
if r > 0 && c > 0 {
scores[color_map[(r - 1) * bw + (c - 1)] as usize] += 1;
}
if r > 0 {
scores[color_map[(r - 1) * bw + c] as usize] += 2;
}
for i in 0..3 {
let mut max_idx = i;
for j in (i + 1)..n {
if scores[j] > scores[max_idx] {
max_idx = j;
}
}
if max_idx != i {
let (ms, mo) = (scores[max_idx], order[max_idx]);
let mut k = max_idx;
while k > i {
scores[k] = scores[k - 1];
order[k] = order[k - 1];
k -= 1;
}
scores[i] = ms;
order[i] = mo;
}
}
let hash = (scores[0] + scores[1] * 2 + scores[2] * 2) as usize;
(order, cdf::PALETTE_COLOR_CONTEXT[hash] as usize)
}
pub(crate) const SEG_ALT_Q: [Option<i32>; 8] =
[None, Some(20), Some(50), None, None, None, None, None];
const LAST_ACTIVE_SEG: i32 = 2;
fn neg_interleave(x: i32, r: i32, max: i32) -> i32 {
if r == 0 {
return x;
}
if r >= max - 1 {
return max - x - 1;
}
let bounded = if 2 * r < max {
(x - r).abs() <= r
} else {
(x - r).abs() < max - r
};
if bounded {
if x <= r { (r - x) * 2 } else { (x - r) * 2 - 1 }
} else if 2 * r < max {
x
} else {
max - (x + 1)
}
}
const fn qctx_for(qindex: i32) -> usize {
if qindex <= 20 {
0
} else if qindex <= 60 {
1
} else if qindex <= 120 {
2
} else {
3
}
}
const fn square_tx(width: usize) -> TxSize {
match width {
4 => TxSize::Tx4x4,
8 => TxSize::Tx8x8,
16 => TxSize::Tx16x16,
32 => TxSize::Tx32x32,
_ => TxSize::Tx64x64,
}
}
const fn block_exceeds_frame(
r: usize,
c: usize,
num4x4: usize,
mi_rows: usize,
mi_cols: usize,
) -> bool {
r + num4x4 > mi_rows || c + num4x4 > mi_cols
}
const fn rect_tx(w: usize, h: usize) -> TxSize {
match (w, h) {
(16, 8) => TxSize::Tx16x8,
(8, 16) => TxSize::Tx8x16,
(32, 16) => TxSize::Tx32x16,
(16, 32) => TxSize::Tx16x32,
_ => square_tx(w),
}
}
#[derive(Clone, Copy)]
struct Pred {
mode: u8,
angle_delta: i8,
filter_intra: Option<u8>,
cfl_alpha: Option<i32>,
}
pub(crate) struct Reconstruction {
pub planes: [Vec<u16>; 3],
pub coded_w: usize,
pub bit_depth: u32,
pub deblocked_luma: Vec<u16>,
}
pub(crate) struct FrameEncoder<'a> {
planes: [&'a [u8]; 3],
width: usize,
height: usize,
mi_cols: usize,
mi_rows: usize,
coded_w: usize,
coded_h: usize,
tile_c0: usize,
tile_x0: usize,
tile_c1: usize,
tile_r1: usize,
qindex: u8,
qctx: usize,
dc_quant: i32,
ac_quant: i32,
current_qindex: i32,
current_dlf: i32,
delta_q_present: bool,
read_deltas: bool,
bit_depth: u32,
recon: [Vec<u16>; 3],
sym: SymbolEncoder,
above_level: [Vec<u8>; 3],
above_dc: [Vec<u8>; 3],
left_level: [Vec<u8>; 3],
left_dc: [Vec<u8>; 3],
mi_bsl: Vec<u8>,
mi_bsl_h: Vec<u8>,
above_partition: Vec<u8>,
left_partition: Vec<u8>,
mi_ymode: Vec<u8>,
mi_skip: Vec<u8>,
mi_segid: Vec<u8>,
mi_psize: Vec<u8>,
mi_pcolors: Vec<[u8; 8]>,
mi_dlf: Vec<i8>,
tx_log2: Vec<u8>,
tx_log2_h: Vec<u8>,
sb_r: usize,
sb_c: usize,
block_decoded: Vec<u8>,
}
const BD_STRIDE: usize = 18;
const AL_PART_CTX: [[[u8; 4]; 5]; 2] = [
[
[0x00, 0x00, 0x10, 0x00],
[0x10, 0x10, 0x18, 0x00],
[0x18, 0x18, 0x1c, 0x00],
[0x1c, 0x1c, 0x1e, 0x00],
[0x1e, 0x1e, 0x1f, 0x1f],
],
[
[0x00, 0x10, 0x00, 0x00],
[0x10, 0x18, 0x10, 0x00],
[0x18, 0x1c, 0x18, 0x00],
[0x1c, 0x1e, 0x1c, 0x00],
[0x1e, 0x1f, 0x1e, 0x1f],
],
];
fn sm_weights(size: usize) -> &'static [i32] {
match size {
8 => &cdf::SM_WEIGHTS_8X8,
16 => &cdf::SM_WEIGHTS_16X16,
32 => &cdf::SM_WEIGHTS_32X32,
_ => &cdf::SM_WEIGHTS_64X64,
}
}
impl<'a> FrameEncoder<'a> {
pub(crate) fn new(planes: &'a Planar8, qindex: u8) -> Self {
let width = planes.width() as usize;
let height = planes.height() as usize;
let mi_cols = 2 * ((width + 7) >> 3);
let mi_rows = 2 * ((height + 7) >> 3);
let coded_w = mi_cols * 4;
let coded_h = mi_rows * 4;
let bit_depth = 8u32;
let recon = if qindex > 0 {
[
vec![0u16; coded_w * coded_h],
vec![0u16; coded_w * coded_h],
vec![0u16; coded_w * coded_h],
]
} else {
[Vec::new(), Vec::new(), Vec::new()]
};
Self {
planes: [planes.plane(0), planes.plane(1), planes.plane(2)],
width,
height,
mi_cols,
mi_rows,
coded_w,
coded_h,
tile_c0: 0,
tile_x0: 0,
tile_c1: mi_cols,
tile_r1: mi_rows,
qindex,
qctx: qctx_for(i32::from(qindex)),
dc_quant: dc_q(bit_depth, i32::from(qindex)),
ac_quant: ac_q(bit_depth, i32::from(qindex)),
current_qindex: i32::from(qindex),
current_dlf: 0,
delta_q_present: qindex > 0,
read_deltas: false,
bit_depth,
recon,
sym: SymbolEncoder::new(),
above_level: [vec![0; mi_cols], vec![0; mi_cols], vec![0; mi_cols]],
above_dc: [vec![0; mi_cols], vec![0; mi_cols], vec![0; mi_cols]],
left_level: [vec![0; mi_rows], vec![0; mi_rows], vec![0; mi_rows]],
left_dc: [vec![0; mi_rows], vec![0; mi_rows], vec![0; mi_rows]],
mi_bsl: vec![0; mi_cols * mi_rows],
mi_bsl_h: vec![0; mi_cols * mi_rows],
above_partition: vec![0; mi_cols],
left_partition: vec![0; mi_rows],
mi_ymode: vec![0; mi_cols * mi_rows],
mi_skip: vec![0; mi_cols * mi_rows],
mi_segid: vec![0; mi_cols * mi_rows],
mi_psize: vec![0; mi_cols * mi_rows],
mi_pcolors: vec![[0u8; 8]; mi_cols * mi_rows],
mi_dlf: vec![0; mi_cols * mi_rows],
tx_log2: vec![2; mi_cols * mi_rows],
tx_log2_h: vec![2; mi_cols * mi_rows],
sb_r: 0,
sb_c: 0,
block_decoded: vec![0; BD_STRIDE * BD_STRIDE],
}
}
fn clear_block_decoded(&mut self, r: usize, c: usize) {
const SB4: isize = 16;
let sb_width4 = (self.tile_c1 - c) as isize;
let sb_height4 = (self.tile_r1 - r) as isize;
for y in -1..=SB4 {
for x in -1..=SB4 {
let avail = (y < 0 && x < sb_width4) || (x < 0 && y < sb_height4);
self.block_decoded[(y + 1) as usize * BD_STRIDE + (x + 1) as usize] = avail.into();
}
}
self.block_decoded[(SB4 + 1) as usize * BD_STRIDE] = 0; }
fn block_decoded_at(&self, by: isize, bx: isize) -> bool {
self.block_decoded[(by + 1) as usize * BD_STRIDE + (bx + 1) as usize] != 0
}
fn write_lr(&mut self, r: usize, c: usize) {
const UNIT: usize = 256;
const TAPS: [(i32, i32, u32); 3] = [(5, 16, 1), (23, 32, 2), (17, 64, 3)];
let unit_cols = ((self.width + UNIT / 2) / UNIT).max(1);
let unit_rows = ((self.height + UNIT / 2) / UNIT).max(1);
let row_start = (r * 4).div_ceil(UNIT);
let row_end = unit_rows.min(((r + 16) * 4).div_ceil(UNIT));
let col_start = (c * 4).div_ceil(UNIT);
let col_end = unit_cols.min(((c + 16) * 4).div_ceil(UNIT));
for _ in row_start..row_end {
for _ in col_start..col_end {
self.sym.encode_symbol(1, &cdf::RESTORE_WIENER);
for _pass in 0..2 {
for (j, &(off, num_syms, k)) in TAPS.iter().enumerate() {
let v = crate::filter::WIENER_DEFAULT[j] + off;
encode_subexp_with_ref(&mut self.sym, v, num_syms, k, v);
}
}
}
}
}
pub(crate) fn encode(mut self) -> (Vec<Vec<u8>>, Reconstruction) {
const SB4: usize = 16; let sb_cols = self.mi_cols.div_ceil(SB4);
let tile_cols = if sb_cols >= 2 { 2 } else { 1 };
let size_sb = sb_cols.div_ceil(tile_cols);
let mut tile_bytes: Vec<Vec<u8>> = Vec::with_capacity(tile_cols);
for t in 0..tile_cols {
let c_start = (t * size_sb * SB4).min(self.mi_cols);
let c_end = ((t + 1) * size_sb * SB4).min(self.mi_cols);
self.tile_c0 = c_start;
self.tile_x0 = c_start * 4;
self.tile_c1 = c_end;
self.set_quant(i32::from(self.qindex));
self.current_dlf = 0;
let mut r = 0;
while r < self.mi_rows {
for plane in 0..3 {
self.left_level[plane].iter_mut().for_each(|v| *v = 0);
self.left_dc[plane].iter_mut().for_each(|v| *v = 0);
}
let mut c = c_start;
while c < c_end {
self.sb_r = r;
self.sb_c = c;
self.read_deltas = self.delta_q_present;
self.clear_block_decoded(r, c);
if self.qindex > 0 {
self.write_lr(r, c);
}
self.encode_partition(r, c, 64);
c += SB4;
}
r += SB4;
}
let sym = std::mem::replace(&mut self.sym, SymbolEncoder::new());
tile_bytes.push(sym.finish());
}
let mut planes = self.recon;
let mut deblocked_luma = Vec::new();
if self.qindex > 0 {
crate::filter::deblock(
&mut planes,
self.coded_w,
self.mi_cols,
self.width,
self.height,
&self.tx_log2,
&self.tx_log2_h,
&self.mi_bsl,
&self.mi_bsl_h,
&self.mi_dlf,
self.qindex,
);
deblocked_luma = planes[0].clone();
planes = crate::filter::cdef(
&planes,
self.coded_w,
&self.mi_skip,
self.mi_cols,
self.qindex,
);
}
let recon = Reconstruction {
planes,
coded_w: self.coded_w,
bit_depth: self.bit_depth,
deblocked_luma,
};
(tile_bytes, recon)
}
fn sample(&self, plane: usize, x: usize, y: usize) -> i32 {
let xx = x.min(self.width - 1);
let yy = y.min(self.height - 1);
i32::from(self.planes[plane][yy * self.width + xx])
}
fn encode_partition(&mut self, r: usize, c: usize, bw: usize) {
if r >= self.mi_rows || c >= self.mi_cols {
return;
}
let num4x4 = bw / 4;
let half = num4x4 >> 1;
let has_rows = r + half < self.mi_rows;
let has_cols = c + half < self.mi_cols;
let bsl = num4x4.trailing_zeros() as usize;
let bp = if bw < 8 {
usize::MAX
} else if has_rows && has_cols {
let ctx = self.partition_ctx(r, c, bsl);
if self.qindex == 0 {
self.sym.encode_symbol(0, partition_cdf(bsl, ctx)); 0
} else if block_exceeds_frame(r, c, num4x4, self.mi_rows, self.mi_cols) {
self.sym.encode_symbol(3, partition_cdf(bsl, ctx)); 3
} else if let Some(d) = self.decide_rect(r, c, bw) {
self.sym.encode_symbol(d, partition_cdf(bsl, ctx));
d
} else if (8..=64).contains(&bw) && !self.should_split(r, c, bw) {
self.sym.encode_symbol(0, partition_cdf(bsl, ctx)); 0
} else {
self.sym.encode_symbol(3, partition_cdf(bsl, ctx)); 3
}
} else if has_cols {
let ctx = self.partition_ctx(r, c, bsl);
let cdf2 = split_or_horz_cdf(partition_cdf(bsl, ctx));
self.sym.encode_symbol(1, &cdf2); 3
} else if has_rows {
let ctx = self.partition_ctx(r, c, bsl);
let cdf2 = split_or_vert_cdf(partition_cdf(bsl, ctx));
self.sym.encode_symbol(1, &cdf2); 3
} else {
3 };
if bw >= 8 && bp != usize::MAX && (bp != 3 || bsl == 1) {
self.update_partition_ctx(r, c, bsl, bp);
}
match bp {
usize::MAX | 0 => self.encode_block(r, c, bw, bw),
1 => {
self.encode_block(r, c, bw, bw / 2);
if r + half < self.mi_rows {
self.encode_block(r + half, c, bw, bw / 2);
}
}
2 => {
self.encode_block(r, c, bw / 2, bw);
if c + half < self.mi_cols {
self.encode_block(r, c + half, bw / 2, bw);
}
}
_ => {
let h = bw / 2;
self.encode_partition(r, c, h);
self.encode_partition(r, c + half, h);
self.encode_partition(r + half, c, h);
self.encode_partition(r + half, c + half, h);
}
}
}
fn partition_ctx(&self, r: usize, c: usize, bsl: usize) -> usize {
let bit = bsl - 1;
let above = (self.above_partition[c] >> bit) & 1;
let left = if c > self.tile_c0 {
(self.left_partition[r] >> bit) & 1
} else {
0
};
usize::from(left) * 2 + usize::from(above)
}
fn update_partition_ctx(&mut self, r: usize, c: usize, bsl: usize, bp: usize) {
let bl = 5 - bsl; let n4 = 1usize << bsl;
let (a, l) = (AL_PART_CTX[0][bl][bp], AL_PART_CTX[1][bl][bp]);
for k in 0..n4 {
if c + k < self.mi_cols {
self.above_partition[c + k] = a;
}
if r + k < self.mi_rows {
self.left_partition[r + k] = l;
}
}
}
fn should_split(&self, r: usize, c: usize, bw: usize) -> bool {
let n4 = bw / 4;
if bw == 64 && (r + n4 > self.mi_rows || c + n4 > self.mi_cols) {
return true;
}
if self.decide_palette(r, c, bw).is_some() {
return false;
}
self.luma_range(c * 4, r * 4, bw, bw) > 32
}
fn set_quant(&mut self, qindex: i32) {
self.current_qindex = qindex;
self.dc_quant = dc_q(8, qindex);
self.ac_quant = ac_q(8, qindex);
}
fn apply_seg_quant(&mut self, segid: usize) {
let alt = SEG_ALT_Q[segid].unwrap_or(0);
let bq = (self.current_qindex + alt).clamp(0, 255);
self.dc_quant = dc_q(8, bq);
self.ac_quant = ac_q(8, bq);
}
fn signal_segment_id(&mut self, r: usize, c: usize, skip: bool) -> usize {
let cols = self.mi_cols;
let prev_ul = if r > 0 && c > self.tile_c0 {
i32::from(self.mi_segid[(r - 1) * cols + (c - 1)])
} else {
-1
};
let prev_u = if r > 0 {
i32::from(self.mi_segid[(r - 1) * cols + c])
} else {
-1
};
let prev_l = if c > self.tile_c0 {
i32::from(self.mi_segid[r * cols + (c - 1)])
} else {
-1
};
let pred = if prev_u == -1 {
prev_l.max(0)
} else if prev_l == -1 || prev_ul == prev_u {
prev_u
} else {
prev_l
};
if skip {
return pred as usize; }
let assigned = ((r + c) as i32) % (LAST_ACTIVE_SEG + 1);
let ctx = if prev_ul < 0 {
0
} else if prev_ul == prev_u && prev_ul == prev_l {
2
} else if prev_ul == prev_u || prev_ul == prev_l || prev_u == prev_l {
1
} else {
0
};
let diff = neg_interleave(assigned, pred, LAST_ACTIVE_SEG + 1);
self.sym.encode_symbol(diff as usize, &cdf::SEGMENT_ID[ctx]);
assigned as usize
}
fn signal_delta_q(&mut self) {
let sb_idx = self.sb_r / 16 + self.sb_c / 16;
let delta: i32 = match sb_idx % 3 {
0 => 1,
1 => -1,
_ => 2,
};
let abs = delta.unsigned_abs() as usize;
self.sym.encode_symbol(abs, &cdf::DELTA_Q);
self.sym.encode_literal(u32::from(delta < 0), 1); let nq = (self.current_qindex + delta).clamp(1, 255);
self.set_quant(nq);
}
fn signal_delta_lf(&mut self) {
let lf = i32::from(crate::filter::deblock_level(self.qindex));
let delta: i32 = if lf == 0 {
0
} else {
(self.sb_r / 16 + self.sb_c / 16) as i32 % 2
};
let abs = delta.unsigned_abs() as usize;
self.sym.encode_symbol(abs, &cdf::DELTA_LF);
if abs > 0 {
self.sym.encode_literal(u32::from(delta < 0), 1); }
self.current_dlf = (self.current_dlf + delta).clamp(-63, 63);
}
fn skip_ctx(&self, r: usize, c: usize) -> usize {
let above = r > 0 && self.mi_skip[(r - 1) * self.mi_cols + c] != 0;
let left = c > self.tile_c0 && self.mi_skip[r * self.mi_cols + (c - 1)] != 0;
usize::from(above) + usize::from(left)
}
fn block_is_skippable(&self, r: usize, c: usize, bw: usize) -> bool {
let (sx, sy) = (c * 4, r * 4);
for plane in 0..3 {
let v0 = self.sample(plane, sx, sy);
for i in 0..bw {
for j in 0..bw {
if self.sample(plane, sx + j, sy + i) != v0 {
return false;
}
}
}
if self.dc_pred(plane, sx, sy, bw, bw) != v0 {
return false;
}
}
true
}
fn decide_palette(&self, r: usize, c: usize, bw: usize) -> Option<PaletteBlock> {
let (sx, sy) = (c * 4, r * 4);
let mut set = [false; 256];
let mut count = 0;
for i in 0..bw {
for j in 0..bw {
let v = self.sample(0, sx + j, sy + i) as usize;
if !set[v] {
set[v] = true;
count += 1;
}
}
}
if !(2..=8).contains(&count) {
return None;
}
for plane in 1..3 {
let v0 = self.sample(plane, sx, sy);
for i in 0..bw {
for j in 0..bw {
if self.sample(plane, sx + j, sy + i) != v0 {
return None;
}
}
}
if self.dc_pred(plane, sx, sy, bw, bw) != v0 {
return None;
}
}
let colors: Vec<u8> = (0..256).filter(|&v| set[v]).map(|v| v as u8).collect();
let mut index_map = vec![0u8; bw * bw];
for i in 0..bw {
for j in 0..bw {
let v = self.sample(0, sx + j, sy + i) as u8;
index_map[i * bw + j] = colors.binary_search(&v).unwrap_or(0) as u8;
}
}
Some(PaletteBlock { colors, index_map })
}
fn palette_cache(&self, r: usize, c: usize) -> Vec<u8> {
let above_n = if !r.is_multiple_of(16) {
self.mi_psize[(r - 1) * self.mi_cols + c] as usize
} else {
0
};
let left_n = if c > self.tile_c0 {
self.mi_psize[r * self.mi_cols + (c - 1)] as usize
} else {
0
};
let blank = [0u8; 8];
let above = if above_n > 0 {
&self.mi_pcolors[(r - 1) * self.mi_cols + c]
} else {
&blank
};
let left = if left_n > 0 {
&self.mi_pcolors[r * self.mi_cols + (c - 1)]
} else {
&blank
};
let mut cache: Vec<u8> = Vec::new();
let push = |cache: &mut Vec<u8>, v: u8| {
if cache.last() != Some(&v) {
cache.push(v);
}
};
let (mut ai, mut li) = (0, 0);
while ai < above_n && li < left_n {
let (ac, lc) = (above[ai], left[li]);
if lc < ac {
push(&mut cache, lc);
li += 1;
} else {
push(&mut cache, ac);
ai += 1;
if lc == ac {
li += 1;
}
}
}
while ai < above_n {
push(&mut cache, above[ai]);
ai += 1;
}
while li < left_n {
push(&mut cache, left[li]);
li += 1;
}
cache
}
fn encode_ns(&mut self, val: usize, n: usize) {
if n <= 1 {
return;
}
let w = n.ilog2() + 1;
let m = (1usize << w) - n;
if val < m {
self.sym.encode_literal(val as u32, w - 1);
} else {
let coded = val + m;
self.sym.encode_literal((coded >> 1) as u32, w - 1);
self.sym.encode_literal((coded & 1) as u32, 1);
}
}
fn signal_palette_colors(&mut self, colors: &[u8], cache_n: usize) {
let psize = colors.len();
for _ in 0..cache_n {
self.sym.encode_literal(0, 1);
}
self.sym.encode_literal(u32::from(colors[0]), 8);
if psize > 1 {
self.sym.encode_literal(3, 2); let mut palette_bits = 5 + 3u32;
for idx in 1..psize {
let delta = i32::from(colors[idx]) - i32::from(colors[idx - 1]); self.sym.encode_literal((delta - 1) as u32, palette_bits);
let range = 256 - i32::from(colors[idx]) - 1;
palette_bits = palette_bits.min(ceil_log2(range.max(1) as usize));
}
}
}
fn signal_palette_tokens(&mut self, index_map: &[u8], bw: usize, psize: usize) {
self.encode_ns(index_map[0] as usize, psize);
for i in 1..(2 * bw - 1) {
let j_start = i.min(bw - 1);
let j_end = i.saturating_sub(bw - 1);
let mut j = j_start;
loop {
let (rr, cc) = (i - j, j);
let (order, ctx) = palette_color_context(index_map, bw, rr, cc, psize);
let actual = index_map[rr * bw + cc] as usize;
let sym = order.iter().position(|&x| x == actual).unwrap_or(0);
self.sym.encode_symbol(sym, palette_color_cdf(psize, ctx));
if j == j_end {
break;
}
j -= 1;
}
}
}
fn recon_palette(&mut self, r: usize, c: usize, bw: usize, pal: &PaletteBlock) {
let (sx, sy) = (c * 4, r * 4);
for i in 0..bw {
for j in 0..bw {
let idx = pal.index_map[i * bw + j] as usize;
self.recon[0][(sy + i) * self.coded_w + (sx + j)] = u16::from(pal.colors[idx]);
}
}
for plane in 1..3 {
let dc = clip_pixel(self.dc_pred(plane, sx, sy, bw, bw), self.bit_depth);
for i in 0..bw {
for j in 0..bw {
self.recon[plane][(sy + i) * self.coded_w + (sx + j)] = dc;
}
}
}
for plane in 0..3 {
self.set_ctx(plane, sx >> 2, sy >> 2, bw / 4, bw / 4, 0, 0);
}
}
fn luma_range(&self, sx: usize, sy: usize, w: usize, h: usize) -> i32 {
let mut lo = i32::MAX;
let mut hi = i32::MIN;
for i in 0..h {
for j in 0..w {
let v = self.sample(0, sx + j, sy + i);
lo = lo.min(v);
hi = hi.max(v);
}
}
hi - lo
}
fn decide_rect(&self, r: usize, c: usize, bw: usize) -> Option<usize> {
if bw != 16 && bw != 32 {
return None;
}
let n4 = bw / 4;
if r + n4 > self.mi_rows || c + n4 > self.mi_cols {
return None; }
let (sx, sy, hp) = (c * 4, r * 4, bw / 2);
if self.luma_range(sx, sy, bw, bw) <= 32 {
return None; }
let horz_ok =
self.luma_range(sx, sy, bw, hp) <= 32 && self.luma_range(sx, sy + hp, bw, hp) <= 32;
let vert_ok =
self.luma_range(sx, sy, hp, bw) <= 32 && self.luma_range(sx + hp, sy, hp, bw) <= 32;
if horz_ok {
Some(1)
} else if vert_ok {
Some(2)
} else {
None
}
}
fn tx_depth_ctx(&self, r: usize, c: usize, bw: usize, bh: usize) -> usize {
let above_w = if r > 0 {
1usize << self.tx_log2[(r - 1) * self.mi_cols + c]
} else {
0
};
let left_h = if c > self.tile_c0 {
1usize << self.tx_log2_h[r * self.mi_cols + (c - 1)]
} else {
0
};
usize::from(above_w >= bw) + usize::from(left_h >= bh)
}
fn select_tx_depth(&self, sx: usize, sy: usize, bw: usize, max_depth: usize) -> usize {
let range = self.luma_range(sx, sy, bw, bw);
let depth = if range > 24 {
2
} else if range > 12 {
1
} else {
0
};
depth.min(max_depth)
}
fn encode_block(&mut self, r: usize, c: usize, bw: usize, bh: usize) {
let n4 = bw / 4;
let n4h = bh / 4;
let bsl = n4.trailing_zeros() as u8;
let bsl_h = n4h.trailing_zeros() as u8;
let is_rect = bw != bh;
let lossy_large = self.qindex > 0 && bw.min(bh) >= 8;
let palette = if self.qindex > 0 && !is_rect && (8..=32).contains(&bw) {
self.decide_palette(r, c, bw)
} else {
None
};
let skip =
palette.is_some() || (self.qindex > 0 && !is_rect && self.block_is_skippable(r, c, bw));
let sctx = self.skip_ctx(r, c);
self.sym.encode_symbol(usize::from(skip), &cdf::SKIP[sctx]);
let segid = if self.qindex > 0 {
self.signal_segment_id(r, c, skip)
} else {
0
};
if self.read_deltas {
if !(bw == 64 && bh == 64 && skip) {
self.signal_delta_q();
self.signal_delta_lf();
}
self.read_deltas = false;
}
if self.qindex > 0 {
self.apply_seg_quant(segid);
}
let (y_mode, angle_delta, filter_intra) = if palette.is_some() || self.qindex == 0 {
(DC_PRED, 0i8, None)
} else if is_rect {
(self.select_luma_mode_rect(c * 4, r * 4, bw, bh), 0, None)
} else if lossy_large {
self.select_luma_mode_nxn(c * 4, r * 4, bw)
} else {
let (m, fi) = self.select_luma_mode(c * 4, r * 4);
(m, 0, fi)
};
let amode = cdf::INTRA_MODE_CONTEXT[if r > 0 {
usize::from(self.mi_ymode[(r - 1) * self.mi_cols + c])
} else {
usize::from(DC_PRED)
}];
let lmode = cdf::INTRA_MODE_CONTEXT[if c > self.tile_c0 {
usize::from(self.mi_ymode[r * self.mi_cols + (c - 1)])
} else {
usize::from(DC_PRED)
}];
self.sym
.encode_symbol(usize::from(y_mode), &cdf::INTRA_FRAME_Y_MODE[amode][lmode]);
if bw >= 8 && is_directional(y_mode) {
let sym = (i32::from(angle_delta) + 3) as usize;
self.sym
.encode_symbol(sym, &cdf::ANGLE_DELTA[(y_mode - V_PRED) as usize]);
}
let cfl_allowed = if self.qindex == 0 {
bw == 4
} else {
bw.max(bh) <= 32
};
let cfl = if palette.is_some() {
None } else if self.qindex > 0 && cfl_allowed && !is_rect {
self.select_cfl(c * 4, r * 4, bw)
} else {
None };
let ym = usize::from(y_mode);
if cfl_allowed {
let uv = if cfl.is_some() { UV_CFL_PRED } else { DC_PRED };
self.sym
.encode_symbol(usize::from(uv), &cdf::UV_MODE_CFL_ALLOWED[ym]);
if let Some((au, av)) = cfl {
self.emit_cfl_alphas(au, av);
}
} else {
self.sym.encode_symbol(0, &cdf::UV_MODE_CFL_NOT_ALLOWED[ym]);
}
if self.qindex > 0 && (8..=64).contains(&bw) {
if y_mode == DC_PRED {
let bctx = bw.trailing_zeros() as usize + bh.trailing_zeros() as usize - 6; let above = r > 0 && self.mi_psize[(r - 1) * self.mi_cols + c] > 0;
let left = c > self.tile_c0 && self.mi_psize[r * self.mi_cols + (c - 1)] > 0;
let pctx = usize::from(above) + usize::from(left);
self.sym.encode_symbol(
usize::from(palette.is_some()),
&cdf::PALETTE_Y_MODE[bctx][pctx],
);
if let Some(pal) = &palette {
self.sym
.encode_symbol(pal.colors.len() - 2, &cdf::PALETTE_Y_SIZE[bctx]);
let cache_n = self.palette_cache(r, c).len();
let colors = pal.colors.clone();
self.signal_palette_colors(&colors, cache_n);
}
}
if cfl.is_none() {
let uctx = usize::from(palette.is_some());
self.sym.encode_symbol(0, &cdf::PALETTE_UV_MODE[uctx]);
}
}
if self.qindex > 0 && y_mode == DC_PRED && palette.is_none() && bw <= 32 && bh <= 32 {
let fi_cdf: &[u16] = match bw {
4 => &cdf::FILTER_INTRA_4X4,
8 => &cdf::FILTER_INTRA_8X8,
16 => &cdf::FILTER_INTRA_16X16,
_ => &cdf::FILTER_INTRA_32X32,
};
self.sym
.encode_symbol(usize::from(filter_intra.is_some()), fi_cdf);
if let Some(fi) = filter_intra {
self.sym
.encode_symbol(usize::from(fi), &cdf::FILTER_INTRA_MODE);
}
}
if let Some(pal) = &palette {
let idx_map = pal.index_map.clone();
self.signal_palette_tokens(&idx_map, bw, pal.colors.len());
}
let luma_tx = if is_rect {
let ctx = self.tx_depth_ctx(r, c, bw, bh);
let cdf: &[u16] = match bw.max(bh) {
16 => &cdf::TX_SIZE_16X16[ctx],
_ => &cdf::TX_SIZE_32X32[ctx],
};
self.sym.encode_symbol(0, cdf);
rect_tx(bw, bh)
} else if lossy_large {
let max_depth = if bw == 8 { 1 } else { 2 };
let tx_depth = self.select_tx_depth(c * 4, r * 4, bw, max_depth);
let ctx = self.tx_depth_ctx(r, c, bw, bw);
let cdf: &[u16] = match bw {
8 => &cdf::TX_SIZE_8X8[ctx],
16 => &cdf::TX_SIZE_16X16[ctx],
32 => &cdf::TX_SIZE_32X32[ctx],
_ => &cdf::TX_SIZE_64X64[ctx],
};
self.sym.encode_symbol(tx_depth, cdf);
square_tx(bw >> tx_depth)
} else {
TxSize::Tx4x4
};
let txl = luma_tx.log2_width() as u8;
let txl_h = luma_tx.log2_height() as u8;
let (psize, pcolors) = match &palette {
Some(pal) => {
let mut buf = [0u8; 8];
buf[..pal.colors.len()].copy_from_slice(&pal.colors);
(pal.colors.len() as u8, buf)
}
None => (0u8, [0u8; 8]),
};
for y in 0..n4h {
for x in 0..n4 {
let (rr, cc) = (r + y, c + x);
if rr < self.mi_rows && cc < self.mi_cols {
self.mi_bsl[rr * self.mi_cols + cc] = bsl;
self.mi_bsl_h[rr * self.mi_cols + cc] = bsl_h;
self.mi_ymode[rr * self.mi_cols + cc] = y_mode;
self.tx_log2[rr * self.mi_cols + cc] = txl;
self.tx_log2_h[rr * self.mi_cols + cc] = txl_h;
self.mi_skip[rr * self.mi_cols + cc] = u8::from(skip);
self.mi_psize[rr * self.mi_cols + cc] = psize;
self.mi_pcolors[rr * self.mi_cols + cc] = pcolors;
self.mi_segid[rr * self.mi_cols + cc] = segid as u8;
self.mi_dlf[rr * self.mi_cols + cc] = self.current_dlf as i8;
}
}
}
if let Some(pal) = palette {
self.recon_palette(r, c, bw, &pal);
for ty in 0..bw / 4 {
for tx in 0..bw / 4 {
let by = (r + ty) as isize - self.sb_r as isize;
let bx = (c + tx) as isize - self.sb_c as isize;
if (0..16).contains(&by) && (0..16).contains(&bx) {
self.block_decoded[(by + 1) as usize * BD_STRIDE + (bx + 1) as usize] = 1;
}
}
}
return;
}
let chroma_tx = if is_rect {
rect_tx(bw, bh)
} else {
match bw {
8 => TxSize::Tx8x8,
16 => TxSize::Tx16x16,
_ => TxSize::Tx32x32,
}
};
for plane in 0..3 {
let pred = Pred {
mode: if plane == 0 { y_mode } else { DC_PRED },
angle_delta: if plane == 0 { angle_delta } else { 0 },
filter_intra: if plane == 0 { filter_intra } else { None },
cfl_alpha: match (plane, cfl) {
(1, Some((au, _))) => Some(au),
(2, Some((_, av))) => Some(av),
_ => None,
},
};
let (ptx, pw, ph) = if plane == 0 {
(luma_tx, luma_tx.width(), luma_tx.height())
} else if lossy_large {
(chroma_tx, chroma_tx.width(), chroma_tx.height())
} else {
(TxSize::Tx4x4, 4, 4)
};
let mut sy = r * 4;
while sy < r * 4 + bh {
let mut sx = c * 4;
while sx < c * 4 + bw {
if sx < self.coded_w && sy < self.coded_h {
self.transform_block(plane, sx, sy, bw, ptx, pred, skip);
}
if plane == 0 {
for ty in 0..ph / 4 {
for tx in 0..pw / 4 {
let by = (sy / 4 + ty) as isize - self.sb_r as isize;
let bx = (sx / 4 + tx) as isize - self.sb_c as isize;
if (0..16).contains(&by) && (0..16).contains(&bx) {
self.block_decoded
[(by + 1) as usize * BD_STRIDE + (bx + 1) as usize] = 1;
}
}
}
}
sx += pw;
}
sy += ph;
}
}
}
#[allow(clippy::too_many_arguments)]
fn transform_block(
&mut self,
plane: usize,
sx: usize,
sy: usize,
block_w: usize,
tx_size: TxSize,
desc: Pred,
skip: bool,
) {
if self.qindex == 0 {
let pred = self.predict_4x4(plane, sx, sy, desc.mode);
let mut res = [0i32; 16];
for i in 0..4 {
for j in 0..4 {
res[i * 4 + j] = self.sample(plane, sx + j, sy + i) - pred[i * 4 + j];
}
}
let quant = gamut_dsp::fwht4x4(&res);
self.code_coeffs(
plane,
sx >> 2,
sy >> 2,
block_w,
TxSize::Tx4x4,
&quant,
1,
0,
);
return;
}
let (tw, th) = (tx_size.width(), tx_size.height());
let pred: Vec<i32> = match desc.filter_intra {
Some(fi) if plane == 0 => self.predict_filter_intra(plane, sx, sy, tw, fi),
_ => {
let mut p = self.predict_intra(plane, sx, sy, desc.mode, tw, th, desc.angle_delta);
if let Some(alpha) = desc.cfl_alpha {
self.apply_cfl(&mut p, sx, sy, alpha, tw);
}
p
}
};
if skip {
for (i, prow) in pred.chunks_exact(tw).enumerate() {
for (j, &pv) in prow.iter().enumerate() {
self.recon[plane][(sy + i) * self.coded_w + (sx + j)] =
clip_pixel(pv, self.bit_depth);
}
}
self.set_ctx(plane, sx >> 2, sy >> 2, tw / 4, th / 4, 0, 0);
return;
}
let mut res = vec![0i32; tw * th];
for i in 0..th {
for j in 0..tw {
res[i * tw + j] = self.sample(plane, sx + j, sy + i) - pred[i * tw + j];
}
}
let (tx, tx_sym, levels) = if plane == 0 && tw == th {
self.select_tx_type(&res, tx_size)
} else {
(
TxType::DctDct,
1,
self.quantize_tx(&res, tx_size, TxType::DctDct),
)
};
let intra_dir = match desc.filter_intra {
Some(fi) => usize::from(FILTER_INTRA_MODE_TO_INTRA_DIR[fi as usize]),
None => usize::from(desc.mode),
};
self.code_coeffs(
plane,
sx >> 2,
sy >> 2,
block_w,
tx_size,
&levels,
tx_sym,
intra_dir,
);
let dq_denom = tx_size.dq_denom();
let mut dq = vec![0i32; tw * th];
if tw == 64 && th == 64 {
for i in 0..32 {
for j in 0..32 {
let q = if i == 0 && j == 0 {
self.dc_quant
} else {
self.ac_quant
};
dq[i * 64 + j] = dequant(levels[i * 32 + j], q, dq_denom, 8);
}
}
} else {
for (i, &lvl) in levels.iter().enumerate() {
let q = if i == 0 { self.dc_quant } else { self.ac_quant };
dq[i] = dequant(lvl, q, dq_denom, 8);
}
}
let resid = inverse_transform_2d(&dq, tx_size, tx, 8);
for i in 0..th {
for j in 0..tw {
let v = clip_pixel(pred[i * tw + j] + resid[i * tw + j], self.bit_depth);
self.recon[plane][(sy + i) * self.coded_w + (sx + j)] = v;
}
}
}
fn quantize_tx(&self, res: &[i32], tx_size: TxSize, tx: TxType) -> Vec<i32> {
let coeff = forward_transform_2d(res, tx_size, tx);
let denom = tx_size.dq_denom();
if tx_size.width() == 64 {
let mut levels = vec![0i32; 32 * 32];
for i in 0..32 {
for j in 0..32 {
let q = if i == 0 && j == 0 {
self.dc_quant
} else {
self.ac_quant
};
levels[i * 32 + j] = quantize(coeff[i * 64 + j] * denom, q);
}
}
return levels;
}
let mut levels = vec![0i32; coeff.len()];
for (i, &c) in coeff.iter().enumerate() {
let q = if i == 0 { self.dc_quant } else { self.ac_quant };
levels[i] = quantize(c * denom, q);
}
levels
}
fn select_tx_type(&self, res: &[i32], tx_size: TxSize) -> (TxType, usize, Vec<i32>) {
if tx_size.width() >= 32 {
return (
TxType::DctDct,
1,
self.quantize_tx(res, tx_size, TxType::DctDct),
);
}
let cost = |levels: &[i32]| -> i32 {
levels
.iter()
.map(|&l| if l == 0 { 0 } else { 1 + l.abs() })
.sum()
};
let mut best = (
TxType::DctDct,
1usize,
self.quantize_tx(res, tx_size, TxType::DctDct),
);
let mut best_cost = cost(&best.2);
for (tx, sym) in [
(TxType::AdstAdst, 2usize),
(TxType::AdstDct, 3),
(TxType::DctAdst, 4),
(TxType::Idtx, 0),
] {
let levels = self.quantize_tx(res, tx_size, tx);
let c = cost(&levels);
if c < best_cost {
best_cost = c;
best = (tx, sym, levels);
}
}
best
}
fn dc_avg(&self, plane: usize, sx: usize, sy: usize) -> i32 {
self.dc_pred(plane, sx, sy, 4, 4)
}
fn dc_pred(&self, plane: usize, sx: usize, sy: usize, w: usize, h: usize) -> i32 {
let nb = |x: usize, y: usize| -> i32 {
if self.qindex > 0 {
i32::from(self.recon[plane][y * self.coded_w + x])
} else {
self.sample(plane, x, y)
}
};
let have_above = sy > 0;
let have_left = sx > self.tile_x0;
match (have_above, have_left) {
(true, true) => {
let mut s = 0;
for k in 0..w {
s += nb(sx + k, sy - 1);
}
for k in 0..h {
s += nb(sx - 1, sy + k);
}
(s + ((w + h) as i32 >> 1)) / (w + h) as i32
}
(false, true) => {
let mut s = 0;
for k in 0..h {
s += nb(sx - 1, sy + k);
}
(s + (h as i32 >> 1)) / h as i32
}
(true, false) => {
let mut s = 0;
for k in 0..w {
s += nb(sx + k, sy - 1);
}
(s + (w as i32 >> 1)) / w as i32
}
(false, false) => 128, }
}
fn reference_4x4(&self, plane: usize, sx: usize, sy: usize) -> ([i32; 8], [i32; 8], i32) {
let nb = |x: usize, y: usize| -> i32 { i32::from(self.recon[plane][y * self.coded_w + x]) };
let have_above = sy > 0;
let have_left = sx > self.tile_x0;
let (by, bx) = (
sy as isize / 4 - self.sb_r as isize,
sx as isize / 4 - self.sb_c as isize,
);
let have_above_right = self.block_decoded_at(by - 1, bx + 1);
let have_below_left = self.block_decoded_at(by + 1, bx - 1);
let (max_x, max_y) = (self.coded_w - 1, self.coded_h - 1);
let mut above = [127i32; 8]; if have_above {
let above_limit = max_x.min(sx + if have_above_right { 8 } else { 4 } - 1);
for (i, a) in above.iter_mut().enumerate() {
*a = nb(above_limit.min(sx + i), sy - 1);
}
} else if have_left {
above = [nb(sx - 1, sy); 8]; }
let mut left = [129i32; 8]; if have_left {
let left_limit = max_y.min(sy + if have_below_left { 8 } else { 4 } - 1);
for (i, l) in left.iter_mut().enumerate() {
*l = nb(sx - 1, left_limit.min(sy + i));
}
} else if have_above {
left = [nb(sx, sy - 1); 8]; }
let top_left = if have_above && have_left {
nb(sx - 1, sy - 1)
} else if have_above {
nb(sx, sy - 1)
} else if have_left {
nb(sx - 1, sy)
} else {
128 };
(above, left, top_left)
}
fn predict_filter_intra(
&self,
plane: usize,
sx: usize,
sy: usize,
n: usize,
fi_mode: u8,
) -> Vec<i32> {
let (above, left, top_left) = self.reference_basic(plane, sx, sy, n, n);
let above_row = |k: i32| -> i32 { if k < 0 { top_left } else { above[k as usize] } };
let left_col = |k: i32| -> i32 { if k < 0 { top_left } else { left[k as usize] } };
let taps = &cdf::INTRA_FILTER_TAPS[fi_mode as usize];
let (w4, h2, nn) = ((n >> 2) as i32, (n >> 1) as i32, n as i32);
let mut pred = vec![0i32; n * n];
for i2 in 0..h2 {
for j4 in 0..w4 {
let mut p = [0i32; 7];
for (i, pi) in p.iter_mut().enumerate() {
let i = i as i32;
*pi = if i < 5 {
if i2 == 0 {
above_row((j4 << 2) + i - 1)
} else if j4 == 0 && i == 0 {
left_col((i2 << 1) - 1)
} else {
pred[(((i2 << 1) - 1) * nn + (j4 << 2) + i - 1) as usize]
}
} else if j4 == 0 {
left_col((i2 << 1) + i - 5)
} else {
pred[(((i2 << 1) + i - 5) * nn + (j4 << 2) - 1) as usize]
};
}
for i1 in 0..2i32 {
for j1 in 0..4i32 {
let row = ((i1 << 2) + j1) as usize;
let mut pr = 0i32;
for (k, &tap) in taps[row].iter().enumerate() {
pr += i32::from(tap) * p[k];
}
let idx = (((i2 << 1) + i1) * nn + (j4 << 2) + j1) as usize;
pred[idx] = round2_signed(pr, 4).clamp(0, 255);
}
}
}
}
pred
}
fn predict_4x4(&self, plane: usize, sx: usize, sy: usize, mode: u8) -> [i32; 16] {
if mode == DC_PRED {
return [self.dc_avg(plane, sx, sy); 16];
}
let (above, left, top_left) = self.reference_4x4(plane, sx, sy);
let (dx, dy): (i32, i32) = match mode {
D45_PRED => (64, 0), D67_PRED => (27, 0), D135_PRED => (64, 64), D113_PRED => (27, 151), D157_PRED => (151, 27), D203_PRED => (0, 27), _ => (0, 0),
};
let w = cdf::SM_WEIGHTS_4X4;
let mut pred = [0i32; 16];
for i in 0..4 {
for j in 0..4 {
pred[i * 4 + j] = match mode {
V_PRED => above[j],
H_PRED => left[i],
D45_PRED | D67_PRED => {
let (ii, jj) = (i as i32, j as i32);
let idx = (ii + 1) * dx;
let base = (idx >> 6) + jj;
let max_base_x = 4 + 4 - 1; if base < max_base_x {
let shift = (idx >> 1) & 0x1F;
(above[base as usize] * (32 - shift)
+ above[(base + 1) as usize] * shift
+ 16)
>> 5
} else {
above[max_base_x as usize]
}
}
D203_PRED => {
let (ii, jj) = (i as i32, j as i32);
let idx = (jj + 1) * dy;
let base = (idx >> 6) + ii;
let shift = (idx >> 1) & 0x1F;
(left[base as usize] * (32 - shift)
+ left[(base + 1) as usize] * shift
+ 16)
>> 5
}
D135_PRED | D113_PRED | D157_PRED => {
let (ii, jj) = (i as i32, j as i32);
let idx = (jj << 6) - (ii + 1) * dx;
let base = idx >> 6;
if base >= -1 {
let shift = (idx >> 1) & 0x1F;
let a0 = if base < 0 {
top_left
} else {
above[base as usize]
};
let a1 = above[(base + 1) as usize];
(a0 * (32 - shift) + a1 * shift + 16) >> 5 } else {
let idx2 = (ii << 6) - (jj + 1) * dy;
let base2 = idx2 >> 6;
let shift = (idx2 >> 1) & 0x1F;
let l0 = if base2 < 0 {
top_left
} else {
left[base2 as usize]
};
let l1 = left[(base2 + 1) as usize];
(l0 * (32 - shift) + l1 * shift + 16) >> 5 }
}
PAETH_PRED => {
let base = above[j] + left[i] - top_left;
let p_left = (base - left[i]).abs();
let p_top = (base - above[j]).abs();
let p_tl = (base - top_left).abs();
if p_left <= p_top && p_left <= p_tl {
left[i]
} else if p_top <= p_tl {
above[j]
} else {
top_left
}
}
SMOOTH_PRED => {
let v = w[i] * above[j]
+ (256 - w[i]) * left[3]
+ w[j] * left[i]
+ (256 - w[j]) * above[3];
(v + 256) >> 9 }
SMOOTH_V_PRED => {
let v = w[i] * above[j] + (256 - w[i]) * left[3];
(v + 128) >> 8 }
_ => {
let v = w[j] * left[i] + (256 - w[j]) * above[3];
(v + 128) >> 8 }
};
}
}
pred
}
#[allow(clippy::too_many_arguments)]
fn predict_intra(
&self,
plane: usize,
sx: usize,
sy: usize,
mode: u8,
w: usize,
h: usize,
angle_delta: i8,
) -> Vec<i32> {
if w != h {
return self.predict_nondir(plane, sx, sy, w, h, mode);
}
let n = w;
if is_directional(mode) {
return self.predict_directional(plane, sx, sy, n, mode, angle_delta);
}
if n == 4 {
return self.predict_4x4(plane, sx, sy, mode).to_vec();
}
self.predict_nondir(plane, sx, sy, n, n, mode)
}
fn reference_directional(
&self,
plane: usize,
sx: usize,
sy: usize,
n: usize,
) -> (Vec<i32>, Vec<i32>, i32) {
let nb = |x: usize, y: usize| -> i32 { i32::from(self.recon[plane][y * self.coded_w + x]) };
let have_above = sy > 0;
let have_left = sx > self.tile_x0;
let step = (n / 4) as isize;
let (by, bx) = (
sy as isize / 4 - self.sb_r as isize,
sx as isize / 4 - self.sb_c as isize,
);
let have_above_right = self.block_decoded_at(by - 1, bx + step);
let have_below_left = self.block_decoded_at(by + step, bx - 1);
let (max_x, max_y) = (self.coded_w - 1, self.coded_h - 1);
let mut above = vec![127i32; 2 * n];
if have_above {
let above_limit = max_x.min(sx + if have_above_right { 2 * n } else { n } - 1);
for (i, a) in above.iter_mut().enumerate() {
*a = nb(above_limit.min(sx + i), sy - 1);
}
} else if have_left {
above = vec![nb(sx - 1, sy); 2 * n];
}
let mut left = vec![129i32; 2 * n];
if have_left {
let left_limit = max_y.min(sy + if have_below_left { 2 * n } else { n } - 1);
for (i, l) in left.iter_mut().enumerate() {
*l = nb(sx - 1, left_limit.min(sy + i));
}
} else if have_above {
left = vec![nb(sx, sy - 1); 2 * n];
}
let top_left = if have_above && have_left {
nb(sx - 1, sy - 1)
} else if have_above {
nb(sx, sy - 1)
} else if have_left {
nb(sx - 1, sy)
} else {
128
};
(above, left, top_left)
}
fn predict_directional(
&self,
plane: usize,
sx: usize,
sy: usize,
n: usize,
mode: u8,
angle_delta: i8,
) -> Vec<i32> {
let p_angle = cdf::MODE_TO_ANGLE[mode as usize] + i32::from(angle_delta) * 3;
let (above, left, top_left) = self.reference_directional(plane, sx, sy, n);
let above_row = |k: i32| -> i32 { if k < 0 { top_left } else { above[k as usize] } };
let left_col = |k: i32| -> i32 { if k < 0 { top_left } else { left[k as usize] } };
let mut pred = vec![0i32; n * n];
for i in 0..n as i32 {
for j in 0..n as i32 {
let v = if p_angle == 90 {
above[j as usize]
} else if p_angle == 180 {
left[i as usize]
} else if p_angle < 90 {
let dx = cdf::DR_INTRA_DERIVATIVE[p_angle as usize];
let idx = (i + 1) * dx;
let base = (idx >> 6) + j;
let max_base_x = (2 * n - 1) as i32; if base < max_base_x {
let shift = (idx >> 1) & 0x1F;
(above[base as usize] * (32 - shift)
+ above[(base + 1) as usize] * shift
+ 16)
>> 5
} else {
above[max_base_x as usize]
}
} else if p_angle < 180 {
let dx = cdf::DR_INTRA_DERIVATIVE[(180 - p_angle) as usize];
let dy = cdf::DR_INTRA_DERIVATIVE[(p_angle - 90) as usize];
let idx = (j << 6) - (i + 1) * dx;
let base = idx >> 6;
if base >= -1 {
let shift = (idx >> 1) & 0x1F;
(above_row(base) * (32 - shift) + above_row(base + 1) * shift + 16) >> 5
} else {
let idx2 = (i << 6) - (j + 1) * dy;
let base2 = idx2 >> 6;
let shift = (idx2 >> 1) & 0x1F;
(left_col(base2) * (32 - shift) + left_col(base2 + 1) * shift + 16) >> 5
}
} else {
let dy = cdf::DR_INTRA_DERIVATIVE[(270 - p_angle) as usize];
let idx = (j + 1) * dy;
let base = (idx >> 6) + i;
let shift = (idx >> 1) & 0x1F;
(left[base as usize] * (32 - shift) + left[(base + 1) as usize] * shift + 16)
>> 5
};
pred[(i * n as i32 + j) as usize] = v;
}
}
pred
}
fn reference_basic(
&self,
plane: usize,
sx: usize,
sy: usize,
w: usize,
h: usize,
) -> (Vec<i32>, Vec<i32>, i32) {
let nb = |x: usize, y: usize| -> i32 { i32::from(self.recon[plane][y * self.coded_w + x]) };
let have_above = sy > 0;
let have_left = sx > self.tile_x0;
let (max_x, max_y) = (self.coded_w - 1, self.coded_h - 1);
let mut above = vec![127i32; w];
if have_above {
for (i, a) in above.iter_mut().enumerate() {
*a = nb(max_x.min(sx + i), sy - 1);
}
} else if have_left {
above = vec![nb(sx - 1, sy); w];
}
let mut left = vec![129i32; h];
if have_left {
for (i, l) in left.iter_mut().enumerate() {
*l = nb(sx - 1, max_y.min(sy + i));
}
} else if have_above {
left = vec![nb(sx, sy - 1); h];
}
let top_left = if have_above && have_left {
nb(sx - 1, sy - 1)
} else if have_above {
nb(sx, sy - 1)
} else if have_left {
nb(sx - 1, sy)
} else {
128
};
(above, left, top_left)
}
fn predict_nondir(
&self,
plane: usize,
sx: usize,
sy: usize,
w: usize,
h: usize,
mode: u8,
) -> Vec<i32> {
if mode == DC_PRED {
return vec![self.dc_pred(plane, sx, sy, w, h); w * h];
}
let (above, left, top_left) = self.reference_basic(plane, sx, sy, w, h);
let sw = sm_weights(w);
let sh = sm_weights(h);
let mut pred = vec![0i32; w * h];
for i in 0..h {
for j in 0..w {
pred[i * w + j] = match mode {
PAETH_PRED => {
let base = above[j] + left[i] - top_left;
let p_left = (base - left[i]).abs();
let p_top = (base - above[j]).abs();
let p_tl = (base - top_left).abs();
if p_left <= p_top && p_left <= p_tl {
left[i]
} else if p_top <= p_tl {
above[j]
} else {
top_left
}
}
SMOOTH_PRED => {
let v = sh[i] * above[j]
+ (256 - sh[i]) * left[h - 1]
+ sw[j] * left[i]
+ (256 - sw[j]) * above[w - 1];
(v + 256) >> 9
}
SMOOTH_V_PRED => {
let v = sh[i] * above[j] + (256 - sh[i]) * left[h - 1];
(v + 128) >> 8
}
_ => {
let v = sw[j] * left[i] + (256 - sw[j]) * above[w - 1];
(v + 128) >> 8
}
};
}
}
pred
}
fn select_luma_mode(&self, sx: usize, sy: usize) -> (u8, Option<u8>) {
let sad = |pred: &[i32]| -> i32 {
let mut s = 0;
for i in 0..4 {
for j in 0..4 {
s += (self.sample(0, sx + j, sy + i) - pred[i * 4 + j]).abs();
}
}
s
};
let mut best_mode = DC_PRED;
let mut best_sad = sad(&self.predict_4x4(0, sx, sy, DC_PRED));
for &mode in &LUMA_MODES[1..] {
let s = sad(&self.predict_4x4(0, sx, sy, mode));
if s < best_sad {
best_sad = s;
best_mode = mode;
}
}
let mut best_fi: Option<u8> = None;
let mut best_fi_sad = i32::MAX;
for fi in 0..5u8 {
let s = sad(&self.predict_filter_intra(0, sx, sy, 4, fi));
if s < best_fi_sad {
best_fi_sad = s;
best_fi = Some(fi);
}
}
if best_fi_sad < best_sad {
(DC_PRED, best_fi)
} else {
(best_mode, None)
}
}
fn select_luma_mode_rect(&self, sx: usize, sy: usize, w: usize, h: usize) -> u8 {
let sad = |pred: &[i32]| -> i32 {
let mut s = 0;
for i in 0..h {
for j in 0..w {
s += (self.sample(0, sx + j, sy + i) - pred[i * w + j]).abs();
}
}
s
};
let mut best = SMOOTH_PRED;
let mut best_sad = sad(&self.predict_nondir(0, sx, sy, w, h, SMOOTH_PRED));
for &mode in &[SMOOTH_V_PRED, SMOOTH_H_PRED, PAETH_PRED] {
let s = sad(&self.predict_nondir(0, sx, sy, w, h, mode));
if s < best_sad {
best_sad = s;
best = mode;
}
}
best
}
fn select_luma_mode_nxn(&self, sx: usize, sy: usize, n: usize) -> (u8, i8, Option<u8>) {
let sad = |pred: &[i32]| -> i32 {
let mut s = 0;
for i in 0..n {
for j in 0..n {
s += (self.sample(0, sx + j, sy + i) - pred[i * n + j]).abs();
}
}
s
};
let mut best = (DC_PRED, 0i8);
let mut best_sad = sad(&self.predict_nondir(0, sx, sy, n, n, DC_PRED));
for &mode in &[SMOOTH_PRED, SMOOTH_V_PRED, SMOOTH_H_PRED, PAETH_PRED] {
let s = sad(&self.predict_nondir(0, sx, sy, n, n, mode));
if s < best_sad {
best_sad = s;
best = (mode, 0);
}
}
for &mode in &DIRECTIONAL_MODES {
for ad in -3..=3i8 {
let s = sad(&self.predict_directional(0, sx, sy, n, mode, ad));
if s < best_sad {
best_sad = s;
best = (mode, ad);
}
}
}
let mut best_fi: Option<u8> = None;
let mut best_fi_sad = i32::MAX;
for fi in 0..5u8 {
let s = sad(&self.predict_filter_intra(0, sx, sy, n, fi));
if s < best_fi_sad {
best_fi_sad = s;
best_fi = Some(fi);
}
}
if best_fi_sad < best_sad {
(DC_PRED, 0, best_fi)
} else {
(best.0, best.1, None)
}
}
fn apply_cfl(&self, pred: &mut [i32], sx: usize, sy: usize, alpha: i32, n: usize) {
let mut l = vec![0i32; n * n];
let mut sum = 0i32;
for i in 0..n {
for j in 0..n {
let v = i32::from(self.recon[0][(sy + i) * self.coded_w + (sx + j)]) << 3;
l[i * n + j] = v;
sum += v;
}
}
let shift = 2 * n.trailing_zeros();
let luma_avg = (sum + (1 << (shift - 1))) >> shift;
for (p, &lv) in pred.iter_mut().zip(&l) {
*p = (*p + round2_signed(alpha * (lv - luma_avg), 6)).clamp(0, 255);
}
}
fn select_cfl(&self, sx: usize, sy: usize, n: usize) -> Option<(i32, i32)> {
let mut l = vec![0i32; n * n];
let mut sum = 0i32;
for i in 0..n {
for j in 0..n {
let v = self.sample(0, sx + j, sy + i) << 3;
l[i * n + j] = v;
sum += v;
}
}
let shift = 2 * n.trailing_zeros();
let luma_avg = (sum + (1 << (shift - 1))) >> shift;
let best_alpha = |plane: usize| -> i32 {
let dc = self.dc_pred(plane, sx, sy, n, n);
let sad = |alpha: i32| -> i32 {
let mut s = 0;
for i in 0..n {
for j in 0..n {
let pred = (dc + round2_signed(alpha * (l[i * n + j] - luma_avg), 6))
.clamp(0, 255);
s += (self.sample(plane, sx + j, sy + i) - pred).abs();
}
}
s
};
let mut best = 0i32;
let mut best_sad = sad(0);
for mag in 1..=16 {
for &a in &[mag, -mag] {
let s = sad(a);
if s < best_sad {
best_sad = s;
best = a;
}
}
}
best
};
let au = best_alpha(1);
let av = best_alpha(2);
if au == 0 && av == 0 {
None
} else {
Some((au, av))
}
}
fn emit_cfl_alphas(&mut self, au: i32, av: i32) {
let sign = |a: i32| -> usize {
if a == 0 {
0 } else if a < 0 {
1 } else {
2 }
};
let (su, sv) = (sign(au), sign(av));
let signs = 3 * su + sv - 1;
self.sym.encode_symbol(signs, &cdf::CFL_SIGN);
if su != 0 {
let ctx = (su - 1) * 3 + sv;
self.sym
.encode_symbol((au.abs() - 1) as usize, &cdf::CFL_ALPHA[ctx]);
}
if sv != 0 {
let ctx = (sv - 1) * 3 + su;
self.sym
.encode_symbol((av.abs() - 1) as usize, &cdf::CFL_ALPHA[ctx]);
}
}
#[allow(clippy::too_many_lines)]
#[allow(
clippy::too_many_lines,
clippy::too_many_arguments,
clippy::type_complexity
)]
fn code_coeffs(
&mut self,
plane: usize,
x4: usize,
y4: usize,
block_w: usize,
tx_size: TxSize,
quant: &[i32],
tx_sym: usize,
intra_dir: usize,
) {
let ptype = usize::from(plane > 0);
let qctx = self.qctx;
let (w, h) = (tx_size.width(), tx_size.height());
let block_h = if w == h { block_w } else { h };
let (w4, h4) = (w / 4, h / 4); let (code_w, code_h) = (w.min(32), h.min(32));
let bwl = code_w.trailing_zeros() as usize;
let area = code_w * code_h;
let up_sq = w.max(h);
let rect_scan;
let scan: &[usize] = match (w, h) {
(4, 4) => &cdf::DEFAULT_SCAN_4X4,
(8, 8) => &cdf::DEFAULT_SCAN_8X8,
(16, 16) => &cdf::DEFAULT_SCAN_16X16,
(32, 32) | (64, 64) => &cdf::DEFAULT_SCAN_32X32, _ => {
rect_scan = cdf::default_scan(code_w, code_h);
&rect_scan
}
};
let (txb_skip, eob_extra, base_eob, base, br, offset): (
&[[[u16; 2]; 13]; 4],
&[[[[u16; 2]; 9]; 2]; 4],
&[[[[u16; 3]; 4]; 2]; 4],
&[[[[u16; 4]; 42]; 2]; 4],
&[[[[u16; 4]; 21]; 2]; 4],
&[[u8; 5]; 5],
) = match up_sq {
4 => (
&cdf::TXB_SKIP,
&cdf::EOB_EXTRA,
&cdf::COEFF_BASE_EOB,
&cdf::COEFF_BASE,
&cdf::COEFF_BR,
&cdf::COEFF_BASE_CTX_OFFSET_4X4,
),
8 => (
&cdf::TXB_SKIP_8X8,
&cdf::EOB_EXTRA_8X8,
&cdf::COEFF_BASE_EOB_8X8,
&cdf::COEFF_BASE_8X8,
&cdf::COEFF_BR_8X8,
&cdf::COEFF_BASE_CTX_OFFSET_8X8,
),
16 => (
&cdf::TXB_SKIP_16X16,
&cdf::EOB_EXTRA_16X16,
&cdf::COEFF_BASE_EOB_16X16,
&cdf::COEFF_BASE_16X16,
&cdf::COEFF_BR_16X16,
&cdf::COEFF_BASE_CTX_OFFSET_8X8,
),
32 => (
&cdf::TXB_SKIP_32X32,
&cdf::EOB_EXTRA_32X32,
&cdf::COEFF_BASE_EOB_32X32,
&cdf::COEFF_BASE_32X32,
&cdf::COEFF_BR_32X32,
&cdf::COEFF_BASE_CTX_OFFSET_8X8,
),
_ => (
&cdf::TXB_SKIP_64X64,
&cdf::EOB_EXTRA_64X64,
&cdf::COEFF_BASE_EOB_64X64,
&cdf::COEFF_BASE_64X64,
&cdf::COEFF_BR_32X32,
&cdf::COEFF_BASE_CTX_OFFSET_8X8,
),
};
let offset: &[[u8; 5]; 5] = if w > h {
&cdf::COEFF_BASE_CTX_OFFSET_WIDE
} else if w < h {
&cdf::COEFF_BASE_CTX_OFFSET_TALL
} else {
offset
};
let mut eob = 0usize;
for c in 0..area {
if quant[scan[c]] != 0 {
eob = c + 1;
}
}
if w != h && plane == 0 && std::env::var("GAMUT_DBG").is_ok() {
eprintln!(
"ENC rect {}x{} eob={} scan5={:?}",
w,
h,
eob,
&scan[..5.min(scan.len())]
);
}
let txb_ctx = self.txb_skip_ctx(plane, x4, y4, block_w, block_h, w, h);
self.sym
.encode_symbol(usize::from(eob == 0), &txb_skip[qctx][txb_ctx]);
if eob == 0 {
self.set_ctx(plane, x4, y4, w4, h4, 0, 0);
return;
}
if self.qindex > 0 && plane == 0 && up_sq <= 16 {
let tx_cdf: &[u16] = if w.min(h) == 16 {
&cdf::INTRA_TX_TYPE_SET2_16X16[intra_dir]
} else {
&cdf::INTRA_TX_TYPE_SET2
};
self.sym.encode_symbol(tx_sym, tx_cdf);
}
let eobpt = eobpt_from_eob(eob);
match area {
16 => self
.sym
.encode_symbol(eobpt - 1, &cdf::EOB_PT_16[qctx][ptype][0]),
64 => self
.sym
.encode_symbol(eobpt - 1, &cdf::EOB_PT_64[qctx][ptype][0]),
128 => self
.sym
.encode_symbol(eobpt - 1, &cdf::EOB_PT_128[qctx][ptype][0]),
256 => self
.sym
.encode_symbol(eobpt - 1, &cdf::EOB_PT_256[qctx][ptype][0]),
512 => self
.sym
.encode_symbol(eobpt - 1, &cdf::EOB_PT_512[qctx][ptype]),
_ => self
.sym
.encode_symbol(eobpt - 1, &cdf::EOB_PT_1024[qctx][ptype]),
}
if eobpt >= 3 {
let nbits = eobpt - 2;
let base_eob_val = (1usize << (eobpt - 2)) + 1;
let extra = eob - base_eob_val;
self.sym.encode_symbol(
(extra >> (nbits - 1)) & 1,
&eob_extra[qctx][ptype][eobpt - 3],
);
let mut i = nbits as isize - 2;
while i >= 0 {
self.sym.encode_literal(((extra >> i) & 1) as u32, 1);
i -= 1;
}
}
let mut levels = vec![0i32; area];
for c in (0..eob).rev() {
let pos = scan[c];
let level = quant[pos].abs();
if c == eob - 1 {
let ctx = coeff_base_eob_ctx(c, area);
self.sym
.encode_symbol((level.min(3) - 1) as usize, &base_eob[qctx][ptype][ctx]);
} else {
let ctx = coeff_base_ctx(pos, &levels, bwl, code_w, code_h, offset);
self.sym
.encode_symbol(level.min(3) as usize, &base[qctx][ptype][ctx]);
}
if level > NUM_BASE_LEVELS {
let br_ctx = coeff_br_ctx(pos, &levels, bwl, code_w, code_h);
let mut rem = level - 3;
for _ in 0..4 {
let brv = rem.min(3);
self.sym
.encode_symbol(brv as usize, &br[qctx][ptype][br_ctx]);
rem -= brv;
if brv < 3 {
break;
}
}
}
levels[pos] = level;
}
for (c, &pos) in scan.iter().enumerate().take(eob) {
let level = quant[pos].abs();
if level != 0 {
let neg = quant[pos] < 0;
if c == 0 {
let ctx = self.dc_sign_ctx(plane, x4, y4, w4, h4);
self.sym
.encode_symbol(usize::from(neg), &cdf::DC_SIGN[ptype][ctx]);
} else {
self.sym.encode_literal(u32::from(neg), 1);
}
if level > COEFF_BASE_PLUS_RANGE {
golomb(&mut self.sym, (level - COEFF_BASE_PLUS_RANGE) as u32);
}
}
}
let cul = levels.iter().sum::<i32>().min(63) as u8;
let dc_cat = if quant[0] == 0 {
0
} else if quant[0] < 0 {
1
} else {
2
};
self.set_ctx(plane, x4, y4, w4, h4, cul, dc_cat);
}
#[allow(clippy::too_many_arguments)]
fn set_ctx(
&mut self,
plane: usize,
x4: usize,
y4: usize,
w4: usize,
h4: usize,
cul: u8,
dc: u8,
) {
for k in 0..w4 {
if x4 + k < self.mi_cols {
self.above_level[plane][x4 + k] = cul;
self.above_dc[plane][x4 + k] = dc;
}
}
for k in 0..h4 {
if y4 + k < self.mi_rows {
self.left_level[plane][y4 + k] = cul;
self.left_dc[plane][y4 + k] = dc;
}
}
}
#[allow(clippy::too_many_arguments)]
fn txb_skip_ctx(
&self,
plane: usize,
x4: usize,
y4: usize,
block_w: usize,
block_h: usize,
tx_w: usize,
tx_h: usize,
) -> usize {
let (w4, h4) = (tx_w / 4, tx_h / 4);
if plane == 0 {
if block_w == tx_w && block_h == tx_h {
return 0;
}
let mut top = 0i32;
let mut left = 0i32;
for k in 0..w4 {
if x4 + k < self.mi_cols {
top = top.max(i32::from(self.above_level[0][x4 + k]));
}
}
for k in 0..h4 {
if y4 + k < self.mi_rows {
left = left.max(i32::from(self.left_level[0][y4 + k]));
}
}
if top == 0 && left == 0 {
1
} else if top == 0 || left == 0 {
2 + usize::from(top.max(left) > 3)
} else if top.max(left) <= 3 {
4
} else if top.min(left) <= 3 {
5
} else {
6
}
} else {
let mut above = 0u8;
let mut left = 0u8;
for k in 0..w4 {
if x4 + k < self.mi_cols {
above |= self.above_level[plane][x4 + k] | self.above_dc[plane][x4 + k];
}
}
for k in 0..h4 {
if y4 + k < self.mi_rows {
left |= self.left_level[plane][y4 + k] | self.left_dc[plane][y4 + k];
}
}
let mut ctx = usize::from(above != 0) + usize::from(left != 0) + 7;
if block_w * block_h > tx_w * tx_h {
ctx += 3;
}
ctx
}
}
fn dc_sign_ctx(&self, plane: usize, x4: usize, y4: usize, w4: usize, h4: usize) -> usize {
let mut s = 0i32;
for k in 0..w4 {
if x4 + k < self.mi_cols {
match self.above_dc[plane][x4 + k] {
1 => s -= 1,
2 => s += 1,
_ => {}
}
}
}
for k in 0..h4 {
if y4 + k < self.mi_rows {
match self.left_dc[plane][y4 + k] {
1 => s -= 1,
2 => s += 1,
_ => {}
}
}
}
if s < 0 {
1
} else if s > 0 {
2
} else {
0
}
}
}
fn partition_cdf(bsl: usize, ctx: usize) -> &'static [u16] {
match bsl {
1 => &cdf::PARTITION_W8[ctx],
2 => &cdf::PARTITION_W16[ctx],
3 => &cdf::PARTITION_W32[ctx],
_ => &cdf::PARTITION_W64[ctx],
}
}
fn split_or_horz_cdf(p: &[u16]) -> [u16; 2] {
let psum = (p[2] - p[1])
+ (p[3] - p[2])
+ (p[4] - p[3])
+ (p[6] - p[5])
+ (p[7] - p[6])
+ (p[9] - p[8]);
[32768 - psum, 32768]
}
fn split_or_vert_cdf(p: &[u16]) -> [u16; 2] {
let psum = (p[1] - p[0])
+ (p[3] - p[2])
+ (p[4] - p[3])
+ (p[5] - p[4])
+ (p[6] - p[5])
+ (p[8] - p[7]);
[32768 - psum, 32768]
}
fn eobpt_from_eob(eob: usize) -> usize {
if eob <= 1 {
eob
} else {
(32 - ((eob - 1) as u32).leading_zeros()) as usize + 1
}
}
fn coeff_base_eob_ctx(c: usize, area: usize) -> usize {
if c == 0 {
0
} else if c <= area / 8 {
1
} else if c <= area / 4 {
2
} else {
3
}
}
fn coeff_base_ctx(
pos: usize,
levels: &[i32],
bwl: usize,
w: usize,
h: usize,
offset: &[[u8; 5]; 5],
) -> usize {
let (row, col) = (pos >> bwl, pos & (w - 1));
let mut mag = 0i32;
for &(dr, dc) in &cdf::SIG_REF_DIFF_OFFSET_2D {
let (rr, cc) = (row + dr, col + dc);
if rr < h && cc < w {
mag += levels[(rr << bwl) + cc].abs().min(3);
}
}
let ctx = (((mag + 1) >> 1).min(4)) as usize;
if row == 0 && col == 0 {
return 0;
}
ctx + usize::from(offset[row.min(4)][col.min(4)])
}
fn coeff_br_ctx(pos: usize, levels: &[i32], bwl: usize, w: usize, h: usize) -> usize {
let (row, col) = (pos >> bwl, pos & (w - 1));
let mut mag = 0i32;
for &(dr, dc) in &cdf::MAG_REF_OFFSET_2D {
let (rr, cc) = (row + dr, col + dc);
if rr < h && cc < w {
mag += levels[(rr << bwl) + cc].abs().min(15);
}
}
let mag = (((mag + 1) >> 1).min(6)) as usize;
if pos == 0 {
mag
} else if row < 2 && col < 2 {
mag + 7
} else {
mag + 14
}
}
fn encode_ns(sym: &mut SymbolEncoder, v: u32, n: u32) {
let w = (32 - n.leading_zeros()) as i32; let m = (1u32 << w) - n;
if v < m {
sym.encode_literal(v, (w - 1) as u32);
} else {
let x = v + m;
sym.encode_literal(x >> 1, (w - 1) as u32);
sym.encode_literal(x & 1, 1);
}
}
fn encode_subexp(sym: &mut SymbolEncoder, v: u32, num_syms: u32, k: u32) {
let (mut i, mut mk) = (0u32, 0u32);
loop {
let b2 = if i != 0 { k + i - 1 } else { k };
let a = 1u32 << b2;
if num_syms <= mk + 3 * a {
encode_ns(sym, v - mk, num_syms - mk);
return;
} else if v >= mk + a {
sym.encode_literal(1, 1); i += 1;
mk += a;
} else {
sym.encode_literal(0, 1);
sym.encode_literal(v - mk, b2);
return;
}
}
}
fn recenter(r: i32, v: i32) -> u32 {
if v > 2 * r {
v as u32
} else if v < r {
(2 * (r - v) - 1) as u32
} else {
(2 * (v - r)) as u32
}
}
fn encode_subexp_with_ref(sym: &mut SymbolEncoder, v: i32, mx: i32, k: u32, r: i32) {
let recentered = if (r << 1) <= mx {
recenter(r, v)
} else {
recenter(mx - 1 - r, mx - 1 - v)
};
encode_subexp(sym, recentered, mx as u32, k);
}
fn golomb(sym: &mut SymbolEncoder, x: u32) {
let len = 32 - x.leading_zeros(); for _ in 0..(len - 1) {
sym.encode_literal(0, 1);
}
sym.encode_literal(1, 1);
let mut i = len as isize - 2;
while i >= 0 {
sym.encode_literal((x >> i) & 1, 1);
i -= 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
use gamut_color::Planar8;
fn grey4x4() -> Planar8 {
Planar8::from_rgb8_identity(&[128u8; 4 * 4 * 3], 4, 4).unwrap()
}
#[test]
fn block_exceeds_frame_flags_only_overhang() {
assert!(!block_exceeds_frame(0, 0, 8, 16, 16));
assert!(!block_exceeds_frame(0, 0, 8, 8, 8)); assert!(!block_exceeds_frame(2, 0, 6, 8, 8)); assert!(!block_exceeds_frame(0, 2, 6, 8, 8)); assert!(block_exceeds_frame(0, 0, 8, 6, 99)); assert!(block_exceeds_frame(0, 0, 8, 99, 6)); }
#[test]
fn tx_type_selection_matches_set2_mapping() {
let p = grey4x4();
let e = FrameEncoder::new(&p, 48);
let (tx, sym, _) = e.select_tx_type(&[40i32; 16], TxSize::Tx4x4);
assert!(matches!(tx, TxType::DctDct));
assert_eq!(sym, 1);
let mut impulse = [0i32; 16];
impulse[5] = 220;
let (tx, sym, levels) = e.select_tx_type(&impulse, TxSize::Tx4x4);
assert!(matches!(tx, TxType::Idtx));
assert_eq!(sym, 0);
assert_eq!(levels.iter().filter(|&&l| l != 0).count(), 1);
}
#[test]
fn tx_type_selection_is_deterministic() {
let p = grey4x4();
let e = FrameEncoder::new(&p, 90);
let mut res = [0i32; 16];
for (i, v) in res.iter_mut().enumerate() {
*v = (i as i32 - 8) * 17;
}
let a = e.select_tx_type(&res, TxSize::Tx4x4);
let b = e.select_tx_type(&res, TxSize::Tx4x4);
assert_eq!(a.1, b.1);
assert_eq!(a.2, b.2);
}
#[test]
fn predictors_match_spec_formulas() {
let p = Planar8::from_rgb8_identity(&[128u8; 12 * 12 * 3], 12, 12).unwrap();
let mut e = FrameEncoder::new(&p, 32);
let cw = e.coded_w;
for j in 0..4 {
e.recon[0][3 * cw + (4 + j)] = 100;
}
for i in 0..4 {
e.recon[0][(4 + i) * cw + 3] = 50;
}
e.recon[0][3 * cw + 3] = 80;
let paeth = e.predict_4x4(0, 4, 4, PAETH_PRED);
assert_eq!(paeth, [80; 16]);
let sv = e.predict_4x4(0, 4, 4, SMOOTH_V_PRED);
assert_eq!(sv[0], 100);
assert_eq!(sv[12], 63);
let sh = e.predict_4x4(0, 4, 4, SMOOTH_H_PRED);
assert_eq!(sh[0], 50);
let sm = e.predict_4x4(0, 4, 4, SMOOTH_PRED);
assert_eq!(sm[0], 75);
}
#[test]
fn general_directional_matches_4x4_path() {
let p = Planar8::from_rgb8_identity(&[128u8; 16 * 16 * 3], 16, 16).unwrap();
let mut e = FrameEncoder::new(&p, 32);
let cw = e.coded_w;
for y in 0..e.coded_h {
for x in 0..cw {
e.recon[0][y * cw + x] = ((x * 7 + y * 13 + 5) & 0xff) as u16;
}
}
e.block_decoded.iter_mut().for_each(|b| *b = 1);
for &mode in &DIRECTIONAL_MODES {
let general = e.predict_directional(0, 4, 4, 4, mode, 0);
let baseline = e.predict_4x4(0, 4, 4, mode).to_vec();
assert_eq!(general, baseline, "directional mode {mode} (n=4) mismatch");
}
}
#[test]
fn directional_8x8_angle_delta_in_range() {
let p = Planar8::from_rgb8_identity(&[128u8; 32 * 32 * 3], 32, 32).unwrap();
let mut e = FrameEncoder::new(&p, 32);
let cw = e.coded_w;
for y in 0..e.coded_h {
for x in 0..cw {
e.recon[0][y * cw + x] = (((x * 11) ^ (y * 5 + 9)) & 0xff) as u16;
}
}
e.block_decoded.iter_mut().for_each(|b| *b = 1);
for &mode in &DIRECTIONAL_MODES {
for ad in -3..=3i8 {
let pred = e.predict_directional(0, 8, 8, 8, mode, ad);
assert_eq!(pred.len(), 64);
assert!(
pred.iter().all(|&x| (0..=255).contains(&x)),
"8×8 mode {mode} angle_delta {ad} out of range"
);
}
}
}
#[test]
fn reference_sample_fallbacks_at_frame_corner() {
let p = grey4x4();
let e = FrameEncoder::new(&p, 32);
assert_eq!(e.predict_4x4(0, 0, 0, PAETH_PRED), [128; 16]);
}
#[test]
fn directional_predictors_match_spec() {
let p = Planar8::from_rgb8_identity(&[128u8; 12 * 12 * 3], 12, 12).unwrap();
let mut e = FrameEncoder::new(&p, 32);
let cw = e.coded_w;
for (j, &v) in [60u16, 90, 120, 150].iter().enumerate() {
e.recon[0][3 * cw + (4 + j)] = v;
}
for (i, &v) in [40u16, 80, 120, 160].iter().enumerate() {
e.recon[0][(4 + i) * cw + 3] = v;
}
e.recon[0][3 * cw + 3] = 100;
let v = e.predict_4x4(0, 4, 4, V_PRED);
assert_eq!(&v[0..4], &[60, 90, 120, 150]);
assert_eq!(&v[12..16], &[60, 90, 120, 150]);
let h = e.predict_4x4(0, 4, 4, H_PRED);
assert_eq!([h[0], h[4], h[8], h[12]], [40, 80, 120, 160]);
let d135 = e.predict_4x4(0, 4, 4, D135_PRED);
assert_eq!(&d135[0..4], &[100, 60, 90, 120]);
assert_eq!([d135[0], d135[5], d135[10], d135[15]], [100, 100, 100, 100]);
for &m in &[D113_PRED, D157_PRED] {
assert!(
e.predict_4x4(0, 4, 4, m)
.iter()
.all(|&x| (0..=255).contains(&x))
);
}
}
#[test]
fn filter_intra_predictor_matches_spec() {
let p = Planar8::from_rgb8_identity(&[128u8; 12 * 12 * 3], 12, 12).unwrap();
let mut e = FrameEncoder::new(&p, 32);
let cw = e.coded_w;
e.recon[0][3 * cw + 4] = 16; let pred = e.predict_filter_intra(0, 4, 4, 4, 0);
assert_eq!(
pred,
[10, 2, 1, 1, 6, 2, 2, 1, 4, 2, 2, 1, 2, 2, 2, 1],
"FILTER_DC recursive prediction"
);
}
#[test]
fn filter_intra_preserves_flat_reference() {
let p = Planar8::from_rgb8_identity(&[128u8; 12 * 12 * 3], 12, 12).unwrap();
let mut e = FrameEncoder::new(&p, 32);
let cw = e.coded_w;
for k in 0..8 {
e.recon[0][3 * cw + (4 + k)] = 77; e.recon[0][(4 + k) * cw + 3] = 77; }
e.recon[0][3 * cw + 3] = 77; for fi in 0..5u8 {
assert_eq!(
e.predict_filter_intra(0, 4, 4, 4, fi),
[77; 16],
"filter-intra mode {fi} should preserve a flat reference"
);
}
}
#[test]
fn cfl_prediction_matches_spec() {
let p = Planar8::from_rgb8_identity(&[128u8; 12 * 12 * 3], 12, 12).unwrap();
let mut e = FrameEncoder::new(&p, 32);
let cw = e.coded_w;
for i in 0..4 {
for j in 0..4 {
e.recon[0][(4 + i) * cw + (4 + j)] = (80 + 8 * j) as u16;
}
}
let mut pred = [128i32; 16];
e.apply_cfl(&mut pred, 4, 4, 2, 4);
assert_eq!(
pred,
[
125, 127, 129, 131, 125, 127, 129, 131, 125, 127, 129, 131, 125, 127, 129, 131
]
);
let mut flat = [100i32; 16];
e.apply_cfl(&mut flat, 4, 4, 0, 4);
assert_eq!(flat, [100; 16]);
}
#[test]
fn d45_reads_above_right_when_available() {
let p = Planar8::from_rgb8_identity(&[128u8; 12 * 12 * 3], 12, 12).unwrap();
let mut e = FrameEncoder::new(&p, 32);
let cw = e.coded_w;
for (k, &v) in [10u16, 20, 30, 40, 50, 60, 70, 80].iter().enumerate() {
e.recon[0][3 * cw + (4 + k)] = v; }
e.clear_block_decoded(0, 0);
e.block_decoded[BD_STRIDE + 3] = 1; let d45 = e.predict_4x4(0, 4, 4, D45_PRED);
assert_eq!(&d45[0..4], &[20, 30, 40, 50]);
assert_eq!(d45[15], 80);
}
}