1use alloc::format;
14use alloc::string::ToString;
15use alloc::vec::Vec;
16
17use zune_core::bytestream::ZByteReaderTrait;
18use zune_core::colorspace::ColorSpace;
19use zune_core::log::{debug, trace, warn};
20
21use core::cmp::max;
22
23use crate::components::{Components, SampleRatios};
24use crate::decoder::{ExtendedXmpSegment, GainMapInfo, ICCChunk, JpegDecoder, MAX_COMPONENTS};
25use crate::errors::DecodeErrors;
26use crate::huffman::HuffmanTable;
27use crate::misc::{SOFMarkers, UN_ZIGZAG};
28
29#[allow(clippy::similar_names, clippy::cast_sign_loss)]
31pub(crate) fn parse_huffman<T: ZByteReaderTrait>(
32 decoder: &mut JpegDecoder<T>
33) -> Result<(), DecodeErrors>
34where
35{
36 let mut dht_length = i32::from(decoder.stream.get_u16_be_err()?.checked_sub(2).ok_or(
38 DecodeErrors::FormatStatic("Invalid Huffman length in image")
39 )?);
40
41 while dht_length > 16 {
42 let ht_info = decoder.stream.read_u8_err()?;
44 let dc_or_ac = (ht_info >> 4) & 0xF;
46 let index = (ht_info & 0xF) as usize;
48 let mut num_symbols: [u8; 17] = [0; 17];
50
51 if index >= MAX_COMPONENTS {
52 return Err(DecodeErrors::HuffmanDecode(format!(
53 "Invalid DHT index {index}, expected between 0 and 3"
54 )));
55 }
56
57 if dc_or_ac > 1 {
58 return Err(DecodeErrors::HuffmanDecode(format!(
59 "Invalid DHT position {dc_or_ac}, should be 0 or 1"
60 )));
61 }
62
63 decoder.stream.read_exact_bytes(&mut num_symbols[1..17])?;
64
65 dht_length -= 1 + 16;
66
67 let symbols_sum: i32 = num_symbols.iter().map(|f| i32::from(*f)).sum();
68
69 if symbols_sum > 256 {
71 return Err(DecodeErrors::FormatStatic(
72 "Encountered Huffman table with excessive length in DHT"
73 ));
74 }
75 if symbols_sum > dht_length {
76 return Err(DecodeErrors::HuffmanDecode(format!(
77 "Excessive Huffman table of length {symbols_sum} found when header length is {dht_length}"
78 )));
79 }
80 dht_length -= symbols_sum;
81 let mut symbols = [0; 256];
83
84 decoder
85 .stream
86 .read_exact_bytes(&mut symbols[0..(symbols_sum as usize)])?;
87 match dc_or_ac {
89 0 => {
90 decoder.dc_huffman_tables[index] = Some(HuffmanTable::new(
91 &num_symbols,
92 symbols,
93 true,
94 decoder.is_progressive
95 )?);
96 }
97 _ => {
98 decoder.ac_huffman_tables[index] = Some(HuffmanTable::new(
99 &num_symbols,
100 symbols,
101 false,
102 decoder.is_progressive
103 )?);
104 }
105 }
106 }
107
108 if dht_length > 0 {
109 return Err(DecodeErrors::FormatStatic("Bogus Huffman table definition"));
110 }
111
112 Ok(())
113}
114
115#[allow(clippy::cast_possible_truncation, clippy::needless_range_loop)]
117pub(crate) fn parse_dqt<T: ZByteReaderTrait>(img: &mut JpegDecoder<T>) -> Result<(), DecodeErrors> {
118 let mut qt_length =
120 img.stream
121 .get_u16_be_err()?
122 .checked_sub(2)
123 .ok_or(DecodeErrors::FormatStatic(
124 "Invalid DQT length. Length should be greater than 2"
125 ))?;
126 while qt_length > 0 {
128 let qt_info = img.stream.read_u8_err()?;
129 let precision = (qt_info >> 4) as usize;
131 let table_position = (qt_info & 0x0f) as usize;
133 let precision_value = 64 * (precision + 1);
134
135 if (precision_value + 1) as u16 > qt_length {
136 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 }
138
139 let dct_table = match precision {
140 0 => {
141 let mut qt_values = [0; 64];
142
143 img.stream.read_exact_bytes(&mut qt_values)?;
144
145 qt_length -= (precision_value as u16) + 1 ;
146 un_zig_zag(&qt_values)
148 }
149 1 => {
150 let mut qt_values = [0_u16; 64];
152
153 for i in 0..64 {
154 qt_values[i] = img.stream.get_u16_be_err()?;
155 }
156 qt_length -= (precision_value as u16) + 1;
157
158 un_zig_zag(&qt_values)
159 }
160 _ => {
161 return Err(DecodeErrors::DqtError(format!(
162 "Expected QT precision value of either 0 or 1, found {precision:?}"
163 )));
164 }
165 };
166
167 if table_position >= MAX_COMPONENTS {
168 return Err(DecodeErrors::DqtError(format!(
169 "Too large table position for QT :{table_position}, expected between 0 and 3"
170 )));
171 }
172
173 trace!("Assigning qt table {table_position} with precision {precision}");
174 img.qt_tables[table_position] = Some(dct_table);
175 }
176
177 return Ok(());
178}
179
180pub(crate) fn parse_start_of_frame<T: ZByteReaderTrait>(
183 sof: SOFMarkers, img: &mut JpegDecoder<T>
184) -> Result<(), DecodeErrors> {
185 if img.seen_sof {
186 return Err(DecodeErrors::SofError(
187 "Two Start of Frame Markers".to_string()
188 ));
189 }
190 let length = img.stream.get_u16_be_err()?;
192 let dt_precision = img.stream.read_u8_err()?;
195
196 if dt_precision != 8 {
197 return Err(DecodeErrors::SofError(format!(
198 "The library can only parse 8-bit images, the image has {dt_precision} bits of precision"
199 )));
200 }
201
202 img.info.set_density(dt_precision);
203
204 let img_height = img.stream.get_u16_be_err()?;
206 img.info.set_height(img_height);
207
208 let img_width = img.stream.get_u16_be_err()?;
210 img.info.set_width(img_width);
211
212 trace!("Image width :{}", img_width);
213 trace!("Image height :{}", img_height);
214
215 if usize::from(img_width) > img.options.max_width() {
216 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 }
218
219 if usize::from(img_height) > img.options.max_height() {
220 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 }
222
223 if img_width == 0 || img_height == 0 {
225 return Err(DecodeErrors::ZeroError);
226 }
227
228 let num_components = img.stream.read_u8_err()?;
230
231 if num_components == 0 {
232 return Err(DecodeErrors::SofError(
233 "Number of components cannot be zero.".to_string()
234 ));
235 }
236
237 let expected = 8 + 3 * u16::from(num_components);
238 if length != expected {
240 return Err(DecodeErrors::SofError(format!(
241 "Length of start of frame differs from expected {expected},value is {length}"
242 )));
243 }
244
245 trace!("Image components : {}", num_components);
246
247 if num_components == 1 {
248 img.input_colorspace = ColorSpace::Luma;
252 debug!("Overriding default colorspace set to Luma");
254 }
255 if num_components == 4 && img.input_colorspace == ColorSpace::YCbCr {
256 trace!("Input image has 4 components, defaulting to CMYK colorspace");
257 img.input_colorspace = ColorSpace::CMYK;
259 }
260
261 img.info.components = num_components;
263
264 let mut components = Vec::with_capacity(num_components as usize);
265 let mut temp = [0; 3];
266
267 for pos in 0..num_components {
268 img.stream.read_exact_bytes(&mut temp)?;
270
271 let component = Components::from(temp, pos)?;
273
274 components.push(component);
275 }
276 img.seen_sof = true;
277
278 img.info.set_sof_marker(sof);
279
280 img.components = components;
281
282 let mut h_max = 1;
283 let mut v_max = 1;
284
285 for comp in &img.components {
286 h_max = max(h_max, comp.horizontal_sample);
287 v_max = max(v_max, comp.vertical_sample);
288 }
289
290 img.info.sample_ratio = match (h_max, v_max) {
291 (1, 1) => SampleRatios::None,
292 (1, 2) => SampleRatios::V,
293 (2, 1) => SampleRatios::H,
294 (2, 2) => SampleRatios::HV,
295 (hs, vs) => SampleRatios::Generic(hs, vs)
296 };
297
298 Ok(())
299}
300
301pub(crate) fn parse_sos<T: ZByteReaderTrait>(
303 image: &mut JpegDecoder<T>
304) -> Result<(), DecodeErrors> {
305 let ls = usize::from(image.stream.get_u16_be_err()?);
307 let ns = image.stream.read_u8_err()?;
309
310 let mut seen: [_; 5] = [-1; { MAX_COMPONENTS + 1 }];
311
312 image.num_scans = ns;
313 let smallest_size = 6 + 2 * usize::from(ns);
314
315 if ls != smallest_size {
316 return Err(DecodeErrors::SosError(format!(
317 "Bad SOS length {ls},corrupt jpeg"
318 )));
319 }
320
321 if !(1..5).contains(&ns) {
323 return Err(DecodeErrors::SosError(format!(
324 "Invalid number of components in start of scan {ns}, expected in range 1..5"
325 )));
326 }
327
328 if image.info.components == 0 {
329 return Err(DecodeErrors::FormatStatic(
330 "Error decoding SOF Marker, Number of components cannot be zero."
331 ));
332 }
333
334 image.scan_subsampled = false;
336
337 for i in 0..ns {
338 let id = image.stream.read_u8_err()?;
339
340 if seen.contains(&i32::from(id)) {
341 return Err(DecodeErrors::SofError(format!(
342 "Duplicate ID {id} seen twice in the same component"
343 )));
344 }
345
346 seen[usize::from(i)] = i32::from(id);
347 let y = image.stream.read_u8_err()?;
351
352 let mut j = 0;
353
354 while j < image.info.components {
355 if image.components[j as usize].id == id {
356 break;
357 }
358
359 j += 1;
360 }
361
362 if j == image.info.components {
363 return Err(DecodeErrors::SofError(format!(
364 "Invalid component id {}, expected one one of {:?}",
365 id,
366 image.components.iter().map(|c| c.id).collect::<Vec<_>>()
367 )));
368 }
369
370 let component = &mut image.components[usize::from(j)];
371 component.dc_huff_table = usize::from((y >> 4) & 0xF);
372 component.ac_huff_table = usize::from(y & 0xF);
373 image.z_order[i as usize] = j as usize;
374
375 if component.vertical_sample != 1 || component.horizontal_sample != 1 {
376 image.scan_subsampled = true;
377 }
378
379 trace!(
380 "Assigned huffman tables {}/{} to component {j}, id={}",
381 image.components[usize::from(j)].dc_huff_table,
382 image.components[usize::from(j)].ac_huff_table,
383 image.components[usize::from(j)].id,
384 );
385 }
386
387 image.spec_start = image.stream.read_u8_err()?;
398 image.spec_end = image.stream.read_u8_err()?;
400
401 let bit_approx = image.stream.read_u8_err()?;
402 image.succ_high = bit_approx >> 4;
404
405 if image.spec_end > 63 {
406 return Err(DecodeErrors::SosError(format!(
407 "Invalid Se parameter {}, range should be 0-63",
408 image.spec_end
409 )));
410 }
411 if image.spec_start > 63 {
412 return Err(DecodeErrors::SosError(format!(
413 "Invalid Ss parameter {}, range should be 0-63",
414 image.spec_start
415 )));
416 }
417 if image.succ_high > 13 {
418 return Err(DecodeErrors::SosError(format!(
419 "Invalid Ah parameter {}, range should be 0-13",
420 image.succ_low
421 )));
422 }
423 image.succ_low = bit_approx & 0xF;
425
426 if image.succ_low > 13 {
427 return Err(DecodeErrors::SosError(format!(
428 "Invalid Al parameter {}, range should be 0-13",
429 image.succ_low
430 )));
431 }
432 image.stream.skip(smallest_size.saturating_sub(ls))?;
434
435 trace!(
436 "Ss={}, Se={} Ah={} Al={}",
437 image.spec_start,
438 image.spec_end,
439 image.succ_high,
440 image.succ_low
441 );
442
443 Ok(())
444}
445
446pub(crate) fn parse_app13<T: ZByteReaderTrait>(
448 decoder: &mut JpegDecoder<T>
449) -> Result<(), DecodeErrors> {
450 const IPTC_PREFIX: &[u8] = b"Photoshop 3.0\0";
451 let mut length = usize::from(decoder.stream.get_u16_be());
453
454 if length < 2 {
455 return Err(DecodeErrors::FormatStatic("Too small APP13 length"));
456 }
457 length -= 2;
459
460 if length > IPTC_PREFIX.len() && decoder.stream.peek_at(0, IPTC_PREFIX.len())? == IPTC_PREFIX {
461 decoder.stream.skip(IPTC_PREFIX.len())?;
463 length -= IPTC_PREFIX.len();
464
465 let iptc_bytes = decoder.stream.peek_at(0, length)?.to_vec();
466
467 decoder.info.iptc_data = Some(iptc_bytes);
468 }
469
470 decoder.stream.skip(length)?;
471 Ok(())
472}
473
474pub(crate) fn parse_app14<T: ZByteReaderTrait>(
476 decoder: &mut JpegDecoder<T>
477) -> Result<(), DecodeErrors> {
478 let mut length = usize::from(decoder.stream.get_u16_be());
480
481 if length < 2 {
482 return Err(DecodeErrors::FormatStatic("Too small APP14 length"));
483 }
484
485 if decoder.stream.peek_at(0, 5)? == b"Adobe" {
486 if length < 14 {
487 return Err(DecodeErrors::FormatStatic(
488 "Too short of a length for App14 segment"
489 ));
490 }
491 decoder.stream.skip(6)?;
493 decoder.stream.skip(5)?;
495 let transform = decoder.stream.read_u8();
497 match transform {
499 0 => decoder.input_colorspace = ColorSpace::CMYK,
500 1 => decoder.input_colorspace = ColorSpace::YCbCr,
501 2 => decoder.input_colorspace = ColorSpace::YCCK,
502 _ => {
503 return Err(DecodeErrors::Format(format!(
504 "Unknown Adobe colorspace {transform}"
505 )))
506 }
507 }
508 length = length.saturating_sub(14);
513 } else {
514 warn!("Not a valid Adobe APP14 Segment, skipping {} bytes", length);
515 length = length.saturating_sub(2);
516 }
517 decoder.stream.skip(length)?;
520
521 Ok(())
522}
523
524pub(crate) fn parse_app1<T: ZByteReaderTrait>(
528 decoder: &mut JpegDecoder<T>
529) -> 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 let mut length = usize::from(decoder.stream.get_u16_be());
540
541 if length < 2 {
542 return Err(DecodeErrors::FormatStatic("Too small app1 length"));
543 }
544 length -= 2;
546
547 if length > 6 && decoder.stream.peek_at(0, 6)? == b"Exif\x00\x00" {
548 trace!("Exif segment present");
549 decoder.stream.skip(6)?;
551 length -= 6;
552
553 let exif_bytes = decoder.stream.peek_at(0, length)?.to_vec();
554
555 decoder.info.exif_data = Some(exif_bytes);
556 } else if length > XMP_NAMESPACE_PREFIX.len()
557 && decoder.stream.peek_at(0, XMP_NAMESPACE_PREFIX.len())? == XMP_NAMESPACE_PREFIX
558 {
559 trace!("XMP Data Present");
560 decoder.stream.skip(XMP_NAMESPACE_PREFIX.len())?;
561 length -= XMP_NAMESPACE_PREFIX.len();
562 let xmp_data = decoder.stream.peek_at(0, length)?.to_vec();
563 decoder.info.xmp_data = Some(xmp_data);
564 } else if length > EXTENDED_XMP_NAMESPACE_PREFIX.len()
565 && decoder.stream.peek_at(0, EXTENDED_XMP_NAMESPACE_PREFIX.len())?
566 == EXTENDED_XMP_NAMESPACE_PREFIX
567 {
568 trace!("Extended XMP Data Present");
569 decoder.stream.skip(EXTENDED_XMP_NAMESPACE_PREFIX.len())?;
570 length -= EXTENDED_XMP_NAMESPACE_PREFIX.len();
571
572 if length < EXTENDED_XMP_HEADER_SIZE {
573 return Err(DecodeErrors::FormatStatic("Too small Extended XMP segment"));
574 }
575
576 let header = decoder.stream.peek_at(0, EXTENDED_XMP_HEADER_SIZE)?;
577 let guid = header[0..EXTENDED_XMP_GUID_SIZE].to_vec();
578
579 let total_size_start = EXTENDED_XMP_GUID_SIZE;
580 let total_size_end = total_size_start + EXTENDED_XMP_TOTAL_SIZE_SIZE;
581 let total_size =
582 u32::from_be_bytes(header[total_size_start..total_size_end].try_into().unwrap());
583
584 let offset_start = total_size_end;
585 let offset_end = offset_start + EXTENDED_XMP_OFFSET_SIZE;
586 let offset = u32::from_be_bytes(header[offset_start..offset_end].try_into().unwrap());
587
588 let data = decoder
589 .stream
590 .peek_at(EXTENDED_XMP_HEADER_SIZE, length - EXTENDED_XMP_HEADER_SIZE)?
591 .to_vec();
592
593 decoder.extended_xmp_segments.push(ExtendedXmpSegment { offset, total_size, guid, data });
594 } else {
595 warn!("Unknown format for APP1 tag, skipping");
596 }
597
598 decoder.stream.skip(length)?;
599 Ok(())
600}
601
602pub(crate) fn parse_app2<T: ZByteReaderTrait>(
603 decoder: &mut JpegDecoder<T>
604) -> 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 let mut length = usize::from(decoder.stream.get_u16_be());
609
610 if length < 2 {
611 return Err(DecodeErrors::FormatStatic("Too small app2 segment"));
612 }
613 length -= 2;
615
616 if length > 14 && decoder.stream.peek_at(0, 12)? == *b"ICC_PROFILE\0" {
617 trace!("ICC Profile present");
618 length -= 12;
620 decoder.stream.skip(12)?;
621 let seq_no = decoder.stream.read_u8();
622 let num_markers = decoder.stream.read_u8();
623 length -= 2;
625
626 let data = decoder.stream.peek_at(0, length)?.to_vec();
627
628 let icc_chunk = ICCChunk {
629 seq_no,
630 num_markers,
631 data
632 };
633 decoder.icc_data.push(icc_chunk);
634 } else if length > HDR_META.len() && decoder.stream.peek_at(0, HDR_META.len())? == HDR_META {
635 length = length.saturating_sub(HDR_META.len());
636 decoder.stream.skip(HDR_META.len())?;
637 trace!("Gain Map metadata found");
638 match length {
639 4 => {
640 let _ = decoder.stream.get_u16_be();
646 let _ = decoder.stream.get_u16_be();
647 length -= 4;
648 decoder
649 .info
650 .gain_map_info
651 .push(GainMapInfo { data: Vec::new() });
652 }
653 n if n > 4 => {
654 let data = decoder.stream.peek_at(0, length)?.to_vec();
658 length -= data.len();
659 decoder.stream.skip(data.len())?;
660
661 decoder.info.gain_map_info.push(GainMapInfo { data });
662 }
663 _ => {}
664 }
665 } else if length > MPF_DATA.len() && decoder.stream.peek_at(0, MPF_DATA.len())? == MPF_DATA {
666 trace!("MPF Signature present");
667 length = length.saturating_sub(MPF_DATA.len());
668 decoder.stream.skip(MPF_DATA.len())?;
669 decoder.info.multi_picture_information_offset = Some(decoder.stream.position()?);
670 let data = decoder.stream.peek_at(0, length)?.to_vec();
675 length -= data.len();
676 decoder.stream.skip(data.len())?;
677 decoder.info.multi_picture_information = Some(data);
678 }
679
680 decoder.stream.skip(length)?;
681
682 Ok(())
683}
684
685fn un_zig_zag<T>(a: &[T]) -> [i32; 64]
688where
689 T: Default + Copy,
690 i32: core::convert::From<T>
691{
692 let mut output = [i32::default(); 64];
693
694 for i in 0..64 {
695 output[UN_ZIGZAG[i]] = i32::from(a[i]);
696 }
697
698 output
699}