Coverage Report

Created: 2026-08-14 08:22

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
0
pub(crate) fn parse_huffman<T: ZByteReaderTrait>(
32
0
    decoder: &mut JpegDecoder<T>
33
0
) -> Result<(), DecodeErrors>
34
0
where
35
{
36
    // Read the length of the Huffman table
37
0
    let mut dht_length = i32::from(decoder.stream.get_u16_be_err()?.checked_sub(2).ok_or(
38
0
        DecodeErrors::FormatStatic("Invalid Huffman length in image")
39
0
    )?);
40
41
0
    while dht_length > 16 {
42
        // HT information
43
0
        let ht_info = decoder.stream.read_u8_err()?;
44
        // third bit indicates whether the huffman encoding is DC or AC type
45
0
        let dc_or_ac = (ht_info >> 4) & 0xF;
46
        // Indicate the position of this table, should be less than 4;
47
0
        let index = (ht_info & 0xF) as usize;
48
        // read the number of symbols
49
0
        let mut num_symbols: [u8; 17] = [0; 17];
50
51
0
        if index >= MAX_COMPONENTS {
52
0
            return Err(DecodeErrors::HuffmanDecode(format!(
53
0
                "Invalid DHT index {index}, expected between 0 and 3"
54
0
            )));
55
0
        }
56
57
0
        if dc_or_ac > 1 {
58
0
            return Err(DecodeErrors::HuffmanDecode(format!(
59
0
                "Invalid DHT position {dc_or_ac}, should be 0 or 1"
60
0
            )));
61
0
        }
62
63
0
        decoder.stream.read_exact_bytes(&mut num_symbols[1..17])?;
64
65
0
        dht_length -= 1 + 16;
66
67
0
        let symbols_sum: i32 = num_symbols.iter().map(|f| i32::from(*f)).sum();
Unexecuted instantiation: zune_jpeg::headers::parse_huffman::<std::io::cursor::Cursor<&[u8]>>::{closure#0}
Unexecuted instantiation: zune_jpeg::headers::parse_huffman::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>::{closure#0}
Unexecuted instantiation: zune_jpeg::headers::parse_huffman::<_>::{closure#0}
68
69
        // The sum of the number of symbols cannot be greater than 256;
70
0
        if symbols_sum > 256 {
71
0
            return Err(DecodeErrors::FormatStatic(
72
0
                "Encountered Huffman table with excessive length in DHT"
73
0
            ));
74
0
        }
75
0
        if symbols_sum > dht_length {
76
0
            return Err(DecodeErrors::HuffmanDecode(format!(
77
0
                "Excessive Huffman table of length {symbols_sum} found when header length is {dht_length}"
78
0
            )));
79
0
        }
80
0
        dht_length -= symbols_sum;
81
        // A table containing symbols in increasing code length
82
0
        let mut symbols = [0; 256];
83
84
0
        decoder
85
0
            .stream
86
0
            .read_exact_bytes(&mut symbols[0..(symbols_sum as usize)])?;
87
        // store
88
0
        match dc_or_ac {
89
            0 => {
90
0
                decoder.dc_huffman_tables[index] = Some(HuffmanTable::new(
91
0
                    &num_symbols,
92
0
                    symbols,
93
                    true,
94
0
                    decoder.is_progressive
95
0
                )?);
96
            }
97
            _ => {
98
0
                decoder.ac_huffman_tables[index] = Some(HuffmanTable::new(
99
0
                    &num_symbols,
100
0
                    symbols,
101
                    false,
102
0
                    decoder.is_progressive
103
0
                )?);
104
            }
105
        }
106
    }
107
108
0
    if dht_length > 0 {
109
0
        return Err(DecodeErrors::FormatStatic("Bogus Huffman table definition"));
110
0
    }
111
112
0
    Ok(())
113
0
}
Unexecuted instantiation: zune_jpeg::headers::parse_huffman::<std::io::cursor::Cursor<&[u8]>>
Unexecuted instantiation: zune_jpeg::headers::parse_huffman::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
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
0
pub(crate) fn parse_dqt<T: ZByteReaderTrait>(img: &mut JpegDecoder<T>) -> Result<(), DecodeErrors> {
118
    // read length
119
0
    let mut qt_length =
120
0
        img.stream
121
0
            .get_u16_be_err()?
122
0
            .checked_sub(2)
123
0
            .ok_or(DecodeErrors::FormatStatic(
124
0
                "Invalid DQT length. Length should be greater than 2"
125
0
            ))?;
126
    // A single DQT header may have multiple QT's
127
0
    while qt_length > 0 {
128
0
        let qt_info = img.stream.read_u8_err()?;
129
        // 0 = 8 bit otherwise 16 bit dqt
130
0
        let precision = (qt_info >> 4) as usize;
131
        // last 4 bits give us position
132
0
        let table_position = (qt_info & 0x0f) as usize;
133
0
        let precision_value = 64 * (precision + 1);
134
135
0
        if (precision_value + 1) as u16 > qt_length {
136
0
            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
0
        }
138
139
0
        let dct_table = match precision {
140
            0 => {
141
0
                let mut qt_values = [0; 64];
142
143
0
                img.stream.read_exact_bytes(&mut qt_values)?;
144
145
0
                qt_length -= (precision_value as u16) + 1 /*QT BIT*/;
146
                // carry out un zig-zag here
147
0
                un_zig_zag(&qt_values)
148
            }
149
            1 => {
150
                // 16 bit quantization tables
151
0
                let mut qt_values = [0_u16; 64];
152
153
0
                for i in 0..64 {
154
0
                    qt_values[i] = img.stream.get_u16_be_err()?;
155
                }
156
0
                qt_length -= (precision_value as u16) + 1;
157
158
0
                un_zig_zag(&qt_values)
159
            }
160
            _ => {
161
0
                return Err(DecodeErrors::DqtError(format!(
162
0
                    "Expected QT precision value of either 0 or 1, found {precision:?}"
163
0
                )));
164
            }
165
        };
166
167
0
        if table_position >= MAX_COMPONENTS {
168
0
            return Err(DecodeErrors::DqtError(format!(
169
0
                "Too large table position for QT :{table_position}, expected between 0 and 3"
170
0
            )));
171
0
        }
172
173
0
        trace!("Assigning qt table {table_position} with precision {precision}");
174
0
        img.qt_tables[table_position] = Some(dct_table);
175
    }
176
177
0
    return Ok(());
178
0
}
Unexecuted instantiation: zune_jpeg::headers::parse_dqt::<std::io::cursor::Cursor<&[u8]>>
Unexecuted instantiation: zune_jpeg::headers::parse_dqt::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Unexecuted instantiation: zune_jpeg::headers::parse_dqt::<_>
179
180
/// Section:`B.2.2 Frame header syntax`
181
182
0
pub(crate) fn parse_start_of_frame<T: ZByteReaderTrait>(
183
0
    sof: SOFMarkers, img: &mut JpegDecoder<T>
184
0
) -> Result<(), DecodeErrors> {
185
0
    if img.seen_sof {
186
0
        return Err(DecodeErrors::SofError(
187
0
            "Two Start of Frame Markers".to_string()
188
0
        ));
189
0
    }
190
    // Get length of the frame header
191
0
    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
0
    let dt_precision = img.stream.read_u8_err()?;
195
196
0
    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
0
    }
201
202
0
    img.info.set_density(dt_precision);
203
204
    // read  and set the image height.
205
0
    let img_height = img.stream.get_u16_be_err()?;
206
0
    img.info.set_height(img_height);
207
208
    // read and set the image width
209
0
    let img_width = img.stream.get_u16_be_err()?;
210
0
    img.info.set_width(img_width);
211
212
0
    trace!("Image width  :{}", img_width);
213
0
    trace!("Image height :{}", img_height);
214
215
0
    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
0
    }
218
219
0
    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
0
    }
222
223
    // Check image width or height is zero
224
0
    if img_width == 0 || img_height == 0 {
225
0
        return Err(DecodeErrors::ZeroError);
226
0
    }
227
228
    // Number of components for the image.
229
0
    let num_components = img.stream.read_u8_err()?;
230
231
0
    if num_components == 0 {
232
0
        return Err(DecodeErrors::SofError(
233
0
            "Number of components cannot be zero.".to_string()
234
0
        ));
235
0
    }
236
237
0
    let expected = 8 + 3 * u16::from(num_components);
238
    // length should be equal to num components
239
0
    if length != expected {
240
0
        return Err(DecodeErrors::SofError(format!(
241
0
            "Length of start of frame differs from expected {expected},value is {length}"
242
0
        )));
243
0
    }
244
245
0
    trace!("Image components : {}", num_components);
246
247
0
    if num_components == 1 {
248
0
        // SOF sets the number of image components
249
0
        // and that to us translates to setting input and output
250
0
        // colorspaces to zero
251
0
        img.input_colorspace = ColorSpace::Luma;
252
0
        //img.options = img.options.jpeg_set_out_colorspace(ColorSpace::Luma);
253
0
        debug!("Overriding default colorspace set to Luma");
254
0
    }
255
0
    if num_components == 4 && img.input_colorspace == ColorSpace::YCbCr {
256
0
        trace!("Input image has 4 components, defaulting to CMYK colorspace");
257
0
        // https://entropymine.wordpress.com/2018/10/22/how-is-a-jpeg-images-color-type-determined/
258
0
        img.input_colorspace = ColorSpace::CMYK;
259
0
    }
260
261
    // set number of components
262
0
    img.info.components = num_components;
263
264
0
    let mut components = Vec::with_capacity(num_components as usize);
265
0
    let mut temp = [0; 3];
266
267
0
    for pos in 0..num_components {
268
        // read 3 bytes for each component
269
0
        img.stream.read_exact_bytes(&mut temp)?;
270
271
        // create a component.
272
0
        let component = Components::from(temp, pos)?;
273
274
0
        components.push(component);
275
    }
276
0
    img.seen_sof = true;
277
278
0
    img.info.set_sof_marker(sof);
279
280
0
    img.components = components;
281
282
0
    let mut h_max = 1;
283
0
    let mut v_max = 1;
284
285
0
    for comp in &img.components {
286
0
        h_max = max(h_max, comp.horizontal_sample);
287
0
        v_max = max(v_max, comp.vertical_sample);
288
0
    }
289
290
0
    img.info.sample_ratio = match (h_max, v_max) {
291
0
        (1, 1) => SampleRatios::None,
292
0
        (1, 2) => SampleRatios::V,
293
0
        (2, 1) => SampleRatios::H,
294
0
        (2, 2) => SampleRatios::HV,
295
0
        (hs, vs) => SampleRatios::Generic(hs, vs)
296
    };
297
298
0
    Ok(())
299
0
}
Unexecuted instantiation: zune_jpeg::headers::parse_start_of_frame::<std::io::cursor::Cursor<&[u8]>>
Unexecuted instantiation: zune_jpeg::headers::parse_start_of_frame::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Unexecuted instantiation: zune_jpeg::headers::parse_start_of_frame::<_>
300
301
/// Parse a start of scan data
302
0
pub(crate) fn parse_sos<T: ZByteReaderTrait>(
303
0
    image: &mut JpegDecoder<T>
304
0
) -> Result<(), DecodeErrors> {
305
    // Scan header length
306
0
    let ls = usize::from(image.stream.get_u16_be_err()?);
307
    // Number of image components in scan
308
0
    let ns = image.stream.read_u8_err()?;
309
310
0
    let mut seen: [_; 5] = [-1; { MAX_COMPONENTS + 1 }];
311
312
0
    image.num_scans = ns;
313
0
    let smallest_size = 6 + 2 * usize::from(ns);
314
315
0
    if ls != smallest_size {
316
0
        return Err(DecodeErrors::SosError(format!(
317
0
            "Bad SOS length {ls},corrupt jpeg"
318
0
        )));
319
0
    }
320
321
    // Check number of components.
322
0
    if !(1..5).contains(&ns) {
323
0
        return Err(DecodeErrors::SosError(format!(
324
0
            "Invalid number of components in start of scan {ns}, expected in range 1..5"
325
0
        )));
326
0
    }
327
328
0
    if image.info.components == 0 {
329
0
        return Err(DecodeErrors::FormatStatic(
330
0
            "Error decoding SOF Marker, Number of components cannot be zero."
331
0
        ));
332
0
    }
333
334
    // consume spec parameters
335
0
    image.scan_subsampled = false;
336
337
0
    for i in 0..ns {
338
0
        let id = image.stream.read_u8_err()?;
339
340
0
        if seen.contains(&i32::from(id)) {
341
0
            return Err(DecodeErrors::SofError(format!(
342
0
                "Duplicate ID {id} seen twice in the same component"
343
0
            )));
344
0
        }
345
346
0
        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
0
        let y = image.stream.read_u8_err()?;
351
352
0
        let mut j = 0;
353
354
0
        while j < image.info.components {
355
0
            if image.components[j as usize].id == id {
356
0
                break;
357
0
            }
358
359
0
            j += 1;
360
        }
361
362
0
        if j == image.info.components {
363
0
            return Err(DecodeErrors::SofError(format!(
364
0
                "Invalid component id {}, expected one one of {:?}",
365
                id,
366
0
                image.components.iter().map(|c| c.id).collect::<Vec<_>>()
367
            )));
368
0
        }
369
370
0
        let component = &mut image.components[usize::from(j)];
371
0
        component.dc_huff_table = usize::from((y >> 4) & 0xF);
372
0
        component.ac_huff_table = usize::from(y & 0xF);
373
0
        image.z_order[i as usize] = j as usize;
374
375
0
        if component.vertical_sample != 1 || component.horizontal_sample != 1 {
376
0
            image.scan_subsampled = true;
377
0
        }
378
379
0
        trace!(
380
0
            "Assigned huffman tables {}/{} to component {j}, id={}",
381
0
            image.components[usize::from(j)].dc_huff_table,
382
0
            image.components[usize::from(j)].ac_huff_table,
383
0
            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
0
    image.spec_start = image.stream.read_u8_err()?;
398
    // End of spectral selection
399
0
    image.spec_end = image.stream.read_u8_err()?;
400
401
0
    let bit_approx = image.stream.read_u8_err()?;
402
    // successive approximation bit position high
403
0
    image.succ_high = bit_approx >> 4;
404
405
0
    if image.spec_end > 63 {
406
0
        return Err(DecodeErrors::SosError(format!(
407
0
            "Invalid Se parameter {}, range should be 0-63",
408
0
            image.spec_end
409
0
        )));
410
0
    }
411
0
    if image.spec_start > 63 {
412
0
        return Err(DecodeErrors::SosError(format!(
413
0
            "Invalid Ss parameter {}, range should be 0-63",
414
0
            image.spec_start
415
0
        )));
416
0
    }
417
0
    if image.succ_high > 13 {
418
0
        return Err(DecodeErrors::SosError(format!(
419
0
            "Invalid Ah parameter {}, range should be 0-13",
420
0
            image.succ_low
421
0
        )));
422
0
    }
423
    // successive approximation bit position low
424
0
    image.succ_low = bit_approx & 0xF;
425
426
0
    if image.succ_low > 13 {
427
0
        return Err(DecodeErrors::SosError(format!(
428
0
            "Invalid Al parameter {}, range should be 0-13",
429
0
            image.succ_low
430
0
        )));
431
0
    }
432
    // skip any bytes not read
433
0
    image.stream.skip(smallest_size.saturating_sub(ls))?;
434
435
0
    trace!(
436
0
        "Ss={}, Se={} Ah={} Al={}",
437
        image.spec_start,
438
        image.spec_end,
439
        image.succ_high,
440
        image.succ_low
441
    );
442
443
0
    Ok(())
444
0
}
Unexecuted instantiation: zune_jpeg::headers::parse_sos::<std::io::cursor::Cursor<&[u8]>>
Unexecuted instantiation: zune_jpeg::headers::parse_sos::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Unexecuted instantiation: zune_jpeg::headers::parse_sos::<_>
445
446
/// Parse the APP13 (IPTC) segment.
447
0
pub(crate) fn parse_app13<T: ZByteReaderTrait>(
448
0
    decoder: &mut JpegDecoder<T>
449
0
) -> Result<(), DecodeErrors> {
450
    const IPTC_PREFIX: &[u8] = b"Photoshop 3.0\0";
451
    // skip length.
452
0
    let mut length = usize::from(decoder.stream.get_u16_be());
453
454
0
    if length < 2 {
455
0
        return Err(DecodeErrors::FormatStatic("Too small APP13 length"));
456
0
    }
457
    // length bytes.
458
0
    length -= 2;
459
460
0
    if length > IPTC_PREFIX.len() && decoder.stream.peek_at(0, IPTC_PREFIX.len())? == IPTC_PREFIX {
461
        // skip bytes we read above.
462
0
        decoder.stream.skip(IPTC_PREFIX.len())?;
463
0
        length -= IPTC_PREFIX.len();
464
465
0
        let iptc_bytes = decoder.stream.peek_at(0, length)?.to_vec();
466
467
0
        decoder.info.iptc_data = Some(iptc_bytes);
468
0
    }
469
470
0
    decoder.stream.skip(length)?;
471
0
    Ok(())
472
0
}
Unexecuted instantiation: zune_jpeg::headers::parse_app13::<std::io::cursor::Cursor<&[u8]>>
Unexecuted instantiation: zune_jpeg::headers::parse_app13::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Unexecuted instantiation: zune_jpeg::headers::parse_app13::<_>
473
474
/// Parse Adobe App14 segment
475
0
pub(crate) fn parse_app14<T: ZByteReaderTrait>(
476
0
    decoder: &mut JpegDecoder<T>
477
0
) -> Result<(), DecodeErrors> {
478
    // skip length
479
0
    let mut length = usize::from(decoder.stream.get_u16_be());
480
481
0
    if length < 2 {
482
0
        return Err(DecodeErrors::FormatStatic("Too small APP14 length"));
483
0
    }
484
485
0
    if decoder.stream.peek_at(0, 5)? == b"Adobe" {
486
0
        if length < 14 {
487
0
            return Err(DecodeErrors::FormatStatic(
488
0
                "Too short of a length for App14 segment"
489
0
            ));
490
0
        }
491
        // move stream 6 bytes to remove adobe id
492
0
        decoder.stream.skip(6)?;
493
        // skip version, flags0 and flags1
494
0
        decoder.stream.skip(5)?;
495
        // get color transform
496
0
        let transform = decoder.stream.read_u8();
497
        // https://exiftool.org/TagNames/JPEG.html#Adobe
498
0
        match transform {
499
0
            0 => decoder.input_colorspace = ColorSpace::CMYK,
500
0
            1 => decoder.input_colorspace = ColorSpace::YCbCr,
501
0
            2 => decoder.input_colorspace = ColorSpace::YCCK,
502
            _ => {
503
0
                return Err(DecodeErrors::Format(format!(
504
0
                    "Unknown Adobe colorspace {transform}"
505
0
                )))
506
            }
507
        }
508
        // length   = 2
509
        // adobe id = 6
510
        // version =  5
511
        // transform = 1
512
0
        length = length.saturating_sub(14);
513
0
    } else {
514
0
        warn!("Not a valid Adobe APP14 Segment, skipping {} bytes", length);
515
0
        length = length.saturating_sub(2);
516
0
    }
517
    // skip any proceeding lengths.
518
    // we do not need them
519
0
    decoder.stream.skip(length)?;
520
521
0
    Ok(())
522
0
}
Unexecuted instantiation: zune_jpeg::headers::parse_app14::<std::io::cursor::Cursor<&[u8]>>
Unexecuted instantiation: zune_jpeg::headers::parse_app14::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Unexecuted instantiation: zune_jpeg::headers::parse_app14::<_>
523
524
/// Parse the APP1 segment
525
///
526
/// This contains the exif tag
527
0
pub(crate) fn parse_app1<T: ZByteReaderTrait>(
528
0
    decoder: &mut JpegDecoder<T>
529
0
) -> 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
0
    let mut length = usize::from(decoder.stream.get_u16_be());
540
541
0
    if length < 2 {
542
0
        return Err(DecodeErrors::FormatStatic("Too small app1 length"));
543
0
    }
544
    // length bytes
545
0
    length -= 2;
546
547
0
    if length > 6 && decoder.stream.peek_at(0, 6)? == b"Exif\x00\x00" {
548
0
        trace!("Exif segment present");
549
        // skip bytes we read above
550
0
        decoder.stream.skip(6)?;
551
0
        length -= 6;
552
553
0
        let exif_bytes = decoder.stream.peek_at(0, length)?.to_vec();
554
555
0
        decoder.info.exif_data = Some(exif_bytes);
556
0
    } else if length > XMP_NAMESPACE_PREFIX.len()
557
0
        && decoder.stream.peek_at(0, XMP_NAMESPACE_PREFIX.len())? == XMP_NAMESPACE_PREFIX
558
    {
559
0
        trace!("XMP Data Present");
560
0
        decoder.stream.skip(XMP_NAMESPACE_PREFIX.len())?;
561
0
        length -= XMP_NAMESPACE_PREFIX.len();
562
0
        let xmp_data = decoder.stream.peek_at(0, length)?.to_vec();
563
0
        decoder.info.xmp_data = Some(xmp_data);
564
0
    } else if length > EXTENDED_XMP_NAMESPACE_PREFIX.len()
565
0
        && decoder.stream.peek_at(0, EXTENDED_XMP_NAMESPACE_PREFIX.len())?
566
0
            == EXTENDED_XMP_NAMESPACE_PREFIX
567
    {
568
0
        trace!("Extended XMP Data Present");
569
0
        decoder.stream.skip(EXTENDED_XMP_NAMESPACE_PREFIX.len())?;
570
0
        length -= EXTENDED_XMP_NAMESPACE_PREFIX.len();
571
572
0
        if length < EXTENDED_XMP_HEADER_SIZE {
573
0
            return Err(DecodeErrors::FormatStatic("Too small Extended XMP segment"));
574
0
        }
575
576
0
        let header = decoder.stream.peek_at(0, EXTENDED_XMP_HEADER_SIZE)?;
577
0
        let guid = header[0..EXTENDED_XMP_GUID_SIZE].to_vec();
578
579
0
        let total_size_start = EXTENDED_XMP_GUID_SIZE;
580
0
        let total_size_end = total_size_start + EXTENDED_XMP_TOTAL_SIZE_SIZE;
581
0
        let total_size =
582
0
            u32::from_be_bytes(header[total_size_start..total_size_end].try_into().unwrap());
583
584
0
        let offset_start = total_size_end;
585
0
        let offset_end = offset_start + EXTENDED_XMP_OFFSET_SIZE;
586
0
        let offset = u32::from_be_bytes(header[offset_start..offset_end].try_into().unwrap());
587
588
0
        let data = decoder
589
0
            .stream
590
0
            .peek_at(EXTENDED_XMP_HEADER_SIZE, length - EXTENDED_XMP_HEADER_SIZE)?
591
0
            .to_vec();
592
593
0
        decoder.extended_xmp_segments.push(ExtendedXmpSegment { offset, total_size, guid, data });
594
0
    } else {
595
0
        warn!("Unknown format for APP1 tag, skipping");
596
0
    }
597
598
0
    decoder.stream.skip(length)?;
599
0
    Ok(())
600
0
}
Unexecuted instantiation: zune_jpeg::headers::parse_app1::<std::io::cursor::Cursor<&[u8]>>
Unexecuted instantiation: zune_jpeg::headers::parse_app1::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Unexecuted instantiation: zune_jpeg::headers::parse_app1::<_>
601
602
0
pub(crate) fn parse_app2<T: ZByteReaderTrait>(
603
0
    decoder: &mut JpegDecoder<T>
604
0
) -> 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
0
    let mut length = usize::from(decoder.stream.get_u16_be());
609
610
0
    if length < 2 {
611
0
        return Err(DecodeErrors::FormatStatic("Too small app2 segment"));
612
0
    }
613
    // length bytes
614
0
    length -= 2;
615
616
0
    if length > 14 && decoder.stream.peek_at(0, 12)? == *b"ICC_PROFILE\0" {
617
0
        trace!("ICC Profile present");
618
        // skip 12 bytes which indicate ICC profile
619
0
        length -= 12;
620
0
        decoder.stream.skip(12)?;
621
0
        let seq_no = decoder.stream.read_u8();
622
0
        let num_markers = decoder.stream.read_u8();
623
        // deduct the two bytes we read above
624
0
        length -= 2;
625
626
0
        let data = decoder.stream.peek_at(0, length)?.to_vec();
627
628
0
        let icc_chunk = ICCChunk {
629
0
            seq_no,
630
0
            num_markers,
631
0
            data
632
0
        };
633
0
        decoder.icc_data.push(icc_chunk);
634
0
    } else if length > HDR_META.len() && decoder.stream.peek_at(0, HDR_META.len())? == HDR_META {
635
0
        length = length.saturating_sub(HDR_META.len());
636
0
        decoder.stream.skip(HDR_META.len())?;
637
0
        trace!("Gain Map metadata found");
638
0
        match length {
639
0
            4 => {
640
0
                // If gain map metadata length == 4 then here it variables
641
0
                // https://github.com/google/libultrahdr/blob/bf2aa439eea9ad5da483003fa44182f990f74091/lib/src/jpegr.cpp#L1076C1-L1077C35
642
0
                // 2 bytes minimum_version: (00 00)
643
0
                // 2 bytes writer_version: (00 00)
644
0
                // Perhaps nothing to do with it ?
645
0
                let _ = decoder.stream.get_u16_be();
646
0
                let _ = decoder.stream.get_u16_be();
647
0
                length -= 4;
648
0
                decoder
649
0
                    .info
650
0
                    .gain_map_info
651
0
                    .push(GainMapInfo { data: Vec::new() });
652
0
            }
653
0
            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
0
                let data = decoder.stream.peek_at(0, length)?.to_vec();
658
0
                length -= data.len();
659
0
                decoder.stream.skip(data.len())?;
660
661
0
                decoder.info.gain_map_info.push(GainMapInfo { data });
662
            }
663
0
            _ => {}
664
        }
665
0
    } else if length > MPF_DATA.len() && decoder.stream.peek_at(0, MPF_DATA.len())? == MPF_DATA {
666
0
        trace!("MPF Signature present");
667
0
        length = length.saturating_sub(MPF_DATA.len());
668
0
        decoder.stream.skip(MPF_DATA.len())?;
669
0
        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
0
        let data = decoder.stream.peek_at(0, length)?.to_vec();
675
0
        length -= data.len();
676
0
        decoder.stream.skip(data.len())?;
677
0
        decoder.info.multi_picture_information = Some(data);
678
0
    }
679
680
0
    decoder.stream.skip(length)?;
681
682
0
    Ok(())
683
0
}
Unexecuted instantiation: zune_jpeg::headers::parse_app2::<std::io::cursor::Cursor<&[u8]>>
Unexecuted instantiation: zune_jpeg::headers::parse_app2::<zune_core::bytestream::reader::no_std_readers::ZCursor<alloc::vec::Vec<u8>>>
Unexecuted instantiation: zune_jpeg::headers::parse_app2::<_>
684
685
/// Small utility function to print Un-zig-zagged quantization tables
686
687
0
fn un_zig_zag<T>(a: &[T]) -> [i32; 64]
688
0
where
689
0
    T: Default + Copy,
690
0
    i32: core::convert::From<T>
691
{
692
0
    let mut output = [i32::default(); 64];
693
694
0
    for i in 0..64 {
695
0
        output[UN_ZIGZAG[i]] = i32::from(a[i]);
696
0
    }
697
698
0
    output
699
0
}
Unexecuted instantiation: zune_jpeg::headers::un_zig_zag::<u8>
Unexecuted instantiation: zune_jpeg::headers::un_zig_zag::<u16>
Unexecuted instantiation: zune_jpeg::headers::un_zig_zag::<_>