#![forbid(unsafe_code)]
use crate::include::common::intops::apply_sign;
use crate::include::dav1d::headers::Rav1dFilterMode;
use crate::include::dav1d::headers::Rav1dFrameHeader;
use crate::include::dav1d::headers::Rav1dWarpedMotionParams;
use crate::include::dav1d::headers::Rav1dWarpedMotionType;
use crate::src::align::Align8;
use crate::src::disjoint_mut::DisjointMut;
use crate::src::disjoint_mut::DisjointMutSlice;
use crate::src::internal::Bxy;
use crate::src::levels::BlockLevel;
use crate::src::levels::BlockPartition;
use crate::src::levels::CompInterType;
use crate::src::levels::DCT_DCT;
use crate::src::levels::H_ADST;
use crate::src::levels::H_FLIPADST;
use crate::src::levels::IDTX;
use crate::src::levels::Mv;
use crate::src::levels::SegmentId;
use crate::src::levels::TxfmSize;
use crate::src::levels::TxfmType;
use crate::src::levels::V_ADST;
use crate::src::levels::V_FLIPADST;
use crate::src::refmvs::RefMvsCandidate;
use crate::src::tables::TxfmInfo;
use std::cmp;
use std::cmp::Ordering;
use std::ffi::c_int;
use std::ffi::c_uint;
#[derive(Default)]
pub struct BlockContext {
pub mode: DisjointMut<Align8<[u8; 32]>>,
pub lcoef: DisjointMut<Align8<[u8; 32]>>,
pub ccoef: [DisjointMut<Align8<[u8; 32]>>; 2],
pub seg_pred: DisjointMut<Align8<[u8; 32]>>,
pub skip: DisjointMut<Align8<[u8; 32]>>,
pub skip_mode: DisjointMut<Align8<[u8; 32]>>,
pub intra: DisjointMut<Align8<[u8; 32]>>,
pub comp_type: DisjointMut<Align8<[Option<CompInterType>; 32]>>,
pub r#ref: [DisjointMut<Align8<[i8; 32]>>; 2],
pub filter: [DisjointMut<Align8<[Rav1dFilterMode; 32]>>; 2],
pub tx_intra: DisjointMut<Align8<[i8; 32]>>,
pub tx: DisjointMut<Align8<[TxfmSize; 32]>>,
pub tx_lpf_y: DisjointMut<Align8<[u8; 32]>>,
pub tx_lpf_uv: DisjointMut<Align8<[u8; 32]>>,
pub partition: DisjointMut<Align8<[u8; 16]>>,
pub uvmode: DisjointMut<Align8<[u8; 32]>>,
pub pal_sz: DisjointMut<Align8<[u8; 32]>>,
}
#[inline]
pub fn get_intra_ctx(
a: &BlockContext,
l: &BlockContext,
yb4: c_int,
xb4: c_int,
have_top: bool,
have_left: bool,
) -> u8 {
if have_left {
if have_top {
let ctx = *l.intra.index(yb4 as usize) + *a.intra.index(xb4 as usize);
ctx + (ctx == 2) as u8
} else {
*l.intra.index(yb4 as usize) * 2
}
} else {
if have_top {
*a.intra.index(xb4 as usize) * 2
} else {
0
}
}
}
#[inline]
pub fn get_tx_ctx(
a: &BlockContext,
l: &BlockContext,
max_tx: &TxfmInfo,
yb4: c_int,
xb4: c_int,
) -> u8 {
(*l.tx_intra.index(yb4 as usize) as i32 >= max_tx.lh as i32) as u8
+ (*a.tx_intra.index(xb4 as usize) as i32 >= max_tx.lw as i32) as u8
}
#[inline]
pub fn get_partition_ctx(
a: &BlockContext,
l: &BlockContext,
bl: BlockLevel,
yb8: c_int,
xb8: c_int,
) -> u8 {
let has_bl = |x| (x >> (4 - bl as u8)) & 1;
has_bl(*a.partition.index(xb8 as usize)) + 2 * has_bl(*l.partition.index(yb8 as usize))
}
#[inline]
pub fn gather_left_partition_prob(r#in: &[u16; 16], bl: BlockLevel) -> u32 {
let mut out =
r#in[BlockPartition::H as usize - 1] as i32 - r#in[BlockPartition::H as usize] as i32;
out += r#in[BlockPartition::Split as usize - 1] as i32
- r#in[BlockPartition::LeftSplit as usize] as i32;
if bl != BlockLevel::Bl128x128 {
out +=
r#in[BlockPartition::H4 as usize - 1] as i32 - r#in[BlockPartition::H4 as usize] as i32;
}
out as u32
}
#[inline]
pub fn gather_top_partition_prob(r#in: &[u16; 16], bl: BlockLevel) -> u32 {
let mut out = r#in[BlockPartition::V as usize - 1] as i32
- r#in[BlockPartition::TopSplit as usize] as i32;
out += r#in[BlockPartition::LeftSplit as usize - 1] as i32;
if bl != BlockLevel::Bl128x128 {
out += r#in[BlockPartition::V4 as usize - 1] as i32
- r#in[BlockPartition::RightSplit as usize] as i32;
}
out as u32
}
#[inline]
pub fn get_uv_inter_txtp(uvt_dim: &TxfmInfo, ytxtp: TxfmType) -> TxfmType {
if uvt_dim.max == TxfmSize::S32x32 as _ {
return if ytxtp == IDTX { IDTX } else { DCT_DCT };
}
if uvt_dim.min == TxfmSize::S16x16 as _
&& ((1 << ytxtp as u8)
& ((1 << H_FLIPADST) | (1 << V_FLIPADST) | (1 << H_ADST) | (1 << V_ADST)))
!= 0
{
return DCT_DCT;
}
ytxtp
}
#[inline]
pub fn get_filter_ctx(
a: &BlockContext,
l: &BlockContext,
comp: bool,
dir: bool,
r#ref: i8,
yb4: c_int,
xb4: c_int,
) -> u8 {
let [a_filter, l_filter] = [(a, xb4), (l, yb4)].map(|(al, b4)| {
if *al.r#ref[0].index(b4 as usize) == r#ref || *al.r#ref[1].index(b4 as usize) == r#ref {
*al.filter[dir as usize].index(b4 as usize)
} else {
Rav1dFilterMode::N_SWITCHABLE_FILTERS
}
});
(comp as u8) * 4
+ (if a_filter == l_filter {
a_filter
} else if a_filter == Rav1dFilterMode::N_SWITCHABLE_FILTERS {
l_filter
} else if l_filter == Rav1dFilterMode::N_SWITCHABLE_FILTERS {
a_filter
} else {
Rav1dFilterMode::N_SWITCHABLE_FILTERS
} as u8)
}
#[inline]
pub fn get_comp_ctx(
a: &BlockContext,
l: &BlockContext,
yb4: c_int,
xb4: c_int,
have_top: bool,
have_left: bool,
) -> u8 {
if have_top {
if have_left {
if a.comp_type.index(xb4 as usize).is_some() {
if l.comp_type.index(yb4 as usize).is_some() {
4
} else {
2 + (*l.r#ref[0].index(yb4 as usize) as c_uint >= 4) as u8
}
} else if l.comp_type.index(yb4 as usize).is_some() {
2 + (*a.r#ref[0].index(xb4 as usize) as c_uint >= 4) as u8
} else {
((*l.r#ref[0].index(yb4 as usize) >= 4) ^ (*a.r#ref[0].index(xb4 as usize) >= 4))
as u8
}
} else {
if a.comp_type.index(xb4 as usize).is_some() {
3
} else {
(*a.r#ref[0].index(xb4 as usize) >= 4) as u8
}
}
} else if have_left {
if l.comp_type.index(yb4 as usize).is_some() {
3
} else {
(*l.r#ref[0].index(yb4 as usize) >= 4) as u8
}
} else {
1
}
}
#[inline]
pub fn get_comp_dir_ctx(
a: &BlockContext,
l: &BlockContext,
yb4: c_int,
xb4: c_int,
have_top: bool,
have_left: bool,
) -> u8 {
let has_uni_comp = |edge: &BlockContext, off| {
(*edge.r#ref[0].index(off as usize) < 4) == (*edge.r#ref[1].index(off as usize) < 4)
};
if have_top && have_left {
let a_intra = *a.intra.index(xb4 as usize) != 0;
let l_intra = *l.intra.index(yb4 as usize) != 0;
if a_intra && l_intra {
return 2;
}
if a_intra || l_intra {
let edge = if a_intra { &l } else { &a };
let off = if a_intra { yb4 } else { xb4 };
if edge.comp_type.index(off as usize).is_none() {
return 2;
}
return 1 + 2 * has_uni_comp(edge, off) as u8;
}
let a_comp = a.comp_type.index(xb4 as usize).is_some();
let l_comp = l.comp_type.index(yb4 as usize).is_some();
let a_ref0 = *a.r#ref[0].index(xb4 as usize);
let l_ref0 = *l.r#ref[0].index(yb4 as usize);
if !a_comp && !l_comp {
return 1 + 2 * ((a_ref0 >= 4) == (l_ref0 >= 4)) as u8;
} else if !a_comp || !l_comp {
let edge = if a_comp { &a } else { &l };
let off = if a_comp { xb4 } else { yb4 };
if !has_uni_comp(edge, off) {
return 1;
}
return 3 + ((a_ref0 >= 4) == (l_ref0 >= 4)) as u8;
} else {
let a_uni = has_uni_comp(&a, xb4);
let l_uni = has_uni_comp(&l, yb4);
if !a_uni && !l_uni {
return 0;
}
if !a_uni || !l_uni {
return 2;
}
return 3 + ((a_ref0 == 4) == (l_ref0 == 4)) as u8;
}
} else if have_top || have_left {
let edge = if have_left { l } else { a };
let off = if have_left { yb4 } else { xb4 };
if *edge.intra.index(off as usize) != 0 {
return 2;
}
if edge.comp_type.index(off as usize).is_none() {
return 2;
}
return 4 * has_uni_comp(&edge, off) as u8;
} else {
return 2;
};
}
#[inline]
pub fn get_poc_diff(order_hint_n_bits: u8, poc0: c_int, poc1: c_int) -> c_int {
if order_hint_n_bits == 0 {
return 0;
}
let mask = 1 << order_hint_n_bits - 1;
let diff = poc0 - poc1;
return (diff & mask - 1) - (diff & mask);
}
#[inline]
pub fn get_jnt_comp_ctx(
order_hint_n_bits: u8,
poc: c_uint,
ref0poc: c_uint,
ref1poc: c_uint,
a: &BlockContext,
l: &BlockContext,
yb4: c_int,
xb4: c_int,
) -> u8 {
let d0 = get_poc_diff(order_hint_n_bits, ref0poc as c_int, poc as c_int).abs();
let d1 = get_poc_diff(order_hint_n_bits, poc as c_int, ref1poc as c_int).abs();
let offset = (d0 == d1) as u8;
let [a_ctx, l_ctx] = [(a, xb4), (l, yb4)].map(|(al, b4)| {
(*al.comp_type.index(b4 as usize) >= Some(CompInterType::Avg)
|| *al.r#ref[0].index(b4 as usize) == 6) as u8
});
3 * offset + a_ctx + l_ctx
}
#[inline]
pub fn get_mask_comp_ctx(a: &BlockContext, l: &BlockContext, yb4: c_int, xb4: c_int) -> u8 {
let [a_ctx, l_ctx] = [(a, xb4), (l, yb4)].map(|(al, b4)| {
if *al.comp_type.index(b4 as usize) >= Some(CompInterType::Seg) {
1
} else if *al.r#ref[0].index(b4 as usize) == 6 {
3
} else {
0
}
});
cmp::min(a_ctx + l_ctx, 5)
}
fn cmp_counts(c1: u8, c2: u8) -> u8 {
use Ordering::*;
match c1.cmp(&c2) {
Less => 0,
Equal => 1,
Greater => 2,
}
}
#[inline]
pub fn av1_get_ref_ctx(
a: &BlockContext,
l: &BlockContext,
yb4: c_int,
xb4: c_int,
have_top: bool,
have_left: bool,
) -> u8 {
let mut cnt = [0; 2];
if have_top && *a.intra.index(xb4 as usize) == 0 {
cnt[(*a.r#ref[0].index(xb4 as usize) >= 4) as usize] += 1;
if a.comp_type.index(xb4 as usize).is_some() {
cnt[(*a.r#ref[1].index(xb4 as usize) >= 4) as usize] += 1;
}
}
if have_left && *l.intra.index(yb4 as usize) == 0 {
cnt[(*l.r#ref[0].index(yb4 as usize) >= 4) as usize] += 1;
if l.comp_type.index(yb4 as usize).is_some() {
cnt[(*l.r#ref[1].index(yb4 as usize) >= 4) as usize] += 1;
}
}
cmp_counts(cnt[0], cnt[1])
}
#[inline]
pub fn av1_get_fwd_ref_ctx(
a: &BlockContext,
l: &BlockContext,
yb4: c_int,
xb4: c_int,
have_top: bool,
have_left: bool,
) -> u8 {
let mut cnt = [0; 4];
if have_top && *a.intra.index(xb4 as usize) == 0 {
let ref0 = *a.r#ref[0].index(xb4 as usize);
if ref0 < 4 {
cnt[ref0 as usize] += 1;
}
let ref1 = *a.r#ref[1].index(xb4 as usize);
if a.comp_type.index(xb4 as usize).is_some() && ref1 < 4 {
cnt[ref1 as usize] += 1;
}
}
if have_left && *l.intra.index(yb4 as usize) == 0 {
let ref0 = *l.r#ref[0].index(yb4 as usize);
if ref0 < 4 {
cnt[ref0 as usize] += 1;
}
let ref1 = *l.r#ref[1].index(yb4 as usize);
if l.comp_type.index(yb4 as usize).is_some() && ref1 < 4 {
cnt[ref1 as usize] += 1;
}
}
cnt[0] += cnt[1];
cnt[2] += cnt[3];
cmp_counts(cnt[0], cnt[2])
}
#[inline]
pub fn av1_get_fwd_ref_1_ctx(
a: &BlockContext,
l: &BlockContext,
yb4: c_int,
xb4: c_int,
have_top: bool,
have_left: bool,
) -> u8 {
let mut cnt = [0; 2];
if have_top && *a.intra.index(xb4 as usize) == 0 {
let ref0 = *a.r#ref[0].index(xb4 as usize);
if ref0 < 2 {
cnt[ref0 as usize] += 1;
}
let ref1 = *a.r#ref[1].index(xb4 as usize);
if a.comp_type.index(xb4 as usize).is_some() && ref1 < 2 {
cnt[ref1 as usize] += 1;
}
}
if have_left && *l.intra.index(yb4 as usize) == 0 {
let ref0 = *l.r#ref[0].index(yb4 as usize);
if ref0 < 2 {
cnt[ref0 as usize] += 1;
}
let ref1 = *l.r#ref[1].index(yb4 as usize);
if l.comp_type.index(yb4 as usize).is_some() && ref1 < 2 {
cnt[ref1 as usize] += 1;
}
}
cmp_counts(cnt[0], cnt[1])
}
#[inline]
pub fn av1_get_fwd_ref_2_ctx(
a: &BlockContext,
l: &BlockContext,
yb4: c_int,
xb4: c_int,
have_top: bool,
have_left: bool,
) -> u8 {
let mut cnt = [0; 2];
if have_top && *a.intra.index(xb4 as usize) == 0 {
let ref0 = *a.r#ref[0].index(xb4 as usize);
if (ref0 ^ 2) < 2 {
cnt[(ref0 - 2) as usize] += 1;
}
let ref1 = *a.r#ref[1].index(xb4 as usize);
if a.comp_type.index(xb4 as usize).is_some() && (ref1 ^ 2) < 2 {
cnt[(ref1 - 2) as usize] += 1;
}
}
if have_left && *l.intra.index(yb4 as usize) == 0 {
let ref0 = *l.r#ref[0].index(yb4 as usize);
if (ref0 ^ 2) < 2 {
cnt[(ref0 - 2) as usize] += 1;
}
let ref1 = *l.r#ref[1].index(yb4 as usize);
if l.comp_type.index(yb4 as usize).is_some() && (ref1 ^ 2) < 2 {
cnt[(ref1 - 2) as usize] += 1;
}
}
cmp_counts(cnt[0], cnt[1])
}
#[inline]
pub fn av1_get_bwd_ref_ctx(
a: &BlockContext,
l: &BlockContext,
yb4: c_int,
xb4: c_int,
have_top: bool,
have_left: bool,
) -> u8 {
let mut cnt = [0; 3];
if have_top && *a.intra.index(xb4 as usize) == 0 {
let ref0 = *a.r#ref[0].index(xb4 as usize);
if ref0 >= 4 {
cnt[(ref0 - 4) as usize] += 1;
}
let ref1 = *a.r#ref[1].index(xb4 as usize);
if a.comp_type.index(xb4 as usize).is_some() && ref1 >= 4 {
cnt[(ref1 - 4) as usize] += 1;
}
}
if have_left && *l.intra.index(yb4 as usize) == 0 {
let ref0 = *l.r#ref[0].index(yb4 as usize);
if ref0 >= 4 {
cnt[(ref0 - 4) as usize] += 1;
}
let ref1 = *l.r#ref[1].index(yb4 as usize);
if l.comp_type.index(yb4 as usize).is_some() && ref1 >= 4 {
cnt[(ref1 - 4) as usize] += 1;
}
}
cnt[1] += cnt[0];
cmp_counts(cnt[1], cnt[2])
}
#[inline]
pub fn av1_get_bwd_ref_1_ctx(
a: &BlockContext,
l: &BlockContext,
yb4: c_int,
xb4: c_int,
have_top: bool,
have_left: bool,
) -> u8 {
let mut cnt = [0; 3];
if have_top && *a.intra.index(xb4 as usize) == 0 {
let ref0 = *a.r#ref[0].index(xb4 as usize);
if ref0 >= 4 {
cnt[(ref0 - 4) as usize] += 1;
}
let ref1 = *a.r#ref[1].index(xb4 as usize);
if a.comp_type.index(xb4 as usize).is_some() && ref1 >= 4 {
cnt[(ref1 - 4) as usize] += 1;
}
}
if have_left && *l.intra.index(yb4 as usize) == 0 {
let ref0 = *l.r#ref[0].index(yb4 as usize);
if ref0 >= 4 {
cnt[(ref0 - 4) as usize] += 1;
}
let ref1 = *l.r#ref[1].index(yb4 as usize);
if l.comp_type.index(yb4 as usize).is_some() && ref1 >= 4 {
cnt[(ref1 - 4) as usize] += 1;
}
}
cmp_counts(cnt[0], cnt[1])
}
#[inline]
pub fn av1_get_uni_p1_ctx(
a: &BlockContext,
l: &BlockContext,
yb4: c_int,
xb4: c_int,
have_top: bool,
have_left: bool,
) -> u8 {
let mut cnt = [0; 3];
if have_top && *a.intra.index(xb4 as usize) == 0 {
if let Some(cnt) = cnt.get_mut((*a.r#ref[0].index(xb4 as usize) - 1) as usize) {
*cnt += 1;
}
if a.comp_type.index(xb4 as usize).is_some() {
if let Some(cnt) = cnt.get_mut((*a.r#ref[1].index(xb4 as usize) - 1) as usize) {
*cnt += 1;
}
}
}
if have_left && *l.intra.index(yb4 as usize) == 0 {
if let Some(cnt) = cnt.get_mut((*l.r#ref[0].index(yb4 as usize) - 1) as usize) {
*cnt += 1;
}
if l.comp_type.index(yb4 as usize).is_some() {
if let Some(cnt) = cnt.get_mut((*l.r#ref[1].index(yb4 as usize) - 1) as usize) {
*cnt += 1;
}
}
}
cnt[1] += cnt[2];
cmp_counts(cnt[0], cnt[1])
}
#[inline]
pub fn get_drl_context(ref_mv_stack: &[RefMvsCandidate; 8], ref_idx: usize) -> c_int {
if ref_mv_stack[ref_idx].weight >= 640 {
(ref_mv_stack[ref_idx + 1].weight < 640) as c_int
} else if ref_mv_stack[ref_idx + 1].weight < 640 {
2
} else {
0
}
}
#[inline]
pub fn get_cur_frame_segid(
b: Bxy,
have_top: bool,
have_left: bool,
cur_seg_map: &DisjointMutSlice<SegmentId>,
stride: usize,
) -> (SegmentId, u8) {
let negative_adjustment = have_left as usize + have_top as usize * stride;
let offset = b.x as usize + b.y as usize * stride - negative_adjustment;
match (have_left, have_top) {
(true, true) => {
let l = *cur_seg_map.index(offset + stride);
let a = *cur_seg_map.index(offset + 1);
let al = *cur_seg_map.index(offset);
let seg_ctx = if l == a && al == l {
2
} else if l == a || al == l || a == al {
1
} else {
0
};
let seg_id = if a == al { a } else { l };
(seg_id, seg_ctx)
}
(true, false) | (false, true) => (*cur_seg_map.index(offset), 0),
(false, false) => (Default::default(), 0),
}
}
#[inline]
fn fix_int_mv_precision(mv: &mut Mv) {
mv.x = (mv.x - (mv.x >> 15) + 3) & !7;
mv.y = (mv.y - (mv.y >> 15) + 3) & !7;
}
#[inline]
pub(crate) fn fix_mv_precision(hdr: &Rav1dFrameHeader, mv: &mut Mv) {
if hdr.force_integer_mv {
fix_int_mv_precision(mv);
} else if !(*hdr).hp {
mv.x = (mv.x - (mv.x >> 15)) & !1;
mv.y = (mv.y - (mv.y >> 15)) & !1;
}
}
#[inline]
pub(crate) fn get_gmv_2d(
gmv: &Rav1dWarpedMotionParams,
bx4: c_int,
by4: c_int,
bw4: c_int,
bh4: c_int,
hdr: &Rav1dFrameHeader,
) -> Mv {
match gmv.r#type {
Rav1dWarpedMotionType::RotZoom => {
assert!(gmv.matrix[5] == gmv.matrix[2]);
assert!(gmv.matrix[4] == -gmv.matrix[3]);
}
Rav1dWarpedMotionType::Translation => {
let mut res = Mv {
y: (gmv.matrix[0] >> 13) as i16,
x: (gmv.matrix[1] >> 13) as i16,
};
if hdr.force_integer_mv {
fix_int_mv_precision(&mut res);
}
return res;
}
Rav1dWarpedMotionType::Identity => {
return Mv::ZERO;
}
Rav1dWarpedMotionType::Affine => {}
}
let x = bx4 * 4 + bw4 * 2 - 1;
let y = by4 * 4 + bh4 * 2 - 1;
let xc = (gmv.matrix[2] - (1 << 16)) * x + gmv.matrix[3] * y + gmv.matrix[0];
let yc = (gmv.matrix[5] - (1 << 16)) * y + gmv.matrix[4] * x + gmv.matrix[1];
let shift = 16 - (3 - !hdr.hp as c_int);
let round = 1 << shift >> 1;
let mut res = Mv {
y: apply_sign(yc.abs() + round >> shift << !hdr.hp as c_int, yc) as i16,
x: apply_sign(xc.abs() + round >> shift << !hdr.hp as c_int, xc) as i16,
};
if hdr.force_integer_mv {
fix_int_mv_precision(&mut res);
}
return res;
}