| 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 | |