#[cfg(test)]
mod tests;
use std::{
mem::size_of,
num::{NonZeroU8, NonZeroUsize},
};
use anyhow::{Result, anyhow, bail, ensure};
use semisafe::slice::get as semisafe_get;
use semisafe::slice::get_mut as semisafe_get_mut;
use crate::{
analysis::MVAnalysisData,
copy::{CopyFn, select_copy},
fake::group_of_planes::FakeGroupOfPlanes,
frame::{FramePlanesMut, FrameView, PlaneSizeTuple},
mv_gof::BorrowedSuperFrame,
mv_plane::MVPlane,
overlaps::{OverlapWindows, OverlapsFn, ToPixelsFn, select_overlaps, select_to_pixels},
params::{MVPlaneSet, SceneChangeBehavior, Subpel},
util::{Pixel, vs_bitblt},
video::{ColorFamily, SampleType, VideoInfo},
};
use super::analyse::SuperClipInfo;
#[derive(Debug, Clone, Copy)]
pub struct CompensateOptions {
pub scene_change_behavior: SceneChangeBehavior,
pub thsad: u64,
pub fields: bool,
pub time_percent: f32,
pub thscd1: u64,
pub thscd2: u64,
}
struct CompensateFunctions {
overlaps: OverlapsFn,
blit: CopyFn,
overlaps_uv: OverlapsFn,
blit_uv: CopyFn,
to_pixels: ToPixelsFn,
}
pub struct Compensate {
info: VideoInfo,
super_info: SuperClipInfo,
vectors_data: MVAnalysisData,
scene_change_behavior: SceneChangeBehavior,
thsad: u64,
fields: bool,
thscd1: u64,
thscd2: u64,
time256: i32,
dest_temp_stride_bytes: NonZeroUsize,
dest_temp_stride_bytes_uv: NonZeroUsize,
over_wins: Option<OverlapWindows>,
over_wins_uv: Option<OverlapWindows>,
functions: CompensateFunctions,
}
impl Compensate {
#[inline]
pub fn new(
info: VideoInfo,
actual_super_width: usize,
super_info: SuperClipInfo,
vectors_data: MVAnalysisData,
options: CompensateOptions,
) -> Result<Self> {
ensure!(
(0.0..=100.0).contains(&options.time_percent),
"Compensate: time must be between 0.0 and 100.0 (inclusive)."
);
validate_standard_input(info, "Compensate")?;
validate_super_geometry(
info,
actual_super_width,
super_info,
vectors_data,
"Compensate",
)?;
let mut thscd1 = options.thscd1;
let mut thscd2 = options.thscd2;
let thscd1_before_scale = thscd1;
vectors_data.scale_thscd(&mut thscd1, &mut thscd2, "Compensate")?;
ensure!(
!options.fields || vectors_data.pel != Subpel::Full,
"Compensate: fields option requires pel > 1."
);
let thsad = options.thsad * thscd1 / thscd1_before_scale;
let bytes_per_sample = info.format.bytes_per_sample.get() as usize;
let dest_temp_stride_bytes =
NonZeroUsize::new(((vectors_data.width.get() + 15) / 16) * 16 * bytes_per_sample * 2)
.expect("cannot be zero");
let dest_temp_stride_bytes_uv = NonZeroUsize::new(
(((vectors_data.width.get() / vectors_data.x_ratio_uv.get() as usize) + 15) / 16)
* 16
* bytes_per_sample
* 2,
)
.expect("cannot be zero");
let blk_size_uv_x = unsafe {
NonZeroUsize::new_unchecked(
vectors_data.blk_size_x.get() / vectors_data.x_ratio_uv.get() as usize,
)
};
let blk_size_uv_y = unsafe {
NonZeroUsize::new_unchecked(
vectors_data.blk_size_y.get() / vectors_data.y_ratio_uv.get() as usize,
)
};
let (over_wins, over_wins_uv) = if vectors_data.overlap_x > 0 || vectors_data.overlap_y > 0
{
let over_wins = Some(OverlapWindows::new(
vectors_data.blk_size_x,
vectors_data.blk_size_y,
vectors_data.overlap_x,
vectors_data.overlap_y,
));
let over_wins_uv = super_info.mode_yuv.contains(MVPlaneSet::UVPLANES).then(|| {
OverlapWindows::new(
blk_size_uv_x,
blk_size_uv_y,
vectors_data.overlap_x / vectors_data.x_ratio_uv.get() as usize,
vectors_data.overlap_y / vectors_data.y_ratio_uv.get() as usize,
)
});
(over_wins, over_wins_uv)
} else {
(None, None)
};
let functions = Self::select_functions(
vectors_data.blk_size_x,
vectors_data.blk_size_y,
blk_size_uv_x,
blk_size_uv_y,
vectors_data.bits_per_sample,
);
Ok(Self {
info,
super_info,
vectors_data,
scene_change_behavior: options.scene_change_behavior,
thsad,
fields: options.fields,
thscd1,
thscd2,
time256: (options.time_percent * 256.0 / 100.0) as i32,
dest_temp_stride_bytes,
dest_temp_stride_bytes_uv,
over_wins,
over_wins_uv,
functions,
})
}
#[inline]
#[must_use]
pub fn field_shift(
&self,
frame_offset: isize,
current_top_field: bool,
reference_top_field: bool,
) -> i32 {
if self.fields && self.vectors_data.pel > Subpel::Full && frame_offset % 2 != 0 {
if current_top_field && !reference_top_field {
i32::from(u8::from(self.vectors_data.pel)) / 2
} else if reference_top_field && !current_top_field {
-(i32::from(u8::from(self.vectors_data.pel)) / 2)
} else {
0
}
} else {
0
}
}
#[inline]
pub fn render_frame<T: Pixel>(
&self,
current_super: &FrameView<'_, T>,
reference_super: Option<&FrameView<'_, T>>,
output: &mut FramePlanesMut<'_, T>,
output_pitch: PlaneSizeTuple,
vectors: &FakeGroupOfPlanes,
field_shift: i32,
) -> Result<()> {
let output_plane_count = self.info.format.plane_count();
let processed_plane_count = if self.super_info.mode_yuv.contains(MVPlaneSet::UVPLANES) {
self.info.format.plane_count()
} else {
1
};
if !vectors.is_usable(self.thscd1, self.thscd2) {
let fallback = match self.scene_change_behavior {
SceneChangeBehavior::ReferenceFrame => reference_super.unwrap_or(current_super),
SceneChangeBehavior::CurrentFrame => current_super,
};
return self.copy_visible_from_super(
fallback,
output,
output_pitch,
0,
output_plane_count,
);
}
let reference_super = reference_super
.ok_or_else(|| anyhow!("Compensate: reference super frame is required"))?;
let current_super = BorrowedSuperFrame::new(
current_super,
self.super_info.levels,
self.vectors_data.width,
self.vectors_data.height,
self.super_info.pel,
self.super_info.hpad,
self.super_info.vpad,
self.super_info.mode_yuv,
self.vectors_data.x_ratio_uv,
self.vectors_data.y_ratio_uv,
self.vectors_data.bits_per_sample,
)?;
let reference_super = BorrowedSuperFrame::new(
reference_super,
self.super_info.levels,
self.vectors_data.width,
self.vectors_data.height,
self.super_info.pel,
self.super_info.hpad,
self.super_info.vpad,
self.super_info.mode_yuv,
self.vectors_data.x_ratio_uv,
self.vectors_data.y_ratio_uv,
self.vectors_data.bits_per_sample,
)?;
let x_ratio_uv = self.vectors_data.x_ratio_uv.get() as usize;
let y_ratio_uv = self.vectors_data.y_ratio_uv.get() as usize;
let width = self.vectors_data.width;
let width_uv = unsafe { NonZeroUsize::new_unchecked(width.get() / x_ratio_uv) };
let height = self.vectors_data.height;
let height_uv = unsafe { NonZeroUsize::new_unchecked(height.get() / y_ratio_uv) };
let overlap_x = self.vectors_data.overlap_x;
let overlap_x_uv = overlap_x / x_ratio_uv;
let overlap_y = self.vectors_data.overlap_y;
let overlap_y_uv = overlap_y / y_ratio_uv;
let blk_size_x = self.vectors_data.blk_size_x;
let blk_size_x_uv = unsafe { NonZeroUsize::new_unchecked(blk_size_x.get() / x_ratio_uv) };
let blk_size_y = self.vectors_data.blk_size_y;
let blk_size_y_uv = unsafe { NonZeroUsize::new_unchecked(blk_size_y.get() / y_ratio_uv) };
let blk_x = self.vectors_data.blk_x;
let blk_y = self.vectors_data.blk_y;
let pel = self.vectors_data.pel;
let pel_i32 = i32::from(u8::from(pel));
let x_ratio_uv_i32 = x_ratio_uv as i32;
let y_ratio_uv_i32 = y_ratio_uv as i32;
let bits_per_sample = self.vectors_data.bits_per_sample;
let width_b = unsafe {
NonZeroUsize::new_unchecked(blk_x.get() * (blk_size_x.get() - overlap_x) + overlap_x)
};
let width_b_uv = unsafe { NonZeroUsize::new_unchecked(width_b.get() / x_ratio_uv) };
let height_b = unsafe {
NonZeroUsize::new_unchecked(blk_y.get() * (blk_size_y.get() - overlap_y) + overlap_y)
};
let height_b_uv = unsafe { NonZeroUsize::new_unchecked(height_b.get() / y_ratio_uv) };
if overlap_x == 0 && overlap_y == 0 {
for plane in 0..processed_plane_count {
match plane {
0 => {
let dest_stride_bytes =
pitch_to_bytes::<T>(pitch_for_plane(output_pitch, 0)?);
self.plane_loop_no_overlap::<T, 0>(
blk_size_x,
blk_size_y,
blk_x,
blk_y,
current_super.mv_plane(0),
current_super.plane(0)?,
reference_super.mv_plane(0),
reference_super.plane(0)?,
output.plane_mut(0)?,
dest_stride_bytes,
vectors,
pel,
pel_i32,
x_ratio_uv_i32,
y_ratio_uv_i32,
self.time256,
field_shift,
self.thsad as i64,
);
}
1 => {
let dest_stride_bytes =
pitch_to_bytes::<T>(pitch_for_plane(output_pitch, 1)?);
self.plane_loop_no_overlap::<T, 1>(
blk_size_x_uv,
blk_size_y_uv,
blk_x,
blk_y,
current_super.mv_plane(1),
current_super.plane(1)?,
reference_super.mv_plane(1),
reference_super.plane(1)?,
output.plane_mut(1)?,
dest_stride_bytes,
vectors,
pel,
pel_i32,
x_ratio_uv_i32,
y_ratio_uv_i32,
self.time256,
field_shift,
self.thsad as i64,
);
}
2 => {
let dest_stride_bytes =
pitch_to_bytes::<T>(pitch_for_plane(output_pitch, 2)?);
self.plane_loop_no_overlap::<T, 2>(
blk_size_x_uv,
blk_size_y_uv,
blk_x,
blk_y,
current_super.mv_plane(2),
current_super.plane(2)?,
reference_super.mv_plane(2),
reference_super.plane(2)?,
output.plane_mut(2)?,
dest_stride_bytes,
vectors,
pel,
pel_i32,
x_ratio_uv_i32,
y_ratio_uv_i32,
self.time256,
field_shift,
self.thsad as i64,
);
}
_ => unreachable!(),
}
}
} else {
for plane in 0..processed_plane_count {
match plane {
0 => {
let dest_stride_bytes =
pitch_to_bytes::<T>(pitch_for_plane(output_pitch, 0)?);
self.plane_loop_overlap::<T, 0>(
height,
blk_size_x,
blk_size_y,
blk_x,
blk_y,
overlap_x,
overlap_y,
current_super.mv_plane(0),
current_super.plane(0)?,
reference_super.mv_plane(0),
reference_super.plane(0)?,
output.plane_mut(0)?,
dest_stride_bytes,
self.dest_temp_stride_bytes,
vectors,
pel,
pel_i32,
x_ratio_uv_i32,
y_ratio_uv_i32,
self.time256,
field_shift,
self.thsad as i64,
width_b,
height_b,
bits_per_sample,
);
}
1 => {
let dest_stride_bytes =
pitch_to_bytes::<T>(pitch_for_plane(output_pitch, 1)?);
self.plane_loop_overlap::<T, 1>(
height_uv,
blk_size_x_uv,
blk_size_y_uv,
blk_x,
blk_y,
overlap_x_uv,
overlap_y_uv,
current_super.mv_plane(1),
current_super.plane(1)?,
reference_super.mv_plane(1),
reference_super.plane(1)?,
output.plane_mut(1)?,
dest_stride_bytes,
self.dest_temp_stride_bytes_uv,
vectors,
pel,
pel_i32,
x_ratio_uv_i32,
y_ratio_uv_i32,
self.time256,
field_shift,
self.thsad as i64,
width_b_uv,
height_b_uv,
bits_per_sample,
);
}
2 => {
let dest_stride_bytes =
pitch_to_bytes::<T>(pitch_for_plane(output_pitch, 2)?);
self.plane_loop_overlap::<T, 2>(
height_uv,
blk_size_x_uv,
blk_size_y_uv,
blk_x,
blk_y,
overlap_x_uv,
overlap_y_uv,
current_super.mv_plane(2),
current_super.plane(2)?,
reference_super.mv_plane(2),
reference_super.plane(2)?,
output.plane_mut(2)?,
dest_stride_bytes,
self.dest_temp_stride_bytes_uv,
vectors,
pel,
pel_i32,
x_ratio_uv_i32,
y_ratio_uv_i32,
self.time256,
field_shift,
self.thsad as i64,
width_b_uv,
height_b_uv,
bits_per_sample,
);
}
_ => unreachable!(),
}
}
}
let scene_change_source = match self.scene_change_behavior {
SceneChangeBehavior::ReferenceFrame => reference_super.view(),
SceneChangeBehavior::CurrentFrame => current_super.view(),
};
self.copy_uncovered_regions(
scene_change_source,
output,
output_pitch,
processed_plane_count,
width,
width_uv,
height,
height_uv,
width_b,
width_b_uv,
height_b,
height_b_uv,
)?;
if processed_plane_count < output_plane_count {
self.copy_visible_from_super(
current_super.view(),
output,
output_pitch,
processed_plane_count,
output_plane_count,
)?;
}
Ok(())
}
fn copy_visible_from_super<T: Pixel>(
&self,
super_frame: &FrameView<'_, T>,
output: &mut FramePlanesMut<'_, T>,
output_pitch: PlaneSizeTuple,
start_plane: usize,
end_plane: usize,
) -> Result<()> {
for plane in start_plane..end_plane {
let width = plane_width(&self.vectors_data, plane)?;
let height = plane_height(&self.vectors_data, plane)?;
let stride = pitch_for_plane(output_pitch, plane)?;
let src_stride = super_frame.pitch_for_plane(plane)?;
let src_offset = visible_offset(&self.vectors_data, plane, src_stride);
vs_bitblt(
output.plane_mut(plane)?,
stride,
semisafe_get(super_frame.plane(plane)?, src_offset..),
src_stride,
width,
height,
);
}
Ok(())
}
fn copy_uncovered_regions<T: Pixel>(
&self,
super_frame: &FrameView<'_, T>,
output: &mut FramePlanesMut<'_, T>,
output_pitch: PlaneSizeTuple,
plane_count: usize,
width: NonZeroUsize,
width_uv: NonZeroUsize,
height: NonZeroUsize,
height_uv: NonZeroUsize,
width_b: NonZeroUsize,
width_b_uv: NonZeroUsize,
height_b: NonZeroUsize,
height_b_uv: NonZeroUsize,
) -> Result<()> {
for plane in 0..plane_count {
let cur_width = if plane == 0 { width } else { width_uv };
let cur_width_b = if plane == 0 { width_b } else { width_b_uv };
let cur_height = if plane == 0 { height } else { height_uv };
let cur_height_b = if plane == 0 { height_b } else { height_b_uv };
let dest_stride = pitch_for_plane(output_pitch, plane)?;
let src_stride = super_frame.pitch_for_plane(plane)?;
let src_offset = visible_offset(&self.vectors_data, plane, src_stride);
let src_plane = super_frame.plane(plane)?;
if cur_width_b < cur_width {
let uncovered_width =
unsafe { NonZeroUsize::new_unchecked(cur_width.get() - cur_width_b.get()) };
vs_bitblt(
semisafe_get_mut(output.plane_mut(plane)?, cur_width_b.get()..),
dest_stride,
semisafe_get(src_plane, (src_offset + cur_width_b.get())..),
src_stride,
uncovered_width,
cur_height_b,
);
}
if cur_height_b < cur_height {
let uncovered_height =
unsafe { NonZeroUsize::new_unchecked(cur_height.get() - cur_height_b.get()) };
vs_bitblt(
semisafe_get_mut(
output.plane_mut(plane)?,
(cur_height_b.get() * dest_stride.get())..,
),
dest_stride,
semisafe_get(
src_plane,
(src_offset + cur_height_b.get() * src_stride.get())..,
),
src_stride,
cur_width,
uncovered_height,
);
}
}
Ok(())
}
fn plane_loop_no_overlap<T: Pixel, const PLANE: usize>(
&self,
blk_size_x: NonZeroUsize,
blk_size_y: NonZeroUsize,
blk_x: NonZeroUsize,
blk_y: NonZeroUsize,
current_plane: &MVPlane,
current_plane_data: &[T],
reference_plane: &MVPlane,
reference_plane_data: &[T],
dest_plane_data: &mut [T],
dest_stride_bytes: NonZeroUsize,
vectors: &FakeGroupOfPlanes,
_pel: Subpel,
pel_i32: i32,
x_ratio_uv: i32,
y_ratio_uv: i32,
time256: i32,
field_shift: i32,
th_sad: i64,
) {
let dest_stride_pixels =
unsafe { NonZeroUsize::new_unchecked(dest_stride_bytes.get() / size_of::<T>()) };
let field_shift_uv = field_shift / y_ratio_uv;
let mut dest_cur_offset = 0;
for by in 0..blk_y.get() {
let mut xx = 0;
for bx in 0..blk_x.get() {
let i = by * blk_x.get() + bx;
let block = vectors.get_block(0, i);
let (blx, bly, plane, plane_data) = if block.vector.sad < th_sad {
(
(block.x * pel_i32 + block.vector.x * time256 / 256)
/ if PLANE == 0 { 1 } else { x_ratio_uv },
(block.y * pel_i32 + block.vector.y * time256 / 256 + field_shift)
/ if PLANE == 0 { 1 } else { y_ratio_uv },
reference_plane,
reference_plane_data,
)
} else {
(
(bx * blk_size_x.get()) as i32 * pel_i32,
(by * blk_size_y.get()) as i32 * pel_i32
+ if PLANE == 0 {
field_shift
} else {
field_shift_uv
},
current_plane,
current_plane_data,
)
};
unsafe {
if PLANE == 0 {
(self.functions.blit)(
semisafe_get_mut(dest_plane_data, (dest_cur_offset + xx)..)
.as_mut_ptr()
.cast(),
dest_stride_bytes,
semisafe_get(plane_data, plane.get_pix_offset(blx, bly)..)
.as_ptr()
.cast(),
pitch_to_bytes::<T>(plane.stride),
);
} else {
(self.functions.blit_uv)(
semisafe_get_mut(dest_plane_data, (dest_cur_offset + xx)..)
.as_mut_ptr()
.cast(),
dest_stride_bytes,
semisafe_get(plane_data, plane.get_pix_offset(blx, bly)..)
.as_ptr()
.cast(),
pitch_to_bytes::<T>(plane.stride),
);
}
}
xx += blk_size_x.get();
}
dest_cur_offset += blk_size_y.get() * dest_stride_pixels.get();
}
}
fn plane_loop_overlap<T: Pixel, const PLANE: usize>(
&self,
height: NonZeroUsize,
blk_size_x: NonZeroUsize,
blk_size_y: NonZeroUsize,
blk_x: NonZeroUsize,
blk_y: NonZeroUsize,
overlap_x: usize,
overlap_y: usize,
current_plane: &MVPlane,
current_plane_data: &[T],
reference_plane: &MVPlane,
reference_plane_data: &[T],
dest_plane_data: &mut [T],
dest_stride_bytes: NonZeroUsize,
dest_temp_stride_bytes: NonZeroUsize,
vectors: &FakeGroupOfPlanes,
_pel: Subpel,
pel_i32: i32,
x_ratio_uv: i32,
y_ratio_uv: i32,
time256: i32,
field_shift: i32,
th_sad: i64,
width_b: NonZeroUsize,
height_b: NonZeroUsize,
bits_per_sample: NonZeroU8,
) {
let mut dest_temp = vec![0u8; height.get() * dest_temp_stride_bytes.get()];
let mut dest_temp_offset_bytes = 0;
let field_shift_uv = field_shift / y_ratio_uv;
let over_wins = if PLANE == 0 {
self.over_wins.as_ref()
} else {
self.over_wins_uv.as_ref()
}
.expect("overlap windows must exist when overlap is active");
for by in 0..blk_y.get() {
let wby = ((by + blk_y.get() - 3) / (blk_y.get() - 2)) * 3;
let mut wbx = 0;
let mut xx = 0;
for bx in 0..blk_x.get() {
wbx = if bx == blk_x.get() - 1 { 2 } else { wbx };
let win_over = over_wins.get_window(wby + wbx);
let i = by * blk_x.get() + bx;
let block = vectors.get_block(0, i);
let (blx, bly, plane, plane_data) = if block.vector.sad < th_sad {
(
(block.x * pel_i32 + block.vector.x * time256 / 256)
/ if PLANE == 0 { 1 } else { x_ratio_uv },
(block.y * pel_i32 + block.vector.y * time256 / 256 + field_shift)
/ if PLANE == 0 { 1 } else { y_ratio_uv },
reference_plane,
reference_plane_data,
)
} else {
(
(bx * (blk_size_x.get() - overlap_x)) as i32 * pel_i32,
(by * (blk_size_y.get() - overlap_y)) as i32 * pel_i32
+ if PLANE == 0 {
field_shift
} else {
field_shift_uv
},
current_plane,
current_plane_data,
)
};
unsafe {
let temp_x_offset_bytes = xx * size_of::<T>() * 2;
if PLANE == 0 {
(self.functions.overlaps)(
semisafe_get_mut(
&mut dest_temp,
(dest_temp_offset_bytes + temp_x_offset_bytes)..,
)
.as_mut_ptr(),
dest_temp_stride_bytes,
semisafe_get(plane_data, plane.get_pix_offset(blx, bly)..)
.as_ptr()
.cast(),
pitch_to_bytes::<T>(plane.stride),
win_over.as_ptr(),
blk_size_x,
);
} else {
(self.functions.overlaps_uv)(
semisafe_get_mut(
&mut dest_temp,
(dest_temp_offset_bytes + temp_x_offset_bytes)..,
)
.as_mut_ptr(),
dest_temp_stride_bytes,
semisafe_get(plane_data, plane.get_pix_offset(blx, bly)..)
.as_ptr()
.cast(),
pitch_to_bytes::<T>(plane.stride),
win_over.as_ptr(),
blk_size_x,
);
}
}
xx += blk_size_x.get() - overlap_x;
wbx = 1;
}
dest_temp_offset_bytes += dest_temp_stride_bytes.get() * (blk_size_y.get() - overlap_y);
}
unsafe {
(self.functions.to_pixels)(
dest_plane_data.as_mut_ptr().cast(),
dest_stride_bytes,
dest_temp.as_ptr(),
dest_temp_stride_bytes,
width_b,
height_b,
bits_per_sample,
);
}
}
fn select_functions(
blk_size_x: NonZeroUsize,
blk_size_y: NonZeroUsize,
chroma_blk_x: NonZeroUsize,
chroma_blk_y: NonZeroUsize,
bit_depth: NonZeroU8,
) -> CompensateFunctions {
match bit_depth.get() {
1..=8 => CompensateFunctions {
overlaps: select_overlaps::<u8>(blk_size_x, blk_size_y),
blit: select_copy::<u8>(blk_size_x, blk_size_y),
overlaps_uv: select_overlaps::<u8>(chroma_blk_x, chroma_blk_y),
blit_uv: select_copy::<u8>(chroma_blk_x, chroma_blk_y),
to_pixels: select_to_pixels::<u8>(),
},
9..=16 => CompensateFunctions {
overlaps: select_overlaps::<u16>(blk_size_x, blk_size_y),
blit: select_copy::<u16>(blk_size_x, blk_size_y),
overlaps_uv: select_overlaps::<u16>(chroma_blk_x, chroma_blk_y),
blit_uv: select_copy::<u16>(chroma_blk_x, chroma_blk_y),
to_pixels: select_to_pixels::<u16>(),
},
_ => unreachable!(),
}
}
}
fn validate_standard_input(info: VideoInfo, filter_name: &str) -> Result<()> {
ensure!(
info.format.bits_per_sample.get() <= 16
&& info.format.sample_type == SampleType::Integer
&& [ColorFamily::Yuv, ColorFamily::Gray].contains(&info.format.color_family)
&& info.format.sub_sampling_w <= 1
&& info.format.sub_sampling_h <= 1,
"{filter_name}: input clip must be GRAY, 420, 422, 440, or 444, up to 16 bits, with constant dimensions."
);
Ok(())
}
fn validate_super_geometry(
info: VideoInfo,
actual_super_width: usize,
super_info: SuperClipInfo,
vectors_data: MVAnalysisData,
filter_name: &str,
) -> Result<()> {
let vectors_super_width = super_info
.hpad
.checked_mul(2)
.and_then(|padding| actual_super_width.checked_sub(padding))
.ok_or_else(|| anyhow!("{filter_name}: wrong source or super clip frame size"))?;
ensure!(
vectors_data.height == super_info.height
&& vectors_data.width.get() == vectors_super_width
&& vectors_data.width.get() == info.resolution.width
&& vectors_data.height.get() == info.resolution.height
&& vectors_data.pel == super_info.pel
&& vectors_data.h_padding == super_info.hpad
&& vectors_data.v_padding == super_info.vpad,
"{filter_name}: wrong source or super clip frame size"
);
Ok(())
}
fn pitch_for_plane(pitch: PlaneSizeTuple, plane: usize) -> Result<NonZeroUsize> {
match plane {
0 => Ok(pitch.0),
1 => pitch
.1
.ok_or_else(|| anyhow!("requested plane {plane} is not available")),
2 => pitch
.2
.ok_or_else(|| anyhow!("requested plane {plane} is not available")),
_ => bail!("requested plane {plane} is not available"),
}
}
const fn pitch_to_bytes<T: Pixel>(pitch_pixels: NonZeroUsize) -> NonZeroUsize {
unsafe { NonZeroUsize::new_unchecked(pitch_pixels.get() * size_of::<T>()) }
}
fn plane_width(vectors_data: &MVAnalysisData, plane: usize) -> Result<NonZeroUsize> {
match plane {
0 => Ok(vectors_data.width),
1 | 2 => Ok(unsafe {
NonZeroUsize::new_unchecked(
vectors_data.width.get() / vectors_data.x_ratio_uv.get() as usize,
)
}),
_ => bail!("requested plane {plane} is not available"),
}
}
fn plane_height(vectors_data: &MVAnalysisData, plane: usize) -> Result<NonZeroUsize> {
match plane {
0 => Ok(vectors_data.height),
1 | 2 => Ok(unsafe {
NonZeroUsize::new_unchecked(
vectors_data.height.get() / vectors_data.y_ratio_uv.get() as usize,
)
}),
_ => bail!("requested plane {plane} is not available"),
}
}
const fn visible_offset(
vectors_data: &MVAnalysisData,
plane: usize,
stride: NonZeroUsize,
) -> usize {
let h_padding = if plane == 0 {
vectors_data.h_padding
} else {
vectors_data.h_padding / vectors_data.x_ratio_uv.get() as usize
};
let v_padding = if plane == 0 {
vectors_data.v_padding
} else {
vectors_data.v_padding / vectors_data.y_ratio_uv.get() as usize
};
h_padding + v_padding * stride.get()
}