image/io/encoder.rs
1use crate::error::{ImageFormatHint, ImageResult, UnsupportedError, UnsupportedErrorKind};
2use crate::{ColorType, DynamicImage, ExtendedColorType};
3
4/// Nominally public but DO NOT expose this type.
5///
6/// To be somewhat sure here's a compile fail test:
7///
8/// ```compile_fail
9/// use image::MethodSealedToImage;
10/// ```
11///
12/// ```compile_fail
13/// use image::io::MethodSealedToImage;
14/// ```
15///
16/// The same implementation strategy for a partially public trait is used in the standard library,
17/// for the different effect of forbidding `Error::type_id` overrides thus making them reliable for
18/// their calls through the `dyn` version of the trait.
19///
20/// Read more: <https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/>
21#[derive(Clone, Copy)]
22pub struct MethodSealedToImage;
23
24/// The trait all encoders implement
25pub trait ImageEncoder {
26 /// Writes all the bytes in an image to the encoder.
27 ///
28 /// This function takes a slice of bytes of the pixel data of the image
29 /// and encodes them. Just like for [`ImageDecoder::read_image`], no particular
30 /// alignment is required and data is expected to be in native endian.
31 /// The implementation will reorder the endianness as necessary for the target encoding format.
32 ///
33 /// # Panics
34 ///
35 /// Panics if `width * height * color_type.bytes_per_pixel() != buf.len()`.
36 fn write_image(
37 self,
38 buf: &[u8],
39 width: u32,
40 height: u32,
41 color_type: ExtendedColorType,
42 ) -> ImageResult<()>;
43
44 /// Set the ICC profile to use for the image.
45 ///
46 /// This function is a no-op for formats that don't support ICC profiles.
47 /// For formats that do support ICC profiles, the profile will be embedded
48 /// in the image when it is saved.
49 ///
50 /// # Errors
51 ///
52 /// This function returns an error if the format does not support ICC profiles.
53 fn set_icc_profile(&mut self, icc_profile: Vec<u8>) -> Result<(), UnsupportedError> {
54 let _ = icc_profile;
55 Err(UnsupportedError::from_format_and_kind(
56 ImageFormatHint::Unknown,
57 UnsupportedErrorKind::GenericFeature(
58 "ICC profiles are not supported for this format".into(),
59 ),
60 ))
61 }
62
63 /// Set the EXIF metadata to use for the image.
64 ///
65 /// This function is a no-op for formats that don't support EXIF metadata.
66 /// For formats that do support EXIF metadata, the metadata will be embedded
67 /// in the image when it is saved.
68 ///
69 /// # Errors
70 ///
71 /// This function returns an error if the format does not support EXIF metadata or if the
72 /// encoder doesn't implement saving EXIF metadata yet.
73 fn set_exif_metadata(&mut self, exif: Vec<u8>) -> Result<(), UnsupportedError> {
74 let _ = exif;
75 Err(UnsupportedError::from_format_and_kind(
76 ImageFormatHint::Unknown,
77 UnsupportedErrorKind::GenericFeature(
78 "EXIF metadata is not supported for this format".into(),
79 ),
80 ))
81 }
82
83 /// Convert the image to a compatible format for the encoder. This is used by the encoding
84 /// methods on `DynamicImage`.
85 ///
86 /// Note that this is method is sealed to the crate and effectively pub(crate) due to the
87 /// argument type not being nameable.
88 #[doc(hidden)]
89 fn make_compatible_img(
90 &self,
91 _: MethodSealedToImage,
92 _input: &DynamicImage,
93 ) -> Option<DynamicImage> {
94 None
95 }
96}
97
98pub(crate) trait ImageEncoderBoxed: ImageEncoder {
99 fn write_image(
100 self: Box<Self>,
101 buf: &'_ [u8],
102 width: u32,
103 height: u32,
104 color: ExtendedColorType,
105 ) -> ImageResult<()>;
106}
107impl<T: ImageEncoder> ImageEncoderBoxed for T {
108 fn write_image(
109 self: Box<Self>,
110 buf: &'_ [u8],
111 width: u32,
112 height: u32,
113 color: ExtendedColorType,
114 ) -> ImageResult<()> {
115 (*self).write_image(buf, width, height, color)
116 }
117}
118
119/// Implement `dynimage_conversion_sequence` for the common case of supporting only 8-bit colors
120/// (with and without alpha).
121#[allow(unused)]
122pub(crate) fn dynimage_conversion_8bit(img: &DynamicImage) -> Option<DynamicImage> {
123 use ColorType::*;
124
125 match img.color() {
126 Rgb8 | Rgba8 | L8 | La8 => None,
127 L16 => Some(img.to_luma8().into()),
128 La16 => Some(img.to_luma_alpha8().into()),
129 Rgb16 | Rgb32F => Some(img.to_rgb8().into()),
130 Rgba16 | Rgba32F => Some(img.to_rgba8().into()),
131 }
132}