use crate::bitreader::BitReader;
use crate::error::DecodeError;
fn e(s: &'static str) -> DecodeError {
DecodeError::Bitstream(s.into())
}
#[inline]
fn wp_offset_range(bit_depth: u8, high_precision: bool) -> i32 {
if high_precision {
1i32 << bit_depth.saturating_sub(1)
} else {
128
}
}
#[inline]
fn wp_offset_scale(bit_depth: u8, high_precision: bool) -> i32 {
if high_precision {
1
} else {
1i32 << bit_depth.saturating_sub(8)
}
}
#[inline]
fn normalize_luma_offset(offset: i32, bit_depth: u8, high_precision: bool) -> i32 {
let range = wp_offset_range(bit_depth, high_precision);
offset
.clamp(-range, range - 1)
.saturating_mul(wp_offset_scale(bit_depth, high_precision))
}
#[inline]
fn derive_chroma_offset(
delta: i32,
weight: i32,
log2_denom: u8,
bit_depth: u8,
high_precision: bool,
) -> i32 {
let range = wp_offset_range(bit_depth, high_precision) as i64;
let pred = range - ((range * weight as i64) >> log2_denom);
let offset = (delta as i64 + pred).clamp(-range, range - 1) as i32;
offset.saturating_mul(wp_offset_scale(bit_depth, high_precision))
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
pub(crate) struct Mv {
pub(crate) x: i16,
pub(crate) y: i16,
}
impl Mv {
#[inline]
pub(crate) fn new(x: i16, y: i16) -> Self {
Mv { x, y }
}
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
pub(crate) struct PredFlags {
pub(crate) l0: bool,
pub(crate) l1: bool,
}
#[derive(Clone, Copy, Default, Debug)]
pub(crate) struct MotionInfo {
pub(crate) pred: PredFlags,
pub(crate) mv: [Mv; 2],
pub(crate) ref_idx: [i8; 2],
pub(crate) ref_poc: [i32; 2],
pub(crate) ref_lt: [bool; 2],
pub(crate) is_intra: bool,
}
impl MotionInfo {
#[inline]
pub(crate) fn intra() -> Self {
MotionInfo {
is_intra: true,
ref_idx: [-1, -1],
..Default::default()
}
}
#[inline]
pub(crate) fn pred_used(&self, l: usize) -> bool {
if l == 0 { self.pred.l0 } else { self.pred.l1 }
}
}
#[derive(Clone, Debug)]
pub(crate) struct PredWeightTable {
pub(crate) luma_log2_denom: u8,
pub(crate) chroma_log2_denom: u8,
pub(crate) luma_weight: [Vec<i32>; 2],
pub(crate) luma_offset: [Vec<i32>; 2],
pub(crate) chroma_weight: [Vec<[i32; 2]>; 2],
pub(crate) chroma_offset: [Vec<[i32; 2]>; 2],
pub(crate) luma_flag: [Vec<bool>; 2],
pub(crate) chroma_flag: [Vec<bool>; 2],
}
impl PredWeightTable {
#[allow(clippy::too_many_arguments)]
pub(crate) fn parse(
r: &mut BitReader,
num_ref: [usize; 2],
has_l1: bool,
chroma: bool,
bd_luma: u8,
bd_chroma: u8,
high_precision_offsets: bool,
is_curr_pic: [&[bool]; 2],
) -> Result<Self, DecodeError> {
let luma_log2_denom = r.read_ue().map_err(|_| e("luma_log2_denom"))? as u8;
let chroma_log2_denom = if chroma {
let d = r.read_se().map_err(|_| e("delta_chroma_log2_denom"))?;
(luma_log2_denom as i32 + d).clamp(0, 7) as u8
} else {
0
};
let default_luma_w = 1i32 << luma_log2_denom;
let default_chroma_w = 1i32 << chroma_log2_denom;
let mut t = PredWeightTable {
luma_log2_denom,
chroma_log2_denom,
luma_weight: [Vec::new(), Vec::new()],
luma_offset: [Vec::new(), Vec::new()],
chroma_weight: [Vec::new(), Vec::new()],
chroma_offset: [Vec::new(), Vec::new()],
luma_flag: [Vec::new(), Vec::new()],
chroma_flag: [Vec::new(), Vec::new()],
};
let lists = if has_l1 { 2 } else { 1 };
for (list, &n) in num_ref[..lists].iter().enumerate() {
let skip = |i: usize| is_curr_pic[list].get(i).copied().unwrap_or(false);
let mut luma_flags = Vec::with_capacity(n);
for i in 0..n {
luma_flags.push(if skip(i) {
false
} else {
r.read_flag().map_err(|_| e("luma_weight_flag"))?
});
}
let mut chroma_flags = vec![false; n];
if chroma {
for (i, dst) in chroma_flags[..n].iter_mut().enumerate() {
*dst = if skip(i) {
false
} else {
r.read_flag().map_err(|_| e("chroma_weight_flag"))?
};
}
}
for i in 0..n {
if luma_flags[i] {
let dw = r.read_se().map_err(|_| e("delta_luma_weight"))?;
let o = r.read_se().map_err(|_| e("luma_offset"))?;
t.luma_weight[list].push(default_luma_w + dw);
t.luma_offset[list].push(normalize_luma_offset(
o,
bd_luma,
high_precision_offsets,
));
} else {
t.luma_weight[list].push(default_luma_w);
t.luma_offset[list].push(0);
}
if chroma && chroma_flags[i] {
let mut w = [0i32; 2];
let mut o = [0i32; 2];
for c in 0..2 {
let dw = r.read_se().map_err(|_| e("delta_chroma_weight"))?;
let doff = r.read_se().map_err(|_| e("delta_chroma_offset"))?;
w[c] = default_chroma_w + dw;
o[c] = derive_chroma_offset(
doff,
w[c],
chroma_log2_denom,
bd_chroma,
high_precision_offsets,
);
}
t.chroma_weight[list].push(w);
t.chroma_offset[list].push(o);
} else {
t.chroma_weight[list].push([default_chroma_w, default_chroma_w]);
t.chroma_offset[list].push([0, 0]);
}
}
t.luma_flag[list] = luma_flags;
t.chroma_flag[list] = chroma_flags;
}
Ok(t)
}
#[inline]
pub(crate) fn luma(&self, list: usize, ref_idx: i8) -> (i32, i32) {
let i = ref_idx.max(0) as usize;
let w = self
.luma_weight
.get(list)
.and_then(|v| v.get(i))
.copied()
.unwrap_or(1 << self.luma_log2_denom);
let o = self
.luma_offset
.get(list)
.and_then(|v| v.get(i))
.copied()
.unwrap_or(0);
(w, o)
}
#[inline]
pub(crate) fn chroma(&self, list: usize, ref_idx: i8, plane: usize) -> (i32, i32) {
let i = ref_idx.max(0) as usize;
let w = self
.chroma_weight
.get(list)
.and_then(|v| v.get(i))
.map(|p| p[plane])
.unwrap_or(1 << self.chroma_log2_denom);
let o = self
.chroma_offset
.get(list)
.and_then(|v| v.get(i))
.map(|p| p[plane])
.unwrap_or(0);
(w, o)
}
}
#[cfg(test)]
mod tests {
use super::{derive_chroma_offset, normalize_luma_offset};
#[test]
fn legacy_offsets_are_scaled_from_eight_bit_units() {
assert_eq!(normalize_luma_offset(7, 8, false), 7);
assert_eq!(normalize_luma_offset(7, 10, false), 28);
assert_eq!(normalize_luma_offset(-128, 12, false), -2048);
}
#[test]
fn high_precision_offsets_stay_in_native_sample_units() {
assert_eq!(normalize_luma_offset(7, 10, true), 7);
assert_eq!(normalize_luma_offset(511, 10, true), 511);
assert_eq!(normalize_luma_offset(-512, 10, true), -512);
}
#[test]
fn chroma_offset_uses_the_mode_specific_prediction_range() {
assert_eq!(derive_chroma_offset(3, 5, 2, 10, false), -116);
assert_eq!(derive_chroma_offset(3, 5, 2, 10, true), -125);
}
}
pub(crate) const SLICE_B: u8 = 0;
pub(crate) const SLICE_P: u8 = 1;
pub(crate) const SLICE_I: u8 = 2;
#[derive(Clone)]
pub(crate) struct RefFramePlanes {
pub(crate) poc: i32,
pub(crate) y: Vec<u16>,
pub(crate) cb: Vec<u16>,
pub(crate) cr: Vec<u16>,
pub(crate) w: usize,
pub(crate) h: usize,
pub(crate) cw: usize,
pub(crate) ch: usize,
pub(crate) motion: Vec<MotionInfo>,
pub(crate) width4: usize,
pub(crate) height4: usize,
}