1#![allow(clippy::too_many_arguments)]
2use std::ffi::OsStr;
3use std::io;
4use std::io::Read;
5use std::ops::{Deref, DerefMut};
6use std::path::Path;
7use std::usize;
8
9use crate::color::{ColorType, ExtendedColorType};
10use crate::error::{
11 ImageError, ImageFormatHint, ImageResult, LimitError, LimitErrorKind, ParameterError,
12 ParameterErrorKind,
13};
14use crate::math::Rect;
15use crate::traits::Pixel;
16use crate::ImageBuffer;
17
18use crate::animation::Frames;
19
20#[cfg(feature = "pnm")]
21use crate::codecs::pnm::PnmSubtype;
22
23#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
26#[non_exhaustive]
27pub enum ImageFormat {
28 Png,
30
31 Jpeg,
33
34 Gif,
36
37 WebP,
39
40 Pnm,
42
43 Tiff,
45
46 Tga,
48
49 Dds,
51
52 Bmp,
54
55 Ico,
57
58 Hdr,
60
61 OpenExr,
63
64 Farbfeld,
66
67 Avif,
69
70 Qoi,
72}
73
74impl ImageFormat {
75 #[inline]
86 pub fn from_extension<S>(ext: S) -> Option<Self>
87 where
88 S: AsRef<OsStr>,
89 {
90 fn inner(ext: &OsStr) -> Option<ImageFormat> {
92 let ext = ext.to_str()?.to_ascii_lowercase();
93
94 Some(match ext.as_str() {
95 "avif" => ImageFormat::Avif,
96 "jpg" | "jpeg" => ImageFormat::Jpeg,
97 "png" => ImageFormat::Png,
98 "gif" => ImageFormat::Gif,
99 "webp" => ImageFormat::WebP,
100 "tif" | "tiff" => ImageFormat::Tiff,
101 "tga" => ImageFormat::Tga,
102 "dds" => ImageFormat::Dds,
103 "bmp" => ImageFormat::Bmp,
104 "ico" => ImageFormat::Ico,
105 "hdr" => ImageFormat::Hdr,
106 "exr" => ImageFormat::OpenExr,
107 "pbm" | "pam" | "ppm" | "pgm" => ImageFormat::Pnm,
108 "ff" | "farbfeld" => ImageFormat::Farbfeld,
109 "qoi" => ImageFormat::Qoi,
110 _ => return None,
111 })
112 }
113
114 inner(ext.as_ref())
115 }
116
117 #[inline]
130 pub fn from_path<P>(path: P) -> ImageResult<Self>
131 where
132 P: AsRef<Path>,
133 {
134 fn inner(path: &Path) -> ImageResult<ImageFormat> {
136 let exact_ext = path.extension();
137 exact_ext
138 .and_then(ImageFormat::from_extension)
139 .ok_or_else(|| {
140 let format_hint = match exact_ext {
141 None => ImageFormatHint::Unknown,
142 Some(os) => ImageFormatHint::PathExtension(os.into()),
143 };
144 ImageError::Unsupported(format_hint.into())
145 })
146 }
147
148 inner(path.as_ref())
149 }
150
151 pub fn from_mime_type<M>(mime_type: M) -> Option<Self>
162 where
163 M: AsRef<str>,
164 {
165 match mime_type.as_ref() {
166 "image/avif" => Some(ImageFormat::Avif),
167 "image/jpeg" => Some(ImageFormat::Jpeg),
168 "image/png" => Some(ImageFormat::Png),
169 "image/gif" => Some(ImageFormat::Gif),
170 "image/webp" => Some(ImageFormat::WebP),
171 "image/tiff" => Some(ImageFormat::Tiff),
172 "image/x-targa" | "image/x-tga" => Some(ImageFormat::Tga),
173 "image/vnd-ms.dds" => Some(ImageFormat::Dds),
174 "image/bmp" => Some(ImageFormat::Bmp),
175 "image/x-icon" => Some(ImageFormat::Ico),
176 "image/vnd.radiance" => Some(ImageFormat::Hdr),
177 "image/x-exr" => Some(ImageFormat::OpenExr),
178 "image/x-portable-bitmap"
179 | "image/x-portable-graymap"
180 | "image/x-portable-pixmap"
181 | "image/x-portable-anymap" => Some(ImageFormat::Pnm),
182 "image/x-qoi" => Some(ImageFormat::Qoi),
185 _ => None,
186 }
187 }
188
189 pub fn to_mime_type(&self) -> &'static str {
210 match self {
211 ImageFormat::Avif => "image/avif",
212 ImageFormat::Jpeg => "image/jpeg",
213 ImageFormat::Png => "image/png",
214 ImageFormat::Gif => "image/gif",
215 ImageFormat::WebP => "image/webp",
216 ImageFormat::Tiff => "image/tiff",
217 ImageFormat::Tga => "image/x-targa",
219 ImageFormat::Dds => "image/vnd-ms.dds",
220 ImageFormat::Bmp => "image/bmp",
221 ImageFormat::Ico => "image/x-icon",
222 ImageFormat::Hdr => "image/vnd.radiance",
223 ImageFormat::OpenExr => "image/x-exr",
224 ImageFormat::Pnm => "image/x-portable-anymap",
226 ImageFormat::Qoi => "image/x-qoi",
229 ImageFormat::Farbfeld => "application/octet-stream",
231 }
232 }
233
234 #[inline]
236 pub fn can_read(&self) -> bool {
237 match self {
239 ImageFormat::Png => true,
240 ImageFormat::Gif => true,
241 ImageFormat::Jpeg => true,
242 ImageFormat::WebP => true,
243 ImageFormat::Tiff => true,
244 ImageFormat::Tga => true,
245 ImageFormat::Dds => false,
246 ImageFormat::Bmp => true,
247 ImageFormat::Ico => true,
248 ImageFormat::Hdr => true,
249 ImageFormat::OpenExr => true,
250 ImageFormat::Pnm => true,
251 ImageFormat::Farbfeld => true,
252 ImageFormat::Avif => true,
253 ImageFormat::Qoi => true,
254 }
255 }
256
257 #[inline]
259 pub fn can_write(&self) -> bool {
260 match self {
262 ImageFormat::Gif => true,
263 ImageFormat::Ico => true,
264 ImageFormat::Jpeg => true,
265 ImageFormat::Png => true,
266 ImageFormat::Bmp => true,
267 ImageFormat::Tiff => true,
268 ImageFormat::Tga => true,
269 ImageFormat::Pnm => true,
270 ImageFormat::Farbfeld => true,
271 ImageFormat::Avif => true,
272 ImageFormat::WebP => true,
273 ImageFormat::Hdr => false,
274 ImageFormat::OpenExr => true,
275 ImageFormat::Dds => false,
276 ImageFormat::Qoi => true,
277 }
278 }
279
280 pub fn extensions_str(self) -> &'static [&'static str] {
290 match self {
291 ImageFormat::Png => &["png"],
292 ImageFormat::Jpeg => &["jpg", "jpeg"],
293 ImageFormat::Gif => &["gif"],
294 ImageFormat::WebP => &["webp"],
295 ImageFormat::Pnm => &["pbm", "pam", "ppm", "pgm"],
296 ImageFormat::Tiff => &["tiff", "tif"],
297 ImageFormat::Tga => &["tga"],
298 ImageFormat::Dds => &["dds"],
299 ImageFormat::Bmp => &["bmp"],
300 ImageFormat::Ico => &["ico"],
301 ImageFormat::Hdr => &["hdr"],
302 ImageFormat::OpenExr => &["exr"],
303 ImageFormat::Farbfeld => &["ff"],
304 ImageFormat::Avif => &["avif"],
306 ImageFormat::Qoi => &["qoi"],
307 }
308 }
309
310 #[inline]
312 pub fn reading_enabled(&self) -> bool {
313 match self {
314 ImageFormat::Png => cfg!(feature = "png"),
315 ImageFormat::Gif => cfg!(feature = "gif"),
316 ImageFormat::Jpeg => cfg!(feature = "jpeg"),
317 ImageFormat::WebP => cfg!(feature = "webp"),
318 ImageFormat::Tiff => cfg!(feature = "tiff"),
319 ImageFormat::Tga => cfg!(feature = "tga"),
320 ImageFormat::Bmp => cfg!(feature = "bmp"),
321 ImageFormat::Ico => cfg!(feature = "ico"),
322 ImageFormat::Hdr => cfg!(feature = "hdr"),
323 ImageFormat::OpenExr => cfg!(feature = "openexr"),
324 ImageFormat::Pnm => cfg!(feature = "pnm"),
325 ImageFormat::Farbfeld => cfg!(feature = "farbfeld"),
326 ImageFormat::Avif => cfg!(feature = "avif"),
327 ImageFormat::Qoi => cfg!(feature = "qoi"),
328 ImageFormat::Dds => false,
329 }
330 }
331
332 #[inline]
334 pub fn writing_enabled(&self) -> bool {
335 match self {
336 ImageFormat::Gif => cfg!(feature = "gif"),
337 ImageFormat::Ico => cfg!(feature = "ico"),
338 ImageFormat::Jpeg => cfg!(feature = "jpeg"),
339 ImageFormat::Png => cfg!(feature = "png"),
340 ImageFormat::Bmp => cfg!(feature = "bmp"),
341 ImageFormat::Tiff => cfg!(feature = "tiff"),
342 ImageFormat::Tga => cfg!(feature = "tga"),
343 ImageFormat::Pnm => cfg!(feature = "pnm"),
344 ImageFormat::Farbfeld => cfg!(feature = "farbfeld"),
345 ImageFormat::Avif => cfg!(feature = "avif"),
346 ImageFormat::WebP => cfg!(feature = "webp"),
347 ImageFormat::OpenExr => cfg!(feature = "openexr"),
348 ImageFormat::Qoi => cfg!(feature = "qoi"),
349 ImageFormat::Dds => false,
350 ImageFormat::Hdr => false,
351 }
352 }
353
354 pub fn all() -> impl Iterator<Item = ImageFormat> {
356 [
357 ImageFormat::Gif,
358 ImageFormat::Ico,
359 ImageFormat::Jpeg,
360 ImageFormat::Png,
361 ImageFormat::Bmp,
362 ImageFormat::Tiff,
363 ImageFormat::Tga,
364 ImageFormat::Pnm,
365 ImageFormat::Farbfeld,
366 ImageFormat::Avif,
367 ImageFormat::WebP,
368 ImageFormat::OpenExr,
369 ImageFormat::Qoi,
370 ImageFormat::Dds,
371 ImageFormat::Hdr,
372 ]
373 .iter()
374 .copied()
375 }
376}
377
378#[derive(Clone, PartialEq, Eq, Debug)]
380#[non_exhaustive]
381pub enum ImageOutputFormat {
382 #[cfg(feature = "png")]
383 Png,
385
386 #[cfg(feature = "jpeg")]
387 Jpeg(u8),
389
390 #[cfg(feature = "pnm")]
391 Pnm(PnmSubtype),
393
394 #[cfg(feature = "gif")]
395 Gif,
397
398 #[cfg(feature = "ico")]
399 Ico,
401
402 #[cfg(feature = "bmp")]
403 Bmp,
405
406 #[cfg(feature = "farbfeld")]
407 Farbfeld,
409
410 #[cfg(feature = "tga")]
411 Tga,
413
414 #[cfg(feature = "exr")]
415 OpenExr,
417
418 #[cfg(feature = "tiff")]
419 Tiff,
421
422 #[cfg(feature = "avif-encoder")]
423 Avif,
425
426 #[cfg(feature = "qoi")]
427 Qoi,
429
430 #[cfg(feature = "webp")]
431 WebP,
433
434 Unsupported(String),
438}
439
440impl From<ImageFormat> for ImageOutputFormat {
441 fn from(fmt: ImageFormat) -> Self {
442 match fmt {
443 #[cfg(feature = "png")]
444 ImageFormat::Png => ImageOutputFormat::Png,
445 #[cfg(feature = "jpeg")]
446 ImageFormat::Jpeg => ImageOutputFormat::Jpeg(75),
447 #[cfg(feature = "pnm")]
448 ImageFormat::Pnm => ImageOutputFormat::Pnm(PnmSubtype::ArbitraryMap),
449 #[cfg(feature = "gif")]
450 ImageFormat::Gif => ImageOutputFormat::Gif,
451 #[cfg(feature = "ico")]
452 ImageFormat::Ico => ImageOutputFormat::Ico,
453 #[cfg(feature = "bmp")]
454 ImageFormat::Bmp => ImageOutputFormat::Bmp,
455 #[cfg(feature = "farbfeld")]
456 ImageFormat::Farbfeld => ImageOutputFormat::Farbfeld,
457 #[cfg(feature = "tga")]
458 ImageFormat::Tga => ImageOutputFormat::Tga,
459 #[cfg(feature = "exr")]
460 ImageFormat::OpenExr => ImageOutputFormat::OpenExr,
461 #[cfg(feature = "tiff")]
462 ImageFormat::Tiff => ImageOutputFormat::Tiff,
463
464 #[cfg(feature = "avif-encoder")]
465 ImageFormat::Avif => ImageOutputFormat::Avif,
466 #[cfg(feature = "webp")]
467 ImageFormat::WebP => ImageOutputFormat::WebP,
468
469 #[cfg(feature = "qoi")]
470 ImageFormat::Qoi => ImageOutputFormat::Qoi,
471
472 f => ImageOutputFormat::Unsupported(format!("{:?}", f)),
473 }
474 }
475}
476
477#[allow(dead_code)]
480pub(crate) struct ImageReadBuffer {
482 scanline_bytes: usize,
483 buffer: Vec<u8>,
484 consumed: usize,
485
486 total_bytes: u64,
487 offset: u64,
488}
489impl ImageReadBuffer {
490 #[allow(dead_code)]
496 pub(crate) fn new(scanline_bytes: u64, total_bytes: u64) -> Self {
498 Self {
499 scanline_bytes: usize::try_from(scanline_bytes).unwrap(),
500 buffer: Vec::new(),
501 consumed: 0,
502 total_bytes,
503 offset: 0,
504 }
505 }
506
507 #[allow(dead_code)]
508 pub(crate) fn read<F>(&mut self, buf: &mut [u8], mut read_scanline: F) -> io::Result<usize>
510 where
511 F: FnMut(&mut [u8]) -> io::Result<usize>,
512 {
513 if self.buffer.len() == self.consumed {
514 if self.offset == self.total_bytes {
515 return Ok(0);
516 } else if buf.len() >= self.scanline_bytes {
517 let bytes_read = read_scanline(&mut buf[..self.scanline_bytes])?;
520 self.offset += u64::try_from(bytes_read).unwrap();
521 return Ok(bytes_read);
522 } else {
523 if self.buffer.is_empty() {
526 self.buffer.resize(self.scanline_bytes, 0);
527 }
528
529 self.consumed = 0;
530 let bytes_read = read_scanline(&mut self.buffer[..])?;
531 self.buffer.resize(bytes_read, 0);
532 self.offset += u64::try_from(bytes_read).unwrap();
533
534 assert!(bytes_read == self.scanline_bytes || self.offset == self.total_bytes);
535 }
536 }
537
538 let bytes_buffered = self.buffer.len() - self.consumed;
540 if bytes_buffered > buf.len() {
541 buf.copy_from_slice(&self.buffer[self.consumed..][..buf.len()]);
542 self.consumed += buf.len();
543 Ok(buf.len())
544 } else {
545 buf[..bytes_buffered].copy_from_slice(&self.buffer[self.consumed..][..bytes_buffered]);
546 self.consumed = self.buffer.len();
547 Ok(bytes_buffered)
548 }
549 }
550}
551
552#[allow(dead_code)]
555pub(crate) fn load_rect<'a, D, F, F1, F2, E>(
557 x: u32,
558 y: u32,
559 width: u32,
560 height: u32,
561 buf: &mut [u8],
562 progress_callback: F,
563 decoder: &mut D,
564 mut seek_scanline: F1,
565 mut read_scanline: F2,
566) -> ImageResult<()>
567where
568 D: ImageDecoder<'a>,
569 F: Fn(Progress),
570 F1: FnMut(&mut D, u64) -> io::Result<()>,
571 F2: FnMut(&mut D, &mut [u8]) -> Result<(), E>,
572 ImageError: From<E>,
573{
574 let (x, y, width, height) = (
575 u64::from(x),
576 u64::from(y),
577 u64::from(width),
578 u64::from(height),
579 );
580 let dimensions = decoder.dimensions();
581 let bytes_per_pixel = u64::from(decoder.color_type().bytes_per_pixel());
582 let row_bytes = bytes_per_pixel * u64::from(dimensions.0);
583 #[allow(deprecated)]
584 let scanline_bytes = decoder.scanline_bytes();
585 let total_bytes = width * height * bytes_per_pixel;
586
587 if buf.len() < usize::try_from(total_bytes).unwrap_or(usize::max_value()) {
588 panic!(
589 "output buffer too short\n expected `{}`, provided `{}`",
590 total_bytes,
591 buf.len()
592 );
593 }
594
595 let mut bytes_read = 0u64;
596 let mut current_scanline = 0;
597 let mut tmp = Vec::new();
598 let mut tmp_scanline = None;
599
600 {
601 let mut read_image_range = |mut start: u64, end: u64| -> ImageResult<()> {
604 let target_scanline = start / scanline_bytes;
607 if tmp_scanline == Some(target_scanline) {
608 let position = target_scanline * scanline_bytes;
609 let offset = start.saturating_sub(position);
610 let len = (end - start)
611 .min(scanline_bytes - offset)
612 .min(end - position);
613
614 buf[(bytes_read as usize)..][..len as usize]
615 .copy_from_slice(&tmp[offset as usize..][..len as usize]);
616 bytes_read += len;
617 start += len;
618
619 progress_callback(Progress {
620 current: bytes_read,
621 total: total_bytes,
622 });
623
624 if start == end {
625 return Ok(());
626 }
627 }
628
629 let target_scanline = start / scanline_bytes;
630 if target_scanline != current_scanline {
631 seek_scanline(decoder, target_scanline)?;
632 current_scanline = target_scanline;
633 }
634
635 let mut position = current_scanline * scanline_bytes;
636 while position < end {
637 if position >= start && end - position >= scanline_bytes {
638 read_scanline(
639 decoder,
640 &mut buf[(bytes_read as usize)..][..(scanline_bytes as usize)],
641 )?;
642 bytes_read += scanline_bytes;
643 } else {
644 tmp.resize(scanline_bytes as usize, 0u8);
645 read_scanline(decoder, &mut tmp)?;
646 tmp_scanline = Some(current_scanline);
647
648 let offset = start.saturating_sub(position);
649 let len = (end - start)
650 .min(scanline_bytes - offset)
651 .min(end - position);
652
653 buf[(bytes_read as usize)..][..len as usize]
654 .copy_from_slice(&tmp[offset as usize..][..len as usize]);
655 bytes_read += len;
656 }
657
658 current_scanline += 1;
659 position += scanline_bytes;
660 progress_callback(Progress {
661 current: bytes_read,
662 total: total_bytes,
663 });
664 }
665 Ok(())
666 };
667
668 if x + width > u64::from(dimensions.0)
669 || y + height > u64::from(dimensions.1)
670 || width == 0
671 || height == 0
672 {
673 return Err(ImageError::Parameter(ParameterError::from_kind(
674 ParameterErrorKind::DimensionMismatch,
675 )));
676 }
677 if scanline_bytes > usize::max_value() as u64 {
678 return Err(ImageError::Limits(LimitError::from_kind(
679 LimitErrorKind::InsufficientMemory,
680 )));
681 }
682
683 progress_callback(Progress {
684 current: 0,
685 total: total_bytes,
686 });
687 if x == 0 && width == u64::from(dimensions.0) {
688 let start = x * bytes_per_pixel + y * row_bytes;
689 let end = (x + width) * bytes_per_pixel + (y + height - 1) * row_bytes;
690 read_image_range(start, end)?;
691 } else {
692 for row in y..(y + height) {
693 let start = x * bytes_per_pixel + row * row_bytes;
694 let end = (x + width) * bytes_per_pixel + row * row_bytes;
695 read_image_range(start, end)?;
696 }
697 }
698 }
699
700 Ok(seek_scanline(decoder, 0)?)
702}
703
704pub(crate) fn decoder_to_vec<'a, T>(decoder: impl ImageDecoder<'a>) -> ImageResult<Vec<T>>
709where
710 T: crate::traits::Primitive + bytemuck::Pod,
711{
712 let total_bytes = usize::try_from(decoder.total_bytes());
713 if total_bytes.is_err() || total_bytes.unwrap() > isize::max_value() as usize {
714 return Err(ImageError::Limits(LimitError::from_kind(
715 LimitErrorKind::InsufficientMemory,
716 )));
717 }
718
719 let mut buf = vec![num_traits::Zero::zero(); total_bytes.unwrap() / std::mem::size_of::<T>()];
720 decoder.read_image(bytemuck::cast_slice_mut(buf.as_mut_slice()))?;
721 Ok(buf)
722}
723
724#[derive(Clone, Copy, Debug, PartialEq, Eq)]
730pub struct Progress {
731 current: u64,
732 total: u64,
733}
734
735impl Progress {
736 pub(crate) fn new(current: u64, total: u64) -> Self {
738 Self { current, total }
739 }
740
741 pub fn current(self) -> u64 {
743 self.current
744 }
745
746 pub fn total(self) -> u64 {
750 self.total
751 }
752
753 pub fn remaining(self) -> u64 {
755 self.total.max(self.current) - self.current
756 }
757}
758
759pub trait ImageDecoder<'a>: Sized {
761 type Reader: Read + 'a;
763
764 fn dimensions(&self) -> (u32, u32);
766
767 fn color_type(&self) -> ColorType;
769
770 fn original_color_type(&self) -> ExtendedColorType {
772 self.color_type().into()
773 }
774
775 fn icc_profile(&mut self) -> Option<Vec<u8>> {
780 None
781 }
782
783 #[deprecated = "Planned for removal. See https://github.com/image-rs/image/issues/1989"]
787 fn into_reader(self) -> ImageResult<Self::Reader>;
788
789 fn total_bytes(&self) -> u64 {
796 let dimensions = self.dimensions();
797 let total_pixels = u64::from(dimensions.0) * u64::from(dimensions.1);
798 let bytes_per_pixel = u64::from(self.color_type().bytes_per_pixel());
799 total_pixels.saturating_mul(bytes_per_pixel)
800 }
801
802 #[deprecated = "Planned for removal. See https://github.com/image-rs/image/issues/1989"]
805 fn scanline_bytes(&self) -> u64 {
806 self.total_bytes()
807 }
808
809 fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
831 #[allow(deprecated)]
832 self.read_image_with_progress(buf, |_| {})
833 }
834
835 #[deprecated = "Use read_image instead. See https://github.com/image-rs/image/issues/1989"]
838 fn read_image_with_progress<F: Fn(Progress)>(
839 self,
840 buf: &mut [u8],
841 progress_callback: F,
842 ) -> ImageResult<()> {
843 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
844
845 let total_bytes = self.total_bytes() as usize;
846 #[allow(deprecated)]
847 let scanline_bytes = self.scanline_bytes() as usize;
848 let target_read_size = if scanline_bytes < 4096 {
849 (4096 / scanline_bytes) * scanline_bytes
850 } else {
851 scanline_bytes
852 };
853
854 #[allow(deprecated)]
855 let mut reader = self.into_reader()?;
856
857 let mut bytes_read = 0;
858 while bytes_read < total_bytes {
859 let read_size = target_read_size.min(total_bytes - bytes_read);
860 reader.read_exact(&mut buf[bytes_read..][..read_size])?;
861 bytes_read += read_size;
862
863 progress_callback(Progress {
864 current: bytes_read as u64,
865 total: total_bytes as u64,
866 });
867 }
868
869 Ok(())
870 }
871
872 fn set_limits(&mut self, limits: crate::io::Limits) -> ImageResult<()> {
884 limits.check_support(&crate::io::LimitSupport::default())?;
885
886 let (width, height) = self.dimensions();
887 limits.check_dimensions(width, height)?;
888
889 Ok(())
890 }
891}
892
893pub trait ImageDecoderRect<'a>: ImageDecoder<'a> + Sized {
895 fn read_rect(
897 &mut self,
898 x: u32,
899 y: u32,
900 width: u32,
901 height: u32,
902 buf: &mut [u8],
903 ) -> ImageResult<()> {
904 #[allow(deprecated)]
905 self.read_rect_with_progress(x, y, width, height, buf, |_| {})
906 }
907
908 #[deprecated = "Use read_image instead. See https://github.com/image-rs/image/issues/1989"]
921 fn read_rect_with_progress<F: Fn(Progress)>(
922 &mut self,
923 x: u32,
924 y: u32,
925 width: u32,
926 height: u32,
927 buf: &mut [u8],
928 progress_callback: F,
929 ) -> ImageResult<()>;
930}
931
932pub trait AnimationDecoder<'a> {
934 fn into_frames(self) -> Frames<'a>;
936}
937
938pub trait ImageEncoder {
940 fn write_image(
955 self,
956 buf: &[u8],
957 width: u32,
958 height: u32,
959 color_type: ColorType,
960 ) -> ImageResult<()>;
961}
962
963#[derive(Debug)]
965pub struct Pixels<'a, I: ?Sized + 'a> {
966 image: &'a I,
967 x: u32,
968 y: u32,
969 width: u32,
970 height: u32,
971}
972
973impl<'a, I: GenericImageView> Iterator for Pixels<'a, I> {
974 type Item = (u32, u32, I::Pixel);
975
976 fn next(&mut self) -> Option<(u32, u32, I::Pixel)> {
977 if self.x >= self.width {
978 self.x = 0;
979 self.y += 1;
980 }
981
982 if self.y >= self.height {
983 None
984 } else {
985 let pixel = self.image.get_pixel(self.x, self.y);
986 let p = (self.x, self.y, pixel);
987
988 self.x += 1;
989
990 Some(p)
991 }
992 }
993}
994
995impl<I: ?Sized> Clone for Pixels<'_, I> {
996 fn clone(&self) -> Self {
997 Pixels { ..*self }
998 }
999}
1000
1001pub trait GenericImageView {
1010 type Pixel: Pixel;
1012
1013 fn dimensions(&self) -> (u32, u32);
1015
1016 fn width(&self) -> u32 {
1018 let (w, _) = self.dimensions();
1019 w
1020 }
1021
1022 fn height(&self) -> u32 {
1024 let (_, h) = self.dimensions();
1025 h
1026 }
1027
1028 #[deprecated = "This method has inconsistent behavior between implementations (#1829). Use `dimensions` instead"]
1030 fn bounds(&self) -> (u32, u32, u32, u32);
1031
1032 fn in_bounds(&self, x: u32, y: u32) -> bool {
1034 #[allow(deprecated)]
1035 let (ix, iy, iw, ih) = self.bounds();
1036 x >= ix && x < ix + iw && y >= iy && y < iy + ih
1037 }
1038
1039 fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel;
1045
1046 unsafe fn unsafe_get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
1055 self.get_pixel(x, y)
1056 }
1057
1058 fn pixels(&self) -> Pixels<Self>
1062 where
1063 Self: Sized,
1064 {
1065 let (width, height) = self.dimensions();
1066
1067 Pixels {
1068 image: self,
1069 x: 0,
1070 y: 0,
1071 width,
1072 height,
1073 }
1074 }
1075
1076 fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&Self>
1080 where
1081 Self: Sized,
1082 {
1083 assert!(x as u64 + width as u64 <= self.width() as u64);
1084 assert!(y as u64 + height as u64 <= self.height() as u64);
1085 SubImage::new(self, x, y, width, height)
1086 }
1087}
1088
1089pub trait GenericImage: GenericImageView {
1091 #[deprecated(since = "0.24.0", note = "Use `get_pixel` and `put_pixel` instead.")]
1112 fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel;
1113
1114 fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
1120
1121 unsafe fn unsafe_put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1130 self.put_pixel(x, y, pixel);
1131 }
1132
1133 #[deprecated(
1135 since = "0.24.0",
1136 note = "Use iterator `pixels_mut` to blend the pixels directly"
1137 )]
1138 fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
1139
1140 fn copy_from<O>(&mut self, other: &O, x: u32, y: u32) -> ImageResult<()>
1156 where
1157 O: GenericImageView<Pixel = Self::Pixel>,
1158 {
1159 if self.width() < other.width() + x || self.height() < other.height() + y {
1162 return Err(ImageError::Parameter(ParameterError::from_kind(
1163 ParameterErrorKind::DimensionMismatch,
1164 )));
1165 }
1166
1167 for k in 0..other.height() {
1168 for i in 0..other.width() {
1169 let p = other.get_pixel(i, k);
1170 self.put_pixel(i + x, k + y, p);
1171 }
1172 }
1173 Ok(())
1174 }
1175
1176 fn copy_within(&mut self, source: Rect, x: u32, y: u32) -> bool {
1184 let Rect {
1185 x: sx,
1186 y: sy,
1187 width,
1188 height,
1189 } = source;
1190 let dx = x;
1191 let dy = y;
1192 assert!(sx < self.width() && dx < self.width());
1193 assert!(sy < self.height() && dy < self.height());
1194 if self.width() - dx.max(sx) < width || self.height() - dy.max(sy) < height {
1195 return false;
1196 }
1197 macro_rules! copy_within_impl_ {
1200 ($xiter:expr, $yiter:expr) => {
1201 for y in $yiter {
1202 let sy = sy + y;
1203 let dy = dy + y;
1204 for x in $xiter {
1205 let sx = sx + x;
1206 let dx = dx + x;
1207 let pixel = self.get_pixel(sx, sy);
1208 self.put_pixel(dx, dy, pixel);
1209 }
1210 }
1211 };
1212 }
1213 match (sx < dx, sy < dy) {
1215 (true, true) => copy_within_impl_!((0..width).rev(), (0..height).rev()),
1216 (true, false) => copy_within_impl_!((0..width).rev(), 0..height),
1217 (false, true) => copy_within_impl_!(0..width, (0..height).rev()),
1218 (false, false) => copy_within_impl_!(0..width, 0..height),
1219 }
1220 true
1221 }
1222
1223 fn sub_image(&mut self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&mut Self>
1227 where
1228 Self: Sized,
1229 {
1230 assert!(x as u64 + width as u64 <= self.width() as u64);
1231 assert!(y as u64 + height as u64 <= self.height() as u64);
1232 SubImage::new(self, x, y, width, height)
1233 }
1234}
1235
1236#[derive(Copy, Clone)]
1258pub struct SubImage<I> {
1259 inner: SubImageInner<I>,
1260}
1261
1262#[derive(Copy, Clone)]
1267pub struct SubImageInner<I> {
1268 image: I,
1269 xoffset: u32,
1270 yoffset: u32,
1271 xstride: u32,
1272 ystride: u32,
1273}
1274
1275type DerefPixel<I> = <<I as Deref>::Target as GenericImageView>::Pixel;
1277
1278type DerefSubpixel<I> = <DerefPixel<I> as Pixel>::Subpixel;
1280
1281impl<I> SubImage<I> {
1282 pub fn new(image: I, x: u32, y: u32, width: u32, height: u32) -> SubImage<I> {
1285 SubImage {
1286 inner: SubImageInner {
1287 image,
1288 xoffset: x,
1289 yoffset: y,
1290 xstride: width,
1291 ystride: height,
1292 },
1293 }
1294 }
1295
1296 pub fn change_bounds(&mut self, x: u32, y: u32, width: u32, height: u32) {
1298 self.inner.xoffset = x;
1299 self.inner.yoffset = y;
1300 self.inner.xstride = width;
1301 self.inner.ystride = height;
1302 }
1303
1304 pub fn offsets(&self) -> (u32, u32) {
1306 (self.inner.xoffset, self.inner.yoffset)
1307 }
1308
1309 pub fn to_image(&self) -> ImageBuffer<DerefPixel<I>, Vec<DerefSubpixel<I>>>
1311 where
1312 I: Deref,
1313 I::Target: GenericImageView + 'static,
1314 {
1315 let mut out = ImageBuffer::new(self.inner.xstride, self.inner.ystride);
1316 let borrowed = self.inner.image.deref();
1317
1318 for y in 0..self.inner.ystride {
1319 for x in 0..self.inner.xstride {
1320 let p = borrowed.get_pixel(x + self.inner.xoffset, y + self.inner.yoffset);
1321 out.put_pixel(x, y, p);
1322 }
1323 }
1324
1325 out
1326 }
1327}
1328
1329impl<I> SubImage<I>
1331where
1332 I: Deref,
1333 I::Target: GenericImageView,
1334{
1335 pub fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&I::Target> {
1354 use crate::GenericImageView as _;
1355 assert!(x as u64 + width as u64 <= self.inner.width() as u64);
1356 assert!(y as u64 + height as u64 <= self.inner.height() as u64);
1357 let x = self.inner.xoffset.saturating_add(x);
1358 let y = self.inner.yoffset.saturating_add(y);
1359 SubImage::new(&*self.inner.image, x, y, width, height)
1360 }
1361
1362 pub fn inner(&self) -> &I::Target {
1364 &self.inner.image
1365 }
1366}
1367
1368impl<I> SubImage<I>
1369where
1370 I: DerefMut,
1371 I::Target: GenericImage,
1372{
1373 pub fn sub_image(
1377 &mut self,
1378 x: u32,
1379 y: u32,
1380 width: u32,
1381 height: u32,
1382 ) -> SubImage<&mut I::Target> {
1383 assert!(x as u64 + width as u64 <= self.inner.width() as u64);
1384 assert!(y as u64 + height as u64 <= self.inner.height() as u64);
1385 let x = self.inner.xoffset.saturating_add(x);
1386 let y = self.inner.yoffset.saturating_add(y);
1387 SubImage::new(&mut *self.inner.image, x, y, width, height)
1388 }
1389
1390 pub fn inner_mut(&mut self) -> &mut I::Target {
1392 &mut self.inner.image
1393 }
1394}
1395
1396impl<I> Deref for SubImage<I>
1397where
1398 I: Deref,
1399{
1400 type Target = SubImageInner<I>;
1401 fn deref(&self) -> &Self::Target {
1402 &self.inner
1403 }
1404}
1405
1406impl<I> DerefMut for SubImage<I>
1407where
1408 I: DerefMut,
1409{
1410 fn deref_mut(&mut self) -> &mut Self::Target {
1411 &mut self.inner
1412 }
1413}
1414
1415#[allow(deprecated)]
1416impl<I> GenericImageView for SubImageInner<I>
1417where
1418 I: Deref,
1419 I::Target: GenericImageView,
1420{
1421 type Pixel = DerefPixel<I>;
1422
1423 fn dimensions(&self) -> (u32, u32) {
1424 (self.xstride, self.ystride)
1425 }
1426
1427 fn bounds(&self) -> (u32, u32, u32, u32) {
1428 (self.xoffset, self.yoffset, self.xstride, self.ystride)
1429 }
1430
1431 fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
1432 self.image.get_pixel(x + self.xoffset, y + self.yoffset)
1433 }
1434}
1435
1436#[allow(deprecated)]
1437impl<I> GenericImage for SubImageInner<I>
1438where
1439 I: DerefMut,
1440 I::Target: GenericImage + Sized,
1441{
1442 fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel {
1443 self.image.get_pixel_mut(x + self.xoffset, y + self.yoffset)
1444 }
1445
1446 fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1447 self.image
1448 .put_pixel(x + self.xoffset, y + self.yoffset, pixel)
1449 }
1450
1451 fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1453 self.image
1454 .blend_pixel(x + self.xoffset, y + self.yoffset, pixel)
1455 }
1456}
1457
1458#[cfg(test)]
1459mod tests {
1460 use std::collections::HashSet;
1461 use std::io;
1462 use std::path::Path;
1463
1464 use super::{
1465 load_rect, ColorType, GenericImage, GenericImageView, ImageDecoder, ImageFormat,
1466 ImageResult,
1467 };
1468 use crate::color::Rgba;
1469 use crate::math::Rect;
1470 use crate::{GrayImage, ImageBuffer};
1471
1472 #[test]
1473 #[allow(deprecated)]
1474 fn test_image_alpha_blending() {
1476 let mut target = ImageBuffer::new(1, 1);
1477 target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
1478 assert!(*target.get_pixel(0, 0) == Rgba([255, 0, 0, 255]));
1479 target.blend_pixel(0, 0, Rgba([0, 255, 0, 255]));
1480 assert!(*target.get_pixel(0, 0) == Rgba([0, 255, 0, 255]));
1481
1482 target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
1484 assert!(*target.get_pixel(0, 0) == Rgba([127, 127, 0, 255]));
1485
1486 target.put_pixel(0, 0, Rgba([0, 255, 0, 127]));
1488 target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
1489 assert!(*target.get_pixel(0, 0) == Rgba([169, 85, 0, 190]));
1490 }
1491
1492 #[test]
1493 fn test_in_bounds() {
1494 let mut target = ImageBuffer::new(2, 2);
1495 target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
1496
1497 assert!(target.in_bounds(0, 0));
1498 assert!(target.in_bounds(1, 0));
1499 assert!(target.in_bounds(0, 1));
1500 assert!(target.in_bounds(1, 1));
1501
1502 assert!(!target.in_bounds(2, 0));
1503 assert!(!target.in_bounds(0, 2));
1504 assert!(!target.in_bounds(2, 2));
1505 }
1506
1507 #[test]
1508 fn test_can_subimage_clone_nonmut() {
1509 let mut source = ImageBuffer::new(3, 3);
1510 source.put_pixel(1, 1, Rgba([255u8, 0, 0, 255]));
1511
1512 let source = source.clone();
1514
1515 let cloned = source.view(1, 1, 1, 1).to_image();
1517
1518 assert!(cloned.get_pixel(0, 0) == source.get_pixel(1, 1));
1519 }
1520
1521 #[test]
1522 fn test_can_nest_views() {
1523 let mut source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1524
1525 {
1526 let mut sub1 = source.sub_image(0, 0, 2, 2);
1527 let mut sub2 = sub1.sub_image(1, 1, 1, 1);
1528 sub2.put_pixel(0, 0, Rgba([0, 0, 0, 0]));
1529 }
1530
1531 assert_eq!(*source.get_pixel(1, 1), Rgba([0, 0, 0, 0]));
1532
1533 let view1 = source.view(0, 0, 2, 2);
1534 assert_eq!(*source.get_pixel(1, 1), view1.get_pixel(1, 1));
1535
1536 let view2 = view1.view(1, 1, 1, 1);
1537 assert_eq!(*source.get_pixel(1, 1), view2.get_pixel(0, 0));
1538 }
1539
1540 #[test]
1541 #[should_panic]
1542 fn test_view_out_of_bounds() {
1543 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1544 source.view(1, 1, 3, 3);
1545 }
1546
1547 #[test]
1548 #[should_panic]
1549 fn test_view_coordinates_out_of_bounds() {
1550 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1551 source.view(3, 3, 3, 3);
1552 }
1553
1554 #[test]
1555 #[should_panic]
1556 fn test_view_width_out_of_bounds() {
1557 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1558 source.view(1, 1, 3, 2);
1559 }
1560
1561 #[test]
1562 #[should_panic]
1563 fn test_view_height_out_of_bounds() {
1564 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1565 source.view(1, 1, 2, 3);
1566 }
1567
1568 #[test]
1569 #[should_panic]
1570 fn test_view_x_out_of_bounds() {
1571 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1572 source.view(3, 1, 3, 3);
1573 }
1574
1575 #[test]
1576 #[should_panic]
1577 fn test_view_y_out_of_bounds() {
1578 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1579 source.view(1, 3, 3, 3);
1580 }
1581
1582 #[test]
1583 fn test_view_in_bounds() {
1584 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1585 source.view(0, 0, 3, 3);
1586 source.view(1, 1, 2, 2);
1587 source.view(2, 2, 0, 0);
1588 }
1589
1590 #[test]
1591 fn test_copy_sub_image() {
1592 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1593 let view = source.view(0, 0, 3, 3);
1594 let _view2 = view;
1595 view.to_image();
1596 }
1597
1598 #[test]
1599 fn test_load_rect() {
1600 struct MockDecoder {
1601 scanline_number: u64,
1602 scanline_bytes: u64,
1603 }
1604 impl<'a> ImageDecoder<'a> for MockDecoder {
1605 type Reader = Box<dyn io::Read>;
1606 fn dimensions(&self) -> (u32, u32) {
1607 (5, 5)
1608 }
1609 fn color_type(&self) -> ColorType {
1610 ColorType::L8
1611 }
1612 fn into_reader(self) -> ImageResult<Self::Reader> {
1613 unimplemented!()
1614 }
1615 fn scanline_bytes(&self) -> u64 {
1616 self.scanline_bytes
1617 }
1618 }
1619
1620 const DATA: [u8; 25] = [
1621 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1622 24,
1623 ];
1624
1625 fn seek_scanline(m: &mut MockDecoder, n: u64) -> io::Result<()> {
1626 m.scanline_number = n;
1627 Ok(())
1628 }
1629 fn read_scanline(m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> {
1630 let bytes_read = m.scanline_number * m.scanline_bytes;
1631 if bytes_read >= 25 {
1632 return Ok(());
1633 }
1634
1635 let len = m.scanline_bytes.min(25 - bytes_read);
1636 buf[..(len as usize)].copy_from_slice(&DATA[(bytes_read as usize)..][..(len as usize)]);
1637 m.scanline_number += 1;
1638 Ok(())
1639 }
1640
1641 for scanline_bytes in 1..30 {
1642 let mut output = [0u8; 26];
1643
1644 load_rect(
1645 0,
1646 0,
1647 5,
1648 5,
1649 &mut output,
1650 |_| {},
1651 &mut MockDecoder {
1652 scanline_number: 0,
1653 scanline_bytes,
1654 },
1655 seek_scanline,
1656 read_scanline,
1657 )
1658 .unwrap();
1659 assert_eq!(output[0..25], DATA);
1660 assert_eq!(output[25], 0);
1661
1662 output = [0u8; 26];
1663 load_rect(
1664 3,
1665 2,
1666 1,
1667 1,
1668 &mut output,
1669 |_| {},
1670 &mut MockDecoder {
1671 scanline_number: 0,
1672 scanline_bytes,
1673 },
1674 seek_scanline,
1675 read_scanline,
1676 )
1677 .unwrap();
1678 assert_eq!(output[0..2], [13, 0]);
1679
1680 output = [0u8; 26];
1681 load_rect(
1682 3,
1683 2,
1684 2,
1685 2,
1686 &mut output,
1687 |_| {},
1688 &mut MockDecoder {
1689 scanline_number: 0,
1690 scanline_bytes,
1691 },
1692 seek_scanline,
1693 read_scanline,
1694 )
1695 .unwrap();
1696 assert_eq!(output[0..5], [13, 14, 18, 19, 0]);
1697
1698 output = [0u8; 26];
1699 load_rect(
1700 1,
1701 1,
1702 2,
1703 4,
1704 &mut output,
1705 |_| {},
1706 &mut MockDecoder {
1707 scanline_number: 0,
1708 scanline_bytes,
1709 },
1710 seek_scanline,
1711 read_scanline,
1712 )
1713 .unwrap();
1714 assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]);
1715 }
1716 }
1717
1718 #[test]
1719 fn test_load_rect_single_scanline() {
1720 const DATA: [u8; 25] = [
1721 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1722 24,
1723 ];
1724
1725 struct MockDecoder;
1726 impl<'a> ImageDecoder<'a> for MockDecoder {
1727 type Reader = Box<dyn io::Read>;
1728 fn dimensions(&self) -> (u32, u32) {
1729 (5, 5)
1730 }
1731 fn color_type(&self) -> ColorType {
1732 ColorType::L8
1733 }
1734 fn into_reader(self) -> ImageResult<Self::Reader> {
1735 unimplemented!()
1736 }
1737 fn scanline_bytes(&self) -> u64 {
1738 25
1739 }
1740 }
1741
1742 let mut seeks = 0;
1744 let seek_scanline = |_d: &mut MockDecoder, n: u64| -> io::Result<()> {
1745 seeks += 1;
1746 assert_eq!(n, 0);
1747 assert_eq!(seeks, 1);
1748 Ok(())
1749 };
1750
1751 fn read_scanline(_m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> {
1752 buf.copy_from_slice(&DATA);
1753 Ok(())
1754 }
1755
1756 let mut output = [0; 26];
1757 load_rect(
1758 1,
1759 1,
1760 2,
1761 4,
1762 &mut output,
1763 |_| {},
1764 &mut MockDecoder,
1765 seek_scanline,
1766 read_scanline,
1767 )
1768 .unwrap();
1769 assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]);
1770 }
1771
1772 #[test]
1773 fn test_image_format_from_path() {
1774 fn from_path(s: &str) -> ImageResult<ImageFormat> {
1775 ImageFormat::from_path(Path::new(s))
1776 }
1777 assert_eq!(from_path("./a.jpg").unwrap(), ImageFormat::Jpeg);
1778 assert_eq!(from_path("./a.jpeg").unwrap(), ImageFormat::Jpeg);
1779 assert_eq!(from_path("./a.JPEG").unwrap(), ImageFormat::Jpeg);
1780 assert_eq!(from_path("./a.pNg").unwrap(), ImageFormat::Png);
1781 assert_eq!(from_path("./a.gif").unwrap(), ImageFormat::Gif);
1782 assert_eq!(from_path("./a.webp").unwrap(), ImageFormat::WebP);
1783 assert_eq!(from_path("./a.tiFF").unwrap(), ImageFormat::Tiff);
1784 assert_eq!(from_path("./a.tif").unwrap(), ImageFormat::Tiff);
1785 assert_eq!(from_path("./a.tga").unwrap(), ImageFormat::Tga);
1786 assert_eq!(from_path("./a.dds").unwrap(), ImageFormat::Dds);
1787 assert_eq!(from_path("./a.bmp").unwrap(), ImageFormat::Bmp);
1788 assert_eq!(from_path("./a.Ico").unwrap(), ImageFormat::Ico);
1789 assert_eq!(from_path("./a.hdr").unwrap(), ImageFormat::Hdr);
1790 assert_eq!(from_path("./a.exr").unwrap(), ImageFormat::OpenExr);
1791 assert_eq!(from_path("./a.pbm").unwrap(), ImageFormat::Pnm);
1792 assert_eq!(from_path("./a.pAM").unwrap(), ImageFormat::Pnm);
1793 assert_eq!(from_path("./a.Ppm").unwrap(), ImageFormat::Pnm);
1794 assert_eq!(from_path("./a.pgm").unwrap(), ImageFormat::Pnm);
1795 assert_eq!(from_path("./a.AViF").unwrap(), ImageFormat::Avif);
1796 assert!(from_path("./a.txt").is_err());
1797 assert!(from_path("./a").is_err());
1798 }
1799
1800 #[test]
1801 fn test_generic_image_copy_within_oob() {
1802 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap();
1803 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1804 Rect {
1805 x: 0,
1806 y: 0,
1807 width: 5,
1808 height: 4
1809 },
1810 0,
1811 0
1812 ));
1813 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1814 Rect {
1815 x: 0,
1816 y: 0,
1817 width: 4,
1818 height: 5
1819 },
1820 0,
1821 0
1822 ));
1823 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1824 Rect {
1825 x: 1,
1826 y: 0,
1827 width: 4,
1828 height: 4
1829 },
1830 0,
1831 0
1832 ));
1833 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1834 Rect {
1835 x: 0,
1836 y: 0,
1837 width: 4,
1838 height: 4
1839 },
1840 1,
1841 0
1842 ));
1843 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1844 Rect {
1845 x: 0,
1846 y: 1,
1847 width: 4,
1848 height: 4
1849 },
1850 0,
1851 0
1852 ));
1853 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1854 Rect {
1855 x: 0,
1856 y: 0,
1857 width: 4,
1858 height: 4
1859 },
1860 0,
1861 1
1862 ));
1863 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1864 Rect {
1865 x: 1,
1866 y: 1,
1867 width: 4,
1868 height: 4
1869 },
1870 0,
1871 0
1872 ));
1873 }
1874
1875 #[test]
1876 fn test_generic_image_copy_within_tl() {
1877 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1878 let expected = [0, 1, 2, 3, 4, 0, 1, 2, 8, 4, 5, 6, 12, 8, 9, 10];
1879 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1880 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1881 Rect {
1882 x: 0,
1883 y: 0,
1884 width: 3,
1885 height: 3
1886 },
1887 1,
1888 1
1889 ));
1890 assert_eq!(&image.into_raw(), &expected);
1891 }
1892
1893 #[test]
1894 fn test_generic_image_copy_within_tr() {
1895 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1896 let expected = [0, 1, 2, 3, 1, 2, 3, 7, 5, 6, 7, 11, 9, 10, 11, 15];
1897 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1898 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1899 Rect {
1900 x: 1,
1901 y: 0,
1902 width: 3,
1903 height: 3
1904 },
1905 0,
1906 1
1907 ));
1908 assert_eq!(&image.into_raw(), &expected);
1909 }
1910
1911 #[test]
1912 fn test_generic_image_copy_within_bl() {
1913 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1914 let expected = [0, 4, 5, 6, 4, 8, 9, 10, 8, 12, 13, 14, 12, 13, 14, 15];
1915 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1916 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1917 Rect {
1918 x: 0,
1919 y: 1,
1920 width: 3,
1921 height: 3
1922 },
1923 1,
1924 0
1925 ));
1926 assert_eq!(&image.into_raw(), &expected);
1927 }
1928
1929 #[test]
1930 fn test_generic_image_copy_within_br() {
1931 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1932 let expected = [5, 6, 7, 3, 9, 10, 11, 7, 13, 14, 15, 11, 12, 13, 14, 15];
1933 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1934 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1935 Rect {
1936 x: 1,
1937 y: 1,
1938 width: 3,
1939 height: 3
1940 },
1941 0,
1942 0
1943 ));
1944 assert_eq!(&image.into_raw(), &expected);
1945 }
1946
1947 #[test]
1948 fn image_formats_are_recognized() {
1949 use ImageFormat::*;
1950 const ALL_FORMATS: &[ImageFormat] = &[
1951 Avif, Png, Jpeg, Gif, WebP, Pnm, Tiff, Tga, Dds, Bmp, Ico, Hdr, Farbfeld, OpenExr,
1952 ];
1953 for &format in ALL_FORMATS {
1954 let mut file = Path::new("file.nothing").to_owned();
1955 for ext in format.extensions_str() {
1956 assert!(file.set_extension(ext));
1957 match ImageFormat::from_path(&file) {
1958 Err(_) => panic!("Path {} not recognized as {:?}", file.display(), format),
1959 Ok(result) => assert_eq!(format, result),
1960 }
1961 }
1962 }
1963 }
1964
1965 #[test]
1966 fn total_bytes_overflow() {
1967 struct D;
1968 impl<'a> ImageDecoder<'a> for D {
1969 type Reader = std::io::Cursor<Vec<u8>>;
1970 fn color_type(&self) -> ColorType {
1971 ColorType::Rgb8
1972 }
1973 fn dimensions(&self) -> (u32, u32) {
1974 (0xffffffff, 0xffffffff)
1975 }
1976 fn into_reader(self) -> ImageResult<Self::Reader> {
1977 unreachable!()
1978 }
1979 }
1980 assert_eq!(D.total_bytes(), u64::max_value());
1981
1982 let v: ImageResult<Vec<u8>> = super::decoder_to_vec(D);
1983 assert!(v.is_err());
1984 }
1985
1986 #[test]
1987 fn all() {
1988 let all_formats: HashSet<ImageFormat> = HashSet::from_iter(ImageFormat::all());
1989 assert!(all_formats.contains(&ImageFormat::Avif));
1990 assert!(all_formats.contains(&ImageFormat::Gif));
1991 assert!(all_formats.contains(&ImageFormat::Bmp));
1992 assert!(all_formats.contains(&ImageFormat::Farbfeld));
1993 assert!(all_formats.contains(&ImageFormat::Jpeg));
1994 }
1995
1996 #[test]
1997 fn reading_enabled() {
1998 assert_eq!(cfg!(feature = "jpeg"), ImageFormat::Jpeg.reading_enabled());
1999 assert!(!ImageFormat::Dds.reading_enabled());
2000 }
2001
2002 #[test]
2003 fn writing_enabled() {
2004 assert_eq!(cfg!(feature = "jpeg"), ImageFormat::Jpeg.writing_enabled());
2005 assert!(!ImageFormat::Hdr.writing_enabled());
2006 assert!(!ImageFormat::Dds.writing_enabled());
2007 }
2008}