1use otf_pixels_core::{
26 Background, Codec, Comment, DecodeCapability, Decoder, Density, Format, ImageDescriptor,
27 Limits, Orientation, PixelFormat, PixelsError, ResolutionUnit, Result, Source, SourceInfo,
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
79const MAX_TEXT: usize = 1 << 20;
81
82const XMP_KEYWORD: &[u8] = b"XML:com.adobe.xmp";
84
85fn parse_text(kind: &[u8; 4], data: &[u8]) -> Option<(Vec<u8>, String)> {
88 let nul = data
89 .iter()
90 .position(|&b| b == 0)
91 .filter(|&n| (1..=79).contains(&n))?;
92 let keyword = data.get(..nul)?.to_vec();
93 let rest = data.get(nul + 1..)?;
94 let latin1 = |b: &[u8]| b.iter().map(|&c| char::from(c)).collect::<String>();
95 match kind {
96 b"tEXt" => Some((keyword, latin1(rest))),
97 b"zTXt" => {
98 let (&method, compressed) = rest.split_first()?;
99 (method == 0).then_some(())?;
100 Some((
101 keyword,
102 latin1(&zlib_decompress(compressed, MAX_TEXT).ok()?),
103 ))
104 }
105 _ => {
106 let (&compressed, rest) = rest.split_first()?;
107 let (&method, rest) = rest.split_first()?;
108 let lang = rest.iter().position(|&b| b == 0)?;
109 let rest = rest.get(lang + 1..)?;
110 let translated = rest.iter().position(|&b| b == 0)?;
111 let text = rest.get(translated + 1..)?;
112 let text = if compressed == 1 {
113 (method == 0).then_some(())?;
114 zlib_decompress(text, MAX_TEXT).ok()?
115 } else {
116 text.to_vec()
117 };
118 Some((keyword, String::from_utf8(text).ok()?))
119 }
120 }
121}
122
123fn read_capped<S: Source>(chunks: &mut ChunkStream<S>, cap: usize) -> Result<Vec<u8>> {
126 let mut data = vec![0_u8; cap + 1];
127 let mut filled = 0;
128 while let Some(rest) = data.get_mut(filled..).filter(|r| !r.is_empty()) {
129 match chunks.read_payload(rest)? {
130 0 => break,
131 n => filled += n,
132 }
133 }
134 data.truncate(filled);
135 chunks.skip_payload()?;
136 chunks.close()?;
137 Ok(data)
138}
139
140#[derive(Debug)]
142pub struct PngDecoder<S: Source> {
143 header: Header,
144 descriptor: ImageDescriptor,
145 prelude: Option<Prelude<S>>,
147 orientation: Orientation,
148 exif: Option<Vec<u8>>,
150 icc: Option<Vec<u8>>,
152 xmp: Option<Vec<u8>>,
154 info: SourceInfo,
156 raster: Option<Vec<u8>>,
161 stream: Option<Box<Streaming<S>>>,
163 row: u32,
164}
165
166#[derive(Debug)]
168struct Prelude<S: Source> {
169 chunks: ChunkStream<S>,
171 palette: Option<Vec<[u8; 3]>>,
172 transparency: Option<Transparency>,
173}
174
175#[derive(Debug)]
181struct Streaming<S: Source> {
182 chunks: ChunkStream<S>,
183 zlib: ZlibStream,
184 filtered: Vec<u8>,
186 at: usize,
188 previous: Vec<u8>,
190 palette: Option<Vec<[u8; 3]>>,
191 transparency: Option<Transparency>,
192 input_done: bool,
194}
195
196impl<S: Source> PngDecoder<S> {
197 pub fn new(mut source: S, limits: Limits) -> Result<Self> {
205 let mut prefix = vec![0_u8; 33];
207 source.read_exact(&mut prefix)?;
208
209 let mut reader = ChunkReader::new(&prefix)?;
210 let chunk = reader.next_chunk()?;
211 if !chunk.is(b"IHDR") {
212 return Err(PixelsError::malformed(
213 "png",
214 format!("first chunk must be IHDR, got `{}`", chunk.name()),
215 ));
216 }
217 let header = Header::parse(&chunk.data, &limits)?;
218
219 let mut chunks = ChunkStream::new(source);
220 let mut palette: Option<Vec<[u8; 3]>> = None;
221 let mut transparency: Option<Transparency> = None;
222 let mut orientation = None;
223 let mut exif: Option<Vec<u8>> = None;
224 let mut icc = None;
225 let mut xmp = None;
226 let mut info = SourceInfo::default();
227 let mut background_index = None;
228 loop {
229 let kind = chunks.open_next()?;
230 match &kind {
231 b"IHDR" => {
232 return Err(PixelsError::malformed("png", "more than one IHDR"));
233 }
234 b"PLTE" => {
235 let data = chunks.read_payload_to_end(MAX_PLTE)?;
236 palette = Some(parse_plte(&data)?);
237 chunks.close()?;
238 }
239 b"tRNS" => {
240 let data = chunks.read_payload_to_end(MAX_TRNS)?;
241 transparency = Some(parse_trns(&data, header.color_type)?);
242 chunks.close()?;
243 }
244 b"eXIf" => {
245 let mut block = vec![0_u8; EXIF_PREFIX + 1];
248 let mut filled = 0;
249 while let Some(rest) = block.get_mut(filled..).filter(|r| !r.is_empty()) {
250 match chunks.read_payload(rest)? {
251 0 => break,
252 n => filled += n,
253 }
254 }
255 block.truncate(filled);
256 chunks.skip_payload()?;
257 chunks.close()?;
258 orientation = orientation.or_else(|| Orientation::from_exif_block(&block));
260 if exif.is_none() && filled <= EXIF_PREFIX {
261 let bare = block.strip_prefix(b"Exif\0\0").unwrap_or(&block);
262 exif = Some(bare.to_vec());
263 }
264 }
265 b"iCCP" => {
266 let mut data = vec![0_u8; MAX_ICC + 1];
268 let mut filled = 0;
269 while let Some(rest) = data.get_mut(filled..).filter(|r| !r.is_empty()) {
270 match chunks.read_payload(rest)? {
271 0 => break,
272 n => filled += n,
273 }
274 }
275 data.truncate(filled);
276 chunks.skip_payload()?;
277 chunks.close()?;
278 if filled <= MAX_ICC {
281 icc = icc.or_else(|| parse_iccp(&data));
282 }
283 }
284 kind @ (b"tEXt" | b"zTXt" | b"iTXt") => {
285 let kind = *kind;
286 let data = read_capped(&mut chunks, MAX_TEXT)?;
287 if let Some((keyword, text)) = (data.len() <= MAX_TEXT)
289 .then(|| parse_text(&kind, &data))
290 .flatten()
291 {
292 if keyword == XMP_KEYWORD {
293 xmp = xmp.or(Some(text.into_bytes()));
294 } else {
295 info.comments.push(Comment {
296 keyword: keyword.iter().map(|&c| char::from(c)).collect(),
297 text,
298 });
299 }
300 }
301 }
302 b"pHYs" => {
303 let data = read_capped(&mut chunks, 9)?;
304 if let [x0, x1, x2, x3, y0, y1, y2, y3, 1] = data[..] {
305 let x = f64::from(u32::from_be_bytes([x0, x1, x2, x3])) / 100.0;
307 let y = f64::from(u32::from_be_bytes([y0, y1, y2, y3])) / 100.0;
308 info.density = Some(Density {
309 x,
310 y,
311 unit: ResolutionUnit::Centimetre,
312 });
313 }
314 }
315 b"bKGD" => {
316 let data = read_capped(&mut chunks, 6)?;
317 let word = |i: usize| {
318 data.get(i..i + 2)
319 .and_then(|b| <[u8; 2]>::try_from(b).ok())
320 .map(u16::from_be_bytes)
321 };
322 info.background = match data.len() {
323 1 => {
324 background_index = data.first().copied();
325 None
326 }
327 2 => word(0).map(Background::Grey),
328 6 => match (word(0), word(2), word(4)) {
329 (Some(r), Some(g), Some(b)) => Some(Background::Rgb(r, g, b)),
330 _ => None,
331 },
332 _ => None,
333 };
334 }
335 b"IDAT" => break,
336 b"IEND" => {
337 return Err(PixelsError::malformed("png", "no IDAT data"));
338 }
339 _ => {
340 if !chunks.is_ancillary() {
341 return Err(PixelsError::malformed(
342 "png",
343 format!("unknown critical chunk `{}`", chunks.name()),
344 ));
345 }
346 chunks.skip_payload()?;
347 chunks.close()?;
348 }
349 }
350 }
351
352 if header.color_type == ColorType::Palette && palette.is_none() {
353 return Err(PixelsError::malformed(
354 "png",
355 "palette image has no PLTE chunk",
356 ));
357 }
358 let descriptor = header.descriptor(transparency.is_some(), &limits)?;
359 if let (Some(i), Some(entries)) = (background_index, &palette) {
360 info.background = entries
361 .get(usize::from(i))
362 .map(|&[r, g, b]| Background::Rgb(r.into(), g.into(), b.into()));
363 }
364 info.progressive = header.interlaced;
365 info.palette = header.color_type == ColorType::Palette;
366 info.bits_per_sample = Some(header.bit_depth);
367
368 Ok(Self {
369 header,
370 descriptor,
371 prelude: Some(Prelude {
372 chunks,
373 palette,
374 transparency,
375 }),
376 orientation: orientation.unwrap_or_default(),
377 icc,
378 exif,
379 xmp,
380 info,
381 raster: None,
382 stream: None,
383 row: 0,
384 })
385 }
386
387 #[must_use]
389 pub const fn header(&self) -> Header {
390 self.header
391 }
392
393 fn begin_streaming(&mut self) -> Result<Streaming<S>> {
395 let Some(Prelude {
396 chunks,
397 palette,
398 transparency,
399 }) = self.prelude.take()
400 else {
401 return Err(PixelsError::graph("png source was already consumed"));
402 };
403
404 let row_bytes = self.header.row_bytes(self.header.width);
405 Ok(Streaming {
406 chunks,
407 zlib: ZlibStream::new(self.header.filtered_size()),
410 filtered: Vec::new(),
411 at: 0,
412 previous: vec![0_u8; row_bytes],
413 palette,
414 transparency,
415 input_done: false,
416 })
417 }
418
419 fn decode_image(&mut self) -> Result<Vec<u8>> {
421 let Some(Prelude {
422 mut chunks,
423 palette,
424 transparency,
425 }) = self.prelude.take()
426 else {
427 return Err(PixelsError::graph("png source was already consumed"));
428 };
429
430 let mut compressed: Vec<u8> = Vec::new();
433 let mut buffer = vec![0_u8; READ_CHUNK];
434 loop {
435 loop {
436 let read = chunks.read_payload(&mut buffer)?;
437 if read == 0 {
438 break;
439 }
440 compressed.extend_from_slice(buffer.get(..read).unwrap_or(&[]));
441 }
442 chunks.close()?;
443 match &chunks.open_next()? {
444 b"IDAT" => {}
445 b"IEND" => {
446 chunks.skip_payload()?;
447 chunks.close()?;
448 break;
449 }
450 _ => {
451 if !chunks.is_ancillary() {
454 return Err(PixelsError::malformed(
455 "png",
456 format!("unknown critical chunk `{}`", chunks.name()),
457 ));
458 }
459 chunks.skip_payload()?;
460 }
461 }
462 }
463
464 let filtered = zlib_decompress(&compressed, self.header.filtered_size())
467 .map_err(crate::compress_error)?;
468 let samples = self.unfilter_all(&filtered)?;
469 self.expand(&samples, palette.as_deref(), transparency.as_ref())
470 }
471
472 fn unfilter_all(&self, filtered: &[u8]) -> Result<Vec<u8>> {
477 let stride = self.header.filter_stride();
478 let full_row = self.header.row_bytes(self.header.width);
479
480 if !self.header.interlaced {
481 let mut out = vec![0_u8; self.header.height as usize * full_row];
482 let mut previous = vec![0_u8; full_row];
483 let mut at = 0;
484 for y in 0..self.header.height as usize {
485 let filter_byte = filtered
486 .get(at)
487 .copied()
488 .ok_or_else(|| PixelsError::malformed("png", "raster ends early"))?;
489 let filter = Filter::from_byte(filter_byte)?;
490 at += 1;
491 let row = filtered
492 .get(at..at + full_row)
493 .ok_or_else(|| PixelsError::malformed("png", "scanline ends early"))?;
494 at += full_row;
495
496 let mut current = row.to_vec();
497 unfilter(filter, &mut current, &previous, stride)?;
498 let start = y * full_row;
499 if let Some(slot) = out.get_mut(start..start + full_row) {
500 slot.copy_from_slice(¤t);
501 }
502 previous = current;
503 }
504 return Ok(out);
505 }
506
507 let mut out = vec![0_u8; self.header.height as usize * full_row];
510 let mut at = 0;
511 for pass in 0..7 {
512 let (pass_width, pass_height) =
513 adam7_pass_size(pass, self.header.width, self.header.height);
514 if pass_width == 0 || pass_height == 0 {
515 continue;
516 }
517 let pass_row = self.header.row_bytes(pass_width);
518 let mut previous = vec![0_u8; pass_row];
519 for y in 0..pass_height {
520 let filter_byte = filtered.get(at).copied().ok_or_else(|| {
521 PixelsError::malformed("png", format!("pass {pass} ends early"))
522 })?;
523 let filter = Filter::from_byte(filter_byte)?;
524 at += 1;
525 let row = filtered.get(at..at + pass_row).ok_or_else(|| {
526 PixelsError::malformed("png", format!("pass {pass} scanline ends early"))
527 })?;
528 at += pass_row;
529
530 let mut current = row.to_vec();
531 unfilter(filter, &mut current, &previous, stride)?;
532 for x in 0..pass_width {
533 let (image_x, image_y) = adam7_position(pass, x, y);
534 copy_pixel_bits(
535 ¤t,
536 x as usize,
537 &mut out,
538 image_y as usize * full_row,
539 image_x as usize,
540 self.header.bits_per_pixel(),
541 );
542 }
543 previous = current;
544 }
545 }
546 Ok(out)
547 }
548
549 fn expand(
551 &self,
552 samples: &[u8],
553 palette: Option<&[[u8; 3]]>,
554 transparency: Option<&Transparency>,
555 ) -> Result<Vec<u8>> {
556 let height = self.header.height as usize;
557 let row_bytes = self.header.row_bytes(self.header.width);
558 let out_row = self.descriptor.row_bytes();
559 let mut out = vec![0_u8; height * out_row];
560
561 for y in 0..height {
562 let row = samples
563 .get(y * row_bytes..(y + 1) * row_bytes)
564 .ok_or_else(|| PixelsError::malformed("png", "sample row missing"))?;
565 let Some(slot) = out.get_mut(y * out_row..(y + 1) * out_row) else {
566 return Err(PixelsError::graph("output row is missing"));
567 };
568 self.expand_row(row, palette, transparency, slot)?;
569 }
570 Ok(out)
571 }
572
573 fn expand_row(
580 &self,
581 samples: &[u8],
582 palette: Option<&[[u8; 3]]>,
583 transparency: Option<&Transparency>,
584 out: &mut [u8],
585 ) -> Result<()> {
586 let format = self.descriptor.pixel;
587 let width = self.header.width as usize;
588 let depth = self.header.bit_depth;
589 let channels = self.header.color_type.channels();
590 let max = ((1_u32 << depth) - 1) as u16;
591
592 if depth == 8 {
593 let same_layout = matches!(
597 (self.header.color_type, format),
598 (ColorType::Rgb, PixelFormat::Rgb8)
599 | (ColorType::Rgba, PixelFormat::Rgba8)
600 | (ColorType::Grayscale, PixelFormat::Gray8)
601 | (ColorType::GrayscaleAlpha, PixelFormat::GrayA8)
602 );
603 if same_layout && samples.len() == out.len() {
604 out.copy_from_slice(samples);
605 return Ok(());
606 }
607 if matches!(self.header.color_type, ColorType::Palette) {
608 return expand_palette_row(samples, format, palette, transparency, out);
609 }
610 }
611
612 let mut at = 0;
613 for x in 0..width {
614 let mut channel = [0_u16; 4];
615 for (c, slot) in channel.iter_mut().take(channels).enumerate() {
616 *slot = read_sample(samples, x * channels + c, depth);
617 }
618 write_pixel(
619 out,
620 &mut at,
621 format,
622 self.header.color_type,
623 &channel,
624 depth,
625 max,
626 palette,
627 transparency,
628 )?;
629 }
630 Ok(())
631 }
632
633 fn read_row_buffered(&mut self, out: &mut [u8]) -> Result<()> {
635 if self.raster.is_none() {
636 self.raster = Some(self.decode_image()?);
637 }
638 let Some(raster) = self.raster.as_ref() else {
639 return Err(PixelsError::graph("raster vanished after decoding"));
640 };
641 if self.row >= self.descriptor.height {
642 return Err(PixelsError::invalid_argument(
643 "out",
644 format!("all {} rows have already been read", self.descriptor.height),
645 ));
646 }
647 let row_bytes = self.descriptor.row_bytes();
648 if out.len() != row_bytes {
649 return Err(PixelsError::invalid_argument(
650 "out",
651 format!("row buffer is {} bytes, expected {row_bytes}", out.len()),
652 ));
653 }
654 let start = self.row as usize * row_bytes;
655 let row = raster
656 .get(start..start + row_bytes)
657 .ok_or_else(|| PixelsError::malformed("png", "decoded raster is short"))?;
658 out.copy_from_slice(row);
659 self.row += 1;
660 Ok(())
661 }
662}
663
664impl<S: Source> Streaming<S> {
665 fn refill(&mut self) -> Result<bool> {
670 if self.input_done {
671 return Ok(false);
672 }
673 let mut buffer = vec![0_u8; READ_CHUNK];
674
675 loop {
679 if self.chunks.payload_done() {
680 self.chunks.close()?;
681 loop {
683 let kind = self.chunks.open_next()?;
684 match &kind {
685 b"IDAT" => break,
686 b"IEND" => {
687 self.chunks.skip_payload()?;
691 self.chunks.close()?;
692 self.input_done = true;
693 let tail = self.zlib.finish().map_err(crate::compress_error)?;
694 self.filtered.extend_from_slice(&tail);
695 return Ok(!tail.is_empty());
696 }
697 _ => {
698 if !self.chunks.is_ancillary() {
699 return Err(PixelsError::malformed(
700 "png",
701 format!("unknown critical chunk `{}`", self.chunks.name()),
702 ));
703 }
704 self.chunks.skip_payload()?;
705 self.chunks.close()?;
706 }
707 }
708 }
709 }
710
711 let read = self.chunks.read_payload(&mut buffer)?;
712 if read == 0 {
713 continue;
714 }
715 let produced = self
716 .zlib
717 .push(buffer.get(..read).unwrap_or(&[]))
718 .map_err(crate::compress_error)?;
719 if !produced.is_empty() {
720 self.filtered.extend_from_slice(&produced);
721 return Ok(true);
722 }
723 }
724 }
725
726 fn finalize(&mut self) -> Result<()> {
733 while self.refill()? {}
734 if !self.input_done {
735 return Err(PixelsError::malformed(
736 "png",
737 "stream ends without an IEND chunk",
738 ));
739 }
740 Ok(())
741 }
742
743 fn next_scanline(&mut self, row_bytes: usize, stride: usize) -> Result<Vec<u8>> {
745 let want = row_bytes + 1;
746 while self.filtered.len() - self.at < want {
747 if !self.refill()? {
748 return Err(PixelsError::malformed(
749 "png",
750 "image data ends before the last scanline",
751 ));
752 }
753 }
754
755 let filter_byte = self
756 .filtered
757 .get(self.at)
758 .copied()
759 .ok_or_else(|| PixelsError::malformed("png", "scanline ends early"))?;
760 let filter = Filter::from_byte(filter_byte)?;
761 let start = self.at + 1;
762 let mut current = self
763 .filtered
764 .get(start..start + row_bytes)
765 .ok_or_else(|| PixelsError::malformed("png", "scanline ends early"))?
766 .to_vec();
767 self.at = start + row_bytes;
768
769 if self.at >= self.filtered.len() {
772 self.filtered.clear();
773 self.at = 0;
774 } else if self.at > READ_CHUNK {
775 self.filtered.drain(..self.at);
776 self.at = 0;
777 }
778
779 unfilter(filter, &mut current, &self.previous, stride)?;
780 self.previous.clear();
781 self.previous.extend_from_slice(¤t);
782 Ok(current)
783 }
784}
785
786fn parse_plte(data: &[u8]) -> Result<Vec<[u8; 3]>> {
788 if data.len() % 3 != 0 || data.is_empty() {
789 return Err(PixelsError::malformed(
790 "png",
791 format!("PLTE length {} is not a positive multiple of 3", data.len()),
792 ));
793 }
794 if data.len() / 3 > 256 {
795 return Err(PixelsError::malformed(
796 "png",
797 "PLTE has more than 256 entries",
798 ));
799 }
800 Ok(data
801 .chunks_exact(3)
802 .map(|rgb| {
803 [
804 rgb.first().copied().unwrap_or(0),
805 rgb.get(1).copied().unwrap_or(0),
806 rgb.get(2).copied().unwrap_or(0),
807 ]
808 })
809 .collect())
810}
811
812fn copy_pixel_bits(
814 source_row: &[u8],
815 source_x: usize,
816 out: &mut [u8],
817 row_start: usize,
818 dest_x: usize,
819 bits_per_pixel: usize,
820) {
821 if bits_per_pixel >= 8 {
822 let bytes = bits_per_pixel / 8;
823 for byte in 0..bytes {
824 let value = source_row
825 .get(source_x * bytes + byte)
826 .copied()
827 .unwrap_or(0);
828 if let Some(slot) = out.get_mut(row_start + dest_x * bytes + byte) {
829 *slot = value;
830 }
831 }
832 return;
833 }
834 let value = read_bits(source_row, source_x, bits_per_pixel);
836 write_bits(out, row_start, dest_x, bits_per_pixel, value);
837}
838
839fn read_bits(row: &[u8], index: usize, bits: usize) -> u8 {
841 let per_byte = 8 / bits;
842 let byte = row.get(index / per_byte).copied().unwrap_or(0);
843 let shift = 8 - bits * (index % per_byte + 1);
844 (byte >> shift) & ((1 << bits) - 1) as u8
845}
846
847fn write_bits(out: &mut [u8], row_start: usize, index: usize, bits: usize, value: u8) {
849 let per_byte = 8 / bits;
850 let offset = row_start + index / per_byte;
851 let shift = 8 - bits * (index % per_byte + 1);
852 let mask = ((1 << bits) - 1) as u8;
853 if let Some(slot) = out.get_mut(offset) {
854 *slot = (*slot & !(mask << shift)) | ((value & mask) << shift);
855 }
856}
857
858fn read_sample(row: &[u8], index: usize, depth: u8) -> u16 {
860 match depth {
861 16 => {
862 let high = row.get(index * 2).copied().unwrap_or(0);
864 let low = row.get(index * 2 + 1).copied().unwrap_or(0);
865 u16::from_be_bytes([high, low])
866 }
867 8 => u16::from(row.get(index).copied().unwrap_or(0)),
868 bits => u16::from(read_bits(row, index, bits as usize)),
869 }
870}
871
872const fn scale8(value: u16, max: u16) -> u8 {
877 if max == 0 {
878 return 0;
879 }
880 ((value as u32 * 255 + max as u32 / 2) / max as u32) as u8
881}
882
883fn expand_palette_row(
888 indices: &[u8],
889 format: PixelFormat,
890 palette: Option<&[[u8; 3]]>,
891 transparency: Option<&Transparency>,
892 out: &mut [u8],
893) -> Result<()> {
894 let entries =
895 palette.ok_or_else(|| PixelsError::malformed("png", "palette image without a palette"))?;
896 let alphas = match transparency {
897 Some(Transparency::Palette(alphas)) => alphas.as_slice(),
898 _ => &[],
899 };
900 let channels = if format == PixelFormat::Rgba8 { 4 } else { 3 };
901 for (&index, pixel) in indices.iter().zip(out.chunks_exact_mut(channels)) {
902 let index = index as usize;
903 let rgb = entries.get(index).ok_or_else(|| {
904 PixelsError::malformed(
905 "png",
906 format!(
907 "palette index {index} is beyond the {}-entry palette",
908 entries.len()
909 ),
910 )
911 })?;
912 for (slot, &value) in pixel.iter_mut().zip(rgb) {
913 *slot = value;
914 }
915 if let Some(alpha) = pixel.get_mut(3) {
916 *alpha = alphas.get(index).copied().unwrap_or(255);
918 }
919 }
920 Ok(())
921}
922
923#[allow(
925 clippy::too_many_arguments,
926 reason = "one pixel conversion needs all of it"
927)]
928fn write_pixel(
929 out: &mut [u8],
930 at: &mut usize,
931 format: PixelFormat,
932 color_type: ColorType,
933 channel: &[u16; 4],
934 depth: u8,
935 max: u16,
936 palette: Option<&[[u8; 3]]>,
937 transparency: Option<&Transparency>,
938) -> Result<()> {
939 fn push(out: &mut [u8], at: &mut usize, value: u8) {
941 if let Some(slot) = out.get_mut(*at) {
942 *slot = value;
943 }
944 *at += 1;
945 }
946 fn push16(out: &mut [u8], at: &mut usize, value: u16) {
948 for byte in value.to_ne_bytes() {
949 push(out, at, byte);
950 }
951 }
952
953 match color_type {
954 ColorType::Palette => {
955 let index = channel[0] as usize;
956 let entries = palette
957 .ok_or_else(|| PixelsError::malformed("png", "palette image without a palette"))?;
958 let rgb = entries.get(index).copied().ok_or_else(|| {
959 PixelsError::malformed(
960 "png",
961 format!(
962 "palette index {index} is beyond the {}-entry palette",
963 entries.len()
964 ),
965 )
966 })?;
967 push(out, at, rgb[0]);
968 push(out, at, rgb[1]);
969 push(out, at, rgb[2]);
970 if format == PixelFormat::Rgba8 {
971 let alpha = match transparency {
972 Some(Transparency::Palette(alphas)) => {
973 alphas.get(index).copied().unwrap_or(255)
975 }
976 _ => 255,
977 };
978 push(out, at, alpha);
979 }
980 }
981 ColorType::Grayscale => {
982 let transparent =
983 matches!(transparency, Some(Transparency::Gray(key)) if *key == channel[0]);
984 match format {
985 PixelFormat::Gray8 => push(out, at, scale8(channel[0], max)),
986 PixelFormat::Gray16 => push16(out, at, channel[0]),
987 PixelFormat::GrayA8 => {
988 push(out, at, scale8(channel[0], max));
989 push(out, at, if transparent { 0 } else { 255 });
990 }
991 PixelFormat::Rgba16 => {
992 let value = if depth == 16 {
993 channel[0]
994 } else {
995 channel[0] * 257
996 };
997 push16(out, at, value);
998 push16(out, at, value);
999 push16(out, at, value);
1000 push16(out, at, if transparent { 0 } else { u16::MAX });
1001 }
1002 other => {
1003 return Err(PixelsError::unsupported(format!(
1004 "greyscale cannot be written as {other}"
1005 )));
1006 }
1007 }
1008 }
1009 ColorType::GrayscaleAlpha => match format {
1010 PixelFormat::GrayA8 => {
1011 push(out, at, scale8(channel[0], max));
1012 push(out, at, scale8(channel[1], max));
1013 }
1014 PixelFormat::Rgba16 => {
1015 push16(out, at, channel[0]);
1016 push16(out, at, channel[0]);
1017 push16(out, at, channel[0]);
1018 push16(out, at, channel[1]);
1019 }
1020 other => {
1021 return Err(PixelsError::unsupported(format!(
1022 "grey+alpha cannot be written as {other}"
1023 )));
1024 }
1025 },
1026 ColorType::Rgb => {
1027 let transparent = matches!(
1028 transparency,
1029 Some(Transparency::Rgb(r, g, b))
1030 if *r == channel[0] && *g == channel[1] && *b == channel[2]
1031 );
1032 match format {
1033 PixelFormat::Rgb8 => {
1034 for &value in channel.iter().take(3) {
1035 push(out, at, scale8(value, max));
1036 }
1037 }
1038 PixelFormat::Rgb16 => {
1039 for &value in channel.iter().take(3) {
1040 push16(out, at, value);
1041 }
1042 }
1043 PixelFormat::Rgba8 => {
1044 for &value in channel.iter().take(3) {
1045 push(out, at, scale8(value, max));
1046 }
1047 push(out, at, if transparent { 0 } else { 255 });
1048 }
1049 PixelFormat::Rgba16 => {
1050 for &value in channel.iter().take(3) {
1051 push16(out, at, value);
1052 }
1053 push16(out, at, if transparent { 0 } else { u16::MAX });
1054 }
1055 other => {
1056 return Err(PixelsError::unsupported(format!(
1057 "RGB cannot be written as {other}"
1058 )));
1059 }
1060 }
1061 }
1062 ColorType::Rgba => match format {
1063 PixelFormat::Rgba8 => {
1064 for &value in channel {
1065 push(out, at, scale8(value, max));
1066 }
1067 }
1068 PixelFormat::Rgba16 => {
1069 for &value in channel {
1070 push16(out, at, value);
1071 }
1072 }
1073 other => {
1074 return Err(PixelsError::unsupported(format!(
1075 "RGBA cannot be written as {other}"
1076 )));
1077 }
1078 },
1079 }
1080 Ok(())
1081}
1082
1083fn parse_trns(data: &[u8], color_type: ColorType) -> Result<Transparency> {
1085 fn be16(data: &[u8], offset: usize) -> u16 {
1087 u16::from_be_bytes([
1088 data.get(offset).copied().unwrap_or(0),
1089 data.get(offset + 1).copied().unwrap_or(0),
1090 ])
1091 }
1092 match color_type {
1093 ColorType::Grayscale => {
1094 if data.len() != 2 {
1095 return Err(PixelsError::malformed(
1096 "png",
1097 format!("greyscale tRNS must be 2 bytes, got {}", data.len()),
1098 ));
1099 }
1100 Ok(Transparency::Gray(be16(data, 0)))
1101 }
1102 ColorType::Rgb => {
1103 if data.len() != 6 {
1104 return Err(PixelsError::malformed(
1105 "png",
1106 format!("RGB tRNS must be 6 bytes, got {}", data.len()),
1107 ));
1108 }
1109 Ok(Transparency::Rgb(
1110 be16(data, 0),
1111 be16(data, 2),
1112 be16(data, 4),
1113 ))
1114 }
1115 ColorType::Palette => {
1116 if data.len() > 256 {
1117 return Err(PixelsError::malformed(
1118 "png",
1119 format!(
1120 "palette tRNS has {} entries, over the 256 maximum",
1121 data.len()
1122 ),
1123 ));
1124 }
1125 Ok(Transparency::Palette(data.to_vec()))
1126 }
1127 ColorType::GrayscaleAlpha | ColorType::Rgba => Err(PixelsError::malformed(
1129 "png",
1130 "tRNS is not allowed for colour types that already carry alpha",
1131 )),
1132 }
1133}
1134
1135impl<S: Source + std::fmt::Debug> Decoder for PngDecoder<S> {
1136 fn descriptor(&self) -> ImageDescriptor {
1137 self.descriptor
1138 }
1139
1140 fn orientation(&self) -> Orientation {
1143 self.orientation
1144 }
1145
1146 fn icc_profile(&self) -> Option<&[u8]> {
1148 self.icc.as_deref()
1149 }
1150
1151 fn exif(&self) -> Option<&[u8]> {
1153 self.exif.as_deref()
1154 }
1155
1156 fn xmp(&self) -> Option<&[u8]> {
1158 self.xmp.as_deref()
1159 }
1160
1161 fn source_info(&self) -> SourceInfo {
1164 self.info.clone()
1165 }
1166
1167 fn capability(&self) -> DecodeCapability {
1168 DecodeCapability::Sequential
1173 }
1174
1175 fn read_row(&mut self, out: &mut [u8]) -> Result<()> {
1176 if self.header.interlaced {
1179 return self.read_row_buffered(out);
1180 }
1181 if self.stream.is_none() {
1182 let started = self.begin_streaming()?;
1183 self.stream = Some(Box::new(started));
1184 }
1185 if self.row >= self.descriptor.height {
1186 return Err(PixelsError::invalid_argument(
1187 "out",
1188 format!("all {} rows have already been read", self.descriptor.height),
1189 ));
1190 }
1191 let row_bytes = self.descriptor.row_bytes();
1192 if out.len() != row_bytes {
1193 return Err(PixelsError::invalid_argument(
1194 "out",
1195 format!("row buffer is {} bytes, expected {row_bytes}", out.len()),
1196 ));
1197 }
1198
1199 let sample_bytes = self.header.row_bytes(self.header.width);
1200 let stride = self.header.filter_stride();
1201 let Some(stream) = self.stream.as_mut() else {
1202 return Err(PixelsError::graph("png stream vanished after starting"));
1203 };
1204 let samples = stream.next_scanline(sample_bytes, stride)?;
1205 let Some(stream) = self.stream.as_deref() else {
1206 return Err(PixelsError::graph("png stream vanished after starting"));
1207 };
1208 self.expand_row(
1209 &samples,
1210 stream.palette.as_deref(),
1211 stream.transparency.as_ref(),
1212 out,
1213 )?;
1214 self.row += 1;
1215
1216 if self.row == self.descriptor.height {
1219 if let Some(stream) = self.stream.as_mut() {
1220 stream.finalize()?;
1221 }
1222 }
1223 Ok(())
1224 }
1225}
1226
1227#[must_use]
1231pub fn probe(prefix: &[u8]) -> bool {
1232 prefix.get(..8) == Some(&SIGNATURE[..])
1233}
1234
1235#[derive(Debug, Clone, Copy, Default)]
1241pub struct PngCodec;
1242
1243impl Codec for PngCodec {
1244 fn format(&self) -> Format {
1245 Format::Png
1246 }
1247
1248 fn magic_len(&self) -> usize {
1249 SIGNATURE.len()
1250 }
1251
1252 fn probe(&self, prefix: &[u8]) -> bool {
1253 probe(prefix)
1254 }
1255}