thread_local! {
pub static COEF_DBG: std::cell::Cell<bool> = std::cell::Cell::new(false);
}
use crate::cdf_av2::CdfCoefContext;
use crate::msac::{
rav1d_msac_decode_bool_adapt, rav1d_msac_decode_bools, rav1d_msac_decode_bools_bypass,
rav1d_msac_decode_symbol_adapt4, rav1d_msac_decode_symbol_adapt8,
rav1d_msac_decode_unary_bypass, MsacContext,
};
fn decode_exp_golomb(msac: &mut MsacContext, k: u32) -> u32 {
let length = rav1d_msac_decode_unary_bypass(msac, 21) + k;
let x = (1u32 << length) + rav1d_msac_decode_bools_bypass(msac, length as u8);
x - (1 << k)
}
fn decode_hr(msac: &mut MsacContext, hr_avg: i32) -> i32 {
let m = 31 - (hr_avg.clamp(2, 64) as u32).leading_zeros(); let cmax = (m + 4).min(6); let q = rav1d_msac_decode_unary_bypass(msac, cmax);
let rem = if q == cmax {
decode_exp_golomb(msac, m + 1)
} else {
rav1d_msac_decode_bools_bypass(msac, m as u8)
};
(rem + (q << m)) as i32
}
#[inline]
fn idtx_lo_ctx(levels: &[i8], idx: usize, stride: usize) -> (usize, usize) {
let mut lo_mag = 0u32;
let mut hi_mag = 0u32;
for off in [idx - 1, idx - stride] {
if !crate::av2_recon::work_tick("coef:55") { break; }
let val = levels[off] as i32 as u32;
lo_mag += val.min(3);
hi_mag += val.min(5);
}
(lo_mag as usize, hi_mag.min(6) as usize)
}
#[inline]
fn idtx_sign_ctx(levels: &[i8], idx: usize, stride: usize) -> usize {
let sum = levels[idx - 1] as i32 + levels[idx - stride] as i32 + levels[idx - stride - 1] as i32;
let offset = if levels[idx] > 3 { 2 } else { 0 };
match sum {
-3 => offset + 6,
-2 | -1 => offset + 2,
0 => 0,
1 | 2 => offset + 1,
3 => offset + 5,
_ => unreachable!("idtx sign sum out of range: {sum}"),
}
}
#[inline]
pub fn get_lo_ctx_2d_luma(levels: &[i8], idx: usize, stride: usize, xy: usize) -> (usize, usize) {
let lo_freq = xy < 4; let mut lim: i32 = if lo_freq { 5 } else { 3 };
let mut lo_mag = 0i32;
let mut hi_mag = 0i32;
for off in [idx + 1, idx + stride, idx + stride + 1] {
if !crate::av2_recon::work_tick("coef:92") { break; }
let v = levels[off] as i32;
lo_mag += v.min(lim);
hi_mag += v.min(5);
}
lo_mag += (levels[idx + 2] as i32).min(lim) + (levels[idx + 2 * stride] as i32).min(lim);
let offset = if lo_freq {
lim = if xy == 0 { 8 } else if xy < 2 { 6 } else { 4 };
if xy == 0 { 0 } else if xy < 2 { 9 } else { 16 }
} else {
lim = 4;
if xy < 6 { 0 } else if xy < 8 { 5 } else { 10 }
};
let hi_off = if lo_freq && xy > 0 { 7 } else { 0 };
let lo_ctx = offset + ((lo_mag + 1) >> 1).min(lim);
let hi_ctx = hi_off + ((hi_mag + 1) >> 1).min(6);
(lo_ctx as usize, hi_ctx as usize)
}
#[allow(clippy::too_many_arguments)]
pub fn decode_coefs_idtx_y(
msac: &mut MsacContext,
cdf: &mut CdfCoefContext,
cf: &mut [i32],
eob: i32,
tx2dszctx: usize,
sz_ctx: usize,
slw: usize,
slh: usize,
scan: &[u16],
) -> u8 {
let stride = 1 + (4 << slh);
let mut levels = vec![0i8; stride * ((4 << slw) + 1)];
let sz = (16i32 << tx2dszctx) - 1;
let bob = sz - eob;
let shift = slh + 2;
let mask = (4usize << slh) - 1;
let bctx = (bob > (2 << tx2dszctx)) as usize + (bob > (4 << tx2dszctx)) as usize;
let mut tok = 1 + rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.bob_base_y_tok[sz_ctx][bctx], 2) as i32;
if tok == 3 {
tok += rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_idtx[sz_ctx][0], 3) as i32;
}
let bob = bob.max(0);
let rc = scan[(bob as usize).min(scan.len() - 1)] as usize;
let rc = rc.min(cf.len() - 1);
cf[rc] = tok;
let li = ((1 + (rc >> shift)) * stride + (1 + (rc & mask))).min(levels.len() - 1);
levels[li] = tok as i8;
for i in (bob.max(0) + 1)..=sz.min(scan.len() as i32 - 1) {
if !crate::av2_recon::work_tick("coef:152") { break; }
let rc = (scan[(i.max(0) as usize).min(scan.len() - 1)] as usize).min(cf.len() - 1);
let lidx = ((1 + (rc >> shift)) * stride + (1 + (rc & mask))).min(levels.len() - 1);
let (lo, hr) = idtx_lo_ctx(&levels, lidx, stride);
let mut tok = rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.base_y_tok_idtx[sz_ctx][lo], 3) as i32;
if tok == 3 {
tok += rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_idtx[sz_ctx][hr], 3) as i32;
}
cf[rc] = tok;
levels[lidx] = tok as i8;
}
let mut dc_sign_level: u8 = 0x40; let mut hr_avg = 0i32;
for i in bob..=sz {
if !crate::av2_recon::work_tick("coef:168") { break; }
let rc = scan[i as usize] as usize;
let mut tok = cf[rc];
if tok == 0 {
continue;
}
let lidx = (1 + (rc >> shift)) * stride + (1 + (rc & mask));
let sctx = idtx_sign_ctx(&levels, lidx, stride);
let sign = rav1d_msac_decode_bool_adapt(msac, &mut cdf.sign_idtx[sz_ctx][sctx]);
levels[lidx] = 1 - 2 * sign as i8;
if i == 0 {
dc_sign_level = if sign { 0x00 } else { 0x80 };
}
if tok >= 6 {
let hr = decode_hr(msac, hr_avg);
tok += hr;
hr_avg = (hr_avg + hr) >> 1;
tok &= 0xfffff;
}
cf[rc] = if sign { -tok } else { tok };
}
let cul_level: u32 = (bob..=sz).map(|i| cf[scan[i as usize] as usize].unsigned_abs()).sum();
(cul_level.min(63) as u8) | dc_sign_level
}
pub fn tx2dsz_ctx(lw: usize, lh: usize) -> usize {
lw.min(3) + lh.min(3)
}
fn fold_ctx(arr: &[u8], lsize: usize) -> u32 {
let n = (1usize << lsize).min(arr.len());
arr[..n].iter().fold(0u32, |acc, &b| acc | b as u32)
}
pub fn skip_ctx_luma(a: &[u8], l: &[u8], lw: usize, lh: usize, b_dim: &[u8]) -> u32 {
if b_dim[2] as usize == lw && b_dim[3] as usize == lh {
return 0;
}
let la = fold_ctx(a, lw) & 0x3F;
let ll = fold_ctx(l, lh) & 0x3F;
(la.min(4) + ll.min(4) + 3) >> 1
}
pub fn decode_eob(msac: &mut MsacContext, cdf: &mut CdfCoefContext, tx2dszctx: usize, eob_ctx: usize) -> i32 {
let mut eob = match tx2dszctx {
0 => rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.eob_bin_16[eob_ctx], 4),
1 => rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.eob_bin_32[eob_ctx], 5),
2 => rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.eob_bin_64[eob_ctx], 6),
3 => rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.eob_bin_128[eob_ctx], 7),
4 => rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.eob_bin_256[eob_ctx], 7),
5 => rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.eob_bin_512[eob_ctx], 7),
_ => rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.eob_bin_1024[eob_ctx], 7),
} as i32;
let eb = match tx2dszctx {
4 => 1,
5 | 6 => 2,
_ => 0,
};
if eb != 0 && eob == 7 {
eob += rav1d_msac_decode_bools(msac, eb) as i32;
}
if COEF_DBG.with(|c| c.get()) { crate::dlog!("EOBDBG coarse_bin={eob} tx2dszctx={tx2dszctx} eob_ctx={eob_ctx} rng={} dif={:x}", msac.rng, msac.dif); }
if eob > 1 {
let eob_hi_bit = rav1d_msac_decode_bool_adapt(msac, &mut cdf.eob_hi_bit) as i32;
if COEF_DBG.with(|c| c.get()) { crate::dlog!("EOBDBG hi_bit={eob_hi_bit} rng={} dif={:x}", msac.rng, msac.dif); }
let eob_bin = eob - 2;
eob = eob_hi_bit | 2;
if eob_bin != 0 {
eob = (eob << eob_bin) | rav1d_msac_decode_bools(msac, eob_bin as u8) as i32;
}
}
eob
}
pub fn get_dc_sign_ctx(a: &[u8], l: &[u8], lw: usize, lh: usize, _tw: i32, _th: i32) -> usize {
let mut t = 0i32;
for &b in &a[..1 << lw] {
t += (b as i32 & 0xC0) >> 6;
}
for &b in &l[..1 << lh] {
t += (b as i32 & 0xC0) >> 6;
}
let s = t - (1i32 << lw) - (1i32 << lh);
(s != 0) as usize + (s > 0) as usize
}
pub fn decode_coefs_dc_only_y(
msac: &mut MsacContext,
cdf: &mut CdfCoefContext,
cf: &mut [i32],
t_dim_ctx: usize,
dc_sign_ctx: usize,
) -> u8 {
let mut dc_tok = 1 + rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.eob_base_y_tok_lf[t_dim_ctx][0], 4) as i32;
if dc_tok == 5 {
dc_tok += rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_lf[0], 3) as i32;
}
let neg = rav1d_msac_decode_bool_adapt(msac, &mut cdf.dc_sign[0][0][dc_sign_ctx]);
let dc_sign_level: u8 = if neg { 0x00 } else { 0x80 };
if dc_tok >= 8 {
let hr = decode_hr(msac, 0);
dc_tok = (dc_tok + hr) & 0xfffff;
}
cf[0] = if neg { -dc_tok } else { dc_tok };
(dc_tok.min(63) as u8) | dc_sign_level
}
#[inline]
fn tcq_next_state(state: i32, abs_level: i32) -> i32 {
(((state & 0x4) ^ (((abs_level & 1) ^ (state & 0x1)) << 2))
| ((state & 0x6) >> 1)
| i32::MIN)
& (state >> 31)
}
#[allow(clippy::too_many_arguments)]
pub fn decode_coefs_dct_y(
msac: &mut MsacContext,
cdf: &mut CdfCoefContext,
cf: &mut [i32],
eob: i32,
tx2dszctx: usize,
t_dim_ctx: usize,
slw: usize,
slh: usize,
scan: &[u16],
tcq_enabled: bool,
dc_sign_ctx: usize,
) -> u8 {
use crate::msac::{rav1d_msac_decode_bool_bypass, rav1d_msac_decode_symbol_adapt8};
let eob = eob.min(scan.len() as i32 - 1);
let stride = 4usize << slh;
let mut levels = vec![0i8; stride * ((4 << slw) + 2)];
let shift = slh + 2;
let mask = (4usize << slh) - 1;
let hi_to_low_tx = 10i32; let mut lim = if eob >= hi_to_low_tx { 3 } else { 5 };
let eob_ctx = 1 + (eob > (2 << tx2dszctx)) as usize + (eob > (4 << tx2dszctx)) as usize;
let mut tok = if lim == 5 {
1 + rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.eob_base_y_tok_lf[t_dim_ctx][eob_ctx], 4) as i32
} else {
1 + rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.eob_base_y_tok_hf[t_dim_ctx][eob_ctx], 2) as i32
};
if tok == lim {
tok += if lim == 5 {
rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_lf[7], 3) as i32
} else {
rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_hf[0], 3) as i32
};
}
let rc0 = scan[(eob as usize).min(scan.len() - 1)] as usize;
cf[rc0] = tok;
levels[rc0] = tok.min(127) as i8;
let mut tcq_state: i32 = if tcq_enabled { i32::MIN } else { 0 };
tcq_state = tcq_next_state(tcq_state, tok);
for i in (1..eob.min(scan.len() as i32)).rev() {
if !crate::av2_recon::work_tick("coef:380") { break; }
if i == hi_to_low_tx - 1 {
lim = 5; }
let rc = scan[i as usize] as usize;
let xy = (rc >> shift) + (rc & mask);
let (lo, hr) = get_lo_ctx_2d_luma(&levels, rc, stride, xy);
let tcq = ((tcq_state & 2) >> 1) as usize;
let mut t = if lim == 5 {
rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.base_y_tok_lf[t_dim_ctx][lo][tcq], 5) as i32
} else {
rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.base_y_tok_hf[t_dim_ctx][lo][tcq], 3) as i32
};
if t == lim {
t += if lim == 5 {
rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_lf[hr], 3) as i32
} else {
rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_hf[hr], 3) as i32
};
}
cf[rc] = t;
levels[rc] = t.min(127) as i8;
tcq_state = tcq_next_state(tcq_state, t);
}
if eob > 0 {
let (lo, hr) = get_lo_ctx_2d_luma(&levels, 0, stride, 0);
let tcq = ((tcq_state & 2) >> 1) as usize;
let mut t = rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.base_y_tok_lf[t_dim_ctx][lo][tcq], 5) as i32;
if t == 5 {
t += rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_lf[hr], 3) as i32;
}
cf[0] = t;
levels[0] = t.min(127) as i8;
}
let mut hr_avg = 0i32;
for i in (1..=eob).rev() {
if !crate::av2_recon::work_tick("coef:423") { break; }
let rc = scan[i as usize] as usize;
let mut tok = cf[rc];
if tok == 0 {
continue;
}
let sign = rav1d_msac_decode_bool_bypass(msac);
let max_br = if i < hi_to_low_tx { 8 } else { 6 };
if tok >= max_br {
let hr = decode_hr(msac, hr_avg);
tok += hr;
hr_avg = (hr_avg + hr) >> 1;
tok &= 0xfffff;
}
cf[rc] = if sign { -tok } else { tok };
}
let mut dc_sign_level: u8 = 0x40; if COEF_DBG.with(|c| c.get()) {
crate::dlog!(
"DCSIGN cf0={} eob={eob} ctx={dc_sign_ctx} pre rng={} dif={:x} cnt={}",
cf[0], msac.rng, msac.dif, msac.cnt
);
}
if cf[0] != 0 {
let mut tok = cf[0];
let neg = rav1d_msac_decode_bool_adapt(msac, &mut cdf.dc_sign[0][0][dc_sign_ctx]);
dc_sign_level = if neg { 0x00 } else { 0x80 };
if tok >= 8 {
let hr = decode_hr(msac, hr_avg);
tok += hr;
hr_avg = (hr_avg + hr) >> 1;
tok &= 0xfffff;
}
cf[0] = if neg { -tok } else { tok };
}
let cul_level: u32 = (0..=eob).map(|i| cf[scan[i as usize] as usize].unsigned_abs()).sum();
(cul_level.min(63) as u8) | dc_sign_level
}
#[inline]
pub fn get_lo_ctx_hv_luma(levels: &[i8], idx: usize, stride: usize, xy: usize) -> (usize, usize) {
let lo_freq = xy < 2;
let lim0 = if lo_freq { 5 } else { 3 };
let n1 = levels[idx + 1] as i32; let n2 = levels[idx + stride] as i32; let mut lo_mag = n1.min(lim0) + n2.min(lim0);
let mut hi_mag = n1.min(5) + n2.min(5);
let n3 = levels[idx + 2] as i32; lo_mag += n3.min(3);
hi_mag += n3.min(5);
lo_mag += (levels[idx + 3] as i32).min(3) + (levels[idx + 4] as i32).min(3); let (offset, lim_final) = if lo_freq {
if xy == 0 { (21i32, 6i32) } else { (28, 4) }
} else {
(15, 4)
};
let hi_off = if lo_freq { 7 } else { 0 }; let lo_ctx = offset + ((lo_mag + 1) >> 1).min(lim_final);
let hi_ctx = hi_off + ((hi_mag + 1) >> 1).min(6);
(lo_ctx as usize, hi_ctx as usize)
}
#[allow(clippy::too_many_arguments)]
pub fn decode_coefs_hv_y(
msac: &mut MsacContext,
cdf: &mut CdfCoefContext,
cf: &mut [i32],
eob: i32,
tx_class: usize, tx2dszctx: usize,
t_dim_ctx: usize,
slw: usize,
slh: usize,
tcq_enabled: bool,
dc_sign_ctx: usize,
) -> u8 {
use crate::msac::{
rav1d_msac_decode_bool_adapt, rav1d_msac_decode_bool_bypass, rav1d_msac_decode_symbol_adapt8,
};
let stride = 32usize;
let is_v = tx_class == 3;
let (shift, shift2, mask, axis) = if is_v {
(slw + 2, slh + 2, (4usize << slw) - 1, 4usize << slw)
} else {
(slh + 2, 0usize, (4usize << slh) - 1, 4usize << slh)
};
let hi_to_low_tx = 8i32 << if is_v { slw } else { slh };
let mut levels = vec![0i8; stride * (axis + 2)];
let pos = |i: i32| -> (usize, usize, usize, usize) {
let x = (i as usize) & mask;
let y = (i as usize) >> shift;
let rc = if is_v { (x << shift2) | y } else { i as usize };
(x, y, rc, x * stride + y)
};
let mut lim = if eob >= hi_to_low_tx { 3 } else { 5 };
let eob_ctx = if eob == 0 {
0
} else {
1 + (eob > (2 << tx2dszctx)) as usize + (eob > (4 << tx2dszctx)) as usize
};
let mut tok = if lim == 5 {
1 + rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.eob_base_y_tok_lf[t_dim_ctx][eob_ctx], 4) as i32
} else {
1 + rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.eob_base_y_tok_hf[t_dim_ctx][eob_ctx], 2) as i32
};
if tok == lim {
tok += if lim == 5 {
rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_lf[7], 3) as i32
} else {
rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_hf[0], 3) as i32
};
}
let (_, _, rc0, lidx0) = pos(eob);
let (rc0, lidx0) = (rc0.min(cf.len() - 1), lidx0.min(levels.len() - 1));
cf[rc0] = tok;
levels[lidx0] = tok.min(127) as i8;
let mut tcq_state: i32 = if tcq_enabled { i32::MIN } else { 0 };
tcq_state = tcq_next_state(tcq_state, tok);
for i in (1..eob).rev() {
if !crate::av2_recon::work_tick("coef:563") { break; }
if i == hi_to_low_tx - 1 {
lim = 5; }
let (_x, y, rc, lidx) = pos(i);
let (lo, hr) = get_lo_ctx_hv_luma(&levels, lidx, stride, y);
let tcq = ((tcq_state & 2) >> 1) as usize;
let mut t = if lim == 5 {
rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.base_y_tok_lf[t_dim_ctx][lo][tcq], 5) as i32
} else {
rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.base_y_tok_hf[t_dim_ctx][lo][tcq], 3) as i32
};
if t == lim {
t += if lim == 5 {
rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_lf[hr], 3) as i32
} else {
rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_hf[hr], 3) as i32
};
}
let (rc, lidx) = (rc.min(cf.len() - 1), lidx.min(levels.len() - 1));
cf[rc] = t;
levels[lidx] = t.min(127) as i8;
tcq_state = tcq_next_state(tcq_state, t);
}
if eob > 0 {
let (lo, hr) = get_lo_ctx_hv_luma(&levels, 0, stride, 0);
let tcq = ((tcq_state & 2) >> 1) as usize;
let mut t = rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.base_y_tok_lf[t_dim_ctx][lo][tcq], 5) as i32;
if t == 5 {
t += rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_y_tok_lf[hr], 3) as i32;
}
cf[0] = t;
levels[0] = t.min(127) as i8;
}
let mut hr_avg = 0i32;
for i in (1..=eob).rev() {
if !crate::av2_recon::work_tick("coef:604") { break; }
let (_x, y, rc, _lidx) = pos(i);
let rc = rc.min(cf.len() - 1);
let mut t = cf[rc];
if t == 0 {
continue;
}
let neg = if y == 0 {
rav1d_msac_decode_bool_adapt(msac, &mut cdf.dc_sign[0][0][0])
} else {
rav1d_msac_decode_bool_bypass(msac)
};
let max_br = if i < hi_to_low_tx { 8 } else { 6 };
if t >= max_br {
let hr = decode_hr(msac, hr_avg);
t += hr;
hr_avg = (hr_avg + hr) >> 1;
t &= 0xfffff;
}
cf[rc] = if neg { -t } else { t };
}
let mut dc_sign_level: u8 = 0x40;
if cf[0] != 0 {
let mut t = cf[0];
let neg = rav1d_msac_decode_bool_adapt(msac, &mut cdf.dc_sign[0][0][dc_sign_ctx]);
dc_sign_level = if neg { 0x00 } else { 0x80 };
if t >= 8 {
let hr = decode_hr(msac, hr_avg);
t += hr;
hr_avg = (hr_avg + hr) >> 1;
t &= 0xfffff;
}
cf[0] = if neg { -t } else { t };
}
let cul_level: u32 = (0..=eob).map(|i| { let (_, _, rc, _) = pos(i); cf[rc.min(cf.len() - 1)].unsigned_abs() }).sum();
(cul_level.min(63) as u8) | dc_sign_level
}
#[allow(clippy::too_many_arguments)]
pub fn skip_ctx_chroma(
a: &[u8],
l: &[u8],
cbx4: usize,
cby4: usize,
tx_w4: usize,
tx_h4: usize,
plane: usize,
u_has_cf: bool,
not_one_blk: bool,
) -> usize {
let ca = a[cbx4..cbx4 + tx_w4].iter().any(|&v| v != 0x40) as usize;
let cl = l[cby4..cby4 + tx_h4].iter().any(|&v| v != 0x40) as usize;
let offset = if plane == 1 { 6 } else { 6 * u_has_cf as usize + not_one_blk as usize * 3 };
offset + ca + cl
}
#[inline]
pub fn get_lo_ctx_2d_chroma(levels: &[i8], idx: usize, stride: usize, xy: usize, plane: usize) -> (usize, usize) {
let lo_freq = xy < 1;
let lim0 = if lo_freq { 5 } else { 3 };
let n1 = levels[idx + 1] as i32; let n2 = levels[idx + stride] as i32; let n3 = levels[idx + stride + 1] as i32; let lo_mag = n1.min(lim0) + n2.min(lim0) + n3.min(lim0);
let hi_mag = n1.min(5) + n2.min(5) + n3.min(5);
let offset = if plane == 1 { 0 } else { 4 };
let lo_ctx = offset + ((lo_mag + 1) >> 1).min(3);
let hi_ctx = ((hi_mag + 1) >> 1).min(3);
(lo_ctx as usize, hi_ctx as usize)
}
#[allow(clippy::too_many_arguments)]
pub fn decode_coefs_uv(
msac: &mut MsacContext,
cdf: &mut CdfCoefContext,
cctx_cdf: &mut [u16],
cf: &mut [i32],
plane: usize,
t_dim_ctx: usize,
slw: usize,
slh: usize,
tx2dszctx: usize,
scan: &[u16],
sctx: usize,
u_skip_set: usize,
intra: bool,
tx_class: usize,
) -> (i32, u8) {
let (slw, slh) = (slw.min(3), slh.min(3));
let sctx_v = sctx.min(cdf.skip_v.len() - 1);
let u_skip_set = u_skip_set.min(cdf.skip.len() - 1);
let t_dim_ctx = t_dim_ctx.min(cdf.skip[u_skip_set].len() - 1);
let sctx_u = sctx.min(cdf.skip[u_skip_set][t_dim_ctx].len() - 1);
let all_zero = if plane == 2 {
rav1d_msac_decode_bool_adapt(msac, &mut cdf.skip_v[sctx_v])
} else {
rav1d_msac_decode_bool_adapt(msac, &mut cdf.skip[u_skip_set][t_dim_ctx][sctx_u])
};
if COEF_DBG.with(|c| c.get()) { crate::dlog!("UVDBG pl={plane} all_zero={} sctx={sctx} tctx={t_dim_ctx} uset={u_skip_set} rng={} dif={:x}", all_zero as u8, msac.rng, msac.dif); }
if all_zero {
return (-1, 0x40);
}
let eob = decode_eob(msac, cdf, tx2dszctx, 2);
if COEF_DBG.with(|c| c.get()) { crate::dlog!("UVDBG pl={plane} eob={eob} tx2dszctx={tx2dszctx} rng={} dif={:x}", msac.rng, msac.dif); }
let cctx_allowed = {
let ss = crate::av2_frame::SS.with(|c| c.get());
(ss.0 == 1 && ss.1 == 1) || slw < 3 || slh < 3
};
if plane == 1 && eob >= intra as i32 && cctx_allowed && crate::av2_recon::SEQ_TOOLS.with(|c| c.get().cctx) {
let cctx = rav1d_msac_decode_symbol_adapt8(msac, cctx_cdf, 6);
if COEF_DBG.with(|c| c.get()) { crate::dlog!("UVDBG pl={plane} cctx={cctx} rng={} dif={:x}", msac.rng, msac.dif); }
}
let cf_ctx = if tx_class != 0 {
decode_coefs_hv_uv(msac, cdf, cf, eob, tx_class, tx2dszctx, slw, slh)
} else {
decode_coefs_dct_uv(msac, cdf, cf, plane, eob, tx2dszctx, slw, slh, scan)
};
(eob, cf_ctx)
}
#[allow(clippy::too_many_arguments)]
fn decode_coefs_dct_uv(
msac: &mut MsacContext,
cdf: &mut CdfCoefContext,
cf: &mut [i32],
plane: usize,
eob: i32,
tx2dszctx: usize,
slw: usize,
slh: usize,
scan: &[u16],
) -> u8 {
use crate::msac::{rav1d_msac_decode_bool_bypass, rav1d_msac_decode_symbol_adapt8};
let eob = eob.min(scan.len() as i32 - 1);
let stride = 4usize << slh;
let mut levels = vec![0i8; stride * ((4 << slw) + 2)];
let shift = slh + 2;
let mask = (4usize << slh) - 1;
let hi_to_low_tx = 1i32; let lim = if eob >= hi_to_low_tx { 3 } else { 5 };
let eob_ctx = if eob == 0 {
0
} else {
1 + (eob > (2 << tx2dszctx)) as usize + (eob > (4 << tx2dszctx)) as usize
};
let tok = if lim == 5 {
1 + rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.eob_base_uv_tok_lf[eob_ctx], 4) as i32
} else {
let mut t = 1 + rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.eob_base_uv_tok_hf[eob_ctx], 2) as i32;
if t == 3 {
t += rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_uv_tok_hf[0], 3) as i32;
}
t
};
if COEF_DBG.with(|c| c.get()) { crate::dlog!("UVDBG pl={plane} eob_base tok={tok} lim={lim} rng={} dif={:x}", msac.rng, msac.dif); }
let rc0 = scan[(eob as usize).min(scan.len() - 1)] as usize;
cf[rc0] = tok;
levels[rc0] = tok.min(127) as i8;
for i in (1..eob).rev() {
if !crate::av2_recon::work_tick("coef:813") { break; }
let rc = scan[i as usize] as usize;
let xy = (rc >> shift) + (rc & mask);
let (lo, hr) = get_lo_ctx_2d_chroma(&levels, rc, stride, xy, plane);
let mut t = rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.base_uv_tok_hf[lo], 3) as i32;
if t == 3 {
t += rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_uv_tok_hf[hr], 3) as i32;
}
cf[rc] = t;
levels[rc] = t.min(127) as i8;
if COEF_DBG.with(|c| c.get()) && plane == 1 { crate::dlog!("CHRAC2m i={i} rc={rc} lo={lo} tok={t} rng={} dif={:x}", msac.rng, msac.dif); }
}
if COEF_DBG.with(|c| c.get()) { crate::dlog!("UVDBG pl={plane} post-AC rng={} dif={:x}", msac.rng, msac.dif); }
if eob > 0 {
let (lo, _hr) = get_lo_ctx_2d_chroma(&levels, 0, stride, 0, plane);
let t = rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.base_uv_tok_lf[lo], 5) as i32;
cf[0] = t;
levels[0] = t.min(127) as i8;
}
if COEF_DBG.with(|c| c.get()) { crate::dlog!("UVDBG pl={plane} post-DC cf0={} rng={} dif={:x}", cf[0], msac.rng, msac.dif); }
let mut hr_avg = 0i32;
for i in (1..=eob).rev() {
if !crate::av2_recon::work_tick("coef:838") { break; }
let rc = scan[i as usize] as usize;
let mut t = cf[rc];
if t == 0 {
continue;
}
let neg = rav1d_msac_decode_bool_bypass(msac);
let max_br = if i < hi_to_low_tx { 5 } else { 6 };
if t >= max_br {
let hr = decode_hr(msac, hr_avg);
t += hr;
hr_avg = (hr_avg + hr) >> 1;
t &= 0xfffff;
}
cf[rc] = if neg { -t } else { t };
}
let mut dc_sign_level: u8 = 0x40;
if cf[0] != 0 {
let mut t = cf[0];
let neg = rav1d_msac_decode_bool_bypass(msac);
dc_sign_level = if neg { 0x00 } else { 0x80 };
if t >= 5 {
let hr = decode_hr(msac, hr_avg);
t += hr;
hr_avg = (hr_avg + hr) >> 1;
t &= 0xfffff;
}
cf[0] = if neg { -t } else { t };
}
let cul_level: u32 = (0..=eob).map(|i| cf[scan[i as usize] as usize].unsigned_abs()).sum();
(cul_level.min(63) as u8) | dc_sign_level
}
#[inline]
pub fn get_lo_ctx_hv_chroma(levels: &[i8], idx: usize, stride: usize, xy: usize) -> (usize, usize) {
let lo_freq = xy < 1;
let lim0 = if lo_freq { 5 } else { 3 };
let n1 = levels[idx + 1] as i32; let n2 = levels[idx + stride] as i32; let lo_mag = n1.min(lim0) + n2.min(lim0);
let hi_mag = n1.min(5) + n2.min(5);
let lo_ctx = 8 + ((lo_mag + 1) >> 1).min(3);
let hi_ctx = ((hi_mag + 1) >> 1).min(3);
(lo_ctx as usize, hi_ctx as usize)
}
#[allow(clippy::too_many_arguments)]
fn decode_coefs_hv_uv(
msac: &mut MsacContext,
cdf: &mut CdfCoefContext,
cf: &mut [i32],
eob: i32,
tx_class: usize, tx2dszctx: usize,
slw: usize,
slh: usize,
) -> u8 {
use crate::msac::{
rav1d_msac_decode_bool_bypass, rav1d_msac_decode_symbol_adapt4, rav1d_msac_decode_symbol_adapt8,
};
let stride = 32usize;
let is_v = tx_class == 3;
let (shift, shift2, mask, axis) = if is_v {
(slw + 2, slh + 2, (4usize << slw) - 1, 4usize << slw)
} else {
(slh + 2, 0usize, (4usize << slh) - 1, 4usize << slh)
};
let hi_to_low_tx = (8i32 << if is_v { slw } else { slh }) >> 1;
let mut levels = vec![0i8; stride * (axis + 2)];
let pos = |i: i32| -> (usize, usize, usize, usize) {
let x = (i as usize) & mask;
let y = (i as usize) >> shift;
let rc = if is_v { (x << shift2) | y } else { i as usize };
(x, y, rc, x * stride + y)
};
let mut lim = if eob >= hi_to_low_tx { 3 } else { 5 };
let eob_ctx = if eob == 0 {
0
} else {
1 + (eob > (2 << tx2dszctx)) as usize + (eob > (4 << tx2dszctx)) as usize
};
let mut tok = if lim == 5 {
1 + rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.eob_base_uv_tok_lf[eob_ctx], 4) as i32
} else {
let mut t = 1 + rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.eob_base_uv_tok_hf[eob_ctx], 2) as i32;
if t == 3 {
t += rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_uv_tok_hf[0], 3) as i32;
}
t
};
let (_, _, rc0, lidx0) = pos(eob);
let (rc0, lidx0) = (rc0.min(cf.len() - 1), lidx0.min(levels.len() - 1));
cf[rc0] = tok;
levels[lidx0] = tok.min(127) as i8;
for i in (1..eob).rev() {
if !crate::av2_recon::work_tick("coef:952") { break; }
if i == hi_to_low_tx - 1 {
lim = 5; }
let (_x, y, rc, lidx) = pos(i);
let (lo, hr) = get_lo_ctx_hv_chroma(&levels, lidx, stride, y);
let mut t = if lim == 5 {
rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.base_uv_tok_lf[lo], 5) as i32
} else {
rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.base_uv_tok_hf[lo], 3) as i32
};
if lim == 3 && t == 3 {
t += rav1d_msac_decode_symbol_adapt4(msac, &mut cdf.br_uv_tok_hf[hr], 3) as i32;
}
cf[rc] = t;
levels[lidx] = t.min(127) as i8;
}
if eob > 0 {
let (lo, _hr) = get_lo_ctx_hv_chroma(&levels, 0, stride, 0);
let t = rav1d_msac_decode_symbol_adapt8(msac, &mut cdf.base_uv_tok_lf[lo], 5) as i32;
cf[0] = t;
levels[0] = t.min(127) as i8;
}
let mut hr_avg = 0i32;
for i in (1..=eob).rev() {
if !crate::av2_recon::work_tick("coef:980") { break; }
let (_x, _y, rc, _lidx) = pos(i);
let mut t = cf[rc];
if t == 0 {
continue;
}
let neg = rav1d_msac_decode_bool_bypass(msac);
let max_br = if i < hi_to_low_tx { 5 } else { 6 };
if t >= max_br {
let hr = decode_hr(msac, hr_avg);
t += hr;
hr_avg = (hr_avg + hr) >> 1;
t &= 0xfffff;
}
cf[rc] = if neg { -t } else { t };
}
let mut dc_sign_level: u8 = 0x40;
if cf[0] != 0 {
let mut t = cf[0];
let neg = rav1d_msac_decode_bool_bypass(msac);
dc_sign_level = if neg { 0x00 } else { 0x80 };
if t >= 5 {
let hr = decode_hr(msac, hr_avg);
t += hr;
hr_avg = (hr_avg + hr) >> 1;
t &= 0xfffff;
}
cf[0] = if neg { -t } else { t };
}
let cul_level: u32 = (0..=eob).map(|i| { let (_, _, rc, _) = pos(i); cf[rc.min(cf.len() - 1)].unsigned_abs() }).sum();
(cul_level.min(63) as u8) | dc_sign_level
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dct_lo_ctx_matches_block1_dc() {
let mut levels = vec![0i8; 16 * 18];
levels[1] = 1; let (lo, hi) = get_lo_ctx_2d_luma(&levels, 0, 16, 0);
assert_eq!(lo, 1, "DC lo_ctx must match oracle ctx=1");
assert_eq!(hi, 1);
let z = vec![0i8; 16 * 18];
assert_eq!(get_lo_ctx_2d_luma(&z, 0, 16, 0), (0, 0));
let mut t = vec![0i8; 16 * 18];
t[1] = 1;
t[16] = 1; assert_eq!(get_lo_ctx_2d_luma(&t, 0, 16, 0).0, 1);
}
#[test]
fn skip_ctx_luma_cases() {
let z = [0u8; 16];
let b_dim_8x8 = [2, 2, 1, 1]; assert_eq!(skip_ctx_luma(&z, &z, 1, 1, &b_dim_8x8), 0); let b_dim_16x16 = [4, 4, 2, 2];
assert_eq!(skip_ctx_luma(&z, &z, 1, 1, &b_dim_16x16), 1); let mut a = [0u8; 16];
a[0] = 0x05;
a[1] = 0x03; assert_eq!(skip_ctx_luma(&a, &z, 1, 1, &b_dim_16x16), (4 + 0 + 3) >> 1);
}
}