vapoursynth4_sys/
helper.rs

1/*
2 This Source Code Form is subject to the terms of the Mozilla Public
3 License, v. 2.0. If a copy of the MPL was not distributed with this
4 file, You can obtain one at http://mozilla.org/MPL/2.0/.
5*/
6
7//! VSHelper4.h
8
9#![allow(clippy::cast_possible_truncation)]
10
11use std::ffi::{c_int, c_void};
12
13use crate::{
14    VSAPI, VSAudioFormat, VSAudioInfo, VSColorFamily, VSCore, VSPresetVideoFormat, VSVideoFormat,
15    VSVideoInfo,
16};
17
18/// Convenience function for checking if the format never changes between frames
19#[inline]
20#[must_use]
21pub const fn is_constant_video_format(vi: &VSVideoInfo) -> bool {
22    vi.height > 0
23        && vi.width > 0
24        && vi.format.color_family as i32 != VSColorFamily::Undefined as i32
25}
26
27/// Convenience function to check if two clips have the same format
28/// (unknown/changeable will be considered the same too)
29#[inline]
30#[must_use]
31pub const fn is_same_video_format(v1: &VSVideoFormat, v2: &VSVideoFormat) -> bool {
32    v1.color_family as i32 == v2.color_family as i32
33        && v1.sample_type as i32 == v2.sample_type as i32
34        && v1.bits_per_sample == v2.bits_per_sample
35        && v1.sub_sampling_w == v2.sub_sampling_w
36        && v1.sub_sampling_h == v2.sub_sampling_h
37}
38
39impl VSAPI {
40    /// Convenience function to check if a clip has the same format as a format id
41    ///
42    /// # Safety
43    ///
44    /// `core` must be valid
45    #[inline]
46    pub unsafe fn is_same_video_preset_format(
47        &self,
48        preset_format: VSPresetVideoFormat,
49        v: &VSVideoFormat,
50        core: *mut VSCore,
51    ) -> bool {
52        unsafe {
53            (self.queryVideoFormatID)(
54                v.color_family,
55                v.sample_type,
56                v.bits_per_sample,
57                v.sub_sampling_w,
58                v.sub_sampling_h,
59                core,
60            ) == preset_format as u32
61        }
62    }
63}
64
65/// Convenience function to check for if two clips have the same format
66/// (but not framerate) while also including width and height
67/// (unknown/changeable will be considered the same too)
68#[inline]
69#[must_use]
70pub const fn is_same_video_info(v1: &VSVideoInfo, v2: &VSVideoInfo) -> bool {
71    v1.height == v2.height && v1.width == v2.width && is_same_video_format(&v1.format, &v2.format)
72}
73
74/// Convenience function to check for if two clips have the same format while also including
75/// `sampleRate` (unknown/changeable will be considered the same too)
76#[inline]
77#[must_use]
78pub const fn is_same_audio_format(a1: &VSAudioFormat, a2: &VSAudioFormat) -> bool {
79    a1.bits_per_sample == a2.bits_per_sample
80        && a1.sample_type as i32 == a2.sample_type as i32
81        && a1.channel_layout == a2.channel_layout
82}
83
84/// Convenience function to check for if two clips have the same format while also including
85/// `sampleRate` (unknown/changeable will be considered the same too)
86#[inline]
87#[must_use]
88pub const fn is_same_audio_info(a1: &VSAudioInfo, a2: &VSAudioInfo) -> bool {
89    a1.sample_rate == a2.sample_rate && is_same_audio_format(&a1.format, &a2.format)
90}
91
92/// Multiplies and divides a rational number,
93/// such as a frame duration, in place and reduces the result
94// TODO: use `const` when available: https://github.com/rust-lang/rust/issues/57349
95#[inline]
96pub fn muldiv_rational(num: &mut i64, den: &mut i64, mul: i64, div: i64) {
97    // do nothing if the rational number is invalid
98    if *den == 0 {
99        return;
100    }
101
102    *num *= mul;
103    *den *= div;
104    let mut a = *num;
105    let mut b = *den;
106    while b != 0 {
107        let t = a;
108        a = b;
109        b = t % b;
110    }
111    if a < 0 {
112        a = -a;
113    }
114
115    *num /= a;
116    *den /= a;
117}
118
119/// Reduces a rational number
120#[inline]
121pub fn reduce_rational(num: &mut i64, den: &mut i64) {
122    muldiv_rational(num, den, 1, 1);
123}
124
125/// Add two rational numbers and reduces the result
126#[inline]
127pub fn add_rational(num: &mut i64, den: &mut i64, mut addnum: i64, addden: i64) {
128    // Do nothing if the rational number is invalid
129    if *den == 0 {
130        return;
131    }
132
133    if *den == addden {
134        *num += addnum;
135    } else {
136        let temp = addden;
137        addnum *= *den;
138        // addden *= *den;
139        *num *= temp;
140        *den *= temp;
141
142        *num += addnum;
143
144        reduce_rational(num, den);
145    }
146}
147
148/// Converts an int64 to int with saturation, useful to silence warnings when reading
149/// int properties among other things
150#[inline]
151#[must_use]
152pub const fn int64_to_int_s(i: i64) -> c_int {
153    if i > c_int::MAX as i64 {
154        c_int::MAX
155    } else if i < c_int::MIN as i64 {
156        c_int::MIN
157    } else {
158        i as c_int
159    }
160}
161
162/// Converts a double to float with saturation, useful to silence warnings when reading
163/// float properties among other things
164#[inline]
165#[must_use]
166pub const fn double_to_float_s(d: f64) -> f32 {
167    d as f32
168}
169
170/// Copies bytes from one plane to another. Basically, it is memcpy in a loop.
171///
172/// # Safety
173/// `srcp` and `dstp` must be valid and not overlapping
174#[inline]
175pub unsafe fn bitblt(
176    dstp: *mut c_void,
177    dst_stride: isize,
178    srcp: *const c_void,
179    src_stride: isize,
180    row_size: usize,
181    height: usize,
182) {
183    if height != 0 {
184        if src_stride == dst_stride && src_stride == row_size as isize {
185            unsafe { dstp.copy_from_nonoverlapping(srcp, row_size * height) };
186        } else {
187            let mut srcp8 = srcp.cast::<u8>();
188            let mut dstp8 = dstp.cast::<u8>();
189            let mut i = 0;
190            while i < height {
191                unsafe { dstp8.copy_from_nonoverlapping(srcp8, row_size) };
192                srcp8 = srcp8.wrapping_offset(src_stride);
193                dstp8 = dstp8.wrapping_offset(dst_stride);
194                i += 1;
195            }
196        }
197    }
198}
199
200// Check if the frame dimensions are valid for a given format
201// returns non-zero for valid width and height
202#[inline]
203#[must_use]
204pub const fn are_valid_dimensions(fi: &VSVideoFormat, width: c_int, height: c_int) -> bool {
205    width % (1 << fi.sub_sampling_w) == 0 && height % (1 << fi.sub_sampling_h) == 0
206}