Coverage Report

Created: 2025-12-14 07:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}