1use crate::Raster;
4use image::{ImageDecoder, ImageReader};
5use std::{
6 io::Cursor,
7 sync::atomic::{AtomicBool, Ordering},
8};
9
10#[cfg(all(feature = "image-worker", unix))]
11mod backend;
12#[cfg(all(feature = "image-native", target_os = "macos"))]
13mod macos;
14#[cfg(all(feature = "image-worker", unix))]
15mod png_rows;
16#[cfg(all(feature = "image-worker", unix))]
17mod portable;
18#[cfg(feature = "image-worker")]
19mod protocol;
20#[cfg(feature = "image-worker")]
21mod regions;
22#[cfg(all(feature = "image-worker", unix))]
23mod svg;
24#[cfg(feature = "image-worker")]
25pub mod worker;
26#[cfg(feature = "image-worker")]
27pub use regions::*;
28#[cfg(feature = "image-worker")]
29pub const MAX_FILE_BYTES: u64 = 256 * 1024 * 1024;
30#[cfg(all(feature = "image-worker", unix))]
31const SVG_SOURCE_LIMIT: usize = 32 * 1024 * 1024;
32#[cfg(all(feature = "image-worker", unix))]
33const PORTABLE_PIXEL_LIMIT: u64 = 16 * 1024 * 1024;
34pub const CANCELLED: &str = "Image request cancelled";
35#[cfg(feature = "image-worker")]
36fn check(cancel: &AtomicBool) -> Result<(), String> {
37 if cancel.load(Ordering::Relaxed) {
38 Err(CANCELLED.into())
39 } else {
40 Ok(())
41 }
42}
43
44#[derive(Clone, Copy, Debug)]
45pub enum Format {
46 Png,
47 Jpeg,
48}
49#[derive(Clone, Copy, Debug, PartialEq, Eq)]
50#[non_exhaustive]
51pub enum Error {
52 Invalid,
53 Limit,
54 Cancelled,
55}
56impl std::fmt::Display for Error {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.write_str(match self {
59 Self::Invalid => "Invalid image",
60 Self::Limit => "Image exceeds decoding limits",
61 Self::Cancelled => CANCELLED,
62 })
63 }
64}
65impl std::error::Error for Error {}
66
67#[derive(Clone, Copy, Debug)]
71pub struct Limits {
72 pub encoded_bytes: usize,
73 pub source_edge: u32,
74 pub source_pixels: u64,
75 pub decoded_bytes: u64,
76 pub output_pixels: u64,
77}
78impl Default for Limits {
79 fn default() -> Self {
80 Self {
81 encoded_bytes: 8 * 1024 * 1024,
82 source_edge: 8192,
83 source_pixels: 4_000_000,
84 decoded_bytes: 32 * 1024 * 1024,
85 output_pixels: 1_000_000,
86 }
87 }
88}
89impl Limits {
90 fn valid(self) -> bool {
91 self.encoded_bytes > 0
92 && self.encoded_bytes <= 32 * 1024 * 1024
93 && self.source_edge > 0
94 && self.source_edge <= 32768
95 && self.source_pixels > 0
96 && self.source_pixels <= 16 * 1024 * 1024
97 && self.decoded_bytes > 0
98 && self.decoded_bytes <= 128 * 1024 * 1024
99 && self.output_pixels > 0
100 && self.output_pixels <= 4 * 1024 * 1024
101 }
102}
103pub fn decode(
107 bytes: &[u8],
108 format: Format,
109 bounds: (u32, u32),
110 limits: Limits,
111 cancel: &AtomicBool,
112) -> Result<Raster, Error> {
113 let check = || {
114 if cancel.load(Ordering::Relaxed) {
115 Err(Error::Cancelled)
116 } else {
117 Ok(())
118 }
119 };
120 check()?;
121 if !limits.valid() || bytes.len() > limits.encoded_bytes || bounds.0 == 0 || bounds.1 == 0 {
122 return Err(Error::Limit);
123 }
124 let format = match format {
125 Format::Png => image::ImageFormat::Png,
126 Format::Jpeg => image::ImageFormat::Jpeg,
127 };
128 let mut codec_limits = image::Limits::default();
129 codec_limits.max_image_width = Some(limits.source_edge);
130 codec_limits.max_image_height = Some(limits.source_edge);
131 codec_limits.max_alloc = Some(limits.decoded_bytes);
132 let mut reader = ImageReader::with_format(Cursor::new(bytes), format);
133 reader.limits(codec_limits);
134 let mut decoder = reader.into_decoder().map_err(image_error)?;
135 let (width, height) = decoder.dimensions();
136 if width == 0
137 || height == 0
138 || width > limits.source_edge
139 || height > limits.source_edge
140 || u64::from(width) * u64::from(height) > limits.source_pixels
141 || decoder.total_bytes() > limits.decoded_bytes
142 {
143 return Err(Error::Limit);
144 }
145 let orientation = decoder.orientation().map_err(image_error)?;
146 check()?;
147 let mut image = image::DynamicImage::from_decoder(decoder).map_err(image_error)?;
148 check()?;
149 image.apply_orientation(orientation);
150 let (original_width, original_height) = (image.width(), image.height());
151 let scale = (bounds.0 as f64 / original_width as f64)
152 .min(bounds.1 as f64 / original_height as f64)
153 .min(1.)
154 .min(
155 (limits.output_pixels as f64 / (original_width as f64 * original_height as f64)).sqrt(),
156 );
157 let mut size = (
158 (original_width as f64 * scale).floor().max(1.) as u32,
159 (original_height as f64 * scale).floor().max(1.) as u32,
160 );
161 size.0 = size.0.min(limits.output_pixels as u32);
163 size.1 = size
164 .1
165 .min((limits.output_pixels / u64::from(size.0)) as u32);
166 let image = if size != (original_width, original_height) {
167 image.thumbnail_exact(size.0, size.1)
168 } else {
169 image
170 };
171 check()?;
172 Ok(Raster {
173 width: image.width(),
174 height: image.height(),
175 original_width,
176 original_height,
177 rgba: image.into_rgba8().into_raw(),
178 })
179}
180fn image_error(error: image::ImageError) -> Error {
181 if matches!(error, image::ImageError::Limits(_)) {
182 Error::Limit
183 } else {
184 Error::Invalid
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 fn png() -> Vec<u8> {
192 let image =
193 image::RgbaImage::from_fn(37, 19, |x, y| image::Rgba([x as u8, y as u8, 92, 128]));
194 let mut bytes = Cursor::new(Vec::new());
195 image.write_to(&mut bytes, image::ImageFormat::Png).unwrap();
196 bytes.into_inner()
197 }
198 #[test]
199 fn preserves_pixels_and_applies_caller_budgets_before_decode() {
200 let bytes = png();
201 let cancel = AtomicBool::new(false);
202 let limits = Limits::default();
203 let result = decode(&bytes, Format::Png, (100, 100), limits, &cancel).unwrap();
204 assert_eq!((result.width, result.height), (37, 19));
205 assert_eq!(&result.rgba[..4], &[0, 0, 92, 128]);
206 for limits in [
207 Limits {
208 encoded_bytes: 1,
209 ..limits
210 },
211 Limits {
212 source_pixels: 10,
213 ..limits
214 },
215 Limits {
216 decoded_bytes: 1,
217 ..limits
218 },
219 Limits {
220 output_pixels: 0,
221 ..limits
222 },
223 ] {
224 assert!(matches!(
225 decode(&bytes, Format::Png, (100, 100), limits, &cancel),
226 Err(Error::Limit)
227 ));
228 }
229 for (width, height) in [(1, 200), (200, 1)] {
230 let mut encoded = Cursor::new(Vec::new());
231 image::RgbaImage::new(width, height)
232 .write_to(&mut encoded, image::ImageFormat::Png)
233 .unwrap();
234 let result = decode(
235 encoded.get_ref(),
236 Format::Png,
237 (1000, 1000),
238 Limits {
239 output_pixels: 3,
240 ..limits
241 },
242 &cancel,
243 )
244 .unwrap();
245 assert!(result.width > 0 && result.height > 0);
246 assert!(u64::from(result.width) * u64::from(result.height) <= 3);
247 }
248 let result = decode(
249 &bytes,
250 Format::Png,
251 (100, 100),
252 Limits {
253 output_pixels: 100,
254 ..limits
255 },
256 &cancel,
257 )
258 .unwrap();
259 assert!(result.width * result.height <= 100);
260 assert!(matches!(
261 decode(
262 &bytes,
263 Format::Png,
264 (100, 100),
265 limits,
266 &AtomicBool::new(true)
267 ),
268 Err(Error::Cancelled)
269 ));
270 }
271}