1use alloc::string::ToString;
23use alloc::vec::Vec;
24use alloc::{format, vec};
25use core::cmp::min;
26
27use zune_core::bytestream::{ZByteReaderTrait, ZReader};
28use zune_core::colorspace::ColorSpace;
29use zune_core::log::{debug, error, warn};
30
31use crate::bitstream::BitStream;
32use crate::components::SampleRatios;
33use crate::decoder::{JpegDecoder, MAX_COMPONENTS};
34use crate::errors::DecodeErrors;
35use crate::headers::parse_sos;
36use crate::marker::Marker;
37use crate::mcu::DCT_BLOCK;
38use crate::misc::{calculate_padded_width, setup_component_params};
39
40impl<T: ZByteReaderTrait> JpegDecoder<T> {
41 #[allow(
45 clippy::needless_range_loop,
46 clippy::cast_sign_loss,
47 clippy::redundant_else,
48 clippy::too_many_lines
49 )]
50 #[inline(never)]
51 pub(crate) fn decode_mcu_ycbcr_progressive(
52 &mut self, pixels: &mut [u8]
53 ) -> Result<(), DecodeErrors> {
54 setup_component_params(self)?;
55
56 let mut mcu_height;
57
58 let mut block: [Vec<i16>; MAX_COMPONENTS] = [vec![], vec![], vec![], vec![]];
60 let mut mcu_width;
61
62 let mut seen_scans = 1;
63
64 if self.input_colorspace == ColorSpace::Luma && self.is_interleaved {
65 warn!("Grayscale image with down-sampled component, resetting component details");
66 self.reset_params();
67 }
68
69 if self.is_interleaved {
70 self.set_upsampling()?;
72 }
73 if self.is_interleaved {
74 mcu_width = self.mcu_x;
75 mcu_height = self.mcu_y;
76 } else {
77 mcu_width = (self.info.width as usize + 7) / 8;
78 mcu_height = (self.info.height as usize + 7) / 8;
79 }
80 if self.is_interleaved
81 && self.input_colorspace.num_components() > 1
82 && self.options.jpeg_get_out_colorspace().num_components() == 1
83 && (self.info.sample_ratio == SampleRatios::V
84 || self.info.sample_ratio == SampleRatios::HV)
85 {
86 mcu_height *= self.v_max;
95 mcu_height /= self.h_max;
96 self.coeff = 2;
97 }
98
99 mcu_width *= 64;
100
101 for i in 0..self.input_colorspace.num_components() {
102 let comp = &self.components[i];
103 let len = mcu_width * comp.vertical_sample * comp.horizontal_sample * mcu_height;
104
105 block[i] = vec![0; len];
106 }
107
108 let mut stream = BitStream::new_progressive(self.succ_low, self.spec_start, self.spec_end);
109
110 let result = self.parse_entropy_coded_data(&mut stream, &mut block);
112
113 if result.is_err() {
114 return if self.options.strict_mode() {
115 Err(result.err().unwrap())
116 } else {
117 error!("{}", result.err().unwrap());
118 return self.finish_progressive_decoding(&block, pixels);
120 };
121 }
122
123 let mut marker = stream
125 .marker
126 .take()
127 .ok_or(DecodeErrors::FormatStatic("Marker missing where expected"))?;
128
129 'eoi: while marker != Marker::EOI {
135 match marker {
136 Marker::SOS => {
137 parse_sos(self)?;
138
139 stream.update_progressive_params(
140 self.succ_high,
141 self.succ_low,
142 self.spec_start,
143 self.spec_end
144 );
145 let result = self.parse_entropy_coded_data(&mut stream, &mut block);
147
148 if result.is_err() {
151 return if self.options.strict_mode() {
152 Err(result.err().unwrap())
153 } else {
154 error!("{}", result.err().unwrap());
155 break 'eoi;
156 };
157 }
158 match get_marker(&mut self.stream, &mut stream) {
161 Ok(marker_n) => {
162 marker = marker_n;
163 seen_scans += 1;
164 if seen_scans > self.options.jpeg_get_max_scans() {
165 return Err(DecodeErrors::Format(format!(
166 "Too many scans, exceeded limit of {}",
167 self.options.jpeg_get_max_scans()
168 )));
169 }
170
171 stream.reset();
172 continue 'eoi;
173 }
174 Err(msg) => {
175 if self.options.strict_mode() {
176 return Err(msg);
177 }
178 error!("{:?}", msg);
179 break 'eoi;
180 }
181 }
182 }
183 Marker::RST(_n) => {
184 self.handle_rst(&mut stream)?;
185 }
186 _ => {
187 self.parse_marker_inner(marker)?;
188 }
189 }
190
191 match get_marker(&mut self.stream, &mut stream) {
192 Ok(marker_n) => {
193 marker = marker_n;
194 }
195 Err(e) => {
196 if self.options.strict_mode() {
197 return Err(e);
198 }
199 error!("{}", e);
200 break 'eoi;
204 }
205 }
206 }
207
208 self.finish_progressive_decoding(&block, pixels)
209 }
210
211 fn reset_prog_params(&mut self, stream: &mut BitStream) {
213 stream.reset();
214 self.components.iter_mut().for_each(|x| x.dc_pred = 0);
215
216 self.todo = if self.restart_interval != 0 { self.restart_interval } else { usize::MAX };
218 }
219
220 #[allow(clippy::too_many_lines, clippy::cast_sign_loss)]
221 fn parse_entropy_coded_data(
222 &mut self, stream: &mut BitStream, buffer: &mut [Vec<i16>; MAX_COMPONENTS]
223 ) -> Result<(), DecodeErrors> {
224 self.reset_prog_params(stream);
225
226 if usize::from(self.num_scans) > self.input_colorspace.num_components() {
227 return Err(DecodeErrors::Format(format!(
228 "Number of scans {} cannot be greater than number of components, {}",
229 self.num_scans,
230 self.input_colorspace.num_components()
231 )));
232 }
233 if self.num_scans == 1 {
234 if self.spec_end != 0 && self.spec_start == 0 {
236 return Err(DecodeErrors::FormatStatic(
237 "Can't merge DC and AC corrupt jpeg"
238 ));
239 }
240 let k = self.z_order[0];
243
244 if k >= self.components.len() {
245 return Err(DecodeErrors::Format(format!(
246 "Cannot find component {k}, corrupt image"
247 )));
248 }
249 let component = &self.components[k];
251
252 let mcu_width = (self.info.width as usize * component.horizontal_sample).div_ceil(self.h_max * 8);
253 let mcu_height = (self.info.height as usize * component.vertical_sample).div_ceil(self.v_max * 8);
254
255 for i in 0..mcu_height {
256 for j in 0..mcu_width {
257 if self.spec_start != 0 && self.succ_high == 0 && stream.eob_run > 0 {
258 stream.eob_run -= 1;
260 } else {
261 let start = 64 * (j + i * (self.components[k].width_stride / 8));
262
263 let data: &mut [i16; 64] = buffer
264 .get_mut(k)
265 .unwrap()
266 .get_mut(start..start + 64)
267 .ok_or(DecodeErrors::FormatStatic("Slice to Small"))?
268 .try_into()
269 .unwrap();
270
271 if self.spec_start == 0 {
272 let pos = self.components[k].dc_huff_table & (MAX_COMPONENTS - 1);
273 let dc_table = self
274 .dc_huffman_tables
275 .get(pos)
276 .ok_or(DecodeErrors::FormatStatic(
277 "No huffman table for DC component"
278 ))?
279 .as_ref()
280 .ok_or(DecodeErrors::FormatStatic(
281 "Huffman table at index {} not initialized"
282 ))?;
283
284 let dc_pred = &mut self.components[k].dc_pred;
285
286 if self.succ_high == 0 {
287 stream.decode_prog_dc_first(
289 &mut self.stream,
290 dc_table,
291 &mut data[0],
292 dc_pred
293 )?;
294 } else {
295 stream.decode_prog_dc_refine(&mut self.stream, &mut data[0])?;
297 }
298 } else {
299 let pos = self.components[k].ac_huff_table;
300 let ac_table = self
301 .ac_huffman_tables
302 .get(pos)
303 .ok_or_else(|| {
304 DecodeErrors::Format(format!(
305 "No huffman table for component:{pos}"
306 ))
307 })?
308 .as_ref()
309 .ok_or_else(|| {
310 DecodeErrors::Format(format!(
311 "Huffman table at index {pos} not initialized"
312 ))
313 })?;
314
315 if self.succ_high == 0 {
316 debug_assert!(stream.eob_run == 0, "EOB run is not zero");
317
318 stream.decode_mcu_ac_first(&mut self.stream, ac_table, data)?;
319 } else {
320 stream.decode_mcu_ac_refine(&mut self.stream, ac_table, data)?;
322 }
323 }
329 }
330
331 self.todo -= 1;
333
334 self.handle_rst_main(stream)?;
335 }
336 }
337 } else {
338 if self.spec_end != 0 {
339 return Err(DecodeErrors::HuffmanDecode(
340 "Can't merge dc and AC corrupt jpeg".to_string()
341 ));
342 }
343 for k in 0..self.num_scans {
348 let n = self.z_order[k as usize];
349
350 if n >= self.components.len() {
351 return Err(DecodeErrors::Format(format!(
352 "Cannot find component {n}, corrupt image"
353 )));
354 }
355
356 let component = &mut self.components[n];
357 let _ = self
358 .dc_huffman_tables
359 .get(component.dc_huff_table)
360 .ok_or_else(|| {
361 DecodeErrors::Format(format!(
362 "No huffman table for component:{}",
363 component.dc_huff_table
364 ))
365 })?
366 .as_ref()
367 .ok_or_else(|| {
368 DecodeErrors::Format(format!(
369 "Huffman table at index {} not initialized",
370 component.dc_huff_table
371 ))
372 })?;
373 }
374 for i in 0..self.mcu_y {
379 for j in 0..self.mcu_x {
380 for k in 0..self.num_scans {
382 let n = self.z_order[k as usize];
383 let component = &mut self.components[n];
384 let huff_table = self
385 .dc_huffman_tables
386 .get(component.dc_huff_table)
387 .ok_or(DecodeErrors::FormatStatic("No huffman table for component"))?
388 .as_ref()
389 .ok_or(DecodeErrors::FormatStatic(
390 "Huffman table at index not initialized"
391 ))?;
392
393 for v_samp in 0..component.vertical_sample {
394 for h_samp in 0..component.horizontal_sample {
395 let x2 = j * component.horizontal_sample + h_samp;
396 let y2 = i * component.vertical_sample + v_samp;
397 let position = 64 * (x2 + y2 * component.width_stride / 8);
398 let buf_n = &mut buffer[n];
399
400 let Some(data) = &mut buf_n.get_mut(position) else {
401 return Err(DecodeErrors::FormatStatic("Invalid image"));
404 };
405
406 if self.succ_high == 0 {
407 stream.decode_prog_dc_first(
408 &mut self.stream,
409 huff_table,
410 data,
411 &mut component.dc_pred
412 )?;
413 } else {
414 stream.decode_prog_dc_refine(&mut self.stream, data)?;
415 }
416 }
417 }
418 }
419 self.todo -= 1;
422 self.handle_rst_main(stream)?;
424 }
425 }
426 }
427 return Ok(());
428 }
429
430 pub(crate) fn handle_rst_main(&mut self, stream: &mut BitStream) -> Result<(), DecodeErrors> {
431 if self.todo == 0 {
432 stream.refill(&mut self.stream)?;
433 }
434
435 if self.todo == 0
436 && self.restart_interval != 0
437 && stream.marker.is_none()
438 && !stream.seen_eoi
439 {
440 let _start = self.stream.position()?;
444 let marker = get_marker(&mut self.stream, stream);
446
447 if let Ok(marker) = marker {
454 let _end = self.stream.position()?;
455 stream.marker = Some(marker);
456 warn!(
458 "{} Extraneous bytes before marker {:?}",
459 _end - _start,
460 marker
461 );
462 } else {
463 warn!("RST marker was not found, where expected, image may be garbled")
464 }
465 }
466 if self.todo == 0 {
467 self.handle_rst(stream)?
468 }
469 Ok(())
470 }
471 #[allow(clippy::too_many_lines)]
472 #[allow(clippy::needless_range_loop, clippy::cast_sign_loss)]
473 fn finish_progressive_decoding(
474 &mut self, block: &[Vec<i16>; MAX_COMPONENTS], pixels: &mut [u8]
475 ) -> Result<(), DecodeErrors> {
476 let mcu_height = if self.is_interleaved {
496 self.mcu_y
497 } else {
498 self.info.height.div_ceil(8) as usize
501 };
502
503 let is_hv = usize::from(self.is_interleaved);
505 let upsampler_scratch_size = is_hv * self.components[0].width_stride;
506 let width = usize::from(self.info.width);
507 let padded_width = calculate_padded_width(width, self.info.sample_ratio);
508
509 let mut upsampler_scratch_space = vec![0; upsampler_scratch_size];
510 let mut tmp = [0_i32; DCT_BLOCK];
511
512 for (pos, comp) in self.components.iter_mut().enumerate() {
513 if min(
518 self.options.jpeg_get_out_colorspace().num_components() - 1,
519 pos
520 ) == pos
521 || self.input_colorspace == ColorSpace::YCCK
522 || self.input_colorspace == ColorSpace::CMYK
523 {
524 let len = comp.width_stride * comp.vertical_sample * 8;
528
529 comp.needed = true;
530 comp.raw_coeff = vec![0; len];
531 } else {
532 comp.needed = false;
533 }
534 }
535
536 let mut pixels_written = 0;
537
538 for i in 0..mcu_height {
540 'component: for (position, component) in &mut self.components.iter_mut().enumerate() {
541 if !component.needed {
542 continue 'component;
543 }
544 let qt_table = &component.quantization_table;
545
546 let step = block[position].len() / mcu_height;
554 let start = i * step;
556
557 let slice = &block[position][start..start + step];
558
559 let temp_channel = &mut component.raw_coeff;
560
561 let mcu_x = component.width_stride / 8;
566
567 for k in 0..component.vertical_sample {
569 for j in 0..mcu_x {
570 let width_stride = k * 8 * component.width_stride;
573 let start = j * 64 + width_stride;
574
575 let Some(qt_slice) = slice.get(start..start + 64) else {
577 return Err(DecodeErrors::FormatStatic(
578 "Invalid slice , would panic, invalid image"
579 ));
580 };
581 for ((x, out), qt_val) in
583 qt_slice.iter().zip(tmp.iter_mut()).zip(qt_table.iter())
584 {
585 *out = i32::from(*x) * qt_val;
586 }
587 let sl = &mut temp_channel[component.idct_pos..];
589
590 component.idct_pos += 8;
591 (self.idct_func)(&mut tmp, sl, component.width_stride);
593 }
594 component.idct_pos += 7 * component.width_stride;
601 }
602 component.idct_pos = 0;
603 }
604
605 self.post_process(
607 pixels,
608 i,
609 mcu_height,
610 width,
611 padded_width,
612 &mut pixels_written,
613 &mut upsampler_scratch_space
614 )?;
615 }
616
617 debug!("Finished decoding image");
618
619 return Ok(());
620 }
621 pub(crate) fn reset_params(&mut self) {
622 self.h_max = 1;
631 self.v_max = 1;
632 self.info.sample_ratio = SampleRatios::None;
633 self.is_interleaved = false;
634 self.components[0].vertical_sample = 1;
635 self.components[0].width_stride = (((self.info.width as usize) + 7) / 8) * 8;
636 self.components[0].horizontal_sample = 1;
637 }
638}
639
640pub fn get_marker<T>(
644 reader: &mut ZReader<T>, stream: &mut BitStream
645) -> Result<Marker, DecodeErrors>
646where
647 T: ZByteReaderTrait
648{
649 if let Some(marker) = stream.marker {
650 stream.marker = None;
651 return Ok(marker);
652 }
653
654 while !reader.eof()? {
657 let marker = reader.read_u8_err()?;
658
659 if marker == 255 {
660 let mut r = reader.read_u8_err()?;
661 while r == 0xFF {
663 r = reader.read_u8_err()?;
664 }
665
666 if r != 0 {
667 return Marker::from_u8(r)
668 .ok_or_else(|| DecodeErrors::Format(format!("Unknown marker 0xFF{r:X}")));
669 }
670 }
671 }
672 return Err(DecodeErrors::ExhaustedData);
673}
674
675#[cfg(test)]
676mod tests{
677 use zune_core::bytestream::ZCursor;
678 use crate::JpegDecoder;
679
680 #[test]
681 fn test_progressive_dri_420_color() {
682 const JPEG: &[u8] = &[
686 0xff, 0xd8, 0xff, 0xdb, 0x00, 0xc5, 0x00, 0x03, 0x04, 0x04, 0x06, 0x04, 0x06, 0x06,
687 0x06, 0x06, 0x06, 0x07, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
688 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x08, 0x09, 0x08, 0x09, 0x09, 0x08, 0x09,
689 0x08, 0x09, 0x08, 0x0a, 0x0a, 0x0a, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x09,
690 0x0a, 0x0c, 0x0c, 0x0c, 0x0a, 0x0c, 0x0b, 0x0b, 0x0c, 0x0d, 0x0c, 0x0d, 0x0b, 0x0b,
691 0x09, 0x01, 0x02, 0x04, 0x04, 0x07, 0x06, 0x07, 0x08, 0x07, 0x07, 0x08, 0x07, 0x08,
692 0x08, 0x08, 0x07, 0x0a, 0x0b, 0x0d, 0x0d, 0x0b, 0x0a, 0x0d, 0x0b, 0x0b, 0x0c, 0x0b,
693 0x0b, 0x0d, 0x15, 0x18, 0x13, 0x0c, 0x0c, 0x13, 0x18, 0x15, 0x10, 0x49, 0x12, 0x0d,
694 0x12, 0x49, 0x10, 0x12, 0x15, 0x0b, 0x0b, 0x15, 0x12, 0x1d, 0x0b, 0x15, 0x0b, 0x1d,
695 0x2a, 0x20, 0x20, 0x2a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x50, 0x02, 0x03, 0x03, 0x03,
696 0x05, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x04, 0x05, 0x04, 0x06, 0x05, 0x05,
697 0x05, 0x05, 0x05, 0x05, 0x06, 0x05, 0x05, 0x04, 0x05, 0x05, 0x06, 0x07, 0x06, 0x05,
698 0x06, 0x06, 0x05, 0x06, 0x07, 0x06, 0x06, 0x06, 0x04, 0x06, 0x06, 0x06, 0x06, 0x06,
699 0x07, 0x07, 0x06, 0x06, 0x07, 0x05, 0x06, 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0a,
700 0x0b, 0x0a, 0x0a, 0x0a, 0x52, 0xff, 0xc2, 0x00, 0x11, 0x08, 0x00, 0x10, 0x00, 0x10,
701 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x02, 0xff, 0xc4, 0x00, 0x17,
702 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
703 0x00, 0x00, 0x00, 0x07, 0x02, 0x05, 0x08, 0xff, 0xdd, 0x00, 0x04, 0x00, 0x04, 0xff,
704 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xca, 0xac, 0xcc, 0x4c, 0xdf,
705 0xff, 0xda, 0x00, 0x08, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xff, 0xda, 0x00,
706 0x08, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xff, 0xc4, 0x00, 0x1f, 0x10, 0x00,
707 0x01, 0x04, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
708 0x00, 0x00, 0x05, 0x06, 0x21, 0x31, 0x07, 0xf1, 0x20, 0x61, 0xa1, 0xc1, 0xe1, 0xff,
709 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x01, 0x02, 0x00, 0x49, 0x6a, 0x24, 0xb5, 0x12,
710 0x5a, 0x89, 0x2d, 0x4f, 0xff, 0xda, 0x00, 0x08, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00,
711 0x59, 0x78, 0x7f, 0xff, 0xda, 0x00, 0x08, 0x01, 0x03, 0x00, 0x01, 0x02, 0x00, 0xc3,
712 0xd9, 0x47, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x03, 0x3f, 0x02, 0xe1, 0xff,
713 0xda, 0x00, 0x08, 0x01, 0x02, 0x00, 0x03, 0x3f, 0x02, 0x3f, 0xff, 0xda, 0x00, 0x08,
714 0x01, 0x03, 0x00, 0x03, 0x3f, 0x02, 0xa9, 0x3f, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
715 0x00, 0x03, 0x3f, 0x21, 0xa8, 0x3a, 0x2a, 0x0a, 0x83, 0xff, 0xda, 0x00, 0x08, 0x01,
716 0x02, 0x00, 0x03, 0x3f, 0x21, 0xec, 0xff, 0xda, 0x00, 0x08, 0x01, 0x03, 0x00, 0x03,
717 0x3f, 0x21, 0xf1, 0x1f, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x03, 0x3f, 0x10,
718 0xd2, 0x57, 0xa9, 0xa4, 0xd2, 0x7f, 0xff, 0xda, 0x00, 0x08, 0x01, 0x02, 0x00, 0x03,
719 0x3f, 0x10, 0xb3, 0xff, 0xda, 0x00, 0x08, 0x01, 0x03, 0x00, 0x03, 0x3f, 0x10, 0xfa,
720 0x8f, 0xff, 0xd9,
721 ];
722
723 let mut decoder = JpegDecoder::new(ZCursor::new(JPEG));
724 let pixels = decoder.decode().unwrap();
725 let (w, h) = decoder.dimensions().unwrap();
726 assert_eq!((w, h), (16, 16));
727
728 let all_gray = pixels.chunks(3).all(|p| p[0] == p[1] && p[1] == p[2]);
731 assert!(
732 !all_gray,
733 "Output is grayscale (R==G==B for all pixels). \
734 Expected color output for progressive 4:2:0 JPEG with DRI."
735 );
736
737 assert!(pixels[2] > 100, "Blue channel should be around 128, got {}", pixels[2]);
739 }
740
741 #[test]
742 fn make_test(){
743 let data = ZCursor::new([255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 0, 2, 0, 28, 0, 28, 0, 0, 255, 219, 0, 67, 0, 40, 28, 30, 20, 30, 25, 40, 35, 33, 35, 45, 43, 40, 48, 60, 100, 65, 60, 55, 55, 60, 123, 88, 93, 65, 100, 145, 128, 153, 150, 143, 128, 140, 138, 160, 180, 230, 195, 160, 170, 218, 173, 138, 140, 200, 255, 203, 218, 255, 238, 245, 255, 101, 0, 62, 8, 255, 255, 250, 255, 230, 253, 255, 17, 255, 219, 0, 67, 1, 43, 45, 45, 42, 60, 48, 60, 118, 65, 65, 118, 248, 165, 140, 165, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 241, 255, 255, 255, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 255, 192, 0, 17, 8, 0, 32, 0, 32, 3, 2, 17, 0, 1, 34, 1, 3, 17, 1, 255, 196, 0, 24, 0, 1, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 3, 0, 1, 4, 255, 196, 0, 37, 16, 0, 2, 2, 1, 4, 1, 3, 5, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 17, 0, 4, 18, 33, 48, 34, 65, 81, 113, 19, 20, 51, 97, 161, 255, 196, 0, 22, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 255, 196, 0, 26, 17, 1, 0, 2, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 17, 18, 38, 65, 255, 218, 0, 12, 3, 1, 0, 2, 17, 3, 17, 0, 63, 0, 175, 119, 49, 197, 184, 2, 0, 0, 0, 16, 13, 129, 103, 161, 102, 178, 115, 125, 202, 68, 236, 173, 25, 42, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 38, 0, 0, 0, 0, 250, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 67, 1, 43, 45, 45, 60, 48, 60, 118, 65, 65, 118, 248, 165, 140, 165, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 241, 255, 255, 255, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 255, 192, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 255, 192, 0, 17, 8, 0, 32, 0, 32, 3, 1, 34, 0, 2, 17, 1, 3, 17, 1, 255, 196, 0, 24, 0, 1, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 126, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 198]);
744 let mut decoder = JpegDecoder::new(data);
745 decoder.decode().unwrap();
746
747 }
748}