1use std::borrow::Cow;
9use std::io::{BufRead, Seek, Write};
10
11use png::{BlendOp, DisposeOp};
12
13use crate::animation::{Delay, Frame, Frames, Ratio};
14use crate::color::{Blend, ColorType, ExtendedColorType};
15use crate::error::{
16 DecodingError, ImageError, ImageResult, LimitError, LimitErrorKind, ParameterError,
17 ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
18};
19use crate::utils::vec_try_with_capacity;
20use crate::{
21 AnimationDecoder, DynamicImage, GenericImage, GenericImageView, ImageBuffer, ImageDecoder,
22 ImageEncoder, ImageFormat, Limits, Luma, LumaA, Rgb, Rgba, RgbaImage,
23};
24
25pub(crate) const PNG_SIGNATURE: [u8; 8] = [137, 80, 78, 71, 13, 10, 26, 10];
28
29pub struct PngDecoder<R: BufRead + Seek> {
31 color_type: ColorType,
32 reader: png::Reader<R>,
33 limits: Limits,
34}
35
36impl<R: BufRead + Seek> PngDecoder<R> {
37 pub fn new(r: R) -> ImageResult<PngDecoder<R>> {
39 Self::with_limits(r, Limits::no_limits())
40 }
41
42 pub fn with_limits(r: R, limits: Limits) -> ImageResult<PngDecoder<R>> {
44 limits.check_support(&crate::LimitSupport::default())?;
45
46 let max_bytes = usize::try_from(limits.max_alloc.unwrap_or(u64::MAX)).unwrap_or(usize::MAX);
47 let mut decoder = png::Decoder::new_with_limits(r, png::Limits { bytes: max_bytes });
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 exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
181 Ok(self
182 .reader
183 .info()
184 .exif_metadata
185 .as_ref()
186 .map(|x| x.to_vec()))
187 }
188
189 fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
190 use byteorder_lite::{BigEndian, ByteOrder, NativeEndian};
191
192 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
193 self.reader.next_frame(buf).map_err(ImageError::from_png)?;
194 let bpc = self.color_type().bytes_per_pixel() / self.color_type().channel_count();
199
200 match bpc {
201 1 => (), 2 => buf.chunks_exact_mut(2).for_each(|c| {
203 let v = BigEndian::read_u16(c);
204 NativeEndian::write_u16(c, v);
205 }),
206 _ => unreachable!(),
207 }
208 Ok(())
209 }
210
211 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
212 (*self).read_image(buf)
213 }
214
215 fn set_limits(&mut self, limits: Limits) -> ImageResult<()> {
216 limits.check_support(&crate::LimitSupport::default())?;
217 let info = self.reader.info();
218 limits.check_dimensions(info.width, info.height)?;
219 self.limits = limits;
220 Ok(())
223 }
224}
225
226pub struct ApngDecoder<R: BufRead + Seek> {
234 inner: PngDecoder<R>,
235 current: Option<RgbaImage>,
237 previous: Option<RgbaImage>,
239 dispose: DisposeOp,
241
242 dispose_region: Option<(u32, u32, u32, u32)>,
244 remaining: u32,
246 has_thumbnail: bool,
248}
249
250impl<R: BufRead + Seek> ApngDecoder<R> {
251 fn new(inner: PngDecoder<R>) -> Self {
252 let info = inner.reader.info();
253 let remaining = match info.animation_control() {
254 Some(actl) => actl.num_frames,
256 None => 0,
257 };
258 let has_thumbnail = info.frame_control.is_none();
261 ApngDecoder {
262 inner,
263 current: None,
264 previous: None,
265 dispose: DisposeOp::Background,
266 dispose_region: None,
267 remaining,
268 has_thumbnail,
269 }
270 }
271
272 fn mix_next_frame(&mut self) -> Result<Option<&RgbaImage>, ImageError> {
276 const COLOR_TYPE: ColorType = ColorType::Rgba8;
278
279 let (width, height) = self.inner.dimensions();
281 {
282 let limits = &mut self.inner.limits;
283 if self.previous.is_none() {
284 limits.reserve_buffer(width, height, COLOR_TYPE)?;
285 self.previous = Some(RgbaImage::new(width, height));
286 }
287
288 if self.current.is_none() {
289 limits.reserve_buffer(width, height, COLOR_TYPE)?;
290 self.current = Some(RgbaImage::new(width, height));
291 }
292 }
293
294 self.remaining = match self.remaining.checked_sub(1) {
296 None => return Ok(None),
297 Some(next) => next,
298 };
299
300 let remaining = self.remaining;
302 self.remaining = 0;
303
304 if self.has_thumbnail {
306 let mut limits = self.inner.limits.clone();
308
309 let buffer_size = self.inner.reader.output_buffer_size().ok_or_else(|| {
310 ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
311 })?;
312
313 limits.reserve_usize(buffer_size)?;
314 let mut buffer = vec![0; buffer_size];
315 self.inner
318 .reader
319 .next_frame(&mut buffer)
320 .map_err(ImageError::from_png)?;
321 self.has_thumbnail = false;
322 }
323
324 self.animatable_color_type()?;
325
326 let previous = self.previous.as_mut().unwrap();
328 let current = self.current.as_mut().unwrap();
329
330 match self.dispose {
333 DisposeOp::None => {
334 previous.clone_from(current);
335 }
336 DisposeOp::Background => {
337 previous.clone_from(current);
338 if let Some((px, py, width, height)) = self.dispose_region {
339 let mut region_current = current.sub_image(px, py, width, height);
340
341 let pixels: Vec<_> = region_current.pixels().collect();
343
344 for (x, y, _) in &pixels {
345 region_current.put_pixel(*x, *y, Rgba::from([0, 0, 0, 0]));
346 }
347 } else {
348 current.pixels_mut().for_each(|pixel| {
350 *pixel = Rgba::from([0, 0, 0, 0]);
351 });
352 }
353 }
354 DisposeOp::Previous => {
355 let (px, py, width, height) = self
356 .dispose_region
357 .expect("The first frame must not set dispose=Previous");
358 let region_previous = previous.sub_image(px, py, width, height);
359 current
360 .copy_from(®ion_previous.to_image(), px, py)
361 .unwrap();
362 }
363 }
364
365 let mut limits = self.inner.limits.clone();
369
370 let raw_frame_size = self.inner.reader.output_buffer_size().ok_or_else(|| {
372 ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
373 })?;
374
375 limits.reserve_usize(raw_frame_size)?;
376 let mut buffer = vec![0; raw_frame_size];
377 self.inner
380 .reader
381 .next_frame(&mut buffer)
382 .map_err(ImageError::from_png)?;
383 let info = self.inner.reader.info();
384
385 let (width, height, px, py, blend);
387 match info.frame_control() {
388 None => {
389 width = info.width;
390 height = info.height;
391 px = 0;
392 py = 0;
393 blend = BlendOp::Source;
394 }
395 Some(fc) => {
396 width = fc.width;
397 height = fc.height;
398 px = fc.x_offset;
399 py = fc.y_offset;
400 blend = fc.blend_op;
401 self.dispose = fc.dispose_op;
402 }
403 }
404
405 self.dispose_region = Some((px, py, width, height));
406
407 limits.reserve_buffer(width, height, COLOR_TYPE)?;
409 let source = match self.inner.color_type {
410 ColorType::L8 => {
411 let image = ImageBuffer::<Luma<_>, _>::from_raw(width, height, buffer).unwrap();
412 DynamicImage::ImageLuma8(image).into_rgba8()
413 }
414 ColorType::La8 => {
415 let image = ImageBuffer::<LumaA<_>, _>::from_raw(width, height, buffer).unwrap();
416 DynamicImage::ImageLumaA8(image).into_rgba8()
417 }
418 ColorType::Rgb8 => {
419 let image = ImageBuffer::<Rgb<_>, _>::from_raw(width, height, buffer).unwrap();
420 DynamicImage::ImageRgb8(image).into_rgba8()
421 }
422 ColorType::Rgba8 => ImageBuffer::<Rgba<_>, _>::from_raw(width, height, buffer).unwrap(),
423 ColorType::L16 | ColorType::Rgb16 | ColorType::La16 | ColorType::Rgba16 => {
424 unreachable!("16-bit apng not yet support")
426 }
427 _ => unreachable!("Invalid png color"),
428 };
429 limits.free_usize(raw_frame_size);
431
432 match blend {
433 BlendOp::Source => {
434 current
435 .copy_from(&source, px, py)
436 .expect("Invalid png image not detected in png");
437 }
438 BlendOp::Over => {
439 for (x, y, p) in source.enumerate_pixels() {
441 current.get_pixel_mut(x + px, y + py).blend(p);
442 }
443 }
444 }
445
446 self.remaining = remaining;
448 Ok(Some(self.current.as_ref().unwrap()))
451 }
452
453 fn animatable_color_type(&self) -> Result<(), ImageError> {
454 match self.inner.color_type {
455 ColorType::L8 | ColorType::Rgb8 | ColorType::La8 | ColorType::Rgba8 => Ok(()),
456 ColorType::L16 | ColorType::Rgb16 | ColorType::La16 | ColorType::Rgba16 => {
458 Err(unsupported_color(self.inner.color_type.into()))
459 }
460 _ => unreachable!("{:?} not a valid png color", self.inner.color_type),
461 }
462 }
463}
464
465impl<'a, R: BufRead + Seek + 'a> AnimationDecoder<'a> for ApngDecoder<R> {
466 fn into_frames(self) -> Frames<'a> {
467 struct FrameIterator<R: BufRead + Seek>(ApngDecoder<R>);
468
469 impl<R: BufRead + Seek> Iterator for FrameIterator<R> {
470 type Item = ImageResult<Frame>;
471
472 fn next(&mut self) -> Option<Self::Item> {
473 let image = match self.0.mix_next_frame() {
474 Ok(Some(image)) => image.clone(),
475 Ok(None) => return None,
476 Err(err) => return Some(Err(err)),
477 };
478
479 let info = self.0.inner.reader.info();
480 let fc = info.frame_control().unwrap();
481 let num = u32::from(fc.delay_num) * 1_000u32;
483 let denom = match fc.delay_den {
484 0 => 100,
486 d => u32::from(d),
487 };
488 let delay = Delay::from_ratio(Ratio::new(num, denom));
489 Some(Ok(Frame::from_parts(image, 0, 0, delay)))
490 }
491 }
492
493 Frames::new(Box::new(FrameIterator(self)))
494 }
495}
496
497pub struct PngEncoder<W: Write> {
499 w: W,
500 compression: CompressionType,
501 filter: FilterType,
502 icc_profile: Vec<u8>,
503 exif_metadata: Vec<u8>,
504}
505
506#[derive(Clone, Copy, Debug, Eq, PartialEq)]
508#[non_exhaustive]
509#[derive(Default)]
510pub enum CompressionType {
511 Default,
513 #[default]
515 Fast,
516 Best,
518}
519
520#[derive(Clone, Copy, Debug, Eq, PartialEq)]
524#[non_exhaustive]
525#[derive(Default)]
526pub enum FilterType {
527 NoFilter,
530 Sub,
532 Up,
534 Avg,
536 Paeth,
538 #[default]
541 Adaptive,
542}
543
544impl<W: Write> PngEncoder<W> {
545 pub fn new(w: W) -> PngEncoder<W> {
547 PngEncoder {
548 w,
549 compression: CompressionType::default(),
550 filter: FilterType::default(),
551 icc_profile: Vec::new(),
552 exif_metadata: Vec::new(),
553 }
554 }
555
556 pub fn new_with_quality(
569 w: W,
570 compression: CompressionType,
571 filter: FilterType,
572 ) -> PngEncoder<W> {
573 PngEncoder {
574 w,
575 compression,
576 filter,
577 icc_profile: Vec::new(),
578 exif_metadata: Vec::new(),
579 }
580 }
581
582 fn encode_inner(
583 self,
584 data: &[u8],
585 width: u32,
586 height: u32,
587 color: ExtendedColorType,
588 ) -> ImageResult<()> {
589 let (ct, bits) = match color {
590 ExtendedColorType::L8 => (png::ColorType::Grayscale, png::BitDepth::Eight),
591 ExtendedColorType::L16 => (png::ColorType::Grayscale, png::BitDepth::Sixteen),
592 ExtendedColorType::La8 => (png::ColorType::GrayscaleAlpha, png::BitDepth::Eight),
593 ExtendedColorType::La16 => (png::ColorType::GrayscaleAlpha, png::BitDepth::Sixteen),
594 ExtendedColorType::Rgb8 => (png::ColorType::Rgb, png::BitDepth::Eight),
595 ExtendedColorType::Rgb16 => (png::ColorType::Rgb, png::BitDepth::Sixteen),
596 ExtendedColorType::Rgba8 => (png::ColorType::Rgba, png::BitDepth::Eight),
597 ExtendedColorType::Rgba16 => (png::ColorType::Rgba, png::BitDepth::Sixteen),
598 _ => {
599 return Err(ImageError::Unsupported(
600 UnsupportedError::from_format_and_kind(
601 ImageFormat::Png.into(),
602 UnsupportedErrorKind::Color(color),
603 ),
604 ))
605 }
606 };
607
608 let comp = match self.compression {
609 CompressionType::Default => png::Compression::Balanced,
610 CompressionType::Best => png::Compression::High,
611 _ => png::Compression::Fast,
612 };
613
614 let filter = match self.filter {
615 FilterType::NoFilter => png::Filter::NoFilter,
616 FilterType::Sub => png::Filter::Sub,
617 FilterType::Up => png::Filter::Up,
618 FilterType::Avg => png::Filter::Avg,
619 FilterType::Paeth => png::Filter::Paeth,
620 FilterType::Adaptive => png::Filter::Adaptive,
621 };
622
623 let mut info = png::Info::with_size(width, height);
624
625 if !self.icc_profile.is_empty() {
626 info.icc_profile = Some(Cow::Borrowed(&self.icc_profile));
627 }
628 if !self.exif_metadata.is_empty() {
629 info.exif_metadata = Some(Cow::Borrowed(&self.exif_metadata));
630 }
631
632 let mut encoder =
633 png::Encoder::with_info(self.w, info).map_err(|e| ImageError::IoError(e.into()))?;
634
635 encoder.set_color(ct);
636 encoder.set_depth(bits);
637 encoder.set_compression(comp);
638 encoder.set_filter(filter);
639 let mut writer = encoder
640 .write_header()
641 .map_err(|e| ImageError::IoError(e.into()))?;
642 writer
643 .write_image_data(data)
644 .map_err(|e| ImageError::IoError(e.into()))
645 }
646}
647
648impl<W: Write> ImageEncoder for PngEncoder<W> {
649 #[track_caller]
655 fn write_image(
656 self,
657 buf: &[u8],
658 width: u32,
659 height: u32,
660 color_type: ExtendedColorType,
661 ) -> ImageResult<()> {
662 use ExtendedColorType::*;
663
664 let expected_buffer_len = color_type.buffer_size(width, height);
665 assert_eq!(
666 expected_buffer_len,
667 buf.len() as u64,
668 "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
669 buf.len(),
670 );
671
672 match color_type {
677 L8 | La8 | Rgb8 | Rgba8 => {
678 self.encode_inner(buf, width, height, color_type)
680 }
681 L16 | La16 | Rgb16 | Rgba16 => {
682 let mut reordered;
686 let buf = if cfg!(target_endian = "little") {
687 reordered = vec_try_with_capacity(buf.len())?;
688 reordered.extend(buf.chunks_exact(2).flat_map(|le| [le[1], le[0]]));
689 &reordered
690 } else {
691 buf
692 };
693 self.encode_inner(buf, width, height, color_type)
694 }
695 _ => Err(ImageError::Unsupported(
696 UnsupportedError::from_format_and_kind(
697 ImageFormat::Hdr.into(),
698 UnsupportedErrorKind::Color(color_type),
699 ),
700 )),
701 }
702 }
703
704 fn set_icc_profile(&mut self, icc_profile: Vec<u8>) -> Result<(), UnsupportedError> {
705 self.icc_profile = icc_profile;
706 Ok(())
707 }
708
709 fn set_exif_metadata(&mut self, exif: Vec<u8>) -> Result<(), UnsupportedError> {
710 self.exif_metadata = exif;
711 Ok(())
712 }
713}
714
715impl ImageError {
716 fn from_png(err: png::DecodingError) -> ImageError {
717 use png::DecodingError::*;
718 match err {
719 IoError(err) => ImageError::IoError(err),
720 err @ Format(_) => {
722 ImageError::Decoding(DecodingError::new(ImageFormat::Png.into(), err))
723 }
724 err @ Parameter(_) => ImageError::Parameter(ParameterError::from_kind(
729 ParameterErrorKind::Generic(err.to_string()),
730 )),
731 LimitsExceeded => {
732 ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
733 }
734 }
735 }
736}
737
738#[cfg(test)]
739mod tests {
740 use super::*;
741 use crate::io::free_functions::decoder_to_vec;
742 use std::io::{BufReader, Cursor, Read};
743
744 #[test]
745 fn ensure_no_decoder_off_by_one() {
746 let dec = PngDecoder::new(BufReader::new(
747 std::fs::File::open("tests/images/png/bugfixes/debug_triangle_corners_widescreen.png")
748 .unwrap(),
749 ))
750 .expect("Unable to read PNG file (does it exist?)");
751
752 assert_eq![(2000, 1000), dec.dimensions()];
753
754 assert_eq![
755 ColorType::Rgb8,
756 dec.color_type(),
757 "Image MUST have the Rgb8 format"
758 ];
759
760 let correct_bytes = decoder_to_vec(dec)
761 .expect("Unable to read file")
762 .bytes()
763 .map(|x| x.expect("Unable to read byte"))
764 .collect::<Vec<u8>>();
765
766 assert_eq![6_000_000, correct_bytes.len()];
767 }
768
769 #[test]
770 fn underlying_error() {
771 use std::error::Error;
772
773 let mut not_png =
774 std::fs::read("tests/images/png/bugfixes/debug_triangle_corners_widescreen.png")
775 .unwrap();
776 not_png[0] = 0;
777
778 let error = PngDecoder::new(Cursor::new(¬_png)).err().unwrap();
779 let _ = error
780 .source()
781 .unwrap()
782 .downcast_ref::<png::DecodingError>()
783 .expect("Caused by a png error");
784 }
785
786 #[test]
787 fn encode_bad_color_type() {
788 let image = DynamicImage::new_rgb32f(1, 1);
790 let mut target = Cursor::new(vec![]);
791 let _ = image.write_to(&mut target, ImageFormat::Png);
792 }
793}