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