/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zune-jpeg-0.4.21/src/decoder.rs
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2023. |
3 | | * |
4 | | * This software is free software; |
5 | | * |
6 | | * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license |
7 | | */ |
8 | | |
9 | | //! Main image logic. |
10 | | #![allow(clippy::doc_markdown)] |
11 | | |
12 | | use alloc::string::ToString; |
13 | | use alloc::vec::Vec; |
14 | | use alloc::{format, vec}; |
15 | | |
16 | | use zune_core::bytestream::{ZByteReader, ZReaderTrait}; |
17 | | use zune_core::colorspace::ColorSpace; |
18 | | use zune_core::log::{error, trace, warn}; |
19 | | use zune_core::options::DecoderOptions; |
20 | | |
21 | | use crate::color_convert::choose_ycbcr_to_rgb_convert_func; |
22 | | use crate::components::{Components, SampleRatios}; |
23 | | use crate::errors::{DecodeErrors, UnsupportedSchemes}; |
24 | | use crate::headers::{ |
25 | | parse_app1, parse_app13, parse_app14, parse_app2, parse_dqt, parse_huffman, parse_sos, |
26 | | parse_start_of_frame |
27 | | }; |
28 | | use crate::huffman::HuffmanTable; |
29 | | use crate::idct::choose_idct_func; |
30 | | use crate::marker::Marker; |
31 | | use crate::misc::SOFMarkers; |
32 | | use crate::upsampler::{ |
33 | | choose_horizontal_samp_function, choose_hv_samp_function, choose_v_samp_function, |
34 | | generic_sampler, upsample_no_op |
35 | | }; |
36 | | |
37 | | /// Maximum components |
38 | | pub(crate) const MAX_COMPONENTS: usize = 4; |
39 | | |
40 | | /// Maximum image dimensions supported. |
41 | | pub(crate) const MAX_DIMENSIONS: usize = 1 << 27; |
42 | | |
43 | | /// Color conversion function that can convert YCbCr colorspace to RGB(A/X) for |
44 | | /// 16 values |
45 | | /// |
46 | | /// The following are guarantees to the following functions |
47 | | /// |
48 | | /// 1. The `&[i16]` slices passed contain 16 items |
49 | | /// |
50 | | /// 2. The slices passed are in the following order |
51 | | /// `y,cb,cr` |
52 | | /// |
53 | | /// 3. `&mut [u8]` is zero initialized |
54 | | /// |
55 | | /// 4. `&mut usize` points to the position in the array where new values should |
56 | | /// be used |
57 | | /// |
58 | | /// The pointer should |
59 | | /// 1. Carry out color conversion |
60 | | /// 2. Update `&mut usize` with the new position |
61 | | |
62 | | pub type ColorConvert16Ptr = fn(&[i16; 16], &[i16; 16], &[i16; 16], &mut [u8], &mut usize); |
63 | | |
64 | | /// IDCT function prototype |
65 | | /// |
66 | | /// This encapsulates a dequantize and IDCT function which will carry out the |
67 | | /// following functions |
68 | | /// |
69 | | /// Multiply each 64 element block of `&mut [i16]` with `&Aligned32<[i32;64]>` |
70 | | /// Carry out IDCT (type 3 dct) on ach block of 64 i16's |
71 | | pub type IDCTPtr = fn(&mut [i32; 64], &mut [i16], usize); |
72 | | |
73 | | /// An encapsulation of an ICC chunk |
74 | | pub(crate) struct ICCChunk { |
75 | | pub(crate) seq_no: u8, |
76 | | pub(crate) num_markers: u8, |
77 | | pub(crate) data: Vec<u8> |
78 | | } |
79 | | |
80 | | /// A JPEG Decoder Instance. |
81 | | #[allow(clippy::upper_case_acronyms, clippy::struct_excessive_bools)] |
82 | | pub struct JpegDecoder<T: ZReaderTrait> { |
83 | | /// Struct to hold image information from SOI |
84 | | pub(crate) info: ImageInfo, |
85 | | /// Quantization tables, will be set to none and the tables will |
86 | | /// be moved to `components` field |
87 | | pub(crate) qt_tables: [Option<[i32; 64]>; MAX_COMPONENTS], |
88 | | /// DC Huffman Tables with a maximum of 4 tables for each component |
89 | | pub(crate) dc_huffman_tables: [Option<HuffmanTable>; MAX_COMPONENTS], |
90 | | /// AC Huffman Tables with a maximum of 4 tables for each component |
91 | | pub(crate) ac_huffman_tables: [Option<HuffmanTable>; MAX_COMPONENTS], |
92 | | /// Image components, holds information like DC prediction and quantization |
93 | | /// tables of a component |
94 | | pub(crate) components: Vec<Components>, |
95 | | /// maximum horizontal component of all channels in the image |
96 | | pub(crate) h_max: usize, |
97 | | // maximum vertical component of all channels in the image |
98 | | pub(crate) v_max: usize, |
99 | | /// mcu's width (interleaved scans) |
100 | | pub(crate) mcu_width: usize, |
101 | | /// MCU height(interleaved scans |
102 | | pub(crate) mcu_height: usize, |
103 | | /// Number of MCU's in the x plane |
104 | | pub(crate) mcu_x: usize, |
105 | | /// Number of MCU's in the y plane |
106 | | pub(crate) mcu_y: usize, |
107 | | /// Is the image interleaved? |
108 | | pub(crate) is_interleaved: bool, |
109 | | pub(crate) sub_sample_ratio: SampleRatios, |
110 | | /// Image input colorspace, should be YCbCr for a sane image, might be |
111 | | /// grayscale too |
112 | | pub(crate) input_colorspace: ColorSpace, |
113 | | // Progressive image details |
114 | | /// Is the image progressive? |
115 | | pub(crate) is_progressive: bool, |
116 | | |
117 | | /// Start of spectral scan |
118 | | pub(crate) spec_start: u8, |
119 | | /// End of spectral scan |
120 | | pub(crate) spec_end: u8, |
121 | | /// Successive approximation bit position high |
122 | | pub(crate) succ_high: u8, |
123 | | /// Successive approximation bit position low |
124 | | pub(crate) succ_low: u8, |
125 | | /// Number of components. |
126 | | pub(crate) num_scans: u8, |
127 | | // Function pointers, for pointy stuff. |
128 | | /// Dequantize and idct function |
129 | | // This is determined at runtime which function to run, statically it's |
130 | | // initialized to a platform independent one and during initialization |
131 | | // of this struct, we check if we can switch to a faster one which |
132 | | // depend on certain CPU extensions. |
133 | | pub(crate) idct_func: IDCTPtr, |
134 | | // Color convert function which acts on 16 YCbCr values |
135 | | pub(crate) color_convert_16: ColorConvert16Ptr, |
136 | | pub(crate) z_order: [usize; MAX_COMPONENTS], |
137 | | /// restart markers |
138 | | pub(crate) restart_interval: usize, |
139 | | pub(crate) todo: usize, |
140 | | // decoder options |
141 | | pub(crate) options: DecoderOptions, |
142 | | // byte-stream |
143 | | pub(crate) stream: ZByteReader<T>, |
144 | | // Indicate whether headers have been decoded |
145 | | pub(crate) headers_decoded: bool, |
146 | | pub(crate) seen_sof: bool, |
147 | | // exif data, lifted from app2 |
148 | | pub(crate) exif_data: Option<Vec<u8>>, |
149 | | |
150 | | pub(crate) icc_data: Vec<ICCChunk>, |
151 | | pub(crate) is_mjpeg: bool, |
152 | | pub(crate) coeff: usize // Solves some weird bug :) |
153 | | } |
154 | | |
155 | | impl<T> JpegDecoder<T> |
156 | | where |
157 | | T: ZReaderTrait |
158 | | { |
159 | | #[allow(clippy::redundant_field_names)] |
160 | 5.54k | fn default(options: DecoderOptions, buffer: T) -> Self { |
161 | 5.54k | let color_convert = choose_ycbcr_to_rgb_convert_func(ColorSpace::RGB, &options).unwrap(); |
162 | 5.54k | JpegDecoder { |
163 | 5.54k | info: ImageInfo::default(), |
164 | 5.54k | qt_tables: [None, None, None, None], |
165 | 5.54k | dc_huffman_tables: [None, None, None, None], |
166 | 5.54k | ac_huffman_tables: [None, None, None, None], |
167 | 5.54k | components: vec![], |
168 | 5.54k | // Interleaved information |
169 | 5.54k | h_max: 1, |
170 | 5.54k | v_max: 1, |
171 | 5.54k | mcu_height: 0, |
172 | 5.54k | mcu_width: 0, |
173 | 5.54k | mcu_x: 0, |
174 | 5.54k | mcu_y: 0, |
175 | 5.54k | is_interleaved: false, |
176 | 5.54k | sub_sample_ratio: SampleRatios::None, |
177 | 5.54k | is_progressive: false, |
178 | 5.54k | spec_start: 0, |
179 | 5.54k | spec_end: 0, |
180 | 5.54k | succ_high: 0, |
181 | 5.54k | succ_low: 0, |
182 | 5.54k | num_scans: 0, |
183 | 5.54k | idct_func: choose_idct_func(&options), |
184 | 5.54k | color_convert_16: color_convert, |
185 | 5.54k | input_colorspace: ColorSpace::YCbCr, |
186 | 5.54k | z_order: [0; MAX_COMPONENTS], |
187 | 5.54k | restart_interval: 0, |
188 | 5.54k | todo: 0x7fff_ffff, |
189 | 5.54k | options: options, |
190 | 5.54k | stream: ZByteReader::new(buffer), |
191 | 5.54k | headers_decoded: false, |
192 | 5.54k | seen_sof: false, |
193 | 5.54k | exif_data: None, |
194 | 5.54k | icc_data: vec![], |
195 | 5.54k | is_mjpeg: false, |
196 | 5.54k | coeff: 1 |
197 | 5.54k | } |
198 | 5.54k | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::default Line | Count | Source | 160 | 5.54k | fn default(options: DecoderOptions, buffer: T) -> Self { | 161 | 5.54k | let color_convert = choose_ycbcr_to_rgb_convert_func(ColorSpace::RGB, &options).unwrap(); | 162 | 5.54k | JpegDecoder { | 163 | 5.54k | info: ImageInfo::default(), | 164 | 5.54k | qt_tables: [None, None, None, None], | 165 | 5.54k | dc_huffman_tables: [None, None, None, None], | 166 | 5.54k | ac_huffman_tables: [None, None, None, None], | 167 | 5.54k | components: vec![], | 168 | 5.54k | // Interleaved information | 169 | 5.54k | h_max: 1, | 170 | 5.54k | v_max: 1, | 171 | 5.54k | mcu_height: 0, | 172 | 5.54k | mcu_width: 0, | 173 | 5.54k | mcu_x: 0, | 174 | 5.54k | mcu_y: 0, | 175 | 5.54k | is_interleaved: false, | 176 | 5.54k | sub_sample_ratio: SampleRatios::None, | 177 | 5.54k | is_progressive: false, | 178 | 5.54k | spec_start: 0, | 179 | 5.54k | spec_end: 0, | 180 | 5.54k | succ_high: 0, | 181 | 5.54k | succ_low: 0, | 182 | 5.54k | num_scans: 0, | 183 | 5.54k | idct_func: choose_idct_func(&options), | 184 | 5.54k | color_convert_16: color_convert, | 185 | 5.54k | input_colorspace: ColorSpace::YCbCr, | 186 | 5.54k | z_order: [0; MAX_COMPONENTS], | 187 | 5.54k | restart_interval: 0, | 188 | 5.54k | todo: 0x7fff_ffff, | 189 | 5.54k | options: options, | 190 | 5.54k | stream: ZByteReader::new(buffer), | 191 | 5.54k | headers_decoded: false, | 192 | 5.54k | seen_sof: false, | 193 | 5.54k | exif_data: None, | 194 | 5.54k | icc_data: vec![], | 195 | 5.54k | is_mjpeg: false, | 196 | 5.54k | coeff: 1 | 197 | 5.54k | } | 198 | 5.54k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::default |
199 | | /// Decode a buffer already in memory |
200 | | /// |
201 | | /// The buffer should be a valid jpeg file, perhaps created by the command |
202 | | /// `std:::fs::read()` or a JPEG file downloaded from the internet. |
203 | | /// |
204 | | /// # Errors |
205 | | /// See DecodeErrors for an explanation |
206 | 4.70k | pub fn decode(&mut self) -> Result<Vec<u8>, DecodeErrors> { |
207 | 4.70k | self.decode_headers()?; |
208 | 4.70k | let size = self.output_buffer_size().unwrap(); |
209 | 4.70k | let mut out = vec![0; size]; |
210 | 4.70k | self.decode_into(&mut out)?; |
211 | 1.04k | Ok(out) |
212 | 4.70k | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode Line | Count | Source | 206 | 4.70k | pub fn decode(&mut self) -> Result<Vec<u8>, DecodeErrors> { | 207 | 4.70k | self.decode_headers()?; | 208 | 4.70k | let size = self.output_buffer_size().unwrap(); | 209 | 4.70k | let mut out = vec![0; size]; | 210 | 4.70k | self.decode_into(&mut out)?; | 211 | 1.04k | Ok(out) | 212 | 4.70k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::decode |
213 | | |
214 | | /// Create a new Decoder instance |
215 | | /// |
216 | | /// # Arguments |
217 | | /// - `stream`: The raw bytes of a jpeg file. |
218 | | #[must_use] |
219 | | #[allow(clippy::new_without_default)] |
220 | 5.54k | pub fn new(stream: T) -> JpegDecoder<T> { |
221 | 5.54k | JpegDecoder::default(DecoderOptions::default(), stream) |
222 | 5.54k | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::new Line | Count | Source | 220 | 5.54k | pub fn new(stream: T) -> JpegDecoder<T> { | 221 | 5.54k | JpegDecoder::default(DecoderOptions::default(), stream) | 222 | 5.54k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::new |
223 | | |
224 | | /// Returns the image information |
225 | | /// |
226 | | /// This **must** be called after a subsequent call to [`decode`] or [`decode_headers`] |
227 | | /// it will return `None` |
228 | | /// |
229 | | /// # Returns |
230 | | /// - `Some(info)`: Image information,width, height, number of components |
231 | | /// - None: Indicates image headers haven't been decoded |
232 | | /// |
233 | | /// [`decode`]: JpegDecoder::decode |
234 | | /// [`decode_headers`]: JpegDecoder::decode_headers |
235 | | #[must_use] |
236 | 0 | pub fn info(&self) -> Option<ImageInfo> { |
237 | | // we check for fails to that call by comparing what we have to the default, if |
238 | | // it's default we assume that the caller failed to uphold the |
239 | | // guarantees. We can be sure that an image cannot be the default since |
240 | | // its a hard panic in-case width or height are set to zero. |
241 | 0 | if !self.headers_decoded { |
242 | 0 | return None; |
243 | 0 | } |
244 | | |
245 | 0 | return Some(self.info.clone()); |
246 | 0 | } |
247 | | |
248 | | /// Return the number of bytes required to hold a decoded image frame |
249 | | /// decoded using the given input transformations |
250 | | /// |
251 | | /// # Returns |
252 | | /// - `Some(usize)`: Minimum size for a buffer needed to decode the image |
253 | | /// - `None`: Indicates the image was not decoded, or image dimensions would overflow a usize |
254 | | /// |
255 | | #[must_use] |
256 | 9.40k | pub fn output_buffer_size(&self) -> Option<usize> { |
257 | 9.40k | return if self.headers_decoded { |
258 | | Some( |
259 | 9.40k | usize::from(self.width()) |
260 | 9.40k | .checked_mul(usize::from(self.height()))? |
261 | 9.40k | .checked_mul(self.options.jpeg_get_out_colorspace().num_components())? |
262 | | ) |
263 | | } else { |
264 | 0 | None |
265 | | }; |
266 | 9.40k | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::output_buffer_size Line | Count | Source | 256 | 9.40k | pub fn output_buffer_size(&self) -> Option<usize> { | 257 | 9.40k | return if self.headers_decoded { | 258 | | Some( | 259 | 9.40k | usize::from(self.width()) | 260 | 9.40k | .checked_mul(usize::from(self.height()))? | 261 | 9.40k | .checked_mul(self.options.jpeg_get_out_colorspace().num_components())? | 262 | | ) | 263 | | } else { | 264 | 0 | None | 265 | | }; | 266 | 9.40k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::output_buffer_size |
267 | | |
268 | | /// Get a mutable reference to the decoder options |
269 | | /// for the decoder instance |
270 | | /// |
271 | | /// This can be used to modify options before actual decoding |
272 | | /// but after initial creation |
273 | | /// |
274 | | /// # Example |
275 | | /// ```no_run |
276 | | /// use zune_jpeg::JpegDecoder; |
277 | | /// |
278 | | /// let mut decoder = JpegDecoder::new(&[]); |
279 | | /// // get current options |
280 | | /// let mut options = decoder.get_options(); |
281 | | /// // modify it |
282 | | /// let new_options = options.set_max_width(10); |
283 | | /// // set it back |
284 | | /// decoder.set_options(new_options); |
285 | | /// |
286 | | /// ``` |
287 | | #[must_use] |
288 | 0 | pub const fn get_options(&self) -> &DecoderOptions { |
289 | 0 | &self.options |
290 | 0 | } |
291 | | /// Return the input colorspace of the image |
292 | | /// |
293 | | /// This indicates the colorspace that is present in |
294 | | /// the image, but this may be different to the colorspace that |
295 | | /// the output will be transformed to |
296 | | /// |
297 | | /// # Returns |
298 | | /// -`Some(Colorspace)`: Input colorspace |
299 | | /// - None : Indicates the headers weren't decoded |
300 | | #[must_use] |
301 | 4.70k | pub fn get_input_colorspace(&self) -> Option<ColorSpace> { |
302 | 4.70k | return if self.headers_decoded { Some(self.input_colorspace) } else { None }; |
303 | 4.70k | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::get_input_colorspace Line | Count | Source | 301 | 4.70k | pub fn get_input_colorspace(&self) -> Option<ColorSpace> { | 302 | 4.70k | return if self.headers_decoded { Some(self.input_colorspace) } else { None }; | 303 | 4.70k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::get_input_colorspace |
304 | | /// Set decoder options |
305 | | /// |
306 | | /// This can be used to set new options even after initialization |
307 | | /// but before decoding. |
308 | | /// |
309 | | /// This does not bear any significance after decoding an image |
310 | | /// |
311 | | /// # Arguments |
312 | | /// - `options`: New decoder options |
313 | | /// |
314 | | /// # Example |
315 | | /// Set maximum jpeg progressive passes to be 4 |
316 | | /// |
317 | | /// ```no_run |
318 | | /// use zune_jpeg::JpegDecoder; |
319 | | /// let mut decoder =JpegDecoder::new(&[]); |
320 | | /// // this works also because DecoderOptions implements `Copy` |
321 | | /// let options = decoder.get_options().jpeg_set_max_scans(4); |
322 | | /// // set the new options |
323 | | /// decoder.set_options(options); |
324 | | /// // now decode |
325 | | /// decoder.decode().unwrap(); |
326 | | /// ``` |
327 | 4.70k | pub fn set_options(&mut self, options: DecoderOptions) { |
328 | 4.70k | self.options = options; |
329 | 4.70k | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::set_options Line | Count | Source | 327 | 4.70k | pub fn set_options(&mut self, options: DecoderOptions) { | 328 | 4.70k | self.options = options; | 329 | 4.70k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::set_options |
330 | | /// Decode Decoder headers |
331 | | /// |
332 | | /// This routine takes care of parsing supported headers from a Decoder |
333 | | /// image |
334 | | /// |
335 | | /// # Supported Headers |
336 | | /// - APP(0) |
337 | | /// - SOF(O) |
338 | | /// - DQT -> Quantization tables |
339 | | /// - DHT -> Huffman tables |
340 | | /// - SOS -> Start of Scan |
341 | | /// # Unsupported Headers |
342 | | /// - SOF(n) -> Decoder images which are not baseline/progressive |
343 | | /// - DAC -> Images using Arithmetic tables |
344 | | /// - JPG(n) |
345 | 14.9k | fn decode_headers_internal(&mut self) -> Result<(), DecodeErrors> { |
346 | 14.9k | if self.headers_decoded { |
347 | | trace!("Headers decoded!"); |
348 | 9.40k | return Ok(()); |
349 | 5.54k | } |
350 | | // match output colorspace here |
351 | | // we know this will only be called once per image |
352 | | // so makes sense |
353 | | // We only care for ycbcr to rgb/rgba here |
354 | | // in case one is using another colorspace. |
355 | | // May god help you |
356 | 5.54k | let out_colorspace = self.options.jpeg_get_out_colorspace(); |
357 | | |
358 | 0 | if matches!( |
359 | 5.54k | out_colorspace, |
360 | | ColorSpace::BGR | ColorSpace::BGRA | ColorSpace::RGB | ColorSpace::RGBA |
361 | 5.54k | ) { |
362 | 5.54k | self.color_convert_16 = choose_ycbcr_to_rgb_convert_func( |
363 | 5.54k | self.options.jpeg_get_out_colorspace(), |
364 | 5.54k | &self.options |
365 | 5.54k | ) |
366 | 5.54k | .unwrap(); |
367 | 5.54k | } |
368 | | // First two bytes should be jpeg soi marker |
369 | 5.54k | let magic_bytes = self.stream.get_u16_be_err()?; |
370 | | |
371 | 5.53k | let mut last_byte = 0; |
372 | 5.53k | let mut bytes_before_marker = 0; |
373 | | |
374 | 5.53k | if magic_bytes != 0xffd8 { |
375 | 61 | return Err(DecodeErrors::IllegalMagicBytes(magic_bytes)); |
376 | 5.47k | } |
377 | | |
378 | | loop { |
379 | | // read a byte |
380 | 129k | let mut m = self.stream.get_u8_err()?; |
381 | | |
382 | | // AND OF COURSE some images will have fill bytes in their marker |
383 | | // bitstreams because why not. |
384 | | // |
385 | | // I am disappointed as a man. |
386 | 129k | if (m == 0xFF || m == 0) && last_byte == 0xFF { |
387 | | // This handles the edge case where |
388 | | // images have markers with fill bytes(0xFF) |
389 | | // or byte stuffing (0) |
390 | | // I.e 0xFF 0xFF 0xDA |
391 | | // and |
392 | | // 0xFF 0 0xDA |
393 | | // It should ignore those fill bytes and take 0xDA |
394 | | // I don't know why such images exist |
395 | | // but they do. |
396 | | // so this is for you (with love) |
397 | 30.1k | while m == 0xFF || m == 0x0 { |
398 | 28.6k | last_byte = m; |
399 | 28.6k | m = self.stream.get_u8_err()?; |
400 | | } |
401 | 127k | } |
402 | | // Last byte should be 0xFF to confirm existence of a marker since markers look |
403 | | // like OxFF(some marker data) |
404 | 129k | if last_byte == 0xFF { |
405 | 25.1k | let marker = Marker::from_u8(m); |
406 | 25.1k | if let Some(n) = marker { |
407 | 24.5k | if bytes_before_marker > 3 { |
408 | 124 | if self.options.get_strict_mode() |
409 | | /*No reason to use this*/ |
410 | | { |
411 | 124 | return Err(DecodeErrors::FormatStatic( |
412 | 124 | "[strict-mode]: Extra bytes between headers" |
413 | 124 | )); |
414 | 0 | } |
415 | | |
416 | 0 | error!( |
417 | | "Extra bytes {} before marker 0xFF{:X}", |
418 | | bytes_before_marker - 3, |
419 | | m |
420 | | ); |
421 | 24.4k | } |
422 | | |
423 | 24.4k | bytes_before_marker = 0; |
424 | | |
425 | 24.4k | self.parse_marker_inner(n)?; |
426 | | |
427 | 23.9k | if n == Marker::SOS { |
428 | 4.70k | self.headers_decoded = true; |
429 | | trace!("Input colorspace {:?}", self.input_colorspace); |
430 | 4.70k | return Ok(()); |
431 | 19.2k | } |
432 | | } else { |
433 | 616 | bytes_before_marker = 0; |
434 | | |
435 | 616 | warn!("Marker 0xFF{:X} not known", m); |
436 | | |
437 | 616 | let length = self.stream.get_u16_be_err()?; |
438 | | |
439 | 600 | if length < 2 { |
440 | 22 | return Err(DecodeErrors::Format(format!( |
441 | 22 | "Found a marker with invalid length : {length}" |
442 | 22 | ))); |
443 | 578 | } |
444 | | |
445 | 578 | warn!("Skipping {} bytes", length - 2); |
446 | 578 | self.stream.skip((length - 2) as usize); |
447 | | } |
448 | 104k | } |
449 | 124k | last_byte = m; |
450 | 124k | bytes_before_marker += 1; |
451 | | } |
452 | 14.9k | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_headers_internal Line | Count | Source | 345 | 14.9k | fn decode_headers_internal(&mut self) -> Result<(), DecodeErrors> { | 346 | 14.9k | if self.headers_decoded { | 347 | | trace!("Headers decoded!"); | 348 | 9.40k | return Ok(()); | 349 | 5.54k | } | 350 | | // match output colorspace here | 351 | | // we know this will only be called once per image | 352 | | // so makes sense | 353 | | // We only care for ycbcr to rgb/rgba here | 354 | | // in case one is using another colorspace. | 355 | | // May god help you | 356 | 5.54k | let out_colorspace = self.options.jpeg_get_out_colorspace(); | 357 | | | 358 | 0 | if matches!( | 359 | 5.54k | out_colorspace, | 360 | | ColorSpace::BGR | ColorSpace::BGRA | ColorSpace::RGB | ColorSpace::RGBA | 361 | 5.54k | ) { | 362 | 5.54k | self.color_convert_16 = choose_ycbcr_to_rgb_convert_func( | 363 | 5.54k | self.options.jpeg_get_out_colorspace(), | 364 | 5.54k | &self.options | 365 | 5.54k | ) | 366 | 5.54k | .unwrap(); | 367 | 5.54k | } | 368 | | // First two bytes should be jpeg soi marker | 369 | 5.54k | let magic_bytes = self.stream.get_u16_be_err()?; | 370 | | | 371 | 5.53k | let mut last_byte = 0; | 372 | 5.53k | let mut bytes_before_marker = 0; | 373 | | | 374 | 5.53k | if magic_bytes != 0xffd8 { | 375 | 61 | return Err(DecodeErrors::IllegalMagicBytes(magic_bytes)); | 376 | 5.47k | } | 377 | | | 378 | | loop { | 379 | | // read a byte | 380 | 129k | let mut m = self.stream.get_u8_err()?; | 381 | | | 382 | | // AND OF COURSE some images will have fill bytes in their marker | 383 | | // bitstreams because why not. | 384 | | // | 385 | | // I am disappointed as a man. | 386 | 129k | if (m == 0xFF || m == 0) && last_byte == 0xFF { | 387 | | // This handles the edge case where | 388 | | // images have markers with fill bytes(0xFF) | 389 | | // or byte stuffing (0) | 390 | | // I.e 0xFF 0xFF 0xDA | 391 | | // and | 392 | | // 0xFF 0 0xDA | 393 | | // It should ignore those fill bytes and take 0xDA | 394 | | // I don't know why such images exist | 395 | | // but they do. | 396 | | // so this is for you (with love) | 397 | 30.1k | while m == 0xFF || m == 0x0 { | 398 | 28.6k | last_byte = m; | 399 | 28.6k | m = self.stream.get_u8_err()?; | 400 | | } | 401 | 127k | } | 402 | | // Last byte should be 0xFF to confirm existence of a marker since markers look | 403 | | // like OxFF(some marker data) | 404 | 129k | if last_byte == 0xFF { | 405 | 25.1k | let marker = Marker::from_u8(m); | 406 | 25.1k | if let Some(n) = marker { | 407 | 24.5k | if bytes_before_marker > 3 { | 408 | 124 | if self.options.get_strict_mode() | 409 | | /*No reason to use this*/ | 410 | | { | 411 | 124 | return Err(DecodeErrors::FormatStatic( | 412 | 124 | "[strict-mode]: Extra bytes between headers" | 413 | 124 | )); | 414 | 0 | } | 415 | | | 416 | 0 | error!( | 417 | | "Extra bytes {} before marker 0xFF{:X}", | 418 | | bytes_before_marker - 3, | 419 | | m | 420 | | ); | 421 | 24.4k | } | 422 | | | 423 | 24.4k | bytes_before_marker = 0; | 424 | | | 425 | 24.4k | self.parse_marker_inner(n)?; | 426 | | | 427 | 23.9k | if n == Marker::SOS { | 428 | 4.70k | self.headers_decoded = true; | 429 | | trace!("Input colorspace {:?}", self.input_colorspace); | 430 | 4.70k | return Ok(()); | 431 | 19.2k | } | 432 | | } else { | 433 | 616 | bytes_before_marker = 0; | 434 | | | 435 | 616 | warn!("Marker 0xFF{:X} not known", m); | 436 | | | 437 | 616 | let length = self.stream.get_u16_be_err()?; | 438 | | | 439 | 600 | if length < 2 { | 440 | 22 | return Err(DecodeErrors::Format(format!( | 441 | 22 | "Found a marker with invalid length : {length}" | 442 | 22 | ))); | 443 | 578 | } | 444 | | | 445 | 578 | warn!("Skipping {} bytes", length - 2); | 446 | 578 | self.stream.skip((length - 2) as usize); | 447 | | } | 448 | 104k | } | 449 | 124k | last_byte = m; | 450 | 124k | bytes_before_marker += 1; | 451 | | } | 452 | 14.9k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::decode_headers_internal |
453 | | #[allow(clippy::too_many_lines)] |
454 | 108k | pub(crate) fn parse_marker_inner(&mut self, m: Marker) -> Result<(), DecodeErrors> { |
455 | 108k | match m { |
456 | 4.94k | Marker::SOF(0..=2) => { |
457 | 4.94k | let marker = { |
458 | | // choose marker |
459 | 4.94k | if m == Marker::SOF(0) || m == Marker::SOF(1) { |
460 | 1.39k | SOFMarkers::BaselineDct |
461 | | } else { |
462 | 3.54k | self.is_progressive = true; |
463 | 3.54k | SOFMarkers::ProgressiveDctHuffman |
464 | | } |
465 | | }; |
466 | | |
467 | | trace!("Image encoding scheme =`{:?}`", marker); |
468 | | // get components |
469 | 4.94k | parse_start_of_frame(marker, self)?; |
470 | | } |
471 | | // Start of Frame Segments not supported |
472 | 0 | Marker::SOF(v) => { |
473 | 0 | let feature = UnsupportedSchemes::from_int(v); |
474 | | |
475 | 0 | if let Some(feature) = feature { |
476 | 0 | return Err(DecodeErrors::Unsupported(feature)); |
477 | 0 | } |
478 | | |
479 | 0 | return Err(DecodeErrors::Format("Unsupported image format".to_string())); |
480 | | } |
481 | | //APP(0) segment |
482 | | Marker::APP(0) => { |
483 | 11.1k | let mut length = self.stream.get_u16_be_err()?; |
484 | | |
485 | 11.1k | if length < 2 { |
486 | 5 | return Err(DecodeErrors::Format(format!( |
487 | 5 | "Found a marker with invalid length:{length}\n" |
488 | 5 | ))); |
489 | 11.1k | } |
490 | | // skip for now |
491 | 11.1k | if length > 5 && self.stream.has(5) { |
492 | 10.1k | let mut buffer = [0u8; 5]; |
493 | 10.1k | self.stream.read_exact(&mut buffer).unwrap(); |
494 | 10.1k | if &buffer == b"AVI1\0" { |
495 | 8.69k | self.is_mjpeg = true; |
496 | 8.69k | } |
497 | 10.1k | length -= 5; |
498 | 977 | } |
499 | 11.1k | self.stream.skip(length.saturating_sub(2) as usize); |
500 | | |
501 | | //parse_app(buf, m, &mut self.info)?; |
502 | | } |
503 | | Marker::APP(1) => { |
504 | 7.13k | parse_app1(self)?; |
505 | | } |
506 | | |
507 | | Marker::APP(2) => { |
508 | 39.2k | parse_app2(self)?; |
509 | | } |
510 | | // Quantization tables |
511 | | Marker::DQT => { |
512 | 7.31k | parse_dqt(self)?; |
513 | | } |
514 | | // Huffman tables |
515 | | Marker::DHT => { |
516 | 17.2k | parse_huffman(self)?; |
517 | | } |
518 | | // Start of Scan Data |
519 | | Marker::SOS => { |
520 | 4.77k | parse_sos(self)?; |
521 | | |
522 | | // break after reading the start of scan. |
523 | | // what follows is the image data |
524 | 4.71k | return Ok(()); |
525 | | } |
526 | 1 | Marker::EOI => return Err(DecodeErrors::FormatStatic("Premature End of image")), |
527 | | |
528 | | Marker::DAC | Marker::DNL => { |
529 | 11 | return Err(DecodeErrors::Format(format!( |
530 | 11 | "Parsing of the following header `{m:?}` is not supported,\ |
531 | 11 | cannot continue" |
532 | 11 | ))); |
533 | | } |
534 | | Marker::DRI => { |
535 | | trace!("DRI marker present"); |
536 | | |
537 | 15.8k | if self.stream.get_u16_be_err()? != 4 { |
538 | 22 | return Err(DecodeErrors::Format( |
539 | 22 | "Bad DRI length, Corrupt JPEG".to_string() |
540 | 22 | )); |
541 | 15.8k | } |
542 | | |
543 | 15.8k | self.restart_interval = usize::from(self.stream.get_u16_be_err()?); |
544 | 15.8k | self.todo = self.restart_interval; |
545 | | } |
546 | | Marker::APP(13) => { |
547 | 249 | parse_app13(self)?; |
548 | | } |
549 | | Marker::APP(14) => { |
550 | 103 | parse_app14(self)?; |
551 | | } |
552 | | _ => { |
553 | 533 | warn!( |
554 | | "Capabilities for processing marker \"{:?}\" not implemented", |
555 | | m |
556 | | ); |
557 | | |
558 | 533 | let length = self.stream.get_u16_be_err()?; |
559 | | |
560 | 529 | if length < 2 { |
561 | 4 | return Err(DecodeErrors::Format(format!( |
562 | 4 | "Found a marker with invalid length:{length}\n" |
563 | 4 | ))); |
564 | 525 | } |
565 | 525 | warn!("Skipping {} bytes", length - 2); |
566 | 525 | self.stream.skip((length - 2) as usize); |
567 | | } |
568 | | } |
569 | 102k | Ok(()) |
570 | 108k | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::parse_marker_inner Line | Count | Source | 454 | 108k | pub(crate) fn parse_marker_inner(&mut self, m: Marker) -> Result<(), DecodeErrors> { | 455 | 108k | match m { | 456 | 4.94k | Marker::SOF(0..=2) => { | 457 | 4.94k | let marker = { | 458 | | // choose marker | 459 | 4.94k | if m == Marker::SOF(0) || m == Marker::SOF(1) { | 460 | 1.39k | SOFMarkers::BaselineDct | 461 | | } else { | 462 | 3.54k | self.is_progressive = true; | 463 | 3.54k | SOFMarkers::ProgressiveDctHuffman | 464 | | } | 465 | | }; | 466 | | | 467 | | trace!("Image encoding scheme =`{:?}`", marker); | 468 | | // get components | 469 | 4.94k | parse_start_of_frame(marker, self)?; | 470 | | } | 471 | | // Start of Frame Segments not supported | 472 | 0 | Marker::SOF(v) => { | 473 | 0 | let feature = UnsupportedSchemes::from_int(v); | 474 | | | 475 | 0 | if let Some(feature) = feature { | 476 | 0 | return Err(DecodeErrors::Unsupported(feature)); | 477 | 0 | } | 478 | | | 479 | 0 | return Err(DecodeErrors::Format("Unsupported image format".to_string())); | 480 | | } | 481 | | //APP(0) segment | 482 | | Marker::APP(0) => { | 483 | 11.1k | let mut length = self.stream.get_u16_be_err()?; | 484 | | | 485 | 11.1k | if length < 2 { | 486 | 5 | return Err(DecodeErrors::Format(format!( | 487 | 5 | "Found a marker with invalid length:{length}\n" | 488 | 5 | ))); | 489 | 11.1k | } | 490 | | // skip for now | 491 | 11.1k | if length > 5 && self.stream.has(5) { | 492 | 10.1k | let mut buffer = [0u8; 5]; | 493 | 10.1k | self.stream.read_exact(&mut buffer).unwrap(); | 494 | 10.1k | if &buffer == b"AVI1\0" { | 495 | 8.69k | self.is_mjpeg = true; | 496 | 8.69k | } | 497 | 10.1k | length -= 5; | 498 | 977 | } | 499 | 11.1k | self.stream.skip(length.saturating_sub(2) as usize); | 500 | | | 501 | | //parse_app(buf, m, &mut self.info)?; | 502 | | } | 503 | | Marker::APP(1) => { | 504 | 7.13k | parse_app1(self)?; | 505 | | } | 506 | | | 507 | | Marker::APP(2) => { | 508 | 39.2k | parse_app2(self)?; | 509 | | } | 510 | | // Quantization tables | 511 | | Marker::DQT => { | 512 | 7.31k | parse_dqt(self)?; | 513 | | } | 514 | | // Huffman tables | 515 | | Marker::DHT => { | 516 | 17.2k | parse_huffman(self)?; | 517 | | } | 518 | | // Start of Scan Data | 519 | | Marker::SOS => { | 520 | 4.77k | parse_sos(self)?; | 521 | | | 522 | | // break after reading the start of scan. | 523 | | // what follows is the image data | 524 | 4.71k | return Ok(()); | 525 | | } | 526 | 1 | Marker::EOI => return Err(DecodeErrors::FormatStatic("Premature End of image")), | 527 | | | 528 | | Marker::DAC | Marker::DNL => { | 529 | 11 | return Err(DecodeErrors::Format(format!( | 530 | 11 | "Parsing of the following header `{m:?}` is not supported,\ | 531 | 11 | cannot continue" | 532 | 11 | ))); | 533 | | } | 534 | | Marker::DRI => { | 535 | | trace!("DRI marker present"); | 536 | | | 537 | 15.8k | if self.stream.get_u16_be_err()? != 4 { | 538 | 22 | return Err(DecodeErrors::Format( | 539 | 22 | "Bad DRI length, Corrupt JPEG".to_string() | 540 | 22 | )); | 541 | 15.8k | } | 542 | | | 543 | 15.8k | self.restart_interval = usize::from(self.stream.get_u16_be_err()?); | 544 | 15.8k | self.todo = self.restart_interval; | 545 | | } | 546 | | Marker::APP(13) => { | 547 | 249 | parse_app13(self)?; | 548 | | } | 549 | | Marker::APP(14) => { | 550 | 103 | parse_app14(self)?; | 551 | | } | 552 | | _ => { | 553 | 533 | warn!( | 554 | | "Capabilities for processing marker \"{:?}\" not implemented", | 555 | | m | 556 | | ); | 557 | | | 558 | 533 | let length = self.stream.get_u16_be_err()?; | 559 | | | 560 | 529 | if length < 2 { | 561 | 4 | return Err(DecodeErrors::Format(format!( | 562 | 4 | "Found a marker with invalid length:{length}\n" | 563 | 4 | ))); | 564 | 525 | } | 565 | 525 | warn!("Skipping {} bytes", length - 2); | 566 | 525 | self.stream.skip((length - 2) as usize); | 567 | | } | 568 | | } | 569 | 102k | Ok(()) | 570 | 108k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::parse_marker_inner |
571 | | /// Get the embedded ICC profile if it exists |
572 | | /// and is correct |
573 | | /// |
574 | | /// One needs not to decode the whole image to extract this, |
575 | | /// calling [`decode_headers`] for an image with an ICC profile |
576 | | /// allows you to decode this |
577 | | /// |
578 | | /// # Returns |
579 | | /// - `Some(Vec<u8>)`: The raw ICC profile of the image |
580 | | /// - `None`: May indicate an error in the ICC profile , non-existence of |
581 | | /// an ICC profile, or that the headers weren't decoded. |
582 | | /// |
583 | | /// [`decode_headers`]:Self::decode_headers |
584 | | #[must_use] |
585 | 0 | pub fn icc_profile(&self) -> Option<Vec<u8>> { |
586 | 0 | let mut marker_present: [Option<&ICCChunk>; 256] = [None; 256]; |
587 | | |
588 | 0 | if !self.headers_decoded { |
589 | 0 | return None; |
590 | 0 | } |
591 | 0 | let num_markers = self.icc_data.len(); |
592 | | |
593 | 0 | if num_markers == 0 || num_markers >= 255 { |
594 | 0 | return None; |
595 | 0 | } |
596 | | // check validity |
597 | 0 | for chunk in &self.icc_data { |
598 | 0 | if usize::from(chunk.num_markers) != num_markers { |
599 | | // all the lengths must match |
600 | 0 | return None; |
601 | 0 | } |
602 | 0 | if chunk.seq_no == 0 { |
603 | 0 | warn!("Zero sequence number in ICC, corrupt ICC chunk"); |
604 | 0 | return None; |
605 | 0 | } |
606 | 0 | if marker_present[usize::from(chunk.seq_no)].is_some() { |
607 | | // duplicate seq_no |
608 | 0 | warn!("Duplicate sequence number in ICC, corrupt chunk"); |
609 | 0 | return None; |
610 | 0 | } |
611 | | |
612 | 0 | marker_present[usize::from(chunk.seq_no)] = Some(chunk); |
613 | | } |
614 | 0 | let mut data = Vec::with_capacity(1000); |
615 | | // assemble the data now |
616 | 0 | for chunk in marker_present.get(1..=num_markers).unwrap() { |
617 | 0 | if let Some(ch) = chunk { |
618 | 0 | data.extend_from_slice(&ch.data); |
619 | 0 | } else { |
620 | 0 | warn!("Missing icc sequence number, corrupt ICC chunk "); |
621 | 0 | return None; |
622 | | } |
623 | | } |
624 | | |
625 | 0 | Some(data) |
626 | 0 | } |
627 | | /// Return the exif data for the file |
628 | | /// |
629 | | /// This returns the raw exif data starting at the |
630 | | /// TIFF header |
631 | | /// |
632 | | /// # Returns |
633 | | /// -`Some(data)`: The raw exif data, if present in the image |
634 | | /// - None: May indicate the following |
635 | | /// |
636 | | /// 1. The image doesn't have exif data |
637 | | /// 2. The image headers haven't been decoded |
638 | | #[must_use] |
639 | 0 | pub fn exif(&self) -> Option<&Vec<u8>> { |
640 | 0 | return self.exif_data.as_ref(); |
641 | 0 | } |
642 | | /// Get the output colorspace the image pixels will be decoded into |
643 | | /// |
644 | | /// |
645 | | /// # Note. |
646 | | /// This field can only be regarded after decoding headers, |
647 | | /// as markers such as Adobe APP14 may dictate different colorspaces |
648 | | /// than requested. |
649 | | /// |
650 | | /// Calling `decode_headers` is sufficient to know what colorspace the |
651 | | /// output is, if this is called after `decode` it indicates the colorspace |
652 | | /// the output is currently in |
653 | | /// |
654 | | /// Additionally not all input->output colorspace mappings are supported |
655 | | /// but all input colorspaces can map to RGB colorspace, so that's a safe bet |
656 | | /// if one is handling image formats |
657 | | /// |
658 | | ///# Returns |
659 | | /// - `Some(Colorspace)`: If headers have been decoded, the colorspace the |
660 | | ///output array will be in |
661 | | ///- `None |
662 | | #[must_use] |
663 | 0 | pub fn get_output_colorspace(&self) -> Option<ColorSpace> { |
664 | 0 | return if self.headers_decoded { |
665 | 0 | Some(self.options.jpeg_get_out_colorspace()) |
666 | | } else { |
667 | 0 | None |
668 | | }; |
669 | 0 | } |
670 | | |
671 | | /// Decode into a pre-allocated buffer |
672 | | /// |
673 | | /// It is an error if the buffer size is smaller than |
674 | | /// [`output_buffer_size()`](Self::output_buffer_size) |
675 | | /// |
676 | | /// If the buffer is bigger than expected, we ignore the end padding bytes |
677 | | /// |
678 | | /// # Example |
679 | | /// |
680 | | /// - Read headers and then alloc a buffer big enough to hold the image |
681 | | /// |
682 | | /// ```no_run |
683 | | /// use zune_jpeg::JpegDecoder; |
684 | | /// let mut decoder = JpegDecoder::new(&[]); |
685 | | /// // before we get output, we must decode the headers to get width |
686 | | /// // height, and input colorspace |
687 | | /// decoder.decode_headers().unwrap(); |
688 | | /// |
689 | | /// let mut out = vec![0;decoder.output_buffer_size().unwrap()]; |
690 | | /// // write into out |
691 | | /// decoder.decode_into(&mut out).unwrap(); |
692 | | /// ``` |
693 | | /// |
694 | | /// |
695 | 4.70k | pub fn decode_into(&mut self, out: &mut [u8]) -> Result<(), DecodeErrors> { |
696 | 4.70k | self.decode_headers_internal()?; |
697 | | |
698 | 4.70k | let expected_size = self.output_buffer_size().unwrap(); |
699 | | |
700 | 4.70k | if out.len() < expected_size { |
701 | | // too small of a size |
702 | 0 | return Err(DecodeErrors::TooSmallOutput(expected_size, out.len())); |
703 | 4.70k | } |
704 | | |
705 | | // ensure we don't touch anyone else's scratch space |
706 | 4.70k | let out_len = core::cmp::min(out.len(), expected_size); |
707 | 4.70k | let out = &mut out[0..out_len]; |
708 | | |
709 | 4.70k | if self.is_progressive { |
710 | 3.43k | self.decode_mcu_ycbcr_progressive(out) |
711 | | } else { |
712 | 1.26k | self.decode_mcu_ycbcr_baseline(out) |
713 | | } |
714 | 4.70k | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_into Line | Count | Source | 695 | 4.70k | pub fn decode_into(&mut self, out: &mut [u8]) -> Result<(), DecodeErrors> { | 696 | 4.70k | self.decode_headers_internal()?; | 697 | | | 698 | 4.70k | let expected_size = self.output_buffer_size().unwrap(); | 699 | | | 700 | 4.70k | if out.len() < expected_size { | 701 | | // too small of a size | 702 | 0 | return Err(DecodeErrors::TooSmallOutput(expected_size, out.len())); | 703 | 4.70k | } | 704 | | | 705 | | // ensure we don't touch anyone else's scratch space | 706 | 4.70k | let out_len = core::cmp::min(out.len(), expected_size); | 707 | 4.70k | let out = &mut out[0..out_len]; | 708 | | | 709 | 4.70k | if self.is_progressive { | 710 | 3.43k | self.decode_mcu_ycbcr_progressive(out) | 711 | | } else { | 712 | 1.26k | self.decode_mcu_ycbcr_baseline(out) | 713 | | } | 714 | 4.70k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::decode_into |
715 | | |
716 | | /// Read only headers from a jpeg image buffer |
717 | | /// |
718 | | /// This allows you to extract important information like |
719 | | /// image width and height without decoding the full image |
720 | | /// |
721 | | /// # Examples |
722 | | /// ```no_run |
723 | | /// use zune_jpeg::{JpegDecoder}; |
724 | | /// |
725 | | /// let img_data = std::fs::read("a_valid.jpeg").unwrap(); |
726 | | /// let mut decoder = JpegDecoder::new(&img_data); |
727 | | /// decoder.decode_headers().unwrap(); |
728 | | /// |
729 | | /// println!("Total decoder dimensions are : {:?} pixels",decoder.dimensions()); |
730 | | /// println!("Number of components in the image are {}", decoder.info().unwrap().components); |
731 | | /// ``` |
732 | | /// # Errors |
733 | | /// See DecodeErrors enum for list of possible errors during decoding |
734 | 10.2k | pub fn decode_headers(&mut self) -> Result<(), DecodeErrors> { |
735 | 10.2k | self.decode_headers_internal()?; |
736 | 9.40k | Ok(()) |
737 | 10.2k | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::decode_headers Line | Count | Source | 734 | 10.2k | pub fn decode_headers(&mut self) -> Result<(), DecodeErrors> { | 735 | 10.2k | self.decode_headers_internal()?; | 736 | 9.40k | Ok(()) | 737 | 10.2k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::decode_headers |
738 | | /// Create a new decoder with the specified options to be used for decoding |
739 | | /// an image |
740 | | /// |
741 | | /// # Arguments |
742 | | /// - `buf`: The input buffer from where we will pull in compressed jpeg bytes from |
743 | | /// - `options`: Options specific to this decoder instance |
744 | | #[must_use] |
745 | 0 | pub fn new_with_options(buf: T, options: DecoderOptions) -> JpegDecoder<T> { |
746 | 0 | JpegDecoder::default(options, buf) |
747 | 0 | } |
748 | | |
749 | | /// Set up-sampling routines in case an image is down sampled |
750 | 395 | pub(crate) fn set_upsampling(&mut self) -> Result<(), DecodeErrors> { |
751 | | // no sampling, return early |
752 | | // check if horizontal max ==1 |
753 | 395 | if self.h_max == self.v_max && self.h_max == 1 { |
754 | 0 | return Ok(()); |
755 | 395 | } |
756 | 395 | match (self.h_max, self.v_max) { |
757 | 0 | (1, 1) => { |
758 | 0 | self.sub_sample_ratio = SampleRatios::None; |
759 | 0 | } |
760 | 68 | (1, 2) => { |
761 | 68 | self.sub_sample_ratio = SampleRatios::V; |
762 | 68 | } |
763 | 26 | (2, 1) => { |
764 | 26 | self.sub_sample_ratio = SampleRatios::H; |
765 | 26 | } |
766 | 1 | (2, 2) => { |
767 | 1 | self.sub_sample_ratio = SampleRatios::HV; |
768 | 1 | } |
769 | 300 | (hs, vs) => { |
770 | 300 | self.sub_sample_ratio = SampleRatios::Generic(hs, vs) |
771 | | // return Err(DecodeErrors::Format(format!( |
772 | | // "Unknown down-sampling method ({hs},{vs}), cannot continue") |
773 | | // )) |
774 | | } |
775 | | } |
776 | | |
777 | 1.53k | for comp in self.components.iter_mut() { |
778 | 1.53k | let hs = self.h_max / comp.horizontal_sample; |
779 | 1.53k | let vs = self.v_max / comp.vertical_sample; |
780 | | |
781 | 1.53k | let samp_factor = match (hs, vs) { |
782 | 399 | (1, 1) => { |
783 | 399 | comp.sample_ratio = SampleRatios::None; |
784 | 399 | upsample_no_op |
785 | 399 | } |
786 | | (2, 1) => { |
787 | 73 | comp.sample_ratio = SampleRatios::H; |
788 | 73 | choose_horizontal_samp_function(self.options.get_use_unsafe()) |
789 | | } |
790 | | (1, 2) => { |
791 | 196 | comp.sample_ratio = SampleRatios::V; |
792 | 196 | choose_v_samp_function(self.options.get_use_unsafe()) |
793 | | } |
794 | | (2, 2) => { |
795 | 0 | comp.sample_ratio = SampleRatios::HV; |
796 | 0 | choose_hv_samp_function(self.options.get_use_unsafe()) |
797 | | } |
798 | 863 | (hs, vs) => { |
799 | 863 | comp.sample_ratio = SampleRatios::Generic(hs, vs); |
800 | 863 | generic_sampler() |
801 | | } |
802 | | }; |
803 | 1.53k | comp.setup_upsample_scanline(); |
804 | 1.53k | comp.up_sampler = samp_factor; |
805 | | } |
806 | | |
807 | 395 | return Ok(()); |
808 | 395 | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::set_upsampling Line | Count | Source | 750 | 395 | pub(crate) fn set_upsampling(&mut self) -> Result<(), DecodeErrors> { | 751 | | // no sampling, return early | 752 | | // check if horizontal max ==1 | 753 | 395 | if self.h_max == self.v_max && self.h_max == 1 { | 754 | 0 | return Ok(()); | 755 | 395 | } | 756 | 395 | match (self.h_max, self.v_max) { | 757 | 0 | (1, 1) => { | 758 | 0 | self.sub_sample_ratio = SampleRatios::None; | 759 | 0 | } | 760 | 68 | (1, 2) => { | 761 | 68 | self.sub_sample_ratio = SampleRatios::V; | 762 | 68 | } | 763 | 26 | (2, 1) => { | 764 | 26 | self.sub_sample_ratio = SampleRatios::H; | 765 | 26 | } | 766 | 1 | (2, 2) => { | 767 | 1 | self.sub_sample_ratio = SampleRatios::HV; | 768 | 1 | } | 769 | 300 | (hs, vs) => { | 770 | 300 | self.sub_sample_ratio = SampleRatios::Generic(hs, vs) | 771 | | // return Err(DecodeErrors::Format(format!( | 772 | | // "Unknown down-sampling method ({hs},{vs}), cannot continue") | 773 | | // )) | 774 | | } | 775 | | } | 776 | | | 777 | 1.53k | for comp in self.components.iter_mut() { | 778 | 1.53k | let hs = self.h_max / comp.horizontal_sample; | 779 | 1.53k | let vs = self.v_max / comp.vertical_sample; | 780 | | | 781 | 1.53k | let samp_factor = match (hs, vs) { | 782 | 399 | (1, 1) => { | 783 | 399 | comp.sample_ratio = SampleRatios::None; | 784 | 399 | upsample_no_op | 785 | 399 | } | 786 | | (2, 1) => { | 787 | 73 | comp.sample_ratio = SampleRatios::H; | 788 | 73 | choose_horizontal_samp_function(self.options.get_use_unsafe()) | 789 | | } | 790 | | (1, 2) => { | 791 | 196 | comp.sample_ratio = SampleRatios::V; | 792 | 196 | choose_v_samp_function(self.options.get_use_unsafe()) | 793 | | } | 794 | | (2, 2) => { | 795 | 0 | comp.sample_ratio = SampleRatios::HV; | 796 | 0 | choose_hv_samp_function(self.options.get_use_unsafe()) | 797 | | } | 798 | 863 | (hs, vs) => { | 799 | 863 | comp.sample_ratio = SampleRatios::Generic(hs, vs); | 800 | 863 | generic_sampler() | 801 | | } | 802 | | }; | 803 | 1.53k | comp.setup_upsample_scanline(); | 804 | 1.53k | comp.up_sampler = samp_factor; | 805 | | } | 806 | | | 807 | 395 | return Ok(()); | 808 | 395 | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::set_upsampling |
809 | | #[must_use] |
810 | | /// Get the width of the image as a u16 |
811 | | /// |
812 | | /// The width lies between 1 and 65535 |
813 | 14.1k | pub(crate) fn width(&self) -> u16 { |
814 | 14.1k | self.info.width |
815 | 14.1k | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::width Line | Count | Source | 813 | 14.1k | pub(crate) fn width(&self) -> u16 { | 814 | 14.1k | self.info.width | 815 | 14.1k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::width |
816 | | |
817 | | /// Get the height of the image as a u16 |
818 | | /// |
819 | | /// The height lies between 1 and 65535 |
820 | | #[must_use] |
821 | 14.1k | pub(crate) fn height(&self) -> u16 { |
822 | 14.1k | self.info.height |
823 | 14.1k | } <zune_jpeg::decoder::JpegDecoder<alloc::vec::Vec<u8>>>::height Line | Count | Source | 821 | 14.1k | pub(crate) fn height(&self) -> u16 { | 822 | 14.1k | self.info.height | 823 | 14.1k | } |
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::height |
824 | | |
825 | | /// Get image dimensions as a tuple of width and height |
826 | | /// or `None` if the image hasn't been decoded. |
827 | | /// |
828 | | /// # Returns |
829 | | /// - `Some(width,height)`: Image dimensions |
830 | | /// - None : The image headers haven't been decoded |
831 | | #[must_use] |
832 | 0 | pub const fn dimensions(&self) -> Option<(usize, usize)> { |
833 | 0 | return if self.headers_decoded { |
834 | 0 | Some((self.info.width as usize, self.info.height as usize)) |
835 | | } else { |
836 | 0 | None |
837 | | }; |
838 | 0 | } |
839 | | } |
840 | | |
841 | | /// A struct representing Image Information |
842 | | #[derive(Default, Clone, Eq, PartialEq)] |
843 | | #[allow(clippy::module_name_repetitions)] |
844 | | pub struct ImageInfo { |
845 | | /// Width of the image |
846 | | pub width: u16, |
847 | | /// Height of image |
848 | | pub height: u16, |
849 | | /// PixelDensity |
850 | | pub pixel_density: u8, |
851 | | /// Start of frame markers |
852 | | pub sof: SOFMarkers, |
853 | | /// Horizontal sample |
854 | | pub x_density: u16, |
855 | | /// Vertical sample |
856 | | pub y_density: u16, |
857 | | /// Number of components |
858 | | pub components: u8, |
859 | | pub iptc_data: Option<Vec<u8>> |
860 | | } |
861 | | |
862 | | impl ImageInfo { |
863 | | /// Set width of the image |
864 | | /// |
865 | | /// Found in the start of frame |
866 | | |
867 | 4.87k | pub(crate) fn set_width(&mut self, width: u16) { |
868 | 4.87k | self.width = width; |
869 | 4.87k | } |
870 | | |
871 | | /// Set height of the image |
872 | | /// |
873 | | /// Found in the start of frame |
874 | | |
875 | 4.87k | pub(crate) fn set_height(&mut self, height: u16) { |
876 | 4.87k | self.height = height; |
877 | 4.87k | } |
878 | | |
879 | | /// Set the image density |
880 | | /// |
881 | | /// Found in the start of frame |
882 | | |
883 | 4.87k | pub(crate) fn set_density(&mut self, density: u8) { |
884 | 4.87k | self.pixel_density = density; |
885 | 4.87k | } |
886 | | |
887 | | /// Set image Start of frame marker |
888 | | /// |
889 | | /// found in the Start of frame header |
890 | | |
891 | 4.84k | pub(crate) fn set_sof_marker(&mut self, marker: SOFMarkers) { |
892 | 4.84k | self.sof = marker; |
893 | 4.84k | } |
894 | | |
895 | | /// Set image x-density(dots per pixel) |
896 | | /// |
897 | | /// Found in the APP(0) marker |
898 | | #[allow(dead_code)] |
899 | 0 | pub(crate) fn set_x(&mut self, sample: u16) { |
900 | 0 | self.x_density = sample; |
901 | 0 | } |
902 | | |
903 | | /// Set image y-density |
904 | | /// |
905 | | /// Found in the APP(0) marker |
906 | | #[allow(dead_code)] |
907 | 0 | pub(crate) fn set_y(&mut self, sample: u16) { |
908 | 0 | self.y_density = sample; |
909 | 0 | } |
910 | | } |