use v_frame::{
pixel::Pixel,
plane::{Plane, PlaneGeometry},
};
use crate::data::{
frame::{FrameInvariants, RefType},
mc::put_8tap,
motion::MotionVector,
plane::{PlaneOffset, PlaneRegionMut, PlaneSlice, plane_to_plane_slice},
tile::TileRect,
};
#[expect(non_camel_case_types)]
#[expect(clippy::upper_case_acronyms)]
#[expect(dead_code)]
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Default)]
pub enum PredictionMode {
#[default]
DC_PRED, V_PRED, H_PRED, D45_PRED, D135_PRED, D113_PRED, D157_PRED, D203_PRED, D67_PRED, SMOOTH_PRED, SMOOTH_V_PRED,
SMOOTH_H_PRED,
PAETH_PRED,
UV_CFL_PRED,
NEARESTMV,
NEAR0MV,
NEAR1MV,
NEAR2MV,
GLOBALMV,
NEWMV,
NEAREST_NEARESTMV,
NEAR_NEAR0MV,
NEAR_NEAR1MV,
NEAR_NEAR2MV,
NEAREST_NEWMV,
NEW_NEARESTMV,
NEAR_NEW0MV,
NEAR_NEW1MV,
NEAR_NEW2MV,
NEW_NEAR0MV,
NEW_NEAR1MV,
NEW_NEAR2MV,
GLOBAL_GLOBALMV,
NEW_NEWMV,
}
impl PredictionMode {
pub fn is_intra(self) -> bool {
self < PredictionMode::NEARESTMV
}
#[expect(clippy::too_many_arguments)]
pub fn predict_inter_single<T: Pixel>(
self,
fi: &FrameInvariants<T>,
tile_rect: TileRect,
p: usize,
po: PlaneOffset,
dst: &mut PlaneRegionMut<'_, T>,
width: usize,
height: usize,
ref_frame: RefType,
mv: MotionVector,
bit_depth: usize,
) {
assert!(!self.is_intra());
let frame_po = tile_rect.to_frame_plane_offset(po);
if let Some(ref rec) = fi.rec_buffer.frames[fi.ref_frames[ref_frame.to_index()] as usize] {
let rec_plane = match p {
0 => &rec.frame.y_plane,
1 => rec
.frame
.u_plane
.as_ref()
.expect("called with p > 0 on a monochrome frame"),
2 => rec
.frame
.v_plane
.as_ref()
.expect("called with p > 0 on a monochrome frame"),
_ => unreachable!(),
};
let (row_frac, col_frac, src) = PredictionMode::get_mv_params(rec_plane, frame_po, mv);
put_8tap(dst, src, width, height, col_frac, row_frac, bit_depth);
}
}
fn get_mv_params<T: Pixel>(
rec_plane: &Plane<T>,
po: PlaneOffset,
mv: MotionVector,
) -> (i32, i32, PlaneSlice<'_, T>) {
let PlaneGeometry {
subsampling_x,
subsampling_y,
..
} = rec_plane.geometry();
let ss_x = subsampling_x.get() >> 1;
let ss_y = subsampling_y.get() >> 1;
let row_offset = mv.row as i32 >> (3 + ss_y);
let col_offset = mv.col as i32 >> (3 + ss_x);
let row_frac = ((mv.row as i32) << (1 - ss_y)) & 0xf;
let col_frac = ((mv.col as i32) << (1 - ss_x)) & 0xf;
let qo = PlaneOffset {
x: po.x + col_offset as isize - 3,
y: po.y + row_offset as isize - 3,
};
(
row_frac,
col_frac,
plane_to_plane_slice(rec_plane, qo).clamp().subslice(3, 3),
)
}
}
#[derive(Copy, Clone, Debug)]
#[expect(clippy::upper_case_acronyms)]
pub enum PredictionVariant {
NONE,
LEFT,
TOP,
BOTH,
}
impl PredictionVariant {
pub const fn new(x: usize, y: usize) -> Self {
match (x, y) {
(0, 0) => PredictionVariant::NONE,
(_, 0) => PredictionVariant::LEFT,
(0, _) => PredictionVariant::TOP,
_ => PredictionVariant::BOTH,
}
}
}