Coverage Report

Created: 2025-11-09 06:56

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/misc.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
//!Miscellaneous stuff
10
#![allow(dead_code)]
11
12
use alloc::format;
13
use core::cmp::max;
14
use core::fmt;
15
16
use zune_core::bytestream::{ZByteReader, ZReaderTrait};
17
use zune_core::colorspace::ColorSpace;
18
use zune_core::log::{trace, warn};
19
20
use crate::components::{ComponentID, SampleRatios};
21
use crate::errors::DecodeErrors;
22
use crate::huffman::HuffmanTable;
23
use crate::JpegDecoder;
24
25
/// Start of baseline DCT Huffman coding
26
27
pub const START_OF_FRAME_BASE: u16 = 0xffc0;
28
29
/// Start of another frame
30
31
pub const START_OF_FRAME_EXT_SEQ: u16 = 0xffc1;
32
33
/// Start of progressive DCT encoding
34
35
pub const START_OF_FRAME_PROG_DCT: u16 = 0xffc2;
36
37
/// Start of Lossless sequential Huffman coding
38
39
pub const START_OF_FRAME_LOS_SEQ: u16 = 0xffc3;
40
41
/// Start of extended sequential DCT arithmetic coding
42
43
pub const START_OF_FRAME_EXT_AR: u16 = 0xffc9;
44
45
/// Start of Progressive DCT arithmetic coding
46
47
pub const START_OF_FRAME_PROG_DCT_AR: u16 = 0xffca;
48
49
/// Start of Lossless sequential Arithmetic coding
50
51
pub const START_OF_FRAME_LOS_SEQ_AR: u16 = 0xffcb;
52
53
/// Undo run length encoding of coefficients by placing them in natural order
54
#[rustfmt::skip]
55
pub const UN_ZIGZAG: [usize; 64 + 16] = [
56
     0,  1,  8, 16,  9,  2,  3, 10,
57
    17, 24, 32, 25, 18, 11,  4,  5,
58
    12, 19, 26, 33, 40, 48, 41, 34,
59
    27, 20, 13,  6,  7, 14, 21, 28,
60
    35, 42, 49, 56, 57, 50, 43, 36,
61
    29, 22, 15, 23, 30, 37, 44, 51,
62
    58, 59, 52, 45, 38, 31, 39, 46,
63
    53, 60, 61, 54, 47, 55, 62, 63,
64
    // Prevent overflowing
65
    63, 63, 63, 63, 63, 63, 63, 63,
66
    63, 63, 63, 63, 63, 63, 63, 63
67
];
68
69
/// Align data to a 16 byte boundary
70
#[repr(align(16))]
71
#[derive(Clone)]
72
73
pub struct Aligned16<T: ?Sized>(pub T);
74
75
impl<T> Default for Aligned16<T>
76
where
77
    T: Default
78
{
79
0
    fn default() -> Self {
80
0
        Aligned16(T::default())
81
0
    }
82
}
83
84
/// Align data to a 32 byte boundary
85
#[repr(align(32))]
86
#[derive(Clone)]
87
pub struct Aligned32<T: ?Sized>(pub T);
88
89
impl<T> Default for Aligned32<T>
90
where
91
    T: Default
92
{
93
0
    fn default() -> Self {
94
0
        Aligned32(T::default())
95
0
    }
96
}
97
98
/// Markers that identify different Start of Image markers
99
/// They identify the type of encoding and whether the file use lossy(DCT) or
100
/// lossless compression and whether we use Huffman or arithmetic coding schemes
101
#[derive(Eq, PartialEq, Copy, Clone)]
102
#[allow(clippy::upper_case_acronyms)]
103
pub enum SOFMarkers {
104
    /// Baseline DCT markers
105
    BaselineDct,
106
    /// SOF_1 Extended sequential DCT,Huffman coding
107
    ExtendedSequentialHuffman,
108
    /// Progressive DCT, Huffman coding
109
    ProgressiveDctHuffman,
110
    /// Lossless (sequential), huffman coding,
111
    LosslessHuffman,
112
    /// Extended sequential DEC, arithmetic coding
113
    ExtendedSequentialDctArithmetic,
114
    /// Progressive DCT, arithmetic coding,
115
    ProgressiveDctArithmetic,
116
    /// Lossless ( sequential), arithmetic coding
117
    LosslessArithmetic
118
}
119
120
impl Default for SOFMarkers {
121
5.34k
    fn default() -> Self {
122
5.34k
        Self::BaselineDct
123
5.34k
    }
124
}
125
126
impl SOFMarkers {
127
    /// Check if a certain marker is sequential DCT or not
128
129
0
    pub fn is_sequential_dct(self) -> bool {
130
0
        matches!(
131
0
            self,
132
            Self::BaselineDct
133
                | Self::ExtendedSequentialHuffman
134
                | Self::ExtendedSequentialDctArithmetic
135
        )
136
0
    }
137
138
    /// Check if a marker is a Lossles type or not
139
140
0
    pub fn is_lossless(self) -> bool {
141
0
        matches!(self, Self::LosslessHuffman | Self::LosslessArithmetic)
142
0
    }
143
144
    /// Check whether a marker is a progressive marker or not
145
146
0
    pub fn is_progressive(self) -> bool {
147
0
        matches!(
148
0
            self,
149
            Self::ProgressiveDctHuffman | Self::ProgressiveDctArithmetic
150
        )
151
0
    }
152
153
    /// Create a marker from an integer
154
155
0
    pub fn from_int(int: u16) -> Option<SOFMarkers> {
156
0
        match int {
157
0
            START_OF_FRAME_BASE => Some(Self::BaselineDct),
158
0
            START_OF_FRAME_PROG_DCT => Some(Self::ProgressiveDctHuffman),
159
0
            START_OF_FRAME_PROG_DCT_AR => Some(Self::ProgressiveDctArithmetic),
160
0
            START_OF_FRAME_LOS_SEQ => Some(Self::LosslessHuffman),
161
0
            START_OF_FRAME_LOS_SEQ_AR => Some(Self::LosslessArithmetic),
162
0
            START_OF_FRAME_EXT_SEQ => Some(Self::ExtendedSequentialHuffman),
163
0
            START_OF_FRAME_EXT_AR => Some(Self::ExtendedSequentialDctArithmetic),
164
0
            _ => None
165
        }
166
0
    }
167
}
168
169
impl fmt::Debug for SOFMarkers {
170
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171
0
        match &self {
172
0
            Self::BaselineDct => write!(f, "Baseline DCT"),
173
            Self::ExtendedSequentialHuffman => {
174
0
                write!(f, "Extended sequential DCT, Huffman Coding")
175
            }
176
0
            Self::ProgressiveDctHuffman => write!(f, "Progressive DCT,Huffman Encoding"),
177
0
            Self::LosslessHuffman => write!(f, "Lossless (sequential) Huffman encoding"),
178
            Self::ExtendedSequentialDctArithmetic => {
179
0
                write!(f, "Extended sequential DCT, arithmetic coding")
180
            }
181
0
            Self::ProgressiveDctArithmetic => write!(f, "Progressive DCT, arithmetic coding"),
182
0
            Self::LosslessArithmetic => write!(f, "Lossless (sequential) arithmetic coding")
183
        }
184
0
    }
185
}
186
187
/// Read `buf.len()*2` data from the underlying `u8` buffer and convert it into
188
/// u16, and store it into `buf`
189
///
190
/// # Arguments
191
/// - reader: A mutable reference to the underlying reader.
192
/// - buf: A mutable reference to a slice containing u16's
193
#[inline]
194
0
pub fn read_u16_into<T>(reader: &mut ZByteReader<T>, buf: &mut [u16]) -> Result<(), DecodeErrors>
195
0
where
196
0
    T: ZReaderTrait
197
{
198
0
    for i in buf {
199
0
        *i = reader.get_u16_be_err()?;
200
    }
201
202
0
    Ok(())
203
0
}
204
205
/// Set up component parameters.
206
///
207
/// This modifies the components in place setting up details needed by other
208
/// parts fo the decoder.
209
4.56k
pub(crate) fn setup_component_params<T: ZReaderTrait>(
210
4.56k
    img: &mut JpegDecoder<T>
211
4.56k
) -> Result<(), DecodeErrors> {
212
4.56k
    let img_width = img.width();
213
4.56k
    let img_height = img.height();
214
215
    // in case of adobe app14 being present, zero may indicate
216
    // either CMYK if components are 4 or RGB if components are 3,
217
    // see https://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html
218
    // so since we may not know how many number of components
219
    // we have when decoding app14, we have to defer that check
220
    // until now.
221
    //
222
    // We know adobe app14 was present since it's the only one that can modify
223
    // input colorspace to be CMYK
224
4.56k
    if img.components.len() == 3 && img.input_colorspace == ColorSpace::CMYK {
225
0
        img.input_colorspace = ColorSpace::RGB;
226
4.56k
    }
227
228
11.0k
    for component in &mut img.components {
229
        // compute interleaved image info
230
        // h_max contains the maximum horizontal component
231
6.47k
        img.h_max = max(img.h_max, component.horizontal_sample);
232
        // v_max contains the maximum vertical component
233
6.47k
        img.v_max = max(img.v_max, component.vertical_sample);
234
6.47k
        img.mcu_width = img.h_max * 8;
235
6.47k
        img.mcu_height = img.v_max * 8;
236
        // Number of MCU's per width
237
6.47k
        img.mcu_x = (usize::from(img.info.width) + img.mcu_width - 1) / img.mcu_width;
238
        // Number of MCU's per height
239
6.47k
        img.mcu_y = (usize::from(img.info.height) + img.mcu_height - 1) / img.mcu_height;
240
241
6.47k
        if img.h_max != 1 || img.v_max != 1 {
242
429
            // interleaved images have horizontal and vertical sampling factors
243
429
            // not equal to 1.
244
429
            img.is_interleaved = true;
245
6.05k
        }
246
        // Extract quantization tables from the arrays into components
247
6.47k
        let qt_table = *img.qt_tables[component.quantization_table_number as usize]
248
6.47k
            .as_ref()
249
6.47k
            .ok_or_else(|| {
250
9
                DecodeErrors::DqtError(format!(
251
9
                    "No quantization table for component {:?}",
252
9
                    component.component_id
253
9
                ))
254
9
            })?;
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#0}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<_>::{closure#0}
zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#0}
Line
Count
Source
249
5
            .ok_or_else(|| {
250
5
                DecodeErrors::DqtError(format!(
251
5
                    "No quantization table for component {:?}",
252
5
                    component.component_id
253
5
                ))
254
5
            })?;
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#0}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#0}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#0}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#0}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#0}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#0}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#0}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#0}
zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#0}
Line
Count
Source
249
4
            .ok_or_else(|| {
250
4
                DecodeErrors::DqtError(format!(
251
4
                    "No quantization table for component {:?}",
252
4
                    component.component_id
253
4
                ))
254
4
            })?;
255
256
6.47k
        let x = (usize::from(img_width) * component.horizontal_sample + img.h_max - 1) / img.h_max;
257
6.47k
        let y = (usize::from(img_height) * component.horizontal_sample + img.h_max - 1) / img.v_max;
258
6.47k
        component.x = x;
259
6.47k
        component.w2 = img.mcu_x * component.horizontal_sample * 8;
260
        // probably not needed. :)
261
6.47k
        component.y = y;
262
6.47k
        component.quantization_table = qt_table;
263
        // initially stride contains its horizontal sub-sampling
264
6.47k
        component.width_stride *= img.mcu_x * 8;
265
    }
266
    {
267
        // Sampling factors are one thing that suck
268
        // this fixes a specific problem with images like
269
        //
270
        // (2 2) None
271
        // (2 1) H
272
        // (2 1) H
273
        //
274
        // The images exist in the wild, the images are not meant to exist
275
        // but they do, it's just an annoying horizontal sub-sampling that
276
        // I don't know why it exists.
277
        // But it does
278
        // So we try to cope with that.
279
        // I am not sure of how to explain how to fix it, but it involved a debugger
280
        // and to much coke(the legal one)
281
        //
282
        // If this wasn't present, self.upsample_dest would have the wrong length
283
4.55k
        let mut handle_that_annoying_bug = false;
284
285
4.55k
        if let Some(y_component) = img
286
4.55k
            .components
287
4.55k
            .iter()
288
4.55k
            .find(|c| c.component_id == ComponentID::Y)
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#1}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<_>::{closure#1}
zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#1}
Line
Count
Source
288
2.54k
            .find(|c| c.component_id == ComponentID::Y)
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#1}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#1}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#1}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#1}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#1}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#1}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#1}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#1}
zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>::{closure#1}
Line
Count
Source
288
2.01k
            .find(|c| c.component_id == ComponentID::Y)
289
        {
290
4.55k
            if y_component.horizontal_sample == 2 || y_component.vertical_sample == 2 {
291
9
                handle_that_annoying_bug = true;
292
4.54k
            }
293
0
        }
294
4.55k
        if handle_that_annoying_bug {
295
24
            for comp in &mut img.components {
296
15
                if (comp.component_id != ComponentID::Y)
297
6
                    && (comp.horizontal_sample != 1 || comp.vertical_sample != 1)
298
2
                {
299
2
                    comp.fix_an_annoying_bug = 2;
300
13
                }
301
            }
302
4.54k
        }
303
    }
304
305
4.55k
    if img.is_mjpeg {
306
794
        fill_default_mjpeg_tables(
307
794
            img.is_progressive,
308
794
            &mut img.dc_huffman_tables,
309
794
            &mut img.ac_huffman_tables
310
794
        );
311
3.76k
    }
312
313
    // check colorspace matches
314
4.55k
    if img.input_colorspace.num_components() > img.components.len() {
315
0
        if img.input_colorspace == ColorSpace::YCCK {
316
            // Some images may have YCCK format (from adobe app14 segment) which is supposed to be 4 components
317
            // but only 3 components, see issue https://github.com/etemesi254/zune-image/issues/275
318
            // So this is the behaviour of other decoders
319
            // - stb_image: Treats it as YCbCr image
320
            // - libjpeg_turbo: Does not know how to parse YCCK images (transform 2 app14) so treats
321
            // it as YCbCr
322
            // So I will match that to match existing ones
323
0
            warn!("Treating YCCK colorspace as YCbCr as component length does not match");
324
0
            img.input_colorspace = ColorSpace::YCbCr
325
        } else {
326
0
            let msg = format!(
327
0
                " Expected {} number of components but found {}",
328
0
                img.input_colorspace.num_components(),
329
0
                img.components.len()
330
            );
331
332
0
            return Err(DecodeErrors::Format(msg));
333
        }
334
4.55k
    }
335
4.55k
    Ok(())
336
4.56k
}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<_>
zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>
Line
Count
Source
209
2.54k
pub(crate) fn setup_component_params<T: ZReaderTrait>(
210
2.54k
    img: &mut JpegDecoder<T>
211
2.54k
) -> Result<(), DecodeErrors> {
212
2.54k
    let img_width = img.width();
213
2.54k
    let img_height = img.height();
214
215
    // in case of adobe app14 being present, zero may indicate
216
    // either CMYK if components are 4 or RGB if components are 3,
217
    // see https://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html
218
    // so since we may not know how many number of components
219
    // we have when decoding app14, we have to defer that check
220
    // until now.
221
    //
222
    // We know adobe app14 was present since it's the only one that can modify
223
    // input colorspace to be CMYK
224
2.54k
    if img.components.len() == 3 && img.input_colorspace == ColorSpace::CMYK {
225
0
        img.input_colorspace = ColorSpace::RGB;
226
2.54k
    }
227
228
6.48k
    for component in &mut img.components {
229
        // compute interleaved image info
230
        // h_max contains the maximum horizontal component
231
3.94k
        img.h_max = max(img.h_max, component.horizontal_sample);
232
        // v_max contains the maximum vertical component
233
3.94k
        img.v_max = max(img.v_max, component.vertical_sample);
234
3.94k
        img.mcu_width = img.h_max * 8;
235
3.94k
        img.mcu_height = img.v_max * 8;
236
        // Number of MCU's per width
237
3.94k
        img.mcu_x = (usize::from(img.info.width) + img.mcu_width - 1) / img.mcu_width;
238
        // Number of MCU's per height
239
3.94k
        img.mcu_y = (usize::from(img.info.height) + img.mcu_height - 1) / img.mcu_height;
240
241
3.94k
        if img.h_max != 1 || img.v_max != 1 {
242
410
            // interleaved images have horizontal and vertical sampling factors
243
410
            // not equal to 1.
244
410
            img.is_interleaved = true;
245
3.53k
        }
246
        // Extract quantization tables from the arrays into components
247
3.94k
        let qt_table = *img.qt_tables[component.quantization_table_number as usize]
248
3.94k
            .as_ref()
249
3.94k
            .ok_or_else(|| {
250
                DecodeErrors::DqtError(format!(
251
                    "No quantization table for component {:?}",
252
                    component.component_id
253
                ))
254
5
            })?;
255
256
3.93k
        let x = (usize::from(img_width) * component.horizontal_sample + img.h_max - 1) / img.h_max;
257
3.93k
        let y = (usize::from(img_height) * component.horizontal_sample + img.h_max - 1) / img.v_max;
258
3.93k
        component.x = x;
259
3.93k
        component.w2 = img.mcu_x * component.horizontal_sample * 8;
260
        // probably not needed. :)
261
3.93k
        component.y = y;
262
3.93k
        component.quantization_table = qt_table;
263
        // initially stride contains its horizontal sub-sampling
264
3.93k
        component.width_stride *= img.mcu_x * 8;
265
    }
266
    {
267
        // Sampling factors are one thing that suck
268
        // this fixes a specific problem with images like
269
        //
270
        // (2 2) None
271
        // (2 1) H
272
        // (2 1) H
273
        //
274
        // The images exist in the wild, the images are not meant to exist
275
        // but they do, it's just an annoying horizontal sub-sampling that
276
        // I don't know why it exists.
277
        // But it does
278
        // So we try to cope with that.
279
        // I am not sure of how to explain how to fix it, but it involved a debugger
280
        // and to much coke(the legal one)
281
        //
282
        // If this wasn't present, self.upsample_dest would have the wrong length
283
2.54k
        let mut handle_that_annoying_bug = false;
284
285
2.54k
        if let Some(y_component) = img
286
2.54k
            .components
287
2.54k
            .iter()
288
2.54k
            .find(|c| c.component_id == ComponentID::Y)
289
        {
290
2.54k
            if y_component.horizontal_sample == 2 || y_component.vertical_sample == 2 {
291
3
                handle_that_annoying_bug = true;
292
2.54k
            }
293
0
        }
294
2.54k
        if handle_that_annoying_bug {
295
12
            for comp in &mut img.components {
296
9
                if (comp.component_id != ComponentID::Y)
297
6
                    && (comp.horizontal_sample != 1 || comp.vertical_sample != 1)
298
2
                {
299
2
                    comp.fix_an_annoying_bug = 2;
300
7
                }
301
            }
302
2.54k
        }
303
    }
304
305
2.54k
    if img.is_mjpeg {
306
793
        fill_default_mjpeg_tables(
307
793
            img.is_progressive,
308
793
            &mut img.dc_huffman_tables,
309
793
            &mut img.ac_huffman_tables
310
793
        );
311
1.75k
    }
312
313
    // check colorspace matches
314
2.54k
    if img.input_colorspace.num_components() > img.components.len() {
315
0
        if img.input_colorspace == ColorSpace::YCCK {
316
            // Some images may have YCCK format (from adobe app14 segment) which is supposed to be 4 components
317
            // but only 3 components, see issue https://github.com/etemesi254/zune-image/issues/275
318
            // So this is the behaviour of other decoders
319
            // - stb_image: Treats it as YCbCr image
320
            // - libjpeg_turbo: Does not know how to parse YCCK images (transform 2 app14) so treats
321
            // it as YCbCr
322
            // So I will match that to match existing ones
323
0
            warn!("Treating YCCK colorspace as YCbCr as component length does not match");
324
0
            img.input_colorspace = ColorSpace::YCbCr
325
        } else {
326
0
            let msg = format!(
327
0
                " Expected {} number of components but found {}",
328
0
                img.input_colorspace.num_components(),
329
0
                img.components.len()
330
            );
331
332
0
            return Err(DecodeErrors::Format(msg));
333
        }
334
2.54k
    }
335
2.54k
    Ok(())
336
2.54k
}
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>
Unexecuted instantiation: zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>
zune_jpeg::misc::setup_component_params::<alloc::vec::Vec<u8>>
Line
Count
Source
209
2.01k
pub(crate) fn setup_component_params<T: ZReaderTrait>(
210
2.01k
    img: &mut JpegDecoder<T>
211
2.01k
) -> Result<(), DecodeErrors> {
212
2.01k
    let img_width = img.width();
213
2.01k
    let img_height = img.height();
214
215
    // in case of adobe app14 being present, zero may indicate
216
    // either CMYK if components are 4 or RGB if components are 3,
217
    // see https://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html
218
    // so since we may not know how many number of components
219
    // we have when decoding app14, we have to defer that check
220
    // until now.
221
    //
222
    // We know adobe app14 was present since it's the only one that can modify
223
    // input colorspace to be CMYK
224
2.01k
    if img.components.len() == 3 && img.input_colorspace == ColorSpace::CMYK {
225
0
        img.input_colorspace = ColorSpace::RGB;
226
2.01k
    }
227
228
4.55k
    for component in &mut img.components {
229
        // compute interleaved image info
230
        // h_max contains the maximum horizontal component
231
2.53k
        img.h_max = max(img.h_max, component.horizontal_sample);
232
        // v_max contains the maximum vertical component
233
2.53k
        img.v_max = max(img.v_max, component.vertical_sample);
234
2.53k
        img.mcu_width = img.h_max * 8;
235
2.53k
        img.mcu_height = img.v_max * 8;
236
        // Number of MCU's per width
237
2.53k
        img.mcu_x = (usize::from(img.info.width) + img.mcu_width - 1) / img.mcu_width;
238
        // Number of MCU's per height
239
2.53k
        img.mcu_y = (usize::from(img.info.height) + img.mcu_height - 1) / img.mcu_height;
240
241
2.53k
        if img.h_max != 1 || img.v_max != 1 {
242
19
            // interleaved images have horizontal and vertical sampling factors
243
19
            // not equal to 1.
244
19
            img.is_interleaved = true;
245
2.51k
        }
246
        // Extract quantization tables from the arrays into components
247
2.53k
        let qt_table = *img.qt_tables[component.quantization_table_number as usize]
248
2.53k
            .as_ref()
249
2.53k
            .ok_or_else(|| {
250
                DecodeErrors::DqtError(format!(
251
                    "No quantization table for component {:?}",
252
                    component.component_id
253
                ))
254
4
            })?;
255
256
2.53k
        let x = (usize::from(img_width) * component.horizontal_sample + img.h_max - 1) / img.h_max;
257
2.53k
        let y = (usize::from(img_height) * component.horizontal_sample + img.h_max - 1) / img.v_max;
258
2.53k
        component.x = x;
259
2.53k
        component.w2 = img.mcu_x * component.horizontal_sample * 8;
260
        // probably not needed. :)
261
2.53k
        component.y = y;
262
2.53k
        component.quantization_table = qt_table;
263
        // initially stride contains its horizontal sub-sampling
264
2.53k
        component.width_stride *= img.mcu_x * 8;
265
    }
266
    {
267
        // Sampling factors are one thing that suck
268
        // this fixes a specific problem with images like
269
        //
270
        // (2 2) None
271
        // (2 1) H
272
        // (2 1) H
273
        //
274
        // The images exist in the wild, the images are not meant to exist
275
        // but they do, it's just an annoying horizontal sub-sampling that
276
        // I don't know why it exists.
277
        // But it does
278
        // So we try to cope with that.
279
        // I am not sure of how to explain how to fix it, but it involved a debugger
280
        // and to much coke(the legal one)
281
        //
282
        // If this wasn't present, self.upsample_dest would have the wrong length
283
2.01k
        let mut handle_that_annoying_bug = false;
284
285
2.01k
        if let Some(y_component) = img
286
2.01k
            .components
287
2.01k
            .iter()
288
2.01k
            .find(|c| c.component_id == ComponentID::Y)
289
        {
290
2.01k
            if y_component.horizontal_sample == 2 || y_component.vertical_sample == 2 {
291
6
                handle_that_annoying_bug = true;
292
2.00k
            }
293
0
        }
294
2.01k
        if handle_that_annoying_bug {
295
12
            for comp in &mut img.components {
296
6
                if (comp.component_id != ComponentID::Y)
297
0
                    && (comp.horizontal_sample != 1 || comp.vertical_sample != 1)
298
0
                {
299
0
                    comp.fix_an_annoying_bug = 2;
300
6
                }
301
            }
302
2.00k
        }
303
    }
304
305
2.01k
    if img.is_mjpeg {
306
1
        fill_default_mjpeg_tables(
307
1
            img.is_progressive,
308
1
            &mut img.dc_huffman_tables,
309
1
            &mut img.ac_huffman_tables
310
1
        );
311
2.01k
    }
312
313
    // check colorspace matches
314
2.01k
    if img.input_colorspace.num_components() > img.components.len() {
315
0
        if img.input_colorspace == ColorSpace::YCCK {
316
            // Some images may have YCCK format (from adobe app14 segment) which is supposed to be 4 components
317
            // but only 3 components, see issue https://github.com/etemesi254/zune-image/issues/275
318
            // So this is the behaviour of other decoders
319
            // - stb_image: Treats it as YCbCr image
320
            // - libjpeg_turbo: Does not know how to parse YCCK images (transform 2 app14) so treats
321
            // it as YCbCr
322
            // So I will match that to match existing ones
323
0
            warn!("Treating YCCK colorspace as YCbCr as component length does not match");
324
0
            img.input_colorspace = ColorSpace::YCbCr
325
        } else {
326
0
            let msg = format!(
327
0
                " Expected {} number of components but found {}",
328
0
                img.input_colorspace.num_components(),
329
0
                img.components.len()
330
            );
331
332
0
            return Err(DecodeErrors::Format(msg));
333
        }
334
2.01k
    }
335
2.01k
    Ok(())
336
2.01k
}
337
338
///Calculate number of fill bytes added to the end of a JPEG image
339
/// to fill the image
340
///
341
/// JPEG usually inserts padding bytes if the image width cannot be evenly divided into
342
/// 8 , 16 or 32 chunks depending on the sub sampling ratio. So given a sub-sampling ratio,
343
/// and the actual width, this calculates the padded bytes that were added to the image
344
///
345
///  # Params
346
/// -actual_width: Actual width of the image
347
/// -sub_sample: Sub sampling factor of the image
348
///
349
/// # Returns
350
/// The padded width, this is how long the width is for a particular image
351
1.37k
pub fn calculate_padded_width(actual_width: usize, sub_sample: SampleRatios) -> usize {
352
1.37k
    match sub_sample {
353
        SampleRatios::None | SampleRatios::V => {
354
            // None+V sends one MCU row, so that's a simple calculation
355
1.05k
            ((actual_width + 7) / 8) * 8
356
        }
357
        SampleRatios::H | SampleRatios::HV => {
358
            // sends two rows, width can be expanded by up to 15 more bytes
359
28
            ((actual_width + 15) / 16) * 16
360
        }
361
286
        SampleRatios::Generic(h, _) => {
362
286
            ((actual_width + ((h * 8).saturating_sub(1))) / (h * 8)) * (h * 8)
363
        }
364
    }
365
1.37k
}
366
367
// https://www.loc.gov/preservation/digital/formats/fdd/fdd000063.shtml
368
// "Avery Lee, writing in the rec.video.desktop newsgroup in 2001, commented that "MJPEG, or at
369
//  least the MJPEG in AVIs having the MJPG fourcc, is restricted JPEG with a fixed -- and
370
//  *omitted* -- Huffman table. The JPEG must be YCbCr colorspace, it must be 4:2:2, and it must
371
//  use basic Huffman encoding, not arithmetic or progressive.... You can indeed extract the
372
//  MJPEG frames and decode them with a regular JPEG decoder, but you have to prepend the DHT
373
//  segment to them, or else the decoder won't have any idea how to decompress the data.
374
//  The exact table necessary is given in the OpenDML spec.""
375
794
pub fn fill_default_mjpeg_tables(
376
794
    is_progressive: bool, dc_huffman_tables: &mut [Option<HuffmanTable>],
377
794
    ac_huffman_tables: &mut [Option<HuffmanTable>]
378
794
) {
379
    // Section K.3.3
380
    trace!("Filling with default mjpeg tables");
381
382
794
    if dc_huffman_tables[0].is_none() {
383
412
        // Table K.3
384
412
        dc_huffman_tables[0] = Some(
385
412
            HuffmanTable::new_unfilled(
386
412
                &[
387
412
                    0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
388
412
                    0x00, 0x00, 0x00, 0x00
389
412
                ],
390
412
                &[
391
412
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B
392
412
                ],
393
412
                true,
394
412
                is_progressive
395
412
            )
396
412
            .unwrap()
397
412
        );
398
412
    }
399
794
    if dc_huffman_tables[1].is_none() {
400
794
        // Table K.4
401
794
        dc_huffman_tables[1] = Some(
402
794
            HuffmanTable::new_unfilled(
403
794
                &[
404
794
                    0x00, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
405
794
                    0x00, 0x00, 0x00, 0x00
406
794
                ],
407
794
                &[
408
794
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B
409
794
                ],
410
794
                true,
411
794
                is_progressive
412
794
            )
413
794
            .unwrap()
414
794
        );
415
794
    }
416
794
    if ac_huffman_tables[0].is_none() {
417
414
        // Table K.5
418
414
        ac_huffman_tables[0] = Some(
419
414
            HuffmanTable::new_unfilled(
420
414
                &[
421
414
                    0x00, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04,
422
414
                    0x00, 0x00, 0x01, 0x7D
423
414
                ],
424
414
                &[
425
414
                    0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13,
426
414
                    0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42,
427
414
                    0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A,
428
414
                    0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35,
429
414
                    0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
430
414
                    0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67,
431
414
                    0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84,
432
414
                    0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
433
414
                    0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3,
434
414
                    0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
435
414
                    0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1,
436
414
                    0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4,
437
414
                    0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA
438
414
                ],
439
414
                false,
440
414
                is_progressive
441
414
            )
442
414
            .unwrap()
443
414
        );
444
414
    }
445
794
    if ac_huffman_tables[1].is_none() {
446
791
        // Table K.6
447
791
        ac_huffman_tables[1] = Some(
448
791
            HuffmanTable::new_unfilled(
449
791
                &[
450
791
                    0x00, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04,
451
791
                    0x00, 0x01, 0x02, 0x77
452
791
                ],
453
791
                &[
454
791
                    0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
455
791
                    0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1,
456
791
                    0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24,
457
791
                    0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
458
791
                    0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
459
791
                    0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
460
791
                    0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82,
461
791
                    0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96,
462
791
                    0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA,
463
791
                    0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
464
791
                    0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
465
791
                    0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4,
466
791
                    0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA
467
791
                ],
468
791
                false,
469
791
                is_progressive
470
791
            )
471
791
            .unwrap()
472
791
        );
473
791
    }
474
794
}