effectcraft/crates/av1enc/src/writer.rs

1023 lines
39 KiB
Rust

//! Tile syntax writer (spec 5.11 with the 8.3 CDF selection). It keeps the same per-4x4 mode
//! info, coefficient contexts and adaptive CDFs as a decoder and writes the decisions taken by
//! `decide.rs` as symbols. The context derivations and the motion vector prediction process
//! (7.10.2) mirror the decoder exactly, since symbols and coded motion vector differences
//! depend on them.
use crate::cdf::Cdfs;
use crate::decide::{FrameGeom, Leaf, Node, intra_uv_tx_type, scan_for, tx_in_set, tx_set, tx_size_for};
use crate::loopfilter::LfInfo;
use crate::symbol::SymbolWriter;
use crate::tables::*;
type MvP = [i32; 2];
/// Mode info per 4x4 (MiSizes, YModes, RefFrames, Mvs, ...).
struct Mi {
cols: usize,
mi_size: Vec<u8>,
y_mode: Vec<u8>,
ref_frame: Vec<[i8; 2]>,
mv: Vec<MvP>,
skip: Vec<bool>,
is_inter: Vec<bool>,
tx_type: Vec<u8>,
written: Vec<bool>,
}
impl Mi {
#[inline(always)]
fn idx(&self, r: usize, c: usize) -> usize {
r * self.cols + c
}
}
/// Output of find_mv_stack.
#[derive(Default)]
struct MvStack {
ref_stack_mv: [MvP; MAX_REF_MV_STACK_SIZE + 1],
weight_stack: [u32; MAX_REF_MV_STACK_SIZE + 1],
num_mv_found: usize,
new_mv_count: usize,
found_match: bool,
close_matches: usize,
total_matches: usize,
new_mv_context: usize,
ref_mv_context: usize,
zero_mv_context: usize,
drl_ctx_stack: [usize; MAX_REF_MV_STACK_SIZE + 1],
}
pub(crate) struct TileWriter<'a> {
g: &'a FrameGeom,
intra_only: bool,
qidx: u8,
sw: SymbolWriter,
cdf: Box<Cdfs>,
kf_y_mode: [[[u16; 14]; 5]; 5],
mi: Mi,
above_level: [Vec<u8>; 3],
above_dc: [Vec<u8>; 3],
left_level: [Vec<u8>; 3],
left_dc: [Vec<u8>; 3],
/// LoopfilterTxSizes per plane (row stride mi_cols + 32).
lf_tx: [Vec<u8>; 3],
// current block
r: usize,
c: usize,
bsize: usize,
avail_u: bool,
avail_l: bool,
mvs: MvStack,
}
fn block_size_px(bs: usize) -> usize {
4 * NUM_4X4_BLOCKS_WIDE[bs] as usize
}
fn is_directional(mode: usize) -> bool {
(V_PRED..=D67_PRED).contains(&mode)
}
impl<'a> TileWriter<'a> {
/// `init`: the CDFs saved by the primary reference frame (load_cdfs), or None for the
/// defaults (init_non_coeff_cdfs / init_coeff_cdfs).
pub fn new(g: &'a FrameGeom, intra_only: bool, qidx: u8, init: Option<&Cdfs>) -> Self {
let n = g.mi_cols * g.mi_rows;
TileWriter {
g,
intra_only,
qidx,
sw: SymbolWriter::new(false),
cdf: match init {
Some(c) => {
let mut c = Box::new(c.clone());
c.clear_counts();
c
}
None => Cdfs::new(qidx as u32),
},
kf_y_mode: DEFAULT_INTRA_FRAME_Y_MODE_CDF,
mi: Mi {
cols: g.mi_cols,
mi_size: vec![0; n],
y_mode: vec![0; n],
ref_frame: vec![[0, -1]; n],
mv: vec![[0, 0]; n],
skip: vec![false; n],
is_inter: vec![false; n],
tx_type: vec![0; n],
written: vec![false; n],
},
above_level: std::array::from_fn(|_| vec![0; g.mi_cols + 64]),
above_dc: std::array::from_fn(|_| vec![0; g.mi_cols + 64]),
left_level: std::array::from_fn(|_| vec![0; g.mi_rows + 64]),
left_dc: std::array::from_fn(|_| vec![0; g.mi_rows + 64]),
lf_tx: std::array::from_fn(|_| vec![0; (g.mi_cols + 32) * (g.mi_rows + 32)]),
r: 0,
c: 0,
bsize: 0,
avail_u: false,
avail_l: false,
mvs: MvStack::default(),
}
}
/// The tile data and the final CDFs (saved for the frame-end CDF update).
pub fn finish(self) -> (Vec<u8>, Box<Cdfs>) {
(self.sw.finish(), self.cdf)
}
/// The mode info the loop filter needs.
pub fn lf_info(&self) -> LfInfo<'_> {
LfInfo {
mi_cols: self.g.mi_cols,
mi_rows: self.g.mi_rows,
width: self.g.width,
height: self.g.height,
mi_size: &self.mi.mi_size,
skip: &self.mi.skip,
is_inter: &self.mi.is_inter,
tx: [&self.lf_tx[0], &self.lf_tx[1], &self.lf_tx[2]],
}
}
/// Start of a superblock row (clear_left_context).
pub fn start_sb_row(&mut self) {
for p in 0..3 {
self.left_level[p].iter_mut().for_each(|v| *v = 0);
self.left_dc[p].iter_mut().for_each(|v| *v = 0);
}
}
pub fn superblock(&mut self, r: usize, c: usize, node: &Node) {
self.partition(r, c, BLOCK_64X64, node);
}
fn partition(&mut self, r: usize, c: usize, bsize: usize, node: &Node) {
let (mi_rows, mi_cols) = (self.g.mi_rows, self.g.mi_cols);
if r >= mi_rows || c >= mi_cols {
return;
}
let avail_u = r > 0;
let avail_l = c > 0;
let n4 = NUM_4X4_BLOCKS_WIDE[bsize] as usize;
let half = n4 >> 1;
let has_rows = r + half < mi_rows;
let has_cols = c + half < mi_cols;
let split = matches!(node, Node::Split(_));
let bsl = MI_WIDTH_LOG2[bsize] as usize;
let above = avail_u && (MI_WIDTH_LOG2[self.mi.mi_size[self.mi.idx(r - 1, c)] as usize] as usize) < bsl;
let left = avail_l && (MI_HEIGHT_LOG2[self.mi.mi_size[self.mi.idx(r, c - 1)] as usize] as usize) < bsl;
let ctx = left as usize * 2 + above as usize;
let partition = if split { PARTITION_SPLIT } else { PARTITION_NONE };
if has_rows && has_cols {
let cdf: &mut [u16] = match bsl {
1 => &mut self.cdf.partition_w8[ctx],
2 => &mut self.cdf.partition_w16[ctx],
3 => &mut self.cdf.partition_w32[ctx],
_ => &mut self.cdf.partition_w64[ctx],
};
self.sw.symbol(partition, cdf);
} else if has_cols || has_rows {
assert!(split, "blocks crossing the frame edge are split");
let pcdf: Vec<u16> = match bsl {
2 => self.cdf.partition_w16[ctx].to_vec(),
3 => self.cdf.partition_w32[ctx].to_vec(),
_ => self.cdf.partition_w64[ctx].to_vec(),
};
let pr = |k: usize| pcdf[k] as i32 - if k == 0 { 0 } else { pcdf[k - 1] as i32 };
let psum = if has_cols {
// split_or_horz
pr(PARTITION_VERT) + pr(PARTITION_SPLIT) + pr(PARTITION_HORZ_A) + pr(PARTITION_VERT_A) + pr(PARTITION_VERT_B) + pr(PARTITION_VERT_4)
} else {
// split_or_vert
pr(PARTITION_HORZ) + pr(PARTITION_SPLIT) + pr(PARTITION_HORZ_A) + pr(PARTITION_HORZ_B) + pr(PARTITION_VERT_A) + pr(PARTITION_HORZ_4)
};
let cdf = [((1i32 << 15) - psum) as u16, 1 << 15, 0];
self.sw.symbol_fixed(1, &cdf);
} else {
assert!(split);
}
match node {
Node::Split(ch) => {
let sub = PARTITION_SUBSIZE[PARTITION_SPLIT][bsize] as usize;
self.partition(r, c, sub, &ch[0]);
self.partition(r, c + half, sub, &ch[1]);
self.partition(r + half, c, sub, &ch[2]);
self.partition(r + half, c + half, sub, &ch[3]);
}
Node::Leaf(leaf) => {
debug_assert_eq!(leaf.bsize, bsize);
self.block(r, c, leaf);
}
// Blocks inside the frame are always decided; nothing is coded outside it.
Node::Out => debug_assert!(false, "undecided block inside the frame"),
}
}
fn block(&mut self, r: usize, c: usize, b: &Leaf) {
self.r = r;
self.c = c;
self.bsize = b.bsize;
self.avail_u = r > 0;
self.avail_l = c > 0;
let bsize = b.bsize;
let n4 = NUM_4X4_BLOCKS_WIDE[bsize] as usize;
// skip
let mut ctx = 0;
if self.avail_u {
ctx += self.mi.skip[self.mi.idx(r - 1, c)] as usize;
}
if self.avail_l {
ctx += self.mi.skip[self.mi.idx(r, c - 1)] as usize;
}
let mut y_mode = b.y_mode;
if self.intra_only {
self.sw.symbol(b.skip as usize, &mut self.cdf.skip[ctx]);
let above = if self.avail_u { self.mi.y_mode[self.mi.idx(r - 1, c)] as usize } else { DC_PRED };
let left = if self.avail_l { self.mi.y_mode[self.mi.idx(r, c - 1)] as usize } else { DC_PRED };
let actx = INTRA_MODE_CONTEXT[above] as usize;
let lctx = INTRA_MODE_CONTEXT[left] as usize;
self.sw.symbol(b.y_mode, &mut self.kf_y_mode[actx][lctx]);
self.intra_angle_and_uv(b);
} else {
self.sw.symbol(b.skip as usize, &mut self.cdf.skip[ctx]);
// is_inter
let left = if self.avail_l { self.mi.ref_frame[self.mi.idx(r, c - 1)] } else { [INTRA_FRAME as i8, -1] };
let above = if self.avail_u { self.mi.ref_frame[self.mi.idx(r - 1, c)] } else { [INTRA_FRAME as i8, -1] };
let left_intra = left[0] <= INTRA_FRAME as i8;
let above_intra = above[0] <= INTRA_FRAME as i8;
let ctx = if self.avail_u && self.avail_l {
if left_intra && above_intra { 3 } else { (left_intra || above_intra) as usize }
} else if self.avail_u || self.avail_l {
2 * (if self.avail_u { above_intra } else { left_intra }) as usize
} else {
0
};
self.sw.symbol(b.is_inter as usize, &mut self.cdf.is_inter[ctx]);
if b.is_inter {
y_mode = self.inter_block_mode_info(b, left, above);
} else {
let ctx = SIZE_GROUP[bsize] as usize;
self.sw.symbol(b.y_mode, &mut self.cdf.y_mode[ctx]);
self.intra_angle_and_uv(b);
}
}
// mode info stored before the residual
let (rows, cols) = (n4.min(self.g.mi_rows - r), n4.min(self.g.mi_cols - c));
for y in 0..rows {
for x in 0..cols {
let i = self.mi.idx(r + y, c + x);
self.mi.y_mode[i] = y_mode as u8;
self.mi.ref_frame[i] = if b.is_inter { [LAST_FRAME as i8, -1] } else { [INTRA_FRAME as i8, -1] };
self.mi.written[i] = true;
if b.is_inter {
self.mi.mv[i] = b.mv;
}
}
}
if b.skip {
// reset_block_context( )
for plane in 0..3 {
let s = (plane > 0) as usize;
for i in (c >> s)..((c + n4) >> s) {
self.above_level[plane][i] = 0;
self.above_dc[plane][i] = 0;
}
for i in (r >> s)..((r + n4) >> s) {
self.left_level[plane][i] = 0;
self.left_dc[plane][i] = 0;
}
}
} else {
let log2 = (n4 * 4).trailing_zeros();
assert!(log2 <= 5, "64x64 blocks are coded as skip blocks");
for plane in 0..3 {
let s = (plane > 0) as u32;
self.coeffs(plane, (c * 4) >> s, (r * 4) >> s, tx_size_for(log2 - s), b, &b.coefs[plane]);
}
}
for y in 0..rows {
for x in 0..cols {
let i = self.mi.idx(r + y, c + x);
self.mi.is_inter[i] = b.is_inter;
self.mi.skip[i] = b.skip;
self.mi.mi_size[i] = bsize as u8;
}
}
// LoopfilterTxSizes: one transform block per plane
let log2 = (n4 * 4).trailing_zeros();
let stride = self.g.mi_cols + 32;
for plane in 0..3 {
let s = (plane > 0) as usize;
let tx = tx_size_for(log2 - s as u32) as u8;
let (r0, c0, m) = (r >> s, c >> s, n4 >> s);
for y in r0..r0 + m {
self.lf_tx[plane][y * stride + c0..y * stride + c0 + m].iter_mut().for_each(|v| *v = tx);
}
}
}
fn intra_angle_and_uv(&mut self, b: &Leaf) {
if self.bsize >= BLOCK_8X8 && is_directional(b.y_mode) {
self.sw.symbol(MAX_ANGLE_DELTA, &mut self.cdf.angle_delta[b.y_mode - V_PRED]);
}
let bs = block_size_px(self.bsize);
if bs <= 32 {
self.sw.symbol(b.uv_mode, &mut self.cdf.uv_mode_cfl_allowed[b.y_mode]);
} else {
self.sw.symbol(b.uv_mode, &mut self.cdf.uv_mode_cfl_not_allowed[b.y_mode]);
}
if self.bsize >= BLOCK_8X8 && is_directional(b.uv_mode) {
self.sw.symbol(MAX_ANGLE_DELTA, &mut self.cdf.angle_delta[b.uv_mode - V_PRED]);
}
}
/// read_ref_frames (LAST_FRAME), find_mv_stack, the inter mode, DRL and motion vector.
/// Returns the inter mode (YMode).
fn inter_block_mode_info(&mut self, b: &Leaf, left: [i8; 2], above: [i8; 2]) -> usize {
let count = |ft: i8| -> usize {
let mut n = 0;
if self.avail_u {
n += (above[0] == ft) as usize + (above[1] == ft) as usize;
}
if self.avail_l {
n += (left[0] == ft) as usize + (left[1] == ft) as usize;
}
n
};
let cmp = |a: usize, b: usize| match a.cmp(&b) {
std::cmp::Ordering::Less => 0,
std::cmp::Ordering::Equal => 1,
std::cmp::Ordering::Greater => 2,
};
let (last, last2, last3, gold) = (count(LAST_FRAME as i8), count(LAST2_FRAME as i8), count(LAST3_FRAME as i8), count(GOLDEN_FRAME as i8));
let (bwd, alt2, alt) = (count(BWDREF_FRAME as i8), count(ALTREF2_FRAME as i8), count(ALTREF_FRAME as i8));
let ctx_p1 = cmp(last + last2 + last3 + gold, bwd + alt2 + alt);
let ctx_comp_ref = cmp(last + last2, last3 + gold);
let ctx_comp_ref_p1 = cmp(last, last2);
self.sw.symbol(0, &mut self.cdf.single_ref[ctx_p1][0]);
self.sw.symbol(0, &mut self.cdf.single_ref[ctx_comp_ref][2]);
self.sw.symbol(0, &mut self.cdf.single_ref[ctx_comp_ref_p1][3]);
self.find_mv_stack();
let mv = b.mv;
let s = &self.mvs;
let mode = if mv == [0, 0] {
GLOBALMV
} else if mv == s.ref_stack_mv[0] {
NEARESTMV
} else if mv == s.ref_stack_mv[1] {
NEARMV
} else {
NEWMV
};
let (nctx, zctx, rctx) = (s.new_mv_context, s.zero_mv_context, s.ref_mv_context);
self.sw.symbol((mode != NEWMV) as usize, &mut self.cdf.new_mv[nctx]);
if mode != NEWMV {
self.sw.symbol((mode != GLOBALMV) as usize, &mut self.cdf.zero_mv[zctx]);
if mode != GLOBALMV {
self.sw.symbol((mode == NEARMV) as usize, &mut self.cdf.ref_mv[rctx]);
}
}
// DRL: ref_mv_idx 0 for NEWMV, 1 for NEARMV
if mode == NEWMV {
if self.mvs.num_mv_found > 1 {
let ctx = self.mvs.drl_ctx_stack[0];
self.sw.symbol(0, &mut self.cdf.drl_mode[ctx]);
}
} else if mode == NEARMV && self.mvs.num_mv_found > 2 {
let ctx = self.mvs.drl_ctx_stack[1];
self.sw.symbol(0, &mut self.cdf.drl_mode[ctx]);
}
if mode == NEWMV {
let pred = self.mvs.ref_stack_mv[0];
self.write_mv([mv[0] - pred[0], mv[1] - pred[1]]);
}
mode
}
fn write_mv(&mut self, diff: MvP) {
let joint = ((diff[0] != 0) as usize) << 1 | (diff[1] != 0) as usize;
// MV_JOINT_ZERO, MV_JOINT_HNZVZ (col only), MV_JOINT_HZVNZ (row only), MV_JOINT_HNZVNZ
self.sw.symbol(joint, &mut self.cdf.mv_joint);
for (comp, &d) in diff.iter().enumerate() {
if d != 0 {
self.write_mv_component(comp, d);
}
}
}
fn write_mv_component(&mut self, comp: usize, v: i32) {
debug_assert!(v % 2 == 0, "quarter-sample precision");
self.sw.symbol((v < 0) as usize, &mut self.cdf.mv_sign[comp]);
let m = v.unsigned_abs() - 1; // odd: the implied hp bit is 1
let fr = ((m >> 1) & 3) as usize;
let int = m >> 3;
if int < CLASS0_SIZE as u32 {
self.sw.symbol(MV_CLASS_0, &mut self.cdf.mv_class[comp]);
self.sw.symbol(int as usize, &mut self.cdf.mv_class0_bit[comp]);
self.sw.symbol(fr, &mut self.cdf.mv_class0_fr[comp][int as usize]);
} else {
let class = 31 - int.leading_zeros();
let d = int - (1 << class);
self.sw.symbol(class as usize, &mut self.cdf.mv_class[comp]);
for i in 0..class as usize {
self.sw.symbol(((d >> i) & 1) as usize, &mut self.cdf.mv_bit[comp][i]);
}
self.sw.symbol(fr, &mut self.cdf.mv_fr[comp]);
}
}
// ---- coefficients ----------------------------------------------------------------------
fn coeffs(&mut self, plane: usize, start_x: usize, start_y: usize, tx_sz: usize, b: &Leaf, levels: &[i32]) {
let x4 = start_x >> 2;
let y4 = start_y >> 2;
let w4 = TX_WIDTH[tx_sz] as usize >> 2;
let h4 = TX_HEIGHT[tx_sz] as usize >> 2;
let tx_sz_ctx = (TX_SIZE_SQR[tx_sz] as usize + TX_SIZE_SQR_UP[tx_sz] as usize + 1) >> 1;
let ptype = (plane > 0) as usize;
let scan = scan_for(tx_sz);
let mut eob = 0;
for (i, &pos) in scan.iter().enumerate() {
if levels[pos as usize] != 0 {
eob = i + 1;
}
}
let ctx = self.all_zero_ctx(plane, tx_sz, x4, y4, w4, h4);
self.sw.symbol((eob == 0) as usize, &mut self.cdf.txb_skip[tx_sz_ctx][ctx]);
let mut cul_level = 0u32;
let mut dc_category = 0u8;
if eob == 0 {
if plane == 0 {
self.set_tx_types(x4, y4, tx_sz, DCT_DCT);
}
} else {
if plane == 0 {
// transform_type( )
let set = tx_set(tx_sz, b.is_inter);
let t = b.tx_type_y;
if set > 0 && self.qidx > 0 {
let sqr = TX_SIZE_SQR[tx_sz] as usize;
if b.is_inter {
// The decision only picks types of this set (DCT_DCT otherwise).
let s = TX_TYPE_INTER_INV_SET3.iter().position(|&v| v as usize == t).unwrap_or_default();
self.sw.symbol(s, &mut self.cdf.inter_tx_type_set3[sqr]);
} else {
let s = TX_TYPE_INTRA_INV_SET2.iter().position(|&v| v as usize == t).unwrap_or_default();
self.sw.symbol(s, &mut self.cdf.intra_tx_type_set2[sqr][b.y_mode]);
}
} else {
assert_eq!(t, DCT_DCT);
}
self.set_tx_types(x4, y4, tx_sz, t);
}
let plane_tx_type = self.compute_tx_type(plane, tx_sz, x4, y4, b);
debug_assert!(plane_tx_type == DCT_DCT || matches!(plane_tx_type, ADST_DCT | DCT_ADST | ADST_ADST));
let eob_multisize = (TX_WIDTH_LOG2[tx_sz] as usize).min(5) + (TX_HEIGHT_LOG2[tx_sz] as usize).min(5) - 4;
let eob_pt = if eob < 2 { eob } else { (32 - ((eob - 1) as u32).leading_zeros()) as usize + 1 };
let ectx = 0; // TX_CLASS_2D
let s = eob_pt - 1;
match eob_multisize {
0 => self.sw.symbol(s, &mut self.cdf.eob_pt_16[ptype][ectx]),
1 => self.sw.symbol(s, &mut self.cdf.eob_pt_32[ptype][ectx]),
2 => self.sw.symbol(s, &mut self.cdf.eob_pt_64[ptype][ectx]),
3 => self.sw.symbol(s, &mut self.cdf.eob_pt_128[ptype][ectx]),
4 => self.sw.symbol(s, &mut self.cdf.eob_pt_256[ptype][ectx]),
5 => self.sw.symbol(s, &mut self.cdf.eob_pt_512[ptype]),
_ => self.sw.symbol(s, &mut self.cdf.eob_pt_1024[ptype]),
}
if eob_pt >= 3 {
let eob_shift = eob_pt - 3;
let offset = eob - ((1 << (eob_pt - 2)) + 1);
self.sw.symbol((offset >> eob_shift) & 1, &mut self.cdf.eob_extra[tx_sz_ctx][ptype][eob_pt - 3]);
for i in 1..eob_pt - 2 {
let sh = eob_pt - 2 - 1 - i;
self.sw.bool((offset >> sh) & 1 == 1);
}
}
let bwl = TX_WIDTH_LOG2[tx_sz] as usize;
let height = TX_HEIGHT[tx_sz] as usize;
let width = 1usize << bwl;
// Quant[] as the decoder sees it while parsing: the levels coded so far (capped)
let mut quant = vec![0i32; width * height];
for c in (0..eob).rev() {
let pos = scan[c] as usize;
let a = levels[pos].unsigned_abs().min(15) as i32;
if c == eob - 1 {
let ctx = coeff_base_eob_ctx(c, bwl, height);
self.sw.symbol((a.min(3) - 1) as usize, &mut self.cdf.coeff_base_eob[tx_sz_ctx][ptype][ctx]);
} else {
let ctx = coeff_base_ctx(&quant, tx_sz, bwl, width, height, pos);
self.sw.symbol(a.min(3) as usize, &mut self.cdf.coeff_base[tx_sz_ctx][ptype][ctx]);
}
if a > NUM_BASE_LEVELS as i32 {
let ctx = coeff_br_ctx(&quant, bwl, height, pos);
let mut rem = a - 3;
for _ in 0..(COEFF_BASE_RANGE / (BR_CDF_SIZE - 1)) {
let br = rem.min(3);
self.sw.symbol(br as usize, &mut self.cdf.coeff_br[tx_sz_ctx.min(TX_32X32)][ptype][ctx]);
rem -= br;
if br < 3 {
break;
}
}
}
quant[pos] = a;
}
for c in 0..eob {
let pos = scan[c] as usize;
let v = levels[pos];
if v != 0 {
if c == 0 {
let ctx = self.dc_sign_ctx(plane, x4, y4, w4, h4);
self.sw.symbol((v < 0) as usize, &mut self.cdf.dc_sign[ptype][ctx]);
} else {
self.sw.bool(v < 0);
}
}
let a = v.unsigned_abs();
if a > (NUM_BASE_LEVELS + COEFF_BASE_RANGE) as u32 {
// Golomb code of a - 15
let x = a - 15 + 1;
let length = 32 - x.leading_zeros();
for _ in 0..length - 1 {
self.sw.bool(false);
}
self.sw.bool(true);
for i in (0..length - 1).rev() {
self.sw.bool((x >> i) & 1 == 1);
}
}
if pos == 0 && a > 0 {
dc_category = if v < 0 { 1 } else { 2 };
}
cul_level += a & 0xFFFFF;
}
cul_level = cul_level.min(63);
}
for i in 0..w4 {
if x4 + i < self.above_level[plane].len() {
self.above_level[plane][x4 + i] = cul_level as u8;
self.above_dc[plane][x4 + i] = dc_category;
}
}
for i in 0..h4 {
if y4 + i < self.left_level[plane].len() {
self.left_level[plane][y4 + i] = cul_level as u8;
self.left_dc[plane][y4 + i] = dc_category;
}
}
}
fn set_tx_types(&mut self, x4: usize, y4: usize, tx_sz: usize, t: usize) {
for i in 0..(TX_WIDTH[tx_sz] as usize >> 2) {
for j in 0..(TX_HEIGHT[tx_sz] as usize >> 2) {
if y4 + j < self.g.mi_rows && x4 + i < self.g.mi_cols {
let k = self.mi.idx(y4 + j, x4 + i);
self.mi.tx_type[k] = t as u8;
}
}
}
}
fn compute_tx_type(&self, plane: usize, tx_sz: usize, x4: usize, y4: usize, b: &Leaf) -> usize {
if TX_SIZE_SQR_UP[tx_sz] as usize > TX_32X32 {
return DCT_DCT;
}
if plane == 0 {
return self.mi.tx_type[self.mi.idx(y4, x4)] as usize;
}
if b.is_inter {
let set = tx_set(tx_sz, true);
let xx = self.c.max(x4 << 1);
let yy = self.r.max(y4 << 1);
let t = self.mi.tx_type[self.mi.idx(yy, xx)] as usize;
return if tx_in_set(set, true, t) { t } else { DCT_DCT };
}
intra_uv_tx_type(b.uv_mode, tx_sz)
}
fn all_zero_ctx(&self, plane: usize, tx_sz: usize, x4: usize, y4: usize, w4: usize, h4: usize) -> usize {
let s = (plane > 0) as usize;
let max_x4 = self.g.mi_cols >> s;
let max_y4 = self.g.mi_rows >> s;
let w = TX_WIDTH[tx_sz] as usize;
let h = TX_HEIGHT[tx_sz] as usize;
let bsize = SUBSAMPLED_SIZE[self.bsize][s][s] as usize;
let bw = block_size_px(bsize);
let bh = 4 * NUM_4X4_BLOCKS_HIGH[bsize] as usize;
let al = &self.above_level[plane];
let ll = &self.left_level[plane];
if plane == 0 {
let mut top = 0u32;
let mut left = 0u32;
for k in 0..w4 {
if x4 + k < max_x4 {
top = top.max(al[x4 + k] as u32);
}
}
for k in 0..h4 {
if y4 + k < max_y4 {
left = left.max(ll[y4 + k] as u32);
}
}
top = top.min(255);
left = left.min(255);
if bw == w && bh == h {
0
} else if top == 0 && left == 0 {
1
} else if top == 0 || left == 0 {
2 + (top.max(left) > 3) as usize
} else if top.max(left) <= 3 {
4
} else if top.min(left) <= 3 {
5
} else {
6
}
} else {
let ad = &self.above_dc[plane];
let ld = &self.left_dc[plane];
let mut above = 0u8;
let mut left = 0u8;
for i in 0..w4 {
if x4 + i < max_x4 {
above |= al[x4 + i] | ad[x4 + i];
}
}
for i in 0..h4 {
if y4 + i < max_y4 {
left |= ll[y4 + i] | ld[y4 + i];
}
}
let mut ctx = (above != 0) as usize + (left != 0) as usize + 7;
if bw * bh > w * h {
ctx += 3;
}
ctx
}
}
fn dc_sign_ctx(&self, plane: usize, x4: usize, y4: usize, w4: usize, h4: usize) -> usize {
let s = (plane > 0) as usize;
let max_x4 = self.g.mi_cols >> s;
let max_y4 = self.g.mi_rows >> s;
let mut dc_sign = 0i32;
for k in 0..w4 {
if x4 + k < max_x4 {
match self.above_dc[plane][x4 + k] {
1 => dc_sign -= 1,
2 => dc_sign += 1,
_ => {}
}
}
}
for k in 0..h4 {
if y4 + k < max_y4 {
match self.left_dc[plane][y4 + k] {
1 => dc_sign -= 1,
2 => dc_sign += 1,
_ => {}
}
}
}
match dc_sign.signum() {
-1 => 1,
1 => 2,
_ => 0,
}
}
// ---- motion vector prediction (7.10.2), single reference LAST_FRAME --------------------
fn inside(&self, r: isize, c: isize) -> bool {
c >= 0 && c < self.g.mi_cols as isize && r >= 0 && r < self.g.mi_rows as isize
}
fn find_mv_stack(&mut self) {
let n4 = NUM_4X4_BLOCKS_WIDE[self.bsize] as isize;
let (bw4, bh4) = (n4, n4);
self.mvs = MvStack::default();
self.scan_row(-1);
let mut found_above = self.mvs.found_match;
self.mvs.found_match = false;
self.scan_col(-1);
let mut found_left = self.mvs.found_match;
self.mvs.found_match = false;
if bw4.max(bh4) <= 16 {
self.scan_point(-1, bw4);
}
if self.mvs.found_match {
found_above = true;
}
self.mvs.close_matches = found_above as usize + found_left as usize;
let num_nearest = self.mvs.num_mv_found;
let num_new = self.mvs.new_mv_count;
for idx in 0..num_nearest {
self.mvs.weight_stack[idx] += REF_CAT_LEVEL as u32;
}
self.mvs.zero_mv_context = 0;
// use_ref_frame_mvs = 0: no temporal candidates
self.scan_point(-1, -1);
if self.mvs.found_match {
found_above = true;
}
self.mvs.found_match = false;
self.scan_row(-3);
if self.mvs.found_match {
found_above = true;
}
self.mvs.found_match = false;
self.scan_col(-3);
if self.mvs.found_match {
found_left = true;
}
self.mvs.found_match = false;
if bh4 > 1 {
self.scan_row(-5);
}
if self.mvs.found_match {
found_above = true;
}
self.mvs.found_match = false;
if bw4 > 1 {
self.scan_col(-5);
}
if self.mvs.found_match {
found_left = true;
}
self.mvs.total_matches = found_above as usize + found_left as usize;
let n = self.mvs.num_mv_found;
self.sort_stack(0, num_nearest);
self.sort_stack(num_nearest, n);
if self.mvs.num_mv_found < 2 {
self.extra_search();
}
self.context_and_clamping(num_new);
}
fn scan_row(&mut self, delta_row: isize) {
let bw4 = NUM_4X4_BLOCKS_WIDE[self.bsize] as isize;
let (mi_row, mi_col) = (self.r as isize, self.c as isize);
let end4 = bw4.min(self.g.mi_cols as isize - mi_col).min(16);
let mut delta_col = 0;
let use_step16 = bw4 >= 16;
let mut delta_row = delta_row;
if delta_row.abs() > 1 {
delta_row += mi_row & 1;
delta_col = 1 - (mi_col & 1);
}
let mut i = 0;
while i < end4 {
let mv_row = mi_row + delta_row;
let mv_col = mi_col + delta_col + i;
if !self.inside(mv_row, mv_col) {
break;
}
let k = self.mi.idx(mv_row as usize, mv_col as usize);
let mut len = bw4.min(NUM_4X4_BLOCKS_WIDE[self.mi.mi_size[k] as usize] as isize);
if delta_row.abs() > 1 {
len = len.max(2);
}
if use_step16 {
len = len.max(4);
}
self.add_ref_mv_candidate(mv_row as usize, mv_col as usize, len as u32 * 2);
i += len;
}
}
fn scan_col(&mut self, delta_col: isize) {
let bh4 = NUM_4X4_BLOCKS_HIGH[self.bsize] as isize;
let (mi_row, mi_col) = (self.r as isize, self.c as isize);
let end4 = bh4.min(self.g.mi_rows as isize - mi_row).min(16);
let mut delta_row = 0;
let use_step16 = bh4 >= 16;
let mut delta_col = delta_col;
if delta_col.abs() > 1 {
delta_row = 1 - (mi_row & 1);
delta_col += mi_col & 1;
}
let mut i = 0;
while i < end4 {
let mv_row = mi_row + delta_row + i;
let mv_col = mi_col + delta_col;
if !self.inside(mv_row, mv_col) {
break;
}
let k = self.mi.idx(mv_row as usize, mv_col as usize);
let mut len = bh4.min(NUM_4X4_BLOCKS_HIGH[self.mi.mi_size[k] as usize] as isize);
if delta_col.abs() > 1 {
len = len.max(2);
}
if use_step16 {
len = len.max(4);
}
self.add_ref_mv_candidate(mv_row as usize, mv_col as usize, len as u32 * 2);
i += len;
}
}
fn scan_point(&mut self, delta_row: isize, delta_col: isize) {
let mv_row = self.r as isize + delta_row;
let mv_col = self.c as isize + delta_col;
if self.inside(mv_row, mv_col) && self.mi.written[self.mi.idx(mv_row as usize, mv_col as usize)] {
self.add_ref_mv_candidate(mv_row as usize, mv_col as usize, 4);
}
}
fn add_ref_mv_candidate(&mut self, mv_row: usize, mv_col: usize, weight: u32) {
let i = self.mi.idx(mv_row, mv_col);
if !self.mi.is_inter[i] {
return;
}
let rf = self.mi.ref_frame[i];
for cand_list in 0..2 {
if rf[cand_list] == LAST_FRAME as i8 {
self.search_stack(i, cand_list, weight);
}
}
}
fn search_stack(&mut self, i: usize, cand_list: usize, weight: u32) {
debug_assert_eq!(cand_list, 0);
let cand_mode = self.mi.y_mode[i] as usize;
// gm_type is IDENTITY: the stored motion vector is used
let mut cand = self.mi.mv[i];
lower_mv_precision(&mut cand);
if cand_mode == NEWMV {
self.mvs.new_mv_count += 1;
}
self.mvs.found_match = true;
let n = self.mvs.num_mv_found;
if let Some(idx) = (0..n).find(|&k| self.mvs.ref_stack_mv[k] == cand) {
self.mvs.weight_stack[idx] += weight;
} else if n < MAX_REF_MV_STACK_SIZE {
self.mvs.ref_stack_mv[n] = cand;
self.mvs.weight_stack[n] = weight;
self.mvs.num_mv_found += 1;
}
}
fn sort_stack(&mut self, start: usize, end: usize) {
let mut end = end;
let s = &mut self.mvs;
while end > start {
let mut new_end = start;
for idx in start + 1..end {
if s.weight_stack[idx - 1] < s.weight_stack[idx] {
s.weight_stack.swap(idx - 1, idx);
s.ref_stack_mv.swap(idx - 1, idx);
new_end = idx;
}
}
end = new_end;
}
}
fn extra_search(&mut self) {
let (mi_row, mi_col) = (self.r as isize, self.c as isize);
let n4 = NUM_4X4_BLOCKS_WIDE[self.bsize] as isize;
let w4 = n4.min(16).min(self.g.mi_cols as isize - mi_col);
let h4 = n4.min(16).min(self.g.mi_rows as isize - mi_row);
let num4x4 = w4.min(h4);
for pass in 0..2 {
let mut idx = 0;
while idx < num4x4 && self.mvs.num_mv_found < 2 {
let (mv_row, mv_col) = if pass == 0 { (mi_row - 1, mi_col + idx) } else { (mi_row + idx, mi_col - 1) };
if !self.inside(mv_row, mv_col) {
break;
}
let k = self.mi.idx(mv_row as usize, mv_col as usize);
// add_extra_mv_candidate( ) (no sign bias: a single forward reference)
let rfs = self.mi.ref_frame[k];
for &cand_ref in rfs.iter().take(2) {
if cand_ref > INTRA_FRAME as i8 {
let cand = self.mi.mv[k];
let n = self.mvs.num_mv_found;
if !(0..n).any(|j| self.mvs.ref_stack_mv[j] == cand) {
self.mvs.ref_stack_mv[n] = cand;
self.mvs.weight_stack[n] = 2;
self.mvs.num_mv_found += 1;
}
}
}
let sz = self.mi.mi_size[k] as usize;
idx += if pass == 0 { NUM_4X4_BLOCKS_WIDE[sz] } else { NUM_4X4_BLOCKS_HIGH[sz] } as isize;
}
}
for idx in self.mvs.num_mv_found..2 {
self.mvs.ref_stack_mv[idx] = [0, 0]; // GlobalMvs[ 0 ]
}
}
fn context_and_clamping(&mut self, num_new: usize) {
let bs = block_size_px(self.bsize) as i32;
let n = self.mvs.num_mv_found;
for idx in 0..n {
let mut z = 0;
if idx + 1 < n {
let w0 = self.mvs.weight_stack[idx];
let w1 = self.mvs.weight_stack[idx + 1];
if w0 >= REF_CAT_LEVEL as u32 {
if w1 < REF_CAT_LEVEL as u32 {
z = 1;
}
} else {
z = 2;
}
}
self.mvs.drl_ctx_stack[idx] = z;
}
let n4 = NUM_4X4_BLOCKS_WIDE[self.bsize] as i32;
for idx in 0..n {
let mut mv = self.mvs.ref_stack_mv[idx];
let border_r = MV_BORDER as i32 + bs * 8;
let to_top = -((self.r as i32 * 4) * 8);
let to_bottom = ((self.g.mi_rows as i32 - n4 - self.r as i32) * 4) * 8;
mv[0] = mv[0].clamp(to_top - border_r, to_bottom + border_r);
let to_left = -((self.c as i32 * 4) * 8);
let to_right = ((self.g.mi_cols as i32 - n4 - self.c as i32) * 4) * 8;
mv[1] = mv[1].clamp(to_left - border_r, to_right + border_r);
self.mvs.ref_stack_mv[idx] = mv;
}
let (close, total) = (self.mvs.close_matches, self.mvs.total_matches);
if close == 0 {
self.mvs.new_mv_context = total.min(1);
self.mvs.ref_mv_context = total;
} else if close == 1 {
self.mvs.new_mv_context = 3 - num_new.min(1);
self.mvs.ref_mv_context = 2 + total;
} else {
self.mvs.new_mv_context = 5 - num_new.min(1);
self.mvs.ref_mv_context = 5;
}
}
}
/// lower_mv_precision( ) with allow_high_precision_mv = 0 and force_integer_mv = 0.
fn lower_mv_precision(mv: &mut MvP) {
for c in mv.iter_mut() {
if *c & 1 != 0 {
*c += if *c > 0 { -1 } else { 1 };
}
}
}
fn coeff_base_eob_ctx(c: usize, bwl: usize, height: usize) -> usize {
if c == 0 {
0
} else if c <= (height << bwl) / 8 {
1
} else if c <= (height << bwl) / 4 {
2
} else {
3
}
}
/// Context of coeff_base for TX_CLASS_2D (8.3.2).
fn coeff_base_ctx(quant: &[i32], tx_sz: usize, bwl: usize, width: usize, height: usize, pos: usize) -> usize {
let row = pos >> bwl;
let col = pos - (row << bwl);
let mut mag = 0i32;
for off in SIG_REF_DIFF_OFFSET[TX_CLASS_2D].iter() {
let ref_row = row + off[0] as usize;
let ref_col = col + off[1] as usize;
if ref_row < height && ref_col < width {
mag += quant[(ref_row << bwl) + ref_col].abs().min(3);
}
}
let ctx = ((mag + 1) >> 1).min(4) as usize;
if row == 0 && col == 0 {
return 0;
}
ctx + COEFF_BASE_CTX_OFFSET[tx_sz][row.min(4)][col.min(4)] as usize
}
/// Context of coeff_br for TX_CLASS_2D.
fn coeff_br_ctx(quant: &[i32], bwl: usize, txh: usize, pos: usize) -> usize {
let txw = 1usize << bwl;
let row = pos >> bwl;
let col = pos - (row << bwl);
let mut mag = 0i32;
for off in MAG_REF_OFFSET_WITH_TX_CLASS[TX_CLASS_2D].iter() {
let ref_row = row + off[0] as usize;
let ref_col = col + off[1] as usize;
if ref_row < txh && ref_col < txw {
mag += quant[ref_row * txw + ref_col].min((COEFF_BASE_RANGE + NUM_BASE_LEVELS + 1) as i32);
}
}
let mag = ((mag + 1) >> 1).min(6) as usize;
if pos == 0 {
mag
} else if row < 2 && col < 2 {
mag + 7
} else {
mag + 14
}
}