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,
12};
13use crate::math::Rect;
14use crate::traits::Pixel;
15use crate::ImageBuffer;
16
17use crate::animation::Frames;
18
19#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
22#[non_exhaustive]
23pub enum ImageFormat {
24 Png,
26
27 Jpeg,
29
30 Gif,
32
33 WebP,
35
36 Pnm,
38
39 Tiff,
41
42 Tga,
44
45 Dds,
47
48 Bmp,
50
51 Ico,
53
54 Hdr,
56
57 OpenExr,
59
60 Farbfeld,
62
63 Avif,
65
66 Qoi,
68}
69
70impl ImageFormat {
71 #[inline]
82 pub fn from_extension<S>(ext: S) -> Option<Self>
83 where
84 S: AsRef<OsStr>,
85 {
86 fn inner(ext: &OsStr) -> Option<ImageFormat> {
88 let ext = ext.to_str()?.to_ascii_lowercase();
89
90 Some(match ext.as_str() {
91 "avif" => ImageFormat::Avif,
92 "jpg" | "jpeg" => ImageFormat::Jpeg,
93 "png" | "apng" => ImageFormat::Png,
94 "gif" => ImageFormat::Gif,
95 "webp" => ImageFormat::WebP,
96 "tif" | "tiff" => ImageFormat::Tiff,
97 "tga" => ImageFormat::Tga,
98 "dds" => ImageFormat::Dds,
99 "bmp" => ImageFormat::Bmp,
100 "ico" => ImageFormat::Ico,
101 "hdr" => ImageFormat::Hdr,
102 "exr" => ImageFormat::OpenExr,
103 "pbm" | "pam" | "ppm" | "pgm" => ImageFormat::Pnm,
104 "ff" => ImageFormat::Farbfeld,
105 "qoi" => ImageFormat::Qoi,
106 _ => return None,
107 })
108 }
109
110 inner(ext.as_ref())
111 }
112
113 #[inline]
126 pub fn from_path<P>(path: P) -> ImageResult<Self>
127 where
128 P: AsRef<Path>,
129 {
130 fn inner(path: &Path) -> ImageResult<ImageFormat> {
132 let exact_ext = path.extension();
133 exact_ext
134 .and_then(ImageFormat::from_extension)
135 .ok_or_else(|| {
136 let format_hint = match exact_ext {
137 None => ImageFormatHint::Unknown,
138 Some(os) => ImageFormatHint::PathExtension(os.into()),
139 };
140 ImageError::Unsupported(format_hint.into())
141 })
142 }
143
144 inner(path.as_ref())
145 }
146
147 pub fn from_mime_type<M>(mime_type: M) -> Option<Self>
158 where
159 M: AsRef<str>,
160 {
161 match mime_type.as_ref() {
162 "image/avif" => Some(ImageFormat::Avif),
163 "image/jpeg" => Some(ImageFormat::Jpeg),
164 "image/png" => Some(ImageFormat::Png),
165 "image/gif" => Some(ImageFormat::Gif),
166 "image/webp" => Some(ImageFormat::WebP),
167 "image/tiff" => Some(ImageFormat::Tiff),
168 "image/x-targa" | "image/x-tga" => Some(ImageFormat::Tga),
169 "image/vnd-ms.dds" => Some(ImageFormat::Dds),
170 "image/bmp" => Some(ImageFormat::Bmp),
171 "image/x-icon" => Some(ImageFormat::Ico),
172 "image/vnd.radiance" => Some(ImageFormat::Hdr),
173 "image/x-exr" => Some(ImageFormat::OpenExr),
174 "image/x-portable-bitmap"
175 | "image/x-portable-graymap"
176 | "image/x-portable-pixmap"
177 | "image/x-portable-anymap" => Some(ImageFormat::Pnm),
178 "image/x-qoi" => Some(ImageFormat::Qoi),
181 _ => None,
182 }
183 }
184
185 #[must_use]
206 pub fn to_mime_type(&self) -> &'static str {
207 match self {
208 ImageFormat::Avif => "image/avif",
209 ImageFormat::Jpeg => "image/jpeg",
210 ImageFormat::Png => "image/png",
211 ImageFormat::Gif => "image/gif",
212 ImageFormat::WebP => "image/webp",
213 ImageFormat::Tiff => "image/tiff",
214 ImageFormat::Tga => "image/x-targa",
216 ImageFormat::Dds => "image/vnd-ms.dds",
217 ImageFormat::Bmp => "image/bmp",
218 ImageFormat::Ico => "image/x-icon",
219 ImageFormat::Hdr => "image/vnd.radiance",
220 ImageFormat::OpenExr => "image/x-exr",
221 ImageFormat::Pnm => "image/x-portable-anymap",
223 ImageFormat::Qoi => "image/x-qoi",
226 ImageFormat::Farbfeld => "application/octet-stream",
228 }
229 }
230
231 #[inline]
233 #[must_use]
234 pub fn can_read(&self) -> bool {
235 match self {
237 ImageFormat::Png => true,
238 ImageFormat::Gif => true,
239 ImageFormat::Jpeg => true,
240 ImageFormat::WebP => true,
241 ImageFormat::Tiff => true,
242 ImageFormat::Tga => true,
243 ImageFormat::Dds => false,
244 ImageFormat::Bmp => true,
245 ImageFormat::Ico => true,
246 ImageFormat::Hdr => true,
247 ImageFormat::OpenExr => true,
248 ImageFormat::Pnm => true,
249 ImageFormat::Farbfeld => true,
250 ImageFormat::Avif => true,
251 ImageFormat::Qoi => true,
252 }
253 }
254
255 #[inline]
257 #[must_use]
258 pub fn can_write(&self) -> bool {
259 match self {
261 ImageFormat::Gif => true,
262 ImageFormat::Ico => true,
263 ImageFormat::Jpeg => true,
264 ImageFormat::Png => true,
265 ImageFormat::Bmp => true,
266 ImageFormat::Tiff => true,
267 ImageFormat::Tga => true,
268 ImageFormat::Pnm => true,
269 ImageFormat::Farbfeld => true,
270 ImageFormat::Avif => true,
271 ImageFormat::WebP => true,
272 ImageFormat::Hdr => true,
273 ImageFormat::OpenExr => true,
274 ImageFormat::Dds => false,
275 ImageFormat::Qoi => true,
276 }
277 }
278
279 #[must_use]
289 pub fn extensions_str(self) -> &'static [&'static str] {
290 match self {
291 ImageFormat::Png => &["png"],
292 ImageFormat::Jpeg => &["jpg", "jpeg"],
293 ImageFormat::Gif => &["gif"],
294 ImageFormat::WebP => &["webp"],
295 ImageFormat::Pnm => &["pbm", "pam", "ppm", "pgm"],
296 ImageFormat::Tiff => &["tiff", "tif"],
297 ImageFormat::Tga => &["tga"],
298 ImageFormat::Dds => &["dds"],
299 ImageFormat::Bmp => &["bmp"],
300 ImageFormat::Ico => &["ico"],
301 ImageFormat::Hdr => &["hdr"],
302 ImageFormat::OpenExr => &["exr"],
303 ImageFormat::Farbfeld => &["ff"],
304 ImageFormat::Avif => &["avif"],
306 ImageFormat::Qoi => &["qoi"],
307 }
308 }
309
310 #[inline]
312 #[must_use]
313 pub fn reading_enabled(&self) -> bool {
314 match self {
315 ImageFormat::Png => cfg!(feature = "png"),
316 ImageFormat::Gif => cfg!(feature = "gif"),
317 ImageFormat::Jpeg => cfg!(feature = "jpeg"),
318 ImageFormat::WebP => cfg!(feature = "webp"),
319 ImageFormat::Tiff => cfg!(feature = "tiff"),
320 ImageFormat::Tga => cfg!(feature = "tga"),
321 ImageFormat::Bmp => cfg!(feature = "bmp"),
322 ImageFormat::Ico => cfg!(feature = "ico"),
323 ImageFormat::Hdr => cfg!(feature = "hdr"),
324 ImageFormat::OpenExr => cfg!(feature = "exr"),
325 ImageFormat::Pnm => cfg!(feature = "pnm"),
326 ImageFormat::Farbfeld => cfg!(feature = "ff"),
327 ImageFormat::Avif => cfg!(feature = "avif"),
328 ImageFormat::Qoi => cfg!(feature = "qoi"),
329 ImageFormat::Dds => false,
330 }
331 }
332
333 #[inline]
335 #[must_use]
336 pub fn writing_enabled(&self) -> bool {
337 match self {
338 ImageFormat::Gif => cfg!(feature = "gif"),
339 ImageFormat::Ico => cfg!(feature = "ico"),
340 ImageFormat::Jpeg => cfg!(feature = "jpeg"),
341 ImageFormat::Png => cfg!(feature = "png"),
342 ImageFormat::Bmp => cfg!(feature = "bmp"),
343 ImageFormat::Tiff => cfg!(feature = "tiff"),
344 ImageFormat::Tga => cfg!(feature = "tga"),
345 ImageFormat::Pnm => cfg!(feature = "pnm"),
346 ImageFormat::Farbfeld => cfg!(feature = "ff"),
347 ImageFormat::Avif => cfg!(feature = "avif"),
348 ImageFormat::WebP => cfg!(feature = "webp"),
349 ImageFormat::OpenExr => cfg!(feature = "exr"),
350 ImageFormat::Qoi => cfg!(feature = "qoi"),
351 ImageFormat::Hdr => cfg!(feature = "hdr"),
352 ImageFormat::Dds => false,
353 }
354 }
355
356 pub fn all() -> impl Iterator<Item = ImageFormat> {
358 [
359 ImageFormat::Gif,
360 ImageFormat::Ico,
361 ImageFormat::Jpeg,
362 ImageFormat::Png,
363 ImageFormat::Bmp,
364 ImageFormat::Tiff,
365 ImageFormat::Tga,
366 ImageFormat::Pnm,
367 ImageFormat::Farbfeld,
368 ImageFormat::Avif,
369 ImageFormat::WebP,
370 ImageFormat::OpenExr,
371 ImageFormat::Qoi,
372 ImageFormat::Dds,
373 ImageFormat::Hdr,
374 ]
375 .iter()
376 .copied()
377 }
378}
379
380#[allow(dead_code)]
383pub(crate) struct ImageReadBuffer {
385 scanline_bytes: usize,
386 buffer: Vec<u8>,
387 consumed: usize,
388
389 total_bytes: u64,
390 offset: u64,
391}
392impl ImageReadBuffer {
393 #[allow(dead_code)]
399 pub(crate) fn new(scanline_bytes: u64, total_bytes: u64) -> Self {
401 Self {
402 scanline_bytes: usize::try_from(scanline_bytes).unwrap(),
403 buffer: Vec::new(),
404 consumed: 0,
405 total_bytes,
406 offset: 0,
407 }
408 }
409
410 #[allow(dead_code)]
411 pub(crate) fn read<F>(&mut self, buf: &mut [u8], mut read_scanline: F) -> io::Result<usize>
413 where
414 F: FnMut(&mut [u8]) -> io::Result<usize>,
415 {
416 if self.buffer.len() == self.consumed {
417 if self.offset == self.total_bytes {
418 return Ok(0);
419 } else if buf.len() >= self.scanline_bytes {
420 let bytes_read = read_scanline(&mut buf[..self.scanline_bytes])?;
423 self.offset += u64::try_from(bytes_read).unwrap();
424 return Ok(bytes_read);
425 } else {
426 if self.buffer.is_empty() {
429 self.buffer.resize(self.scanline_bytes, 0);
430 }
431
432 self.consumed = 0;
433 let bytes_read = read_scanline(&mut self.buffer[..])?;
434 self.buffer.resize(bytes_read, 0);
435 self.offset += u64::try_from(bytes_read).unwrap();
436
437 assert!(bytes_read == self.scanline_bytes || self.offset == self.total_bytes);
438 }
439 }
440
441 let bytes_buffered = self.buffer.len() - self.consumed;
443 if bytes_buffered > buf.len() {
444 buf.copy_from_slice(&self.buffer[self.consumed..][..buf.len()]);
445 self.consumed += buf.len();
446 Ok(buf.len())
447 } else {
448 buf[..bytes_buffered].copy_from_slice(&self.buffer[self.consumed..][..bytes_buffered]);
449 self.consumed = self.buffer.len();
450 Ok(bytes_buffered)
451 }
452 }
453}
454
455#[allow(dead_code)]
458pub(crate) fn load_rect<D, F1, F2, E>(
460 x: u32,
461 y: u32,
462 width: u32,
463 height: u32,
464 buf: &mut [u8],
465 row_pitch: usize,
466 decoder: &mut D,
467 scanline_bytes: usize,
468 mut seek_scanline: F1,
469 mut read_scanline: F2,
470) -> ImageResult<()>
471where
472 D: ImageDecoder,
473 F1: FnMut(&mut D, u64) -> io::Result<()>,
474 F2: FnMut(&mut D, &mut [u8]) -> Result<(), E>,
475 ImageError: From<E>,
476{
477 let scanline_bytes = u64::try_from(scanline_bytes).unwrap();
478 let row_pitch = u64::try_from(row_pitch).unwrap();
479
480 let (x, y, width, height) = (
481 u64::from(x),
482 u64::from(y),
483 u64::from(width),
484 u64::from(height),
485 );
486 let dimensions = decoder.dimensions();
487 let bytes_per_pixel = u64::from(decoder.color_type().bytes_per_pixel());
488 let row_bytes = bytes_per_pixel * u64::from(dimensions.0);
489 let total_bytes = width * height * bytes_per_pixel;
490
491 assert!(
492 buf.len() >= usize::try_from(total_bytes).unwrap_or(usize::MAX),
493 "output buffer too short\n expected `{}`, provided `{}`",
494 total_bytes,
495 buf.len()
496 );
497
498 let mut current_scanline = 0;
499 let mut tmp = Vec::new();
500 let mut tmp_scanline = None;
501
502 {
503 let mut read_image_range =
506 |mut start: u64, end: u64, mut output: &mut [u8]| -> ImageResult<()> {
507 let target_scanline = start / scanline_bytes;
510 if tmp_scanline == Some(target_scanline) {
511 let position = target_scanline * scanline_bytes;
512 let offset = start.saturating_sub(position);
513 let len = (end - start)
514 .min(scanline_bytes - offset)
515 .min(end - position);
516
517 output
518 .write_all(&tmp[offset as usize..][..len as usize])
519 .unwrap();
520 start += len;
521
522 if start == end {
523 return Ok(());
524 }
525 }
526
527 let target_scanline = start / scanline_bytes;
528 if target_scanline != current_scanline {
529 seek_scanline(decoder, target_scanline)?;
530 current_scanline = target_scanline;
531 }
532
533 let mut position = current_scanline * scanline_bytes;
534 while position < end {
535 if position >= start && end - position >= scanline_bytes {
536 read_scanline(decoder, &mut output[..(scanline_bytes as usize)])?;
537 output = &mut output[scanline_bytes as usize..];
538 } else {
539 tmp.resize(scanline_bytes as usize, 0u8);
540 read_scanline(decoder, &mut tmp)?;
541 tmp_scanline = Some(current_scanline);
542
543 let offset = start.saturating_sub(position);
544 let len = (end - start)
545 .min(scanline_bytes - offset)
546 .min(end - position);
547
548 output
549 .write_all(&tmp[offset as usize..][..len as usize])
550 .unwrap();
551 }
552
553 current_scanline += 1;
554 position += scanline_bytes;
555 }
556 Ok(())
557 };
558
559 if x + width > u64::from(dimensions.0)
560 || y + height > u64::from(dimensions.1)
561 || width == 0
562 || height == 0
563 {
564 return Err(ImageError::Parameter(ParameterError::from_kind(
565 ParameterErrorKind::DimensionMismatch,
566 )));
567 }
568 if scanline_bytes > usize::MAX as u64 {
569 return Err(ImageError::Limits(LimitError::from_kind(
570 LimitErrorKind::InsufficientMemory,
571 )));
572 }
573
574 if x == 0 && width == u64::from(dimensions.0) && row_pitch == row_bytes {
575 let start = x * bytes_per_pixel + y * row_bytes;
576 let end = (x + width) * bytes_per_pixel + (y + height - 1) * row_bytes;
577 read_image_range(start, end, buf)?;
578 } else {
579 for (output_slice, row) in buf.chunks_mut(row_pitch as usize).zip(y..(y + height)) {
580 let start = x * bytes_per_pixel + row * row_bytes;
581 let end = (x + width) * bytes_per_pixel + row * row_bytes;
582 read_image_range(start, end, output_slice)?;
583 }
584 }
585 }
586
587 Ok(seek_scanline(decoder, 0)?)
589}
590
591pub(crate) fn decoder_to_vec<T>(decoder: impl ImageDecoder) -> ImageResult<Vec<T>>
596where
597 T: crate::traits::Primitive + bytemuck::Pod,
598{
599 let total_bytes = usize::try_from(decoder.total_bytes());
600 if total_bytes.is_err() || total_bytes.unwrap() > isize::MAX as usize {
601 return Err(ImageError::Limits(LimitError::from_kind(
602 LimitErrorKind::InsufficientMemory,
603 )));
604 }
605
606 let mut buf = vec![num_traits::Zero::zero(); total_bytes.unwrap() / size_of::<T>()];
607 decoder.read_image(bytemuck::cast_slice_mut(buf.as_mut_slice()))?;
608 Ok(buf)
609}
610
611pub trait ImageDecoder {
613 fn dimensions(&self) -> (u32, u32);
615
616 fn color_type(&self) -> ColorType;
618
619 fn original_color_type(&self) -> ExtendedColorType {
621 self.color_type().into()
622 }
623
624 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
628 Ok(None)
629 }
630
631 fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
636 Ok(None)
637 }
638
639 fn total_bytes(&self) -> u64 {
646 let dimensions = self.dimensions();
647 let total_pixels = u64::from(dimensions.0) * u64::from(dimensions.1);
648 let bytes_per_pixel = u64::from(self.color_type().bytes_per_pixel());
649 total_pixels.saturating_mul(bytes_per_pixel)
650 }
651
652 fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
674 where
675 Self: Sized;
676
677 fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> {
689 limits.check_support(&crate::LimitSupport::default())?;
690 let (width, height) = self.dimensions();
691 limits.check_dimensions(width, height)?;
692 Ok(())
693 }
694
695 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()>;
706}
707
708impl<T: ?Sized + ImageDecoder> ImageDecoder for Box<T> {
709 fn dimensions(&self) -> (u32, u32) {
710 (**self).dimensions()
711 }
712 fn color_type(&self) -> ColorType {
713 (**self).color_type()
714 }
715 fn original_color_type(&self) -> ExtendedColorType {
716 (**self).original_color_type()
717 }
718 fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
719 (**self).icc_profile()
720 }
721 fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
722 (**self).exif_metadata()
723 }
724 fn total_bytes(&self) -> u64 {
725 (**self).total_bytes()
726 }
727 fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
728 where
729 Self: Sized,
730 {
731 T::read_image_boxed(self, buf)
732 }
733 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
734 T::read_image_boxed(*self, buf)
735 }
736 fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> {
737 (**self).set_limits(limits)
738 }
739}
740
741pub trait ImageDecoderRect: ImageDecoder {
743 fn read_rect(
751 &mut self,
752 x: u32,
753 y: u32,
754 width: u32,
755 height: u32,
756 buf: &mut [u8],
757 row_pitch: usize,
758 ) -> ImageResult<()>;
759}
760
761pub trait AnimationDecoder<'a> {
763 fn into_frames(self) -> Frames<'a>;
765}
766
767pub trait ImageEncoder {
769 fn write_image(
784 self,
785 buf: &[u8],
786 width: u32,
787 height: u32,
788 color_type: ExtendedColorType,
789 ) -> ImageResult<()>;
790}
791
792#[derive(Debug)]
794pub struct Pixels<'a, I: ?Sized + 'a> {
795 image: &'a I,
796 x: u32,
797 y: u32,
798 width: u32,
799 height: u32,
800}
801
802impl<'a, I: GenericImageView> Iterator for Pixels<'a, I> {
803 type Item = (u32, u32, I::Pixel);
804
805 fn next(&mut self) -> Option<(u32, u32, I::Pixel)> {
806 if self.x >= self.width {
807 self.x = 0;
808 self.y += 1;
809 }
810
811 if self.y >= self.height {
812 None
813 } else {
814 let pixel = self.image.get_pixel(self.x, self.y);
815 let p = (self.x, self.y, pixel);
816
817 self.x += 1;
818
819 Some(p)
820 }
821 }
822}
823
824impl<I: ?Sized> Clone for Pixels<'_, I> {
825 fn clone(&self) -> Self {
826 Pixels { ..*self }
827 }
828}
829
830pub trait GenericImageView {
839 type Pixel: Pixel;
841
842 fn dimensions(&self) -> (u32, u32);
844
845 fn width(&self) -> u32 {
847 let (w, _) = self.dimensions();
848 w
849 }
850
851 fn height(&self) -> u32 {
853 let (_, h) = self.dimensions();
854 h
855 }
856
857 fn in_bounds(&self, x: u32, y: u32) -> bool {
859 let (width, height) = self.dimensions();
860 x < width && y < height
861 }
862
863 fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel;
869
870 unsafe fn unsafe_get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
879 self.get_pixel(x, y)
880 }
881
882 fn pixels(&self) -> Pixels<Self>
886 where
887 Self: Sized,
888 {
889 let (width, height) = self.dimensions();
890
891 Pixels {
892 image: self,
893 x: 0,
894 y: 0,
895 width,
896 height,
897 }
898 }
899
900 fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&Self>
904 where
905 Self: Sized,
906 {
907 assert!(u64::from(x) + u64::from(width) <= u64::from(self.width()));
908 assert!(u64::from(y) + u64::from(height) <= u64::from(self.height()));
909 SubImage::new(self, x, y, width, height)
910 }
911}
912
913pub trait GenericImage: GenericImageView {
915 #[deprecated(since = "0.24.0", note = "Use `get_pixel` and `put_pixel` instead.")]
936 fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel;
937
938 fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
944
945 unsafe fn unsafe_put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
954 self.put_pixel(x, y, pixel);
955 }
956
957 #[deprecated(
959 since = "0.24.0",
960 note = "Use iterator `pixels_mut` to blend the pixels directly"
961 )]
962 fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
963
964 fn copy_from<O>(&mut self, other: &O, x: u32, y: u32) -> ImageResult<()>
980 where
981 O: GenericImageView<Pixel = Self::Pixel>,
982 {
983 if self.width() < other.width() + x || self.height() < other.height() + y {
986 return Err(ImageError::Parameter(ParameterError::from_kind(
987 ParameterErrorKind::DimensionMismatch,
988 )));
989 }
990
991 for k in 0..other.height() {
992 for i in 0..other.width() {
993 let p = other.get_pixel(i, k);
994 self.put_pixel(i + x, k + y, p);
995 }
996 }
997 Ok(())
998 }
999
1000 fn copy_within(&mut self, source: Rect, x: u32, y: u32) -> bool {
1008 let Rect {
1009 x: sx,
1010 y: sy,
1011 width,
1012 height,
1013 } = source;
1014 let dx = x;
1015 let dy = y;
1016 assert!(sx < self.width() && dx < self.width());
1017 assert!(sy < self.height() && dy < self.height());
1018 if self.width() - dx.max(sx) < width || self.height() - dy.max(sy) < height {
1019 return false;
1020 }
1021 macro_rules! copy_within_impl_ {
1024 ($xiter:expr, $yiter:expr) => {
1025 for y in $yiter {
1026 let sy = sy + y;
1027 let dy = dy + y;
1028 for x in $xiter {
1029 let sx = sx + x;
1030 let dx = dx + x;
1031 let pixel = self.get_pixel(sx, sy);
1032 self.put_pixel(dx, dy, pixel);
1033 }
1034 }
1035 };
1036 }
1037 match (sx < dx, sy < dy) {
1039 (true, true) => copy_within_impl_!((0..width).rev(), (0..height).rev()),
1040 (true, false) => copy_within_impl_!((0..width).rev(), 0..height),
1041 (false, true) => copy_within_impl_!(0..width, (0..height).rev()),
1042 (false, false) => copy_within_impl_!(0..width, 0..height),
1043 }
1044 true
1045 }
1046
1047 fn sub_image(&mut self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&mut Self>
1051 where
1052 Self: Sized,
1053 {
1054 assert!(u64::from(x) + u64::from(width) <= u64::from(self.width()));
1055 assert!(u64::from(y) + u64::from(height) <= u64::from(self.height()));
1056 SubImage::new(self, x, y, width, height)
1057 }
1058}
1059
1060#[derive(Copy, Clone)]
1082pub struct SubImage<I> {
1083 inner: SubImageInner<I>,
1084}
1085
1086#[derive(Copy, Clone)]
1091pub struct SubImageInner<I> {
1092 image: I,
1093 xoffset: u32,
1094 yoffset: u32,
1095 xstride: u32,
1096 ystride: u32,
1097}
1098
1099type DerefPixel<I> = <<I as Deref>::Target as GenericImageView>::Pixel;
1101
1102type DerefSubpixel<I> = <DerefPixel<I> as Pixel>::Subpixel;
1104
1105impl<I> SubImage<I> {
1106 pub fn new(image: I, x: u32, y: u32, width: u32, height: u32) -> SubImage<I> {
1109 SubImage {
1110 inner: SubImageInner {
1111 image,
1112 xoffset: x,
1113 yoffset: y,
1114 xstride: width,
1115 ystride: height,
1116 },
1117 }
1118 }
1119
1120 pub fn change_bounds(&mut self, x: u32, y: u32, width: u32, height: u32) {
1122 self.inner.xoffset = x;
1123 self.inner.yoffset = y;
1124 self.inner.xstride = width;
1125 self.inner.ystride = height;
1126 }
1127
1128 pub fn offsets(&self) -> (u32, u32) {
1130 (self.inner.xoffset, self.inner.yoffset)
1131 }
1132
1133 pub fn to_image(&self) -> ImageBuffer<DerefPixel<I>, Vec<DerefSubpixel<I>>>
1135 where
1136 I: Deref,
1137 I::Target: GenericImageView + 'static,
1138 {
1139 let mut out = ImageBuffer::new(self.inner.xstride, self.inner.ystride);
1140 let borrowed = &*self.inner.image;
1141
1142 for y in 0..self.inner.ystride {
1143 for x in 0..self.inner.xstride {
1144 let p = borrowed.get_pixel(x + self.inner.xoffset, y + self.inner.yoffset);
1145 out.put_pixel(x, y, p);
1146 }
1147 }
1148
1149 out
1150 }
1151}
1152
1153impl<I> SubImage<I>
1155where
1156 I: Deref,
1157 I::Target: GenericImageView,
1158{
1159 pub fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&I::Target> {
1178 use crate::GenericImageView as _;
1179 assert!(u64::from(x) + u64::from(width) <= u64::from(self.inner.width()));
1180 assert!(u64::from(y) + u64::from(height) <= u64::from(self.inner.height()));
1181 let x = self.inner.xoffset.saturating_add(x);
1182 let y = self.inner.yoffset.saturating_add(y);
1183 SubImage::new(&*self.inner.image, x, y, width, height)
1184 }
1185
1186 pub fn inner(&self) -> &I::Target {
1188 &self.inner.image
1189 }
1190}
1191
1192impl<I> SubImage<I>
1193where
1194 I: DerefMut,
1195 I::Target: GenericImage,
1196{
1197 pub fn sub_image(
1201 &mut self,
1202 x: u32,
1203 y: u32,
1204 width: u32,
1205 height: u32,
1206 ) -> SubImage<&mut I::Target> {
1207 assert!(u64::from(x) + u64::from(width) <= u64::from(self.inner.width()));
1208 assert!(u64::from(y) + u64::from(height) <= u64::from(self.inner.height()));
1209 let x = self.inner.xoffset.saturating_add(x);
1210 let y = self.inner.yoffset.saturating_add(y);
1211 SubImage::new(&mut *self.inner.image, x, y, width, height)
1212 }
1213
1214 pub fn inner_mut(&mut self) -> &mut I::Target {
1216 &mut self.inner.image
1217 }
1218}
1219
1220impl<I> Deref for SubImage<I>
1221where
1222 I: Deref,
1223{
1224 type Target = SubImageInner<I>;
1225 fn deref(&self) -> &Self::Target {
1226 &self.inner
1227 }
1228}
1229
1230impl<I> DerefMut for SubImage<I>
1231where
1232 I: DerefMut,
1233{
1234 fn deref_mut(&mut self) -> &mut Self::Target {
1235 &mut self.inner
1236 }
1237}
1238
1239#[allow(deprecated)]
1240impl<I> GenericImageView for SubImageInner<I>
1241where
1242 I: Deref,
1243 I::Target: GenericImageView,
1244{
1245 type Pixel = DerefPixel<I>;
1246
1247 fn dimensions(&self) -> (u32, u32) {
1248 (self.xstride, self.ystride)
1249 }
1250
1251 fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
1252 self.image.get_pixel(x + self.xoffset, y + self.yoffset)
1253 }
1254}
1255
1256#[allow(deprecated)]
1257impl<I> GenericImage for SubImageInner<I>
1258where
1259 I: DerefMut,
1260 I::Target: GenericImage + Sized,
1261{
1262 fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel {
1263 self.image.get_pixel_mut(x + self.xoffset, y + self.yoffset)
1264 }
1265
1266 fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1267 self.image
1268 .put_pixel(x + self.xoffset, y + self.yoffset, pixel);
1269 }
1270
1271 fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1273 self.image
1274 .blend_pixel(x + self.xoffset, y + self.yoffset, pixel);
1275 }
1276}
1277
1278#[cfg(test)]
1279mod tests {
1280 use std::collections::HashSet;
1281 use std::io;
1282 use std::path::Path;
1283
1284 use super::{
1285 load_rect, ColorType, GenericImage, GenericImageView, ImageDecoder, ImageFormat,
1286 ImageResult,
1287 };
1288 use crate::color::Rgba;
1289 use crate::math::Rect;
1290 use crate::{GrayImage, ImageBuffer};
1291
1292 #[test]
1293 #[allow(deprecated)]
1294 fn test_image_alpha_blending() {
1296 let mut target = ImageBuffer::new(1, 1);
1297 target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
1298 assert!(*target.get_pixel(0, 0) == Rgba([255, 0, 0, 255]));
1299 target.blend_pixel(0, 0, Rgba([0, 255, 0, 255]));
1300 assert!(*target.get_pixel(0, 0) == Rgba([0, 255, 0, 255]));
1301
1302 target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
1304 assert!(*target.get_pixel(0, 0) == Rgba([127, 127, 0, 255]));
1305
1306 target.put_pixel(0, 0, Rgba([0, 255, 0, 127]));
1308 target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
1309 assert!(*target.get_pixel(0, 0) == Rgba([169, 85, 0, 190]));
1310 }
1311
1312 #[test]
1313 fn test_in_bounds() {
1314 let mut target = ImageBuffer::new(2, 2);
1315 target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
1316
1317 assert!(target.in_bounds(0, 0));
1318 assert!(target.in_bounds(1, 0));
1319 assert!(target.in_bounds(0, 1));
1320 assert!(target.in_bounds(1, 1));
1321
1322 assert!(!target.in_bounds(2, 0));
1323 assert!(!target.in_bounds(0, 2));
1324 assert!(!target.in_bounds(2, 2));
1325 }
1326
1327 #[test]
1328 fn test_can_subimage_clone_nonmut() {
1329 let mut source = ImageBuffer::new(3, 3);
1330 source.put_pixel(1, 1, Rgba([255u8, 0, 0, 255]));
1331
1332 let source = source.clone();
1334
1335 let cloned = source.view(1, 1, 1, 1).to_image();
1337
1338 assert!(cloned.get_pixel(0, 0) == source.get_pixel(1, 1));
1339 }
1340
1341 #[test]
1342 fn test_can_nest_views() {
1343 let mut source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1344
1345 {
1346 let mut sub1 = source.sub_image(0, 0, 2, 2);
1347 let mut sub2 = sub1.sub_image(1, 1, 1, 1);
1348 sub2.put_pixel(0, 0, Rgba([0, 0, 0, 0]));
1349 }
1350
1351 assert_eq!(*source.get_pixel(1, 1), Rgba([0, 0, 0, 0]));
1352
1353 let view1 = source.view(0, 0, 2, 2);
1354 assert_eq!(*source.get_pixel(1, 1), view1.get_pixel(1, 1));
1355
1356 let view2 = view1.view(1, 1, 1, 1);
1357 assert_eq!(*source.get_pixel(1, 1), view2.get_pixel(0, 0));
1358 }
1359
1360 #[test]
1361 #[should_panic]
1362 fn test_view_out_of_bounds() {
1363 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1364 source.view(1, 1, 3, 3);
1365 }
1366
1367 #[test]
1368 #[should_panic]
1369 fn test_view_coordinates_out_of_bounds() {
1370 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1371 source.view(3, 3, 3, 3);
1372 }
1373
1374 #[test]
1375 #[should_panic]
1376 fn test_view_width_out_of_bounds() {
1377 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1378 source.view(1, 1, 3, 2);
1379 }
1380
1381 #[test]
1382 #[should_panic]
1383 fn test_view_height_out_of_bounds() {
1384 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1385 source.view(1, 1, 2, 3);
1386 }
1387
1388 #[test]
1389 #[should_panic]
1390 fn test_view_x_out_of_bounds() {
1391 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1392 source.view(3, 1, 3, 3);
1393 }
1394
1395 #[test]
1396 #[should_panic]
1397 fn test_view_y_out_of_bounds() {
1398 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1399 source.view(1, 3, 3, 3);
1400 }
1401
1402 #[test]
1403 fn test_view_in_bounds() {
1404 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1405 source.view(0, 0, 3, 3);
1406 source.view(1, 1, 2, 2);
1407 source.view(2, 2, 0, 0);
1408 }
1409
1410 #[test]
1411 fn test_copy_sub_image() {
1412 let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
1413 let view = source.view(0, 0, 3, 3);
1414 let _view2 = view;
1415 view.to_image();
1416 }
1417
1418 #[test]
1419 fn test_load_rect() {
1420 struct MockDecoder {
1421 scanline_number: u64,
1422 scanline_bytes: u64,
1423 }
1424 impl ImageDecoder for MockDecoder {
1425 fn dimensions(&self) -> (u32, u32) {
1426 (5, 5)
1427 }
1428 fn color_type(&self) -> ColorType {
1429 ColorType::L8
1430 }
1431 fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
1432 unimplemented!()
1433 }
1434 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
1435 (*self).read_image(buf)
1436 }
1437 }
1438
1439 const DATA: [u8; 25] = [
1440 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1441 24,
1442 ];
1443
1444 fn seek_scanline(m: &mut MockDecoder, n: u64) -> io::Result<()> {
1445 m.scanline_number = n;
1446 Ok(())
1447 }
1448 fn read_scanline(m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> {
1449 let bytes_read = m.scanline_number * m.scanline_bytes;
1450 if bytes_read >= 25 {
1451 return Ok(());
1452 }
1453
1454 let len = m.scanline_bytes.min(25 - bytes_read);
1455 buf[..(len as usize)].copy_from_slice(&DATA[(bytes_read as usize)..][..(len as usize)]);
1456 m.scanline_number += 1;
1457 Ok(())
1458 }
1459
1460 for scanline_bytes in 1..30 {
1461 let mut output = [0u8; 26];
1462
1463 load_rect(
1464 0,
1465 0,
1466 5,
1467 5,
1468 &mut output,
1469 5,
1470 &mut MockDecoder {
1471 scanline_number: 0,
1472 scanline_bytes,
1473 },
1474 scanline_bytes as usize,
1475 seek_scanline,
1476 read_scanline,
1477 )
1478 .unwrap();
1479 assert_eq!(output[0..25], DATA);
1480 assert_eq!(output[25], 0);
1481
1482 output = [0u8; 26];
1483 load_rect(
1484 3,
1485 2,
1486 1,
1487 1,
1488 &mut output,
1489 1,
1490 &mut MockDecoder {
1491 scanline_number: 0,
1492 scanline_bytes,
1493 },
1494 scanline_bytes as usize,
1495 seek_scanline,
1496 read_scanline,
1497 )
1498 .unwrap();
1499 assert_eq!(output[0..2], [13, 0]);
1500
1501 output = [0u8; 26];
1502 load_rect(
1503 3,
1504 2,
1505 2,
1506 2,
1507 &mut output,
1508 2,
1509 &mut MockDecoder {
1510 scanline_number: 0,
1511 scanline_bytes,
1512 },
1513 scanline_bytes as usize,
1514 seek_scanline,
1515 read_scanline,
1516 )
1517 .unwrap();
1518 assert_eq!(output[0..5], [13, 14, 18, 19, 0]);
1519
1520 output = [0u8; 26];
1521 load_rect(
1522 1,
1523 1,
1524 2,
1525 4,
1526 &mut output,
1527 2,
1528 &mut MockDecoder {
1529 scanline_number: 0,
1530 scanline_bytes,
1531 },
1532 scanline_bytes as usize,
1533 seek_scanline,
1534 read_scanline,
1535 )
1536 .unwrap();
1537 assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]);
1538 }
1539 }
1540
1541 #[test]
1542 fn test_load_rect_single_scanline() {
1543 const DATA: [u8; 25] = [
1544 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1545 24,
1546 ];
1547
1548 struct MockDecoder;
1549 impl ImageDecoder for MockDecoder {
1550 fn dimensions(&self) -> (u32, u32) {
1551 (5, 5)
1552 }
1553 fn color_type(&self) -> ColorType {
1554 ColorType::L8
1555 }
1556 fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
1557 unimplemented!()
1558 }
1559 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
1560 (*self).read_image(buf)
1561 }
1562 }
1563
1564 let mut seeks = 0;
1566 let seek_scanline = |_d: &mut MockDecoder, n: u64| -> io::Result<()> {
1567 seeks += 1;
1568 assert_eq!(n, 0);
1569 assert_eq!(seeks, 1);
1570 Ok(())
1571 };
1572
1573 fn read_scanline(_m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> {
1574 buf.copy_from_slice(&DATA);
1575 Ok(())
1576 }
1577
1578 let mut output = [0; 26];
1579 load_rect(
1580 1,
1581 1,
1582 2,
1583 4,
1584 &mut output,
1585 2,
1586 &mut MockDecoder,
1587 DATA.len(),
1588 seek_scanline,
1589 read_scanline,
1590 )
1591 .unwrap();
1592 assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]);
1593 }
1594
1595 #[test]
1596 fn test_image_format_from_path() {
1597 fn from_path(s: &str) -> ImageResult<ImageFormat> {
1598 ImageFormat::from_path(Path::new(s))
1599 }
1600 assert_eq!(from_path("./a.jpg").unwrap(), ImageFormat::Jpeg);
1601 assert_eq!(from_path("./a.jpeg").unwrap(), ImageFormat::Jpeg);
1602 assert_eq!(from_path("./a.JPEG").unwrap(), ImageFormat::Jpeg);
1603 assert_eq!(from_path("./a.pNg").unwrap(), ImageFormat::Png);
1604 assert_eq!(from_path("./a.gif").unwrap(), ImageFormat::Gif);
1605 assert_eq!(from_path("./a.webp").unwrap(), ImageFormat::WebP);
1606 assert_eq!(from_path("./a.tiFF").unwrap(), ImageFormat::Tiff);
1607 assert_eq!(from_path("./a.tif").unwrap(), ImageFormat::Tiff);
1608 assert_eq!(from_path("./a.tga").unwrap(), ImageFormat::Tga);
1609 assert_eq!(from_path("./a.dds").unwrap(), ImageFormat::Dds);
1610 assert_eq!(from_path("./a.bmp").unwrap(), ImageFormat::Bmp);
1611 assert_eq!(from_path("./a.Ico").unwrap(), ImageFormat::Ico);
1612 assert_eq!(from_path("./a.hdr").unwrap(), ImageFormat::Hdr);
1613 assert_eq!(from_path("./a.exr").unwrap(), ImageFormat::OpenExr);
1614 assert_eq!(from_path("./a.pbm").unwrap(), ImageFormat::Pnm);
1615 assert_eq!(from_path("./a.pAM").unwrap(), ImageFormat::Pnm);
1616 assert_eq!(from_path("./a.Ppm").unwrap(), ImageFormat::Pnm);
1617 assert_eq!(from_path("./a.pgm").unwrap(), ImageFormat::Pnm);
1618 assert_eq!(from_path("./a.AViF").unwrap(), ImageFormat::Avif);
1619 assert!(from_path("./a.txt").is_err());
1620 assert!(from_path("./a").is_err());
1621 }
1622
1623 #[test]
1624 fn test_generic_image_copy_within_oob() {
1625 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap();
1626 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1627 Rect {
1628 x: 0,
1629 y: 0,
1630 width: 5,
1631 height: 4
1632 },
1633 0,
1634 0
1635 ));
1636 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1637 Rect {
1638 x: 0,
1639 y: 0,
1640 width: 4,
1641 height: 5
1642 },
1643 0,
1644 0
1645 ));
1646 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1647 Rect {
1648 x: 1,
1649 y: 0,
1650 width: 4,
1651 height: 4
1652 },
1653 0,
1654 0
1655 ));
1656 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1657 Rect {
1658 x: 0,
1659 y: 0,
1660 width: 4,
1661 height: 4
1662 },
1663 1,
1664 0
1665 ));
1666 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1667 Rect {
1668 x: 0,
1669 y: 1,
1670 width: 4,
1671 height: 4
1672 },
1673 0,
1674 0
1675 ));
1676 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1677 Rect {
1678 x: 0,
1679 y: 0,
1680 width: 4,
1681 height: 4
1682 },
1683 0,
1684 1
1685 ));
1686 assert!(!image.sub_image(0, 0, 4, 4).copy_within(
1687 Rect {
1688 x: 1,
1689 y: 1,
1690 width: 4,
1691 height: 4
1692 },
1693 0,
1694 0
1695 ));
1696 }
1697
1698 #[test]
1699 fn test_generic_image_copy_within_tl() {
1700 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1701 let expected = [0, 1, 2, 3, 4, 0, 1, 2, 8, 4, 5, 6, 12, 8, 9, 10];
1702 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1703 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1704 Rect {
1705 x: 0,
1706 y: 0,
1707 width: 3,
1708 height: 3
1709 },
1710 1,
1711 1
1712 ));
1713 assert_eq!(&image.into_raw(), &expected);
1714 }
1715
1716 #[test]
1717 fn test_generic_image_copy_within_tr() {
1718 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1719 let expected = [0, 1, 2, 3, 1, 2, 3, 7, 5, 6, 7, 11, 9, 10, 11, 15];
1720 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1721 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1722 Rect {
1723 x: 1,
1724 y: 0,
1725 width: 3,
1726 height: 3
1727 },
1728 0,
1729 1
1730 ));
1731 assert_eq!(&image.into_raw(), &expected);
1732 }
1733
1734 #[test]
1735 fn test_generic_image_copy_within_bl() {
1736 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1737 let expected = [0, 4, 5, 6, 4, 8, 9, 10, 8, 12, 13, 14, 12, 13, 14, 15];
1738 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1739 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1740 Rect {
1741 x: 0,
1742 y: 1,
1743 width: 3,
1744 height: 3
1745 },
1746 1,
1747 0
1748 ));
1749 assert_eq!(&image.into_raw(), &expected);
1750 }
1751
1752 #[test]
1753 fn test_generic_image_copy_within_br() {
1754 let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
1755 let expected = [5, 6, 7, 3, 9, 10, 11, 7, 13, 14, 15, 11, 12, 13, 14, 15];
1756 let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
1757 assert!(image.sub_image(0, 0, 4, 4).copy_within(
1758 Rect {
1759 x: 1,
1760 y: 1,
1761 width: 3,
1762 height: 3
1763 },
1764 0,
1765 0
1766 ));
1767 assert_eq!(&image.into_raw(), &expected);
1768 }
1769
1770 #[test]
1771 fn image_formats_are_recognized() {
1772 use ImageFormat::*;
1773 const ALL_FORMATS: &[ImageFormat] = &[
1774 Avif, Png, Jpeg, Gif, WebP, Pnm, Tiff, Tga, Dds, Bmp, Ico, Hdr, Farbfeld, OpenExr,
1775 ];
1776 for &format in ALL_FORMATS {
1777 let mut file = Path::new("file.nothing").to_owned();
1778 for ext in format.extensions_str() {
1779 assert!(file.set_extension(ext));
1780 match ImageFormat::from_path(&file) {
1781 Err(_) => panic!("Path {} not recognized as {:?}", file.display(), format),
1782 Ok(result) => assert_eq!(format, result),
1783 }
1784 }
1785 }
1786 }
1787
1788 #[test]
1789 fn total_bytes_overflow() {
1790 struct D;
1791 impl ImageDecoder for D {
1792 fn color_type(&self) -> ColorType {
1793 ColorType::Rgb8
1794 }
1795 fn dimensions(&self) -> (u32, u32) {
1796 (0xffff_ffff, 0xffff_ffff)
1797 }
1798 fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
1799 unimplemented!()
1800 }
1801 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
1802 (*self).read_image(buf)
1803 }
1804 }
1805 assert_eq!(D.total_bytes(), u64::MAX);
1806
1807 let v: ImageResult<Vec<u8>> = super::decoder_to_vec(D);
1808 assert!(v.is_err());
1809 }
1810
1811 #[test]
1812 fn all() {
1813 let all_formats: HashSet<ImageFormat> = ImageFormat::all().collect();
1814 assert!(all_formats.contains(&ImageFormat::Avif));
1815 assert!(all_formats.contains(&ImageFormat::Gif));
1816 assert!(all_formats.contains(&ImageFormat::Bmp));
1817 assert!(all_formats.contains(&ImageFormat::Farbfeld));
1818 assert!(all_formats.contains(&ImageFormat::Jpeg));
1819 }
1820
1821 #[test]
1822 fn reading_enabled() {
1823 assert_eq!(cfg!(feature = "jpeg"), ImageFormat::Jpeg.reading_enabled());
1824 assert_eq!(
1825 cfg!(feature = "ff"),
1826 ImageFormat::Farbfeld.reading_enabled()
1827 );
1828 assert!(!ImageFormat::Dds.reading_enabled());
1829 }
1830
1831 #[test]
1832 fn writing_enabled() {
1833 assert_eq!(cfg!(feature = "jpeg"), ImageFormat::Jpeg.writing_enabled());
1834 assert_eq!(
1835 cfg!(feature = "ff"),
1836 ImageFormat::Farbfeld.writing_enabled()
1837 );
1838 assert!(!ImageFormat::Dds.writing_enabled());
1839 }
1840}