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}