image/codecs/jpeg/
decoder.rs1use std::io::{BufRead, Seek};
2use std::marker::PhantomData;
3
4use crate::color::ColorType;
5use crate::error::{
6 DecodingError, ImageError, ImageResult, LimitError, UnsupportedError, UnsupportedErrorKind,
7};
8use crate::image::{ImageDecoder, ImageFormat};
9use crate::metadata::Orientation;
10use crate::Limits;
11
12type ZuneColorSpace = zune_core::colorspace::ColorSpace;
13
14pub struct JpegDecoder<R> {
16 input: Vec<u8>,
17 orig_color_space: ZuneColorSpace,
18 width: u16,
19 height: u16,
20 limits: Limits,
21 orientation: Option<Orientation>,
22 phantom: PhantomData<R>,
25}
26
27impl<R: BufRead + Seek> JpegDecoder<R> {
28 pub fn new(r: R) -> ImageResult<JpegDecoder<R>> {
30 let mut input = Vec::new();
31 let mut r = r;
32 r.read_to_end(&mut input)?;
33 let options = zune_core::options::DecoderOptions::default()
34 .set_strict_mode(false)
35 .set_max_width(usize::MAX)
36 .set_max_height(usize::MAX);
37 let mut decoder = zune_jpeg::JpegDecoder::new_with_options(input.as_slice(), options);
38 decoder.decode_headers().map_err(ImageError::from_jpeg)?;
39 let (width, height) = decoder.dimensions().unwrap();
42 let width: u16 = width.try_into().unwrap();
44 let height: u16 = height.try_into().unwrap();
45 let orig_color_space = decoder.get_output_colorspace().unwrap();
46 let limits = Limits::no_limits();
48 Ok(JpegDecoder {
49 input,
50 orig_color_space,
51 width,
52 height,
53 limits,
54 orientation: None,
55 phantom: PhantomData,
56 })
57 }
58}
59
60impl<R: BufRead + Seek> ImageDecoder for JpegDecoder<R> {
61 fn dimensions(&self) -> (u32, u32) {
62 (u32::from(self.width), u32::from(self.height))
63 }
64
65 fn color_type(&self) -> ColorType {
66 ColorType::from_jpeg(self.orig_color_space)
67 }
68
69 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
70 let mut decoder = zune_jpeg::JpegDecoder::new(&self.input);
71 decoder.decode_headers().map_err(ImageError::from_jpeg)?;
72 Ok(decoder.icc_profile())
73 }
74
75 fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
76 let mut decoder = zune_jpeg::JpegDecoder::new(&self.input);
77 decoder.decode_headers().map_err(ImageError::from_jpeg)?;
78 let exif = decoder.exif().cloned();
79
80 self.orientation = Some(
81 exif.as_ref()
82 .and_then(|exif| Orientation::from_exif_chunk(exif))
83 .unwrap_or(Orientation::NoTransforms),
84 );
85
86 Ok(exif)
87 }
88
89 fn orientation(&mut self) -> ImageResult<Orientation> {
90 if self.orientation.is_none() {
92 let _ = self.exif_metadata()?;
93 }
94 Ok(self.orientation.unwrap())
95 }
96
97 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
98 let advertised_len = self.total_bytes();
99 let actual_len = buf.len() as u64;
100
101 if actual_len != advertised_len {
102 return Err(ImageError::Decoding(DecodingError::new(
103 ImageFormat::Jpeg.into(),
104 format!(
105 "Length of the decoded data {actual_len}\
106 doesn't match the advertised dimensions of the image\
107 that imply length {advertised_len}"
108 ),
109 )));
110 }
111
112 let mut decoder = new_zune_decoder(&self.input, self.orig_color_space, self.limits);
113 decoder.decode_into(buf).map_err(ImageError::from_jpeg)?;
114 Ok(())
115 }
116
117 fn set_limits(&mut self, limits: Limits) -> ImageResult<()> {
118 limits.check_support(&crate::LimitSupport::default())?;
119 let (width, height) = self.dimensions();
120 limits.check_dimensions(width, height)?;
121 self.limits = limits;
122 Ok(())
123 }
124
125 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
126 (*self).read_image(buf)
127 }
128}
129
130impl ColorType {
131 fn from_jpeg(colorspace: ZuneColorSpace) -> ColorType {
132 let colorspace = to_supported_color_space(colorspace);
133 use zune_core::colorspace::ColorSpace::*;
134 match colorspace {
135 RGB => ColorType::Rgb8,
138 RGBA => ColorType::Rgba8,
139 Luma => ColorType::L8,
140 LumaA => ColorType::La8,
141 _ => unreachable!(),
143 }
144 }
145}
146
147fn to_supported_color_space(orig: ZuneColorSpace) -> ZuneColorSpace {
148 use zune_core::colorspace::ColorSpace::*;
149 match orig {
150 RGB | RGBA | Luma | LumaA => orig,
151 _ => RGB,
153 }
154}
155
156fn new_zune_decoder(
157 input: &[u8],
158 orig_color_space: ZuneColorSpace,
159 limits: Limits,
160) -> zune_jpeg::JpegDecoder<&[u8]> {
161 let target_color_space = to_supported_color_space(orig_color_space);
162 let mut options = zune_core::options::DecoderOptions::default()
163 .jpeg_set_out_colorspace(target_color_space)
164 .set_strict_mode(false);
165 options = options.set_max_width(match limits.max_image_width {
166 Some(max_width) => max_width as usize, None => usize::MAX,
168 });
169 options = options.set_max_height(match limits.max_image_height {
170 Some(max_height) => max_height as usize, None => usize::MAX,
172 });
173 zune_jpeg::JpegDecoder::new_with_options(input, options)
174}
175
176impl ImageError {
177 fn from_jpeg(err: zune_jpeg::errors::DecodeErrors) -> ImageError {
178 use zune_jpeg::errors::DecodeErrors::*;
179 match err {
180 Unsupported(desc) => ImageError::Unsupported(UnsupportedError::from_format_and_kind(
181 ImageFormat::Jpeg.into(),
182 UnsupportedErrorKind::GenericFeature(format!("{desc:?}")),
183 )),
184 LargeDimensions(_) => ImageError::Limits(LimitError::from_kind(
185 crate::error::LimitErrorKind::DimensionError,
186 )),
187 err => ImageError::Decoding(DecodingError::new(ImageFormat::Jpeg.into(), err)),
188 }
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195 use std::{fs, io::Cursor};
196
197 #[test]
198 fn test_exif_orientation() {
199 let data = fs::read("tests/images/jpg/portrait_2.jpg").unwrap();
200 let mut decoder = JpegDecoder::new(Cursor::new(data)).unwrap();
201 assert_eq!(decoder.orientation().unwrap(), Orientation::FlipHorizontal);
202 }
203}