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