Coverage Report

Created: 2025-11-24 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}