1 | #![allow (clippy::too_many_arguments)] |
2 | use std::ffi::OsStr; |
3 | use std::io::{self, Write}; |
4 | use std::mem::size_of; |
5 | use std::ops::{Deref, DerefMut}; |
6 | use std::path::Path; |
7 | |
8 | #[cfg (feature = "serde" )] |
9 | use serde::{Deserialize, Serialize}; |
10 | |
11 | use crate::color::{ColorType, ExtendedColorType}; |
12 | use crate::error::{ |
13 | ImageError, ImageFormatHint, ImageResult, LimitError, LimitErrorKind, ParameterError, |
14 | ParameterErrorKind, UnsupportedError, UnsupportedErrorKind, |
15 | }; |
16 | use crate::math::Rect; |
17 | use crate::metadata::Orientation; |
18 | use crate::traits::Pixel; |
19 | use crate::ImageBuffer; |
20 | |
21 | use crate::animation::Frames; |
22 | |
23 | /// An enumeration of supported image formats. |
24 | /// Not all formats support both encoding and decoding. |
25 | #[derive (Clone, Copy, PartialEq, Eq, Debug, Hash)] |
26 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
27 | #[non_exhaustive ] |
28 | pub enum ImageFormat { |
29 | /// An Image in PNG Format |
30 | Png, |
31 | |
32 | /// An Image in JPEG Format |
33 | Jpeg, |
34 | |
35 | /// An Image in GIF Format |
36 | Gif, |
37 | |
38 | /// An Image in WEBP Format |
39 | WebP, |
40 | |
41 | /// An Image in general PNM Format |
42 | Pnm, |
43 | |
44 | /// An Image in TIFF Format |
45 | Tiff, |
46 | |
47 | /// An Image in TGA Format |
48 | Tga, |
49 | |
50 | /// An Image in DDS Format |
51 | Dds, |
52 | |
53 | /// An Image in BMP Format |
54 | Bmp, |
55 | |
56 | /// An Image in ICO Format |
57 | Ico, |
58 | |
59 | /// An Image in Radiance HDR Format |
60 | Hdr, |
61 | |
62 | /// An Image in OpenEXR Format |
63 | OpenExr, |
64 | |
65 | /// An Image in farbfeld Format |
66 | Farbfeld, |
67 | |
68 | /// An Image in AVIF Format |
69 | Avif, |
70 | |
71 | /// An Image in QOI Format |
72 | Qoi, |
73 | |
74 | /// An Image in PCX Format |
75 | Pcx, |
76 | } |
77 | |
78 | impl ImageFormat { |
79 | /// Return the image format specified by a path's file extension. |
80 | /// |
81 | /// # Example |
82 | /// |
83 | /// ``` |
84 | /// use image::ImageFormat; |
85 | /// |
86 | /// let format = ImageFormat::from_extension("jpg" ); |
87 | /// assert_eq!(format, Some(ImageFormat::Jpeg)); |
88 | /// ``` |
89 | #[inline ] |
90 | pub fn from_extension<S>(ext: S) -> Option<Self> |
91 | where |
92 | S: AsRef<OsStr>, |
93 | { |
94 | // thin wrapper function to strip generics |
95 | fn inner(ext: &OsStr) -> Option<ImageFormat> { |
96 | let ext = ext.to_str()?.to_ascii_lowercase(); |
97 | |
98 | Some(match ext.as_str() { |
99 | "avif" => ImageFormat::Avif, |
100 | "jpg" | "jpeg" | "jfif" => ImageFormat::Jpeg, |
101 | "png" | "apng" => ImageFormat::Png, |
102 | "gif" => ImageFormat::Gif, |
103 | "webp" => ImageFormat::WebP, |
104 | "tif" | "tiff" => ImageFormat::Tiff, |
105 | "tga" => ImageFormat::Tga, |
106 | "dds" => ImageFormat::Dds, |
107 | "bmp" => ImageFormat::Bmp, |
108 | "ico" => ImageFormat::Ico, |
109 | "hdr" => ImageFormat::Hdr, |
110 | "exr" => ImageFormat::OpenExr, |
111 | "pbm" | "pam" | "ppm" | "pgm" => ImageFormat::Pnm, |
112 | "ff" => ImageFormat::Farbfeld, |
113 | "qoi" => ImageFormat::Qoi, |
114 | "pcx" => ImageFormat::Pcx, |
115 | _ => return None, |
116 | }) |
117 | } |
118 | |
119 | inner(ext.as_ref()) |
120 | } |
121 | |
122 | /// Return the image format specified by the path's file extension. |
123 | /// |
124 | /// # Example |
125 | /// |
126 | /// ``` |
127 | /// use image::ImageFormat; |
128 | /// |
129 | /// let format = ImageFormat::from_path("images/ferris.png" )?; |
130 | /// assert_eq!(format, ImageFormat::Png); |
131 | /// |
132 | /// # Ok::<(), image::error::ImageError>(()) |
133 | /// ``` |
134 | #[inline ] |
135 | pub fn from_path<P>(path: P) -> ImageResult<Self> |
136 | where |
137 | P: AsRef<Path>, |
138 | { |
139 | // thin wrapper function to strip generics |
140 | fn inner(path: &Path) -> ImageResult<ImageFormat> { |
141 | let exact_ext = path.extension(); |
142 | exact_ext |
143 | .and_then(ImageFormat::from_extension) |
144 | .ok_or_else(|| { |
145 | let format_hint = match exact_ext { |
146 | None => ImageFormatHint::Unknown, |
147 | Some(os) => ImageFormatHint::PathExtension(os.into()), |
148 | }; |
149 | ImageError::Unsupported(format_hint.into()) |
150 | }) |
151 | } |
152 | |
153 | inner(path.as_ref()) |
154 | } |
155 | |
156 | /// Return the image format specified by a MIME type. |
157 | /// |
158 | /// # Example |
159 | /// |
160 | /// ``` |
161 | /// use image::ImageFormat; |
162 | /// |
163 | /// let format = ImageFormat::from_mime_type("image/png" ).unwrap(); |
164 | /// assert_eq!(format, ImageFormat::Png); |
165 | /// ``` |
166 | pub fn from_mime_type<M>(mime_type: M) -> Option<Self> |
167 | where |
168 | M: AsRef<str>, |
169 | { |
170 | match mime_type.as_ref() { |
171 | "image/avif" => Some(ImageFormat::Avif), |
172 | "image/jpeg" => Some(ImageFormat::Jpeg), |
173 | "image/png" => Some(ImageFormat::Png), |
174 | "image/gif" => Some(ImageFormat::Gif), |
175 | "image/webp" => Some(ImageFormat::WebP), |
176 | "image/tiff" => Some(ImageFormat::Tiff), |
177 | "image/x-targa" | "image/x-tga" => Some(ImageFormat::Tga), |
178 | "image/vnd-ms.dds" => Some(ImageFormat::Dds), |
179 | "image/bmp" => Some(ImageFormat::Bmp), |
180 | "image/x-icon" | "image/vnd.microsoft.icon" => Some(ImageFormat::Ico), |
181 | "image/vnd.radiance" => Some(ImageFormat::Hdr), |
182 | "image/x-exr" => Some(ImageFormat::OpenExr), |
183 | "image/x-portable-bitmap" |
184 | | "image/x-portable-graymap" |
185 | | "image/x-portable-pixmap" |
186 | | "image/x-portable-anymap" => Some(ImageFormat::Pnm), |
187 | // Qoi's MIME type is being worked on. |
188 | // See: https://github.com/phoboslab/qoi/issues/167 |
189 | "image/x-qoi" => Some(ImageFormat::Qoi), |
190 | "image/vnd.zbrush.pcx" | "image/x-pcx" => Some(ImageFormat::Pcx), |
191 | _ => None, |
192 | } |
193 | } |
194 | |
195 | /// Return the MIME type for this image format or "application/octet-stream" if no MIME type |
196 | /// exists for the format. |
197 | /// |
198 | /// Some notes on a few of the MIME types: |
199 | /// |
200 | /// - The portable anymap format has a separate MIME type for the pixmap, graymap and bitmap |
201 | /// formats, but this method returns the general "image/x-portable-anymap" MIME type. |
202 | /// - The Targa format has two common MIME types, "image/x-targa" and "image/x-tga"; this |
203 | /// method returns "image/x-targa" for that format. |
204 | /// - The QOI MIME type is still a work in progress. This method returns "image/x-qoi" for |
205 | /// that format. |
206 | /// |
207 | /// # Example |
208 | /// |
209 | /// ``` |
210 | /// use image::ImageFormat; |
211 | /// |
212 | /// let mime_type = ImageFormat::Png.to_mime_type(); |
213 | /// assert_eq!(mime_type, "image/png" ); |
214 | /// ``` |
215 | #[must_use ] |
216 | pub fn to_mime_type(&self) -> &'static str { |
217 | match self { |
218 | ImageFormat::Avif => "image/avif" , |
219 | ImageFormat::Jpeg => "image/jpeg" , |
220 | ImageFormat::Png => "image/png" , |
221 | ImageFormat::Gif => "image/gif" , |
222 | ImageFormat::WebP => "image/webp" , |
223 | ImageFormat::Tiff => "image/tiff" , |
224 | // the targa MIME type has two options, but this one seems to be used more |
225 | ImageFormat::Tga => "image/x-targa" , |
226 | ImageFormat::Dds => "image/vnd-ms.dds" , |
227 | ImageFormat::Bmp => "image/bmp" , |
228 | ImageFormat::Ico => "image/x-icon" , |
229 | ImageFormat::Hdr => "image/vnd.radiance" , |
230 | ImageFormat::OpenExr => "image/x-exr" , |
231 | // return the most general MIME type |
232 | ImageFormat::Pnm => "image/x-portable-anymap" , |
233 | // Qoi's MIME type is being worked on. |
234 | // See: https://github.com/phoboslab/qoi/issues/167 |
235 | ImageFormat::Qoi => "image/x-qoi" , |
236 | // farbfeld's MIME type taken from https://www.wikidata.org/wiki/Q28206109 |
237 | ImageFormat::Farbfeld => "application/octet-stream" , |
238 | ImageFormat::Pcx => "image/vnd.zbrush.pcx" , |
239 | } |
240 | } |
241 | |
242 | /// Return if the `ImageFormat` can be decoded by the lib. |
243 | #[inline ] |
244 | #[must_use ] |
245 | pub fn can_read(&self) -> bool { |
246 | // Needs to be updated once a new variant's decoder is added to free_functions.rs::load |
247 | match self { |
248 | ImageFormat::Png => true, |
249 | ImageFormat::Gif => true, |
250 | ImageFormat::Jpeg => true, |
251 | ImageFormat::WebP => true, |
252 | ImageFormat::Tiff => true, |
253 | ImageFormat::Tga => true, |
254 | ImageFormat::Dds => false, |
255 | ImageFormat::Bmp => true, |
256 | ImageFormat::Ico => true, |
257 | ImageFormat::Hdr => true, |
258 | ImageFormat::OpenExr => true, |
259 | ImageFormat::Pnm => true, |
260 | ImageFormat::Farbfeld => true, |
261 | ImageFormat::Avif => true, |
262 | ImageFormat::Qoi => true, |
263 | ImageFormat::Pcx => true, |
264 | } |
265 | } |
266 | |
267 | /// Return if the `ImageFormat` can be encoded by the lib. |
268 | #[inline ] |
269 | #[must_use ] |
270 | pub fn can_write(&self) -> bool { |
271 | // Needs to be updated once a new variant's encoder is added to free_functions.rs::save_buffer_with_format_impl |
272 | match self { |
273 | ImageFormat::Gif => true, |
274 | ImageFormat::Ico => true, |
275 | ImageFormat::Jpeg => true, |
276 | ImageFormat::Png => true, |
277 | ImageFormat::Bmp => true, |
278 | ImageFormat::Tiff => true, |
279 | ImageFormat::Tga => true, |
280 | ImageFormat::Pnm => true, |
281 | ImageFormat::Farbfeld => true, |
282 | ImageFormat::Avif => true, |
283 | ImageFormat::WebP => true, |
284 | ImageFormat::Hdr => true, |
285 | ImageFormat::OpenExr => true, |
286 | ImageFormat::Dds => false, |
287 | ImageFormat::Qoi => true, |
288 | ImageFormat::Pcx => false, |
289 | } |
290 | } |
291 | |
292 | /// Return a list of applicable extensions for this format. |
293 | /// |
294 | /// All currently recognized image formats specify at least on extension but for future |
295 | /// compatibility you should not rely on this fact. The list may be empty if the format has no |
296 | /// recognized file representation, for example in case it is used as a purely transient memory |
297 | /// format. |
298 | /// |
299 | /// The method name `extensions` remains reserved for introducing another method in the future |
300 | /// that yields a slice of `OsStr` which is blocked by several features of const evaluation. |
301 | #[must_use ] |
302 | pub fn extensions_str(self) -> &'static [&'static str] { |
303 | match self { |
304 | ImageFormat::Png => &["png" ], |
305 | ImageFormat::Jpeg => &["jpg" , "jpeg" ], |
306 | ImageFormat::Gif => &["gif" ], |
307 | ImageFormat::WebP => &["webp" ], |
308 | ImageFormat::Pnm => &["pbm" , "pam" , "ppm" , "pgm" ], |
309 | ImageFormat::Tiff => &["tiff" , "tif" ], |
310 | ImageFormat::Tga => &["tga" ], |
311 | ImageFormat::Dds => &["dds" ], |
312 | ImageFormat::Bmp => &["bmp" ], |
313 | ImageFormat::Ico => &["ico" ], |
314 | ImageFormat::Hdr => &["hdr" ], |
315 | ImageFormat::OpenExr => &["exr" ], |
316 | ImageFormat::Farbfeld => &["ff" ], |
317 | // According to: https://aomediacodec.github.io/av1-avif/#mime-registration |
318 | ImageFormat::Avif => &["avif" ], |
319 | ImageFormat::Qoi => &["qoi" ], |
320 | ImageFormat::Pcx => &["pcx" ], |
321 | } |
322 | } |
323 | |
324 | /// Return the `ImageFormat`s which are enabled for reading. |
325 | #[inline ] |
326 | #[must_use ] |
327 | pub fn reading_enabled(&self) -> bool { |
328 | match self { |
329 | ImageFormat::Png => cfg!(feature = "png" ), |
330 | ImageFormat::Gif => cfg!(feature = "gif" ), |
331 | ImageFormat::Jpeg => cfg!(feature = "jpeg" ), |
332 | ImageFormat::WebP => cfg!(feature = "webp" ), |
333 | ImageFormat::Tiff => cfg!(feature = "tiff" ), |
334 | ImageFormat::Tga => cfg!(feature = "tga" ), |
335 | ImageFormat::Bmp => cfg!(feature = "bmp" ), |
336 | ImageFormat::Ico => cfg!(feature = "ico" ), |
337 | ImageFormat::Hdr => cfg!(feature = "hdr" ), |
338 | ImageFormat::OpenExr => cfg!(feature = "exr" ), |
339 | ImageFormat::Pnm => cfg!(feature = "pnm" ), |
340 | ImageFormat::Farbfeld => cfg!(feature = "ff" ), |
341 | ImageFormat::Avif => cfg!(feature = "avif" ), |
342 | ImageFormat::Qoi => cfg!(feature = "qoi" ), |
343 | ImageFormat::Pcx => cfg!(feature = "pcx" ), |
344 | ImageFormat::Dds => false, |
345 | } |
346 | } |
347 | |
348 | /// Return the `ImageFormat`s which are enabled for writing. |
349 | #[inline ] |
350 | #[must_use ] |
351 | pub fn writing_enabled(&self) -> bool { |
352 | match self { |
353 | ImageFormat::Gif => cfg!(feature = "gif" ), |
354 | ImageFormat::Ico => cfg!(feature = "ico" ), |
355 | ImageFormat::Jpeg => cfg!(feature = "jpeg" ), |
356 | ImageFormat::Png => cfg!(feature = "png" ), |
357 | ImageFormat::Bmp => cfg!(feature = "bmp" ), |
358 | ImageFormat::Tiff => cfg!(feature = "tiff" ), |
359 | ImageFormat::Tga => cfg!(feature = "tga" ), |
360 | ImageFormat::Pnm => cfg!(feature = "pnm" ), |
361 | ImageFormat::Farbfeld => cfg!(feature = "ff" ), |
362 | ImageFormat::Avif => cfg!(feature = "avif" ), |
363 | ImageFormat::WebP => cfg!(feature = "webp" ), |
364 | ImageFormat::OpenExr => cfg!(feature = "exr" ), |
365 | ImageFormat::Qoi => cfg!(feature = "qoi" ), |
366 | ImageFormat::Hdr => cfg!(feature = "hdr" ), |
367 | ImageFormat::Pcx => false, |
368 | ImageFormat::Dds => false, |
369 | } |
370 | } |
371 | |
372 | /// Return all `ImageFormat`s |
373 | pub fn all() -> impl Iterator<Item = ImageFormat> { |
374 | [ |
375 | ImageFormat::Gif, |
376 | ImageFormat::Ico, |
377 | ImageFormat::Jpeg, |
378 | ImageFormat::Png, |
379 | ImageFormat::Bmp, |
380 | ImageFormat::Tiff, |
381 | ImageFormat::Tga, |
382 | ImageFormat::Pnm, |
383 | ImageFormat::Farbfeld, |
384 | ImageFormat::Avif, |
385 | ImageFormat::WebP, |
386 | ImageFormat::OpenExr, |
387 | ImageFormat::Qoi, |
388 | ImageFormat::Dds, |
389 | ImageFormat::Hdr, |
390 | ImageFormat::Pcx, |
391 | ] |
392 | .iter() |
393 | .copied() |
394 | } |
395 | } |
396 | |
397 | // This struct manages buffering associated with implementing `Read` and `Seek` on decoders that can |
398 | // must decode ranges of bytes at a time. |
399 | #[allow (dead_code)] |
400 | // When no image formats that use it are enabled |
401 | pub(crate) struct ImageReadBuffer { |
402 | scanline_bytes: usize, |
403 | buffer: Vec<u8>, |
404 | consumed: usize, |
405 | |
406 | total_bytes: u64, |
407 | offset: u64, |
408 | } |
409 | impl ImageReadBuffer { |
410 | /// Create a new `ImageReadBuffer`. |
411 | /// |
412 | /// Panics if `scanline_bytes` doesn't fit into a usize, because that would mean reading anything |
413 | /// from the image would take more RAM than the entire virtual address space. In other words, |
414 | /// actually using this struct would instantly OOM so just get it out of the way now. |
415 | #[allow (dead_code)] |
416 | // When no image formats that use it are enabled |
417 | pub(crate) fn new(scanline_bytes: u64, total_bytes: u64) -> Self { |
418 | Self { |
419 | scanline_bytes: usize::try_from(scanline_bytes).unwrap(), |
420 | buffer: Vec::new(), |
421 | consumed: 0, |
422 | total_bytes, |
423 | offset: 0, |
424 | } |
425 | } |
426 | |
427 | #[allow (dead_code)] |
428 | // When no image formats that use it are enabled |
429 | pub(crate) fn read<F>(&mut self, buf: &mut [u8], mut read_scanline: F) -> io::Result<usize> |
430 | where |
431 | F: FnMut(&mut [u8]) -> io::Result<usize>, |
432 | { |
433 | if self.buffer.len() == self.consumed { |
434 | if self.offset == self.total_bytes { |
435 | return Ok(0); |
436 | } else if buf.len() >= self.scanline_bytes { |
437 | // If there is nothing buffered and the user requested a full scanline worth of |
438 | // data, skip buffering. |
439 | let bytes_read = read_scanline(&mut buf[..self.scanline_bytes])?; |
440 | self.offset += u64::try_from(bytes_read).unwrap(); |
441 | return Ok(bytes_read); |
442 | } else { |
443 | // Lazily allocate buffer the first time that read is called with a buffer smaller |
444 | // than the scanline size. |
445 | if self.buffer.is_empty() { |
446 | self.buffer.resize(self.scanline_bytes, 0); |
447 | } |
448 | |
449 | self.consumed = 0; |
450 | let bytes_read = read_scanline(&mut self.buffer[..])?; |
451 | self.buffer.resize(bytes_read, 0); |
452 | self.offset += u64::try_from(bytes_read).unwrap(); |
453 | |
454 | assert!(bytes_read == self.scanline_bytes || self.offset == self.total_bytes); |
455 | } |
456 | } |
457 | |
458 | // Finally, copy bytes into output buffer. |
459 | let bytes_buffered = self.buffer.len() - self.consumed; |
460 | if bytes_buffered > buf.len() { |
461 | buf.copy_from_slice(&self.buffer[self.consumed..][..buf.len()]); |
462 | self.consumed += buf.len(); |
463 | Ok(buf.len()) |
464 | } else { |
465 | buf[..bytes_buffered].copy_from_slice(&self.buffer[self.consumed..][..bytes_buffered]); |
466 | self.consumed = self.buffer.len(); |
467 | Ok(bytes_buffered) |
468 | } |
469 | } |
470 | } |
471 | |
472 | /// Decodes a specific region of the image, represented by the rectangle |
473 | /// starting from ```x``` and ```y``` and having ```length``` and ```width``` |
474 | #[allow (dead_code)] |
475 | // When no image formats that use it are enabled |
476 | pub(crate) fn load_rect<D, F1, F2, E>( |
477 | x: u32, |
478 | y: u32, |
479 | width: u32, |
480 | height: u32, |
481 | buf: &mut [u8], |
482 | row_pitch: usize, |
483 | decoder: &mut D, |
484 | scanline_bytes: usize, |
485 | mut seek_scanline: F1, |
486 | mut read_scanline: F2, |
487 | ) -> ImageResult<()> |
488 | where |
489 | D: ImageDecoder, |
490 | F1: FnMut(&mut D, u64) -> io::Result<()>, |
491 | F2: FnMut(&mut D, &mut [u8]) -> Result<(), E>, |
492 | ImageError: From<E>, |
493 | { |
494 | let scanline_bytes = u64::try_from(scanline_bytes).unwrap(); |
495 | let row_pitch = u64::try_from(row_pitch).unwrap(); |
496 | |
497 | let (x, y, width, height) = ( |
498 | u64::from(x), |
499 | u64::from(y), |
500 | u64::from(width), |
501 | u64::from(height), |
502 | ); |
503 | let dimensions = decoder.dimensions(); |
504 | let bytes_per_pixel = u64::from(decoder.color_type().bytes_per_pixel()); |
505 | let row_bytes = bytes_per_pixel * u64::from(dimensions.0); |
506 | let total_bytes = width * height * bytes_per_pixel; |
507 | |
508 | assert!( |
509 | buf.len() >= usize::try_from(total_bytes).unwrap_or(usize::MAX), |
510 | "output buffer too short \n expected ` {}`, provided ` {}`" , |
511 | total_bytes, |
512 | buf.len() |
513 | ); |
514 | |
515 | let mut current_scanline = 0; |
516 | let mut tmp = Vec::new(); |
517 | let mut tmp_scanline = None; |
518 | |
519 | { |
520 | // Read a range of the image starting from byte number `start` and continuing until byte |
521 | // number `end`. Updates `current_scanline` and `bytes_read` appropriately. |
522 | let mut read_image_range = |
523 | |mut start: u64, end: u64, mut output: &mut [u8]| -> ImageResult<()> { |
524 | // If the first scanline we need is already stored in the temporary buffer, then handle |
525 | // it first. |
526 | let target_scanline = start / scanline_bytes; |
527 | if tmp_scanline == Some(target_scanline) { |
528 | let position = target_scanline * scanline_bytes; |
529 | let offset = start.saturating_sub(position); |
530 | let len = (end - start) |
531 | .min(scanline_bytes - offset) |
532 | .min(end - position); |
533 | |
534 | output |
535 | .write_all(&tmp[offset as usize..][..len as usize]) |
536 | .unwrap(); |
537 | start += len; |
538 | |
539 | if start == end { |
540 | return Ok(()); |
541 | } |
542 | } |
543 | |
544 | let target_scanline = start / scanline_bytes; |
545 | if target_scanline != current_scanline { |
546 | seek_scanline(decoder, target_scanline)?; |
547 | current_scanline = target_scanline; |
548 | } |
549 | |
550 | let mut position = current_scanline * scanline_bytes; |
551 | while position < end { |
552 | if position >= start && end - position >= scanline_bytes { |
553 | read_scanline(decoder, &mut output[..(scanline_bytes as usize)])?; |
554 | output = &mut output[scanline_bytes as usize..]; |
555 | } else { |
556 | tmp.resize(scanline_bytes as usize, 0u8); |
557 | read_scanline(decoder, &mut tmp)?; |
558 | tmp_scanline = Some(current_scanline); |
559 | |
560 | let offset = start.saturating_sub(position); |
561 | let len = (end - start) |
562 | .min(scanline_bytes - offset) |
563 | .min(end - position); |
564 | |
565 | output |
566 | .write_all(&tmp[offset as usize..][..len as usize]) |
567 | .unwrap(); |
568 | } |
569 | |
570 | current_scanline += 1; |
571 | position += scanline_bytes; |
572 | } |
573 | Ok(()) |
574 | }; |
575 | |
576 | if x + width > u64::from(dimensions.0) |
577 | || y + height > u64::from(dimensions.1) |
578 | || width == 0 |
579 | || height == 0 |
580 | { |
581 | return Err(ImageError::Parameter(ParameterError::from_kind( |
582 | ParameterErrorKind::DimensionMismatch, |
583 | ))); |
584 | } |
585 | if scanline_bytes > usize::MAX as u64 { |
586 | return Err(ImageError::Limits(LimitError::from_kind( |
587 | LimitErrorKind::InsufficientMemory, |
588 | ))); |
589 | } |
590 | |
591 | if x == 0 && width == u64::from(dimensions.0) && row_pitch == row_bytes { |
592 | let start = x * bytes_per_pixel + y * row_bytes; |
593 | let end = (x + width) * bytes_per_pixel + (y + height - 1) * row_bytes; |
594 | read_image_range(start, end, buf)?; |
595 | } else { |
596 | for (output_slice, row) in buf.chunks_mut(row_pitch as usize).zip(y..(y + height)) { |
597 | let start = x * bytes_per_pixel + row * row_bytes; |
598 | let end = (x + width) * bytes_per_pixel + row * row_bytes; |
599 | read_image_range(start, end, output_slice)?; |
600 | } |
601 | } |
602 | } |
603 | |
604 | // Seek back to the start |
605 | Ok(seek_scanline(decoder, 0)?) |
606 | } |
607 | |
608 | /// Reads all of the bytes of a decoder into a Vec<T>. No particular alignment |
609 | /// of the output buffer is guaranteed. |
610 | /// |
611 | /// Panics if there isn't enough memory to decode the image. |
612 | pub(crate) fn decoder_to_vec<T>(decoder: impl ImageDecoder) -> ImageResult<Vec<T>> |
613 | where |
614 | T: crate::traits::Primitive + bytemuck::Pod, |
615 | { |
616 | let total_bytes: Result = usize::try_from(decoder.total_bytes()); |
617 | if total_bytes.is_err() || total_bytes.unwrap() > isize::MAX as usize { |
618 | return Err(ImageError::Limits(LimitError::from_kind( |
619 | LimitErrorKind::InsufficientMemory, |
620 | ))); |
621 | } |
622 | |
623 | let mut buf: Vec = vec![num_traits::Zero::zero(); total_bytes.unwrap() / size_of::<T>()]; |
624 | decoder.read_image(buf:bytemuck::cast_slice_mut(buf.as_mut_slice()))?; |
625 | Ok(buf) |
626 | } |
627 | |
628 | /// The trait that all decoders implement |
629 | pub trait ImageDecoder { |
630 | /// Returns a tuple containing the width and height of the image |
631 | fn dimensions(&self) -> (u32, u32); |
632 | |
633 | /// Returns the color type of the image data produced by this decoder |
634 | fn color_type(&self) -> ColorType; |
635 | |
636 | /// Returns the color type of the image file before decoding |
637 | fn original_color_type(&self) -> ExtendedColorType { |
638 | self.color_type().into() |
639 | } |
640 | |
641 | /// Returns the ICC color profile embedded in the image, or `Ok(None)` if the image does not have one. |
642 | /// |
643 | /// For formats that don't support embedded profiles this function should always return `Ok(None)`. |
644 | fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> { |
645 | Ok(None) |
646 | } |
647 | |
648 | /// Returns the raw [Exif](https://en.wikipedia.org/wiki/Exif) chunk, if it is present. |
649 | /// A third-party crate such as [`kamadak-exif`](https://docs.rs/kamadak-exif/) is required to actually parse it. |
650 | /// |
651 | /// For formats that don't support embedded profiles this function should always return `Ok(None)`. |
652 | fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> { |
653 | Ok(None) |
654 | } |
655 | |
656 | /// Returns the orientation of the image. |
657 | /// |
658 | /// This is usually obtained from the Exif metadata, if present. Formats that don't support |
659 | /// indicating orientation in their image metadata will return `Ok(Orientation::NoTransforms)`. |
660 | fn orientation(&mut self) -> ImageResult<Orientation> { |
661 | Ok(self |
662 | .exif_metadata()? |
663 | .and_then(|chunk| Orientation::from_exif_chunk(&chunk)) |
664 | .unwrap_or(Orientation::NoTransforms)) |
665 | } |
666 | |
667 | /// Returns the total number of bytes in the decoded image. |
668 | /// |
669 | /// This is the size of the buffer that must be passed to `read_image` or |
670 | /// `read_image_with_progress`. The returned value may exceed `usize::MAX`, in |
671 | /// which case it isn't actually possible to construct a buffer to decode all the image data |
672 | /// into. If, however, the size does not fit in a u64 then `u64::MAX` is returned. |
673 | fn total_bytes(&self) -> u64 { |
674 | let dimensions = self.dimensions(); |
675 | let total_pixels = u64::from(dimensions.0) * u64::from(dimensions.1); |
676 | let bytes_per_pixel = u64::from(self.color_type().bytes_per_pixel()); |
677 | total_pixels.saturating_mul(bytes_per_pixel) |
678 | } |
679 | |
680 | /// Returns all the bytes in the image. |
681 | /// |
682 | /// This function takes a slice of bytes and writes the pixel data of the image into it. |
683 | /// Although not required, for certain color types callers may want to pass buffers which are |
684 | /// aligned to 2 or 4 byte boundaries to the slice can be cast to a [u16] or [u32]. To accommodate |
685 | /// such casts, the returned contents will always be in native endian. |
686 | /// |
687 | /// # Panics |
688 | /// |
689 | /// This function panics if `buf.len() != self.total_bytes()`. |
690 | /// |
691 | /// # Examples |
692 | /// |
693 | /// ```no_build |
694 | /// use zerocopy::{AsBytes, FromBytes}; |
695 | /// fn read_16bit_image(decoder: impl ImageDecoder) -> Vec<16> { |
696 | /// let mut buf: Vec<u16> = vec![0; decoder.total_bytes()/2]; |
697 | /// decoder.read_image(buf.as_bytes()); |
698 | /// buf |
699 | /// } |
700 | /// ``` |
701 | fn read_image(self, buf: &mut [u8]) -> ImageResult<()> |
702 | where |
703 | Self: Sized; |
704 | |
705 | /// Set the decoder to have the specified limits. See [`Limits`] for the different kinds of |
706 | /// limits that is possible to set. |
707 | /// |
708 | /// Note to implementors: make sure you call [`Limits::check_support`] so that |
709 | /// decoding fails if any unsupported strict limits are set. Also make sure |
710 | /// you call [`Limits::check_dimensions`] to check the `max_image_width` and |
711 | /// `max_image_height` limits. |
712 | /// |
713 | /// [`Limits`]: ./io/struct.Limits.html |
714 | /// [`Limits::check_support`]: ./io/struct.Limits.html#method.check_support |
715 | /// [`Limits::check_dimensions`]: ./io/struct.Limits.html#method.check_dimensions |
716 | fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> { |
717 | limits.check_support(&crate::LimitSupport::default())?; |
718 | let (width, height) = self.dimensions(); |
719 | limits.check_dimensions(width, height)?; |
720 | Ok(()) |
721 | } |
722 | |
723 | /// Use `read_image` instead; this method is an implementation detail needed so the trait can |
724 | /// be object safe. |
725 | /// |
726 | /// Note to implementors: This method should be implemented by calling `read_image` on |
727 | /// the boxed decoder... |
728 | /// ```no_build |
729 | /// fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> { |
730 | /// (*self).read_image(buf) |
731 | /// } |
732 | /// ``` |
733 | fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()>; |
734 | } |
735 | |
736 | impl<T: ?Sized + ImageDecoder> ImageDecoder for Box<T> { |
737 | fn dimensions(&self) -> (u32, u32) { |
738 | (**self).dimensions() |
739 | } |
740 | fn color_type(&self) -> ColorType { |
741 | (**self).color_type() |
742 | } |
743 | fn original_color_type(&self) -> ExtendedColorType { |
744 | (**self).original_color_type() |
745 | } |
746 | fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> { |
747 | (**self).icc_profile() |
748 | } |
749 | fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> { |
750 | (**self).exif_metadata() |
751 | } |
752 | fn total_bytes(&self) -> u64 { |
753 | (**self).total_bytes() |
754 | } |
755 | fn read_image(self, buf: &mut [u8]) -> ImageResult<()> |
756 | where |
757 | Self: Sized, |
758 | { |
759 | T::read_image_boxed(self, buf) |
760 | } |
761 | fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> { |
762 | T::read_image_boxed(*self, buf) |
763 | } |
764 | fn set_limits(&mut self, limits: crate::Limits) -> ImageResult<()> { |
765 | (**self).set_limits(limits) |
766 | } |
767 | } |
768 | |
769 | /// Specialized image decoding not be supported by all formats |
770 | pub trait ImageDecoderRect: ImageDecoder { |
771 | /// Decode a rectangular section of the image. |
772 | /// |
773 | /// This function takes a slice of bytes and writes the pixel data of the image into it. |
774 | /// The rectangle is specified by the x and y coordinates of the top left corner, the width |
775 | /// and height of the rectangle, and the row pitch of the buffer. The row pitch is the number |
776 | /// of bytes between the start of one row and the start of the next row. The row pitch must be |
777 | /// at least as large as the width of the rectangle in bytes. |
778 | fn read_rect( |
779 | &mut self, |
780 | x: u32, |
781 | y: u32, |
782 | width: u32, |
783 | height: u32, |
784 | buf: &mut [u8], |
785 | row_pitch: usize, |
786 | ) -> ImageResult<()>; |
787 | } |
788 | |
789 | /// `AnimationDecoder` trait |
790 | pub trait AnimationDecoder<'a> { |
791 | /// Consume the decoder producing a series of frames. |
792 | fn into_frames(self) -> Frames<'a>; |
793 | } |
794 | |
795 | /// The trait all encoders implement |
796 | pub trait ImageEncoder { |
797 | /// Writes all the bytes in an image to the encoder. |
798 | /// |
799 | /// This function takes a slice of bytes of the pixel data of the image |
800 | /// and encodes them. Unlike particular format encoders inherent impl encode |
801 | /// methods where endianness is not specified, here image data bytes should |
802 | /// always be in native endian. The implementor will reorder the endianness |
803 | /// as necessary for the target encoding format. |
804 | /// |
805 | /// See also `ImageDecoder::read_image` which reads byte buffers into |
806 | /// native endian. |
807 | /// |
808 | /// # Panics |
809 | /// |
810 | /// Panics if `width * height * color_type.bytes_per_pixel() != buf.len()`. |
811 | fn write_image( |
812 | self, |
813 | buf: &[u8], |
814 | width: u32, |
815 | height: u32, |
816 | color_type: ExtendedColorType, |
817 | ) -> ImageResult<()>; |
818 | |
819 | /// Set the ICC profile to use for the image. |
820 | /// |
821 | /// This function is a no-op for formats that don't support ICC profiles. |
822 | /// For formats that do support ICC profiles, the profile will be embedded |
823 | /// in the image when it is saved. |
824 | /// |
825 | /// # Errors |
826 | /// |
827 | /// This function returns an error if the format does not support ICC profiles. |
828 | fn set_icc_profile(&mut self, icc_profile: Vec<u8>) -> Result<(), UnsupportedError> { |
829 | let _ = icc_profile; |
830 | Err(UnsupportedError::from_format_and_kind( |
831 | ImageFormatHint::Unknown, |
832 | UnsupportedErrorKind::GenericFeature( |
833 | "ICC profiles are not supported for this format" .into(), |
834 | ), |
835 | )) |
836 | } |
837 | } |
838 | |
839 | /// Immutable pixel iterator |
840 | #[derive (Debug)] |
841 | pub struct Pixels<'a, I: ?Sized + 'a> { |
842 | image: &'a I, |
843 | x: u32, |
844 | y: u32, |
845 | width: u32, |
846 | height: u32, |
847 | } |
848 | |
849 | impl<I: GenericImageView> Iterator for Pixels<'_, I> { |
850 | type Item = (u32, u32, I::Pixel); |
851 | |
852 | fn next(&mut self) -> Option<(u32, u32, I::Pixel)> { |
853 | if self.x >= self.width { |
854 | self.x = 0; |
855 | self.y += 1; |
856 | } |
857 | |
858 | if self.y >= self.height { |
859 | None |
860 | } else { |
861 | let pixel: ::Pixel = self.image.get_pixel(self.x, self.y); |
862 | let p: (u32, u32, ::Pixel) = (self.x, self.y, pixel); |
863 | |
864 | self.x += 1; |
865 | |
866 | Some(p) |
867 | } |
868 | } |
869 | } |
870 | |
871 | impl<I: ?Sized> Clone for Pixels<'_, I> { |
872 | fn clone(&self) -> Self { |
873 | Pixels { ..*self } |
874 | } |
875 | } |
876 | |
877 | /// Trait to inspect an image. |
878 | /// |
879 | /// ``` |
880 | /// use image::{GenericImageView, Rgb, RgbImage}; |
881 | /// |
882 | /// let buffer = RgbImage::new(10, 10); |
883 | /// let image: &dyn GenericImageView<Pixel = Rgb<u8>> = &buffer; |
884 | /// ``` |
885 | pub trait GenericImageView { |
886 | /// The type of pixel. |
887 | type Pixel: Pixel; |
888 | |
889 | /// The width and height of this image. |
890 | fn dimensions(&self) -> (u32, u32); |
891 | |
892 | /// The width of this image. |
893 | fn width(&self) -> u32 { |
894 | let (w, _) = self.dimensions(); |
895 | w |
896 | } |
897 | |
898 | /// The height of this image. |
899 | fn height(&self) -> u32 { |
900 | let (_, h) = self.dimensions(); |
901 | h |
902 | } |
903 | |
904 | /// Returns true if this x, y coordinate is contained inside the image. |
905 | fn in_bounds(&self, x: u32, y: u32) -> bool { |
906 | let (width, height) = self.dimensions(); |
907 | x < width && y < height |
908 | } |
909 | |
910 | /// Returns the pixel located at (x, y). Indexed from top left. |
911 | /// |
912 | /// # Panics |
913 | /// |
914 | /// Panics if `(x, y)` is out of bounds. |
915 | fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel; |
916 | |
917 | /// Returns the pixel located at (x, y). Indexed from top left. |
918 | /// |
919 | /// This function can be implemented in a way that ignores bounds checking. |
920 | /// # Safety |
921 | /// |
922 | /// The coordinates must be [`in_bounds`] of the image. |
923 | /// |
924 | /// [`in_bounds`]: #method.in_bounds |
925 | unsafe fn unsafe_get_pixel(&self, x: u32, y: u32) -> Self::Pixel { |
926 | self.get_pixel(x, y) |
927 | } |
928 | |
929 | /// Returns an Iterator over the pixels of this image. |
930 | /// The iterator yields the coordinates of each pixel |
931 | /// along with their value |
932 | fn pixels(&self) -> Pixels<Self> |
933 | where |
934 | Self: Sized, |
935 | { |
936 | let (width, height) = self.dimensions(); |
937 | |
938 | Pixels { |
939 | image: self, |
940 | x: 0, |
941 | y: 0, |
942 | width, |
943 | height, |
944 | } |
945 | } |
946 | |
947 | /// Returns a subimage that is an immutable view into this image. |
948 | /// You can use [`GenericImage::sub_image`] if you need a mutable view instead. |
949 | /// The coordinates set the position of the top left corner of the view. |
950 | fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&Self> |
951 | where |
952 | Self: Sized, |
953 | { |
954 | assert!(u64::from(x) + u64::from(width) <= u64::from(self.width())); |
955 | assert!(u64::from(y) + u64::from(height) <= u64::from(self.height())); |
956 | SubImage::new(self, x, y, width, height) |
957 | } |
958 | } |
959 | |
960 | /// A trait for manipulating images. |
961 | pub trait GenericImage: GenericImageView { |
962 | /// Gets a reference to the mutable pixel at location `(x, y)`. Indexed from top left. |
963 | /// |
964 | /// # Panics |
965 | /// |
966 | /// Panics if `(x, y)` is out of bounds. |
967 | /// |
968 | /// Panics for dynamic images (this method is deprecated and will be removed). |
969 | /// |
970 | /// ## Known issues |
971 | /// |
972 | /// This requires the buffer to contain a unique set of continuous channels in the exact order |
973 | /// and byte representation that the pixel type requires. This is somewhat restrictive. |
974 | /// |
975 | /// TODO: Maybe use some kind of entry API? this would allow pixel type conversion on the fly |
976 | /// while still doing only one array lookup: |
977 | /// |
978 | /// ```ignore |
979 | /// let px = image.pixel_entry_at(x,y); |
980 | /// px.set_from_rgba(rgba) |
981 | /// ``` |
982 | #[deprecated (since = "0.24.0" , note = "Use `get_pixel` and `put_pixel` instead." )] |
983 | fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel; |
984 | |
985 | /// Put a pixel at location (x, y). Indexed from top left. |
986 | /// |
987 | /// # Panics |
988 | /// |
989 | /// Panics if `(x, y)` is out of bounds. |
990 | fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel); |
991 | |
992 | /// Puts a pixel at location (x, y). Indexed from top left. |
993 | /// |
994 | /// This function can be implemented in a way that ignores bounds checking. |
995 | /// # Safety |
996 | /// |
997 | /// The coordinates must be [`in_bounds`] of the image. |
998 | /// |
999 | /// [`in_bounds`]: traits.GenericImageView.html#method.in_bounds |
1000 | unsafe fn unsafe_put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) { |
1001 | self.put_pixel(x, y, pixel); |
1002 | } |
1003 | |
1004 | /// Put a pixel at location (x, y), taking into account alpha channels |
1005 | #[deprecated ( |
1006 | since = "0.24.0" , |
1007 | note = "Use iterator `pixels_mut` to blend the pixels directly" |
1008 | )] |
1009 | fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel); |
1010 | |
1011 | /// Copies all of the pixels from another image into this image. |
1012 | /// |
1013 | /// The other image is copied with the top-left corner of the |
1014 | /// other image placed at (x, y). |
1015 | /// |
1016 | /// In order to copy only a piece of the other image, use [`GenericImageView::view`]. |
1017 | /// |
1018 | /// You can use [`FlatSamples`] to source pixels from an arbitrary regular raster of channel |
1019 | /// values, for example from a foreign interface or a fixed image. |
1020 | /// |
1021 | /// # Returns |
1022 | /// Returns an error if the image is too large to be copied at the given position |
1023 | /// |
1024 | /// [`GenericImageView::view`]: trait.GenericImageView.html#method.view |
1025 | /// [`FlatSamples`]: flat/struct.FlatSamples.html |
1026 | fn copy_from<O>(&mut self, other: &O, x: u32, y: u32) -> ImageResult<()> |
1027 | where |
1028 | O: GenericImageView<Pixel = Self::Pixel>, |
1029 | { |
1030 | // Do bounds checking here so we can use the non-bounds-checking |
1031 | // functions to copy pixels. |
1032 | if self.width() < other.width() + x || self.height() < other.height() + y { |
1033 | return Err(ImageError::Parameter(ParameterError::from_kind( |
1034 | ParameterErrorKind::DimensionMismatch, |
1035 | ))); |
1036 | } |
1037 | |
1038 | for k in 0..other.height() { |
1039 | for i in 0..other.width() { |
1040 | let p = other.get_pixel(i, k); |
1041 | self.put_pixel(i + x, k + y, p); |
1042 | } |
1043 | } |
1044 | Ok(()) |
1045 | } |
1046 | |
1047 | /// Copies all of the pixels from one part of this image to another part of this image. |
1048 | /// |
1049 | /// The destination rectangle of the copy is specified with the top-left corner placed at (x, y). |
1050 | /// |
1051 | /// # Returns |
1052 | /// `true` if the copy was successful, `false` if the image could not |
1053 | /// be copied due to size constraints. |
1054 | fn copy_within(&mut self, source: Rect, x: u32, y: u32) -> bool { |
1055 | let Rect { |
1056 | x: sx, |
1057 | y: sy, |
1058 | width, |
1059 | height, |
1060 | } = source; |
1061 | let dx = x; |
1062 | let dy = y; |
1063 | assert!(sx < self.width() && dx < self.width()); |
1064 | assert!(sy < self.height() && dy < self.height()); |
1065 | if self.width() - dx.max(sx) < width || self.height() - dy.max(sy) < height { |
1066 | return false; |
1067 | } |
1068 | // since `.rev()` creates a new dype we would either have to go with dynamic dispatch for the ranges |
1069 | // or have quite a lot of code bloat. A macro gives us static dispatch with less visible bloat. |
1070 | macro_rules! copy_within_impl_ { |
1071 | ($xiter:expr, $yiter:expr) => { |
1072 | for y in $yiter { |
1073 | let sy = sy + y; |
1074 | let dy = dy + y; |
1075 | for x in $xiter { |
1076 | let sx = sx + x; |
1077 | let dx = dx + x; |
1078 | let pixel = self.get_pixel(sx, sy); |
1079 | self.put_pixel(dx, dy, pixel); |
1080 | } |
1081 | } |
1082 | }; |
1083 | } |
1084 | // check how target and source rectangles relate to each other so we dont overwrite data before we copied it. |
1085 | match (sx < dx, sy < dy) { |
1086 | (true, true) => copy_within_impl_!((0..width).rev(), (0..height).rev()), |
1087 | (true, false) => copy_within_impl_!((0..width).rev(), 0..height), |
1088 | (false, true) => copy_within_impl_!(0..width, (0..height).rev()), |
1089 | (false, false) => copy_within_impl_!(0..width, 0..height), |
1090 | } |
1091 | true |
1092 | } |
1093 | |
1094 | /// Returns a mutable subimage that is a view into this image. |
1095 | /// If you want an immutable subimage instead, use [`GenericImageView::view`] |
1096 | /// The coordinates set the position of the top left corner of the `SubImage`. |
1097 | fn sub_image(&mut self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&mut Self> |
1098 | where |
1099 | Self: Sized, |
1100 | { |
1101 | assert!(u64::from(x) + u64::from(width) <= u64::from(self.width())); |
1102 | assert!(u64::from(y) + u64::from(height) <= u64::from(self.height())); |
1103 | SubImage::new(self, x, y, width, height) |
1104 | } |
1105 | } |
1106 | |
1107 | /// A View into another image |
1108 | /// |
1109 | /// Instances of this struct can be created using: |
1110 | /// - [`GenericImage::sub_image`] to create a mutable view, |
1111 | /// - [`GenericImageView::view`] to create an immutable view, |
1112 | /// - [`SubImage::new`] to instantiate the struct directly. |
1113 | /// |
1114 | /// Note that this does _not_ implement `GenericImage`, but it dereferences to one which allows you |
1115 | /// to use it as if it did. See [Design Considerations](#Design-Considerations) below for details. |
1116 | /// |
1117 | /// # Design Considerations |
1118 | /// |
1119 | /// For reasons relating to coherence, this is not itself a `GenericImage` or a `GenericImageView`. |
1120 | /// In short, we want to reserve the ability of adding traits implemented for _all_ generic images |
1121 | /// but in a different manner for `SubImage`. This may be required to ensure that stacking |
1122 | /// sub-images comes at no double indirect cost. |
1123 | /// |
1124 | /// If, ultimately, this is not needed then a directly implementation of `GenericImage` can and |
1125 | /// will get added. This inconvenience may alternatively get resolved if Rust allows some forms of |
1126 | /// specialization, which might make this trick unnecessary and thus also allows for a direct |
1127 | /// implementation. |
1128 | #[derive (Copy, Clone)] |
1129 | pub struct SubImage<I> { |
1130 | inner: SubImageInner<I>, |
1131 | } |
1132 | |
1133 | /// The inner type of `SubImage` that implements `GenericImage{,View}`. |
1134 | /// |
1135 | /// This type is _nominally_ `pub` but it is not exported from the crate. It should be regarded as |
1136 | /// an existential type in any case. |
1137 | #[derive (Copy, Clone)] |
1138 | pub struct SubImageInner<I> { |
1139 | image: I, |
1140 | xoffset: u32, |
1141 | yoffset: u32, |
1142 | xstride: u32, |
1143 | ystride: u32, |
1144 | } |
1145 | |
1146 | /// Alias to access Pixel behind a reference |
1147 | type DerefPixel<I> = <<I as Deref>::Target as GenericImageView>::Pixel; |
1148 | |
1149 | /// Alias to access Subpixel behind a reference |
1150 | type DerefSubpixel<I> = <DerefPixel<I> as Pixel>::Subpixel; |
1151 | |
1152 | impl<I> SubImage<I> { |
1153 | /// Construct a new subimage |
1154 | /// The coordinates set the position of the top left corner of the `SubImage`. |
1155 | pub fn new(image: I, x: u32, y: u32, width: u32, height: u32) -> SubImage<I> { |
1156 | SubImage { |
1157 | inner: SubImageInner { |
1158 | image, |
1159 | xoffset: x, |
1160 | yoffset: y, |
1161 | xstride: width, |
1162 | ystride: height, |
1163 | }, |
1164 | } |
1165 | } |
1166 | |
1167 | /// Change the coordinates of this subimage. |
1168 | pub fn change_bounds(&mut self, x: u32, y: u32, width: u32, height: u32) { |
1169 | self.inner.xoffset = x; |
1170 | self.inner.yoffset = y; |
1171 | self.inner.xstride = width; |
1172 | self.inner.ystride = height; |
1173 | } |
1174 | |
1175 | /// The offsets of this subimage relative to the underlying image. |
1176 | pub fn offsets(&self) -> (u32, u32) { |
1177 | (self.inner.xoffset, self.inner.yoffset) |
1178 | } |
1179 | |
1180 | /// Convert this subimage to an `ImageBuffer` |
1181 | pub fn to_image(&self) -> ImageBuffer<DerefPixel<I>, Vec<DerefSubpixel<I>>> |
1182 | where |
1183 | I: Deref, |
1184 | I::Target: GenericImageView + 'static, |
1185 | { |
1186 | let mut out = ImageBuffer::new(self.inner.xstride, self.inner.ystride); |
1187 | let borrowed = &*self.inner.image; |
1188 | |
1189 | for y in 0..self.inner.ystride { |
1190 | for x in 0..self.inner.xstride { |
1191 | let p = borrowed.get_pixel(x + self.inner.xoffset, y + self.inner.yoffset); |
1192 | out.put_pixel(x, y, p); |
1193 | } |
1194 | } |
1195 | |
1196 | out |
1197 | } |
1198 | } |
1199 | |
1200 | /// Methods for readable images. |
1201 | impl<I> SubImage<I> |
1202 | where |
1203 | I: Deref, |
1204 | I::Target: GenericImageView, |
1205 | { |
1206 | /// Create a sub-view of the image. |
1207 | /// |
1208 | /// The coordinates given are relative to the current view on the underlying image. |
1209 | /// |
1210 | /// Note that this method is preferred to the one from `GenericImageView`. This is accessible |
1211 | /// with the explicit method call syntax but it should rarely be needed due to causing an |
1212 | /// extra level of indirection. |
1213 | /// |
1214 | /// ``` |
1215 | /// use image::{GenericImageView, RgbImage, SubImage}; |
1216 | /// let buffer = RgbImage::new(10, 10); |
1217 | /// |
1218 | /// let subimage: SubImage<&RgbImage> = buffer.view(0, 0, 10, 10); |
1219 | /// let subview: SubImage<&RgbImage> = subimage.view(0, 0, 10, 10); |
1220 | /// |
1221 | /// // Less efficient and NOT &RgbImage |
1222 | /// let _: SubImage<&_> = GenericImageView::view(&*subimage, 0, 0, 10, 10); |
1223 | /// ``` |
1224 | pub fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&I::Target> { |
1225 | use crate::GenericImageView as _; |
1226 | assert!(u64::from(x) + u64::from(width) <= u64::from(self.inner.width())); |
1227 | assert!(u64::from(y) + u64::from(height) <= u64::from(self.inner.height())); |
1228 | let x = self.inner.xoffset.saturating_add(x); |
1229 | let y = self.inner.yoffset.saturating_add(y); |
1230 | SubImage::new(&*self.inner.image, x, y, width, height) |
1231 | } |
1232 | |
1233 | /// Get a reference to the underlying image. |
1234 | pub fn inner(&self) -> &I::Target { |
1235 | &self.inner.image |
1236 | } |
1237 | } |
1238 | |
1239 | impl<I> SubImage<I> |
1240 | where |
1241 | I: DerefMut, |
1242 | I::Target: GenericImage, |
1243 | { |
1244 | /// Create a mutable sub-view of the image. |
1245 | /// |
1246 | /// The coordinates given are relative to the current view on the underlying image. |
1247 | pub fn sub_image( |
1248 | &mut self, |
1249 | x: u32, |
1250 | y: u32, |
1251 | width: u32, |
1252 | height: u32, |
1253 | ) -> SubImage<&mut I::Target> { |
1254 | assert!(u64::from(x) + u64::from(width) <= u64::from(self.inner.width())); |
1255 | assert!(u64::from(y) + u64::from(height) <= u64::from(self.inner.height())); |
1256 | let x: u32 = self.inner.xoffset.saturating_add(x); |
1257 | let y: u32 = self.inner.yoffset.saturating_add(y); |
1258 | SubImage::new(&mut *self.inner.image, x, y, width, height) |
1259 | } |
1260 | |
1261 | /// Get a mutable reference to the underlying image. |
1262 | pub fn inner_mut(&mut self) -> &mut I::Target { |
1263 | &mut self.inner.image |
1264 | } |
1265 | } |
1266 | |
1267 | impl<I> Deref for SubImage<I> |
1268 | where |
1269 | I: Deref, |
1270 | { |
1271 | type Target = SubImageInner<I>; |
1272 | |
1273 | fn deref(&self) -> &Self::Target { |
1274 | &self.inner |
1275 | } |
1276 | } |
1277 | |
1278 | impl<I> DerefMut for SubImage<I> |
1279 | where |
1280 | I: DerefMut, |
1281 | { |
1282 | fn deref_mut(&mut self) -> &mut Self::Target { |
1283 | &mut self.inner |
1284 | } |
1285 | } |
1286 | |
1287 | #[allow (deprecated)] |
1288 | impl<I> GenericImageView for SubImageInner<I> |
1289 | where |
1290 | I: Deref, |
1291 | I::Target: GenericImageView, |
1292 | { |
1293 | type Pixel = DerefPixel<I>; |
1294 | |
1295 | fn dimensions(&self) -> (u32, u32) { |
1296 | (self.xstride, self.ystride) |
1297 | } |
1298 | |
1299 | fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel { |
1300 | self.image.get_pixel(x:x + self.xoffset, y:y + self.yoffset) |
1301 | } |
1302 | } |
1303 | |
1304 | #[allow (deprecated)] |
1305 | impl<I> GenericImage for SubImageInner<I> |
1306 | where |
1307 | I: DerefMut, |
1308 | I::Target: GenericImage + Sized, |
1309 | { |
1310 | fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel { |
1311 | self.image.get_pixel_mut(x:x + self.xoffset, y:y + self.yoffset) |
1312 | } |
1313 | |
1314 | fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) { |
1315 | self.image |
1316 | .put_pixel(x:x + self.xoffset, y:y + self.yoffset, pixel); |
1317 | } |
1318 | |
1319 | /// DEPRECATED: This method will be removed. Blend the pixel directly instead. |
1320 | fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) { |
1321 | self.image |
1322 | .blend_pixel(x:x + self.xoffset, y:y + self.yoffset, pixel); |
1323 | } |
1324 | } |
1325 | |
1326 | #[cfg (test)] |
1327 | mod tests { |
1328 | use std::collections::HashSet; |
1329 | use std::io; |
1330 | use std::path::Path; |
1331 | |
1332 | use super::{ |
1333 | load_rect, ColorType, GenericImage, GenericImageView, ImageDecoder, ImageFormat, |
1334 | ImageResult, |
1335 | }; |
1336 | use crate::color::Rgba; |
1337 | use crate::math::Rect; |
1338 | use crate::{GrayImage, ImageBuffer}; |
1339 | |
1340 | #[test ] |
1341 | #[allow (deprecated)] |
1342 | /// Test that alpha blending works as expected |
1343 | fn test_image_alpha_blending() { |
1344 | let mut target = ImageBuffer::new(1, 1); |
1345 | target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255])); |
1346 | assert!(*target.get_pixel(0, 0) == Rgba([255, 0, 0, 255])); |
1347 | target.blend_pixel(0, 0, Rgba([0, 255, 0, 255])); |
1348 | assert!(*target.get_pixel(0, 0) == Rgba([0, 255, 0, 255])); |
1349 | |
1350 | // Blending an alpha channel onto a solid background |
1351 | target.blend_pixel(0, 0, Rgba([255, 0, 0, 127])); |
1352 | assert!(*target.get_pixel(0, 0) == Rgba([127, 127, 0, 255])); |
1353 | |
1354 | // Blending two alpha channels |
1355 | target.put_pixel(0, 0, Rgba([0, 255, 0, 127])); |
1356 | target.blend_pixel(0, 0, Rgba([255, 0, 0, 127])); |
1357 | assert!(*target.get_pixel(0, 0) == Rgba([169, 85, 0, 190])); |
1358 | } |
1359 | |
1360 | #[test ] |
1361 | fn test_in_bounds() { |
1362 | let mut target = ImageBuffer::new(2, 2); |
1363 | target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255])); |
1364 | |
1365 | assert!(target.in_bounds(0, 0)); |
1366 | assert!(target.in_bounds(1, 0)); |
1367 | assert!(target.in_bounds(0, 1)); |
1368 | assert!(target.in_bounds(1, 1)); |
1369 | |
1370 | assert!(!target.in_bounds(2, 0)); |
1371 | assert!(!target.in_bounds(0, 2)); |
1372 | assert!(!target.in_bounds(2, 2)); |
1373 | } |
1374 | |
1375 | #[test ] |
1376 | fn test_can_subimage_clone_nonmut() { |
1377 | let mut source = ImageBuffer::new(3, 3); |
1378 | source.put_pixel(1, 1, Rgba([255u8, 0, 0, 255])); |
1379 | |
1380 | // A non-mutable copy of the source image |
1381 | let source = source.clone(); |
1382 | |
1383 | // Clone a view into non-mutable to a separate buffer |
1384 | let cloned = source.view(1, 1, 1, 1).to_image(); |
1385 | |
1386 | assert!(cloned.get_pixel(0, 0) == source.get_pixel(1, 1)); |
1387 | } |
1388 | |
1389 | #[test ] |
1390 | fn test_can_nest_views() { |
1391 | let mut source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
1392 | |
1393 | { |
1394 | let mut sub1 = source.sub_image(0, 0, 2, 2); |
1395 | let mut sub2 = sub1.sub_image(1, 1, 1, 1); |
1396 | sub2.put_pixel(0, 0, Rgba([0, 0, 0, 0])); |
1397 | } |
1398 | |
1399 | assert_eq!(*source.get_pixel(1, 1), Rgba([0, 0, 0, 0])); |
1400 | |
1401 | let view1 = source.view(0, 0, 2, 2); |
1402 | assert_eq!(*source.get_pixel(1, 1), view1.get_pixel(1, 1)); |
1403 | |
1404 | let view2 = view1.view(1, 1, 1, 1); |
1405 | assert_eq!(*source.get_pixel(1, 1), view2.get_pixel(0, 0)); |
1406 | } |
1407 | |
1408 | #[test ] |
1409 | #[should_panic ] |
1410 | fn test_view_out_of_bounds() { |
1411 | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
1412 | source.view(1, 1, 3, 3); |
1413 | } |
1414 | |
1415 | #[test ] |
1416 | #[should_panic ] |
1417 | fn test_view_coordinates_out_of_bounds() { |
1418 | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
1419 | source.view(3, 3, 3, 3); |
1420 | } |
1421 | |
1422 | #[test ] |
1423 | #[should_panic ] |
1424 | fn test_view_width_out_of_bounds() { |
1425 | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
1426 | source.view(1, 1, 3, 2); |
1427 | } |
1428 | |
1429 | #[test ] |
1430 | #[should_panic ] |
1431 | fn test_view_height_out_of_bounds() { |
1432 | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
1433 | source.view(1, 1, 2, 3); |
1434 | } |
1435 | |
1436 | #[test ] |
1437 | #[should_panic ] |
1438 | fn test_view_x_out_of_bounds() { |
1439 | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
1440 | source.view(3, 1, 3, 3); |
1441 | } |
1442 | |
1443 | #[test ] |
1444 | #[should_panic ] |
1445 | fn test_view_y_out_of_bounds() { |
1446 | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
1447 | source.view(1, 3, 3, 3); |
1448 | } |
1449 | |
1450 | #[test ] |
1451 | fn test_view_in_bounds() { |
1452 | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
1453 | source.view(0, 0, 3, 3); |
1454 | source.view(1, 1, 2, 2); |
1455 | source.view(2, 2, 0, 0); |
1456 | } |
1457 | |
1458 | #[test ] |
1459 | fn test_copy_sub_image() { |
1460 | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
1461 | let view = source.view(0, 0, 3, 3); |
1462 | let _view2 = view; |
1463 | view.to_image(); |
1464 | } |
1465 | |
1466 | #[test ] |
1467 | fn test_load_rect() { |
1468 | struct MockDecoder { |
1469 | scanline_number: u64, |
1470 | scanline_bytes: u64, |
1471 | } |
1472 | impl ImageDecoder for MockDecoder { |
1473 | fn dimensions(&self) -> (u32, u32) { |
1474 | (5, 5) |
1475 | } |
1476 | fn color_type(&self) -> ColorType { |
1477 | ColorType::L8 |
1478 | } |
1479 | fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> { |
1480 | unimplemented!() |
1481 | } |
1482 | fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> { |
1483 | (*self).read_image(buf) |
1484 | } |
1485 | } |
1486 | |
1487 | const DATA: [u8; 25] = [ |
1488 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, |
1489 | 24, |
1490 | ]; |
1491 | |
1492 | fn seek_scanline(m: &mut MockDecoder, n: u64) -> io::Result<()> { |
1493 | m.scanline_number = n; |
1494 | Ok(()) |
1495 | } |
1496 | fn read_scanline(m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> { |
1497 | let bytes_read = m.scanline_number * m.scanline_bytes; |
1498 | if bytes_read >= 25 { |
1499 | return Ok(()); |
1500 | } |
1501 | |
1502 | let len = m.scanline_bytes.min(25 - bytes_read); |
1503 | buf[..(len as usize)].copy_from_slice(&DATA[(bytes_read as usize)..][..(len as usize)]); |
1504 | m.scanline_number += 1; |
1505 | Ok(()) |
1506 | } |
1507 | |
1508 | for scanline_bytes in 1..30 { |
1509 | let mut output = [0u8; 26]; |
1510 | |
1511 | load_rect( |
1512 | 0, |
1513 | 0, |
1514 | 5, |
1515 | 5, |
1516 | &mut output, |
1517 | 5, |
1518 | &mut MockDecoder { |
1519 | scanline_number: 0, |
1520 | scanline_bytes, |
1521 | }, |
1522 | scanline_bytes as usize, |
1523 | seek_scanline, |
1524 | read_scanline, |
1525 | ) |
1526 | .unwrap(); |
1527 | assert_eq!(output[0..25], DATA); |
1528 | assert_eq!(output[25], 0); |
1529 | |
1530 | output = [0u8; 26]; |
1531 | load_rect( |
1532 | 3, |
1533 | 2, |
1534 | 1, |
1535 | 1, |
1536 | &mut output, |
1537 | 1, |
1538 | &mut MockDecoder { |
1539 | scanline_number: 0, |
1540 | scanline_bytes, |
1541 | }, |
1542 | scanline_bytes as usize, |
1543 | seek_scanline, |
1544 | read_scanline, |
1545 | ) |
1546 | .unwrap(); |
1547 | assert_eq!(output[0..2], [13, 0]); |
1548 | |
1549 | output = [0u8; 26]; |
1550 | load_rect( |
1551 | 3, |
1552 | 2, |
1553 | 2, |
1554 | 2, |
1555 | &mut output, |
1556 | 2, |
1557 | &mut MockDecoder { |
1558 | scanline_number: 0, |
1559 | scanline_bytes, |
1560 | }, |
1561 | scanline_bytes as usize, |
1562 | seek_scanline, |
1563 | read_scanline, |
1564 | ) |
1565 | .unwrap(); |
1566 | assert_eq!(output[0..5], [13, 14, 18, 19, 0]); |
1567 | |
1568 | output = [0u8; 26]; |
1569 | load_rect( |
1570 | 1, |
1571 | 1, |
1572 | 2, |
1573 | 4, |
1574 | &mut output, |
1575 | 2, |
1576 | &mut MockDecoder { |
1577 | scanline_number: 0, |
1578 | scanline_bytes, |
1579 | }, |
1580 | scanline_bytes as usize, |
1581 | seek_scanline, |
1582 | read_scanline, |
1583 | ) |
1584 | .unwrap(); |
1585 | assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]); |
1586 | } |
1587 | } |
1588 | |
1589 | #[test ] |
1590 | fn test_load_rect_single_scanline() { |
1591 | const DATA: [u8; 25] = [ |
1592 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, |
1593 | 24, |
1594 | ]; |
1595 | |
1596 | struct MockDecoder; |
1597 | impl ImageDecoder for MockDecoder { |
1598 | fn dimensions(&self) -> (u32, u32) { |
1599 | (5, 5) |
1600 | } |
1601 | fn color_type(&self) -> ColorType { |
1602 | ColorType::L8 |
1603 | } |
1604 | fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> { |
1605 | unimplemented!() |
1606 | } |
1607 | fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> { |
1608 | (*self).read_image(buf) |
1609 | } |
1610 | } |
1611 | |
1612 | // Ensure that seek scanline is called only once. |
1613 | let mut seeks = 0; |
1614 | let seek_scanline = |_d: &mut MockDecoder, n: u64| -> io::Result<()> { |
1615 | seeks += 1; |
1616 | assert_eq!(n, 0); |
1617 | assert_eq!(seeks, 1); |
1618 | Ok(()) |
1619 | }; |
1620 | |
1621 | fn read_scanline(_m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<()> { |
1622 | buf.copy_from_slice(&DATA); |
1623 | Ok(()) |
1624 | } |
1625 | |
1626 | let mut output = [0; 26]; |
1627 | load_rect( |
1628 | 1, |
1629 | 1, |
1630 | 2, |
1631 | 4, |
1632 | &mut output, |
1633 | 2, |
1634 | &mut MockDecoder, |
1635 | DATA.len(), |
1636 | seek_scanline, |
1637 | read_scanline, |
1638 | ) |
1639 | .unwrap(); |
1640 | assert_eq!(output[0..9], [6, 7, 11, 12, 16, 17, 21, 22, 0]); |
1641 | } |
1642 | |
1643 | #[test ] |
1644 | fn test_image_format_from_path() { |
1645 | fn from_path(s: &str) -> ImageResult<ImageFormat> { |
1646 | ImageFormat::from_path(Path::new(s)) |
1647 | } |
1648 | assert_eq!(from_path("./a.jpg" ).unwrap(), ImageFormat::Jpeg); |
1649 | assert_eq!(from_path("./a.jpeg" ).unwrap(), ImageFormat::Jpeg); |
1650 | assert_eq!(from_path("./a.JPEG" ).unwrap(), ImageFormat::Jpeg); |
1651 | assert_eq!(from_path("./a.pNg" ).unwrap(), ImageFormat::Png); |
1652 | assert_eq!(from_path("./a.gif" ).unwrap(), ImageFormat::Gif); |
1653 | assert_eq!(from_path("./a.webp" ).unwrap(), ImageFormat::WebP); |
1654 | assert_eq!(from_path("./a.tiFF" ).unwrap(), ImageFormat::Tiff); |
1655 | assert_eq!(from_path("./a.tif" ).unwrap(), ImageFormat::Tiff); |
1656 | assert_eq!(from_path("./a.tga" ).unwrap(), ImageFormat::Tga); |
1657 | assert_eq!(from_path("./a.dds" ).unwrap(), ImageFormat::Dds); |
1658 | assert_eq!(from_path("./a.bmp" ).unwrap(), ImageFormat::Bmp); |
1659 | assert_eq!(from_path("./a.Ico" ).unwrap(), ImageFormat::Ico); |
1660 | assert_eq!(from_path("./a.hdr" ).unwrap(), ImageFormat::Hdr); |
1661 | assert_eq!(from_path("./a.exr" ).unwrap(), ImageFormat::OpenExr); |
1662 | assert_eq!(from_path("./a.pbm" ).unwrap(), ImageFormat::Pnm); |
1663 | assert_eq!(from_path("./a.pAM" ).unwrap(), ImageFormat::Pnm); |
1664 | assert_eq!(from_path("./a.Ppm" ).unwrap(), ImageFormat::Pnm); |
1665 | assert_eq!(from_path("./a.pgm" ).unwrap(), ImageFormat::Pnm); |
1666 | assert_eq!(from_path("./a.AViF" ).unwrap(), ImageFormat::Avif); |
1667 | assert_eq!(from_path("./a.PCX" ).unwrap(), ImageFormat::Pcx); |
1668 | assert!(from_path("./a.txt" ).is_err()); |
1669 | assert!(from_path("./a" ).is_err()); |
1670 | } |
1671 | |
1672 | #[test ] |
1673 | fn test_generic_image_copy_within_oob() { |
1674 | let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap(); |
1675 | assert!(!image.sub_image(0, 0, 4, 4).copy_within( |
1676 | Rect { |
1677 | x: 0, |
1678 | y: 0, |
1679 | width: 5, |
1680 | height: 4 |
1681 | }, |
1682 | 0, |
1683 | 0 |
1684 | )); |
1685 | assert!(!image.sub_image(0, 0, 4, 4).copy_within( |
1686 | Rect { |
1687 | x: 0, |
1688 | y: 0, |
1689 | width: 4, |
1690 | height: 5 |
1691 | }, |
1692 | 0, |
1693 | 0 |
1694 | )); |
1695 | assert!(!image.sub_image(0, 0, 4, 4).copy_within( |
1696 | Rect { |
1697 | x: 1, |
1698 | y: 0, |
1699 | width: 4, |
1700 | height: 4 |
1701 | }, |
1702 | 0, |
1703 | 0 |
1704 | )); |
1705 | assert!(!image.sub_image(0, 0, 4, 4).copy_within( |
1706 | Rect { |
1707 | x: 0, |
1708 | y: 0, |
1709 | width: 4, |
1710 | height: 4 |
1711 | }, |
1712 | 1, |
1713 | 0 |
1714 | )); |
1715 | assert!(!image.sub_image(0, 0, 4, 4).copy_within( |
1716 | Rect { |
1717 | x: 0, |
1718 | y: 1, |
1719 | width: 4, |
1720 | height: 4 |
1721 | }, |
1722 | 0, |
1723 | 0 |
1724 | )); |
1725 | assert!(!image.sub_image(0, 0, 4, 4).copy_within( |
1726 | Rect { |
1727 | x: 0, |
1728 | y: 0, |
1729 | width: 4, |
1730 | height: 4 |
1731 | }, |
1732 | 0, |
1733 | 1 |
1734 | )); |
1735 | assert!(!image.sub_image(0, 0, 4, 4).copy_within( |
1736 | Rect { |
1737 | x: 1, |
1738 | y: 1, |
1739 | width: 4, |
1740 | height: 4 |
1741 | }, |
1742 | 0, |
1743 | 0 |
1744 | )); |
1745 | } |
1746 | |
1747 | #[test ] |
1748 | fn test_generic_image_copy_within_tl() { |
1749 | let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; |
1750 | let expected = [0, 1, 2, 3, 4, 0, 1, 2, 8, 4, 5, 6, 12, 8, 9, 10]; |
1751 | let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap(); |
1752 | assert!(image.sub_image(0, 0, 4, 4).copy_within( |
1753 | Rect { |
1754 | x: 0, |
1755 | y: 0, |
1756 | width: 3, |
1757 | height: 3 |
1758 | }, |
1759 | 1, |
1760 | 1 |
1761 | )); |
1762 | assert_eq!(&image.into_raw(), &expected); |
1763 | } |
1764 | |
1765 | #[test ] |
1766 | fn test_generic_image_copy_within_tr() { |
1767 | let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; |
1768 | let expected = [0, 1, 2, 3, 1, 2, 3, 7, 5, 6, 7, 11, 9, 10, 11, 15]; |
1769 | let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap(); |
1770 | assert!(image.sub_image(0, 0, 4, 4).copy_within( |
1771 | Rect { |
1772 | x: 1, |
1773 | y: 0, |
1774 | width: 3, |
1775 | height: 3 |
1776 | }, |
1777 | 0, |
1778 | 1 |
1779 | )); |
1780 | assert_eq!(&image.into_raw(), &expected); |
1781 | } |
1782 | |
1783 | #[test ] |
1784 | fn test_generic_image_copy_within_bl() { |
1785 | let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; |
1786 | let expected = [0, 4, 5, 6, 4, 8, 9, 10, 8, 12, 13, 14, 12, 13, 14, 15]; |
1787 | let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap(); |
1788 | assert!(image.sub_image(0, 0, 4, 4).copy_within( |
1789 | Rect { |
1790 | x: 0, |
1791 | y: 1, |
1792 | width: 3, |
1793 | height: 3 |
1794 | }, |
1795 | 1, |
1796 | 0 |
1797 | )); |
1798 | assert_eq!(&image.into_raw(), &expected); |
1799 | } |
1800 | |
1801 | #[test ] |
1802 | fn test_generic_image_copy_within_br() { |
1803 | let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; |
1804 | let expected = [5, 6, 7, 3, 9, 10, 11, 7, 13, 14, 15, 11, 12, 13, 14, 15]; |
1805 | let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap(); |
1806 | assert!(image.sub_image(0, 0, 4, 4).copy_within( |
1807 | Rect { |
1808 | x: 1, |
1809 | y: 1, |
1810 | width: 3, |
1811 | height: 3 |
1812 | }, |
1813 | 0, |
1814 | 0 |
1815 | )); |
1816 | assert_eq!(&image.into_raw(), &expected); |
1817 | } |
1818 | |
1819 | #[test ] |
1820 | fn image_formats_are_recognized() { |
1821 | use ImageFormat::*; |
1822 | const ALL_FORMATS: &[ImageFormat] = &[ |
1823 | Avif, Png, Jpeg, Gif, WebP, Pnm, Tiff, Tga, Dds, Bmp, Ico, Hdr, Farbfeld, OpenExr, Pcx, |
1824 | ]; |
1825 | for &format in ALL_FORMATS { |
1826 | let mut file = Path::new("file.nothing" ).to_owned(); |
1827 | for ext in format.extensions_str() { |
1828 | assert!(file.set_extension(ext)); |
1829 | match ImageFormat::from_path(&file) { |
1830 | Err(_) => panic!("Path {} not recognized as {:?}" , file.display(), format), |
1831 | Ok(result) => assert_eq!(format, result), |
1832 | } |
1833 | } |
1834 | } |
1835 | } |
1836 | |
1837 | #[test ] |
1838 | fn total_bytes_overflow() { |
1839 | struct D; |
1840 | impl ImageDecoder for D { |
1841 | fn color_type(&self) -> ColorType { |
1842 | ColorType::Rgb8 |
1843 | } |
1844 | fn dimensions(&self) -> (u32, u32) { |
1845 | (0xffff_ffff, 0xffff_ffff) |
1846 | } |
1847 | fn read_image(self, _buf: &mut [u8]) -> ImageResult<()> { |
1848 | unimplemented!() |
1849 | } |
1850 | fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> { |
1851 | (*self).read_image(buf) |
1852 | } |
1853 | } |
1854 | assert_eq!(D.total_bytes(), u64::MAX); |
1855 | |
1856 | let v: ImageResult<Vec<u8>> = super::decoder_to_vec(D); |
1857 | assert!(v.is_err()); |
1858 | } |
1859 | |
1860 | #[test ] |
1861 | fn all() { |
1862 | let all_formats: HashSet<ImageFormat> = ImageFormat::all().collect(); |
1863 | assert!(all_formats.contains(&ImageFormat::Avif)); |
1864 | assert!(all_formats.contains(&ImageFormat::Gif)); |
1865 | assert!(all_formats.contains(&ImageFormat::Bmp)); |
1866 | assert!(all_formats.contains(&ImageFormat::Farbfeld)); |
1867 | assert!(all_formats.contains(&ImageFormat::Jpeg)); |
1868 | } |
1869 | |
1870 | #[test ] |
1871 | fn reading_enabled() { |
1872 | assert_eq!(cfg!(feature = "jpeg" ), ImageFormat::Jpeg.reading_enabled()); |
1873 | assert_eq!( |
1874 | cfg!(feature = "ff" ), |
1875 | ImageFormat::Farbfeld.reading_enabled() |
1876 | ); |
1877 | assert!(!ImageFormat::Dds.reading_enabled()); |
1878 | } |
1879 | |
1880 | #[test ] |
1881 | fn writing_enabled() { |
1882 | assert_eq!(cfg!(feature = "jpeg" ), ImageFormat::Jpeg.writing_enabled()); |
1883 | assert_eq!( |
1884 | cfg!(feature = "ff" ), |
1885 | ImageFormat::Farbfeld.writing_enabled() |
1886 | ); |
1887 | assert!(!ImageFormat::Dds.writing_enabled()); |
1888 | } |
1889 | } |
1890 | |