Coverage Report

Created: 2025-07-11 07:25

/src/image/src/codecs/png.rs
Line
Count
Source (jump to first uncovered line)
1
//! Decoding and Encoding of PNG Images
2
//!
3
//! PNG (Portable Network Graphics) is an image format that supports lossless compression.
4
//!
5
//! # Related Links
6
//! * <http://www.w3.org/TR/PNG/> - The PNG Specification
7
8
use std::borrow::Cow;
9
use std::fmt;
10
use std::io::{BufRead, Seek, Write};
11
12
use png::{BlendOp, DisposeOp};
13
14
use crate::animation::{Delay, Frame, Frames, Ratio};
15
use crate::color::{Blend, ColorType, ExtendedColorType};
16
use crate::error::{
17
    DecodingError, EncodingError, ImageError, ImageResult, LimitError, LimitErrorKind,
18
    ParameterError, ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
19
};
20
use crate::utils::vec_try_with_capacity;
21
use crate::{
22
    AnimationDecoder, DynamicImage, GenericImage, GenericImageView, ImageBuffer, ImageDecoder,
23
    ImageEncoder, ImageFormat, Limits, Luma, LumaA, Rgb, Rgba, RgbaImage,
24
};
25
26
// http://www.w3.org/TR/PNG-Structure.html
27
// The first eight bytes of a PNG file always contain the following (decimal) values:
28
pub(crate) const PNG_SIGNATURE: [u8; 8] = [137, 80, 78, 71, 13, 10, 26, 10];
29
30
/// PNG decoder
31
pub struct PngDecoder<R: BufRead + Seek> {
32
    color_type: ColorType,
33
    reader: png::Reader<R>,
34
    limits: Limits,
35
}
36
37
impl<R: BufRead + Seek> PngDecoder<R> {
38
    /// Creates a new decoder that decodes from the stream ```r```
39
4.61k
    pub fn new(r: R) -> ImageResult<PngDecoder<R>> {
40
4.61k
        Self::with_limits(r, Limits::no_limits())
41
4.61k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::new
Unexecuted instantiation: <image::codecs::png::PngDecoder<_>>::new
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::new
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::new
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::new
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::new
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::new
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::new
Line
Count
Source
39
4.61k
    pub fn new(r: R) -> ImageResult<PngDecoder<R>> {
40
4.61k
        Self::with_limits(r, Limits::no_limits())
41
4.61k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::new
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::new
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::new
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::new
42
43
    /// Creates a new decoder that decodes from the stream ```r``` with the given limits.
44
7.53k
    pub fn with_limits(r: R, limits: Limits) -> ImageResult<PngDecoder<R>> {
45
7.53k
        limits.check_support(&crate::LimitSupport::default())?;
46
47
7.53k
        let max_bytes = usize::try_from(limits.max_alloc.unwrap_or(u64::MAX)).unwrap_or(usize::MAX);
48
7.53k
        let mut decoder = png::Decoder::new_with_limits(r, png::Limits { bytes: max_bytes });
49
7.53k
        decoder.set_ignore_text_chunk(true);
50
51
7.53k
        let info = decoder.read_header_info().map_err(ImageError::from_png)?;
52
7.18k
        limits.check_dimensions(info.width, info.height)?;
53
54
        // By default the PNG decoder will scale 16 bpc to 8 bpc, so custom
55
        // transformations must be set. EXPAND preserves the default behavior
56
        // expanding bpc < 8 to 8 bpc.
57
7.18k
        decoder.set_transformations(png::Transformations::EXPAND);
58
7.18k
        let reader = decoder.read_info().map_err(ImageError::from_png)?;
59
4.34k
        let (color_type, bits) = reader.output_color_type();
60
4.34k
        let color_type = match (color_type, bits) {
61
653
            (png::ColorType::Grayscale, png::BitDepth::Eight) => ColorType::L8,
62
198
            (png::ColorType::Grayscale, png::BitDepth::Sixteen) => ColorType::L16,
63
201
            (png::ColorType::GrayscaleAlpha, png::BitDepth::Eight) => ColorType::La8,
64
74
            (png::ColorType::GrayscaleAlpha, png::BitDepth::Sixteen) => ColorType::La16,
65
477
            (png::ColorType::Rgb, png::BitDepth::Eight) => ColorType::Rgb8,
66
151
            (png::ColorType::Rgb, png::BitDepth::Sixteen) => ColorType::Rgb16,
67
2.51k
            (png::ColorType::Rgba, png::BitDepth::Eight) => ColorType::Rgba8,
68
79
            (png::ColorType::Rgba, png::BitDepth::Sixteen) => ColorType::Rgba16,
69
70
            (png::ColorType::Grayscale, png::BitDepth::One) => {
71
0
                return Err(unsupported_color(ExtendedColorType::L1))
72
            }
73
            (png::ColorType::GrayscaleAlpha, png::BitDepth::One) => {
74
0
                return Err(unsupported_color(ExtendedColorType::La1))
75
            }
76
            (png::ColorType::Rgb, png::BitDepth::One) => {
77
0
                return Err(unsupported_color(ExtendedColorType::Rgb1))
78
            }
79
            (png::ColorType::Rgba, png::BitDepth::One) => {
80
0
                return Err(unsupported_color(ExtendedColorType::Rgba1))
81
            }
82
83
            (png::ColorType::Grayscale, png::BitDepth::Two) => {
84
0
                return Err(unsupported_color(ExtendedColorType::L2))
85
            }
86
            (png::ColorType::GrayscaleAlpha, png::BitDepth::Two) => {
87
0
                return Err(unsupported_color(ExtendedColorType::La2))
88
            }
89
            (png::ColorType::Rgb, png::BitDepth::Two) => {
90
0
                return Err(unsupported_color(ExtendedColorType::Rgb2))
91
            }
92
            (png::ColorType::Rgba, png::BitDepth::Two) => {
93
0
                return Err(unsupported_color(ExtendedColorType::Rgba2))
94
            }
95
96
            (png::ColorType::Grayscale, png::BitDepth::Four) => {
97
0
                return Err(unsupported_color(ExtendedColorType::L4))
98
            }
99
            (png::ColorType::GrayscaleAlpha, png::BitDepth::Four) => {
100
0
                return Err(unsupported_color(ExtendedColorType::La4))
101
            }
102
            (png::ColorType::Rgb, png::BitDepth::Four) => {
103
0
                return Err(unsupported_color(ExtendedColorType::Rgb4))
104
            }
105
            (png::ColorType::Rgba, png::BitDepth::Four) => {
106
0
                return Err(unsupported_color(ExtendedColorType::Rgba4))
107
            }
108
109
0
            (png::ColorType::Indexed, bits) => {
110
0
                return Err(unsupported_color(ExtendedColorType::Unknown(bits as u8)))
111
            }
112
        };
113
114
4.34k
        Ok(PngDecoder {
115
4.34k
            color_type,
116
4.34k
            reader,
117
4.34k
            limits,
118
4.34k
        })
119
7.53k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::with_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<_>>::with_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::with_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::with_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::with_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::with_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::with_limits
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::with_limits
Line
Count
Source
44
4.61k
    pub fn with_limits(r: R, limits: Limits) -> ImageResult<PngDecoder<R>> {
45
4.61k
        limits.check_support(&crate::LimitSupport::default())?;
46
47
4.61k
        let max_bytes = usize::try_from(limits.max_alloc.unwrap_or(u64::MAX)).unwrap_or(usize::MAX);
48
4.61k
        let mut decoder = png::Decoder::new_with_limits(r, png::Limits { bytes: max_bytes });
49
4.61k
        decoder.set_ignore_text_chunk(true);
50
51
4.61k
        let info = decoder.read_header_info().map_err(ImageError::from_png)?;
52
4.32k
        limits.check_dimensions(info.width, info.height)?;
53
54
        // By default the PNG decoder will scale 16 bpc to 8 bpc, so custom
55
        // transformations must be set. EXPAND preserves the default behavior
56
        // expanding bpc < 8 to 8 bpc.
57
4.32k
        decoder.set_transformations(png::Transformations::EXPAND);
58
4.32k
        let reader = decoder.read_info().map_err(ImageError::from_png)?;
59
2.40k
        let (color_type, bits) = reader.output_color_type();
60
2.40k
        let color_type = match (color_type, bits) {
61
63
            (png::ColorType::Grayscale, png::BitDepth::Eight) => ColorType::L8,
62
28
            (png::ColorType::Grayscale, png::BitDepth::Sixteen) => ColorType::L16,
63
4
            (png::ColorType::GrayscaleAlpha, png::BitDepth::Eight) => ColorType::La8,
64
6
            (png::ColorType::GrayscaleAlpha, png::BitDepth::Sixteen) => ColorType::La16,
65
2
            (png::ColorType::Rgb, png::BitDepth::Eight) => ColorType::Rgb8,
66
7
            (png::ColorType::Rgb, png::BitDepth::Sixteen) => ColorType::Rgb16,
67
2.28k
            (png::ColorType::Rgba, png::BitDepth::Eight) => ColorType::Rgba8,
68
15
            (png::ColorType::Rgba, png::BitDepth::Sixteen) => ColorType::Rgba16,
69
70
            (png::ColorType::Grayscale, png::BitDepth::One) => {
71
0
                return Err(unsupported_color(ExtendedColorType::L1))
72
            }
73
            (png::ColorType::GrayscaleAlpha, png::BitDepth::One) => {
74
0
                return Err(unsupported_color(ExtendedColorType::La1))
75
            }
76
            (png::ColorType::Rgb, png::BitDepth::One) => {
77
0
                return Err(unsupported_color(ExtendedColorType::Rgb1))
78
            }
79
            (png::ColorType::Rgba, png::BitDepth::One) => {
80
0
                return Err(unsupported_color(ExtendedColorType::Rgba1))
81
            }
82
83
            (png::ColorType::Grayscale, png::BitDepth::Two) => {
84
0
                return Err(unsupported_color(ExtendedColorType::L2))
85
            }
86
            (png::ColorType::GrayscaleAlpha, png::BitDepth::Two) => {
87
0
                return Err(unsupported_color(ExtendedColorType::La2))
88
            }
89
            (png::ColorType::Rgb, png::BitDepth::Two) => {
90
0
                return Err(unsupported_color(ExtendedColorType::Rgb2))
91
            }
92
            (png::ColorType::Rgba, png::BitDepth::Two) => {
93
0
                return Err(unsupported_color(ExtendedColorType::Rgba2))
94
            }
95
96
            (png::ColorType::Grayscale, png::BitDepth::Four) => {
97
0
                return Err(unsupported_color(ExtendedColorType::L4))
98
            }
99
            (png::ColorType::GrayscaleAlpha, png::BitDepth::Four) => {
100
0
                return Err(unsupported_color(ExtendedColorType::La4))
101
            }
102
            (png::ColorType::Rgb, png::BitDepth::Four) => {
103
0
                return Err(unsupported_color(ExtendedColorType::Rgb4))
104
            }
105
            (png::ColorType::Rgba, png::BitDepth::Four) => {
106
0
                return Err(unsupported_color(ExtendedColorType::Rgba4))
107
            }
108
109
0
            (png::ColorType::Indexed, bits) => {
110
0
                return Err(unsupported_color(ExtendedColorType::Unknown(bits as u8)))
111
            }
112
        };
113
114
2.40k
        Ok(PngDecoder {
115
2.40k
            color_type,
116
2.40k
            reader,
117
2.40k
            limits,
118
2.40k
        })
119
4.61k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::with_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::with_limits
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::with_limits
Line
Count
Source
44
2.92k
    pub fn with_limits(r: R, limits: Limits) -> ImageResult<PngDecoder<R>> {
45
2.92k
        limits.check_support(&crate::LimitSupport::default())?;
46
47
2.92k
        let max_bytes = usize::try_from(limits.max_alloc.unwrap_or(u64::MAX)).unwrap_or(usize::MAX);
48
2.92k
        let mut decoder = png::Decoder::new_with_limits(r, png::Limits { bytes: max_bytes });
49
2.92k
        decoder.set_ignore_text_chunk(true);
50
51
2.92k
        let info = decoder.read_header_info().map_err(ImageError::from_png)?;
52
2.86k
        limits.check_dimensions(info.width, info.height)?;
53
54
        // By default the PNG decoder will scale 16 bpc to 8 bpc, so custom
55
        // transformations must be set. EXPAND preserves the default behavior
56
        // expanding bpc < 8 to 8 bpc.
57
2.86k
        decoder.set_transformations(png::Transformations::EXPAND);
58
2.86k
        let reader = decoder.read_info().map_err(ImageError::from_png)?;
59
1.93k
        let (color_type, bits) = reader.output_color_type();
60
1.93k
        let color_type = match (color_type, bits) {
61
590
            (png::ColorType::Grayscale, png::BitDepth::Eight) => ColorType::L8,
62
170
            (png::ColorType::Grayscale, png::BitDepth::Sixteen) => ColorType::L16,
63
197
            (png::ColorType::GrayscaleAlpha, png::BitDepth::Eight) => ColorType::La8,
64
68
            (png::ColorType::GrayscaleAlpha, png::BitDepth::Sixteen) => ColorType::La16,
65
475
            (png::ColorType::Rgb, png::BitDepth::Eight) => ColorType::Rgb8,
66
144
            (png::ColorType::Rgb, png::BitDepth::Sixteen) => ColorType::Rgb16,
67
226
            (png::ColorType::Rgba, png::BitDepth::Eight) => ColorType::Rgba8,
68
64
            (png::ColorType::Rgba, png::BitDepth::Sixteen) => ColorType::Rgba16,
69
70
            (png::ColorType::Grayscale, png::BitDepth::One) => {
71
0
                return Err(unsupported_color(ExtendedColorType::L1))
72
            }
73
            (png::ColorType::GrayscaleAlpha, png::BitDepth::One) => {
74
0
                return Err(unsupported_color(ExtendedColorType::La1))
75
            }
76
            (png::ColorType::Rgb, png::BitDepth::One) => {
77
0
                return Err(unsupported_color(ExtendedColorType::Rgb1))
78
            }
79
            (png::ColorType::Rgba, png::BitDepth::One) => {
80
0
                return Err(unsupported_color(ExtendedColorType::Rgba1))
81
            }
82
83
            (png::ColorType::Grayscale, png::BitDepth::Two) => {
84
0
                return Err(unsupported_color(ExtendedColorType::L2))
85
            }
86
            (png::ColorType::GrayscaleAlpha, png::BitDepth::Two) => {
87
0
                return Err(unsupported_color(ExtendedColorType::La2))
88
            }
89
            (png::ColorType::Rgb, png::BitDepth::Two) => {
90
0
                return Err(unsupported_color(ExtendedColorType::Rgb2))
91
            }
92
            (png::ColorType::Rgba, png::BitDepth::Two) => {
93
0
                return Err(unsupported_color(ExtendedColorType::Rgba2))
94
            }
95
96
            (png::ColorType::Grayscale, png::BitDepth::Four) => {
97
0
                return Err(unsupported_color(ExtendedColorType::L4))
98
            }
99
            (png::ColorType::GrayscaleAlpha, png::BitDepth::Four) => {
100
0
                return Err(unsupported_color(ExtendedColorType::La4))
101
            }
102
            (png::ColorType::Rgb, png::BitDepth::Four) => {
103
0
                return Err(unsupported_color(ExtendedColorType::Rgb4))
104
            }
105
            (png::ColorType::Rgba, png::BitDepth::Four) => {
106
0
                return Err(unsupported_color(ExtendedColorType::Rgba4))
107
            }
108
109
0
            (png::ColorType::Indexed, bits) => {
110
0
                return Err(unsupported_color(ExtendedColorType::Unknown(bits as u8)))
111
            }
112
        };
113
114
1.93k
        Ok(PngDecoder {
115
1.93k
            color_type,
116
1.93k
            reader,
117
1.93k
            limits,
118
1.93k
        })
119
2.92k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>>>::with_limits
120
121
    /// Returns the gamma value of the image or None if no gamma value is indicated.
122
    ///
123
    /// If an sRGB chunk is present this method returns a gamma value of 0.45455 and ignores the
124
    /// value in the gAMA chunk. This is the recommended behavior according to the PNG standard:
125
    ///
126
    /// > When the sRGB chunk is present, [...] decoders that recognize the sRGB chunk but are not
127
    /// > capable of colour management are recommended to ignore the gAMA and cHRM chunks, and use
128
    /// > the values given above as if they had appeared in gAMA and cHRM chunks.
129
0
    pub fn gamma_value(&self) -> ImageResult<Option<f64>> {
130
0
        Ok(self
131
0
            .reader
132
0
            .info()
133
0
            .source_gamma
134
0
            .map(|x| f64::from(x.into_scaled()) / 100_000.0))
135
0
    }
136
137
    /// Turn this into an iterator over the animation frames.
138
    ///
139
    /// Reading the complete animation requires more memory than reading the data from the IDAT
140
    /// frame–multiple frame buffers need to be reserved at the same time. We further do not
141
    /// support compositing 16-bit colors. In any case this would be lossy as the interface of
142
    /// animation decoders does not support 16-bit colors.
143
    ///
144
    /// If something is not supported or a limit is violated then the decoding step that requires
145
    /// them will fail and an error will be returned instead of the frame. No further frames will
146
    /// be returned.
147
0
    pub fn apng(self) -> ImageResult<ApngDecoder<R>> {
148
0
        Ok(ApngDecoder::new(self))
149
0
    }
150
151
    /// Returns if the image contains an animation.
152
    ///
153
    /// Note that the file itself decides if the default image is considered to be part of the
154
    /// animation. When it is not the common interpretation is to use it as a thumbnail.
155
    ///
156
    /// If a non-animated image is converted into an `ApngDecoder` then its iterator is empty.
157
0
    pub fn is_apng(&self) -> ImageResult<bool> {
158
0
        Ok(self.reader.info().animation_control.is_some())
159
0
    }
160
}
161
162
0
fn unsupported_color(ect: ExtendedColorType) -> ImageError {
163
0
    ImageError::Unsupported(UnsupportedError::from_format_and_kind(
164
0
        ImageFormat::Png.into(),
165
0
        UnsupportedErrorKind::Color(ect),
166
0
    ))
167
0
}
168
169
impl<R: BufRead + Seek> ImageDecoder for PngDecoder<R> {
170
23.7k
    fn dimensions(&self) -> (u32, u32) {
171
23.7k
        self.reader.info().size()
172
23.7k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::dimensions
Unexecuted instantiation: <image::codecs::png::PngDecoder<_> as image::io::decoder::ImageDecoder>::dimensions
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::dimensions
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::dimensions
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::dimensions
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::dimensions
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::dimensions
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::dimensions
Line
Count
Source
170
16.1k
    fn dimensions(&self) -> (u32, u32) {
171
16.1k
        self.reader.info().size()
172
16.1k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::dimensions
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::dimensions
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::dimensions
Line
Count
Source
170
7.58k
    fn dimensions(&self) -> (u32, u32) {
171
7.58k
        self.reader.info().size()
172
7.58k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::dimensions
173
174
21.5k
    fn color_type(&self) -> ColorType {
175
21.5k
        self.color_type
176
21.5k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::color_type
Unexecuted instantiation: <image::codecs::png::PngDecoder<_> as image::io::decoder::ImageDecoder>::color_type
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::color_type
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::color_type
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::color_type
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::color_type
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::color_type
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::color_type
Line
Count
Source
174
13.8k
    fn color_type(&self) -> ColorType {
175
13.8k
        self.color_type
176
13.8k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::color_type
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::color_type
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::color_type
Line
Count
Source
174
7.74k
    fn color_type(&self) -> ColorType {
175
7.74k
        self.color_type
176
7.74k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::color_type
177
178
0
    fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
179
0
        Ok(self.reader.info().icc_profile.as_ref().map(|x| x.to_vec()))
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<_> as image::io::decoder::ImageDecoder>::icc_profile::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile::{closure#0}
180
0
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile
Unexecuted instantiation: <image::codecs::png::PngDecoder<_> as image::io::decoder::ImageDecoder>::icc_profile
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::icc_profile
181
182
4.13k
    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
183
        use byteorder_lite::{BigEndian, ByteOrder, NativeEndian};
184
185
4.13k
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
186
4.13k
        self.reader.next_frame(buf).map_err(ImageError::from_png)?;
187
        // PNG images are big endian. For 16 bit per channel and larger types,
188
        // the buffer may need to be reordered to native endianness per the
189
        // contract of `read_image`.
190
        // TODO: assumes equal channel bit depth.
191
92
        let bpc = self.color_type().bytes_per_pixel() / self.color_type().channel_count();
192
92
193
92
        match bpc {
194
70
            1 => (), // No reodering necessary for u8
195
33.1M
            2 => buf.chunks_exact_mut(2).for_each(|c| {
196
33.1M
                let v = BigEndian::read_u16(c);
197
33.1M
                NativeEndian::write_u16(c, v);
198
33.1M
            }),
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<_> as image::io::decoder::ImageDecoder>::read_image::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image::{closure#0}
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image::{closure#0}
Line
Count
Source
195
33.1M
            2 => buf.chunks_exact_mut(2).for_each(|c| {
196
33.1M
                let v = BigEndian::read_u16(c);
197
33.1M
                NativeEndian::write_u16(c, v);
198
33.1M
            }),
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image::{closure#0}
199
0
            _ => unreachable!(),
200
        }
201
92
        Ok(())
202
4.13k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image
Unexecuted instantiation: <image::codecs::png::PngDecoder<_> as image::io::decoder::ImageDecoder>::read_image
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image
Line
Count
Source
182
2.25k
    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
183
        use byteorder_lite::{BigEndian, ByteOrder, NativeEndian};
184
185
2.25k
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
186
2.25k
        self.reader.next_frame(buf).map_err(ImageError::from_png)?;
187
        // PNG images are big endian. For 16 bit per channel and larger types,
188
        // the buffer may need to be reordered to native endianness per the
189
        // contract of `read_image`.
190
        // TODO: assumes equal channel bit depth.
191
9
        let bpc = self.color_type().bytes_per_pixel() / self.color_type().channel_count();
192
9
193
9
        match bpc {
194
9
            1 => (), // No reodering necessary for u8
195
0
            2 => buf.chunks_exact_mut(2).for_each(|c| {
196
                let v = BigEndian::read_u16(c);
197
                NativeEndian::write_u16(c, v);
198
0
            }),
199
0
            _ => unreachable!(),
200
        }
201
9
        Ok(())
202
2.25k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image
Line
Count
Source
182
1.88k
    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
183
        use byteorder_lite::{BigEndian, ByteOrder, NativeEndian};
184
185
1.88k
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
186
1.88k
        self.reader.next_frame(buf).map_err(ImageError::from_png)?;
187
        // PNG images are big endian. For 16 bit per channel and larger types,
188
        // the buffer may need to be reordered to native endianness per the
189
        // contract of `read_image`.
190
        // TODO: assumes equal channel bit depth.
191
83
        let bpc = self.color_type().bytes_per_pixel() / self.color_type().channel_count();
192
83
193
83
        match bpc {
194
61
            1 => (), // No reodering necessary for u8
195
22
            2 => buf.chunks_exact_mut(2).for_each(|c| {
196
                let v = BigEndian::read_u16(c);
197
                NativeEndian::write_u16(c, v);
198
22
            }),
199
0
            _ => unreachable!(),
200
        }
201
83
        Ok(())
202
1.88k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image
203
204
4.13k
    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
205
4.13k
        (*self).read_image(buf)
206
4.13k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image_boxed
Unexecuted instantiation: <image::codecs::png::PngDecoder<_> as image::io::decoder::ImageDecoder>::read_image_boxed
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image_boxed
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image_boxed
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image_boxed
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image_boxed
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image_boxed
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image_boxed
Line
Count
Source
204
2.25k
    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
205
2.25k
        (*self).read_image(buf)
206
2.25k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image_boxed
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image_boxed
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image_boxed
Line
Count
Source
204
1.88k
    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
205
1.88k
        (*self).read_image(buf)
206
1.88k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::read_image_boxed
207
208
1.88k
    fn set_limits(&mut self, limits: Limits) -> ImageResult<()> {
209
1.88k
        limits.check_support(&crate::LimitSupport::default())?;
210
1.88k
        let info = self.reader.info();
211
1.88k
        limits.check_dimensions(info.width, info.height)?;
212
1.88k
        self.limits = limits;
213
1.88k
        // TODO: add `png::Reader::change_limits()` and call it here
214
1.88k
        // to also constrain the internal buffer allocations in the PNG crate
215
1.88k
        Ok(())
216
1.88k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::set_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<_> as image::io::decoder::ImageDecoder>::set_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::set_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::set_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::set_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::set_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::set_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::set_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::set_limits
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::set_limits
<image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::set_limits
Line
Count
Source
208
1.88k
    fn set_limits(&mut self, limits: Limits) -> ImageResult<()> {
209
1.88k
        limits.check_support(&crate::LimitSupport::default())?;
210
1.88k
        let info = self.reader.info();
211
1.88k
        limits.check_dimensions(info.width, info.height)?;
212
1.88k
        self.limits = limits;
213
1.88k
        // TODO: add `png::Reader::change_limits()` and call it here
214
1.88k
        // to also constrain the internal buffer allocations in the PNG crate
215
1.88k
        Ok(())
216
1.88k
    }
Unexecuted instantiation: <image::codecs::png::PngDecoder<std::io::cursor::Cursor<&[u8]>> as image::io::decoder::ImageDecoder>::set_limits
217
}
218
219
/// An [`AnimationDecoder`] adapter of [`PngDecoder`].
220
///
221
/// See [`PngDecoder::apng`] for more information.
222
///
223
/// [`AnimationDecoder`]: ../trait.AnimationDecoder.html
224
/// [`PngDecoder`]: struct.PngDecoder.html
225
/// [`PngDecoder::apng`]: struct.PngDecoder.html#method.apng
226
pub struct ApngDecoder<R: BufRead + Seek> {
227
    inner: PngDecoder<R>,
228
    /// The current output buffer.
229
    current: Option<RgbaImage>,
230
    /// The previous output buffer, used for dispose op previous.
231
    previous: Option<RgbaImage>,
232
    /// The dispose op of the current frame.
233
    dispose: DisposeOp,
234
235
    /// The region to dispose of the previous frame.
236
    dispose_region: Option<(u32, u32, u32, u32)>,
237
    /// The number of image still expected to be able to load.
238
    remaining: u32,
239
    /// The next (first) image is the thumbnail.
240
    has_thumbnail: bool,
241
}
242
243
impl<R: BufRead + Seek> ApngDecoder<R> {
244
0
    fn new(inner: PngDecoder<R>) -> Self {
245
0
        let info = inner.reader.info();
246
0
        let remaining = match info.animation_control() {
247
            // The expected number of fcTL in the remaining image.
248
0
            Some(actl) => actl.num_frames,
249
0
            None => 0,
250
        };
251
        // If the IDAT has no fcTL then it is not part of the animation counted by
252
        // num_frames. All following fdAT chunks must be preceded by an fcTL
253
0
        let has_thumbnail = info.frame_control.is_none();
254
0
        ApngDecoder {
255
0
            inner,
256
0
            current: None,
257
0
            previous: None,
258
0
            dispose: DisposeOp::Background,
259
0
            dispose_region: None,
260
0
            remaining,
261
0
            has_thumbnail,
262
0
        }
263
0
    }
264
265
    // TODO: thumbnail(&mut self) -> Option<impl ImageDecoder<'_>>
266
267
    /// Decode one subframe and overlay it on the canvas.
268
0
    fn mix_next_frame(&mut self) -> Result<Option<&RgbaImage>, ImageError> {
269
        // The iterator always produces RGBA8 images
270
        const COLOR_TYPE: ColorType = ColorType::Rgba8;
271
272
        // Allocate the buffers, honoring the memory limits
273
0
        let (width, height) = self.inner.dimensions();
274
0
        {
275
0
            let limits = &mut self.inner.limits;
276
0
            if self.previous.is_none() {
277
0
                limits.reserve_buffer(width, height, COLOR_TYPE)?;
278
0
                self.previous = Some(RgbaImage::new(width, height));
279
0
            }
280
281
0
            if self.current.is_none() {
282
0
                limits.reserve_buffer(width, height, COLOR_TYPE)?;
283
0
                self.current = Some(RgbaImage::new(width, height));
284
0
            }
285
        }
286
287
        // Remove this image from remaining.
288
0
        self.remaining = match self.remaining.checked_sub(1) {
289
0
            None => return Ok(None),
290
0
            Some(next) => next,
291
0
        };
292
0
293
0
        // Shorten ourselves to 0 in case of error.
294
0
        let remaining = self.remaining;
295
0
        self.remaining = 0;
296
0
297
0
        // Skip the thumbnail that is not part of the animation.
298
0
        if self.has_thumbnail {
299
            // Clone the limits so that our one-off allocation that's destroyed after this scope doesn't persist
300
0
            let mut limits = self.inner.limits.clone();
301
0
            limits.reserve_usize(self.inner.reader.output_buffer_size())?;
302
0
            let mut buffer = vec![0; self.inner.reader.output_buffer_size()];
303
0
            // TODO: add `png::Reader::change_limits()` and call it here
304
0
            // to also constrain the internal buffer allocations in the PNG crate
305
0
            self.inner
306
0
                .reader
307
0
                .next_frame(&mut buffer)
308
0
                .map_err(ImageError::from_png)?;
309
0
            self.has_thumbnail = false;
310
0
        }
311
312
0
        self.animatable_color_type()?;
313
314
        // We've initialized them earlier in this function
315
0
        let previous = self.previous.as_mut().unwrap();
316
0
        let current = self.current.as_mut().unwrap();
317
0
318
0
        // Dispose of the previous frame.
319
0
320
0
        match self.dispose {
321
0
            DisposeOp::None => {
322
0
                previous.clone_from(current);
323
0
            }
324
            DisposeOp::Background => {
325
0
                previous.clone_from(current);
326
0
                if let Some((px, py, width, height)) = self.dispose_region {
327
0
                    let mut region_current = current.sub_image(px, py, width, height);
328
0
329
0
                    // FIXME: This is a workaround for the fact that `pixels_mut` is not implemented
330
0
                    let pixels: Vec<_> = region_current.pixels().collect();
331
332
0
                    for (x, y, _) in &pixels {
333
0
                        region_current.put_pixel(*x, *y, Rgba::from([0, 0, 0, 0]));
334
0
                    }
335
0
                } else {
336
0
                    // The first frame is always a background frame.
337
0
                    current.pixels_mut().for_each(|pixel| {
338
0
                        *pixel = Rgba::from([0, 0, 0, 0]);
339
0
                    });
340
0
                }
341
            }
342
0
            DisposeOp::Previous => {
343
0
                let (px, py, width, height) = self
344
0
                    .dispose_region
345
0
                    .expect("The first frame must not set dispose=Previous");
346
0
                let region_previous = previous.sub_image(px, py, width, height);
347
0
                current
348
0
                    .copy_from(&region_previous.to_image(), px, py)
349
0
                    .unwrap();
350
0
            }
351
        }
352
353
        // The allocations from now on are not going to persist,
354
        // and will be destroyed at the end of the scope.
355
        // Clone the limits so that any changes to them die with the allocations.
356
0
        let mut limits = self.inner.limits.clone();
357
0
358
0
        // Read next frame data.
359
0
        let raw_frame_size = self.inner.reader.output_buffer_size();
360
0
        limits.reserve_usize(raw_frame_size)?;
361
0
        let mut buffer = vec![0; raw_frame_size];
362
0
        // TODO: add `png::Reader::change_limits()` and call it here
363
0
        // to also constrain the internal buffer allocations in the PNG crate
364
0
        self.inner
365
0
            .reader
366
0
            .next_frame(&mut buffer)
367
0
            .map_err(ImageError::from_png)?;
368
0
        let info = self.inner.reader.info();
369
0
370
0
        // Find out how to interpret the decoded frame.
371
0
        let (width, height, px, py, blend);
372
0
        match info.frame_control() {
373
0
            None => {
374
0
                width = info.width;
375
0
                height = info.height;
376
0
                px = 0;
377
0
                py = 0;
378
0
                blend = BlendOp::Source;
379
0
            }
380
0
            Some(fc) => {
381
0
                width = fc.width;
382
0
                height = fc.height;
383
0
                px = fc.x_offset;
384
0
                py = fc.y_offset;
385
0
                blend = fc.blend_op;
386
0
                self.dispose = fc.dispose_op;
387
0
            }
388
        };
389
390
0
        self.dispose_region = Some((px, py, width, height));
391
0
392
0
        // Turn the data into an rgba image proper.
393
0
        limits.reserve_buffer(width, height, COLOR_TYPE)?;
394
0
        let source = match self.inner.color_type {
395
            ColorType::L8 => {
396
0
                let image = ImageBuffer::<Luma<_>, _>::from_raw(width, height, buffer).unwrap();
397
0
                DynamicImage::ImageLuma8(image).into_rgba8()
398
            }
399
            ColorType::La8 => {
400
0
                let image = ImageBuffer::<LumaA<_>, _>::from_raw(width, height, buffer).unwrap();
401
0
                DynamicImage::ImageLumaA8(image).into_rgba8()
402
            }
403
            ColorType::Rgb8 => {
404
0
                let image = ImageBuffer::<Rgb<_>, _>::from_raw(width, height, buffer).unwrap();
405
0
                DynamicImage::ImageRgb8(image).into_rgba8()
406
            }
407
0
            ColorType::Rgba8 => ImageBuffer::<Rgba<_>, _>::from_raw(width, height, buffer).unwrap(),
408
            ColorType::L16 | ColorType::Rgb16 | ColorType::La16 | ColorType::Rgba16 => {
409
                // TODO: to enable remove restriction in `animatable_color_type` method.
410
0
                unreachable!("16-bit apng not yet support")
411
            }
412
0
            _ => unreachable!("Invalid png color"),
413
        };
414
        // We've converted the raw frame to RGBA8 and disposed of the original allocation
415
0
        limits.free_usize(raw_frame_size);
416
0
417
0
        match blend {
418
0
            BlendOp::Source => {
419
0
                current
420
0
                    .copy_from(&source, px, py)
421
0
                    .expect("Invalid png image not detected in png");
422
0
            }
423
            BlendOp::Over => {
424
                // TODO: investigate speed, speed-ups, and bounds-checks.
425
0
                for (x, y, p) in source.enumerate_pixels() {
426
0
                    current.get_pixel_mut(x + px, y + py).blend(p);
427
0
                }
428
            }
429
        }
430
431
        // Ok, we can proceed with actually remaining images.
432
0
        self.remaining = remaining;
433
0
        // Return composited output buffer.
434
0
435
0
        Ok(Some(self.current.as_ref().unwrap()))
436
0
    }
437
438
0
    fn animatable_color_type(&self) -> Result<(), ImageError> {
439
0
        match self.inner.color_type {
440
0
            ColorType::L8 | ColorType::Rgb8 | ColorType::La8 | ColorType::Rgba8 => Ok(()),
441
            // TODO: do not handle multi-byte colors. Remember to implement it in `mix_next_frame`.
442
            ColorType::L16 | ColorType::Rgb16 | ColorType::La16 | ColorType::Rgba16 => {
443
0
                Err(unsupported_color(self.inner.color_type.into()))
444
            }
445
0
            _ => unreachable!("{:?} not a valid png color", self.inner.color_type),
446
        }
447
0
    }
448
}
449
450
impl<'a, R: BufRead + Seek + 'a> AnimationDecoder<'a> for ApngDecoder<R> {
451
0
    fn into_frames(self) -> Frames<'a> {
452
        struct FrameIterator<R: BufRead + Seek>(ApngDecoder<R>);
453
454
        impl<R: BufRead + Seek> Iterator for FrameIterator<R> {
455
            type Item = ImageResult<Frame>;
456
457
0
            fn next(&mut self) -> Option<Self::Item> {
458
0
                let image = match self.0.mix_next_frame() {
459
0
                    Ok(Some(image)) => image.clone(),
460
0
                    Ok(None) => return None,
461
0
                    Err(err) => return Some(Err(err)),
462
                };
463
464
0
                let info = self.0.inner.reader.info();
465
0
                let fc = info.frame_control().unwrap();
466
0
                // PNG delays are rations in seconds.
467
0
                let num = u32::from(fc.delay_num) * 1_000u32;
468
0
                let denom = match fc.delay_den {
469
                    // The standard dictates to replace by 100 when the denominator is 0.
470
0
                    0 => 100,
471
0
                    d => u32::from(d),
472
                };
473
0
                let delay = Delay::from_ratio(Ratio::new(num, denom));
474
0
                Some(Ok(Frame::from_parts(image, 0, 0, delay)))
475
0
            }
476
        }
477
478
0
        Frames::new(Box::new(FrameIterator(self)))
479
0
    }
480
}
481
482
/// PNG encoder
483
pub struct PngEncoder<W: Write> {
484
    w: W,
485
    compression: CompressionType,
486
    filter: FilterType,
487
    icc_profile: Vec<u8>,
488
}
489
490
/// Compression level of a PNG encoder. The default setting is `Fast`.
491
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
492
#[non_exhaustive]
493
#[derive(Default)]
494
pub enum CompressionType {
495
    /// Default compression level
496
    Default,
497
    /// Fast, minimal compression
498
    #[default]
499
    Fast,
500
    /// High compression level
501
    Best,
502
}
503
504
/// Filter algorithms used to process image data to improve compression.
505
///
506
/// The default filter is `Adaptive`.
507
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
508
#[non_exhaustive]
509
#[derive(Default)]
510
pub enum FilterType {
511
    /// No processing done, best used for low bit depth grayscale or data with a
512
    /// low color count
513
    NoFilter,
514
    /// Filters based on previous pixel in the same scanline
515
    Sub,
516
    /// Filters based on the scanline above
517
    Up,
518
    /// Filters based on the average of left and right neighbor pixels
519
    Avg,
520
    /// Algorithm that takes into account the left, upper left, and above pixels
521
    Paeth,
522
    /// Uses a heuristic to select one of the preceding filters for each
523
    /// scanline rather than one filter for the entire image
524
    #[default]
525
    Adaptive,
526
}
527
528
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
529
#[non_exhaustive]
530
enum BadPngRepresentation {
531
    ColorType(ExtendedColorType),
532
}
533
534
impl<W: Write> PngEncoder<W> {
535
    /// Create a new encoder that writes its output to ```w```
536
0
    pub fn new(w: W) -> PngEncoder<W> {
537
0
        PngEncoder {
538
0
            w,
539
0
            compression: CompressionType::default(),
540
0
            filter: FilterType::default(),
541
0
            icc_profile: Vec::new(),
542
0
        }
543
0
    }
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut alloc::vec::Vec<u8>>>::new
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::new
544
545
    /// Create a new encoder that writes its output to `w` with `CompressionType` `compression` and
546
    /// `FilterType` `filter`.
547
    ///
548
    /// It is best to view the options as a _hint_ to the implementation on the smallest or fastest
549
    /// option for encoding a particular image. That is, using options that map directly to a PNG
550
    /// image parameter will use this parameter where possible. But variants that have no direct
551
    /// mapping may be interpreted differently in minor versions. The exact output is expressly
552
    /// __not__ part of the SemVer stability guarantee.
553
    ///
554
    /// Note that it is not optimal to use a single filter type, so an adaptive
555
    /// filter type is selected as the default. The filter which best minimizes
556
    /// file size may change with the type of compression used.
557
0
    pub fn new_with_quality(
558
0
        w: W,
559
0
        compression: CompressionType,
560
0
        filter: FilterType,
561
0
    ) -> PngEncoder<W> {
562
0
        PngEncoder {
563
0
            w,
564
0
            compression,
565
0
            filter,
566
0
            icc_profile: Vec::new(),
567
0
        }
568
0
    }
569
570
0
    fn encode_inner(
571
0
        self,
572
0
        data: &[u8],
573
0
        width: u32,
574
0
        height: u32,
575
0
        color: ExtendedColorType,
576
0
    ) -> ImageResult<()> {
577
0
        let (ct, bits) = match color {
578
0
            ExtendedColorType::L8 => (png::ColorType::Grayscale, png::BitDepth::Eight),
579
0
            ExtendedColorType::L16 => (png::ColorType::Grayscale, png::BitDepth::Sixteen),
580
0
            ExtendedColorType::La8 => (png::ColorType::GrayscaleAlpha, png::BitDepth::Eight),
581
0
            ExtendedColorType::La16 => (png::ColorType::GrayscaleAlpha, png::BitDepth::Sixteen),
582
0
            ExtendedColorType::Rgb8 => (png::ColorType::Rgb, png::BitDepth::Eight),
583
0
            ExtendedColorType::Rgb16 => (png::ColorType::Rgb, png::BitDepth::Sixteen),
584
0
            ExtendedColorType::Rgba8 => (png::ColorType::Rgba, png::BitDepth::Eight),
585
0
            ExtendedColorType::Rgba16 => (png::ColorType::Rgba, png::BitDepth::Sixteen),
586
            _ => {
587
0
                return Err(ImageError::Unsupported(
588
0
                    UnsupportedError::from_format_and_kind(
589
0
                        ImageFormat::Png.into(),
590
0
                        UnsupportedErrorKind::Color(color),
591
0
                    ),
592
0
                ))
593
            }
594
        };
595
0
        let comp = match self.compression {
596
0
            CompressionType::Default => png::Compression::Default,
597
0
            CompressionType::Best => png::Compression::Best,
598
0
            _ => png::Compression::Fast,
599
        };
600
0
        let (filter, adaptive_filter) = match self.filter {
601
0
            FilterType::NoFilter => (
602
0
                png::FilterType::NoFilter,
603
0
                png::AdaptiveFilterType::NonAdaptive,
604
0
            ),
605
0
            FilterType::Sub => (png::FilterType::Sub, png::AdaptiveFilterType::NonAdaptive),
606
0
            FilterType::Up => (png::FilterType::Up, png::AdaptiveFilterType::NonAdaptive),
607
0
            FilterType::Avg => (png::FilterType::Avg, png::AdaptiveFilterType::NonAdaptive),
608
0
            FilterType::Paeth => (png::FilterType::Paeth, png::AdaptiveFilterType::NonAdaptive),
609
0
            FilterType::Adaptive => (png::FilterType::Sub, png::AdaptiveFilterType::Adaptive),
610
        };
611
612
0
        let mut info = png::Info::with_size(width, height);
613
0
614
0
        if !self.icc_profile.is_empty() {
615
0
            info.icc_profile = Some(Cow::Borrowed(&self.icc_profile));
616
0
        }
617
618
0
        let mut encoder =
619
0
            png::Encoder::with_info(self.w, info).map_err(|e| ImageError::IoError(e.into()))?;
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut alloc::vec::Vec<u8>>>::encode_inner::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_inner::{closure#0}
620
621
0
        encoder.set_color(ct);
622
0
        encoder.set_depth(bits);
623
0
        encoder.set_compression(comp);
624
0
        encoder.set_filter(filter);
625
0
        encoder.set_adaptive_filter(adaptive_filter);
626
0
        let mut writer = encoder
627
0
            .write_header()
628
0
            .map_err(|e| ImageError::IoError(e.into()))?;
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut alloc::vec::Vec<u8>>>::encode_inner::{closure#1}
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_inner::{closure#1}
629
0
        writer
630
0
            .write_image_data(data)
631
0
            .map_err(|e| ImageError::IoError(e.into()))
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut alloc::vec::Vec<u8>>>::encode_inner::{closure#2}
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_inner::{closure#2}
632
0
    }
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut alloc::vec::Vec<u8>>>::encode_inner
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_inner
633
}
634
635
impl<W: Write> ImageEncoder for PngEncoder<W> {
636
    /// Write a PNG image with the specified width, height, and color type.
637
    ///
638
    /// For color types with 16-bit per channel or larger, the contents of `buf` should be in
639
    /// native endian. `PngEncoder` will automatically convert to big endian as required by the
640
    /// underlying PNG format.
641
    #[track_caller]
642
0
    fn write_image(
643
0
        self,
644
0
        buf: &[u8],
645
0
        width: u32,
646
0
        height: u32,
647
0
        color_type: ExtendedColorType,
648
0
    ) -> ImageResult<()> {
649
        use ExtendedColorType::*;
650
651
0
        let expected_buffer_len = color_type.buffer_size(width, height);
652
0
        assert_eq!(
653
0
            expected_buffer_len,
654
0
            buf.len() as u64,
655
0
            "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
656
0
            buf.len(),
657
        );
658
659
        // PNG images are big endian. For 16 bit per channel and larger types,
660
        // the buffer may need to be reordered to big endian per the
661
        // contract of `write_image`.
662
        // TODO: assumes equal channel bit depth.
663
0
        match color_type {
664
            L8 | La8 | Rgb8 | Rgba8 => {
665
                // No reodering necessary for u8
666
0
                self.encode_inner(buf, width, height, color_type)
667
            }
668
            L16 | La16 | Rgb16 | Rgba16 => {
669
                // Because the buffer is immutable and the PNG encoder does not
670
                // yet take Write/Read traits, create a temporary buffer for
671
                // big endian reordering.
672
                let mut reordered;
673
0
                let buf = if cfg!(target_endian = "little") {
674
0
                    reordered = vec_try_with_capacity(buf.len())?;
675
0
                    reordered.extend(buf.chunks_exact(2).flat_map(|le| [le[1], le[0]]));
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut alloc::vec::Vec<u8>> as image::io::encoder::ImageEncoder>::write_image::{closure#0}
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> as image::io::encoder::ImageEncoder>::write_image::{closure#0}
676
0
                    &reordered
677
                } else {
678
0
                    buf
679
                };
680
0
                self.encode_inner(buf, width, height, color_type)
681
            }
682
0
            _ => Err(ImageError::Encoding(EncodingError::new(
683
0
                ImageFormat::Png.into(),
684
0
                BadPngRepresentation::ColorType(color_type),
685
0
            ))),
686
        }
687
0
    }
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut alloc::vec::Vec<u8>> as image::io::encoder::ImageEncoder>::write_image
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> as image::io::encoder::ImageEncoder>::write_image
688
689
0
    fn set_icc_profile(&mut self, icc_profile: Vec<u8>) -> Result<(), UnsupportedError> {
690
0
        self.icc_profile = icc_profile;
691
0
        Ok(())
692
0
    }
Unexecuted instantiation: <image::codecs::png::PngEncoder<_> as image::io::encoder::ImageEncoder>::set_icc_profile
Unexecuted instantiation: <image::codecs::png::PngEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> as image::io::encoder::ImageEncoder>::set_icc_profile
693
}
694
695
impl ImageError {
696
7.23k
    fn from_png(err: png::DecodingError) -> ImageError {
697
        use png::DecodingError::*;
698
7.23k
        match err {
699
5.24k
            IoError(err) => ImageError::IoError(err),
700
            // The input image was not a valid PNG.
701
1.98k
            err @ Format(_) => {
702
1.98k
                ImageError::Decoding(DecodingError::new(ImageFormat::Png.into(), err))
703
            }
704
            // Other is used when:
705
            // - The decoder is polled for more animation frames despite being done (or not being animated
706
            //   in the first place).
707
            // - The output buffer does not have the required size.
708
5
            err @ Parameter(_) => ImageError::Parameter(ParameterError::from_kind(
709
5
                ParameterErrorKind::Generic(err.to_string()),
710
5
            )),
711
            LimitsExceeded => {
712
2
                ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
713
            }
714
        }
715
7.23k
    }
716
}
717
718
impl fmt::Display for BadPngRepresentation {
719
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
720
0
        match self {
721
0
            Self::ColorType(color_type) => {
722
0
                write!(f, "The color {color_type:?} can not be represented in PNG.")
723
0
            }
724
0
        }
725
0
    }
726
}
727
728
impl std::error::Error for BadPngRepresentation {}
729
730
#[cfg(test)]
731
mod tests {
732
    use super::*;
733
    use crate::io::free_functions::decoder_to_vec;
734
    use std::io::{BufReader, Cursor, Read};
735
736
    #[test]
737
    fn ensure_no_decoder_off_by_one() {
738
        let dec = PngDecoder::new(BufReader::new(
739
            std::fs::File::open("tests/images/png/bugfixes/debug_triangle_corners_widescreen.png")
740
                .unwrap(),
741
        ))
742
        .expect("Unable to read PNG file (does it exist?)");
743
744
        assert_eq![(2000, 1000), dec.dimensions()];
745
746
        assert_eq![
747
            ColorType::Rgb8,
748
            dec.color_type(),
749
            "Image MUST have the Rgb8 format"
750
        ];
751
752
        let correct_bytes = decoder_to_vec(dec)
753
            .expect("Unable to read file")
754
            .bytes()
755
            .map(|x| x.expect("Unable to read byte"))
756
            .collect::<Vec<u8>>();
757
758
        assert_eq![6_000_000, correct_bytes.len()];
759
    }
760
761
    #[test]
762
    fn underlying_error() {
763
        use std::error::Error;
764
765
        let mut not_png =
766
            std::fs::read("tests/images/png/bugfixes/debug_triangle_corners_widescreen.png")
767
                .unwrap();
768
        not_png[0] = 0;
769
770
        let error = PngDecoder::new(Cursor::new(&not_png)).err().unwrap();
771
        let _ = error
772
            .source()
773
            .unwrap()
774
            .downcast_ref::<png::DecodingError>()
775
            .expect("Caused by a png error");
776
    }
777
778
    #[test]
779
    fn encode_bad_color_type() {
780
        // regression test for issue #1663
781
        let image = DynamicImage::new_rgb32f(1, 1);
782
        let mut target = Cursor::new(vec![]);
783
        let _ = image.write_to(&mut target, ImageFormat::Png);
784
    }
785
}