diff --git a/benches/decoder.rs b/benches/decoder.rs index c47089d7..53413c84 100644 --- a/benches/decoder.rs +++ b/benches/decoder.rs @@ -42,11 +42,11 @@ fn load_all(c: &mut Criterion) { bench_noncompressed_png(&mut g, 12288, 0x7fffffff); // 576 MB g.finish(); - // Incremental decoding via `next_row` + // Incremental decoding via `read_row` let mut g = c.benchmark_group("row-by-row"); let mut data = Vec::new(); test_utils::write_noncompressed_png(&mut data, 128, 4096); - bench_next_row(&mut g, data, "128x128-4k-idat"); + bench_read_row(&mut g, data, "128x128-4k-idat"); g.finish(); } @@ -78,8 +78,8 @@ fn bench_file(g: &mut BenchmarkGroup, data: Vec, name: String) { }); } -/// This benchmarks decoding via a sequence of `Reader::next_row` calls. -fn bench_next_row(g: &mut BenchmarkGroup, data: Vec, name: &str) { +/// This benchmarks decoding via a sequence of `Reader::read_row` calls. +fn bench_read_row(g: &mut BenchmarkGroup, data: Vec, name: &str) { let reader = create_reader(data.as_slice()); let mut image = vec![0; reader.output_buffer_size()]; let bytes_per_row = reader.output_line_size(reader.info().width); @@ -89,8 +89,7 @@ fn bench_next_row(g: &mut BenchmarkGroup, data: Vec, name: &str) { let mut reader = create_reader(data.as_slice()); for output_row in image.chunks_exact_mut(bytes_per_row) { - let decoded_row = reader.next_row().unwrap().unwrap(); - output_row.copy_from_slice(decoded_row.data()); + reader.read_row(output_row).unwrap().unwrap(); } }) }); diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 2a189649..57c49462 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -586,38 +586,68 @@ impl Reader { Ok(output_info) } - /// Returns the next processed row of the image + /// Returns the next processed row of the image. + /// + /// See also [`Reader.read_row`], which reads into a caller-provided buffer. pub fn next_row(&mut self) -> Result, DecodingError> { self.next_interlaced_row() .map(|v| v.map(|v| Row { data: v.data })) } - /// Returns the next processed row of the image - pub fn next_interlaced_row(&mut self) -> Result, DecodingError> { - let (rowlen, interlace) = match self.next_pass() { - Some((rowlen, interlace)) => (rowlen, interlace), - None => return Ok(None), - }; - + fn output_line_size_for_interlace_info(&self, interlace: &InterlaceInfo) -> usize { let width = if let InterlaceInfo::Adam7 { width, .. } = interlace { - width + *width } else { self.subframe.width }; - let output_line_size = self.output_line_size(width); + self.output_line_size(width) + } + /// Returns the next processed row of the image. + /// + /// See also [`Reader.read_row`], which reads into a caller-provided buffer. + pub fn next_interlaced_row(&mut self) -> Result, DecodingError> { // TODO: change the interface of `next_interlaced_row` to take an output buffer instead of // making us return a reference to a buffer that we own. let mut output_buffer = mem::take(&mut self.scratch_buffer); - output_buffer.resize(output_line_size, 0u8); - let ret = self.next_interlaced_row_impl(rowlen, &mut output_buffer); + output_buffer.resize(self.output_line_size(self.info().width), 0u8); + let result = self.read_row(&mut output_buffer); self.scratch_buffer = output_buffer; - ret?; - Ok(Some(InterlacedRow { - data: &self.scratch_buffer[..output_line_size], - interlace, - })) + result.map(move |option| { + option.map(move |interlace| { + let output_line_size = self.output_line_size_for_interlace_info(&interlace); + InterlacedRow { + data: &self.scratch_buffer[..output_line_size], + interlace, + } + }) + }) + } + + /// Reads the next row of the image into the provided `output_buffer`. + /// `Ok(None)` will be returned if the current image frame has no more rows. + /// + /// `output_buffer` needs to be long enough to accommodate [`Reader.output_line_size`] for + /// [`Info.width`] (initial interlaced rows may need less than that). + /// + /// See also [`Reader.next_row`] and [`Reader.next_interlaced_row`], which read into a + /// `Reader`-owned buffer. + pub fn read_row( + &mut self, + output_buffer: &mut [u8], + ) -> Result, DecodingError> { + let (rowlen, interlace) = match self.next_pass() { + Some((rowlen, interlace)) => (rowlen, interlace), + None => return Ok(None), + }; + + let output_line_size = self.output_line_size_for_interlace_info(&interlace); + let output_buffer = &mut output_buffer[..output_line_size]; + + self.next_interlaced_row_impl(rowlen, output_buffer)?; + + Ok(Some(interlace)) } /// Read the rest of the image and chunks and finish up, including text chunks or others