rapid_qoi/decode.rs
1use core::convert::TryInto;
2
3use super::*;
4
5#[cfg(feature = "alloc")]
6use alloc::{vec, vec::Vec};
7
8/// Errros that may occur during image decoding.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub enum DecodeError {
11 /// Buffer does not contain enough encoded data.
12 NotEnoughData,
13
14 /// Encoded header contains invalid magic value.\
15 /// First four bytes must contain `b"qoif"`.\
16 /// This usually indicates that buffer does not contain QOI image.
17 InvalidMagic,
18
19 /// Encoded header contains invalud channels number.\
20 /// QOI supports only images with `3` or `4` channels.\
21 /// Any other value cannot be produced by valid encoder.
22 InvalidChannelsValue,
23
24 /// Encoded header contains invalud color space value.'
25 /// QOI supports only images with SRGB color channels and linear alpha (if present) denoted by `0` and all linear channels denoted by `1`.\
26 /// Any other value cannot be produced by valid encoder.
27 InvalidColorSpaceValue,
28
29 /// Output buffer is too small to fit decoded image.
30 OutputIsTooSmall,
31}
32
33impl Display for DecodeError {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 match self {
36 DecodeError::NotEnoughData => {
37 f.write_str("Buffer does not contain enough encoded data")
38 }
39 DecodeError::InvalidMagic => f.write_str("Encoded header contains invalid magic value"),
40 DecodeError::InvalidChannelsValue => {
41 f.write_str("Encoded header contains invalud channels number. Must be 3 or 4")
42 }
43 DecodeError::InvalidColorSpaceValue => {
44 f.write_str("Encoded header contains invalud color space value. Must be 0 or 1")
45 }
46 DecodeError::OutputIsTooSmall => {
47 f.write_str("Output buffer is too small to fit decoded image")
48 }
49 }
50 }
51}
52
53#[cfg(feature = "std")]
54impl std::error::Error for DecodeError {}
55
56impl Qoi {
57 /// Returns bytes size for the decoded image.
58 #[inline]
59 pub fn decoded_size(&self) -> usize {
60 self.width as usize * self.height as usize * self.colors.channels()
61 }
62
63 /// Reads header from encoded QOI image.\
64 /// Returned header can be analyzed before proceeding parsing with [`Qoi::decode_skip_header`].
65 pub fn decode_header(bytes: &[u8]) -> Result<Self, DecodeError> {
66 if bytes.len() < QOI_HEADER_SIZE {
67 return Err(DecodeError::NotEnoughData);
68 }
69
70 let magic = u32::from_be_bytes(bytes[0..4].try_into().unwrap());
71 if magic != QOI_MAGIC {
72 return Err(DecodeError::InvalidMagic);
73 }
74
75 let w = u32::from_be_bytes(bytes[4..8].try_into().unwrap());
76 let h = u32::from_be_bytes(bytes[8..12].try_into().unwrap());
77
78 let channels = bytes[12];
79 let colors = bytes[13];
80
81 Ok(Qoi {
82 width: w,
83 height: h,
84 colors: match (channels, colors) {
85 (3, 0) => Colors::Srgb,
86 (4, 0) => Colors::SrgbLinA,
87 (3, 1) => Colors::Rgb,
88 (4, 1) => Colors::Rgba,
89 (_, 0 | 1) => return Err(DecodeError::InvalidChannelsValue),
90 (_, _) => return Err(DecodeError::InvalidColorSpaceValue),
91 },
92 })
93 }
94
95 /// Decode a QOI image from bytes slice.\
96 /// Decoded raw RGB or RGBA pixels are written into `output` slice.
97 ///
98 /// On success this function returns `Ok(qoi)` with `qoi` describing image dimensions and color space.\
99 /// On failure this function returns `Err(err)` with `err` describing cause of the error.
100 #[inline]
101 pub fn decode(bytes: &[u8], output: &mut [u8]) -> Result<Self, DecodeError> {
102 let qoi = Self::decode_header(bytes)?;
103 qoi.decode_skip_header(&bytes[QOI_HEADER_SIZE..], output)?;
104 Ok(qoi)
105 }
106
107 /// Decode a QOI image from bytes slice.\
108 /// `bytes` does not include QOI header. Uses provided `Qoi` value instead.\
109 /// Decoded raw RGB or RGBA (depending on `self.colors` value) pixels are written into `output` slice.
110 ///
111 /// On success this function returns `Ok(())`.\
112 /// On failure this function returns `Err(err)` with `err` describing cause of the error.
113 #[inline]
114 pub fn decode_skip_header(&self, bytes: &[u8], output: &mut [u8]) -> Result<(), DecodeError> {
115 if self.width == 0 || self.height == 0 {
116 return Ok(());
117 }
118
119 let px_len = self.decoded_size();
120
121 let output = match output.get_mut(..px_len) {
122 None => return Err(DecodeError::OutputIsTooSmall),
123 Some(output) => output,
124 };
125
126 match self.colors.has_alpha() {
127 true => {
128 Self::decode_range::<4>(
129 &mut [Pixel::new(); 64],
130 &mut Pixel::new_opaque(),
131 &mut 0,
132 bytes,
133 output,
134 )?;
135 }
136 false => {
137 Self::decode_range::<3>(
138 &mut [Pixel::new(); 64],
139 &mut Pixel::new_opaque(),
140 &mut 0,
141 bytes,
142 output,
143 )?;
144 }
145 }
146 Ok(())
147 }
148
149 /// Decode range of pixels into pixels slice.
150 #[inline]
151 pub fn decode_range<const N: usize>(
152 index: &mut [[u8; N]; 64],
153 ppx: &mut [u8; N],
154 prun: &mut usize,
155 bytes: &[u8],
156 pixels: &mut [u8],
157 ) -> Result<usize, DecodeError>
158 where
159 [u8; N]: Pixel,
160 {
161 assert_eq!(pixels.len() % N, 0);
162
163 // let (mut pixels, rem) = pixels.as_chunks_mut::<N>();
164 // let mut pixels = pixels.chunks_exact_mut(N).map(cast_pixel::<N>);
165
166 let mut pixels = bytemuck::cast_slice_mut(pixels);
167
168 // assert!(rem.is_empty());
169
170 let mut px = *ppx;
171
172 if *prun > 0 {
173 // let len = pixels.len();
174
175 let (head, tail) = pixels.split_at_mut((*prun).min(pixels.len()));
176
177 // pixels.by_ref().take(*prun).for_each(|pixel| *pixel = px);
178
179 pixels = tail;
180 head.fill(px);
181
182 if pixels.is_empty() {
183 cold();
184 *prun -= head.len();
185 return Ok(0);
186 } else {
187 *prun = 0;
188 }
189 }
190
191 let mut rest = bytes;
192
193 loop {
194 match pixels {
195 [out, tail @ ..] => {
196 // Some(out) => {
197 pixels = tail;
198 match rest {
199 [b1 @ 0b00000000..=0b00111111, tail @ ..] => {
200 px = index[*b1 as usize];
201 *out = px;
202
203 rest = tail;
204 continue;
205 }
206 [b1 @ 0b01000000..=0b01111111, tail @ ..] => {
207 let vr = ((b1 >> 4) & 0x03).wrapping_sub(2);
208 let vg = ((b1 >> 2) & 0x03).wrapping_sub(2);
209 let vb = (b1 & 0x03).wrapping_sub(2);
210 px.add_rgb(vr, vg, vb);
211
212 rest = tail;
213 }
214 [b1 @ 0b10000000..=0b10111111, b2, tail @ ..] => {
215 let vg = (b1 & 0x3f).wrapping_sub(32);
216 let vr = ((b2 >> 4) & 0x0f).wrapping_sub(8).wrapping_add(vg);
217 let vb = (b2 & 0x0f).wrapping_sub(8).wrapping_add(vg);
218 px.add_rgb(vr, vg, vb);
219
220 rest = tail;
221 }
222 [0b11111110, b2, b3, b4, tail @ ..] => {
223 px.set_rgb(*b2, *b3, *b4);
224 // px[0] = *b2;
225 // px[1] = *b3;
226 // px[2] = *b4;
227
228 rest = tail;
229 }
230 [0b11111111, b2, b3, b4, _b5, tail @ ..] if N == 3 => {
231 cold();
232 px.set_rgb(*b2, *b3, *b4);
233 // px[0] = *b2;
234 // px[1] = *b3;
235 // px[2] = *b4;
236
237 rest = tail;
238 }
239 [0b11111111, b2, b3, b4, b5, tail @ ..] => {
240 px.set_rgba(*b2, *b3, *b4, *b5);
241
242 // px[0] = *b2;
243 // px[1] = *b3;
244 // px[2] = *b4;
245 // px[3] = *b5;
246
247 rest = tail;
248 }
249 [b1 @ 0b11000000..=0b11111101, dtail @ ..] => {
250 *out = px;
251 let run = *b1 as usize & 0x3f;
252 let (head, tail) = pixels.split_at_mut(run);
253 head.fill(px);
254 pixels = tail;
255 rest = dtail;
256
257 // let len = pixels.len();
258 // pixels.by_ref().take(run.min(len)).for_each(|out| *out = px);
259
260 if unlikely(pixels.is_empty()) {
261 *prun = run - head.len();
262 break;
263 }
264
265 continue;
266 }
267 _ => {
268 // if unlikely(rest.len() < QOI_PADDING) {
269 return Err(DecodeError::NotEnoughData);
270 // }
271 // Unreachable arm due to length check above.
272 // unreachable();
273 }
274 }
275 // }
276 // }
277
278 index[px.hash() as usize] = px;
279
280 // px.write(chunk);
281 *out = px;
282 // output = px.write_head(output);
283 }
284 [] => {
285 // None => {
286 // None => {
287 cold();
288 break;
289 }
290 }
291 }
292
293 *ppx = px;
294
295 Ok(bytes.len() - rest.len())
296 }
297
298 /// Decode a QOI image from bytes slice.\
299 /// Decoded raw RGB or RGBA pixels are written into allocated `Vec`.
300 ///
301 /// On success this function returns `Ok((qoi, vec))` with `qoi` describing image dimensions and color space and `vec` containing raw pixels data.\
302 /// On failure this function returns `Err(err)` with `err` describing cause of the error.
303 #[cfg(feature = "alloc")]
304 #[inline]
305 pub fn decode_alloc(bytes: &[u8]) -> Result<(Self, Vec<u8>), DecodeError> {
306 let qoi = Self::decode_header(bytes)?;
307
308 let size = qoi.decoded_size();
309 let mut output = vec![0; size];
310 let qoi = Self::decode(bytes, &mut output)?;
311 Ok((qoi, output))
312 }
313}