1use alloc::format;
4use alloc::vec::Vec;
5
6use zencodec::decode::{DecodeRowSink, SinkError};
7use zenpixels::{PixelBuffer, PixelDescriptor, PixelSlice, PixelSliceMut};
8
9#[derive(Default)]
18pub struct CollectSink {
19 data: Option<PixelBuffer>,
20 width: u32,
21 height: u32,
22 descriptor: Option<PixelDescriptor>,
23 rows_written: u64,
24 covered_rows: Vec<u64>,
25 unique_rows: u32,
26 finished: bool,
27}
28
29impl CollectSink {
30 pub fn new() -> Self {
32 Self::default()
33 }
34
35 pub fn width(&self) -> u32 {
37 self.width
38 }
39
40 pub fn height(&self) -> u32 {
42 self.height
43 }
44
45 pub fn descriptor(&self) -> Option<PixelDescriptor> {
47 self.descriptor
48 }
49
50 pub fn rows_written(&self) -> u64 {
52 self.rows_written
53 }
54
55 pub fn finished(&self) -> bool {
57 self.finished
58 }
59
60 pub fn bytes(&self) -> &[u8] {
62 self.data
63 .as_ref()
64 .map_or(&[], |buffer| buffer.as_slice().as_strided_bytes())
65 }
66
67 pub fn as_pixel_slice(&self) -> Option<PixelSlice<'_>> {
69 self.data.as_ref().map(PixelBuffer::as_slice)
70 }
71
72 pub fn into_buffer(self) -> Option<PixelBuffer> {
79 self.finished.then_some(self.data).flatten()
80 }
81
82 pub fn to_buffer(&self) -> Option<PixelBuffer> {
84 let src = self.as_pixel_slice()?;
85 let mut out = PixelBuffer::new(self.width, self.height, src.descriptor());
86 {
87 let mut dst = out.as_slice_mut();
88 for y in 0..self.height {
89 let n = src.row(y).len().min(dst.row_mut(y).len());
90 dst.row_mut(y)[..n].copy_from_slice(&src.row(y)[..n]);
91 }
92 }
93 Some(out)
94 }
95}
96
97impl core::fmt::Debug for CollectSink {
98 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
99 f.debug_struct("CollectSink")
100 .field("width", &self.width)
101 .field("height", &self.height)
102 .field("descriptor", &self.descriptor)
103 .field("rows_written", &self.rows_written)
104 .field("finished", &self.finished)
105 .finish_non_exhaustive()
106 }
107}
108
109impl DecodeRowSink for CollectSink {
110 fn begin(
111 &mut self,
112 width: u32,
113 height: u32,
114 descriptor: PixelDescriptor,
115 ) -> Result<(), SinkError> {
116 if self.descriptor.is_some() {
117 return Err("CollectSink: begin called twice".into());
118 }
119 let stride = (width as usize)
120 .checked_mul(descriptor.bytes_per_pixel())
121 .ok_or_else(|| SinkError::from("CollectSink: row too large"))?;
122 let total = stride
123 .checked_mul(height as usize)
124 .ok_or_else(|| SinkError::from("CollectSink: image too large"))?;
125 let capacity = total
129 .checked_add(descriptor.min_alignment() - 1)
130 .ok_or_else(|| SinkError::from("CollectSink: aligned image too large"))?;
131 let mut data = Vec::new();
132 data.try_reserve_exact(capacity)?;
133 data.resize(capacity, 0);
134 let buffer = PixelBuffer::from_vec(data, width, height, descriptor)?;
135 let words = usize::try_from(u64::from(height).div_ceil(64))
136 .map_err(|_| SinkError::from("CollectSink: coverage too large"))?;
137 let mut coverage = Vec::new();
138 coverage.try_reserve_exact(words)?;
139 coverage.resize(words, 0);
140 self.covered_rows = coverage;
141 self.data = Some(buffer);
142 self.width = width;
143 self.height = height;
144 self.descriptor = Some(descriptor);
145 Ok(())
146 }
147
148 fn provide_next_buffer(
149 &mut self,
150 y: u32,
151 height: u32,
152 width: u32,
153 descriptor: PixelDescriptor,
154 ) -> Result<PixelSliceMut<'_>, SinkError> {
155 if self.finished {
156 return Err("CollectSink: rows after finish".into());
157 }
158 let Some(desc) = self.descriptor else {
159 return Err("CollectSink: rows before begin".into());
160 };
161 if width != self.width || descriptor != desc {
162 return Err(format!(
163 "CollectSink: strip {width}x{height} {descriptor:?} does not match image {}x{} {desc:?}",
164 self.width, self.height
165 )
166 .into());
167 }
168 y.checked_add(height)
169 .filter(|end| *end <= self.height)
170 .ok_or_else(|| {
171 SinkError::from(format!(
172 "CollectSink: rows {y}..{} exceed height {}",
173 y as u64 + height as u64,
174 self.height
175 ))
176 })?;
177 for row in y..y + height {
178 let word = &mut self.covered_rows[(row / 64) as usize];
179 let bit = 1u64 << (row % 64);
180 if *word & bit == 0 {
181 self.unique_rows += 1;
182 *word |= bit;
183 }
184 }
185 self.rows_written += height as u64;
186 Ok(self
187 .data
188 .as_mut()
189 .expect("begin installed pixel storage")
190 .rows_mut(y, height))
191 }
192
193 fn finish(&mut self) -> Result<(), SinkError> {
194 if self.descriptor.is_none() {
195 return Err("CollectSink: finish before begin".into());
196 }
197 if self.unique_rows != self.height {
198 return Err("CollectSink: finish before every row was supplied".into());
199 }
200 self.finished = true;
201 Ok(())
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 #[test]
210 fn finish_without_begin_is_not_completion() {
211 let mut sink = CollectSink::new();
212 assert!(sink.finish().is_err());
213 assert!(!sink.finished());
214 assert!(sink.into_buffer().is_none());
215 }
216
217 #[test]
218 fn completed_pixels_cannot_be_reopened_for_writing() {
219 let mut sink = CollectSink::new();
220 sink.begin(1, 1, PixelDescriptor::GRAY8_SRGB).unwrap();
221 sink.provide_next_buffer(0, 1, 1, PixelDescriptor::GRAY8_SRGB)
222 .unwrap();
223 sink.finish().unwrap();
224 assert!(
225 sink.provide_next_buffer(0, 1, 1, PixelDescriptor::GRAY8_SRGB)
226 .is_err()
227 );
228 }
229
230 #[test]
231 fn completion_requires_unique_coverage_and_closes_writes() {
232 let mut sink = CollectSink::new();
233 sink.begin(1, 65, PixelDescriptor::GRAY8_SRGB).unwrap();
234 for _ in 0..65 {
235 sink.provide_next_buffer(0, 1, 1, PixelDescriptor::GRAY8_SRGB)
236 .unwrap();
237 }
238 assert_eq!(sink.rows_written(), 65);
239 assert!(sink.finish().is_err());
240 assert!(!sink.finished());
241 sink.provide_next_buffer(32, 33, 1, PixelDescriptor::GRAY8_SRGB)
243 .unwrap();
244 sink.provide_next_buffer(1, 32, 1, PixelDescriptor::GRAY8_SRGB)
245 .unwrap();
246 sink.finish().unwrap();
247 assert!(
248 sink.provide_next_buffer(0, 1, 1, PixelDescriptor::GRAY8_SRGB)
249 .is_err()
250 );
251 assert!(sink.into_buffer().is_some());
252 }
253
254 #[test]
255 fn finished_storage_moves_without_copy_for_integer_and_float_rows() {
256 for descriptor in [
257 PixelDescriptor::RGB8_SRGB,
258 PixelDescriptor::RGB16_SRGB,
259 PixelDescriptor::RGBAF32_LINEAR,
260 ] {
261 let mut sink = CollectSink::new();
262 sink.begin(3, 2, descriptor).unwrap();
263 let original = sink.bytes().as_ptr();
264 for y in 0..2 {
265 let mut rows = sink.provide_next_buffer(y, 1, 3, descriptor).unwrap();
266 rows.row_mut(0).fill(37 + y as u8);
267 }
268 sink.finish().unwrap();
269 let buffer = sink.into_buffer().unwrap();
270 assert_eq!(buffer.as_slice().as_strided_bytes().as_ptr(), original);
271 assert_eq!(buffer.descriptor(), descriptor);
272 for y in 0..2 {
273 assert!(buffer.as_slice().row(y).iter().all(|b| *b == 37 + y as u8));
274 }
275 }
276 }
277
278 #[test]
279 fn unfinished_collectors_do_not_yield_owned_completion() {
280 assert!(CollectSink::new().into_buffer().is_none());
281 let mut sink = CollectSink::new();
282 sink.begin(2, 1, PixelDescriptor::GRAY8_SRGB).unwrap();
283 sink.provide_next_buffer(0, 1, 2, PixelDescriptor::GRAY8_SRGB)
284 .unwrap()
285 .row_mut(0)
286 .copy_from_slice(&[17, 23]);
287 assert!(sink.into_buffer().is_none());
288 }
289
290 #[test]
291 fn collect_sink_refuses_unrepresentable_capacity_without_panic_or_state_change() {
292 let mut sink = CollectSink::new();
293 assert!(
296 sink.begin(u32::MAX, u32::MAX, PixelDescriptor::GRAY8_SRGB)
297 .is_err()
298 );
299 assert_eq!(sink.descriptor(), None);
300 assert_eq!((sink.width(), sink.height()), (0, 0));
301 assert!(sink.bytes().is_empty());
302 assert!(!sink.finished());
303 sink.begin(3, 2, PixelDescriptor::GRAY8_SRGB).unwrap();
304 for y in 0..2 {
305 sink.provide_next_buffer(y, 1, 3, PixelDescriptor::GRAY8_SRGB)
306 .unwrap()
307 .row_mut(0)
308 .copy_from_slice(&[1 + y as u8, 3, 5]);
309 }
310 sink.finish().unwrap();
311 assert_eq!(sink.bytes(), &[1, 3, 5, 2, 3, 5]);
312 assert_eq!(sink.rows_written(), 2);
313 assert!(sink.finished());
314 }
315
316 #[test]
317 fn collect_sink_refuses_geometry_overflow() {
318 let mut sink = CollectSink::new();
319 assert!(
320 sink.begin(u32::MAX, u32::MAX, PixelDescriptor::RGBAF32_LINEAR)
321 .is_err()
322 );
323 assert!(sink.bytes().is_empty());
324 assert_eq!(sink.descriptor(), None);
325 }
326}