zoomvtools 2.0.0

Video motion vector analysis utilities in pure Rust
Documentation
#[cfg(test)]
use std::mem::size_of;
use std::{num::NonZeroUsize, slice};

#[cfg(test)]
use crate::util::Pixel;

#[cfg(test)]
#[must_use]
pub(super) fn get_sad<T: Pixel>(
    width: NonZeroUsize,
    height: NonZeroUsize,
    src: &[T],
    src_pitch: NonZeroUsize,
    ref_: &[T],
    ref_pitch: NonZeroUsize,
) -> u64 {
    match (size_of::<T>(), width.get(), height.get()) {
        (1, 2, 2) => get_sad_u8::<2, 2>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 2, 4) => get_sad_u8::<2, 4>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 4, 2) => get_sad_u8::<4, 2>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 4, 4) => get_sad_u8::<4, 4>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 4, 8) => get_sad_u8::<4, 8>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 8, 1) => get_sad_u8::<8, 1>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 8, 2) => get_sad_u8::<8, 2>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 8, 4) => get_sad_u8::<8, 4>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 8, 8) => get_sad_u8::<8, 8>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 8, 16) => get_sad_u8::<8, 16>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 16, 1) => get_sad_u8::<16, 1>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 16, 2) => get_sad_u8::<16, 2>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 16, 4) => get_sad_u8::<16, 4>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 16, 8) => get_sad_u8::<16, 8>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 16, 16) => get_sad_u8::<16, 16>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 16, 32) => get_sad_u8::<16, 32>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 32, 8) => get_sad_u8::<32, 8>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 32, 16) => get_sad_u8::<32, 16>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 32, 32) => get_sad_u8::<32, 32>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 32, 64) => get_sad_u8::<32, 64>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 64, 16) => get_sad_u8::<64, 16>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 64, 32) => get_sad_u8::<64, 32>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 64, 64) => get_sad_u8::<64, 64>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 64, 128) => get_sad_u8::<64, 128>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 128, 32) => get_sad_u8::<128, 32>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 128, 64) => get_sad_u8::<128, 64>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (1, 128, 128) => get_sad_u8::<128, 128>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 2, 2) => get_sad_u16::<2, 2>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 2, 4) => get_sad_u16::<2, 4>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 4, 2) => get_sad_u16::<4, 2>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 4, 4) => get_sad_u16::<4, 4>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 4, 8) => get_sad_u16::<4, 8>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 8, 1) => get_sad_u16::<8, 1>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 8, 2) => get_sad_u16::<8, 2>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 8, 4) => get_sad_u16::<8, 4>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 8, 8) => get_sad_u16::<8, 8>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 8, 16) => get_sad_u16::<8, 16>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 16, 1) => get_sad_u16::<16, 1>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 16, 2) => get_sad_u16::<16, 2>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 16, 4) => get_sad_u16::<16, 4>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 16, 8) => get_sad_u16::<16, 8>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 16, 16) => get_sad_u16::<16, 16>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 16, 32) => get_sad_u16::<16, 32>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 32, 8) => get_sad_u16::<32, 8>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 32, 16) => get_sad_u16::<32, 16>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 32, 32) => get_sad_u16::<32, 32>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 32, 64) => get_sad_u16::<32, 64>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 64, 16) => get_sad_u16::<64, 16>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 64, 32) => get_sad_u16::<64, 32>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 64, 64) => get_sad_u16::<64, 64>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 64, 128) => get_sad_u16::<64, 128>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 128, 32) => get_sad_u16::<128, 32>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 128, 64) => get_sad_u16::<128, 64>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        (2, 128, 128) => get_sad_u16::<128, 128>(
            src.as_ptr().cast(),
            src_pitch,
            ref_.as_ptr().cast(),
            ref_pitch,
        ),
        _ => unreachable!("unsupported block size"),
    }
}

/// Block-size-specific SAD implementation.
/// This is made pub(crate) to allow direct access for pre-selected function pointers.
#[must_use]
pub(crate) fn get_sad_u8<const WIDTH: usize, const HEIGHT: usize>(
    src: *const u8,
    src_pitch: NonZeroUsize,
    ref_: *const u8,
    ref_pitch: NonZeroUsize,
) -> u64 {
    let mut sum = 0;
    for y in 0..HEIGHT {
        // SAFETY: size of the block data is checked early
        let src_row = unsafe { slice::from_raw_parts(src.add(y * src_pitch.get()), WIDTH) };
        // SAFETY: size of the block data is checked early
        let ref_row = unsafe { slice::from_raw_parts(ref_.add(y * ref_pitch.get()), WIDTH) };
        sum += src_row.iter().zip(ref_row.iter()).fold(0, |acc, (&s, &r)| {
            // smaller types for 8-bit for faster code generation
            let val1: i16 = s as _;
            let val2: i16 = r as _;
            acc + (val1 - val2).unsigned_abs() as u64
        });
    }
    sum
}

/// Block-size-specific SAD implementation.
/// This is made pub(crate) to allow direct access for pre-selected function pointers.
#[must_use]
pub(crate) fn get_sad_u16<const WIDTH: usize, const HEIGHT: usize>(
    src: *const u8,
    src_pitch: NonZeroUsize,
    ref_: *const u8,
    ref_pitch: NonZeroUsize,
) -> u64 {
    let src: *const u16 = src.cast();
    let ref_: *const u16 = ref_.cast();
    let mut sum = 0;
    for y in 0..HEIGHT {
        // SAFETY: size of the block data is checked early
        let src_row = unsafe { slice::from_raw_parts(src.add(y * src_pitch.get()), WIDTH) };
        // SAFETY: size of the block data is checked early
        let ref_row = unsafe { slice::from_raw_parts(ref_.add(y * ref_pitch.get()), WIDTH) };
        sum += src_row.iter().zip(ref_row.iter()).fold(0, |acc, (&s, &r)| {
            let val1: i32 = s as _;
            let val2: i32 = r as _;
            acc + (val1 - val2).unsigned_abs() as u64
        });
    }
    sum
}