1use pixelflow_core::{
4 AllocatorConfig, ClipMedia, Frame, FrameBuilder, MetadataSchema, Rational, Result, Sample,
5 resolve_format_alias,
6};
7
8#[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
52pub 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
62pub 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
72pub 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}