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

Provided by KDAB

Privacy Policy