#[derive(Clone, Copy, Debug)]
pub(crate) struct CtxModel {
pub(crate) p_state_idx: u8,
pub(crate) val_mps: u8,
}
impl CtxModel {
pub(crate) fn init(init_value: u8, qp: u8) -> Self {
let slope_idx = (init_value >> 4) as i32;
let offset_idx = (init_value & 0x0F) as i32;
let m = slope_idx * 5 - 45;
let n = (offset_idx << 3) - 16;
let qpc = (qp as i32).clamp(0, 51);
let pre = (((m * qpc) >> 4) + n).clamp(1, 126);
if pre >= 64 {
CtxModel {
p_state_idx: (pre - 64) as u8,
val_mps: 1,
}
} else {
CtxModel {
p_state_idx: (63 - pre) as u8,
val_mps: 0,
}
}
}
}
#[derive(Clone)]
pub(crate) struct ContextSet {
pub(crate) _qp: u8,
pub(crate) split_cu_flag: [CtxModel; 3],
pub(crate) split_transform_flag: [CtxModel; 3],
pub(crate) cbf_luma: [CtxModel; 2],
pub(crate) cbf_chroma: [CtxModel; 5],
pub(crate) last_sig_coeff_x_prefix: [CtxModel; 18],
pub(crate) last_sig_coeff_y_prefix: [CtxModel; 18],
pub(crate) sig_coeff_flag: [CtxModel; 44],
pub(crate) coded_sub_block_flag: [CtxModel; 4],
pub(crate) coeff_abs_level_greater1: [CtxModel; 24],
pub(crate) coeff_abs_level_greater2: [CtxModel; 6],
pub(crate) sao_merge_flag: CtxModel,
pub(crate) sao_type_idx: CtxModel,
pub(crate) transform_skip_flag: [CtxModel; 2],
pub(crate) cu_qp_delta_abs: [CtxModel; 2],
pub(crate) cu_transquant_bypass_flag: CtxModel,
}
impl ContextSet {
pub(crate) fn init_islice(qp: u8) -> Self {
fn c(iv: u8, qp: u8) -> CtxModel {
CtxModel::init(iv, qp)
}
fn arr<const N: usize>(ivs: [u8; N], qp: u8) -> [CtxModel; N] {
ivs.map(|iv| CtxModel::init(iv, qp))
}
Self {
_qp: qp,
split_cu_flag: arr([139, 141, 157], qp),
split_transform_flag: arr([153, 138, 138], qp),
cbf_luma: arr([111, 141], qp),
cbf_chroma: arr([94, 138, 182, 154, 154], qp),
last_sig_coeff_x_prefix: arr(
[
110, 110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111, 79, 108,
123, 63,
],
qp,
),
last_sig_coeff_y_prefix: arr(
[
110, 110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111, 79, 108,
123, 63,
],
qp,
),
sig_coeff_flag: arr(
[
111, 111, 125, 110, 110, 94, 124, 108, 124, 107, 125, 141, 179, 153, 125, 107,
125, 141, 179, 153, 125, 107, 125, 141, 179, 153, 125, 140, 139, 182, 182, 152,
136, 152, 136, 153, 136, 139, 111, 136, 139, 111, 141, 111,
],
qp,
),
coded_sub_block_flag: arr([91, 171, 134, 141], qp),
coeff_abs_level_greater1: arr(
[
140, 92, 137, 138, 140, 152, 138, 139, 153, 74, 149, 92, 139, 107, 122, 152,
140, 179, 166, 182, 140, 227, 122, 197,
],
qp,
),
coeff_abs_level_greater2: arr([138, 153, 136, 167, 152, 152], qp),
sao_merge_flag: c(153, qp),
sao_type_idx: c(200, qp),
transform_skip_flag: arr([139, 139], qp),
cu_qp_delta_abs: arr([154, 154], qp),
cu_transquant_bypass_flag: c(154, qp),
}
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct IntraModeContexts {
pub(crate) part_mode: CtxModel,
pub(crate) prev_intra_luma_pred_flag: CtxModel,
pub(crate) intra_chroma_pred_mode: CtxModel,
}
impl IntraModeContexts {
pub(crate) fn init_islice(qp: u8) -> Self {
Self {
part_mode: CtxModel::init(184, qp),
prev_intra_luma_pred_flag: CtxModel::init(184, qp),
intra_chroma_pred_mode: CtxModel::init(63, qp),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn context_set_init() {
let ctx = ContextSet::init_islice(26);
assert_eq!(ctx.cbf_luma[0].p_state_idx, 15);
assert_eq!(ctx.cbf_luma[0].val_mps, 1);
assert_eq!(ctx.cbf_chroma[0].p_state_idx, 0);
let ictx = IntraModeContexts::init_islice(26);
assert!(ictx.prev_intra_luma_pred_flag.p_state_idx < 64);
}
#[test]
fn intra_mode_contexts() {
let ictx = IntraModeContexts::init_islice(26);
assert!(ictx.prev_intra_luma_pred_flag.p_state_idx < 64);
assert!(ictx.intra_chroma_pred_mode.p_state_idx < 64);
}
}