Skip to main content

zune_image/codecs/
qoi.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//! QOI decoding and encoding support
9//!
10//! This uses the delegate library [`zune-qoi`](zune_qoi)
11//! for encoding and decoding images
12#![cfg(feature = "qoi")]
13#![cfg_attr(feature = "docs", doc(cfg(feature = "qoi")))]
14
15use zune_core::bit_depth::BitDepth;
16use zune_core::bytestream::{ZByteReaderTrait, ZByteWriterTrait};
17use zune_core::colorspace::ColorSpace;
18use zune_core::options::EncoderOptions;
19pub use zune_qoi::*;
20
21use crate::codecs::{create_options_for_encoder, ImageFormat};
22use crate::errors::{ImageErrors, ImgEncodeErrors};
23use crate::image::Image;
24use crate::metadata::ImageMetadata;
25use crate::traits::{DecodeInto, DecoderTrait, EncoderTrait};
26
27impl<T> DecoderTrait for QoiDecoder<T>
28where
29    T: ZByteReaderTrait
30{
31    fn decode(&mut self) -> Result<Image, ImageErrors> {
32        let pixels = self.decode()?;
33        // safe because these are none when we haven't decoded.
34        let colorspace = self.colorspace().unwrap();
35        let (width, height) = self.dimensions().unwrap();
36
37        let depth = self.bit_depth();
38
39        let mut image = Image::from_u8(&pixels, width, height, colorspace);
40
41        // set metadata details
42        image.metadata.format = Some(ImageFormat::QOI);
43
44        Ok(image)
45    }
46
47    fn dimensions(&self) -> Option<(usize, usize)> {
48        self.dimensions()
49    }
50
51    fn out_colorspace(&self) -> ColorSpace {
52        self.colorspace().unwrap()
53    }
54
55    fn name(&self) -> &'static str {
56        "QOI Decoder"
57    }
58
59    fn is_experimental(&self) -> bool {
60        true
61    }
62
63    fn read_headers(&mut self) -> Result<Option<ImageMetadata>, crate::errors::ImageErrors> {
64        self.decode_headers()
65            .map_err(<QoiErrors as Into<ImageErrors>>::into)?;
66
67        let (width, height) = self.dimensions().unwrap();
68        let depth = self.bit_depth();
69
70        let metadata = ImageMetadata {
71            format: Some(ImageFormat::QOI),
72            colorspace: self.colorspace().unwrap(),
73            depth: depth,
74            width: width,
75            height: height,
76            ..Default::default()
77        };
78
79        Ok(Some(metadata))
80    }
81}
82
83#[derive(Copy, Clone, Default)]
84pub struct QoiEncoder {
85    options: Option<EncoderOptions>
86}
87
88impl QoiEncoder {
89    pub fn new() -> QoiEncoder {
90        QoiEncoder::default()
91    }
92
93    pub fn new_with_options(options: EncoderOptions) -> QoiEncoder {
94        QoiEncoder {
95            options: Some(options)
96        }
97    }
98}
99
100impl EncoderTrait for QoiEncoder {
101    fn name(&self) -> &'static str {
102        "QOI Encoder"
103    }
104
105    fn encode_inner<T: ZByteWriterTrait>(
106        &mut self, image: &Image, sink: T
107    ) -> Result<usize, ImageErrors> {
108        let options = create_options_for_encoder(self.options, image);
109
110        let data = &image.to_u8()[0];
111
112        let mut qoi_encoder = zune_qoi::QoiEncoder::new(data, options);
113
114        let bytes_written = qoi_encoder
115            .encode(sink)
116            .map_err(<QoiEncodeErrors as Into<ImgEncodeErrors>>::into)?;
117
118        Ok(bytes_written)
119    }
120    fn supported_colorspaces(&self) -> &'static [ColorSpace] {
121        &[ColorSpace::RGBA, ColorSpace::RGB]
122    }
123
124    fn format(&self) -> ImageFormat {
125        ImageFormat::QOI
126    }
127
128    fn supported_bit_depth(&self) -> &'static [BitDepth] {
129        &[BitDepth::Eight]
130    }
131
132    fn default_depth(&self, _: BitDepth) -> BitDepth {
133        BitDepth::Eight
134    }
135
136    fn default_colorspace(&self, colorspace: ColorSpace) -> ColorSpace {
137        // if colorspace has an alpha channel,
138        // we want to preserve it in the final encoder
139        if colorspace.has_alpha() {
140            ColorSpace::RGBA
141        } else {
142            // otherwise, just stick up to the one we know
143            ColorSpace::RGB
144        }
145    }
146    fn set_options(&mut self, opts: EncoderOptions) {
147        self.options = Some(opts)
148    }
149}
150
151impl From<zune_qoi::QoiErrors> for ImageErrors {
152    fn from(error: zune_qoi::QoiErrors) -> Self {
153        let err = format!("qoi: {error:?}");
154
155        ImageErrors::ImageDecodeErrors(err)
156    }
157}
158
159impl From<zune_qoi::QoiEncodeErrors> for ImgEncodeErrors {
160    fn from(error: zune_qoi::QoiEncodeErrors) -> Self {
161        let err = format!("qoi: {error:?}");
162
163        ImgEncodeErrors::Generic(err)
164    }
165}
166
167impl<T> DecodeInto for QoiDecoder<T>
168where
169    T: ZByteReaderTrait
170{
171    type BufferType = u8;
172
173    fn decode_into(&mut self, buffer: &mut [Self::BufferType]) -> Result<(), ImageErrors> {
174        self.decode_into(buffer)?;
175
176        Ok(())
177    }
178
179    fn decode_output_buffer_size(&mut self) -> Result<usize, ImageErrors> {
180        self.decode_headers()?;
181
182        // unwrap is okay because we successfully decoded image headers
183        Ok(self.output_buffer_size().unwrap())
184    }
185}