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}