Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented incremental png loading #860

Merged
merged 2 commits into from
Feb 27, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 89 additions & 4 deletions src/png.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,87 @@ extern crate png;

use self::png::HasParameters;

use std::io::{self, Cursor, Read, Write};
use std;
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<R: Read> {
reader: png::Reader<R>,
buffer: Vec<u8>,
index: usize,
}

impl<R: Read> PNGReader<R> {
fn new(mut reader: png::Reader<R>) -> ImageResult<PNGReader<R>> {
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<R: Read> Read for PNGReader<R> {
fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
// 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<u8>) -> io::Result<usize> {
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<R: Read> {
colortype: ColorType,
Expand All @@ -24,7 +100,10 @@ pub struct PNGDecoder<R: Read> {
impl<R: Read> PNGDecoder<R> {
/// Creates a new decoder that decodes from the stream ```r```
pub fn new(r: R) -> ImageResult<PNGDecoder<R>> {
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();

Expand All @@ -33,7 +112,7 @@ impl<R: Read> PNGDecoder<R> {
}

impl<R: Read> ImageDecoder for PNGDecoder<R> {
type Reader = Cursor<Vec<u8>>;
type Reader = PNGReader<R>;

fn dimensions(&self) -> (u64, u64) {
let (w, h) = self.reader.info().size();
Expand All @@ -45,14 +124,20 @@ impl<R: Read> ImageDecoder for PNGDecoder<R> {
}

fn into_reader(self) -> ImageResult<Self::Reader> {
Ok(Cursor::new(self.read_image()?))
PNGReader::new(self.reader)
}

fn read_image(mut self) -> ImageResult<Vec<u8>> {
// 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
Expand Down