1 use num_iter;
2 use std::io;
3 use std::io::Write;
4 use std::path::Path;
5 use std::u32;
6
7 #[cfg(feature = "bmp")]
8 use bmp;
9 #[cfg(feature = "gif_codec")]
10 use gif;
11 #[cfg(feature = "ico")]
12 use ico;
13 #[cfg(feature = "jpeg")]
14 use jpeg;
15 #[cfg(feature = "png_codec")]
16 use png;
17 #[cfg(feature = "pnm")]
18 use pnm;
19
20 use buffer::{
21 BgrImage, BgraImage, ConvertBuffer, GrayAlphaImage, GrayImage, ImageBuffer, Pixel, RgbImage,
22 RgbaImage,
23 };
24 use color;
25 use flat::FlatSamples;
26 use image;
27 use image::{
28 GenericImage, GenericImageView, ImageDecoder, ImageFormat, ImageOutputFormat, ImageResult,
29 };
30 use io::free_functions;
31 use imageops;
32
33 /// A Dynamic Image
34 #[derive(Clone)]
35 pub enum DynamicImage {
36 /// Each pixel in this image is 8-bit Luma
37 ImageLuma8(GrayImage),
38
39 /// Each pixel in this image is 8-bit Luma with alpha
40 ImageLumaA8(GrayAlphaImage),
41
42 /// Each pixel in this image is 8-bit Rgb
43 ImageRgb8(RgbImage),
44
45 /// Each pixel in this image is 8-bit Rgb with alpha
46 ImageRgba8(RgbaImage),
47
48 /// Each pixel in this image is 8-bit Bgr
49 ImageBgr8(BgrImage),
50
51 /// Each pixel in this image is 8-bit Bgr with alpha
52 ImageBgra8(BgraImage),
53 }
54
55 macro_rules! dynamic_map(
56 ($dynimage: expr, ref $image: ident => $action: expr) => (
57 match $dynimage {
58 DynamicImage::ImageLuma8(ref $image) => DynamicImage::ImageLuma8($action),
59 DynamicImage::ImageLumaA8(ref $image) => DynamicImage::ImageLumaA8($action),
60 DynamicImage::ImageRgb8(ref $image) => DynamicImage::ImageRgb8($action),
61 DynamicImage::ImageRgba8(ref $image) => DynamicImage::ImageRgba8($action),
62 DynamicImage::ImageBgr8(ref $image) => DynamicImage::ImageBgr8($action),
63 DynamicImage::ImageBgra8(ref $image) => DynamicImage::ImageBgra8($action),
64 }
65 );
66
67 ($dynimage: expr, ref mut $image: ident => $action: expr) => (
68 match $dynimage {
69 DynamicImage::ImageLuma8(ref mut $image) => DynamicImage::ImageLuma8($action),
70 DynamicImage::ImageLumaA8(ref mut $image) => DynamicImage::ImageLumaA8($action),
71 DynamicImage::ImageRgb8(ref mut $image) => DynamicImage::ImageRgb8($action),
72 DynamicImage::ImageRgba8(ref mut $image) => DynamicImage::ImageRgba8($action),
73 DynamicImage::ImageBgr8(ref mut $image) => DynamicImage::ImageBgr8($action),
74 DynamicImage::ImageBgra8(ref mut $image) => DynamicImage::ImageBgra8($action),
75 }
76 );
77
78 ($dynimage: expr, ref $image: ident -> $action: expr) => (
79 match $dynimage {
80 DynamicImage::ImageLuma8(ref $image) => $action,
81 DynamicImage::ImageLumaA8(ref $image) => $action,
82 DynamicImage::ImageRgb8(ref $image) => $action,
83 DynamicImage::ImageRgba8(ref $image) => $action,
84 DynamicImage::ImageBgr8(ref $image) => $action,
85 DynamicImage::ImageBgra8(ref $image) => $action,
86 }
87 );
88
89 ($dynimage: expr, ref mut $image: ident -> $action: expr) => (
90 match $dynimage {
91 DynamicImage::ImageLuma8(ref mut $image) => $action,
92 DynamicImage::ImageLumaA8(ref mut $image) => $action,
93 DynamicImage::ImageRgb8(ref mut $image) => $action,
94 DynamicImage::ImageRgba8(ref mut $image) => $action,
95 DynamicImage::ImageBgr8(ref mut $image) => $action,
96 DynamicImage::ImageBgra8(ref mut $image) => $action,
97 }
98 );
99 );
100
101 impl DynamicImage {
102 /// Creates a dynamic image backed by a buffer of grey pixels.
new_luma8(w: u32, h: u32) -> DynamicImage103 pub fn new_luma8(w: u32, h: u32) -> DynamicImage {
104 DynamicImage::ImageLuma8(ImageBuffer::new(w, h))
105 }
106
107 /// Creates a dynamic image backed by a buffer of grey
108 /// pixels with transparency.
new_luma_a8(w: u32, h: u32) -> DynamicImage109 pub fn new_luma_a8(w: u32, h: u32) -> DynamicImage {
110 DynamicImage::ImageLumaA8(ImageBuffer::new(w, h))
111 }
112
113 /// Creates a dynamic image backed by a buffer of RGB pixels.
new_rgb8(w: u32, h: u32) -> DynamicImage114 pub fn new_rgb8(w: u32, h: u32) -> DynamicImage {
115 DynamicImage::ImageRgb8(ImageBuffer::new(w, h))
116 }
117
118 /// Creates a dynamic image backed by a buffer of RGBA pixels.
new_rgba8(w: u32, h: u32) -> DynamicImage119 pub fn new_rgba8(w: u32, h: u32) -> DynamicImage {
120 DynamicImage::ImageRgba8(ImageBuffer::new(w, h))
121 }
122
123 /// Creates a dynamic image backed by a buffer of BGRA pixels.
new_bgra8(w: u32, h: u32) -> DynamicImage124 pub fn new_bgra8(w: u32, h: u32) -> DynamicImage {
125 DynamicImage::ImageBgra8(ImageBuffer::new(w, h))
126 }
127
128 /// Creates a dynamic image backed by a buffer of BGR pixels.
new_bgr8(w: u32, h: u32) -> DynamicImage129 pub fn new_bgr8(w: u32, h: u32) -> DynamicImage {
130 DynamicImage::ImageBgr8(ImageBuffer::new(w, h))
131 }
132
133 /// Decodes an encoded image into a dynamic image.
from_decoder<'a>(decoder: impl ImageDecoder<'a>) -> ImageResult<Self>134 pub fn from_decoder<'a>(decoder: impl ImageDecoder<'a>)
135 -> ImageResult<Self>
136 {
137 decoder_to_image(decoder)
138 }
139
140 /// Returns a copy of this image as an RGB image.
to_rgb(&self) -> RgbImage141 pub fn to_rgb(&self) -> RgbImage {
142 dynamic_map!(*self, ref p -> {
143 p.convert()
144 })
145 }
146
147 /// Returns a copy of this image as an RGBA image.
to_rgba(&self) -> RgbaImage148 pub fn to_rgba(&self) -> RgbaImage {
149 dynamic_map!(*self, ref p -> {
150 p.convert()
151 })
152 }
153
154 /// Returns a copy of this image as an BGR image.
to_bgr(&self) -> BgrImage155 pub fn to_bgr(&self) -> BgrImage {
156 dynamic_map!(*self, ref p -> {
157 p.convert()
158 })
159 }
160
161 /// Returns a copy of this image as an BGRA image.
to_bgra(&self) -> BgraImage162 pub fn to_bgra(&self) -> BgraImage {
163 dynamic_map!(*self, ref p -> {
164 p.convert()
165 })
166 }
167
168 /// Returns a copy of this image as a Luma image.
to_luma(&self) -> GrayImage169 pub fn to_luma(&self) -> GrayImage {
170 dynamic_map!(*self, ref p -> {
171 p.convert()
172 })
173 }
174
175 /// Returns a copy of this image as a LumaA image.
to_luma_alpha(&self) -> GrayAlphaImage176 pub fn to_luma_alpha(&self) -> GrayAlphaImage {
177 dynamic_map!(*self, ref p -> {
178 p.convert()
179 })
180 }
181
182 /// Return a cut out of this image delimited by the bounding rectangle.
crop(&mut self, x: u32, y: u32, width: u32, height: u32) -> DynamicImage183 pub fn crop(&mut self, x: u32, y: u32, width: u32, height: u32) -> DynamicImage {
184 dynamic_map!(*self, ref mut p => imageops::crop(p, x, y, width, height).to_image())
185 }
186
187 /// Return a reference to an 8bit RGB image
as_rgb8(&self) -> Option<&RgbImage>188 pub fn as_rgb8(&self) -> Option<&RgbImage> {
189 match *self {
190 DynamicImage::ImageRgb8(ref p) => Some(p),
191 _ => None,
192 }
193 }
194
195 /// Return a mutable reference to an 8bit RGB image
as_mut_rgb8(&mut self) -> Option<&mut RgbImage>196 pub fn as_mut_rgb8(&mut self) -> Option<&mut RgbImage> {
197 match *self {
198 DynamicImage::ImageRgb8(ref mut p) => Some(p),
199 _ => None,
200 }
201 }
202
203 /// Return a reference to an 8bit BGR image
as_bgr8(&self) -> Option<&BgrImage>204 pub fn as_bgr8(&self) -> Option<&BgrImage> {
205 match *self {
206 DynamicImage::ImageBgr8(ref p) => Some(p),
207 _ => None,
208 }
209 }
210
211 /// Return a mutable reference to an 8bit BGR image
as_mut_bgr8(&mut self) -> Option<&mut BgrImage>212 pub fn as_mut_bgr8(&mut self) -> Option<&mut BgrImage> {
213 match *self {
214 DynamicImage::ImageBgr8(ref mut p) => Some(p),
215 _ => None,
216 }
217 }
218
219 /// Return a reference to an 8bit RGBA image
as_rgba8(&self) -> Option<&RgbaImage>220 pub fn as_rgba8(&self) -> Option<&RgbaImage> {
221 match *self {
222 DynamicImage::ImageRgba8(ref p) => Some(p),
223 _ => None,
224 }
225 }
226
227 /// Return a mutable reference to an 8bit RGBA image
as_mut_rgba8(&mut self) -> Option<&mut RgbaImage>228 pub fn as_mut_rgba8(&mut self) -> Option<&mut RgbaImage> {
229 match *self {
230 DynamicImage::ImageRgba8(ref mut p) => Some(p),
231 _ => None,
232 }
233 }
234
235 /// Return a reference to an 8bit BGRA image
as_bgra8(&self) -> Option<&BgraImage>236 pub fn as_bgra8(&self) -> Option<&BgraImage> {
237 match *self {
238 DynamicImage::ImageBgra8(ref p) => Some(p),
239 _ => None,
240 }
241 }
242
243 /// Return a mutable reference to an 8bit RGBA image
as_mut_bgra8(&mut self) -> Option<&mut BgraImage>244 pub fn as_mut_bgra8(&mut self) -> Option<&mut BgraImage> {
245 match *self {
246 DynamicImage::ImageBgra8(ref mut p) => Some(p),
247 _ => None,
248 }
249 }
250
251 /// Return a reference to an 8bit Grayscale image
as_luma8(&self) -> Option<&GrayImage>252 pub fn as_luma8(&self) -> Option<&GrayImage> {
253 match *self {
254 DynamicImage::ImageLuma8(ref p) => Some(p),
255 _ => None,
256 }
257 }
258
259 /// Return a mutable reference to an 8bit Grayscale image
as_mut_luma8(&mut self) -> Option<&mut GrayImage>260 pub fn as_mut_luma8(&mut self) -> Option<&mut GrayImage> {
261 match *self {
262 DynamicImage::ImageLuma8(ref mut p) => Some(p),
263 _ => None,
264 }
265 }
266
267 /// Return a reference to an 8bit Grayscale image with an alpha channel
as_luma_alpha8(&self) -> Option<&GrayAlphaImage>268 pub fn as_luma_alpha8(&self) -> Option<&GrayAlphaImage> {
269 match *self {
270 DynamicImage::ImageLumaA8(ref p) => Some(p),
271 _ => None,
272 }
273 }
274
275 /// Return a mutable reference to an 8bit Grayscale image with an alpha channel
as_mut_luma_alpha8(&mut self) -> Option<&mut GrayAlphaImage>276 pub fn as_mut_luma_alpha8(&mut self) -> Option<&mut GrayAlphaImage> {
277 match *self {
278 DynamicImage::ImageLumaA8(ref mut p) => Some(p),
279 _ => None,
280 }
281 }
282
283 /// Return this image's pixels as a byte vector.
raw_pixels(&self) -> Vec<u8>284 pub fn raw_pixels(&self) -> Vec<u8> {
285 image_to_bytes(self)
286 }
287
288 /// Return a view on the raw sample buffer.
as_flat_samples(&self) -> FlatSamples<&[u8]>289 pub fn as_flat_samples(&self) -> FlatSamples<&[u8]> {
290 dynamic_map!(*self, ref p -> p.as_flat_samples())
291 }
292
293 /// Return this image's color type.
color(&self) -> color::ColorType294 pub fn color(&self) -> color::ColorType {
295 match *self {
296 DynamicImage::ImageLuma8(_) => color::ColorType::Gray(8),
297 DynamicImage::ImageLumaA8(_) => color::ColorType::GrayA(8),
298 DynamicImage::ImageRgb8(_) => color::ColorType::RGB(8),
299 DynamicImage::ImageRgba8(_) => color::ColorType::RGBA(8),
300 DynamicImage::ImageBgra8(_) => color::ColorType::BGRA(8),
301 DynamicImage::ImageBgr8(_) => color::ColorType::BGR(8),
302 }
303 }
304
305 /// Return a grayscale version of this image.
grayscale(&self) -> DynamicImage306 pub fn grayscale(&self) -> DynamicImage {
307 match *self {
308 DynamicImage::ImageLuma8(ref p) => DynamicImage::ImageLuma8(p.clone()),
309 DynamicImage::ImageLumaA8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
310 DynamicImage::ImageRgb8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
311 DynamicImage::ImageRgba8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
312 DynamicImage::ImageBgr8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
313 DynamicImage::ImageBgra8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
314 }
315 }
316
317 /// Invert the colors of this image.
318 /// This method operates inplace.
invert(&mut self)319 pub fn invert(&mut self) {
320 dynamic_map!(*self, ref mut p -> imageops::invert(p))
321 }
322
323 /// Resize this image using the specified filter algorithm.
324 /// Returns a new image. The image's aspect ratio is preserved.
325 /// The image is scaled to the maximum possible size that fits
326 /// within the bounds specified by ```nwidth``` and ```nheight```.
resize(&self, nwidth: u32, nheight: u32, filter: imageops::FilterType) -> DynamicImage327 pub fn resize(&self, nwidth: u32, nheight: u32, filter: imageops::FilterType) -> DynamicImage {
328 let (width2, height2) =
329 resize_dimensions(self.width(), self.height(), nwidth, nheight, false);
330
331 self.resize_exact(width2, height2, filter)
332 }
333
334 /// Resize this image using the specified filter algorithm.
335 /// Returns a new image. Does not preserve aspect ratio.
336 /// ```nwidth``` and ```nheight``` are the new image's dimensions
resize_exact( &self, nwidth: u32, nheight: u32, filter: imageops::FilterType, ) -> DynamicImage337 pub fn resize_exact(
338 &self,
339 nwidth: u32,
340 nheight: u32,
341 filter: imageops::FilterType,
342 ) -> DynamicImage {
343 dynamic_map!(*self, ref p => imageops::resize(p, nwidth, nheight, filter))
344 }
345
346 /// Scale this image down to fit within a specific size.
347 /// Returns a new image. The image's aspect ratio is preserved.
348 /// The image is scaled to the maximum possible size that fits
349 /// within the bounds specified by ```nwidth``` and ```nheight```.
350 ///
351 /// This method uses a fast integer algorithm where each source
352 /// pixel contributes to exactly one target pixel.
353 /// May give aliasing artifacts if new size is close to old size.
thumbnail(&self, nwidth: u32, nheight: u32) -> DynamicImage354 pub fn thumbnail(&self, nwidth: u32, nheight: u32) -> DynamicImage {
355 let (width2, height2) =
356 resize_dimensions(self.width(), self.height(), nwidth, nheight, false);
357 self.thumbnail_exact(width2, height2)
358 }
359
360 /// Scale this image down to a specific size.
361 /// Returns a new image. Does not preserve aspect ratio.
362 /// ```nwidth``` and ```nheight``` are the new image's dimensions.
363 /// This method uses a fast integer algorithm where each source
364 /// pixel contributes to exactly one target pixel.
365 /// May give aliasing artifacts if new size is close to old size.
thumbnail_exact(&self, nwidth: u32, nheight: u32) -> DynamicImage366 pub fn thumbnail_exact(&self, nwidth: u32, nheight: u32) -> DynamicImage {
367 dynamic_map!(*self, ref p => imageops::thumbnail(p, nwidth, nheight))
368 }
369
370 /// Resize this image using the specified filter algorithm.
371 /// Returns a new image. The image's aspect ratio is preserved.
372 /// The image is scaled to the maximum possible size that fits
373 /// within the larger (relative to aspect ratio) of the bounds
374 /// specified by ```nwidth``` and ```nheight```, then cropped to
375 /// fit within the other bound.
resize_to_fill( &self, nwidth: u32, nheight: u32, filter: imageops::FilterType, ) -> DynamicImage376 pub fn resize_to_fill(
377 &self,
378 nwidth: u32,
379 nheight: u32,
380 filter: imageops::FilterType,
381 ) -> DynamicImage {
382 let (width2, height2) =
383 resize_dimensions(self.width(), self.height(), nwidth, nheight, true);
384
385 let mut intermediate = self.resize_exact(width2, height2, filter);
386 let (iwidth, iheight) = intermediate.dimensions();
387 let ratio = u64::from(iwidth) * u64::from(nheight);
388 let nratio = u64::from(nwidth) * u64::from(iheight);
389
390 if nratio > ratio {
391 intermediate.crop(0, (iheight - nheight) / 2, nwidth, nheight)
392 } else {
393 intermediate.crop((iwidth - nwidth) / 2, 0, nwidth, nheight)
394 }
395 }
396
397 /// Performs a Gaussian blur on this image.
398 /// ```sigma``` is a measure of how much to blur by.
blur(&self, sigma: f32) -> DynamicImage399 pub fn blur(&self, sigma: f32) -> DynamicImage {
400 dynamic_map!(*self, ref p => imageops::blur(p, sigma))
401 }
402
403 /// Performs an unsharpen mask on this image.
404 /// ```sigma``` is the amount to blur the image by.
405 /// ```threshold``` is a control of how much to sharpen.
406 ///
407 /// See <https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking>
unsharpen(&self, sigma: f32, threshold: i32) -> DynamicImage408 pub fn unsharpen(&self, sigma: f32, threshold: i32) -> DynamicImage {
409 dynamic_map!(*self, ref p => imageops::unsharpen(p, sigma, threshold))
410 }
411
412 /// Filters this image with the specified 3x3 kernel.
filter3x3(&self, kernel: &[f32]) -> DynamicImage413 pub fn filter3x3(&self, kernel: &[f32]) -> DynamicImage {
414 if kernel.len() != 9 {
415 panic!("filter must be 3 x 3")
416 }
417
418 dynamic_map!(*self, ref p => imageops::filter3x3(p, kernel))
419 }
420
421 /// Adjust the contrast of this image.
422 /// ```contrast``` is the amount to adjust the contrast by.
423 /// Negative values decrease the contrast and positive values increase the contrast.
adjust_contrast(&self, c: f32) -> DynamicImage424 pub fn adjust_contrast(&self, c: f32) -> DynamicImage {
425 dynamic_map!(*self, ref p => imageops::contrast(p, c))
426 }
427
428 /// Brighten the pixels of this image.
429 /// ```value``` is the amount to brighten each pixel by.
430 /// Negative values decrease the brightness and positive values increase it.
brighten(&self, value: i32) -> DynamicImage431 pub fn brighten(&self, value: i32) -> DynamicImage {
432 dynamic_map!(*self, ref p => imageops::brighten(p, value))
433 }
434
435 /// Hue rotate the supplied image.
436 /// `value` is the degrees to rotate each pixel by.
437 /// 0 and 360 do nothing, the rest rotates by the given degree value.
438 /// just like the css webkit filter hue-rotate(180)
huerotate(&self, value: i32) -> DynamicImage439 pub fn huerotate(&self, value: i32) -> DynamicImage {
440 dynamic_map!(*self, ref p => imageops::huerotate(p, value))
441 }
442
443 /// Flip this image vertically
flipv(&self) -> DynamicImage444 pub fn flipv(&self) -> DynamicImage {
445 dynamic_map!(*self, ref p => imageops::flip_vertical(p))
446 }
447
448 /// Flip this image horizontally
fliph(&self) -> DynamicImage449 pub fn fliph(&self) -> DynamicImage {
450 dynamic_map!(*self, ref p => imageops::flip_horizontal(p))
451 }
452
453 /// Rotate this image 90 degrees clockwise.
rotate90(&self) -> DynamicImage454 pub fn rotate90(&self) -> DynamicImage {
455 dynamic_map!(*self, ref p => imageops::rotate90(p))
456 }
457
458 /// Rotate this image 180 degrees clockwise.
rotate180(&self) -> DynamicImage459 pub fn rotate180(&self) -> DynamicImage {
460 dynamic_map!(*self, ref p => imageops::rotate180(p))
461 }
462
463 /// Rotate this image 270 degrees clockwise.
rotate270(&self) -> DynamicImage464 pub fn rotate270(&self) -> DynamicImage {
465 dynamic_map!(*self, ref p => imageops::rotate270(p))
466 }
467
468 /// Encode this image and write it to ```w```
write_to<W: Write, F: Into<ImageOutputFormat>>( &self, w: &mut W, format: F, ) -> ImageResult<()>469 pub fn write_to<W: Write, F: Into<ImageOutputFormat>>(
470 &self,
471 w: &mut W,
472 format: F,
473 ) -> ImageResult<()> {
474 let mut bytes = self.raw_pixels();
475 let (width, height) = self.dimensions();
476 let mut color = self.color();
477 let format = format.into();
478
479 #[allow(deprecated)]
480 match format {
481 #[cfg(feature = "png_codec")]
482 image::ImageOutputFormat::PNG => {
483 let p = png::PNGEncoder::new(w);
484 match *self {
485 DynamicImage::ImageBgra8(_) => {
486 bytes = self.to_rgba().iter().cloned().collect();
487 color = color::ColorType::RGBA(8);
488 }
489 DynamicImage::ImageBgr8(_) => {
490 bytes = self.to_rgb().iter().cloned().collect();
491 color = color::ColorType::RGB(8);
492 }
493 _ => {}
494 }
495 p.encode(&bytes, width, height, color)?;
496 Ok(())
497 }
498 #[cfg(feature = "pnm")]
499 image::ImageOutputFormat::PNM(subtype) => {
500 let mut p = pnm::PNMEncoder::new(w).with_subtype(subtype);
501 match *self {
502 DynamicImage::ImageBgra8(_) => {
503 bytes = self.to_rgba().iter().cloned().collect();
504 color = color::ColorType::RGBA(8);
505 }
506 DynamicImage::ImageBgr8(_) => {
507 bytes = self.to_rgb().iter().cloned().collect();
508 color = color::ColorType::RGB(8);
509 }
510 _ => {}
511 }
512 p.encode(&bytes[..], width, height, color)?;
513 Ok(())
514 }
515 #[cfg(feature = "jpeg")]
516 image::ImageOutputFormat::JPEG(quality) => {
517 let mut j = jpeg::JPEGEncoder::new_with_quality(w, quality);
518
519 j.encode(&bytes, width, height, color)?;
520 Ok(())
521 }
522
523 #[cfg(feature = "gif_codec")]
524 image::ImageOutputFormat::GIF => {
525 let mut g = gif::Encoder::new(w);
526
527 g.encode(&gif::Frame::from_rgba(
528 width as u16,
529 height as u16,
530 &mut *self.to_rgba().iter().cloned().collect::<Vec<u8>>(),
531 ))?;
532 Ok(())
533 }
534
535 #[cfg(feature = "ico")]
536 image::ImageOutputFormat::ICO => {
537 let i = ico::ICOEncoder::new(w);
538
539 i.encode(&bytes, width, height, color)?;
540 Ok(())
541 }
542
543 #[cfg(feature = "bmp")]
544 image::ImageOutputFormat::BMP => {
545 let mut b = bmp::BMPEncoder::new(w);
546 b.encode(&bytes, width, height, color)?;
547 Ok(())
548 }
549
550 image::ImageOutputFormat::Unsupported(msg) => {
551 Err(image::ImageError::UnsupportedError(msg))
552 }
553 }
554 }
555
556 /// Saves the buffer to a file at the path specified.
557 ///
558 /// The image format is derived from the file extension.
save<Q>(&self, path: Q) -> io::Result<()> where Q: AsRef<Path>,559 pub fn save<Q>(&self, path: Q) -> io::Result<()>
560 where
561 Q: AsRef<Path>,
562 {
563 dynamic_map!(*self, ref p -> {
564 p.save(path)
565 })
566 }
567
568 /// Saves the buffer to a file at the specified path in
569 /// the specified format.
570 ///
571 /// See [`save_buffer_with_format`](fn.save_buffer_with_format.html) for
572 /// supported types.
save_with_format<Q>(&self, path: Q, format: ImageFormat) -> io::Result<()> where Q: AsRef<Path>,573 pub fn save_with_format<Q>(&self, path: Q, format: ImageFormat) -> io::Result<()>
574 where
575 Q: AsRef<Path>,
576 {
577 dynamic_map!(*self, ref p -> {
578 p.save_with_format(path, format)
579 })
580 }
581 }
582
583 #[allow(deprecated)]
584 impl GenericImageView for DynamicImage {
585 type Pixel = color::Rgba<u8>;
586 type InnerImageView = Self;
587
dimensions(&self) -> (u32, u32)588 fn dimensions(&self) -> (u32, u32) {
589 dynamic_map!(*self, ref p -> p.dimensions())
590 }
591
bounds(&self) -> (u32, u32, u32, u32)592 fn bounds(&self) -> (u32, u32, u32, u32) {
593 dynamic_map!(*self, ref p -> p.bounds())
594 }
595
get_pixel(&self, x: u32, y: u32) -> color::Rgba<u8>596 fn get_pixel(&self, x: u32, y: u32) -> color::Rgba<u8> {
597 dynamic_map!(*self, ref p -> p.get_pixel(x, y).to_rgba())
598 }
599
inner(&self) -> &Self::InnerImageView600 fn inner(&self) -> &Self::InnerImageView {
601 self
602 }
603 }
604
605 #[allow(deprecated)]
606 impl GenericImage for DynamicImage {
607 type InnerImage = DynamicImage;
608
put_pixel(&mut self, x: u32, y: u32, pixel: color::Rgba<u8>)609 fn put_pixel(&mut self, x: u32, y: u32, pixel: color::Rgba<u8>) {
610 match *self {
611 DynamicImage::ImageLuma8(ref mut p) => p.put_pixel(x, y, pixel.to_luma()),
612 DynamicImage::ImageLumaA8(ref mut p) => p.put_pixel(x, y, pixel.to_luma_alpha()),
613 DynamicImage::ImageRgb8(ref mut p) => p.put_pixel(x, y, pixel.to_rgb()),
614 DynamicImage::ImageRgba8(ref mut p) => p.put_pixel(x, y, pixel),
615 DynamicImage::ImageBgr8(ref mut p) => p.put_pixel(x, y, pixel.to_bgr()),
616 DynamicImage::ImageBgra8(ref mut p) => p.put_pixel(x, y, pixel.to_bgra()),
617 }
618 }
619 /// DEPRECATED: Use iterator `pixels_mut` to blend the pixels directly.
blend_pixel(&mut self, x: u32, y: u32, pixel: color::Rgba<u8>)620 fn blend_pixel(&mut self, x: u32, y: u32, pixel: color::Rgba<u8>) {
621 match *self {
622 DynamicImage::ImageLuma8(ref mut p) => p.blend_pixel(x, y, pixel.to_luma()),
623 DynamicImage::ImageLumaA8(ref mut p) => p.blend_pixel(x, y, pixel.to_luma_alpha()),
624 DynamicImage::ImageRgb8(ref mut p) => p.blend_pixel(x, y, pixel.to_rgb()),
625 DynamicImage::ImageRgba8(ref mut p) => p.blend_pixel(x, y, pixel),
626
627 DynamicImage::ImageBgr8(ref mut p) => p.blend_pixel(x, y, pixel.to_bgr()),
628 DynamicImage::ImageBgra8(ref mut p) => p.blend_pixel(x, y, pixel.to_bgra()),
629 }
630 }
631
632 /// DEPRECATED: Do not use is function: It is unimplemented!
get_pixel_mut(&mut self, _: u32, _: u32) -> &mut color::Rgba<u8>633 fn get_pixel_mut(&mut self, _: u32, _: u32) -> &mut color::Rgba<u8> {
634 unimplemented!()
635 }
636
inner_mut(&mut self) -> &mut Self::InnerImage637 fn inner_mut(&mut self) -> &mut Self::InnerImage {
638 self
639 }
640 }
641
decoder_to_image<'a, I: ImageDecoder<'a>>(codec: I) -> ImageResult<DynamicImage>642 fn decoder_to_image<'a, I: ImageDecoder<'a>>(codec: I) -> ImageResult<DynamicImage> {
643 let color = codec.colortype();
644 let (w, h) = codec.dimensions();
645 let buf = codec.read_image()?;
646
647 // TODO: Avoid this cast by having ImageBuffer use u64's
648 assert!(w <= u64::from(u32::max_value()));
649 assert!(h <= u64::from(u32::max_value()));
650 let (w, h) = (w as u32, h as u32);
651
652 let image = match color {
653 color::ColorType::RGB(8) => ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb8),
654
655 color::ColorType::RGBA(8) => ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba8),
656
657 color::ColorType::BGR(8) => ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageBgr8),
658
659 color::ColorType::BGRA(8) => ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageBgra8),
660
661 color::ColorType::Gray(8) => ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLuma8),
662
663 color::ColorType::GrayA(8) => {
664 ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLumaA8)
665 }
666 color::ColorType::Gray(bit_depth) if bit_depth == 1 || bit_depth == 2 || bit_depth == 4 => {
667 gray_to_luma8(bit_depth, w, h, &buf).map(DynamicImage::ImageLuma8)
668 }
669 _ => return Err(image::ImageError::UnsupportedColor(color)),
670 };
671 match image {
672 Some(image) => Ok(image),
673 None => Err(image::ImageError::DimensionError),
674 }
675 }
676
gray_to_luma8(bit_depth: u8, w: u32, h: u32, buf: &[u8]) -> Option<GrayImage>677 fn gray_to_luma8(bit_depth: u8, w: u32, h: u32, buf: &[u8]) -> Option<GrayImage> {
678 // Note: this conversion assumes that the scanlines begin on byte boundaries
679 let mask = (1u8 << bit_depth as usize) - 1;
680 let scaling_factor = 255 / ((1 << bit_depth as usize) - 1);
681 let bit_width = w * u32::from(bit_depth);
682 let skip = if bit_width % 8 == 0 {
683 0
684 } else {
685 (8 - bit_width % 8) / u32::from(bit_depth)
686 };
687 let row_len = w + skip;
688 let mut p = Vec::new();
689 let mut i = 0;
690 for v in buf {
691 for shift in num_iter::range_step_inclusive(8i8 - (bit_depth as i8), 0, -(bit_depth as i8))
692 {
693 // skip the pixels that can be neglected because scanlines should
694 // start at byte boundaries
695 if i % (row_len as usize) < (w as usize) {
696 let pixel = (v & mask << shift as usize) >> shift as usize;
697 p.push(pixel * scaling_factor);
698 }
699 i += 1;
700 }
701 }
702 ImageBuffer::from_raw(w, h, p)
703 }
704
705 #[allow(deprecated)]
image_to_bytes(image: &DynamicImage) -> Vec<u8>706 fn image_to_bytes(image: &DynamicImage) -> Vec<u8> {
707 match *image {
708 // TODO: consider transmuting
709 DynamicImage::ImageLuma8(ref a) => a.iter().cloned().collect(),
710
711 DynamicImage::ImageLumaA8(ref a) => a.iter().cloned().collect(),
712
713 DynamicImage::ImageRgb8(ref a) => a.iter().cloned().collect(),
714
715 DynamicImage::ImageRgba8(ref a) => a.iter().cloned().collect(),
716
717 DynamicImage::ImageBgr8(ref a) => a.iter().cloned().collect(),
718
719 DynamicImage::ImageBgra8(ref a) => a.iter().cloned().collect(),
720 }
721 }
722
723 /// Open the image located at the path specified.
724 /// The image's format is determined from the path's file extension.
725 ///
726 /// Try [`io::Reader`] for more advanced uses, including guessing the format based on the file's
727 /// content before its path.
728 ///
729 /// [`io::Reader`]: io/struct.Reader.html
open<P>(path: P) -> ImageResult<DynamicImage> where P: AsRef<Path>,730 pub fn open<P>(path: P) -> ImageResult<DynamicImage>
731 where
732 P: AsRef<Path>,
733 {
734 // thin wrapper function to strip generics before calling open_impl
735 free_functions::open_impl(path.as_ref())
736 }
737
738 /// Read the dimensions of the image located at the specified path.
739 /// This is faster than fully loading the image and then getting its dimensions.
740 ///
741 /// Try [`io::Reader`] for more advanced uses, including guessing the format based on the file's
742 /// content before its path or manually supplying the format.
743 ///
744 /// [`io::Reader`]: io/struct.Reader.html
image_dimensions<P>(path: P) -> ImageResult<(u32, u32)> where P: AsRef<Path>,745 pub fn image_dimensions<P>(path: P) -> ImageResult<(u32, u32)>
746 where
747 P: AsRef<Path>,
748 {
749 // thin wrapper function to strip generics before calling open_impl
750 free_functions::image_dimensions_impl(path.as_ref())
751 }
752
753 /// Saves the supplied buffer to a file at the path specified.
754 ///
755 /// The image format is derived from the file extension. The buffer is assumed to have
756 /// the correct format according to the specified color type.
757
758 /// This will lead to corrupted files if the buffer contains malformed data. Currently only
759 /// jpeg, png, ico, pnm, bmp and tiff files are supported.
save_buffer<P>( path: P, buf: &[u8], width: u32, height: u32, color: color::ColorType, ) -> io::Result<()> where P: AsRef<Path>,760 pub fn save_buffer<P>(
761 path: P,
762 buf: &[u8],
763 width: u32,
764 height: u32,
765 color: color::ColorType,
766 ) -> io::Result<()>
767 where
768 P: AsRef<Path>,
769 {
770 // thin wrapper function to strip generics before calling save_buffer_impl
771 free_functions::save_buffer_impl(path.as_ref(), buf, width, height, color)
772 }
773
774 /// Saves the supplied buffer to a file at the path specified
775 /// in the specified format.
776 ///
777 /// The buffer is assumed to have the correct format according
778 /// to the specified color type.
779 /// This will lead to corrupted files if the buffer contains
780 /// malformed data. Currently only jpeg, png, ico, bmp and
781 /// tiff files are supported.
save_buffer_with_format<P>( path: P, buf: &[u8], width: u32, height: u32, color: color::ColorType, format: ImageFormat, ) -> io::Result<()> where P: AsRef<Path>,782 pub fn save_buffer_with_format<P>(
783 path: P,
784 buf: &[u8],
785 width: u32,
786 height: u32,
787 color: color::ColorType,
788 format: ImageFormat,
789 ) -> io::Result<()>
790 where
791 P: AsRef<Path>,
792 {
793 // thin wrapper function to strip generics
794 free_functions::save_buffer_with_format_impl(path.as_ref(), buf, width, height, color, format)
795 }
796
797 /// Create a new image from a byte slice
798 ///
799 /// Makes an educated guess about the image format.
800 /// TGA is not supported by this function.
801 ///
802 /// Try [`io::Reader`] for more advanced uses.
803 ///
804 /// [`io::Reader`]: io/struct.Reader.html
load_from_memory(buffer: &[u8]) -> ImageResult<DynamicImage>805 pub fn load_from_memory(buffer: &[u8]) -> ImageResult<DynamicImage> {
806 let format = free_functions::guess_format(buffer)?;
807 load_from_memory_with_format(buffer, format)
808 }
809
810 /// Create a new image from a byte slice
811 ///
812 /// This is just a simple wrapper that constructs an `std::io::Cursor` around the buffer and then
813 /// calls `load` with that reader.
814 ///
815 /// Try [`io::Reader`] for more advanced uses.
816 ///
817 /// [`load`]: fn.load.html
818 /// [`io::Reader`]: io/struct.Reader.html
819 #[inline(always)]
load_from_memory_with_format(buf: &[u8], format: ImageFormat) -> ImageResult<DynamicImage>820 pub fn load_from_memory_with_format(buf: &[u8], format: ImageFormat) -> ImageResult<DynamicImage> {
821 let b = io::Cursor::new(buf);
822 free_functions::load(b, format)
823 }
824
825 /// Calculates the width and height an image should be resized to.
826 /// This preserves aspect ratio, and based on the `fill` parameter
827 /// will either fill the dimensions to fit inside the smaller constraint
828 /// (will overflow the specified bounds on one axis to preserve
829 /// aspect ratio), or will shrink so that both dimensions are
830 /// completely contained with in the given `width` and `height`,
831 /// with empty space on one axis.
resize_dimensions(width: u32, height: u32, nwidth: u32, nheight: u32, fill: bool) -> (u32, u32)832 fn resize_dimensions(width: u32, height: u32, nwidth: u32, nheight: u32, fill: bool) -> (u32, u32) {
833 let ratio = u64::from(width) * u64::from(nheight);
834 let nratio = u64::from(nwidth) * u64::from(height);
835
836 let use_width = if fill {
837 nratio > ratio
838 } else {
839 nratio <= ratio
840 };
841 let intermediate = if use_width {
842 u64::from(height) * u64::from(nwidth) / u64::from(width)
843 } else {
844 u64::from(width) * u64::from(nheight) / u64::from(height)
845 };
846 if use_width {
847 if intermediate <= u64::from(::std::u32::MAX) {
848 (nwidth, intermediate as u32)
849 } else {
850 (
851 (u64::from(nwidth) * u64::from(::std::u32::MAX) / intermediate) as u32,
852 ::std::u32::MAX,
853 )
854 }
855 } else if intermediate <= u64::from(::std::u32::MAX) {
856 (intermediate as u32, nheight)
857 } else {
858 (
859 ::std::u32::MAX,
860 (u64::from(nheight) * u64::from(::std::u32::MAX) / intermediate) as u32,
861 )
862 }
863 }
864
865 #[cfg(test)]
866 mod bench {
867 #[cfg(feature = "benchmarks")]
868 use test;
869
870 #[bench]
871 #[cfg(feature = "benchmarks")]
bench_conversion(b: &mut test::Bencher)872 fn bench_conversion(b: &mut test::Bencher) {
873 let a = super::DynamicImage::ImageRgb8(::ImageBuffer::new(1000, 1000));
874 b.iter(|| a.to_luma());
875 b.bytes = 1000 * 1000 * 3
876 }
877 }
878
879 #[cfg(test)]
880 mod test {
881 #[test]
test_empty_file()882 fn test_empty_file() {
883 assert!(super::load_from_memory(b"").is_err());
884 }
885
886 quickcheck! {
887 fn resize_bounds_correctly_width(old_w: u32, new_w: u32) -> bool {
888 if old_w == 0 || new_w == 0 { return true; }
889 let result = super::resize_dimensions(old_w, 400, new_w, ::std::u32::MAX, false);
890 result.0 == new_w && result.1 == (400 as f64 * new_w as f64 / old_w as f64) as u32
891 }
892 }
893
894 quickcheck! {
895 fn resize_bounds_correctly_height(old_h: u32, new_h: u32) -> bool {
896 if old_h == 0 || new_h == 0 { return true; }
897 let result = super::resize_dimensions(400, old_h, ::std::u32::MAX, new_h, false);
898 result.1 == new_h && result.0 == (400 as f64 * new_h as f64 / old_h as f64) as u32
899 }
900 }
901
902 #[test]
resize_handles_fill()903 fn resize_handles_fill() {
904 let result = super::resize_dimensions(100, 200, 200, 500, true);
905 assert!(result.0 == 250);
906 assert!(result.1 == 500);
907
908 let result = super::resize_dimensions(200, 100, 500, 200, true);
909 assert!(result.0 == 500);
910 assert!(result.1 == 250);
911 }
912
913 #[test]
resize_handles_overflow()914 fn resize_handles_overflow() {
915 let result = super::resize_dimensions(100, ::std::u32::MAX, 200, ::std::u32::MAX, true);
916 assert!(result.0 == 100);
917 assert!(result.1 == ::std::u32::MAX);
918
919 let result = super::resize_dimensions(::std::u32::MAX, 100, ::std::u32::MAX, 200, true);
920 assert!(result.0 == ::std::u32::MAX);
921 assert!(result.1 == 100);
922 }
923
924 #[test]
925 #[rustfmt::skip]
gray_to_luma8_skip()926 fn gray_to_luma8_skip() {
927 let check = |bit_depth, w, h, from, to| {
928 assert_eq!(
929 super::gray_to_luma8(bit_depth, w, h, from).map(super::GrayImage::into_raw),
930 Some(to)
931 );
932 };
933 // Bit depth 1, skip is more than half a byte
934 check(
935 1,
936 10,
937 2,
938 &[0b11110000, 0b11000000, 0b00001111, 0b11000000],
939 vec![
940 255, 255, 255, 255, 0,
941 0, 0, 0, 255, 255,
942 0, 0, 0, 0, 255,
943 255, 255, 255, 255, 255,
944 ],
945 );
946 // Bit depth 2, skip is more than half a byte
947 check(
948 2,
949 5,
950 2,
951 &[0b11110000, 0b11000000, 0b00001111, 0b11000000],
952 vec![255, 255, 0, 0, 255, 0, 0, 255, 255, 255],
953 );
954 // Bit depth 2, skip is 0
955 check(
956 2,
957 4,
958 2,
959 &[0b11110000, 0b00001111],
960 vec![255, 255, 0, 0, 0, 0, 255, 255],
961 );
962 // Bit depth 4, skip is half a byte
963 check(4, 1, 2, &[0b11110011, 0b00001100], vec![255, 0]);
964 }
965
966 #[cfg(feature = "jpeg")]
967 #[test]
image_dimensions()968 fn image_dimensions() {
969 let im_path = "./tests/images/jpg/progressive/cat.jpg";
970 let dims = super::image_dimensions(im_path).unwrap();
971 assert_eq!(dims, (320, 240));
972 }
973 }
974