1use std::fs::File;
2use std::io::{self, BufWriter, Seek, Write};
3use std::path::Path;
4
5use crate::color::{self, FromColor, IntoColor};
6use crate::error::{ImageError, ImageResult, ParameterError, ParameterErrorKind};
7use crate::flat::FlatSamples;
8use crate::imageops::{gaussian_blur_dyn_image, GaussianBlurParameters};
9use crate::images::buffer::{
10 ConvertBuffer, Gray16Image, GrayAlpha16Image, GrayAlphaImage, GrayImage, ImageBuffer,
11 Rgb16Image, Rgb32FImage, RgbImage, Rgba16Image, Rgba32FImage, RgbaImage,
12};
13use crate::io::encoder::ImageEncoderBoxed;
14use crate::io::free_functions::{self, encoder_for_format};
15use crate::math::resize_dimensions;
16use crate::metadata::Orientation;
17use crate::traits::Pixel;
18use crate::{
19 imageops,
20 metadata::{Cicp, CicpColorPrimaries, CicpTransferCharacteristics},
21 ConvertColorOptions, ExtendedColorType, GenericImage, GenericImageView, ImageDecoder,
22 ImageEncoder, ImageFormat, ImageReader, Luma, LumaA,
23};
24
25#[derive(Debug, PartialEq)]
70#[non_exhaustive]
71pub enum DynamicImage {
72 ImageLuma8(GrayImage),
74
75 ImageLumaA8(GrayAlphaImage),
77
78 ImageRgb8(RgbImage),
80
81 ImageRgba8(RgbaImage),
83
84 ImageLuma16(Gray16Image),
86
87 ImageLumaA16(GrayAlpha16Image),
89
90 ImageRgb16(Rgb16Image),
92
93 ImageRgba16(Rgba16Image),
95
96 ImageRgb32F(Rgb32FImage),
98
99 ImageRgba32F(Rgba32FImage),
101}
102
103macro_rules! dynamic_map(
104 ($dynimage: expr, $image: pat => $action: expr) => ({
105 use DynamicImage::*;
106 match $dynimage {
107 ImageLuma8($image) => ImageLuma8($action),
108 ImageLumaA8($image) => ImageLumaA8($action),
109 ImageRgb8($image) => ImageRgb8($action),
110 ImageRgba8($image) => ImageRgba8($action),
111 ImageLuma16($image) => ImageLuma16($action),
112 ImageLumaA16($image) => ImageLumaA16($action),
113 ImageRgb16($image) => ImageRgb16($action),
114 ImageRgba16($image) => ImageRgba16($action),
115 ImageRgb32F($image) => ImageRgb32F($action),
116 ImageRgba32F($image) => ImageRgba32F($action),
117 }
118 });
119
120 ($dynimage: expr, $image:pat_param, $action: expr) => (
121 match $dynimage {
122 DynamicImage::ImageLuma8($image) => $action,
123 DynamicImage::ImageLumaA8($image) => $action,
124 DynamicImage::ImageRgb8($image) => $action,
125 DynamicImage::ImageRgba8($image) => $action,
126 DynamicImage::ImageLuma16($image) => $action,
127 DynamicImage::ImageLumaA16($image) => $action,
128 DynamicImage::ImageRgb16($image) => $action,
129 DynamicImage::ImageRgba16($image) => $action,
130 DynamicImage::ImageRgb32F($image) => $action,
131 DynamicImage::ImageRgba32F($image) => $action,
132 }
133 );
134);
135
136impl Clone for DynamicImage {
137 fn clone(&self) -> Self {
138 dynamic_map!(*self, ref p, DynamicImage::from(p.clone()))
139 }
140
141 fn clone_from(&mut self, source: &Self) {
142 match (self, source) {
143 (Self::ImageLuma8(p1), Self::ImageLuma8(p2)) => p1.clone_from(p2),
144 (Self::ImageLumaA8(p1), Self::ImageLumaA8(p2)) => p1.clone_from(p2),
145 (Self::ImageRgb8(p1), Self::ImageRgb8(p2)) => p1.clone_from(p2),
146 (Self::ImageRgba8(p1), Self::ImageRgba8(p2)) => p1.clone_from(p2),
147 (Self::ImageLuma16(p1), Self::ImageLuma16(p2)) => p1.clone_from(p2),
148 (Self::ImageLumaA16(p1), Self::ImageLumaA16(p2)) => p1.clone_from(p2),
149 (Self::ImageRgb16(p1), Self::ImageRgb16(p2)) => p1.clone_from(p2),
150 (Self::ImageRgba16(p1), Self::ImageRgba16(p2)) => p1.clone_from(p2),
151 (Self::ImageRgb32F(p1), Self::ImageRgb32F(p2)) => p1.clone_from(p2),
152 (Self::ImageRgba32F(p1), Self::ImageRgba32F(p2)) => p1.clone_from(p2),
153 (this, source) => *this = source.clone(),
154 }
155 }
156}
157
158impl DynamicImage {
159 #[must_use]
164 pub fn new(w: u32, h: u32, color: color::ColorType) -> DynamicImage {
165 use color::ColorType::*;
166 match color {
167 L8 => Self::new_luma8(w, h),
168 La8 => Self::new_luma_a8(w, h),
169 Rgb8 => Self::new_rgb8(w, h),
170 Rgba8 => Self::new_rgba8(w, h),
171 L16 => Self::new_luma16(w, h),
172 La16 => Self::new_luma_a16(w, h),
173 Rgb16 => Self::new_rgb16(w, h),
174 Rgba16 => Self::new_rgba16(w, h),
175 Rgb32F => Self::new_rgb32f(w, h),
176 Rgba32F => Self::new_rgba32f(w, h),
177 }
178 }
179
180 #[must_use]
182 pub fn new_luma8(w: u32, h: u32) -> DynamicImage {
183 DynamicImage::ImageLuma8(ImageBuffer::new(w, h))
184 }
185
186 #[must_use]
189 pub fn new_luma_a8(w: u32, h: u32) -> DynamicImage {
190 DynamicImage::ImageLumaA8(ImageBuffer::new(w, h))
191 }
192
193 #[must_use]
195 pub fn new_rgb8(w: u32, h: u32) -> DynamicImage {
196 DynamicImage::ImageRgb8(ImageBuffer::new(w, h))
197 }
198
199 #[must_use]
201 pub fn new_rgba8(w: u32, h: u32) -> DynamicImage {
202 DynamicImage::ImageRgba8(ImageBuffer::new(w, h))
203 }
204
205 #[must_use]
207 pub fn new_luma16(w: u32, h: u32) -> DynamicImage {
208 DynamicImage::ImageLuma16(ImageBuffer::new(w, h))
209 }
210
211 #[must_use]
214 pub fn new_luma_a16(w: u32, h: u32) -> DynamicImage {
215 DynamicImage::ImageLumaA16(ImageBuffer::new(w, h))
216 }
217
218 #[must_use]
220 pub fn new_rgb16(w: u32, h: u32) -> DynamicImage {
221 DynamicImage::ImageRgb16(ImageBuffer::new(w, h))
222 }
223
224 #[must_use]
226 pub fn new_rgba16(w: u32, h: u32) -> DynamicImage {
227 DynamicImage::ImageRgba16(ImageBuffer::new(w, h))
228 }
229
230 #[must_use]
232 pub fn new_rgb32f(w: u32, h: u32) -> DynamicImage {
233 DynamicImage::ImageRgb32F(ImageBuffer::new(w, h))
234 }
235
236 #[must_use]
238 pub fn new_rgba32f(w: u32, h: u32) -> DynamicImage {
239 DynamicImage::ImageRgba32F(ImageBuffer::new(w, h))
240 }
241
242 pub fn from_decoder(decoder: impl ImageDecoder) -> ImageResult<Self> {
244 decoder_to_image(decoder)
245 }
246
247 #[inline]
256 #[must_use]
257 pub fn to<
258 T: Pixel
259 + FromColor<color::Rgb<u8>>
260 + FromColor<color::Rgb<f32>>
261 + FromColor<color::Rgba<u8>>
262 + FromColor<color::Rgba<u16>>
263 + FromColor<color::Rgba<f32>>
264 + FromColor<color::Rgb<u16>>
265 + FromColor<Luma<u8>>
266 + FromColor<Luma<u16>>
267 + FromColor<LumaA<u16>>
268 + FromColor<LumaA<u8>>,
269 >(
270 &self,
271 ) -> ImageBuffer<T, Vec<T::Subpixel>> {
272 dynamic_map!(*self, ref p, p.convert())
273 }
274
275 #[must_use]
277 pub fn to_rgb8(&self) -> RgbImage {
278 match self {
279 DynamicImage::ImageRgb8(x) => x.clone(),
280 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
281 }
282 }
283
284 #[must_use]
286 pub fn to_rgb16(&self) -> Rgb16Image {
287 match self {
288 DynamicImage::ImageRgb16(x) => x.clone(),
289 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
290 }
291 }
292
293 #[must_use]
295 pub fn to_rgb32f(&self) -> Rgb32FImage {
296 match self {
297 DynamicImage::ImageRgb32F(x) => x.clone(),
298 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
299 }
300 }
301
302 #[must_use]
304 pub fn to_rgba8(&self) -> RgbaImage {
305 match self {
306 DynamicImage::ImageRgba8(x) => x.clone(),
307 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
308 }
309 }
310
311 #[must_use]
313 pub fn to_rgba16(&self) -> Rgba16Image {
314 match self {
315 DynamicImage::ImageRgba16(x) => x.clone(),
316 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
317 }
318 }
319
320 #[must_use]
322 pub fn to_rgba32f(&self) -> Rgba32FImage {
323 match self {
324 DynamicImage::ImageRgba32F(x) => x.clone(),
325 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
326 }
327 }
328
329 #[must_use]
331 pub fn to_luma8(&self) -> GrayImage {
332 match self {
333 DynamicImage::ImageLuma8(x) => x.clone(),
334 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
335 }
336 }
337
338 #[must_use]
340 pub fn to_luma16(&self) -> Gray16Image {
341 match self {
342 DynamicImage::ImageLuma16(x) => x.clone(),
343 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
344 }
345 }
346
347 #[must_use]
349 pub fn to_luma32f(&self) -> ImageBuffer<Luma<f32>, Vec<f32>> {
350 dynamic_map!(self, ref p, p.cast_in_color_space())
351 }
352
353 #[must_use]
355 pub fn to_luma_alpha8(&self) -> GrayAlphaImage {
356 match self {
357 DynamicImage::ImageLumaA8(x) => x.clone(),
358 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
359 }
360 }
361
362 #[must_use]
364 pub fn to_luma_alpha16(&self) -> GrayAlpha16Image {
365 match self {
366 DynamicImage::ImageLumaA16(x) => x.clone(),
367 x => dynamic_map!(x, ref p, p.cast_in_color_space()),
368 }
369 }
370
371 #[must_use]
373 pub fn to_luma_alpha32f(&self) -> ImageBuffer<LumaA<f32>, Vec<f32>> {
374 dynamic_map!(self, ref p, p.cast_in_color_space())
375 }
376
377 #[must_use]
382 pub fn into_rgb8(self) -> RgbImage {
383 match self {
384 DynamicImage::ImageRgb8(x) => x,
385 x => x.to_rgb8(),
386 }
387 }
388
389 #[must_use]
394 pub fn into_rgb16(self) -> Rgb16Image {
395 match self {
396 DynamicImage::ImageRgb16(x) => x,
397 x => x.to_rgb16(),
398 }
399 }
400
401 #[must_use]
406 pub fn into_rgb32f(self) -> Rgb32FImage {
407 match self {
408 DynamicImage::ImageRgb32F(x) => x,
409 x => x.to_rgb32f(),
410 }
411 }
412
413 #[must_use]
418 pub fn into_rgba8(self) -> RgbaImage {
419 match self {
420 DynamicImage::ImageRgba8(x) => x,
421 x => x.to_rgba8(),
422 }
423 }
424
425 #[must_use]
430 pub fn into_rgba16(self) -> Rgba16Image {
431 match self {
432 DynamicImage::ImageRgba16(x) => x,
433 x => x.to_rgba16(),
434 }
435 }
436
437 #[must_use]
442 pub fn into_rgba32f(self) -> Rgba32FImage {
443 match self {
444 DynamicImage::ImageRgba32F(x) => x,
445 x => x.to_rgba32f(),
446 }
447 }
448
449 #[must_use]
454 pub fn into_luma8(self) -> GrayImage {
455 match self {
456 DynamicImage::ImageLuma8(x) => x,
457 x => x.to_luma8(),
458 }
459 }
460
461 #[must_use]
466 pub fn into_luma16(self) -> Gray16Image {
467 match self {
468 DynamicImage::ImageLuma16(x) => x,
469 x => x.to_luma16(),
470 }
471 }
472
473 #[must_use]
478 pub fn into_luma_alpha8(self) -> GrayAlphaImage {
479 match self {
480 DynamicImage::ImageLumaA8(x) => x,
481 x => x.to_luma_alpha8(),
482 }
483 }
484
485 #[must_use]
490 pub fn into_luma_alpha16(self) -> GrayAlpha16Image {
491 match self {
492 DynamicImage::ImageLumaA16(x) => x,
493 x => x.to_luma_alpha16(),
494 }
495 }
496
497 #[must_use]
502 pub fn crop(&mut self, x: u32, y: u32, width: u32, height: u32) -> DynamicImage {
503 dynamic_map!(*self, ref mut p => imageops::crop(p, x, y, width, height).to_image())
504 }
505
506 #[must_use]
508 pub fn crop_imm(&self, x: u32, y: u32, width: u32, height: u32) -> DynamicImage {
509 dynamic_map!(*self, ref p => imageops::crop_imm(p, x, y, width, height).to_image())
510 }
511
512 #[must_use]
514 pub fn as_rgb8(&self) -> Option<&RgbImage> {
515 match *self {
516 DynamicImage::ImageRgb8(ref p) => Some(p),
517 _ => None,
518 }
519 }
520
521 pub fn as_mut_rgb8(&mut self) -> Option<&mut RgbImage> {
523 match *self {
524 DynamicImage::ImageRgb8(ref mut p) => Some(p),
525 _ => None,
526 }
527 }
528
529 #[must_use]
531 pub fn as_rgba8(&self) -> Option<&RgbaImage> {
532 match *self {
533 DynamicImage::ImageRgba8(ref p) => Some(p),
534 _ => None,
535 }
536 }
537
538 pub fn as_mut_rgba8(&mut self) -> Option<&mut RgbaImage> {
540 match *self {
541 DynamicImage::ImageRgba8(ref mut p) => Some(p),
542 _ => None,
543 }
544 }
545
546 #[must_use]
548 pub fn as_luma8(&self) -> Option<&GrayImage> {
549 match *self {
550 DynamicImage::ImageLuma8(ref p) => Some(p),
551 _ => None,
552 }
553 }
554
555 pub fn as_mut_luma8(&mut self) -> Option<&mut GrayImage> {
557 match *self {
558 DynamicImage::ImageLuma8(ref mut p) => Some(p),
559 _ => None,
560 }
561 }
562
563 #[must_use]
565 pub fn as_luma_alpha8(&self) -> Option<&GrayAlphaImage> {
566 match *self {
567 DynamicImage::ImageLumaA8(ref p) => Some(p),
568 _ => None,
569 }
570 }
571
572 pub fn as_mut_luma_alpha8(&mut self) -> Option<&mut GrayAlphaImage> {
574 match *self {
575 DynamicImage::ImageLumaA8(ref mut p) => Some(p),
576 _ => None,
577 }
578 }
579
580 #[must_use]
582 pub fn as_rgb16(&self) -> Option<&Rgb16Image> {
583 match *self {
584 DynamicImage::ImageRgb16(ref p) => Some(p),
585 _ => None,
586 }
587 }
588
589 pub fn as_mut_rgb16(&mut self) -> Option<&mut Rgb16Image> {
591 match *self {
592 DynamicImage::ImageRgb16(ref mut p) => Some(p),
593 _ => None,
594 }
595 }
596
597 #[must_use]
599 pub fn as_rgba16(&self) -> Option<&Rgba16Image> {
600 match *self {
601 DynamicImage::ImageRgba16(ref p) => Some(p),
602 _ => None,
603 }
604 }
605
606 pub fn as_mut_rgba16(&mut self) -> Option<&mut Rgba16Image> {
608 match *self {
609 DynamicImage::ImageRgba16(ref mut p) => Some(p),
610 _ => None,
611 }
612 }
613
614 #[must_use]
616 pub fn as_rgb32f(&self) -> Option<&Rgb32FImage> {
617 match *self {
618 DynamicImage::ImageRgb32F(ref p) => Some(p),
619 _ => None,
620 }
621 }
622
623 pub fn as_mut_rgb32f(&mut self) -> Option<&mut Rgb32FImage> {
625 match *self {
626 DynamicImage::ImageRgb32F(ref mut p) => Some(p),
627 _ => None,
628 }
629 }
630
631 #[must_use]
633 pub fn as_rgba32f(&self) -> Option<&Rgba32FImage> {
634 match *self {
635 DynamicImage::ImageRgba32F(ref p) => Some(p),
636 _ => None,
637 }
638 }
639
640 pub fn as_mut_rgba32f(&mut self) -> Option<&mut Rgba32FImage> {
642 match *self {
643 DynamicImage::ImageRgba32F(ref mut p) => Some(p),
644 _ => None,
645 }
646 }
647
648 #[must_use]
650 pub fn as_luma16(&self) -> Option<&Gray16Image> {
651 match *self {
652 DynamicImage::ImageLuma16(ref p) => Some(p),
653 _ => None,
654 }
655 }
656
657 pub fn as_mut_luma16(&mut self) -> Option<&mut Gray16Image> {
659 match *self {
660 DynamicImage::ImageLuma16(ref mut p) => Some(p),
661 _ => None,
662 }
663 }
664
665 #[must_use]
667 pub fn as_luma_alpha16(&self) -> Option<&GrayAlpha16Image> {
668 match *self {
669 DynamicImage::ImageLumaA16(ref p) => Some(p),
670 _ => None,
671 }
672 }
673
674 pub fn as_mut_luma_alpha16(&mut self) -> Option<&mut GrayAlpha16Image> {
676 match *self {
677 DynamicImage::ImageLumaA16(ref mut p) => Some(p),
678 _ => None,
679 }
680 }
681
682 #[must_use]
684 pub fn as_flat_samples_u8(&self) -> Option<FlatSamples<&[u8]>> {
685 match *self {
686 DynamicImage::ImageLuma8(ref p) => Some(p.as_flat_samples()),
687 DynamicImage::ImageLumaA8(ref p) => Some(p.as_flat_samples()),
688 DynamicImage::ImageRgb8(ref p) => Some(p.as_flat_samples()),
689 DynamicImage::ImageRgba8(ref p) => Some(p.as_flat_samples()),
690 _ => None,
691 }
692 }
693
694 #[must_use]
696 pub fn as_flat_samples_u16(&self) -> Option<FlatSamples<&[u16]>> {
697 match *self {
698 DynamicImage::ImageLuma16(ref p) => Some(p.as_flat_samples()),
699 DynamicImage::ImageLumaA16(ref p) => Some(p.as_flat_samples()),
700 DynamicImage::ImageRgb16(ref p) => Some(p.as_flat_samples()),
701 DynamicImage::ImageRgba16(ref p) => Some(p.as_flat_samples()),
702 _ => None,
703 }
704 }
705
706 #[must_use]
708 pub fn as_flat_samples_f32(&self) -> Option<FlatSamples<&[f32]>> {
709 match *self {
710 DynamicImage::ImageRgb32F(ref p) => Some(p.as_flat_samples()),
711 DynamicImage::ImageRgba32F(ref p) => Some(p.as_flat_samples()),
712 _ => None,
713 }
714 }
715
716 #[must_use]
718 pub fn as_bytes(&self) -> &[u8] {
719 dynamic_map!(
721 *self,
722 ref image_buffer,
723 bytemuck::cast_slice(image_buffer.as_raw())
724 )
725 }
726
727 #[must_use]
731 pub fn into_bytes(self) -> Vec<u8> {
732 dynamic_map!(self, image_buffer, {
734 match bytemuck::allocation::try_cast_vec(image_buffer.into_raw()) {
735 Ok(vec) => vec,
736 Err((_, vec)) => {
737 bytemuck::cast_slice(&vec).to_owned()
743 }
744 }
745 })
746 }
747
748 #[must_use]
750 pub fn color(&self) -> color::ColorType {
751 match *self {
752 DynamicImage::ImageLuma8(_) => color::ColorType::L8,
753 DynamicImage::ImageLumaA8(_) => color::ColorType::La8,
754 DynamicImage::ImageRgb8(_) => color::ColorType::Rgb8,
755 DynamicImage::ImageRgba8(_) => color::ColorType::Rgba8,
756 DynamicImage::ImageLuma16(_) => color::ColorType::L16,
757 DynamicImage::ImageLumaA16(_) => color::ColorType::La16,
758 DynamicImage::ImageRgb16(_) => color::ColorType::Rgb16,
759 DynamicImage::ImageRgba16(_) => color::ColorType::Rgba16,
760 DynamicImage::ImageRgb32F(_) => color::ColorType::Rgb32F,
761 DynamicImage::ImageRgba32F(_) => color::ColorType::Rgba32F,
762 }
763 }
764
765 #[must_use]
767 pub fn width(&self) -> u32 {
768 dynamic_map!(*self, ref p, { p.width() })
769 }
770
771 #[must_use]
773 pub fn height(&self) -> u32 {
774 dynamic_map!(*self, ref p, { p.height() })
775 }
776
777 pub fn set_rgb_primaries(&mut self, color: CicpColorPrimaries) {
788 dynamic_map!(self, ref mut p, p.set_rgb_primaries(color));
789 }
790
791 pub fn set_transfer_function(&mut self, tf: CicpTransferCharacteristics) {
798 dynamic_map!(self, ref mut p, p.set_transfer_function(tf));
799 }
800
801 pub fn color_space(&self) -> Cicp {
803 dynamic_map!(self, ref p, p.color_space())
804 }
805
806 pub fn set_color_space(&mut self, cicp: Cicp) -> ImageResult<()> {
811 dynamic_map!(self, ref mut p, p.set_color_space(cicp))
812 }
813
814 #[must_use]
823 pub fn has_alpha(&self) -> bool {
824 self.color().has_alpha()
825 }
826
827 #[must_use]
831 pub fn grayscale(&self) -> DynamicImage {
832 match *self {
833 DynamicImage::ImageLuma8(ref p) => DynamicImage::ImageLuma8(p.clone()),
834 DynamicImage::ImageLumaA8(ref p) => {
835 DynamicImage::ImageLumaA8(imageops::grayscale_alpha(p))
836 }
837 DynamicImage::ImageRgb8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
838 DynamicImage::ImageRgba8(ref p) => {
839 DynamicImage::ImageLumaA8(imageops::grayscale_alpha(p))
840 }
841 DynamicImage::ImageLuma16(ref p) => DynamicImage::ImageLuma16(p.clone()),
842 DynamicImage::ImageLumaA16(ref p) => {
843 DynamicImage::ImageLumaA16(imageops::grayscale_alpha(p))
844 }
845 DynamicImage::ImageRgb16(ref p) => DynamicImage::ImageLuma16(imageops::grayscale(p)),
846 DynamicImage::ImageRgba16(ref p) => {
847 DynamicImage::ImageLumaA16(imageops::grayscale_alpha(p))
848 }
849 DynamicImage::ImageRgb32F(ref p) => {
850 DynamicImage::ImageRgb32F(imageops::grayscale_with_type(p))
851 }
852 DynamicImage::ImageRgba32F(ref p) => {
853 DynamicImage::ImageRgba32F(imageops::grayscale_with_type_alpha(p))
854 }
855 }
856 }
857
858 pub fn invert(&mut self) {
864 dynamic_map!(*self, ref mut p, imageops::invert(p));
865 }
866
867 #[must_use]
875 pub fn resize(&self, nwidth: u32, nheight: u32, filter: imageops::FilterType) -> DynamicImage {
876 if (nwidth, nheight) == self.dimensions() {
877 return self.clone();
878 }
879 let (width2, height2) =
880 resize_dimensions(self.width(), self.height(), nwidth, nheight, false);
881
882 self.resize_exact(width2, height2, filter)
883 }
884
885 #[must_use]
892 pub fn resize_exact(
893 &self,
894 nwidth: u32,
895 nheight: u32,
896 filter: imageops::FilterType,
897 ) -> DynamicImage {
898 dynamic_map!(*self, ref p => imageops::resize(p, nwidth, nheight, filter))
899 }
900
901 #[must_use]
913 pub fn thumbnail(&self, nwidth: u32, nheight: u32) -> DynamicImage {
914 let (width2, height2) =
915 resize_dimensions(self.width(), self.height(), nwidth, nheight, false);
916 self.thumbnail_exact(width2, height2)
917 }
918
919 #[must_use]
929 pub fn thumbnail_exact(&self, nwidth: u32, nheight: u32) -> DynamicImage {
930 dynamic_map!(*self, ref p => imageops::thumbnail(p, nwidth, nheight))
931 }
932
933 #[must_use]
943 pub fn resize_to_fill(
944 &self,
945 nwidth: u32,
946 nheight: u32,
947 filter: imageops::FilterType,
948 ) -> DynamicImage {
949 let (width2, height2) =
950 resize_dimensions(self.width(), self.height(), nwidth, nheight, true);
951
952 let mut intermediate = self.resize_exact(width2, height2, filter);
953 let (iwidth, iheight) = intermediate.dimensions();
954 let ratio = u64::from(iwidth) * u64::from(nheight);
955 let nratio = u64::from(nwidth) * u64::from(iheight);
956
957 if nratio > ratio {
958 intermediate.crop(0, (iheight - nheight) / 2, nwidth, nheight)
959 } else {
960 intermediate.crop((iwidth - nwidth) / 2, 0, nwidth, nheight)
961 }
962 }
963
964 #[must_use]
980 pub fn blur(&self, sigma: f32) -> DynamicImage {
981 gaussian_blur_dyn_image(
982 self,
983 GaussianBlurParameters::new_from_sigma(if sigma == 0.0 { 0.8 } else { sigma }),
984 )
985 }
986
987 #[must_use]
1000 pub fn blur_advanced(&self, parameters: GaussianBlurParameters) -> DynamicImage {
1001 gaussian_blur_dyn_image(self, parameters)
1002 }
1003
1004 #[must_use]
1016 pub fn fast_blur(&self, sigma: f32) -> DynamicImage {
1017 dynamic_map!(*self, ref p => imageops::fast_blur(p, sigma))
1018 }
1019
1020 #[must_use]
1034 pub fn unsharpen(&self, sigma: f32, threshold: i32) -> DynamicImage {
1035 dynamic_map!(*self, ref p => imageops::unsharpen(p, sigma, threshold))
1036 }
1037
1038 #[must_use]
1048 pub fn filter3x3(&self, kernel: &[f32]) -> DynamicImage {
1049 assert_eq!(9, kernel.len(), "filter must be 3 x 3");
1050
1051 dynamic_map!(*self, ref p => imageops::filter3x3(p, kernel))
1052 }
1053
1054 #[must_use]
1061 pub fn adjust_contrast(&self, c: f32) -> DynamicImage {
1062 dynamic_map!(*self, ref p => imageops::contrast(p, c))
1063 }
1064
1065 #[must_use]
1072 pub fn brighten(&self, value: i32) -> DynamicImage {
1073 dynamic_map!(*self, ref p => imageops::brighten(p, value))
1074 }
1075
1076 #[must_use]
1084 pub fn huerotate(&self, value: i32) -> DynamicImage {
1085 dynamic_map!(*self, ref p => imageops::huerotate(p, value))
1086 }
1087
1088 #[must_use]
1092 pub fn flipv(&self) -> DynamicImage {
1093 dynamic_map!(*self, ref p => imageops::flip_vertical(p))
1094 }
1095
1096 fn flipv_in_place(&mut self) {
1098 dynamic_map!(*self, ref mut p, imageops::flip_vertical_in_place(p))
1099 }
1100
1101 #[must_use]
1105 pub fn fliph(&self) -> DynamicImage {
1106 dynamic_map!(*self, ref p => imageops::flip_horizontal(p))
1107 }
1108
1109 fn fliph_in_place(&mut self) {
1111 dynamic_map!(*self, ref mut p, imageops::flip_horizontal_in_place(p))
1112 }
1113
1114 #[must_use]
1116 pub fn rotate90(&self) -> DynamicImage {
1117 dynamic_map!(*self, ref p => imageops::rotate90(p))
1118 }
1119
1120 #[must_use]
1124 pub fn rotate180(&self) -> DynamicImage {
1125 dynamic_map!(*self, ref p => imageops::rotate180(p))
1126 }
1127
1128 fn rotate180_in_place(&mut self) {
1130 dynamic_map!(*self, ref mut p, imageops::rotate180_in_place(p))
1131 }
1132
1133 #[must_use]
1135 pub fn rotate270(&self) -> DynamicImage {
1136 dynamic_map!(*self, ref p => imageops::rotate270(p))
1137 }
1138
1139 pub fn apply_orientation(&mut self, orientation: Orientation) {
1162 let image = self;
1163 match orientation {
1164 Orientation::NoTransforms => (),
1165 Orientation::Rotate90 => *image = image.rotate90(),
1166 Orientation::Rotate180 => image.rotate180_in_place(),
1167 Orientation::Rotate270 => *image = image.rotate270(),
1168 Orientation::FlipHorizontal => image.fliph_in_place(),
1169 Orientation::FlipVertical => image.flipv_in_place(),
1170 Orientation::Rotate90FlipH => {
1171 let mut new_image = image.rotate90();
1172 new_image.fliph_in_place();
1173 *image = new_image;
1174 }
1175 Orientation::Rotate270FlipH => {
1176 let mut new_image = image.rotate270();
1177 new_image.fliph_in_place();
1178 *image = new_image;
1179 }
1180 }
1181 }
1182
1183 pub fn copy_from_color_space(
1204 &mut self,
1205 other: &DynamicImage,
1206 mut options: ConvertColorOptions,
1207 ) -> ImageResult<()> {
1208 if self.color_space() == other.color_space() {
1210 dynamic_map!(
1212 self,
1213 ref mut p,
1214 *p = dynamic_map!(other, ref o, o.cast_in_color_space())
1215 );
1216
1217 return Ok(());
1218 }
1219
1220 match (&mut *self, other) {
1224 (DynamicImage::ImageRgb8(img), DynamicImage::ImageRgb8(other)) => {
1226 return img.copy_from_color_space(other, options);
1227 }
1228 (DynamicImage::ImageRgb8(img), DynamicImage::ImageRgba8(other)) => {
1229 return img.copy_from_color_space(other, options);
1230 }
1231 (DynamicImage::ImageRgba8(img), DynamicImage::ImageRgb8(other)) => {
1232 return img.copy_from_color_space(other, options);
1233 }
1234 (DynamicImage::ImageRgba8(img), DynamicImage::ImageRgba8(other)) => {
1235 return img.copy_from_color_space(other, options);
1236 }
1237 (DynamicImage::ImageRgb16(img), DynamicImage::ImageRgb16(other)) => {
1239 return img.copy_from_color_space(other, options);
1240 }
1241 (DynamicImage::ImageRgb16(img), DynamicImage::ImageRgba16(other)) => {
1242 return img.copy_from_color_space(other, options);
1243 }
1244 (DynamicImage::ImageRgba16(img), DynamicImage::ImageRgb16(other)) => {
1245 return img.copy_from_color_space(other, options);
1246 }
1247 (DynamicImage::ImageRgba16(img), DynamicImage::ImageRgba16(other)) => {
1248 return img.copy_from_color_space(other, options);
1249 }
1250 (DynamicImage::ImageRgb32F(img), DynamicImage::ImageRgb32F(other)) => {
1252 return img.copy_from_color_space(other, options);
1253 }
1254 (DynamicImage::ImageRgb32F(img), DynamicImage::ImageRgba32F(other)) => {
1255 return img.copy_from_color_space(other, options);
1256 }
1257 (DynamicImage::ImageRgba32F(img), DynamicImage::ImageRgb32F(other)) => {
1258 return img.copy_from_color_space(other, options);
1259 }
1260 (DynamicImage::ImageRgba32F(img), DynamicImage::ImageRgba32F(other)) => {
1261 return img.copy_from_color_space(other, options);
1262 }
1263 _ => {}
1264 };
1265
1266 let cicp = options.as_transform(other.color_space(), self.color_space())?;
1272 cicp.transform_dynamic(self, other);
1273
1274 Ok(())
1275 }
1276
1277 pub fn apply_color_space(
1292 &mut self,
1293 cicp: Cicp,
1294 options: ConvertColorOptions,
1295 ) -> ImageResult<()> {
1296 if self.color_space() == cicp {
1298 return Ok(());
1299 }
1300
1301 let mut target = self.clone();
1306 target.set_color_space(cicp)?;
1307 target.copy_from_color_space(self, options)?;
1308
1309 *self = target;
1310 Ok(())
1311 }
1312
1313 pub fn convert_color_space(
1327 &mut self,
1328 cicp: Cicp,
1329 options: ConvertColorOptions,
1330 color: color::ColorType,
1331 ) -> ImageResult<()> {
1332 if self.color() == color {
1333 return self.apply_color_space(cicp, options);
1334 }
1335
1336 let rgb = cicp.try_into_rgb()?;
1338 let mut target = DynamicImage::new(self.width(), self.height(), color);
1339 dynamic_map!(target, ref mut p, p.set_rgb_color_space(rgb));
1340 target.copy_from_color_space(self, options)?;
1341
1342 *self = target;
1343 Ok(())
1344 }
1345
1346 fn write_with_encoder_impl<'a>(
1347 &self,
1348 encoder: Box<dyn ImageEncoderBoxed + 'a>,
1349 ) -> ImageResult<()> {
1350 let converted = encoder.make_compatible_img(crate::io::encoder::MethodSealedToImage, self);
1351 let img = converted.as_ref().unwrap_or(self);
1352
1353 encoder.write_image(
1354 img.as_bytes(),
1355 img.width(),
1356 img.height(),
1357 img.color().into(),
1358 )
1359 }
1360
1361 pub fn write_to<W: Write + Seek>(&self, mut w: W, format: ImageFormat) -> ImageResult<()> {
1372 let encoder = encoder_for_format(format, &mut w)?;
1373 self.write_with_encoder_impl(encoder)
1374 }
1375
1376 pub fn write_with_encoder(&self, encoder: impl ImageEncoder) -> ImageResult<()> {
1384 self.write_with_encoder_impl(Box::new(encoder))
1385 }
1386
1387 pub fn save<Q>(&self, path: Q) -> ImageResult<()>
1395 where
1396 Q: AsRef<Path>,
1397 {
1398 let format = ImageFormat::from_path(path.as_ref())?;
1399 self.save_with_format(path, format)
1400 }
1401
1402 pub fn save_with_format<Q>(&self, path: Q, format: ImageFormat) -> ImageResult<()>
1410 where
1411 Q: AsRef<Path>,
1412 {
1413 let file = &mut BufWriter::new(File::create(path)?);
1414 let encoder = encoder_for_format(format, file)?;
1415 self.write_with_encoder_impl(encoder)
1416 }
1417}
1418
1419impl From<GrayImage> for DynamicImage {
1420 fn from(image: GrayImage) -> Self {
1421 DynamicImage::ImageLuma8(image)
1422 }
1423}
1424
1425impl From<GrayAlphaImage> for DynamicImage {
1426 fn from(image: GrayAlphaImage) -> Self {
1427 DynamicImage::ImageLumaA8(image)
1428 }
1429}
1430
1431impl From<RgbImage> for DynamicImage {
1432 fn from(image: RgbImage) -> Self {
1433 DynamicImage::ImageRgb8(image)
1434 }
1435}
1436
1437impl From<RgbaImage> for DynamicImage {
1438 fn from(image: RgbaImage) -> Self {
1439 DynamicImage::ImageRgba8(image)
1440 }
1441}
1442
1443impl From<Gray16Image> for DynamicImage {
1444 fn from(image: Gray16Image) -> Self {
1445 DynamicImage::ImageLuma16(image)
1446 }
1447}
1448
1449impl From<GrayAlpha16Image> for DynamicImage {
1450 fn from(image: GrayAlpha16Image) -> Self {
1451 DynamicImage::ImageLumaA16(image)
1452 }
1453}
1454
1455impl From<Rgb16Image> for DynamicImage {
1456 fn from(image: Rgb16Image) -> Self {
1457 DynamicImage::ImageRgb16(image)
1458 }
1459}
1460
1461impl From<Rgba16Image> for DynamicImage {
1462 fn from(image: Rgba16Image) -> Self {
1463 DynamicImage::ImageRgba16(image)
1464 }
1465}
1466
1467impl From<Rgb32FImage> for DynamicImage {
1468 fn from(image: Rgb32FImage) -> Self {
1469 DynamicImage::ImageRgb32F(image)
1470 }
1471}
1472
1473impl From<Rgba32FImage> for DynamicImage {
1474 fn from(image: Rgba32FImage) -> Self {
1475 DynamicImage::ImageRgba32F(image)
1476 }
1477}
1478
1479impl From<ImageBuffer<Luma<f32>, Vec<f32>>> for DynamicImage {
1480 fn from(image: ImageBuffer<Luma<f32>, Vec<f32>>) -> Self {
1481 DynamicImage::ImageRgb32F(image.convert())
1482 }
1483}
1484
1485impl From<ImageBuffer<LumaA<f32>, Vec<f32>>> for DynamicImage {
1486 fn from(image: ImageBuffer<LumaA<f32>, Vec<f32>>) -> Self {
1487 DynamicImage::ImageRgba32F(image.convert())
1488 }
1489}
1490
1491#[allow(deprecated)]
1492impl GenericImageView for DynamicImage {
1493 type Pixel = color::Rgba<u8>; fn dimensions(&self) -> (u32, u32) {
1496 dynamic_map!(*self, ref p, p.dimensions())
1497 }
1498
1499 fn get_pixel(&self, x: u32, y: u32) -> color::Rgba<u8> {
1500 dynamic_map!(*self, ref p, p.get_pixel(x, y).to_rgba().into_color())
1501 }
1502}
1503
1504#[allow(deprecated)]
1505impl GenericImage for DynamicImage {
1506 fn put_pixel(&mut self, x: u32, y: u32, pixel: color::Rgba<u8>) {
1507 match *self {
1508 DynamicImage::ImageLuma8(ref mut p) => p.put_pixel(x, y, pixel.to_luma()),
1509 DynamicImage::ImageLumaA8(ref mut p) => p.put_pixel(x, y, pixel.to_luma_alpha()),
1510 DynamicImage::ImageRgb8(ref mut p) => p.put_pixel(x, y, pixel.to_rgb()),
1511 DynamicImage::ImageRgba8(ref mut p) => p.put_pixel(x, y, pixel),
1512 DynamicImage::ImageLuma16(ref mut p) => p.put_pixel(x, y, pixel.to_luma().into_color()),
1513 DynamicImage::ImageLumaA16(ref mut p) => {
1514 p.put_pixel(x, y, pixel.to_luma_alpha().into_color());
1515 }
1516 DynamicImage::ImageRgb16(ref mut p) => p.put_pixel(x, y, pixel.to_rgb().into_color()),
1517 DynamicImage::ImageRgba16(ref mut p) => p.put_pixel(x, y, pixel.into_color()),
1518 DynamicImage::ImageRgb32F(ref mut p) => p.put_pixel(x, y, pixel.to_rgb().into_color()),
1519 DynamicImage::ImageRgba32F(ref mut p) => p.put_pixel(x, y, pixel.into_color()),
1520 }
1521 }
1522
1523 fn blend_pixel(&mut self, x: u32, y: u32, pixel: color::Rgba<u8>) {
1524 match *self {
1525 DynamicImage::ImageLuma8(ref mut p) => p.blend_pixel(x, y, pixel.to_luma()),
1526 DynamicImage::ImageLumaA8(ref mut p) => p.blend_pixel(x, y, pixel.to_luma_alpha()),
1527 DynamicImage::ImageRgb8(ref mut p) => p.blend_pixel(x, y, pixel.to_rgb()),
1528 DynamicImage::ImageRgba8(ref mut p) => p.blend_pixel(x, y, pixel),
1529 DynamicImage::ImageLuma16(ref mut p) => {
1530 p.blend_pixel(x, y, pixel.to_luma().into_color());
1531 }
1532 DynamicImage::ImageLumaA16(ref mut p) => {
1533 p.blend_pixel(x, y, pixel.to_luma_alpha().into_color());
1534 }
1535 DynamicImage::ImageRgb16(ref mut p) => p.blend_pixel(x, y, pixel.to_rgb().into_color()),
1536 DynamicImage::ImageRgba16(ref mut p) => p.blend_pixel(x, y, pixel.into_color()),
1537 DynamicImage::ImageRgb32F(ref mut p) => {
1538 p.blend_pixel(x, y, pixel.to_rgb().into_color());
1539 }
1540 DynamicImage::ImageRgba32F(ref mut p) => p.blend_pixel(x, y, pixel.into_color()),
1541 }
1542 }
1543
1544 fn get_pixel_mut(&mut self, _: u32, _: u32) -> &mut color::Rgba<u8> {
1546 unimplemented!()
1547 }
1548}
1549
1550impl Default for DynamicImage {
1551 fn default() -> Self {
1552 Self::ImageRgba8(Default::default())
1553 }
1554}
1555
1556fn decoder_to_image<I: ImageDecoder>(decoder: I) -> ImageResult<DynamicImage> {
1558 let (w, h) = decoder.dimensions();
1559 let color_type = decoder.color_type();
1560
1561 let mut image = match color_type {
1562 color::ColorType::Rgb8 => {
1563 let buf = free_functions::decoder_to_vec(decoder)?;
1564 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb8)
1565 }
1566
1567 color::ColorType::Rgba8 => {
1568 let buf = free_functions::decoder_to_vec(decoder)?;
1569 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba8)
1570 }
1571
1572 color::ColorType::L8 => {
1573 let buf = free_functions::decoder_to_vec(decoder)?;
1574 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLuma8)
1575 }
1576
1577 color::ColorType::La8 => {
1578 let buf = free_functions::decoder_to_vec(decoder)?;
1579 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLumaA8)
1580 }
1581
1582 color::ColorType::Rgb16 => {
1583 let buf = free_functions::decoder_to_vec(decoder)?;
1584 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb16)
1585 }
1586
1587 color::ColorType::Rgba16 => {
1588 let buf = free_functions::decoder_to_vec(decoder)?;
1589 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba16)
1590 }
1591
1592 color::ColorType::Rgb32F => {
1593 let buf = free_functions::decoder_to_vec(decoder)?;
1594 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb32F)
1595 }
1596
1597 color::ColorType::Rgba32F => {
1598 let buf = free_functions::decoder_to_vec(decoder)?;
1599 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba32F)
1600 }
1601
1602 color::ColorType::L16 => {
1603 let buf = free_functions::decoder_to_vec(decoder)?;
1604 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLuma16)
1605 }
1606
1607 color::ColorType::La16 => {
1608 let buf = free_functions::decoder_to_vec(decoder)?;
1609 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLumaA16)
1610 }
1611 }
1612 .ok_or_else(|| {
1613 ImageError::Parameter(ParameterError::from_kind(
1614 ParameterErrorKind::DimensionMismatch,
1615 ))
1616 })?;
1617
1618 image.set_rgb_primaries(Cicp::SRGB.primaries);
1622 image.set_transfer_function(Cicp::SRGB.transfer);
1623
1624 Ok(image)
1625}
1626
1627pub fn open<P>(path: P) -> ImageResult<DynamicImage>
1633where
1634 P: AsRef<Path>,
1635{
1636 ImageReader::open(path)?.decode()
1637}
1638
1639pub fn image_dimensions<P>(path: P) -> ImageResult<(u32, u32)>
1645where
1646 P: AsRef<Path>,
1647{
1648 ImageReader::open(path)?.into_dimensions()
1649}
1650
1651pub fn write_buffer_with_format<W: Write + Seek>(
1659 buffered_writer: &mut W,
1660 buf: &[u8],
1661 width: u32,
1662 height: u32,
1663 color: impl Into<ExtendedColorType>,
1664 format: ImageFormat,
1665) -> ImageResult<()> {
1666 let encoder = encoder_for_format(format, buffered_writer)?;
1667 encoder.write_image(buf, width, height, color.into())
1668}
1669
1670pub fn load_from_memory(buffer: &[u8]) -> ImageResult<DynamicImage> {
1677 let format = free_functions::guess_format(buffer)?;
1678 load_from_memory_with_format(buffer, format)
1679}
1680
1681#[inline(always)]
1690pub fn load_from_memory_with_format(buf: &[u8], format: ImageFormat) -> ImageResult<DynamicImage> {
1691 let b = io::Cursor::new(buf);
1698 free_functions::load(b, format)
1699}
1700
1701#[cfg(test)]
1702mod bench {
1703 #[bench]
1704 #[cfg(feature = "benchmarks")]
1705 fn bench_conversion(b: &mut test::Bencher) {
1706 let a = super::DynamicImage::ImageRgb8(crate::ImageBuffer::new(1000, 1000));
1707 b.iter(|| a.to_luma8());
1708 b.bytes = 1000 * 1000 * 3;
1709 }
1710}
1711
1712#[cfg(test)]
1713mod test {
1714 use crate::metadata::{CicpColorPrimaries, CicpTransform};
1715 use crate::ConvertColorOptions;
1716 use crate::{color::ColorType, images::dynimage::Gray16Image};
1717 use crate::{metadata::Cicp, ImageBuffer, Luma, Rgb, Rgba};
1718
1719 #[test]
1720 fn test_empty_file() {
1721 assert!(super::load_from_memory(b"").is_err());
1722 }
1723
1724 #[cfg(feature = "jpeg")]
1725 #[test]
1726 fn image_dimensions() {
1727 let im_path = "./tests/images/jpg/progressive/cat.jpg";
1728 let dims = super::image_dimensions(im_path).unwrap();
1729 assert_eq!(dims, (320, 240));
1730 }
1731
1732 #[cfg(feature = "png")]
1733 #[test]
1734 fn open_16bpc_png() {
1735 let im_path = "./tests/images/png/16bpc/basn6a16.png";
1736 let image = super::open(im_path).unwrap();
1737 assert_eq!(image.color(), ColorType::Rgba16);
1738 }
1739
1740 fn test_grayscale(mut img: super::DynamicImage, alpha_discarded: bool) {
1741 use crate::{GenericImage as _, GenericImageView as _};
1742 img.put_pixel(0, 0, Rgba([255, 0, 0, 100]));
1743 let expected_alpha = if alpha_discarded { 255 } else { 100 };
1744 assert_eq!(
1745 img.grayscale().get_pixel(0, 0),
1746 Rgba([54, 54, 54, expected_alpha])
1747 );
1748 }
1749
1750 fn test_grayscale_alpha_discarded(img: super::DynamicImage) {
1751 test_grayscale(img, true);
1752 }
1753
1754 fn test_grayscale_alpha_preserved(img: super::DynamicImage) {
1755 test_grayscale(img, false);
1756 }
1757
1758 #[test]
1759 fn test_grayscale_luma8() {
1760 test_grayscale_alpha_discarded(super::DynamicImage::new_luma8(1, 1));
1761 test_grayscale_alpha_discarded(super::DynamicImage::new(1, 1, ColorType::L8));
1762 }
1763
1764 #[test]
1765 fn test_grayscale_luma_a8() {
1766 test_grayscale_alpha_preserved(super::DynamicImage::new_luma_a8(1, 1));
1767 test_grayscale_alpha_preserved(super::DynamicImage::new(1, 1, ColorType::La8));
1768 }
1769
1770 #[test]
1771 fn test_grayscale_rgb8() {
1772 test_grayscale_alpha_discarded(super::DynamicImage::new_rgb8(1, 1));
1773 test_grayscale_alpha_discarded(super::DynamicImage::new(1, 1, ColorType::Rgb8));
1774 }
1775
1776 #[test]
1777 fn test_grayscale_rgba8() {
1778 test_grayscale_alpha_preserved(super::DynamicImage::new_rgba8(1, 1));
1779 test_grayscale_alpha_preserved(super::DynamicImage::new(1, 1, ColorType::Rgba8));
1780 }
1781
1782 #[test]
1783 fn test_grayscale_luma16() {
1784 test_grayscale_alpha_discarded(super::DynamicImage::new_luma16(1, 1));
1785 test_grayscale_alpha_discarded(super::DynamicImage::new(1, 1, ColorType::L16));
1786 }
1787
1788 #[test]
1789 fn test_grayscale_luma_a16() {
1790 test_grayscale_alpha_preserved(super::DynamicImage::new_luma_a16(1, 1));
1791 test_grayscale_alpha_preserved(super::DynamicImage::new(1, 1, ColorType::La16));
1792 }
1793
1794 #[test]
1795 fn test_grayscale_rgb16() {
1796 test_grayscale_alpha_discarded(super::DynamicImage::new_rgb16(1, 1));
1797 test_grayscale_alpha_discarded(super::DynamicImage::new(1, 1, ColorType::Rgb16));
1798 }
1799
1800 #[test]
1801 fn test_grayscale_rgba16() {
1802 test_grayscale_alpha_preserved(super::DynamicImage::new_rgba16(1, 1));
1803 test_grayscale_alpha_preserved(super::DynamicImage::new(1, 1, ColorType::Rgba16));
1804 }
1805
1806 #[test]
1807 fn test_grayscale_rgb32f() {
1808 test_grayscale_alpha_discarded(super::DynamicImage::new_rgb32f(1, 1));
1809 test_grayscale_alpha_discarded(super::DynamicImage::new(1, 1, ColorType::Rgb32F));
1810 }
1811
1812 #[test]
1813 fn test_grayscale_rgba32f() {
1814 test_grayscale_alpha_preserved(super::DynamicImage::new_rgba32f(1, 1));
1815 test_grayscale_alpha_preserved(super::DynamicImage::new(1, 1, ColorType::Rgba32F));
1816 }
1817
1818 #[test]
1819 fn test_dynamic_image_default_implementation() {
1820 #[derive(Default)]
1823 #[allow(dead_code)]
1824 struct Foo {
1825 _image: super::DynamicImage,
1826 }
1827 }
1828
1829 #[test]
1830 fn test_to_vecu8() {
1831 let _ = super::DynamicImage::new_luma8(1, 1).into_bytes();
1832 let _ = super::DynamicImage::new_luma16(1, 1).into_bytes();
1833 }
1834
1835 #[test]
1836 fn issue_1705_can_turn_16bit_image_into_bytes() {
1837 let pixels = vec![65535u16; 64 * 64];
1838 let img = ImageBuffer::from_vec(64, 64, pixels).unwrap();
1839
1840 let img = super::DynamicImage::ImageLuma16(img);
1841 assert!(img.as_luma16().is_some());
1842
1843 let bytes: Vec<u8> = img.into_bytes();
1844 assert_eq!(bytes, vec![0xFF; 64 * 64 * 2]);
1845 }
1846
1847 #[test]
1848 fn test_convert_to() {
1849 use crate::Luma;
1850 let image_luma8 = super::DynamicImage::new_luma8(1, 1);
1851 let image_luma16 = super::DynamicImage::new_luma16(1, 1);
1852 assert_eq!(image_luma8.to_luma16(), image_luma16.to_luma16());
1853
1854 let conv: Gray16Image = image_luma8.to();
1856 assert_eq!(image_luma8.to_luma16(), conv);
1857
1858 let converted = image_luma8.to::<Luma<u16>>();
1860 assert_eq!(image_luma8.to_luma16(), converted);
1861 }
1862
1863 #[test]
1864 fn color_conversion_srgb_p3() {
1865 let mut source = super::DynamicImage::ImageRgb8({
1866 ImageBuffer::from_fn(128, 128, |_, _| Rgb([255, 0, 0]))
1867 });
1868
1869 let mut target = super::DynamicImage::ImageRgba8({
1870 ImageBuffer::from_fn(128, 128, |_, _| Rgba(Default::default()))
1871 });
1872
1873 source.set_rgb_primaries(Cicp::SRGB.primaries);
1874 source.set_transfer_function(Cicp::SRGB.transfer);
1875 target.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
1876 target.set_transfer_function(Cicp::DISPLAY_P3.transfer);
1877
1878 let result = target.copy_from_color_space(&source, Default::default());
1879
1880 assert!(result.is_ok(), "{result:?}");
1881 let target = target.as_rgba8().expect("Sample type unchanged");
1882 assert_eq!(target[(0, 0)], Rgba([234u8, 51, 35, 255]));
1883 }
1884
1885 #[test]
1886 fn color_conversion_preserves_sample() {
1887 let mut source = super::DynamicImage::ImageRgb16({
1888 ImageBuffer::from_fn(128, 128, |_, _| Rgb([u16::MAX, 0, 0]))
1889 });
1890
1891 let mut target = super::DynamicImage::ImageRgba8({
1892 ImageBuffer::from_fn(128, 128, |_, _| Rgba(Default::default()))
1893 });
1894
1895 source.set_rgb_primaries(Cicp::SRGB.primaries);
1896 source.set_transfer_function(Cicp::SRGB.transfer);
1897 target.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
1898 target.set_transfer_function(Cicp::DISPLAY_P3.transfer);
1899
1900 let result = target.copy_from_color_space(&source, Default::default());
1901
1902 assert!(result.is_ok(), "{result:?}");
1903 let target = target.as_rgba8().expect("Sample type unchanged");
1904 assert_eq!(target[(0, 0)], Rgba([234u8, 51, 35, 255]));
1905 }
1906
1907 #[test]
1908 fn color_conversion_preserves_sample_in_fastpath() {
1909 let source = super::DynamicImage::ImageRgb16({
1910 ImageBuffer::from_fn(128, 128, |_, _| Rgb([u16::MAX, 0, 0]))
1911 });
1912
1913 let mut target = super::DynamicImage::ImageRgba8({
1914 ImageBuffer::from_fn(128, 128, |_, _| Rgba(Default::default()))
1915 });
1916
1917 let result = target.copy_from_color_space(&source, Default::default());
1919
1920 assert!(result.is_ok(), "{result:?}");
1921 let target = target.as_rgba8().expect("Sample type unchanged");
1922 assert_eq!(target[(0, 0)], Rgba([255u8, 0, 0, 255]));
1923 }
1924
1925 #[test]
1926 fn color_conversion_rgb_to_luma() {
1927 let source = super::DynamicImage::ImageRgb16({
1928 ImageBuffer::from_fn(128, 128, |_, _| Rgb([0, u16::MAX, 0]))
1929 });
1930
1931 let mut target = super::DynamicImage::ImageLuma8({
1932 ImageBuffer::from_fn(128, 128, |_, _| Luma(Default::default()))
1933 });
1934
1935 let result = target.copy_from_color_space(&source, Default::default());
1937
1938 assert!(result.is_ok(), "{result:?}");
1939 target.as_luma8().expect("Sample type unchanged");
1941 }
1942
1943 #[test]
1944 fn copy_color_space_coverage() {
1945 const TYPES: [ColorType; 10] = [
1946 ColorType::L8,
1947 ColorType::La8,
1948 ColorType::Rgb8,
1949 ColorType::Rgba8,
1950 ColorType::L16,
1951 ColorType::La16,
1952 ColorType::Rgb16,
1953 ColorType::Rgba16,
1954 ColorType::Rgb32F,
1955 ColorType::Rgba32F,
1956 ];
1957
1958 let transform =
1959 CicpTransform::new(Cicp::SRGB, Cicp::DISPLAY_P3).expect("Failed to create transform");
1960
1961 for from in TYPES {
1962 for to in TYPES {
1963 let mut source = super::DynamicImage::new(16, 16, from);
1964 let mut target = super::DynamicImage::new(16, 16, to);
1965
1966 source.set_rgb_primaries(Cicp::SRGB.primaries);
1967 source.set_transfer_function(Cicp::SRGB.transfer);
1968
1969 target.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
1970 target.set_transfer_function(Cicp::DISPLAY_P3.transfer);
1971
1972 target
1973 .copy_from_color_space(
1974 &source,
1975 ConvertColorOptions {
1976 transform: Some(transform.clone()),
1977 ..Default::default()
1978 },
1979 )
1980 .expect("Failed to convert color space");
1981 }
1982 }
1983 }
1984
1985 #[test]
1986 fn apply_color_space() {
1987 let mut buffer = super::DynamicImage::ImageRgb8({
1988 ImageBuffer::from_fn(128, 128, |_, _| Rgb([u8::MAX, 0, 0]))
1989 });
1990
1991 buffer.set_rgb_primaries(Cicp::SRGB.primaries);
1992 buffer.set_transfer_function(Cicp::SRGB.transfer);
1993
1994 buffer
1995 .apply_color_space(Cicp::DISPLAY_P3, Default::default())
1996 .unwrap();
1997
1998 let target = buffer.as_rgb8().expect("Sample type unchanged");
1999 assert_eq!(target[(0, 0)], Rgb([234u8, 51, 35]));
2000 }
2001
2002 #[test]
2003 fn apply_color_space_coverage() {
2004 const TYPES: [ColorType; 10] = [
2005 ColorType::L8,
2006 ColorType::La8,
2007 ColorType::Rgb8,
2008 ColorType::Rgba8,
2009 ColorType::L16,
2010 ColorType::La16,
2011 ColorType::Rgb16,
2012 ColorType::Rgba16,
2013 ColorType::Rgb32F,
2014 ColorType::Rgba32F,
2015 ];
2016
2017 let transform =
2018 CicpTransform::new(Cicp::SRGB, Cicp::DISPLAY_P3).expect("Failed to create transform");
2019
2020 for buffer in TYPES {
2021 let mut buffer = super::DynamicImage::new(16, 16, buffer);
2022
2023 buffer.set_rgb_primaries(Cicp::SRGB.primaries);
2024 buffer.set_transfer_function(Cicp::SRGB.transfer);
2025
2026 buffer
2027 .apply_color_space(
2028 Cicp::DISPLAY_P3,
2029 ConvertColorOptions {
2030 transform: Some(transform.clone()),
2031 ..Default::default()
2032 },
2033 )
2034 .expect("Failed to convert color space");
2035 }
2036 }
2037
2038 #[test]
2039 fn convert_color_space() {
2040 let mut buffer = super::DynamicImage::ImageRgb16({
2041 ImageBuffer::from_fn(128, 128, |_, _| Rgb([u16::MAX, 0, 0]))
2042 });
2043
2044 buffer.set_rgb_primaries(Cicp::SRGB.primaries);
2045 buffer.set_transfer_function(Cicp::SRGB.transfer);
2046
2047 buffer
2048 .convert_color_space(Cicp::DISPLAY_P3, Default::default(), ColorType::Rgb8)
2049 .unwrap();
2050
2051 let target = buffer.as_rgb8().expect("Sample type now rgb8");
2052 assert_eq!(target[(0, 0)], Rgb([234u8, 51, 35]));
2053 }
2054
2055 #[test]
2056 fn into_luma_is_color_space_aware() {
2057 let mut buffer = super::DynamicImage::ImageRgb16({
2058 ImageBuffer::from_fn(128, 128, |_, _| Rgb([u16::MAX, 0, 0]))
2059 });
2060
2061 buffer.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
2062 buffer.set_transfer_function(Cicp::DISPLAY_P3.transfer);
2063
2064 let luma8 = buffer.clone().into_luma8();
2065 assert_eq!(luma8[(0, 0)], Luma([58u8]));
2066 assert_eq!(luma8.color_space(), Cicp::DISPLAY_P3);
2067
2068 buffer.set_rgb_primaries(Cicp::SRGB.primaries);
2069
2070 let luma8 = buffer.clone().into_luma8();
2071 assert_eq!(luma8[(0, 0)], Luma([54u8]));
2072 assert_ne!(luma8.color_space(), Cicp::DISPLAY_P3);
2073 }
2074
2075 #[test]
2076 fn from_luma_is_color_space_aware() {
2077 let mut buffer = super::DynamicImage::ImageLuma16({
2078 ImageBuffer::from_fn(128, 128, |_, _| Luma([u16::MAX]))
2079 });
2080
2081 buffer.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
2082 buffer.set_transfer_function(Cicp::DISPLAY_P3.transfer);
2083
2084 let rgb8 = buffer.clone().into_rgb8();
2085 assert_eq!(rgb8[(0, 0)], Rgb([u8::MAX; 3]));
2086 assert_eq!(rgb8.color_space(), Cicp::DISPLAY_P3);
2087
2088 buffer.set_rgb_primaries(Cicp::SRGB.primaries);
2089
2090 let rgb8 = buffer.clone().into_rgb8();
2091 assert_eq!(rgb8[(0, 0)], Rgb([u8::MAX; 3]));
2092 assert_ne!(rgb8.color_space(), Cicp::DISPLAY_P3);
2093 }
2094
2095 #[test]
2096 fn from_luma_for_all_chromaticities() {
2097 const CHROMA: &[CicpColorPrimaries] = &[
2098 (CicpColorPrimaries::SRgb),
2099 (CicpColorPrimaries::RgbM),
2100 (CicpColorPrimaries::RgbB),
2101 (CicpColorPrimaries::Bt601),
2102 (CicpColorPrimaries::Rgb240m),
2103 (CicpColorPrimaries::GenericFilm),
2104 (CicpColorPrimaries::Rgb2020),
2105 (CicpColorPrimaries::Xyz),
2107 (CicpColorPrimaries::SmpteRp431),
2108 (CicpColorPrimaries::SmpteRp432),
2109 (CicpColorPrimaries::Industry22),
2110 (CicpColorPrimaries::Unspecified),
2112 ];
2113
2114 let mut buffer = super::DynamicImage::ImageLuma16({
2115 ImageBuffer::from_fn(128, 128, |_, _| Luma([u16::MAX]))
2116 });
2117
2118 for &chroma in CHROMA {
2119 buffer.set_rgb_primaries(chroma);
2120 let rgb = buffer.to_rgb8();
2121 assert_eq!(
2122 rgb[(0, 0)],
2123 Rgb([u8::MAX; 3]),
2124 "Failed for chroma: {chroma:?}"
2125 );
2126 }
2127 }
2128
2129 #[test]
2130 fn from_rgb_for_all_chromaticities() {
2131 const CHROMA: &[(CicpColorPrimaries, [u8; 3], u8)] = &[
2134 (CicpColorPrimaries::SRgb, [54, 182, 18], 143),
2135 (CicpColorPrimaries::RgbM, [76, 150, 29], 114),
2136 (CicpColorPrimaries::RgbB, [57, 180, 18], 141),
2137 (CicpColorPrimaries::Bt601, [54, 179, 22], 139),
2138 (CicpColorPrimaries::Rgb240m, [54, 179, 22], 139),
2139 (CicpColorPrimaries::GenericFilm, [65, 173, 17], 135),
2140 (CicpColorPrimaries::Rgb2020, [67, 173, 15], 136),
2141 (CicpColorPrimaries::Xyz, [0, 255, 0], 255),
2143 (CicpColorPrimaries::SmpteRp431, [53, 184, 18], 145),
2144 (CicpColorPrimaries::SmpteRp432, [58, 176, 20], 137),
2145 (CicpColorPrimaries::Industry22, [59, 171, 24], 131),
2146 (CicpColorPrimaries::Unspecified, [54, 182, 18], 143),
2148 ];
2149
2150 let mut buffer = super::DynamicImage::ImageRgb8({
2151 ImageBuffer::from_fn(128, 128, |_, _| Rgb([u8::MAX; 3]))
2152 });
2153
2154 for &(chroma, rgb, luma) in CHROMA {
2155 buffer.set_rgb_primaries(chroma);
2156
2157 for px in buffer.as_mut_rgb8().unwrap().pixels_mut() {
2158 px.0 = rgb;
2159 }
2160
2161 let buf = buffer.to_luma8();
2162 assert_eq!(buf[(0, 0)], Luma([luma]), "Failed for chroma: {chroma:?}");
2163 }
2164 }
2165
2166 #[test]
2167 fn convert_color_space_coverage() {
2168 const TYPES: [ColorType; 10] = [
2169 ColorType::L8,
2170 ColorType::La8,
2171 ColorType::Rgb8,
2172 ColorType::Rgba8,
2173 ColorType::L16,
2174 ColorType::La16,
2175 ColorType::Rgb16,
2176 ColorType::Rgba16,
2177 ColorType::Rgb32F,
2178 ColorType::Rgba32F,
2179 ];
2180
2181 let transform =
2182 CicpTransform::new(Cicp::SRGB, Cicp::DISPLAY_P3).expect("Failed to create transform");
2183
2184 for from in TYPES {
2185 for to in TYPES {
2186 let mut buffer = super::DynamicImage::new(16, 16, from);
2187
2188 buffer.set_rgb_primaries(Cicp::SRGB.primaries);
2189 buffer.set_transfer_function(Cicp::SRGB.transfer);
2190
2191 let options = ConvertColorOptions {
2192 transform: Some(transform.clone()),
2193 ..Default::default()
2194 };
2195
2196 buffer
2197 .convert_color_space(Cicp::DISPLAY_P3, options, to)
2198 .expect("Failed to convert color space");
2199 }
2200 }
2201 }
2202
2203 #[cfg(feature = "png")]
2206 #[test]
2207 fn color_space_independent_imageops() {
2208 let im_path = "./tests/images/png/16bpc/basn6a16.png";
2209
2210 let mut image = super::open(im_path).unwrap();
2211 let mut clone = image.clone();
2212
2213 image.set_color_space(Cicp::SRGB).unwrap();
2214 clone.set_color_space(Cicp::DISPLAY_P3).unwrap();
2215
2216 const IMAGEOPS: &[&dyn Fn(&super::DynamicImage) -> super::DynamicImage] = &[
2217 &|img| img.resize(32, 32, crate::imageops::FilterType::Lanczos3),
2218 &|img| img.resize_exact(32, 32, crate::imageops::FilterType::Lanczos3),
2219 &|img| img.thumbnail(8, 8),
2220 &|img| img.thumbnail_exact(8, 8),
2221 &|img| img.resize_to_fill(32, 32, crate::imageops::FilterType::Lanczos3),
2222 &|img| img.blur(1.0),
2223 &|img| {
2224 img.blur_advanced(
2225 crate::imageops::GaussianBlurParameters::new_anisotropic_kernel_size(1.0, 2.0),
2226 )
2227 },
2228 &|img| img.fast_blur(1.0),
2229 &|img| img.unsharpen(1.0, 3),
2230 &|img| img.filter3x3(&[0.0, -1.0, 0.0, -1.0, 5.0, -1.0, 0.0, -1.0, 0.0]),
2231 &|img| img.adjust_contrast(0.5),
2232 &|img| img.brighten(10),
2233 &|img| img.huerotate(180),
2234 ];
2235
2236 for (idx, &op) in IMAGEOPS.iter().enumerate() {
2237 let result_a = op(&image);
2238 let result_b = op(&clone);
2239 assert_eq!(result_a.color_space(), image.color_space(), "{idx}");
2240 assert_eq!(result_b.color_space(), clone.color_space(), "{idx}");
2241
2242 assert_ne!(result_a, result_b, "{idx}");
2243 assert_eq!(result_a.as_bytes(), result_b.as_bytes(), "{idx}");
2244 }
2245 }
2246}