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" => 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 fn deref(&self) -> &Self::Target {
1273 &self.inner
1274 }
1275}
1276
1277impl<I> DerefMut for SubImage<I>
1278where
1279 I: DerefMut,
1280{
1281 fn deref_mut(&mut self) -> &mut Self::Target {
1282 &mut self.inner
1283 }
1284}
1285
1286#[allow(deprecated)]
1287impl<I> GenericImageView for SubImageInner<I>
1288where
1289 I: Deref,
1290 I::Target: GenericImageView,
1291{
1292 type Pixel = DerefPixel<I>;
1293
1294 fn dimensions(&self) -> (u32, u32) {
1295 (self.xstride, self.ystride)
1296 }
1297
1298 fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
1299 self.image.get_pixel(x + self.xoffset, y + self.yoffset)
1300 }
1301}
1302
1303#[allow(deprecated)]
1304impl<I> GenericImage for SubImageInner<I>
1305where
1306 I: DerefMut,
1307 I::Target: GenericImage + Sized,
1308{
1309 fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel {
1310 self.image.get_pixel_mut(x + self.xoffset, y + self.yoffset)
1311 }
1312
1313 fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1314 self.image
1315 .put_pixel(x + self.xoffset, y + self.yoffset, pixel);
1316 }
1317
1318 fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1320 self.image
1321 .blend_pixel(x + self.xoffset, y + self.yoffset, pixel);
1322 }
1323}
1324
1325#[cfg(test)]
1326mod tests {
1327 use std::collections::HashSet;
1328 use std::io;
1329 use std::path::Path;
1330
1331 use super::{
1332 load_rect, ColorType, GenericImage, GenericImageView, ImageDecoder, ImageFormat,
1333 ImageResult,
1334 };
1335 use crate::color::Rgba;
1336 use crate::math::Rect;
1337 use crate::{GrayImage, ImageBuffer};
1338
1339 #[test]
1340 #[allow(deprecated)]
1341 fn test_image_alpha_blending() {
1343 let mut target = ImageBuffer::new(1, 1);
1344 target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
1345 assert!(*target.get_pixel(0, 0) == Rgba([255, 0, 0, 255]));
1346 target.blend_pixel(0, 0, Rgba([0, 255, 0, 255]));
1347 assert!(*target.get_pixel(0, 0) == Rgba([0, 255, 0, 255]));
1348
1349 target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
1351 assert!(*target.get_pixel(0, 0) == Rgba([127, 127, 0, 255]));
1352
1353 target.put_pixel(0, 0, Rgba([0, 255, 0, 127]));
1355 target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
1356 assert!(*target.get_pixel(0, 0) == Rgba([169, 85, 0, 190]));
1357 }
1358
1359 #[test]
1360 fn test_in_bounds() {
1361 let mut target = ImageBuffer::new(2, 2);
1362 target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
1363
1364 assert!(target.in_bounds(0, 0));
1365 assert!(target.in_bounds(1, 0));
1366 assert!(target.in_bounds(0, 1));
1367 assert!(target.in_bounds(1, 1));
1368
1369 assert!(!target.in_bounds(2, 0));
1370 assert!(!target.in_bounds(0, 2));
1371 assert!(!target.in_bounds(2, 2));
1372 }
1373
1374 #[test]
1375 fn test_can_subimage_clone_nonmut() {
1376 let mut source = ImageBuffer::new(3, 3);
1377 source.put_pixel(1, 1, Rgba([255u8, 0, 0, 255]));
1378
1379 let source = source.clone();
1381
1382 let cloned = source.view(1, 1, 1, 1).to_image();
1384
1385 assert!(cloned.get_pixel(0, 0) == source.get_pixel(1, 1));
1386 }
1387
1388 #[test]
1389 fn test_can_nest_views() {
1390 let mut source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1391
1392 {
1393 let mut sub1 = source.sub_image(0, 0, 2, 2);
1394 let mut sub2 = sub1.sub_image(1, 1, 1, 1);
1395 sub2.put_pixel(0, 0, Rgba([0, 0, 0, 0]));
1396 }
1397
1398 assert_eq!(*source.get_pixel(1, 1), Rgba([0, 0, 0, 0]));
1399
1400 let view1 = source.view(0, 0, 2, 2);
1401 assert_eq!(*source.get_pixel(1, 1), view1.get_pixel(1, 1));
1402
1403 let view2 = view1.view(1, 1, 1, 1);
1404 assert_eq!(*source.get_pixel(1, 1), view2.get_pixel(0, 0));
1405 }
1406
1407 #[test]
1408 #[should_panic]
1409 fn test_view_out_of_bounds() {
1410 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1411 source.view(1, 1, 3, 3);
1412 }
1413
1414 #[test]
1415 #[should_panic]
1416 fn test_view_coordinates_out_of_bounds() {
1417 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1418 source.view(3, 3, 3, 3);
1419 }
1420
1421 #[test]
1422 #[should_panic]
1423 fn test_view_width_out_of_bounds() {
1424 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1425 source.view(1, 1, 3, 2);
1426 }
1427
1428 #[test]
1429 #[should_panic]
1430 fn test_view_height_out_of_bounds() {
1431 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1432 source.view(1, 1, 2, 3);
1433 }
1434
1435 #[test]
1436 #[should_panic]
1437 fn test_view_x_out_of_bounds() {
1438 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1439 source.view(3, 1, 3, 3);
1440 }
1441
1442 #[test]
1443 #[should_panic]
1444 fn test_view_y_out_of_bounds() {
1445 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1446 source.view(1, 3, 3, 3);
1447 }
1448
1449 #[test]
1450 fn test_view_in_bounds() {
1451 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1452 source.view(0, 0, 3, 3);
1453 source.view(1, 1, 2, 2);
1454 source.view(2, 2, 0, 0);
1455 }
1456
1457 #[test]
1458 fn test_copy_sub_image() {
1459 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1460 let view = source.view(0, 0, 3, 3);
1461 let _view2 = view;
1462 view.to_image();
1463 }
1464
1465 #[test]
1466 fn test_load_rect() {
1467 struct MockDecoder {
1468 scanline_number: u64,
1469 scanline_bytes: u64,
1470 }
1471 impl ImageDecoder for MockDecoder {
1472 fn dimensions(&self) -> (u32, u32) {
1473 (5, 5)
1474 }
1475 fn color_type(&self) -> ColorType {
1476 ColorType::L8
1477 }
1478 fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
1479 unimplemented!()
1480 }
1481 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
1482 (*self).read_image(buf)
1483 }
1484 }
1485
1486 const DATA: [u8; 25] = [
1487 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1488 24,
1489 ];
1490
1491 fn seek_scanline(m: &mut MockDecoder, n: u64) -> io::Result<()> {
1492 m.scanline_number = n;
1493 Ok(())
1494 }
1495 fn read_scanline(m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> {
1496 let bytes_read = m.scanline_number * m.scanline_bytes;
1497 if bytes_read >= 25 {
1498 return Ok(());
1499 }
1500
1501 let len = m.scanline_bytes.min(25 - bytes_read);
1502 buf[..(len as usize)].copy_from_slice(&DATA[(bytes_read as usize)..][..(len as usize)]);
1503 m.scanline_number += 1;
1504 Ok(())
1505 }
1506
1507 for scanline_bytes in 1..30 {
1508 let mut output = [0u8; 26];
1509
1510 load_rect(
1511 0,
1512 0,
1513 5,
1514 5,
1515 &mut output,
1516 5,
1517 &mut MockDecoder {
1518 scanline_number: 0,
1519 scanline_bytes,
1520 },
1521 scanline_bytes as usize,
1522 seek_scanline,
1523 read_scanline,
1524 )
1525 .unwrap();
1526 assert_eq!(output[0..25], DATA);
1527 assert_eq!(output[25], 0);
1528
1529 output = [0u8; 26];
1530 load_rect(
1531 3,
1532 2,
1533 1,
1534 1,
1535 &mut output,
1536 1,
1537 &mut MockDecoder {
1538 scanline_number: 0,
1539 scanline_bytes,
1540 },
1541 scanline_bytes as usize,
1542 seek_scanline,
1543 read_scanline,
1544 )
1545 .unwrap();
1546 assert_eq!(output[0..2], [13, 0]);
1547
1548 output = [0u8; 26];
1549 load_rect(
1550 3,
1551 2,
1552 2,
1553 2,
1554 &mut output,
1555 2,
1556 &mut MockDecoder {
1557 scanline_number: 0,
1558 scanline_bytes,
1559 },
1560 scanline_bytes as usize,
1561 seek_scanline,
1562 read_scanline,
1563 )
1564 .unwrap();
1565 assert_eq!(output[0..5], [13, 14, 18, 19, 0]);
1566
1567 output = [0u8; 26];
1568 load_rect(
1569 1,
1570 1,
1571 2,
1572 4,
1573 &mut output,
1574 2,
1575 &mut MockDecoder {
1576 scanline_number: 0,
1577 scanline_bytes,
1578 },
1579 scanline_bytes as usize,
1580 seek_scanline,
1581 read_scanline,
1582 )
1583 .unwrap();
1584 assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]);
1585 }
1586 }
1587
1588 #[test]
1589 fn test_load_rect_single_scanline() {
1590 const DATA: [u8; 25] = [
1591 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1592 24,
1593 ];
1594
1595 struct MockDecoder;
1596 impl ImageDecoder for MockDecoder {
1597 fn dimensions(&self) -> (u32, u32) {
1598 (5, 5)
1599 }
1600 fn color_type(&self) -> ColorType {
1601 ColorType::L8
1602 }
1603 fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
1604 unimplemented!()
1605 }
1606 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
1607 (*self).read_image(buf)
1608 }
1609 }
1610
1611 let mut seeks = 0;
1613 let seek_scanline = |_d: &mut MockDecoder, n: u64| -> io::Result<()> {
1614 seeks += 1;
1615 assert_eq!(n, 0);
1616 assert_eq!(seeks, 1);
1617 Ok(())
1618 };
1619
1620 fn read_scanline(_m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> {
1621 buf.copy_from_slice(&DATA);
1622 Ok(())
1623 }
1624
1625 let mut output = [0; 26];
1626 load_rect(
1627 1,
1628 1,
1629 2,
1630 4,
1631 &mut output,
1632 2,
1633 &mut MockDecoder,
1634 DATA.len(),
1635 seek_scanline,
1636 read_scanline,
1637 )
1638 .unwrap();
1639 assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]);
1640 }
1641
1642 #[test]
1643 fn test_image_format_from_path() {
1644 fn from_path(s: &str) -> ImageResult<ImageFormat> {
1645 ImageFormat::from_path(Path::new(s))
1646 }
1647 assert_eq!(from_path("./a.jpg").unwrap(), ImageFormat::Jpeg);
1648 assert_eq!(from_path("./a.jpeg").unwrap(), ImageFormat::Jpeg);
1649 assert_eq!(from_path("./a.JPEG").unwrap(), ImageFormat::Jpeg);
1650 assert_eq!(from_path("./a.pNg").unwrap(), ImageFormat::Png);
1651 assert_eq!(from_path("./a.gif").unwrap(), ImageFormat::Gif);
1652 assert_eq!(from_path("./a.webp").unwrap(), ImageFormat::WebP);
1653 assert_eq!(from_path("./a.tiFF").unwrap(), ImageFormat::Tiff);
1654 assert_eq!(from_path("./a.tif").unwrap(), ImageFormat::Tiff);
1655 assert_eq!(from_path("./a.tga").unwrap(), ImageFormat::Tga);
1656 assert_eq!(from_path("./a.dds").unwrap(), ImageFormat::Dds);
1657 assert_eq!(from_path("./a.bmp").unwrap(), ImageFormat::Bmp);
1658 assert_eq!(from_path("./a.Ico").unwrap(), ImageFormat::Ico);
1659 assert_eq!(from_path("./a.hdr").unwrap(), ImageFormat::Hdr);
1660 assert_eq!(from_path("./a.exr").unwrap(), ImageFormat::OpenExr);
1661 assert_eq!(from_path("./a.pbm").unwrap(), ImageFormat::Pnm);
1662 assert_eq!(from_path("./a.pAM").unwrap(), ImageFormat::Pnm);
1663 assert_eq!(from_path("./a.Ppm").unwrap(), ImageFormat::Pnm);
1664 assert_eq!(from_path("./a.pgm").unwrap(), ImageFormat::Pnm);
1665 assert_eq!(from_path("./a.AViF").unwrap(), ImageFormat::Avif);
1666 assert_eq!(from_path("./a.PCX").unwrap(), ImageFormat::Pcx);
1667 assert!(from_path("./a.txt").is_err());
1668 assert!(from_path("./a").is_err());
1669 }
1670
1671 #[test]
1672 fn test_generic_image_copy_within_oob() {
1673 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap();
1674 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1675 Rect {
1676 x: 0,
1677 y: 0,
1678 width: 5,
1679 height: 4
1680 },
1681 0,
1682 0
1683 ));
1684 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1685 Rect {
1686 x: 0,
1687 y: 0,
1688 width: 4,
1689 height: 5
1690 },
1691 0,
1692 0
1693 ));
1694 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1695 Rect {
1696 x: 1,
1697 y: 0,
1698 width: 4,
1699 height: 4
1700 },
1701 0,
1702 0
1703 ));
1704 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1705 Rect {
1706 x: 0,
1707 y: 0,
1708 width: 4,
1709 height: 4
1710 },
1711 1,
1712 0
1713 ));
1714 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1715 Rect {
1716 x: 0,
1717 y: 1,
1718 width: 4,
1719 height: 4
1720 },
1721 0,
1722 0
1723 ));
1724 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1725 Rect {
1726 x: 0,
1727 y: 0,
1728 width: 4,
1729 height: 4
1730 },
1731 0,
1732 1
1733 ));
1734 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1735 Rect {
1736 x: 1,
1737 y: 1,
1738 width: 4,
1739 height: 4
1740 },
1741 0,
1742 0
1743 ));
1744 }
1745
1746 #[test]
1747 fn test_generic_image_copy_within_tl() {
1748 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1749 let expected = [0, 1, 2, 3, 4, 0, 1, 2, 8, 4, 5, 6, 12, 8, 9, 10];
1750 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1751 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1752 Rect {
1753 x: 0,
1754 y: 0,
1755 width: 3,
1756 height: 3
1757 },
1758 1,
1759 1
1760 ));
1761 assert_eq!(&image.into_raw(), &expected);
1762 }
1763
1764 #[test]
1765 fn test_generic_image_copy_within_tr() {
1766 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1767 let expected = [0, 1, 2, 3, 1, 2, 3, 7, 5, 6, 7, 11, 9, 10, 11, 15];
1768 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1769 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1770 Rect {
1771 x: 1,
1772 y: 0,
1773 width: 3,
1774 height: 3
1775 },
1776 0,
1777 1
1778 ));
1779 assert_eq!(&image.into_raw(), &expected);
1780 }
1781
1782 #[test]
1783 fn test_generic_image_copy_within_bl() {
1784 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1785 let expected = [0, 4, 5, 6, 4, 8, 9, 10, 8, 12, 13, 14, 12, 13, 14, 15];
1786 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1787 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1788 Rect {
1789 x: 0,
1790 y: 1,
1791 width: 3,
1792 height: 3
1793 },
1794 1,
1795 0
1796 ));
1797 assert_eq!(&image.into_raw(), &expected);
1798 }
1799
1800 #[test]
1801 fn test_generic_image_copy_within_br() {
1802 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1803 let expected = [5, 6, 7, 3, 9, 10, 11, 7, 13, 14, 15, 11, 12, 13, 14, 15];
1804 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1805 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1806 Rect {
1807 x: 1,
1808 y: 1,
1809 width: 3,
1810 height: 3
1811 },
1812 0,
1813 0
1814 ));
1815 assert_eq!(&image.into_raw(), &expected);
1816 }
1817
1818 #[test]
1819 fn image_formats_are_recognized() {
1820 use ImageFormat::*;
1821 const ALL_FORMATS: &[ImageFormat] = &[
1822 Avif, Png, Jpeg, Gif, WebP, Pnm, Tiff, Tga, Dds, Bmp, Ico, Hdr, Farbfeld, OpenExr, Pcx,
1823 ];
1824 for &format in ALL_FORMATS {
1825 let mut file = Path::new("file.nothing").to_owned();
1826 for ext in format.extensions_str() {
1827 assert!(file.set_extension(ext));
1828 match ImageFormat::from_path(&file) {
1829 Err(_) => panic!("Path {} not recognized as {:?}", file.display(), format),
1830 Ok(result) => assert_eq!(format, result),
1831 }
1832 }
1833 }
1834 }
1835
1836 #[test]
1837 fn total_bytes_overflow() {
1838 struct D;
1839 impl ImageDecoder for D {
1840 fn color_type(&self) -> ColorType {
1841 ColorType::Rgb8
1842 }
1843 fn dimensions(&self) -> (u32, u32) {
1844 (0xffff_ffff, 0xffff_ffff)
1845 }
1846 fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
1847 unimplemented!()
1848 }
1849 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
1850 (*self).read_image(buf)
1851 }
1852 }
1853 assert_eq!(D.total_bytes(), u64::MAX);
1854
1855 let v: ImageResult<Vec<u8>> = super::decoder_to_vec(D);
1856 assert!(v.is_err());
1857 }
1858
1859 #[test]
1860 fn all() {
1861 let all_formats: HashSet<ImageFormat> = ImageFormat::all().collect();
1862 assert!(all_formats.contains(&ImageFormat::Avif));
1863 assert!(all_formats.contains(&ImageFormat::Gif));
1864 assert!(all_formats.contains(&ImageFormat::Bmp));
1865 assert!(all_formats.contains(&ImageFormat::Farbfeld));
1866 assert!(all_formats.contains(&ImageFormat::Jpeg));
1867 }
1868
1869 #[test]
1870 fn reading_enabled() {
1871 assert_eq!(cfg!(feature = "jpeg"), ImageFormat::Jpeg.reading_enabled());
1872 assert_eq!(
1873 cfg!(feature = "ff"),
1874 ImageFormat::Farbfeld.reading_enabled()
1875 );
1876 assert!(!ImageFormat::Dds.reading_enabled());
1877 }
1878
1879 #[test]
1880 fn writing_enabled() {
1881 assert_eq!(cfg!(feature = "jpeg"), ImageFormat::Jpeg.writing_enabled());
1882 assert_eq!(
1883 cfg!(feature = "ff"),
1884 ImageFormat::Farbfeld.writing_enabled()
1885 );
1886 assert!(!ImageFormat::Dds.writing_enabled());
1887 }
1888}