1use crate::error::{Error, Result, UnsupportedFeature};
2use crate::huffman::{HuffmanDecoder, HuffmanTable, fill_default_mjpeg_tables};
3use crate::marker::Marker;
4use crate::parser::{
5 AdobeColorTransform, AppData, CodingProcess, Component, Dimensions, EntropyCoding, FrameInfo,
6 IccChunk, ScanInfo, parse_app, parse_com, parse_dht, parse_dqt, parse_dri, parse_sof,
7 parse_sos,
8};
9use crate::read_u8;
10use crate::upsampler::Upsampler;
11use crate::worker::{PreferWorkerKind, RowData, Worker, WorkerScope, compute_image_parallel};
12use alloc::borrow::ToOwned;
13use alloc::sync::Arc;
14use alloc::vec::Vec;
15use alloc::{format, vec};
16use core::cmp;
17use core::mem;
18use core::ops::Range;
19use std::io::Read;
20
21pub const MAX_COMPONENTS: usize = 4;
22
23mod lossless;
24use self::lossless::compute_image_lossless;
25
26#[rustfmt::skip]
27static UNZIGZAG: [u8; 64] = [
28 0, 1, 8, 16, 9, 2, 3, 10,
29 17, 24, 32, 25, 18, 11, 4, 5,
30 12, 19, 26, 33, 40, 48, 41, 34,
31 27, 20, 13, 6, 7, 14, 21, 28,
32 35, 42, 49, 56, 57, 50, 43, 36,
33 29, 22, 15, 23, 30, 37, 44, 51,
34 58, 59, 52, 45, 38, 31, 39, 46,
35 53, 60, 61, 54, 47, 55, 62, 63,
36];
37
38#[derive(Clone, Copy, Debug, PartialEq)]
40pub enum PixelFormat {
41 L8,
43 L16,
45 RGB24,
47 CMYK32,
49}
50
51impl PixelFormat {
52 pub fn pixel_bytes(&self) -> usize {
54 match self {
55 PixelFormat::L8 => 1,
56 PixelFormat::L16 => 2,
57 PixelFormat::RGB24 => 3,
58 PixelFormat::CMYK32 => 4,
59 }
60 }
61}
62
63#[derive(Clone, Copy, Debug, PartialEq)]
65pub struct ImageInfo {
66 pub width: u16,
68 pub height: u16,
70 pub pixel_format: PixelFormat,
72 pub coding_process: CodingProcess,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
78#[non_exhaustive]
79pub enum ColorTransform {
80 None,
82 Unknown,
84 Grayscale,
86 RGB,
88 YCbCr,
90 CMYK,
92 YCCK,
94 JcsBgYcc,
96 JcsBgRgb,
98}
99
100#[derive(Clone, Debug)]
103pub struct RawCoefficients {
104 pub components: Vec<Vec<i16>>,
110
111 pub width: u16,
113
114 pub height: u16,
116
117 pub blocks_per_component: Vec<usize>,
119
120 pub quantization_tables: Vec<[u16; 64]>,
122}
123
124pub struct Decoder<R> {
126 reader: R,
127
128 frame: Option<FrameInfo>,
129 dc_huffman_tables: Vec<Option<HuffmanTable>>,
130 ac_huffman_tables: Vec<Option<HuffmanTable>>,
131 quantization_tables: [Option<Arc<[u16; 64]>>; 4],
132
133 restart_interval: u16,
134
135 adobe_color_transform: Option<AdobeColorTransform>,
136 color_transform: Option<ColorTransform>,
137
138 is_jfif: bool,
139 is_mjpeg: bool,
140
141 icc_markers: Vec<IccChunk>,
142
143 exif_data: Option<Vec<u8>>,
144 xmp_data: Option<Vec<u8>>,
145 psir_data: Option<Vec<u8>>,
146
147 coefficients: Vec<Vec<i16>>,
149 coefficients_finished: [u64; MAX_COMPONENTS],
151
152 raw_coefficient_mode: bool,
155
156 decoding_buffer_size_limit: usize,
158}
159
160impl<R: Read> Decoder<R> {
161 pub fn new(reader: R) -> Decoder<R> {
163 Decoder {
164 reader,
165 frame: None,
166 dc_huffman_tables: vec![None, None, None, None],
167 ac_huffman_tables: vec![None, None, None, None],
168 quantization_tables: [None, None, None, None],
169 restart_interval: 0,
170 adobe_color_transform: None,
171 color_transform: None,
172 is_jfif: false,
173 is_mjpeg: false,
174 icc_markers: Vec::new(),
175 exif_data: None,
176 xmp_data: None,
177 psir_data: None,
178 coefficients: Vec::new(),
179 coefficients_finished: [0; MAX_COMPONENTS],
180 raw_coefficient_mode: false,
181 decoding_buffer_size_limit: usize::MAX,
182 }
183 }
184
185 pub fn set_color_transform(&mut self, transform: ColorTransform) {
188 self.color_transform = Some(transform);
189 }
190
191 pub fn set_max_decoding_buffer_size(&mut self, max: usize) {
193 self.decoding_buffer_size_limit = max;
194 }
195
196 pub fn info(&self) -> Option<ImageInfo> {
201 match self.frame {
202 Some(ref frame) => {
203 let pixel_format = match frame.components.len() {
204 1 => match frame.precision {
205 2..=8 => PixelFormat::L8,
206 9..=16 => PixelFormat::L16,
207 _ => panic!(),
208 },
209 3 => PixelFormat::RGB24,
210 4 => PixelFormat::CMYK32,
211 _ => panic!(),
212 };
213
214 Some(ImageInfo {
215 width: frame.output_size.width,
216 height: frame.output_size.height,
217 pixel_format,
218 coding_process: frame.coding_process,
219 })
220 }
221 None => None,
222 }
223 }
224
225 pub fn frame_info(&self) -> Option<&FrameInfo> {
230 self.frame.as_ref()
231 }
232
233 pub fn exif_data(&self) -> Option<&[u8]> {
237 self.exif_data.as_deref()
238 }
239
240 pub fn xmp_data(&self) -> Option<&[u8]> {
244 self.xmp_data.as_deref()
245 }
246
247 pub fn icc_profile(&self) -> Option<Vec<u8>> {
249 let mut marker_present: [Option<&IccChunk>; 256] = [None; 256];
250 let num_markers = self.icc_markers.len();
251 if num_markers == 0 || num_markers >= 255 {
252 return None;
253 }
254 for chunk in &self.icc_markers {
256 if usize::from(chunk.num_markers) != num_markers {
257 return None;
259 }
260 if chunk.seq_no == 0 {
261 return None;
262 }
263 if marker_present[usize::from(chunk.seq_no)].is_some() {
264 return None;
266 } else {
267 marker_present[usize::from(chunk.seq_no)] = Some(chunk);
268 }
269 }
270
271 let mut data = Vec::new();
273 for &chunk in marker_present.get(1..=num_markers)? {
275 data.extend_from_slice(&chunk?.data);
276 }
277 Some(data)
278 }
279
280 fn select_worker(frame: &FrameInfo, worker_preference: PreferWorkerKind) -> PreferWorkerKind {
283 const PARALLELISM_THRESHOLD: u64 = 128 * 128;
284
285 match worker_preference {
286 PreferWorkerKind::Immediate => PreferWorkerKind::Immediate,
287 PreferWorkerKind::Multithreaded => {
288 let width: u64 = frame.output_size.width.into();
289 let height: u64 = frame.output_size.width.into();
290 if width * height > PARALLELISM_THRESHOLD {
291 PreferWorkerKind::Multithreaded
292 } else {
293 PreferWorkerKind::Immediate
294 }
295 }
296 }
297 }
298
299 pub fn read_info(&mut self) -> Result<()> {
303 WorkerScope::with(|worker| self.decode_internal(true, worker)).map(|_| ())
304 }
305
306 pub fn scale(&mut self, requested_width: u16, requested_height: u16) -> Result<(u16, u16)> {
316 self.read_info()?;
317 let frame = self.frame.as_mut().unwrap();
318 let idct_size = crate::idct::choose_idct_size(
319 frame.image_size,
320 Dimensions {
321 width: requested_width,
322 height: requested_height,
323 },
324 );
325 frame.update_idct_size(idct_size)?;
326 Ok((frame.output_size.width, frame.output_size.height))
327 }
328
329 pub fn decode(&mut self) -> Result<Vec<u8>> {
331 WorkerScope::with(|worker| self.decode_internal(false, worker))
332 }
333
334 pub fn decode_raw_coefficients(&mut self) -> Result<RawCoefficients> {
344 self.raw_coefficient_mode = true;
345
346 WorkerScope::with(|worker| self.decode_internal(false, worker))?;
350
351 let frame = self
352 .frame
353 .as_ref()
354 .ok_or_else(|| Error::Format("no frame found in JPEG".to_owned()))?;
355
356 let width = frame.image_size.width;
357 let height = frame.image_size.height;
358
359 let blocks_per_component: Vec<usize> = frame
360 .components
361 .iter()
362 .map(|c| c.block_size.width as usize * c.block_size.height as usize)
363 .collect();
364
365 let quantization_tables: Vec<[u16; 64]> = frame
367 .components
368 .iter()
369 .map(|c| {
370 self.quantization_tables[c.quantization_table_index]
371 .as_ref()
372 .map(|t| **t)
373 .unwrap_or([0u16; 64])
374 })
375 .collect();
376
377 let components = mem::take(&mut self.coefficients);
378
379 Ok(RawCoefficients {
380 components,
381 width,
382 height,
383 blocks_per_component,
384 quantization_tables,
385 })
386 }
387
388 fn decode_internal(
389 &mut self,
390 stop_after_metadata: bool,
391 worker_scope: &WorkerScope,
392 ) -> Result<Vec<u8>> {
393 if stop_after_metadata && self.frame.is_some() {
394 return Ok(Vec::new());
396 } else if self.frame.is_none()
397 && (read_u8(&mut self.reader)? != 0xFF
398 || Marker::from_u8(read_u8(&mut self.reader)?) != Some(Marker::SOI))
399 {
400 return Err(Error::Format(
401 "first two bytes are not an SOI marker".to_owned(),
402 ));
403 }
404
405 let mut previous_marker = Marker::SOI;
406 let mut pending_marker = None;
407 let mut scans_processed = 0;
408 let mut planes = vec![
409 Vec::<u8>::new();
410 self.frame
411 .as_ref()
412 .map_or(0, |frame| frame.components.len())
413 ];
414 let mut planes_u16 = vec![
415 Vec::<u16>::new();
416 self.frame
417 .as_ref()
418 .map_or(0, |frame| frame.components.len())
419 ];
420
421 loop {
422 let marker = match pending_marker.take() {
423 Some(m) => m,
424 None => self.read_marker()?,
425 };
426
427 match marker {
428 Marker::SOF(..) => {
430 if self.frame.is_some() {
435 return Err(Error::Unsupported(UnsupportedFeature::Hierarchical));
436 }
437
438 let frame = parse_sof(&mut self.reader, marker)?;
439 let component_count = frame.components.len();
440
441 if frame.is_differential {
442 return Err(Error::Unsupported(UnsupportedFeature::Hierarchical));
443 }
444 if frame.entropy_coding == EntropyCoding::Arithmetic {
445 return Err(Error::Unsupported(
446 UnsupportedFeature::ArithmeticEntropyCoding,
447 ));
448 }
449 if frame.precision != 8 && frame.coding_process != CodingProcess::Lossless {
450 return Err(Error::Unsupported(UnsupportedFeature::SamplePrecision(
451 frame.precision,
452 )));
453 }
454 if !(2..=16).contains(&frame.precision) {
455 return Err(Error::Unsupported(UnsupportedFeature::SamplePrecision(
456 frame.precision,
457 )));
458 }
459 if component_count != 1 && component_count != 3 && component_count != 4 {
460 return Err(Error::Unsupported(UnsupportedFeature::ComponentCount(
461 component_count as u8,
462 )));
463 }
464
465 let _ = Upsampler::new(
467 &frame.components,
468 frame.image_size.width,
469 frame.image_size.height,
470 )?;
471
472 self.frame = Some(frame);
473
474 if stop_after_metadata {
475 return Ok(Vec::new());
476 }
477
478 planes = vec![Vec::new(); component_count];
479 planes_u16 = vec![Vec::new(); component_count];
480 }
481
482 Marker::SOS => {
484 if self.frame.is_none() {
485 return Err(Error::Format("scan encountered before frame".to_owned()));
486 }
487
488 let frame = self.frame.clone().unwrap();
489 let scan = parse_sos(&mut self.reader, &frame)?;
490
491 if (frame.coding_process == CodingProcess::DctProgressive
492 || self.raw_coefficient_mode)
493 && self.coefficients.is_empty()
494 {
495 self.coefficients = frame
496 .components
497 .iter()
498 .map(|c| {
499 let block_count =
500 c.block_size.width as usize * c.block_size.height as usize;
501 vec![0; block_count * 64]
502 })
503 .collect();
504 }
505
506 if frame.coding_process == CodingProcess::Lossless {
507 let (marker, data) = self.decode_scan_lossless(&frame, &scan)?;
508
509 for (i, plane) in data
510 .into_iter()
511 .enumerate()
512 .filter(|(_, plane)| !plane.is_empty())
513 {
514 planes_u16[i] = plane;
515 }
516 pending_marker = marker;
517 } else {
518 let mut finished = [false; MAX_COMPONENTS];
532
533 if scan.successive_approximation_low == 0 {
534 for (&i, component_finished) in
535 scan.component_indices.iter().zip(&mut finished)
536 {
537 if self.coefficients_finished[i] == !0 {
538 continue;
539 }
540 for j in scan.spectral_selection.clone() {
541 self.coefficients_finished[i] |= 1 << j;
542 }
543 if self.coefficients_finished[i] == !0 {
544 *component_finished = true;
545 }
546 }
547 }
548
549 let preference =
550 Self::select_worker(&frame, PreferWorkerKind::Multithreaded);
551
552 let (marker, data) = worker_scope
553 .get_or_init_worker(preference, |worker| {
554 self.decode_scan(&frame, &scan, worker, &finished)
555 })?;
556
557 if let Some(data) = data {
558 for (i, plane) in data
559 .into_iter()
560 .enumerate()
561 .filter(|(_, plane)| !plane.is_empty())
562 {
563 if self.coefficients_finished[i] == !0 {
564 planes[i] = plane;
565 }
566 }
567 }
568
569 pending_marker = marker;
570 }
571
572 scans_processed += 1;
573 }
574
575 Marker::DQT => {
578 let tables = parse_dqt(&mut self.reader)?;
579
580 for (i, &table) in tables.iter().enumerate() {
581 if let Some(table) = table {
582 let mut unzigzagged_table = [0u16; 64];
583
584 for j in 0..64 {
585 unzigzagged_table[UNZIGZAG[j] as usize] = table[j];
586 }
587
588 self.quantization_tables[i] = Some(Arc::new(unzigzagged_table));
589 }
590 }
591 }
592 Marker::DHT => {
594 let is_baseline = self.frame.as_ref().map(|frame| frame.is_baseline);
595 let (dc_tables, ac_tables) = parse_dht(&mut self.reader, is_baseline)?;
596
597 let current_dc_tables = mem::take(&mut self.dc_huffman_tables);
598 self.dc_huffman_tables = dc_tables
599 .into_iter()
600 .zip(current_dc_tables)
601 .map(|(a, b)| a.or(b))
602 .collect();
603
604 let current_ac_tables = mem::take(&mut self.ac_huffman_tables);
605 self.ac_huffman_tables = ac_tables
606 .into_iter()
607 .zip(current_ac_tables)
608 .map(|(a, b)| a.or(b))
609 .collect();
610 }
611 Marker::DAC => {
613 return Err(Error::Unsupported(
614 UnsupportedFeature::ArithmeticEntropyCoding,
615 ));
616 }
617 Marker::DRI => self.restart_interval = parse_dri(&mut self.reader)?,
619 Marker::COM => {
621 let _comment = parse_com(&mut self.reader)?;
622 }
623 Marker::APP(..) => {
625 if let Some(data) = parse_app(&mut self.reader, marker)? {
626 match data {
627 AppData::Adobe(color_transform) => {
628 self.adobe_color_transform = Some(color_transform)
629 }
630 AppData::Jfif => {
631 self.is_jfif = true;
643 }
644 AppData::Avi1 => self.is_mjpeg = true,
645 AppData::Icc(icc) => self.icc_markers.push(icc),
646 AppData::Exif(data) => self.exif_data = Some(data),
647 AppData::Xmp(data) => self.xmp_data = Some(data),
648 AppData::Psir(data) => self.psir_data = Some(data),
649 }
650 }
651 }
652 Marker::RST(..) => {
654 if previous_marker != Marker::SOS {
657 return Err(Error::Format(
658 "RST found outside of entropy-coded data".to_owned(),
659 ));
660 }
661 }
662
663 Marker::DNL => {
665 if previous_marker != Marker::SOS || scans_processed != 1 {
668 return Err(Error::Format(
669 "DNL is only allowed immediately after the first scan".to_owned(),
670 ));
671 }
672
673 return Err(Error::Unsupported(UnsupportedFeature::DNL));
674 }
675
676 Marker::DHP | Marker::EXP => {
678 return Err(Error::Unsupported(UnsupportedFeature::Hierarchical));
679 }
680
681 Marker::EOI => break,
683
684 _ => {
685 return Err(Error::Format(format!(
686 "{:?} marker found where not allowed",
687 marker
688 )));
689 }
690 }
691
692 previous_marker = marker;
693 }
694
695 if self.frame.is_none() {
696 return Err(Error::Format(
697 "end of image encountered before frame".to_owned(),
698 ));
699 }
700
701 if self.raw_coefficient_mode {
704 return Ok(Vec::new());
705 }
706
707 let frame = self.frame.as_ref().unwrap();
708 let preference = Self::select_worker(frame, PreferWorkerKind::Multithreaded);
709
710 worker_scope.get_or_init_worker(preference, |worker| {
711 self.decode_planes(worker, planes, planes_u16)
712 })
713 }
714
715 fn decode_planes(
716 &mut self,
717 worker: &mut dyn Worker,
718 mut planes: Vec<Vec<u8>>,
719 planes_u16: Vec<Vec<u16>>,
720 ) -> Result<Vec<u8>> {
721 if self.frame.is_none() {
722 return Err(Error::Format(
723 "end of image encountered before frame".to_owned(),
724 ));
725 }
726
727 let frame = self.frame.as_ref().unwrap();
728
729 if frame
730 .components
731 .len()
732 .checked_mul(frame.output_size.width.into())
733 .and_then(|m| m.checked_mul(frame.output_size.height.into()))
734 .is_none_or(|m| self.decoding_buffer_size_limit < m)
735 {
736 return Err(Error::Format(
737 "size of decoded image exceeds maximum allowed size".to_owned(),
738 ));
739 }
740
741 if frame.coding_process == CodingProcess::DctProgressive
743 && self.coefficients.len() == frame.components.len()
744 {
745 for (i, component) in frame.components.iter().enumerate() {
746 if self.coefficients_finished[i] == !0 {
748 continue;
749 }
750
751 let quantization_table =
752 match self.quantization_tables[component.quantization_table_index].clone() {
753 Some(quantization_table) => quantization_table,
754 None => continue,
755 };
756
757 let row_data = RowData {
759 index: i,
760 component: component.clone(),
761 quantization_table,
762 };
763 worker.start(row_data)?;
764
765 let coefficients_per_mcu_row = usize::from(component.block_size.width)
767 * usize::from(component.vertical_sampling_factor)
768 * 64;
769
770 let mut tasks = (0..frame.mcu_size.height).map(|mcu_y| {
771 let offset = usize::from(mcu_y) * coefficients_per_mcu_row;
772 let row_coefficients =
773 self.coefficients[i][offset..offset + coefficients_per_mcu_row].to_vec();
774 (i, row_coefficients)
775 });
776
777 worker.append_rows(&mut tasks)?;
780 planes[i] = worker.get_result(i)?;
781 }
782 }
783
784 if frame.coding_process == CodingProcess::Lossless {
785 compute_image_lossless(frame, planes_u16)
786 } else {
787 compute_image(
788 &frame.components,
789 planes,
790 frame.output_size,
791 self.determine_color_transform(),
792 )
793 }
794 }
795
796 fn determine_color_transform(&self) -> ColorTransform {
797 if let Some(color_transform) = self.color_transform {
798 return color_transform;
799 }
800
801 let frame = self.frame.as_ref().unwrap();
802
803 if frame.components.len() == 1 {
804 return ColorTransform::Grayscale;
805 }
806
807 if frame.components.len() == 3 {
810 match (
811 frame.components[0].identifier,
812 frame.components[1].identifier,
813 frame.components[2].identifier,
814 ) {
815 (1, 2, 3) => {
816 return ColorTransform::YCbCr;
817 }
818 (1, 34, 35) => {
819 return ColorTransform::JcsBgYcc;
820 }
821 (82, 71, 66) => {
822 return ColorTransform::RGB;
823 }
824 (114, 103, 98) => {
825 return ColorTransform::JcsBgRgb;
826 }
827 _ => {}
828 }
829
830 if self.is_jfif {
831 return ColorTransform::YCbCr;
832 }
833 }
834
835 if let Some(colour_transform) = self.adobe_color_transform {
836 match colour_transform {
837 AdobeColorTransform::Unknown => {
838 if frame.components.len() == 3 {
839 return ColorTransform::RGB;
840 } else if frame.components.len() == 4 {
841 return ColorTransform::CMYK;
842 }
843 }
844 AdobeColorTransform::YCbCr => {
845 return ColorTransform::YCbCr;
846 }
847 AdobeColorTransform::YCCK => {
848 return ColorTransform::YCCK;
849 }
850 }
851 } else if frame.components.len() == 4 {
852 return ColorTransform::CMYK;
853 }
854
855 if frame.components.len() == 4 {
856 ColorTransform::YCCK
857 } else if frame.components.len() == 3 {
858 ColorTransform::YCbCr
859 } else {
860 ColorTransform::Unknown
861 }
862 }
863
864 fn read_marker(&mut self) -> Result<Marker> {
865 loop {
866 while read_u8(&mut self.reader)? != 0xFF {}
871
872 let mut byte = read_u8(&mut self.reader)?;
878
879 while byte == 0xFF {
882 byte = read_u8(&mut self.reader)?;
883 }
884
885 if byte != 0x00 && byte != 0xFF {
886 return Ok(Marker::from_u8(byte).unwrap());
887 }
888 }
889 }
890
891 #[allow(clippy::type_complexity)]
892 fn decode_scan(
893 &mut self,
894 frame: &FrameInfo,
895 scan: &ScanInfo,
896 worker: &mut dyn Worker,
897 finished: &[bool; MAX_COMPONENTS],
898 ) -> Result<(Option<Marker>, Option<Vec<Vec<u8>>>)> {
899 assert!(scan.component_indices.len() <= MAX_COMPONENTS);
900
901 let components: Vec<Component> = scan
902 .component_indices
903 .iter()
904 .map(|&i| frame.components[i].clone())
905 .collect();
906
907 if components
909 .iter()
910 .any(|component| self.quantization_tables[component.quantization_table_index].is_none())
911 {
912 return Err(Error::Format("use of unset quantization table".to_owned()));
913 }
914
915 if self.is_mjpeg {
916 fill_default_mjpeg_tables(
917 scan,
918 &mut self.dc_huffman_tables,
919 &mut self.ac_huffman_tables,
920 );
921 }
922
923 if scan.spectral_selection.start == 0
925 && scan
926 .dc_table_indices
927 .iter()
928 .any(|&i| self.dc_huffman_tables[i].is_none())
929 {
930 return Err(Error::Format(
931 "scan makes use of unset dc huffman table".to_owned(),
932 ));
933 }
934 if scan.spectral_selection.end > 1
935 && scan
936 .ac_table_indices
937 .iter()
938 .any(|&i| self.ac_huffman_tables[i].is_none())
939 {
940 return Err(Error::Format(
941 "scan makes use of unset ac huffman table".to_owned(),
942 ));
943 }
944
945 if !self.raw_coefficient_mode {
949 for (i, component) in components.iter().enumerate() {
950 if finished[i] {
951 let row_data = RowData {
952 index: i,
953 component: component.clone(),
954 quantization_table: self.quantization_tables
955 [component.quantization_table_index]
956 .clone()
957 .unwrap(),
958 };
959
960 worker.start(row_data)?;
961 }
962 }
963 }
964
965 let is_progressive = frame.coding_process == CodingProcess::DctProgressive;
966 let is_interleaved = components.len() > 1;
967 let mut dummy_block = [0i16; 64];
968 let mut huffman = HuffmanDecoder::new();
969 let mut dc_predictors = [0i16; MAX_COMPONENTS];
970 let mut mcus_left_until_restart = self.restart_interval;
971 let mut expected_rst_num = 0;
972 let mut eob_run = 0;
973 let mut mcu_row_coefficients = vec![vec![]; components.len()];
974
975 if !is_progressive {
976 for (i, component) in components.iter().enumerate().filter(|&(i, _)| finished[i]) {
977 let coefficients_per_mcu_row = component.block_size.width as usize
978 * component.vertical_sampling_factor as usize
979 * 64;
980 mcu_row_coefficients[i] = vec![0i16; coefficients_per_mcu_row];
981 }
982 }
983
984 let (mcu_horizontal_samples, mcu_vertical_samples) = if is_interleaved {
988 let horizontal = components
989 .iter()
990 .map(|component| component.horizontal_sampling_factor as u16)
991 .collect::<Vec<_>>();
992 let vertical = components
993 .iter()
994 .map(|component| component.vertical_sampling_factor as u16)
995 .collect::<Vec<_>>();
996 (horizontal, vertical)
997 } else {
998 (vec![1], vec![1])
999 };
1000
1001 let (max_mcu_x, max_mcu_y) = if is_interleaved {
1004 (frame.mcu_size.width, frame.mcu_size.height)
1005 } else {
1006 (
1007 components[0].block_size.width,
1008 components[0].block_size.height,
1009 )
1010 };
1011
1012 for mcu_y in 0..max_mcu_y {
1013 if mcu_y * 8 >= frame.image_size.height {
1014 break;
1015 }
1016
1017 for mcu_x in 0..max_mcu_x {
1018 if mcu_x * 8 >= frame.image_size.width {
1019 break;
1020 }
1021
1022 if self.restart_interval > 0 {
1023 if mcus_left_until_restart == 0 {
1024 match huffman.take_marker(&mut self.reader)? {
1025 Some(Marker::RST(n)) => {
1026 if n != expected_rst_num {
1027 return Err(Error::Format(format!(
1028 "found RST{} where RST{} was expected",
1029 n, expected_rst_num
1030 )));
1031 }
1032
1033 huffman.reset();
1034 dc_predictors = [0i16; MAX_COMPONENTS];
1036 eob_run = 0;
1038
1039 expected_rst_num = (expected_rst_num + 1) % 8;
1040 mcus_left_until_restart = self.restart_interval;
1041 }
1042 Some(marker) => {
1043 return Err(Error::Format(format!(
1044 "found marker {:?} inside scan where RST{} was expected",
1045 marker, expected_rst_num
1046 )));
1047 }
1048 None => {
1049 return Err(Error::Format(format!(
1050 "no marker found where RST{} was expected",
1051 expected_rst_num
1052 )));
1053 }
1054 }
1055 }
1056
1057 mcus_left_until_restart -= 1;
1058 }
1059
1060 for (i, component) in components.iter().enumerate() {
1061 for v_pos in 0..mcu_vertical_samples[i] {
1062 for h_pos in 0..mcu_horizontal_samples[i] {
1063 let coefficients = if is_progressive {
1064 let block_y = (mcu_y * mcu_vertical_samples[i] + v_pos) as usize;
1065 let block_x = (mcu_x * mcu_horizontal_samples[i] + h_pos) as usize;
1066 let block_offset =
1067 (block_y * component.block_size.width as usize + block_x) * 64;
1068 &mut self.coefficients[scan.component_indices[i]]
1069 [block_offset..block_offset + 64]
1070 } else if finished[i] {
1071 let mcu_batch_current_row = if is_interleaved {
1075 0
1076 } else {
1077 mcu_y % component.vertical_sampling_factor as u16
1078 };
1079
1080 let block_y = (mcu_batch_current_row * mcu_vertical_samples[i]
1081 + v_pos) as usize;
1082 let block_x = (mcu_x * mcu_horizontal_samples[i] + h_pos) as usize;
1083 let block_offset =
1084 (block_y * component.block_size.width as usize + block_x) * 64;
1085 &mut mcu_row_coefficients[i][block_offset..block_offset + 64]
1086 } else {
1087 &mut dummy_block[..64]
1088 }
1089 .try_into()
1090 .unwrap();
1091
1092 if scan.successive_approximation_high == 0 {
1093 decode_block(
1094 &mut self.reader,
1095 coefficients,
1096 &mut huffman,
1097 self.dc_huffman_tables[scan.dc_table_indices[i]].as_ref(),
1098 self.ac_huffman_tables[scan.ac_table_indices[i]].as_ref(),
1099 scan.spectral_selection.clone(),
1100 scan.successive_approximation_low,
1101 &mut eob_run,
1102 &mut dc_predictors[i],
1103 )?;
1104 } else {
1105 decode_block_successive_approximation(
1106 &mut self.reader,
1107 coefficients,
1108 &mut huffman,
1109 self.ac_huffman_tables[scan.ac_table_indices[i]].as_ref(),
1110 scan.spectral_selection.clone(),
1111 scan.successive_approximation_low,
1112 &mut eob_run,
1113 )?;
1114 }
1115 }
1116 }
1117 }
1118 }
1119
1120 for (i, component) in components.iter().enumerate() {
1122 if finished[i] {
1123 if !is_interleaved
1127 && (mcu_y + 1) * 8 < frame.image_size.height
1128 && (mcu_y + 1) % component.vertical_sampling_factor as u16 > 0
1129 {
1130 continue;
1131 }
1132
1133 let coefficients_per_mcu_row = component.block_size.width as usize
1134 * component.vertical_sampling_factor as usize
1135 * 64;
1136
1137 let row_coefficients = if is_progressive {
1138 let worker_mcu_y = if is_interleaved {
1141 mcu_y
1142 } else {
1143 mcu_y / component.vertical_sampling_factor as u16
1145 };
1146
1147 let offset = worker_mcu_y as usize * coefficients_per_mcu_row;
1148 self.coefficients[scan.component_indices[i]]
1149 [offset..offset + coefficients_per_mcu_row]
1150 .to_vec()
1151 } else {
1152 mem::replace(
1153 &mut mcu_row_coefficients[i],
1154 vec![0i16; coefficients_per_mcu_row],
1155 )
1156 };
1157
1158 if self.raw_coefficient_mode && !is_progressive {
1159 let component_index = scan.component_indices[i];
1163 let worker_mcu_y = if is_interleaved {
1164 mcu_y
1165 } else {
1166 mcu_y / component.vertical_sampling_factor as u16
1167 };
1168 let offset = worker_mcu_y as usize * coefficients_per_mcu_row;
1169 self.coefficients[component_index]
1170 [offset..offset + coefficients_per_mcu_row]
1171 .copy_from_slice(&row_coefficients);
1172 } else if !self.raw_coefficient_mode {
1173 worker.append_row((i, row_coefficients))?;
1176 }
1177 }
1178 }
1179 }
1180
1181 let mut marker = huffman.take_marker(&mut self.reader)?;
1182 while let Some(Marker::RST(_)) = marker {
1183 marker = self.read_marker().ok();
1184 }
1185
1186 if self.raw_coefficient_mode {
1187 Ok((marker, None))
1190 } else if finished.iter().any(|&c| c) {
1191 let mut data = vec![Vec::new(); frame.components.len()];
1193
1194 for (i, &component_index) in scan.component_indices.iter().enumerate() {
1195 if finished[i] {
1196 data[component_index] = worker.get_result(i)?;
1197 }
1198 }
1199
1200 Ok((marker, Some(data)))
1201 } else {
1202 Ok((marker, None))
1203 }
1204 }
1205}
1206
1207#[allow(clippy::too_many_arguments)]
1208fn decode_block<R: Read>(
1209 reader: &mut R,
1210 coefficients: &mut [i16; 64],
1211 huffman: &mut HuffmanDecoder,
1212 dc_table: Option<&HuffmanTable>,
1213 ac_table: Option<&HuffmanTable>,
1214 spectral_selection: Range<u8>,
1215 successive_approximation_low: u8,
1216 eob_run: &mut u16,
1217 dc_predictor: &mut i16,
1218) -> Result<()> {
1219 debug_assert_eq!(coefficients.len(), 64);
1220
1221 if spectral_selection.start == 0 {
1222 let value = huffman.decode(reader, dc_table.unwrap())?;
1225 let diff = match value {
1226 0 => 0,
1227 1..=11 => huffman.receive_extend(reader, value)?,
1228 _ => {
1229 return Err(Error::Format(
1232 "invalid DC difference magnitude category".to_owned(),
1233 ));
1234 }
1235 };
1236
1237 *dc_predictor = dc_predictor.wrapping_add(diff);
1240 coefficients[0] = *dc_predictor << successive_approximation_low;
1241 }
1242
1243 let mut index = cmp::max(spectral_selection.start, 1);
1244
1245 if index < spectral_selection.end && *eob_run > 0 {
1246 *eob_run -= 1;
1247 return Ok(());
1248 }
1249
1250 while index < spectral_selection.end {
1252 if let Some((value, run)) = huffman.decode_fast_ac(reader, ac_table.unwrap())? {
1253 index += run;
1254
1255 if index >= spectral_selection.end {
1256 break;
1257 }
1258
1259 coefficients[UNZIGZAG[index as usize] as usize] = value << successive_approximation_low;
1260 index += 1;
1261 } else {
1262 let byte = huffman.decode(reader, ac_table.unwrap())?;
1263 let r = byte >> 4;
1264 let s = byte & 0x0f;
1265
1266 if s == 0 {
1267 match r {
1268 15 => index += 16, _ => {
1270 *eob_run = (1 << r) - 1;
1271
1272 if r > 0 {
1273 *eob_run += huffman.get_bits(reader, r)?;
1274 }
1275
1276 break;
1277 }
1278 }
1279 } else {
1280 index += r;
1281
1282 if index >= spectral_selection.end {
1283 break;
1284 }
1285
1286 coefficients[UNZIGZAG[index as usize] as usize] =
1287 huffman.receive_extend(reader, s)? << successive_approximation_low;
1288 index += 1;
1289 }
1290 }
1291 }
1292
1293 Ok(())
1294}
1295
1296fn decode_block_successive_approximation<R: Read>(
1297 reader: &mut R,
1298 coefficients: &mut [i16; 64],
1299 huffman: &mut HuffmanDecoder,
1300 ac_table: Option<&HuffmanTable>,
1301 spectral_selection: Range<u8>,
1302 successive_approximation_low: u8,
1303 eob_run: &mut u16,
1304) -> Result<()> {
1305 debug_assert_eq!(coefficients.len(), 64);
1306
1307 let bit = 1 << successive_approximation_low;
1308
1309 if spectral_selection.start == 0 {
1310 if huffman.get_bits(reader, 1)? == 1 {
1313 coefficients[0] |= bit;
1314 }
1315 } else {
1316 if *eob_run > 0 {
1319 *eob_run -= 1;
1320 refine_non_zeroes(reader, coefficients, huffman, spectral_selection, 64, bit)?;
1321 return Ok(());
1322 }
1323
1324 let mut index = spectral_selection.start;
1325
1326 while index < spectral_selection.end {
1327 let byte = huffman.decode(reader, ac_table.unwrap())?;
1328 let r = byte >> 4;
1329 let s = byte & 0x0f;
1330
1331 let mut zero_run_length = r;
1332 let mut value = 0;
1333
1334 match s {
1335 0 => {
1336 match r {
1337 15 => {
1338 }
1343 _ => {
1344 *eob_run = (1 << r) - 1;
1345
1346 if r > 0 {
1347 *eob_run += huffman.get_bits(reader, r)?;
1348 }
1349
1350 zero_run_length = 64;
1352 }
1353 }
1354 }
1355 1 => {
1356 if huffman.get_bits(reader, 1)? == 1 {
1357 value = bit;
1358 } else {
1359 value = -bit;
1360 }
1361 }
1362 _ => return Err(Error::Format("unexpected huffman code".to_owned())),
1363 }
1364
1365 let range = Range {
1366 start: index,
1367 end: spectral_selection.end,
1368 };
1369 index = refine_non_zeroes(reader, coefficients, huffman, range, zero_run_length, bit)?;
1370
1371 if value != 0 {
1372 coefficients[UNZIGZAG[index as usize] as usize] = value;
1373 }
1374
1375 index += 1;
1376 }
1377 }
1378
1379 Ok(())
1380}
1381
1382fn refine_non_zeroes<R: Read>(
1383 reader: &mut R,
1384 coefficients: &mut [i16; 64],
1385 huffman: &mut HuffmanDecoder,
1386 range: Range<u8>,
1387 zrl: u8,
1388 bit: i16,
1389) -> Result<u8> {
1390 debug_assert_eq!(coefficients.len(), 64);
1391
1392 let last = range.end - 1;
1393 let mut zero_run_length = zrl;
1394
1395 for i in range {
1396 let index = UNZIGZAG[i as usize] as usize;
1397
1398 let coefficient = &mut coefficients[index];
1399
1400 if *coefficient == 0 {
1401 if zero_run_length == 0 {
1402 return Ok(i);
1403 }
1404
1405 zero_run_length -= 1;
1406 } else if huffman.get_bits(reader, 1)? == 1 && *coefficient & bit == 0 {
1407 if *coefficient > 0 {
1408 *coefficient = coefficient
1409 .checked_add(bit)
1410 .ok_or_else(|| Error::Format("Coefficient overflow".to_owned()))?;
1411 } else {
1412 *coefficient = coefficient
1413 .checked_sub(bit)
1414 .ok_or_else(|| Error::Format("Coefficient overflow".to_owned()))?;
1415 }
1416 }
1417 }
1418
1419 Ok(last)
1420}
1421
1422fn compute_image(
1423 components: &[Component],
1424 mut data: Vec<Vec<u8>>,
1425 output_size: Dimensions,
1426 color_transform: ColorTransform,
1427) -> Result<Vec<u8>> {
1428 if data.is_empty() || data.iter().any(Vec::is_empty) {
1429 return Err(Error::Format("not all components have data".to_owned()));
1430 }
1431
1432 if components.len() == 1 {
1433 let component = &components[0];
1434 let mut decoded: Vec<u8> = data.remove(0);
1435
1436 let width = component.size.width as usize;
1437 let height = component.size.height as usize;
1438 let size = width * height;
1439 let line_stride = component.block_size.width as usize * component.dct_scale;
1440
1441 if usize::from(output_size.width) != line_stride {
1444 for y in 1..height {
1447 let destination_idx = y * width;
1448 let source_idx = y * line_stride;
1449 let end = source_idx + width;
1450 decoded.copy_within(source_idx..end, destination_idx);
1451 }
1452 }
1453 decoded.resize(size, 0);
1454 Ok(decoded)
1455 } else {
1456 compute_image_parallel(components, data, output_size, color_transform)
1457 }
1458}
1459
1460#[allow(clippy::type_complexity)]
1461pub(crate) fn choose_color_convert_func(
1462 component_count: usize,
1463 color_transform: ColorTransform,
1464) -> Result<fn(&[Vec<u8>], &mut [u8])> {
1465 match component_count {
1466 3 => match color_transform {
1467 ColorTransform::None => Ok(color_no_convert),
1468 ColorTransform::Grayscale => Err(Error::Format(
1469 "Invalid number of channels (3) for Grayscale data".to_string(),
1470 )),
1471 ColorTransform::RGB => Ok(color_convert_line_rgb),
1472 ColorTransform::YCbCr => Ok(color_convert_line_ycbcr),
1473 ColorTransform::CMYK => Err(Error::Format(
1474 "Invalid number of channels (3) for CMYK data".to_string(),
1475 )),
1476 ColorTransform::YCCK => Err(Error::Format(
1477 "Invalid number of channels (3) for YCCK data".to_string(),
1478 )),
1479 ColorTransform::JcsBgYcc => Err(Error::Unsupported(
1480 UnsupportedFeature::ColorTransform(ColorTransform::JcsBgYcc),
1481 )),
1482 ColorTransform::JcsBgRgb => Err(Error::Unsupported(
1483 UnsupportedFeature::ColorTransform(ColorTransform::JcsBgRgb),
1484 )),
1485 ColorTransform::Unknown => Err(Error::Format("Unknown colour transform".to_string())),
1486 },
1487 4 => match color_transform {
1488 ColorTransform::None => Ok(color_no_convert),
1489 ColorTransform::Grayscale => Err(Error::Format(
1490 "Invalid number of channels (4) for Grayscale data".to_string(),
1491 )),
1492 ColorTransform::RGB => Err(Error::Format(
1493 "Invalid number of channels (4) for RGB data".to_string(),
1494 )),
1495 ColorTransform::YCbCr => Err(Error::Format(
1496 "Invalid number of channels (4) for YCbCr data".to_string(),
1497 )),
1498 ColorTransform::CMYK => Ok(color_convert_line_cmyk),
1499 ColorTransform::YCCK => Ok(color_convert_line_ycck),
1500
1501 ColorTransform::JcsBgYcc => Err(Error::Unsupported(
1502 UnsupportedFeature::ColorTransform(ColorTransform::JcsBgYcc),
1503 )),
1504 ColorTransform::JcsBgRgb => Err(Error::Unsupported(
1505 UnsupportedFeature::ColorTransform(ColorTransform::JcsBgRgb),
1506 )),
1507 ColorTransform::Unknown => Err(Error::Format("Unknown colour transform".to_string())),
1508 },
1509 _ => panic!(),
1510 }
1511}
1512
1513fn color_convert_line_rgb(data: &[Vec<u8>], output: &mut [u8]) {
1514 assert!(data.len() == 3, "wrong number of components for rgb");
1515 let [r, g, b]: &[Vec<u8>; 3] = data.try_into().unwrap();
1516 for (((chunk, r), g), b) in output
1517 .chunks_exact_mut(3)
1518 .zip(r.iter())
1519 .zip(g.iter())
1520 .zip(b.iter())
1521 {
1522 chunk[0] = *r;
1523 chunk[1] = *g;
1524 chunk[2] = *b;
1525 }
1526}
1527
1528fn color_convert_line_ycbcr(data: &[Vec<u8>], output: &mut [u8]) {
1529 assert!(data.len() == 3, "wrong number of components for ycbcr");
1530 let [y, cb, cr]: &[_; 3] = data.try_into().unwrap();
1531
1532 #[cfg(not(feature = "platform_independent"))]
1533 let arch_specific_pixels = {
1534 if let Some(ycbcr) = crate::arch::get_color_convert_line_ycbcr() {
1535 #[allow(unsafe_code)]
1536 unsafe {
1537 ycbcr(y, cb, cr, output)
1538 }
1539 } else {
1540 0
1541 }
1542 };
1543
1544 #[cfg(feature = "platform_independent")]
1545 let arch_specific_pixels = 0;
1546
1547 for (((chunk, y), cb), cr) in output
1548 .chunks_exact_mut(3)
1549 .zip(y.iter())
1550 .zip(cb.iter())
1551 .zip(cr.iter())
1552 .skip(arch_specific_pixels)
1553 {
1554 let (r, g, b) = ycbcr_to_rgb(*y, *cb, *cr);
1555 chunk[0] = r;
1556 chunk[1] = g;
1557 chunk[2] = b;
1558 }
1559}
1560
1561fn color_convert_line_ycck(data: &[Vec<u8>], output: &mut [u8]) {
1562 assert!(data.len() == 4, "wrong number of components for ycck");
1563 let [c, m, y, k]: &[Vec<u8>; 4] = data.try_into().unwrap();
1564
1565 for ((((chunk, c), m), y), k) in output
1566 .chunks_exact_mut(4)
1567 .zip(c.iter())
1568 .zip(m.iter())
1569 .zip(y.iter())
1570 .zip(k.iter())
1571 {
1572 let (r, g, b) = ycbcr_to_rgb(*c, *m, *y);
1573 chunk[0] = r;
1574 chunk[1] = g;
1575 chunk[2] = b;
1576 chunk[3] = 255 - *k;
1577 }
1578}
1579
1580fn color_convert_line_cmyk(data: &[Vec<u8>], output: &mut [u8]) {
1581 assert!(data.len() == 4, "wrong number of components for cmyk");
1582 let [c, m, y, k]: &[Vec<u8>; 4] = data.try_into().unwrap();
1583
1584 for ((((chunk, c), m), y), k) in output
1585 .chunks_exact_mut(4)
1586 .zip(c.iter())
1587 .zip(m.iter())
1588 .zip(y.iter())
1589 .zip(k.iter())
1590 {
1591 chunk[0] = 255 - c;
1592 chunk[1] = 255 - m;
1593 chunk[2] = 255 - y;
1594 chunk[3] = 255 - k;
1595 }
1596}
1597
1598fn color_no_convert(data: &[Vec<u8>], output: &mut [u8]) {
1599 let mut output_iter = output.iter_mut();
1600
1601 for pixel in data {
1602 for d in pixel {
1603 *(output_iter.next().unwrap()) = *d;
1604 }
1605 }
1606}
1607
1608const FIXED_POINT_OFFSET: i32 = 20;
1609const HALF: i32 = (1 << FIXED_POINT_OFFSET) / 2;
1610
1611fn ycbcr_to_rgb(y: u8, cb: u8, cr: u8) -> (u8, u8, u8) {
1614 let y = y as i32 * (1 << FIXED_POINT_OFFSET) + HALF;
1615 let cb = cb as i32 - 128;
1616 let cr = cr as i32 - 128;
1617
1618 let r = clamp_fixed_point(y + stbi_f2f(1.40200) * cr);
1619 let g = clamp_fixed_point(y - stbi_f2f(0.34414) * cb - stbi_f2f(0.71414) * cr);
1620 let b = clamp_fixed_point(y + stbi_f2f(1.77200) * cb);
1621 (r, g, b)
1622}
1623
1624fn stbi_f2f(x: f32) -> i32 {
1625 (x * ((1 << FIXED_POINT_OFFSET) as f32) + 0.5) as i32
1626}
1627
1628fn clamp_fixed_point(value: i32) -> u8 {
1629 (value >> FIXED_POINT_OFFSET).clamp(0, 255) as u8
1630}
1631
1632#[cfg(test)]
1633mod tests {
1634 use super::*;
1635 use std::path::Path;
1636
1637 #[test]
1638 fn test_decode_raw_coefficients_progressive() {
1639 let path = Path::new(env!("CARGO_MANIFEST_DIR"))
1640 .join("tests/reftest/images/mozilla/jpg-progressive.jpg");
1641 let data = std::fs::read(&path).expect("failed to read test JPEG");
1642 let mut decoder = Decoder::new(&data[..]);
1643 let raw = decoder.decode_raw_coefficients().unwrap();
1644
1645 assert!(raw.width > 0);
1646 assert!(raw.height > 0);
1647 assert!(!raw.components.is_empty());
1648 assert!(!raw.components[0].is_empty());
1649 for comp in &raw.components {
1651 assert_eq!(comp.len() % 64, 0, "component length not a multiple of 64");
1652 }
1653 for (i, blocks) in raw.blocks_per_component.iter().enumerate() {
1655 assert_eq!(
1656 raw.components[i].len(),
1657 blocks * 64,
1658 "blocks_per_component mismatch for component {}",
1659 i
1660 );
1661 }
1662 assert_eq!(raw.quantization_tables.len(), raw.components.len());
1664 for qt in &raw.quantization_tables {
1666 assert!(
1667 qt.iter().any(|&v| v != 0),
1668 "quantization table is all zeros"
1669 );
1670 }
1671 }
1672
1673 #[test]
1674 fn test_decode_raw_coefficients_baseline() {
1675 let path =
1676 Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/reftest/images/extraneous-data.jpg");
1677 let data = std::fs::read(&path).expect("failed to read test JPEG");
1678 let mut decoder = Decoder::new(&data[..]);
1679 let raw = decoder.decode_raw_coefficients().unwrap();
1680
1681 assert!(raw.width > 0);
1682 assert!(raw.height > 0);
1683 assert!(!raw.components.is_empty());
1684 assert!(!raw.components[0].is_empty());
1685 for comp in &raw.components {
1687 assert_eq!(comp.len() % 64, 0, "component length not a multiple of 64");
1688 }
1689 for (i, blocks) in raw.blocks_per_component.iter().enumerate() {
1690 assert_eq!(
1691 raw.components[i].len(),
1692 blocks * 64,
1693 "blocks_per_component mismatch for component {}",
1694 i
1695 );
1696 }
1697 assert_eq!(raw.quantization_tables.len(), raw.components.len());
1698 for qt in &raw.quantization_tables {
1699 assert!(
1700 qt.iter().any(|&v| v != 0),
1701 "quantization table is all zeros"
1702 );
1703 }
1704 }
1705
1706 #[test]
1707 fn test_decode_raw_coefficients_grayscale() {
1708 let path =
1709 Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/reftest/images/mozilla/jpg-gray.jpg");
1710 let data = std::fs::read(&path).expect("failed to read test JPEG");
1711 let mut decoder = Decoder::new(&data[..]);
1712 let raw = decoder.decode_raw_coefficients().unwrap();
1713
1714 assert!(raw.width > 0);
1715 assert!(raw.height > 0);
1716 assert_eq!(raw.components.len(), 1);
1718 assert!(!raw.components[0].is_empty());
1719 assert_eq!(raw.components[0].len() % 64, 0);
1720 assert_eq!(raw.blocks_per_component.len(), 1);
1721 assert_eq!(raw.components[0].len(), raw.blocks_per_component[0] * 64);
1722 assert_eq!(raw.quantization_tables.len(), 1);
1723 }
1724
1725 #[test]
1726 fn test_decode_raw_coefficients_has_nonzero_coefficients() {
1727 let path = Path::new(env!("CARGO_MANIFEST_DIR"))
1729 .join("tests/reftest/images/mozilla/jpg-progressive.jpg");
1730 let data = std::fs::read(&path).expect("failed to read test JPEG");
1731 let mut decoder = Decoder::new(&data[..]);
1732 let raw = decoder.decode_raw_coefficients().unwrap();
1733
1734 let has_nonzero_dc = raw.components[0].chunks(64).any(|block| block[0] != 0);
1736 assert!(
1737 has_nonzero_dc,
1738 "expected at least some nonzero DC coefficients"
1739 );
1740
1741 let has_nonzero_ac = raw.components[0]
1743 .chunks(64)
1744 .any(|block| block[1..].iter().any(|&v| v != 0));
1745 assert!(
1746 has_nonzero_ac,
1747 "expected at least some nonzero AC coefficients"
1748 );
1749 }
1750}