veridian_kernel/media/image_codecs/
mod.rs1#![allow(dead_code)]
19
20pub mod gif;
21pub mod jpeg;
22pub mod png;
23
24use alloc::vec::Vec;
25
26pub use gif::{decode_gif, DecodedGif, GifDisposal, GifFrame};
28pub use jpeg::decode_jpeg;
29pub use png::decode_png;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum ImageCodecError {
38 TruncatedData,
40 InvalidSignature,
42 Unsupported,
44 CorruptData,
46 ChecksumMismatch,
48 InvalidDimensions,
50 DecompressionError,
52 InvalidHuffmanTable,
54 InvalidQuantTable,
56}
57
58#[derive(Debug, Clone, PartialEq)]
60pub struct DecodedImage {
61 pub width: u32,
63 pub height: u32,
65 pub pixels: Vec<u8>,
67}
68
69impl DecodedImage {
70 pub fn new(width: u32, height: u32) -> Self {
72 let size = (width as usize)
73 .checked_mul(height as usize)
74 .and_then(|n| n.checked_mul(4))
75 .unwrap_or(0);
76 Self {
77 width,
78 height,
79 pixels: alloc::vec![0u8; size],
80 }
81 }
82
83 #[inline]
85 pub fn set_pixel(&mut self, x: u32, y: u32, r: u8, g: u8, b: u8, a: u8) {
86 if x < self.width && y < self.height {
87 let off = ((y as usize) * (self.width as usize) + (x as usize)) * 4;
88 if off + 3 < self.pixels.len() {
89 self.pixels[off] = r;
90 self.pixels[off + 1] = g;
91 self.pixels[off + 2] = b;
92 self.pixels[off + 3] = a;
93 }
94 }
95 }
96
97 #[inline]
99 pub fn get_pixel(&self, x: u32, y: u32) -> (u8, u8, u8, u8) {
100 if x < self.width && y < self.height {
101 let off = ((y as usize) * (self.width as usize) + (x as usize)) * 4;
102 if off + 3 < self.pixels.len() {
103 return (
104 self.pixels[off],
105 self.pixels[off + 1],
106 self.pixels[off + 2],
107 self.pixels[off + 3],
108 );
109 }
110 }
111 (0, 0, 0, 0)
112 }
113}
114
115#[inline]
120pub(crate) fn read_be_u16(data: &[u8], off: usize) -> u16 {
121 ((data[off] as u16) << 8) | (data[off + 1] as u16)
122}
123
124#[inline]
125pub(crate) fn read_be_u32(data: &[u8], off: usize) -> u32 {
126 ((data[off] as u32) << 24)
127 | ((data[off + 1] as u32) << 16)
128 | ((data[off + 2] as u32) << 8)
129 | (data[off + 3] as u32)
130}
131
132#[inline]
133pub(crate) fn read_le_u16(data: &[u8], off: usize) -> u16 {
134 (data[off] as u16) | ((data[off + 1] as u16) << 8)
135}
136
137#[inline]
138pub(crate) fn read_le_u32(data: &[u8], off: usize) -> u32 {
139 (data[off] as u32)
140 | ((data[off + 1] as u32) << 8)
141 | ((data[off + 2] as u32) << 16)
142 | ((data[off + 3] as u32) << 24)
143}
144
145#[inline]
147pub(crate) fn clamp_u8(v: i32) -> u8 {
148 if v < 0 {
149 0
150 } else if v > 255 {
151 255
152 } else {
153 v as u8
154 }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub enum ImageCodecFormat {
164 Png,
165 Jpeg,
166 Gif,
167 Unknown,
168}
169
170pub fn detect_codec_format(data: &[u8]) -> ImageCodecFormat {
172 if data.len() >= 8 && data[..8] == png::PNG_SIGNATURE {
173 ImageCodecFormat::Png
174 } else if data.len() >= 2 && data[0] == 0xFF && data[1] == 0xD8 {
175 ImageCodecFormat::Jpeg
176 } else if data.len() >= 6 && (&data[..6] == gif::GIF87A || &data[..6] == gif::GIF89A) {
177 ImageCodecFormat::Gif
178 } else {
179 ImageCodecFormat::Unknown
180 }
181}
182
183pub fn decode_image(data: &[u8]) -> Result<DecodedImage, ImageCodecError> {
185 match detect_codec_format(data) {
186 ImageCodecFormat::Png => decode_png(data),
187 ImageCodecFormat::Jpeg => decode_jpeg(data),
188 ImageCodecFormat::Gif => {
189 let gif = decode_gif(data)?;
190 if let Some(frame) = gif.frames.into_iter().next() {
191 Ok(frame.image)
192 } else {
193 Err(ImageCodecError::CorruptData)
194 }
195 }
196 ImageCodecFormat::Unknown => Err(ImageCodecError::Unsupported),
197 }
198}
199
200#[cfg(test)]
205mod tests {
206 #[allow(unused_imports)]
207 use alloc::vec;
208
209 use super::*;
210
211 #[test]
216 fn test_detect_png() {
217 let data = [137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 0];
218 assert_eq!(detect_codec_format(&data), ImageCodecFormat::Png);
219 }
220
221 #[test]
222 fn test_detect_jpeg() {
223 let data = [0xFF, 0xD8, 0xFF, 0xE0];
224 assert_eq!(detect_codec_format(&data), ImageCodecFormat::Jpeg);
225 }
226
227 #[test]
228 fn test_detect_gif87a() {
229 let mut data = vec![0u8; 20];
230 data[..6].copy_from_slice(b"GIF87a");
231 assert_eq!(detect_codec_format(&data), ImageCodecFormat::Gif);
232 }
233
234 #[test]
235 fn test_detect_gif89a() {
236 let mut data = vec![0u8; 20];
237 data[..6].copy_from_slice(b"GIF89a");
238 assert_eq!(detect_codec_format(&data), ImageCodecFormat::Gif);
239 }
240
241 #[test]
242 fn test_detect_unknown() {
243 let data = [0x00, 0x01, 0x02, 0x03];
244 assert_eq!(detect_codec_format(&data), ImageCodecFormat::Unknown);
245 }
246
247 #[test]
248 fn test_decoded_image_set_get_pixel() {
249 let mut img = DecodedImage::new(4, 4);
250 img.set_pixel(1, 2, 0xAA, 0xBB, 0xCC, 0xDD);
251 assert_eq!(img.get_pixel(1, 2), (0xAA, 0xBB, 0xCC, 0xDD));
252 }
253
254 #[test]
255 fn test_decoded_image_out_of_bounds() {
256 let mut img = DecodedImage::new(2, 2);
257 img.set_pixel(10, 10, 255, 0, 0, 255); assert_eq!(img.get_pixel(10, 10), (0, 0, 0, 0));
259 }
260
261 #[test]
262 fn test_clamp_u8_values() {
263 assert_eq!(clamp_u8(-10), 0);
264 assert_eq!(clamp_u8(0), 0);
265 assert_eq!(clamp_u8(128), 128);
266 assert_eq!(clamp_u8(255), 255);
267 assert_eq!(clamp_u8(300), 255);
268 }
269
270 #[test]
271 fn test_image_codec_error_equality() {
272 assert_eq!(
273 ImageCodecError::TruncatedData,
274 ImageCodecError::TruncatedData
275 );
276 assert_ne!(ImageCodecError::TruncatedData, ImageCodecError::CorruptData);
277 }
278
279 #[test]
280 fn test_decoded_image_new_size() {
281 let img = DecodedImage::new(10, 20);
282 assert_eq!(img.width, 10);
283 assert_eq!(img.height, 20);
284 assert_eq!(img.pixels.len(), 10 * 20 * 4);
285 }
286
287 #[test]
288 fn test_read_helpers() {
289 let data = [0x01, 0x02, 0x03, 0x04];
290 assert_eq!(read_be_u16(&data, 0), 0x0102);
291 assert_eq!(read_be_u32(&data, 0), 0x01020304);
292 assert_eq!(read_le_u16(&data, 0), 0x0201);
293 assert_eq!(read_le_u32(&data, 0), 0x04030201);
294 }
295}