image_meta/loader/
jpeg.rs

1use std::io::{BufRead, Cursor, Seek, SeekFrom};
2
3use byteorder::{BigEndian, ReadBytesExt};
4
5use crate::errors::{ImageError, ImageResult};
6use crate::types::{Color, ColorMode, Dimensions, Format, ImageMeta};
7
8const MARKER: u8 = 0xff;
9const SOI: u8 = 0xd8;
10
11pub fn load<R: ?Sized + BufRead + Seek>(image: &mut R) -> ImageResult<ImageMeta> {
12    read_signature(image)?;
13    let dimensions = read_sof(image)?;
14    let color = Color {
15        alpha_channel: false,
16        mode: ColorMode::Rgb,
17        resolution: 8,
18    };
19    Ok(ImageMeta {
20        animation_frames: None,
21        color,
22        dimensions,
23        format: Format::Jpeg,
24    })
25}
26
27fn read_signature<R: ?Sized + BufRead + Seek>(image: &mut R) -> ImageResult {
28    let mut soi = [0u8; 2];
29    image.read_exact(&mut soi)?;
30    if [MARKER, SOI] != soi {
31        return Err(ImageError::InvalidSignature);
32    }
33    Ok(())
34}
35
36fn read_sof<R: ?Sized + BufRead + Seek>(image: &mut R) -> ImageResult<Dimensions> {
37    loop {
38        if let (_, Some(data)) = read_segment(image, is_sof)? {
39            let mut data = Cursor::new(data);
40            data.seek(SeekFrom::Current(1))?;
41            let height = data.read_u16::<BigEndian>().map(u32::from)?;
42            let width = data.read_u16::<BigEndian>().map(u32::from)?;
43            return Ok(Dimensions { width, height });
44        }
45    }
46}
47
48fn read_segment<R: ?Sized + BufRead + Seek, F>(
49    image: &mut R,
50    target_marker: F,
51) -> ImageResult<(u8, Option<Vec<u8>>)>
52where
53    F: Fn(u8) -> bool,
54{
55    let prefix = image.read_u8()?;
56    if prefix != MARKER {
57        return Err(ImageError::CorruptImage("Marker not found".into()));
58    }
59
60    // Skip stuffing bytes
61    let mut marker = image.read_u8()?;
62    while marker == MARKER {
63        marker = image.read_u8()?;
64    }
65
66    let length = image.read_u16::<BigEndian>()? - 2;
67
68    if target_marker(marker) {
69        let mut result = vec![0u8; length as usize];
70        image.read_exact(&mut result)?;
71        Ok((marker, Some(result)))
72    } else {
73        image.seek(SeekFrom::Current(i64::from(length)))?;
74        Ok((marker, None))
75    }
76}
77
78fn is_sof(marker: u8) -> bool {
79    matches!(
80        marker,
81        0xc0 | 0xc1 | 0xc2 | 0xc3 | 0xc5 | 0xc6 | 0xc7 | 0xc9 | 0xca | 0xcb | 0xcd | 0xce | 0xcf
82    )
83}