From eaeefa88a3f0508a9e7bd3b6e81db8bb034814c1 Mon Sep 17 00:00:00 2001 From: Birk Tjelmeland Date: Fri, 1 Feb 2019 14:56:25 +0100 Subject: [PATCH 1/2] Implemented incremental png loading --- src/png.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 3 deletions(-) diff --git a/src/png.rs b/src/png.rs index cc0111c7c7..406c72567b 100644 --- a/src/png.rs +++ b/src/png.rs @@ -10,11 +10,86 @@ extern crate png; use self::png::HasParameters; -use std::io::{self, Cursor, Read, Write}; +use std::io::{self, Read, Write}; use color::ColorType; use image::{ImageDecoder, ImageError, ImageResult}; +/// PNG Reader +/// +/// This reader will try to read the png one row at a time, +/// however for interlaced png files this is not posible and +/// these are therefore readed at once. +pub struct PNGReader { + reader: png::Reader, + buffer: Vec, + index: usize, +} + +impl PNGReader { + fn new(mut reader: png::Reader) -> ImageResult> { + let len = reader.output_buffer_size(); + // Since interlaced images do not come in + // scanline order it is almost impossible to + // read them in a streaming fashion, however + // this shouldn't be a too big of a problem + // as most interlaced images should fit in memory. + let buffer = if reader.info().interlaced { + let mut buffer = vec![0; len]; + reader.next_frame(&mut buffer)?; + buffer + } else { + Vec::new() + }; + + Ok(PNGReader { + reader, + buffer, + index: 0, + }) + } +} + +impl Read for PNGReader { + fn read(&mut self, mut buf: &mut [u8]) -> io::Result { + // io::Write::write for slice cannot fail + let readed = buf.write(&self.buffer[self.index..]).unwrap(); + + let mut bytes = readed; + self.index += readed; + + while self.index + 1 >= self.buffer.len() { + match self.reader.next_row()? { + Some(row) => { + // Faster to copy directly to external buffer + let readed = buf.write(row).unwrap(); + bytes += readed; + + self.buffer = (&row[readed..]).to_owned(); + self.index = 0; + } + None => return Ok(bytes) + } + } + + Ok(bytes) + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + let mut bytes = self.buffer.len(); + buf.extend_from_slice(&self.buffer); + self.buffer = Vec::new(); + self.index = 0; + + while let Some(row) = self.reader.next_row()? { + buf.extend_from_slice(row); + bytes += row.len(); + } + + Ok(bytes) + } +} + /// PNG decoder pub struct PNGDecoder { colortype: ColorType, @@ -33,7 +108,7 @@ impl PNGDecoder { } impl ImageDecoder for PNGDecoder { - type Reader = Cursor>; + type Reader = PNGReader; fn dimensions(&self) -> (u64, u64) { let (w, h) = self.reader.info().size(); @@ -45,14 +120,20 @@ impl ImageDecoder for PNGDecoder { } fn into_reader(self) -> ImageResult { - Ok(Cursor::new(self.read_image()?)) + PNGReader::new(self.reader) } fn read_image(mut self) -> ImageResult> { + // This should be slightly faster than the default implementation let mut data = vec![0; self.reader.output_buffer_size()]; self.reader.next_frame(&mut data)?; Ok(data) } + + fn scanline_bytes(&self) -> u64 { + let width = self.reader.info().width; + self.reader.output_line_size(width) as u64 + } } /// PNG encoder From 2173f03c5e8e4151b65dd12470b9d771a433e5d8 Mon Sep 17 00:00:00 2001 From: Birk Tjelmeland Date: Mon, 4 Feb 2019 20:12:37 +0100 Subject: [PATCH 2/2] Update png limits --- src/png.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/png.rs b/src/png.rs index 406c72567b..94c4874614 100644 --- a/src/png.rs +++ b/src/png.rs @@ -10,6 +10,7 @@ extern crate png; use self::png::HasParameters; +use std; use std::io::{self, Read, Write}; use color::ColorType; @@ -99,7 +100,10 @@ pub struct PNGDecoder { impl PNGDecoder { /// Creates a new decoder that decodes from the stream ```r``` pub fn new(r: R) -> ImageResult> { - let decoder = png::Decoder::new(r); + let limits = png::Limits { + pixels: std::u64::MAX, + }; + let decoder = png::Decoder::new_with_limits(r, limits); let (_, mut reader) = decoder.read_info()?; let colortype = reader.output_color_type().into();