use super::{contexts::ContextSet, engine::CabacEncoder};
pub(crate) fn encode_cbf_luma(
enc: &mut CabacEncoder,
ctx: &mut ContextSet,
flag: bool,
trafo_depth: usize,
) {
let idx = if trafo_depth == 0 { 1 } else { 0 };
enc.encode_bin(flag as u8, &mut ctx.cbf_luma[idx]);
}
pub(crate) fn encode_cbf_chroma(
enc: &mut CabacEncoder,
ctx: &mut ContextSet,
flag: bool,
trafo_depth: usize,
) {
let idx = trafo_depth.min(4);
enc.encode_bin(flag as u8, &mut ctx.cbf_chroma[idx]);
}
pub(crate) fn encode_residual(
enc: &mut CabacEncoder,
ctx: &mut ContextSet,
coeffs: &[i16],
log2_ts: u32,
is_luma: bool,
scan_idx: u8,
) {
let n_coeffs = (1usize << log2_ts) * (1usize << log2_ts);
debug_assert!(coeffs.len() >= n_coeffs);
let scan = crate::dct::coeff_scan(log2_ts, scan_idx);
let last_scan_pos = match coeffs[..n_coeffs].iter().rposition(|&c| c != 0) {
Some(p) => p,
None => return, };
let (last_row, last_col) = scan[last_scan_pos];
if scan_idx == 2 {
encode_last_sig(enc, ctx, last_row as u32, last_col as u32, log2_ts, is_luma);
} else {
encode_last_sig(enc, ctx, last_col as u32, last_row as u32, log2_ts, is_luma);
}
let tu_side = 1usize << log2_ts; let sb_side = (tu_side / 4).max(1); let num_sb = sb_side * sb_side; let last_sb = last_scan_pos / 16;
let sb_scan = crate::dct::sb_scan_for(log2_ts, scan_idx);
let mut csbf_storage = [0u8; 16];
let csbf_neighbors = &mut csbf_storage[..num_sb];
let pos_of = |abs_pos: usize| -> (usize, usize) {
let (r, c) = scan[abs_pos];
(c, r) };
let mut level_state = LevelState {
c1: 1,
first_subblock: true,
};
for sb in (0..=last_sb).rev() {
let sb_start = sb * 16;
let (sbx, sby) = sb_scan[sb];
let sb_grid = sbx + sby * sb_side;
let has_nonzero = coeffs[sb_start..(sb_start + 16).min(n_coeffs)]
.iter()
.any(|&c| c != 0);
let sub_block_coded;
let mut infer_dc = false;
if sb != 0 && sb != last_sb {
let nb = csbf_neighbors[sb_grid];
let ctx_inc = (nb > 0) as usize;
let cg_ctx = if is_luma { ctx_inc } else { 2 + ctx_inc };
enc.encode_bin(has_nonzero as u8, &mut ctx.coded_sub_block_flag[cg_ctx]);
sub_block_coded = has_nonzero;
infer_dc = true;
} else {
sub_block_coded = sb == 0 || sb == last_sb;
}
if sub_block_coded {
if sbx > 0 {
csbf_neighbors[(sbx - 1) + sby * sb_side] |= 1;
}
if sby > 0 {
csbf_neighbors[sbx + (sby - 1) * sb_side] |= 2;
}
}
if !sub_block_coded {
continue;
}
let prev_csbf = csbf_neighbors[sb_grid];
let scan_top = if sb == last_sb {
last_scan_pos % 16
} else {
15
};
let mut sig_positions = [0usize; 16];
let mut sig_len = 0usize;
let mut any_sig_in_sb = false;
let start = scan_top;
for k in (0..=start).rev() {
let abs_pos = sb_start + k;
if abs_pos >= n_coeffs {
continue;
}
if sb == last_sb && k == scan_top {
sig_positions[sig_len] = abs_pos;
sig_len += 1;
any_sig_in_sb = true;
continue;
}
if k == 0 && infer_dc && !any_sig_in_sb {
sig_positions[sig_len] = abs_pos;
sig_len += 1;
any_sig_in_sb = true;
continue;
}
let (xc, yc) = pos_of(abs_pos);
let ci = sig_coeff_ctx(xc, yc, prev_csbf, log2_ts, scan_idx, is_luma)
.min(ctx.sig_coeff_flag.len() - 1);
let is_sig = (coeffs[abs_pos] != 0) as u8;
enc.encode_bin(is_sig, &mut ctx.sig_coeff_flag[ci]);
if is_sig != 0 {
sig_positions[sig_len] = abs_pos;
sig_len += 1;
any_sig_in_sb = true;
}
}
if sig_len == 0 {
continue;
}
encode_coeff_levels(
enc,
ctx,
coeffs,
&sig_positions[..sig_len],
sb,
is_luma,
&mut level_state,
);
}
}
fn encode_last_sig(
enc: &mut CabacEncoder,
ctx: &mut ContextSet,
last_x: u32,
last_y: u32,
log2_size: u32,
is_luma: bool,
) {
let (ctx_offset, ctx_shift) = if is_luma {
let off = 3 * (log2_size - 2) + ((log2_size - 1) >> 2);
let shift = (log2_size + 1) >> 2;
(off as usize, shift as usize)
} else {
(15usize, (log2_size - 2) as usize)
};
static G_GROUP_IDX: [u32; 32] = [
0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9,
9, 9,
];
static G_MIN_IN_GROUP: [u32; 10] = [0, 1, 2, 3, 4, 6, 8, 12, 16, 24];
let size = 1u32 << log2_size;
let max_group = G_GROUP_IDX[(size - 1) as usize];
let encode_prefix = |enc: &mut CabacEncoder,
ctx_arr: &mut [super::engine::CtxModel],
v: u32,
ctx_offset: usize,
ctx_shift: usize| {
let group = G_GROUP_IDX[v as usize];
let n = ctx_arr.len();
for i in 0..group {
let ci = (ctx_offset + (i as usize >> ctx_shift)).min(n - 1);
enc.encode_bin(1, &mut ctx_arr[ci]);
}
if group < max_group {
let ci = (ctx_offset + (group as usize >> ctx_shift)).min(n - 1);
enc.encode_bin(0, &mut ctx_arr[ci]);
}
};
let encode_suffix = |enc: &mut CabacEncoder, v: u32| {
let group = G_GROUP_IDX[v as usize];
if group > 3 {
let suffix = v - G_MIN_IN_GROUP[group as usize];
let nbits = (group - 2) / 2;
for i in (0..nbits).rev() {
enc.encode_bypass(((suffix >> i) & 1) as u8);
}
}
};
encode_prefix(
enc,
&mut ctx.last_sig_coeff_x_prefix,
last_x,
ctx_offset,
ctx_shift,
);
encode_prefix(
enc,
&mut ctx.last_sig_coeff_y_prefix,
last_y,
ctx_offset,
ctx_shift,
);
encode_suffix(enc, last_x);
encode_suffix(enc, last_y);
}
fn sig_coeff_ctx(
xc: usize,
yc: usize,
prev_csbf: u8,
log2_ts: u32,
scan_idx: u8,
is_luma: bool,
) -> usize {
static CTX_IDX_MAP_4X4: [u8; 16] = [0, 1, 4, 5, 2, 3, 4, 5, 6, 6, 8, 8, 7, 7, 8, 99];
let sb_width = 1usize << (log2_ts - 2);
let mut sig_ctx: i32;
if sb_width == 1 {
sig_ctx = CTX_IDX_MAP_4X4[(yc << 2) + xc] as i32;
} else if xc + yc == 0 {
sig_ctx = 0;
} else {
let xp = xc & 3;
let yp = yc & 3;
let xs = xc >> 2;
let ys = yc >> 2;
sig_ctx = match prev_csbf {
0 => {
if xp + yp >= 3 {
0
} else if xp + yp > 0 {
1
} else {
2
}
}
1 => {
if yp == 0 {
2
} else if yp == 1 {
1
} else {
0
}
}
2 => {
if xp == 0 {
2
} else if xp == 1 {
1
} else {
0
}
}
_ => 2,
};
if is_luma {
if xs + ys > 0 {
sig_ctx += 3;
}
if sb_width == 2 {
sig_ctx += if scan_idx == 0 { 9 } else { 15 };
} else {
sig_ctx += 21;
}
} else {
if sb_width == 2 {
sig_ctx += 9;
} else {
sig_ctx += 12;
}
}
}
if is_luma {
sig_ctx as usize
} else {
27 + sig_ctx as usize
}
}
struct LevelState {
c1: i32, first_subblock: bool,
}
fn encode_coeff_levels(
enc: &mut CabacEncoder,
ctx: &mut ContextSet,
coeffs: &[i16],
sig_pos: &[usize],
sb: usize,
is_luma: bool,
st: &mut LevelState,
) {
let n = sig_pos.len();
if n == 0 {
return;
}
let mut ctx_set: i32 = if sb == 0 || !is_luma { 0 } else { 2 };
if st.c1 == 0 {
ctx_set += 1;
}
st.c1 = 1;
let chroma_off = if is_luma { 0 } else { 16 };
let last_g1 = n.min(8);
let mut greater1_flags = [false; 16];
let mut first_g1_one: Option<usize> = None;
let mut has_max_base = [true; 16];
for c in 0..last_g1 {
let abs_val = coeffs[sig_pos[c]].unsigned_abs() as i32;
let gr1 = abs_val > 1;
let g1ctx = if st.c1 >= 3 { 3 } else { st.c1 };
let ci = (ctx_set * 4 + g1ctx + chroma_off) as usize;
let ci = ci.min(ctx.coeff_abs_level_greater1.len() - 1);
enc.encode_bin(gr1 as u8, &mut ctx.coeff_abs_level_greater1[ci]);
greater1_flags[c] = gr1;
if gr1 {
st.c1 = 0;
if first_g1_one.is_none() {
first_g1_one = Some(c);
}
} else {
has_max_base[c] = false; if st.c1 < 3 && st.c1 > 0 {
st.c1 += 1;
}
}
}
st.first_subblock = false;
if let Some(c) = first_g1_one {
let abs_val = coeffs[sig_pos[c]].unsigned_abs() as i32;
let gr2 = abs_val > 2;
let ci2 = (ctx_set + if is_luma { 0 } else { 4 }) as usize;
let ci2 = ci2.min(ctx.coeff_abs_level_greater2.len() - 1);
enc.encode_bin(gr2 as u8, &mut ctx.coeff_abs_level_greater2[ci2]);
has_max_base[c] = gr2;
}
for &pos in sig_pos.iter() {
enc.encode_bypass((coeffs[pos] < 0) as u8);
}
let mut rice: u32 = 0;
for c in 0..n {
if !has_max_base[c] {
continue;
}
let g1 = if c < last_g1 {
greater1_flags[c] as i32
} else {
0
};
let g2 = if Some(c) == first_g1_one {
(coeffs[sig_pos[c]].unsigned_abs() as i32 > 2) as i32
} else {
0
};
let base_level = 1 + g1 + g2;
let abs_val = coeffs[sig_pos[c]].unsigned_abs() as i32;
let remaining = (abs_val - base_level).max(0) as u32;
encode_coeff_remaining(enc, remaining, rice);
let total = base_level + remaining as i32;
if total > (3 << rice) {
rice = (rice + 1).min(4);
}
}
}
fn encode_coeff_remaining(enc: &mut CabacEncoder, value: u32, rice_k: u32) {
if value < (4u32 << rice_k) {
let prefix = value >> rice_k;
for _ in 0..prefix {
enc.encode_bypass(1);
}
enc.encode_bypass(0);
for i in (0..rice_k).rev() {
enc.encode_bypass(((value >> i) & 1) as u8);
}
} else {
let mut p = 4u32;
loop {
let base_next = ((1u32 << (p + 1 - 3)) + 2) << rice_k;
if value < base_next {
break;
}
p += 1;
}
for _ in 0..p {
enc.encode_bypass(1);
}
enc.encode_bypass(0);
let suffix_bits = p - 3 + rice_k;
let codeword = value - (((1u32 << (p - 3)) + 2) << rice_k);
for i in (0..suffix_bits).rev() {
enc.encode_bypass(((codeword >> i) & 1) as u8);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cabac::contexts::ContextSet;
fn make_coeffs(vals: &[(usize, i16)], len: usize) -> Vec<i16> {
let mut c = vec![0i16; len];
for &(i, v) in vals {
c[i] = v;
}
c
}
#[test]
fn encode_single_dc_8x8() {
let mut enc = CabacEncoder::new();
let mut ctx = ContextSet::init_islice(26);
let coeffs = make_coeffs(&[(0, 8)], 64);
encode_residual(&mut enc, &mut ctx, &coeffs, 3, true, 0);
enc.encode_terminate(1);
let out = enc.finish();
assert!(!out.is_empty());
}
#[test]
fn encode_single_dc_4x4() {
let mut enc = CabacEncoder::new();
let mut ctx = ContextSet::init_islice(26);
let coeffs = make_coeffs(&[(0, 5)], 16);
encode_residual(&mut enc, &mut ctx, &coeffs, 2, false, 0);
enc.encode_terminate(1);
let out = enc.finish();
assert!(!out.is_empty());
}
#[test]
fn encode_multiple_coeffs() {
let mut enc = CabacEncoder::new();
let mut ctx = ContextSet::init_islice(26);
let coeffs = make_coeffs(&[(0, 12), (1, -3), (2, 1), (8, 5)], 64);
encode_residual(&mut enc, &mut ctx, &coeffs, 3, true, 0);
enc.encode_terminate(1);
let out = enc.finish();
assert!(!out.is_empty());
}
#[test]
fn encode_all_zero_does_nothing() {
let mut enc = CabacEncoder::new();
let mut ctx = ContextSet::init_islice(26);
let coeffs = vec![0i16; 64];
encode_residual(&mut enc, &mut ctx, &coeffs, 3, true, 0);
enc.encode_terminate(1);
let out = enc.finish();
assert!(out.len() < 4); }
}