use super::motion_estimation::MotionVector;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BListUse {
L0,
L1,
Bi,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BPartitionShape {
H,
V,
}
impl BPartitionShape {
pub fn part_offset(self, idx: usize) -> (usize, usize) {
debug_assert!(idx < 2);
match (self, idx) {
(BPartitionShape::H, 0) => (0, 0), (BPartitionShape::H, 1) => (0, 2), (BPartitionShape::V, 0) => (0, 0), (BPartitionShape::V, 1) => (2, 0), _ => unreachable!(),
}
}
pub fn part_dim_4x4(self) -> (usize, usize) {
match self {
BPartitionShape::H => (4, 2), BPartitionShape::V => (2, 4), }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BPartitionedMeta {
pub shape: BPartitionShape,
pub part0: BListUse,
pub part1: BListUse,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct BPartitionMv {
pub mv_l0: Option<MotionVector>,
pub mv_l1: Option<MotionVector>,
}
pub const fn partitioned_b_meta(mb_type: u32) -> Option<BPartitionedMeta> {
use BListUse::*;
use BPartitionShape::*;
let (shape, p0, p1) = match mb_type {
4 => (H, L0, L0),
5 => (V, L0, L0),
6 => (H, L1, L1),
7 => (V, L1, L1),
8 => (H, L0, L1),
9 => (V, L0, L1),
10 => (H, L1, L0),
11 => (V, L1, L0),
12 => (H, L0, Bi),
13 => (V, L0, Bi),
14 => (H, L1, Bi),
15 => (V, L1, Bi),
16 => (H, Bi, L0),
17 => (V, Bi, L0),
18 => (H, Bi, L1),
19 => (V, Bi, L1),
20 => (H, Bi, Bi),
21 => (V, Bi, Bi),
_ => return None,
};
Some(BPartitionedMeta { shape, part0: p0, part1: p1 })
}
pub const fn mb_type_from_partitioned(meta: BPartitionedMeta) -> u32 {
use BListUse::*;
use BPartitionShape::*;
let combo = match (meta.part0, meta.part1) {
(L0, L0) => 0,
(L1, L1) => 1,
(L0, L1) => 2,
(L1, L0) => 3,
(L0, Bi) => 4,
(L1, Bi) => 5,
(Bi, L0) => 6,
(Bi, L1) => 7,
(Bi, Bi) => 8,
};
let shape_offset = match meta.shape {
H => 0,
V => 1,
};
4 + 2 * combo + shape_offset
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn meta_round_trip_4_to_21() {
for mb_type in 4..=21u32 {
let meta = partitioned_b_meta(mb_type)
.unwrap_or_else(|| panic!("mb_type {mb_type} should have meta"));
let mb_type_back = mb_type_from_partitioned(meta);
assert_eq!(mb_type, mb_type_back,
"round-trip mismatch at mb_type {mb_type}: meta={meta:?} → back={mb_type_back}");
}
}
#[test]
fn meta_returns_none_for_non_partitioned() {
assert!(partitioned_b_meta(0).is_none());
assert!(partitioned_b_meta(1).is_none());
assert!(partitioned_b_meta(2).is_none());
assert!(partitioned_b_meta(3).is_none());
assert!(partitioned_b_meta(22).is_none());
assert!(partitioned_b_meta(23).is_none());
assert!(partitioned_b_meta(99).is_none());
}
#[test]
fn mb_type_4_is_h_l0_l0_per_spec() {
let m = partitioned_b_meta(4).unwrap();
assert_eq!(m.shape, BPartitionShape::H);
assert_eq!(m.part0, BListUse::L0);
assert_eq!(m.part1, BListUse::L0);
}
#[test]
fn mb_type_21_is_v_bi_bi_per_spec() {
let m = partitioned_b_meta(21).unwrap();
assert_eq!(m.shape, BPartitionShape::V);
assert_eq!(m.part0, BListUse::Bi);
assert_eq!(m.part1, BListUse::Bi);
}
#[test]
fn h_partition_offsets_match_spec() {
assert_eq!(BPartitionShape::H.part_offset(0), (0, 0));
assert_eq!(BPartitionShape::H.part_offset(1), (0, 2));
assert_eq!(BPartitionShape::H.part_dim_4x4(), (4, 2));
}
#[test]
fn v_partition_offsets_match_spec() {
assert_eq!(BPartitionShape::V.part_offset(0), (0, 0));
assert_eq!(BPartitionShape::V.part_offset(1), (2, 0));
assert_eq!(BPartitionShape::V.part_dim_4x4(), (2, 4));
}
#[test]
fn all_18_partitioned_mb_types_have_unique_meta() {
let mut seen: Vec<BPartitionedMeta> = Vec::new();
for mb_type in 4..=21u32 {
let meta = partitioned_b_meta(mb_type).unwrap();
for prior in &seen {
assert_ne!(*prior, meta,
"mb_type {mb_type} duplicates a prior meta: {meta:?}");
}
seen.push(meta);
}
assert_eq!(seen.len(), 18);
}
}