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
8use crate::color::{ColorType, ExtendedColorType};
9use crate::error::{
10 ImageError, ImageFormatHint, ImageResult, LimitError, LimitErrorKind, ParameterError,
11 ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
12};
13use crate::math::Rect;
14use crate::metadata::Orientation;
15use crate::traits::Pixel;
16use crate::ImageBuffer;
17
18use crate::animation::Frames;
19
20#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
23#[non_exhaustive]
24pub enum ImageFormat {
25 Png,
27
28 Jpeg,
30
31 Gif,
33
34 WebP,
36
37 Pnm,
39
40 Tiff,
42
43 Tga,
45
46 Dds,
48
49 Bmp,
51
52 Ico,
54
55 Hdr,
57
58 OpenExr,
60
61 Farbfeld,
63
64 Avif,
66
67 Qoi,
69}
70
71impl ImageFormat {
72 #[inline]
83 pub fn from_extension<S>(ext: S) -> Option<Self>
84 where
85 S: AsRef<OsStr>,
86 {
87 fn inner(ext: &OsStr) -> Option<ImageFormat> {
89 let ext = ext.to_str()?.to_ascii_lowercase();
90
91 Some(match ext.as_str() {
92 "avif" => ImageFormat::Avif,
93 "jpg" | "jpeg" => ImageFormat::Jpeg,
94 "png" | "apng" => ImageFormat::Png,
95 "gif" => ImageFormat::Gif,
96 "webp" => ImageFormat::WebP,
97 "tif" | "tiff" => ImageFormat::Tiff,
98 "tga" => ImageFormat::Tga,
99 "dds" => ImageFormat::Dds,
100 "bmp" => ImageFormat::Bmp,
101 "ico" => ImageFormat::Ico,
102 "hdr" => ImageFormat::Hdr,
103 "exr" => ImageFormat::OpenExr,
104 "pbm" | "pam" | "ppm" | "pgm" => ImageFormat::Pnm,
105 "ff" => ImageFormat::Farbfeld,
106 "qoi" => ImageFormat::Qoi,
107 _ => return None,
108 })
109 }
110
111 inner(ext.as_ref())
112 }
113
114 #[inline]
127 pub fn from_path<P>(path: P) -> ImageResult<Self>
128 where
129 P: AsRef<Path>,
130 {
131 fn inner(path: &Path) -> ImageResult<ImageFormat> {
133 let exact_ext = path.extension();
134 exact_ext
135 .and_then(ImageFormat::from_extension)
136 .ok_or_else(|| {
137 let format_hint = match exact_ext {
138 None => ImageFormatHint::Unknown,
139 Some(os) => ImageFormatHint::PathExtension(os.into()),
140 };
141 ImageError::Unsupported(format_hint.into())
142 })
143 }
144
145 inner(path.as_ref())
146 }
147
148 pub fn from_mime_type<M>(mime_type: M) -> Option<Self>
159 where
160 M: AsRef<str>,
161 {
162 match mime_type.as_ref() {
163 "image/avif" => Some(ImageFormat::Avif),
164 "image/jpeg" => Some(ImageFormat::Jpeg),
165 "image/png" => Some(ImageFormat::Png),
166 "image/gif" => Some(ImageFormat::Gif),
167 "image/webp" => Some(ImageFormat::WebP),
168 "image/tiff" => Some(ImageFormat::Tiff),
169 "image/x-targa" | "image/x-tga" => Some(ImageFormat::Tga),
170 "image/vnd-ms.dds" => Some(ImageFormat::Dds),
171 "image/bmp" => Some(ImageFormat::Bmp),
172 "image/x-icon" => Some(ImageFormat::Ico),
173 "image/vnd.radiance" => Some(ImageFormat::Hdr),
174 "image/x-exr" => Some(ImageFormat::OpenExr),
175 "image/x-portable-bitmap"
176 | "image/x-portable-graymap"
177 | "image/x-portable-pixmap"
178 | "image/x-portable-anymap" => Some(ImageFormat::Pnm),
179 "image/x-qoi" => Some(ImageFormat::Qoi),
182 _ => None,
183 }
184 }
185
186 #[must_use]
207 pub fn to_mime_type(&self) -> &'static str {
208 match self {
209 ImageFormat::Avif => "image/avif",
210 ImageFormat::Jpeg => "image/jpeg",
211 ImageFormat::Png => "image/png",
212 ImageFormat::Gif => "image/gif",
213 ImageFormat::WebP => "image/webp",
214 ImageFormat::Tiff => "image/tiff",
215 ImageFormat::Tga => "image/x-targa",
217 ImageFormat::Dds => "image/vnd-ms.dds",
218 ImageFormat::Bmp => "image/bmp",
219 ImageFormat::Ico => "image/x-icon",
220 ImageFormat::Hdr => "image/vnd.radiance",
221 ImageFormat::OpenExr => "image/x-exr",
222 ImageFormat::Pnm => "image/x-portable-anymap",
224 ImageFormat::Qoi => "image/x-qoi",
227 ImageFormat::Farbfeld => "application/octet-stream",
229 }
230 }
231
232 #[inline]
234 #[must_use]
235 pub fn can_read(&self) -> bool {
236 match self {
238 ImageFormat::Png => true,
239 ImageFormat::Gif => true,
240 ImageFormat::Jpeg => true,
241 ImageFormat::WebP => true,
242 ImageFormat::Tiff => true,
243 ImageFormat::Tga => true,
244 ImageFormat::Dds => false,
245 ImageFormat::Bmp => true,
246 ImageFormat::Ico => true,
247 ImageFormat::Hdr => true,
248 ImageFormat::OpenExr => true,
249 ImageFormat::Pnm => true,
250 ImageFormat::Farbfeld => true,
251 ImageFormat::Avif => true,
252 ImageFormat::Qoi => true,
253 }
254 }
255
256 #[inline]
258 #[must_use]
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 => true,
274 ImageFormat::OpenExr => true,
275 ImageFormat::Dds => false,
276 ImageFormat::Qoi => true,
277 }
278 }
279
280 #[must_use]
290 pub fn extensions_str(self) -> &'static [&'static str] {
291 match self {
292 ImageFormat::Png => &["png"],
293 ImageFormat::Jpeg => &["jpg", "jpeg"],
294 ImageFormat::Gif => &["gif"],
295 ImageFormat::WebP => &["webp"],
296 ImageFormat::Pnm => &["pbm", "pam", "ppm", "pgm"],
297 ImageFormat::Tiff => &["tiff", "tif"],
298 ImageFormat::Tga => &["tga"],
299 ImageFormat::Dds => &["dds"],
300 ImageFormat::Bmp => &["bmp"],
301 ImageFormat::Ico => &["ico"],
302 ImageFormat::Hdr => &["hdr"],
303 ImageFormat::OpenExr => &["exr"],
304 ImageFormat::Farbfeld => &["ff"],
305 ImageFormat::Avif => &["avif"],
307 ImageFormat::Qoi => &["qoi"],
308 }
309 }
310
311 #[inline]
313 #[must_use]
314 pub fn reading_enabled(&self) -> bool {
315 match self {
316 ImageFormat::Png => cfg!(feature = "png"),
317 ImageFormat::Gif => cfg!(feature = "gif"),
318 ImageFormat::Jpeg => cfg!(feature = "jpeg"),
319 ImageFormat::WebP => cfg!(feature = "webp"),
320 ImageFormat::Tiff => cfg!(feature = "tiff"),
321 ImageFormat::Tga => cfg!(feature = "tga"),
322 ImageFormat::Bmp => cfg!(feature = "bmp"),
323 ImageFormat::Ico => cfg!(feature = "ico"),
324 ImageFormat::Hdr => cfg!(feature = "hdr"),
325 ImageFormat::OpenExr => cfg!(feature = "exr"),
326 ImageFormat::Pnm => cfg!(feature = "pnm"),
327 ImageFormat::Farbfeld => cfg!(feature = "ff"),
328 ImageFormat::Avif => cfg!(feature = "avif"),
329 ImageFormat::Qoi => cfg!(feature = "qoi"),
330 ImageFormat::Dds => false,
331 }
332 }
333
334 #[inline]
336 #[must_use]
337 pub fn writing_enabled(&self) -> bool {
338 match self {
339 ImageFormat::Gif => cfg!(feature = "gif"),
340 ImageFormat::Ico => cfg!(feature = "ico"),
341 ImageFormat::Jpeg => cfg!(feature = "jpeg"),
342 ImageFormat::Png => cfg!(feature = "png"),
343 ImageFormat::Bmp => cfg!(feature = "bmp"),
344 ImageFormat::Tiff => cfg!(feature = "tiff"),
345 ImageFormat::Tga => cfg!(feature = "tga"),
346 ImageFormat::Pnm => cfg!(feature = "pnm"),
347 ImageFormat::Farbfeld => cfg!(feature = "ff"),
348 ImageFormat::Avif => cfg!(feature = "avif"),
349 ImageFormat::WebP => cfg!(feature = "webp"),
350 ImageFormat::OpenExr => cfg!(feature = "exr"),
351 ImageFormat::Qoi => cfg!(feature = "qoi"),
352 ImageFormat::Hdr => cfg!(feature = "hdr"),
353 ImageFormat::Dds => false,
354 }
355 }
356
357 pub fn all() -> impl Iterator<Item = ImageFormat> {
359 [
360 ImageFormat::Gif,
361 ImageFormat::Ico,
362 ImageFormat::Jpeg,
363 ImageFormat::Png,
364 ImageFormat::Bmp,
365 ImageFormat::Tiff,
366 ImageFormat::Tga,
367 ImageFormat::Pnm,
368 ImageFormat::Farbfeld,
369 ImageFormat::Avif,
370 ImageFormat::WebP,
371 ImageFormat::OpenExr,
372 ImageFormat::Qoi,
373 ImageFormat::Dds,
374 ImageFormat::Hdr,
375 ]
376 .iter()
377 .copied()
378 }
379}
380
381#[allow(dead_code)]
384pub(crate) struct ImageReadBuffer {
386 scanline_bytes: usize,
387 buffer: Vec<u8>,
388 consumed: usize,
389
390 total_bytes: u64,
391 offset: u64,
392}
393impl ImageReadBuffer {
394 #[allow(dead_code)]
400 pub(crate) fn new(scanline_bytes: u64, total_bytes: u64) -> Self {
402 Self {
403 scanline_bytes: usize::try_from(scanline_bytes).unwrap(),
404 buffer: Vec::new(),
405 consumed: 0,
406 total_bytes,
407 offset: 0,
408 }
409 }
410
411 #[allow(dead_code)]
412 pub(crate) fn read<F>(&mut self, buf: &mut [u8], mut read_scanline: F) -> io::Result<usize>
414 where
415 F: FnMut(&mut [u8]) -> io::Result<usize>,
416 {
417 if self.buffer.len() == self.consumed {
418 if self.offset == self.total_bytes {
419 return Ok(0);
420 } else if buf.len() >= self.scanline_bytes {
421 let bytes_read = read_scanline(&mut buf[..self.scanline_bytes])?;
424 self.offset += u64::try_from(bytes_read).unwrap();
425 return Ok(bytes_read);
426 } else {
427 if self.buffer.is_empty() {
430 self.buffer.resize(self.scanline_bytes, 0);
431 }
432
433 self.consumed = 0;
434 let bytes_read = read_scanline(&mut self.buffer[..])?;
435 self.buffer.resize(bytes_read, 0);
436 self.offset += u64::try_from(bytes_read).unwrap();
437
438 assert!(bytes_read == self.scanline_bytes || self.offset == self.total_bytes);
439 }
440 }
441
442 let bytes_buffered = self.buffer.len() - self.consumed;
444 if bytes_buffered > buf.len() {
445 buf.copy_from_slice(&self.buffer[self.consumed..][..buf.len()]);
446 self.consumed += buf.len();
447 Ok(buf.len())
448 } else {
449 buf[..bytes_buffered].copy_from_slice(&self.buffer[self.consumed..][..bytes_buffered]);
450 self.consumed = self.buffer.len();
451 Ok(bytes_buffered)
452 }
453 }
454}
455
456#[allow(dead_code)]
459pub(crate) fn load_rect<D, F1, F2, E>(
461 x: u32,
462 y: u32,
463 width: u32,
464 height: u32,
465 buf: &mut [u8],
466 row_pitch: usize,
467 decoder: &mut D,
468 scanline_bytes: usize,
469 mut seek_scanline: F1,
470 mut read_scanline: F2,
471) -> ImageResult<()>
472where
473 D: ImageDecoder,
474 F1: FnMut(&mut D, u64) -> io::Result<()>,
475 F2: FnMut(&mut D, &mut [u8]) -> Result<(), E>,
476 ImageError: From<E>,
477{
478 let scanline_bytes = u64::try_from(scanline_bytes).unwrap();
479 let row_pitch = u64::try_from(row_pitch).unwrap();
480
481 let (x, y, width, height) = (
482 u64::from(x),
483 u64::from(y),
484 u64::from(width),
485 u64::from(height),
486 );
487 let dimensions = decoder.dimensions();
488 let bytes_per_pixel = u64::from(decoder.color_type().bytes_per_pixel());
489 let row_bytes = bytes_per_pixel * u64::from(dimensions.0);
490 let total_bytes = width * height * bytes_per_pixel;
491
492 assert!(
493 buf.len() >= usize::try_from(total_bytes).unwrap_or(usize::MAX),
494 "output buffer too short\n expected `{}`, provided `{}`",
495 total_bytes,
496 buf.len()
497 );
498
499 let mut current_scanline = 0;
500 let mut tmp = Vec::new();
501 let mut tmp_scanline = None;
502
503 {
504 let mut read_image_range =
507 |mut start: u64, end: u64, mut output: &mut [u8]| -> ImageResult<()> {
508 let target_scanline = start / scanline_bytes;
511 if tmp_scanline == Some(target_scanline) {
512 let position = target_scanline * scanline_bytes;
513 let offset = start.saturating_sub(position);
514 let len = (end - start)
515 .min(scanline_bytes - offset)
516 .min(end - position);
517
518 output
519 .write_all(&tmp[offset as usize..][..len as usize])
520 .unwrap();
521 start += len;
522
523 if start == end {
524 return Ok(());
525 }
526 }
527
528 let target_scanline = start / scanline_bytes;
529 if target_scanline != current_scanline {
530 seek_scanline(decoder, target_scanline)?;
531 current_scanline = target_scanline;
532 }
533
534 let mut position = current_scanline * scanline_bytes;
535 while position < end {
536 if position >= start && end - position >= scanline_bytes {
537 read_scanline(decoder, &mut output[..(scanline_bytes as usize)])?;
538 output = &mut output[scanline_bytes as usize..];
539 } else {
540 tmp.resize(scanline_bytes as usize, 0u8);
541 read_scanline(decoder, &mut tmp)?;
542 tmp_scanline = Some(current_scanline);
543
544 let offset = start.saturating_sub(position);
545 let len = (end - start)
546 .min(scanline_bytes - offset)
547 .min(end - position);
548
549 output
550 .write_all(&tmp[offset as usize..][..len as usize])
551 .unwrap();
552 }
553
554 current_scanline += 1;
555 position += scanline_bytes;
556 }
557 Ok(())
558 };
559
560 if x + width > u64::from(dimensions.0)
561 || y + height > u64::from(dimensions.1)
562 || width == 0
563 || height == 0
564 {
565 return Err(ImageError::Parameter(ParameterError::from_kind(
566 ParameterErrorKind::DimensionMismatch,
567 )));
568 }
569 if scanline_bytes > usize::MAX as u64 {
570 return Err(ImageError::Limits(LimitError::from_kind(
571 LimitErrorKind::InsufficientMemory,
572 )));
573 }
574
575 if x == 0 && width == u64::from(dimensions.0) && row_pitch == row_bytes {
576 let start = x * bytes_per_pixel + y * row_bytes;
577 let end = (x + width) * bytes_per_pixel + (y + height - 1) * row_bytes;
578 read_image_range(start, end, buf)?;
579 } else {
580 for (output_slice, row) in buf.chunks_mut(row_pitch as usize).zip(y..(y + height)) {
581 let start = x * bytes_per_pixel + row * row_bytes;
582 let end = (x + width) * bytes_per_pixel + row * row_bytes;
583 read_image_range(start, end, output_slice)?;
584 }
585 }
586 }
587
588 Ok(seek_scanline(decoder, 0)?)
590}
591
592pub(crate) fn decoder_to_vec<T>(decoder: impl ImageDecoder) -> ImageResult<Vec<T>>
597where
598 T: crate::traits::Primitive + bytemuck::Pod,
599{
600 let total_bytes = usize::try_from(decoder.total_bytes());
601 if total_bytes.is_err() || total_bytes.unwrap() > isize::MAX as usize {
602 return Err(ImageError::Limits(LimitError::from_kind(
603 LimitErrorKind::InsufficientMemory,
604 )));
605 }
606
607 let mut buf = vec![num_traits::Zero::zero(); total_bytes.unwrap() / size_of::<T>()];
608 decoder.read_image(bytemuck::cast_slice_mut(buf.as_mut_slice()))?;
609 Ok(buf)
610}
611
612pub trait ImageDecoder {
614 fn dimensions(&self) -> (u32, u32);
616
617 fn color_type(&self) -> ColorType;
619
620 fn original_color_type(&self) -> ExtendedColorType {
622 self.color_type().into()
623 }
624
625 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
629 Ok(None)
630 }
631
632 fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
637 Ok(None)
638 }
639
640 fn orientation(&mut self) -> ImageResult<Orientation> {
645 Ok(self
646 .exif_metadata()?
647 .and_then(|chunk| Orientation::from_exif_chunk(&chunk))
648 .unwrap_or(Orientation::NoTransforms))
649 }
650
651 fn total_bytes(&self) -> u64 {
658 let dimensions = self.dimensions();
659 let total_pixels = u64::from(dimensions.0) * u64::from(dimensions.1);
660 let bytes_per_pixel = u64::from(self.color_type().bytes_per_pixel());
661 total_pixels.saturating_mul(bytes_per_pixel)
662 }
663
664 fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
686 where
687 Self: Sized;
688
689 fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> {
701 limits.check_support(&crate::LimitSupport::default())?;
702 let (width, height) = self.dimensions();
703 limits.check_dimensions(width, height)?;
704 Ok(())
705 }
706
707 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()>;
718}
719
720impl<T: ?Sized + ImageDecoder> ImageDecoder for Box<T> {
721 fn dimensions(&self) -> (u32, u32) {
722 (**self).dimensions()
723 }
724 fn color_type(&self) -> ColorType {
725 (**self).color_type()
726 }
727 fn original_color_type(&self) -> ExtendedColorType {
728 (**self).original_color_type()
729 }
730 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
731 (**self).icc_profile()
732 }
733 fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
734 (**self).exif_metadata()
735 }
736 fn total_bytes(&self) -> u64 {
737 (**self).total_bytes()
738 }
739 fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
740 where
741 Self: Sized,
742 {
743 T::read_image_boxed(self, buf)
744 }
745 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
746 T::read_image_boxed(*self, buf)
747 }
748 fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> {
749 (**self).set_limits(limits)
750 }
751}
752
753pub trait ImageDecoderRect: ImageDecoder {
755 fn read_rect(
763 &mut self,
764 x: u32,
765 y: u32,
766 width: u32,
767 height: u32,
768 buf: &mut [u8],
769 row_pitch: usize,
770 ) -> ImageResult<()>;
771}
772
773pub trait AnimationDecoder<'a> {
775 fn into_frames(self) -> Frames<'a>;
777}
778
779pub trait ImageEncoder {
781 fn write_image(
796 self,
797 buf: &[u8],
798 width: u32,
799 height: u32,
800 color_type: ExtendedColorType,
801 ) -> ImageResult<()>;
802
803 fn set_icc_profile(&mut self, icc_profile: Vec<u8>) -> Result<(), UnsupportedError> {
813 let _ = icc_profile;
814 Err(UnsupportedError::from_format_and_kind(
815 ImageFormatHint::Unknown,
816 UnsupportedErrorKind::GenericFeature(
817 "ICC profiles are not supported for this format".into(),
818 ),
819 ))
820 }
821}
822
823#[derive(Debug)]
825pub struct Pixels<'a, I: ?Sized + 'a> {
826 image: &'a I,
827 x: u32,
828 y: u32,
829 width: u32,
830 height: u32,
831}
832
833impl<I: GenericImageView> Iterator for Pixels<'_, I> {
834 type Item = (u32, u32, I::Pixel);
835
836 fn next(&mut self) -> Option<(u32, u32, I::Pixel)> {
837 if self.x >= self.width {
838 self.x = 0;
839 self.y += 1;
840 }
841
842 if self.y >= self.height {
843 None
844 } else {
845 let pixel = self.image.get_pixel(self.x, self.y);
846 let p = (self.x, self.y, pixel);
847
848 self.x += 1;
849
850 Some(p)
851 }
852 }
853}
854
855impl<I: ?Sized> Clone for Pixels<'_, I> {
856 fn clone(&self) -> Self {
857 Pixels { ..*self }
858 }
859}
860
861pub trait GenericImageView {
870 type Pixel: Pixel;
872
873 fn dimensions(&self) -> (u32, u32);
875
876 fn width(&self) -> u32 {
878 let (w, _) = self.dimensions();
879 w
880 }
881
882 fn height(&self) -> u32 {
884 let (_, h) = self.dimensions();
885 h
886 }
887
888 fn in_bounds(&self, x: u32, y: u32) -> bool {
890 let (width, height) = self.dimensions();
891 x < width && y < height
892 }
893
894 fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel;
900
901 unsafe fn unsafe_get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
910 self.get_pixel(x, y)
911 }
912
913 fn pixels(&self) -> Pixels<Self>
917 where
918 Self: Sized,
919 {
920 let (width, height) = self.dimensions();
921
922 Pixels {
923 image: self,
924 x: 0,
925 y: 0,
926 width,
927 height,
928 }
929 }
930
931 fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&Self>
935 where
936 Self: Sized,
937 {
938 assert!(u64::from(x) + u64::from(width) <= u64::from(self.width()));
939 assert!(u64::from(y) + u64::from(height) <= u64::from(self.height()));
940 SubImage::new(self, x, y, width, height)
941 }
942}
943
944pub trait GenericImage: GenericImageView {
946 #[deprecated(since = "0.24.0", note = "Use `get_pixel` and `put_pixel` instead.")]
967 fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel;
968
969 fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
975
976 unsafe fn unsafe_put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
985 self.put_pixel(x, y, pixel);
986 }
987
988 #[deprecated(
990 since = "0.24.0",
991 note = "Use iterator `pixels_mut` to blend the pixels directly"
992 )]
993 fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
994
995 fn copy_from<O>(&mut self, other: &O, x: u32, y: u32) -> ImageResult<()>
1011 where
1012 O: GenericImageView<Pixel = Self::Pixel>,
1013 {
1014 if self.width() < other.width() + x || self.height() < other.height() + y {
1017 return Err(ImageError::Parameter(ParameterError::from_kind(
1018 ParameterErrorKind::DimensionMismatch,
1019 )));
1020 }
1021
1022 for k in 0..other.height() {
1023 for i in 0..other.width() {
1024 let p = other.get_pixel(i, k);
1025 self.put_pixel(i + x, k + y, p);
1026 }
1027 }
1028 Ok(())
1029 }
1030
1031 fn copy_within(&mut self, source: Rect, x: u32, y: u32) -> bool {
1039 let Rect {
1040 x: sx,
1041 y: sy,
1042 width,
1043 height,
1044 } = source;
1045 let dx = x;
1046 let dy = y;
1047 assert!(sx < self.width() && dx < self.width());
1048 assert!(sy < self.height() && dy < self.height());
1049 if self.width() - dx.max(sx) < width || self.height() - dy.max(sy) < height {
1050 return false;
1051 }
1052 macro_rules! copy_within_impl_ {
1055 ($xiter:expr, $yiter:expr) => {
1056 for y in $yiter {
1057 let sy = sy + y;
1058 let dy = dy + y;
1059 for x in $xiter {
1060 let sx = sx + x;
1061 let dx = dx + x;
1062 let pixel = self.get_pixel(sx, sy);
1063 self.put_pixel(dx, dy, pixel);
1064 }
1065 }
1066 };
1067 }
1068 match (sx < dx, sy < dy) {
1070 (true, true) => copy_within_impl_!((0..width).rev(), (0..height).rev()),
1071 (true, false) => copy_within_impl_!((0..width).rev(), 0..height),
1072 (false, true) => copy_within_impl_!(0..width, (0..height).rev()),
1073 (false, false) => copy_within_impl_!(0..width, 0..height),
1074 }
1075 true
1076 }
1077
1078 fn sub_image(&mut self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&mut Self>
1082 where
1083 Self: Sized,
1084 {
1085 assert!(u64::from(x) + u64::from(width) <= u64::from(self.width()));
1086 assert!(u64::from(y) + u64::from(height) <= u64::from(self.height()));
1087 SubImage::new(self, x, y, width, height)
1088 }
1089}
1090
1091#[derive(Copy, Clone)]
1113pub struct SubImage<I> {
1114 inner: SubImageInner<I>,
1115}
1116
1117#[derive(Copy, Clone)]
1122pub struct SubImageInner<I> {
1123 image: I,
1124 xoffset: u32,
1125 yoffset: u32,
1126 xstride: u32,
1127 ystride: u32,
1128}
1129
1130type DerefPixel<I> = <<I as Deref>::Target as GenericImageView>::Pixel;
1132
1133type DerefSubpixel<I> = <DerefPixel<I> as Pixel>::Subpixel;
1135
1136impl<I> SubImage<I> {
1137 pub fn new(image: I, x: u32, y: u32, width: u32, height: u32) -> SubImage<I> {
1140 SubImage {
1141 inner: SubImageInner {
1142 image,
1143 xoffset: x,
1144 yoffset: y,
1145 xstride: width,
1146 ystride: height,
1147 },
1148 }
1149 }
1150
1151 pub fn change_bounds(&mut self, x: u32, y: u32, width: u32, height: u32) {
1153 self.inner.xoffset = x;
1154 self.inner.yoffset = y;
1155 self.inner.xstride = width;
1156 self.inner.ystride = height;
1157 }
1158
1159 pub fn offsets(&self) -> (u32, u32) {
1161 (self.inner.xoffset, self.inner.yoffset)
1162 }
1163
1164 pub fn to_image(&self) -> ImageBuffer<DerefPixel<I>, Vec<DerefSubpixel<I>>>
1166 where
1167 I: Deref,
1168 I::Target: GenericImageView + 'static,
1169 {
1170 let mut out = ImageBuffer::new(self.inner.xstride, self.inner.ystride);
1171 let borrowed = &*self.inner.image;
1172
1173 for y in 0..self.inner.ystride {
1174 for x in 0..self.inner.xstride {
1175 let p = borrowed.get_pixel(x + self.inner.xoffset, y + self.inner.yoffset);
1176 out.put_pixel(x, y, p);
1177 }
1178 }
1179
1180 out
1181 }
1182}
1183
1184impl<I> SubImage<I>
1186where
1187 I: Deref,
1188 I::Target: GenericImageView,
1189{
1190 pub fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&I::Target> {
1209 use crate::GenericImageView as _;
1210 assert!(u64::from(x) + u64::from(width) <= u64::from(self.inner.width()));
1211 assert!(u64::from(y) + u64::from(height) <= u64::from(self.inner.height()));
1212 let x = self.inner.xoffset.saturating_add(x);
1213 let y = self.inner.yoffset.saturating_add(y);
1214 SubImage::new(&*self.inner.image, x, y, width, height)
1215 }
1216
1217 pub fn inner(&self) -> &I::Target {
1219 &self.inner.image
1220 }
1221}
1222
1223impl<I> SubImage<I>
1224where
1225 I: DerefMut,
1226 I::Target: GenericImage,
1227{
1228 pub fn sub_image(
1232 &mut self,
1233 x: u32,
1234 y: u32,
1235 width: u32,
1236 height: u32,
1237 ) -> SubImage<&mut I::Target> {
1238 assert!(u64::from(x) + u64::from(width) <= u64::from(self.inner.width()));
1239 assert!(u64::from(y) + u64::from(height) <= u64::from(self.inner.height()));
1240 let x = self.inner.xoffset.saturating_add(x);
1241 let y = self.inner.yoffset.saturating_add(y);
1242 SubImage::new(&mut *self.inner.image, x, y, width, height)
1243 }
1244
1245 pub fn inner_mut(&mut self) -> &mut I::Target {
1247 &mut self.inner.image
1248 }
1249}
1250
1251impl<I> Deref for SubImage<I>
1252where
1253 I: Deref,
1254{
1255 type Target = SubImageInner<I>;
1256 fn deref(&self) -> &Self::Target {
1257 &self.inner
1258 }
1259}
1260
1261impl<I> DerefMut for SubImage<I>
1262where
1263 I: DerefMut,
1264{
1265 fn deref_mut(&mut self) -> &mut Self::Target {
1266 &mut self.inner
1267 }
1268}
1269
1270#[allow(deprecated)]
1271impl<I> GenericImageView for SubImageInner<I>
1272where
1273 I: Deref,
1274 I::Target: GenericImageView,
1275{
1276 type Pixel = DerefPixel<I>;
1277
1278 fn dimensions(&self) -> (u32, u32) {
1279 (self.xstride, self.ystride)
1280 }
1281
1282 fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
1283 self.image.get_pixel(x + self.xoffset, y + self.yoffset)
1284 }
1285}
1286
1287#[allow(deprecated)]
1288impl<I> GenericImage for SubImageInner<I>
1289where
1290 I: DerefMut,
1291 I::Target: GenericImage + Sized,
1292{
1293 fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel {
1294 self.image.get_pixel_mut(x + self.xoffset, y + self.yoffset)
1295 }
1296
1297 fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1298 self.image
1299 .put_pixel(x + self.xoffset, y + self.yoffset, pixel);
1300 }
1301
1302 fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1304 self.image
1305 .blend_pixel(x + self.xoffset, y + self.yoffset, pixel);
1306 }
1307}
1308
1309#[cfg(test)]
1310mod tests {
1311 use std::collections::HashSet;
1312 use std::io;
1313 use std::path::Path;
1314
1315 use super::{
1316 load_rect, ColorType, GenericImage, GenericImageView, ImageDecoder, ImageFormat,
1317 ImageResult,
1318 };
1319 use crate::color::Rgba;
1320 use crate::math::Rect;
1321 use crate::{GrayImage, ImageBuffer};
1322
1323 #[test]
1324 #[allow(deprecated)]
1325 fn test_image_alpha_blending() {
1327 let mut target = ImageBuffer::new(1, 1);
1328 target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
1329 assert!(*target.get_pixel(0, 0) == Rgba([255, 0, 0, 255]));
1330 target.blend_pixel(0, 0, Rgba([0, 255, 0, 255]));
1331 assert!(*target.get_pixel(0, 0) == Rgba([0, 255, 0, 255]));
1332
1333 target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
1335 assert!(*target.get_pixel(0, 0) == Rgba([127, 127, 0, 255]));
1336
1337 target.put_pixel(0, 0, Rgba([0, 255, 0, 127]));
1339 target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
1340 assert!(*target.get_pixel(0, 0) == Rgba([169, 85, 0, 190]));
1341 }
1342
1343 #[test]
1344 fn test_in_bounds() {
1345 let mut target = ImageBuffer::new(2, 2);
1346 target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
1347
1348 assert!(target.in_bounds(0, 0));
1349 assert!(target.in_bounds(1, 0));
1350 assert!(target.in_bounds(0, 1));
1351 assert!(target.in_bounds(1, 1));
1352
1353 assert!(!target.in_bounds(2, 0));
1354 assert!(!target.in_bounds(0, 2));
1355 assert!(!target.in_bounds(2, 2));
1356 }
1357
1358 #[test]
1359 fn test_can_subimage_clone_nonmut() {
1360 let mut source = ImageBuffer::new(3, 3);
1361 source.put_pixel(1, 1, Rgba([255u8, 0, 0, 255]));
1362
1363 let source = source.clone();
1365
1366 let cloned = source.view(1, 1, 1, 1).to_image();
1368
1369 assert!(cloned.get_pixel(0, 0) == source.get_pixel(1, 1));
1370 }
1371
1372 #[test]
1373 fn test_can_nest_views() {
1374 let mut source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1375
1376 {
1377 let mut sub1 = source.sub_image(0, 0, 2, 2);
1378 let mut sub2 = sub1.sub_image(1, 1, 1, 1);
1379 sub2.put_pixel(0, 0, Rgba([0, 0, 0, 0]));
1380 }
1381
1382 assert_eq!(*source.get_pixel(1, 1), Rgba([0, 0, 0, 0]));
1383
1384 let view1 = source.view(0, 0, 2, 2);
1385 assert_eq!(*source.get_pixel(1, 1), view1.get_pixel(1, 1));
1386
1387 let view2 = view1.view(1, 1, 1, 1);
1388 assert_eq!(*source.get_pixel(1, 1), view2.get_pixel(0, 0));
1389 }
1390
1391 #[test]
1392 #[should_panic]
1393 fn test_view_out_of_bounds() {
1394 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1395 source.view(1, 1, 3, 3);
1396 }
1397
1398 #[test]
1399 #[should_panic]
1400 fn test_view_coordinates_out_of_bounds() {
1401 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1402 source.view(3, 3, 3, 3);
1403 }
1404
1405 #[test]
1406 #[should_panic]
1407 fn test_view_width_out_of_bounds() {
1408 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1409 source.view(1, 1, 3, 2);
1410 }
1411
1412 #[test]
1413 #[should_panic]
1414 fn test_view_height_out_of_bounds() {
1415 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1416 source.view(1, 1, 2, 3);
1417 }
1418
1419 #[test]
1420 #[should_panic]
1421 fn test_view_x_out_of_bounds() {
1422 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1423 source.view(3, 1, 3, 3);
1424 }
1425
1426 #[test]
1427 #[should_panic]
1428 fn test_view_y_out_of_bounds() {
1429 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1430 source.view(1, 3, 3, 3);
1431 }
1432
1433 #[test]
1434 fn test_view_in_bounds() {
1435 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1436 source.view(0, 0, 3, 3);
1437 source.view(1, 1, 2, 2);
1438 source.view(2, 2, 0, 0);
1439 }
1440
1441 #[test]
1442 fn test_copy_sub_image() {
1443 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1444 let view = source.view(0, 0, 3, 3);
1445 let _view2 = view;
1446 view.to_image();
1447 }
1448
1449 #[test]
1450 fn test_load_rect() {
1451 struct MockDecoder {
1452 scanline_number: u64,
1453 scanline_bytes: u64,
1454 }
1455 impl ImageDecoder for MockDecoder {
1456 fn dimensions(&self) -> (u32, u32) {
1457 (5, 5)
1458 }
1459 fn color_type(&self) -> ColorType {
1460 ColorType::L8
1461 }
1462 fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
1463 unimplemented!()
1464 }
1465 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
1466 (*self).read_image(buf)
1467 }
1468 }
1469
1470 const DATA: [u8; 25] = [
1471 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1472 24,
1473 ];
1474
1475 fn seek_scanline(m: &mut MockDecoder, n: u64) -> io::Result<()> {
1476 m.scanline_number = n;
1477 Ok(())
1478 }
1479 fn read_scanline(m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> {
1480 let bytes_read = m.scanline_number * m.scanline_bytes;
1481 if bytes_read >= 25 {
1482 return Ok(());
1483 }
1484
1485 let len = m.scanline_bytes.min(25 - bytes_read);
1486 buf[..(len as usize)].copy_from_slice(&DATA[(bytes_read as usize)..][..(len as usize)]);
1487 m.scanline_number += 1;
1488 Ok(())
1489 }
1490
1491 for scanline_bytes in 1..30 {
1492 let mut output = [0u8; 26];
1493
1494 load_rect(
1495 0,
1496 0,
1497 5,
1498 5,
1499 &mut output,
1500 5,
1501 &mut MockDecoder {
1502 scanline_number: 0,
1503 scanline_bytes,
1504 },
1505 scanline_bytes as usize,
1506 seek_scanline,
1507 read_scanline,
1508 )
1509 .unwrap();
1510 assert_eq!(output[0..25], DATA);
1511 assert_eq!(output[25], 0);
1512
1513 output = [0u8; 26];
1514 load_rect(
1515 3,
1516 2,
1517 1,
1518 1,
1519 &mut output,
1520 1,
1521 &mut MockDecoder {
1522 scanline_number: 0,
1523 scanline_bytes,
1524 },
1525 scanline_bytes as usize,
1526 seek_scanline,
1527 read_scanline,
1528 )
1529 .unwrap();
1530 assert_eq!(output[0..2], [13, 0]);
1531
1532 output = [0u8; 26];
1533 load_rect(
1534 3,
1535 2,
1536 2,
1537 2,
1538 &mut output,
1539 2,
1540 &mut MockDecoder {
1541 scanline_number: 0,
1542 scanline_bytes,
1543 },
1544 scanline_bytes as usize,
1545 seek_scanline,
1546 read_scanline,
1547 )
1548 .unwrap();
1549 assert_eq!(output[0..5], [13, 14, 18, 19, 0]);
1550
1551 output = [0u8; 26];
1552 load_rect(
1553 1,
1554 1,
1555 2,
1556 4,
1557 &mut output,
1558 2,
1559 &mut MockDecoder {
1560 scanline_number: 0,
1561 scanline_bytes,
1562 },
1563 scanline_bytes as usize,
1564 seek_scanline,
1565 read_scanline,
1566 )
1567 .unwrap();
1568 assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]);
1569 }
1570 }
1571
1572 #[test]
1573 fn test_load_rect_single_scanline() {
1574 const DATA: [u8; 25] = [
1575 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1576 24,
1577 ];
1578
1579 struct MockDecoder;
1580 impl ImageDecoder for MockDecoder {
1581 fn dimensions(&self) -> (u32, u32) {
1582 (5, 5)
1583 }
1584 fn color_type(&self) -> ColorType {
1585 ColorType::L8
1586 }
1587 fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
1588 unimplemented!()
1589 }
1590 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
1591 (*self).read_image(buf)
1592 }
1593 }
1594
1595 let mut seeks = 0;
1597 let seek_scanline = |_d: &mut MockDecoder, n: u64| -> io::Result<()> {
1598 seeks += 1;
1599 assert_eq!(n, 0);
1600 assert_eq!(seeks, 1);
1601 Ok(())
1602 };
1603
1604 fn read_scanline(_m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> {
1605 buf.copy_from_slice(&DATA);
1606 Ok(())
1607 }
1608
1609 let mut output = [0; 26];
1610 load_rect(
1611 1,
1612 1,
1613 2,
1614 4,
1615 &mut output,
1616 2,
1617 &mut MockDecoder,
1618 DATA.len(),
1619 seek_scanline,
1620 read_scanline,
1621 )
1622 .unwrap();
1623 assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]);
1624 }
1625
1626 #[test]
1627 fn test_image_format_from_path() {
1628 fn from_path(s: &str) -> ImageResult<ImageFormat> {
1629 ImageFormat::from_path(Path::new(s))
1630 }
1631 assert_eq!(from_path("./a.jpg").unwrap(), ImageFormat::Jpeg);
1632 assert_eq!(from_path("./a.jpeg").unwrap(), ImageFormat::Jpeg);
1633 assert_eq!(from_path("./a.JPEG").unwrap(), ImageFormat::Jpeg);
1634 assert_eq!(from_path("./a.pNg").unwrap(), ImageFormat::Png);
1635 assert_eq!(from_path("./a.gif").unwrap(), ImageFormat::Gif);
1636 assert_eq!(from_path("./a.webp").unwrap(), ImageFormat::WebP);
1637 assert_eq!(from_path("./a.tiFF").unwrap(), ImageFormat::Tiff);
1638 assert_eq!(from_path("./a.tif").unwrap(), ImageFormat::Tiff);
1639 assert_eq!(from_path("./a.tga").unwrap(), ImageFormat::Tga);
1640 assert_eq!(from_path("./a.dds").unwrap(), ImageFormat::Dds);
1641 assert_eq!(from_path("./a.bmp").unwrap(), ImageFormat::Bmp);
1642 assert_eq!(from_path("./a.Ico").unwrap(), ImageFormat::Ico);
1643 assert_eq!(from_path("./a.hdr").unwrap(), ImageFormat::Hdr);
1644 assert_eq!(from_path("./a.exr").unwrap(), ImageFormat::OpenExr);
1645 assert_eq!(from_path("./a.pbm").unwrap(), ImageFormat::Pnm);
1646 assert_eq!(from_path("./a.pAM").unwrap(), ImageFormat::Pnm);
1647 assert_eq!(from_path("./a.Ppm").unwrap(), ImageFormat::Pnm);
1648 assert_eq!(from_path("./a.pgm").unwrap(), ImageFormat::Pnm);
1649 assert_eq!(from_path("./a.AViF").unwrap(), ImageFormat::Avif);
1650 assert!(from_path("./a.txt").is_err());
1651 assert!(from_path("./a").is_err());
1652 }
1653
1654 #[test]
1655 fn test_generic_image_copy_within_oob() {
1656 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap();
1657 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1658 Rect {
1659 x: 0,
1660 y: 0,
1661 width: 5,
1662 height: 4
1663 },
1664 0,
1665 0
1666 ));
1667 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1668 Rect {
1669 x: 0,
1670 y: 0,
1671 width: 4,
1672 height: 5
1673 },
1674 0,
1675 0
1676 ));
1677 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1678 Rect {
1679 x: 1,
1680 y: 0,
1681 width: 4,
1682 height: 4
1683 },
1684 0,
1685 0
1686 ));
1687 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1688 Rect {
1689 x: 0,
1690 y: 0,
1691 width: 4,
1692 height: 4
1693 },
1694 1,
1695 0
1696 ));
1697 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1698 Rect {
1699 x: 0,
1700 y: 1,
1701 width: 4,
1702 height: 4
1703 },
1704 0,
1705 0
1706 ));
1707 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1708 Rect {
1709 x: 0,
1710 y: 0,
1711 width: 4,
1712 height: 4
1713 },
1714 0,
1715 1
1716 ));
1717 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1718 Rect {
1719 x: 1,
1720 y: 1,
1721 width: 4,
1722 height: 4
1723 },
1724 0,
1725 0
1726 ));
1727 }
1728
1729 #[test]
1730 fn test_generic_image_copy_within_tl() {
1731 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1732 let expected = [0, 1, 2, 3, 4, 0, 1, 2, 8, 4, 5, 6, 12, 8, 9, 10];
1733 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1734 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1735 Rect {
1736 x: 0,
1737 y: 0,
1738 width: 3,
1739 height: 3
1740 },
1741 1,
1742 1
1743 ));
1744 assert_eq!(&image.into_raw(), &expected);
1745 }
1746
1747 #[test]
1748 fn test_generic_image_copy_within_tr() {
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, 1, 2, 3, 7, 5, 6, 7, 11, 9, 10, 11, 15];
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: 1,
1755 y: 0,
1756 width: 3,
1757 height: 3
1758 },
1759 0,
1760 1
1761 ));
1762 assert_eq!(&image.into_raw(), &expected);
1763 }
1764
1765 #[test]
1766 fn test_generic_image_copy_within_bl() {
1767 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1768 let expected = [0, 4, 5, 6, 4, 8, 9, 10, 8, 12, 13, 14, 12, 13, 14, 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: 0,
1773 y: 1,
1774 width: 3,
1775 height: 3
1776 },
1777 1,
1778 0
1779 ));
1780 assert_eq!(&image.into_raw(), &expected);
1781 }
1782
1783 #[test]
1784 fn test_generic_image_copy_within_br() {
1785 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1786 let expected = [5, 6, 7, 3, 9, 10, 11, 7, 13, 14, 15, 11, 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: 1,
1791 y: 1,
1792 width: 3,
1793 height: 3
1794 },
1795 0,
1796 0
1797 ));
1798 assert_eq!(&image.into_raw(), &expected);
1799 }
1800
1801 #[test]
1802 fn image_formats_are_recognized() {
1803 use ImageFormat::*;
1804 const ALL_FORMATS: &[ImageFormat] = &[
1805 Avif, Png, Jpeg, Gif, WebP, Pnm, Tiff, Tga, Dds, Bmp, Ico, Hdr, Farbfeld, OpenExr,
1806 ];
1807 for &format in ALL_FORMATS {
1808 let mut file = Path::new("file.nothing").to_owned();
1809 for ext in format.extensions_str() {
1810 assert!(file.set_extension(ext));
1811 match ImageFormat::from_path(&file) {
1812 Err(_) => panic!("Path {} not recognized as {:?}", file.display(), format),
1813 Ok(result) => assert_eq!(format, result),
1814 }
1815 }
1816 }
1817 }
1818
1819 #[test]
1820 fn total_bytes_overflow() {
1821 struct D;
1822 impl ImageDecoder for D {
1823 fn color_type(&self) -> ColorType {
1824 ColorType::Rgb8
1825 }
1826 fn dimensions(&self) -> (u32, u32) {
1827 (0xffff_ffff, 0xffff_ffff)
1828 }
1829 fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
1830 unimplemented!()
1831 }
1832 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
1833 (*self).read_image(buf)
1834 }
1835 }
1836 assert_eq!(D.total_bytes(), u64::MAX);
1837
1838 let v: ImageResult<Vec<u8>> = super::decoder_to_vec(D);
1839 assert!(v.is_err());
1840 }
1841
1842 #[test]
1843 fn all() {
1844 let all_formats: HashSet<ImageFormat> = ImageFormat::all().collect();
1845 assert!(all_formats.contains(&ImageFormat::Avif));
1846 assert!(all_formats.contains(&ImageFormat::Gif));
1847 assert!(all_formats.contains(&ImageFormat::Bmp));
1848 assert!(all_formats.contains(&ImageFormat::Farbfeld));
1849 assert!(all_formats.contains(&ImageFormat::Jpeg));
1850 }
1851
1852 #[test]
1853 fn reading_enabled() {
1854 assert_eq!(cfg!(feature = "jpeg"), ImageFormat::Jpeg.reading_enabled());
1855 assert_eq!(
1856 cfg!(feature = "ff"),
1857 ImageFormat::Farbfeld.reading_enabled()
1858 );
1859 assert!(!ImageFormat::Dds.reading_enabled());
1860 }
1861
1862 #[test]
1863 fn writing_enabled() {
1864 assert_eq!(cfg!(feature = "jpeg"), ImageFormat::Jpeg.writing_enabled());
1865 assert_eq!(
1866 cfg!(feature = "ff"),
1867 ImageFormat::Farbfeld.writing_enabled()
1868 );
1869 assert!(!ImageFormat::Dds.writing_enabled());
1870 }
1871}