Skip to main content

pixelflow_test_support/
frames.rs

1//! Synthetic frame and media builders.
2
3use pixelflow_core::{
4    AllocatorConfig, ClipMedia, Frame, FrameBuilder, MetadataSchema, Rational, Result, Sample,
5    resolve_format_alias,
6};
7
8/// Creates fixed media with deterministic test defaults.
9#[must_use]
10pub fn fixed_media(alias: &str, width: usize, height: usize) -> ClipMedia {
11    ClipMedia::fixed(
12        resolve_format_alias(alias).expect("format alias should resolve"),
13        width,
14        height,
15        24,
16        Rational {
17            numerator: 24_000,
18            denominator: 1_001,
19        },
20    )
21}
22
23fn synthetic_frame<T: Sample>(
24    alias: &str,
25    width: usize,
26    height: usize,
27    mut sample: impl FnMut(usize, usize, usize) -> T,
28) -> Result<Frame> {
29    let format = resolve_format_alias(alias)?;
30    let schema = MetadataSchema::core();
31    let mut builder = FrameBuilder::new(
32        format.clone(),
33        width,
34        height,
35        &schema,
36        AllocatorConfig::default(),
37    )?;
38
39    for plane_index in 0..format.planes().len() {
40        let mut plane = builder.plane_mut::<T>(plane_index)?;
41        for y in 0..plane.height() {
42            let row = plane.row_mut(y).expect("row should exist");
43            for (x, value) in row.iter_mut().enumerate() {
44                *value = sample(plane_index, x, y);
45            }
46        }
47    }
48
49    Ok(builder.finish())
50}
51
52/// Builds one synthetic frame with `u8` storage using per-plane coordinates.
53pub fn synthetic_u8_frame(
54    alias: &str,
55    width: usize,
56    height: usize,
57    sample: impl FnMut(usize, usize, usize) -> u8,
58) -> Result<Frame> {
59    synthetic_frame(alias, width, height, sample)
60}
61
62/// Builds one synthetic frame with `u16` storage using per-plane coordinates.
63pub fn synthetic_u16_frame(
64    alias: &str,
65    width: usize,
66    height: usize,
67    sample: impl FnMut(usize, usize, usize) -> u16,
68) -> Result<Frame> {
69    synthetic_frame(alias, width, height, sample)
70}
71
72/// Builds one synthetic frame with `f32` storage using per-plane coordinates.
73pub fn synthetic_f32_frame(
74    alias: &str,
75    width: usize,
76    height: usize,
77    sample: impl FnMut(usize, usize, usize) -> f32,
78) -> Result<Frame> {
79    synthetic_frame(alias, width, height, sample)
80}
81
82#[cfg(test)]
83mod tests {
84    use pixelflow_core::{ClipFormat, FrameCount, FrameRate, Rational};
85
86    use crate::{
87        EXACT_GOLDEN_TOLERANCE, assert_plane_f32_near, assert_plane_u8_near, assert_plane_u16_near,
88    };
89
90    use super::{fixed_media, synthetic_f32_frame, synthetic_u8_frame, synthetic_u16_frame};
91
92    #[test]
93    fn fixed_media_uses_standard_test_defaults() {
94        let media = fixed_media("gray8", 8, 6);
95
96        assert!(matches!(
97            media.format(),
98            ClipFormat::Fixed(format) if format.name() == "gray8"
99        ));
100        assert_eq!(media.frame_count(), FrameCount::Finite(24));
101        assert_eq!(
102            media.frame_rate(),
103            FrameRate::Cfr(Rational {
104                numerator: 24_000,
105                denominator: 1_001,
106            })
107        );
108    }
109
110    #[test]
111    fn synthetic_u8_frame_builds_deterministic_rows() {
112        let frame = synthetic_u8_frame("gray8", 3, 2, |_plane, x, y| {
113            u8::try_from(x + y * 10).expect("fixture sample fits u8")
114        })
115        .expect("synthetic frame should build");
116
117        assert_plane_u8_near(
118            &frame,
119            0,
120            &[&[0, 1, 2], &[10, 11, 12]],
121            EXACT_GOLDEN_TOLERANCE,
122        );
123    }
124
125    #[test]
126    fn synthetic_u16_frame_uses_u16_storage() {
127        let frame = synthetic_u16_frame("gray10", 2, 1, |_plane, x, _y| {
128            100 + u16::try_from(x).expect("fixture sample fits u16")
129        })
130        .expect("synthetic frame should build");
131
132        assert_plane_u16_near(&frame, 0, &[&[100, 101]], EXACT_GOLDEN_TOLERANCE);
133    }
134
135    #[test]
136    fn synthetic_f32_frame_uses_f32_storage() {
137        let frame = synthetic_f32_frame("grayf32", 2, 1, |_plane, x, _y| match x {
138            0 => 0.25,
139            1 => 1.25,
140            _ => unreachable!("fixture width is 2"),
141        })
142        .expect("synthetic frame should build");
143
144        assert_plane_f32_near(&frame, 0, &[&[0.25, 1.25]], EXACT_GOLDEN_TOLERANCE);
145    }
146}