#![forbid(unsafe_code)]
#![allow(dead_code)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::similar_names)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_sign_loss)]
#![allow(clippy::cast_possible_wrap)]
#![allow(clippy::needless_pass_by_value)]
#![allow(clippy::unused_self)]
#![allow(clippy::trivially_copy_pass_by_ref)]
#![allow(clippy::needless_range_loop)]
#![allow(clippy::manual_memcpy)]
#![allow(clippy::if_same_then_else)]
#![allow(clippy::comparison_chain)]
#![allow(clippy::manual_rem_euclid)]
#![allow(clippy::cast_lossless)]
use super::{
AngleDelta, BitDepth, BlockDimensions, DirectionalMode, IntraPredContext, IntraPredictor,
};
#[derive(Clone, Copy, Debug)]
pub struct DirectionalPredictor {
angle: u16,
delta: AngleDelta,
bit_depth: BitDepth,
}
impl DirectionalPredictor {
#[must_use]
pub const fn new(angle: u16, bit_depth: BitDepth) -> Self {
Self {
angle,
delta: AngleDelta::Zero,
bit_depth,
}
}
#[must_use]
pub const fn with_delta(angle: u16, delta: AngleDelta, bit_depth: BitDepth) -> Self {
Self {
angle,
delta,
bit_depth,
}
}
#[must_use]
pub const fn effective_angle(&self) -> i16 {
self.angle as i16 + self.delta.degrees()
}
pub fn predict_angle(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let angle = self.effective_angle();
match angle {
90 => self.predict_vertical(ctx, output, stride, dims),
180 => self.predict_horizontal(ctx, output, stride, dims),
45 => self.predict_d45(ctx, output, stride, dims),
135 => self.predict_d135(ctx, output, stride, dims),
_ => self.predict_generic(ctx, output, stride, dims, angle),
}
}
fn predict_vertical(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let top = ctx.top_samples();
for y in 0..dims.height {
let row_start = y * stride;
for x in 0..dims.width {
output[row_start + x] = top[x];
}
}
}
fn predict_horizontal(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let left = ctx.left_samples();
for y in 0..dims.height {
let row_start = y * stride;
let left_val = left[y];
for x in 0..dims.width {
output[row_start + x] = left_val;
}
}
}
fn predict_d45(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let top = ctx.top_samples();
for y in 0..dims.height {
let row_start = y * stride;
for x in 0..dims.width {
let idx = x + y + 1;
let sample = if idx < ctx.top_samples().len() {
top[idx]
} else {
top[ctx.top_samples().len().saturating_sub(1)]
};
output[row_start + x] = sample;
}
}
}
fn predict_d135(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let top = ctx.top_samples();
let left = ctx.left_samples();
let top_left = ctx.top_left_sample();
for y in 0..dims.height {
let row_start = y * stride;
for x in 0..dims.width {
let sample = if y > x {
left[y - x - 1]
} else if y < x {
top[x - y - 1]
} else {
top_left
};
output[row_start + x] = sample;
}
}
}
fn predict_generic(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
angle: i16,
) {
let (dx, dy) = get_direction_deltas(angle);
if angle < 90 {
self.predict_from_top(ctx, output, stride, dims, dx, dy);
} else if angle < 180 {
self.predict_from_top_left(ctx, output, stride, dims, dx, dy, angle);
} else if angle < 270 {
self.predict_from_left(ctx, output, stride, dims, dx, dy);
} else {
self.predict_from_left_up(ctx, output, stride, dims, dx, dy);
}
}
fn predict_from_top(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
dx: i32,
_dy: i32,
) {
let top = ctx.top_samples();
let max_idx = top.len().saturating_sub(1);
for y in 0..dims.height {
let row_start = y * stride;
for x in 0..dims.width {
let src_x = ((x as i32) * 256 + (y as i32 + 1) * dx) / 256;
let frac = (((x as i32) * 256 + (y as i32 + 1) * dx) % 256) as u16;
let idx = src_x.clamp(0, max_idx as i32) as usize;
let idx_next = (idx + 1).min(max_idx);
let sample = interpolate(top[idx], top[idx_next], frac);
output[row_start + x] = sample;
}
}
}
fn predict_from_top_left(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
dx: i32,
dy: i32,
angle: i16,
) {
let top = ctx.top_samples();
let left = ctx.left_samples();
let top_left = ctx.top_left_sample();
for y in 0..dims.height {
let row_start = y * stride;
for x in 0..dims.width {
let sample = if angle <= 90 {
let src_x = (x as i32) + ((y as i32 + 1) * dx) / 256;
let frac = ((y as i32 + 1) * dx) % 256;
get_sample_from_neighbors(
top,
left,
top_left,
src_x,
-1, frac.unsigned_abs() as u16,
true,
)
} else {
let src_y = (y as i32) + ((x as i32 + 1) * dy) / 256;
let frac = ((x as i32 + 1) * dy) % 256;
get_sample_from_neighbors(
top,
left,
top_left,
-1, src_y,
frac.unsigned_abs() as u16,
false,
)
};
output[row_start + x] = sample;
}
}
}
fn predict_from_left(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
_dx: i32,
dy: i32,
) {
let left = ctx.left_samples();
let max_idx = left.len().saturating_sub(1);
for y in 0..dims.height {
let row_start = y * stride;
for x in 0..dims.width {
let src_y = ((y as i32) * 256 + (x as i32 + 1) * dy) / 256;
let frac = (((y as i32) * 256 + (x as i32 + 1) * dy) % 256) as u16;
let idx = src_y.clamp(0, max_idx as i32) as usize;
let idx_next = (idx + 1).min(max_idx);
let sample = interpolate(left[idx], left[idx_next], frac);
output[row_start + x] = sample;
}
}
}
fn predict_from_left_up(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
dx: i32,
_dy: i32,
) {
let left = ctx.left_samples();
let top = ctx.top_samples();
let top_left = ctx.top_left_sample();
for y in 0..dims.height {
let row_start = y * stride;
for x in 0..dims.width {
let src_idx = (y as i32) - ((x as i32 + 1) * dx.abs()) / 256;
let frac = (((x as i32 + 1) * dx.abs()) % 256) as u16;
let sample = if src_idx >= 0 {
let idx = src_idx as usize;
let idx_next = (idx + 1).min(left.len().saturating_sub(1));
interpolate(left[idx], left[idx_next], frac)
} else {
let top_idx = (-(src_idx + 1)) as usize;
if top_idx == 0 {
top_left
} else if top_idx <= top.len() {
top[top_idx - 1]
} else {
top[top.len().saturating_sub(1)]
}
};
output[row_start + x] = sample;
}
}
}
}
impl IntraPredictor for DirectionalPredictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
self.predict_angle(ctx, output, stride, dims);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct VerticalPredictor;
impl VerticalPredictor {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl IntraPredictor for VerticalPredictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let top = ctx.top_samples();
for y in 0..dims.height {
let row_start = y * stride;
for x in 0..dims.width {
output[row_start + x] = top[x];
}
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct HorizontalPredictor;
impl HorizontalPredictor {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl IntraPredictor for HorizontalPredictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let left = ctx.left_samples();
for y in 0..dims.height {
let row_start = y * stride;
let left_val = left[y];
for x in 0..dims.width {
output[row_start + x] = left_val;
}
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct D45Predictor;
impl D45Predictor {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl IntraPredictor for D45Predictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let predictor = DirectionalPredictor::new(45, BitDepth::Bits8);
predictor.predict_d45(ctx, output, stride, dims);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct D63Predictor;
impl D63Predictor {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl IntraPredictor for D63Predictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let predictor = DirectionalPredictor::new(63, BitDepth::Bits8);
predictor.predict_angle(ctx, output, stride, dims);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct D67Predictor;
impl D67Predictor {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl IntraPredictor for D67Predictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let predictor = DirectionalPredictor::new(67, BitDepth::Bits8);
predictor.predict_angle(ctx, output, stride, dims);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct D113Predictor;
impl D113Predictor {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl IntraPredictor for D113Predictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let predictor = DirectionalPredictor::new(113, BitDepth::Bits8);
predictor.predict_angle(ctx, output, stride, dims);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct D117Predictor;
impl D117Predictor {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl IntraPredictor for D117Predictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let predictor = DirectionalPredictor::new(117, BitDepth::Bits8);
predictor.predict_angle(ctx, output, stride, dims);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct D135Predictor;
impl D135Predictor {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl IntraPredictor for D135Predictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let predictor = DirectionalPredictor::new(135, BitDepth::Bits8);
predictor.predict_d135(ctx, output, stride, dims);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct D153Predictor;
impl D153Predictor {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl IntraPredictor for D153Predictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let predictor = DirectionalPredictor::new(153, BitDepth::Bits8);
predictor.predict_angle(ctx, output, stride, dims);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct D157Predictor;
impl D157Predictor {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl IntraPredictor for D157Predictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let predictor = DirectionalPredictor::new(157, BitDepth::Bits8);
predictor.predict_angle(ctx, output, stride, dims);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct D203Predictor;
impl D203Predictor {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl IntraPredictor for D203Predictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let predictor = DirectionalPredictor::new(203, BitDepth::Bits8);
predictor.predict_angle(ctx, output, stride, dims);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct D207Predictor;
impl D207Predictor {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl IntraPredictor for D207Predictor {
fn predict(
&self,
ctx: &IntraPredContext,
output: &mut [u16],
stride: usize,
dims: BlockDimensions,
) {
let predictor = DirectionalPredictor::new(207, BitDepth::Bits8);
predictor.predict_angle(ctx, output, stride, dims);
}
}
#[must_use]
fn get_direction_deltas(angle: i16) -> (i32, i32) {
let angle = ((angle % 360) + 360) % 360;
match angle {
0 => (0, 256),
45 => (181, 181),
90 => (256, 0),
135 => (181, -181),
180 => (0, -256),
225 => (-181, -181),
270 => (-256, 0),
315 => (-181, 181),
_ => {
let radians = (angle as f64) * std::f64::consts::PI / 180.0;
let dx = (radians.sin() * 256.0).round() as i32;
let dy = (radians.cos() * 256.0).round() as i32;
(dx, dy)
}
}
}
#[inline]
fn interpolate(a: u16, b: u16, frac: u16) -> u16 {
let a32 = u32::from(a);
let b32 = u32::from(b);
let frac32 = u32::from(frac);
let result = (a32 * (256 - frac32) + b32 * frac32 + 128) / 256;
result as u16
}
fn get_sample_from_neighbors(
top: &[u16],
left: &[u16],
top_left: u16,
x: i32,
y: i32,
frac: u16,
use_top: bool,
) -> u16 {
if use_top {
let idx = x.clamp(0, (top.len() - 1) as i32) as usize;
let idx_next = (idx + 1).min(top.len() - 1);
interpolate(top[idx], top[idx_next], frac)
} else {
if y < 0 {
top_left
} else {
let idx = y.clamp(0, (left.len() - 1) as i32) as usize;
let idx_next = (idx + 1).min(left.len() - 1);
interpolate(left[idx], left[idx_next], frac)
}
}
}
pub fn get_direction_samples(
ctx: &IntraPredContext,
angle: i16,
width: usize,
height: usize,
) -> Vec<u16> {
let mode = DirectionalMode::new(angle as u16);
let mut samples = Vec::with_capacity(width * height);
let top = ctx.top_samples();
let left = ctx.left_samples();
if mode.is_vertical_ish() {
for y in 0..height {
for x in 0..width {
let idx = (x + y).min(top.len() - 1);
samples.push(top[idx]);
}
}
} else {
for y in 0..height {
for x in 0..width {
let idx = (x + y).min(left.len() - 1);
samples.push(left[idx]);
}
}
}
samples
}
#[cfg(test)]
mod tests {
use super::*;
use crate::intra::context::IntraPredContext;
fn create_test_context() -> IntraPredContext {
let mut ctx = IntraPredContext::new(8, 8, BitDepth::Bits8);
for i in 0..16 {
ctx.set_top_sample(i, ((i + 1) * 10) as u16);
}
for i in 0..16 {
ctx.set_left_sample(i, (15 + i * 10) as u16);
}
ctx.set_top_left_sample(5);
ctx.set_availability(true, true);
ctx
}
#[test]
fn test_vertical_prediction() {
let ctx = create_test_context();
let predictor = VerticalPredictor::new();
let dims = BlockDimensions::new(4, 4);
let mut output = vec![0u16; 16];
predictor.predict(&ctx, &mut output, 4, dims);
assert_eq!(output[0], 10); assert_eq!(output[1], 20); assert_eq!(output[2], 30); assert_eq!(output[3], 40);
assert_eq!(output[4], 10);
assert_eq!(output[5], 20);
}
#[test]
fn test_horizontal_prediction() {
let ctx = create_test_context();
let predictor = HorizontalPredictor::new();
let dims = BlockDimensions::new(4, 4);
let mut output = vec![0u16; 16];
predictor.predict(&ctx, &mut output, 4, dims);
assert_eq!(output[0], 15);
assert_eq!(output[1], 15);
assert_eq!(output[2], 15);
assert_eq!(output[3], 15);
assert_eq!(output[4], 25);
assert_eq!(output[5], 25);
}
#[test]
fn test_d45_prediction() {
let ctx = create_test_context();
let predictor = D45Predictor::new();
let dims = BlockDimensions::new(4, 4);
let mut output = vec![0u16; 16];
predictor.predict(&ctx, &mut output, 4, dims);
assert_eq!(output[0], 20);
assert_eq!(output[1], 30);
assert_eq!(output[4], 30); }
#[test]
fn test_d135_prediction() {
let ctx = create_test_context();
let predictor = D135Predictor::new();
let dims = BlockDimensions::new(4, 4);
let mut output = vec![0u16; 16];
predictor.predict(&ctx, &mut output, 4, dims);
assert_eq!(output[0], 5);
assert_eq!(output[1], 10);
assert_eq!(output[4], 15); }
#[test]
fn test_interpolation() {
assert_eq!(interpolate(100, 200, 0), 100);
let result = interpolate(100, 200, 255);
assert!(result >= 199 && result <= 200);
let result = interpolate(100, 200, 128);
assert!(result >= 149 && result <= 151);
}
#[test]
fn test_direction_deltas() {
let (dx, dy) = get_direction_deltas(0);
assert_eq!((dx, dy), (0, 256));
let (dx, dy) = get_direction_deltas(90);
assert_eq!((dx, dy), (256, 0));
let (dx, dy) = get_direction_deltas(180);
assert_eq!((dx, dy), (0, -256));
let (dx, dy) = get_direction_deltas(45);
assert_eq!((dx, dy), (181, 181));
}
#[test]
fn test_directional_predictor() {
let ctx = create_test_context();
let predictor = DirectionalPredictor::new(90, BitDepth::Bits8);
let dims = BlockDimensions::new(4, 4);
let mut output = vec![0u16; 16];
predictor.predict(&ctx, &mut output, 4, dims);
assert_eq!(output[0], 10);
assert_eq!(output[1], 20);
}
#[test]
fn test_directional_with_delta() {
let predictor = DirectionalPredictor::with_delta(90, AngleDelta::Plus3, BitDepth::Bits8);
assert_eq!(predictor.effective_angle(), 99);
}
}