Coverage Report

Created: 2025-12-14 07:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zune-jpeg-0.5.6/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::{ZByteReaderTrait, ZReader};
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, choose_idct_1x1_func, choose_idct_4x4_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: ZByteReaderTrait> {
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
    /// For a scan, check if any component has vertical/horizontal sampling.
128
    pub(crate) scan_subsampled:  bool,
129
    // Function pointers, for pointy stuff.
130
    /// Dequantize and idct function
131
    // This is determined at runtime which function to run, statically it's
132
    // initialized to a platform independent one and during initialization
133
    // of this struct, we check if we can switch to a faster one which
134
    // depend on certain CPU extensions.
135
    pub(crate) idct_func: IDCTPtr,
136
    pub(crate) idct_4x4_func: IDCTPtr,
137
    pub(crate) idct_1x1_func: IDCTPtr,
138
    // Color convert function which acts on 16 YCbCr values
139
    pub(crate) color_convert_16: ColorConvert16Ptr,
140
    pub(crate) z_order:          [usize; MAX_COMPONENTS],
141
    /// restart markers
142
    pub(crate) restart_interval: usize,
143
    pub(crate) todo:             usize,
144
    // decoder options
145
    pub(crate) options:          DecoderOptions,
146
    // byte-stream
147
    pub(crate) stream:           ZReader<T>,
148
    // Indicate whether headers have been decoded
149
    pub(crate) headers_decoded:  bool,
150
    pub(crate) seen_sof:         bool,
151
152
    // exif data, lifted from app2
153
    pub(crate) icc_data: Vec<ICCChunk>,
154
    pub(crate) is_mjpeg: bool,
155
    pub(crate) coeff:    usize // Solves some weird bug :)
156
}
157
158
impl<T> JpegDecoder<T>
159
where
160
    T: ZByteReaderTrait
161
{
162
    #[allow(clippy::redundant_field_names)]
163
0
    fn default(options: DecoderOptions, buffer: T) -> Self {
164
0
        let color_convert = choose_ycbcr_to_rgb_convert_func(ColorSpace::RGB, &options).unwrap();
165
0
        JpegDecoder {
166
0
            info:              ImageInfo::default(),
167
0
            qt_tables:         [None, None, None, None],
168
0
            dc_huffman_tables: [None, None, None, None],
169
0
            ac_huffman_tables: [None, None, None, None],
170
0
            components:        vec![],
171
0
            // Interleaved information
172
0
            h_max:             1,
173
0
            v_max:             1,
174
0
            mcu_height:        0,
175
0
            mcu_width:         0,
176
0
            mcu_x:             0,
177
0
            mcu_y:             0,
178
0
            is_interleaved:    false,
179
0
            sub_sample_ratio:  SampleRatios::None,
180
0
            is_progressive:    false,
181
0
            spec_start:        0,
182
0
            spec_end:          0,
183
0
            succ_high:         0,
184
0
            succ_low:          0,
185
0
            num_scans:         0,
186
0
            scan_subsampled:   false, 
187
0
            idct_func:         choose_idct_func(&options),
188
0
            idct_4x4_func:     choose_idct_4x4_func(&options),
189
0
            idct_1x1_func:     choose_idct_1x1_func(&options),
190
0
            color_convert_16:  color_convert,
191
0
            input_colorspace:  ColorSpace::YCbCr,
192
0
            z_order:           [0; MAX_COMPONENTS],
193
0
            restart_interval:  0,
194
0
            todo:              0x7fff_ffff,
195
0
            options:           options,
196
0
            stream:            ZReader::new(buffer),
197
0
            headers_decoded:   false,
198
0
            seen_sof:          false,
199
0
            icc_data:          vec![],
200
0
            is_mjpeg:          false,
201
0
            coeff:             1
202
0
        }
203
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&alloc::vec::Vec<u8>>>>::default
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::default
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::default
204
    /// Decode a buffer already in memory
205
    ///
206
    /// The buffer should be a valid jpeg file, perhaps created by the command
207
    /// `std:::fs::read()` or a JPEG file downloaded from the internet.
208
    ///
209
    /// # Errors
210
    /// See DecodeErrors for an explanation
211
0
    pub fn decode(&mut self) -> Result<Vec<u8>, DecodeErrors> {
212
0
        self.decode_headers()?;
213
0
        let size = self.output_buffer_size().unwrap();
214
0
        let mut out = vec![0; size];
215
0
        self.decode_into(&mut out)?;
216
0
        Ok(out)
217
0
    }
218
219
    /// Create a new Decoder instance
220
    ///
221
    /// # Arguments
222
    ///  - `stream`: The raw bytes of a jpeg file.
223
    #[must_use]
224
    #[allow(clippy::new_without_default)]
225
0
    pub fn new(stream: T) -> JpegDecoder<T> {
226
0
        JpegDecoder::default(DecoderOptions::default(), stream)
227
0
    }
228
229
    /// Returns the image information
230
    ///
231
    /// This **must** be called after a subsequent call to [`decode`] or [`decode_headers`]
232
    /// it will return `None`
233
    ///
234
    /// # Returns
235
    /// - `Some(info)`: Image information,width, height, number of components
236
    /// - None: Indicates image headers haven't been decoded
237
    ///
238
    /// [`decode`]: JpegDecoder::decode
239
    /// [`decode_headers`]: JpegDecoder::decode_headers
240
    #[must_use]
241
0
    pub fn info(&self) -> Option<ImageInfo> {
242
        // we check for fails to that call by comparing what we have to the default, if
243
        // it's default we assume that the caller failed to uphold the
244
        // guarantees. We can be sure that an image cannot be the default since
245
        // its a hard panic in-case width or height are set to zero.
246
0
        if !self.headers_decoded {
247
0
            return None;
248
0
        }
249
250
0
        return Some(self.info.clone());
251
0
    }
252
253
    /// Return the number of bytes required to hold a decoded image frame
254
    /// decoded using the given input transformations
255
    ///
256
    /// # Returns
257
    ///  - `Some(usize)`: Minimum size for a buffer needed to decode the image
258
    ///  - `None`: Indicates the image was not decoded, or image dimensions would overflow a usize
259
    ///
260
    #[must_use]
261
0
    pub fn output_buffer_size(&self) -> Option<usize> {
262
0
        return if self.headers_decoded {
263
            Some(
264
0
                usize::from(self.width())
265
0
                    .checked_mul(usize::from(self.height()))?
266
0
                    .checked_mul(self.options.jpeg_get_out_colorspace().num_components())?
267
            )
268
        } else {
269
0
            None
270
        };
271
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::output_buffer_size
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::output_buffer_size
272
273
    /// Get an immutable reference to the decoder options
274
    /// for the decoder instance
275
    ///
276
    /// This can be used to modify options before actual decoding
277
    /// but after initial creation
278
    ///
279
    /// # Example
280
    /// ```no_run
281
    /// use zune_core::bytestream::ZCursor;
282
    /// use zune_jpeg::JpegDecoder;
283
    ///
284
    /// let mut decoder = JpegDecoder::new(ZCursor::new(&[]));
285
    /// // get current options
286
    /// let mut options = decoder.options();
287
    /// // modify it
288
    ///  let new_options = options.set_max_width(10);
289
    /// // set it back
290
    /// decoder.set_options(new_options);
291
    ///
292
    /// ```
293
    #[must_use]
294
0
    pub const fn options(&self) -> &DecoderOptions {
295
0
        &self.options
296
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::options
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::options
297
    /// Return the input colorspace of the image
298
    ///
299
    /// This indicates the colorspace that is present in
300
    /// the image, but this may be different to the colorspace that
301
    /// the output will be transformed to
302
    ///
303
    /// # Returns
304
    /// -`Some(Colorspace)`: Input colorspace
305
    /// - None : Indicates the headers weren't decoded
306
    #[must_use]
307
0
    pub fn input_colorspace(&self) -> Option<ColorSpace> {
308
0
        return if self.headers_decoded { Some(self.input_colorspace) } else { None };
309
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::input_colorspace
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::input_colorspace
310
    /// Set decoder options
311
    ///
312
    /// This can be used to set new options even after initialization
313
    /// but before decoding.
314
    ///
315
    /// This does not bear any significance after decoding an image
316
    ///
317
    /// # Arguments
318
    /// - `options`: New decoder options
319
    ///
320
    /// # Example
321
    /// Set maximum jpeg progressive passes to be 4
322
    ///
323
    /// ```no_run
324
    /// use zune_core::bytestream::ZCursor;
325
    /// use zune_jpeg::JpegDecoder;
326
    /// let mut decoder =JpegDecoder::new(ZCursor::new(&[]));
327
    /// // this works also because DecoderOptions implements `Copy`
328
    /// let options = decoder.options().jpeg_set_max_scans(4);
329
    /// // set the new options
330
    /// decoder.set_options(options);
331
    /// // now decode
332
    /// decoder.decode().unwrap();
333
    /// ```
334
0
    pub fn set_options(&mut self, options: DecoderOptions) {
335
0
        self.options = options;
336
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::set_options
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::set_options
337
    /// Decode Decoder headers
338
    ///
339
    /// This routine takes care of parsing supported headers from a Decoder
340
    /// image
341
    ///
342
    /// # Supported Headers
343
    ///  - APP(0)
344
    ///  - SOF(O)
345
    ///  - DQT -> Quantization tables
346
    ///  - DHT -> Huffman tables
347
    ///  - SOS -> Start of Scan
348
    /// # Unsupported Headers
349
    ///  - SOF(n) -> Decoder images which are not baseline/progressive
350
    ///  - DAC -> Images using Arithmetic tables
351
    ///  - JPG(n)
352
0
    fn decode_headers_internal(&mut self) -> Result<(), DecodeErrors> {
353
0
        if self.headers_decoded {
354
            trace!("Headers decoded!");
355
0
            return Ok(());
356
0
        }
357
358
        // match output colorspace here
359
        // we know this will only be called once per image
360
        // so makes sense
361
        // We only care for ycbcr to rgb/rgba here
362
        // in case one is using another colorspace.
363
        // May god help you
364
0
        let out_colorspace = self.options.jpeg_get_out_colorspace();
365
366
0
        if matches!(
367
0
            out_colorspace,
368
            ColorSpace::BGR | ColorSpace::BGRA | ColorSpace::RGB | ColorSpace::RGBA
369
0
        ) {
370
0
            self.color_convert_16 = choose_ycbcr_to_rgb_convert_func(
371
0
                self.options.jpeg_get_out_colorspace(),
372
0
                &self.options
373
0
            )
374
0
            .unwrap();
375
0
        }
376
        // First two bytes should be jpeg soi marker
377
0
        let magic_bytes = self.stream.get_u16_be_err()?;
378
379
0
        let mut last_byte = 0;
380
0
        let mut bytes_before_marker = 0;
381
382
0
        if magic_bytes != 0xffd8 {
383
0
            return Err(DecodeErrors::IllegalMagicBytes(magic_bytes));
384
0
        }
385
386
        loop {
387
            // read a byte
388
0
            let mut m = self.stream.read_u8_err()?;
389
390
            // AND OF COURSE some images will have fill bytes in their marker
391
            // bitstreams because why not.
392
            //
393
            // I am disappointed as a man.
394
0
            if (m == 0xFF || m == 0) && last_byte == 0xFF {
395
                // This handles the edge case where
396
                // images have markers with fill bytes(0xFF)
397
                // or byte stuffing (0)
398
                // I.e 0xFF 0xFF 0xDA
399
                // and
400
                // 0xFF 0 0xDA
401
                // It should ignore those fill bytes and take 0xDA
402
                // I don't know why such images exist
403
                // but they do.
404
                // so this is for you (with love)
405
0
                while m == 0xFF || m == 0x0 {
406
0
                    last_byte = m;
407
0
                    m = self.stream.read_u8_err()?;
408
                }
409
0
            }
410
            // Last byte should be 0xFF to confirm existence of a marker since markers look
411
            // like OxFF(some marker data)
412
0
            if last_byte == 0xFF {
413
0
                let marker = Marker::from_u8(m);
414
0
                if let Some(n) = marker {
415
0
                    if bytes_before_marker > 3 {
416
0
                        if self.options.strict_mode()
417
                        /*No reason to use this*/
418
                        {
419
0
                            return Err(DecodeErrors::FormatStatic(
420
0
                                "[strict-mode]: Extra bytes between headers"
421
0
                            ));
422
0
                        }
423
424
0
                        error!(
425
                            "Extra bytes {} before marker 0xFF{:X}",
426
                            bytes_before_marker - 3,
427
                            m
428
                        );
429
0
                    }
430
431
0
                    bytes_before_marker = 0;
432
433
0
                    self.parse_marker_inner(n)?;
434
435
                    // break after reading the start of scan.
436
                    // what follows is the image data
437
0
                    if n == Marker::SOS {
438
0
                        self.headers_decoded = true;
439
                        trace!("Input colorspace {:?}", self.input_colorspace);
440
441
                        // Check if image is RGB
442
                        // The check is weird, we need to check if ID
443
                        // represents R, G and B in ascii,
444
                        //
445
                        // I am not sure if this is even specified in any standard,
446
                        // but jpegli https://github.com/google/jpegli does encode
447
                        // its images that way, so this will check for that. and handle it appropriately
448
                        // It is spefified here so that on a successful header decode,we can at least
449
                        // try to attribute image colorspace  correctly.
450
                        //
451
                        // It was first the issue in https://github.com/etemesi254/zune-image/issues/291
452
                        // that brought it to light
453
                        //
454
0
                        let mut is_rgb = self.components.len() == 3;
455
0
                        let chars = ['R', 'G', 'B'];
456
0
                        for (comp, single_char) in self.components.iter().zip(chars.iter()) {
457
0
                            is_rgb &= comp.id == (*single_char) as u8
458
                        }
459
                        // Image is RGB, change colorspace
460
0
                        if is_rgb {
461
0
                            self.input_colorspace = ColorSpace::RGB;
462
0
                        }
463
464
0
                        return Ok(());
465
0
                    }
466
                } else {
467
0
                    bytes_before_marker = 0;
468
469
0
                    warn!("Marker 0xFF{:X} not known", m);
470
471
0
                    let length = self.stream.get_u16_be_err()?;
472
473
0
                    if length < 2 {
474
0
                        return Err(DecodeErrors::Format(format!(
475
0
                            "Found a marker with invalid length : {length}"
476
0
                        )));
477
0
                    }
478
479
0
                    warn!("Skipping {} bytes", length - 2);
480
0
                    self.stream.skip((length - 2) as usize)?;
481
                }
482
0
            }
483
0
            last_byte = m;
484
0
            bytes_before_marker += 1;
485
        }
486
        // Check if image is RGB
487
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&alloc::vec::Vec<u8>>>>::decode_headers_internal
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::decode_headers_internal
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::decode_headers_internal
488
    #[allow(clippy::too_many_lines)]
489
0
    pub(crate) fn parse_marker_inner(&mut self, m: Marker) -> Result<(), DecodeErrors> {
490
0
        match m {
491
0
            Marker::SOF(0..=2) => {
492
0
                let marker = {
493
                    // choose marker
494
0
                    if m == Marker::SOF(0) || m == Marker::SOF(1) {
495
0
                        SOFMarkers::BaselineDct
496
                    } else {
497
0
                        self.is_progressive = true;
498
0
                        SOFMarkers::ProgressiveDctHuffman
499
                    }
500
                };
501
502
                trace!("Image encoding scheme =`{:?}`", marker);
503
                // get components
504
0
                parse_start_of_frame(marker, self)?;
505
            }
506
            // Start of Frame Segments not supported
507
0
            Marker::SOF(v) => {
508
0
                let feature = UnsupportedSchemes::from_int(v);
509
510
0
                if let Some(feature) = feature {
511
0
                    return Err(DecodeErrors::Unsupported(feature));
512
0
                }
513
514
0
                return Err(DecodeErrors::Format("Unsupported image format".to_string()));
515
            }
516
            //APP(0) segment
517
            Marker::APP(0) => {
518
0
                let mut length = self.stream.get_u16_be_err()?;
519
520
0
                if length < 2 {
521
0
                    return Err(DecodeErrors::Format(format!(
522
0
                        "Found a marker with invalid length:{length}\n"
523
0
                    )));
524
0
                }
525
                // skip for now
526
0
                if length > 5 {
527
0
                    let mut buffer = [0u8; 5];
528
0
                    self.stream.read_exact_bytes(&mut buffer)?;
529
0
                    if &buffer == b"AVI1\0" {
530
0
                        self.is_mjpeg = true;
531
0
                    }
532
0
                    length -= 5;
533
0
                }
534
535
0
                self.stream.skip(length.saturating_sub(2) as usize)?;
536
537
                //parse_app(buf, m, &mut self.info)?;
538
            }
539
            Marker::APP(1) => {
540
0
                parse_app1(self)?;
541
            }
542
543
            Marker::APP(2) => {
544
0
                parse_app2(self)?;
545
            }
546
            // Quantization tables
547
            Marker::DQT => {
548
0
                parse_dqt(self)?;
549
            }
550
            // Huffman tables
551
            Marker::DHT => {
552
0
                parse_huffman(self)?;
553
            }
554
            // Start of Scan Data
555
            Marker::SOS => {
556
0
                parse_sos(self)?;
557
            }
558
0
            Marker::EOI => return Err(DecodeErrors::FormatStatic("Premature End of image")),
559
560
            Marker::DAC | Marker::DNL => {
561
0
                return Err(DecodeErrors::Format(format!(
562
0
                    "Parsing of the following header `{m:?}` is not supported,\
563
0
                                cannot continue"
564
0
                )));
565
            }
566
            Marker::DRI => {
567
0
                if self.stream.get_u16_be_err()? != 4 {
568
0
                    return Err(DecodeErrors::Format(
569
0
                        "Bad DRI length, Corrupt JPEG".to_string()
570
0
                    ));
571
0
                }
572
573
0
                self.restart_interval = usize::from(self.stream.get_u16_be_err()?);
574
                trace!("DRI marker present ({})", self.restart_interval);
575
576
0
                self.todo = self.restart_interval;
577
            }
578
            Marker::APP(14) => {
579
0
                parse_app14(self)?;
580
            }
581
            Marker::APP(13) => {
582
0
                parse_app13(self)?;
583
            }
584
            _ => {
585
0
                warn!(
586
                    "Capabilities for processing marker \"{:?}\" not implemented",
587
                    m
588
                );
589
590
0
                let length = self.stream.get_u16_be_err()?;
591
592
0
                if length < 2 {
593
0
                    return Err(DecodeErrors::Format(format!(
594
0
                        "Found a marker with invalid length:{length}\n"
595
0
                    )));
596
0
                }
597
0
                warn!("Skipping {} bytes", length - 2);
598
0
                self.stream.skip((length - 2) as usize)?;
599
            }
600
        }
601
0
        Ok(())
602
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&alloc::vec::Vec<u8>>>>::parse_marker_inner
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::parse_marker_inner
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::parse_marker_inner
603
    /// Get the embedded ICC profile if it exists
604
    /// and is correct
605
    ///
606
    /// One needs not to decode the whole image to extract this,
607
    /// calling [`decode_headers`] for an image with an ICC profile
608
    /// allows you to decode this
609
    ///
610
    /// # Returns
611
    /// - `Some(Vec<u8>)`: The raw ICC profile of the image
612
    /// - `None`: May indicate an error  in the ICC profile , non-existence of
613
    /// an ICC profile, or that the headers weren't decoded.
614
    ///
615
    /// [`decode_headers`]:Self::decode_headers
616
    #[must_use]
617
0
    pub fn icc_profile(&self) -> Option<Vec<u8>> {
618
0
        let mut marker_present: [Option<&ICCChunk>; 256] = [None; 256];
619
620
0
        if !self.headers_decoded {
621
0
            return None;
622
0
        }
623
0
        let num_markers = self.icc_data.len();
624
625
0
        if num_markers == 0 || num_markers >= 255 {
626
0
            return None;
627
0
        }
628
        // check validity
629
0
        for chunk in &self.icc_data {
630
0
            if usize::from(chunk.num_markers) != num_markers {
631
                // all the lengths must match
632
0
                return None;
633
0
            }
634
0
            if chunk.seq_no == 0 {
635
0
                warn!("Zero sequence number in ICC, corrupt ICC chunk");
636
0
                return None;
637
0
            }
638
0
            if marker_present[usize::from(chunk.seq_no)].is_some() {
639
                // duplicate seq_no
640
0
                warn!("Duplicate sequence number in ICC, corrupt chunk");
641
0
                return None;
642
0
            }
643
644
0
            marker_present[usize::from(chunk.seq_no)] = Some(chunk);
645
        }
646
0
        let mut data = Vec::with_capacity(1000);
647
        // assemble the data now
648
0
        for chunk in marker_present.get(1..=num_markers).unwrap() {
649
0
            if let Some(ch) = chunk {
650
0
                data.extend_from_slice(&ch.data);
651
0
            } else {
652
0
                warn!("Missing icc sequence number, corrupt ICC chunk ");
653
0
                return None;
654
            }
655
        }
656
657
0
        Some(data)
658
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&alloc::vec::Vec<u8>>>>::icc_profile
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::icc_profile
659
    /// Return the exif data for the file
660
    ///
661
    /// This returns the raw exif data starting at the
662
    /// TIFF header
663
    ///
664
    /// # Returns
665
    /// -`Some(data)`: The raw exif data, if present in the image
666
    /// - None: May indicate the following
667
    ///
668
    ///    1. The image doesn't have exif data
669
    ///    2. The image headers haven't been decoded
670
    #[must_use]
671
0
    pub fn exif(&self) -> Option<&Vec<u8>> {
672
0
        return self.info.exif_data.as_ref();
673
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&alloc::vec::Vec<u8>>>>::exif
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::exif
674
    /// Return the XMP data for the file
675
    ///
676
    /// This returns raw XMP data starting at the XML header
677
    /// One needs an XML/XMP decoder to extract valuable metadata
678
    ///
679
    ///
680
    /// # Returns
681
    ///  - `Some(data)`: Raw xmp data
682
    ///  - `None`: May indicate the following
683
    ///     1. The image does not have xmp data
684
    ///     2. The image headers have not been decoded
685
    ///
686
    /// # Example
687
    ///
688
    /// ```no_run
689
    /// use zune_core::bytestream::ZCursor;
690
    /// use zune_jpeg::JpegDecoder;
691
    /// let mut decoder = JpegDecoder::new(ZCursor::new(&[]));
692
    /// // decode headers to extract xmp metadata if present
693
    /// decoder.decode_headers().unwrap();
694
    /// if let Some(data) = decoder.xmp(){
695
    ///     let stringified = String::from_utf8_lossy(data);
696
    ///     println!("XMP")
697
    /// } else{
698
    ///     println!("No XMP Found")
699
    /// }
700
    ///
701
    /// ```
702
0
    pub fn xmp(&self) -> Option<&Vec<u8>> {
703
0
        return self.info.xmp_data.as_ref();
704
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&alloc::vec::Vec<u8>>>>::xmp
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::xmp
705
    /// Return the IPTC data for the file
706
    ///
707
    /// This returns the raw IPTC data.
708
    ///
709
    /// # Returns
710
    /// -`Some(data)`: The raw IPTC data, if present in the image
711
    /// - None: May indicate the following
712
    ///
713
    ///    1. The image doesn't have IPTC data
714
    ///    2. The image headers haven't been decoded
715
    #[must_use]
716
0
    pub fn iptc(&self) -> Option<&Vec<u8>> {
717
0
        return self.info.iptc_data.as_ref();
718
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&alloc::vec::Vec<u8>>>>::iptc
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::iptc
719
    /// Get the output colorspace the image pixels will be decoded into
720
    ///
721
    ///
722
    /// # Note.
723
    /// This field can only be regarded after decoding headers,
724
    /// as markers such as Adobe APP14 may dictate different colorspaces
725
    /// than requested.
726
    ///
727
    /// Calling `decode_headers` is sufficient to know what colorspace the
728
    /// output is, if this is called after `decode` it indicates the colorspace
729
    /// the output is currently in
730
    ///
731
    /// Additionally not all input->output colorspace mappings are supported
732
    /// but all input colorspaces can map to RGB colorspace, so that's a safe bet
733
    /// if one is handling image formats
734
    ///
735
    ///# Returns
736
    /// - `Some(Colorspace)`: If headers have been decoded, the colorspace the
737
    ///output array will be in
738
    ///- `None
739
    #[must_use]
740
0
    pub fn output_colorspace(&self) -> Option<ColorSpace> {
741
0
        return if self.headers_decoded {
742
0
            Some(self.options.jpeg_get_out_colorspace())
743
        } else {
744
0
            None
745
        };
746
0
    }
747
748
    /// Decode into a pre-allocated buffer
749
    ///
750
    /// It is an error if the buffer size is smaller than
751
    /// [`output_buffer_size()`](Self::output_buffer_size)
752
    ///
753
    /// If the buffer is bigger than expected, we ignore the end padding bytes
754
    ///
755
    /// # Example
756
    ///
757
    /// - Read  headers and then alloc a buffer big enough to hold the image
758
    ///
759
    /// ```no_run
760
    /// use zune_core::bytestream::ZCursor;
761
    /// use zune_jpeg::JpegDecoder;
762
    /// let mut decoder = JpegDecoder::new(ZCursor::new(&[]));
763
    /// // before we get output, we must decode the headers to get width
764
    /// // height, and input colorspace
765
    /// decoder.decode_headers().unwrap();
766
    ///
767
    /// let mut out = vec![0;decoder.output_buffer_size().unwrap()];
768
    /// // write into out
769
    /// decoder.decode_into(&mut out).unwrap();
770
    /// ```
771
    ///
772
    ///
773
0
    pub fn decode_into(&mut self, out: &mut [u8]) -> Result<(), DecodeErrors> {
774
0
        self.decode_headers_internal()?;
775
776
0
        let expected_size = self.output_buffer_size().unwrap();
777
778
0
        if out.len() < expected_size {
779
            // too small of a size
780
0
            return Err(DecodeErrors::TooSmallOutput(expected_size, out.len()));
781
0
        }
782
783
        // ensure we don't touch anyone else's scratch space
784
0
        let out_len = core::cmp::min(out.len(), expected_size);
785
0
        let out = &mut out[0..out_len];
786
787
0
        if self.is_progressive {
788
0
            self.decode_mcu_ycbcr_progressive(out)
789
        } else {
790
0
            self.decode_mcu_ycbcr_baseline(out)
791
        }
792
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::decode_into
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::decode_into
793
794
    /// Read only headers from a jpeg image buffer
795
    ///
796
    /// This allows you to extract important information like
797
    /// image width and height without decoding the full image
798
    ///
799
    /// # Examples
800
    /// ```no_run
801
    /// use zune_core::bytestream::ZCursor;
802
    /// use zune_jpeg::{JpegDecoder};
803
    ///
804
    /// let img_data = std::fs::read("a_valid.jpeg").unwrap();
805
    /// let mut decoder = JpegDecoder::new(ZCursor::new(&img_data));
806
    /// decoder.decode_headers().unwrap();
807
    ///
808
    /// println!("Total decoder dimensions are : {:?} pixels",decoder.dimensions());
809
    /// println!("Number of components in the image are {}", decoder.info().unwrap().components);
810
    /// ```
811
    /// # Errors
812
    /// See DecodeErrors enum for list of possible errors during decoding
813
0
    pub fn decode_headers(&mut self) -> Result<(), DecodeErrors> {
814
0
        self.decode_headers_internal()?;
815
0
        Ok(())
816
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&alloc::vec::Vec<u8>>>>::decode_headers
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::decode_headers
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::decode_headers
817
    /// Create a new decoder with the specified options to be used for decoding
818
    /// an image
819
    ///
820
    /// # Arguments
821
    /// - `buf`: The input buffer from where we will pull in compressed jpeg bytes from
822
    /// - `options`: Options specific to this decoder instance
823
    #[must_use]
824
0
    pub fn new_with_options(buf: T, options: DecoderOptions) -> JpegDecoder<T> {
825
0
        JpegDecoder::default(options, buf)
826
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&alloc::vec::Vec<u8>>>>::new_with_options
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::new_with_options
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::new_with_options
827
828
    /// Set up-sampling routines in case an image is down sampled
829
0
    pub(crate) fn set_upsampling(&mut self) -> Result<(), DecodeErrors> {
830
        // no sampling, return early
831
        // check if horizontal max ==1
832
0
        if self.h_max == self.v_max && self.h_max == 1 {
833
0
            return Ok(());
834
0
        }
835
0
        match (self.h_max, self.v_max) {
836
0
            (1, 1) => {
837
0
                self.sub_sample_ratio = SampleRatios::None;
838
0
            }
839
0
            (1, 2) => {
840
0
                self.sub_sample_ratio = SampleRatios::V;
841
0
            }
842
0
            (2, 1) => {
843
0
                self.sub_sample_ratio = SampleRatios::H;
844
0
            }
845
0
            (2, 2) => {
846
0
                self.sub_sample_ratio = SampleRatios::HV;
847
0
            }
848
0
            (hs, vs) => {
849
0
                self.sub_sample_ratio = SampleRatios::Generic(hs, vs)
850
                // return Err(DecodeErrors::Format(format!(
851
                //     "Unknown down-sampling method ({hs},{vs}), cannot continue")
852
                // ))
853
            }
854
        }
855
856
0
        for comp in &mut self.components {
857
0
            let hs = self.h_max / comp.horizontal_sample;
858
0
            let vs = self.v_max / comp.vertical_sample;
859
860
0
            let samp_factor = match (hs, vs) {
861
0
                (1, 1) => {
862
0
                    comp.sample_ratio = SampleRatios::None;
863
0
                    upsample_no_op
864
0
                }
865
                (2, 1) => {
866
0
                    comp.sample_ratio = SampleRatios::H;
867
0
                    choose_horizontal_samp_function(self.options.use_unsafe())
868
                }
869
                (1, 2) => {
870
0
                    comp.sample_ratio = SampleRatios::V;
871
0
                    choose_v_samp_function(self.options.use_unsafe())
872
                }
873
                (2, 2) => {
874
0
                    comp.sample_ratio = SampleRatios::HV;
875
0
                    choose_hv_samp_function(self.options.use_unsafe())
876
                }
877
0
                (hs, vs) => {
878
0
                    comp.sample_ratio = SampleRatios::Generic(hs, vs);
879
0
                    generic_sampler()
880
                }
881
            };
882
0
            comp.setup_upsample_scanline();
883
0
            comp.up_sampler = samp_factor;
884
        }
885
886
0
        return Ok(());
887
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::set_upsampling
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::set_upsampling
888
    #[must_use]
889
    /// Get the width of the image as a u16
890
    ///
891
    /// The width lies between 1 and 65535
892
0
    pub(crate) fn width(&self) -> u16 {
893
0
        self.info.width
894
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::width
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::width
895
896
    /// Get the height of the image as a u16
897
    ///
898
    /// The height lies between 1 and 65535
899
    #[must_use]
900
0
    pub(crate) fn height(&self) -> u16 {
901
0
        self.info.height
902
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::height
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::height
903
904
    /// Get image dimensions as a tuple of width and height
905
    /// or `None` if the image hasn't been decoded.
906
    ///
907
    /// # Returns
908
    /// - `Some(width,height)`: Image dimensions
909
    /// -  None : The image headers haven't been decoded
910
    #[must_use]
911
0
    pub const fn dimensions(&self) -> Option<(usize, usize)> {
912
0
        return if self.headers_decoded {
913
0
            Some((self.info.width as usize, self.info.height as usize))
914
        } else {
915
0
            None
916
        };
917
0
    }
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<zune_core::bytestream::reader::no_std_readers::ZCursor<&[u8]>>>::dimensions
Unexecuted instantiation: <zune_jpeg::decoder::JpegDecoder<_>>::dimensions
918
}
919
920
#[derive(Default, Clone, Eq, PartialEq, Debug)]
921
pub struct GainMapInfo {
922
    pub data: Vec<u8>
923
}
924
/// A struct representing Image Information
925
#[derive(Default, Clone, Eq, PartialEq)]
926
#[allow(clippy::module_name_repetitions)]
927
pub struct ImageInfo {
928
    /// Width of the image
929
    pub width: u16,
930
    /// Height of image
931
    pub height: u16,
932
    /// PixelDensity
933
    pub pixel_density: u8,
934
    /// Start of frame markers
935
    pub sof: SOFMarkers,
936
    /// Horizontal sample
937
    pub x_density: u16,
938
    /// Vertical sample
939
    pub y_density: u16,
940
    /// Number of components
941
    pub components: u8,
942
    /// Gain Map information, useful for
943
    /// UHDR images
944
    pub gain_map_info: Vec<GainMapInfo>,
945
    /// Multi picture information, useful for
946
    /// UHDR images
947
    pub multi_picture_information: Option<Vec<u8>>,
948
    /// Exif Data
949
    pub exif_data: Option<Vec<u8>>,
950
    /// XMP Data
951
    pub xmp_data: Option<Vec<u8>>,
952
    /// IPTC Data
953
    pub iptc_data: Option<Vec<u8>>
954
}
955
956
impl ImageInfo {
957
    /// Set width of the image
958
    ///
959
    /// Found in the start of frame
960
961
0
    pub(crate) fn set_width(&mut self, width: u16) {
962
0
        self.width = width;
963
0
    }
964
965
    /// Set height of the image
966
    ///
967
    /// Found in the start of frame
968
969
0
    pub(crate) fn set_height(&mut self, height: u16) {
970
0
        self.height = height;
971
0
    }
972
973
    /// Set the image density
974
    ///
975
    /// Found in the start of frame
976
977
0
    pub(crate) fn set_density(&mut self, density: u8) {
978
0
        self.pixel_density = density;
979
0
    }
980
981
    /// Set image Start of frame marker
982
    ///
983
    /// found in the Start of frame header
984
985
0
    pub(crate) fn set_sof_marker(&mut self, marker: SOFMarkers) {
986
0
        self.sof = marker;
987
0
    }
988
989
    /// Set image x-density(dots per pixel)
990
    ///
991
    /// Found in the APP(0) marker
992
    #[allow(dead_code)]
993
0
    pub(crate) fn set_x(&mut self, sample: u16) {
994
0
        self.x_density = sample;
995
0
    }
996
997
    /// Set image y-density
998
    ///
999
    /// Found in the APP(0) marker
1000
    #[allow(dead_code)]
1001
0
    pub(crate) fn set_y(&mut self, sample: u16) {
1002
0
        self.y_density = sample;
1003
0
    }
1004
}