zoomvtools 1.1.0

Video motion vector analysis utilities in pure Rust
Documentation
use std::num::NonZeroUsize;

use anyhow::Result;

use crate::{
    analysis::MVAnalysisData,
    fake::{block_data::FakeBlockData, plane_of_blocks::FakePlaneOfBlocks},
    params::Subpel,
};
use semisafe::slice::get as semisafe_get;
use semisafe::slice::get_mut as semisafe_get_mut;

pub struct FakeGroupOfPlanes {
    pub level_count: usize,
    pub validity: bool,
    pub planes: Vec<FakePlaneOfBlocks>,
}

impl FakeGroupOfPlanes {
    #[must_use]
    #[inline]
    pub fn new(analysis_data: &MVAnalysisData) -> Self {
        let blk_x1 = analysis_data.blk_x;
        let blk_y1 = analysis_data.blk_y;
        // SAFETY: overlap cannot be gte block size
        let width_b = unsafe {
            NonZeroUsize::new_unchecked(analysis_data.blk_size_x.get() - analysis_data.overlap_x)
                .saturating_mul(blk_x1)
                .saturating_add(analysis_data.overlap_x)
        };
        // SAFETY: overlap cannot be gte block size
        let height_b = unsafe {
            NonZeroUsize::new_unchecked(analysis_data.blk_size_y.get() - analysis_data.overlap_y)
                .saturating_mul(blk_y1)
                .saturating_add(analysis_data.overlap_y)
        };

        let mut planes = Vec::with_capacity(analysis_data.level_count);
        planes.push(FakePlaneOfBlocks::new(
            analysis_data.blk_size_x,
            analysis_data.blk_size_y,
            analysis_data.pel,
            analysis_data.overlap_x,
            analysis_data.overlap_y,
            blk_x1,
            blk_y1,
        ));

        for i in 1..analysis_data.level_count {
            // SAFETY: overlap cannot be gte scaled block size
            let blk_x1 = unsafe {
                NonZeroUsize::new_unchecked(
                    ((width_b.get() >> i) - analysis_data.overlap_x)
                        / (analysis_data.blk_size_x.get() - analysis_data.overlap_x),
                )
            };
            // SAFETY: overlap cannot be gte scaled block size
            let blk_y1 = unsafe {
                NonZeroUsize::new_unchecked(
                    ((height_b.get() >> i) - analysis_data.overlap_y)
                        / (analysis_data.blk_size_y.get() - analysis_data.overlap_y),
                )
            };
            planes.push(FakePlaneOfBlocks::new(
                analysis_data.blk_size_x,
                analysis_data.blk_size_y,
                Subpel::Full,
                analysis_data.overlap_x,
                analysis_data.overlap_y,
                blk_x1,
                blk_y1,
            ));
        }

        Self {
            level_count: analysis_data.level_count,
            validity: false,
            planes,
        }
    }

    #[inline]
    pub fn update(&mut self, vectors_data: &[u8]) -> Result<()> {
        self.validity = Self::get_validity(vectors_data)?;

        let mut offset = 2 * size_of::<i32>();
        for i in (0..self.level_count).rev() {
            semisafe_get_mut(&mut self.planes, i)
                .update(semisafe_get(vectors_data, (offset + size_of::<i32>())..));

            let size = i32::from_le_bytes(
                semisafe_get(semisafe_get(vectors_data, offset..), ..size_of::<i32>())
                    .try_into()?,
            );
            offset += size as usize;
        }

        Ok(())
    }

    #[must_use]
    #[inline]
    pub fn get_block(&self, level: usize, blk: usize) -> &FakeBlockData {
        semisafe_get(&self.planes, level).get_block(blk)
    }

    fn get_validity(vectors_data: &[u8]) -> Result<bool> {
        let validity = i32::from_le_bytes(
            semisafe_get(
                semisafe_get(vectors_data, size_of::<i32>()..),
                ..size_of::<i32>(),
            )
            .try_into()?,
        );
        Ok(validity > 0)
    }

    #[must_use]
    #[inline]
    pub fn is_usable(&self, thscd1: u64, thscd2: u64) -> bool {
        !self.is_scenechange(thscd1, thscd2) && self.is_valid()
    }

    fn is_scenechange(&self, thscd1: u64, thscd2: u64) -> bool {
        semisafe_get(&self.planes, 0).is_scenechange(thscd1, thscd2)
    }

    const fn is_valid(&self) -> bool {
        self.validity
    }
}