use std::rc::Rc;
use crate::cabac::{CabacContexts, CabacReader};
use crate::cabac_tables::ctx;
use crate::dpb::DecodedPicture;
use crate::error::DecodeError;
use crate::inter_pred;
use crate::intra_pred::{
ReferenceAvailability, add_residual, build_reference_samples, filter_reference_samples,
predict_angular, predict_dc, predict_planar,
};
use crate::inverse_transform::apply_inverse_transform;
use crate::pixel::Pixel;
use crate::pps::Pps;
use crate::residual_coding::{ResidualBlock, ResidualPlane, ScanOrder, decode_residual_coding};
use crate::slice::SliceType;
use crate::sps::Sps;
pub const INTRA_PLANAR: u8 = 0;
pub const INTRA_DC: u8 = 1;
pub const INTRA_ANGULAR_10: u8 = 10;
pub const INTRA_ANGULAR_26: u8 = 26;
pub const INTRA_ANGULAR_34: u8 = 34;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PredMode {
Intra,
Inter,
Skip,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PartMode {
Part2Nx2N,
PartNxN,
Part2NxN,
PartNx2N,
Part2NxnU,
Part2NxnD,
PartnLx2N,
PartnRx2N,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InterPredIdc {
PredL0,
PredL1,
PredBi,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Mv {
pub x: i16,
pub y: i16,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct MvField {
pub mv: [Mv; 2],
pub ref_idx: [i8; 2],
pub pred_flag: u8,
}
impl MvField {
fn merge_eq(&self, other: &Self) -> bool {
if self.pred_flag != other.pred_flag {
return false;
}
match self.pred_flag {
3 => {
self.ref_idx[0] == other.ref_idx[0]
&& self.mv[0] == other.mv[0]
&& self.ref_idx[1] == other.ref_idx[1]
&& self.mv[1] == other.mv[1]
}
1 => {
self.ref_idx[0] == other.ref_idx[0] && self.mv[0] == other.mv[0]
}
2 => {
self.ref_idx[1] == other.ref_idx[1] && self.mv[1] == other.mv[1]
}
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub struct SliceParams {
pub slice_type: SliceType,
pub max_num_merge_cand: u32,
pub num_ref_idx_l0_active: u32,
pub num_ref_idx_l1_active: u32,
pub mvd_l1_zero_flag: bool,
pub log2_parallel_merge_level: u8,
pub poc: i32,
pub ref_pic_list_pocs: [Vec<i32>; 2],
pub ref_frames_l0: Vec<Rc<DecodedPicture>>,
pub ref_frames_l1: Vec<Rc<DecodedPicture>>,
pub collocated_ref: Option<Rc<DecodedPicture>>,
pub slice_temporal_mvp_enabled_flag: bool,
pub collocated_from_l0_flag: bool,
pub slice_cb_qp_offset: i32,
pub slice_cr_qp_offset: i32,
pub cu_chroma_qp_offset_enabled_flag: bool,
pub weighted_pred_flag: bool,
pub pred_weight_table: crate::slice::PredWeightTable,
}
pub struct PictureState<P: Pixel> {
pub width: u32,
pub height: u32,
pub bit_depth: u8,
pub log2_min_cb_size: u8,
pub log2_min_pu_size: u8,
pub log2_ctb_size: u8,
pub min_cb_width: usize,
pub min_pu_width: usize,
pub tab_ct_depth: Vec<u8>,
pub tab_ipm: Vec<u8>,
pub y_plane: Vec<P>,
pub u_plane: Vec<P>,
pub v_plane: Vec<P>,
pub y_stride: usize,
pub uv_stride: usize,
pub last_luma_pred_mode: u8,
pub last_chroma_pred_mode: u8,
pub cu_count: u32,
pub last_split_transform_flag: bool,
pub last_cbf_luma: bool,
pub last_cbf_cb: bool,
pub last_cbf_cr: bool,
pub last_cu_qp_delta: i32,
pub is_cu_qp_delta_coded: bool,
pub is_cu_chroma_qp_offset_coded: bool,
pub cu_qp_offset_cb: i32,
pub cu_qp_offset_cr: i32,
pub last_qp_y: i32,
pub qpy_pred: i32,
pub first_qp_group: bool,
pub last_luma_residual: Option<ResidualBlock>,
pub tab_qp_y: Vec<u8>,
pub bs_vertical: Vec<u8>,
pub bs_horizontal: Vec<u8>,
pub tab_cbf_luma: Vec<u8>,
pub log2_min_tb_size: u32,
pub sao_params: Vec<crate::sao::SaoParams>,
pub tab_slice_addr_rs: Vec<i32>,
pub filter_slice_edges: Vec<bool>,
pub tab_tile_id: Vec<u32>,
pub tab_mvf: Vec<MvField>,
pub tab_skip_flag: Vec<u8>,
pub min_tb_addr_zs: Vec<i32>,
pub min_tb_addr_zs_stride: usize,
pub min_tb_width: usize,
}
impl<P: Pixel> PictureState<P> {
pub fn new(sps: &Sps) -> Self {
let log2_min_cb_size = sps.min_cb_log2_size_y;
let log2_min_pu_size = 2u8;
let log2_ctb_size = sps.ctb_log2_size_y;
let w = sps.pic_width_in_luma_samples;
let h = sps.pic_height_in_luma_samples;
let ctb_size = 1u32 << log2_ctb_size;
let w_aligned = w.div_ceil(ctb_size) * ctb_size;
let h_aligned = h.div_ceil(ctb_size) * ctb_size;
let min_cb_width = (w_aligned >> log2_min_cb_size) as usize;
let min_cb_height = (h_aligned >> log2_min_cb_size) as usize;
let min_pu_width = (w_aligned >> log2_min_pu_size) as usize;
let min_pu_height = (h_aligned >> log2_min_pu_size) as usize;
let log2_min_tb_size = sps.min_tb_log2_size_y as u32;
let min_tb_width = (w_aligned >> log2_min_tb_size) as usize;
let min_tb_height = (h_aligned >> log2_min_tb_size) as usize;
let y_stride = w_aligned as usize;
let uv_stride = (w_aligned / 2) as usize;
Self {
width: w,
height: h,
bit_depth: sps.bit_depth_luma,
log2_min_cb_size,
log2_min_pu_size,
log2_ctb_size,
min_cb_width,
min_pu_width,
tab_ct_depth: vec![0u8; min_cb_width * min_cb_height],
tab_ipm: vec![INTRA_DC; min_pu_width * min_pu_height],
y_plane: vec![P::zero(); (w_aligned * h_aligned) as usize],
u_plane: vec![P::zero(); ((w_aligned / 2) * (h_aligned / 2)) as usize],
v_plane: vec![P::zero(); ((w_aligned / 2) * (h_aligned / 2)) as usize],
y_stride,
uv_stride,
last_luma_pred_mode: 0,
last_chroma_pred_mode: 0,
cu_count: 0,
last_split_transform_flag: false,
last_cbf_luma: false,
last_cbf_cb: false,
last_cbf_cr: false,
last_cu_qp_delta: 0,
is_cu_qp_delta_coded: false,
is_cu_chroma_qp_offset_coded: false,
cu_qp_offset_cb: 0,
cu_qp_offset_cr: 0,
last_qp_y: 0,
qpy_pred: 0,
first_qp_group: false,
last_luma_residual: None,
tab_qp_y: vec![0u8; min_cb_width * min_cb_height],
bs_vertical: vec![0u8; ((w_aligned / 4) * (h_aligned / 4)) as usize],
bs_horizontal: vec![0u8; ((w_aligned / 4) * (h_aligned / 4)) as usize],
tab_cbf_luma: vec![0u8; min_tb_width * min_tb_height],
log2_min_tb_size,
sao_params: {
let ctb_size = 1u32 << log2_ctb_size;
let pw = w.div_ceil(ctb_size) as usize;
let ph = h.div_ceil(ctb_size) as usize;
vec![crate::sao::SaoParams::default(); pw * ph]
},
tab_slice_addr_rs: {
let ctb_size = 1u32 << log2_ctb_size;
let pw = w.div_ceil(ctb_size) as usize;
let ph = h.div_ceil(ctb_size) as usize;
vec![-1i32; pw * ph]
},
filter_slice_edges: {
let ctb_size = 1u32 << log2_ctb_size;
let pw = w.div_ceil(ctb_size) as usize;
let ph = h.div_ceil(ctb_size) as usize;
vec![true; pw * ph]
},
tab_tile_id: {
let ctb_size = 1u32 << log2_ctb_size;
let pw = w.div_ceil(ctb_size) as usize;
let ph = h.div_ceil(ctb_size) as usize;
vec![0u32; pw * ph]
},
tab_mvf: vec![MvField::default(); min_pu_width * min_pu_height],
tab_skip_flag: vec![0u8; min_cb_width * min_cb_height],
min_tb_addr_zs: {
let log2_diff = (log2_ctb_size - sps.min_tb_log2_size_y) as u32;
let tb_per_ctb = 1u32 << log2_diff; let tb_mask = tb_per_ctb - 1;
let ctb_size = 1u32 << log2_ctb_size;
let pic_w_in_ctbs = w.div_ceil(ctb_size);
let pic_h_in_ctbs = h.div_ceil(ctb_size);
let pic_w_in_tbs = pic_w_in_ctbs * tb_per_ctb;
let pic_h_in_tbs = pic_h_in_ctbs * tb_per_ctb;
let stride = (pic_w_in_tbs + 1) as usize;
let rows = (pic_h_in_tbs + 1) as usize;
let mut tab = vec![-1i32; stride * rows];
for y in 0..pic_h_in_tbs {
for x in 0..pic_w_in_tbs {
let tb_x = x >> log2_diff; let tb_y = y >> log2_diff; let rs = pic_w_in_ctbs * tb_y + tb_x;
let mut val = (rs << (log2_diff * 2)) as i32;
let lx = x & tb_mask;
let ly = y & tb_mask;
for i in 0..log2_diff {
let m = 1u32 << i;
if lx & m != 0 {
val += (m * m) as i32;
}
if ly & m != 0 {
val += (2 * m * m) as i32;
}
}
tab[(y as usize + 1) * stride + (x as usize + 1)] = val;
}
}
tab
},
min_tb_addr_zs_stride: {
let log2_diff = (log2_ctb_size - sps.min_tb_log2_size_y) as u32;
let tb_per_ctb = 1u32 << log2_diff;
let ctb_size = 1u32 << log2_ctb_size;
let pic_w_in_ctbs = w.div_ceil(ctb_size);
(pic_w_in_ctbs * tb_per_ctb + 1) as usize
},
min_tb_width: {
let log2_diff = (log2_ctb_size - sps.min_tb_log2_size_y) as u32;
let tb_per_ctb = 1u32 << log2_diff;
let ctb_size = 1u32 << log2_ctb_size;
let pic_w_in_ctbs = w.div_ceil(ctb_size);
(pic_w_in_ctbs * tb_per_ctb) as usize
},
}
}
pub fn zscan_addr(&self, x_tb: i32, y_tb: i32) -> i32 {
let col = (x_tb + 1) as usize;
let row = (y_tb + 1) as usize;
if col >= self.min_tb_addr_zs_stride
|| row * self.min_tb_addr_zs_stride >= self.min_tb_addr_zs.len()
{
return -1;
}
self.min_tb_addr_zs[row * self.min_tb_addr_zs_stride + col]
}
pub fn take_planes_and_mvf(
&mut self,
) -> (
crate::pixel::PixelData,
crate::pixel::PixelData,
crate::pixel::PixelData,
Vec<MvField>,
u8,
usize,
u8,
) {
let y = P::wrap_vec(std::mem::take(&mut self.y_plane));
let u = P::wrap_vec(std::mem::take(&mut self.u_plane));
let v = P::wrap_vec(std::mem::take(&mut self.v_plane));
let tab_mvf = std::mem::take(&mut self.tab_mvf);
(
y,
u,
v,
tab_mvf,
self.log2_min_pu_size,
self.min_pu_width,
self.log2_ctb_size,
)
}
}
#[allow(clippy::too_many_arguments)]
pub fn decode_coding_quadtree<P: Pixel>(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
state: &mut PictureState<P>,
sps: &Sps,
pps: &Pps,
slice_qp_y: i32,
slice_params: &SliceParams,
x0: u32,
y0: u32,
log2_cb_size: u8,
cb_depth: u8,
) -> Result<bool, DecodeError> {
let cb_size = 1u32 << log2_cb_size;
if pps.cu_qp_delta_enabled_flag
&& log2_cb_size >= sps.ctb_log2_size_y - pps.diff_cu_qp_delta_depth as u8
{
state.is_cu_qp_delta_coded = false;
state.last_cu_qp_delta = 0;
}
let split_cu = if x0 + cb_size <= state.width
&& y0 + cb_size <= state.height
&& log2_cb_size > state.log2_min_cb_size
{
decode_split_cu_flag(cabac, contexts, state, x0, y0, cb_depth)? != 0
} else {
log2_cb_size > state.log2_min_cb_size
};
let more_data;
if split_cu {
let cb_size_split = cb_size >> 1;
let x1 = x0 + cb_size_split;
let y1 = y0 + cb_size_split;
let mut md = decode_coding_quadtree(
cabac,
contexts,
state,
sps,
pps,
slice_qp_y,
slice_params,
x0,
y0,
log2_cb_size - 1,
cb_depth + 1,
)?;
if md && x1 < state.width {
md = decode_coding_quadtree(
cabac,
contexts,
state,
sps,
pps,
slice_qp_y,
slice_params,
x1,
y0,
log2_cb_size - 1,
cb_depth + 1,
)?;
}
if md && y1 < state.height {
md = decode_coding_quadtree(
cabac,
contexts,
state,
sps,
pps,
slice_qp_y,
slice_params,
x0,
y1,
log2_cb_size - 1,
cb_depth + 1,
)?;
}
if md && x1 < state.width && y1 < state.height {
md = decode_coding_quadtree(
cabac,
contexts,
state,
sps,
pps,
slice_qp_y,
slice_params,
x1,
y1,
log2_cb_size - 1,
cb_depth + 1,
)?;
}
if md {
more_data = (x1 + cb_size_split) < state.width || (y1 + cb_size_split) < state.height;
} else {
more_data = false;
}
maybe_save_qpy_pred(state, sps, pps, x0, y0, log2_cb_size);
} else {
decode_coding_unit(
cabac,
contexts,
state,
sps,
pps,
slice_qp_y,
slice_params,
x0,
y0,
log2_cb_size,
cb_depth,
)?;
let ctb_size = 1u32 << state.log2_ctb_size;
let at_ctb_x_edge =
(x0 + cb_size).is_multiple_of(ctb_size) || (x0 + cb_size >= state.width);
let at_ctb_y_edge =
(y0 + cb_size).is_multiple_of(ctb_size) || (y0 + cb_size >= state.height);
if at_ctb_x_edge && at_ctb_y_edge {
let end_of_slice = cabac.decode_terminate();
more_data = end_of_slice == 0;
} else {
more_data = true;
}
set_ct_depth(state, x0, y0, log2_cb_size, cb_depth);
}
Ok(more_data)
}
fn decode_split_cu_flag<P: Pixel>(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
state: &PictureState<P>,
x0: u32,
y0: u32,
cb_depth: u8,
) -> Result<u32, DecodeError> {
let x_cb = (x0 >> state.log2_min_cb_size) as usize;
let y_cb = (y0 >> state.log2_min_cb_size) as usize;
let ctb_log2 = state.log2_ctb_size;
let ctb_w = state.width.div_ceil(1 << ctb_log2) as usize;
let cur_ctb_rs = (y0 >> ctb_log2) as usize * ctb_w + (x0 >> ctb_log2) as usize;
let cur_slice = state.tab_slice_addr_rs[cur_ctb_rs];
let depth_left = if x_cb > 0 {
let left_ctb_rs = (y0 >> ctb_log2) as usize * ctb_w + ((x0 - 1) >> ctb_log2) as usize;
if state.tab_slice_addr_rs[left_ctb_rs] == cur_slice {
state.tab_ct_depth[y_cb * state.min_cb_width + x_cb - 1]
} else {
0
}
} else {
0
};
let depth_top = if y_cb > 0 {
let top_ctb_rs = ((y0 - 1) >> ctb_log2) as usize * ctb_w + (x0 >> ctb_log2) as usize;
if state.tab_slice_addr_rs[top_ctb_rs] == cur_slice {
state.tab_ct_depth[(y_cb - 1) * state.min_cb_width + x_cb]
} else {
0
}
} else {
0
};
let mut inc = 0usize;
if depth_left > cb_depth {
inc += 1;
}
if depth_top > cb_depth {
inc += 1;
}
Ok(cabac.decode_bin(&mut contexts.state[ctx::SPLIT_CODING_UNIT_FLAG + inc]))
}
fn set_ct_depth<P: Pixel>(
state: &mut PictureState<P>,
x0: u32,
y0: u32,
log2_cb_size: u8,
cb_depth: u8,
) {
let length = ((1u32 << log2_cb_size) >> state.log2_min_cb_size) as usize;
let x_cb = (x0 >> state.log2_min_cb_size) as usize;
let y_cb = (y0 >> state.log2_min_cb_size) as usize;
for j in 0..length {
let row = (y_cb + j) * state.min_cb_width;
for i in 0..length {
state.tab_ct_depth[row + x_cb + i] = cb_depth;
}
}
}
fn decode_skip_flag<P: Pixel>(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
state: &PictureState<P>,
x0: u32,
y0: u32,
) -> u32 {
let x_cb = (x0 >> state.log2_min_cb_size) as usize;
let y_cb = (y0 >> state.log2_min_cb_size) as usize;
let mut inc = 0usize;
if x_cb > 0 {
inc += (state.tab_skip_flag[y_cb * state.min_cb_width + x_cb - 1] != 0) as usize;
}
if y_cb > 0 {
inc += (state.tab_skip_flag[(y_cb - 1) * state.min_cb_width + x_cb] != 0) as usize;
}
cabac.decode_bin(&mut contexts.state[ctx::SKIP_FLAG + inc])
}
fn decode_part_mode(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
sps: &Sps,
log2_cb_size: u8,
pred_mode: PredMode,
) -> PartMode {
if cabac.decode_bin(&mut contexts.state[ctx::PART_MODE]) != 0 {
return PartMode::Part2Nx2N;
}
if log2_cb_size == sps.min_cb_log2_size_y {
if pred_mode == PredMode::Intra {
return PartMode::PartNxN;
}
if cabac.decode_bin(&mut contexts.state[ctx::PART_MODE + 1]) != 0 {
return PartMode::Part2NxN;
}
if log2_cb_size == 3 {
return PartMode::PartNx2N;
}
if cabac.decode_bin(&mut contexts.state[ctx::PART_MODE + 2]) != 0 {
return PartMode::PartNx2N;
}
return PartMode::PartNxN;
}
if !sps.amp_enabled_flag {
if cabac.decode_bin(&mut contexts.state[ctx::PART_MODE + 1]) != 0 {
return PartMode::Part2NxN;
}
return PartMode::PartNx2N;
}
if cabac.decode_bin(&mut contexts.state[ctx::PART_MODE + 1]) != 0 {
if cabac.decode_bin(&mut contexts.state[ctx::PART_MODE + 3]) != 0 {
return PartMode::Part2NxN;
}
if cabac.decode_bypass() != 0 {
return PartMode::Part2NxnD;
}
return PartMode::Part2NxnU;
}
if cabac.decode_bin(&mut contexts.state[ctx::PART_MODE + 3]) != 0 {
return PartMode::PartNx2N;
}
if cabac.decode_bypass() != 0 {
return PartMode::PartnRx2N;
}
PartMode::PartnLx2N
}
fn is_diff_mer(log2_parallel_merge_level: u8, x_n: i32, y_n: i32, x_p: i32, y_p: i32) -> bool {
let pl = log2_parallel_merge_level;
(x_n >> pl) == (x_p >> pl) && (y_n >> pl) == (y_p >> pl)
}
fn tab_mvf_at<P: Pixel>(state: &PictureState<P>, x: i32, y: i32) -> MvField {
let x_pu = (x as u32 >> state.log2_min_pu_size) as usize;
let y_pu = (y as u32 >> state.log2_min_pu_size) as usize;
state.tab_mvf[y_pu * state.min_pu_width + x_pu]
}
fn spatial_cand_available<P: Pixel>(
state: &PictureState<P>,
x0: i32,
y0: i32,
x_n: i32,
y_n: i32,
) -> bool {
if x_n < 0 || y_n < 0 || x_n >= state.width as i32 || y_n >= state.height as i32 {
return false;
}
let log2_ctb = state.log2_ctb_size;
let ctb_w = (state.width as usize).div_ceil(1 << log2_ctb);
let curr_ctb_rs = (y0 as usize >> log2_ctb) * ctb_w + (x0 as usize >> log2_ctb);
let n_ctb_rs = (y_n as usize >> log2_ctb) * ctb_w + (x_n as usize >> log2_ctb);
if state.tab_slice_addr_rs[n_ctb_rs] != state.tab_slice_addr_rs[curr_ctb_rs] {
return false;
}
if state.tab_tile_id[n_ctb_rs] != state.tab_tile_id[curr_ctb_rs] {
return false;
}
let mvf = tab_mvf_at(state, x_n, y_n);
mvf.pred_flag != 0
}
#[allow(clippy::too_many_arguments)]
fn build_merge_candidates<P: Pixel>(
state: &PictureState<P>,
slice_params: &SliceParams,
x0: u32,
y0: u32,
n_pb_w: u32,
n_pb_h: u32,
_log2_cb_size: u8,
part_mode: PartMode,
part_idx: u8,
single_mcl_flag: bool,
) -> Vec<MvField> {
let max_cand = slice_params.max_num_merge_cand as usize;
let mut list: Vec<MvField> = Vec::with_capacity(max_cand);
let x0i = x0 as i32;
let y0i = y0 as i32;
let w = n_pb_w as i32;
let h = n_pb_h as i32;
let pl = slice_params.log2_parallel_merge_level;
let x_a1 = x0i - 1;
let y_a1 = y0i + h - 1;
let x_b1 = x0i + w - 1;
let y_b1 = y0i - 1;
let x_b0 = x0i + w;
let y_b0 = y0i - 1;
let x_a0 = x0i - 1;
let y_a0 = y0i + h;
let x_b2 = x0i - 1;
let y_b2 = y0i - 1;
let is_available_a1 = if !single_mcl_flag
&& part_idx == 1
&& matches!(
part_mode,
PartMode::PartNx2N | PartMode::PartnLx2N | PartMode::PartnRx2N
)
|| is_diff_mer(pl, x_a1, y_a1, x0i, y0i)
{
false
} else {
spatial_cand_available(state, x0i, y0i, x_a1, y_a1)
};
if is_available_a1 {
list.push(tab_mvf_at(state, x_a1, y_a1));
if list.len() >= max_cand {
return list;
}
}
let is_available_b1 = if !single_mcl_flag
&& part_idx == 1
&& matches!(
part_mode,
PartMode::Part2NxN | PartMode::Part2NxnU | PartMode::Part2NxnD
)
|| is_diff_mer(pl, x_b1, y_b1, x0i, y0i)
{
false
} else {
spatial_cand_available(state, x0i, y0i, x_b1, y_b1)
};
if is_available_b1
&& !(is_available_a1
&& tab_mvf_at(state, x_b1, y_b1).merge_eq(&tab_mvf_at(state, x_a1, y_a1)))
{
list.push(tab_mvf_at(state, x_b1, y_b1));
if list.len() >= max_cand {
return list;
}
}
let is_available_b0 = spatial_cand_available(state, x0i, y0i, x_b0, y_b0)
&& x_b0 < state.width as i32
&& !is_diff_mer(pl, x_b0, y_b0, x0i, y0i)
&& !(is_available_b1
&& tab_mvf_at(state, x_b0, y_b0).merge_eq(&tab_mvf_at(state, x_b1, y_b1)));
if is_available_b0 {
list.push(tab_mvf_at(state, x_b0, y_b0));
if list.len() >= max_cand {
return list;
}
}
let is_available_a0 = spatial_cand_available(state, x0i, y0i, x_a0, y_a0)
&& y_a0 < state.height as i32
&& !is_diff_mer(pl, x_a0, y_a0, x0i, y0i)
&& !(is_available_a1
&& tab_mvf_at(state, x_a0, y_a0).merge_eq(&tab_mvf_at(state, x_a1, y_a1)));
if is_available_a0 {
list.push(tab_mvf_at(state, x_a0, y_a0));
if list.len() >= max_cand {
return list;
}
}
if list.len() < 4 {
#[allow(clippy::nonminimal_bool)]
let is_available_b2 = spatial_cand_available(state, x0i, y0i, x_b2, y_b2)
&& !is_diff_mer(pl, x_b2, y_b2, x0i, y0i)
&& !(is_available_a1
&& tab_mvf_at(state, x_b2, y_b2).merge_eq(&tab_mvf_at(state, x_a1, y_a1)))
&& !(is_available_b1
&& tab_mvf_at(state, x_b2, y_b2).merge_eq(&tab_mvf_at(state, x_b1, y_b1)));
if is_available_b2 {
list.push(tab_mvf_at(state, x_b2, y_b2));
if list.len() >= max_cand {
return list;
}
}
}
if slice_params.slice_temporal_mvp_enabled_flag && list.len() < max_cand {
let mut mv_l0_col = Mv::default();
let mut mv_l1_col = Mv::default();
let available_l0 = temporal_luma_motion_vector(
state,
slice_params,
x0i,
y0i,
w,
h,
0, 0, );
let available_l1 = if slice_params.slice_type == SliceType::B {
temporal_luma_motion_vector(state, slice_params, x0i, y0i, w, h, 0, 1)
} else {
None
};
if available_l0.is_some() || available_l1.is_some() {
if let Some(mv) = available_l0 {
mv_l0_col = mv;
}
if let Some(mv) = available_l1 {
mv_l1_col = mv;
}
let pred_flag = available_l0.is_some() as u8 | ((available_l1.is_some() as u8) << 1);
list.push(MvField {
mv: [mv_l0_col, mv_l1_col],
ref_idx: [0, 0],
pred_flag,
});
if list.len() >= max_cand {
return list;
}
}
}
let nb_orig_merge_cand = list.len();
if slice_params.slice_type == SliceType::B
&& nb_orig_merge_cand > 1
&& nb_orig_merge_cand < max_cand
{
const L0_L1_CAND_IDX: [[usize; 2]; 12] = [
[0, 1],
[1, 0],
[0, 2],
[2, 0],
[1, 2],
[2, 1],
[0, 3],
[3, 0],
[1, 3],
[3, 1],
[2, 3],
[3, 2],
];
let max_comb = nb_orig_merge_cand * (nb_orig_merge_cand - 1);
for entry in L0_L1_CAND_IDX.iter().take(max_comb.min(12)) {
if list.len() >= max_cand {
break;
}
let l0_idx = entry[0];
let l1_idx = entry[1];
if l0_idx >= nb_orig_merge_cand || l1_idx >= nb_orig_merge_cand {
continue;
}
let l0_cand = list[l0_idx];
let l1_cand = list[l1_idx];
if (l0_cand.pred_flag & 1 != 0)
&& (l1_cand.pred_flag & 2 != 0)
&& (slice_params.ref_pic_list_pocs[0].get(l0_cand.ref_idx[0] as usize)
!= slice_params.ref_pic_list_pocs[1].get(l1_cand.ref_idx[1] as usize)
|| l0_cand.mv[0] != l1_cand.mv[1])
{
list.push(MvField {
mv: [l0_cand.mv[0], l1_cand.mv[1]],
ref_idx: [l0_cand.ref_idx[0], l1_cand.ref_idx[1]],
pred_flag: 3, });
}
}
}
let nb_refs = if slice_params.slice_type == SliceType::P {
slice_params.num_ref_idx_l0_active
} else {
slice_params
.num_ref_idx_l0_active
.min(slice_params.num_ref_idx_l1_active)
};
let mut zero_idx: u32 = 0;
while list.len() < max_cand {
let ref0 = if zero_idx < nb_refs {
zero_idx as i8
} else {
0
};
let ref1 = if zero_idx < nb_refs {
zero_idx as i8
} else {
0
};
let pred = if slice_params.slice_type == SliceType::B {
3 } else {
1 };
list.push(MvField {
mv: [Mv::default(), Mv::default()],
ref_idx: [ref0, ref1],
pred_flag: pred,
});
zero_idx += 1;
}
list
}
fn mv_scale(mv: Mv, td: i32, tb: i32) -> Mv {
let td = td.clamp(-128, 127);
let tb = tb.clamp(-128, 127);
if td == 0 {
return mv;
}
let tx = (0x4000 + (td.abs() >> 1)) / td;
let scale_factor = ((tb * tx + 32) >> 6).clamp(-4096, 4095);
let sx = (scale_factor * mv.x as i32 + 127 + ((scale_factor * mv.x as i32) < 0) as i32) >> 8;
let sy = (scale_factor * mv.y as i32 + 127 + ((scale_factor * mv.y as i32) < 0) as i32) >> 8;
Mv {
x: sx.clamp(-32768, 32767) as i16,
y: sy.clamp(-32768, 32767) as i16,
}
}
fn check_mvset(
col_mv: Mv,
col_ref_idx: i8,
col_pic_poc: i32,
col_ref_pocs: &[i32],
curr_poc: i32,
curr_ref_poc: i32,
) -> Option<Mv> {
if col_ref_idx < 0 || (col_ref_idx as usize) >= col_ref_pocs.len() {
return None;
}
let col_ref_poc = col_ref_pocs[col_ref_idx as usize];
let col_poc_diff = col_pic_poc - col_ref_poc;
let cur_poc_diff = curr_poc - curr_ref_poc;
if col_poc_diff == cur_poc_diff || col_poc_diff == 0 {
Some(col_mv)
} else {
Some(mv_scale(col_mv, col_poc_diff, cur_poc_diff))
}
}
fn derive_temporal_colocated_mvs(
temp_col: MvField,
ref_idx_lx: i8,
x: usize,
col_pic_poc: i32,
col_ref_pocs: &[Vec<i32>; 2],
slice_params: &SliceParams,
) -> Option<Mv> {
let curr_poc = slice_params.poc;
let curr_ref_poc = if !slice_params.ref_pic_list_pocs[x].is_empty() {
slice_params.ref_pic_list_pocs[x][ref_idx_lx as usize]
} else {
return None;
};
if temp_col.pred_flag == 0 {
return None;
}
if temp_col.pred_flag & 1 == 0 {
return check_mvset(
temp_col.mv[1],
temp_col.ref_idx[1],
col_pic_poc,
&col_ref_pocs[1],
curr_poc,
curr_ref_poc,
);
} else if temp_col.pred_flag == 1 {
return check_mvset(
temp_col.mv[0],
temp_col.ref_idx[0],
col_pic_poc,
&col_ref_pocs[0],
curr_poc,
curr_ref_poc,
);
}
let mut check_diffpicount = 0;
for j in 0..2 {
for &ref_poc in &slice_params.ref_pic_list_pocs[j] {
if ref_poc > curr_poc {
check_diffpicount += 1;
break;
}
}
}
if check_diffpicount == 0 {
if x == 0 {
check_mvset(
temp_col.mv[0],
temp_col.ref_idx[0],
col_pic_poc,
&col_ref_pocs[0],
curr_poc,
curr_ref_poc,
)
} else {
check_mvset(
temp_col.mv[1],
temp_col.ref_idx[1],
col_pic_poc,
&col_ref_pocs[1],
curr_poc,
curr_ref_poc,
)
}
} else {
let col_list = if slice_params.collocated_from_l0_flag {
0usize
} else {
1usize
};
if col_list == 1 {
check_mvset(
temp_col.mv[0],
temp_col.ref_idx[0],
col_pic_poc,
&col_ref_pocs[0],
curr_poc,
curr_ref_poc,
)
} else {
check_mvset(
temp_col.mv[1],
temp_col.ref_idx[1],
col_pic_poc,
&col_ref_pocs[1],
curr_poc,
curr_ref_poc,
)
}
}
}
#[allow(clippy::too_many_arguments)]
fn temporal_luma_motion_vector<P: Pixel>(
state: &PictureState<P>,
slice_params: &SliceParams,
x0: i32,
y0: i32,
n_pb_w: i32,
n_pb_h: i32,
ref_idx_lx: i8,
x: usize, ) -> Option<Mv> {
let col_ref = slice_params.collocated_ref.as_ref()?;
if col_ref.tab_mvf.is_empty() {
return None;
}
let col_pic_poc = col_ref.poc;
let min_pu_width = col_ref.min_pu_width;
let log2_min_pu = col_ref.log2_min_pu_size;
let log2_ctb = state.log2_ctb_size;
let xbr = x0 + n_pb_w;
let ybr = y0 + n_pb_h;
let mut result: Option<Mv> = None;
if (y0 >> log2_ctb) == (ybr >> log2_ctb)
&& ybr < state.height as i32
&& xbr < state.width as i32
{
let xc = (xbr & !15) as usize;
let yc = (ybr & !15) as usize;
let x_pu = xc >> log2_min_pu;
let y_pu = yc >> log2_min_pu;
if y_pu * min_pu_width + x_pu < col_ref.tab_mvf.len() {
let temp_col = col_ref.tab_mvf[y_pu * min_pu_width + x_pu];
result = derive_temporal_colocated_mvs(
temp_col,
ref_idx_lx,
x,
col_pic_poc,
&col_ref.ref_pic_list_pocs,
slice_params,
);
}
}
if result.is_none() {
let xc = ((x0 + (n_pb_w >> 1)) & !15) as usize;
let yc = ((y0 + (n_pb_h >> 1)) & !15) as usize;
let x_pu = xc >> log2_min_pu;
let y_pu = yc >> log2_min_pu;
if y_pu * min_pu_width + x_pu < col_ref.tab_mvf.len() {
let temp_col = col_ref.tab_mvf[y_pu * min_pu_width + x_pu];
result = derive_temporal_colocated_mvs(
temp_col,
ref_idx_lx,
x,
col_pic_poc,
&col_ref.ref_pic_list_pocs,
slice_params,
);
}
}
result
}
fn amvp_same_ref_mv<P: Pixel>(
state: &PictureState<P>,
slice_params: &SliceParams,
x_n: i32,
y_n: i32,
pred_flag_idx: usize,
ref_idx_curr: usize,
ref_idx: i8,
) -> Option<Mv> {
let mvf = tab_mvf_at(state, x_n, y_n);
if (mvf.pred_flag & (1 << pred_flag_idx)) == 0 {
return None;
}
let neighbor_ref_idx = mvf.ref_idx[pred_flag_idx] as usize;
let neighbor_poc = slice_params.ref_pic_list_pocs[pred_flag_idx]
.get(neighbor_ref_idx)
.copied()?;
let curr_poc = slice_params.ref_pic_list_pocs[ref_idx_curr]
.get(ref_idx as usize)
.copied()?;
if neighbor_poc == curr_poc {
Some(mvf.mv[pred_flag_idx])
} else {
None
}
}
fn amvp_scaled_ref_mv<P: Pixel>(
state: &PictureState<P>,
slice_params: &SliceParams,
x_n: i32,
y_n: i32,
pred_flag_idx: usize,
ref_idx_curr: usize,
ref_idx: i8,
) -> Option<Mv> {
let mvf = tab_mvf_at(state, x_n, y_n);
if (mvf.pred_flag & (1 << pred_flag_idx)) == 0 {
return None;
}
let neighbor_ref_idx = mvf.ref_idx[pred_flag_idx] as usize;
let neighbor_ref_poc = slice_params.ref_pic_list_pocs[pred_flag_idx]
.get(neighbor_ref_idx)
.copied()?;
let curr_ref_poc = slice_params.ref_pic_list_pocs[ref_idx_curr]
.get(ref_idx as usize)
.copied()?;
let neighbor_mv = mvf.mv[pred_flag_idx];
if neighbor_ref_poc == curr_ref_poc {
Some(neighbor_mv)
} else {
let td = slice_params.poc - neighbor_ref_poc;
let tb = slice_params.poc - curr_ref_poc;
Some(mv_scale(neighbor_mv, td, tb))
}
}
#[allow(clippy::too_many_arguments)]
fn amvp_spatial_candidate<P: Pixel>(
state: &PictureState<P>,
slice_params: &SliceParams,
x0: i32,
y0: i32,
positions: &[(i32, i32)],
ref_idx_curr: usize,
ref_idx: i8,
same_ref_only: bool,
) -> (Option<Mv>, bool) {
let pred_flag_l0 = ref_idx_curr;
let pred_flag_l1 = 1 - ref_idx_curr;
for &(x_n, y_n) in positions {
if !spatial_cand_available(state, x0, y0, x_n, y_n) {
continue;
}
if let Some(mv) = amvp_same_ref_mv(
state,
slice_params,
x_n,
y_n,
pred_flag_l0,
ref_idx_curr,
ref_idx,
) {
return (Some(mv), true);
}
if let Some(mv) = amvp_same_ref_mv(
state,
slice_params,
x_n,
y_n,
pred_flag_l1,
ref_idx_curr,
ref_idx,
) {
return (Some(mv), true);
}
}
if same_ref_only {
return (None, false);
}
for &(x_n, y_n) in positions {
if !spatial_cand_available(state, x0, y0, x_n, y_n) {
continue;
}
if let Some(mv) = amvp_scaled_ref_mv(
state,
slice_params,
x_n,
y_n,
pred_flag_l0,
ref_idx_curr,
ref_idx,
) {
return (Some(mv), true);
}
if let Some(mv) = amvp_scaled_ref_mv(
state,
slice_params,
x_n,
y_n,
pred_flag_l1,
ref_idx_curr,
ref_idx,
) {
return (Some(mv), true);
}
}
(None, false)
}
#[allow(clippy::too_many_arguments)]
fn build_amvp_candidates<P: Pixel>(
state: &PictureState<P>,
slice_params: &SliceParams,
x0: u32,
y0: u32,
n_pb_w: u32,
n_pb_h: u32,
ref_idx: i8,
list_idx: usize,
) -> [Mv; 2] {
let mut mvp_list = [Mv::default(); 2];
let mut num_cand = 0usize;
let x0i = x0 as i32;
let y0i = y0 as i32;
let w = n_pb_w as i32;
let h = n_pb_h as i32;
let left_positions = [
(x0i - 1, y0i + h), (x0i - 1, y0i + h - 1), ];
let is_scaled_flag_l0 = left_positions.iter().any(|&(x_n, y_n)| {
if y_n >= state.height as i32 {
return false;
}
spatial_cand_available(state, x0i, y0i, x_n, y_n)
});
let (left_mv, left_available) = amvp_spatial_candidate(
state,
slice_params,
x0i,
y0i,
&left_positions,
list_idx,
ref_idx,
false, );
let above_positions = [
(x0i + w, y0i - 1), (x0i + w - 1, y0i - 1), (x0i - 1, y0i - 1), ];
let (above_mv, above_available) = amvp_spatial_candidate(
state,
slice_params,
x0i,
y0i,
&above_positions,
list_idx,
ref_idx,
is_scaled_flag_l0, );
let (left_mv, left_available, above_mv, above_available) = if !is_scaled_flag_l0 {
if above_available {
let new_left_mv = above_mv;
let new_left_available = true;
let pred_flag_l0 = list_idx;
let pred_flag_l1 = 1 - list_idx;
let mut new_above_mv = None;
for &(x_n, y_n) in &above_positions {
if !spatial_cand_available(state, x0i, y0i, x_n, y_n) {
continue;
}
if let Some(mv) = amvp_scaled_ref_mv(
state,
slice_params,
x_n,
y_n,
pred_flag_l0,
list_idx,
ref_idx,
) {
new_above_mv = Some(mv);
break;
}
if let Some(mv) = amvp_scaled_ref_mv(
state,
slice_params,
x_n,
y_n,
pred_flag_l1,
list_idx,
ref_idx,
) {
new_above_mv = Some(mv);
break;
}
}
(
new_left_mv,
new_left_available,
new_above_mv,
new_above_mv.is_some(),
)
} else {
(left_mv, left_available, above_mv, above_available)
}
} else {
(left_mv, left_available, above_mv, above_available)
};
if left_available {
mvp_list[num_cand] = left_mv.unwrap_or_default();
num_cand += 1;
}
if above_available {
let above = above_mv.unwrap_or_default();
if !left_available || above != left_mv.unwrap_or_default() {
mvp_list[num_cand] = above;
num_cand += 1;
}
}
if num_cand < 2
&& slice_params.slice_temporal_mvp_enabled_flag
&& let Some(mv_col) = temporal_luma_motion_vector(
state,
slice_params,
x0 as i32,
y0 as i32,
n_pb_w as i32,
n_pb_h as i32,
ref_idx,
list_idx,
)
{
mvp_list[num_cand] = mv_col;
num_cand += 1;
}
let _ = num_cand;
mvp_list
}
#[allow(clippy::too_many_arguments)]
fn decode_prediction_unit<P: Pixel>(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
state: &mut PictureState<P>,
slice_params: &SliceParams,
x0: u32,
y0: u32,
n_pb_w: u32,
n_pb_h: u32,
log2_cb_size: u8,
is_skip: bool,
cb_depth: u8,
part_mode: PartMode,
part_idx: u8,
) -> Result<bool, DecodeError> {
let mut current_mv = MvField::default();
let merge_flag = if is_skip {
true
} else {
cabac.decode_bin(&mut contexts.state[ctx::MERGE_FLAG]) != 0
};
if merge_flag {
let merge_idx = if slice_params.max_num_merge_cand > 1 {
decode_merge_idx(cabac, contexts, slice_params.max_num_merge_cand)
} else {
0
};
let n_cs = 1u32 << log2_cb_size;
let single_mcl_flag = slice_params.log2_parallel_merge_level > 2 && n_cs == 8;
let (mx0, my0, mw, mh, m_part_idx) = if single_mcl_flag {
(x0, y0, n_cs, n_cs, 0u8)
} else {
(x0, y0, n_pb_w, n_pb_h, part_idx)
};
let candidates = build_merge_candidates(
state,
slice_params,
mx0,
my0,
mw,
mh,
log2_cb_size,
part_mode,
m_part_idx,
single_mcl_flag,
);
let idx = (merge_idx as usize).min(candidates.len().saturating_sub(1));
current_mv = candidates[idx];
if current_mv.pred_flag == 3 && (n_pb_w + n_pb_h) == 12 {
current_mv.pred_flag = 1; }
} else {
let inter_pred_idc = if slice_params.slice_type == SliceType::B {
decode_inter_pred_idc(cabac, contexts, n_pb_w, n_pb_h, cb_depth)
} else {
InterPredIdc::PredL0
};
if inter_pred_idc != InterPredIdc::PredL1 {
if slice_params.num_ref_idx_l0_active > 0 {
current_mv.ref_idx[0] =
decode_ref_idx(cabac, contexts, slice_params.num_ref_idx_l0_active, false)
as i8;
}
current_mv.pred_flag |= 1; let mvd = decode_mvd_coding(cabac, contexts);
let mvp_l0_flag = cabac.decode_bin(&mut contexts.state[ctx::MVP_LX_FLAG]);
let mvp_list = build_amvp_candidates(
state,
slice_params,
x0,
y0,
n_pb_w,
n_pb_h,
current_mv.ref_idx[0],
0,
);
let mvp = mvp_list[mvp_l0_flag as usize];
current_mv.mv[0] = Mv {
x: (mvp.x as i32 + mvd.x as i32).clamp(-32768, 32767) as i16,
y: (mvp.y as i32 + mvd.y as i32).clamp(-32768, 32767) as i16,
};
}
if inter_pred_idc != InterPredIdc::PredL0 {
if slice_params.num_ref_idx_l1_active > 0 {
current_mv.ref_idx[1] =
decode_ref_idx(cabac, contexts, slice_params.num_ref_idx_l1_active, true) as i8;
}
let mvd = if slice_params.mvd_l1_zero_flag && inter_pred_idc == InterPredIdc::PredBi {
Mv::default()
} else {
decode_mvd_coding(cabac, contexts)
};
current_mv.pred_flag |= 2; let mvp_l1_flag = cabac.decode_bin(&mut contexts.state[ctx::MVP_LX_FLAG]);
let mvp_list = build_amvp_candidates(
state,
slice_params,
x0,
y0,
n_pb_w,
n_pb_h,
current_mv.ref_idx[1],
1,
);
let mvp = mvp_list[mvp_l1_flag as usize];
current_mv.mv[1] = Mv {
x: (mvp.x as i32 + mvd.x as i32).clamp(-32768, 32767) as i16,
y: (mvp.y as i32 + mvd.y as i32).clamp(-32768, 32767) as i16,
};
}
}
let x_pu = (x0 >> state.log2_min_pu_size) as usize;
let y_pu = (y0 >> state.log2_min_pu_size) as usize;
let pu_w = (n_pb_w >> state.log2_min_pu_size).max(1) as usize;
let pu_h = (n_pb_h >> state.log2_min_pu_size).max(1) as usize;
for j in 0..pu_h {
for i in 0..pu_w {
state.tab_mvf[(y_pu + j) * state.min_pu_width + x_pu + i] = current_mv;
}
}
Ok(merge_flag)
}
fn decode_merge_idx(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
max_num_merge_cand: u32,
) -> u32 {
let mut i = cabac.decode_bin(&mut contexts.state[ctx::MERGE_IDX]);
if i != 0 {
while i < max_num_merge_cand - 1 && cabac.decode_bypass() != 0 {
i += 1;
}
}
i
}
fn decode_inter_pred_idc(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
n_pb_w: u32,
n_pb_h: u32,
cb_depth: u8,
) -> InterPredIdc {
if n_pb_w + n_pb_h == 12 {
let v = cabac.decode_bin(&mut contexts.state[ctx::INTER_PRED_IDC + 4]);
return if v != 0 {
InterPredIdc::PredL1
} else {
InterPredIdc::PredL0
};
}
if cabac.decode_bin(&mut contexts.state[ctx::INTER_PRED_IDC + cb_depth as usize]) != 0 {
return InterPredIdc::PredBi;
}
let v = cabac.decode_bin(&mut contexts.state[ctx::INTER_PRED_IDC + 4]);
if v != 0 {
InterPredIdc::PredL1
} else {
InterPredIdc::PredL0
}
}
fn decode_ref_idx(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
num_ref_idx_active: u32,
is_l1: bool,
) -> u32 {
let max = num_ref_idx_active - 1;
if max == 0 {
return 0;
}
let _ = is_l1;
let ctx_base = ctx::REF_IDX_L0;
let max_ctx = max.min(2) as usize;
let mut i = 0u32;
while (i as usize) < max_ctx
&& cabac.decode_bin(&mut contexts.state[ctx_base + i as usize]) != 0
{
i += 1;
}
if i == 2 {
while i < max && cabac.decode_bypass() != 0 {
i += 1;
}
}
i
}
fn decode_mvd_coding(cabac: &mut CabacReader, contexts: &mut CabacContexts) -> Mv {
let mut x_val = cabac.decode_bin(&mut contexts.state[ctx::ABS_MVD_GREATER0_FLAG]) as i32;
let mut y_val = cabac.decode_bin(&mut contexts.state[ctx::ABS_MVD_GREATER0_FLAG]) as i32;
if x_val != 0 {
x_val += cabac.decode_bin(&mut contexts.state[ctx::ABS_MVD_GREATER1_FLAG + 1]) as i32;
}
if y_val != 0 {
y_val += cabac.decode_bin(&mut contexts.state[ctx::ABS_MVD_GREATER1_FLAG + 1]) as i32;
}
let mvd_x = match x_val {
2 => decode_mvd_abs(cabac),
1 => decode_mvd_sign(cabac),
_ => 0,
};
let mvd_y = match y_val {
2 => decode_mvd_abs(cabac),
1 => decode_mvd_sign(cabac),
_ => 0,
};
Mv {
x: mvd_x as i16,
y: mvd_y as i16,
}
}
fn decode_mvd_abs(cabac: &mut CabacReader) -> i32 {
let mut ret: i32 = 2;
let mut k: u32 = 1;
const CABAC_MAX_BIN: u32 = 31;
while k < CABAC_MAX_BIN && cabac.decode_bypass() != 0 {
ret += 1 << k;
k += 1;
}
let mut kk = k;
while kk > 0 {
kk -= 1;
ret += (cabac.decode_bypass() as i32) << kk;
}
if cabac.decode_bypass() != 0 {
-ret
} else {
ret
}
}
fn decode_mvd_sign(cabac: &mut CabacReader) -> i32 {
if cabac.decode_bypass() != 0 { -1 } else { 1 }
}
fn inter_boundary_strength(
curr: &MvField,
neigh: &MvField,
curr_ref_pocs: &[Vec<i32>; 2],
neigh_ref_pocs: &[Vec<i32>; 2],
) -> u8 {
if curr.pred_flag == 3 && neigh.pred_flag == 3 {
let curr_l0_poc = curr_ref_pocs[0]
.get(curr.ref_idx[0] as usize)
.copied()
.unwrap_or(-1);
let curr_l1_poc = curr_ref_pocs[1]
.get(curr.ref_idx[1] as usize)
.copied()
.unwrap_or(-1);
let neigh_l0_poc = neigh_ref_pocs[0]
.get(neigh.ref_idx[0] as usize)
.copied()
.unwrap_or(-1);
let neigh_l1_poc = neigh_ref_pocs[1]
.get(neigh.ref_idx[1] as usize)
.copied()
.unwrap_or(-1);
if curr_l0_poc == neigh_l0_poc && curr_l0_poc == curr_l1_poc && neigh_l0_poc == neigh_l1_poc
{
let order_a = (neigh.mv[0].x - curr.mv[0].x).abs() >= 4
|| (neigh.mv[0].y - curr.mv[0].y).abs() >= 4
|| (neigh.mv[1].x - curr.mv[1].x).abs() >= 4
|| (neigh.mv[1].y - curr.mv[1].y).abs() >= 4;
let order_b = (neigh.mv[1].x - curr.mv[0].x).abs() >= 4
|| (neigh.mv[1].y - curr.mv[0].y).abs() >= 4
|| (neigh.mv[0].x - curr.mv[1].x).abs() >= 4
|| (neigh.mv[0].y - curr.mv[1].y).abs() >= 4;
if order_a && order_b { 1 } else { 0 }
} else if curr_l0_poc == neigh_l0_poc && curr_l1_poc == neigh_l1_poc {
if (neigh.mv[0].x - curr.mv[0].x).abs() >= 4
|| (neigh.mv[0].y - curr.mv[0].y).abs() >= 4
|| (neigh.mv[1].x - curr.mv[1].x).abs() >= 4
|| (neigh.mv[1].y - curr.mv[1].y).abs() >= 4
{
1
} else {
0
}
} else if curr_l1_poc == neigh_l0_poc && curr_l0_poc == neigh_l1_poc {
if (neigh.mv[1].x - curr.mv[0].x).abs() >= 4
|| (neigh.mv[1].y - curr.mv[0].y).abs() >= 4
|| (neigh.mv[0].x - curr.mv[1].x).abs() >= 4
|| (neigh.mv[0].y - curr.mv[1].y).abs() >= 4
{
1
} else {
0
}
} else {
1
}
} else if curr.pred_flag != 3 && neigh.pred_flag != 3 {
let (a, ref_a_poc) = if curr.pred_flag & 1 != 0 {
(
curr.mv[0],
curr_ref_pocs[0]
.get(curr.ref_idx[0] as usize)
.copied()
.unwrap_or(-1),
)
} else {
(
curr.mv[1],
curr_ref_pocs[1]
.get(curr.ref_idx[1] as usize)
.copied()
.unwrap_or(-1),
)
};
let (b, ref_b_poc) = if neigh.pred_flag & 1 != 0 {
(
neigh.mv[0],
neigh_ref_pocs[0]
.get(neigh.ref_idx[0] as usize)
.copied()
.unwrap_or(-1),
)
} else {
(
neigh.mv[1],
neigh_ref_pocs[1]
.get(neigh.ref_idx[1] as usize)
.copied()
.unwrap_or(-1),
)
};
if ref_a_poc == ref_b_poc {
if (a.x - b.x).abs() >= 4 || (a.y - b.y).abs() >= 4 {
1
} else {
0
}
} else {
1
}
} else {
1
}
}
fn compute_deblocking_boundary_strengths<P: Pixel>(
state: &mut PictureState<P>,
slice_params: &SliceParams,
x0: u32,
y0: u32,
log2_size: u8,
) {
let size = 1u32 << log2_size;
let pic_w = state.width as usize;
let bs_w = pic_w >> 2;
let log2_min_pu = state.log2_min_pu_size as u32;
let min_pu_w = state.min_pu_width;
let log2_min_tb = state.log2_min_tb_size;
let min_tb_w = state.min_tb_width;
let x_pu = (x0 >> log2_min_pu) as usize;
let y_pu = (y0 >> log2_min_pu) as usize;
let is_intra = state.tab_mvf[y_pu * min_pu_w + x_pu].pred_flag == 0;
let curr_ref_pocs = &slice_params.ref_pic_list_pocs;
if y0 > 0 && (y0 & 7) == 0 {
let yp_pu = ((y0 - 1) >> log2_min_pu) as usize;
let yq_pu = (y0 >> log2_min_pu) as usize;
let yp_tu = ((y0 - 1) >> log2_min_tb) as usize;
let yq_tu = (y0 >> log2_min_tb) as usize;
let yy = (y0 >> 2) as usize;
for i in (0..size).step_by(4) {
let x_cur = x0 + i;
let xx = (x_cur >> 2) as usize;
let xpu = (x_cur >> log2_min_pu) as usize;
let xtu = (x_cur >> log2_min_tb) as usize;
let top_mvf = &state.tab_mvf[yp_pu * min_pu_w + xpu];
let curr_mvf = &state.tab_mvf[yq_pu * min_pu_w + xpu];
let bs = if curr_mvf.pred_flag == 0 || top_mvf.pred_flag == 0 {
2
} else {
let top_cbf = state.tab_cbf_luma[yp_tu * min_tb_w + xtu];
let curr_cbf = state.tab_cbf_luma[yq_tu * min_tb_w + xtu];
if top_cbf != 0 || curr_cbf != 0 {
1
} else {
inter_boundary_strength(curr_mvf, top_mvf, curr_ref_pocs, curr_ref_pocs)
}
};
state.bs_horizontal[yy * bs_w + xx] = bs;
}
}
if x0 > 0 && (x0 & 7) == 0 {
let xp_pu = ((x0 - 1) >> log2_min_pu) as usize;
let xq_pu = (x0 >> log2_min_pu) as usize;
let xp_tu = ((x0 - 1) >> log2_min_tb) as usize;
let xq_tu = (x0 >> log2_min_tb) as usize;
let xx = (x0 >> 2) as usize;
for i in (0..size).step_by(4) {
let y_cur = y0 + i;
let yy = (y_cur >> 2) as usize;
let ypu = (y_cur >> log2_min_pu) as usize;
let ytu = (y_cur >> log2_min_tb) as usize;
let left_mvf = &state.tab_mvf[ypu * min_pu_w + xp_pu];
let curr_mvf = &state.tab_mvf[ypu * min_pu_w + xq_pu];
let bs = if curr_mvf.pred_flag == 0 || left_mvf.pred_flag == 0 {
2
} else {
let left_cbf = state.tab_cbf_luma[ytu * min_tb_w + xp_tu];
let curr_cbf = state.tab_cbf_luma[ytu * min_tb_w + xq_tu];
if left_cbf != 0 || curr_cbf != 0 {
1
} else {
inter_boundary_strength(curr_mvf, left_mvf, curr_ref_pocs, curr_ref_pocs)
}
};
state.bs_vertical[yy * bs_w + xx] = bs;
}
}
if (log2_size as u32) > log2_min_pu && !is_intra {
for j in (8..size).step_by(8) {
let yp_pu = ((y0 + j - 1) >> log2_min_pu) as usize;
let yq_pu = ((y0 + j) >> log2_min_pu) as usize;
let yy = ((y0 + j) >> 2) as usize;
for i in (0..size).step_by(4) {
let x_cur = x0 + i;
let xx = (x_cur >> 2) as usize;
let xpu = (x_cur >> log2_min_pu) as usize;
let top_mvf = &state.tab_mvf[yp_pu * min_pu_w + xpu];
let curr_mvf = &state.tab_mvf[yq_pu * min_pu_w + xpu];
let bs = inter_boundary_strength(curr_mvf, top_mvf, curr_ref_pocs, curr_ref_pocs);
state.bs_horizontal[yy * bs_w + xx] = bs;
}
}
for j in (0..size).step_by(4) {
let ypu = ((y0 + j) >> log2_min_pu) as usize;
let yy = ((y0 + j) >> 2) as usize;
for i in (8..size).step_by(8) {
let xp_pu = ((x0 + i - 1) >> log2_min_pu) as usize;
let xq_pu = ((x0 + i) >> log2_min_pu) as usize;
let xx = ((x0 + i) >> 2) as usize;
let left_mvf = &state.tab_mvf[ypu * min_pu_w + xp_pu];
let curr_mvf = &state.tab_mvf[ypu * min_pu_w + xq_pu];
let bs = inter_boundary_strength(curr_mvf, left_mvf, curr_ref_pocs, curr_ref_pocs);
state.bs_vertical[yy * bs_w + xx] = bs;
}
}
}
}
#[allow(clippy::too_many_arguments)]
fn decode_coding_unit<P: Pixel>(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
state: &mut PictureState<P>,
sps: &Sps,
pps: &Pps,
slice_qp_y: i32,
slice_params: &SliceParams,
x0: u32,
y0: u32,
log2_cb_size: u8,
cb_depth: u8,
) -> Result<(), DecodeError> {
let cu_transquant_bypass = if pps.transquant_bypass_enabled_flag {
cabac.decode_bin(&mut contexts.state[ctx::CU_TRANSQUANT_BYPASS_FLAG]) != 0
} else {
false
};
if slice_params.cu_chroma_qp_offset_enabled_flag && !cu_transquant_bypass {
state.is_cu_chroma_qp_offset_coded = false;
state.cu_qp_offset_cb = 0;
state.cu_qp_offset_cr = 0;
}
let cb_size = 1u32 << log2_cb_size;
let x_cb = (x0 >> state.log2_min_cb_size) as usize;
let y_cb = (y0 >> state.log2_min_cb_size) as usize;
let length = (cb_size >> state.log2_min_cb_size) as usize;
let mut pred_mode = PredMode::Intra;
if slice_params.slice_type != SliceType::I {
let skip_flag = decode_skip_flag(cabac, contexts, state, x0, y0);
for j in 0..length {
let row = (y_cb + j) * state.min_cb_width;
for i in 0..length {
state.tab_skip_flag[row + x_cb + i] = skip_flag as u8;
}
}
pred_mode = if skip_flag != 0 {
PredMode::Skip
} else {
PredMode::Inter
};
} else {
for j in 0..length {
let row = (y_cb + j) * state.min_cb_width;
for i in 0..length {
state.tab_skip_flag[row + x_cb + i] = 0;
}
}
}
if pred_mode == PredMode::Skip {
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0,
y0,
cb_size,
cb_size,
log2_cb_size,
true,
cb_depth,
PartMode::Part2Nx2N,
0,
)?;
write_intra_pred_mode(state, x0, y0, cb_size, INTRA_DC);
inter_pred::motion_compensation_cu(
state,
&slice_params.ref_frames_l0,
&slice_params.ref_frames_l1,
x0,
y0,
cb_size,
PartMode::Part2Nx2N,
slice_params.weighted_pred_flag,
&slice_params.pred_weight_table,
);
if pps.cu_qp_delta_enabled_flag && !state.is_cu_qp_delta_coded {
set_qpy(state, sps, pps, slice_qp_y, x0, y0);
}
write_qp_y_table(state, x0, y0, log2_cb_size, state.last_qp_y);
maybe_save_qpy_pred(state, sps, pps, x0, y0, log2_cb_size);
compute_deblocking_boundary_strengths(state, slice_params, x0, y0, log2_cb_size);
state.cu_count += 1;
return Ok(());
}
if slice_params.slice_type != SliceType::I {
let pm_flag = cabac.decode_bin(&mut contexts.state[ctx::PRED_MODE_FLAG]);
pred_mode = if pm_flag != 0 {
PredMode::Intra
} else {
PredMode::Inter
};
}
let part_mode = if pred_mode != PredMode::Intra || log2_cb_size == state.log2_min_cb_size {
decode_part_mode(cabac, contexts, sps, log2_cb_size, pred_mode)
} else {
PartMode::Part2Nx2N
};
let intra_split = part_mode == PartMode::PartNxN && pred_mode == PredMode::Intra;
let mut merge_flag_for_rqt = false;
if pred_mode == PredMode::Intra {
let pcm_allowed = sps.pcm_enabled_flag
&& part_mode == PartMode::Part2Nx2N
&& log2_cb_size >= sps.log2_min_pcm_cb_size
&& log2_cb_size <= sps.log2_max_pcm_cb_size;
if pcm_allowed && cabac.decode_terminate() != 0 {
decode_pcm_block(cabac, state, sps, x0, y0, log2_cb_size)?;
state.cu_count += 1;
return Ok(());
}
decode_intra_mode_signaling(cabac, contexts, state, x0, y0, log2_cb_size, part_mode)?;
} else {
write_intra_pred_mode(state, x0, y0, cb_size, INTRA_DC);
match part_mode {
PartMode::Part2Nx2N => {
merge_flag_for_rqt = decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0,
y0,
cb_size,
cb_size,
log2_cb_size,
false,
cb_depth,
part_mode,
0,
)?;
}
PartMode::Part2NxN => {
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0,
y0,
cb_size,
cb_size / 2,
log2_cb_size,
false,
cb_depth,
part_mode,
0,
)?;
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0,
y0 + cb_size / 2,
cb_size,
cb_size / 2,
log2_cb_size,
false,
cb_depth,
part_mode,
1,
)?;
}
PartMode::PartNx2N => {
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0,
y0,
cb_size / 2,
cb_size,
log2_cb_size,
false,
cb_depth,
part_mode,
0,
)?;
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0 + cb_size / 2,
y0,
cb_size / 2,
cb_size,
log2_cb_size,
false,
cb_depth,
part_mode,
1,
)?;
}
PartMode::Part2NxnU => {
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0,
y0,
cb_size,
cb_size / 4,
log2_cb_size,
false,
cb_depth,
part_mode,
0,
)?;
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0,
y0 + cb_size / 4,
cb_size,
cb_size * 3 / 4,
log2_cb_size,
false,
cb_depth,
part_mode,
1,
)?;
}
PartMode::Part2NxnD => {
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0,
y0,
cb_size,
cb_size * 3 / 4,
log2_cb_size,
false,
cb_depth,
part_mode,
0,
)?;
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0,
y0 + cb_size * 3 / 4,
cb_size,
cb_size / 4,
log2_cb_size,
false,
cb_depth,
part_mode,
1,
)?;
}
PartMode::PartnLx2N => {
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0,
y0,
cb_size / 4,
cb_size,
log2_cb_size,
false,
cb_depth,
part_mode,
0,
)?;
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0 + cb_size / 4,
y0,
cb_size * 3 / 4,
cb_size,
log2_cb_size,
false,
cb_depth,
part_mode,
1,
)?;
}
PartMode::PartnRx2N => {
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0,
y0,
cb_size * 3 / 4,
cb_size,
log2_cb_size,
false,
cb_depth,
part_mode,
0,
)?;
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0 + cb_size * 3 / 4,
y0,
cb_size / 4,
cb_size,
log2_cb_size,
false,
cb_depth,
part_mode,
1,
)?;
}
PartMode::PartNxN => {
let half = cb_size / 2;
let mut pidx = 0u8;
for pi in 0..2u32 {
for pj in 0..2u32 {
decode_prediction_unit(
cabac,
contexts,
state,
slice_params,
x0 + pj * half,
y0 + pi * half,
half,
half,
log2_cb_size,
false,
cb_depth,
part_mode,
pidx,
)?;
pidx += 1;
}
}
}
}
inter_pred::motion_compensation_cu(
state,
&slice_params.ref_frames_l0,
&slice_params.ref_frames_l1,
x0,
y0,
cb_size,
part_mode,
slice_params.weighted_pred_flag,
&slice_params.pred_weight_table,
);
}
{
let rqt_root_cbf = if pred_mode != PredMode::Intra
&& !(part_mode == PartMode::Part2Nx2N && merge_flag_for_rqt)
{
cabac.decode_bin(&mut contexts.state[ctx::NO_RESIDUAL_DATA_FLAG]) != 0
} else {
pred_mode == PredMode::Intra || (part_mode == PartMode::Part2Nx2N && merge_flag_for_rqt)
};
if rqt_root_cbf {
let max_trafo_depth = if pred_mode == PredMode::Intra {
sps.max_transform_hierarchy_depth_intra + if intra_split { 1 } else { 0 }
} else {
sps.max_transform_hierarchy_depth_inter
};
let inter_split = sps.max_transform_hierarchy_depth_inter == 0
&& pred_mode != PredMode::Intra
&& part_mode != PartMode::Part2Nx2N;
decode_transform_tree(
cabac,
contexts,
state,
sps,
pps,
slice_qp_y,
pred_mode,
x0,
y0,
x0,
y0,
log2_cb_size,
log2_cb_size,
0,
max_trafo_depth,
intra_split,
inter_split,
0,
TransformTreeCbf::default(),
slice_params,
cu_transquant_bypass,
)?;
} else {
compute_deblocking_boundary_strengths(state, slice_params, x0, y0, log2_cb_size);
}
}
if pps.cu_qp_delta_enabled_flag && !state.is_cu_qp_delta_coded {
set_qpy(state, sps, pps, slice_qp_y, x0, y0);
}
write_qp_y_table(state, x0, y0, log2_cb_size, state.last_qp_y);
maybe_save_qpy_pred(state, sps, pps, x0, y0, log2_cb_size);
state.cu_count += 1;
Ok(())
}
fn decode_pcm_block<P: Pixel>(
cabac: &mut CabacReader,
state: &mut PictureState<P>,
sps: &Sps,
x0: u32,
y0: u32,
log2_cb_size: u8,
) -> Result<(), DecodeError> {
let cb_size = 1usize << log2_cb_size;
let pcm_bd_luma: u8 = sps.pcm_sample_bit_depth_luma;
let pcm_bd_chroma: u8 = sps.pcm_sample_bit_depth_chroma;
let luma_shift: u8 = sps.bit_depth_luma - pcm_bd_luma;
let chroma_shift: u8 = sps.bit_depth_chroma - pcm_bd_chroma;
let pcm_start = cabac.pcm_byte_position();
let cb_chroma = cb_size / 2;
let length_bits = cb_size * cb_size * pcm_bd_luma as usize
+ 2 * cb_chroma * cb_chroma * pcm_bd_chroma as usize;
let length_bytes = length_bits.div_ceil(8);
if pcm_start + length_bytes > cabac.rbsp().len() {
return Err(DecodeError::UnexpectedEof);
}
let pcm_bytes: Vec<u8> = cabac.rbsp()[pcm_start..pcm_start + length_bytes].to_vec();
let mut reader = PcmBitReader::new(&pcm_bytes);
{
let stride = state.y_stride;
let dst_off = (y0 as usize) * stride + (x0 as usize);
for j in 0..cb_size {
for i in 0..cb_size {
let sample = reader.read_bits(pcm_bd_luma);
state.y_plane[dst_off + j * stride + i] =
P::from_i32_clamped((sample << luma_shift) as i32, state.bit_depth);
}
}
}
{
let stride = state.uv_stride;
let x_c = (x0 as usize) >> 1;
let y_c = (y0 as usize) >> 1;
let dst_off = y_c * stride + x_c;
for plane_idx in 0..2 {
let plane = if plane_idx == 0 {
&mut state.u_plane
} else {
&mut state.v_plane
};
for j in 0..cb_chroma {
for i in 0..cb_chroma {
let sample = reader.read_bits(pcm_bd_chroma);
plane[dst_off + j * stride + i] =
P::from_i32_clamped((sample << chroma_shift) as i32, state.bit_depth);
}
}
}
}
let pb_size = cb_size as u32;
write_intra_pred_mode(state, x0, y0, pb_size, INTRA_DC);
state.last_luma_pred_mode = INTRA_DC;
state.last_chroma_pred_mode = INTRA_DC;
cabac.reinit_at(pcm_start + length_bytes);
Ok(())
}
struct PcmBitReader<'a> {
data: &'a [u8],
bit_pos: usize,
}
impl<'a> PcmBitReader<'a> {
fn new(data: &'a [u8]) -> Self {
Self { data, bit_pos: 0 }
}
fn read_bits(&mut self, n: u8) -> u32 {
if n == 8 && self.bit_pos.is_multiple_of(8) {
let b = self.data[self.bit_pos / 8] as u32;
self.bit_pos += 8;
return b;
}
let mut val: u32 = 0;
for _ in 0..n {
let byte = self.data[self.bit_pos / 8] as u32;
let bit = (byte >> (7 - (self.bit_pos & 7))) & 1;
val = (val << 1) | bit;
self.bit_pos += 1;
}
val
}
}
#[derive(Debug, Clone, Copy, Default)]
struct TransformTreeCbf {
cbf_cb: bool,
cbf_cr: bool,
}
#[allow(clippy::too_many_arguments)]
#[allow(clippy::only_used_in_recursion)]
fn decode_transform_tree<P: Pixel>(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
state: &mut PictureState<P>,
sps: &Sps,
pps: &Pps,
slice_qp_y: i32,
pred_mode: PredMode,
x0: u32,
y0: u32,
x_base: u32,
y_base: u32,
log2_cb_size: u8,
log2_trafo_size: u8,
trafo_depth: u8,
max_trafo_depth: u32,
intra_split: bool,
inter_split: bool,
blk_idx: u8,
parent_cbf: TransformTreeCbf,
slice_params: &SliceParams,
cu_transquant_bypass: bool,
) -> Result<TransformTreeCbf, DecodeError> {
let split_transform_flag = if log2_trafo_size <= sps.max_tb_log2_size_y
&& log2_trafo_size > sps.min_tb_log2_size_y
&& (trafo_depth as u32) < max_trafo_depth
&& !(intra_split && trafo_depth == 0)
{
decode_split_transform_flag(cabac, contexts, log2_trafo_size) != 0
} else {
log2_trafo_size > sps.max_tb_log2_size_y || (intra_split && trafo_depth == 0) || inter_split
};
state.last_split_transform_flag = split_transform_flag;
let mut cbf_cb = parent_cbf.cbf_cb;
let mut cbf_cr = parent_cbf.cbf_cr;
if sps.chroma_format_idc == 1 && log2_trafo_size > 2 {
if trafo_depth == 0 || parent_cbf.cbf_cb {
cbf_cb = decode_cbf_cb_cr(cabac, contexts, trafo_depth) != 0;
}
if trafo_depth == 0 || parent_cbf.cbf_cr {
cbf_cr = decode_cbf_cb_cr(cabac, contexts, trafo_depth) != 0;
}
}
state.last_cbf_cb = cbf_cb;
state.last_cbf_cr = cbf_cr;
let inherited = TransformTreeCbf { cbf_cb, cbf_cr };
if split_transform_flag {
let trafo_size_split = 1u32 << (log2_trafo_size - 1);
let x1 = x0 + trafo_size_split;
let y1 = y0 + trafo_size_split;
let _ = decode_transform_tree(
cabac,
contexts,
state,
sps,
pps,
slice_qp_y,
pred_mode,
x0,
y0,
x0,
y0,
log2_cb_size,
log2_trafo_size - 1,
trafo_depth + 1,
max_trafo_depth,
intra_split,
false, 0,
inherited,
slice_params,
cu_transquant_bypass,
)?;
let _ = decode_transform_tree(
cabac,
contexts,
state,
sps,
pps,
slice_qp_y,
pred_mode,
x1,
y0,
x0,
y0,
log2_cb_size,
log2_trafo_size - 1,
trafo_depth + 1,
max_trafo_depth,
intra_split,
false,
1,
inherited,
slice_params,
cu_transquant_bypass,
)?;
let _ = decode_transform_tree(
cabac,
contexts,
state,
sps,
pps,
slice_qp_y,
pred_mode,
x0,
y1,
x0,
y0,
log2_cb_size,
log2_trafo_size - 1,
trafo_depth + 1,
max_trafo_depth,
intra_split,
false,
2,
inherited,
slice_params,
cu_transquant_bypass,
)?;
let final_cbf = decode_transform_tree(
cabac,
contexts,
state,
sps,
pps,
slice_qp_y,
pred_mode,
x1,
y1,
x0,
y0,
log2_cb_size,
log2_trafo_size - 1,
trafo_depth + 1,
max_trafo_depth,
intra_split,
false,
3,
inherited,
slice_params,
cu_transquant_bypass,
)?;
Ok(final_cbf)
} else {
decode_transform_unit(
cabac,
contexts,
state,
sps,
pps,
slice_qp_y,
pred_mode,
x0,
y0,
x_base,
y_base,
log2_trafo_size,
trafo_depth,
blk_idx,
inherited,
slice_params,
cu_transquant_bypass,
)
}
}
fn decode_split_transform_flag(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
log2_trafo_size: u8,
) -> u32 {
let inc = 5usize - log2_trafo_size as usize;
cabac.decode_bin(&mut contexts.state[ctx::SPLIT_TRANSFORM_FLAG + inc])
}
fn decode_cbf_cb_cr(cabac: &mut CabacReader, contexts: &mut CabacContexts, trafo_depth: u8) -> u32 {
cabac.decode_bin(&mut contexts.state[ctx::CBF_CB_CR + trafo_depth as usize])
}
fn decode_cbf_luma(cabac: &mut CabacReader, contexts: &mut CabacContexts, trafo_depth: u8) -> u32 {
let inc = if trafo_depth == 0 { 1 } else { 0 };
cabac.decode_bin(&mut contexts.state[ctx::CBF_LUMA + inc])
}
fn decode_cu_qp_delta_abs(cabac: &mut CabacReader, contexts: &mut CabacContexts) -> u32 {
let mut prefix = 0u32;
let mut inc = 0usize;
while prefix < 5 && cabac.decode_bin(&mut contexts.state[ctx::CU_QP_DELTA + inc]) != 0 {
prefix += 1;
inc = 1;
}
if prefix < 5 {
return prefix;
}
let mut suffix = 0u32;
let mut k = 0u32;
while k < 7 && cabac.decode_bypass() != 0 {
suffix += 1 << k;
k += 1;
}
while k > 0 {
k -= 1;
suffix += cabac.decode_bypass() << k;
}
prefix + suffix
}
fn decode_cu_qp_delta_sign_flag(cabac: &mut CabacReader) -> u32 {
cabac.decode_bypass()
}
#[allow(clippy::too_many_arguments)]
fn decode_transform_unit<P: Pixel>(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
state: &mut PictureState<P>,
sps: &Sps,
pps: &Pps,
slice_qp_y: i32,
pred_mode: PredMode,
x0: u32,
y0: u32,
x_base: u32,
y_base: u32,
log2_trafo_size: u8,
trafo_depth: u8,
blk_idx: u8,
inherited: TransformTreeCbf,
slice_params: &SliceParams,
cu_transquant_bypass: bool,
) -> Result<TransformTreeCbf, DecodeError> {
let is_intra = pred_mode == PredMode::Intra;
if is_intra {
let x_pu = (x0 >> state.log2_min_pu_size) as usize;
let y_pu = (y0 >> state.log2_min_pu_size) as usize;
let luma_mode = state.tab_ipm[y_pu * state.min_pu_width + x_pu];
predict_intra_luma(
state,
sps,
x0,
y0,
log2_trafo_size,
luma_mode,
pps.constrained_intra_pred_flag,
)?;
}
let cbf_luma = if is_intra || trafo_depth != 0 || inherited.cbf_cb || inherited.cbf_cr {
decode_cbf_luma(cabac, contexts, trafo_depth) != 0
} else {
true
};
state.last_cbf_luma = cbf_luma;
{
let tu_size = 1u32 << log2_trafo_size;
let x_tu_start = (x0 >> state.log2_min_tb_size) as usize;
let y_tu_start = (y0 >> state.log2_min_tb_size) as usize;
let tu_count = (tu_size >> state.log2_min_tb_size).max(1) as usize;
let val = if cbf_luma { 1u8 } else { 0u8 };
for j in 0..tu_count {
for i in 0..tu_count {
let idx = (y_tu_start + j) * state.min_tb_width + (x_tu_start + i);
if idx < state.tab_cbf_luma.len() {
state.tab_cbf_luma[idx] = val;
}
}
}
}
let new_cbf = inherited;
let do_chroma_inline = sps.chroma_format_idc == 1 && log2_trafo_size > 2;
let do_chroma_deferred = sps.chroma_format_idc == 1 && log2_trafo_size == 2 && blk_idx == 3;
if cbf_luma || inherited.cbf_cb || inherited.cbf_cr {
if pps.cu_qp_delta_enabled_flag && !state.is_cu_qp_delta_coded {
let abs = decode_cu_qp_delta_abs(cabac, contexts) as i32;
let signed = if abs != 0 {
let sign = decode_cu_qp_delta_sign_flag(cabac);
if sign != 0 { -abs } else { abs }
} else {
0
};
state.last_cu_qp_delta = signed;
if !(-26..=25).contains(&signed) {
return Err(DecodeError::InvalidSyntax("cu_qp_delta out of range"));
}
state.is_cu_qp_delta_coded = true;
set_qpy(state, sps, pps, slice_qp_y, x_base, y_base);
}
let cbf_chroma = inherited.cbf_cb || inherited.cbf_cr;
if slice_params.cu_chroma_qp_offset_enabled_flag
&& cbf_chroma
&& !cu_transquant_bypass
&& !state.is_cu_chroma_qp_offset_coded
{
let cu_chroma_qp_offset_flag =
cabac.decode_bin(&mut contexts.state[ctx::CU_CHROMA_QP_OFFSET_FLAG]);
if cu_chroma_qp_offset_flag != 0 {
let mut cu_chroma_qp_offset_idx = 0u32;
if pps.chroma_qp_offset_list_len_minus1 > 0 {
cu_chroma_qp_offset_idx = 0;
while cu_chroma_qp_offset_idx < pps.chroma_qp_offset_list_len_minus1 {
let bin =
cabac.decode_bin(&mut contexts.state[ctx::CU_CHROMA_QP_OFFSET_IDX]);
if bin == 0 {
break;
}
cu_chroma_qp_offset_idx += 1;
}
}
state.cu_qp_offset_cb = pps.cb_qp_offset_list[cu_chroma_qp_offset_idx as usize];
state.cu_qp_offset_cr = pps.cr_qp_offset_list[cu_chroma_qp_offset_idx as usize];
} else {
state.cu_qp_offset_cb = 0;
state.cu_qp_offset_cr = 0;
}
state.is_cu_chroma_qp_offset_coded = true;
}
let qp_y = state.last_qp_y;
if cbf_luma {
let scan_idx = if is_intra {
let x_pu = (x0 >> state.log2_min_pu_size) as usize;
let y_pu = (y0 >> state.log2_min_pu_size) as usize;
let luma_mode = state.tab_ipm[y_pu * state.min_pu_width + x_pu];
pick_scan_order(log2_trafo_size, luma_mode)
} else {
ScanOrder::Diag
};
let block = decode_residual_coding(
cabac,
contexts,
sps,
pps,
log2_trafo_size,
ResidualPlane::Luma,
qp_y,
scan_idx,
is_intra,
cu_transquant_bypass,
)?;
apply_residual_to_luma(state, x0, y0, log2_trafo_size, &block, is_intra);
state.last_luma_residual = Some(block);
}
let log2_trafo_size_c = if do_chroma_inline || do_chroma_deferred {
if do_chroma_inline {
log2_trafo_size - 1
} else {
log2_trafo_size }
} else {
0 };
if is_intra {
if do_chroma_inline {
let chroma_mode = state.last_chroma_pred_mode;
predict_intra_chroma(
state,
sps,
x0,
y0,
log2_trafo_size - 1,
chroma_mode,
pps.constrained_intra_pred_flag,
)?;
decode_chroma_residuals(
cabac,
contexts,
state,
sps,
pps,
x0,
y0,
log2_trafo_size_c,
log2_trafo_size,
qp_y,
inherited.cbf_cb,
inherited.cbf_cr,
true,
cu_transquant_bypass,
slice_params.slice_cb_qp_offset,
slice_params.slice_cr_qp_offset,
)?;
} else if do_chroma_deferred {
let chroma_mode = state.last_chroma_pred_mode;
predict_intra_chroma(
state,
sps,
x_base,
y_base,
log2_trafo_size,
chroma_mode,
pps.constrained_intra_pred_flag,
)?;
decode_chroma_residuals(
cabac,
contexts,
state,
sps,
pps,
x_base,
y_base,
log2_trafo_size_c,
log2_trafo_size + 1,
qp_y,
inherited.cbf_cb,
inherited.cbf_cr,
true,
cu_transquant_bypass,
slice_params.slice_cb_qp_offset,
slice_params.slice_cr_qp_offset,
)?;
}
} else {
if do_chroma_inline && (inherited.cbf_cb || inherited.cbf_cr) {
decode_chroma_residuals(
cabac,
contexts,
state,
sps,
pps,
x0,
y0,
log2_trafo_size_c,
log2_trafo_size,
qp_y,
inherited.cbf_cb,
inherited.cbf_cr,
false,
cu_transquant_bypass,
slice_params.slice_cb_qp_offset,
slice_params.slice_cr_qp_offset,
)?;
} else if do_chroma_deferred && (inherited.cbf_cb || inherited.cbf_cr) {
decode_chroma_residuals(
cabac,
contexts,
state,
sps,
pps,
x_base,
y_base,
log2_trafo_size_c,
log2_trafo_size + 1,
qp_y,
inherited.cbf_cb,
inherited.cbf_cr,
false,
cu_transquant_bypass,
slice_params.slice_cb_qp_offset,
slice_params.slice_cr_qp_offset,
)?;
}
}
} else if is_intra {
if do_chroma_inline {
let chroma_mode = state.last_chroma_pred_mode;
predict_intra_chroma(
state,
sps,
x0,
y0,
log2_trafo_size - 1,
chroma_mode,
pps.constrained_intra_pred_flag,
)?;
} else if do_chroma_deferred {
let chroma_mode = state.last_chroma_pred_mode;
predict_intra_chroma(
state,
sps,
x_base,
y_base,
log2_trafo_size,
chroma_mode,
pps.constrained_intra_pred_flag,
)?;
}
}
if is_intra {
mark_intra_tu_boundaries(state, x0, y0, log2_trafo_size);
} else {
compute_deblocking_boundary_strengths(state, slice_params, x0, y0, log2_trafo_size);
}
Ok(new_cbf)
}
fn get_qpy_pred<P: Pixel>(
state: &mut PictureState<P>,
sps: &Sps,
pps: &Pps,
slice_qp: i32,
x_base: u32,
y_base: u32,
) -> i32 {
let ctb_mask: u32 = (1u32 << sps.ctb_log2_size_y) - 1;
let min_qp_log2 = sps.ctb_log2_size_y - pps.diff_cu_qp_delta_depth as u8;
let min_qp_mask: u32 = (1u32 << min_qp_log2) - 1;
let x_qg = x_base & !min_qp_mask;
let y_qg = y_base & !min_qp_mask;
let x_cb = (x_qg >> state.log2_min_cb_size) as usize;
let y_cb = (y_qg >> state.log2_min_cb_size) as usize;
let available_a = (x_base & ctb_mask) != 0 && (x_qg & ctb_mask) != 0;
let available_b = (y_base & ctb_mask) != 0 && (y_qg & ctb_mask) != 0;
let qpy_pred_fallback = if state.first_qp_group || (x_qg == 0 && y_qg == 0) {
state.first_qp_group = !state.is_cu_qp_delta_coded;
slice_qp
} else {
state.qpy_pred
};
let qpy_a = if !available_a {
qpy_pred_fallback
} else {
state.tab_qp_y[(x_cb - 1) + y_cb * state.min_cb_width] as i32
};
let qpy_b = if !available_b {
qpy_pred_fallback
} else {
state.tab_qp_y[x_cb + (y_cb - 1) * state.min_cb_width] as i32
};
(qpy_a + qpy_b + 1) >> 1
}
fn set_qpy<P: Pixel>(
state: &mut PictureState<P>,
sps: &Sps,
pps: &Pps,
slice_qp: i32,
x_base: u32,
y_base: u32,
) {
let qp_pred = get_qpy_pred(state, sps, pps, slice_qp, x_base, y_base);
let delta = state.last_cu_qp_delta;
state.last_qp_y = if delta != 0 {
let off = 0i32;
(qp_pred + delta + 52 + 2 * off).rem_euclid(52 + off) - off
} else {
qp_pred
};
}
fn maybe_save_qpy_pred<P: Pixel>(
state: &mut PictureState<P>,
sps: &Sps,
pps: &Pps,
x0: u32,
y0: u32,
log2_cb_size: u8,
) {
let min_qp_log2 = sps.ctb_log2_size_y - pps.diff_cu_qp_delta_depth as u8;
let mask: u32 = (1u32 << min_qp_log2) - 1;
let cb_size = 1u32 << log2_cb_size;
if ((x0 + cb_size) & mask) == 0 && ((y0 + cb_size) & mask) == 0 {
state.qpy_pred = state.last_qp_y;
}
}
fn write_qp_y_table<P: Pixel>(
state: &mut PictureState<P>,
x0: u32,
y0: u32,
log2_size: u8,
qp_y: i32,
) {
let length = ((1u32 << log2_size) >> state.log2_min_cb_size).max(1) as usize;
let x_cb = (x0 >> state.log2_min_cb_size) as usize;
let y_cb = (y0 >> state.log2_min_cb_size) as usize;
let v = qp_y.clamp(0, 51) as u8;
for j in 0..length {
let row = (y_cb + j) * state.min_cb_width;
for i in 0..length {
state.tab_qp_y[row + x_cb + i] = v;
}
}
}
#[allow(clippy::too_many_arguments)]
fn decode_chroma_residuals<P: Pixel>(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
state: &mut PictureState<P>,
sps: &Sps,
pps: &Pps,
x0: u32,
y0: u32,
log2_trafo_size_c: u8,
log2_trafo_size_luma: u8,
qp_y: i32,
cbf_cb: bool,
cbf_cr: bool,
is_intra: bool,
cu_transquant_bypass: bool,
slice_cb_qp_offset: i32,
slice_cr_qp_offset: i32,
) -> Result<(), DecodeError> {
let x_c = (x0 >> 1) as usize;
let y_c = (y0 >> 1) as usize;
for c_idx in 1..=2u8 {
let cbf = if c_idx == 1 { cbf_cb } else { cbf_cr };
if !cbf {
continue;
}
let qp_offset = if c_idx == 1 {
pps.pps_cb_qp_offset + slice_cb_qp_offset + state.cu_qp_offset_cb
} else {
pps.pps_cr_qp_offset + slice_cr_qp_offset + state.cu_qp_offset_cr
};
let qp_i = (qp_y + qp_offset).clamp(0, 57);
let qp_c = if qp_i < 30 {
qp_i
} else if qp_i > 43 {
qp_i - 6
} else {
const QP_C: [i32; 14] = [29, 30, 31, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37];
QP_C[(qp_i - 30) as usize]
};
let plane = if c_idx == 1 {
ResidualPlane::Cb
} else {
ResidualPlane::Cr
};
let scan_idx = if is_intra && log2_trafo_size_luma < 4 {
pick_scan_order(log2_trafo_size_c, state.last_chroma_pred_mode)
} else {
ScanOrder::Diag
};
let block = decode_residual_coding(
cabac,
contexts,
sps,
pps,
log2_trafo_size_c,
plane,
qp_c,
scan_idx,
is_intra,
cu_transquant_bypass,
)?;
let size_c = 1usize << log2_trafo_size_c;
let mut residual = block.coeffs.clone();
if !block.transform_skip && !block.cu_transquant_bypass {
apply_inverse_transform(
&mut residual,
log2_trafo_size_c,
block.last_sig_x,
block.last_sig_y,
sps.bit_depth_chroma as u32,
false, );
}
let dst_stride = state.uv_stride;
let dst_plane = if c_idx == 1 {
&mut state.u_plane
} else {
&mut state.v_plane
};
let dst_offset = y_c * dst_stride + x_c;
let dst = &mut dst_plane[dst_offset..dst_offset + (size_c - 1) * dst_stride + size_c];
add_residual(
dst,
dst_stride,
&residual,
log2_trafo_size_c,
state.bit_depth,
);
}
Ok(())
}
fn mark_intra_tu_boundaries<P: Pixel>(
state: &mut PictureState<P>,
x0: u32,
y0: u32,
log2_size: u8,
) {
let size = 1u32 << log2_size;
let pic_w = state.width as usize;
let bs_w = pic_w >> 2;
if y0 > 0 {
let yy = (y0 >> 2) as usize;
let xx_start = (x0 >> 2) as usize;
let xx_end = ((x0 + size) >> 2) as usize;
for xx in xx_start..xx_end {
state.bs_horizontal[yy * bs_w + xx] = 2;
}
}
if x0 > 0 {
let xx = (x0 >> 2) as usize;
let yy_start = (y0 >> 2) as usize;
let yy_end = ((y0 + size) >> 2) as usize;
for yy in yy_start..yy_end {
state.bs_vertical[yy * bs_w + xx] = 2;
}
}
}
fn predict_intra_luma<P: Pixel>(
state: &mut PictureState<P>,
sps: &Sps,
x0: u32,
y0: u32,
log2_size: u8,
mode: u8,
constrained_intra_pred_flag: bool,
) -> Result<(), DecodeError> {
let size = 1usize << log2_size;
let pic_w = state.width as usize;
let pic_h = state.height as usize;
let avail = compute_luma_avail_inner(state, x0, y0, size as u32, constrained_intra_pred_flag);
let (mut top, mut left) = build_reference_samples(
&state.y_plane,
state.y_stride,
pic_w,
pic_h,
x0 as usize,
y0 as usize,
log2_size,
state.bit_depth,
avail,
);
if mode != 1 {
filter_reference_samples(
&mut top,
&mut left,
log2_size,
mode,
sps.strong_intra_smoothing_enabled_flag,
0, sps.chroma_format_idc,
state.bit_depth,
);
}
let dst_stride = state.y_stride;
let dst_offset = (y0 as usize) * dst_stride + (x0 as usize);
let dst = &mut state.y_plane[dst_offset..dst_offset + (size - 1) * dst_stride + size];
match mode {
0 => predict_planar(dst, dst_stride, &top, &left, log2_size, state.bit_depth),
1 => predict_dc(
dst,
dst_stride,
&top,
&left,
log2_size,
true,
state.bit_depth,
),
2..=34 => predict_angular(
dst,
dst_stride,
&top,
&left,
log2_size,
mode,
0,
state.bit_depth,
),
_ => {
return Err(DecodeError::Unsupported("invalid intra prediction mode"));
}
}
Ok(())
}
fn compute_luma_avail<P: Pixel>(
state: &PictureState<P>,
x0: u32,
y0: u32,
size: u32,
) -> ReferenceAvailability {
compute_luma_avail_inner(state, x0, y0, size, false)
}
fn compute_luma_avail_inner<P: Pixel>(
state: &PictureState<P>,
x0: u32,
y0: u32,
size: u32,
constrained_intra_pred: bool,
) -> ReferenceAvailability {
let pic_w = state.width;
let pic_h = state.height;
let log2_ctb = state.log2_ctb_size;
let ctb_size = 1u32 << log2_ctb;
let pic_w_in_ctbs = pic_w.div_ceil(ctb_size);
let ctb_x = x0 >> log2_ctb;
let ctb_y = y0 >> log2_ctb;
let cur_ctb_rs = ctb_y * pic_w_in_ctbs + ctb_x;
let cur_slice_addr = state
.tab_slice_addr_rs
.get(cur_ctb_rs as usize)
.copied()
.unwrap_or(-1);
let cur_tile_id = state
.tab_tile_id
.get(cur_ctb_rs as usize)
.copied()
.unwrap_or(0);
let ctb_avail = |cx: u32, cy: u32| -> bool {
let rs = cy * pic_w_in_ctbs + cx;
let sa = state
.tab_slice_addr_rs
.get(rs as usize)
.copied()
.unwrap_or(-1);
sa >= 0
&& sa == cur_slice_addr
&& state.tab_tile_id.get(rs as usize).copied().unwrap_or(0) == cur_tile_id
};
let ctb_left_flag = ctb_x > 0 && ctb_avail(ctb_x - 1, ctb_y);
let ctb_up_flag = ctb_y > 0 && ctb_avail(ctb_x, ctb_y - 1);
let ctb_up_left_flag = ctb_x > 0 && ctb_y > 0 && ctb_avail(ctb_x - 1, ctb_y - 1);
let ctb_up_right_flag =
ctb_y > 0 && ctb_x + 1 < pic_w_in_ctbs && ctb_avail(ctb_x + 1, ctb_y - 1);
let x0b = x0 & (ctb_size - 1);
let y0b = y0 & (ctb_size - 1);
let cand_up = y0b > 0 || ctb_up_flag;
let cand_left = x0b > 0 || ctb_left_flag;
let cand_up_left = if x0b > 0 || y0b > 0 {
cand_left && cand_up
} else {
ctb_up_left_flag
};
let cand_up_right = if x0b + size == ctb_size {
ctb_up_right_flag && y0b == 0
} else {
cand_up
};
let cand_up_right = cand_up_right && (x0 + size) < pic_w;
let end_of_tiles_y = ((ctb_y + 1) * ctb_size).min(pic_h);
let cand_bottom_left = if (y0 + size) >= end_of_tiles_y {
false
} else {
cand_left
};
let min_tb_log2 = 2u32; let log2_diff = (log2_ctb as u32) - min_tb_log2;
let tb_mask = (1i32 << log2_diff) - 1;
let x_tb = (x0 >> min_tb_log2) as i32 & tb_mask;
let y_tb = (y0 >> min_tb_log2) as i32 & tb_mask;
let size_in_tbs = (size >> min_tb_log2) as i32;
let zscan = |x: i32, y: i32| -> i32 {
if x < 0 || y < 0 {
return -1;
}
let mut val = 0i32;
for i in 0..log2_diff {
let m = 1i32 << i;
if x & m != 0 {
val += m * m;
}
if y & m != 0 {
val += 2 * m * m;
}
}
val
};
let cur_z = zscan(x_tb, y_tb);
let up_right = cand_up_right && cur_z > zscan((x_tb + size_in_tbs) & tb_mask, y_tb - 1);
let bottom_left = cand_bottom_left && cur_z > zscan(x_tb - 1, (y_tb + size_in_tbs) & tb_mask);
let up = cand_up && y0 > 0;
let left = cand_left && x0 > 0;
let up_left = cand_up_left && x0 > 0 && y0 > 0;
let mut avail = ReferenceAvailability {
up_left,
up,
up_right,
left,
bottom_left,
};
if constrained_intra_pred {
let log2_min_pu = state.log2_min_pu_size;
let min_pu_w = state.min_pu_width;
let tab_mvf = &state.tab_mvf;
let is_intra_at = |sx: u32, sy: u32| -> bool {
let idx = (sy >> log2_min_pu) as usize * min_pu_w + (sx >> log2_min_pu) as usize;
tab_mvf.get(idx).is_some_and(|m| m.pred_flag == 0)
};
if avail.up_left && !is_intra_at(x0 - 1, y0 - 1) {
avail.up_left = false;
}
if avail.up {
for i in 0..size {
if !is_intra_at(x0 + i, y0 - 1) {
avail.up = false;
break;
}
}
}
if avail.up_right {
let count = (pic_w.saturating_sub(x0 + size)).min(size);
for i in 0..count {
if !is_intra_at(x0 + size + i, y0 - 1) {
avail.up_right = false;
break;
}
}
}
if avail.left {
for i in 0..size {
if !is_intra_at(x0 - 1, y0 + i) {
avail.left = false;
break;
}
}
}
if avail.bottom_left {
let count = (pic_h.saturating_sub(y0 + size)).min(size);
for i in 0..count {
if !is_intra_at(x0 - 1, y0 + size + i) {
avail.bottom_left = false;
break;
}
}
}
}
avail
}
fn predict_intra_chroma<P: Pixel>(
state: &mut PictureState<P>,
sps: &Sps,
x0_luma: u32,
y0_luma: u32,
log2_size: u8,
mode: u8,
constrained_intra_pred_flag: bool,
) -> Result<(), DecodeError> {
let size = 1usize << log2_size;
let pic_w_c = (state.width / 2) as usize;
let pic_h_c = (state.height / 2) as usize;
let x_c = (x0_luma >> 1) as usize;
let y_c = (y0_luma >> 1) as usize;
let avail = compute_chroma_avail(
state,
x0_luma,
y0_luma,
(size as u32) * 2,
constrained_intra_pred_flag,
);
let dst_stride = state.uv_stride;
for plane_idx in 0..2 {
let c_idx = (plane_idx + 1) as u8; let (mut top, mut left) = {
let src_plane = if plane_idx == 0 {
&state.u_plane
} else {
&state.v_plane
};
build_reference_samples(
src_plane,
state.uv_stride,
pic_w_c,
pic_h_c,
x_c,
y_c,
log2_size,
state.bit_depth,
avail,
)
};
if (2..=34).contains(&mode) {
filter_reference_samples(
&mut top,
&mut left,
log2_size,
mode,
sps.strong_intra_smoothing_enabled_flag,
c_idx,
sps.chroma_format_idc,
state.bit_depth,
);
}
let plane = if plane_idx == 0 {
&mut state.u_plane
} else {
&mut state.v_plane
};
let dst_offset = y_c * dst_stride + x_c;
let dst = &mut plane[dst_offset..dst_offset + (size - 1) * dst_stride + size];
match mode {
0 => predict_planar(dst, dst_stride, &top, &left, log2_size, state.bit_depth),
1 => predict_dc(
dst,
dst_stride,
&top,
&left,
log2_size,
false,
state.bit_depth,
),
2..=34 => predict_angular(
dst,
dst_stride,
&top,
&left,
log2_size,
mode,
c_idx,
state.bit_depth,
),
_ => {
return Err(DecodeError::Unsupported("invalid intra prediction mode"));
}
}
}
Ok(())
}
fn compute_chroma_avail<P: Pixel>(
state: &PictureState<P>,
x0_luma: u32,
y0_luma: u32,
luma_size: u32,
constrained_intra_pred: bool,
) -> ReferenceAvailability {
compute_luma_avail_inner(state, x0_luma, y0_luma, luma_size, constrained_intra_pred)
}
fn apply_residual_to_luma<P: Pixel>(
state: &mut PictureState<P>,
x0: u32,
y0: u32,
log2_size: u8,
block: &ResidualBlock,
is_intra: bool,
) {
let size = 1usize << log2_size;
let mut residual_pixels = block.coeffs.clone();
if !block.transform_skip && !block.cu_transquant_bypass {
let is_luma_intra_4x4 = log2_size == 2 && is_intra;
apply_inverse_transform(
&mut residual_pixels,
log2_size,
block.last_sig_x,
block.last_sig_y,
state.bit_depth as u32,
is_luma_intra_4x4,
);
}
let dst_stride = state.y_stride;
let dst_offset = (y0 as usize) * dst_stride + (x0 as usize);
let dst = &mut state.y_plane[dst_offset..dst_offset + (size - 1) * dst_stride + size];
add_residual(
dst,
dst_stride,
&residual_pixels,
log2_size,
state.bit_depth,
);
}
fn pick_scan_order(log2_trafo_size: u8, intra_pred_mode: u8) -> ScanOrder {
if log2_trafo_size > 3 {
return ScanOrder::Diag;
}
if (6..=14).contains(&intra_pred_mode) {
ScanOrder::Vert
} else if (22..=30).contains(&intra_pred_mode) {
ScanOrder::Horiz
} else {
ScanOrder::Diag
}
}
fn decode_intra_mode_signaling<P: Pixel>(
cabac: &mut CabacReader,
contexts: &mut CabacContexts,
state: &mut PictureState<P>,
x0: u32,
y0: u32,
log2_cb_size: u8,
part_mode: PartMode,
) -> Result<(), DecodeError> {
let split = part_mode == PartMode::PartNxN;
let cb_size = 1u32 << log2_cb_size;
let pb_size = if split { cb_size >> 1 } else { cb_size };
let side = if split { 2usize } else { 1 };
let n_pus = side * side;
let mut prev_flag = [false; 4];
for slot in prev_flag.iter_mut().take(n_pus) {
*slot = cabac.decode_bin(&mut contexts.state[ctx::PREV_INTRA_LUMA_PRED_FLAG]) != 0;
}
let mut intra_pred_mode = [0u8; 4];
for k in 0..n_pus {
let mpm_idx;
let rem;
if prev_flag[k] {
let mut i = 0u8;
while i < 2 && cabac.decode_bypass() != 0 {
i += 1;
}
mpm_idx = i;
rem = 0;
} else {
mpm_idx = 0;
rem = cabac.decode_bypass_bits(5) as u8;
}
let pj = (k % side) as u32;
let pi = (k / side) as u32;
let pu_x = x0 + pb_size * pj;
let pu_y = y0 + pb_size * pi;
intra_pred_mode[k] =
compute_luma_intra_pred_mode(state, pu_x, pu_y, prev_flag[k], mpm_idx, rem);
write_intra_pred_mode(state, pu_x, pu_y, pb_size, intra_pred_mode[k]);
}
let chroma_mode_idx = decode_intra_chroma_pred_mode(cabac, contexts);
let chroma_pred_mode = if chroma_mode_idx == 4 {
intra_pred_mode[0]
} else {
const TABLE: [u8; 4] = [INTRA_PLANAR, INTRA_ANGULAR_26, INTRA_ANGULAR_10, INTRA_DC];
let mapped = TABLE[chroma_mode_idx as usize];
if intra_pred_mode[0] == mapped {
INTRA_ANGULAR_34
} else {
mapped
}
};
state.last_luma_pred_mode = intra_pred_mode[0];
state.last_chroma_pred_mode = chroma_pred_mode;
Ok(())
}
fn write_intra_pred_mode<P: Pixel>(
state: &mut PictureState<P>,
x0: u32,
y0: u32,
pu_size: u32,
mode: u8,
) {
let size_in_pus = (pu_size >> state.log2_min_pu_size).max(1) as usize;
let x_pu = (x0 >> state.log2_min_pu_size) as usize;
let y_pu = (y0 >> state.log2_min_pu_size) as usize;
for j in 0..size_in_pus {
let row = (y_pu + j) * state.min_pu_width;
for i in 0..size_in_pus {
state.tab_ipm[row + x_pu + i] = mode;
}
}
}
fn decode_intra_chroma_pred_mode(cabac: &mut CabacReader, contexts: &mut CabacContexts) -> u8 {
if cabac.decode_bin(&mut contexts.state[ctx::INTRA_CHROMA_PRED_MODE]) == 0 {
return 4;
}
let hi = cabac.decode_bypass();
let lo = cabac.decode_bypass();
((hi << 1) | lo) as u8
}
fn compute_luma_intra_pred_mode<P: Pixel>(
state: &PictureState<P>,
x0: u32,
y0: u32,
prev_intra_luma_pred_flag: bool,
mpm_idx: u8,
rem_intra_luma_pred_mode: u8,
) -> u8 {
let log2_ctb = state.log2_ctb_size as u32;
let x_pu = (x0 >> state.log2_min_pu_size) as usize;
let y_pu = (y0 >> state.log2_min_pu_size) as usize;
let y_ctb = (y0 >> log2_ctb) << log2_ctb;
let mut cand_up = if y_pu > 0 {
state.tab_ipm[(y_pu - 1) * state.min_pu_width + x_pu]
} else {
INTRA_DC
};
let cand_left = if x_pu > 0 {
state.tab_ipm[y_pu * state.min_pu_width + x_pu - 1]
} else {
INTRA_DC
};
if (y0 as i64 - 1) < y_ctb as i64 {
cand_up = INTRA_DC;
}
let mut candidate = [0u8; 3];
if cand_left == cand_up {
if cand_left < 2 {
candidate[0] = INTRA_PLANAR;
candidate[1] = INTRA_DC;
candidate[2] = INTRA_ANGULAR_26;
} else {
candidate[0] = cand_left;
candidate[1] = 2 + (((cand_left as i32) - 2 - 1 + 32) & 31) as u8;
candidate[2] = 2 + (((cand_left as i32) - 2 + 1) & 31) as u8;
}
} else {
candidate[0] = cand_left;
candidate[1] = cand_up;
if candidate[0] != INTRA_PLANAR && candidate[1] != INTRA_PLANAR {
candidate[2] = INTRA_PLANAR;
} else if candidate[0] != INTRA_DC && candidate[1] != INTRA_DC {
candidate[2] = INTRA_DC;
} else {
candidate[2] = INTRA_ANGULAR_26;
}
}
if prev_intra_luma_pred_flag {
candidate[mpm_idx as usize]
} else {
if candidate[0] > candidate[1] {
candidate.swap(0, 1);
}
if candidate[0] > candidate[2] {
candidate.swap(0, 2);
}
if candidate[1] > candidate[2] {
candidate.swap(1, 2);
}
let mut mode = rem_intra_luma_pred_mode;
for c in &candidate {
if mode >= *c {
mode += 1;
}
}
mode
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cabac::CabacContexts;
use crate::nal::{NalUnitType, parse_annex_b};
use crate::pps::parse_pps;
use crate::slice::{SliceType, parse_slice_segment_header};
use crate::sps::parse_sps;
#[test]
fn test_decode_tiny_intra_cu_tree_phase2c2() {
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/testdata/tiny_intra.h265");
let data = std::fs::read(path).expect("read fixture");
let nals = parse_annex_b(&data);
let sps_nal = nals
.iter()
.find(|n| n.nal_unit_type == NalUnitType::Sps)
.expect("SPS NAL");
let pps_nal = nals
.iter()
.find(|n| n.nal_unit_type == NalUnitType::Pps)
.expect("PPS NAL");
let slice_nal = nals
.iter()
.find(|n| n.nal_unit_type.is_idr())
.expect("IDR slice NAL");
let sps = parse_sps(&sps_nal.rbsp).expect("parse SPS");
let pps = parse_pps(&pps_nal.rbsp).expect("parse PPS");
let sh = parse_slice_segment_header(&slice_nal.rbsp, slice_nal.nal_unit_type, &sps, &pps)
.expect("parse slice header");
assert_eq!(sh.slice_type, SliceType::I);
assert_eq!(sh.slice_qp_y, 25);
let mut contexts = CabacContexts::init(sh.slice_qp_y, sh.slice_type, false);
let cabac_byte_offset = sh.header_size_bits / 8;
assert_eq!(
cabac_byte_offset, 2,
"fixture slice header is exactly 2 bytes"
);
let mut cabac = CabacReader::new(&slice_nal.rbsp, cabac_byte_offset);
let mut state = PictureState::<u8>::new(&sps);
let slice_params = SliceParams {
slice_type: sh.slice_type,
max_num_merge_cand: sh.max_num_merge_cand,
num_ref_idx_l0_active: sh.num_ref_idx_l0_active_minus1 + 1,
num_ref_idx_l1_active: 0,
mvd_l1_zero_flag: false,
log2_parallel_merge_level: 2,
poc: 0,
ref_pic_list_pocs: [vec![], vec![]],
ref_frames_l0: vec![],
ref_frames_l1: vec![],
collocated_ref: None,
slice_temporal_mvp_enabled_flag: false,
collocated_from_l0_flag: true,
slice_cb_qp_offset: 0,
slice_cr_qp_offset: 0,
cu_chroma_qp_offset_enabled_flag: false,
weighted_pred_flag: false,
pred_weight_table: crate::slice::PredWeightTable::default(),
};
decode_coding_quadtree(
&mut cabac,
&mut contexts,
&mut state,
&sps,
&pps,
sh.slice_qp_y,
&slice_params,
0,
0,
sps.ctb_log2_size_y,
0,
)
.expect("decode coding tree");
assert!(
state.cu_count >= 1,
"expected at least one CU; got {}",
state.cu_count
);
assert!(
state.last_luma_pred_mode <= 34,
"luma intra mode {} out of range",
state.last_luma_pred_mode
);
assert!(
state.last_chroma_pred_mode <= 34,
"chroma intra mode {} out of range",
state.last_chroma_pred_mode
);
assert_eq!(
state.last_luma_pred_mode, INTRA_PLANAR,
"expected PLANAR luma intra for flat-gray fixture, got {}",
state.last_luma_pred_mode
);
assert_eq!(
state.last_chroma_pred_mode, INTRA_PLANAR,
"expected DM chroma (= PLANAR) for flat-gray fixture, got {}",
state.last_chroma_pred_mode
);
assert!(
!state.last_split_transform_flag,
"expected implicit no-split for 16x16 CU at depth 0"
);
assert!(
state.last_cbf_luma,
"expected cbf_luma=1 for non-trivial luma residual"
);
assert!(
!state.last_cbf_cb,
"expected cbf_cb=0 for chroma matching prediction"
);
assert!(
!state.last_cbf_cr,
"expected cbf_cr=0 for chroma matching prediction"
);
assert_eq!(
state.last_cu_qp_delta, -5,
"expected cu_qp_delta=-5 for x265 CRF AQ on flat fixture"
);
assert_eq!(state.last_qp_y, 20);
let resid = state
.last_luma_residual
.as_ref()
.expect("luma residual block must be present when cbf_luma=1");
assert_eq!(resid.log2_size, 4, "16x16 luma TU");
assert_eq!(resid.last_sig_x, 0, "last_sig_x");
assert_eq!(resid.last_sig_y, 0, "last_sig_y");
let nonzero = resid.coeffs.iter().filter(|&&c| c != 0).count();
assert_eq!(nonzero, 1, "expected single DC coefficient");
assert_eq!(resid.coeffs[0], -255, "dequantized DC coefficient");
assert_eq!(
cabac.decode_terminate(),
1,
"CABAC must be at end of slice after residual_coding"
);
let mut residual_pixels = state.last_luma_residual.as_ref().unwrap().coeffs.clone();
crate::inverse_transform::apply_inverse_transform(
&mut residual_pixels,
4,
resid.last_sig_x,
resid.last_sig_y,
8,
false,
);
assert!(
residual_pixels.iter().all(|&p| p == -2),
"expected all-(-2) residual after IDCT, got: first 4 = {:?}",
&residual_pixels[..4]
);
}
#[test]
fn test_pcm_bit_reader_byte_aligned() {
let data = [0x12, 0x34, 0x56, 0x78];
let mut r = PcmBitReader::new(&data);
assert_eq!(r.read_bits(8), 0x12);
assert_eq!(r.read_bits(8), 0x34);
assert_eq!(r.read_bits(8), 0x56);
assert_eq!(r.read_bits(8), 0x78);
}
#[test]
fn test_pcm_bit_reader_bit_packed() {
let data = [0xAC, 0x39];
let mut r = PcmBitReader::new(&data);
assert_eq!(r.read_bits(4), 0xA);
assert_eq!(r.read_bits(4), 0xC);
assert_eq!(r.read_bits(4), 0x3);
assert_eq!(r.read_bits(4), 0x9);
}
#[test]
fn test_pcm_bit_reader_unaligned() {
let data = [0b1011_0101, 0b1011_0001];
let mut r = PcmBitReader::new(&data);
assert_eq!(r.read_bits(5), 0b10110);
assert_eq!(r.read_bits(3), 0b101);
assert_eq!(r.read_bits(8), 0b1011_0001);
}
#[test]
fn test_chroma_qp_derivation_with_slice_offsets() {
let qp_c_from_qp_i = |qp_i: i32| -> i32 {
let qp_i = qp_i.clamp(0, 57);
if qp_i < 30 {
qp_i
} else if qp_i > 43 {
qp_i - 6
} else {
const QP_C: [i32; 14] = [29, 30, 31, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37];
QP_C[(qp_i - 30) as usize]
}
};
let qp_y = 26;
let pps_cb_offset = 4;
let slice_cb_offset = 0;
let qp_i = qp_y + pps_cb_offset + slice_cb_offset; assert_eq!(qp_c_from_qp_i(qp_i), 29);
let slice_cb_offset = 3;
let qp_i = qp_y + pps_cb_offset + slice_cb_offset; assert_eq!(qp_c_from_qp_i(qp_i), 32);
let slice_cb_offset = -6;
let qp_i = qp_y + pps_cb_offset + slice_cb_offset; assert_eq!(qp_c_from_qp_i(qp_i), 24);
let qp_y = 40;
let pps_cr_offset = -2;
let slice_cr_offset = 8;
let qp_i = qp_y + pps_cr_offset + slice_cr_offset; assert_eq!(qp_c_from_qp_i(qp_i), 40);
let qp_i = (-5i32 + 2 + 0).clamp(0, 57); assert_eq!(qp_c_from_qp_i(qp_i), 0);
let qp_i = (51 + 5 + 3).clamp(0, 57); assert_eq!(qp_c_from_qp_i(qp_i), 51); }
}