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}