use std::rc::Rc;
use crate::dpb::DecodedPicture;
use crate::inter_pred;
use crate::residual::OFFSET_TO_BLOCK;
use crate::slice::PredWeightTable;
#[derive(Clone, Copy)]
pub(crate) struct MbaffCtx<'a> {
pub mbaff: bool,
pub mb_field_decoding: &'a [bool],
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn predict_mv_sub(
mv_store_l0: &[[i16; 2]],
ref_idx_store_l0: &[i8],
mb_idx: usize,
mb_width: usize,
px: usize,
py: usize,
spw: usize,
_sph: usize,
ref_idx: i8,
mb_slice_id: &[u16],
cur_slice_id: u16,
mctx: MbaffCtx,
) -> (i16, i16) {
let a = get_mv_neighbor_left_mbaff(
mv_store_l0,
ref_idx_store_l0,
mb_idx,
mb_width,
py,
px,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
);
let b = get_mv_neighbor_above_mbaff(
mv_store_l0,
ref_idx_store_l0,
mb_idx,
mb_width,
py,
px,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
);
let c = get_mv_neighbor_above_right_mbaff(
mv_store_l0,
ref_idx_store_l0,
mb_idx,
mb_width,
py,
px,
spw,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
)
.or_else(|| {
get_mv_neighbor_above_left_mbaff(
mv_store_l0,
ref_idx_store_l0,
mb_idx,
mb_width,
py,
px,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
)
});
let ref_a = a.map(|(_, r)| r).unwrap_or(-1);
let ref_b = b.map(|(_, r)| r).unwrap_or(-1);
let ref_c = c.map(|(_, r)| r).unwrap_or(-1);
let match_count =
(ref_a == ref_idx) as u8 + (ref_b == ref_idx) as u8 + (ref_c == ref_idx) as u8;
if match_count == 1 {
if ref_a == ref_idx {
if let Some((mv, _)) = a {
return (mv[0], mv[1]);
}
}
if ref_b == ref_idx {
if let Some((mv, _)) = b {
return (mv[0], mv[1]);
}
}
if ref_c == ref_idx {
if let Some((mv, _)) = c {
return (mv[0], mv[1]);
}
}
}
if let (None, None, Some((mv, _))) = (b, c, a) {
return (mv[0], mv[1]);
}
let mv_a = a.map(|(mv, _)| mv).unwrap_or([0, 0]);
let mv_b = b.map(|(mv, _)| mv).unwrap_or([0, 0]);
let mv_c = c.map(|(mv, _)| mv).unwrap_or([0, 0]);
let mut xs = [mv_a[0], mv_b[0], mv_c[0]];
let mut ys = [mv_a[1], mv_b[1], mv_c[1]];
xs.sort();
ys.sort();
(xs[1], ys[1])
}
#[inline]
pub(crate) fn ref_pic_safe(list: &[Rc<DecodedPicture>], idx: i8) -> Option<&Rc<DecodedPicture>> {
if list.is_empty() {
return None;
}
Some(&list[(idx.max(0) as usize).min(list.len() - 1)])
}
pub(crate) struct WeightContext<'a> {
pub(crate) use_weight: u8,
pub(crate) wt: Option<&'a PredWeightTable>,
pub(crate) implicit_weights: &'a [Vec<i32>],
}
impl WeightContext<'_> {
pub(crate) fn apply_uni(
&self,
pred: &mut [u8],
list: usize,
ref_idx: usize,
is_chroma: bool,
chroma_comp: usize,
) {
if self.use_weight != 1 {
return;
}
let wt = match self.wt {
Some(w) => w,
None => return,
};
let refs = if list == 0 { &wt.l0 } else { &wt.l1 };
if ref_idx >= refs.len() {
return;
}
let rw = &refs[ref_idx];
if is_chroma {
inter_pred::weighted_uni(
pred,
wt.chroma_log2_weight_denom,
rw.chroma_weight[chroma_comp],
rw.chroma_offset[chroma_comp],
);
} else {
inter_pred::weighted_uni(
pred,
wt.luma_log2_weight_denom,
rw.luma_weight,
rw.luma_offset,
);
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn apply_bi(
&self,
pred_l0: &[u8],
pred_l1: &[u8],
output: &mut [u8],
ref_idx_l0: usize,
ref_idx_l1: usize,
is_chroma: bool,
chroma_comp: usize,
) {
match self.use_weight {
1 => {
let wt = match self.wt {
Some(w) => w,
None => {
inter_pred::bi_pred_avg(pred_l0, pred_l1, output);
return;
}
};
let rw0 = wt.l0.get(ref_idx_l0);
let rw1 = wt.l1.get(ref_idx_l1);
match (rw0, rw1) {
(Some(w0), Some(w1)) if is_chroma => {
inter_pred::weighted_bi(
pred_l0,
pred_l1,
output,
wt.chroma_log2_weight_denom,
w0.chroma_weight[chroma_comp],
w0.chroma_offset[chroma_comp],
w1.chroma_weight[chroma_comp],
w1.chroma_offset[chroma_comp],
);
}
(Some(w0), Some(w1)) => {
inter_pred::weighted_bi(
pred_l0,
pred_l1,
output,
wt.luma_log2_weight_denom,
w0.luma_weight,
w0.luma_offset,
w1.luma_weight,
w1.luma_offset,
);
}
_ => inter_pred::bi_pred_avg(pred_l0, pred_l1, output),
}
}
2 => {
if ref_idx_l0 < self.implicit_weights.len()
&& ref_idx_l1 < self.implicit_weights[ref_idx_l0].len()
{
let w0 = self.implicit_weights[ref_idx_l0][ref_idx_l1];
inter_pred::weighted_bi_implicit(pred_l0, pred_l1, output, w0, 64 - w0);
} else {
inter_pred::bi_pred_avg(pred_l0, pred_l1, output);
}
}
_ => inter_pred::bi_pred_avg(pred_l0, pred_l1, output),
}
}
}
pub(crate) fn predict_mv_skip(
mv_store_l0: &[[i16; 2]],
ref_idx_store_l0: &[i8],
mb_idx: usize,
mb_width: usize,
mb_slice_id: &[u16],
cur_slice_id: u16,
mctx: MbaffCtx,
) -> (i16, i16) {
let a = get_mv_neighbor_left_mbaff(
mv_store_l0,
ref_idx_store_l0,
mb_idx,
mb_width,
0,
0,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
);
let b = get_mv_neighbor_above_mbaff(
mv_store_l0,
ref_idx_store_l0,
mb_idx,
mb_width,
0,
0,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
);
let a_zero = match a {
None => true,
Some((mv, ri)) => ri == 0 && mv[0] == 0 && mv[1] == 0,
};
let b_zero = match b {
None => true,
Some((mv, ri)) => ri == 0 && mv[0] == 0 && mv[1] == 0,
};
if a_zero || b_zero {
return (0, 0);
}
predict_mv(
mv_store_l0,
ref_idx_store_l0,
mb_idx,
mb_width,
0,
16,
16,
0,
mb_slice_id,
cur_slice_id,
mctx,
)
}
#[allow(clippy::type_complexity, clippy::too_many_arguments)]
pub(crate) fn derive_spatial_direct_blk(
mv_store_l0: &[[i16; 2]],
ref_idx_store_l0: &[i8],
mv_store_l1: &[[i16; 2]],
ref_idx_store_l1: &[i8],
mb_idx: usize,
mb_width: usize,
col_pic: Option<&DecodedPicture>,
col_blk: usize,
mb_slice_id: &[u16],
cur_slice_id: u16,
direct_8x8_inference_flag: bool,
mctx: MbaffCtx,
) -> ([i16; 2], [i16; 2], i8, i8, bool, bool) {
let mut ref_idx = [-1i8; 2];
let mut mv = [[0i16; 2]; 2];
let mut pred_flag = [false; 2];
for list in 0..2 {
let (mv_s, ref_s) = if list == 0 {
(mv_store_l0, ref_idx_store_l0)
} else {
(mv_store_l1, ref_idx_store_l1)
};
let a = get_mv_neighbor_left_mbaff(
mv_s,
ref_s,
mb_idx,
mb_width,
0,
0,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
);
let b = get_mv_neighbor_above_mbaff(
mv_s,
ref_s,
mb_idx,
mb_width,
0,
0,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
);
let c = get_mv_neighbor_above_right_mbaff(
mv_s,
ref_s,
mb_idx,
mb_width,
0,
0,
16,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
)
.or_else(|| {
get_mv_neighbor_above_left_mbaff(
mv_s,
ref_s,
mb_idx,
mb_width,
0,
0,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
)
});
let ref_a = a.map(|(_, r)| r).unwrap_or(-1);
let ref_b = b.map(|(_, r)| r).unwrap_or(-1);
let ref_c = c.map(|(_, r)| r).unwrap_or(-1);
let min_ref = [ref_a, ref_b, ref_c]
.iter()
.filter(|&&r| r >= 0)
.min()
.copied()
.unwrap_or(-1);
ref_idx[list] = min_ref;
if min_ref >= 0 {
pred_flag[list] = true;
let match_count =
(ref_a == min_ref) as u8 + (ref_b == min_ref) as u8 + (ref_c == min_ref) as u8;
if match_count == 1 {
if ref_a == min_ref {
if let Some((m, _)) = a {
mv[list] = m;
continue;
}
}
if ref_b == min_ref {
if let Some((m, _)) = b {
mv[list] = m;
continue;
}
}
if ref_c == min_ref {
if let Some((m, _)) = c {
mv[list] = m;
continue;
}
}
}
if let (None, None, Some((m, _))) = (b, c, a) {
mv[list] = m;
continue;
}
let mv_a = a.map(|(m, _)| m).unwrap_or([0, 0]);
let mv_b = b.map(|(m, _)| m).unwrap_or([0, 0]);
let mv_c = c.map(|(m, _)| m).unwrap_or([0, 0]);
let mut xs = [mv_a[0], mv_b[0], mv_c[0]];
let mut ys = [mv_a[1], mv_b[1], mv_c[1]];
xs.sort();
ys.sort();
mv[list] = [xs[1], ys[1]];
}
}
if ref_idx[0] < 0 && ref_idx[1] < 0 {
ref_idx = [0, 0];
pred_flag = [true, true];
mv = [[0, 0], [0, 0]];
}
if let Some(col) = col_pic {
let effective_blk = if direct_8x8_inference_flag {
const INFERENCE_MAP: [usize; 4] = [0, 5, 10, 15];
INFERENCE_MAP[col_blk / 4]
} else {
col_blk
};
let col_pos = mb_idx * 16 + effective_blk;
if col_pos < col.ref_idx_l0.len() && !col.is_intra {
let col_ref_l0 = col.ref_idx_l0[col_pos];
let col_zero = if col_ref_l0 == 0 {
let col_mv = if col_pos < col.mv_l0.len() {
col.mv_l0[col_pos]
} else {
[0, 0]
};
col_mv[0].abs() <= 1 && col_mv[1].abs() <= 1
} else if col_ref_l0 < 0
&& col_pos < col.ref_idx_l1.len()
&& col.ref_idx_l1[col_pos] == 0
{
let col_mv_l1 = if col_pos < col.mv_l1.len() {
col.mv_l1[col_pos]
} else {
[0, 0]
};
col_mv_l1[0].abs() <= 1 && col_mv_l1[1].abs() <= 1
} else {
false
};
if col_zero {
if ref_idx[0] == 0 {
mv[0] = [0, 0];
}
if ref_idx[1] == 0 {
mv[1] = [0, 0];
}
}
}
}
(
mv[0],
mv[1],
ref_idx[0],
ref_idx[1],
pred_flag[0],
pred_flag[1],
)
}
#[allow(clippy::type_complexity, clippy::too_many_arguments)]
pub(crate) fn derive_temporal_direct_blk(
col_pic: &DecodedPicture,
ref_pic_list_l0: &[Rc<DecodedPicture>],
current_poc: i32,
col_poc: i32,
mb_idx: usize,
blk: usize,
direct_8x8_inference_flag: bool,
_mctx: MbaffCtx,
) -> ([i16; 2], [i16; 2], i8, i8, bool, bool) {
let col_blk = if direct_8x8_inference_flag {
const INFERENCE_MAP: [usize; 4] = [0, 5, 10, 15];
INFERENCE_MAP[blk / 4]
} else {
blk
};
let col_base = mb_idx * 16;
if col_pic.is_intra
|| col_base + col_blk >= col_pic.ref_idx_l0.len()
|| col_pic.ref_idx_l0[col_base + col_blk] < 0
{
return ([0, 0], [0, 0], 0, 0, true, true);
}
let col_mv = col_pic.mv_l0[col_base + col_blk];
let _col_ref_idx = col_pic.ref_idx_l0[col_base + col_blk];
let col_ref_poc_val = col_pic.ref_poc_l0[col_base + col_blk];
let ref0 = ref_pic_list_l0
.iter()
.position(|p| p.pic_order_cnt == col_ref_poc_val)
.unwrap_or(0);
let poc0 = ref_pic_list_l0
.get(ref0)
.map(|p| p.pic_order_cnt)
.unwrap_or(0);
let td = (col_poc - poc0).clamp(-128, 127);
let tb = (current_poc - poc0).clamp(-128, 127);
let (mv_l0, mv_l1) = if td == 0 {
(col_mv, [0, 0])
} else {
let tx = (16384 + (td.abs() >> 1)) / td;
let scale = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
let mx_l0 = ((scale * col_mv[0] as i32 + 128) >> 8) as i16;
let my_l0 = ((scale * col_mv[1] as i32 + 128) >> 8) as i16;
let mx_l1 = mx_l0 - col_mv[0];
let my_l1 = my_l0 - col_mv[1];
([mx_l0, my_l0], [mx_l1, my_l1])
};
(mv_l0, mv_l1, ref0 as i8, 0, true, true)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn predict_mv(
mv_store_l0: &[[i16; 2]],
ref_idx_store_l0: &[i8],
mb_idx: usize,
mb_width: usize,
part_idx: usize,
part_w: usize,
part_h: usize,
ref_idx: i8,
mb_slice_id: &[u16],
cur_slice_id: u16,
mctx: MbaffCtx,
) -> (i16, i16) {
let py_off = if part_h == 8 && part_w == 16 {
part_idx * 8
} else {
0
};
let px_off = if part_w == 8 && part_h == 16 {
part_idx * 8
} else {
0
};
let a = get_mv_neighbor_left_mbaff(
mv_store_l0,
ref_idx_store_l0,
mb_idx,
mb_width,
py_off,
px_off,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
);
let b = get_mv_neighbor_above_mbaff(
mv_store_l0,
ref_idx_store_l0,
mb_idx,
mb_width,
py_off,
px_off,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
);
let c = get_mv_neighbor_above_right_mbaff(
mv_store_l0,
ref_idx_store_l0,
mb_idx,
mb_width,
py_off,
px_off,
part_w,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
)
.or_else(|| {
get_mv_neighbor_above_left_mbaff(
mv_store_l0,
ref_idx_store_l0,
mb_idx,
mb_width,
py_off,
px_off,
mb_slice_id,
cur_slice_id,
mctx.mbaff,
mctx.mb_field_decoding,
)
});
if part_w == 16 && part_h == 8 {
if part_idx == 0 {
if let Some((mv, ri)) = b {
if ri == ref_idx {
return (mv[0], mv[1]);
}
}
} else if let Some((mv, ri)) = a {
if ri == ref_idx {
return (mv[0], mv[1]);
}
}
}
if part_w == 8 && part_h == 16 {
if part_idx == 0 {
if let Some((mv, ri)) = a {
if ri == ref_idx {
return (mv[0], mv[1]);
}
}
} else if let Some((mv, ri)) = c {
if ri == ref_idx {
return (mv[0], mv[1]);
}
}
}
let ref_a = a.map(|(_, r)| r).unwrap_or(-1);
let ref_b = b.map(|(_, r)| r).unwrap_or(-1);
let ref_c = c.map(|(_, r)| r).unwrap_or(-1);
let match_count =
(ref_a == ref_idx) as u8 + (ref_b == ref_idx) as u8 + (ref_c == ref_idx) as u8;
if match_count == 1 {
if ref_a == ref_idx {
if let Some((mv, _)) = a {
return (mv[0], mv[1]);
}
}
if ref_b == ref_idx {
if let Some((mv, _)) = b {
return (mv[0], mv[1]);
}
}
if ref_c == ref_idx {
if let Some((mv, _)) = c {
return (mv[0], mv[1]);
}
}
}
if let (None, None, Some((mv, _))) = (b, c, a) {
return (mv[0], mv[1]);
}
let mv_a = a.map(|(mv, _)| mv).unwrap_or([0, 0]);
let mv_b = b.map(|(mv, _)| mv).unwrap_or([0, 0]);
let mv_c = c.map(|(mv, _)| mv).unwrap_or([0, 0]);
let mut xs = [mv_a[0], mv_b[0], mv_c[0]];
let mut ys = [mv_a[1], mv_b[1], mv_c[1]];
xs.sort();
ys.sort();
(xs[1], ys[1])
}
#[inline]
fn mbaff_left_neighbor(
mb_idx: usize,
mb_width: usize,
py_off: usize,
mb_field_decoding: &[bool],
) -> Option<(usize, usize)> {
let pair_addr = mb_idx / 2;
let pair_col = pair_addr % mb_width;
if pair_col == 0 {
return None;
}
let left_pair = pair_addr - 1;
let is_top = mb_idx.is_multiple_of(2);
let cur_is_field = mb_field_decoding[pair_addr];
let left_is_field = mb_field_decoding[left_pair];
match (cur_is_field, left_is_field) {
(false, false) => {
let left_mb = left_pair * 2 + (if is_top { 0 } else { 1 });
Some((left_mb, py_off))
}
(true, true) => {
let left_mb = left_pair * 2 + (if is_top { 0 } else { 1 });
Some((left_mb, py_off))
}
(false, true) => {
let y_in_pair = py_off + if is_top { 0 } else { 16 };
let left_mb = left_pair * 2 + (y_in_pair % 2); let remap_py = y_in_pair / 2;
Some((left_mb, remap_py.min(15)))
}
(true, false) => {
let y_in_pair = py_off * 2 + if is_top { 0 } else { 1 };
let left_mb = left_pair * 2 + (if y_in_pair < 16 { 0 } else { 1 });
let remap_py = y_in_pair % 16;
Some((left_mb, remap_py))
}
}
}
#[inline]
fn mbaff_above_neighbor(
mb_idx: usize,
mb_width: usize,
mb_field_decoding: &[bool],
) -> Option<(usize, usize)> {
let pair_addr = mb_idx / 2;
let is_top = mb_idx.is_multiple_of(2);
if !is_top {
let cur_is_field = mb_field_decoding[pair_addr];
if cur_is_field {
Some((mb_idx - 1, 15))
} else {
Some((mb_idx - 1, 15))
}
} else {
let pair_row = pair_addr / mb_width;
if pair_row == 0 {
return None;
}
let above_pair = pair_addr - mb_width;
let cur_is_field = mb_field_decoding[pair_addr];
let above_is_field = mb_field_decoding[above_pair];
match (cur_is_field, above_is_field) {
(false, false) => {
Some((above_pair * 2 + 1, 15))
}
(true, true) => {
Some((above_pair * 2, 15))
}
(false, true) => {
Some((above_pair * 2 + 1, 15))
}
(true, false) => {
Some((above_pair * 2 + 1, 15))
}
}
}
}
#[inline]
fn mbaff_scale_mv(
mv: [i16; 2],
cur_pair: usize,
nbr_pair: usize,
mb_field_decoding: &[bool],
) -> [i16; 2] {
let cur_field = mb_field_decoding[cur_pair];
let nbr_field = mb_field_decoding[nbr_pair];
if cur_field == nbr_field {
mv
} else if !cur_field && nbr_field {
[mv[0], mv[1].saturating_mul(2)]
} else {
[mv[0], mv[1] / 2]
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn get_mv_neighbor_left_mbaff(
mv_store_l0: &[[i16; 2]],
ref_idx_store_l0: &[i8],
mb_idx: usize,
mb_width: usize,
py_off: usize,
px_off: usize,
mb_slice_id: &[u16],
cur_slice_id: u16,
mbaff: bool,
mb_field_decoding: &[bool],
) -> Option<([i16; 2], i8)> {
if px_off > 0 {
let lr = py_off / 4;
let lc = (px_off - 4) / 4;
let blk = OFFSET_TO_BLOCK[lr][lc];
Some((
mv_store_l0[mb_idx * 16 + blk],
ref_idx_store_l0[mb_idx * 16 + blk],
))
} else if !mbaff {
let mb_col = mb_idx % mb_width;
if mb_col == 0 {
return None;
}
let left_mb = mb_idx - 1;
if mb_slice_id[left_mb] != cur_slice_id {
return None;
}
let blk = OFFSET_TO_BLOCK[py_off / 4][3];
Some((
mv_store_l0[left_mb * 16 + blk],
ref_idx_store_l0[left_mb * 16 + blk],
))
} else {
let (left_mb, remap_py) = mbaff_left_neighbor(mb_idx, mb_width, py_off, mb_field_decoding)?;
if mb_slice_id[left_mb] != cur_slice_id {
return None;
}
let blk = OFFSET_TO_BLOCK[remap_py / 4][3];
let mv = mbaff_scale_mv(
mv_store_l0[left_mb * 16 + blk],
mb_idx / 2,
left_mb / 2,
mb_field_decoding,
);
Some((mv, ref_idx_store_l0[left_mb * 16 + blk]))
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn get_mv_neighbor_above_mbaff(
mv_store_l0: &[[i16; 2]],
ref_idx_store_l0: &[i8],
mb_idx: usize,
mb_width: usize,
py_off: usize,
px_off: usize,
mb_slice_id: &[u16],
cur_slice_id: u16,
mbaff: bool,
mb_field_decoding: &[bool],
) -> Option<([i16; 2], i8)> {
if py_off > 0 {
let lr = (py_off - 4) / 4;
let lc = px_off / 4;
let blk = OFFSET_TO_BLOCK[lr][lc];
Some((
mv_store_l0[mb_idx * 16 + blk],
ref_idx_store_l0[mb_idx * 16 + blk],
))
} else if !mbaff {
let mb_row = mb_idx / mb_width;
if mb_row == 0 {
return None;
}
let above_mb = mb_idx - mb_width;
if mb_slice_id[above_mb] != cur_slice_id {
return None;
}
let blk = OFFSET_TO_BLOCK[3][px_off / 4];
Some((
mv_store_l0[above_mb * 16 + blk],
ref_idx_store_l0[above_mb * 16 + blk],
))
} else {
let (above_mb, remap_py) = mbaff_above_neighbor(mb_idx, mb_width, mb_field_decoding)?;
if mb_slice_id[above_mb] != cur_slice_id {
return None;
}
let blk = OFFSET_TO_BLOCK[remap_py / 4][px_off / 4];
let mv = mbaff_scale_mv(
mv_store_l0[above_mb * 16 + blk],
mb_idx / 2,
above_mb / 2,
mb_field_decoding,
);
Some((mv, ref_idx_store_l0[above_mb * 16 + blk]))
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn get_mv_neighbor_above_right_mbaff(
mv_store_l0: &[[i16; 2]],
ref_idx_store_l0: &[i8],
mb_idx: usize,
mb_width: usize,
py_off: usize,
px_off: usize,
part_w: usize,
mb_slice_id: &[u16],
cur_slice_id: u16,
mbaff: bool,
mb_field_decoding: &[bool],
) -> Option<([i16; 2], i8)> {
let right_col = px_off + part_w;
if py_off > 0 {
if right_col < 16 {
let cur_8x8_col = px_off / 8;
let tgt_col = right_col;
let tgt_8x8_col = tgt_col / 8;
let tgt_8x8_row = (py_off - 4) / 8;
let cur_8x8_row = py_off / 8;
let cur_8x8 = cur_8x8_row * 2 + cur_8x8_col;
let tgt_8x8 = tgt_8x8_row * 2 + tgt_8x8_col;
if tgt_8x8 > cur_8x8 {
return None;
}
let lr = (py_off - 4) / 4;
let lc = right_col / 4;
let blk = OFFSET_TO_BLOCK[lr][lc];
Some((
mv_store_l0[mb_idx * 16 + blk],
ref_idx_store_l0[mb_idx * 16 + blk],
))
} else {
None }
} else if !mbaff && mb_idx / mb_width > 0 {
if right_col < 16 {
let above_mb = mb_idx - mb_width;
if mb_slice_id[above_mb] != cur_slice_id {
return None;
}
let blk = OFFSET_TO_BLOCK[3][right_col / 4];
Some((
mv_store_l0[above_mb * 16 + blk],
ref_idx_store_l0[above_mb * 16 + blk],
))
} else if mb_idx % mb_width + 1 < mb_width {
let above_right_mb = mb_idx - mb_width + 1;
if mb_slice_id[above_right_mb] != cur_slice_id {
return None;
}
let blk = OFFSET_TO_BLOCK[3][0];
Some((
mv_store_l0[above_right_mb * 16 + blk],
ref_idx_store_l0[above_right_mb * 16 + blk],
))
} else {
None
}
} else if mbaff {
let (above_mb, remap_py) = mbaff_above_neighbor(mb_idx, mb_width, mb_field_decoding)?;
if right_col < 16 {
if mb_slice_id[above_mb] != cur_slice_id {
return None;
}
let blk = OFFSET_TO_BLOCK[remap_py / 4][right_col / 4];
let mv = mbaff_scale_mv(
mv_store_l0[above_mb * 16 + blk],
mb_idx / 2,
above_mb / 2,
mb_field_decoding,
);
Some((mv, ref_idx_store_l0[above_mb * 16 + blk]))
} else {
if !mb_idx.is_multiple_of(2) {
return None;
}
let pair_addr = mb_idx / 2;
let pair_col = pair_addr % mb_width;
if pair_col + 1 >= mb_width {
return None;
}
let above_pair = match mbaff_above_neighbor(mb_idx, mb_width, mb_field_decoding) {
Some((above_mb, _)) => above_mb / 2,
None => return None,
};
let above_right_pair = above_pair + 1;
let above_mb_pos = mbaff_above_neighbor(mb_idx, mb_width, mb_field_decoding)
.map(|(m, _)| m % 2)
.unwrap_or(1);
let ar_mb = above_right_pair * 2 + above_mb_pos;
if mb_slice_id.get(ar_mb).copied() != Some(cur_slice_id) {
return None;
}
let blk = OFFSET_TO_BLOCK[remap_py / 4][0];
let mv = mbaff_scale_mv(
mv_store_l0[ar_mb * 16 + blk],
mb_idx / 2,
ar_mb / 2,
mb_field_decoding,
);
Some((mv, ref_idx_store_l0[ar_mb * 16 + blk]))
}
} else {
None
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn get_mv_neighbor_above_left_mbaff(
mv_store_l0: &[[i16; 2]],
ref_idx_store_l0: &[i8],
mb_idx: usize,
mb_width: usize,
py_off: usize,
px_off: usize,
mb_slice_id: &[u16],
cur_slice_id: u16,
mbaff: bool,
mb_field_decoding: &[bool],
) -> Option<([i16; 2], i8)> {
if py_off > 0 && px_off > 0 {
let blk = OFFSET_TO_BLOCK[(py_off - 4) / 4][(px_off - 4) / 4];
return Some((
mv_store_l0[mb_idx * 16 + blk],
ref_idx_store_l0[mb_idx * 16 + blk],
));
}
if py_off == 0 && px_off == 0 {
let (above_mb, above_py) = if !mbaff {
let mb_row = mb_idx / mb_width;
if mb_row == 0 {
return None;
}
(mb_idx - mb_width, 15usize)
} else {
mbaff_above_neighbor(mb_idx, mb_width, mb_field_decoding)?
};
let (al_mb, al_py) = if !mbaff {
let mb_col = mb_idx % mb_width;
if mb_col == 0 {
return None;
}
(above_mb - 1, above_py)
} else {
mbaff_left_neighbor(above_mb, mb_width, above_py, mb_field_decoding)?
};
if mb_slice_id.get(al_mb).copied() != Some(cur_slice_id) {
return None;
}
let blk = OFFSET_TO_BLOCK[al_py / 4][3];
let mv = if mbaff {
mbaff_scale_mv(
mv_store_l0[al_mb * 16 + blk],
mb_idx / 2,
al_mb / 2,
mb_field_decoding,
)
} else {
mv_store_l0[al_mb * 16 + blk]
};
Some((mv, ref_idx_store_l0[al_mb * 16 + blk]))
} else if py_off == 0 && px_off > 0 {
let (above_mb, above_py) = if !mbaff {
let mb_row = mb_idx / mb_width;
if mb_row == 0 {
return None;
}
(mb_idx - mb_width, 15usize)
} else {
mbaff_above_neighbor(mb_idx, mb_width, mb_field_decoding)?
};
if mb_slice_id[above_mb] != cur_slice_id {
return None;
}
let blk = OFFSET_TO_BLOCK[above_py / 4][(px_off - 4) / 4];
let mv = if mbaff {
mbaff_scale_mv(
mv_store_l0[above_mb * 16 + blk],
mb_idx / 2,
above_mb / 2,
mb_field_decoding,
)
} else {
mv_store_l0[above_mb * 16 + blk]
};
Some((mv, ref_idx_store_l0[above_mb * 16 + blk]))
} else if py_off > 0 && px_off == 0 {
let (left_mb, left_py) = if !mbaff {
let mb_col = mb_idx % mb_width;
if mb_col == 0 {
return None;
}
(mb_idx - 1, py_off - 4)
} else {
mbaff_left_neighbor(mb_idx, mb_width, py_off - 4, mb_field_decoding)?
};
if mb_slice_id[left_mb] != cur_slice_id {
return None;
}
let blk = OFFSET_TO_BLOCK[left_py / 4][3];
let mv = if mbaff {
mbaff_scale_mv(
mv_store_l0[left_mb * 16 + blk],
mb_idx / 2,
left_mb / 2,
mb_field_decoding,
)
} else {
mv_store_l0[left_mb * 16 + blk]
};
Some((mv, ref_idx_store_l0[left_mb * 16 + blk]))
} else {
None
}
}