1use std::borrow::Cow;
9use std::fmt;
10use std::io::{BufRead, Seek, Write};
11
12use png::{BlendOp, DisposeOp};
13
14use crate::animation::{Delay, Frame, Frames, Ratio};
15use crate::color::{Blend, ColorType, ExtendedColorType};
16use crate::error::{
17    DecodingError, EncodingError, ImageError, ImageResult, LimitError, LimitErrorKind,
18    ParameterError, ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
19};
20use crate::image::{AnimationDecoder, ImageDecoder, ImageEncoder, ImageFormat};
21use crate::{DynamicImage, GenericImage, ImageBuffer, Luma, LumaA, Rgb, Rgba, RgbaImage};
22use crate::{GenericImageView, Limits};
23
24pub(crate) const PNG_SIGNATURE: [u8; 8] = [137, 80, 78, 71, 13, 10, 26, 10];
27
28pub struct PngDecoder<R: BufRead + Seek> {
30    color_type: ColorType,
31    reader: png::Reader<R>,
32    limits: Limits,
33}
34
35impl<R: BufRead + Seek> PngDecoder<R> {
36    pub fn new(r: R) -> ImageResult<PngDecoder<R>> {
38        Self::with_limits(r, Limits::no_limits())
39    }
40
41    pub fn with_limits(r: R, limits: Limits) -> ImageResult<PngDecoder<R>> {
43        limits.check_support(&crate::LimitSupport::default())?;
44
45        let max_bytes = usize::try_from(limits.max_alloc.unwrap_or(u64::MAX)).unwrap_or(usize::MAX);
46        let mut decoder = png::Decoder::new_with_limits(r, png::Limits { bytes: max_bytes });
47        decoder.set_ignore_text_chunk(true);
48
49        let info = decoder.read_header_info().map_err(ImageError::from_png)?;
50        limits.check_dimensions(info.width, info.height)?;
51
52        decoder.set_transformations(png::Transformations::EXPAND);
56        let reader = decoder.read_info().map_err(ImageError::from_png)?;
57        let (color_type, bits) = reader.output_color_type();
58        let color_type = match (color_type, bits) {
59            (png::ColorType::Grayscale, png::BitDepth::Eight) => ColorType::L8,
60            (png::ColorType::Grayscale, png::BitDepth::Sixteen) => ColorType::L16,
61            (png::ColorType::GrayscaleAlpha, png::BitDepth::Eight) => ColorType::La8,
62            (png::ColorType::GrayscaleAlpha, png::BitDepth::Sixteen) => ColorType::La16,
63            (png::ColorType::Rgb, png::BitDepth::Eight) => ColorType::Rgb8,
64            (png::ColorType::Rgb, png::BitDepth::Sixteen) => ColorType::Rgb16,
65            (png::ColorType::Rgba, png::BitDepth::Eight) => ColorType::Rgba8,
66            (png::ColorType::Rgba, png::BitDepth::Sixteen) => ColorType::Rgba16,
67
68            (png::ColorType::Grayscale, png::BitDepth::One) => {
69                return Err(unsupported_color(ExtendedColorType::L1))
70            }
71            (png::ColorType::GrayscaleAlpha, png::BitDepth::One) => {
72                return Err(unsupported_color(ExtendedColorType::La1))
73            }
74            (png::ColorType::Rgb, png::BitDepth::One) => {
75                return Err(unsupported_color(ExtendedColorType::Rgb1))
76            }
77            (png::ColorType::Rgba, png::BitDepth::One) => {
78                return Err(unsupported_color(ExtendedColorType::Rgba1))
79            }
80
81            (png::ColorType::Grayscale, png::BitDepth::Two) => {
82                return Err(unsupported_color(ExtendedColorType::L2))
83            }
84            (png::ColorType::GrayscaleAlpha, png::BitDepth::Two) => {
85                return Err(unsupported_color(ExtendedColorType::La2))
86            }
87            (png::ColorType::Rgb, png::BitDepth::Two) => {
88                return Err(unsupported_color(ExtendedColorType::Rgb2))
89            }
90            (png::ColorType::Rgba, png::BitDepth::Two) => {
91                return Err(unsupported_color(ExtendedColorType::Rgba2))
92            }
93
94            (png::ColorType::Grayscale, png::BitDepth::Four) => {
95                return Err(unsupported_color(ExtendedColorType::L4))
96            }
97            (png::ColorType::GrayscaleAlpha, png::BitDepth::Four) => {
98                return Err(unsupported_color(ExtendedColorType::La4))
99            }
100            (png::ColorType::Rgb, png::BitDepth::Four) => {
101                return Err(unsupported_color(ExtendedColorType::Rgb4))
102            }
103            (png::ColorType::Rgba, png::BitDepth::Four) => {
104                return Err(unsupported_color(ExtendedColorType::Rgba4))
105            }
106
107            (png::ColorType::Indexed, bits) => {
108                return Err(unsupported_color(ExtendedColorType::Unknown(bits as u8)))
109            }
110        };
111
112        Ok(PngDecoder {
113            color_type,
114            reader,
115            limits,
116        })
117    }
118
119    pub fn gamma_value(&self) -> ImageResult<Option<f64>> {
128        Ok(self
129            .reader
130            .info()
131            .source_gamma
132            .map(|x| f64::from(x.into_scaled()) / 100_000.0))
133    }
134
135    pub fn apng(self) -> ImageResult<ApngDecoder<R>> {
146        Ok(ApngDecoder::new(self))
147    }
148
149    pub fn is_apng(&self) -> ImageResult<bool> {
156        Ok(self.reader.info().animation_control.is_some())
157    }
158}
159
160fn unsupported_color(ect: ExtendedColorType) -> ImageError {
161    ImageError::Unsupported(UnsupportedError::from_format_and_kind(
162        ImageFormat::Png.into(),
163        UnsupportedErrorKind::Color(ect),
164    ))
165}
166
167impl<R: BufRead + Seek> ImageDecoder for PngDecoder<R> {
168    fn dimensions(&self) -> (u32, u32) {
169        self.reader.info().size()
170    }
171
172    fn color_type(&self) -> ColorType {
173        self.color_type
174    }
175
176    fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
177        Ok(self.reader.info().icc_profile.as_ref().map(|x| x.to_vec()))
178    }
179
180    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
181        use byteorder_lite::{BigEndian, ByteOrder, NativeEndian};
182
183        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
184        self.reader.next_frame(buf).map_err(ImageError::from_png)?;
185        let bpc = self.color_type().bytes_per_pixel() / self.color_type().channel_count();
190
191        match bpc {
192            1 => (), 2 => buf.chunks_exact_mut(2).for_each(|c| {
194                let v = BigEndian::read_u16(c);
195                NativeEndian::write_u16(c, v);
196            }),
197            _ => unreachable!(),
198        }
199        Ok(())
200    }
201
202    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
203        (*self).read_image(buf)
204    }
205
206    fn set_limits(&mut self, limits: Limits) -> ImageResult<()> {
207        limits.check_support(&crate::LimitSupport::default())?;
208        let info = self.reader.info();
209        limits.check_dimensions(info.width, info.height)?;
210        self.limits = limits;
211        Ok(())
214    }
215}
216
217pub struct ApngDecoder<R: BufRead + Seek> {
225    inner: PngDecoder<R>,
226    current: Option<RgbaImage>,
228    previous: Option<RgbaImage>,
230    dispose: DisposeOp,
232
233    dispose_region: Option<(u32, u32, u32, u32)>,
235    remaining: u32,
237    has_thumbnail: bool,
239}
240
241impl<R: BufRead + Seek> ApngDecoder<R> {
242    fn new(inner: PngDecoder<R>) -> Self {
243        let info = inner.reader.info();
244        let remaining = match info.animation_control() {
245            Some(actl) => actl.num_frames,
247            None => 0,
248        };
249        let has_thumbnail = info.frame_control.is_none();
252        ApngDecoder {
253            inner,
254            current: None,
255            previous: None,
256            dispose: DisposeOp::Background,
257            dispose_region: None,
258            remaining,
259            has_thumbnail,
260        }
261    }
262
263    fn mix_next_frame(&mut self) -> Result<Option<&RgbaImage>, ImageError> {
267        const COLOR_TYPE: ColorType = ColorType::Rgba8;
269
270        let (width, height) = self.inner.dimensions();
272        {
273            let limits = &mut self.inner.limits;
274            if self.previous.is_none() {
275                limits.reserve_buffer(width, height, COLOR_TYPE)?;
276                self.previous = Some(RgbaImage::new(width, height));
277            }
278
279            if self.current.is_none() {
280                limits.reserve_buffer(width, height, COLOR_TYPE)?;
281                self.current = Some(RgbaImage::new(width, height));
282            }
283        }
284
285        self.remaining = match self.remaining.checked_sub(1) {
287            None => return Ok(None),
288            Some(next) => next,
289        };
290
291        let remaining = self.remaining;
293        self.remaining = 0;
294
295        if self.has_thumbnail {
297            let mut limits = self.inner.limits.clone();
299            limits.reserve_usize(self.inner.reader.output_buffer_size())?;
300            let mut buffer = vec![0; self.inner.reader.output_buffer_size()];
301            self.inner
304                .reader
305                .next_frame(&mut buffer)
306                .map_err(ImageError::from_png)?;
307            self.has_thumbnail = false;
308        }
309
310        self.animatable_color_type()?;
311
312        let previous = self.previous.as_mut().unwrap();
314        let current = self.current.as_mut().unwrap();
315
316        match self.dispose {
319            DisposeOp::None => {
320                previous.clone_from(current);
321            }
322            DisposeOp::Background => {
323                previous.clone_from(current);
324                if let Some((px, py, width, height)) = self.dispose_region {
325                    let mut region_current = current.sub_image(px, py, width, height);
326
327                    let pixels: Vec<_> = region_current.pixels().collect();
329
330                    for (x, y, _) in &pixels {
331                        region_current.put_pixel(*x, *y, Rgba::from([0, 0, 0, 0]));
332                    }
333                } else {
334                    current.pixels_mut().for_each(|pixel| {
336                        *pixel = Rgba::from([0, 0, 0, 0]);
337                    });
338                }
339            }
340            DisposeOp::Previous => {
341                let (px, py, width, height) = self
342                    .dispose_region
343                    .expect("The first frame must not set dispose=Previous");
344                let region_previous = previous.sub_image(px, py, width, height);
345                current
346                    .copy_from(®ion_previous.to_image(), px, py)
347                    .unwrap();
348            }
349        }
350
351        let mut limits = self.inner.limits.clone();
355
356        let raw_frame_size = self.inner.reader.output_buffer_size();
358        limits.reserve_usize(raw_frame_size)?;
359        let mut buffer = vec![0; raw_frame_size];
360        self.inner
363            .reader
364            .next_frame(&mut buffer)
365            .map_err(ImageError::from_png)?;
366        let info = self.inner.reader.info();
367
368        let (width, height, px, py, blend);
370        match info.frame_control() {
371            None => {
372                width = info.width;
373                height = info.height;
374                px = 0;
375                py = 0;
376                blend = BlendOp::Source;
377            }
378            Some(fc) => {
379                width = fc.width;
380                height = fc.height;
381                px = fc.x_offset;
382                py = fc.y_offset;
383                blend = fc.blend_op;
384                self.dispose = fc.dispose_op;
385            }
386        };
387
388        self.dispose_region = Some((px, py, width, height));
389
390        limits.reserve_buffer(width, height, COLOR_TYPE)?;
392        let source = match self.inner.color_type {
393            ColorType::L8 => {
394                let image = ImageBuffer::<Luma<_>, _>::from_raw(width, height, buffer).unwrap();
395                DynamicImage::ImageLuma8(image).into_rgba8()
396            }
397            ColorType::La8 => {
398                let image = ImageBuffer::<LumaA<_>, _>::from_raw(width, height, buffer).unwrap();
399                DynamicImage::ImageLumaA8(image).into_rgba8()
400            }
401            ColorType::Rgb8 => {
402                let image = ImageBuffer::<Rgb<_>, _>::from_raw(width, height, buffer).unwrap();
403                DynamicImage::ImageRgb8(image).into_rgba8()
404            }
405            ColorType::Rgba8 => ImageBuffer::<Rgba<_>, _>::from_raw(width, height, buffer).unwrap(),
406            ColorType::L16 | ColorType::Rgb16 | ColorType::La16 | ColorType::Rgba16 => {
407                unreachable!("16-bit apng not yet support")
409            }
410            _ => unreachable!("Invalid png color"),
411        };
412        limits.free_usize(raw_frame_size);
414
415        match blend {
416            BlendOp::Source => {
417                current
418                    .copy_from(&source, px, py)
419                    .expect("Invalid png image not detected in png");
420            }
421            BlendOp::Over => {
422                for (x, y, p) in source.enumerate_pixels() {
424                    current.get_pixel_mut(x + px, y + py).blend(p);
425                }
426            }
427        }
428
429        self.remaining = remaining;
431        Ok(Some(self.current.as_ref().unwrap()))
434    }
435
436    fn animatable_color_type(&self) -> Result<(), ImageError> {
437        match self.inner.color_type {
438            ColorType::L8 | ColorType::Rgb8 | ColorType::La8 | ColorType::Rgba8 => Ok(()),
439            ColorType::L16 | ColorType::Rgb16 | ColorType::La16 | ColorType::Rgba16 => {
441                Err(unsupported_color(self.inner.color_type.into()))
442            }
443            _ => unreachable!("{:?} not a valid png color", self.inner.color_type),
444        }
445    }
446}
447
448impl<'a, R: BufRead + Seek + 'a> AnimationDecoder<'a> for ApngDecoder<R> {
449    fn into_frames(self) -> Frames<'a> {
450        struct FrameIterator<R: BufRead + Seek>(ApngDecoder<R>);
451
452        impl<R: BufRead + Seek> Iterator for FrameIterator<R> {
453            type Item = ImageResult<Frame>;
454
455            fn next(&mut self) -> Option<Self::Item> {
456                let image = match self.0.mix_next_frame() {
457                    Ok(Some(image)) => image.clone(),
458                    Ok(None) => return None,
459                    Err(err) => return Some(Err(err)),
460                };
461
462                let info = self.0.inner.reader.info();
463                let fc = info.frame_control().unwrap();
464                let num = u32::from(fc.delay_num) * 1_000u32;
466                let denom = match fc.delay_den {
467                    0 => 100,
469                    d => u32::from(d),
470                };
471                let delay = Delay::from_ratio(Ratio::new(num, denom));
472                Some(Ok(Frame::from_parts(image, 0, 0, delay)))
473            }
474        }
475
476        Frames::new(Box::new(FrameIterator(self)))
477    }
478}
479
480pub struct PngEncoder<W: Write> {
482    w: W,
483    compression: CompressionType,
484    filter: FilterType,
485    icc_profile: Vec<u8>,
486}
487
488#[derive(Clone, Copy, Debug, Eq, PartialEq)]
490#[non_exhaustive]
491#[derive(Default)]
492pub enum CompressionType {
493    Default,
495    #[default]
497    Fast,
498    Best,
500}
501
502#[derive(Clone, Copy, Debug, Eq, PartialEq)]
506#[non_exhaustive]
507#[derive(Default)]
508pub enum FilterType {
509    NoFilter,
512    Sub,
514    Up,
516    Avg,
518    Paeth,
520    #[default]
523    Adaptive,
524}
525
526#[derive(Clone, Copy, Debug, Eq, PartialEq)]
527#[non_exhaustive]
528enum BadPngRepresentation {
529    ColorType(ExtendedColorType),
530}
531
532impl<W: Write> PngEncoder<W> {
533    pub fn new(w: W) -> PngEncoder<W> {
535        PngEncoder {
536            w,
537            compression: CompressionType::default(),
538            filter: FilterType::default(),
539            icc_profile: Vec::new(),
540        }
541    }
542
543    pub fn new_with_quality(
556        w: W,
557        compression: CompressionType,
558        filter: FilterType,
559    ) -> PngEncoder<W> {
560        PngEncoder {
561            w,
562            compression,
563            filter,
564            icc_profile: Vec::new(),
565        }
566    }
567
568    fn encode_inner(
569        self,
570        data: &[u8],
571        width: u32,
572        height: u32,
573        color: ExtendedColorType,
574    ) -> ImageResult<()> {
575        let (ct, bits) = match color {
576            ExtendedColorType::L8 => (png::ColorType::Grayscale, png::BitDepth::Eight),
577            ExtendedColorType::L16 => (png::ColorType::Grayscale, png::BitDepth::Sixteen),
578            ExtendedColorType::La8 => (png::ColorType::GrayscaleAlpha, png::BitDepth::Eight),
579            ExtendedColorType::La16 => (png::ColorType::GrayscaleAlpha, png::BitDepth::Sixteen),
580            ExtendedColorType::Rgb8 => (png::ColorType::Rgb, png::BitDepth::Eight),
581            ExtendedColorType::Rgb16 => (png::ColorType::Rgb, png::BitDepth::Sixteen),
582            ExtendedColorType::Rgba8 => (png::ColorType::Rgba, png::BitDepth::Eight),
583            ExtendedColorType::Rgba16 => (png::ColorType::Rgba, png::BitDepth::Sixteen),
584            _ => {
585                return Err(ImageError::Unsupported(
586                    UnsupportedError::from_format_and_kind(
587                        ImageFormat::Png.into(),
588                        UnsupportedErrorKind::Color(color),
589                    ),
590                ))
591            }
592        };
593        let comp = match self.compression {
594            CompressionType::Default => png::Compression::Default,
595            CompressionType::Best => png::Compression::Best,
596            _ => png::Compression::Fast,
597        };
598        let (filter, adaptive_filter) = match self.filter {
599            FilterType::NoFilter => (
600                png::FilterType::NoFilter,
601                png::AdaptiveFilterType::NonAdaptive,
602            ),
603            FilterType::Sub => (png::FilterType::Sub, png::AdaptiveFilterType::NonAdaptive),
604            FilterType::Up => (png::FilterType::Up, png::AdaptiveFilterType::NonAdaptive),
605            FilterType::Avg => (png::FilterType::Avg, png::AdaptiveFilterType::NonAdaptive),
606            FilterType::Paeth => (png::FilterType::Paeth, png::AdaptiveFilterType::NonAdaptive),
607            FilterType::Adaptive => (png::FilterType::Sub, png::AdaptiveFilterType::Adaptive),
608        };
609
610        let mut info = png::Info::with_size(width, height);
611
612        if !self.icc_profile.is_empty() {
613            info.icc_profile = Some(Cow::Borrowed(&self.icc_profile));
614        }
615
616        let mut encoder =
617            png::Encoder::with_info(self.w, info).map_err(|e| ImageError::IoError(e.into()))?;
618
619        encoder.set_color(ct);
620        encoder.set_depth(bits);
621        encoder.set_compression(comp);
622        encoder.set_filter(filter);
623        encoder.set_adaptive_filter(adaptive_filter);
624        let mut writer = encoder
625            .write_header()
626            .map_err(|e| ImageError::IoError(e.into()))?;
627        writer
628            .write_image_data(data)
629            .map_err(|e| ImageError::IoError(e.into()))
630    }
631}
632
633impl<W: Write> ImageEncoder for PngEncoder<W> {
634    #[track_caller]
640    fn write_image(
641        self,
642        buf: &[u8],
643        width: u32,
644        height: u32,
645        color_type: ExtendedColorType,
646    ) -> ImageResult<()> {
647        use byteorder_lite::{BigEndian, ByteOrder, NativeEndian};
648        use ExtendedColorType::*;
649
650        let expected_buffer_len = color_type.buffer_size(width, height);
651        assert_eq!(
652            expected_buffer_len,
653            buf.len() as u64,
654            "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
655            buf.len(),
656        );
657
658        match color_type {
663            L8 | La8 | Rgb8 | Rgba8 => {
664                self.encode_inner(buf, width, height, color_type)
666            }
667            L16 | La16 | Rgb16 | Rgba16 => {
668                let mut reordered = vec![0; buf.len()];
672                buf.chunks_exact(2)
673                    .zip(reordered.chunks_exact_mut(2))
674                    .for_each(|(b, r)| BigEndian::write_u16(r, NativeEndian::read_u16(b)));
675                self.encode_inner(&reordered, width, height, color_type)
676            }
677            _ => Err(ImageError::Encoding(EncodingError::new(
678                ImageFormat::Png.into(),
679                BadPngRepresentation::ColorType(color_type),
680            ))),
681        }
682    }
683
684    fn set_icc_profile(&mut self, icc_profile: Vec<u8>) -> Result<(), UnsupportedError> {
685        self.icc_profile = icc_profile;
686        Ok(())
687    }
688}
689
690impl ImageError {
691    fn from_png(err: png::DecodingError) -> ImageError {
692        use png::DecodingError::*;
693        match err {
694            IoError(err) => ImageError::IoError(err),
695            err @ Format(_) => {
697                ImageError::Decoding(DecodingError::new(ImageFormat::Png.into(), err))
698            }
699            err @ Parameter(_) => ImageError::Parameter(ParameterError::from_kind(
704                ParameterErrorKind::Generic(err.to_string()),
705            )),
706            LimitsExceeded => {
707                ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
708            }
709        }
710    }
711}
712
713impl fmt::Display for BadPngRepresentation {
714    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
715        match self {
716            Self::ColorType(color_type) => {
717                write!(f, "The color {color_type:?} can not be represented in PNG.")
718            }
719        }
720    }
721}
722
723impl std::error::Error for BadPngRepresentation {}
724
725#[cfg(test)]
726mod tests {
727    use super::*;
728    use std::io::{BufReader, Cursor, Read};
729
730    #[test]
731    fn ensure_no_decoder_off_by_one() {
732        let dec = PngDecoder::new(BufReader::new(
733            std::fs::File::open("tests/images/png/bugfixes/debug_triangle_corners_widescreen.png")
734                .unwrap(),
735        ))
736        .expect("Unable to read PNG file (does it exist?)");
737
738        assert_eq![(2000, 1000), dec.dimensions()];
739
740        assert_eq![
741            ColorType::Rgb8,
742            dec.color_type(),
743            "Image MUST have the Rgb8 format"
744        ];
745
746        let correct_bytes = crate::image::decoder_to_vec(dec)
747            .expect("Unable to read file")
748            .bytes()
749            .map(|x| x.expect("Unable to read byte"))
750            .collect::<Vec<u8>>();
751
752        assert_eq![6_000_000, correct_bytes.len()];
753    }
754
755    #[test]
756    fn underlying_error() {
757        use std::error::Error;
758
759        let mut not_png =
760            std::fs::read("tests/images/png/bugfixes/debug_triangle_corners_widescreen.png")
761                .unwrap();
762        not_png[0] = 0;
763
764        let error = PngDecoder::new(Cursor::new(¬_png)).err().unwrap();
765        let _ = error
766            .source()
767            .unwrap()
768            .downcast_ref::<png::DecodingError>()
769            .expect("Caused by a png error");
770    }
771
772    #[test]
773    fn encode_bad_color_type() {
774        let image = DynamicImage::new_rgb32f(1, 1);
776        let mut target = Cursor::new(vec![]);
777        let _ = image.write_to(&mut target, ImageFormat::Png);
778    }
779}