Wrong image data when using `png` crate streaming decoder

When I use the Streaming decoder it completes with no errors but it gives wrong image data. It is especially the case with white images.

Here is my code:

use std::io::{Cursor, Read};

use image::{ImageFormat, Rgb, RgbImage};
use png::StreamingDecoder;

fn main() {
    // let image = RgbImage::new(10, 10);
    let image = RgbImage::from_pixel(10, 10, Rgb([255, 255, 255])); // even worse with white

    let mut image_encoded = Vec::new();

    image
        .write_to(&mut Cursor::new(&mut image_encoded), ImageFormat::Png)
        .unwrap();

    let mut decoder = StreamingDecoder::new();
    decoder.set_ignore_adler32(false);

    let mut image_data = Vec::new();

    let mut buf = [0u8; 16];

    let mut reader = image_encoded.as_slice();

    while let Ok(bytes_read) = reader.read(&mut buf) {
        if bytes_read == 0 {
            break;
        }

        let mut bytes = &buf[..bytes_read];

        while !bytes.is_empty() {
            let bytes_consumed = decoder.update(bytes, &mut image_data).unwrap().0;

            bytes = &bytes[bytes_consumed..];
        }
    }

    assert_eq!(image.into_vec(), image_data);
}

It panics like this:

thread 'main' panicked at src/main.rs:39:5:
assertion `left == right` failed
  left: [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]
 right: [4, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I feel like I'm doing something wrong but not sure where.

The image is probably not being serialized into specifically 8-bit truecolor, which you are comparing it to. Try reading the png::Decoded returned from StreamingDecoder::update. Check at least the Header variant (the order is (width, height, bit_depth, color_type, interlaced)). You might also want to look for reading a chunk of type png::chunk::PLTE, or the PNG’s palette.

Also, StreamingDecoder is kind of a misnomer. That name implies that png::Decoder is not streaming, but it is.
Decoder wraps a generic io::Read, like a file or Vec. StreamingDecoder is a low-level interface that doesn’t need to be used often. You can read an image like so:

use std::io::Read;
use png::{Decoder, Info};

let mut data_reader: impl Read = ...
let decoder = Decoder::new(data_reader);
let mut reader = decoder.read_info()?;
let info = reader.info();
let buffer_size = (info.width as usize) * (info.height as usize);
let mut buf = vec![0; buffer_size];
reader.next_frame(&mut buf)?;
// Interpret the image data in buf according to these:
let bit_depth = info.bit_depth;
let color_type = info.color_type;
let palette = info.palette;
1 Like

Thanks for the help.

The Header returns the correct information. Unfortunately I could not find a PLTE chunk being decoded. All I can find is IHDR, IDAT and IEND.

As for Decoder the example you gave needs to decode the entire frame in the buffer. That would become a problem for large images. I don't know exactly how StreamingDecoder does it but I assume it's different from being able to buffer very small amounts of data. I found next_row() in the api though so if nothing works I guess that would be an option.