Skip to main content

zune_image/codecs/
png.rs

1/*
2 * Copyright (c) 2023.
3 *
4 * This software is free software;
5 *
6 * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license
7 */
8#![cfg_attr(feature = "docs", doc(cfg(feature = "png")))]
9#![cfg(feature = "png")]
10#![allow(unused_variables)]
11
12//! Represents an png image decoder and encoder
13use std::io::Cursor;
14
15use zune_core::bit_depth::BitDepth;
16use zune_core::bytestream::{ZByteReaderTrait, ZByteWriterTrait};
17use zune_core::colorspace::ColorSpace;
18use zune_core::log::warn;
19use zune_core::options::EncoderOptions;
20use zune_core::result::DecodingResult;
21use zune_png::error::PngDecodeErrors;
22use zune_png::*;
23
24pub use zune_png::PngDecoder;
25use crate::codecs::{create_options_for_encoder, ImageFormat};
26use crate::errors::ImageErrors;
27use crate::errors::ImageErrors::ImageDecodeErrors;
28use crate::errors::ImgEncodeErrors::ImageEncodeErrors;
29use crate::frame::Frame;
30use crate::image::Image;
31use crate::metadata::ImageMetadata;
32use crate::traits::{DecodeInto, DecoderTrait, EncoderTrait};
33
34impl<T> DecoderTrait for PngDecoder<T>
35where
36    T: ZByteReaderTrait
37{
38    fn decode(&mut self) -> Result<Image, ImageErrors> {
39        let metadata = self.read_headers()?.unwrap();
40
41        let depth = self.depth().unwrap();
42        let (width, height) = self.dimensions().unwrap();
43        let colorspace = self.colorspace().unwrap();
44
45        if self.is_animated() && self.options().png_decode_animated() {
46            // decode apng frames
47            //let mut previous_frame
48            let info = self.info().unwrap().clone();
49            // the output, since we know that no frame will be bigger than the width and height, we can
50            // set this up outside of the loop.
51            let mut output = vec![0; info.width * info.height * colorspace.num_components()];
52            let mut output_frames = Vec::new();
53            while self.more_frames() {
54                self.decode_headers()?;
55
56                let mut frame = self.frame_info().unwrap();
57                if frame.dispose_op == DisposeOp::Previous {
58                    // we don't clear our buffer, so output always contains the previous frame
59                    //
60                    // this means that there is no need to store the previous frame and copy it
61                    frame.dispose_op = DisposeOp::None;
62                }
63                let pix = self.decode()?;
64                match pix {
65                    DecodingResult::U8(pix) => {
66                        post_process_image(
67                            &info,
68                            colorspace,
69                            &frame,
70                            &pix,
71                            None,
72                            &mut output,
73                            None,
74                        )?;
75                        let duration = f64::from(frame.delay_num) / f64::from(frame.delay_denom);
76                        // then build a frame from that
77                        let im_frame = Frame::from_u8(&output, colorspace, usize::from(frame.delay_num),usize::from(frame.delay_denom));
78                        output_frames.push(im_frame);
79                    }
80                    _ => return Err(ImageDecodeErrors("The current image is an  Animated PNG but has a depth of 16, such an image isn't supported".to_string()))
81                }
82            }
83            let mut image = Image::new_frames(output_frames, depth, width, height, colorspace);
84            image.metadata = metadata;
85
86            Ok(image)
87        } else {
88            let pixels = self
89                .decode()
90                .map_err(<error::PngDecodeErrors as Into<ImageErrors>>::into)?;
91
92            let mut image = match pixels {
93                DecodingResult::U8(data) => Image::from_u8(&data, width, height, colorspace),
94                DecodingResult::U16(data) => Image::from_u16(&data, width, height, colorspace),
95                _ => unreachable!()
96            };
97            // metadata
98            image.metadata = metadata;
99
100            Ok(image)
101        }
102    }
103    fn dimensions(&self) -> Option<(usize, usize)> {
104        self.dimensions()
105    }
106
107    fn out_colorspace(&self) -> ColorSpace {
108        self.colorspace().unwrap()
109    }
110
111    fn name(&self) -> &'static str {
112        "PNG Decoder"
113    }
114
115    fn read_headers(&mut self) -> Result<Option<ImageMetadata>, crate::errors::ImageErrors> {
116        self.decode_headers()
117            .map_err(<error::PngDecodeErrors as Into<ImageErrors>>::into)?;
118
119        let (width, height) = self.dimensions().unwrap();
120        let depth = self.depth().unwrap();
121
122        let mut metadata = ImageMetadata {
123            format: Some(ImageFormat::PNG),
124            colorspace: self.colorspace().unwrap(),
125            depth: depth,
126            width: width,
127            height: height,
128            default_gamma: self.info().unwrap().gamma,
129            ..Default::default()
130        };
131        #[cfg(feature = "metadata")]
132        {
133            let info = self.info().unwrap();
134            // see if we have an exif chunk
135            if let Some(exif) = &info.exif {
136                metadata.parse_raw_exif(exif)
137            }
138        }
139        // load icc
140        if let Some(icc) = &self.info().unwrap().icc_profile {
141            metadata.set_icc_chunk(icc.to_owned());
142        }
143
144        Ok(Some(metadata))
145    }
146}
147
148impl From<zune_png::error::PngDecodeErrors> for ImageErrors {
149    fn from(from: zune_png::error::PngDecodeErrors) -> Self {
150        let err = format!("png: {from:?}");
151
152        ImageErrors::ImageDecodeErrors(err)
153    }
154}
155
156#[derive(Default)]
157pub struct PngEncoder {
158    options: Option<EncoderOptions>
159}
160
161impl PngEncoder {
162    pub fn new() -> PngEncoder {
163        PngEncoder::default()
164    }
165    pub fn new_with_options(options: EncoderOptions) -> PngEncoder {
166        PngEncoder {
167            options: Some(options)
168        }
169    }
170}
171
172impl EncoderTrait for PngEncoder {
173    fn name(&self) -> &'static str {
174        "PNG encoder"
175    }
176
177    fn encode_inner<T: ZByteWriterTrait>(
178        &mut self, image: &Image, sink: T
179    ) -> Result<usize, ImageErrors> {
180        let options = create_options_for_encoder(self.options, image);
181
182        let frame = &image.to_u8_be()[0];
183
184        let mut encoder = zune_png::PngEncoder::new(frame, options);
185
186        #[allow(unused_mut)]
187        let mut buf: Cursor<Vec<u8>> = std::io::Cursor::new(vec![]);
188
189        #[cfg(feature = "metadata")]
190        {
191            use exif::experimental::Writer;
192
193            if !options.strip_metadata() {
194                if let Some(fields) = &image.metadata.exif {
195                    let mut writer = Writer::new();
196
197                    for metadatum in fields {
198                        writer.push_field(metadatum);
199                    }
200                    let result = writer.write(&mut buf, false);
201                    if result.is_ok() {
202                        encoder.add_exif_segment(buf.get_ref());
203                    } else {
204                        warn!("Writing exif failed {:?}", result);
205                    }
206                }
207            }
208        }
209        encoder
210            .encode(sink)
211            .map_err(|e| ImageErrors::EncodeErrors(ImageEncodeErrors(format!("{:?}", e))))
212    }
213
214    fn supported_colorspaces(&self) -> &'static [ColorSpace] {
215        &[
216            ColorSpace::Luma,
217            ColorSpace::LumaA,
218            ColorSpace::RGB,
219            ColorSpace::RGBA
220        ]
221    }
222
223    fn format(&self) -> ImageFormat {
224        ImageFormat::PNG
225    }
226
227    fn supported_bit_depth(&self) -> &'static [BitDepth] {
228        &[BitDepth::Eight, BitDepth::Sixteen]
229    }
230
231    fn default_depth(&self, depth: BitDepth) -> BitDepth {
232        match depth {
233            BitDepth::Sixteen | BitDepth::Float32 => BitDepth::Sixteen,
234            _ => BitDepth::Eight
235        }
236    }
237    fn set_options(&mut self, opts: EncoderOptions) {
238        self.options = Some(opts)
239    }
240}
241
242impl<T> DecodeInto for PngDecoder<T>
243where
244    T: ZByteReaderTrait
245{
246    type BufferType = u8;
247
248    fn decode_into(&mut self, buffer: &mut [Self::BufferType]) -> Result<(), ImageErrors> {
249        self.decode_into(buffer)
250            .map_err(<PngDecodeErrors as Into<ImageErrors>>::into)?;
251
252        Ok(())
253    }
254
255    fn decode_output_buffer_size(&mut self) -> Result<usize, ImageErrors> {
256        self.decode_headers()
257            .map_err(<PngDecodeErrors as Into<ImageErrors>>::into)?;
258
259        // unwrap is okay because we successfully decoded image headers
260        Ok(self.output_buffer_size().unwrap())
261    }
262}
263
264#[cfg(test)]
265mod tests {
266    use zune_core::bytestream::ZCursor;
267    use zune_core::colorspace::ColorSpace;
268    use zune_png::PngDecoder;
269
270    use crate::codecs::png::PngEncoder;
271    use crate::codecs::ImageFormat;
272    use crate::image::Image;
273    use crate::traits::DecodeInto;
274
275    fn create_png() -> Vec<u8> {
276        let encoder = PngEncoder::new();
277        let image = Image::fill(10_u8, ColorSpace::RGB, 100, 100);
278        image.write_to_vec(ImageFormat::PNG).unwrap()
279    }
280    #[test]
281    fn test_png_decode_into() {
282        let mut output = vec![0; 100 * 100 * 3];
283        let img = create_png();
284        let mut decoder = PngDecoder::new(ZCursor::new(&img));
285        decoder.decode_into(&mut output).unwrap();
286    }
287}