jpeg_decoder/
parser.rs

1use alloc::borrow::ToOwned;
2use alloc::{format, vec};
3use alloc::vec::Vec;
4use core::ops::{self, Range};
5use std::io::{self, Read};
6use crate::{read_u16_from_be, read_u8};
7use crate::error::{Error, Result, UnsupportedFeature};
8use crate::huffman::{HuffmanTable, HuffmanTableClass};
9use crate::marker::Marker;
10use crate::marker::Marker::*;
11
12#[derive(Clone, Copy, Debug, PartialEq)]
13pub struct Dimensions {
14    pub width: u16,
15    pub height: u16,
16}
17
18#[derive(Clone, Copy, Debug, PartialEq)]
19pub enum EntropyCoding {
20    Huffman,
21    Arithmetic,
22}
23
24/// Represents the coding process of an image.
25#[derive(Clone, Copy, Debug, PartialEq)]
26pub enum CodingProcess {
27    /// Sequential Discrete Cosine Transform
28    DctSequential,
29    /// Progressive Discrete Cosine Transform
30    DctProgressive,
31    /// Lossless
32    Lossless,
33}
34
35// Table H.1
36#[derive(Clone, Copy, Debug, PartialEq)]
37pub enum Predictor {
38    NoPrediction,
39    Ra,
40    Rb,
41    Rc,
42    RaRbRc1, // Ra + Rb - Rc
43    RaRbRc2, // Ra + ((Rb - Rc) >> 1)
44    RaRbRc3, // Rb + ((Ra - Rb) >> 1)
45    RaRb,    // (Ra + Rb)/2
46}
47
48
49#[derive(Clone)]
50pub struct FrameInfo {
51    pub is_baseline: bool,
52    pub is_differential: bool,
53    pub coding_process: CodingProcess,
54    pub entropy_coding: EntropyCoding,
55    pub precision: u8,
56
57    pub image_size: Dimensions,
58    pub output_size: Dimensions,
59    pub mcu_size: Dimensions,
60    pub components: Vec<Component>,
61}
62
63#[derive(Debug)]
64pub struct ScanInfo {
65    pub component_indices: Vec<usize>,
66    pub dc_table_indices: Vec<usize>,
67    pub ac_table_indices: Vec<usize>,
68
69    pub spectral_selection: Range<u8>,
70    pub predictor_selection: Predictor, // for lossless
71    pub successive_approximation_high: u8,
72    pub successive_approximation_low: u8,
73    pub point_transform: u8, // for lossless
74}
75
76#[derive(Clone, Debug)]
77pub struct Component {
78    pub identifier: u8,
79
80    pub horizontal_sampling_factor: u8,
81    pub vertical_sampling_factor: u8,
82
83    pub quantization_table_index: usize,
84
85    pub dct_scale: usize,
86
87    pub size: Dimensions,
88    pub block_size: Dimensions,
89}
90
91#[derive(Debug)]
92pub enum AppData {
93    Adobe(AdobeColorTransform),
94    Jfif,
95    Avi1,
96    Icc(IccChunk),
97    Exif(Vec<u8>),
98    Xmp(Vec<u8>),
99    Psir(Vec<u8>),
100}
101
102// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
103#[allow(clippy::upper_case_acronyms)]
104#[derive(Clone, Copy, Debug, PartialEq)]
105pub enum AdobeColorTransform {
106    // RGB or CMYK
107    Unknown,
108    YCbCr,
109    // YCbCrK
110    YCCK,
111}
112#[derive(Debug)]
113pub struct IccChunk {
114    pub num_markers: u8,
115    pub seq_no: u8,
116    pub data: Vec<u8>,
117}
118
119impl FrameInfo {
120    pub(crate) fn update_idct_size(&mut self, idct_size: usize) -> Result<()> {
121        for component in &mut self.components {
122            component.dct_scale = idct_size;
123        }
124
125        update_component_sizes(self.image_size, &mut self.components)?;
126
127        self.output_size = Dimensions {
128            width: (self.image_size.width as f32 * idct_size as f32 / 8.0).ceil() as u16,
129            height: (self.image_size.height as f32 * idct_size as f32 / 8.0).ceil() as u16
130        };
131
132        Ok(())
133    }
134}
135
136fn read_length<R: Read>(reader: &mut R, marker: Marker) -> Result<usize> {
137    assert!(marker.has_length());
138
139    // length is including itself.
140    let length = usize::from(read_u16_from_be(reader)?);
141
142    if length < 2 {
143        return Err(Error::Format(format!("encountered {:?} with invalid length {}", marker, length)));
144    }
145
146    Ok(length - 2)
147}
148
149fn skip_bytes<R: Read>(reader: &mut R, length: usize) -> Result<()> {
150    let length = length as u64;
151    let to_skip = &mut reader.by_ref().take(length);
152    let copied = io::copy(to_skip, &mut io::sink())?;
153    if copied < length {
154        Err(Error::Io(io::ErrorKind::UnexpectedEof.into()))
155    } else {
156        Ok(())
157    }
158}
159
160// Section B.2.2
161pub fn parse_sof<R: Read>(reader: &mut R, marker: Marker) -> Result<FrameInfo> {
162    let length = read_length(reader, marker)?;
163
164    if length <= 6 {
165        return Err(Error::Format("invalid length in SOF".to_owned()));
166    }
167
168    let is_baseline = marker == SOF(0);
169    let is_differential = match marker {
170        SOF(0 ..= 3) | SOF(9 ..= 11)  => false,
171        SOF(5 ..= 7) | SOF(13 ..= 15) => true,
172        _ => panic!(),
173    };
174    let coding_process = match marker {
175        SOF(0) | SOF(1) | SOF(5) | SOF(9) | SOF(13) => CodingProcess::DctSequential,
176        SOF(2) | SOF(6) | SOF(10) | SOF(14)         => CodingProcess::DctProgressive,
177        SOF(3) | SOF(7) | SOF(11) | SOF(15)         => CodingProcess::Lossless,
178        _ => panic!(),
179    };
180    let entropy_coding = match marker {
181        SOF(0 ..= 3) | SOF(5 ..= 7)     => EntropyCoding::Huffman,
182        SOF(9 ..= 11) | SOF(13 ..= 15)  => EntropyCoding::Arithmetic,
183        _ => panic!(),
184    };
185
186    let precision = read_u8(reader)?;
187
188    match precision {
189        8 => {},
190        12 => {
191            if is_baseline {
192                return Err(Error::Format("12 bit sample precision is not allowed in baseline".to_owned()));
193            }
194        },
195        _ => {
196            if coding_process != CodingProcess::Lossless || precision > 16 {
197                return Err(Error::Format(format!("invalid precision {} in frame header", precision)))
198            }
199        },
200    }
201
202    let height = read_u16_from_be(reader)?;
203    let width = read_u16_from_be(reader)?;
204
205    // height:
206    // "Value 0 indicates that the number of lines shall be defined by the DNL marker and
207    //     parameters at the end of the first scan (see B.2.5)."
208    if height == 0 {
209        return Err(Error::Unsupported(UnsupportedFeature::DNL));
210    }
211
212    if width == 0 {
213        return Err(Error::Format("zero width in frame header".to_owned()));
214    }
215
216    let component_count = read_u8(reader)?;
217
218    if component_count == 0 {
219        return Err(Error::Format("zero component count in frame header".to_owned()));
220    }
221    if coding_process == CodingProcess::DctProgressive && component_count > 4 {
222        return Err(Error::Format("progressive frame with more than 4 components".to_owned()));
223    }
224
225    if length != 6 + 3 * component_count as usize {
226        return Err(Error::Format("invalid length in SOF".to_owned()));
227    }
228
229    let mut components: Vec<Component> = Vec::with_capacity(component_count as usize);
230
231    for _ in 0 .. component_count {
232        let identifier = read_u8(reader)?;
233
234        // Each component's identifier must be unique.
235        if components.iter().any(|c| c.identifier == identifier) {
236            return Err(Error::Format(format!("duplicate frame component identifier {}", identifier)));
237        }
238
239        let byte = read_u8(reader)?;
240        let horizontal_sampling_factor = byte >> 4;
241        let vertical_sampling_factor = byte & 0x0f;
242
243        if horizontal_sampling_factor == 0 || horizontal_sampling_factor > 4 {
244            return Err(Error::Format(format!("invalid horizontal sampling factor {}", horizontal_sampling_factor)));
245        }
246        if vertical_sampling_factor == 0 || vertical_sampling_factor > 4 {
247            return Err(Error::Format(format!("invalid vertical sampling factor {}", vertical_sampling_factor)));
248        }
249
250        let quantization_table_index = read_u8(reader)?;
251
252        if quantization_table_index > 3 || (coding_process == CodingProcess::Lossless && quantization_table_index != 0) {
253            return Err(Error::Format(format!("invalid quantization table index {}", quantization_table_index)));
254        }
255
256        components.push(Component {
257            identifier,
258            horizontal_sampling_factor,
259            vertical_sampling_factor,
260            quantization_table_index: quantization_table_index as usize,
261            dct_scale: 8,
262            size: Dimensions {width: 0, height: 0},
263            block_size: Dimensions {width: 0, height: 0},
264        });
265    }
266
267    let mcu_size = update_component_sizes(Dimensions { width, height }, &mut components)?;
268
269    Ok(FrameInfo {
270        is_baseline,
271        is_differential,
272        coding_process,
273        entropy_coding,
274        precision,
275        image_size: Dimensions { width, height },
276        output_size: Dimensions { width, height },
277        mcu_size,
278        components,
279    })
280}
281
282/// Returns ceil(x/y), requires x>0
283fn ceil_div(x: u32, y: u32) -> Result<u16> {
284    if x == 0 || y == 0 {
285        // TODO Determine how this error is reached. Can we validate input
286        // earlier and error out then?
287        return Err(Error::Format("invalid dimensions".to_owned()));
288    }
289    Ok((1 + ((x - 1) / y)) as u16)
290}
291
292fn update_component_sizes(size: Dimensions, components: &mut [Component]) -> Result<Dimensions> {
293    let h_max = components.iter().map(|c| c.horizontal_sampling_factor).max().unwrap() as u32;
294    let v_max = components.iter().map(|c| c.vertical_sampling_factor).max().unwrap() as u32;
295
296    let mcu_size = Dimensions {
297        width: ceil_div(size.width as u32, h_max * 8)?,
298        height: ceil_div(size.height as u32, v_max * 8)?,
299    };
300
301    for component in components {
302        component.size.width = ceil_div(size.width as u32 * component.horizontal_sampling_factor as u32 * component.dct_scale as u32, h_max * 8)?;
303        component.size.height = ceil_div(size.height as u32 * component.vertical_sampling_factor as u32 * component.dct_scale as u32, v_max * 8)?;
304
305        component.block_size.width = mcu_size.width * component.horizontal_sampling_factor as u16;
306        component.block_size.height = mcu_size.height * component.vertical_sampling_factor as u16;
307    }
308
309    Ok(mcu_size)
310}
311
312#[test]
313fn test_update_component_sizes() {
314    let mut components = [Component {
315        identifier: 1,
316        horizontal_sampling_factor: 2,
317        vertical_sampling_factor: 2,
318        quantization_table_index: 0,
319        dct_scale: 8,
320        size: Dimensions { width: 0, height: 0 },
321        block_size: Dimensions { width: 0, height: 0 },
322    }];
323    let mcu = update_component_sizes(
324        Dimensions { width: 800, height: 280 },
325        &mut components).unwrap();
326    assert_eq!(mcu, Dimensions { width: 50, height: 18 });
327    assert_eq!(components[0].block_size, Dimensions { width: 100, height: 36 });
328    assert_eq!(components[0].size, Dimensions { width: 800, height: 280 });
329}
330
331// Section B.2.3
332pub fn parse_sos<R: Read>(reader: &mut R, frame: &FrameInfo) -> Result<ScanInfo> {
333    let length = read_length(reader, SOS)?;
334    if 0 == length {
335        return Err(Error::Format("zero length in SOS".to_owned()));
336    }
337
338    let component_count = read_u8(reader)?;
339
340    if component_count == 0 || component_count > 4 {
341        return Err(Error::Format(format!("invalid component count {} in scan header", component_count)));
342    }
343
344    if length != 4 + 2 * component_count as usize {
345        return Err(Error::Format("invalid length in SOS".to_owned()));
346    }
347
348    let mut component_indices = Vec::with_capacity(component_count as usize);
349    let mut dc_table_indices = Vec::with_capacity(component_count as usize);
350    let mut ac_table_indices = Vec::with_capacity(component_count as usize);
351
352    for _ in 0 .. component_count {
353        let identifier = read_u8(reader)?;
354
355        let component_index = match frame.components.iter().position(|c| c.identifier == identifier) {
356            Some(value) => value,
357            None => return Err(Error::Format(format!("scan component identifier {} does not match any of the component identifiers defined in the frame", identifier))),
358        };
359
360        // Each of the scan's components must be unique.
361        if component_indices.contains(&component_index) {
362            return Err(Error::Format(format!("duplicate scan component identifier {}", identifier)));
363        }
364
365        // "... the ordering in the scan header shall follow the ordering in the frame header."
366        if component_index < *component_indices.iter().max().unwrap_or(&0) {
367            return Err(Error::Format("the scan component order does not follow the order in the frame header".to_owned()));
368        }
369
370        let byte = read_u8(reader)?;
371        let dc_table_index = byte >> 4;
372        let ac_table_index = byte & 0x0f;
373
374        if dc_table_index > 3 || (frame.is_baseline && dc_table_index > 1) {
375            return Err(Error::Format(format!("invalid dc table index {}", dc_table_index)));
376        }
377        if ac_table_index > 3 || (frame.is_baseline && ac_table_index > 1) {
378            return Err(Error::Format(format!("invalid ac table index {}", ac_table_index)));
379        }
380
381        component_indices.push(component_index);
382        dc_table_indices.push(dc_table_index as usize);
383        ac_table_indices.push(ac_table_index as usize);
384    }
385
386    let blocks_per_mcu = component_indices.iter().map(|&i| {
387        frame.components[i].horizontal_sampling_factor as u32 * frame.components[i].vertical_sampling_factor as u32
388    }).fold(0, ops::Add::add);
389
390    if component_count > 1 && blocks_per_mcu > 10 {
391        return Err(Error::Format("scan with more than one component and more than 10 blocks per MCU".to_owned()));
392    }
393
394    // Also utilized as 'Predictor' in lossless coding, as MEAN in JPEG-LS etc.
395    let spectral_selection_start = read_u8(reader)?;
396    // Also utilized as ILV parameter in JPEG-LS.
397    let mut spectral_selection_end = read_u8(reader)?;
398
399    let byte = read_u8(reader)?;
400    let successive_approximation_high = byte >> 4;
401    let successive_approximation_low = byte & 0x0f;
402
403    // The Differential Pulse-Mode prediction used (similar to png). Only utilized in Lossless
404    // coding. Don't confuse with the JPEG-LS parameter coded using the same scan info portion.
405    let predictor_selection;
406    let point_transform = successive_approximation_low;
407
408    if point_transform >= frame.precision {
409        return Err(Error::Format(
410            "invalid point transform, must be less than the frame precision".to_owned(),
411        ));
412    }
413
414    if frame.coding_process == CodingProcess::DctProgressive {
415        predictor_selection = Predictor::NoPrediction;
416        if spectral_selection_end > 63 || spectral_selection_start > spectral_selection_end ||
417                (spectral_selection_start == 0 && spectral_selection_end != 0) {
418            return Err(Error::Format(format!("invalid spectral selection parameters: ss={}, se={}", spectral_selection_start, spectral_selection_end)));
419        }
420        if spectral_selection_start != 0 && component_count != 1 {
421            return Err(Error::Format("spectral selection scan with AC coefficients can't have more than one component".to_owned()));
422        }
423
424        if successive_approximation_high > 13 || successive_approximation_low > 13 {
425            return Err(Error::Format(format!("invalid successive approximation parameters: ah={}, al={}", successive_approximation_high, successive_approximation_low)));
426        }
427
428        // Section G.1.1.1.2
429        // "Each scan which follows the first scan for a given band progressively improves
430        //     the precision of the coefficients by one bit, until full precision is reached."
431        if successive_approximation_high != 0 && successive_approximation_high != successive_approximation_low + 1 {
432            return Err(Error::Format("successive approximation scan with more than one bit of improvement".to_owned()));
433        }
434    }
435    else if frame.coding_process == CodingProcess::Lossless {
436        if spectral_selection_end != 0 {
437            return Err(Error::Format("spectral selection end shall be zero in lossless scan".to_owned()));
438        }
439        if successive_approximation_high != 0 {
440            return Err(Error::Format("successive approximation high shall be zero in lossless scan".to_owned()));
441        }
442        predictor_selection = match spectral_selection_start {
443            0 => Predictor::NoPrediction,
444            1 => Predictor::Ra,
445            2 => Predictor::Rb,
446            3 => Predictor::Rc,
447            4 => Predictor::RaRbRc1,
448            5 => Predictor::RaRbRc2,
449            6 => Predictor::RaRbRc3,
450            7 => Predictor::RaRb,
451            _ => {
452                return Err(Error::Format(format!("invalid predictor selection value: {}", spectral_selection_start)));
453            },
454        };
455    }
456    else {
457        predictor_selection = Predictor::NoPrediction;
458        if spectral_selection_end == 0 {
459            spectral_selection_end = 63;
460        }
461        if spectral_selection_start != 0 || spectral_selection_end != 63 {
462            return Err(Error::Format("spectral selection is not allowed in non-progressive scan".to_owned()));
463        }
464        if successive_approximation_high != 0 || successive_approximation_low != 0 {
465            return Err(Error::Format("successive approximation is not allowed in non-progressive scan".to_owned()));
466        }
467    }
468
469    Ok(ScanInfo {
470        component_indices,
471        dc_table_indices,
472        ac_table_indices,
473        spectral_selection: Range {
474            start: spectral_selection_start,
475            end: spectral_selection_end + 1,
476        },
477        predictor_selection,
478        successive_approximation_high,
479        successive_approximation_low,
480        point_transform,
481    })
482}
483
484// Section B.2.4.1
485pub fn parse_dqt<R: Read>(reader: &mut R) -> Result<[Option<[u16; 64]>; 4]> {
486    let mut length = read_length(reader, DQT)?;
487    let mut tables = [None; 4];
488
489    // Each DQT segment may contain multiple quantization tables.
490    while length > 0 {
491        let byte = read_u8(reader)?;
492        let precision = (byte >> 4) as usize;
493        let index = (byte & 0x0f) as usize;
494
495        // The combination of 8-bit sample precision and 16-bit quantization tables is explicitly
496        // disallowed by the JPEG spec:
497        //     "An 8-bit DCT-based process shall not use a 16-bit precision quantization table."
498        //     "Pq: Quantization table element precision – Specifies the precision of the Qk
499        //      values. Value 0 indicates 8-bit Qk values; value 1 indicates 16-bit Qk values. Pq
500        //      shall be zero for 8 bit sample precision P (see B.2.2)."
501        // libjpeg allows this behavior though, and there are images in the wild using it. So to
502        // match libjpeg's behavior we are deviating from the JPEG spec here.
503        if precision > 1 {
504            return Err(Error::Format(format!("invalid precision {} in DQT", precision)));
505        }
506        if index > 3 {
507            return Err(Error::Format(format!("invalid destination identifier {} in DQT", index)));
508        }
509        if length < 65 + 64 * precision {
510            return Err(Error::Format("invalid length in DQT".to_owned()));
511        }
512
513        let mut table = [0u16; 64];
514
515        for item in table.iter_mut() {
516            *item = match precision {
517                0 => u16::from(read_u8(reader)?),
518                1 => read_u16_from_be(reader)?,
519                _ => unreachable!(),
520            };
521        }
522
523        if table.iter().any(|&val| val == 0) {
524            return Err(Error::Format("quantization table contains element with a zero value".to_owned()));
525        }
526
527        tables[index] = Some(table);
528        length -= 65 + 64 * precision;
529    }
530
531    Ok(tables)
532}
533
534// Section B.2.4.2
535#[allow(clippy::type_complexity)]
536pub fn parse_dht<R: Read>(reader: &mut R, is_baseline: Option<bool>) -> Result<(Vec<Option<HuffmanTable>>, Vec<Option<HuffmanTable>>)> {
537    let mut length = read_length(reader, DHT)?;
538    let mut dc_tables = vec![None, None, None, None];
539    let mut ac_tables = vec![None, None, None, None];
540
541    // Each DHT segment may contain multiple huffman tables.
542    while length > 17 {
543        let byte = read_u8(reader)?;
544        let class = byte >> 4;
545        let index = (byte & 0x0f) as usize;
546
547        if class != 0 && class != 1 {
548            return Err(Error::Format(format!("invalid class {} in DHT", class)));
549        }
550        if is_baseline == Some(true) && index > 1 {
551            return Err(Error::Format("a maximum of two huffman tables per class are allowed in baseline".to_owned()));
552        }
553        if index > 3 {
554            return Err(Error::Format(format!("invalid destination identifier {} in DHT", index)));
555        }
556
557        let mut counts = [0u8; 16];
558        reader.read_exact(&mut counts)?;
559
560        let size = counts.iter().map(|&val| val as usize).fold(0, ops::Add::add);
561
562        if size == 0 {
563            return Err(Error::Format("encountered table with zero length in DHT".to_owned()));
564        }
565        else if size > 256 {
566            return Err(Error::Format("encountered table with excessive length in DHT".to_owned()));
567        }
568        else if size > length - 17 {
569            return Err(Error::Format("invalid length in DHT".to_owned()));
570        }
571
572        let mut values = vec![0u8; size];
573        reader.read_exact(&mut values)?;
574
575        match class {
576            0 => dc_tables[index] = Some(HuffmanTable::new(&counts, &values, HuffmanTableClass::DC)?),
577            1 => ac_tables[index] = Some(HuffmanTable::new(&counts, &values, HuffmanTableClass::AC)?),
578            _ => unreachable!(),
579        }
580
581        length -= 17 + size;
582    }
583
584    if length != 0 {
585        return Err(Error::Format("invalid length in DHT".to_owned()));
586    }
587
588    Ok((dc_tables, ac_tables))
589}
590
591// Section B.2.4.4
592pub fn parse_dri<R: Read>(reader: &mut R) -> Result<u16> {
593    let length = read_length(reader, DRI)?;
594
595    if length != 2 {
596        return Err(Error::Format("DRI with invalid length".to_owned()));
597    }
598
599    Ok(read_u16_from_be(reader)?)
600}
601
602// Section B.2.4.5
603pub fn parse_com<R: Read>(reader: &mut R) -> Result<Vec<u8>> {
604    let length = read_length(reader, COM)?;
605    let mut buffer = vec![0u8; length];
606
607    reader.read_exact(&mut buffer)?;
608
609    Ok(buffer)
610}
611
612// Section B.2.4.6
613pub fn parse_app<R: Read>(reader: &mut R, marker: Marker) -> Result<Option<AppData>> {
614    let length = read_length(reader, marker)?;
615    let mut bytes_read = 0;
616    let mut result = None;
617
618    match marker {
619        APP(0) => {
620            if length >= 5 {
621                let mut buffer = [0u8; 5];
622                reader.read_exact(&mut buffer)?;
623                bytes_read = buffer.len();
624
625                // http://www.w3.org/Graphics/JPEG/jfif3.pdf
626                if buffer[0..5] == *b"JFIF\0" {
627                    result = Some(AppData::Jfif);
628                // https://sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#AVI1
629                } else if buffer[0..5] == *b"AVI1\0" {
630                    result = Some(AppData::Avi1);
631                }
632            }
633        }
634        APP(1) => {
635            let mut buffer = vec![0u8; length];
636            reader.read_exact(&mut buffer)?;
637            bytes_read = buffer.len();
638
639            // https://web.archive.org/web/20190624045241if_/http://www.cipa.jp:80/std/documents/e/DC-008-Translation-2019-E.pdf
640            // 4.5.4 Basic Structure of JPEG Compressed Data
641            if length >= 6 && buffer[0..6] == *b"Exif\x00\x00" {
642                result = Some(AppData::Exif(buffer[6..].to_vec()));
643            }
644            // XMP packet
645            // https://github.com/adobe/XMP-Toolkit-SDK/blob/main/docs/XMPSpecificationPart3.pdf
646            else if length >= 29 && buffer[0..29] == *b"http://ns.adobe.com/xap/1.0/\0" {
647                result = Some(AppData::Xmp(buffer[29..].to_vec()));
648            }
649        }
650        APP(2) => {
651            if length > 14 {
652                let mut buffer = [0u8; 14];
653                reader.read_exact(&mut buffer)?;
654                bytes_read = buffer.len();
655
656                // http://www.color.org/ICC_Minor_Revision_for_Web.pdf
657                // B.4 Embedding ICC profiles in JFIF files
658                if buffer[0..12] == *b"ICC_PROFILE\0" {
659                    let mut data = vec![0; length - bytes_read];
660                    reader.read_exact(&mut data)?;
661                    bytes_read += data.len();
662                    result = Some(AppData::Icc(IccChunk {
663                        seq_no: buffer[12],
664                        num_markers: buffer[13],
665                        data,
666                    }));
667                }
668            }
669        }
670        APP(13) => {
671            if length >= 14 {
672                let mut buffer = [0u8; 14];
673                reader.read_exact(&mut buffer)?;
674                bytes_read = buffer.len();
675
676                // PSIR (Photoshop)
677                // https://github.com/adobe/XMP-Toolkit-SDK/blob/main/docs/XMPSpecificationPart3.pdf
678                if buffer[0..14] == *b"Photoshop 3.0\0" {
679                    let mut data = vec![0; length - bytes_read];
680                    reader.read_exact(&mut data)?;
681                    bytes_read += data.len();
682                    result = Some(AppData::Psir(data));
683                }
684            }
685        }
686        APP(14) => {
687            if length >= 12 {
688                let mut buffer = [0u8; 12];
689                reader.read_exact(&mut buffer)?;
690                bytes_read = buffer.len();
691
692                // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
693                if buffer[0 .. 6] == *b"Adobe\0" {
694                    let color_transform = match buffer[11] {
695                        0 => AdobeColorTransform::Unknown,
696                        1 => AdobeColorTransform::YCbCr,
697                        2 => AdobeColorTransform::YCCK,
698                        _ => return Err(Error::Format("invalid color transform in adobe app segment".to_owned())),
699                    };
700
701                    result = Some(AppData::Adobe(color_transform));
702                }
703            }
704        },
705        _ => {},
706    }
707
708    skip_bytes(reader, length - bytes_read)?;
709    Ok(result)
710}