/src/image/src/codecs/qoi.rs
Line | Count | Source |
1 | | //! Decoding and encoding of QOI images |
2 | | |
3 | | use crate::error::{DecodingError, EncodingError, UnsupportedError, UnsupportedErrorKind}; |
4 | | use crate::{ |
5 | | ColorType, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageFormat, ImageResult, |
6 | | }; |
7 | | use std::io::{Read, Write}; |
8 | | |
9 | | /// QOI decoder |
10 | | pub struct QoiDecoder<R> { |
11 | | decoder: qoi::Decoder<R>, |
12 | | } |
13 | | |
14 | | impl<R> QoiDecoder<R> |
15 | | where |
16 | | R: Read, |
17 | | { |
18 | | /// Creates a new decoder that decodes from the stream ```reader``` |
19 | 0 | pub fn new(reader: R) -> ImageResult<Self> { |
20 | 0 | let decoder = qoi::Decoder::from_stream(reader).map_err(decoding_error)?; |
21 | 0 | Ok(Self { decoder }) |
22 | 0 | } |
23 | | } |
24 | | |
25 | | impl<R: Read> ImageDecoder for QoiDecoder<R> { |
26 | 0 | fn dimensions(&self) -> (u32, u32) { |
27 | 0 | (self.decoder.header().width, self.decoder.header().height) |
28 | 0 | } |
29 | | |
30 | 0 | fn color_type(&self) -> ColorType { |
31 | 0 | match self.decoder.header().channels { |
32 | 0 | qoi::Channels::Rgb => ColorType::Rgb8, |
33 | 0 | qoi::Channels::Rgba => ColorType::Rgba8, |
34 | | } |
35 | 0 | } |
36 | | |
37 | 0 | fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> { |
38 | 0 | self.decoder.decode_to_buf(buf).map_err(decoding_error)?; |
39 | 0 | Ok(()) |
40 | 0 | } |
41 | | |
42 | 0 | fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> { |
43 | 0 | (*self).read_image(buf) |
44 | 0 | } |
45 | | } |
46 | | |
47 | 0 | fn decoding_error(error: qoi::Error) -> ImageError { |
48 | 0 | ImageError::Decoding(DecodingError::new(ImageFormat::Qoi.into(), error)) |
49 | 0 | } |
50 | | |
51 | 0 | fn encoding_error(error: qoi::Error) -> ImageError { |
52 | 0 | ImageError::Encoding(EncodingError::new(ImageFormat::Qoi.into(), error)) |
53 | 0 | } |
54 | | |
55 | | /// QOI encoder |
56 | | pub struct QoiEncoder<W> { |
57 | | writer: W, |
58 | | } |
59 | | |
60 | | impl<W: Write> QoiEncoder<W> { |
61 | | /// Creates a new encoder that writes its output to ```writer``` |
62 | 0 | pub fn new(writer: W) -> Self { |
63 | 0 | Self { writer } |
64 | 0 | } Unexecuted instantiation: <image::codecs::qoi::QoiEncoder<_>>::new Unexecuted instantiation: <image::codecs::qoi::QoiEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::new |
65 | | } |
66 | | |
67 | | impl<W: Write> ImageEncoder for QoiEncoder<W> { |
68 | | #[track_caller] |
69 | 0 | fn write_image( |
70 | 0 | mut self, |
71 | 0 | buf: &[u8], |
72 | 0 | width: u32, |
73 | 0 | height: u32, |
74 | 0 | color_type: ExtendedColorType, |
75 | 0 | ) -> ImageResult<()> { |
76 | 0 | if !matches!( |
77 | 0 | color_type, |
78 | | ExtendedColorType::Rgba8 | ExtendedColorType::Rgb8 |
79 | | ) { |
80 | 0 | return Err(ImageError::Unsupported( |
81 | 0 | UnsupportedError::from_format_and_kind( |
82 | 0 | ImageFormat::Qoi.into(), |
83 | 0 | UnsupportedErrorKind::Color(color_type), |
84 | 0 | ), |
85 | 0 | )); |
86 | 0 | } |
87 | | |
88 | 0 | let expected_buffer_len = color_type.buffer_size(width, height); |
89 | 0 | assert_eq!( |
90 | | expected_buffer_len, |
91 | 0 | buf.len() as u64, |
92 | 0 | "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", |
93 | 0 | buf.len(), |
94 | | ); |
95 | | |
96 | | // Encode data in QOI |
97 | 0 | let data = qoi::encode_to_vec(buf, width, height).map_err(encoding_error)?; |
98 | | |
99 | | // Write data to buffer |
100 | 0 | self.writer.write_all(&data[..])?; |
101 | 0 | self.writer.flush()?; |
102 | | |
103 | 0 | Ok(()) |
104 | 0 | } Unexecuted instantiation: <image::codecs::qoi::QoiEncoder<_> as image::io::encoder::ImageEncoder>::write_image Unexecuted instantiation: <image::codecs::qoi::QoiEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> as image::io::encoder::ImageEncoder>::write_image |
105 | | } |
106 | | |
107 | | #[cfg(test)] |
108 | | mod tests { |
109 | | use super::*; |
110 | | use std::fs::File; |
111 | | |
112 | | #[test] |
113 | | fn decode_test_image() { |
114 | | let decoder = QoiDecoder::new(File::open("tests/images/qoi/basic-test.qoi").unwrap()) |
115 | | .expect("Unable to read QOI file"); |
116 | | |
117 | | assert_eq!((5, 5), decoder.dimensions()); |
118 | | assert_eq!(ColorType::Rgba8, decoder.color_type()); |
119 | | } |
120 | | } |