Skip to main content

zune_image/codecs/
webp.rs

1#![cfg(feature = "webp")]
2
3use std::io::{BufRead, Seek, Write};
4
5use image_webp::{ColorType, DecodingError, WebPEncoder};
6use jxl_oxide::color::WhitePoint::E;
7use zune_core::bit_depth::BitDepth;
8use zune_core::bytestream::ZByteWriterTrait;
9use zune_core::colorspace::ColorSpace;
10use zune_core::log::{trace, warn};
11
12use crate::codecs::{create_options_for_encoder, ImageFormat};
13use crate::errors::{ImageErrors, ImgEncodeErrors};
14use crate::image::Image;
15use crate::metadata::{AlphaState, ImageMetadata};
16use crate::traits::{DecoderTrait, EncoderTrait};
17
18pub struct ZuneWebpDecoder<T: BufRead + Seek> {
19    inner: image_webp::WebPDecoder<T>
20}
21impl<T: BufRead + Seek> ZuneWebpDecoder<T> {
22    pub fn new(r: T) -> Result<Self, ImageErrors> {
23        let decoder = image_webp::WebPDecoder::new(r).map_err(|e| {
24            println!("{:?}", e);
25            ImageErrors::ImageDecodeErrors(e.to_string())
26        })?;
27        return Ok(ZuneWebpDecoder { inner: decoder });
28    }
29}
30impl<T: BufRead + Seek> DecoderTrait for ZuneWebpDecoder<T> {
31    fn decode(&mut self) -> Result<Image, ImageErrors> {
32        let (w, h) = self.dimensions().expect("Failed to determine dimensions");
33        let mut raw_pixels = vec![
34            0_u8;
35            self.inner
36                .output_buffer_size()
37                .expect("Max output size greater than usize max")
38        ];
39        self.inner
40            .read_image(&mut raw_pixels)
41            .map_err(|e| ImageErrors::ImageDecodeErrors(e.to_string()))?;
42
43        let mut img = Image::from_u8(&raw_pixels, w, h, self.out_colorspace());
44        img.metadata.format = Some(ImageFormat::WEBP);
45
46        Ok(img)
47    }
48
49    fn dimensions(&self) -> Option<(usize, usize)> {
50        let (w, h) = self.inner.dimensions();
51
52        Some((w as usize, h as usize))
53    }
54
55    fn out_colorspace(&self) -> ColorSpace {
56        // According to the docs, if image has alpha
57        // colorspace is rgba, if not RGB
58        match self.inner.has_alpha() {
59            true => ColorSpace::RGBA,
60            false => ColorSpace::RGB
61        }
62    }
63
64    fn name(&self) -> &'static str {
65        "Webp Decoder (image-rs)"
66    }
67    fn read_headers(&mut self) -> Result<Option<ImageMetadata>, ImageErrors> {
68        let (w, h) = self.dimensions().expect("Failed to determine dimensions");
69        return Ok(Some(ImageMetadata {
70            color_trc: None,
71            default_gamma: None,
72            width: w,
73            height: h,
74            colorspace: self.out_colorspace(),
75            depth: BitDepth::Eight,
76            format: Some(ImageFormat::WEBP),
77            alpha: AlphaState::NonPreMultiplied,
78            #[cfg(feature = "metadata")]
79            exif: None,
80            icc_chunk: None,
81            is_linear: false
82        }));
83    }
84}
85
86pub struct ZuneWebpImageEncoder {}
87impl ZuneWebpImageEncoder {
88    pub fn new() -> Self {
89        Self {}
90    }
91}
92impl EncoderTrait for ZuneWebpImageEncoder {
93    fn name(&self) -> &'static str {
94        "Webp Image Encoder"
95    }
96
97    fn encode_inner<T: ZByteWriterTrait>(
98        &mut self, image: &Image, mut sink: T
99    ) -> Result<usize, ImageErrors> {
100        trace!("Starting webp encoder");
101        // First make options
102        let options = create_options_for_encoder(None, image);
103        let data = &image.to_u8()[0];
104        let mut output = Vec::with_capacity(data.len());
105
106        let mut encoder = WebPEncoder::new(&mut output);
107
108        #[cfg(feature = "metadata")]
109        {
110            use exif::experimental::Writer;
111
112            let mut inner_buff = std::io::Cursor::new(Vec::with_capacity(300));
113            if !options.strip_metadata() {
114                trace!("Writing exif data");
115                if let Some(fields) = &image.metadata.exif {
116                    let mut writer = Writer::new();
117
118                    for metadatum in fields {
119                        writer.push_field(metadatum);
120                    }
121                    let result = writer.write(&mut inner_buff, false);
122                    if result.is_ok() {
123                        encoder.set_exif_metadata(inner_buff.into_inner())
124                    } else {
125                        warn!("Writing exif failed {:?}", result);
126                    }
127                }
128            }
129        }
130
131        let color_type = match options.colorspace() {
132            ColorSpace::RGBA => ColorType::Rgba8,
133            ColorSpace::RGB => ColorType::Rgb8,
134            ColorSpace::Luma => ColorType::L8,
135            ColorSpace::LumaA => ColorType::La8,
136            _ => {
137                return Err(ImageErrors::EncodeErrors(
138                    ImgEncodeErrors::UnsupportedColorspace(
139                        options.colorspace(),
140                        self.supported_colorspaces()
141                    )
142                ))
143            }
144        };
145        encoder
146            .encode(
147                data,
148                options.width() as u32,
149                options.height() as u32,
150                color_type
151            )
152            .map_err(|e| ImageErrors::EncodeErrors(ImgEncodeErrors::Generic(e.to_string())))?;
153
154        sink.write_all_bytes(output.as_ref())?;
155        Ok(output.len())
156    }
157    fn supported_colorspaces(&self) -> &'static [ColorSpace] {
158        &[
159            ColorSpace::RGB,
160            ColorSpace::RGBA,
161            ColorSpace::Luma,
162            ColorSpace::LumaA
163        ]
164    }
165    fn format(&self) -> ImageFormat {
166        ImageFormat::WEBP
167    }
168    fn supported_bit_depth(&self) -> &'static [BitDepth] {
169        &[BitDepth::Eight]
170    }
171    fn default_depth(&self, depth: BitDepth) -> BitDepth {
172        BitDepth::Eight
173    }
174}