1use bytemuck::Pod;
3use crate::{colorspace::ColorSpace, PixelDensity};
4use crate::colorspace::ColorSpaceExt;
5use crate::component::CompInfo;
6use crate::component::CompInfoExt;
7use crate::errormgr::unwinding_error_mgr;
8use crate::errormgr::ErrorMgr;
9use crate::ffi;
10use crate::ffi::jpeg_decompress_struct;
11use crate::ffi::DCTSIZE;
12use crate::ffi::JPEG_LIB_VERSION;
13use crate::ffi::J_COLOR_SPACE as COLOR_SPACE;
14use crate::marker::Marker;
15use crate::readsrc::SourceMgr;
16use libc::fdopen;
17use std::cmp::min;
18use std::fs::File;
19use std::io;
20use std::io::BufRead;
21use std::io::BufReader;
22use std::marker::PhantomData;
23use std::mem;
24use std::mem::MaybeUninit;
25use std::os::raw::{c_int, c_uchar, c_ulong, c_void};
26use std::path::Path;
27use std::ptr;
28use std::ptr::addr_of_mut;
29use std::slice;
30
31const MAX_MCU_HEIGHT: usize = 16;
32const MAX_COMPONENTS: usize = 4;
33
34pub const NO_MARKERS: &[Marker] = &[];
38
39pub const ALL_MARKERS: &[Marker] = &[
46 Marker::APP(0), Marker::APP(1), Marker::APP(2), Marker::APP(3), Marker::APP(4),
47 Marker::APP(5), Marker::APP(6), Marker::APP(7), Marker::APP(8), Marker::APP(9),
48 Marker::APP(10), Marker::APP(11), Marker::APP(12), Marker::APP(13), Marker::APP(14),
49 Marker::COM,
50];
51
52#[derive(Clone, Copy, Debug)]
54pub enum DctMethod {
55 IntegerSlow,
57 IntegerFast,
59 Float,
61}
62
63pub struct DecompressBuilder<'markers> {
65 save_markers: &'markers [Marker],
66 err_mgr: Option<Box<ErrorMgr>>,
67}
68
69#[deprecated(note = "Renamed to DecompressBuilder")]
70#[doc(hidden)]
71pub use DecompressBuilder as DecompressConfig;
72
73impl<'markers> DecompressBuilder<'markers> {
74 #[inline]
75 #[must_use]
76 pub const fn new() -> Self {
77 DecompressBuilder {
78 err_mgr: None,
79 save_markers: NO_MARKERS,
80 }
81 }
82
83 #[inline]
84 #[must_use]
85 pub fn with_err(mut self, err: ErrorMgr) -> Self {
86 self.err_mgr = Some(Box::new(err));
87 self
88 }
89
90 #[inline]
91 #[must_use]
92 pub const fn with_markers(mut self, save_markers: &'markers [Marker]) -> Self {
93 self.save_markers = save_markers;
94 self
95 }
96
97 #[inline]
98 pub fn from_path<P: AsRef<Path>>(self, path: P) -> io::Result<Decompress<BufReader<File>>> {
99 self.from_file(File::open(path.as_ref())?)
100 }
101
102 #[inline]
105 pub fn from_file(self, file: File) -> io::Result<Decompress<BufReader<File>>> {
106 self.from_reader(BufReader::new(file))
107 }
108
109 #[inline]
111 pub fn from_mem(self, mem: &[u8]) -> io::Result<Decompress<&[u8]>> {
112 self.from_reader(mem)
113 }
114
115 #[inline]
117 pub fn from_reader<R: BufRead>(self, reader: R) -> io::Result<Decompress<R>> {
118 Decompress::from_builder_and_reader(self, reader)
119 }
120}
121
122impl<'markers> Default for DecompressBuilder<'markers> {
123 #[inline]
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129pub struct Decompress<R> {
140 cinfo: jpeg_decompress_struct,
141 err_mgr: Box<ErrorMgr>,
142 src_mgr: Option<Box<SourceMgr<R>>>,
143}
144
145pub struct MarkerData<'a> {
147 pub marker: Marker,
148 pub data: &'a [u8],
149}
150
151pub struct MarkerIter<'a> {
153 marker_list: *mut ffi::jpeg_marker_struct,
154 _references: ::std::marker::PhantomData<MarkerData<'a>>,
155}
156
157impl<'a> Iterator for MarkerIter<'a> {
158 type Item = MarkerData<'a>;
159 #[inline]
160 fn next(&mut self) -> Option<MarkerData<'a>> {
161 if self.marker_list.is_null() {
162 return None;
163 }
164 unsafe {
165 let last = &*self.marker_list;
166 self.marker_list = last.next;
167 Some(MarkerData {
168 marker: last.marker.into(),
169 data: ::std::slice::from_raw_parts(last.data, last.data_length as usize),
170 })
171 }
172 }
173}
174
175impl Decompress<()> {
176 #[inline]
178 #[doc(hidden)]
179 #[must_use]
180 pub fn with_err(err_mgr: ErrorMgr) -> DecompressBuilder<'static> {
181 DecompressBuilder::new().with_err(err_mgr)
182 }
183
184 #[inline]
186 #[doc(hidden)]
187 #[must_use]
188 pub fn with_markers(markers: &[Marker]) -> DecompressBuilder<'_> {
189 DecompressBuilder::new().with_markers(markers)
190 }
191
192 #[deprecated(note = "renamed to builder()")]
194 #[doc(hidden)]
195 #[must_use]
196 pub fn config() -> DecompressBuilder<'static> {
197 DecompressBuilder::new()
198 }
199
200 #[inline]
202 #[must_use]
203 pub const fn builder() -> DecompressBuilder<'static> {
204 DecompressBuilder::new()
205 }
206}
207
208impl Decompress<BufReader<File>> {
209 #[inline]
211 pub fn new_path<P: AsRef<Path>>(path: P) -> io::Result<Self> {
212 DecompressBuilder::new().from_path(path)
213 }
214
215 #[inline]
217 pub fn new_file(file: File) -> io::Result<Self> {
218 DecompressBuilder::new().from_file(file)
219 }
220}
221
222impl<'mem> Decompress<&'mem [u8]> {
223 #[inline]
225 pub fn new_mem(mem: &'mem [u8]) -> io::Result<Self> {
226 DecompressBuilder::new().from_mem(mem)
227 }
228}
229
230impl<R> Decompress<R> {
231 #[inline]
233 pub fn new_reader(reader: R) -> io::Result<Self> where R: BufRead {
234 DecompressBuilder::new().from_reader(reader)
235 }
236
237 fn from_builder_and_reader(builder: DecompressBuilder<'_>, reader: R) -> io::Result<Self> where R: BufRead {
238 let src_mgr = Box::new(SourceMgr::new(reader)?);
239 let err_mgr = builder.err_mgr.unwrap_or_else(unwinding_error_mgr);
240 unsafe {
241 let mut newself = Decompress {
242 cinfo: mem::zeroed(),
243 src_mgr: Some(src_mgr),
244 err_mgr,
245 };
246 let src_ptr = newself.src_mgr.as_mut().unwrap().iface_c_ptr();
247 newself.cinfo.common.err = addr_of_mut!(*newself.err_mgr);
248 ffi::jpeg_create_decompress(&mut newself.cinfo);
249 newself.cinfo.src = src_ptr;
250 for &marker in builder.save_markers {
251 newself.save_marker(marker);
252 }
253 newself.read_header()?;
254 Ok(newself)
255 }
256 }
257
258 #[inline]
259 #[must_use]
260 pub fn components(&self) -> &[CompInfo] {
261 if self.cinfo.comp_info.is_null() {
262 return &[];
263 }
264 unsafe {
265 slice::from_raw_parts(self.cinfo.comp_info, self.cinfo.num_components as usize)
266 }
267 }
268
269 #[inline]
270 pub(crate) fn components_mut(&mut self) -> &mut [CompInfo] {
271 if self.cinfo.comp_info.is_null() {
272 return &mut [];
273 }
274 unsafe {
275 slice::from_raw_parts_mut(self.cinfo.comp_info, self.cinfo.num_components as usize)
276 }
277 }
278
279 #[inline]
281 fn read_header(&mut self) -> io::Result<()> {
282 let res = unsafe { ffi::jpeg_read_header(&mut self.cinfo, 0) };
284 if res == 1 {
285 Ok(())
286 } else {
287 Err(io::Error::new(io::ErrorKind::Other, "no image in the JPEG file"))
288 }
289 }
290
291 #[inline]
292 #[must_use]
293 pub fn color_space(&self) -> COLOR_SPACE {
294 self.cinfo.jpeg_color_space
295 }
296
297 #[inline]
299 #[must_use]
300 pub fn gamma(&self) -> f64 {
301 self.cinfo.output_gamma
302 }
303
304 #[inline]
307 #[must_use]
308 pub fn pixel_density(&mut self) -> Option<PixelDensity> {
309 Some(PixelDensity {
310 unit: match self.cinfo.density_unit {
311 0 => crate::PixelDensityUnit::PixelAspectRatio,
312 1 => crate::PixelDensityUnit::Inches,
313 2 => crate::PixelDensityUnit::Centimeters,
314 _ => return None,
315 },
316 x: self.cinfo.X_density,
317 y: self.cinfo.Y_density,
318 })
319 }
320
321 #[inline]
323 #[must_use]
324 pub fn markers(&self) -> MarkerIter<'_> {
325 MarkerIter {
326 marker_list: self.cinfo.marker_list,
327 _references: PhantomData,
328 }
329 }
330
331 #[inline]
332 fn save_marker(&mut self, marker: Marker) {
333 unsafe {
334 ffi::jpeg_save_markers(&mut self.cinfo, marker.into(), 0xFFFF);
335 }
336 }
337
338 #[inline]
340 #[must_use]
341 pub fn size(&self) -> (usize, usize) {
342 (self.width(), self.height())
343 }
344
345 #[inline]
346 #[must_use]
347 pub fn width(&self) -> usize {
348 self.cinfo.image_width as usize
349 }
350
351 #[inline]
352 #[must_use]
353 pub fn height(&self) -> usize {
354 self.cinfo.image_height as usize
355 }
356
357 #[inline(always)]
359 pub fn rgb(self) -> io::Result<DecompressStarted<R>> {
360 self.to_colorspace(ffi::J_COLOR_SPACE::JCS_RGB)
361 }
362
363 pub fn to_colorspace(mut self, colorspace: ColorSpace) -> io::Result<DecompressStarted<R>> {
365 self.cinfo.out_color_space = colorspace;
366 DecompressStarted::start_decompress(self)
367 }
368
369 #[inline(always)]
371 pub fn rgba(self) -> io::Result<DecompressStarted<R>> {
372 self.to_colorspace(ffi::J_COLOR_SPACE::JCS_EXT_RGBA)
373 }
374
375 #[inline(always)]
377 pub fn grayscale(self) -> io::Result<DecompressStarted<R>> {
378 self.to_colorspace(ffi::J_COLOR_SPACE::JCS_GRAYSCALE)
379 }
380
381 pub fn dct_method(&mut self, method: DctMethod) {
383 self.cinfo.dct_method = match method {
384 DctMethod::IntegerSlow => ffi::J_DCT_METHOD::JDCT_ISLOW,
385 DctMethod::IntegerFast => ffi::J_DCT_METHOD::JDCT_IFAST,
386 DctMethod::Float => ffi::J_DCT_METHOD::JDCT_FLOAT,
387 }
388 }
389
390 pub fn do_fancy_upsampling(&mut self, value: bool) {
394 self.cinfo.do_fancy_upsampling = ffi::boolean::from(value);
395 }
396
397 pub fn do_block_smoothing(&mut self, value: bool) {
404 self.cinfo.do_block_smoothing = ffi::boolean::from(value);
405 }
406
407 #[inline(always)]
408 pub fn raw(mut self) -> io::Result<DecompressStarted<R>> {
409 self.cinfo.raw_data_out = ffi::boolean::from(true);
410 DecompressStarted::start_decompress(self)
411 }
412
413 fn out_color_space(&self) -> ColorSpace {
414 self.cinfo.out_color_space
415 }
416
417 pub fn image(self) -> io::Result<Format<R>> {
419 use crate::ffi::J_COLOR_SPACE::{JCS_CMYK, JCS_GRAYSCALE, JCS_RGB};
420 match self.out_color_space() {
421 JCS_RGB => Ok(Format::RGB(DecompressStarted::start_decompress(self)?)),
422 JCS_CMYK => Ok(Format::CMYK(DecompressStarted::start_decompress(self)?)),
423 JCS_GRAYSCALE => Ok(Format::Gray(DecompressStarted::start_decompress(self)?)),
424 _ => Ok(Format::RGB(self.rgb()?)),
425 }
426 }
427
428 #[track_caller]
432 #[inline]
433 pub fn scale(&mut self, numerator: u8) {
434 assert!(1 <= numerator && numerator <= 16, "numerator must be between 1 and 16");
435 self.cinfo.scale_num = numerator.into();
436 self.cinfo.scale_denom = 8;
437 }
438}
439
440pub enum Format<R> {
442 RGB(DecompressStarted<R>),
443 Gray(DecompressStarted<R>),
444 CMYK(DecompressStarted<R>),
445}
446
447pub struct DecompressStarted<R> {
449 dec: Decompress<R>,
450}
451
452impl<R> DecompressStarted<R> {
453 fn start_decompress(dec: Decompress<R>) -> io::Result<Self> {
454 let mut dec = Self { dec };
455 if 0 != unsafe { ffi::jpeg_start_decompress(&mut dec.dec.cinfo) } {
456 Ok(dec)
457 } else {
458 io_suspend_err()
459 }
460 }
461
462 #[must_use]
463 pub fn color_space(&self) -> ColorSpace {
464 self.dec.out_color_space()
465 }
466
467 #[inline(always)]
469 #[must_use]
470 pub fn min_flat_buffer_size(&self) -> usize {
471 self.color_space().num_components() * self.width() * self.height()
472 }
473
474 fn can_read_more_scanlines(&self) -> bool {
475 self.dec.cinfo.output_scanline < self.dec.cinfo.output_height
476 }
477
478 #[track_caller]
480 pub fn read_raw_data(&mut self, image_dest: &mut [&mut Vec<ffi::JSAMPLE>]) {
481 while self.can_read_more_scanlines() {
482 self.read_raw_data_chunk(image_dest);
483 }
484 }
485
486 #[track_caller]
487 fn read_raw_data_chunk(&mut self, image_dest: &mut [&mut Vec<ffi::JSAMPLE>]) {
488 assert!(0 != self.dec.cinfo.raw_data_out, "Raw data not set");
489
490 let mcu_height = self.dec.cinfo.max_v_samp_factor as usize * DCTSIZE;
491 if mcu_height > MAX_MCU_HEIGHT {
492 panic!("Subsampling factor too large");
493 }
494
495 let num_components = self.dec.components().len();
496 if num_components > MAX_COMPONENTS || num_components > image_dest.len() {
497 panic!("Too many components. Image has {}, destination vector has {} (max supported is {})", num_components, image_dest.len(), MAX_COMPONENTS);
498 }
499
500 unsafe {
501 let mut row_ptrs = [[ptr::null_mut::<ffi::JSAMPLE>(); MAX_MCU_HEIGHT]; MAX_COMPONENTS];
502 let mut comp_ptrs = [ptr::null_mut::<*mut ffi::JSAMPLE>(); MAX_COMPONENTS];
503 for ((comp_info, comp_dest), (comp_ptrs, row_ptrs)) in self.dec.components().iter().zip(&mut *image_dest).zip(comp_ptrs.iter_mut().zip(row_ptrs.iter_mut())) {
504 let row_stride = comp_info.row_stride();
505
506 let comp_height = comp_info.v_samp_factor as usize * DCTSIZE;
507 let required_len = comp_height * row_stride;
508 comp_dest.try_reserve(required_len).expect("oom");
509 let comp_dest = &mut comp_dest.spare_capacity_mut()[..required_len];
510
511 for (row_ptr, comp_dest) in row_ptrs.iter_mut().zip(comp_dest.chunks_exact_mut(row_stride)).take(comp_height) {
513 *row_ptr = comp_dest.as_mut_ptr().cast();
514 }
515 *comp_ptrs = row_ptrs.as_mut_ptr();
516 }
517
518 let lines_read = ffi::jpeg_read_raw_data(&mut self.dec.cinfo, comp_ptrs.as_mut_ptr(), mcu_height as u32) as usize;
519
520 assert_eq!(lines_read, mcu_height); for (comp_info, comp_dest) in self.dec.components().iter().zip(image_dest) {
523 let row_stride = comp_info.row_stride();
524
525 let comp_height = comp_info.v_samp_factor as usize * DCTSIZE;
526 let original_len = comp_dest.len();
527 let required_len = comp_height * row_stride;
528 debug_assert!(original_len + required_len <= comp_dest.capacity());
529 comp_dest.set_len(original_len + required_len);
530 }
531 }
532 }
533
534 #[must_use]
535 pub fn width(&self) -> usize {
536 self.dec.cinfo.output_width as usize
537 }
538
539 #[must_use]
540 pub fn height(&self) -> usize {
541 self.dec.cinfo.output_height as usize
542 }
543
544 pub fn read_scanlines<T: Pod>(&mut self) -> io::Result<Vec<T>> {
549 let num_components = self.color_space().num_components();
550 if num_components != mem::size_of::<T>() && mem::size_of::<T>() != 1 {
551 return Err(io::Error::new(
552 io::ErrorKind::Unsupported,
553 format!("pixel size must have {num_components} bytes, but has {}", mem::size_of::<T>()),
554 ));
555 }
556 let width = self.width();
557 let height = self.height();
558 let mut image_dst: Vec<T> = Vec::new();
559 let required_len = height * width * (num_components / mem::size_of::<T>());
560 image_dst.try_reserve_exact(required_len).map_err(|_| io::ErrorKind::OutOfMemory)?;
561 let read_len = self.read_scanlines_into_uninit(&mut image_dst.spare_capacity_mut()[..required_len])?.len();
562 if read_len <= required_len {
563 unsafe { image_dst.set_len(read_len); }
564 }
565 Ok(image_dst)
566 }
567
568 pub fn read_scanlines_into<'dest, T: Pod>(&mut self, dest: &'dest mut [T]) -> io::Result<&'dest mut [T]> {
573 let dest_uninit = unsafe {
574 std::mem::transmute::<&'dest mut [T], &'dest mut [MaybeUninit<T>]>(dest)
575 };
576 self.read_scanlines_into_uninit(dest_uninit)
577 }
578
579 pub fn read_scanlines_into_uninit<'dest, T: Pod>(&mut self, dest: &'dest mut [MaybeUninit<T>]) -> io::Result<&'dest mut [T]> {
581 let num_components = self.color_space().num_components();
582 let item_size = if mem::size_of::<T>() == 1 {
583 num_components
584 } else if num_components == mem::size_of::<T>() {
585 1
586 } else {
587 return Err(io::Error::new(
588 io::ErrorKind::Unsupported,
589 format!("pixel size must have {num_components} bytes, but has {}", mem::size_of::<T>()),
590 ));
591 };
592 let width = self.width();
593 let height = self.height();
594 let line_width = width * item_size;
595 if dest.len() % line_width != 0 {
596 return Err(io::Error::new(
597 io::ErrorKind::Unsupported,
598 format!("destination slice length must be multiple of {width}x{num_components} bytes long, got {}B", std::mem::size_of_val(dest)),
599 ));
600 }
601 for row in dest.chunks_exact_mut(line_width) {
602 if !self.can_read_more_scanlines() {
603 return Err(io::ErrorKind::UnexpectedEof.into());
604 }
605 let start_line = self.dec.cinfo.output_scanline as usize;
606 let mut row_ptr = row.as_mut_ptr().cast::<ffi::JSAMPLE>();
607 let rows = std::ptr::addr_of_mut!(row_ptr);
608 unsafe {
609 let rows_read = ffi::jpeg_read_scanlines(&mut self.dec.cinfo, rows, 1) as usize;
610 debug_assert_eq!(start_line + rows_read, self.dec.cinfo.output_scanline as usize, "{start_line}+{rows_read} != {} of {height}", self.dec.cinfo.output_scanline);
611 if 0 == rows_read {
612 return Err(io::ErrorKind::UnexpectedEof.into());
613 }
614 }
615 }
616 let dest_init = unsafe {
617 std::mem::transmute::<&'dest mut [MaybeUninit<T>], &'dest mut [T]>(dest)
618 };
619 Ok(dest_init)
620 }
621
622 #[deprecated(note = "use read_scanlines::<u8>")]
623 #[doc(hidden)]
624 pub fn read_scanlines_flat(&mut self) -> io::Result<Vec<u8>> {
625 self.read_scanlines()
626 }
627
628 #[deprecated(note = "use read_scanlines_into::<u8>")]
629 #[doc(hidden)]
630 pub fn read_scanlines_flat_into<'dest>(&mut self, dest: &'dest mut [u8]) -> io::Result<&'dest mut [u8]> {
631 self.read_scanlines_into(dest)
632 }
633
634 #[must_use]
635 pub fn components(&self) -> &[CompInfo] {
636 self.dec.components()
637 }
638
639 #[deprecated(note = "too late to mutate, use components()")]
640 #[doc(hidden)]
641 pub fn components_mut(&mut self) -> &[CompInfo] {
642 self.dec.components_mut()
643 }
644
645 #[deprecated(note = "use finish()")]
646 #[doc(hidden)]
647 #[must_use]
648 pub fn finish_decompress(mut self) -> bool {
649 self.finish_internal().is_ok()
650 }
651
652 pub fn finish_into_inner(mut self) -> io::Result<R> where R: BufRead {
654 self.finish_internal()?;
655 self.dec.cinfo.src = ptr::null_mut();
656 let mgr = self.dec.src_mgr.take().ok_or(io::ErrorKind::Other)?;
657 Ok(mgr.into_inner())
658 }
659
660 #[inline]
661 pub fn finish(mut self) -> io::Result<()> {
662 self.finish_internal()
663 }
664
665 #[inline]
666 fn finish_internal(&mut self) -> io::Result<()> {
667 if 0 != unsafe { ffi::jpeg_finish_decompress(&mut self.dec.cinfo) } {
668 Ok(())
669 } else {
670 io_suspend_err()
671 }
672 }
673}
674
675#[cold]
676fn io_suspend_err<T>() -> io::Result<T> {
677 Err(io::ErrorKind::WouldBlock.into())
678}
679
680impl<R> Drop for Decompress<R> {
681 fn drop(&mut self) {
682 unsafe {
683 ffi::jpeg_destroy_decompress(&mut self.cinfo);
684 }
685 }
686}
687
688#[test]
689fn read_incomplete_file() {
690 use crate::colorspace::{ColorSpace, ColorSpaceExt};
691 use std::fs::File;
692 use std::io::Read;
693
694 let data = std::fs::read("tests/test.jpg").unwrap();
695 assert_eq!(2169, data.len());
696
697 for l in [data.len()/2, data.len()/3, data.len()/4, data.len()-4, data.len()-3, data.len()-2, data.len()-1] {
699 let dinfo = Decompress::new_mem(&data[..l]).unwrap();
700 let mut dinfo = dinfo.rgb().unwrap();
701 let _bitmap: Vec<[u8; 3]> = dinfo.read_scanlines().unwrap();
702 let remaining = dinfo.finish_into_inner().unwrap();
703 assert_eq!(0, remaining.len());
704 }
705}
706
707#[test]
708fn mem_no_trailer() {
709 let data = std::fs::read("tests/test.jpg").unwrap();
710 let dinfo = Decompress::new_mem(&data).unwrap();
711 let mut dinfo = dinfo.rgb().unwrap();
712
713 let _: Vec<[u8; 3]> = dinfo.read_scanlines().unwrap();
714
715 assert_eq!(dinfo.finish_into_inner().unwrap().len(), 0);
716}
717
718#[test]
719fn file_no_trailer() {
720 let dinfo = Decompress::new_file(File::open("tests/test.jpg").unwrap()).unwrap();
721 let mut dinfo = dinfo.rgb().unwrap();
722
723 let _: Vec<[u8; 3]> = dinfo.read_scanlines().unwrap();
724
725 assert_eq!(dinfo.finish_into_inner().unwrap().buffer().len(), 0);
726}
727
728#[test]
729fn mem_trailer() {
730 let data = std::fs::read("tests/trailer.jpg").unwrap();
731 let dinfo = Decompress::new_mem(&data).unwrap();
732 let mut dinfo = dinfo.rgb().unwrap();
733
734 let _: Vec<[u8; 3]> = dinfo.read_scanlines().unwrap();
735
736 assert_ne!(dinfo.finish_into_inner().unwrap().len(), 0);
737}
738
739#[test]
740fn file_trailer() {
741 let dinfo = Decompress::new_file(File::open("tests/trailer.jpg").unwrap()).unwrap();
742 let mut dinfo = dinfo.rgb().unwrap();
743
744 let _: Vec<[u8; 3]> = dinfo.read_scanlines().unwrap();
745
746 assert_ne!(dinfo.finish_into_inner().unwrap().buffer().len(), 0);
747}
748
749#[test]
750fn file_trailer_bytes_left() {
751 let dinfo = Decompress::new_file(File::open("tests/test.jpg").unwrap()).unwrap();
752 let mut dinfo = dinfo.rgb().unwrap();
753
754 let _: Vec<[u8; 3]> = dinfo.read_scanlines().unwrap();
755
756 assert_eq!(dinfo.finish_into_inner().unwrap().buffer().len(), 0);
757}
758
759#[test]
760fn read_file() {
761 use crate::colorspace::{ColorSpace, ColorSpaceExt};
762 use std::fs::File;
763 use std::io::Read;
764
765 let data = std::fs::read("tests/test.jpg").unwrap();
766 assert_eq!(2169, data.len());
767
768 let dinfo = Decompress::new_mem(&data[..]).unwrap();
769
770 assert_eq!(1.0, dinfo.gamma());
771 assert_eq!(ColorSpace::JCS_YCbCr, dinfo.color_space());
772 assert_eq!(dinfo.components().len(), dinfo.color_space().num_components() as usize);
773
774 assert_eq!((45, 30), dinfo.size());
775 {
776 let comps = dinfo.components();
777 assert_eq!(2, comps[0].h_samp_factor);
778 assert_eq!(2, comps[0].v_samp_factor);
779
780 assert_eq!(48, comps[0].row_stride());
781 assert_eq!(32, comps[0].col_stride());
782
783 assert_eq!(1, comps[1].h_samp_factor);
784 assert_eq!(1, comps[1].v_samp_factor);
785 assert_eq!(1, comps[2].h_samp_factor);
786 assert_eq!(1, comps[2].v_samp_factor);
787
788 assert_eq!(24, comps[1].row_stride());
789 assert_eq!(16, comps[1].col_stride());
790 assert_eq!(24, comps[2].row_stride());
791 assert_eq!(16, comps[2].col_stride());
792 }
793
794 let mut dinfo = dinfo.raw().unwrap();
795
796 let mut has_chunks = false;
797 let mut bitmaps = [&mut Vec::new(), &mut Vec::new(), &mut Vec::new()];
798 while dinfo.can_read_more_scanlines() {
799 has_chunks = true;
800 dinfo.read_raw_data_chunk(&mut bitmaps);
801 assert_eq!(bitmaps[0].len(), 4 * bitmaps[1].len());
802 }
803 assert!(has_chunks);
804
805 for (bitmap, comp) in bitmaps.iter().zip(dinfo.components()) {
806 assert_eq!(comp.row_stride() * comp.col_stride(), bitmap.len());
807 }
808
809 assert!(dinfo.finish().is_ok());
810}
811
812#[test]
813fn no_markers() {
814 use crate::colorspace::{ColorSpace, ColorSpaceExt};
815 use std::fs::File;
816 use std::io::Read;
817
818 let tricky_buf = io::BufReader::with_capacity(1, File::open("tests/test.jpg").unwrap());
820 let dinfo = Decompress::builder().from_reader(tricky_buf).unwrap();
821 assert_eq!(0, dinfo.markers().count());
822
823 let res = dinfo.rgb().unwrap().read_scanlines::<[u8; 3]>().unwrap();
824 assert_eq!(res.len(), 45 * 30);
825
826 let dinfo = Decompress::builder().with_markers(&[]).from_path("tests/test.jpg").unwrap();
827 assert_eq!(0, dinfo.markers().count());
828}
829
830#[test]
831fn buffer_into_inner() {
832 use std::io::Read;
833
834 let data = std::fs::read("tests/test.jpg").unwrap();
835 let orig_data_len = data.len();
836
837 let dec = Decompress::builder()
838 .from_reader(BufReader::with_capacity(data.len()/17, std::io::Cursor::new(data)))
839 .unwrap();
840 let mut dec = dec.rgb().unwrap();
841 let _: Vec<[u8; 3]> = dec.read_scanlines().unwrap();
842 let mut buf = dec.finish_into_inner().unwrap();
843 assert_eq!(0, buf.fill_buf().unwrap().len());
844
845 let mut data = buf.into_inner().into_inner();
846 assert_eq!(orig_data_len, data.len());
847
848 data.extend_from_slice(b"unexpected data after first image eoi");
850 data.append(&mut data.clone());
851 let appended_len = data.len() - orig_data_len;
852
853 let dec = Decompress::builder()
855 .from_reader(BufReader::with_capacity(data.len()/21, std::io::Cursor::new(data)))
856 .unwrap();
857 let mut dec = dec.rgb().unwrap();
858 let _: Vec<[u8; 3]> = dec.read_scanlines().unwrap();
859
860 let mut buf = dec.finish_into_inner().unwrap();
862 let mut tmp = Vec::new();
863 buf.read_to_end(&mut tmp).unwrap();
864 let data = buf.into_inner().into_inner();
865 assert_eq!(appended_len, tmp.len());
866 assert_eq!(data[orig_data_len..], tmp);
867}
868
869#[test]
870fn read_file_rgb() {
871 use crate::colorspace::{ColorSpace, ColorSpaceExt};
872 use std::fs::File;
873 use std::io::Read;
874
875 let data = std::fs::read("tests/test.jpg").unwrap();
876 let dinfo = Decompress::builder().with_markers(ALL_MARKERS).from_mem(&data[..]).unwrap();
877
878 assert_eq!(ColorSpace::JCS_YCbCr, dinfo.color_space());
879
880 assert_eq!(1, dinfo.markers().count());
881
882 let mut dinfo = dinfo.rgb().unwrap();
883 assert_eq!(ColorSpace::JCS_RGB, dinfo.color_space());
884 assert_eq!(dinfo.components().len(), dinfo.color_space().num_components() as usize);
885
886 let bitmap: Vec<[u8; 3]> = dinfo.read_scanlines().unwrap();
887 assert_eq!(bitmap.len(), 45 * 30);
888
889 assert!(!bitmap.contains(&[0; 3]));
890
891 dinfo.finish().unwrap();
892}
893
894#[test]
895fn drops_reader() {
896 #[repr(align(1024))]
897 struct CountsDrops<'a, R> {drop_count: &'a mut u8, reader: R}
898
899 impl<R> Drop for CountsDrops<'_, R> {
900 fn drop(&mut self) {
901 assert!(self as *mut _ as usize % 1024 == 0); *self.drop_count += 1;
903 }
904 }
905 impl<R: io::Read> io::Read for CountsDrops<'_, R> {
906 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
907 self.reader.read(buf)
908 }
909 }
910 let mut drop_count = 0;
911 let r = Decompress::builder().from_reader(BufReader::new(CountsDrops {
912 drop_count: &mut drop_count,
913 reader: File::open("tests/test.jpg").unwrap(),
914 })).unwrap();
915 drop(r);
916 assert_eq!(1, drop_count);
917}