Coverage Report

Created: 2026-09-28 07:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zune-jpeg-0.5.15/src/headers.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
//! Decode Decoder markers/segments
10
//!
11
//! This file deals with decoding header information in a jpeg file
12
//!
13
use alloc::format;
14
use alloc::string::ToString;
15
use alloc::vec::Vec;
16
17
use zune_core::bytestream::ZByteReaderTrait;
18
use zune_core::colorspace::ColorSpace;
19
use zune_core::log::{debug, trace, warn};
20
21
use core::cmp::max;
22
23
use crate::components::{Components, SampleRatios};
24
use crate::decoder::{ExtendedXmpSegment, GainMapInfo, ICCChunk, JpegDecoder, MAX_COMPONENTS};
25
use crate::errors::DecodeErrors;
26
use crate::huffman::HuffmanTable;
27
use crate::misc::{SOFMarkers, UN_ZIGZAG};
28
29
///**B.2.4.2 Huffman table-specification syntax**
30
#[allow(clippy::similar_names, clippy::cast_sign_loss)]
31
33.7k
pub(crate) fn parse_huffman<T: ZByteReaderTrait>(
32
33.7k
    decoder: &mut JpegDecoder<T>
33
33.7k
) -> Result<(), DecodeErrors>
34
33.7k
where
35
{
36
    // Read the length of the Huffman table
37
33.7k
    let mut dht_length = i32::from(decoder.stream.get_u16_be_err()?.checked_sub(2).ok_or(
38
33.7k
        DecodeErrors::FormatStatic("Invalid Huffman length in image")
39
8
    )?);
40
41
95.6k
    while dht_length > 16 {
42
        // HT information
43
62.7k
        let ht_info = decoder.stream.read_u8_err()?;
44
        // third bit indicates whether the huffman encoding is DC or AC type
45
62.6k
        let dc_or_ac = (ht_info >> 4) & 0xF;
46
        // Indicate the position of this table, should be less than 4;
47
62.6k
        let index = (ht_info & 0xF) as usize;
48
        // read the number of symbols
49
62.6k
        let mut num_symbols: [u8; 17] = [0; 17];
50
51
62.6k
        if index >= MAX_COMPONENTS {
52
55
            return Err(DecodeErrors::HuffmanDecode(format!(
53
55
                "Invalid DHT index {index}, expected between 0 and 3"
54
55
            )));
55
62.5k
        }
56
57
62.5k
        if dc_or_ac > 1 {
58
16
            return Err(DecodeErrors::HuffmanDecode(format!(
59
16
                "Invalid DHT position {dc_or_ac}, should be 0 or 1"
60
16
            )));
61
62.5k
        }
62
63
62.5k
        decoder.stream.read_exact_bytes(&mut num_symbols[1..17])?;
64
65
62.4k
        dht_length -= 1 + 16;
66
67
1.06M
        let symbols_sum: i32 = num_symbols.iter().map(|f| i32::from(*f)).sum();
zune_jpeg::headers::parse_huffman::<std::io::cursor::Cursor<&[u8]>>::{closure#0}
Line
Count
Source
67
829k
        let symbols_sum: i32 = num_symbols.iter().map(|f| i32::from(*f)).sum();
zune_jpeg::headers::parse_huffman::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>::{closure#0}
Line
Count
Source
67
231k
        let symbols_sum: i32 = num_symbols.iter().map(|f| i32::from(*f)).sum();
Unexecuted instantiation: zune_jpeg::headers::parse_huffman::<_>::{closure#0}
68
69
        // The sum of the number of symbols cannot be greater than 256;
70
62.4k
        if symbols_sum > 256 {
71
112
            return Err(DecodeErrors::FormatStatic(
72
112
                "Encountered Huffman table with excessive length in DHT"
73
112
            ));
74
62.2k
        }
75
62.2k
        if symbols_sum > dht_length {
76
26
            return Err(DecodeErrors::HuffmanDecode(format!(
77
26
                "Excessive Huffman table of length {symbols_sum} found when header length is {dht_length}"
78
26
            )));
79
62.2k
        }
80
62.2k
        dht_length -= symbols_sum;
81
        // A table containing symbols in increasing code length
82
62.2k
        let mut symbols = [0; 256];
83
84
62.2k
        decoder
85
62.2k
            .stream
86
62.2k
            .read_exact_bytes(&mut symbols[0..(symbols_sum as usize)])?;
87
        // store
88
62.2k
        match dc_or_ac {
89
            0 => {
90
35.0k
                decoder.dc_huffman_tables[index] = Some(HuffmanTable::new(
91
35.0k
                    &num_symbols,
92
35.0k
                    symbols,
93
                    true,
94
35.0k
                    decoder.is_progressive
95
297
                )?);
96
            }
97
            _ => {
98
27.1k
                decoder.ac_huffman_tables[index] = Some(HuffmanTable::new(
99
27.1k
                    &num_symbols,
100
27.1k
                    symbols,
101
                    false,
102
27.1k
                    decoder.is_progressive
103
13
                )?);
104
            }
105
        }
106
    }
107
108
32.9k
    if dht_length > 0 {
109
12
        return Err(DecodeErrors::FormatStatic("Bogus Huffman table definition"));
110
32.9k
    }
111
112
32.9k
    Ok(())
113
33.7k
}
zune_jpeg::headers::parse_huffman::<std::io::cursor::Cursor<&[u8]>>
Line
Count
Source
31
20.8k
pub(crate) fn parse_huffman<T: ZByteReaderTrait>(
32
20.8k
    decoder: &mut JpegDecoder<T>
33
20.8k
) -> Result<(), DecodeErrors>
34
20.8k
where
35
{
36
    // Read the length of the Huffman table
37
20.8k
    let mut dht_length = i32::from(decoder.stream.get_u16_be_err()?.checked_sub(2).ok_or(
38
20.7k
        DecodeErrors::FormatStatic("Invalid Huffman length in image")
39
6
    )?);
40
41
69.0k
    while dht_length > 16 {
42
        // HT information
43
49.0k
        let ht_info = decoder.stream.read_u8_err()?;
44
        // third bit indicates whether the huffman encoding is DC or AC type
45
49.0k
        let dc_or_ac = (ht_info >> 4) & 0xF;
46
        // Indicate the position of this table, should be less than 4;
47
49.0k
        let index = (ht_info & 0xF) as usize;
48
        // read the number of symbols
49
49.0k
        let mut num_symbols: [u8; 17] = [0; 17];
50
51
49.0k
        if index >= MAX_COMPONENTS {
52
48
            return Err(DecodeErrors::HuffmanDecode(format!(
53
48
                "Invalid DHT index {index}, expected between 0 and 3"
54
48
            )));
55
48.9k
        }
56
57
48.9k
        if dc_or_ac > 1 {
58
13
            return Err(DecodeErrors::HuffmanDecode(format!(
59
13
                "Invalid DHT position {dc_or_ac}, should be 0 or 1"
60
13
            )));
61
48.9k
        }
62
63
48.9k
        decoder.stream.read_exact_bytes(&mut num_symbols[1..17])?;
64
65
48.7k
        dht_length -= 1 + 16;
66
67
48.7k
        let symbols_sum: i32 = num_symbols.iter().map(|f| i32::from(*f)).sum();
68
69
        // The sum of the number of symbols cannot be greater than 256;
70
48.7k
        if symbols_sum > 256 {
71
95
            return Err(DecodeErrors::FormatStatic(
72
95
                "Encountered Huffman table with excessive length in DHT"
73
95
            ));
74
48.6k
        }
75
48.6k
        if symbols_sum > dht_length {
76
20
            return Err(DecodeErrors::HuffmanDecode(format!(
77
20
                "Excessive Huffman table of length {symbols_sum} found when header length is {dht_length}"
78
20
            )));
79
48.6k
        }
80
48.6k
        dht_length -= symbols_sum;
81
        // A table containing symbols in increasing code length
82
48.6k
        let mut symbols = [0; 256];
83
84
48.6k
        decoder
85
48.6k
            .stream
86
48.6k
            .read_exact_bytes(&mut symbols[0..(symbols_sum as usize)])?;
87
        // store
88
48.6k
        match dc_or_ac {
89
            0 => {
90
30.5k
                decoder.dc_huffman_tables[index] = Some(HuffmanTable::new(
91
30.5k
                    &num_symbols,
92
30.5k
                    symbols,
93
                    true,
94
30.5k
                    decoder.is_progressive
95
293
                )?);
96
            }
97
            _ => {
98
18.0k
                decoder.ac_huffman_tables[index] = Some(HuffmanTable::new(
99
18.0k
                    &num_symbols,
100
18.0k
                    symbols,
101
                    false,
102
18.0k
                    decoder.is_progressive
103
12
                )?);
104
            }
105
        }
106
    }
107
108
20.0k
    if dht_length > 0 {
109
9
        return Err(DecodeErrors::FormatStatic("Bogus Huffman table definition"));
110
20.0k
    }
111
112
20.0k
    Ok(())
113
20.8k
}
zune_jpeg::headers::parse_huffman::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Line
Count
Source
31
12.9k
pub(crate) fn parse_huffman<T: ZByteReaderTrait>(
32
12.9k
    decoder: &mut JpegDecoder<T>
33
12.9k
) -> Result<(), DecodeErrors>
34
12.9k
where
35
{
36
    // Read the length of the Huffman table
37
12.9k
    let mut dht_length = i32::from(decoder.stream.get_u16_be_err()?.checked_sub(2).ok_or(
38
12.9k
        DecodeErrors::FormatStatic("Invalid Huffman length in image")
39
2
    )?);
40
41
26.5k
    while dht_length > 16 {
42
        // HT information
43
13.6k
        let ht_info = decoder.stream.read_u8_err()?;
44
        // third bit indicates whether the huffman encoding is DC or AC type
45
13.6k
        let dc_or_ac = (ht_info >> 4) & 0xF;
46
        // Indicate the position of this table, should be less than 4;
47
13.6k
        let index = (ht_info & 0xF) as usize;
48
        // read the number of symbols
49
13.6k
        let mut num_symbols: [u8; 17] = [0; 17];
50
51
13.6k
        if index >= MAX_COMPONENTS {
52
7
            return Err(DecodeErrors::HuffmanDecode(format!(
53
7
                "Invalid DHT index {index}, expected between 0 and 3"
54
7
            )));
55
13.6k
        }
56
57
13.6k
        if dc_or_ac > 1 {
58
3
            return Err(DecodeErrors::HuffmanDecode(format!(
59
3
                "Invalid DHT position {dc_or_ac}, should be 0 or 1"
60
3
            )));
61
13.6k
        }
62
63
13.6k
        decoder.stream.read_exact_bytes(&mut num_symbols[1..17])?;
64
65
13.6k
        dht_length -= 1 + 16;
66
67
13.6k
        let symbols_sum: i32 = num_symbols.iter().map(|f| i32::from(*f)).sum();
68
69
        // The sum of the number of symbols cannot be greater than 256;
70
13.6k
        if symbols_sum > 256 {
71
17
            return Err(DecodeErrors::FormatStatic(
72
17
                "Encountered Huffman table with excessive length in DHT"
73
17
            ));
74
13.6k
        }
75
13.6k
        if symbols_sum > dht_length {
76
6
            return Err(DecodeErrors::HuffmanDecode(format!(
77
6
                "Excessive Huffman table of length {symbols_sum} found when header length is {dht_length}"
78
6
            )));
79
13.5k
        }
80
13.5k
        dht_length -= symbols_sum;
81
        // A table containing symbols in increasing code length
82
13.5k
        let mut symbols = [0; 256];
83
84
13.5k
        decoder
85
13.5k
            .stream
86
13.5k
            .read_exact_bytes(&mut symbols[0..(symbols_sum as usize)])?;
87
        // store
88
13.5k
        match dc_or_ac {
89
            0 => {
90
4.51k
                decoder.dc_huffman_tables[index] = Some(HuffmanTable::new(
91
4.51k
                    &num_symbols,
92
4.51k
                    symbols,
93
                    true,
94
4.51k
                    decoder.is_progressive
95
4
                )?);
96
            }
97
            _ => {
98
9.07k
                decoder.ac_huffman_tables[index] = Some(HuffmanTable::new(
99
9.07k
                    &num_symbols,
100
9.07k
                    symbols,
101
                    false,
102
9.07k
                    decoder.is_progressive
103
1
                )?);
104
            }
105
        }
106
    }
107
108
12.9k
    if dht_length > 0 {
109
3
        return Err(DecodeErrors::FormatStatic("Bogus Huffman table definition"));
110
12.9k
    }
111
112
12.9k
    Ok(())
113
12.9k
}
Unexecuted instantiation: zune_jpeg::headers::parse_huffman::<_>
114
115
///**B.2.4.1 Quantization table-specification syntax**
116
#[allow(clippy::cast_possible_truncation, clippy::needless_range_loop)]
117
60.8k
pub(crate) fn parse_dqt<T: ZByteReaderTrait>(img: &mut JpegDecoder<T>) -> Result<(), DecodeErrors> {
118
    // read length
119
60.8k
    let mut qt_length =
120
60.8k
        img.stream
121
60.8k
            .get_u16_be_err()?
122
60.8k
            .checked_sub(2)
123
60.8k
            .ok_or(DecodeErrors::FormatStatic(
124
60.8k
                "Invalid DQT length. Length should be greater than 2"
125
60.8k
            ))?;
126
    // A single DQT header may have multiple QT's
127
120k
    while qt_length > 0 {
128
59.5k
        let qt_info = img.stream.read_u8_err()?;
129
        // 0 = 8 bit otherwise 16 bit dqt
130
59.5k
        let precision = (qt_info >> 4) as usize;
131
        // last 4 bits give us position
132
59.5k
        let table_position = (qt_info & 0x0f) as usize;
133
59.5k
        let precision_value = 64 * (precision + 1);
134
135
59.5k
        if (precision_value + 1) as u16 > qt_length {
136
39
            return Err(DecodeErrors::DqtError(format!("Invalid QT table bytes left :{}. Too small to construct a valid qt table which should be {} long", qt_length, precision_value + 1)));
137
59.4k
        }
138
139
59.4k
        let dct_table = match precision {
140
            0 => {
141
57.0k
                let mut qt_values = [0; 64];
142
143
57.0k
                img.stream.read_exact_bytes(&mut qt_values)?;
144
145
56.8k
                qt_length -= (precision_value as u16) + 1 /*QT BIT*/;
146
                // carry out un zig-zag here
147
56.8k
                un_zig_zag(&qt_values)
148
            }
149
            1 => {
150
                // 16 bit quantization tables
151
2.43k
                let mut qt_values = [0_u16; 64];
152
153
155k
                for i in 0..64 {
154
153k
                    qt_values[i] = img.stream.get_u16_be_err()?;
155
                }
156
2.38k
                qt_length -= (precision_value as u16) + 1;
157
158
2.38k
                un_zig_zag(&qt_values)
159
            }
160
            _ => {
161
41
                return Err(DecodeErrors::DqtError(format!(
162
41
                    "Expected QT precision value of either 0 or 1, found {precision:?}"
163
41
                )));
164
            }
165
        };
166
167
59.2k
        if table_position >= MAX_COMPONENTS {
168
24
            return Err(DecodeErrors::DqtError(format!(
169
24
                "Too large table position for QT :{table_position}, expected between 0 and 3"
170
24
            )));
171
59.2k
        }
172
173
59.2k
        trace!("Assigning qt table {table_position} with precision {precision}");
174
59.2k
        img.qt_tables[table_position] = Some(dct_table);
175
    }
176
177
60.5k
    return Ok(());
178
60.8k
}
zune_jpeg::headers::parse_dqt::<std::io::cursor::Cursor<&[u8]>>
Line
Count
Source
117
47.1k
pub(crate) fn parse_dqt<T: ZByteReaderTrait>(img: &mut JpegDecoder<T>) -> Result<(), DecodeErrors> {
118
    // read length
119
47.1k
    let mut qt_length =
120
47.1k
        img.stream
121
47.1k
            .get_u16_be_err()?
122
47.1k
            .checked_sub(2)
123
47.1k
            .ok_or(DecodeErrors::FormatStatic(
124
47.1k
                "Invalid DQT length. Length should be greater than 2"
125
47.1k
            ))?;
126
    // A single DQT header may have multiple QT's
127
92.6k
    while qt_length > 0 {
128
45.7k
        let qt_info = img.stream.read_u8_err()?;
129
        // 0 = 8 bit otherwise 16 bit dqt
130
45.7k
        let precision = (qt_info >> 4) as usize;
131
        // last 4 bits give us position
132
45.7k
        let table_position = (qt_info & 0x0f) as usize;
133
45.7k
        let precision_value = 64 * (precision + 1);
134
135
45.7k
        if (precision_value + 1) as u16 > qt_length {
136
32
            return Err(DecodeErrors::DqtError(format!("Invalid QT table bytes left :{}. Too small to construct a valid qt table which should be {} long", qt_length, precision_value + 1)));
137
45.7k
        }
138
139
45.7k
        let dct_table = match precision {
140
            0 => {
141
43.3k
                let mut qt_values = [0; 64];
142
143
43.3k
                img.stream.read_exact_bytes(&mut qt_values)?;
144
145
43.2k
                qt_length -= (precision_value as u16) + 1 /*QT BIT*/;
146
                // carry out un zig-zag here
147
43.2k
                un_zig_zag(&qt_values)
148
            }
149
            1 => {
150
                // 16 bit quantization tables
151
2.35k
                let mut qt_values = [0_u16; 64];
152
153
151k
                for i in 0..64 {
154
148k
                    qt_values[i] = img.stream.get_u16_be_err()?;
155
                }
156
2.32k
                qt_length -= (precision_value as u16) + 1;
157
158
2.32k
                un_zig_zag(&qt_values)
159
            }
160
            _ => {
161
38
                return Err(DecodeErrors::DqtError(format!(
162
38
                    "Expected QT precision value of either 0 or 1, found {precision:?}"
163
38
                )));
164
            }
165
        };
166
167
45.5k
        if table_position >= MAX_COMPONENTS {
168
19
            return Err(DecodeErrors::DqtError(format!(
169
19
                "Too large table position for QT :{table_position}, expected between 0 and 3"
170
19
            )));
171
45.5k
        }
172
173
45.5k
        trace!("Assigning qt table {table_position} with precision {precision}");
174
45.5k
        img.qt_tables[table_position] = Some(dct_table);
175
    }
176
177
46.8k
    return Ok(());
178
47.1k
}
zune_jpeg::headers::parse_dqt::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Line
Count
Source
117
13.7k
pub(crate) fn parse_dqt<T: ZByteReaderTrait>(img: &mut JpegDecoder<T>) -> Result<(), DecodeErrors> {
118
    // read length
119
13.7k
    let mut qt_length =
120
13.7k
        img.stream
121
13.7k
            .get_u16_be_err()?
122
13.7k
            .checked_sub(2)
123
13.7k
            .ok_or(DecodeErrors::FormatStatic(
124
13.7k
                "Invalid DQT length. Length should be greater than 2"
125
13.7k
            ))?;
126
    // A single DQT header may have multiple QT's
127
27.4k
    while qt_length > 0 {
128
13.7k
        let qt_info = img.stream.read_u8_err()?;
129
        // 0 = 8 bit otherwise 16 bit dqt
130
13.7k
        let precision = (qt_info >> 4) as usize;
131
        // last 4 bits give us position
132
13.7k
        let table_position = (qt_info & 0x0f) as usize;
133
13.7k
        let precision_value = 64 * (precision + 1);
134
135
13.7k
        if (precision_value + 1) as u16 > qt_length {
136
7
            return Err(DecodeErrors::DqtError(format!("Invalid QT table bytes left :{}. Too small to construct a valid qt table which should be {} long", qt_length, precision_value + 1)));
137
13.7k
        }
138
139
13.7k
        let dct_table = match precision {
140
            0 => {
141
13.6k
                let mut qt_values = [0; 64];
142
143
13.6k
                img.stream.read_exact_bytes(&mut qt_values)?;
144
145
13.6k
                qt_length -= (precision_value as u16) + 1 /*QT BIT*/;
146
                // carry out un zig-zag here
147
13.6k
                un_zig_zag(&qt_values)
148
            }
149
            1 => {
150
                // 16 bit quantization tables
151
79
                let mut qt_values = [0_u16; 64];
152
153
4.62k
                for i in 0..64 {
154
4.55k
                    qt_values[i] = img.stream.get_u16_be_err()?;
155
                }
156
69
                qt_length -= (precision_value as u16) + 1;
157
158
69
                un_zig_zag(&qt_values)
159
            }
160
            _ => {
161
3
                return Err(DecodeErrors::DqtError(format!(
162
3
                    "Expected QT precision value of either 0 or 1, found {precision:?}"
163
3
                )));
164
            }
165
        };
166
167
13.7k
        if table_position >= MAX_COMPONENTS {
168
5
            return Err(DecodeErrors::DqtError(format!(
169
5
                "Too large table position for QT :{table_position}, expected between 0 and 3"
170
5
            )));
171
13.7k
        }
172
173
13.7k
        trace!("Assigning qt table {table_position} with precision {precision}");
174
13.7k
        img.qt_tables[table_position] = Some(dct_table);
175
    }
176
177
13.6k
    return Ok(());
178
13.7k
}
Unexecuted instantiation: zune_jpeg::headers::parse_dqt::<_>
179
180
/// Section:`B.2.2 Frame header syntax`
181
182
17.3k
pub(crate) fn parse_start_of_frame<T: ZByteReaderTrait>(
183
17.3k
    sof: SOFMarkers, img: &mut JpegDecoder<T>
184
17.3k
) -> Result<(), DecodeErrors> {
185
17.3k
    if img.seen_sof {
186
117
        return Err(DecodeErrors::SofError(
187
117
            "Two Start of Frame Markers".to_string()
188
117
        ));
189
17.1k
    }
190
    // Get length of the frame header
191
17.1k
    let length = img.stream.get_u16_be_err()?;
192
    // usually 8, but can be 12 and 16, we currently support only 8
193
    // so sorry about that 12 bit images
194
17.1k
    let dt_precision = img.stream.read_u8_err()?;
195
196
17.1k
    if dt_precision != 8 {
197
4
        return Err(DecodeErrors::SofError(format!(
198
4
            "The library can only parse 8-bit images, the image has {dt_precision} bits of precision"
199
4
        )));
200
17.1k
    }
201
202
17.1k
    img.info.set_density(dt_precision);
203
204
    // read  and set the image height.
205
17.1k
    let img_height = img.stream.get_u16_be_err()?;
206
17.1k
    img.info.set_height(img_height);
207
208
    // read and set the image width
209
17.1k
    let img_width = img.stream.get_u16_be_err()?;
210
17.1k
    img.info.set_width(img_width);
211
212
17.1k
    trace!("Image width  :{}", img_width);
213
17.1k
    trace!("Image height :{}", img_height);
214
215
17.1k
    if usize::from(img_width) > img.options.max_width() {
216
1
        return Err(DecodeErrors::Format(format!("Image width {} greater than width limit {}. If use `set_limits` if you want to support huge images", img_width, img.options.max_width())));
217
17.1k
    }
218
219
17.1k
    if usize::from(img_height) > img.options.max_height() {
220
4
        return Err(DecodeErrors::Format(format!("Image height {} greater than height limit {}. If use `set_limits` if you want to support huge images", img_height, img.options.max_height())));
221
17.1k
    }
222
223
    // Check image width or height is zero
224
17.1k
    if img_width == 0 || img_height == 0 {
225
2
        return Err(DecodeErrors::ZeroError);
226
17.1k
    }
227
228
    // Number of components for the image.
229
17.1k
    let num_components = img.stream.read_u8_err()?;
230
231
17.1k
    if num_components == 0 {
232
2
        return Err(DecodeErrors::SofError(
233
2
            "Number of components cannot be zero.".to_string()
234
2
        ));
235
17.1k
    }
236
237
17.1k
    let expected = 8 + 3 * u16::from(num_components);
238
    // length should be equal to num components
239
17.1k
    if length != expected {
240
18
        return Err(DecodeErrors::SofError(format!(
241
18
            "Length of start of frame differs from expected {expected},value is {length}"
242
18
        )));
243
17.1k
    }
244
245
17.1k
    trace!("Image components : {}", num_components);
246
247
17.1k
    if num_components == 1 {
248
11.3k
        // SOF sets the number of image components
249
11.3k
        // and that to us translates to setting input and output
250
11.3k
        // colorspaces to zero
251
11.3k
        img.input_colorspace = ColorSpace::Luma;
252
11.3k
        //img.options = img.options.jpeg_set_out_colorspace(ColorSpace::Luma);
253
11.3k
        debug!("Overriding default colorspace set to Luma");
254
11.3k
    }
255
17.1k
    if num_components == 4 && img.input_colorspace == ColorSpace::YCbCr {
256
1.23k
        trace!("Input image has 4 components, defaulting to CMYK colorspace");
257
1.23k
        // https://entropymine.wordpress.com/2018/10/22/how-is-a-jpeg-images-color-type-determined/
258
1.23k
        img.input_colorspace = ColorSpace::CMYK;
259
15.9k
    }
260
261
    // set number of components
262
17.1k
    img.info.components = num_components;
263
264
17.1k
    let mut components = Vec::with_capacity(num_components as usize);
265
17.1k
    let mut temp = [0; 3];
266
267
30.0k
    for pos in 0..num_components {
268
        // read 3 bytes for each component
269
30.0k
        img.stream.read_exact_bytes(&mut temp)?;
270
271
        // create a component.
272
30.0k
        let component = Components::from(temp, pos)?;
273
274
30.0k
        components.push(component);
275
    }
276
17.1k
    img.seen_sof = true;
277
278
17.1k
    img.info.set_sof_marker(sof);
279
280
17.1k
    img.components = components;
281
282
17.1k
    let mut h_max = 1;
283
17.1k
    let mut v_max = 1;
284
285
47.1k
    for comp in &img.components {
286
30.0k
        h_max = max(h_max, comp.horizontal_sample);
287
30.0k
        v_max = max(v_max, comp.vertical_sample);
288
30.0k
    }
289
290
17.1k
    img.info.sample_ratio = match (h_max, v_max) {
291
6.94k
        (1, 1) => SampleRatios::None,
292
1.24k
        (1, 2) => SampleRatios::V,
293
2.34k
        (2, 1) => SampleRatios::H,
294
3.62k
        (2, 2) => SampleRatios::HV,
295
2.97k
        (hs, vs) => SampleRatios::Generic(hs, vs)
296
    };
297
298
17.1k
    Ok(())
299
17.3k
}
zune_jpeg::headers::parse_start_of_frame::<std::io::cursor::Cursor<&[u8]>>
Line
Count
Source
182
9.83k
pub(crate) fn parse_start_of_frame<T: ZByteReaderTrait>(
183
9.83k
    sof: SOFMarkers, img: &mut JpegDecoder<T>
184
9.83k
) -> Result<(), DecodeErrors> {
185
9.83k
    if img.seen_sof {
186
89
        return Err(DecodeErrors::SofError(
187
89
            "Two Start of Frame Markers".to_string()
188
89
        ));
189
9.74k
    }
190
    // Get length of the frame header
191
9.74k
    let length = img.stream.get_u16_be_err()?;
192
    // usually 8, but can be 12 and 16, we currently support only 8
193
    // so sorry about that 12 bit images
194
9.74k
    let dt_precision = img.stream.read_u8_err()?;
195
196
9.74k
    if dt_precision != 8 {
197
4
        return Err(DecodeErrors::SofError(format!(
198
4
            "The library can only parse 8-bit images, the image has {dt_precision} bits of precision"
199
4
        )));
200
9.74k
    }
201
202
9.74k
    img.info.set_density(dt_precision);
203
204
    // read  and set the image height.
205
9.74k
    let img_height = img.stream.get_u16_be_err()?;
206
9.73k
    img.info.set_height(img_height);
207
208
    // read and set the image width
209
9.73k
    let img_width = img.stream.get_u16_be_err()?;
210
9.73k
    img.info.set_width(img_width);
211
212
9.73k
    trace!("Image width  :{}", img_width);
213
9.73k
    trace!("Image height :{}", img_height);
214
215
9.73k
    if usize::from(img_width) > img.options.max_width() {
216
0
        return Err(DecodeErrors::Format(format!("Image width {} greater than width limit {}. If use `set_limits` if you want to support huge images", img_width, img.options.max_width())));
217
9.73k
    }
218
219
9.73k
    if usize::from(img_height) > img.options.max_height() {
220
0
        return Err(DecodeErrors::Format(format!("Image height {} greater than height limit {}. If use `set_limits` if you want to support huge images", img_height, img.options.max_height())));
221
9.73k
    }
222
223
    // Check image width or height is zero
224
9.73k
    if img_width == 0 || img_height == 0 {
225
1
        return Err(DecodeErrors::ZeroError);
226
9.73k
    }
227
228
    // Number of components for the image.
229
9.73k
    let num_components = img.stream.read_u8_err()?;
230
231
9.73k
    if num_components == 0 {
232
1
        return Err(DecodeErrors::SofError(
233
1
            "Number of components cannot be zero.".to_string()
234
1
        ));
235
9.73k
    }
236
237
9.73k
    let expected = 8 + 3 * u16::from(num_components);
238
    // length should be equal to num components
239
9.73k
    if length != expected {
240
11
        return Err(DecodeErrors::SofError(format!(
241
11
            "Length of start of frame differs from expected {expected},value is {length}"
242
11
        )));
243
9.72k
    }
244
245
9.72k
    trace!("Image components : {}", num_components);
246
247
9.72k
    if num_components == 1 {
248
6.22k
        // SOF sets the number of image components
249
6.22k
        // and that to us translates to setting input and output
250
6.22k
        // colorspaces to zero
251
6.22k
        img.input_colorspace = ColorSpace::Luma;
252
6.22k
        //img.options = img.options.jpeg_set_out_colorspace(ColorSpace::Luma);
253
6.22k
        debug!("Overriding default colorspace set to Luma");
254
6.22k
    }
255
9.72k
    if num_components == 4 && img.input_colorspace == ColorSpace::YCbCr {
256
499
        trace!("Input image has 4 components, defaulting to CMYK colorspace");
257
499
        // https://entropymine.wordpress.com/2018/10/22/how-is-a-jpeg-images-color-type-determined/
258
499
        img.input_colorspace = ColorSpace::CMYK;
259
9.22k
    }
260
261
    // set number of components
262
9.72k
    img.info.components = num_components;
263
264
9.72k
    let mut components = Vec::with_capacity(num_components as usize);
265
9.72k
    let mut temp = [0; 3];
266
267
17.2k
    for pos in 0..num_components {
268
        // read 3 bytes for each component
269
17.2k
        img.stream.read_exact_bytes(&mut temp)?;
270
271
        // create a component.
272
17.2k
        let component = Components::from(temp, pos)?;
273
274
17.2k
        components.push(component);
275
    }
276
9.71k
    img.seen_sof = true;
277
278
9.71k
    img.info.set_sof_marker(sof);
279
280
9.71k
    img.components = components;
281
282
9.71k
    let mut h_max = 1;
283
9.71k
    let mut v_max = 1;
284
285
26.9k
    for comp in &img.components {
286
17.2k
        h_max = max(h_max, comp.horizontal_sample);
287
17.2k
        v_max = max(v_max, comp.vertical_sample);
288
17.2k
    }
289
290
9.71k
    img.info.sample_ratio = match (h_max, v_max) {
291
2.11k
        (1, 1) => SampleRatios::None,
292
1.06k
        (1, 2) => SampleRatios::V,
293
2.17k
        (2, 1) => SampleRatios::H,
294
2.40k
        (2, 2) => SampleRatios::HV,
295
1.95k
        (hs, vs) => SampleRatios::Generic(hs, vs)
296
    };
297
298
9.71k
    Ok(())
299
9.83k
}
zune_jpeg::headers::parse_start_of_frame::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Line
Count
Source
182
7.46k
pub(crate) fn parse_start_of_frame<T: ZByteReaderTrait>(
183
7.46k
    sof: SOFMarkers, img: &mut JpegDecoder<T>
184
7.46k
) -> Result<(), DecodeErrors> {
185
7.46k
    if img.seen_sof {
186
28
        return Err(DecodeErrors::SofError(
187
28
            "Two Start of Frame Markers".to_string()
188
28
        ));
189
7.44k
    }
190
    // Get length of the frame header
191
7.44k
    let length = img.stream.get_u16_be_err()?;
192
    // usually 8, but can be 12 and 16, we currently support only 8
193
    // so sorry about that 12 bit images
194
7.43k
    let dt_precision = img.stream.read_u8_err()?;
195
196
7.43k
    if dt_precision != 8 {
197
0
        return Err(DecodeErrors::SofError(format!(
198
0
            "The library can only parse 8-bit images, the image has {dt_precision} bits of precision"
199
0
        )));
200
7.43k
    }
201
202
7.43k
    img.info.set_density(dt_precision);
203
204
    // read  and set the image height.
205
7.43k
    let img_height = img.stream.get_u16_be_err()?;
206
7.43k
    img.info.set_height(img_height);
207
208
    // read and set the image width
209
7.43k
    let img_width = img.stream.get_u16_be_err()?;
210
7.43k
    img.info.set_width(img_width);
211
212
7.43k
    trace!("Image width  :{}", img_width);
213
7.43k
    trace!("Image height :{}", img_height);
214
215
7.43k
    if usize::from(img_width) > img.options.max_width() {
216
1
        return Err(DecodeErrors::Format(format!("Image width {} greater than width limit {}. If use `set_limits` if you want to support huge images", img_width, img.options.max_width())));
217
7.43k
    }
218
219
7.43k
    if usize::from(img_height) > img.options.max_height() {
220
4
        return Err(DecodeErrors::Format(format!("Image height {} greater than height limit {}. If use `set_limits` if you want to support huge images", img_height, img.options.max_height())));
221
7.42k
    }
222
223
    // Check image width or height is zero
224
7.42k
    if img_width == 0 || img_height == 0 {
225
1
        return Err(DecodeErrors::ZeroError);
226
7.42k
    }
227
228
    // Number of components for the image.
229
7.42k
    let num_components = img.stream.read_u8_err()?;
230
231
7.42k
    if num_components == 0 {
232
1
        return Err(DecodeErrors::SofError(
233
1
            "Number of components cannot be zero.".to_string()
234
1
        ));
235
7.42k
    }
236
237
7.42k
    let expected = 8 + 3 * u16::from(num_components);
238
    // length should be equal to num components
239
7.42k
    if length != expected {
240
7
        return Err(DecodeErrors::SofError(format!(
241
7
            "Length of start of frame differs from expected {expected},value is {length}"
242
7
        )));
243
7.41k
    }
244
245
7.41k
    trace!("Image components : {}", num_components);
246
247
7.41k
    if num_components == 1 {
248
5.17k
        // SOF sets the number of image components
249
5.17k
        // and that to us translates to setting input and output
250
5.17k
        // colorspaces to zero
251
5.17k
        img.input_colorspace = ColorSpace::Luma;
252
5.17k
        //img.options = img.options.jpeg_set_out_colorspace(ColorSpace::Luma);
253
5.17k
        debug!("Overriding default colorspace set to Luma");
254
5.17k
    }
255
7.41k
    if num_components == 4 && img.input_colorspace == ColorSpace::YCbCr {
256
739
        trace!("Input image has 4 components, defaulting to CMYK colorspace");
257
739
        // https://entropymine.wordpress.com/2018/10/22/how-is-a-jpeg-images-color-type-determined/
258
739
        img.input_colorspace = ColorSpace::CMYK;
259
6.68k
    }
260
261
    // set number of components
262
7.41k
    img.info.components = num_components;
263
264
7.41k
    let mut components = Vec::with_capacity(num_components as usize);
265
7.41k
    let mut temp = [0; 3];
266
267
12.7k
    for pos in 0..num_components {
268
        // read 3 bytes for each component
269
12.7k
        img.stream.read_exact_bytes(&mut temp)?;
270
271
        // create a component.
272
12.7k
        let component = Components::from(temp, pos)?;
273
274
12.7k
        components.push(component);
275
    }
276
7.41k
    img.seen_sof = true;
277
278
7.41k
    img.info.set_sof_marker(sof);
279
280
7.41k
    img.components = components;
281
282
7.41k
    let mut h_max = 1;
283
7.41k
    let mut v_max = 1;
284
285
20.1k
    for comp in &img.components {
286
12.7k
        h_max = max(h_max, comp.horizontal_sample);
287
12.7k
        v_max = max(v_max, comp.vertical_sample);
288
12.7k
    }
289
290
7.41k
    img.info.sample_ratio = match (h_max, v_max) {
291
4.83k
        (1, 1) => SampleRatios::None,
292
181
        (1, 2) => SampleRatios::V,
293
166
        (2, 1) => SampleRatios::H,
294
1.21k
        (2, 2) => SampleRatios::HV,
295
1.02k
        (hs, vs) => SampleRatios::Generic(hs, vs)
296
    };
297
298
7.41k
    Ok(())
299
7.46k
}
Unexecuted instantiation: zune_jpeg::headers::parse_start_of_frame::<_>
300
301
/// Parse a start of scan data
302
69.4k
pub(crate) fn parse_sos<T: ZByteReaderTrait>(
303
69.4k
    image: &mut JpegDecoder<T>
304
69.4k
) -> Result<(), DecodeErrors> {
305
    // Scan header length
306
69.4k
    let ls = usize::from(image.stream.get_u16_be_err()?);
307
    // Number of image components in scan
308
69.4k
    let ns = image.stream.read_u8_err()?;
309
310
69.4k
    let mut seen: [_; 5] = [-1; { MAX_COMPONENTS + 1 }];
311
312
69.4k
    image.num_scans = ns;
313
69.4k
    let smallest_size = 6 + 2 * usize::from(ns);
314
315
69.4k
    if ls != smallest_size {
316
172
        return Err(DecodeErrors::SosError(format!(
317
172
            "Bad SOS length {ls},corrupt jpeg"
318
172
        )));
319
69.2k
    }
320
321
    // Check number of components.
322
69.2k
    if !(1..5).contains(&ns) {
323
2
        return Err(DecodeErrors::SosError(format!(
324
2
            "Invalid number of components in start of scan {ns}, expected in range 1..5"
325
2
        )));
326
69.2k
    }
327
328
69.2k
    if image.info.components == 0 {
329
193
        return Err(DecodeErrors::FormatStatic(
330
193
            "Error decoding SOF Marker, Number of components cannot be zero."
331
193
        ));
332
69.0k
    }
333
334
    // consume spec parameters
335
69.0k
    image.scan_subsampled = false;
336
337
114k
    for i in 0..ns {
338
114k
        let id = image.stream.read_u8_err()?;
339
340
114k
        if seen.contains(&i32::from(id)) {
341
8
            return Err(DecodeErrors::SofError(format!(
342
8
                "Duplicate ID {id} seen twice in the same component"
343
8
            )));
344
114k
        }
345
346
114k
        seen[usize::from(i)] = i32::from(id);
347
        // DC and AC huffman table position
348
        // top 4 bits contain dc huffman destination table
349
        // lower four bits contain ac huffman destination table
350
114k
        let y = image.stream.read_u8_err()?;
351
352
114k
        let mut j = 0;
353
354
191k
        while j < image.info.components {
355
191k
            if image.components[j as usize].id == id {
356
114k
                break;
357
77.6k
            }
358
359
77.6k
            j += 1;
360
        }
361
362
114k
        if j == image.info.components {
363
176
            return Err(DecodeErrors::SofError(format!(
364
176
                "Invalid component id {}, expected one one of {:?}",
365
                id,
366
176
                image.components.iter().map(|c| c.id).collect::<Vec<_>>()
367
            )));
368
114k
        }
369
370
114k
        let component = &mut image.components[usize::from(j)];
371
114k
        component.dc_huff_table = usize::from((y >> 4) & 0xF);
372
114k
        component.ac_huff_table = usize::from(y & 0xF);
373
114k
        image.z_order[i as usize] = j as usize;
374
375
114k
        if component.vertical_sample != 1 || component.horizontal_sample != 1 {
376
52.6k
            image.scan_subsampled = true;
377
61.3k
        }
378
379
114k
        trace!(
380
114k
            "Assigned huffman tables {}/{} to component {j}, id={}",
381
114k
            image.components[usize::from(j)].dc_huff_table,
382
114k
            image.components[usize::from(j)].ac_huff_table,
383
114k
            image.components[usize::from(j)].id,
384
        );
385
    }
386
387
    // Collect the component spec parameters
388
    // This is only needed for progressive images but I'll read
389
    // them in order to ensure they are correct according to the spec
390
391
    // Extract progressive information
392
393
    // https://www.w3.org/Graphics/JPEG/itu-t81.pdf
394
    // Page 42
395
396
    // Start of spectral / predictor selection. (between 0 and 63)
397
68.8k
    image.spec_start = image.stream.read_u8_err()?;
398
    // End of spectral selection
399
68.8k
    image.spec_end = image.stream.read_u8_err()?;
400
401
68.7k
    let bit_approx = image.stream.read_u8_err()?;
402
    // successive approximation bit position high
403
68.7k
    image.succ_high = bit_approx >> 4;
404
405
68.7k
    if image.spec_end > 63 {
406
43
        return Err(DecodeErrors::SosError(format!(
407
43
            "Invalid Se parameter {}, range should be 0-63",
408
43
            image.spec_end
409
43
        )));
410
68.7k
    }
411
68.7k
    if image.spec_start > 63 {
412
11
        return Err(DecodeErrors::SosError(format!(
413
11
            "Invalid Ss parameter {}, range should be 0-63",
414
11
            image.spec_start
415
11
        )));
416
68.7k
    }
417
68.7k
    if image.succ_high > 13 {
418
11
        return Err(DecodeErrors::SosError(format!(
419
11
            "Invalid Ah parameter {}, range should be 0-13",
420
11
            image.succ_low
421
11
        )));
422
68.7k
    }
423
    // successive approximation bit position low
424
68.7k
    image.succ_low = bit_approx & 0xF;
425
426
68.7k
    if image.succ_low > 13 {
427
11
        return Err(DecodeErrors::SosError(format!(
428
11
            "Invalid Al parameter {}, range should be 0-13",
429
11
            image.succ_low
430
11
        )));
431
68.7k
    }
432
    // skip any bytes not read
433
68.7k
    image.stream.skip(smallest_size.saturating_sub(ls))?;
434
435
68.7k
    trace!(
436
68.7k
        "Ss={}, Se={} Ah={} Al={}",
437
        image.spec_start,
438
        image.spec_end,
439
        image.succ_high,
440
        image.succ_low
441
    );
442
443
68.7k
    Ok(())
444
69.4k
}
zune_jpeg::headers::parse_sos::<std::io::cursor::Cursor<&[u8]>>
Line
Count
Source
302
44.4k
pub(crate) fn parse_sos<T: ZByteReaderTrait>(
303
44.4k
    image: &mut JpegDecoder<T>
304
44.4k
) -> Result<(), DecodeErrors> {
305
    // Scan header length
306
44.4k
    let ls = usize::from(image.stream.get_u16_be_err()?);
307
    // Number of image components in scan
308
44.4k
    let ns = image.stream.read_u8_err()?;
309
310
44.4k
    let mut seen: [_; 5] = [-1; { MAX_COMPONENTS + 1 }];
311
312
44.4k
    image.num_scans = ns;
313
44.4k
    let smallest_size = 6 + 2 * usize::from(ns);
314
315
44.4k
    if ls != smallest_size {
316
144
        return Err(DecodeErrors::SosError(format!(
317
144
            "Bad SOS length {ls},corrupt jpeg"
318
144
        )));
319
44.2k
    }
320
321
    // Check number of components.
322
44.2k
    if !(1..5).contains(&ns) {
323
1
        return Err(DecodeErrors::SosError(format!(
324
1
            "Invalid number of components in start of scan {ns}, expected in range 1..5"
325
1
        )));
326
44.2k
    }
327
328
44.2k
    if image.info.components == 0 {
329
38
        return Err(DecodeErrors::FormatStatic(
330
38
            "Error decoding SOF Marker, Number of components cannot be zero."
331
38
        ));
332
44.2k
    }
333
334
    // consume spec parameters
335
44.2k
    image.scan_subsampled = false;
336
337
73.9k
    for i in 0..ns {
338
73.9k
        let id = image.stream.read_u8_err()?;
339
340
73.9k
        if seen.contains(&i32::from(id)) {
341
5
            return Err(DecodeErrors::SofError(format!(
342
5
                "Duplicate ID {id} seen twice in the same component"
343
5
            )));
344
73.9k
        }
345
346
73.9k
        seen[usize::from(i)] = i32::from(id);
347
        // DC and AC huffman table position
348
        // top 4 bits contain dc huffman destination table
349
        // lower four bits contain ac huffman destination table
350
73.9k
        let y = image.stream.read_u8_err()?;
351
352
73.8k
        let mut j = 0;
353
354
122k
        while j < image.info.components {
355
122k
            if image.components[j as usize].id == id {
356
73.7k
                break;
357
48.7k
            }
358
359
48.7k
            j += 1;
360
        }
361
362
73.8k
        if j == image.info.components {
363
158
            return Err(DecodeErrors::SofError(format!(
364
158
                "Invalid component id {}, expected one one of {:?}",
365
                id,
366
158
                image.components.iter().map(|c| c.id).collect::<Vec<_>>()
367
            )));
368
73.7k
        }
369
370
73.7k
        let component = &mut image.components[usize::from(j)];
371
73.7k
        component.dc_huff_table = usize::from((y >> 4) & 0xF);
372
73.7k
        component.ac_huff_table = usize::from(y & 0xF);
373
73.7k
        image.z_order[i as usize] = j as usize;
374
375
73.7k
        if component.vertical_sample != 1 || component.horizontal_sample != 1 {
376
45.1k
            image.scan_subsampled = true;
377
45.1k
        }
378
379
73.7k
        trace!(
380
73.7k
            "Assigned huffman tables {}/{} to component {j}, id={}",
381
73.7k
            image.components[usize::from(j)].dc_huff_table,
382
73.7k
            image.components[usize::from(j)].ac_huff_table,
383
73.7k
            image.components[usize::from(j)].id,
384
        );
385
    }
386
387
    // Collect the component spec parameters
388
    // This is only needed for progressive images but I'll read
389
    // them in order to ensure they are correct according to the spec
390
391
    // Extract progressive information
392
393
    // https://www.w3.org/Graphics/JPEG/itu-t81.pdf
394
    // Page 42
395
396
    // Start of spectral / predictor selection. (between 0 and 63)
397
44.0k
    image.spec_start = image.stream.read_u8_err()?;
398
    // End of spectral selection
399
44.0k
    image.spec_end = image.stream.read_u8_err()?;
400
401
44.0k
    let bit_approx = image.stream.read_u8_err()?;
402
    // successive approximation bit position high
403
44.0k
    image.succ_high = bit_approx >> 4;
404
405
44.0k
    if image.spec_end > 63 {
406
31
        return Err(DecodeErrors::SosError(format!(
407
31
            "Invalid Se parameter {}, range should be 0-63",
408
31
            image.spec_end
409
31
        )));
410
43.9k
    }
411
43.9k
    if image.spec_start > 63 {
412
8
        return Err(DecodeErrors::SosError(format!(
413
8
            "Invalid Ss parameter {}, range should be 0-63",
414
8
            image.spec_start
415
8
        )));
416
43.9k
    }
417
43.9k
    if image.succ_high > 13 {
418
8
        return Err(DecodeErrors::SosError(format!(
419
8
            "Invalid Ah parameter {}, range should be 0-13",
420
8
            image.succ_low
421
8
        )));
422
43.9k
    }
423
    // successive approximation bit position low
424
43.9k
    image.succ_low = bit_approx & 0xF;
425
426
43.9k
    if image.succ_low > 13 {
427
7
        return Err(DecodeErrors::SosError(format!(
428
7
            "Invalid Al parameter {}, range should be 0-13",
429
7
            image.succ_low
430
7
        )));
431
43.9k
    }
432
    // skip any bytes not read
433
43.9k
    image.stream.skip(smallest_size.saturating_sub(ls))?;
434
435
43.9k
    trace!(
436
43.9k
        "Ss={}, Se={} Ah={} Al={}",
437
        image.spec_start,
438
        image.spec_end,
439
        image.succ_high,
440
        image.succ_low
441
    );
442
443
43.9k
    Ok(())
444
44.4k
}
zune_jpeg::headers::parse_sos::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Line
Count
Source
302
24.9k
pub(crate) fn parse_sos<T: ZByteReaderTrait>(
303
24.9k
    image: &mut JpegDecoder<T>
304
24.9k
) -> Result<(), DecodeErrors> {
305
    // Scan header length
306
24.9k
    let ls = usize::from(image.stream.get_u16_be_err()?);
307
    // Number of image components in scan
308
24.9k
    let ns = image.stream.read_u8_err()?;
309
310
24.9k
    let mut seen: [_; 5] = [-1; { MAX_COMPONENTS + 1 }];
311
312
24.9k
    image.num_scans = ns;
313
24.9k
    let smallest_size = 6 + 2 * usize::from(ns);
314
315
24.9k
    if ls != smallest_size {
316
28
        return Err(DecodeErrors::SosError(format!(
317
28
            "Bad SOS length {ls},corrupt jpeg"
318
28
        )));
319
24.9k
    }
320
321
    // Check number of components.
322
24.9k
    if !(1..5).contains(&ns) {
323
1
        return Err(DecodeErrors::SosError(format!(
324
1
            "Invalid number of components in start of scan {ns}, expected in range 1..5"
325
1
        )));
326
24.9k
    }
327
328
24.9k
    if image.info.components == 0 {
329
155
        return Err(DecodeErrors::FormatStatic(
330
155
            "Error decoding SOF Marker, Number of components cannot be zero."
331
155
        ));
332
24.8k
    }
333
334
    // consume spec parameters
335
24.8k
    image.scan_subsampled = false;
336
337
40.3k
    for i in 0..ns {
338
40.3k
        let id = image.stream.read_u8_err()?;
339
340
40.3k
        if seen.contains(&i32::from(id)) {
341
3
            return Err(DecodeErrors::SofError(format!(
342
3
                "Duplicate ID {id} seen twice in the same component"
343
3
            )));
344
40.3k
        }
345
346
40.3k
        seen[usize::from(i)] = i32::from(id);
347
        // DC and AC huffman table position
348
        // top 4 bits contain dc huffman destination table
349
        // lower four bits contain ac huffman destination table
350
40.3k
        let y = image.stream.read_u8_err()?;
351
352
40.3k
        let mut j = 0;
353
354
69.1k
        while j < image.info.components {
355
69.1k
            if image.components[j as usize].id == id {
356
40.3k
                break;
357
28.8k
            }
358
359
28.8k
            j += 1;
360
        }
361
362
40.3k
        if j == image.info.components {
363
18
            return Err(DecodeErrors::SofError(format!(
364
18
                "Invalid component id {}, expected one one of {:?}",
365
                id,
366
18
                image.components.iter().map(|c| c.id).collect::<Vec<_>>()
367
            )));
368
40.3k
        }
369
370
40.3k
        let component = &mut image.components[usize::from(j)];
371
40.3k
        component.dc_huff_table = usize::from((y >> 4) & 0xF);
372
40.3k
        component.ac_huff_table = usize::from(y & 0xF);
373
40.3k
        image.z_order[i as usize] = j as usize;
374
375
40.3k
        if component.vertical_sample != 1 || component.horizontal_sample != 1 {
376
7.47k
            image.scan_subsampled = true;
377
32.8k
        }
378
379
40.3k
        trace!(
380
40.3k
            "Assigned huffman tables {}/{} to component {j}, id={}",
381
40.3k
            image.components[usize::from(j)].dc_huff_table,
382
40.3k
            image.components[usize::from(j)].ac_huff_table,
383
40.3k
            image.components[usize::from(j)].id,
384
        );
385
    }
386
387
    // Collect the component spec parameters
388
    // This is only needed for progressive images but I'll read
389
    // them in order to ensure they are correct according to the spec
390
391
    // Extract progressive information
392
393
    // https://www.w3.org/Graphics/JPEG/itu-t81.pdf
394
    // Page 42
395
396
    // Start of spectral / predictor selection. (between 0 and 63)
397
24.7k
    image.spec_start = image.stream.read_u8_err()?;
398
    // End of spectral selection
399
24.7k
    image.spec_end = image.stream.read_u8_err()?;
400
401
24.7k
    let bit_approx = image.stream.read_u8_err()?;
402
    // successive approximation bit position high
403
24.7k
    image.succ_high = bit_approx >> 4;
404
405
24.7k
    if image.spec_end > 63 {
406
12
        return Err(DecodeErrors::SosError(format!(
407
12
            "Invalid Se parameter {}, range should be 0-63",
408
12
            image.spec_end
409
12
        )));
410
24.7k
    }
411
24.7k
    if image.spec_start > 63 {
412
3
        return Err(DecodeErrors::SosError(format!(
413
3
            "Invalid Ss parameter {}, range should be 0-63",
414
3
            image.spec_start
415
3
        )));
416
24.7k
    }
417
24.7k
    if image.succ_high > 13 {
418
3
        return Err(DecodeErrors::SosError(format!(
419
3
            "Invalid Ah parameter {}, range should be 0-13",
420
3
            image.succ_low
421
3
        )));
422
24.7k
    }
423
    // successive approximation bit position low
424
24.7k
    image.succ_low = bit_approx & 0xF;
425
426
24.7k
    if image.succ_low > 13 {
427
4
        return Err(DecodeErrors::SosError(format!(
428
4
            "Invalid Al parameter {}, range should be 0-13",
429
4
            image.succ_low
430
4
        )));
431
24.7k
    }
432
    // skip any bytes not read
433
24.7k
    image.stream.skip(smallest_size.saturating_sub(ls))?;
434
435
24.7k
    trace!(
436
24.7k
        "Ss={}, Se={} Ah={} Al={}",
437
        image.spec_start,
438
        image.spec_end,
439
        image.succ_high,
440
        image.succ_low
441
    );
442
443
24.7k
    Ok(())
444
24.9k
}
Unexecuted instantiation: zune_jpeg::headers::parse_sos::<_>
445
446
/// Parse the APP13 (IPTC) segment.
447
49.4k
pub(crate) fn parse_app13<T: ZByteReaderTrait>(
448
49.4k
    decoder: &mut JpegDecoder<T>
449
49.4k
) -> Result<(), DecodeErrors> {
450
    const IPTC_PREFIX: &[u8] = b"Photoshop 3.0\0";
451
    // skip length.
452
49.4k
    let mut length = usize::from(decoder.stream.get_u16_be());
453
454
49.4k
    if length < 2 {
455
29
        return Err(DecodeErrors::FormatStatic("Too small APP13 length"));
456
49.4k
    }
457
    // length bytes.
458
49.4k
    length -= 2;
459
460
49.4k
    if length > IPTC_PREFIX.len() && decoder.stream.peek_at(0, IPTC_PREFIX.len())? == IPTC_PREFIX {
461
        // skip bytes we read above.
462
17.2k
        decoder.stream.skip(IPTC_PREFIX.len())?;
463
17.2k
        length -= IPTC_PREFIX.len();
464
465
17.2k
        let iptc_bytes = decoder.stream.peek_at(0, length)?.to_vec();
466
467
17.2k
        decoder.info.iptc_data = Some(iptc_bytes);
468
32.0k
    }
469
470
49.3k
    decoder.stream.skip(length)?;
471
49.3k
    Ok(())
472
49.4k
}
zune_jpeg::headers::parse_app13::<std::io::cursor::Cursor<&[u8]>>
Line
Count
Source
447
48.3k
pub(crate) fn parse_app13<T: ZByteReaderTrait>(
448
48.3k
    decoder: &mut JpegDecoder<T>
449
48.3k
) -> Result<(), DecodeErrors> {
450
    const IPTC_PREFIX: &[u8] = b"Photoshop 3.0\0";
451
    // skip length.
452
48.3k
    let mut length = usize::from(decoder.stream.get_u16_be());
453
454
48.3k
    if length < 2 {
455
22
        return Err(DecodeErrors::FormatStatic("Too small APP13 length"));
456
48.3k
    }
457
    // length bytes.
458
48.3k
    length -= 2;
459
460
48.3k
    if length > IPTC_PREFIX.len() && decoder.stream.peek_at(0, IPTC_PREFIX.len())? == IPTC_PREFIX {
461
        // skip bytes we read above.
462
16.8k
        decoder.stream.skip(IPTC_PREFIX.len())?;
463
16.8k
        length -= IPTC_PREFIX.len();
464
465
16.8k
        let iptc_bytes = decoder.stream.peek_at(0, length)?.to_vec();
466
467
16.8k
        decoder.info.iptc_data = Some(iptc_bytes);
468
31.4k
    }
469
470
48.2k
    decoder.stream.skip(length)?;
471
48.2k
    Ok(())
472
48.3k
}
zune_jpeg::headers::parse_app13::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Line
Count
Source
447
1.07k
pub(crate) fn parse_app13<T: ZByteReaderTrait>(
448
1.07k
    decoder: &mut JpegDecoder<T>
449
1.07k
) -> Result<(), DecodeErrors> {
450
    const IPTC_PREFIX: &[u8] = b"Photoshop 3.0\0";
451
    // skip length.
452
1.07k
    let mut length = usize::from(decoder.stream.get_u16_be());
453
454
1.07k
    if length < 2 {
455
7
        return Err(DecodeErrors::FormatStatic("Too small APP13 length"));
456
1.06k
    }
457
    // length bytes.
458
1.06k
    length -= 2;
459
460
1.06k
    if length > IPTC_PREFIX.len() && decoder.stream.peek_at(0, IPTC_PREFIX.len())? == IPTC_PREFIX {
461
        // skip bytes we read above.
462
428
        decoder.stream.skip(IPTC_PREFIX.len())?;
463
428
        length -= IPTC_PREFIX.len();
464
465
428
        let iptc_bytes = decoder.stream.peek_at(0, length)?.to_vec();
466
467
427
        decoder.info.iptc_data = Some(iptc_bytes);
468
641
    }
469
470
1.06k
    decoder.stream.skip(length)?;
471
1.06k
    Ok(())
472
1.07k
}
Unexecuted instantiation: zune_jpeg::headers::parse_app13::<_>
473
474
/// Parse Adobe App14 segment
475
76.0k
pub(crate) fn parse_app14<T: ZByteReaderTrait>(
476
76.0k
    decoder: &mut JpegDecoder<T>
477
76.0k
) -> Result<(), DecodeErrors> {
478
    // skip length
479
76.0k
    let mut length = usize::from(decoder.stream.get_u16_be());
480
481
76.0k
    if length < 2 {
482
22
        return Err(DecodeErrors::FormatStatic("Too small APP14 length"));
483
76.0k
    }
484
485
76.0k
    if decoder.stream.peek_at(0, 5)? == b"Adobe" {
486
28.1k
        if length < 14 {
487
2
            return Err(DecodeErrors::FormatStatic(
488
2
                "Too short of a length for App14 segment"
489
2
            ));
490
28.0k
        }
491
        // move stream 6 bytes to remove adobe id
492
28.0k
        decoder.stream.skip(6)?;
493
        // skip version, flags0 and flags1
494
28.0k
        decoder.stream.skip(5)?;
495
        // get color transform
496
28.0k
        let transform = decoder.stream.read_u8();
497
        // https://exiftool.org/TagNames/JPEG.html#Adobe
498
28.0k
        match transform {
499
26.0k
            0 => decoder.input_colorspace = ColorSpace::CMYK,
500
598
            1 => decoder.input_colorspace = ColorSpace::YCbCr,
501
1.39k
            2 => decoder.input_colorspace = ColorSpace::YCCK,
502
            _ => {
503
19
                return Err(DecodeErrors::Format(format!(
504
19
                    "Unknown Adobe colorspace {transform}"
505
19
                )))
506
            }
507
        }
508
        // length   = 2
509
        // adobe id = 6
510
        // version =  5
511
        // transform = 1
512
28.0k
        length = length.saturating_sub(14);
513
47.9k
    } else {
514
47.9k
        warn!("Not a valid Adobe APP14 Segment, skipping {} bytes", length);
515
47.9k
        length = length.saturating_sub(2);
516
47.9k
    }
517
    // skip any proceeding lengths.
518
    // we do not need them
519
76.0k
    decoder.stream.skip(length)?;
520
521
76.0k
    Ok(())
522
76.0k
}
zune_jpeg::headers::parse_app14::<std::io::cursor::Cursor<&[u8]>>
Line
Count
Source
475
73.0k
pub(crate) fn parse_app14<T: ZByteReaderTrait>(
476
73.0k
    decoder: &mut JpegDecoder<T>
477
73.0k
) -> Result<(), DecodeErrors> {
478
    // skip length
479
73.0k
    let mut length = usize::from(decoder.stream.get_u16_be());
480
481
73.0k
    if length < 2 {
482
20
        return Err(DecodeErrors::FormatStatic("Too small APP14 length"));
483
73.0k
    }
484
485
73.0k
    if decoder.stream.peek_at(0, 5)? == b"Adobe" {
486
26.9k
        if length < 14 {
487
1
            return Err(DecodeErrors::FormatStatic(
488
1
                "Too short of a length for App14 segment"
489
1
            ));
490
26.9k
        }
491
        // move stream 6 bytes to remove adobe id
492
26.9k
        decoder.stream.skip(6)?;
493
        // skip version, flags0 and flags1
494
26.9k
        decoder.stream.skip(5)?;
495
        // get color transform
496
26.9k
        let transform = decoder.stream.read_u8();
497
        // https://exiftool.org/TagNames/JPEG.html#Adobe
498
26.9k
        match transform {
499
25.5k
            0 => decoder.input_colorspace = ColorSpace::CMYK,
500
468
            1 => decoder.input_colorspace = ColorSpace::YCbCr,
501
985
            2 => decoder.input_colorspace = ColorSpace::YCCK,
502
            _ => {
503
16
                return Err(DecodeErrors::Format(format!(
504
16
                    "Unknown Adobe colorspace {transform}"
505
16
                )))
506
            }
507
        }
508
        // length   = 2
509
        // adobe id = 6
510
        // version =  5
511
        // transform = 1
512
26.9k
        length = length.saturating_sub(14);
513
46.0k
    } else {
514
46.0k
        warn!("Not a valid Adobe APP14 Segment, skipping {} bytes", length);
515
46.0k
        length = length.saturating_sub(2);
516
46.0k
    }
517
    // skip any proceeding lengths.
518
    // we do not need them
519
72.9k
    decoder.stream.skip(length)?;
520
521
72.9k
    Ok(())
522
73.0k
}
zune_jpeg::headers::parse_app14::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Line
Count
Source
475
3.04k
pub(crate) fn parse_app14<T: ZByteReaderTrait>(
476
3.04k
    decoder: &mut JpegDecoder<T>
477
3.04k
) -> Result<(), DecodeErrors> {
478
    // skip length
479
3.04k
    let mut length = usize::from(decoder.stream.get_u16_be());
480
481
3.04k
    if length < 2 {
482
2
        return Err(DecodeErrors::FormatStatic("Too small APP14 length"));
483
3.03k
    }
484
485
3.03k
    if decoder.stream.peek_at(0, 5)? == b"Adobe" {
486
1.11k
        if length < 14 {
487
1
            return Err(DecodeErrors::FormatStatic(
488
1
                "Too short of a length for App14 segment"
489
1
            ));
490
1.10k
        }
491
        // move stream 6 bytes to remove adobe id
492
1.10k
        decoder.stream.skip(6)?;
493
        // skip version, flags0 and flags1
494
1.10k
        decoder.stream.skip(5)?;
495
        // get color transform
496
1.10k
        let transform = decoder.stream.read_u8();
497
        // https://exiftool.org/TagNames/JPEG.html#Adobe
498
1.10k
        match transform {
499
571
            0 => decoder.input_colorspace = ColorSpace::CMYK,
500
130
            1 => decoder.input_colorspace = ColorSpace::YCbCr,
501
405
            2 => decoder.input_colorspace = ColorSpace::YCCK,
502
            _ => {
503
3
                return Err(DecodeErrors::Format(format!(
504
3
                    "Unknown Adobe colorspace {transform}"
505
3
                )))
506
            }
507
        }
508
        // length   = 2
509
        // adobe id = 6
510
        // version =  5
511
        // transform = 1
512
1.10k
        length = length.saturating_sub(14);
513
1.92k
    } else {
514
1.92k
        warn!("Not a valid Adobe APP14 Segment, skipping {} bytes", length);
515
1.92k
        length = length.saturating_sub(2);
516
1.92k
    }
517
    // skip any proceeding lengths.
518
    // we do not need them
519
3.03k
    decoder.stream.skip(length)?;
520
521
3.03k
    Ok(())
522
3.04k
}
Unexecuted instantiation: zune_jpeg::headers::parse_app14::<_>
523
524
/// Parse the APP1 segment
525
///
526
/// This contains the exif tag
527
257k
pub(crate) fn parse_app1<T: ZByteReaderTrait>(
528
257k
    decoder: &mut JpegDecoder<T>
529
257k
) -> Result<(), DecodeErrors> {
530
    const XMP_NAMESPACE_PREFIX: &[u8] = b"http://ns.adobe.com/xap/1.0/\0";
531
    const EXTENDED_XMP_NAMESPACE_PREFIX: &[u8] = b"http://ns.adobe.com/xmp/extension/\0";
532
    const EXTENDED_XMP_GUID_SIZE: usize = 32;
533
    const EXTENDED_XMP_TOTAL_SIZE_SIZE: usize = 4;
534
    const EXTENDED_XMP_OFFSET_SIZE: usize = 4;
535
    const EXTENDED_XMP_HEADER_SIZE: usize =
536
        EXTENDED_XMP_GUID_SIZE + EXTENDED_XMP_TOTAL_SIZE_SIZE + EXTENDED_XMP_OFFSET_SIZE;
537
538
    // contains exif data
539
257k
    let mut length = usize::from(decoder.stream.get_u16_be());
540
541
257k
    if length < 2 {
542
30
        return Err(DecodeErrors::FormatStatic("Too small app1 length"));
543
257k
    }
544
    // length bytes
545
257k
    length -= 2;
546
547
257k
    if length > 6 && decoder.stream.peek_at(0, 6)? == b"Exif\x00\x00" {
548
23.5k
        trace!("Exif segment present");
549
        // skip bytes we read above
550
23.5k
        decoder.stream.skip(6)?;
551
23.5k
        length -= 6;
552
553
23.5k
        let exif_bytes = decoder.stream.peek_at(0, length)?.to_vec();
554
555
23.5k
        decoder.info.exif_data = Some(exif_bytes);
556
234k
    } else if length > XMP_NAMESPACE_PREFIX.len()
557
188k
        && decoder.stream.peek_at(0, XMP_NAMESPACE_PREFIX.len())? == XMP_NAMESPACE_PREFIX
558
    {
559
6.43k
        trace!("XMP Data Present");
560
6.43k
        decoder.stream.skip(XMP_NAMESPACE_PREFIX.len())?;
561
6.43k
        length -= XMP_NAMESPACE_PREFIX.len();
562
6.43k
        let xmp_data = decoder.stream.peek_at(0, length)?.to_vec();
563
6.39k
        decoder.info.xmp_data = Some(xmp_data);
564
227k
    } else if length > EXTENDED_XMP_NAMESPACE_PREFIX.len()
565
180k
        && decoder.stream.peek_at(0, EXTENDED_XMP_NAMESPACE_PREFIX.len())?
566
180k
            == EXTENDED_XMP_NAMESPACE_PREFIX
567
    {
568
157k
        trace!("Extended XMP Data Present");
569
157k
        decoder.stream.skip(EXTENDED_XMP_NAMESPACE_PREFIX.len())?;
570
157k
        length -= EXTENDED_XMP_NAMESPACE_PREFIX.len();
571
572
157k
        if length < EXTENDED_XMP_HEADER_SIZE {
573
2
            return Err(DecodeErrors::FormatStatic("Too small Extended XMP segment"));
574
157k
        }
575
576
157k
        let header = decoder.stream.peek_at(0, EXTENDED_XMP_HEADER_SIZE)?;
577
157k
        let guid = header[0..EXTENDED_XMP_GUID_SIZE].to_vec();
578
579
157k
        let total_size_start = EXTENDED_XMP_GUID_SIZE;
580
157k
        let total_size_end = total_size_start + EXTENDED_XMP_TOTAL_SIZE_SIZE;
581
157k
        let total_size =
582
157k
            u32::from_be_bytes(header[total_size_start..total_size_end].try_into().unwrap());
583
584
157k
        let offset_start = total_size_end;
585
157k
        let offset_end = offset_start + EXTENDED_XMP_OFFSET_SIZE;
586
157k
        let offset = u32::from_be_bytes(header[offset_start..offset_end].try_into().unwrap());
587
588
157k
        let data = decoder
589
157k
            .stream
590
157k
            .peek_at(EXTENDED_XMP_HEADER_SIZE, length - EXTENDED_XMP_HEADER_SIZE)?
591
157k
            .to_vec();
592
593
157k
        decoder.extended_xmp_segments.push(ExtendedXmpSegment { offset, total_size, guid, data });
594
70.4k
    } else {
595
70.4k
        warn!("Unknown format for APP1 tag, skipping");
596
70.4k
    }
597
598
257k
    decoder.stream.skip(length)?;
599
257k
    Ok(())
600
257k
}
zune_jpeg::headers::parse_app1::<std::io::cursor::Cursor<&[u8]>>
Line
Count
Source
527
232k
pub(crate) fn parse_app1<T: ZByteReaderTrait>(
528
232k
    decoder: &mut JpegDecoder<T>
529
232k
) -> Result<(), DecodeErrors> {
530
    const XMP_NAMESPACE_PREFIX: &[u8] = b"http://ns.adobe.com/xap/1.0/\0";
531
    const EXTENDED_XMP_NAMESPACE_PREFIX: &[u8] = b"http://ns.adobe.com/xmp/extension/\0";
532
    const EXTENDED_XMP_GUID_SIZE: usize = 32;
533
    const EXTENDED_XMP_TOTAL_SIZE_SIZE: usize = 4;
534
    const EXTENDED_XMP_OFFSET_SIZE: usize = 4;
535
    const EXTENDED_XMP_HEADER_SIZE: usize =
536
        EXTENDED_XMP_GUID_SIZE + EXTENDED_XMP_TOTAL_SIZE_SIZE + EXTENDED_XMP_OFFSET_SIZE;
537
538
    // contains exif data
539
232k
    let mut length = usize::from(decoder.stream.get_u16_be());
540
541
232k
    if length < 2 {
542
29
        return Err(DecodeErrors::FormatStatic("Too small app1 length"));
543
232k
    }
544
    // length bytes
545
232k
    length -= 2;
546
547
232k
    if length > 6 && decoder.stream.peek_at(0, 6)? == b"Exif\x00\x00" {
548
23.0k
        trace!("Exif segment present");
549
        // skip bytes we read above
550
23.0k
        decoder.stream.skip(6)?;
551
23.0k
        length -= 6;
552
553
23.0k
        let exif_bytes = decoder.stream.peek_at(0, length)?.to_vec();
554
555
23.0k
        decoder.info.exif_data = Some(exif_bytes);
556
209k
    } else if length > XMP_NAMESPACE_PREFIX.len()
557
165k
        && decoder.stream.peek_at(0, XMP_NAMESPACE_PREFIX.len())? == XMP_NAMESPACE_PREFIX
558
    {
559
6.10k
        trace!("XMP Data Present");
560
6.10k
        decoder.stream.skip(XMP_NAMESPACE_PREFIX.len())?;
561
6.10k
        length -= XMP_NAMESPACE_PREFIX.len();
562
6.10k
        let xmp_data = decoder.stream.peek_at(0, length)?.to_vec();
563
6.06k
        decoder.info.xmp_data = Some(xmp_data);
564
203k
    } else if length > EXTENDED_XMP_NAMESPACE_PREFIX.len()
565
157k
        && decoder.stream.peek_at(0, EXTENDED_XMP_NAMESPACE_PREFIX.len())?
566
157k
            == EXTENDED_XMP_NAMESPACE_PREFIX
567
    {
568
139k
        trace!("Extended XMP Data Present");
569
139k
        decoder.stream.skip(EXTENDED_XMP_NAMESPACE_PREFIX.len())?;
570
139k
        length -= EXTENDED_XMP_NAMESPACE_PREFIX.len();
571
572
139k
        if length < EXTENDED_XMP_HEADER_SIZE {
573
1
            return Err(DecodeErrors::FormatStatic("Too small Extended XMP segment"));
574
139k
        }
575
576
139k
        let header = decoder.stream.peek_at(0, EXTENDED_XMP_HEADER_SIZE)?;
577
139k
        let guid = header[0..EXTENDED_XMP_GUID_SIZE].to_vec();
578
579
139k
        let total_size_start = EXTENDED_XMP_GUID_SIZE;
580
139k
        let total_size_end = total_size_start + EXTENDED_XMP_TOTAL_SIZE_SIZE;
581
139k
        let total_size =
582
139k
            u32::from_be_bytes(header[total_size_start..total_size_end].try_into().unwrap());
583
584
139k
        let offset_start = total_size_end;
585
139k
        let offset_end = offset_start + EXTENDED_XMP_OFFSET_SIZE;
586
139k
        let offset = u32::from_be_bytes(header[offset_start..offset_end].try_into().unwrap());
587
588
139k
        let data = decoder
589
139k
            .stream
590
139k
            .peek_at(EXTENDED_XMP_HEADER_SIZE, length - EXTENDED_XMP_HEADER_SIZE)?
591
139k
            .to_vec();
592
593
139k
        decoder.extended_xmp_segments.push(ExtendedXmpSegment { offset, total_size, guid, data });
594
63.8k
    } else {
595
63.8k
        warn!("Unknown format for APP1 tag, skipping");
596
63.8k
    }
597
598
232k
    decoder.stream.skip(length)?;
599
232k
    Ok(())
600
232k
}
zune_jpeg::headers::parse_app1::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Line
Count
Source
527
25.0k
pub(crate) fn parse_app1<T: ZByteReaderTrait>(
528
25.0k
    decoder: &mut JpegDecoder<T>
529
25.0k
) -> Result<(), DecodeErrors> {
530
    const XMP_NAMESPACE_PREFIX: &[u8] = b"http://ns.adobe.com/xap/1.0/\0";
531
    const EXTENDED_XMP_NAMESPACE_PREFIX: &[u8] = b"http://ns.adobe.com/xmp/extension/\0";
532
    const EXTENDED_XMP_GUID_SIZE: usize = 32;
533
    const EXTENDED_XMP_TOTAL_SIZE_SIZE: usize = 4;
534
    const EXTENDED_XMP_OFFSET_SIZE: usize = 4;
535
    const EXTENDED_XMP_HEADER_SIZE: usize =
536
        EXTENDED_XMP_GUID_SIZE + EXTENDED_XMP_TOTAL_SIZE_SIZE + EXTENDED_XMP_OFFSET_SIZE;
537
538
    // contains exif data
539
25.0k
    let mut length = usize::from(decoder.stream.get_u16_be());
540
541
25.0k
    if length < 2 {
542
1
        return Err(DecodeErrors::FormatStatic("Too small app1 length"));
543
25.0k
    }
544
    // length bytes
545
25.0k
    length -= 2;
546
547
25.0k
    if length > 6 && decoder.stream.peek_at(0, 6)? == b"Exif\x00\x00" {
548
489
        trace!("Exif segment present");
549
        // skip bytes we read above
550
489
        decoder.stream.skip(6)?;
551
489
        length -= 6;
552
553
489
        let exif_bytes = decoder.stream.peek_at(0, length)?.to_vec();
554
555
488
        decoder.info.exif_data = Some(exif_bytes);
556
24.5k
    } else if length > XMP_NAMESPACE_PREFIX.len()
557
23.0k
        && decoder.stream.peek_at(0, XMP_NAMESPACE_PREFIX.len())? == XMP_NAMESPACE_PREFIX
558
    {
559
333
        trace!("XMP Data Present");
560
333
        decoder.stream.skip(XMP_NAMESPACE_PREFIX.len())?;
561
333
        length -= XMP_NAMESPACE_PREFIX.len();
562
333
        let xmp_data = decoder.stream.peek_at(0, length)?.to_vec();
563
329
        decoder.info.xmp_data = Some(xmp_data);
564
24.1k
    } else if length > EXTENDED_XMP_NAMESPACE_PREFIX.len()
565
22.3k
        && decoder.stream.peek_at(0, EXTENDED_XMP_NAMESPACE_PREFIX.len())?
566
22.3k
            == EXTENDED_XMP_NAMESPACE_PREFIX
567
    {
568
17.5k
        trace!("Extended XMP Data Present");
569
17.5k
        decoder.stream.skip(EXTENDED_XMP_NAMESPACE_PREFIX.len())?;
570
17.5k
        length -= EXTENDED_XMP_NAMESPACE_PREFIX.len();
571
572
17.5k
        if length < EXTENDED_XMP_HEADER_SIZE {
573
1
            return Err(DecodeErrors::FormatStatic("Too small Extended XMP segment"));
574
17.5k
        }
575
576
17.5k
        let header = decoder.stream.peek_at(0, EXTENDED_XMP_HEADER_SIZE)?;
577
17.5k
        let guid = header[0..EXTENDED_XMP_GUID_SIZE].to_vec();
578
579
17.5k
        let total_size_start = EXTENDED_XMP_GUID_SIZE;
580
17.5k
        let total_size_end = total_size_start + EXTENDED_XMP_TOTAL_SIZE_SIZE;
581
17.5k
        let total_size =
582
17.5k
            u32::from_be_bytes(header[total_size_start..total_size_end].try_into().unwrap());
583
584
17.5k
        let offset_start = total_size_end;
585
17.5k
        let offset_end = offset_start + EXTENDED_XMP_OFFSET_SIZE;
586
17.5k
        let offset = u32::from_be_bytes(header[offset_start..offset_end].try_into().unwrap());
587
588
17.5k
        let data = decoder
589
17.5k
            .stream
590
17.5k
            .peek_at(EXTENDED_XMP_HEADER_SIZE, length - EXTENDED_XMP_HEADER_SIZE)?
591
17.5k
            .to_vec();
592
593
17.5k
        decoder.extended_xmp_segments.push(ExtendedXmpSegment { offset, total_size, guid, data });
594
6.59k
    } else {
595
6.59k
        warn!("Unknown format for APP1 tag, skipping");
596
6.59k
    }
597
598
24.9k
    decoder.stream.skip(length)?;
599
24.9k
    Ok(())
600
25.0k
}
Unexecuted instantiation: zune_jpeg::headers::parse_app1::<_>
601
602
324k
pub(crate) fn parse_app2<T: ZByteReaderTrait>(
603
324k
    decoder: &mut JpegDecoder<T>
604
324k
) -> Result<(), DecodeErrors> {
605
    static HDR_META: &[u8] = b"urn:iso:std:iso:ts:21496:-1\0";
606
    static MPF_DATA: &[u8] = b"MPF\0";
607
608
324k
    let mut length = usize::from(decoder.stream.get_u16_be());
609
610
324k
    if length < 2 {
611
46
        return Err(DecodeErrors::FormatStatic("Too small app2 segment"));
612
324k
    }
613
    // length bytes
614
324k
    length -= 2;
615
616
324k
    if length > 14 && decoder.stream.peek_at(0, 12)? == *b"ICC_PROFILE\0" {
617
226k
        trace!("ICC Profile present");
618
        // skip 12 bytes which indicate ICC profile
619
226k
        length -= 12;
620
226k
        decoder.stream.skip(12)?;
621
226k
        let seq_no = decoder.stream.read_u8();
622
226k
        let num_markers = decoder.stream.read_u8();
623
        // deduct the two bytes we read above
624
226k
        length -= 2;
625
626
226k
        let data = decoder.stream.peek_at(0, length)?.to_vec();
627
628
226k
        let icc_chunk = ICCChunk {
629
226k
            seq_no,
630
226k
            num_markers,
631
226k
            data
632
226k
        };
633
226k
        decoder.icc_data.push(icc_chunk);
634
98.3k
    } else if length > HDR_META.len() && decoder.stream.peek_at(0, HDR_META.len())? == HDR_META {
635
15.0k
        length = length.saturating_sub(HDR_META.len());
636
15.0k
        decoder.stream.skip(HDR_META.len())?;
637
15.0k
        trace!("Gain Map metadata found");
638
11.6k
        match length {
639
3.44k
            4 => {
640
3.44k
                // If gain map metadata length == 4 then here it variables
641
3.44k
                // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/src/jpegr.cpp#L1076C1-L1077C35
642
3.44k
                // 2 bytes minimum_version: (00 00)
643
3.44k
                // 2 bytes writer_version: (00 00)
644
3.44k
                // Perhaps nothing to do with it ?
645
3.44k
                let _ = decoder.stream.get_u16_be();
646
3.44k
                let _ = decoder.stream.get_u16_be();
647
3.44k
                length -= 4;
648
3.44k
                decoder
649
3.44k
                    .info
650
3.44k
                    .gain_map_info
651
3.44k
                    .push(GainMapInfo { data: Vec::new() });
652
3.44k
            }
653
11.6k
            n if n > 4 => {
654
                // If there is perhaps useful gain map info
655
                // we'll read this until end
656
                // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/src/jpegr.cpp#L1323
657
8.12k
                let data = decoder.stream.peek_at(0, length)?.to_vec();
658
8.09k
                length -= data.len();
659
8.09k
                decoder.stream.skip(data.len())?;
660
661
8.09k
                decoder.info.gain_map_info.push(GainMapInfo { data });
662
            }
663
3.50k
            _ => {}
664
        }
665
83.2k
    } else if length > MPF_DATA.len() && decoder.stream.peek_at(0, MPF_DATA.len())? == MPF_DATA {
666
29.0k
        trace!("MPF Signature present");
667
29.0k
        length = length.saturating_sub(MPF_DATA.len());
668
29.0k
        decoder.stream.skip(MPF_DATA.len())?;
669
29.0k
        decoder.info.multi_picture_information_offset = Some(decoder.stream.position()?);
670
        // MPF signature taken from here
671
        // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/include/ultrahdr/multipictureformat.h#L50
672
        // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/src/multipictureformat.cpp#L36
673
        // More info https://www.cipa.jp/std/documents/e/DC-X007-KEY_E.pdf
674
29.0k
        let data = decoder.stream.peek_at(0, length)?.to_vec();
675
29.0k
        length -= data.len();
676
29.0k
        decoder.stream.skip(data.len())?;
677
29.0k
        decoder.info.multi_picture_information = Some(data);
678
54.0k
    }
679
680
324k
    decoder.stream.skip(length)?;
681
682
324k
    Ok(())
683
324k
}
zune_jpeg::headers::parse_app2::<std::io::cursor::Cursor<&[u8]>>
Line
Count
Source
602
313k
pub(crate) fn parse_app2<T: ZByteReaderTrait>(
603
313k
    decoder: &mut JpegDecoder<T>
604
313k
) -> Result<(), DecodeErrors> {
605
    static HDR_META: &[u8] = b"urn:iso:std:iso:ts:21496:-1\0";
606
    static MPF_DATA: &[u8] = b"MPF\0";
607
608
313k
    let mut length = usize::from(decoder.stream.get_u16_be());
609
610
313k
    if length < 2 {
611
40
        return Err(DecodeErrors::FormatStatic("Too small app2 segment"));
612
313k
    }
613
    // length bytes
614
313k
    length -= 2;
615
616
313k
    if length > 14 && decoder.stream.peek_at(0, 12)? == *b"ICC_PROFILE\0" {
617
220k
        trace!("ICC Profile present");
618
        // skip 12 bytes which indicate ICC profile
619
220k
        length -= 12;
620
220k
        decoder.stream.skip(12)?;
621
220k
        let seq_no = decoder.stream.read_u8();
622
220k
        let num_markers = decoder.stream.read_u8();
623
        // deduct the two bytes we read above
624
220k
        length -= 2;
625
626
220k
        let data = decoder.stream.peek_at(0, length)?.to_vec();
627
628
220k
        let icc_chunk = ICCChunk {
629
220k
            seq_no,
630
220k
            num_markers,
631
220k
            data
632
220k
        };
633
220k
        decoder.icc_data.push(icc_chunk);
634
92.7k
    } else if length > HDR_META.len() && decoder.stream.peek_at(0, HDR_META.len())? == HDR_META {
635
14.2k
        length = length.saturating_sub(HDR_META.len());
636
14.2k
        decoder.stream.skip(HDR_META.len())?;
637
14.2k
        trace!("Gain Map metadata found");
638
10.8k
        match length {
639
3.43k
            4 => {
640
3.43k
                // If gain map metadata length == 4 then here it variables
641
3.43k
                // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/src/jpegr.cpp#L1076C1-L1077C35
642
3.43k
                // 2 bytes minimum_version: (00 00)
643
3.43k
                // 2 bytes writer_version: (00 00)
644
3.43k
                // Perhaps nothing to do with it ?
645
3.43k
                let _ = decoder.stream.get_u16_be();
646
3.43k
                let _ = decoder.stream.get_u16_be();
647
3.43k
                length -= 4;
648
3.43k
                decoder
649
3.43k
                    .info
650
3.43k
                    .gain_map_info
651
3.43k
                    .push(GainMapInfo { data: Vec::new() });
652
3.43k
            }
653
10.8k
            n if n > 4 => {
654
                // If there is perhaps useful gain map info
655
                // we'll read this until end
656
                // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/src/jpegr.cpp#L1323
657
7.72k
                let data = decoder.stream.peek_at(0, length)?.to_vec();
658
7.70k
                length -= data.len();
659
7.70k
                decoder.stream.skip(data.len())?;
660
661
7.70k
                decoder.info.gain_map_info.push(GainMapInfo { data });
662
            }
663
3.13k
            _ => {}
664
        }
665
78.3k
    } else if length > MPF_DATA.len() && decoder.stream.peek_at(0, MPF_DATA.len())? == MPF_DATA {
666
27.7k
        trace!("MPF Signature present");
667
27.7k
        length = length.saturating_sub(MPF_DATA.len());
668
27.7k
        decoder.stream.skip(MPF_DATA.len())?;
669
27.7k
        decoder.info.multi_picture_information_offset = Some(decoder.stream.position()?);
670
        // MPF signature taken from here
671
        // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/include/ultrahdr/multipictureformat.h#L50
672
        // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/src/multipictureformat.cpp#L36
673
        // More info https://www.cipa.jp/std/documents/e/DC-X007-KEY_E.pdf
674
27.7k
        let data = decoder.stream.peek_at(0, length)?.to_vec();
675
27.7k
        length -= data.len();
676
27.7k
        decoder.stream.skip(data.len())?;
677
27.7k
        decoder.info.multi_picture_information = Some(data);
678
50.6k
    }
679
680
313k
    decoder.stream.skip(length)?;
681
682
313k
    Ok(())
683
313k
}
zune_jpeg::headers::parse_app2::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Line
Count
Source
602
11.1k
pub(crate) fn parse_app2<T: ZByteReaderTrait>(
603
11.1k
    decoder: &mut JpegDecoder<T>
604
11.1k
) -> Result<(), DecodeErrors> {
605
    static HDR_META: &[u8] = b"urn:iso:std:iso:ts:21496:-1\0";
606
    static MPF_DATA: &[u8] = b"MPF\0";
607
608
11.1k
    let mut length = usize::from(decoder.stream.get_u16_be());
609
610
11.1k
    if length < 2 {
611
6
        return Err(DecodeErrors::FormatStatic("Too small app2 segment"));
612
11.1k
    }
613
    // length bytes
614
11.1k
    length -= 2;
615
616
11.1k
    if length > 14 && decoder.stream.peek_at(0, 12)? == *b"ICC_PROFILE\0" {
617
5.49k
        trace!("ICC Profile present");
618
        // skip 12 bytes which indicate ICC profile
619
5.49k
        length -= 12;
620
5.49k
        decoder.stream.skip(12)?;
621
5.49k
        let seq_no = decoder.stream.read_u8();
622
5.49k
        let num_markers = decoder.stream.read_u8();
623
        // deduct the two bytes we read above
624
5.49k
        length -= 2;
625
626
5.49k
        let data = decoder.stream.peek_at(0, length)?.to_vec();
627
628
5.49k
        let icc_chunk = ICCChunk {
629
5.49k
            seq_no,
630
5.49k
            num_markers,
631
5.49k
            data
632
5.49k
        };
633
5.49k
        decoder.icc_data.push(icc_chunk);
634
5.59k
    } else if length > HDR_META.len() && decoder.stream.peek_at(0, HDR_META.len())? == HDR_META {
635
779
        length = length.saturating_sub(HDR_META.len());
636
779
        decoder.stream.skip(HDR_META.len())?;
637
779
        trace!("Gain Map metadata found");
638
769
        match length {
639
10
            4 => {
640
10
                // If gain map metadata length == 4 then here it variables
641
10
                // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/src/jpegr.cpp#L1076C1-L1077C35
642
10
                // 2 bytes minimum_version: (00 00)
643
10
                // 2 bytes writer_version: (00 00)
644
10
                // Perhaps nothing to do with it ?
645
10
                let _ = decoder.stream.get_u16_be();
646
10
                let _ = decoder.stream.get_u16_be();
647
10
                length -= 4;
648
10
                decoder
649
10
                    .info
650
10
                    .gain_map_info
651
10
                    .push(GainMapInfo { data: Vec::new() });
652
10
            }
653
769
            n if n > 4 => {
654
                // If there is perhaps useful gain map info
655
                // we'll read this until end
656
                // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/src/jpegr.cpp#L1323
657
399
                let data = decoder.stream.peek_at(0, length)?.to_vec();
658
391
                length -= data.len();
659
391
                decoder.stream.skip(data.len())?;
660
661
391
                decoder.info.gain_map_info.push(GainMapInfo { data });
662
            }
663
370
            _ => {}
664
        }
665
4.80k
    } else if length > MPF_DATA.len() && decoder.stream.peek_at(0, MPF_DATA.len())? == MPF_DATA {
666
1.33k
        trace!("MPF Signature present");
667
1.33k
        length = length.saturating_sub(MPF_DATA.len());
668
1.33k
        decoder.stream.skip(MPF_DATA.len())?;
669
1.33k
        decoder.info.multi_picture_information_offset = Some(decoder.stream.position()?);
670
        // MPF signature taken from here
671
        // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/include/ultrahdr/multipictureformat.h#L50
672
        // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/src/multipictureformat.cpp#L36
673
        // More info https://www.cipa.jp/std/documents/e/DC-X007-KEY_E.pdf
674
1.33k
        let data = decoder.stream.peek_at(0, length)?.to_vec();
675
1.33k
        length -= data.len();
676
1.33k
        decoder.stream.skip(data.len())?;
677
1.33k
        decoder.info.multi_picture_information = Some(data);
678
3.46k
    }
679
680
11.0k
    decoder.stream.skip(length)?;
681
682
11.0k
    Ok(())
683
11.1k
}
Unexecuted instantiation: zune_jpeg::headers::parse_app2::<_>
684
685
/// Small utility function to print Un-zig-zagged quantization tables
686
687
59.2k
fn un_zig_zag<T>(a: &[T]) -> [i32; 64]
688
59.2k
where
689
59.2k
    T: Default + Copy,
690
59.2k
    i32: core::convert::From<T>
691
{
692
59.2k
    let mut output = [i32::default(); 64];
693
694
3.85M
    for i in 0..64 {
695
3.79M
        output[UN_ZIGZAG[i]] = i32::from(a[i]);
696
3.79M
    }
697
698
59.2k
    output
699
59.2k
}
zune_jpeg::headers::un_zig_zag::<u8>
Line
Count
Source
687
56.8k
fn un_zig_zag<T>(a: &[T]) -> [i32; 64]
688
56.8k
where
689
56.8k
    T: Default + Copy,
690
56.8k
    i32: core::convert::From<T>
691
{
692
56.8k
    let mut output = [i32::default(); 64];
693
694
3.69M
    for i in 0..64 {
695
3.64M
        output[UN_ZIGZAG[i]] = i32::from(a[i]);
696
3.64M
    }
697
698
56.8k
    output
699
56.8k
}
zune_jpeg::headers::un_zig_zag::<u16>
Line
Count
Source
687
2.38k
fn un_zig_zag<T>(a: &[T]) -> [i32; 64]
688
2.38k
where
689
2.38k
    T: Default + Copy,
690
2.38k
    i32: core::convert::From<T>
691
{
692
2.38k
    let mut output = [i32::default(); 64];
693
694
155k
    for i in 0..64 {
695
152k
        output[UN_ZIGZAG[i]] = i32::from(a[i]);
696
152k
    }
697
698
2.38k
    output
699
2.38k
}
Unexecuted instantiation: zune_jpeg::headers::un_zig_zag::<_>