1use otf_pixels_core::{
26 Codec, DecodeCapability, Decoder, Format, ImageDescriptor, Limits, Orientation, PixelFormat,
27 PixelsError, Result, Source,
28};
29
30use crate::format::{
31 ChunkReader, ChunkStream, ColorType, Filter, Header, SIGNATURE, adam7_pass_size,
32 adam7_position, unfilter,
33};
34use otf_pixels_compress::{ZlibStream, zlib_decompress};
35
36const MAX_PLTE: usize = 256 * 3;
38const MAX_TRNS: usize = 256;
40const READ_CHUNK: usize = 64 * 1024;
42const EXIF_PREFIX: usize = 64 * 1024;
48
49const MAX_ICC: usize = 4 << 20;
53
54#[derive(Debug, Clone)]
56enum Transparency {
57 Gray(u16),
59 Rgb(u16, u16, u16),
61 Palette(Vec<u8>),
63}
64
65fn parse_iccp(data: &[u8]) -> Option<Vec<u8>> {
68 let nul = data
69 .iter()
70 .position(|&b| b == 0)
71 .filter(|&n| (1..=79).contains(&n))?;
72 let (&method, compressed) = data.get(nul + 1..)?.split_first()?;
73 if method != 0 {
74 return None;
75 }
76 zlib_decompress(compressed, MAX_ICC).ok()
77}
78
79#[derive(Debug)]
81pub struct PngDecoder<S: Source> {
82 header: Header,
83 descriptor: ImageDescriptor,
84 prelude: Option<Prelude<S>>,
86 orientation: Orientation,
87 exif: Option<Vec<u8>>,
89 icc: Option<Vec<u8>>,
91 raster: Option<Vec<u8>>,
96 stream: Option<Box<Streaming<S>>>,
98 row: u32,
99}
100
101#[derive(Debug)]
103struct Prelude<S: Source> {
104 chunks: ChunkStream<S>,
106 palette: Option<Vec<[u8; 3]>>,
107 transparency: Option<Transparency>,
108}
109
110#[derive(Debug)]
116struct Streaming<S: Source> {
117 chunks: ChunkStream<S>,
118 zlib: ZlibStream,
119 filtered: Vec<u8>,
121 at: usize,
123 previous: Vec<u8>,
125 palette: Option<Vec<[u8; 3]>>,
126 transparency: Option<Transparency>,
127 input_done: bool,
129}
130
131impl<S: Source> PngDecoder<S> {
132 pub fn new(mut source: S, limits: Limits) -> Result<Self> {
140 let mut prefix = vec![0_u8; 33];
142 source.read_exact(&mut prefix)?;
143
144 let mut reader = ChunkReader::new(&prefix)?;
145 let chunk = reader.next_chunk()?;
146 if !chunk.is(b"IHDR") {
147 return Err(PixelsError::malformed(
148 "png",
149 format!("first chunk must be IHDR, got `{}`", chunk.name()),
150 ));
151 }
152 let header = Header::parse(&chunk.data, &limits)?;
153
154 let mut chunks = ChunkStream::new(source);
155 let mut palette: Option<Vec<[u8; 3]>> = None;
156 let mut transparency: Option<Transparency> = None;
157 let mut orientation = None;
158 let mut exif: Option<Vec<u8>> = None;
159 let mut icc = None;
160 loop {
161 let kind = chunks.open_next()?;
162 match &kind {
163 b"IHDR" => {
164 return Err(PixelsError::malformed("png", "more than one IHDR"));
165 }
166 b"PLTE" => {
167 let data = chunks.read_payload_to_end(MAX_PLTE)?;
168 palette = Some(parse_plte(&data)?);
169 chunks.close()?;
170 }
171 b"tRNS" => {
172 let data = chunks.read_payload_to_end(MAX_TRNS)?;
173 transparency = Some(parse_trns(&data, header.color_type)?);
174 chunks.close()?;
175 }
176 b"eXIf" => {
177 let mut block = vec![0_u8; EXIF_PREFIX + 1];
180 let mut filled = 0;
181 while let Some(rest) = block.get_mut(filled..).filter(|r| !r.is_empty()) {
182 match chunks.read_payload(rest)? {
183 0 => break,
184 n => filled += n,
185 }
186 }
187 block.truncate(filled);
188 chunks.skip_payload()?;
189 chunks.close()?;
190 orientation = orientation.or_else(|| Orientation::from_exif_block(&block));
192 if exif.is_none() && filled <= EXIF_PREFIX {
193 let bare = block.strip_prefix(b"Exif\0\0").unwrap_or(&block);
194 exif = Some(bare.to_vec());
195 }
196 }
197 b"iCCP" => {
198 let mut data = vec![0_u8; MAX_ICC + 1];
200 let mut filled = 0;
201 while let Some(rest) = data.get_mut(filled..).filter(|r| !r.is_empty()) {
202 match chunks.read_payload(rest)? {
203 0 => break,
204 n => filled += n,
205 }
206 }
207 data.truncate(filled);
208 chunks.skip_payload()?;
209 chunks.close()?;
210 if filled <= MAX_ICC {
213 icc = icc.or_else(|| parse_iccp(&data));
214 }
215 }
216 b"IDAT" => break,
217 b"IEND" => {
218 return Err(PixelsError::malformed("png", "no IDAT data"));
219 }
220 _ => {
221 if !chunks.is_ancillary() {
222 return Err(PixelsError::malformed(
223 "png",
224 format!("unknown critical chunk `{}`", chunks.name()),
225 ));
226 }
227 chunks.skip_payload()?;
228 chunks.close()?;
229 }
230 }
231 }
232
233 if header.color_type == ColorType::Palette && palette.is_none() {
234 return Err(PixelsError::malformed(
235 "png",
236 "palette image has no PLTE chunk",
237 ));
238 }
239 let descriptor = header.descriptor(transparency.is_some(), &limits)?;
240
241 Ok(Self {
242 header,
243 descriptor,
244 prelude: Some(Prelude {
245 chunks,
246 palette,
247 transparency,
248 }),
249 orientation: orientation.unwrap_or_default(),
250 icc,
251 exif,
252 raster: None,
253 stream: None,
254 row: 0,
255 })
256 }
257
258 #[must_use]
260 pub const fn header(&self) -> Header {
261 self.header
262 }
263
264 fn begin_streaming(&mut self) -> Result<Streaming<S>> {
266 let Some(Prelude {
267 chunks,
268 palette,
269 transparency,
270 }) = self.prelude.take()
271 else {
272 return Err(PixelsError::graph("png source was already consumed"));
273 };
274
275 let row_bytes = self.header.row_bytes(self.header.width);
276 Ok(Streaming {
277 chunks,
278 zlib: ZlibStream::new(self.header.filtered_size()),
281 filtered: Vec::new(),
282 at: 0,
283 previous: vec![0_u8; row_bytes],
284 palette,
285 transparency,
286 input_done: false,
287 })
288 }
289
290 fn decode_image(&mut self) -> Result<Vec<u8>> {
292 let Some(Prelude {
293 mut chunks,
294 palette,
295 transparency,
296 }) = self.prelude.take()
297 else {
298 return Err(PixelsError::graph("png source was already consumed"));
299 };
300
301 let mut compressed: Vec<u8> = Vec::new();
304 let mut buffer = vec![0_u8; READ_CHUNK];
305 loop {
306 loop {
307 let read = chunks.read_payload(&mut buffer)?;
308 if read == 0 {
309 break;
310 }
311 compressed.extend_from_slice(buffer.get(..read).unwrap_or(&[]));
312 }
313 chunks.close()?;
314 match &chunks.open_next()? {
315 b"IDAT" => {}
316 b"IEND" => {
317 chunks.skip_payload()?;
318 chunks.close()?;
319 break;
320 }
321 _ => {
322 if !chunks.is_ancillary() {
325 return Err(PixelsError::malformed(
326 "png",
327 format!("unknown critical chunk `{}`", chunks.name()),
328 ));
329 }
330 chunks.skip_payload()?;
331 }
332 }
333 }
334
335 let filtered = zlib_decompress(&compressed, self.header.filtered_size())
338 .map_err(crate::compress_error)?;
339 let samples = self.unfilter_all(&filtered)?;
340 self.expand(&samples, palette.as_deref(), transparency.as_ref())
341 }
342
343 fn unfilter_all(&self, filtered: &[u8]) -> Result<Vec<u8>> {
348 let stride = self.header.filter_stride();
349 let full_row = self.header.row_bytes(self.header.width);
350
351 if !self.header.interlaced {
352 let mut out = vec![0_u8; self.header.height as usize * full_row];
353 let mut previous = vec![0_u8; full_row];
354 let mut at = 0;
355 for y in 0..self.header.height as usize {
356 let filter_byte = filtered
357 .get(at)
358 .copied()
359 .ok_or_else(|| PixelsError::malformed("png", "raster ends early"))?;
360 let filter = Filter::from_byte(filter_byte)?;
361 at += 1;
362 let row = filtered
363 .get(at..at + full_row)
364 .ok_or_else(|| PixelsError::malformed("png", "scanline ends early"))?;
365 at += full_row;
366
367 let mut current = row.to_vec();
368 unfilter(filter, &mut current, &previous, stride)?;
369 let start = y * full_row;
370 if let Some(slot) = out.get_mut(start..start + full_row) {
371 slot.copy_from_slice(¤t);
372 }
373 previous = current;
374 }
375 return Ok(out);
376 }
377
378 let mut out = vec![0_u8; self.header.height as usize * full_row];
381 let mut at = 0;
382 for pass in 0..7 {
383 let (pass_width, pass_height) =
384 adam7_pass_size(pass, self.header.width, self.header.height);
385 if pass_width == 0 || pass_height == 0 {
386 continue;
387 }
388 let pass_row = self.header.row_bytes(pass_width);
389 let mut previous = vec![0_u8; pass_row];
390 for y in 0..pass_height {
391 let filter_byte = filtered.get(at).copied().ok_or_else(|| {
392 PixelsError::malformed("png", format!("pass {pass} ends early"))
393 })?;
394 let filter = Filter::from_byte(filter_byte)?;
395 at += 1;
396 let row = filtered.get(at..at + pass_row).ok_or_else(|| {
397 PixelsError::malformed("png", format!("pass {pass} scanline ends early"))
398 })?;
399 at += pass_row;
400
401 let mut current = row.to_vec();
402 unfilter(filter, &mut current, &previous, stride)?;
403 for x in 0..pass_width {
404 let (image_x, image_y) = adam7_position(pass, x, y);
405 copy_pixel_bits(
406 ¤t,
407 x as usize,
408 &mut out,
409 image_y as usize * full_row,
410 image_x as usize,
411 self.header.bits_per_pixel(),
412 );
413 }
414 previous = current;
415 }
416 }
417 Ok(out)
418 }
419
420 fn expand(
422 &self,
423 samples: &[u8],
424 palette: Option<&[[u8; 3]]>,
425 transparency: Option<&Transparency>,
426 ) -> Result<Vec<u8>> {
427 let height = self.header.height as usize;
428 let row_bytes = self.header.row_bytes(self.header.width);
429 let out_row = self.descriptor.row_bytes();
430 let mut out = vec![0_u8; height * out_row];
431
432 for y in 0..height {
433 let row = samples
434 .get(y * row_bytes..(y + 1) * row_bytes)
435 .ok_or_else(|| PixelsError::malformed("png", "sample row missing"))?;
436 let Some(slot) = out.get_mut(y * out_row..(y + 1) * out_row) else {
437 return Err(PixelsError::graph("output row is missing"));
438 };
439 self.expand_row(row, palette, transparency, slot)?;
440 }
441 Ok(out)
442 }
443
444 fn expand_row(
451 &self,
452 samples: &[u8],
453 palette: Option<&[[u8; 3]]>,
454 transparency: Option<&Transparency>,
455 out: &mut [u8],
456 ) -> Result<()> {
457 let format = self.descriptor.pixel;
458 let width = self.header.width as usize;
459 let depth = self.header.bit_depth;
460 let channels = self.header.color_type.channels();
461 let max = ((1_u32 << depth) - 1) as u16;
462
463 if depth == 8 {
464 let same_layout = matches!(
468 (self.header.color_type, format),
469 (ColorType::Rgb, PixelFormat::Rgb8)
470 | (ColorType::Rgba, PixelFormat::Rgba8)
471 | (ColorType::Grayscale, PixelFormat::Gray8)
472 | (ColorType::GrayscaleAlpha, PixelFormat::GrayA8)
473 );
474 if same_layout && samples.len() == out.len() {
475 out.copy_from_slice(samples);
476 return Ok(());
477 }
478 if matches!(self.header.color_type, ColorType::Palette) {
479 return expand_palette_row(samples, format, palette, transparency, out);
480 }
481 }
482
483 let mut at = 0;
484 for x in 0..width {
485 let mut channel = [0_u16; 4];
486 for (c, slot) in channel.iter_mut().take(channels).enumerate() {
487 *slot = read_sample(samples, x * channels + c, depth);
488 }
489 write_pixel(
490 out,
491 &mut at,
492 format,
493 self.header.color_type,
494 &channel,
495 depth,
496 max,
497 palette,
498 transparency,
499 )?;
500 }
501 Ok(())
502 }
503
504 fn read_row_buffered(&mut self, out: &mut [u8]) -> Result<()> {
506 if self.raster.is_none() {
507 self.raster = Some(self.decode_image()?);
508 }
509 let Some(raster) = self.raster.as_ref() else {
510 return Err(PixelsError::graph("raster vanished after decoding"));
511 };
512 if self.row >= self.descriptor.height {
513 return Err(PixelsError::invalid_argument(
514 "out",
515 format!("all {} rows have already been read", self.descriptor.height),
516 ));
517 }
518 let row_bytes = self.descriptor.row_bytes();
519 if out.len() != row_bytes {
520 return Err(PixelsError::invalid_argument(
521 "out",
522 format!("row buffer is {} bytes, expected {row_bytes}", out.len()),
523 ));
524 }
525 let start = self.row as usize * row_bytes;
526 let row = raster
527 .get(start..start + row_bytes)
528 .ok_or_else(|| PixelsError::malformed("png", "decoded raster is short"))?;
529 out.copy_from_slice(row);
530 self.row += 1;
531 Ok(())
532 }
533}
534
535impl<S: Source> Streaming<S> {
536 fn refill(&mut self) -> Result<bool> {
541 if self.input_done {
542 return Ok(false);
543 }
544 let mut buffer = vec![0_u8; READ_CHUNK];
545
546 loop {
550 if self.chunks.payload_done() {
551 self.chunks.close()?;
552 loop {
554 let kind = self.chunks.open_next()?;
555 match &kind {
556 b"IDAT" => break,
557 b"IEND" => {
558 self.chunks.skip_payload()?;
562 self.chunks.close()?;
563 self.input_done = true;
564 let tail = self.zlib.finish().map_err(crate::compress_error)?;
565 self.filtered.extend_from_slice(&tail);
566 return Ok(!tail.is_empty());
567 }
568 _ => {
569 if !self.chunks.is_ancillary() {
570 return Err(PixelsError::malformed(
571 "png",
572 format!("unknown critical chunk `{}`", self.chunks.name()),
573 ));
574 }
575 self.chunks.skip_payload()?;
576 self.chunks.close()?;
577 }
578 }
579 }
580 }
581
582 let read = self.chunks.read_payload(&mut buffer)?;
583 if read == 0 {
584 continue;
585 }
586 let produced = self
587 .zlib
588 .push(buffer.get(..read).unwrap_or(&[]))
589 .map_err(crate::compress_error)?;
590 if !produced.is_empty() {
591 self.filtered.extend_from_slice(&produced);
592 return Ok(true);
593 }
594 }
595 }
596
597 fn finalize(&mut self) -> Result<()> {
604 while self.refill()? {}
605 if !self.input_done {
606 return Err(PixelsError::malformed(
607 "png",
608 "stream ends without an IEND chunk",
609 ));
610 }
611 Ok(())
612 }
613
614 fn next_scanline(&mut self, row_bytes: usize, stride: usize) -> Result<Vec<u8>> {
616 let want = row_bytes + 1;
617 while self.filtered.len() - self.at < want {
618 if !self.refill()? {
619 return Err(PixelsError::malformed(
620 "png",
621 "image data ends before the last scanline",
622 ));
623 }
624 }
625
626 let filter_byte = self
627 .filtered
628 .get(self.at)
629 .copied()
630 .ok_or_else(|| PixelsError::malformed("png", "scanline ends early"))?;
631 let filter = Filter::from_byte(filter_byte)?;
632 let start = self.at + 1;
633 let mut current = self
634 .filtered
635 .get(start..start + row_bytes)
636 .ok_or_else(|| PixelsError::malformed("png", "scanline ends early"))?
637 .to_vec();
638 self.at = start + row_bytes;
639
640 if self.at >= self.filtered.len() {
643 self.filtered.clear();
644 self.at = 0;
645 } else if self.at > READ_CHUNK {
646 self.filtered.drain(..self.at);
647 self.at = 0;
648 }
649
650 unfilter(filter, &mut current, &self.previous, stride)?;
651 self.previous.clear();
652 self.previous.extend_from_slice(¤t);
653 Ok(current)
654 }
655}
656
657fn parse_plte(data: &[u8]) -> Result<Vec<[u8; 3]>> {
659 if data.len() % 3 != 0 || data.is_empty() {
660 return Err(PixelsError::malformed(
661 "png",
662 format!("PLTE length {} is not a positive multiple of 3", data.len()),
663 ));
664 }
665 if data.len() / 3 > 256 {
666 return Err(PixelsError::malformed(
667 "png",
668 "PLTE has more than 256 entries",
669 ));
670 }
671 Ok(data
672 .chunks_exact(3)
673 .map(|rgb| {
674 [
675 rgb.first().copied().unwrap_or(0),
676 rgb.get(1).copied().unwrap_or(0),
677 rgb.get(2).copied().unwrap_or(0),
678 ]
679 })
680 .collect())
681}
682
683fn copy_pixel_bits(
685 source_row: &[u8],
686 source_x: usize,
687 out: &mut [u8],
688 row_start: usize,
689 dest_x: usize,
690 bits_per_pixel: usize,
691) {
692 if bits_per_pixel >= 8 {
693 let bytes = bits_per_pixel / 8;
694 for byte in 0..bytes {
695 let value = source_row
696 .get(source_x * bytes + byte)
697 .copied()
698 .unwrap_or(0);
699 if let Some(slot) = out.get_mut(row_start + dest_x * bytes + byte) {
700 *slot = value;
701 }
702 }
703 return;
704 }
705 let value = read_bits(source_row, source_x, bits_per_pixel);
707 write_bits(out, row_start, dest_x, bits_per_pixel, value);
708}
709
710fn read_bits(row: &[u8], index: usize, bits: usize) -> u8 {
712 let per_byte = 8 / bits;
713 let byte = row.get(index / per_byte).copied().unwrap_or(0);
714 let shift = 8 - bits * (index % per_byte + 1);
715 (byte >> shift) & ((1 << bits) - 1) as u8
716}
717
718fn write_bits(out: &mut [u8], row_start: usize, index: usize, bits: usize, value: u8) {
720 let per_byte = 8 / bits;
721 let offset = row_start + index / per_byte;
722 let shift = 8 - bits * (index % per_byte + 1);
723 let mask = ((1 << bits) - 1) as u8;
724 if let Some(slot) = out.get_mut(offset) {
725 *slot = (*slot & !(mask << shift)) | ((value & mask) << shift);
726 }
727}
728
729fn read_sample(row: &[u8], index: usize, depth: u8) -> u16 {
731 match depth {
732 16 => {
733 let high = row.get(index * 2).copied().unwrap_or(0);
735 let low = row.get(index * 2 + 1).copied().unwrap_or(0);
736 u16::from_be_bytes([high, low])
737 }
738 8 => u16::from(row.get(index).copied().unwrap_or(0)),
739 bits => u16::from(read_bits(row, index, bits as usize)),
740 }
741}
742
743const fn scale8(value: u16, max: u16) -> u8 {
748 if max == 0 {
749 return 0;
750 }
751 ((value as u32 * 255 + max as u32 / 2) / max as u32) as u8
752}
753
754fn expand_palette_row(
759 indices: &[u8],
760 format: PixelFormat,
761 palette: Option<&[[u8; 3]]>,
762 transparency: Option<&Transparency>,
763 out: &mut [u8],
764) -> Result<()> {
765 let entries =
766 palette.ok_or_else(|| PixelsError::malformed("png", "palette image without a palette"))?;
767 let alphas = match transparency {
768 Some(Transparency::Palette(alphas)) => alphas.as_slice(),
769 _ => &[],
770 };
771 let channels = if format == PixelFormat::Rgba8 { 4 } else { 3 };
772 for (&index, pixel) in indices.iter().zip(out.chunks_exact_mut(channels)) {
773 let index = index as usize;
774 let rgb = entries.get(index).ok_or_else(|| {
775 PixelsError::malformed(
776 "png",
777 format!(
778 "palette index {index} is beyond the {}-entry palette",
779 entries.len()
780 ),
781 )
782 })?;
783 for (slot, &value) in pixel.iter_mut().zip(rgb) {
784 *slot = value;
785 }
786 if let Some(alpha) = pixel.get_mut(3) {
787 *alpha = alphas.get(index).copied().unwrap_or(255);
789 }
790 }
791 Ok(())
792}
793
794#[allow(
796 clippy::too_many_arguments,
797 reason = "one pixel conversion needs all of it"
798)]
799fn write_pixel(
800 out: &mut [u8],
801 at: &mut usize,
802 format: PixelFormat,
803 color_type: ColorType,
804 channel: &[u16; 4],
805 depth: u8,
806 max: u16,
807 palette: Option<&[[u8; 3]]>,
808 transparency: Option<&Transparency>,
809) -> Result<()> {
810 fn push(out: &mut [u8], at: &mut usize, value: u8) {
812 if let Some(slot) = out.get_mut(*at) {
813 *slot = value;
814 }
815 *at += 1;
816 }
817 fn push16(out: &mut [u8], at: &mut usize, value: u16) {
819 for byte in value.to_ne_bytes() {
820 push(out, at, byte);
821 }
822 }
823
824 match color_type {
825 ColorType::Palette => {
826 let index = channel[0] as usize;
827 let entries = palette
828 .ok_or_else(|| PixelsError::malformed("png", "palette image without a palette"))?;
829 let rgb = entries.get(index).copied().ok_or_else(|| {
830 PixelsError::malformed(
831 "png",
832 format!(
833 "palette index {index} is beyond the {}-entry palette",
834 entries.len()
835 ),
836 )
837 })?;
838 push(out, at, rgb[0]);
839 push(out, at, rgb[1]);
840 push(out, at, rgb[2]);
841 if format == PixelFormat::Rgba8 {
842 let alpha = match transparency {
843 Some(Transparency::Palette(alphas)) => {
844 alphas.get(index).copied().unwrap_or(255)
846 }
847 _ => 255,
848 };
849 push(out, at, alpha);
850 }
851 }
852 ColorType::Grayscale => {
853 let transparent =
854 matches!(transparency, Some(Transparency::Gray(key)) if *key == channel[0]);
855 match format {
856 PixelFormat::Gray8 => push(out, at, scale8(channel[0], max)),
857 PixelFormat::Gray16 => push16(out, at, channel[0]),
858 PixelFormat::GrayA8 => {
859 push(out, at, scale8(channel[0], max));
860 push(out, at, if transparent { 0 } else { 255 });
861 }
862 PixelFormat::Rgba16 => {
863 let value = if depth == 16 {
864 channel[0]
865 } else {
866 channel[0] * 257
867 };
868 push16(out, at, value);
869 push16(out, at, value);
870 push16(out, at, value);
871 push16(out, at, if transparent { 0 } else { u16::MAX });
872 }
873 other => {
874 return Err(PixelsError::unsupported(format!(
875 "greyscale cannot be written as {other}"
876 )));
877 }
878 }
879 }
880 ColorType::GrayscaleAlpha => match format {
881 PixelFormat::GrayA8 => {
882 push(out, at, scale8(channel[0], max));
883 push(out, at, scale8(channel[1], max));
884 }
885 PixelFormat::Rgba16 => {
886 push16(out, at, channel[0]);
887 push16(out, at, channel[0]);
888 push16(out, at, channel[0]);
889 push16(out, at, channel[1]);
890 }
891 other => {
892 return Err(PixelsError::unsupported(format!(
893 "grey+alpha cannot be written as {other}"
894 )));
895 }
896 },
897 ColorType::Rgb => {
898 let transparent = matches!(
899 transparency,
900 Some(Transparency::Rgb(r, g, b))
901 if *r == channel[0] && *g == channel[1] && *b == channel[2]
902 );
903 match format {
904 PixelFormat::Rgb8 => {
905 for &value in channel.iter().take(3) {
906 push(out, at, scale8(value, max));
907 }
908 }
909 PixelFormat::Rgb16 => {
910 for &value in channel.iter().take(3) {
911 push16(out, at, value);
912 }
913 }
914 PixelFormat::Rgba8 => {
915 for &value in channel.iter().take(3) {
916 push(out, at, scale8(value, max));
917 }
918 push(out, at, if transparent { 0 } else { 255 });
919 }
920 PixelFormat::Rgba16 => {
921 for &value in channel.iter().take(3) {
922 push16(out, at, value);
923 }
924 push16(out, at, if transparent { 0 } else { u16::MAX });
925 }
926 other => {
927 return Err(PixelsError::unsupported(format!(
928 "RGB cannot be written as {other}"
929 )));
930 }
931 }
932 }
933 ColorType::Rgba => match format {
934 PixelFormat::Rgba8 => {
935 for &value in channel {
936 push(out, at, scale8(value, max));
937 }
938 }
939 PixelFormat::Rgba16 => {
940 for &value in channel {
941 push16(out, at, value);
942 }
943 }
944 other => {
945 return Err(PixelsError::unsupported(format!(
946 "RGBA cannot be written as {other}"
947 )));
948 }
949 },
950 }
951 Ok(())
952}
953
954fn parse_trns(data: &[u8], color_type: ColorType) -> Result<Transparency> {
956 fn be16(data: &[u8], offset: usize) -> u16 {
958 u16::from_be_bytes([
959 data.get(offset).copied().unwrap_or(0),
960 data.get(offset + 1).copied().unwrap_or(0),
961 ])
962 }
963 match color_type {
964 ColorType::Grayscale => {
965 if data.len() != 2 {
966 return Err(PixelsError::malformed(
967 "png",
968 format!("greyscale tRNS must be 2 bytes, got {}", data.len()),
969 ));
970 }
971 Ok(Transparency::Gray(be16(data, 0)))
972 }
973 ColorType::Rgb => {
974 if data.len() != 6 {
975 return Err(PixelsError::malformed(
976 "png",
977 format!("RGB tRNS must be 6 bytes, got {}", data.len()),
978 ));
979 }
980 Ok(Transparency::Rgb(
981 be16(data, 0),
982 be16(data, 2),
983 be16(data, 4),
984 ))
985 }
986 ColorType::Palette => {
987 if data.len() > 256 {
988 return Err(PixelsError::malformed(
989 "png",
990 format!(
991 "palette tRNS has {} entries, over the 256 maximum",
992 data.len()
993 ),
994 ));
995 }
996 Ok(Transparency::Palette(data.to_vec()))
997 }
998 ColorType::GrayscaleAlpha | ColorType::Rgba => Err(PixelsError::malformed(
1000 "png",
1001 "tRNS is not allowed for colour types that already carry alpha",
1002 )),
1003 }
1004}
1005
1006impl<S: Source + std::fmt::Debug> Decoder for PngDecoder<S> {
1007 fn descriptor(&self) -> ImageDescriptor {
1008 self.descriptor
1009 }
1010
1011 fn orientation(&self) -> Orientation {
1014 self.orientation
1015 }
1016
1017 fn icc_profile(&self) -> Option<&[u8]> {
1019 self.icc.as_deref()
1020 }
1021
1022 fn exif(&self) -> Option<&[u8]> {
1024 self.exif.as_deref()
1025 }
1026
1027 fn capability(&self) -> DecodeCapability {
1028 DecodeCapability::Sequential
1033 }
1034
1035 fn read_row(&mut self, out: &mut [u8]) -> Result<()> {
1036 if self.header.interlaced {
1039 return self.read_row_buffered(out);
1040 }
1041 if self.stream.is_none() {
1042 let started = self.begin_streaming()?;
1043 self.stream = Some(Box::new(started));
1044 }
1045 if self.row >= self.descriptor.height {
1046 return Err(PixelsError::invalid_argument(
1047 "out",
1048 format!("all {} rows have already been read", self.descriptor.height),
1049 ));
1050 }
1051 let row_bytes = self.descriptor.row_bytes();
1052 if out.len() != row_bytes {
1053 return Err(PixelsError::invalid_argument(
1054 "out",
1055 format!("row buffer is {} bytes, expected {row_bytes}", out.len()),
1056 ));
1057 }
1058
1059 let sample_bytes = self.header.row_bytes(self.header.width);
1060 let stride = self.header.filter_stride();
1061 let Some(stream) = self.stream.as_mut() else {
1062 return Err(PixelsError::graph("png stream vanished after starting"));
1063 };
1064 let samples = stream.next_scanline(sample_bytes, stride)?;
1065 let Some(stream) = self.stream.as_deref() else {
1066 return Err(PixelsError::graph("png stream vanished after starting"));
1067 };
1068 self.expand_row(
1069 &samples,
1070 stream.palette.as_deref(),
1071 stream.transparency.as_ref(),
1072 out,
1073 )?;
1074 self.row += 1;
1075
1076 if self.row == self.descriptor.height {
1079 if let Some(stream) = self.stream.as_mut() {
1080 stream.finalize()?;
1081 }
1082 }
1083 Ok(())
1084 }
1085}
1086
1087#[must_use]
1091pub fn probe(prefix: &[u8]) -> bool {
1092 prefix.get(..8) == Some(&SIGNATURE[..])
1093}
1094
1095#[derive(Debug, Clone, Copy, Default)]
1101pub struct PngCodec;
1102
1103impl Codec for PngCodec {
1104 fn format(&self) -> Format {
1105 Format::Png
1106 }
1107
1108 fn magic_len(&self) -> usize {
1109 SIGNATURE.len()
1110 }
1111
1112 fn probe(&self, prefix: &[u8]) -> bool {
1113 probe(prefix)
1114 }
1115}