#![forbid(unsafe_code)]
#![allow(dead_code)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::similar_names)]
#![allow(clippy::fn_params_excessive_bools)]
#![allow(clippy::if_not_else)]
#![allow(clippy::bool_to_int_with_if)]
use super::mv::{MotionVector, MvPair, MvRefType, RefPair};
use super::partition::BlockSize;
pub const INTER_MODES: usize = 4;
pub const COMPOUND_MODES: usize = 8;
pub const REF_FRAMES: usize = 4;
pub const INTER_REFS: usize = 3;
pub const MAX_REF_MV_CANDIDATES: usize = 8;
pub const MAX_MV_REF_CANDIDATES: usize = 2;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Hash)]
#[repr(u8)]
pub enum InterMode {
#[default]
NearestMv = 0,
NearMv = 1,
ZeroMv = 2,
NewMv = 3,
}
impl InterMode {
pub const ALL: [InterMode; INTER_MODES] = [
InterMode::NearestMv,
InterMode::NearMv,
InterMode::ZeroMv,
InterMode::NewMv,
];
#[must_use]
pub const fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(Self::NearestMv),
1 => Some(Self::NearMv),
2 => Some(Self::ZeroMv),
3 => Some(Self::NewMv),
_ => None,
}
}
#[must_use]
pub const fn requires_mv_delta(&self) -> bool {
matches!(self, Self::NewMv)
}
#[must_use]
pub const fn is_zero(&self) -> bool {
matches!(self, Self::ZeroMv)
}
#[must_use]
pub const fn uses_ref_mv(&self) -> bool {
matches!(self, Self::NearestMv | Self::NearMv)
}
#[must_use]
pub const fn index(&self) -> usize {
*self as usize
}
}
impl From<InterMode> for u8 {
fn from(value: InterMode) -> Self {
value as u8
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Hash)]
#[repr(u8)]
pub enum CompoundMode {
#[default]
NearestNearest = 0,
NearestNear = 1,
NearNearest = 2,
NearNear = 3,
NearestNew = 4,
NewNearest = 5,
NearNew = 6,
NewNear = 7,
}
impl CompoundMode {
pub const ALL: [CompoundMode; COMPOUND_MODES] = [
CompoundMode::NearestNearest,
CompoundMode::NearestNear,
CompoundMode::NearNearest,
CompoundMode::NearNear,
CompoundMode::NearestNew,
CompoundMode::NewNearest,
CompoundMode::NearNew,
CompoundMode::NewNear,
];
#[must_use]
pub const fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(Self::NearestNearest),
1 => Some(Self::NearestNear),
2 => Some(Self::NearNearest),
3 => Some(Self::NearNear),
4 => Some(Self::NearestNew),
5 => Some(Self::NewNearest),
6 => Some(Self::NearNew),
7 => Some(Self::NewNear),
_ => None,
}
}
#[must_use]
pub const fn first_requires_new_mv(&self) -> bool {
matches!(self, Self::NewNearest | Self::NewNear)
}
#[must_use]
pub const fn second_requires_new_mv(&self) -> bool {
matches!(self, Self::NearestNew | Self::NearNew)
}
#[must_use]
pub const fn requires_new_mv(&self) -> bool {
self.first_requires_new_mv() || self.second_requires_new_mv()
}
#[must_use]
pub const fn first_mode(&self) -> InterMode {
match self {
Self::NearestNearest | Self::NearestNear | Self::NearestNew => InterMode::NearestMv,
Self::NearNearest | Self::NearNear | Self::NearNew => InterMode::NearMv,
Self::NewNearest | Self::NewNear => InterMode::NewMv,
}
}
#[must_use]
pub const fn second_mode(&self) -> InterMode {
match self {
Self::NearestNearest | Self::NearNearest | Self::NewNearest => InterMode::NearestMv,
Self::NearestNear | Self::NearNear | Self::NewNear => InterMode::NearMv,
Self::NearestNew | Self::NearNew => InterMode::NewMv,
}
}
#[must_use]
pub const fn index(&self) -> usize {
*self as usize
}
}
impl From<CompoundMode> for u8 {
fn from(value: CompoundMode) -> Self {
value as u8
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Hash)]
#[repr(u8)]
pub enum RefFrameType {
#[default]
Intra = 0,
Last = 1,
Golden = 2,
AltRef = 3,
}
impl RefFrameType {
pub const ALL: [RefFrameType; REF_FRAMES] = [
RefFrameType::Intra,
RefFrameType::Last,
RefFrameType::Golden,
RefFrameType::AltRef,
];
pub const INTER_REFS: [RefFrameType; INTER_REFS] = [
RefFrameType::Last,
RefFrameType::Golden,
RefFrameType::AltRef,
];
#[must_use]
pub const fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(Self::Intra),
1 => Some(Self::Last),
2 => Some(Self::Golden),
3 => Some(Self::AltRef),
_ => None,
}
}
#[must_use]
pub const fn is_inter(&self) -> bool {
!matches!(self, Self::Intra)
}
#[must_use]
pub const fn is_intra(&self) -> bool {
matches!(self, Self::Intra)
}
#[must_use]
pub const fn index(&self) -> usize {
*self as usize
}
#[must_use]
pub const fn inter_index(&self) -> Option<usize> {
match self {
Self::Intra => None,
Self::Last => Some(0),
Self::Golden => Some(1),
Self::AltRef => Some(2),
}
}
#[must_use]
pub const fn from_mv_ref_type(mv_ref: MvRefType) -> Self {
match mv_ref {
MvRefType::Intra => Self::Intra,
MvRefType::Last => Self::Last,
MvRefType::Golden => Self::Golden,
MvRefType::AltRef => Self::AltRef,
}
}
#[must_use]
pub const fn to_mv_ref_type(&self) -> MvRefType {
match self {
Self::Intra => MvRefType::Intra,
Self::Last => MvRefType::Last,
Self::Golden => MvRefType::Golden,
Self::AltRef => MvRefType::AltRef,
}
}
}
impl From<RefFrameType> for u8 {
fn from(value: RefFrameType) -> Self {
value as u8
}
}
impl From<MvRefType> for RefFrameType {
fn from(value: MvRefType) -> Self {
Self::from_mv_ref_type(value)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum PredictionMode {
#[default]
Intra,
SingleRef {
mode: InterMode,
ref_frame: RefFrameType,
mv: MotionVector,
},
Compound {
mode: CompoundMode,
ref_frames: RefPair,
mvs: MvPair,
},
}
impl PredictionMode {
#[must_use]
pub const fn single(mode: InterMode, ref_frame: RefFrameType, mv: MotionVector) -> Self {
Self::SingleRef {
mode,
ref_frame,
mv,
}
}
#[must_use]
pub const fn compound(mode: CompoundMode, ref_frames: RefPair, mvs: MvPair) -> Self {
Self::Compound {
mode,
ref_frames,
mvs,
}
}
#[must_use]
pub const fn is_intra(&self) -> bool {
matches!(self, Self::Intra)
}
#[must_use]
pub const fn is_single_ref(&self) -> bool {
matches!(self, Self::SingleRef { .. })
}
#[must_use]
pub const fn is_compound(&self) -> bool {
matches!(self, Self::Compound { .. })
}
#[must_use]
pub const fn is_inter(&self) -> bool {
!self.is_intra()
}
#[must_use]
pub const fn mv0(&self) -> Option<MotionVector> {
match self {
Self::Intra => None,
Self::SingleRef { mv, .. } => Some(*mv),
Self::Compound { mvs, .. } => Some(mvs.mv0),
}
}
#[must_use]
pub const fn mv1(&self) -> Option<MotionVector> {
match self {
Self::Intra | Self::SingleRef { .. } => None,
Self::Compound { mvs, .. } => Some(mvs.mv1),
}
}
#[must_use]
pub const fn ref0(&self) -> Option<RefFrameType> {
match self {
Self::Intra => None,
Self::SingleRef { ref_frame, .. } => Some(*ref_frame),
Self::Compound { ref_frames, .. } => {
Some(RefFrameType::from_mv_ref_type(ref_frames.ref0))
}
}
}
#[must_use]
pub const fn ref1(&self) -> Option<RefFrameType> {
match self {
Self::Intra | Self::SingleRef { .. } => None,
Self::Compound { ref_frames, .. } => {
Some(RefFrameType::from_mv_ref_type(ref_frames.ref1))
}
}
}
}
#[derive(Clone, Debug, Default)]
pub struct InterPredContext {
pub block_size: BlockSize,
pub mi_row: usize,
pub mi_col: usize,
pub mode: PredictionMode,
pub ref_sign_bias: [bool; REF_FRAMES],
pub is_compound: bool,
pub ref_mv_candidates_0: [MotionVector; MAX_MV_REF_CANDIDATES],
pub ref_mv_candidates_1: [MotionVector; MAX_MV_REF_CANDIDATES],
pub ref_mv_count_0: usize,
pub ref_mv_count_1: usize,
}
impl InterPredContext {
#[must_use]
pub const fn new(block_size: BlockSize, mi_row: usize, mi_col: usize) -> Self {
Self {
block_size,
mi_row,
mi_col,
mode: PredictionMode::Intra,
ref_sign_bias: [false; REF_FRAMES],
is_compound: false,
ref_mv_candidates_0: [MotionVector::zero(); MAX_MV_REF_CANDIDATES],
ref_mv_candidates_1: [MotionVector::zero(); MAX_MV_REF_CANDIDATES],
ref_mv_count_0: 0,
ref_mv_count_1: 0,
}
}
pub fn set_mode(&mut self, mode: PredictionMode) {
self.mode = mode;
self.is_compound = mode.is_compound();
}
pub fn set_sign_bias(&mut self, ref_type: RefFrameType, bias: bool) {
self.ref_sign_bias[ref_type.index()] = bias;
}
#[must_use]
pub const fn sign_bias(&self, ref_type: RefFrameType) -> bool {
self.ref_sign_bias[ref_type.index()]
}
pub fn add_mv_candidate_0(&mut self, mv: MotionVector) {
if self.ref_mv_count_0 < MAX_MV_REF_CANDIDATES {
self.ref_mv_candidates_0[self.ref_mv_count_0] = mv;
self.ref_mv_count_0 += 1;
}
}
pub fn add_mv_candidate_1(&mut self, mv: MotionVector) {
if self.ref_mv_count_1 < MAX_MV_REF_CANDIDATES {
self.ref_mv_candidates_1[self.ref_mv_count_1] = mv;
self.ref_mv_count_1 += 1;
}
}
#[must_use]
pub const fn nearest_mv_0(&self) -> MotionVector {
if self.ref_mv_count_0 > 0 {
self.ref_mv_candidates_0[0]
} else {
MotionVector::zero()
}
}
#[must_use]
pub const fn near_mv_0(&self) -> MotionVector {
if self.ref_mv_count_0 > 1 {
self.ref_mv_candidates_0[1]
} else {
MotionVector::zero()
}
}
#[must_use]
pub const fn nearest_mv_1(&self) -> MotionVector {
if self.ref_mv_count_1 > 0 {
self.ref_mv_candidates_1[0]
} else {
MotionVector::zero()
}
}
#[must_use]
pub const fn near_mv_1(&self) -> MotionVector {
if self.ref_mv_count_1 > 1 {
self.ref_mv_candidates_1[1]
} else {
MotionVector::zero()
}
}
pub fn clear_candidates(&mut self) {
self.ref_mv_count_0 = 0;
self.ref_mv_count_1 = 0;
self.ref_mv_candidates_0 = [MotionVector::zero(); MAX_MV_REF_CANDIDATES];
self.ref_mv_candidates_1 = [MotionVector::zero(); MAX_MV_REF_CANDIDATES];
}
#[must_use]
pub const fn pixel_x(&self) -> usize {
self.mi_col * 4
}
#[must_use]
pub const fn pixel_y(&self) -> usize {
self.mi_row * 4
}
#[must_use]
pub const fn width(&self) -> usize {
self.block_size.width()
}
#[must_use]
pub const fn height(&self) -> usize {
self.block_size.height()
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct InterModeContext {
pub mode_context: u8,
pub new_mv_context: u8,
pub zero_mv_context: u8,
pub ref_mv_context: u8,
}
impl InterModeContext {
#[must_use]
pub const fn new() -> Self {
Self {
mode_context: 0,
new_mv_context: 0,
zero_mv_context: 0,
ref_mv_context: 0,
}
}
#[must_use]
pub fn from_neighbors(
same_ref_count: u8,
diff_ref_count: u8,
new_mv_count: u8,
zero_mv_count: u8,
) -> Self {
let mode_context = match (same_ref_count, diff_ref_count) {
(0, 0) => 0,
(1, 0) | (0, 1) => 1,
(1, 1) => 2,
(2, 0) | (0, 2) => 3,
(2, 1) | (1, 2) => 4,
_ => 5,
};
Self {
mode_context,
new_mv_context: new_mv_count.min(2),
zero_mv_context: zero_mv_count.min(2),
ref_mv_context: same_ref_count.min(2),
}
}
#[must_use]
pub const fn mode_index(&self) -> usize {
self.mode_context as usize
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct RefFrameContext {
pub comp_mode_context: u8,
pub single_ref_context_0: u8,
pub single_ref_context_1: u8,
pub comp_ref_context: u8,
}
impl RefFrameContext {
#[must_use]
pub const fn new() -> Self {
Self {
comp_mode_context: 0,
single_ref_context_0: 0,
single_ref_context_1: 0,
comp_ref_context: 0,
}
}
#[must_use]
#[allow(clippy::too_many_arguments)]
pub fn from_neighbors(
above_ref: Option<RefFrameType>,
left_ref: Option<RefFrameType>,
above_compound: bool,
left_compound: bool,
above_intra: bool,
left_intra: bool,
has_above: bool,
has_left: bool,
) -> Self {
let mut ctx = Self::new();
if has_above && has_left {
if above_compound && left_compound {
ctx.comp_mode_context = 4;
} else if above_compound || left_compound {
ctx.comp_mode_context = 2;
} else if above_intra || left_intra {
ctx.comp_mode_context = 1;
} else {
ctx.comp_mode_context = 0;
}
} else if has_above {
ctx.comp_mode_context = if above_compound { 3 } else { 0 };
} else if has_left {
ctx.comp_mode_context = if left_compound { 3 } else { 0 };
}
let above_is_last = matches!(above_ref, Some(RefFrameType::Last));
let left_is_last = matches!(left_ref, Some(RefFrameType::Last));
let above_is_golden = matches!(above_ref, Some(RefFrameType::Golden));
let left_is_golden = matches!(left_ref, Some(RefFrameType::Golden));
if has_above && has_left {
if above_intra && left_intra {
ctx.single_ref_context_0 = 2;
ctx.single_ref_context_1 = 2;
} else if above_intra || left_intra {
let ref_frame = if above_intra { left_ref } else { above_ref };
let is_last = matches!(ref_frame, Some(RefFrameType::Last));
let is_golden = matches!(ref_frame, Some(RefFrameType::Golden));
ctx.single_ref_context_0 = if is_last { 2 } else { 3 };
ctx.single_ref_context_1 = if is_golden { 2 } else { 3 };
} else {
ctx.single_ref_context_0 = if above_is_last && left_is_last {
3
} else if above_is_last || left_is_last {
1
} else {
0
};
ctx.single_ref_context_1 = if above_is_golden && left_is_golden {
3
} else if above_is_golden || left_is_golden {
1
} else {
0
};
}
} else if has_above {
ctx.single_ref_context_0 = if above_intra {
2
} else if above_is_last {
3
} else {
0
};
ctx.single_ref_context_1 = if above_intra {
2
} else if above_is_golden {
3
} else {
0
};
} else if has_left {
ctx.single_ref_context_0 = if left_intra {
2
} else if left_is_last {
3
} else {
0
};
ctx.single_ref_context_1 = if left_intra {
2
} else if left_is_golden {
3
} else {
0
};
} else {
ctx.single_ref_context_0 = 2;
ctx.single_ref_context_1 = 2;
}
ctx
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ScalingFactors {
pub x_scale: i32,
pub y_scale: i32,
pub is_scaled: bool,
}
impl ScalingFactors {
pub const SCALE_ONE: i32 = 1 << 14;
#[must_use]
pub const fn identity() -> Self {
Self {
x_scale: Self::SCALE_ONE,
y_scale: Self::SCALE_ONE,
is_scaled: false,
}
}
#[must_use]
pub fn from_dimensions(
src_width: u32,
src_height: u32,
dst_width: u32,
dst_height: u32,
) -> Self {
let x_scale = ((i64::from(src_width) << 14) / i64::from(dst_width)) as i32;
let y_scale = ((i64::from(src_height) << 14) / i64::from(dst_height)) as i32;
let is_scaled = x_scale != Self::SCALE_ONE || y_scale != Self::SCALE_ONE;
Self {
x_scale,
y_scale,
is_scaled,
}
}
#[must_use]
pub const fn scale_x(&self, x: i32) -> i32 {
if self.is_scaled {
(x * self.x_scale) >> 14
} else {
x
}
}
#[must_use]
pub const fn scale_y(&self, y: i32) -> i32 {
if self.is_scaled {
(y * self.y_scale) >> 14
} else {
y
}
}
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub fn scale_mv(&self, mv: MotionVector) -> MotionVector {
if self.is_scaled {
MotionVector::new(
((i32::from(mv.row) * self.y_scale) >> 14) as i16,
((i32::from(mv.col) * self.x_scale) >> 14) as i16,
)
} else {
mv
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_inter_mode() {
assert_eq!(InterMode::NearestMv.index(), 0);
assert_eq!(InterMode::NearMv.index(), 1);
assert_eq!(InterMode::ZeroMv.index(), 2);
assert_eq!(InterMode::NewMv.index(), 3);
assert!(InterMode::NewMv.requires_mv_delta());
assert!(!InterMode::ZeroMv.requires_mv_delta());
assert!(InterMode::ZeroMv.is_zero());
assert!(!InterMode::NearestMv.is_zero());
assert!(InterMode::NearestMv.uses_ref_mv());
assert!(InterMode::NearMv.uses_ref_mv());
assert!(!InterMode::NewMv.uses_ref_mv());
}
#[test]
fn test_inter_mode_from_u8() {
assert_eq!(InterMode::from_u8(0), Some(InterMode::NearestMv));
assert_eq!(InterMode::from_u8(3), Some(InterMode::NewMv));
assert_eq!(InterMode::from_u8(4), None);
}
#[test]
fn test_compound_mode() {
assert!(CompoundMode::NewNearest.first_requires_new_mv());
assert!(!CompoundMode::NewNearest.second_requires_new_mv());
assert!(CompoundMode::NearestNew.second_requires_new_mv());
assert!(!CompoundMode::NearestNew.first_requires_new_mv());
assert_eq!(
CompoundMode::NearestNearest.first_mode(),
InterMode::NearestMv
);
assert_eq!(
CompoundMode::NearestNearest.second_mode(),
InterMode::NearestMv
);
assert_eq!(CompoundMode::NewNear.first_mode(), InterMode::NewMv);
assert_eq!(CompoundMode::NewNear.second_mode(), InterMode::NearMv);
}
#[test]
fn test_compound_mode_from_u8() {
assert_eq!(CompoundMode::from_u8(0), Some(CompoundMode::NearestNearest));
assert_eq!(CompoundMode::from_u8(7), Some(CompoundMode::NewNear));
assert_eq!(CompoundMode::from_u8(8), None);
}
#[test]
fn test_ref_frame_type() {
assert!(RefFrameType::Intra.is_intra());
assert!(!RefFrameType::Intra.is_inter());
assert!(RefFrameType::Last.is_inter());
assert!(RefFrameType::Golden.is_inter());
assert!(RefFrameType::AltRef.is_inter());
assert_eq!(RefFrameType::Intra.index(), 0);
assert_eq!(RefFrameType::Last.index(), 1);
assert_eq!(RefFrameType::Golden.index(), 2);
assert_eq!(RefFrameType::AltRef.index(), 3);
assert_eq!(RefFrameType::Intra.inter_index(), None);
assert_eq!(RefFrameType::Last.inter_index(), Some(0));
assert_eq!(RefFrameType::Golden.inter_index(), Some(1));
assert_eq!(RefFrameType::AltRef.inter_index(), Some(2));
}
#[test]
fn test_ref_frame_type_conversion() {
assert_eq!(
RefFrameType::from_mv_ref_type(MvRefType::Last),
RefFrameType::Last
);
assert_eq!(RefFrameType::Last.to_mv_ref_type(), MvRefType::Last);
let ref_type: RefFrameType = MvRefType::Golden.into();
assert_eq!(ref_type, RefFrameType::Golden);
}
#[test]
fn test_prediction_mode_intra() {
let mode = PredictionMode::Intra;
assert!(mode.is_intra());
assert!(!mode.is_inter());
assert!(!mode.is_single_ref());
assert!(!mode.is_compound());
assert!(mode.mv0().is_none());
assert!(mode.ref0().is_none());
}
#[test]
fn test_prediction_mode_single_ref() {
let mv = MotionVector::new(10, 20);
let mode = PredictionMode::single(InterMode::NearestMv, RefFrameType::Last, mv);
assert!(!mode.is_intra());
assert!(mode.is_inter());
assert!(mode.is_single_ref());
assert!(!mode.is_compound());
assert_eq!(mode.mv0(), Some(mv));
assert!(mode.mv1().is_none());
assert_eq!(mode.ref0(), Some(RefFrameType::Last));
assert!(mode.ref1().is_none());
}
#[test]
fn test_prediction_mode_compound() {
let mvs = MvPair::new(MotionVector::new(10, 20), MotionVector::new(30, 40));
let ref_frames = RefPair::compound(MvRefType::Last, MvRefType::Golden);
let mode = PredictionMode::compound(CompoundMode::NearestNearest, ref_frames, mvs);
assert!(!mode.is_intra());
assert!(mode.is_inter());
assert!(!mode.is_single_ref());
assert!(mode.is_compound());
assert_eq!(mode.mv0(), Some(mvs.mv0));
assert_eq!(mode.mv1(), Some(mvs.mv1));
assert_eq!(mode.ref0(), Some(RefFrameType::Last));
assert_eq!(mode.ref1(), Some(RefFrameType::Golden));
}
#[test]
fn test_inter_pred_context() {
let mut ctx = InterPredContext::new(BlockSize::Block16x16, 4, 8);
assert_eq!(ctx.block_size, BlockSize::Block16x16);
assert_eq!(ctx.mi_row, 4);
assert_eq!(ctx.mi_col, 8);
assert_eq!(ctx.pixel_x(), 32);
assert_eq!(ctx.pixel_y(), 16);
assert_eq!(ctx.width(), 16);
assert_eq!(ctx.height(), 16);
ctx.add_mv_candidate_0(MotionVector::new(10, 20));
ctx.add_mv_candidate_0(MotionVector::new(30, 40));
assert_eq!(ctx.ref_mv_count_0, 2);
assert_eq!(ctx.nearest_mv_0(), MotionVector::new(10, 20));
assert_eq!(ctx.near_mv_0(), MotionVector::new(30, 40));
ctx.clear_candidates();
assert_eq!(ctx.ref_mv_count_0, 0);
assert_eq!(ctx.nearest_mv_0(), MotionVector::zero());
}
#[test]
fn test_inter_pred_context_sign_bias() {
let mut ctx = InterPredContext::new(BlockSize::Block8x8, 0, 0);
ctx.set_sign_bias(RefFrameType::Golden, true);
ctx.set_sign_bias(RefFrameType::AltRef, true);
assert!(!ctx.sign_bias(RefFrameType::Intra));
assert!(!ctx.sign_bias(RefFrameType::Last));
assert!(ctx.sign_bias(RefFrameType::Golden));
assert!(ctx.sign_bias(RefFrameType::AltRef));
}
#[test]
fn test_inter_mode_context() {
let ctx = InterModeContext::from_neighbors(2, 0, 1, 0);
assert_eq!(ctx.mode_context, 3);
assert_eq!(ctx.new_mv_context, 1);
assert_eq!(ctx.zero_mv_context, 0);
assert_eq!(ctx.ref_mv_context, 2);
}
#[test]
fn test_ref_frame_context() {
let ctx = RefFrameContext::from_neighbors(
Some(RefFrameType::Last),
Some(RefFrameType::Last),
false,
false,
false,
false,
true,
true,
);
assert_eq!(ctx.comp_mode_context, 0);
assert_eq!(ctx.single_ref_context_0, 3);
}
#[test]
fn test_scaling_factors_identity() {
let sf = ScalingFactors::identity();
assert!(!sf.is_scaled);
assert_eq!(sf.x_scale, ScalingFactors::SCALE_ONE);
assert_eq!(sf.y_scale, ScalingFactors::SCALE_ONE);
}
#[test]
fn test_scaling_factors_scaled() {
let sf = ScalingFactors::from_dimensions(1920, 1080, 960, 540);
assert!(sf.is_scaled);
assert_eq!(sf.scale_x(100), 200);
assert_eq!(sf.scale_y(100), 200);
}
#[test]
fn test_scaling_factors_mv() {
let sf = ScalingFactors::from_dimensions(1920, 1080, 960, 540);
let mv = MotionVector::new(10, 20);
let scaled = sf.scale_mv(mv);
assert_eq!(scaled.row, 20);
assert_eq!(scaled.col, 40);
}
#[test]
fn test_scaling_factors_no_scale() {
let sf = ScalingFactors::from_dimensions(1920, 1080, 1920, 1080);
assert!(!sf.is_scaled);
}
}