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