1use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
2use no_std_io::io::{Seek, Write};
3#[cfg(feature = "std")]
4use std::fs::File;
5#[cfg(feature = "std")]
6use std::path::Path;
7
8use crate::color::{self, FromColor, IntoColor};
9use crate::error::{ImageError, ImageResult, ParameterError, ParameterErrorKind};
10use crate::flat::FlatSamples;
11use crate::imageops::{gaussian_blur_dyn_image, GaussianBlurParameters};
12use crate::images::buffer::{
13 ConvertBuffer, Gray16Image, GrayAlpha16Image, GrayAlphaImage, GrayImage, ImageBuffer,
14 Rgb16Image, Rgb32FImage, RgbImage, Rgba16Image, Rgba32FImage, RgbaImage,
15};
16use crate::io::encoder::ImageEncoderBoxed;
17use crate::io::free_functions::{self, encoder_for_format};
18use crate::math::resize_dimensions;
19use crate::metadata::Orientation;
20use crate::traits::Pixel;
21#[cfg(feature = "std")]
22use crate::ImageReader;
23use crate::{
24 imageops,
25 metadata::{Cicp, CicpColorPrimaries, CicpTransferCharacteristics},
26 ConvertColorOptions, ExtendedColorType, GenericImage, GenericImageView, ImageDecoder,
27 ImageEncoder, ImageFormat, Luma, LumaA,
28};
29
30#[derive(Debug, PartialEq)]
75#[non_exhaustive]
76pub enum DynamicImage {
77 ImageLuma8(GrayImage),
79
80 ImageLumaA8(GrayAlphaImage),
82
83 ImageRgb8(RgbImage),
85
86 ImageRgba8(RgbaImage),
88
89 ImageLuma16(Gray16Image),
91
92 ImageLumaA16(GrayAlpha16Image),
94
95 ImageRgb16(Rgb16Image),
97
98 ImageRgba16(Rgba16Image),
100
101 ImageRgb32F(Rgb32FImage),
103
104 ImageRgba32F(Rgba32FImage),
106}
107
108macro_rules! dynamic_map(
109 ($dynimage: expr, $image: pat => $action: expr) => ({
110 use DynamicImage::*;
111 match $dynimage {
112 ImageLuma8($image) => ImageLuma8($action),
113 ImageLumaA8($image) => ImageLumaA8($action),
114 ImageRgb8($image) => ImageRgb8($action),
115 ImageRgba8($image) => ImageRgba8($action),
116 ImageLuma16($image) => ImageLuma16($action),
117 ImageLumaA16($image) => ImageLumaA16($action),
118 ImageRgb16($image) => ImageRgb16($action),
119 ImageRgba16($image) => ImageRgba16($action),
120 ImageRgb32F($image) => ImageRgb32F($action),
121 ImageRgba32F($image) => ImageRgba32F($action),
122 }
123 });
124
125 ($dynimage: expr, $image:pat_param, $action: expr) => (
126 match $dynimage {
127 DynamicImage::ImageLuma8($image) => $action,
128 DynamicImage::ImageLumaA8($image) => $action,
129 DynamicImage::ImageRgb8($image) => $action,
130 DynamicImage::ImageRgba8($image) => $action,
131 DynamicImage::ImageLuma16($image) => $action,
132 DynamicImage::ImageLumaA16($image) => $action,
133 DynamicImage::ImageRgb16($image) => $action,
134 DynamicImage::ImageRgba16($image) => $action,
135 DynamicImage::ImageRgb32F($image) => $action,
136 DynamicImage::ImageRgba32F($image) => $action,
137 }
138 );
139);
140
141impl Clone for DynamicImage {
142 fn clone(&self) -> Self {
143 dynamic_map!(*self, ref p, DynamicImage::from(p.clone()))
144 }
145
146 fn clone_from(&mut self, source: &Self) {
147 match (self, source) {
148 (Self::ImageLuma8(p1), Self::ImageLuma8(p2)) => p1.clone_from(p2),
149 (Self::ImageLumaA8(p1), Self::ImageLumaA8(p2)) => p1.clone_from(p2),
150 (Self::ImageRgb8(p1), Self::ImageRgb8(p2)) => p1.clone_from(p2),
151 (Self::ImageRgba8(p1), Self::ImageRgba8(p2)) => p1.clone_from(p2),
152 (Self::ImageLuma16(p1), Self::ImageLuma16(p2)) => p1.clone_from(p2),
153 (Self::ImageLumaA16(p1), Self::ImageLumaA16(p2)) => p1.clone_from(p2),
154 (Self::ImageRgb16(p1), Self::ImageRgb16(p2)) => p1.clone_from(p2),
155 (Self::ImageRgba16(p1), Self::ImageRgba16(p2)) => p1.clone_from(p2),
156 (Self::ImageRgb32F(p1), Self::ImageRgb32F(p2)) => p1.clone_from(p2),
157 (Self::ImageRgba32F(p1), Self::ImageRgba32F(p2)) => p1.clone_from(p2),
158 (this, source) => *this = source.clone(),
159 }
160 }
161}
162
163impl DynamicImage {
164 #[must_use]
169 pub fn new(w: u32, h: u32, color: color::ColorType) -> DynamicImage {
170 use color::ColorType::*;
171 match color {
172 L8 => Self::new_luma8(w, h),
173 La8 => Self::new_luma_a8(w, h),
174 Rgb8 => Self::new_rgb8(w, h),
175 Rgba8 => Self::new_rgba8(w, h),
176 L16 => Self::new_luma16(w, h),
177 La16 => Self::new_luma_a16(w, h),
178 Rgb16 => Self::new_rgb16(w, h),
179 Rgba16 => Self::new_rgba16(w, h),
180 Rgb32F => Self::new_rgb32f(w, h),
181 Rgba32F => Self::new_rgba32f(w, h),
182 }
183 }
184
185 #[must_use]
187 pub fn new_luma8(w: u32, h: u32) -> DynamicImage {
188 DynamicImage::ImageLuma8(ImageBuffer::new(w, h))
189 }
190
191 #[must_use]
194 pub fn new_luma_a8(w: u32, h: u32) -> DynamicImage {
195 DynamicImage::ImageLumaA8(ImageBuffer::new(w, h))
196 }
197
198 #[must_use]
200 pub fn new_rgb8(w: u32, h: u32) -> DynamicImage {
201 DynamicImage::ImageRgb8(ImageBuffer::new(w, h))
202 }
203
204 #[must_use]
206 pub fn new_rgba8(w: u32, h: u32) -> DynamicImage {
207 DynamicImage::ImageRgba8(ImageBuffer::new(w, h))
208 }
209
210 #[must_use]
212 pub fn new_luma16(w: u32, h: u32) -> DynamicImage {
213 DynamicImage::ImageLuma16(ImageBuffer::new(w, h))
214 }
215
216 #[must_use]
219 pub fn new_luma_a16(w: u32, h: u32) -> DynamicImage {
220 DynamicImage::ImageLumaA16(ImageBuffer::new(w, h))
221 }
222
223 #[must_use]
225 pub fn new_rgb16(w: u32, h: u32) -> DynamicImage {
226 DynamicImage::ImageRgb16(ImageBuffer::new(w, h))
227 }
228
229 #[must_use]
231 pub fn new_rgba16(w: u32, h: u32) -> DynamicImage {
232 DynamicImage::ImageRgba16(ImageBuffer::new(w, h))
233 }
234
235 #[must_use]
237 pub fn new_rgb32f(w: u32, h: u32) -> DynamicImage {
238 DynamicImage::ImageRgb32F(ImageBuffer::new(w, h))
239 }
240
241 #[must_use]
243 pub fn new_rgba32f(w: u32, h: u32) -> DynamicImage {
244 DynamicImage::ImageRgba32F(ImageBuffer::new(w, h))
245 }
246
247 pub fn from_decoder(decoder: impl ImageDecoder) -> ImageResult<Self> {
249 decoder_to_image(decoder)
250 }
251
252 #[inline]
261 #[must_use]
262 pub fn to<
263 T: Pixel
264 + FromColor<color::Rgb<u8>>
265 + FromColor<color::Rgb<f32>>
266 + FromColor<color::Rgba<u8>>
267 + FromColor<color::Rgba<u16>>
268 + FromColor<color::Rgba<f32>>
269 + FromColor<color::Rgb<u16>>
270 + FromColor<Luma<u8>>
271 + FromColor<Luma<u16>>
272 + FromColor<LumaA<u16>>
273 + FromColor<LumaA<u8>>,
274 >(
275 &self,
276 ) -> ImageBuffer<T, Vec<T::Subpixel>> {
277 dynamic_map!(*self, ref p, p.convert())
278 }
279
280 #[must_use]
282 pub fn to_rgb8(&self) -> RgbImage {
283 match self {
284 DynamicImage::ImageRgb8(x) => x.clone(),
285 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
286 }
287 }
288
289 #[must_use]
291 pub fn to_rgb16(&self) -> Rgb16Image {
292 match self {
293 DynamicImage::ImageRgb16(x) => x.clone(),
294 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
295 }
296 }
297
298 #[must_use]
300 pub fn to_rgb32f(&self) -> Rgb32FImage {
301 match self {
302 DynamicImage::ImageRgb32F(x) => x.clone(),
303 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
304 }
305 }
306
307 #[must_use]
309 pub fn to_rgba8(&self) -> RgbaImage {
310 match self {
311 DynamicImage::ImageRgba8(x) => x.clone(),
312 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
313 }
314 }
315
316 #[must_use]
318 pub fn to_rgba16(&self) -> Rgba16Image {
319 match self {
320 DynamicImage::ImageRgba16(x) => x.clone(),
321 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
322 }
323 }
324
325 #[must_use]
327 pub fn to_rgba32f(&self) -> Rgba32FImage {
328 match self {
329 DynamicImage::ImageRgba32F(x) => x.clone(),
330 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
331 }
332 }
333
334 #[must_use]
336 pub fn to_luma8(&self) -> GrayImage {
337 match self {
338 DynamicImage::ImageLuma8(x) => x.clone(),
339 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
340 }
341 }
342
343 #[must_use]
345 pub fn to_luma16(&self) -> Gray16Image {
346 match self {
347 DynamicImage::ImageLuma16(x) => x.clone(),
348 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
349 }
350 }
351
352 #[must_use]
354 pub fn to_luma32f(&self) -> ImageBuffer<Luma<f32>, Vec<f32>> {
355 dynamic_map!(self, ref p, p.cast_in_color_space())
356 }
357
358 #[must_use]
360 pub fn to_luma_alpha8(&self) -> GrayAlphaImage {
361 match self {
362 DynamicImage::ImageLumaA8(x) => x.clone(),
363 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
364 }
365 }
366
367 #[must_use]
369 pub fn to_luma_alpha16(&self) -> GrayAlpha16Image {
370 match self {
371 DynamicImage::ImageLumaA16(x) => x.clone(),
372 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
373 }
374 }
375
376 #[must_use]
378 pub fn to_luma_alpha32f(&self) -> ImageBuffer<LumaA<f32>, Vec<f32>> {
379 dynamic_map!(self, ref p, p.cast_in_color_space())
380 }
381
382 #[must_use]
387 pub fn into_rgb8(self) -> RgbImage {
388 match self {
389 DynamicImage::ImageRgb8(x) => x,
390 x => x.to_rgb8(),
391 }
392 }
393
394 #[must_use]
399 pub fn into_rgb16(self) -> Rgb16Image {
400 match self {
401 DynamicImage::ImageRgb16(x) => x,
402 x => x.to_rgb16(),
403 }
404 }
405
406 #[must_use]
411 pub fn into_rgb32f(self) -> Rgb32FImage {
412 match self {
413 DynamicImage::ImageRgb32F(x) => x,
414 x => x.to_rgb32f(),
415 }
416 }
417
418 #[must_use]
423 pub fn into_rgba8(self) -> RgbaImage {
424 match self {
425 DynamicImage::ImageRgba8(x) => x,
426 x => x.to_rgba8(),
427 }
428 }
429
430 #[must_use]
435 pub fn into_rgba16(self) -> Rgba16Image {
436 match self {
437 DynamicImage::ImageRgba16(x) => x,
438 x => x.to_rgba16(),
439 }
440 }
441
442 #[must_use]
447 pub fn into_rgba32f(self) -> Rgba32FImage {
448 match self {
449 DynamicImage::ImageRgba32F(x) => x,
450 x => x.to_rgba32f(),
451 }
452 }
453
454 #[must_use]
459 pub fn into_luma8(self) -> GrayImage {
460 match self {
461 DynamicImage::ImageLuma8(x) => x,
462 x => x.to_luma8(),
463 }
464 }
465
466 #[must_use]
471 pub fn into_luma16(self) -> Gray16Image {
472 match self {
473 DynamicImage::ImageLuma16(x) => x,
474 x => x.to_luma16(),
475 }
476 }
477
478 #[must_use]
483 pub fn into_luma_alpha8(self) -> GrayAlphaImage {
484 match self {
485 DynamicImage::ImageLumaA8(x) => x,
486 x => x.to_luma_alpha8(),
487 }
488 }
489
490 #[must_use]
495 pub fn into_luma_alpha16(self) -> GrayAlpha16Image {
496 match self {
497 DynamicImage::ImageLumaA16(x) => x,
498 x => x.to_luma_alpha16(),
499 }
500 }
501
502 #[must_use]
507 pub fn crop(&mut self, x: u32, y: u32, width: u32, height: u32) -> DynamicImage {
508 dynamic_map!(*self, ref mut p => imageops::crop(p, x, y, width, height).to_image())
509 }
510
511 #[must_use]
513 pub fn crop_imm(&self, x: u32, y: u32, width: u32, height: u32) -> DynamicImage {
514 dynamic_map!(*self, ref p => imageops::crop_imm(p, x, y, width, height).to_image())
515 }
516
517 #[must_use]
519 pub fn as_rgb8(&self) -> Option<&RgbImage> {
520 match *self {
521 DynamicImage::ImageRgb8(ref p) => Some(p),
522 _ => None,
523 }
524 }
525
526 pub fn as_mut_rgb8(&mut self) -> Option<&mut RgbImage> {
528 match *self {
529 DynamicImage::ImageRgb8(ref mut p) => Some(p),
530 _ => None,
531 }
532 }
533
534 #[must_use]
536 pub fn as_rgba8(&self) -> Option<&RgbaImage> {
537 match *self {
538 DynamicImage::ImageRgba8(ref p) => Some(p),
539 _ => None,
540 }
541 }
542
543 pub fn as_mut_rgba8(&mut self) -> Option<&mut RgbaImage> {
545 match *self {
546 DynamicImage::ImageRgba8(ref mut p) => Some(p),
547 _ => None,
548 }
549 }
550
551 #[must_use]
553 pub fn as_luma8(&self) -> Option<&GrayImage> {
554 match *self {
555 DynamicImage::ImageLuma8(ref p) => Some(p),
556 _ => None,
557 }
558 }
559
560 pub fn as_mut_luma8(&mut self) -> Option<&mut GrayImage> {
562 match *self {
563 DynamicImage::ImageLuma8(ref mut p) => Some(p),
564 _ => None,
565 }
566 }
567
568 #[must_use]
570 pub fn as_luma_alpha8(&self) -> Option<&GrayAlphaImage> {
571 match *self {
572 DynamicImage::ImageLumaA8(ref p) => Some(p),
573 _ => None,
574 }
575 }
576
577 pub fn as_mut_luma_alpha8(&mut self) -> Option<&mut GrayAlphaImage> {
579 match *self {
580 DynamicImage::ImageLumaA8(ref mut p) => Some(p),
581 _ => None,
582 }
583 }
584
585 #[must_use]
587 pub fn as_rgb16(&self) -> Option<&Rgb16Image> {
588 match *self {
589 DynamicImage::ImageRgb16(ref p) => Some(p),
590 _ => None,
591 }
592 }
593
594 pub fn as_mut_rgb16(&mut self) -> Option<&mut Rgb16Image> {
596 match *self {
597 DynamicImage::ImageRgb16(ref mut p) => Some(p),
598 _ => None,
599 }
600 }
601
602 #[must_use]
604 pub fn as_rgba16(&self) -> Option<&Rgba16Image> {
605 match *self {
606 DynamicImage::ImageRgba16(ref p) => Some(p),
607 _ => None,
608 }
609 }
610
611 pub fn as_mut_rgba16(&mut self) -> Option<&mut Rgba16Image> {
613 match *self {
614 DynamicImage::ImageRgba16(ref mut p) => Some(p),
615 _ => None,
616 }
617 }
618
619 #[must_use]
621 pub fn as_rgb32f(&self) -> Option<&Rgb32FImage> {
622 match *self {
623 DynamicImage::ImageRgb32F(ref p) => Some(p),
624 _ => None,
625 }
626 }
627
628 pub fn as_mut_rgb32f(&mut self) -> Option<&mut Rgb32FImage> {
630 match *self {
631 DynamicImage::ImageRgb32F(ref mut p) => Some(p),
632 _ => None,
633 }
634 }
635
636 #[must_use]
638 pub fn as_rgba32f(&self) -> Option<&Rgba32FImage> {
639 match *self {
640 DynamicImage::ImageRgba32F(ref p) => Some(p),
641 _ => None,
642 }
643 }
644
645 pub fn as_mut_rgba32f(&mut self) -> Option<&mut Rgba32FImage> {
647 match *self {
648 DynamicImage::ImageRgba32F(ref mut p) => Some(p),
649 _ => None,
650 }
651 }
652
653 #[must_use]
655 pub fn as_luma16(&self) -> Option<&Gray16Image> {
656 match *self {
657 DynamicImage::ImageLuma16(ref p) => Some(p),
658 _ => None,
659 }
660 }
661
662 pub fn as_mut_luma16(&mut self) -> Option<&mut Gray16Image> {
664 match *self {
665 DynamicImage::ImageLuma16(ref mut p) => Some(p),
666 _ => None,
667 }
668 }
669
670 #[must_use]
672 pub fn as_luma_alpha16(&self) -> Option<&GrayAlpha16Image> {
673 match *self {
674 DynamicImage::ImageLumaA16(ref p) => Some(p),
675 _ => None,
676 }
677 }
678
679 pub fn as_mut_luma_alpha16(&mut self) -> Option<&mut GrayAlpha16Image> {
681 match *self {
682 DynamicImage::ImageLumaA16(ref mut p) => Some(p),
683 _ => None,
684 }
685 }
686
687 #[must_use]
689 pub fn as_flat_samples_u8(&self) -> Option<FlatSamples<&[u8]>> {
690 match *self {
691 DynamicImage::ImageLuma8(ref p) => Some(p.as_flat_samples()),
692 DynamicImage::ImageLumaA8(ref p) => Some(p.as_flat_samples()),
693 DynamicImage::ImageRgb8(ref p) => Some(p.as_flat_samples()),
694 DynamicImage::ImageRgba8(ref p) => Some(p.as_flat_samples()),
695 _ => None,
696 }
697 }
698
699 #[must_use]
701 pub fn as_flat_samples_u16(&self) -> Option<FlatSamples<&[u16]>> {
702 match *self {
703 DynamicImage::ImageLuma16(ref p) => Some(p.as_flat_samples()),
704 DynamicImage::ImageLumaA16(ref p) => Some(p.as_flat_samples()),
705 DynamicImage::ImageRgb16(ref p) => Some(p.as_flat_samples()),
706 DynamicImage::ImageRgba16(ref p) => Some(p.as_flat_samples()),
707 _ => None,
708 }
709 }
710
711 #[must_use]
713 pub fn as_flat_samples_f32(&self) -> Option<FlatSamples<&[f32]>> {
714 match *self {
715 DynamicImage::ImageRgb32F(ref p) => Some(p.as_flat_samples()),
716 DynamicImage::ImageRgba32F(ref p) => Some(p.as_flat_samples()),
717 _ => None,
718 }
719 }
720
721 #[must_use]
723 pub fn as_bytes(&self) -> &[u8] {
724 dynamic_map!(
726 *self,
727 ref image_buffer,
728 bytemuck::cast_slice(image_buffer.as_raw())
729 )
730 }
731
732 #[must_use]
736 pub fn into_bytes(self) -> Vec<u8> {
737 dynamic_map!(self, image_buffer, {
739 match bytemuck::allocation::try_cast_vec(image_buffer.into_raw()) {
740 Ok(vec) => vec,
741 Err((_, vec)) => {
742 bytemuck::cast_slice(&vec).to_owned()
748 }
749 }
750 })
751 }
752
753 #[must_use]
755 pub fn color(&self) -> color::ColorType {
756 match *self {
757 DynamicImage::ImageLuma8(_) => color::ColorType::L8,
758 DynamicImage::ImageLumaA8(_) => color::ColorType::La8,
759 DynamicImage::ImageRgb8(_) => color::ColorType::Rgb8,
760 DynamicImage::ImageRgba8(_) => color::ColorType::Rgba8,
761 DynamicImage::ImageLuma16(_) => color::ColorType::L16,
762 DynamicImage::ImageLumaA16(_) => color::ColorType::La16,
763 DynamicImage::ImageRgb16(_) => color::ColorType::Rgb16,
764 DynamicImage::ImageRgba16(_) => color::ColorType::Rgba16,
765 DynamicImage::ImageRgb32F(_) => color::ColorType::Rgb32F,
766 DynamicImage::ImageRgba32F(_) => color::ColorType::Rgba32F,
767 }
768 }
769
770 #[must_use]
772 pub fn width(&self) -> u32 {
773 dynamic_map!(*self, ref p, { p.width() })
774 }
775
776 #[must_use]
778 pub fn height(&self) -> u32 {
779 dynamic_map!(*self, ref p, { p.height() })
780 }
781
782 pub fn set_rgb_primaries(&mut self, color: CicpColorPrimaries) {
793 dynamic_map!(self, ref mut p, p.set_rgb_primaries(color));
794 }
795
796 pub fn set_transfer_function(&mut self, tf: CicpTransferCharacteristics) {
803 dynamic_map!(self, ref mut p, p.set_transfer_function(tf));
804 }
805
806 pub fn color_space(&self) -> Cicp {
808 dynamic_map!(self, ref p, p.color_space())
809 }
810
811 pub fn set_color_space(&mut self, cicp: Cicp) -> ImageResult<()> {
816 dynamic_map!(self, ref mut p, p.set_color_space(cicp))
817 }
818
819 #[must_use]
828 pub fn has_alpha(&self) -> bool {
829 self.color().has_alpha()
830 }
831
832 #[must_use]
836 pub fn grayscale(&self) -> DynamicImage {
837 match *self {
838 DynamicImage::ImageLuma8(ref p) => DynamicImage::ImageLuma8(p.clone()),
839 DynamicImage::ImageLumaA8(ref p) => {
840 DynamicImage::ImageLumaA8(imageops::grayscale_alpha(p))
841 }
842 DynamicImage::ImageRgb8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
843 DynamicImage::ImageRgba8(ref p) => {
844 DynamicImage::ImageLumaA8(imageops::grayscale_alpha(p))
845 }
846 DynamicImage::ImageLuma16(ref p) => DynamicImage::ImageLuma16(p.clone()),
847 DynamicImage::ImageLumaA16(ref p) => {
848 DynamicImage::ImageLumaA16(imageops::grayscale_alpha(p))
849 }
850 DynamicImage::ImageRgb16(ref p) => DynamicImage::ImageLuma16(imageops::grayscale(p)),
851 DynamicImage::ImageRgba16(ref p) => {
852 DynamicImage::ImageLumaA16(imageops::grayscale_alpha(p))
853 }
854 DynamicImage::ImageRgb32F(ref p) => {
855 DynamicImage::ImageRgb32F(imageops::grayscale_with_type(p))
856 }
857 DynamicImage::ImageRgba32F(ref p) => {
858 DynamicImage::ImageRgba32F(imageops::grayscale_with_type_alpha(p))
859 }
860 }
861 }
862
863 pub fn invert(&mut self) {
869 dynamic_map!(*self, ref mut p, imageops::invert(p));
870 }
871
872 #[must_use]
880 pub fn resize(&self, nwidth: u32, nheight: u32, filter: imageops::FilterType) -> DynamicImage {
881 if (nwidth, nheight) == self.dimensions() {
882 return self.clone();
883 }
884 let (width2, height2) =
885 resize_dimensions(self.width(), self.height(), nwidth, nheight, false);
886
887 self.resize_exact(width2, height2, filter)
888 }
889
890 #[must_use]
897 pub fn resize_exact(
898 &self,
899 nwidth: u32,
900 nheight: u32,
901 filter: imageops::FilterType,
902 ) -> DynamicImage {
903 dynamic_map!(*self, ref p => imageops::resize(p, nwidth, nheight, filter))
904 }
905
906 #[must_use]
918 pub fn thumbnail(&self, nwidth: u32, nheight: u32) -> DynamicImage {
919 let (width2, height2) =
920 resize_dimensions(self.width(), self.height(), nwidth, nheight, false);
921 self.thumbnail_exact(width2, height2)
922 }
923
924 #[must_use]
934 pub fn thumbnail_exact(&self, nwidth: u32, nheight: u32) -> DynamicImage {
935 dynamic_map!(*self, ref p => imageops::thumbnail(p, nwidth, nheight))
936 }
937
938 #[must_use]
948 pub fn resize_to_fill(
949 &self,
950 nwidth: u32,
951 nheight: u32,
952 filter: imageops::FilterType,
953 ) -> DynamicImage {
954 let (width2, height2) =
955 resize_dimensions(self.width(), self.height(), nwidth, nheight, true);
956
957 let mut intermediate = self.resize_exact(width2, height2, filter);
958 let (iwidth, iheight) = intermediate.dimensions();
959 let ratio = u64::from(iwidth) * u64::from(nheight);
960 let nratio = u64::from(nwidth) * u64::from(iheight);
961
962 if nratio > ratio {
963 intermediate.crop(0, (iheight - nheight) / 2, nwidth, nheight)
964 } else {
965 intermediate.crop((iwidth - nwidth) / 2, 0, nwidth, nheight)
966 }
967 }
968
969 #[must_use]
985 pub fn blur(&self, sigma: f32) -> DynamicImage {
986 gaussian_blur_dyn_image(
987 self,
988 GaussianBlurParameters::new_from_sigma(if sigma == 0.0 { 0.8 } else { sigma }),
989 )
990 }
991
992 #[must_use]
1005 pub fn blur_advanced(&self, parameters: GaussianBlurParameters) -> DynamicImage {
1006 gaussian_blur_dyn_image(self, parameters)
1007 }
1008
1009 #[must_use]
1021 pub fn fast_blur(&self, sigma: f32) -> DynamicImage {
1022 dynamic_map!(*self, ref p => imageops::fast_blur(p, sigma))
1023 }
1024
1025 #[must_use]
1039 pub fn unsharpen(&self, sigma: f32, threshold: i32) -> DynamicImage {
1040 dynamic_map!(*self, ref p => imageops::unsharpen(p, sigma, threshold))
1041 }
1042
1043 #[must_use]
1053 pub fn filter3x3(&self, kernel: &[f32]) -> DynamicImage {
1054 assert_eq!(9, kernel.len(), "filter must be 3 x 3");
1055
1056 dynamic_map!(*self, ref p => imageops::filter3x3(p, kernel))
1057 }
1058
1059 #[must_use]
1066 pub fn adjust_contrast(&self, c: f32) -> DynamicImage {
1067 dynamic_map!(*self, ref p => imageops::contrast(p, c))
1068 }
1069
1070 #[must_use]
1077 pub fn brighten(&self, value: i32) -> DynamicImage {
1078 dynamic_map!(*self, ref p => imageops::brighten(p, value))
1079 }
1080
1081 #[must_use]
1089 pub fn huerotate(&self, value: i32) -> DynamicImage {
1090 dynamic_map!(*self, ref p => imageops::huerotate(p, value))
1091 }
1092
1093 #[must_use]
1097 pub fn flipv(&self) -> DynamicImage {
1098 dynamic_map!(*self, ref p => imageops::flip_vertical(p))
1099 }
1100
1101 fn flipv_in_place(&mut self) {
1103 dynamic_map!(*self, ref mut p, imageops::flip_vertical_in_place(p))
1104 }
1105
1106 #[must_use]
1110 pub fn fliph(&self) -> DynamicImage {
1111 dynamic_map!(*self, ref p => imageops::flip_horizontal(p))
1112 }
1113
1114 fn fliph_in_place(&mut self) {
1116 dynamic_map!(*self, ref mut p, imageops::flip_horizontal_in_place(p))
1117 }
1118
1119 #[must_use]
1121 pub fn rotate90(&self) -> DynamicImage {
1122 dynamic_map!(*self, ref p => imageops::rotate90(p))
1123 }
1124
1125 #[must_use]
1129 pub fn rotate180(&self) -> DynamicImage {
1130 dynamic_map!(*self, ref p => imageops::rotate180(p))
1131 }
1132
1133 fn rotate180_in_place(&mut self) {
1135 dynamic_map!(*self, ref mut p, imageops::rotate180_in_place(p))
1136 }
1137
1138 #[must_use]
1140 pub fn rotate270(&self) -> DynamicImage {
1141 dynamic_map!(*self, ref p => imageops::rotate270(p))
1142 }
1143
1144 pub fn apply_orientation(&mut self, orientation: Orientation) {
1167 let image = self;
1168 match orientation {
1169 Orientation::NoTransforms => (),
1170 Orientation::Rotate90 => *image = image.rotate90(),
1171 Orientation::Rotate180 => image.rotate180_in_place(),
1172 Orientation::Rotate270 => *image = image.rotate270(),
1173 Orientation::FlipHorizontal => image.fliph_in_place(),
1174 Orientation::FlipVertical => image.flipv_in_place(),
1175 Orientation::Rotate90FlipH => {
1176 let mut new_image = image.rotate90();
1177 new_image.fliph_in_place();
1178 *image = new_image;
1179 }
1180 Orientation::Rotate270FlipH => {
1181 let mut new_image = image.rotate270();
1182 new_image.fliph_in_place();
1183 *image = new_image;
1184 }
1185 }
1186 }
1187
1188 pub fn copy_from_color_space(
1209 &mut self,
1210 other: &DynamicImage,
1211 mut options: ConvertColorOptions,
1212 ) -> ImageResult<()> {
1213 if self.color_space() == other.color_space() {
1215 dynamic_map!(
1217 self,
1218 ref mut p,
1219 *p = dynamic_map!(other, ref o, o.cast_in_color_space())
1220 );
1221
1222 return Ok(());
1223 }
1224
1225 match (&mut *self, other) {
1229 (DynamicImage::ImageRgb8(img), DynamicImage::ImageRgb8(other)) => {
1231 return img.copy_from_color_space(other, options);
1232 }
1233 (DynamicImage::ImageRgb8(img), DynamicImage::ImageRgba8(other)) => {
1234 return img.copy_from_color_space(other, options);
1235 }
1236 (DynamicImage::ImageRgba8(img), DynamicImage::ImageRgb8(other)) => {
1237 return img.copy_from_color_space(other, options);
1238 }
1239 (DynamicImage::ImageRgba8(img), DynamicImage::ImageRgba8(other)) => {
1240 return img.copy_from_color_space(other, options);
1241 }
1242 (DynamicImage::ImageRgb16(img), DynamicImage::ImageRgb16(other)) => {
1244 return img.copy_from_color_space(other, options);
1245 }
1246 (DynamicImage::ImageRgb16(img), DynamicImage::ImageRgba16(other)) => {
1247 return img.copy_from_color_space(other, options);
1248 }
1249 (DynamicImage::ImageRgba16(img), DynamicImage::ImageRgb16(other)) => {
1250 return img.copy_from_color_space(other, options);
1251 }
1252 (DynamicImage::ImageRgba16(img), DynamicImage::ImageRgba16(other)) => {
1253 return img.copy_from_color_space(other, options);
1254 }
1255 (DynamicImage::ImageRgb32F(img), DynamicImage::ImageRgb32F(other)) => {
1257 return img.copy_from_color_space(other, options);
1258 }
1259 (DynamicImage::ImageRgb32F(img), DynamicImage::ImageRgba32F(other)) => {
1260 return img.copy_from_color_space(other, options);
1261 }
1262 (DynamicImage::ImageRgba32F(img), DynamicImage::ImageRgb32F(other)) => {
1263 return img.copy_from_color_space(other, options);
1264 }
1265 (DynamicImage::ImageRgba32F(img), DynamicImage::ImageRgba32F(other)) => {
1266 return img.copy_from_color_space(other, options);
1267 }
1268 _ => {}
1269 };
1270
1271 let cicp = options.as_transform(other.color_space(), self.color_space())?;
1277 cicp.transform_dynamic(self, other);
1278
1279 Ok(())
1280 }
1281
1282 pub fn apply_color_space(
1297 &mut self,
1298 cicp: Cicp,
1299 options: ConvertColorOptions,
1300 ) -> ImageResult<()> {
1301 if self.color_space() == cicp {
1303 return Ok(());
1304 }
1305
1306 let mut target = self.clone();
1311 target.set_color_space(cicp)?;
1312 target.copy_from_color_space(self, options)?;
1313
1314 *self = target;
1315 Ok(())
1316 }
1317
1318 pub fn convert_color_space(
1332 &mut self,
1333 cicp: Cicp,
1334 options: ConvertColorOptions,
1335 color: color::ColorType,
1336 ) -> ImageResult<()> {
1337 if self.color() == color {
1338 return self.apply_color_space(cicp, options);
1339 }
1340
1341 let rgb = cicp.try_into_rgb()?;
1343 let mut target = DynamicImage::new(self.width(), self.height(), color);
1344 dynamic_map!(target, ref mut p, p.set_rgb_color_space(rgb));
1345 target.copy_from_color_space(self, options)?;
1346
1347 *self = target;
1348 Ok(())
1349 }
1350
1351 fn write_with_encoder_impl<'a>(
1352 &self,
1353 encoder: Box<dyn ImageEncoderBoxed + 'a>,
1354 ) -> ImageResult<()> {
1355 let converted = encoder.make_compatible_img(crate::io::encoder::MethodSealedToImage, self);
1356 let img = converted.as_ref().unwrap_or(self);
1357
1358 encoder.write_image(
1359 img.as_bytes(),
1360 img.width(),
1361 img.height(),
1362 img.color().into(),
1363 )
1364 }
1365
1366 pub fn write_to<W: Write + Seek>(&self, mut w: W, format: ImageFormat) -> ImageResult<()> {
1377 let encoder = encoder_for_format(format, &mut w)?;
1378 self.write_with_encoder_impl(encoder)
1379 }
1380
1381 pub fn write_with_encoder(&self, encoder: impl ImageEncoder) -> ImageResult<()> {
1389 self.write_with_encoder_impl(Box::new(encoder))
1390 }
1391
1392 #[cfg(feature = "std")]
1400 pub fn save<Q>(&self, path: Q) -> ImageResult<()>
1401 where
1402 Q: AsRef<Path>,
1403 {
1404 let format = ImageFormat::from_path(path.as_ref())?;
1405 self.save_with_format(path, format)
1406 }
1407
1408 #[cfg(feature = "std")]
1416 pub fn save_with_format<Q>(&self, path: Q, format: ImageFormat) -> ImageResult<()>
1417 where
1418 Q: AsRef<Path>,
1419 {
1420 let file = &mut std::io::BufWriter::new(File::create(path)?);
1421 let encoder = encoder_for_format(format, file)?;
1422 self.write_with_encoder_impl(encoder)
1423 }
1424}
1425
1426impl From<GrayImage> for DynamicImage {
1427 fn from(image: GrayImage) -> Self {
1428 DynamicImage::ImageLuma8(image)
1429 }
1430}
1431
1432impl From<GrayAlphaImage> for DynamicImage {
1433 fn from(image: GrayAlphaImage) -> Self {
1434 DynamicImage::ImageLumaA8(image)
1435 }
1436}
1437
1438impl From<RgbImage> for DynamicImage {
1439 fn from(image: RgbImage) -> Self {
1440 DynamicImage::ImageRgb8(image)
1441 }
1442}
1443
1444impl From<RgbaImage> for DynamicImage {
1445 fn from(image: RgbaImage) -> Self {
1446 DynamicImage::ImageRgba8(image)
1447 }
1448}
1449
1450impl From<Gray16Image> for DynamicImage {
1451 fn from(image: Gray16Image) -> Self {
1452 DynamicImage::ImageLuma16(image)
1453 }
1454}
1455
1456impl From<GrayAlpha16Image> for DynamicImage {
1457 fn from(image: GrayAlpha16Image) -> Self {
1458 DynamicImage::ImageLumaA16(image)
1459 }
1460}
1461
1462impl From<Rgb16Image> for DynamicImage {
1463 fn from(image: Rgb16Image) -> Self {
1464 DynamicImage::ImageRgb16(image)
1465 }
1466}
1467
1468impl From<Rgba16Image> for DynamicImage {
1469 fn from(image: Rgba16Image) -> Self {
1470 DynamicImage::ImageRgba16(image)
1471 }
1472}
1473
1474impl From<Rgb32FImage> for DynamicImage {
1475 fn from(image: Rgb32FImage) -> Self {
1476 DynamicImage::ImageRgb32F(image)
1477 }
1478}
1479
1480impl From<Rgba32FImage> for DynamicImage {
1481 fn from(image: Rgba32FImage) -> Self {
1482 DynamicImage::ImageRgba32F(image)
1483 }
1484}
1485
1486impl From<ImageBuffer<Luma<f32>, Vec<f32>>> for DynamicImage {
1487 fn from(image: ImageBuffer<Luma<f32>, Vec<f32>>) -> Self {
1488 DynamicImage::ImageRgb32F(image.convert())
1489 }
1490}
1491
1492impl From<ImageBuffer<LumaA<f32>, Vec<f32>>> for DynamicImage {
1493 fn from(image: ImageBuffer<LumaA<f32>, Vec<f32>>) -> Self {
1494 DynamicImage::ImageRgba32F(image.convert())
1495 }
1496}
1497
1498#[allow(deprecated)]
1499impl GenericImageView for DynamicImage {
1500 type Pixel = color::Rgba<u8>; fn dimensions(&self) -> (u32, u32) {
1503 dynamic_map!(*self, ref p, p.dimensions())
1504 }
1505
1506 fn get_pixel(&self, x: u32, y: u32) -> color::Rgba<u8> {
1507 dynamic_map!(*self, ref p, p.get_pixel(x, y).to_rgba().into_color())
1508 }
1509}
1510
1511#[allow(deprecated)]
1512impl GenericImage for DynamicImage {
1513 fn put_pixel(&mut self, x: u32, y: u32, pixel: color::Rgba<u8>) {
1514 match *self {
1515 DynamicImage::ImageLuma8(ref mut p) => p.put_pixel(x, y, pixel.to_luma()),
1516 DynamicImage::ImageLumaA8(ref mut p) => p.put_pixel(x, y, pixel.to_luma_alpha()),
1517 DynamicImage::ImageRgb8(ref mut p) => p.put_pixel(x, y, pixel.to_rgb()),
1518 DynamicImage::ImageRgba8(ref mut p) => p.put_pixel(x, y, pixel),
1519 DynamicImage::ImageLuma16(ref mut p) => p.put_pixel(x, y, pixel.to_luma().into_color()),
1520 DynamicImage::ImageLumaA16(ref mut p) => {
1521 p.put_pixel(x, y, pixel.to_luma_alpha().into_color());
1522 }
1523 DynamicImage::ImageRgb16(ref mut p) => p.put_pixel(x, y, pixel.to_rgb().into_color()),
1524 DynamicImage::ImageRgba16(ref mut p) => p.put_pixel(x, y, pixel.into_color()),
1525 DynamicImage::ImageRgb32F(ref mut p) => p.put_pixel(x, y, pixel.to_rgb().into_color()),
1526 DynamicImage::ImageRgba32F(ref mut p) => p.put_pixel(x, y, pixel.into_color()),
1527 }
1528 }
1529
1530 fn blend_pixel(&mut self, x: u32, y: u32, pixel: color::Rgba<u8>) {
1531 match *self {
1532 DynamicImage::ImageLuma8(ref mut p) => p.blend_pixel(x, y, pixel.to_luma()),
1533 DynamicImage::ImageLumaA8(ref mut p) => p.blend_pixel(x, y, pixel.to_luma_alpha()),
1534 DynamicImage::ImageRgb8(ref mut p) => p.blend_pixel(x, y, pixel.to_rgb()),
1535 DynamicImage::ImageRgba8(ref mut p) => p.blend_pixel(x, y, pixel),
1536 DynamicImage::ImageLuma16(ref mut p) => {
1537 p.blend_pixel(x, y, pixel.to_luma().into_color());
1538 }
1539 DynamicImage::ImageLumaA16(ref mut p) => {
1540 p.blend_pixel(x, y, pixel.to_luma_alpha().into_color());
1541 }
1542 DynamicImage::ImageRgb16(ref mut p) => p.blend_pixel(x, y, pixel.to_rgb().into_color()),
1543 DynamicImage::ImageRgba16(ref mut p) => p.blend_pixel(x, y, pixel.into_color()),
1544 DynamicImage::ImageRgb32F(ref mut p) => {
1545 p.blend_pixel(x, y, pixel.to_rgb().into_color());
1546 }
1547 DynamicImage::ImageRgba32F(ref mut p) => p.blend_pixel(x, y, pixel.into_color()),
1548 }
1549 }
1550
1551 fn get_pixel_mut(&mut self, _: u32, _: u32) -> &mut color::Rgba<u8> {
1553 unimplemented!()
1554 }
1555}
1556
1557impl Default for DynamicImage {
1558 fn default() -> Self {
1559 Self::ImageRgba8(Default::default())
1560 }
1561}
1562
1563fn decoder_to_image<I: ImageDecoder>(decoder: I) -> ImageResult<DynamicImage> {
1565 let (w, h) = decoder.dimensions();
1566 let color_type = decoder.color_type();
1567
1568 let mut image = match color_type {
1569 color::ColorType::Rgb8 => {
1570 let buf = free_functions::decoder_to_vec(decoder)?;
1571 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb8)
1572 }
1573
1574 color::ColorType::Rgba8 => {
1575 let buf = free_functions::decoder_to_vec(decoder)?;
1576 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba8)
1577 }
1578
1579 color::ColorType::L8 => {
1580 let buf = free_functions::decoder_to_vec(decoder)?;
1581 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLuma8)
1582 }
1583
1584 color::ColorType::La8 => {
1585 let buf = free_functions::decoder_to_vec(decoder)?;
1586 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLumaA8)
1587 }
1588
1589 color::ColorType::Rgb16 => {
1590 let buf = free_functions::decoder_to_vec(decoder)?;
1591 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb16)
1592 }
1593
1594 color::ColorType::Rgba16 => {
1595 let buf = free_functions::decoder_to_vec(decoder)?;
1596 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba16)
1597 }
1598
1599 color::ColorType::Rgb32F => {
1600 let buf = free_functions::decoder_to_vec(decoder)?;
1601 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb32F)
1602 }
1603
1604 color::ColorType::Rgba32F => {
1605 let buf = free_functions::decoder_to_vec(decoder)?;
1606 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba32F)
1607 }
1608
1609 color::ColorType::L16 => {
1610 let buf = free_functions::decoder_to_vec(decoder)?;
1611 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLuma16)
1612 }
1613
1614 color::ColorType::La16 => {
1615 let buf = free_functions::decoder_to_vec(decoder)?;
1616 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLumaA16)
1617 }
1618 }
1619 .ok_or_else(|| {
1620 ImageError::Parameter(ParameterError::from_kind(
1621 ParameterErrorKind::DimensionMismatch,
1622 ))
1623 })?;
1624
1625 image.set_rgb_primaries(Cicp::SRGB.primaries);
1629 image.set_transfer_function(Cicp::SRGB.transfer);
1630
1631 Ok(image)
1632}
1633
1634#[cfg(feature = "std")]
1640pub fn open<P>(path: P) -> ImageResult<DynamicImage>
1641where
1642 P: AsRef<Path>,
1643{
1644 ImageReader::open(path)?.decode()
1645}
1646
1647#[cfg(feature = "std")]
1653pub fn image_dimensions<P>(path: P) -> ImageResult<(u32, u32)>
1654where
1655 P: AsRef<Path>,
1656{
1657 ImageReader::open(path)?.into_dimensions()
1658}
1659
1660pub fn write_buffer_with_format<W: Write + Seek>(
1668 buffered_writer: &mut W,
1669 buf: &[u8],
1670 width: u32,
1671 height: u32,
1672 color: impl Into<ExtendedColorType>,
1673 format: ImageFormat,
1674) -> ImageResult<()> {
1675 let encoder = encoder_for_format(format, buffered_writer)?;
1676 encoder.write_image(buf, width, height, color.into())
1677}
1678
1679#[cfg(feature = "std")]
1686pub fn load_from_memory(buffer: &[u8]) -> ImageResult<DynamicImage> {
1687 ImageReader::new(std::io::Cursor::new(buffer))
1688 .with_guessed_format()?
1689 .decode()
1690}
1691
1692#[cfg(feature = "std")]
1701#[inline(always)]
1702pub fn load_from_memory_with_format(buf: &[u8], format: ImageFormat) -> ImageResult<DynamicImage> {
1703 let b = no_std_io::io::Cursor::new(buf);
1710 free_functions::load(b, format)
1711}
1712
1713#[cfg(test)]
1714mod bench {
1715 #[bench]
1716 #[cfg(feature = "benchmarks")]
1717 fn bench_conversion(b: &mut test::Bencher) {
1718 let a = super::DynamicImage::ImageRgb8(crate::ImageBuffer::new(1000, 1000));
1719 b.iter(|| a.to_luma8());
1720 b.bytes = 1000 * 1000 * 3;
1721 }
1722}
1723
1724#[cfg(test)]
1725mod test {
1726 use crate::metadata::{CicpColorPrimaries, CicpTransform};
1727 use crate::ConvertColorOptions;
1728 use crate::{color::ColorType, images::dynimage::Gray16Image};
1729 use crate::{metadata::Cicp, ImageBuffer, Luma, Rgb, Rgba};
1730
1731 #[test]
1732 fn test_empty_file() {
1733 assert!(super::load_from_memory(b"").is_err());
1734 }
1735
1736 #[cfg(feature = "jpeg")]
1737 #[test]
1738 fn image_dimensions() {
1739 let im_path = "./tests/images/jpg/progressive/cat.jpg";
1740 let dims = super::image_dimensions(im_path).unwrap();
1741 assert_eq!(dims, (320, 240));
1742 }
1743
1744 #[cfg(feature = "png")]
1745 #[test]
1746 fn open_16bpc_png() {
1747 let im_path = "./tests/images/png/16bpc/basn6a16.png";
1748 let image = super::open(im_path).unwrap();
1749 assert_eq!(image.color(), ColorType::Rgba16);
1750 }
1751
1752 fn test_grayscale(mut img: super::DynamicImage, alpha_discarded: bool) {
1753 use crate::{GenericImage as _, GenericImageView as _};
1754 img.put_pixel(0, 0, Rgba([255, 0, 0, 100]));
1755 let expected_alpha = if alpha_discarded { 255 } else { 100 };
1756 assert_eq!(
1757 img.grayscale().get_pixel(0, 0),
1758 Rgba([54, 54, 54, expected_alpha])
1759 );
1760 }
1761
1762 fn test_grayscale_alpha_discarded(img: super::DynamicImage) {
1763 test_grayscale(img, true);
1764 }
1765
1766 fn test_grayscale_alpha_preserved(img: super::DynamicImage) {
1767 test_grayscale(img, false);
1768 }
1769
1770 #[test]
1771 fn test_grayscale_luma8() {
1772 test_grayscale_alpha_discarded(super::DynamicImage::new_luma8(1, 1));
1773 test_grayscale_alpha_discarded(super::DynamicImage::new(1, 1, ColorType::L8));
1774 }
1775
1776 #[test]
1777 fn test_grayscale_luma_a8() {
1778 test_grayscale_alpha_preserved(super::DynamicImage::new_luma_a8(1, 1));
1779 test_grayscale_alpha_preserved(super::DynamicImage::new(1, 1, ColorType::La8));
1780 }
1781
1782 #[test]
1783 fn test_grayscale_rgb8() {
1784 test_grayscale_alpha_discarded(super::DynamicImage::new_rgb8(1, 1));
1785 test_grayscale_alpha_discarded(super::DynamicImage::new(1, 1, ColorType::Rgb8));
1786 }
1787
1788 #[test]
1789 fn test_grayscale_rgba8() {
1790 test_grayscale_alpha_preserved(super::DynamicImage::new_rgba8(1, 1));
1791 test_grayscale_alpha_preserved(super::DynamicImage::new(1, 1, ColorType::Rgba8));
1792 }
1793
1794 #[test]
1795 fn test_grayscale_luma16() {
1796 test_grayscale_alpha_discarded(super::DynamicImage::new_luma16(1, 1));
1797 test_grayscale_alpha_discarded(super::DynamicImage::new(1, 1, ColorType::L16));
1798 }
1799
1800 #[test]
1801 fn test_grayscale_luma_a16() {
1802 test_grayscale_alpha_preserved(super::DynamicImage::new_luma_a16(1, 1));
1803 test_grayscale_alpha_preserved(super::DynamicImage::new(1, 1, ColorType::La16));
1804 }
1805
1806 #[test]
1807 fn test_grayscale_rgb16() {
1808 test_grayscale_alpha_discarded(super::DynamicImage::new_rgb16(1, 1));
1809 test_grayscale_alpha_discarded(super::DynamicImage::new(1, 1, ColorType::Rgb16));
1810 }
1811
1812 #[test]
1813 fn test_grayscale_rgba16() {
1814 test_grayscale_alpha_preserved(super::DynamicImage::new_rgba16(1, 1));
1815 test_grayscale_alpha_preserved(super::DynamicImage::new(1, 1, ColorType::Rgba16));
1816 }
1817
1818 #[test]
1819 fn test_grayscale_rgb32f() {
1820 test_grayscale_alpha_discarded(super::DynamicImage::new_rgb32f(1, 1));
1821 test_grayscale_alpha_discarded(super::DynamicImage::new(1, 1, ColorType::Rgb32F));
1822 }
1823
1824 #[test]
1825 fn test_grayscale_rgba32f() {
1826 test_grayscale_alpha_preserved(super::DynamicImage::new_rgba32f(1, 1));
1827 test_grayscale_alpha_preserved(super::DynamicImage::new(1, 1, ColorType::Rgba32F));
1828 }
1829
1830 #[test]
1831 fn test_dynamic_image_default_implementation() {
1832 #[derive(Default)]
1835 #[allow(dead_code)]
1836 struct Foo {
1837 _image: super::DynamicImage,
1838 }
1839 }
1840
1841 #[test]
1842 fn test_to_vecu8() {
1843 let _ = super::DynamicImage::new_luma8(1, 1).into_bytes();
1844 let _ = super::DynamicImage::new_luma16(1, 1).into_bytes();
1845 }
1846
1847 #[test]
1848 fn issue_1705_can_turn_16bit_image_into_bytes() {
1849 let pixels = vec![65535u16; 64 * 64];
1850 let img = ImageBuffer::from_vec(64, 64, pixels).unwrap();
1851
1852 let img = super::DynamicImage::ImageLuma16(img);
1853 assert!(img.as_luma16().is_some());
1854
1855 let bytes: Vec<u8> = img.into_bytes();
1856 assert_eq!(bytes, vec![0xFF; 64 * 64 * 2]);
1857 }
1858
1859 #[test]
1860 fn test_convert_to() {
1861 use crate::Luma;
1862 let image_luma8 = super::DynamicImage::new_luma8(1, 1);
1863 let image_luma16 = super::DynamicImage::new_luma16(1, 1);
1864 assert_eq!(image_luma8.to_luma16(), image_luma16.to_luma16());
1865
1866 let conv: Gray16Image = image_luma8.to();
1868 assert_eq!(image_luma8.to_luma16(), conv);
1869
1870 let converted = image_luma8.to::<Luma<u16>>();
1872 assert_eq!(image_luma8.to_luma16(), converted);
1873 }
1874
1875 #[test]
1876 fn color_conversion_srgb_p3() {
1877 let mut source = super::DynamicImage::ImageRgb8({
1878 ImageBuffer::from_fn(128, 128, |_, _| Rgb([255, 0, 0]))
1879 });
1880
1881 let mut target = super::DynamicImage::ImageRgba8({
1882 ImageBuffer::from_fn(128, 128, |_, _| Rgba(Default::default()))
1883 });
1884
1885 source.set_rgb_primaries(Cicp::SRGB.primaries);
1886 source.set_transfer_function(Cicp::SRGB.transfer);
1887 target.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
1888 target.set_transfer_function(Cicp::DISPLAY_P3.transfer);
1889
1890 let result = target.copy_from_color_space(&source, Default::default());
1891
1892 assert!(result.is_ok(), "{result:?}");
1893 let target = target.as_rgba8().expect("Sample type unchanged");
1894 assert_eq!(target[(0, 0)], Rgba([234u8, 51, 35, 255]));
1895 }
1896
1897 #[test]
1898 fn color_conversion_preserves_sample() {
1899 let mut source = super::DynamicImage::ImageRgb16({
1900 ImageBuffer::from_fn(128, 128, |_, _| Rgb([u16::MAX, 0, 0]))
1901 });
1902
1903 let mut target = super::DynamicImage::ImageRgba8({
1904 ImageBuffer::from_fn(128, 128, |_, _| Rgba(Default::default()))
1905 });
1906
1907 source.set_rgb_primaries(Cicp::SRGB.primaries);
1908 source.set_transfer_function(Cicp::SRGB.transfer);
1909 target.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
1910 target.set_transfer_function(Cicp::DISPLAY_P3.transfer);
1911
1912 let result = target.copy_from_color_space(&source, Default::default());
1913
1914 assert!(result.is_ok(), "{result:?}");
1915 let target = target.as_rgba8().expect("Sample type unchanged");
1916 assert_eq!(target[(0, 0)], Rgba([234u8, 51, 35, 255]));
1917 }
1918
1919 #[test]
1920 fn color_conversion_preserves_sample_in_fastpath() {
1921 let source = super::DynamicImage::ImageRgb16({
1922 ImageBuffer::from_fn(128, 128, |_, _| Rgb([u16::MAX, 0, 0]))
1923 });
1924
1925 let mut target = super::DynamicImage::ImageRgba8({
1926 ImageBuffer::from_fn(128, 128, |_, _| Rgba(Default::default()))
1927 });
1928
1929 let result = target.copy_from_color_space(&source, Default::default());
1931
1932 assert!(result.is_ok(), "{result:?}");
1933 let target = target.as_rgba8().expect("Sample type unchanged");
1934 assert_eq!(target[(0, 0)], Rgba([255u8, 0, 0, 255]));
1935 }
1936
1937 #[test]
1938 fn color_conversion_rgb_to_luma() {
1939 let source = super::DynamicImage::ImageRgb16({
1940 ImageBuffer::from_fn(128, 128, |_, _| Rgb([0, u16::MAX, 0]))
1941 });
1942
1943 let mut target = super::DynamicImage::ImageLuma8({
1944 ImageBuffer::from_fn(128, 128, |_, _| Luma(Default::default()))
1945 });
1946
1947 let result = target.copy_from_color_space(&source, Default::default());
1949
1950 assert!(result.is_ok(), "{result:?}");
1951 target.as_luma8().expect("Sample type unchanged");
1953 }
1954
1955 #[test]
1956 fn copy_color_space_coverage() {
1957 const TYPES: [ColorType; 10] = [
1958 ColorType::L8,
1959 ColorType::La8,
1960 ColorType::Rgb8,
1961 ColorType::Rgba8,
1962 ColorType::L16,
1963 ColorType::La16,
1964 ColorType::Rgb16,
1965 ColorType::Rgba16,
1966 ColorType::Rgb32F,
1967 ColorType::Rgba32F,
1968 ];
1969
1970 let transform =
1971 CicpTransform::new(Cicp::SRGB, Cicp::DISPLAY_P3).expect("Failed to create transform");
1972
1973 for from in TYPES {
1974 for to in TYPES {
1975 let mut source = super::DynamicImage::new(16, 16, from);
1976 let mut target = super::DynamicImage::new(16, 16, to);
1977
1978 source.set_rgb_primaries(Cicp::SRGB.primaries);
1979 source.set_transfer_function(Cicp::SRGB.transfer);
1980
1981 target.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
1982 target.set_transfer_function(Cicp::DISPLAY_P3.transfer);
1983
1984 target
1985 .copy_from_color_space(
1986 &source,
1987 ConvertColorOptions {
1988 transform: Some(transform.clone()),
1989 ..Default::default()
1990 },
1991 )
1992 .expect("Failed to convert color space");
1993 }
1994 }
1995 }
1996
1997 #[test]
1998 fn apply_color_space() {
1999 let mut buffer = super::DynamicImage::ImageRgb8({
2000 ImageBuffer::from_fn(128, 128, |_, _| Rgb([u8::MAX, 0, 0]))
2001 });
2002
2003 buffer.set_rgb_primaries(Cicp::SRGB.primaries);
2004 buffer.set_transfer_function(Cicp::SRGB.transfer);
2005
2006 buffer
2007 .apply_color_space(Cicp::DISPLAY_P3, Default::default())
2008 .unwrap();
2009
2010 let target = buffer.as_rgb8().expect("Sample type unchanged");
2011 assert_eq!(target[(0, 0)], Rgb([234u8, 51, 35]));
2012 }
2013
2014 #[test]
2015 fn apply_color_space_coverage() {
2016 const TYPES: [ColorType; 10] = [
2017 ColorType::L8,
2018 ColorType::La8,
2019 ColorType::Rgb8,
2020 ColorType::Rgba8,
2021 ColorType::L16,
2022 ColorType::La16,
2023 ColorType::Rgb16,
2024 ColorType::Rgba16,
2025 ColorType::Rgb32F,
2026 ColorType::Rgba32F,
2027 ];
2028
2029 let transform =
2030 CicpTransform::new(Cicp::SRGB, Cicp::DISPLAY_P3).expect("Failed to create transform");
2031
2032 for buffer in TYPES {
2033 let mut buffer = super::DynamicImage::new(16, 16, buffer);
2034
2035 buffer.set_rgb_primaries(Cicp::SRGB.primaries);
2036 buffer.set_transfer_function(Cicp::SRGB.transfer);
2037
2038 buffer
2039 .apply_color_space(
2040 Cicp::DISPLAY_P3,
2041 ConvertColorOptions {
2042 transform: Some(transform.clone()),
2043 ..Default::default()
2044 },
2045 )
2046 .expect("Failed to convert color space");
2047 }
2048 }
2049
2050 #[test]
2051 fn convert_color_space() {
2052 let mut buffer = super::DynamicImage::ImageRgb16({
2053 ImageBuffer::from_fn(128, 128, |_, _| Rgb([u16::MAX, 0, 0]))
2054 });
2055
2056 buffer.set_rgb_primaries(Cicp::SRGB.primaries);
2057 buffer.set_transfer_function(Cicp::SRGB.transfer);
2058
2059 buffer
2060 .convert_color_space(Cicp::DISPLAY_P3, Default::default(), ColorType::Rgb8)
2061 .unwrap();
2062
2063 let target = buffer.as_rgb8().expect("Sample type now rgb8");
2064 assert_eq!(target[(0, 0)], Rgb([234u8, 51, 35]));
2065 }
2066
2067 #[test]
2068 fn into_luma_is_color_space_aware() {
2069 let mut buffer = super::DynamicImage::ImageRgb16({
2070 ImageBuffer::from_fn(128, 128, |_, _| Rgb([u16::MAX, 0, 0]))
2071 });
2072
2073 buffer.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
2074 buffer.set_transfer_function(Cicp::DISPLAY_P3.transfer);
2075
2076 let luma8 = buffer.clone().into_luma8();
2077 assert_eq!(luma8[(0, 0)], Luma([58u8]));
2078 assert_eq!(luma8.color_space(), Cicp::DISPLAY_P3);
2079
2080 buffer.set_rgb_primaries(Cicp::SRGB.primaries);
2081
2082 let luma8 = buffer.clone().into_luma8();
2083 assert_eq!(luma8[(0, 0)], Luma([54u8]));
2084 assert_ne!(luma8.color_space(), Cicp::DISPLAY_P3);
2085 }
2086
2087 #[test]
2088 fn from_luma_is_color_space_aware() {
2089 let mut buffer = super::DynamicImage::ImageLuma16({
2090 ImageBuffer::from_fn(128, 128, |_, _| Luma([u16::MAX]))
2091 });
2092
2093 buffer.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
2094 buffer.set_transfer_function(Cicp::DISPLAY_P3.transfer);
2095
2096 let rgb8 = buffer.clone().into_rgb8();
2097 assert_eq!(rgb8[(0, 0)], Rgb([u8::MAX; 3]));
2098 assert_eq!(rgb8.color_space(), Cicp::DISPLAY_P3);
2099
2100 buffer.set_rgb_primaries(Cicp::SRGB.primaries);
2101
2102 let rgb8 = buffer.clone().into_rgb8();
2103 assert_eq!(rgb8[(0, 0)], Rgb([u8::MAX; 3]));
2104 assert_ne!(rgb8.color_space(), Cicp::DISPLAY_P3);
2105 }
2106
2107 #[test]
2108 fn from_luma_for_all_chromaticities() {
2109 const CHROMA: &[CicpColorPrimaries] = &[
2110 (CicpColorPrimaries::SRgb),
2111 (CicpColorPrimaries::RgbM),
2112 (CicpColorPrimaries::RgbB),
2113 (CicpColorPrimaries::Bt601),
2114 (CicpColorPrimaries::Rgb240m),
2115 (CicpColorPrimaries::GenericFilm),
2116 (CicpColorPrimaries::Rgb2020),
2117 (CicpColorPrimaries::Xyz),
2119 (CicpColorPrimaries::SmpteRp431),
2120 (CicpColorPrimaries::SmpteRp432),
2121 (CicpColorPrimaries::Industry22),
2122 (CicpColorPrimaries::Unspecified),
2124 ];
2125
2126 let mut buffer = super::DynamicImage::ImageLuma16({
2127 ImageBuffer::from_fn(128, 128, |_, _| Luma([u16::MAX]))
2128 });
2129
2130 for &chroma in CHROMA {
2131 buffer.set_rgb_primaries(chroma);
2132 let rgb = buffer.to_rgb8();
2133 assert_eq!(
2134 rgb[(0, 0)],
2135 Rgb([u8::MAX; 3]),
2136 "Failed for chroma: {chroma:?}"
2137 );
2138 }
2139 }
2140
2141 #[test]
2142 fn from_rgb_for_all_chromaticities() {
2143 const CHROMA: &[(CicpColorPrimaries, [u8; 3], u8)] = &[
2146 (CicpColorPrimaries::SRgb, [54, 182, 18], 143),
2147 (CicpColorPrimaries::RgbM, [76, 150, 29], 114),
2148 (CicpColorPrimaries::RgbB, [57, 180, 18], 141),
2149 (CicpColorPrimaries::Bt601, [54, 179, 22], 139),
2150 (CicpColorPrimaries::Rgb240m, [54, 179, 22], 139),
2151 (CicpColorPrimaries::GenericFilm, [65, 173, 17], 135),
2152 (CicpColorPrimaries::Rgb2020, [67, 173, 15], 136),
2153 (CicpColorPrimaries::Xyz, [0, 255, 0], 255),
2155 (CicpColorPrimaries::SmpteRp431, [53, 184, 18], 145),
2156 (CicpColorPrimaries::SmpteRp432, [58, 176, 20], 137),
2157 (CicpColorPrimaries::Industry22, [59, 171, 24], 131),
2158 (CicpColorPrimaries::Unspecified, [54, 182, 18], 143),
2160 ];
2161
2162 let mut buffer = super::DynamicImage::ImageRgb8({
2163 ImageBuffer::from_fn(128, 128, |_, _| Rgb([u8::MAX; 3]))
2164 });
2165
2166 for &(chroma, rgb, luma) in CHROMA {
2167 buffer.set_rgb_primaries(chroma);
2168
2169 for px in buffer.as_mut_rgb8().unwrap().pixels_mut() {
2170 px.0 = rgb;
2171 }
2172
2173 let buf = buffer.to_luma8();
2174 assert_eq!(buf[(0, 0)], Luma([luma]), "Failed for chroma: {chroma:?}");
2175 }
2176 }
2177
2178 #[test]
2179 fn convert_color_space_coverage() {
2180 const TYPES: [ColorType; 10] = [
2181 ColorType::L8,
2182 ColorType::La8,
2183 ColorType::Rgb8,
2184 ColorType::Rgba8,
2185 ColorType::L16,
2186 ColorType::La16,
2187 ColorType::Rgb16,
2188 ColorType::Rgba16,
2189 ColorType::Rgb32F,
2190 ColorType::Rgba32F,
2191 ];
2192
2193 let transform =
2194 CicpTransform::new(Cicp::SRGB, Cicp::DISPLAY_P3).expect("Failed to create transform");
2195
2196 for from in TYPES {
2197 for to in TYPES {
2198 let mut buffer = super::DynamicImage::new(16, 16, from);
2199
2200 buffer.set_rgb_primaries(Cicp::SRGB.primaries);
2201 buffer.set_transfer_function(Cicp::SRGB.transfer);
2202
2203 let options = ConvertColorOptions {
2204 transform: Some(transform.clone()),
2205 ..Default::default()
2206 };
2207
2208 buffer
2209 .convert_color_space(Cicp::DISPLAY_P3, options, to)
2210 .expect("Failed to convert color space");
2211 }
2212 }
2213 }
2214
2215 #[cfg(feature = "png")]
2218 #[test]
2219 fn color_space_independent_imageops() {
2220 let im_path = "./tests/images/png/16bpc/basn6a16.png";
2221
2222 let mut image = super::open(im_path).unwrap();
2223 let mut clone = image.clone();
2224
2225 image.set_color_space(Cicp::SRGB).unwrap();
2226 clone.set_color_space(Cicp::DISPLAY_P3).unwrap();
2227
2228 const IMAGEOPS: &[&dyn Fn(&super::DynamicImage) -> super::DynamicImage] = &[
2229 &|img| img.resize(32, 32, crate::imageops::FilterType::Lanczos3),
2230 &|img| img.resize_exact(32, 32, crate::imageops::FilterType::Lanczos3),
2231 &|img| img.thumbnail(8, 8),
2232 &|img| img.thumbnail_exact(8, 8),
2233 &|img| img.resize_to_fill(32, 32, crate::imageops::FilterType::Lanczos3),
2234 &|img| img.blur(1.0),
2235 &|img| {
2236 img.blur_advanced(
2237 crate::imageops::GaussianBlurParameters::new_anisotropic_kernel_size(1.0, 2.0),
2238 )
2239 },
2240 &|img| img.fast_blur(1.0),
2241 &|img| img.unsharpen(1.0, 3),
2242 &|img| img.filter3x3(&[0.0, -1.0, 0.0, -1.0, 5.0, -1.0, 0.0, -1.0, 0.0]),
2243 &|img| img.adjust_contrast(0.5),
2244 &|img| img.brighten(10),
2245 &|img| img.huerotate(180),
2246 ];
2247
2248 for (idx, &op) in IMAGEOPS.iter().enumerate() {
2249 let result_a = op(&image);
2250 let result_b = op(&clone);
2251 assert_eq!(result_a.color_space(), image.color_space(), "{idx}");
2252 assert_eq!(result_b.color_space(), clone.color_space(), "{idx}");
2253
2254 assert_ne!(result_a, result_b, "{idx}");
2255 assert_eq!(result_a.as_bytes(), result_b.as_bytes(), "{idx}");
2256 }
2257 }
2258}