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