av_denoise_vs/frames.rs
1//! Converts between VapourSynth's strided plane buffers and the tightly
2//! packed rows core's converters accept, plus the frame window core
3//! windowed algorithms request around each output frame.
4
5/// Copies `src`, a plane with row stride `stride` bytes, into a new
6/// tightly packed buffer of `width_bytes * height` bytes.
7///
8/// `width_bytes` is `width * bytes_per_sample`, not a pixel count, so
9/// this works the same at any bit depth. Passing a pixel count here
10/// packs the wrong number of bytes per row.
11pub fn pack_plane(src: &[u8], stride: usize, width_bytes: usize, height: usize) -> Vec<u8> {
12 let mut packed = Vec::with_capacity(width_bytes * height);
13 for row in src.chunks(stride).take(height) {
14 packed.extend_from_slice(&row[..width_bytes]);
15 }
16 packed
17}
18
19/// Writes a tightly packed plane, `src`, back into `dst`, a strided
20/// buffer with row stride `stride` bytes. The reverse of [`pack_plane`].
21///
22/// `width_bytes` is `width * bytes_per_sample`, not a pixel count, so
23/// this works the same at any bit depth. `dst`'s padding bytes, if any,
24/// are left untouched.
25pub fn unpack_plane_into(dst: &mut [u8], stride: usize, width_bytes: usize, height: usize, src: &[u8]) {
26 for (y, row) in dst.chunks_mut(stride).take(height).enumerate() {
27 let packed_row = &src[y * width_bytes..(y + 1) * width_bytes];
28 row[..width_bytes].copy_from_slice(packed_row);
29 }
30}
31
32/// The `behind + 1 + ahead` source frame indices for the window around
33/// output frame `n`, `behind` older and `ahead` newer, clamped so
34/// nothing runs off either end of a clip whose last valid index is
35/// `last_frame`.
36///
37/// `behind` and `ahead` come from the denoiser's own
38/// [`av_denoise_core::PlanarDenoiser::window_span`], so this stays
39/// correct for whichever algorithm the denoiser is running rather than
40/// assuming every algorithm needs the same symmetric window.
41///
42/// Frame requests and window builds both call this, so the two always
43/// agree on which frames a window at `n` pulls in.
44pub fn window_indices(n: usize, behind: usize, ahead: usize, last_frame: usize) -> Vec<usize> {
45 (0..=behind + ahead)
46 .map(|i| (n + i).saturating_sub(behind).min(last_frame))
47 .collect()
48}