1#![allow(clippy::too_many_arguments)]
2use std::ffi::OsStr;
3use std::io::{self, Write};
4use std::mem::size_of;
5use std::ops::{Deref, DerefMut};
6use std::path::Path;
7
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11use crate::color::{ColorType, ExtendedColorType};
12use crate::error::{
13 ImageError, ImageFormatHint, ImageResult, LimitError, LimitErrorKind, ParameterError,
14 ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
15};
16use crate::math::Rect;
17use crate::metadata::Orientation;
18use crate::traits::Pixel;
19use crate::ImageBuffer;
20
21use crate::animation::Frames;
22
23#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27#[non_exhaustive]
28pub enum ImageFormat {
29 Png,
31
32 Jpeg,
34
35 Gif,
37
38 WebP,
40
41 Pnm,
43
44 Tiff,
46
47 Tga,
49
50 Dds,
52
53 Bmp,
55
56 Ico,
58
59 Hdr,
61
62 OpenExr,
64
65 Farbfeld,
67
68 Avif,
70
71 Qoi,
73
74 Pcx,
76}
77
78impl ImageFormat {
79 #[inline]
90 pub fn from_extension<S>(ext: S) -> Option<Self>
91 where
92 S: AsRef<OsStr>,
93 {
94 fn inner(ext: &OsStr) -> Option<ImageFormat> {
96 let ext = ext.to_str()?.to_ascii_lowercase();
97
98 Some(match ext.as_str() {
99 "avif" => ImageFormat::Avif,
100 "jpg" | "jpeg" | "jfif" => ImageFormat::Jpeg,
101 "png" | "apng" => ImageFormat::Png,
102 "gif" => ImageFormat::Gif,
103 "webp" => ImageFormat::WebP,
104 "tif" | "tiff" => ImageFormat::Tiff,
105 "tga" => ImageFormat::Tga,
106 "dds" => ImageFormat::Dds,
107 "bmp" => ImageFormat::Bmp,
108 "ico" => ImageFormat::Ico,
109 "hdr" => ImageFormat::Hdr,
110 "exr" => ImageFormat::OpenExr,
111 "pbm" | "pam" | "ppm" | "pgm" => ImageFormat::Pnm,
112 "ff" => ImageFormat::Farbfeld,
113 "qoi" => ImageFormat::Qoi,
114 "pcx" => ImageFormat::Pcx,
115 _ => return None,
116 })
117 }
118
119 inner(ext.as_ref())
120 }
121
122 #[inline]
135 pub fn from_path<P>(path: P) -> ImageResult<Self>
136 where
137 P: AsRef<Path>,
138 {
139 fn inner(path: &Path) -> ImageResult<ImageFormat> {
141 let exact_ext = path.extension();
142 exact_ext
143 .and_then(ImageFormat::from_extension)
144 .ok_or_else(|| {
145 let format_hint = match exact_ext {
146 None => ImageFormatHint::Unknown,
147 Some(os) => ImageFormatHint::PathExtension(os.into()),
148 };
149 ImageError::Unsupported(format_hint.into())
150 })
151 }
152
153 inner(path.as_ref())
154 }
155
156 pub fn from_mime_type<M>(mime_type: M) -> Option<Self>
167 where
168 M: AsRef<str>,
169 {
170 match mime_type.as_ref() {
171 "image/avif" => Some(ImageFormat::Avif),
172 "image/jpeg" => Some(ImageFormat::Jpeg),
173 "image/png" => Some(ImageFormat::Png),
174 "image/gif" => Some(ImageFormat::Gif),
175 "image/webp" => Some(ImageFormat::WebP),
176 "image/tiff" => Some(ImageFormat::Tiff),
177 "image/x-targa" | "image/x-tga" => Some(ImageFormat::Tga),
178 "image/vnd-ms.dds" => Some(ImageFormat::Dds),
179 "image/bmp" => Some(ImageFormat::Bmp),
180 "image/x-icon" | "image/vnd.microsoft.icon" => Some(ImageFormat::Ico),
181 "image/vnd.radiance" => Some(ImageFormat::Hdr),
182 "image/x-exr" => Some(ImageFormat::OpenExr),
183 "image/x-portable-bitmap"
184 | "image/x-portable-graymap"
185 | "image/x-portable-pixmap"
186 | "image/x-portable-anymap" => Some(ImageFormat::Pnm),
187 "image/x-qoi" => Some(ImageFormat::Qoi),
190 "image/vnd.zbrush.pcx" | "image/x-pcx" => Some(ImageFormat::Pcx),
191 _ => None,
192 }
193 }
194
195 #[must_use]
216 pub fn to_mime_type(&self) -> &'static str {
217 match self {
218 ImageFormat::Avif => "image/avif",
219 ImageFormat::Jpeg => "image/jpeg",
220 ImageFormat::Png => "image/png",
221 ImageFormat::Gif => "image/gif",
222 ImageFormat::WebP => "image/webp",
223 ImageFormat::Tiff => "image/tiff",
224 ImageFormat::Tga => "image/x-targa",
226 ImageFormat::Dds => "image/vnd-ms.dds",
227 ImageFormat::Bmp => "image/bmp",
228 ImageFormat::Ico => "image/x-icon",
229 ImageFormat::Hdr => "image/vnd.radiance",
230 ImageFormat::OpenExr => "image/x-exr",
231 ImageFormat::Pnm => "image/x-portable-anymap",
233 ImageFormat::Qoi => "image/x-qoi",
236 ImageFormat::Farbfeld => "application/octet-stream",
238 ImageFormat::Pcx => "image/vnd.zbrush.pcx",
239 }
240 }
241
242 #[inline]
244 #[must_use]
245 pub fn can_read(&self) -> bool {
246 match self {
248 ImageFormat::Png => true,
249 ImageFormat::Gif => true,
250 ImageFormat::Jpeg => true,
251 ImageFormat::WebP => true,
252 ImageFormat::Tiff => true,
253 ImageFormat::Tga => true,
254 ImageFormat::Dds => false,
255 ImageFormat::Bmp => true,
256 ImageFormat::Ico => true,
257 ImageFormat::Hdr => true,
258 ImageFormat::OpenExr => true,
259 ImageFormat::Pnm => true,
260 ImageFormat::Farbfeld => true,
261 ImageFormat::Avif => true,
262 ImageFormat::Qoi => true,
263 ImageFormat::Pcx => true,
264 }
265 }
266
267 #[inline]
269 #[must_use]
270 pub fn can_write(&self) -> bool {
271 match self {
273 ImageFormat::Gif => true,
274 ImageFormat::Ico => true,
275 ImageFormat::Jpeg => true,
276 ImageFormat::Png => true,
277 ImageFormat::Bmp => true,
278 ImageFormat::Tiff => true,
279 ImageFormat::Tga => true,
280 ImageFormat::Pnm => true,
281 ImageFormat::Farbfeld => true,
282 ImageFormat::Avif => true,
283 ImageFormat::WebP => true,
284 ImageFormat::Hdr => true,
285 ImageFormat::OpenExr => true,
286 ImageFormat::Dds => false,
287 ImageFormat::Qoi => true,
288 ImageFormat::Pcx => false,
289 }
290 }
291
292 #[must_use]
302 pub fn extensions_str(self) -> &'static [&'static str] {
303 match self {
304 ImageFormat::Png => &["png"],
305 ImageFormat::Jpeg => &["jpg", "jpeg"],
306 ImageFormat::Gif => &["gif"],
307 ImageFormat::WebP => &["webp"],
308 ImageFormat::Pnm => &["pbm", "pam", "ppm", "pgm"],
309 ImageFormat::Tiff => &["tiff", "tif"],
310 ImageFormat::Tga => &["tga"],
311 ImageFormat::Dds => &["dds"],
312 ImageFormat::Bmp => &["bmp"],
313 ImageFormat::Ico => &["ico"],
314 ImageFormat::Hdr => &["hdr"],
315 ImageFormat::OpenExr => &["exr"],
316 ImageFormat::Farbfeld => &["ff"],
317 ImageFormat::Avif => &["avif"],
319 ImageFormat::Qoi => &["qoi"],
320 ImageFormat::Pcx => &["pcx"],
321 }
322 }
323
324 #[inline]
326 #[must_use]
327 pub fn reading_enabled(&self) -> bool {
328 match self {
329 ImageFormat::Png => cfg!(feature = "png"),
330 ImageFormat::Gif => cfg!(feature = "gif"),
331 ImageFormat::Jpeg => cfg!(feature = "jpeg"),
332 ImageFormat::WebP => cfg!(feature = "webp"),
333 ImageFormat::Tiff => cfg!(feature = "tiff"),
334 ImageFormat::Tga => cfg!(feature = "tga"),
335 ImageFormat::Bmp => cfg!(feature = "bmp"),
336 ImageFormat::Ico => cfg!(feature = "ico"),
337 ImageFormat::Hdr => cfg!(feature = "hdr"),
338 ImageFormat::OpenExr => cfg!(feature = "exr"),
339 ImageFormat::Pnm => cfg!(feature = "pnm"),
340 ImageFormat::Farbfeld => cfg!(feature = "ff"),
341 ImageFormat::Avif => cfg!(feature = "avif"),
342 ImageFormat::Qoi => cfg!(feature = "qoi"),
343 ImageFormat::Pcx => cfg!(feature = "pcx"),
344 ImageFormat::Dds => false,
345 }
346 }
347
348 #[inline]
350 #[must_use]
351 pub fn writing_enabled(&self) -> bool {
352 match self {
353 ImageFormat::Gif => cfg!(feature = "gif"),
354 ImageFormat::Ico => cfg!(feature = "ico"),
355 ImageFormat::Jpeg => cfg!(feature = "jpeg"),
356 ImageFormat::Png => cfg!(feature = "png"),
357 ImageFormat::Bmp => cfg!(feature = "bmp"),
358 ImageFormat::Tiff => cfg!(feature = "tiff"),
359 ImageFormat::Tga => cfg!(feature = "tga"),
360 ImageFormat::Pnm => cfg!(feature = "pnm"),
361 ImageFormat::Farbfeld => cfg!(feature = "ff"),
362 ImageFormat::Avif => cfg!(feature = "avif"),
363 ImageFormat::WebP => cfg!(feature = "webp"),
364 ImageFormat::OpenExr => cfg!(feature = "exr"),
365 ImageFormat::Qoi => cfg!(feature = "qoi"),
366 ImageFormat::Hdr => cfg!(feature = "hdr"),
367 ImageFormat::Pcx => false,
368 ImageFormat::Dds => false,
369 }
370 }
371
372 pub fn all() -> impl Iterator<Item = ImageFormat> {
374 [
375 ImageFormat::Gif,
376 ImageFormat::Ico,
377 ImageFormat::Jpeg,
378 ImageFormat::Png,
379 ImageFormat::Bmp,
380 ImageFormat::Tiff,
381 ImageFormat::Tga,
382 ImageFormat::Pnm,
383 ImageFormat::Farbfeld,
384 ImageFormat::Avif,
385 ImageFormat::WebP,
386 ImageFormat::OpenExr,
387 ImageFormat::Qoi,
388 ImageFormat::Dds,
389 ImageFormat::Hdr,
390 ImageFormat::Pcx,
391 ]
392 .iter()
393 .copied()
394 }
395}
396
397#[allow(dead_code)]
400pub(crate) struct ImageReadBuffer {
402 scanline_bytes: usize,
403 buffer: Vec<u8>,
404 consumed: usize,
405
406 total_bytes: u64,
407 offset: u64,
408}
409impl ImageReadBuffer {
410 #[allow(dead_code)]
416 pub(crate) fn new(scanline_bytes: u64, total_bytes: u64) -> Self {
418 Self {
419 scanline_bytes: usize::try_from(scanline_bytes).unwrap(),
420 buffer: Vec::new(),
421 consumed: 0,
422 total_bytes,
423 offset: 0,
424 }
425 }
426
427 #[allow(dead_code)]
428 pub(crate) fn read<F>(&mut self, buf: &mut [u8], mut read_scanline: F) -> io::Result<usize>
430 where
431 F: FnMut(&mut [u8]) -> io::Result<usize>,
432 {
433 if self.buffer.len() == self.consumed {
434 if self.offset == self.total_bytes {
435 return Ok(0);
436 } else if buf.len() >= self.scanline_bytes {
437 let bytes_read = read_scanline(&mut buf[..self.scanline_bytes])?;
440 self.offset += u64::try_from(bytes_read).unwrap();
441 return Ok(bytes_read);
442 } else {
443 if self.buffer.is_empty() {
446 self.buffer.resize(self.scanline_bytes, 0);
447 }
448
449 self.consumed = 0;
450 let bytes_read = read_scanline(&mut self.buffer[..])?;
451 self.buffer.resize(bytes_read, 0);
452 self.offset += u64::try_from(bytes_read).unwrap();
453
454 assert!(bytes_read == self.scanline_bytes || self.offset == self.total_bytes);
455 }
456 }
457
458 let bytes_buffered = self.buffer.len() - self.consumed;
460 if bytes_buffered > buf.len() {
461 buf.copy_from_slice(&self.buffer[self.consumed..][..buf.len()]);
462 self.consumed += buf.len();
463 Ok(buf.len())
464 } else {
465 buf[..bytes_buffered].copy_from_slice(&self.buffer[self.consumed..][..bytes_buffered]);
466 self.consumed = self.buffer.len();
467 Ok(bytes_buffered)
468 }
469 }
470}
471
472#[allow(dead_code)]
475pub(crate) fn load_rect<D, F1, F2, E>(
477 x: u32,
478 y: u32,
479 width: u32,
480 height: u32,
481 buf: &mut [u8],
482 row_pitch: usize,
483 decoder: &mut D,
484 scanline_bytes: usize,
485 mut seek_scanline: F1,
486 mut read_scanline: F2,
487) -> ImageResult<()>
488where
489 D: ImageDecoder,
490 F1: FnMut(&mut D, u64) -> io::Result<()>,
491 F2: FnMut(&mut D, &mut [u8]) -> Result<(), E>,
492 ImageError: From<E>,
493{
494 let scanline_bytes = u64::try_from(scanline_bytes).unwrap();
495 let row_pitch = u64::try_from(row_pitch).unwrap();
496
497 let (x, y, width, height) = (
498 u64::from(x),
499 u64::from(y),
500 u64::from(width),
501 u64::from(height),
502 );
503 let dimensions = decoder.dimensions();
504 let bytes_per_pixel = u64::from(decoder.color_type().bytes_per_pixel());
505 let row_bytes = bytes_per_pixel * u64::from(dimensions.0);
506 let total_bytes = width * height * bytes_per_pixel;
507
508 assert!(
509 buf.len() >= usize::try_from(total_bytes).unwrap_or(usize::MAX),
510 "output buffer too short\n expected `{}`, provided `{}`",
511 total_bytes,
512 buf.len()
513 );
514
515 let mut current_scanline = 0;
516 let mut tmp = Vec::new();
517 let mut tmp_scanline = None;
518
519 {
520 let mut read_image_range =
523 |mut start: u64, end: u64, mut output: &mut [u8]| -> ImageResult<()> {
524 let target_scanline = start / scanline_bytes;
527 if tmp_scanline == Some(target_scanline) {
528 let position = target_scanline * scanline_bytes;
529 let offset = start.saturating_sub(position);
530 let len = (end - start)
531 .min(scanline_bytes - offset)
532 .min(end - position);
533
534 output
535 .write_all(&tmp[offset as usize..][..len as usize])
536 .unwrap();
537 start += len;
538
539 if start == end {
540 return Ok(());
541 }
542 }
543
544 let target_scanline = start / scanline_bytes;
545 if target_scanline != current_scanline {
546 seek_scanline(decoder, target_scanline)?;
547 current_scanline = target_scanline;
548 }
549
550 let mut position = current_scanline * scanline_bytes;
551 while position < end {
552 if position >= start && end - position >= scanline_bytes {
553 read_scanline(decoder, &mut output[..(scanline_bytes as usize)])?;
554 output = &mut output[scanline_bytes as usize..];
555 } else {
556 tmp.resize(scanline_bytes as usize, 0u8);
557 read_scanline(decoder, &mut tmp)?;
558 tmp_scanline = Some(current_scanline);
559
560 let offset = start.saturating_sub(position);
561 let len = (end - start)
562 .min(scanline_bytes - offset)
563 .min(end - position);
564
565 output
566 .write_all(&tmp[offset as usize..][..len as usize])
567 .unwrap();
568 }
569
570 current_scanline += 1;
571 position += scanline_bytes;
572 }
573 Ok(())
574 };
575
576 if x + width > u64::from(dimensions.0)
577 || y + height > u64::from(dimensions.1)
578 || width == 0
579 || height == 0
580 {
581 return Err(ImageError::Parameter(ParameterError::from_kind(
582 ParameterErrorKind::DimensionMismatch,
583 )));
584 }
585 if scanline_bytes > usize::MAX as u64 {
586 return Err(ImageError::Limits(LimitError::from_kind(
587 LimitErrorKind::InsufficientMemory,
588 )));
589 }
590
591 if x == 0 && width == u64::from(dimensions.0) && row_pitch == row_bytes {
592 let start = x * bytes_per_pixel + y * row_bytes;
593 let end = (x + width) * bytes_per_pixel + (y + height - 1) * row_bytes;
594 read_image_range(start, end, buf)?;
595 } else {
596 for (output_slice, row) in buf.chunks_mut(row_pitch as usize).zip(y..(y + height)) {
597 let start = x * bytes_per_pixel + row * row_bytes;
598 let end = (x + width) * bytes_per_pixel + row * row_bytes;
599 read_image_range(start, end, output_slice)?;
600 }
601 }
602 }
603
604 Ok(seek_scanline(decoder, 0)?)
606}
607
608pub(crate) fn decoder_to_vec<T>(decoder: impl ImageDecoder) -> ImageResult<Vec<T>>
613where
614 T: crate::traits::Primitive + bytemuck::Pod,
615{
616 let total_bytes = usize::try_from(decoder.total_bytes());
617 if total_bytes.is_err() || total_bytes.unwrap() > isize::MAX as usize {
618 return Err(ImageError::Limits(LimitError::from_kind(
619 LimitErrorKind::InsufficientMemory,
620 )));
621 }
622
623 let mut buf = vec![num_traits::Zero::zero(); total_bytes.unwrap() / size_of::<T>()];
624 decoder.read_image(bytemuck::cast_slice_mut(buf.as_mut_slice()))?;
625 Ok(buf)
626}
627
628pub trait ImageDecoder {
630 fn dimensions(&self) -> (u32, u32);
632
633 fn color_type(&self) -> ColorType;
635
636 fn original_color_type(&self) -> ExtendedColorType {
638 self.color_type().into()
639 }
640
641 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
645 Ok(None)
646 }
647
648 fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
653 Ok(None)
654 }
655
656 fn orientation(&mut self) -> ImageResult<Orientation> {
661 Ok(self
662 .exif_metadata()?
663 .and_then(|chunk| Orientation::from_exif_chunk(&chunk))
664 .unwrap_or(Orientation::NoTransforms))
665 }
666
667 fn total_bytes(&self) -> u64 {
674 let dimensions = self.dimensions();
675 let total_pixels = u64::from(dimensions.0) * u64::from(dimensions.1);
676 let bytes_per_pixel = u64::from(self.color_type().bytes_per_pixel());
677 total_pixels.saturating_mul(bytes_per_pixel)
678 }
679
680 fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
702 where
703 Self: Sized;
704
705 fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> {
717 limits.check_support(&crate::LimitSupport::default())?;
718 let (width, height) = self.dimensions();
719 limits.check_dimensions(width, height)?;
720 Ok(())
721 }
722
723 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()>;
734}
735
736impl<T: ?Sized + ImageDecoder> ImageDecoder for Box<T> {
737 fn dimensions(&self) -> (u32, u32) {
738 (**self).dimensions()
739 }
740 fn color_type(&self) -> ColorType {
741 (**self).color_type()
742 }
743 fn original_color_type(&self) -> ExtendedColorType {
744 (**self).original_color_type()
745 }
746 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
747 (**self).icc_profile()
748 }
749 fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
750 (**self).exif_metadata()
751 }
752 fn total_bytes(&self) -> u64 {
753 (**self).total_bytes()
754 }
755 fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
756 where
757 Self: Sized,
758 {
759 T::read_image_boxed(self, buf)
760 }
761 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
762 T::read_image_boxed(*self, buf)
763 }
764 fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> {
765 (**self).set_limits(limits)
766 }
767}
768
769pub trait ImageDecoderRect: ImageDecoder {
771 fn read_rect(
779 &mut self,
780 x: u32,
781 y: u32,
782 width: u32,
783 height: u32,
784 buf: &mut [u8],
785 row_pitch: usize,
786 ) -> ImageResult<()>;
787}
788
789pub trait AnimationDecoder<'a> {
791 fn into_frames(self) -> Frames<'a>;
793}
794
795pub trait ImageEncoder {
797 fn write_image(
812 self,
813 buf: &[u8],
814 width: u32,
815 height: u32,
816 color_type: ExtendedColorType,
817 ) -> ImageResult<()>;
818
819 fn set_icc_profile(&mut self, icc_profile: Vec<u8>) -> Result<(), UnsupportedError> {
829 let _ = icc_profile;
830 Err(UnsupportedError::from_format_and_kind(
831 ImageFormatHint::Unknown,
832 UnsupportedErrorKind::GenericFeature(
833 "ICC profiles are not supported for this format".into(),
834 ),
835 ))
836 }
837}
838
839#[derive(Debug)]
841pub struct Pixels<'a, I: ?Sized + 'a> {
842 image: &'a I,
843 x: u32,
844 y: u32,
845 width: u32,
846 height: u32,
847}
848
849impl<I: GenericImageView> Iterator for Pixels<'_, I> {
850 type Item = (u32, u32, I::Pixel);
851
852 fn next(&mut self) -> Option<(u32, u32, I::Pixel)> {
853 if self.x >= self.width {
854 self.x = 0;
855 self.y += 1;
856 }
857
858 if self.y >= self.height {
859 None
860 } else {
861 let pixel = self.image.get_pixel(self.x, self.y);
862 let p = (self.x, self.y, pixel);
863
864 self.x += 1;
865
866 Some(p)
867 }
868 }
869}
870
871impl<I: ?Sized> Clone for Pixels<'_, I> {
872 fn clone(&self) -> Self {
873 Pixels { ..*self }
874 }
875}
876
877pub trait GenericImageView {
886 type Pixel: Pixel;
888
889 fn dimensions(&self) -> (u32, u32);
891
892 fn width(&self) -> u32 {
894 let (w, _) = self.dimensions();
895 w
896 }
897
898 fn height(&self) -> u32 {
900 let (_, h) = self.dimensions();
901 h
902 }
903
904 fn in_bounds(&self, x: u32, y: u32) -> bool {
906 let (width, height) = self.dimensions();
907 x < width && y < height
908 }
909
910 fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel;
916
917 unsafe fn unsafe_get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
926 self.get_pixel(x, y)
927 }
928
929 fn pixels(&self) -> Pixels<Self>
933 where
934 Self: Sized,
935 {
936 let (width, height) = self.dimensions();
937
938 Pixels {
939 image: self,
940 x: 0,
941 y: 0,
942 width,
943 height,
944 }
945 }
946
947 fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&Self>
951 where
952 Self: Sized,
953 {
954 assert!(u64::from(x) + u64::from(width) <= u64::from(self.width()));
955 assert!(u64::from(y) + u64::from(height) <= u64::from(self.height()));
956 SubImage::new(self, x, y, width, height)
957 }
958}
959
960pub trait GenericImage: GenericImageView {
962 #[deprecated(since = "0.24.0", note = "Use `get_pixel` and `put_pixel` instead.")]
983 fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel;
984
985 fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
991
992 unsafe fn unsafe_put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1001 self.put_pixel(x, y, pixel);
1002 }
1003
1004 #[deprecated(
1006 since = "0.24.0",
1007 note = "Use iterator `pixels_mut` to blend the pixels directly"
1008 )]
1009 fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
1010
1011 fn copy_from<O>(&mut self, other: &O, x: u32, y: u32) -> ImageResult<()>
1027 where
1028 O: GenericImageView<Pixel = Self::Pixel>,
1029 {
1030 if self.width() < other.width() + x || self.height() < other.height() + y {
1033 return Err(ImageError::Parameter(ParameterError::from_kind(
1034 ParameterErrorKind::DimensionMismatch,
1035 )));
1036 }
1037
1038 for k in 0..other.height() {
1039 for i in 0..other.width() {
1040 let p = other.get_pixel(i, k);
1041 self.put_pixel(i + x, k + y, p);
1042 }
1043 }
1044 Ok(())
1045 }
1046
1047 fn copy_within(&mut self, source: Rect, x: u32, y: u32) -> bool {
1055 let Rect {
1056 x: sx,
1057 y: sy,
1058 width,
1059 height,
1060 } = source;
1061 let dx = x;
1062 let dy = y;
1063 assert!(sx < self.width() && dx < self.width());
1064 assert!(sy < self.height() && dy < self.height());
1065 if self.width() - dx.max(sx) < width || self.height() - dy.max(sy) < height {
1066 return false;
1067 }
1068 macro_rules! copy_within_impl_ {
1071 ($xiter:expr, $yiter:expr) => {
1072 for y in $yiter {
1073 let sy = sy + y;
1074 let dy = dy + y;
1075 for x in $xiter {
1076 let sx = sx + x;
1077 let dx = dx + x;
1078 let pixel = self.get_pixel(sx, sy);
1079 self.put_pixel(dx, dy, pixel);
1080 }
1081 }
1082 };
1083 }
1084 match (sx < dx, sy < dy) {
1086 (true, true) => copy_within_impl_!((0..width).rev(), (0..height).rev()),
1087 (true, false) => copy_within_impl_!((0..width).rev(), 0..height),
1088 (false, true) => copy_within_impl_!(0..width, (0..height).rev()),
1089 (false, false) => copy_within_impl_!(0..width, 0..height),
1090 }
1091 true
1092 }
1093
1094 fn sub_image(&mut self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&mut Self>
1098 where
1099 Self: Sized,
1100 {
1101 assert!(u64::from(x) + u64::from(width) <= u64::from(self.width()));
1102 assert!(u64::from(y) + u64::from(height) <= u64::from(self.height()));
1103 SubImage::new(self, x, y, width, height)
1104 }
1105}
1106
1107#[derive(Copy, Clone)]
1129pub struct SubImage<I> {
1130 inner: SubImageInner<I>,
1131}
1132
1133#[derive(Copy, Clone)]
1138pub struct SubImageInner<I> {
1139 image: I,
1140 xoffset: u32,
1141 yoffset: u32,
1142 xstride: u32,
1143 ystride: u32,
1144}
1145
1146type DerefPixel<I> = <<I as Deref>::Target as GenericImageView>::Pixel;
1148
1149type DerefSubpixel<I> = <DerefPixel<I> as Pixel>::Subpixel;
1151
1152impl<I> SubImage<I> {
1153 pub fn new(image: I, x: u32, y: u32, width: u32, height: u32) -> SubImage<I> {
1156 SubImage {
1157 inner: SubImageInner {
1158 image,
1159 xoffset: x,
1160 yoffset: y,
1161 xstride: width,
1162 ystride: height,
1163 },
1164 }
1165 }
1166
1167 pub fn change_bounds(&mut self, x: u32, y: u32, width: u32, height: u32) {
1169 self.inner.xoffset = x;
1170 self.inner.yoffset = y;
1171 self.inner.xstride = width;
1172 self.inner.ystride = height;
1173 }
1174
1175 pub fn offsets(&self) -> (u32, u32) {
1177 (self.inner.xoffset, self.inner.yoffset)
1178 }
1179
1180 pub fn to_image(&self) -> ImageBuffer<DerefPixel<I>, Vec<DerefSubpixel<I>>>
1182 where
1183 I: Deref,
1184 I::Target: GenericImageView + 'static,
1185 {
1186 let mut out = ImageBuffer::new(self.inner.xstride, self.inner.ystride);
1187 let borrowed = &*self.inner.image;
1188
1189 for y in 0..self.inner.ystride {
1190 for x in 0..self.inner.xstride {
1191 let p = borrowed.get_pixel(x + self.inner.xoffset, y + self.inner.yoffset);
1192 out.put_pixel(x, y, p);
1193 }
1194 }
1195
1196 out
1197 }
1198}
1199
1200impl<I> SubImage<I>
1202where
1203 I: Deref,
1204 I::Target: GenericImageView,
1205{
1206 pub fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&I::Target> {
1225 use crate::GenericImageView as _;
1226 assert!(u64::from(x) + u64::from(width) <= u64::from(self.inner.width()));
1227 assert!(u64::from(y) + u64::from(height) <= u64::from(self.inner.height()));
1228 let x = self.inner.xoffset.saturating_add(x);
1229 let y = self.inner.yoffset.saturating_add(y);
1230 SubImage::new(&*self.inner.image, x, y, width, height)
1231 }
1232
1233 pub fn inner(&self) -> &I::Target {
1235 &self.inner.image
1236 }
1237}
1238
1239impl<I> SubImage<I>
1240where
1241 I: DerefMut,
1242 I::Target: GenericImage,
1243{
1244 pub fn sub_image(
1248 &mut self,
1249 x: u32,
1250 y: u32,
1251 width: u32,
1252 height: u32,
1253 ) -> SubImage<&mut I::Target> {
1254 assert!(u64::from(x) + u64::from(width) <= u64::from(self.inner.width()));
1255 assert!(u64::from(y) + u64::from(height) <= u64::from(self.inner.height()));
1256 let x = self.inner.xoffset.saturating_add(x);
1257 let y = self.inner.yoffset.saturating_add(y);
1258 SubImage::new(&mut *self.inner.image, x, y, width, height)
1259 }
1260
1261 pub fn inner_mut(&mut self) -> &mut I::Target {
1263 &mut self.inner.image
1264 }
1265}
1266
1267impl<I> Deref for SubImage<I>
1268where
1269 I: Deref,
1270{
1271 type Target = SubImageInner<I>;
1272
1273 fn deref(&self) -> &Self::Target {
1274 &self.inner
1275 }
1276}
1277
1278impl<I> DerefMut for SubImage<I>
1279where
1280 I: DerefMut,
1281{
1282 fn deref_mut(&mut self) -> &mut Self::Target {
1283 &mut self.inner
1284 }
1285}
1286
1287#[allow(deprecated)]
1288impl<I> GenericImageView for SubImageInner<I>
1289where
1290 I: Deref,
1291 I::Target: GenericImageView,
1292{
1293 type Pixel = DerefPixel<I>;
1294
1295 fn dimensions(&self) -> (u32, u32) {
1296 (self.xstride, self.ystride)
1297 }
1298
1299 fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
1300 self.image.get_pixel(x + self.xoffset, y + self.yoffset)
1301 }
1302}
1303
1304#[allow(deprecated)]
1305impl<I> GenericImage for SubImageInner<I>
1306where
1307 I: DerefMut,
1308 I::Target: GenericImage + Sized,
1309{
1310 fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel {
1311 self.image.get_pixel_mut(x + self.xoffset, y + self.yoffset)
1312 }
1313
1314 fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1315 self.image
1316 .put_pixel(x + self.xoffset, y + self.yoffset, pixel);
1317 }
1318
1319 fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1321 self.image
1322 .blend_pixel(x + self.xoffset, y + self.yoffset, pixel);
1323 }
1324}
1325
1326#[cfg(test)]
1327mod tests {
1328 use std::collections::HashSet;
1329 use std::io;
1330 use std::path::Path;
1331
1332 use super::{
1333 load_rect, ColorType, GenericImage, GenericImageView, ImageDecoder, ImageFormat,
1334 ImageResult,
1335 };
1336 use crate::color::Rgba;
1337 use crate::math::Rect;
1338 use crate::{GrayImage, ImageBuffer};
1339
1340 #[test]
1341 #[allow(deprecated)]
1342 fn test_image_alpha_blending() {
1344 let mut target = ImageBuffer::new(1, 1);
1345 target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
1346 assert!(*target.get_pixel(0, 0) == Rgba([255, 0, 0, 255]));
1347 target.blend_pixel(0, 0, Rgba([0, 255, 0, 255]));
1348 assert!(*target.get_pixel(0, 0) == Rgba([0, 255, 0, 255]));
1349
1350 target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
1352 assert!(*target.get_pixel(0, 0) == Rgba([127, 127, 0, 255]));
1353
1354 target.put_pixel(0, 0, Rgba([0, 255, 0, 127]));
1356 target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
1357 assert!(*target.get_pixel(0, 0) == Rgba([169, 85, 0, 190]));
1358 }
1359
1360 #[test]
1361 fn test_in_bounds() {
1362 let mut target = ImageBuffer::new(2, 2);
1363 target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
1364
1365 assert!(target.in_bounds(0, 0));
1366 assert!(target.in_bounds(1, 0));
1367 assert!(target.in_bounds(0, 1));
1368 assert!(target.in_bounds(1, 1));
1369
1370 assert!(!target.in_bounds(2, 0));
1371 assert!(!target.in_bounds(0, 2));
1372 assert!(!target.in_bounds(2, 2));
1373 }
1374
1375 #[test]
1376 fn test_can_subimage_clone_nonmut() {
1377 let mut source = ImageBuffer::new(3, 3);
1378 source.put_pixel(1, 1, Rgba([255u8, 0, 0, 255]));
1379
1380 let source = source.clone();
1382
1383 let cloned = source.view(1, 1, 1, 1).to_image();
1385
1386 assert!(cloned.get_pixel(0, 0) == source.get_pixel(1, 1));
1387 }
1388
1389 #[test]
1390 fn test_can_nest_views() {
1391 let mut source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1392
1393 {
1394 let mut sub1 = source.sub_image(0, 0, 2, 2);
1395 let mut sub2 = sub1.sub_image(1, 1, 1, 1);
1396 sub2.put_pixel(0, 0, Rgba([0, 0, 0, 0]));
1397 }
1398
1399 assert_eq!(*source.get_pixel(1, 1), Rgba([0, 0, 0, 0]));
1400
1401 let view1 = source.view(0, 0, 2, 2);
1402 assert_eq!(*source.get_pixel(1, 1), view1.get_pixel(1, 1));
1403
1404 let view2 = view1.view(1, 1, 1, 1);
1405 assert_eq!(*source.get_pixel(1, 1), view2.get_pixel(0, 0));
1406 }
1407
1408 #[test]
1409 #[should_panic]
1410 fn test_view_out_of_bounds() {
1411 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1412 source.view(1, 1, 3, 3);
1413 }
1414
1415 #[test]
1416 #[should_panic]
1417 fn test_view_coordinates_out_of_bounds() {
1418 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1419 source.view(3, 3, 3, 3);
1420 }
1421
1422 #[test]
1423 #[should_panic]
1424 fn test_view_width_out_of_bounds() {
1425 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1426 source.view(1, 1, 3, 2);
1427 }
1428
1429 #[test]
1430 #[should_panic]
1431 fn test_view_height_out_of_bounds() {
1432 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1433 source.view(1, 1, 2, 3);
1434 }
1435
1436 #[test]
1437 #[should_panic]
1438 fn test_view_x_out_of_bounds() {
1439 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1440 source.view(3, 1, 3, 3);
1441 }
1442
1443 #[test]
1444 #[should_panic]
1445 fn test_view_y_out_of_bounds() {
1446 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1447 source.view(1, 3, 3, 3);
1448 }
1449
1450 #[test]
1451 fn test_view_in_bounds() {
1452 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1453 source.view(0, 0, 3, 3);
1454 source.view(1, 1, 2, 2);
1455 source.view(2, 2, 0, 0);
1456 }
1457
1458 #[test]
1459 fn test_copy_sub_image() {
1460 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1461 let view = source.view(0, 0, 3, 3);
1462 let _view2 = view;
1463 view.to_image();
1464 }
1465
1466 #[test]
1467 fn test_load_rect() {
1468 struct MockDecoder {
1469 scanline_number: u64,
1470 scanline_bytes: u64,
1471 }
1472 impl ImageDecoder for MockDecoder {
1473 fn dimensions(&self) -> (u32, u32) {
1474 (5, 5)
1475 }
1476 fn color_type(&self) -> ColorType {
1477 ColorType::L8
1478 }
1479 fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
1480 unimplemented!()
1481 }
1482 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
1483 (*self).read_image(buf)
1484 }
1485 }
1486
1487 const DATA: [u8; 25] = [
1488 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1489 24,
1490 ];
1491
1492 fn seek_scanline(m: &mut MockDecoder, n: u64) -> io::Result<()> {
1493 m.scanline_number = n;
1494 Ok(())
1495 }
1496 fn read_scanline(m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> {
1497 let bytes_read = m.scanline_number * m.scanline_bytes;
1498 if bytes_read >= 25 {
1499 return Ok(());
1500 }
1501
1502 let len = m.scanline_bytes.min(25 - bytes_read);
1503 buf[..(len as usize)].copy_from_slice(&DATA[(bytes_read as usize)..][..(len as usize)]);
1504 m.scanline_number += 1;
1505 Ok(())
1506 }
1507
1508 for scanline_bytes in 1..30 {
1509 let mut output = [0u8; 26];
1510
1511 load_rect(
1512 0,
1513 0,
1514 5,
1515 5,
1516 &mut output,
1517 5,
1518 &mut MockDecoder {
1519 scanline_number: 0,
1520 scanline_bytes,
1521 },
1522 scanline_bytes as usize,
1523 seek_scanline,
1524 read_scanline,
1525 )
1526 .unwrap();
1527 assert_eq!(output[0..25], DATA);
1528 assert_eq!(output[25], 0);
1529
1530 output = [0u8; 26];
1531 load_rect(
1532 3,
1533 2,
1534 1,
1535 1,
1536 &mut output,
1537 1,
1538 &mut MockDecoder {
1539 scanline_number: 0,
1540 scanline_bytes,
1541 },
1542 scanline_bytes as usize,
1543 seek_scanline,
1544 read_scanline,
1545 )
1546 .unwrap();
1547 assert_eq!(output[0..2], [13, 0]);
1548
1549 output = [0u8; 26];
1550 load_rect(
1551 3,
1552 2,
1553 2,
1554 2,
1555 &mut output,
1556 2,
1557 &mut MockDecoder {
1558 scanline_number: 0,
1559 scanline_bytes,
1560 },
1561 scanline_bytes as usize,
1562 seek_scanline,
1563 read_scanline,
1564 )
1565 .unwrap();
1566 assert_eq!(output[0..5], [13, 14, 18, 19, 0]);
1567
1568 output = [0u8; 26];
1569 load_rect(
1570 1,
1571 1,
1572 2,
1573 4,
1574 &mut output,
1575 2,
1576 &mut MockDecoder {
1577 scanline_number: 0,
1578 scanline_bytes,
1579 },
1580 scanline_bytes as usize,
1581 seek_scanline,
1582 read_scanline,
1583 )
1584 .unwrap();
1585 assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]);
1586 }
1587 }
1588
1589 #[test]
1590 fn test_load_rect_single_scanline() {
1591 const DATA: [u8; 25] = [
1592 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1593 24,
1594 ];
1595
1596 struct MockDecoder;
1597 impl ImageDecoder for MockDecoder {
1598 fn dimensions(&self) -> (u32, u32) {
1599 (5, 5)
1600 }
1601 fn color_type(&self) -> ColorType {
1602 ColorType::L8
1603 }
1604 fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
1605 unimplemented!()
1606 }
1607 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
1608 (*self).read_image(buf)
1609 }
1610 }
1611
1612 let mut seeks = 0;
1614 let seek_scanline = |_d: &mut MockDecoder, n: u64| -> io::Result<()> {
1615 seeks += 1;
1616 assert_eq!(n, 0);
1617 assert_eq!(seeks, 1);
1618 Ok(())
1619 };
1620
1621 fn read_scanline(_m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> {
1622 buf.copy_from_slice(&DATA);
1623 Ok(())
1624 }
1625
1626 let mut output = [0; 26];
1627 load_rect(
1628 1,
1629 1,
1630 2,
1631 4,
1632 &mut output,
1633 2,
1634 &mut MockDecoder,
1635 DATA.len(),
1636 seek_scanline,
1637 read_scanline,
1638 )
1639 .unwrap();
1640 assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]);
1641 }
1642
1643 #[test]
1644 fn test_image_format_from_path() {
1645 fn from_path(s: &str) -> ImageResult<ImageFormat> {
1646 ImageFormat::from_path(Path::new(s))
1647 }
1648 assert_eq!(from_path("./a.jpg").unwrap(), ImageFormat::Jpeg);
1649 assert_eq!(from_path("./a.jpeg").unwrap(), ImageFormat::Jpeg);
1650 assert_eq!(from_path("./a.JPEG").unwrap(), ImageFormat::Jpeg);
1651 assert_eq!(from_path("./a.pNg").unwrap(), ImageFormat::Png);
1652 assert_eq!(from_path("./a.gif").unwrap(), ImageFormat::Gif);
1653 assert_eq!(from_path("./a.webp").unwrap(), ImageFormat::WebP);
1654 assert_eq!(from_path("./a.tiFF").unwrap(), ImageFormat::Tiff);
1655 assert_eq!(from_path("./a.tif").unwrap(), ImageFormat::Tiff);
1656 assert_eq!(from_path("./a.tga").unwrap(), ImageFormat::Tga);
1657 assert_eq!(from_path("./a.dds").unwrap(), ImageFormat::Dds);
1658 assert_eq!(from_path("./a.bmp").unwrap(), ImageFormat::Bmp);
1659 assert_eq!(from_path("./a.Ico").unwrap(), ImageFormat::Ico);
1660 assert_eq!(from_path("./a.hdr").unwrap(), ImageFormat::Hdr);
1661 assert_eq!(from_path("./a.exr").unwrap(), ImageFormat::OpenExr);
1662 assert_eq!(from_path("./a.pbm").unwrap(), ImageFormat::Pnm);
1663 assert_eq!(from_path("./a.pAM").unwrap(), ImageFormat::Pnm);
1664 assert_eq!(from_path("./a.Ppm").unwrap(), ImageFormat::Pnm);
1665 assert_eq!(from_path("./a.pgm").unwrap(), ImageFormat::Pnm);
1666 assert_eq!(from_path("./a.AViF").unwrap(), ImageFormat::Avif);
1667 assert_eq!(from_path("./a.PCX").unwrap(), ImageFormat::Pcx);
1668 assert!(from_path("./a.txt").is_err());
1669 assert!(from_path("./a").is_err());
1670 }
1671
1672 #[test]
1673 fn test_generic_image_copy_within_oob() {
1674 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap();
1675 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1676 Rect {
1677 x: 0,
1678 y: 0,
1679 width: 5,
1680 height: 4
1681 },
1682 0,
1683 0
1684 ));
1685 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1686 Rect {
1687 x: 0,
1688 y: 0,
1689 width: 4,
1690 height: 5
1691 },
1692 0,
1693 0
1694 ));
1695 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1696 Rect {
1697 x: 1,
1698 y: 0,
1699 width: 4,
1700 height: 4
1701 },
1702 0,
1703 0
1704 ));
1705 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1706 Rect {
1707 x: 0,
1708 y: 0,
1709 width: 4,
1710 height: 4
1711 },
1712 1,
1713 0
1714 ));
1715 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1716 Rect {
1717 x: 0,
1718 y: 1,
1719 width: 4,
1720 height: 4
1721 },
1722 0,
1723 0
1724 ));
1725 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1726 Rect {
1727 x: 0,
1728 y: 0,
1729 width: 4,
1730 height: 4
1731 },
1732 0,
1733 1
1734 ));
1735 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1736 Rect {
1737 x: 1,
1738 y: 1,
1739 width: 4,
1740 height: 4
1741 },
1742 0,
1743 0
1744 ));
1745 }
1746
1747 #[test]
1748 fn test_generic_image_copy_within_tl() {
1749 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1750 let expected = [0, 1, 2, 3, 4, 0, 1, 2, 8, 4, 5, 6, 12, 8, 9, 10];
1751 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1752 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1753 Rect {
1754 x: 0,
1755 y: 0,
1756 width: 3,
1757 height: 3
1758 },
1759 1,
1760 1
1761 ));
1762 assert_eq!(&image.into_raw(), &expected);
1763 }
1764
1765 #[test]
1766 fn test_generic_image_copy_within_tr() {
1767 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1768 let expected = [0, 1, 2, 3, 1, 2, 3, 7, 5, 6, 7, 11, 9, 10, 11, 15];
1769 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1770 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1771 Rect {
1772 x: 1,
1773 y: 0,
1774 width: 3,
1775 height: 3
1776 },
1777 0,
1778 1
1779 ));
1780 assert_eq!(&image.into_raw(), &expected);
1781 }
1782
1783 #[test]
1784 fn test_generic_image_copy_within_bl() {
1785 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1786 let expected = [0, 4, 5, 6, 4, 8, 9, 10, 8, 12, 13, 14, 12, 13, 14, 15];
1787 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1788 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1789 Rect {
1790 x: 0,
1791 y: 1,
1792 width: 3,
1793 height: 3
1794 },
1795 1,
1796 0
1797 ));
1798 assert_eq!(&image.into_raw(), &expected);
1799 }
1800
1801 #[test]
1802 fn test_generic_image_copy_within_br() {
1803 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1804 let expected = [5, 6, 7, 3, 9, 10, 11, 7, 13, 14, 15, 11, 12, 13, 14, 15];
1805 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1806 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1807 Rect {
1808 x: 1,
1809 y: 1,
1810 width: 3,
1811 height: 3
1812 },
1813 0,
1814 0
1815 ));
1816 assert_eq!(&image.into_raw(), &expected);
1817 }
1818
1819 #[test]
1820 fn image_formats_are_recognized() {
1821 use ImageFormat::*;
1822 const ALL_FORMATS: &[ImageFormat] = &[
1823 Avif, Png, Jpeg, Gif, WebP, Pnm, Tiff, Tga, Dds, Bmp, Ico, Hdr, Farbfeld, OpenExr, Pcx,
1824 ];
1825 for &format in ALL_FORMATS {
1826 let mut file = Path::new("file.nothing").to_owned();
1827 for ext in format.extensions_str() {
1828 assert!(file.set_extension(ext));
1829 match ImageFormat::from_path(&file) {
1830 Err(_) => panic!("Path {} not recognized as {:?}", file.display(), format),
1831 Ok(result) => assert_eq!(format, result),
1832 }
1833 }
1834 }
1835 }
1836
1837 #[test]
1838 fn total_bytes_overflow() {
1839 struct D;
1840 impl ImageDecoder for D {
1841 fn color_type(&self) -> ColorType {
1842 ColorType::Rgb8
1843 }
1844 fn dimensions(&self) -> (u32, u32) {
1845 (0xffff_ffff, 0xffff_ffff)
1846 }
1847 fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
1848 unimplemented!()
1849 }
1850 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
1851 (*self).read_image(buf)
1852 }
1853 }
1854 assert_eq!(D.total_bytes(), u64::MAX);
1855
1856 let v: ImageResult<Vec<u8>> = super::decoder_to_vec(D);
1857 assert!(v.is_err());
1858 }
1859
1860 #[test]
1861 fn all() {
1862 let all_formats: HashSet<ImageFormat> = ImageFormat::all().collect();
1863 assert!(all_formats.contains(&ImageFormat::Avif));
1864 assert!(all_formats.contains(&ImageFormat::Gif));
1865 assert!(all_formats.contains(&ImageFormat::Bmp));
1866 assert!(all_formats.contains(&ImageFormat::Farbfeld));
1867 assert!(all_formats.contains(&ImageFormat::Jpeg));
1868 }
1869
1870 #[test]
1871 fn reading_enabled() {
1872 assert_eq!(cfg!(feature = "jpeg"), ImageFormat::Jpeg.reading_enabled());
1873 assert_eq!(
1874 cfg!(feature = "ff"),
1875 ImageFormat::Farbfeld.reading_enabled()
1876 );
1877 assert!(!ImageFormat::Dds.reading_enabled());
1878 }
1879
1880 #[test]
1881 fn writing_enabled() {
1882 assert_eq!(cfg!(feature = "jpeg"), ImageFormat::Jpeg.writing_enabled());
1883 assert_eq!(
1884 cfg!(feature = "ff"),
1885 ImageFormat::Farbfeld.writing_enabled()
1886 );
1887 assert!(!ImageFormat::Dds.writing_enabled());
1888 }
1889}