Line | Count | Source |
1 | | //! Input and output of images. |
2 | | |
3 | | use std::io; |
4 | | use std::io::Read as _; |
5 | | |
6 | | /// The decoder traits. |
7 | | pub(crate) mod decoder; |
8 | | /// The encoder traits. |
9 | | pub(crate) mod encoder; |
10 | | |
11 | | pub(crate) mod format; |
12 | | pub(crate) mod free_functions; |
13 | | pub(crate) mod image_reader_type; |
14 | | pub(crate) mod limits; |
15 | | |
16 | | pub use decoder::{ |
17 | | DecodedAnimationAttributes, DecodedImageAttributes, DecodedMetadataHint, FormatAttributes, |
18 | | SequenceControl, |
19 | | }; |
20 | | |
21 | | pub use image_reader_type::DecodedImageMetadata; |
22 | | |
23 | | /// Adds `read_exact_vec` |
24 | | pub(crate) trait ReadExt { |
25 | | fn read_exact_vec(&mut self, vec: &mut Vec<u8>, len: usize) -> io::Result<()>; |
26 | | } |
27 | | |
28 | | impl<R: io::Read> ReadExt for R { |
29 | 656 | fn read_exact_vec(&mut self, vec: &mut Vec<u8>, len: usize) -> io::Result<()> { |
30 | 656 | let initial_len = vec.len(); |
31 | 656 | vec.try_reserve(len)?; |
32 | 656 | match self.take(len as u64).read_to_end(vec) { |
33 | 656 | Ok(read) if read == len => Ok(()), |
34 | 114 | fail => { |
35 | 114 | vec.truncate(initial_len); |
36 | 114 | Err(fail.err().unwrap_or(io::ErrorKind::UnexpectedEof.into())) |
37 | | } |
38 | | } |
39 | 656 | } |
40 | | } |
41 | | |
42 | | /// Communicate the layout of an image. |
43 | | /// |
44 | | /// Describes a packed rectangular layout with given bit-depth in |
45 | | /// [`ImageDecoder::peek_layout`](crate::ImageDecoder::peek_layout). Standard layouts from `image` |
46 | | /// are row-major with no padding between rows and pixels packed by consecutive channels. |
47 | | #[non_exhaustive] |
48 | | pub struct ImageLayout { |
49 | | /// The color model of each pixel. |
50 | | pub color: crate::ColorType, |
51 | | /// The number of pixels in the horizontal direction. |
52 | | pub width: u32, |
53 | | /// The number of pixels in the vertical direction. |
54 | | pub height: u32, |
55 | | } |
56 | | |
57 | | impl ImageLayout { |
58 | | /// A layout matching a [`DynamicImage`][`crate::DynamicImage`] of the given dimensions and |
59 | | /// color type. |
60 | 152k | pub fn new(w: u32, h: u32, color: crate::ColorType) -> ImageLayout { |
61 | 152k | ImageLayout { |
62 | 152k | color, |
63 | 152k | width: w, |
64 | 152k | height: h, |
65 | 152k | } |
66 | 152k | } |
67 | | |
68 | | /// A layout with no pixels, of the given [`ColorType`][`crate::ColorType`]. |
69 | | /// |
70 | | /// This is equivalent to `ImageLayout::new(0, 0, color)`. |
71 | 0 | pub fn empty(color: crate::ColorType) -> Self { |
72 | 0 | ImageLayout { |
73 | 0 | color, |
74 | 0 | width: 0, |
75 | 0 | height: 0, |
76 | 0 | } |
77 | 0 | } |
78 | | |
79 | | /// Return width and height as a tuple, consistent with |
80 | | /// [`GenericImageView::dimensions`][`crate::GenericImageView::dimensions`]. |
81 | | /// |
82 | | /// Note that this refers to underlying pixel matrix, not the orientation of the image as |
83 | | /// indicated to be viewed in user facing applications by metadata. |
84 | 6.85k | pub fn dimensions(&self) -> (u32, u32) { |
85 | 6.85k | (self.width, self.height) |
86 | 6.85k | } |
87 | | |
88 | | /// The total number of bytes in the described image. |
89 | | /// |
90 | | /// See also [`DecodedLayout::total_bytes`]. |
91 | 120k | pub fn total_bytes(&self) -> u64 { |
92 | 120k | let ImageLayout { width, height, .. } = *self; |
93 | 120k | let total_pixels = u64::from(width) * u64::from(height); |
94 | 120k | let bytes_per_pixel = u64::from(self.color.bytes_per_pixel()); |
95 | 120k | total_pixels.saturating_mul(bytes_per_pixel) |
96 | 120k | } |
97 | | |
98 | | /// Checks if the provided dimensions would overflow a `u64`. |
99 | | /// |
100 | | /// FIXME: instead have `try_total_bytes() -> Result<u64, _>`? But should it return an |
101 | | /// unsupported error (as formats that use this do) or should it instead return another error |
102 | | /// since the method is then unrelated to a format. |
103 | 3.09k | pub(crate) fn total_bytes_overflows_u64(&self) -> bool { |
104 | 3.09k | let &ImageLayout { width, height, .. } = self; |
105 | 3.09k | let bytes_per_pixel: u8 = self.color.bytes_per_pixel(); |
106 | 3.09k | u64::from(width) * u64::from(height) > u64::MAX / u64::from(bytes_per_pixel) |
107 | 3.09k | } |
108 | | } |
109 | | |
110 | | /// Describes the next image for |
111 | | /// [`ImageDecoder::prepare_image`](`crate::ImageDecoder::prepare_image`). |
112 | | /// |
113 | | /// For external crates constructing an instance, use [`Self::new`] with the intended color type |
114 | | /// and then fill in all applicable fields. This initializes the layout, a minimal descriptor of an |
115 | | /// expected [`DynamicImage`][`crate::DynamicImage`] (or equivalently sized other buffer). |
116 | | #[non_exhaustive] |
117 | | pub struct DecoderPreparedImage { |
118 | | /// The layout of the primary image data. |
119 | | pub layout: ImageLayout, |
120 | | } |
121 | | |
122 | | /// Defaults all fields except the layout. |
123 | | impl From<ImageLayout> for DecoderPreparedImage { |
124 | 5.46k | fn from(layout: ImageLayout) -> Self { |
125 | 5.46k | DecoderPreparedImage { layout } |
126 | 5.46k | } |
127 | | } |
128 | | |
129 | | impl DecoderPreparedImage { |
130 | | /// A layout matching a [`DynamicImage`][`crate::DynamicImage`] of the given dimensions and |
131 | | /// color type. |
132 | 143k | pub fn new(w: u32, h: u32, color: crate::ColorType) -> DecoderPreparedImage { |
133 | 143k | DecoderPreparedImage { |
134 | 143k | layout: ImageLayout::new(w, h, color), |
135 | 143k | } |
136 | 143k | } |
137 | | |
138 | | /// The total number of bytes in the decoded image. |
139 | | /// |
140 | | /// This is the size of the buffer that must be passed to |
141 | | /// [`ImageDecoder::read_image`](`crate::ImageDecoder::read_image`) or |
142 | | /// `read_image_with_progress`. The returned value may exceed `usize::MAX`, in which case it |
143 | | /// isn't actually possible to construct a buffer to decode all the image data into. If the |
144 | | /// size does not fit in a u64 then `u64::MAX` is returned. For all practical purposes all |
145 | | /// platforms will fail to allocate that much memory. |
146 | 118k | pub fn total_bytes(&self) -> u64 { |
147 | 118k | self.layout.total_bytes() |
148 | 118k | } |
149 | | } |