png_achunk/
decode.rs

1use std::io::{BufReader, Read, Seek};
2use std::path::Path;
3
4use crate::chunk::{self, Chunk};
5
6#[derive(Debug, thiserror::Error)]
7pub enum DecodingError {
8    #[error("I/O error: {0}")]
9    Io(#[from] std::io::Error),
10    #[error("Not a PNG")]
11    NotPng,
12    #[error("Invalid checksum")]
13    InvalidChecksum,
14    #[error("Failed to decode image")]
15    Image(#[from] image::ImageError),
16    #[error("Invalid chunk name {0:?}")]
17    Chunk(#[from] chunk::Error),
18    #[error("Invalid filename")]
19    Filename(#[from] std::str::Utf8Error),
20    #[error("Chunk not found")]
21    ChunkNotFound,
22}
23
24/// PNG decoding, including ancillary chunks.
25pub struct Decoder<R: Read + Seek> {
26    reader: R,
27}
28impl<R: Read + Seek> Decoder<R> {
29    pub fn from_reader(reader: R) -> Self {
30        Self { reader }
31    }
32}
33impl Decoder<BufReader<std::fs::File>> {
34    /// Create a decoder from a file, using buffered reading.
35    pub fn from_file(filename: impl AsRef<Path>) -> Result<Self, std::io::Error> {
36        let reader = std::fs::File::open(filename)?;
37        let reader = std::io::BufReader::new(reader);
38        Ok(Self::from_reader(reader))
39    }
40}
41impl<R: Read + Seek> Decoder<R> {
42    /// Decode the non-critical chunks only, without reading the full image if they are placed before
43    /// the IDAT section.
44    pub fn decode_ancillary_chunks(&mut self) -> Result<Vec<Chunk>, DecodingError> {
45        self.reader.seek(std::io::SeekFrom::Start(0))?;
46        // Decode chunks
47        let mut header = [0; 8];
48        self.reader.read_exact(&mut header)?;
49        if &header[1..4] != b"PNG" {
50            return Err(DecodingError::NotPng);
51        }
52        let mut chunks = vec![];
53        loop {
54            let c = Chunk::from_reader(&mut self.reader)?;
55            if c.chunk_type == png::chunk::IEND || c.chunk_type == png::chunk::IDAT {
56                break;
57            }
58            if !c.chunk_type.is_critical() {
59                chunks.push(c);
60            }
61        }
62        Ok(chunks)
63    }
64    /// Decode image and ancillary chunks.
65    pub fn decode_all(&mut self) -> Result<(image::DynamicImage, Vec<Chunk>), DecodingError> {
66        let chunks = self.decode_ancillary_chunks()?;
67        // Decode image
68        // TODO: Avoid re-reading the ancillary chunks
69        self.reader.seek(std::io::SeekFrom::Start(0))?;
70        let image = image::DynamicImage::from_decoder(image::codecs::png::PngDecoder::new(
71            &mut self.reader,
72        )?)?;
73        Ok((image, chunks))
74    }
75}