Skip to main content

ai_image/io/
decoder.rs

1use crate::animation::Frames;
2use crate::color::{ColorType, ExtendedColorType};
3use crate::error::ImageResult;
4use crate::metadata::{LoopCount, Orientation};
5use alloc::{boxed::Box, vec::Vec};
6
7/// The trait that all decoders implement
8pub trait ImageDecoder {
9    /// Returns a tuple containing the width and height of the image
10    fn dimensions(&self) -> (u32, u32);
11
12    /// Returns the color type of the image data produced by this decoder
13    fn color_type(&self) -> ColorType;
14
15    /// Returns the color type of the image file before decoding
16    fn original_color_type(&self) -> ExtendedColorType {
17        self.color_type().into()
18    }
19
20    /// Returns the ICC color profile embedded in the image, or `Ok(None)` if the image does not have one.
21    ///
22    /// For formats that don't support embedded profiles this function should always return `Ok(None)`.
23    fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
24        Ok(None)
25    }
26
27    /// Returns the raw [Exif](https://en.wikipedia.org/wiki/Exif) chunk, if it is present.
28    /// A third-party crate such as [`kamadak-exif`](https://docs.rs/kamadak-exif/) is required to actually parse it.
29    ///
30    /// For formats that don't support embedded profiles this function should always return `Ok(None)`.
31    fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
32        Ok(None)
33    }
34
35    /// Returns the raw [XMP](https://en.wikipedia.org/wiki/Extensible_Metadata_Platform) chunk, if it is present.
36    /// A third-party crate such as [`roxmltree`](https://docs.rs/roxmltree/) is required to actually parse it.
37    ///
38    /// For formats that don't support embedded profiles this function should always return `Ok(None)`.
39    fn xmp_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
40        Ok(None)
41    }
42
43    /// Returns the raw [IPTC](https://en.wikipedia.org/wiki/IPTC_Information_Interchange_Model) chunk, if it is present.
44    ///
45    /// For formats that don't support embedded profiles this function should always return `Ok(None)`.
46    fn iptc_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
47        Ok(None)
48    }
49
50    /// Returns the orientation of the image.
51    ///
52    /// This is usually obtained from the Exif metadata, if present. Formats that don't support
53    /// indicating orientation in their image metadata will return `Ok(Orientation::NoTransforms)`.
54    fn orientation(&mut self) -> ImageResult<Orientation> {
55        Ok(self
56            .exif_metadata()?
57            .and_then(|chunk| Orientation::from_exif_chunk(&chunk))
58            .unwrap_or(Orientation::NoTransforms))
59    }
60
61    /// Returns the total number of bytes in the decoded image.
62    ///
63    /// This is the size of the buffer that must be passed to `read_image` or
64    /// `read_image_with_progress`. The returned value may exceed `usize::MAX`, in
65    /// which case it isn't actually possible to construct a buffer to decode all the image data
66    /// into. If, however, the size does not fit in a u64 then `u64::MAX` is returned.
67    fn total_bytes(&self) -> u64 {
68        let dimensions = self.dimensions();
69        let total_pixels = u64::from(dimensions.0) * u64::from(dimensions.1);
70        let bytes_per_pixel = u64::from(self.color_type().bytes_per_pixel());
71        total_pixels.saturating_mul(bytes_per_pixel)
72    }
73
74    /// Returns all the bytes in the image.
75    ///
76    /// This function takes a slice of bytes and writes the pixel data of the image into it.
77    /// `buf` does not need to be aligned to any byte boundaries. However,
78    /// alignment to 2 or 4 byte boundaries may result in small performance
79    /// improvements for certain decoder implementations.
80    ///
81    /// The returned pixel data will always be in native endian. This allows
82    /// `[u16]` and `[f32]` slices to be cast to `[u8]` and used for this method.
83    ///
84    /// # Panics
85    ///
86    /// This function panics if `buf.len() != self.total_bytes()`.
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// # use ai_image::ImageDecoder;
92    /// fn read_16bit_image(decoder: impl ImageDecoder) -> Vec<u16> {
93    ///     let mut buf: Vec<u16> = vec![0; (decoder.total_bytes() / 2) as usize];
94    ///     decoder.read_image(bytemuck::cast_slice_mut(&mut buf));
95    ///     buf
96    /// }
97    /// ```
98    fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
99    where
100        Self: Sized;
101
102    /// Set the decoder to have the specified limits. See [`Limits`] for the different kinds of
103    /// limits that is possible to set.
104    ///
105    /// Note to implementors: make sure you call [`Limits::check_support`] so that
106    /// decoding fails if any unsupported strict limits are set. Also make sure
107    /// you call [`Limits::check_dimensions`] to check the `max_image_width` and
108    /// `max_image_height` limits.
109    ///
110    /// **Note**: By default, _no_ limits are defined. This may be changed in future major version
111    /// increases.
112    ///
113    /// [`Limits`]: ./io/struct.Limits.html
114    /// [`Limits::check_support`]: ./io/struct.Limits.html#method.check_support
115    /// [`Limits::check_dimensions`]: ./io/struct.Limits.html#method.check_dimensions
116    fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> {
117        limits.check_support(&crate::LimitSupport::default())?;
118        let (width, height) = self.dimensions();
119        limits.check_dimensions(width, height)?;
120        Ok(())
121    }
122
123    /// Use `read_image` instead; this method is an implementation detail needed so the trait can
124    /// be object safe.
125    ///
126    /// Note to implementors: This method should be implemented by calling `read_image` on
127    /// the boxed decoder...
128    /// ```ignore
129    /// fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
130    ///     (*self).read_image(buf)
131    /// }
132    /// ```
133    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()>;
134}
135
136#[deny(clippy::missing_trait_methods)]
137impl<T: ?Sized + ImageDecoder> ImageDecoder for Box<T> {
138    fn dimensions(&self) -> (u32, u32) {
139        (**self).dimensions()
140    }
141    fn color_type(&self) -> ColorType {
142        (**self).color_type()
143    }
144    fn original_color_type(&self) -> ExtendedColorType {
145        (**self).original_color_type()
146    }
147    fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
148        (**self).icc_profile()
149    }
150    fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
151        (**self).exif_metadata()
152    }
153    fn xmp_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
154        (**self).xmp_metadata()
155    }
156    fn iptc_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
157        (**self).iptc_metadata()
158    }
159    fn orientation(&mut self) -> ImageResult<Orientation> {
160        (**self).orientation()
161    }
162    fn total_bytes(&self) -> u64 {
163        (**self).total_bytes()
164    }
165    fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
166    where
167        Self: Sized,
168    {
169        T::read_image_boxed(self, buf)
170    }
171    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
172        T::read_image_boxed(*self, buf)
173    }
174    fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> {
175        (**self).set_limits(limits)
176    }
177}
178
179/// Specialized image decoding not be supported by all formats
180pub trait ImageDecoderRect: ImageDecoder {
181    /// Decode a rectangular section of the image.
182    ///
183    /// This function takes a slice of bytes and writes the pixel data of the image into it.
184    /// The rectangle is specified by the x and y coordinates of the top left corner, the width
185    /// and height of the rectangle, and the row pitch of the buffer. The row pitch is the number
186    /// of bytes between the start of one row and the start of the next row. The row pitch must be
187    /// at least as large as the width of the rectangle in bytes.
188    fn read_rect(
189        &mut self,
190        x: u32,
191        y: u32,
192        width: u32,
193        height: u32,
194        buf: &mut [u8],
195        row_pitch: usize,
196    ) -> ImageResult<()>;
197}
198
199/// `AnimationDecoder` trait
200pub trait AnimationDecoder<'a> {
201    /// Consume the decoder producing a series of frames.
202    fn into_frames(self) -> Frames<'a>;
203    /// Loop count of the animated image.
204    ///
205    /// By default, indicates the animation should run once. Formats may implement other defaults
206    /// and read such metadata from the file.
207    fn loop_count(&self) -> LoopCount {
208        LoopCount::Finite(core::num::NonZeroU32::new(1).unwrap())
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use super::{ColorType, ImageDecoder, ImageResult};
215
216    #[test]
217    fn total_bytes_overflow() {
218        struct D;
219        impl ImageDecoder for D {
220            fn color_type(&self) -> ColorType {
221                ColorType::Rgb8
222            }
223            fn dimensions(&self) -> (u32, u32) {
224                (0xffff_ffff, 0xffff_ffff)
225            }
226            fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> {
227                unimplemented!()
228            }
229            fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
230                (*self).read_image(buf)
231            }
232        }
233        assert_eq!(D.total_bytes(), u64::MAX);
234
235        let v: ImageResult<Vec<u8>> = crate::io::free_functions::decoder_to_vec(D);
236        assert!(v.is_err());
237    }
238}