1362 lines
46 KiB
Rust
1362 lines
46 KiB
Rust
use std::io;
|
|
use std::io::{Seek, Write};
|
|
use std::path::Path;
|
|
use std::u32;
|
|
|
|
#[cfg(feature = "gif")]
|
|
use crate::codecs::gif;
|
|
#[cfg(feature = "png")]
|
|
use crate::codecs::png;
|
|
#[cfg(feature = "pnm")]
|
|
use crate::codecs::pnm;
|
|
|
|
use crate::buffer_::{
|
|
ConvertBuffer, Gray16Image, GrayAlpha16Image, GrayAlphaImage, GrayImage, ImageBuffer,
|
|
Rgb16Image, RgbImage, Rgba16Image, RgbaImage,
|
|
};
|
|
use crate::color::{self, IntoColor};
|
|
use crate::error::{ImageError, ImageResult, ParameterError, ParameterErrorKind};
|
|
// FIXME: These imports exist because we don't support all of our own color types.
|
|
use crate::error::{ImageFormatHint, UnsupportedError, UnsupportedErrorKind};
|
|
use crate::flat::FlatSamples;
|
|
use crate::image::{
|
|
GenericImage, GenericImageView, ImageDecoder, ImageEncoder, ImageFormat, ImageOutputFormat,
|
|
};
|
|
use crate::imageops;
|
|
use crate::io::free_functions;
|
|
use crate::math::resize_dimensions;
|
|
use crate::traits::Pixel;
|
|
use crate::{image, Luma, LumaA};
|
|
use crate::{Rgb32FImage, Rgba32FImage};
|
|
|
|
/// A Dynamic Image
|
|
///
|
|
/// This represents a _matrix_ of _pixels_ which are _convertible_ from and to an _RGBA_
|
|
/// representation. More variants that adhere to these principles may get added in the future, in
|
|
/// particular to cover other combinations typically used.
|
|
///
|
|
/// # Usage
|
|
///
|
|
/// This type can act as a converter between specific `ImageBuffer` instances.
|
|
///
|
|
/// ```
|
|
/// use image::{DynamicImage, GrayImage, RgbImage};
|
|
///
|
|
/// let rgb: RgbImage = RgbImage::new(10, 10);
|
|
/// let luma: GrayImage = DynamicImage::ImageRgb8(rgb).into_luma8();
|
|
/// ```
|
|
///
|
|
/// # Design
|
|
///
|
|
/// There is no goal to provide an all-encompassing type with all possible memory layouts. This
|
|
/// would hardly be feasible as a simple enum, due to the sheer number of combinations of channel
|
|
/// kinds, channel order, and bit depth. Rather, this type provides an opinionated selection with
|
|
/// normalized channel order which can store common pixel values without loss.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
#[non_exhaustive]
|
|
pub enum DynamicImage {
|
|
/// Each pixel in this image is 8-bit Luma
|
|
ImageLuma8(GrayImage),
|
|
|
|
/// Each pixel in this image is 8-bit Luma with alpha
|
|
ImageLumaA8(GrayAlphaImage),
|
|
|
|
/// Each pixel in this image is 8-bit Rgb
|
|
ImageRgb8(RgbImage),
|
|
|
|
/// Each pixel in this image is 8-bit Rgb with alpha
|
|
ImageRgba8(RgbaImage),
|
|
|
|
/// Each pixel in this image is 16-bit Luma
|
|
ImageLuma16(Gray16Image),
|
|
|
|
/// Each pixel in this image is 16-bit Luma with alpha
|
|
ImageLumaA16(GrayAlpha16Image),
|
|
|
|
/// Each pixel in this image is 16-bit Rgb
|
|
ImageRgb16(Rgb16Image),
|
|
|
|
/// Each pixel in this image is 16-bit Rgb with alpha
|
|
ImageRgba16(Rgba16Image),
|
|
|
|
/// Each pixel in this image is 32-bit float Rgb
|
|
ImageRgb32F(Rgb32FImage),
|
|
|
|
/// Each pixel in this image is 32-bit float Rgb with alpha
|
|
ImageRgba32F(Rgba32FImage),
|
|
}
|
|
|
|
macro_rules! dynamic_map(
|
|
($dynimage: expr, $image: pat => $action: expr) => ({
|
|
use DynamicImage::*;
|
|
match $dynimage {
|
|
ImageLuma8($image) => ImageLuma8($action),
|
|
ImageLumaA8($image) => ImageLumaA8($action),
|
|
ImageRgb8($image) => ImageRgb8($action),
|
|
ImageRgba8($image) => ImageRgba8($action),
|
|
ImageLuma16($image) => ImageLuma16($action),
|
|
ImageLumaA16($image) => ImageLumaA16($action),
|
|
ImageRgb16($image) => ImageRgb16($action),
|
|
ImageRgba16($image) => ImageRgba16($action),
|
|
ImageRgb32F($image) => ImageRgb32F($action),
|
|
ImageRgba32F($image) => ImageRgba32F($action),
|
|
}
|
|
});
|
|
|
|
($dynimage: expr, |$image: pat| $action: expr) => (
|
|
match $dynimage {
|
|
DynamicImage::ImageLuma8($image) => $action,
|
|
DynamicImage::ImageLumaA8($image) => $action,
|
|
DynamicImage::ImageRgb8($image) => $action,
|
|
DynamicImage::ImageRgba8($image) => $action,
|
|
DynamicImage::ImageLuma16($image) => $action,
|
|
DynamicImage::ImageLumaA16($image) => $action,
|
|
DynamicImage::ImageRgb16($image) => $action,
|
|
DynamicImage::ImageRgba16($image) => $action,
|
|
DynamicImage::ImageRgb32F($image) => $action,
|
|
DynamicImage::ImageRgba32F($image) => $action,
|
|
}
|
|
);
|
|
);
|
|
|
|
impl DynamicImage {
|
|
/// Creates a dynamic image backed by a buffer of gray pixels.
|
|
pub fn new_luma8(w: u32, h: u32) -> DynamicImage {
|
|
DynamicImage::ImageLuma8(ImageBuffer::new(w, h))
|
|
}
|
|
|
|
/// Creates a dynamic image backed by a buffer of gray
|
|
/// pixels with transparency.
|
|
pub fn new_luma_a8(w: u32, h: u32) -> DynamicImage {
|
|
DynamicImage::ImageLumaA8(ImageBuffer::new(w, h))
|
|
}
|
|
|
|
/// Creates a dynamic image backed by a buffer of RGB pixels.
|
|
pub fn new_rgb8(w: u32, h: u32) -> DynamicImage {
|
|
DynamicImage::ImageRgb8(ImageBuffer::new(w, h))
|
|
}
|
|
|
|
/// Creates a dynamic image backed by a buffer of RGBA pixels.
|
|
pub fn new_rgba8(w: u32, h: u32) -> DynamicImage {
|
|
DynamicImage::ImageRgba8(ImageBuffer::new(w, h))
|
|
}
|
|
|
|
/// Creates a dynamic image backed by a buffer of gray pixels.
|
|
pub fn new_luma16(w: u32, h: u32) -> DynamicImage {
|
|
DynamicImage::ImageLuma16(ImageBuffer::new(w, h))
|
|
}
|
|
|
|
/// Creates a dynamic image backed by a buffer of gray
|
|
/// pixels with transparency.
|
|
pub fn new_luma_a16(w: u32, h: u32) -> DynamicImage {
|
|
DynamicImage::ImageLumaA16(ImageBuffer::new(w, h))
|
|
}
|
|
|
|
/// Creates a dynamic image backed by a buffer of RGB pixels.
|
|
pub fn new_rgb16(w: u32, h: u32) -> DynamicImage {
|
|
DynamicImage::ImageRgb16(ImageBuffer::new(w, h))
|
|
}
|
|
|
|
/// Creates a dynamic image backed by a buffer of RGBA pixels.
|
|
pub fn new_rgba16(w: u32, h: u32) -> DynamicImage {
|
|
DynamicImage::ImageRgba16(ImageBuffer::new(w, h))
|
|
}
|
|
|
|
/// Creates a dynamic image backed by a buffer of RGB pixels.
|
|
pub fn new_rgb32f(w: u32, h: u32) -> DynamicImage {
|
|
DynamicImage::ImageRgb32F(ImageBuffer::new(w, h))
|
|
}
|
|
|
|
/// Creates a dynamic image backed by a buffer of RGBA pixels.
|
|
pub fn new_rgba32f(w: u32, h: u32) -> DynamicImage {
|
|
DynamicImage::ImageRgba32F(ImageBuffer::new(w, h))
|
|
}
|
|
|
|
/// Decodes an encoded image into a dynamic image.
|
|
pub fn from_decoder<'a>(decoder: impl ImageDecoder<'a>) -> ImageResult<Self> {
|
|
decoder_to_image(decoder)
|
|
}
|
|
|
|
/// Returns a copy of this image as an RGB image.
|
|
pub fn to_rgb8(&self) -> RgbImage {
|
|
dynamic_map!(*self, |ref p| p.convert())
|
|
}
|
|
|
|
/// Returns a copy of this image as an RGB image.
|
|
pub fn to_rgb16(&self) -> Rgb16Image {
|
|
dynamic_map!(*self, |ref p| p.convert())
|
|
}
|
|
|
|
/// Returns a copy of this image as an RGB image.
|
|
pub fn to_rgb32f(&self) -> Rgb32FImage {
|
|
dynamic_map!(*self, |ref p| p.convert())
|
|
}
|
|
|
|
/// Returns a copy of this image as an RGBA image.
|
|
pub fn to_rgba8(&self) -> RgbaImage {
|
|
dynamic_map!(*self, |ref p| p.convert())
|
|
}
|
|
|
|
/// Returns a copy of this image as an RGBA image.
|
|
pub fn to_rgba16(&self) -> Rgba16Image {
|
|
dynamic_map!(*self, |ref p| p.convert())
|
|
}
|
|
|
|
/// Returns a copy of this image as an RGBA image.
|
|
pub fn to_rgba32f(&self) -> Rgba32FImage {
|
|
dynamic_map!(*self, |ref p| p.convert())
|
|
}
|
|
|
|
/// Returns a copy of this image as a Luma image.
|
|
pub fn to_luma8(&self) -> GrayImage {
|
|
dynamic_map!(*self, |ref p| p.convert())
|
|
}
|
|
|
|
/// Returns a copy of this image as a Luma image.
|
|
pub fn to_luma16(&self) -> Gray16Image {
|
|
dynamic_map!(*self, |ref p| p.convert())
|
|
}
|
|
|
|
/// Returns a copy of this image as a Luma image.
|
|
pub fn to_luma32f(&self) -> ImageBuffer<Luma<f32>, Vec<f32>> {
|
|
dynamic_map!(*self, |ref p| p.convert())
|
|
}
|
|
|
|
/// Returns a copy of this image as a LumaA image.
|
|
pub fn to_luma_alpha8(&self) -> GrayAlphaImage {
|
|
dynamic_map!(*self, |ref p| p.convert())
|
|
}
|
|
|
|
/// Returns a copy of this image as a LumaA image.
|
|
pub fn to_luma_alpha16(&self) -> GrayAlpha16Image {
|
|
dynamic_map!(*self, |ref p| p.convert())
|
|
}
|
|
|
|
/// Returns a copy of this image as a LumaA image.
|
|
pub fn to_luma_alpha32f(&self) -> ImageBuffer<LumaA<f32>, Vec<f32>> {
|
|
dynamic_map!(*self, |ref p| p.convert())
|
|
}
|
|
|
|
/// Consume the image and returns a RGB image.
|
|
///
|
|
/// If the image was already the correct format, it is returned as is.
|
|
/// Otherwise, a copy is created.
|
|
pub fn into_rgb8(self) -> RgbImage {
|
|
match self {
|
|
DynamicImage::ImageRgb8(x) => x,
|
|
x => x.to_rgb8(),
|
|
}
|
|
}
|
|
|
|
/// Consume the image and returns a RGB image.
|
|
///
|
|
/// If the image was already the correct format, it is returned as is.
|
|
/// Otherwise, a copy is created.
|
|
pub fn into_rgb16(self) -> Rgb16Image {
|
|
match self {
|
|
DynamicImage::ImageRgb16(x) => x,
|
|
x => x.to_rgb16(),
|
|
}
|
|
}
|
|
|
|
/// Consume the image and returns a RGB image.
|
|
///
|
|
/// If the image was already the correct format, it is returned as is.
|
|
/// Otherwise, a copy is created.
|
|
pub fn into_rgb32f(self) -> Rgb32FImage {
|
|
match self {
|
|
DynamicImage::ImageRgb32F(x) => x,
|
|
x => x.to_rgb32f(),
|
|
}
|
|
}
|
|
|
|
/// Consume the image and returns a RGBA image.
|
|
///
|
|
/// If the image was already the correct format, it is returned as is.
|
|
/// Otherwise, a copy is created.
|
|
pub fn into_rgba8(self) -> RgbaImage {
|
|
match self {
|
|
DynamicImage::ImageRgba8(x) => x,
|
|
x => x.to_rgba8(),
|
|
}
|
|
}
|
|
|
|
/// Consume the image and returns a RGBA image.
|
|
///
|
|
/// If the image was already the correct format, it is returned as is.
|
|
/// Otherwise, a copy is created.
|
|
pub fn into_rgba16(self) -> Rgba16Image {
|
|
match self {
|
|
DynamicImage::ImageRgba16(x) => x,
|
|
x => x.to_rgba16(),
|
|
}
|
|
}
|
|
|
|
/// Consume the image and returns a RGBA image.
|
|
///
|
|
/// If the image was already the correct format, it is returned as is.
|
|
/// Otherwise, a copy is created.
|
|
pub fn into_rgba32f(self) -> Rgba32FImage {
|
|
match self {
|
|
DynamicImage::ImageRgba32F(x) => x,
|
|
x => x.to_rgba32f(),
|
|
}
|
|
}
|
|
|
|
/// Consume the image and returns a Luma image.
|
|
///
|
|
/// If the image was already the correct format, it is returned as is.
|
|
/// Otherwise, a copy is created.
|
|
pub fn into_luma8(self) -> GrayImage {
|
|
match self {
|
|
DynamicImage::ImageLuma8(x) => x,
|
|
x => x.to_luma8(),
|
|
}
|
|
}
|
|
|
|
/// Consume the image and returns a Luma image.
|
|
///
|
|
/// If the image was already the correct format, it is returned as is.
|
|
/// Otherwise, a copy is created.
|
|
pub fn into_luma16(self) -> Gray16Image {
|
|
match self {
|
|
DynamicImage::ImageLuma16(x) => x,
|
|
x => x.to_luma16(),
|
|
}
|
|
}
|
|
|
|
/// Consume the image and returns a LumaA image.
|
|
///
|
|
/// If the image was already the correct format, it is returned as is.
|
|
/// Otherwise, a copy is created.
|
|
pub fn into_luma_alpha8(self) -> GrayAlphaImage {
|
|
match self {
|
|
DynamicImage::ImageLumaA8(x) => x,
|
|
x => x.to_luma_alpha8(),
|
|
}
|
|
}
|
|
|
|
/// Consume the image and returns a LumaA image.
|
|
///
|
|
/// If the image was already the correct format, it is returned as is.
|
|
/// Otherwise, a copy is created.
|
|
pub fn into_luma_alpha16(self) -> GrayAlpha16Image {
|
|
match self {
|
|
DynamicImage::ImageLumaA16(x) => x,
|
|
x => x.to_luma_alpha16(),
|
|
}
|
|
}
|
|
|
|
/// Return a cut-out of this image delimited by the bounding rectangle.
|
|
///
|
|
/// Note: this method does *not* modify the object,
|
|
/// and its signature will be replaced with `crop_imm()`'s in the 0.24 release
|
|
pub fn crop(&mut self, x: u32, y: u32, width: u32, height: u32) -> DynamicImage {
|
|
dynamic_map!(*self, ref mut p => imageops::crop(p, x, y, width, height).to_image())
|
|
}
|
|
|
|
/// Return a cut-out of this image delimited by the bounding rectangle.
|
|
pub fn crop_imm(&self, x: u32, y: u32, width: u32, height: u32) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::crop_imm(p, x, y, width, height).to_image())
|
|
}
|
|
|
|
/// Return a reference to an 8bit RGB image
|
|
pub fn as_rgb8(&self) -> Option<&RgbImage> {
|
|
match *self {
|
|
DynamicImage::ImageRgb8(ref p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a mutable reference to an 8bit RGB image
|
|
pub fn as_mut_rgb8(&mut self) -> Option<&mut RgbImage> {
|
|
match *self {
|
|
DynamicImage::ImageRgb8(ref mut p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a reference to an 8bit RGBA image
|
|
pub fn as_rgba8(&self) -> Option<&RgbaImage> {
|
|
match *self {
|
|
DynamicImage::ImageRgba8(ref p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a mutable reference to an 8bit RGBA image
|
|
pub fn as_mut_rgba8(&mut self) -> Option<&mut RgbaImage> {
|
|
match *self {
|
|
DynamicImage::ImageRgba8(ref mut p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a reference to an 8bit Grayscale image
|
|
pub fn as_luma8(&self) -> Option<&GrayImage> {
|
|
match *self {
|
|
DynamicImage::ImageLuma8(ref p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a mutable reference to an 8bit Grayscale image
|
|
pub fn as_mut_luma8(&mut self) -> Option<&mut GrayImage> {
|
|
match *self {
|
|
DynamicImage::ImageLuma8(ref mut p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a reference to an 8bit Grayscale image with an alpha channel
|
|
pub fn as_luma_alpha8(&self) -> Option<&GrayAlphaImage> {
|
|
match *self {
|
|
DynamicImage::ImageLumaA8(ref p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a mutable reference to an 8bit Grayscale image with an alpha channel
|
|
pub fn as_mut_luma_alpha8(&mut self) -> Option<&mut GrayAlphaImage> {
|
|
match *self {
|
|
DynamicImage::ImageLumaA8(ref mut p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a reference to an 16bit RGB image
|
|
pub fn as_rgb16(&self) -> Option<&Rgb16Image> {
|
|
match *self {
|
|
DynamicImage::ImageRgb16(ref p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a mutable reference to an 16bit RGB image
|
|
pub fn as_mut_rgb16(&mut self) -> Option<&mut Rgb16Image> {
|
|
match *self {
|
|
DynamicImage::ImageRgb16(ref mut p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a reference to an 16bit RGBA image
|
|
pub fn as_rgba16(&self) -> Option<&Rgba16Image> {
|
|
match *self {
|
|
DynamicImage::ImageRgba16(ref p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a mutable reference to an 16bit RGBA image
|
|
pub fn as_mut_rgba16(&mut self) -> Option<&mut Rgba16Image> {
|
|
match *self {
|
|
DynamicImage::ImageRgba16(ref mut p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a reference to an 32bit RGB image
|
|
pub fn as_rgb32f(&self) -> Option<&Rgb32FImage> {
|
|
match *self {
|
|
DynamicImage::ImageRgb32F(ref p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a mutable reference to an 32bit RGB image
|
|
pub fn as_mut_rgb32f(&mut self) -> Option<&mut Rgb32FImage> {
|
|
match *self {
|
|
DynamicImage::ImageRgb32F(ref mut p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a reference to an 32bit RGBA image
|
|
pub fn as_rgba32f(&self) -> Option<&Rgba32FImage> {
|
|
match *self {
|
|
DynamicImage::ImageRgba32F(ref p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a mutable reference to an 16bit RGBA image
|
|
pub fn as_mut_rgba32f(&mut self) -> Option<&mut Rgba32FImage> {
|
|
match *self {
|
|
DynamicImage::ImageRgba32F(ref mut p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a reference to an 16bit Grayscale image
|
|
pub fn as_luma16(&self) -> Option<&Gray16Image> {
|
|
match *self {
|
|
DynamicImage::ImageLuma16(ref p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a mutable reference to an 16bit Grayscale image
|
|
pub fn as_mut_luma16(&mut self) -> Option<&mut Gray16Image> {
|
|
match *self {
|
|
DynamicImage::ImageLuma16(ref mut p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a reference to an 16bit Grayscale image with an alpha channel
|
|
pub fn as_luma_alpha16(&self) -> Option<&GrayAlpha16Image> {
|
|
match *self {
|
|
DynamicImage::ImageLumaA16(ref p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a mutable reference to an 16bit Grayscale image with an alpha channel
|
|
pub fn as_mut_luma_alpha16(&mut self) -> Option<&mut GrayAlpha16Image> {
|
|
match *self {
|
|
DynamicImage::ImageLumaA16(ref mut p) => Some(p),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a view on the raw sample buffer for 8 bit per channel images.
|
|
pub fn as_flat_samples_u8(&self) -> Option<FlatSamples<&[u8]>> {
|
|
match *self {
|
|
DynamicImage::ImageLuma8(ref p) => Some(p.as_flat_samples()),
|
|
DynamicImage::ImageLumaA8(ref p) => Some(p.as_flat_samples()),
|
|
DynamicImage::ImageRgb8(ref p) => Some(p.as_flat_samples()),
|
|
DynamicImage::ImageRgba8(ref p) => Some(p.as_flat_samples()),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a view on the raw sample buffer for 16 bit per channel images.
|
|
pub fn as_flat_samples_u16(&self) -> Option<FlatSamples<&[u16]>> {
|
|
match *self {
|
|
DynamicImage::ImageLuma16(ref p) => Some(p.as_flat_samples()),
|
|
DynamicImage::ImageLumaA16(ref p) => Some(p.as_flat_samples()),
|
|
DynamicImage::ImageRgb16(ref p) => Some(p.as_flat_samples()),
|
|
DynamicImage::ImageRgba16(ref p) => Some(p.as_flat_samples()),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return a view on the raw sample buffer for 32bit per channel images.
|
|
pub fn as_flat_samples_f32(&self) -> Option<FlatSamples<&[f32]>> {
|
|
match *self {
|
|
DynamicImage::ImageRgb32F(ref p) => Some(p.as_flat_samples()),
|
|
DynamicImage::ImageRgba32F(ref p) => Some(p.as_flat_samples()),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
/// Return this image's pixels as a native endian byte slice.
|
|
pub fn as_bytes(&self) -> &[u8] {
|
|
// we can do this because every variant contains an `ImageBuffer<_, Vec<_>>`
|
|
dynamic_map!(*self, |ref image_buffer| bytemuck::cast_slice(
|
|
image_buffer.as_raw().as_ref()
|
|
))
|
|
}
|
|
|
|
// TODO: choose a name under which to expose?
|
|
fn inner_bytes(&self) -> &[u8] {
|
|
// we can do this because every variant contains an `ImageBuffer<_, Vec<_>>`
|
|
dynamic_map!(*self, |ref image_buffer| bytemuck::cast_slice(
|
|
image_buffer.inner_pixels()
|
|
))
|
|
}
|
|
|
|
/// Return this image's pixels as a byte vector. If the `ImageBuffer`
|
|
/// container is `Vec<u8>`, this operation is free. Otherwise, a copy
|
|
/// is returned.
|
|
pub fn into_bytes(self) -> Vec<u8> {
|
|
// we can do this because every variant contains an `ImageBuffer<_, Vec<_>>`
|
|
dynamic_map!(self, |image_buffer| {
|
|
match bytemuck::allocation::try_cast_vec(image_buffer.into_raw()) {
|
|
Ok(vec) => vec,
|
|
Err((_, vec)) => {
|
|
// Fallback: vector requires an exact alignment and size match
|
|
// Reuse of the allocation as done in the Ok branch only works if the
|
|
// underlying container is exactly Vec<u8> (or compatible but that's the only
|
|
// alternative at the time of writing).
|
|
// In all other cases we must allocate a new vector with the 'same' contents.
|
|
bytemuck::cast_slice(&vec).to_owned()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Return a copy of this image's pixels as a byte vector.
|
|
/// Deprecated, because it does nothing but hide an expensive clone operation.
|
|
#[deprecated(
|
|
since = "0.24.0",
|
|
note = "use `image.into_bytes()` or `image.as_bytes().to_vec()` instead"
|
|
)]
|
|
pub fn to_bytes(&self) -> Vec<u8> {
|
|
self.as_bytes().to_vec()
|
|
}
|
|
|
|
/// Return this image's color type.
|
|
pub fn color(&self) -> color::ColorType {
|
|
match *self {
|
|
DynamicImage::ImageLuma8(_) => color::ColorType::L8,
|
|
DynamicImage::ImageLumaA8(_) => color::ColorType::La8,
|
|
DynamicImage::ImageRgb8(_) => color::ColorType::Rgb8,
|
|
DynamicImage::ImageRgba8(_) => color::ColorType::Rgba8,
|
|
DynamicImage::ImageLuma16(_) => color::ColorType::L16,
|
|
DynamicImage::ImageLumaA16(_) => color::ColorType::La16,
|
|
DynamicImage::ImageRgb16(_) => color::ColorType::Rgb16,
|
|
DynamicImage::ImageRgba16(_) => color::ColorType::Rgba16,
|
|
DynamicImage::ImageRgb32F(_) => color::ColorType::Rgb32F,
|
|
DynamicImage::ImageRgba32F(_) => color::ColorType::Rgba32F,
|
|
}
|
|
}
|
|
|
|
/// Returns the width of the underlying image
|
|
pub fn width(&self) -> u32 {
|
|
dynamic_map!(*self, |ref p| { p.width() })
|
|
}
|
|
|
|
/// Returns the height of the underlying image
|
|
pub fn height(&self) -> u32 {
|
|
dynamic_map!(*self, |ref p| { p.height() })
|
|
}
|
|
|
|
/// Return a grayscale version of this image.
|
|
/// Returns `Luma` images in most cases. However, for `f32` images,
|
|
/// this will return a grayscale `Rgb/Rgba` image instead.
|
|
pub fn grayscale(&self) -> DynamicImage {
|
|
match *self {
|
|
DynamicImage::ImageLuma8(ref p) => DynamicImage::ImageLuma8(p.clone()),
|
|
DynamicImage::ImageLumaA8(ref p) => {
|
|
DynamicImage::ImageLumaA8(imageops::grayscale_alpha(p))
|
|
}
|
|
DynamicImage::ImageRgb8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
|
|
DynamicImage::ImageRgba8(ref p) => {
|
|
DynamicImage::ImageLumaA8(imageops::grayscale_alpha(p))
|
|
}
|
|
DynamicImage::ImageLuma16(ref p) => DynamicImage::ImageLuma16(p.clone()),
|
|
DynamicImage::ImageLumaA16(ref p) => {
|
|
DynamicImage::ImageLumaA16(imageops::grayscale_alpha(p))
|
|
}
|
|
DynamicImage::ImageRgb16(ref p) => DynamicImage::ImageLuma16(imageops::grayscale(p)),
|
|
DynamicImage::ImageRgba16(ref p) => {
|
|
DynamicImage::ImageLumaA16(imageops::grayscale_alpha(p))
|
|
}
|
|
DynamicImage::ImageRgb32F(ref p) => {
|
|
DynamicImage::ImageRgb32F(imageops::grayscale_with_type(p))
|
|
}
|
|
DynamicImage::ImageRgba32F(ref p) => {
|
|
DynamicImage::ImageRgba32F(imageops::grayscale_with_type_alpha(p))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Invert the colors of this image.
|
|
/// This method operates inplace.
|
|
pub fn invert(&mut self) {
|
|
dynamic_map!(*self, |ref mut p| imageops::invert(p))
|
|
}
|
|
|
|
/// Resize this image using the specified filter algorithm.
|
|
/// Returns a new image. The image's aspect ratio is preserved.
|
|
/// The image is scaled to the maximum possible size that fits
|
|
/// within the bounds specified by `nwidth` and `nheight`.
|
|
pub fn resize(&self, nwidth: u32, nheight: u32, filter: imageops::FilterType) -> DynamicImage {
|
|
if (nwidth, nheight) == self.dimensions() {
|
|
return self.clone();
|
|
}
|
|
let (width2, height2) =
|
|
resize_dimensions(self.width(), self.height(), nwidth, nheight, false);
|
|
|
|
self.resize_exact(width2, height2, filter)
|
|
}
|
|
|
|
/// Resize this image using the specified filter algorithm.
|
|
/// Returns a new image. Does not preserve aspect ratio.
|
|
/// `nwidth` and `nheight` are the new image's dimensions
|
|
pub fn resize_exact(
|
|
&self,
|
|
nwidth: u32,
|
|
nheight: u32,
|
|
filter: imageops::FilterType,
|
|
) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::resize(p, nwidth, nheight, filter))
|
|
}
|
|
|
|
/// Scale this image down to fit within a specific size.
|
|
/// Returns a new image. The image's aspect ratio is preserved.
|
|
/// The image is scaled to the maximum possible size that fits
|
|
/// within the bounds specified by `nwidth` and `nheight`.
|
|
///
|
|
/// This method uses a fast integer algorithm where each source
|
|
/// pixel contributes to exactly one target pixel.
|
|
/// May give aliasing artifacts if new size is close to old size.
|
|
pub fn thumbnail(&self, nwidth: u32, nheight: u32) -> DynamicImage {
|
|
let (width2, height2) =
|
|
resize_dimensions(self.width(), self.height(), nwidth, nheight, false);
|
|
self.thumbnail_exact(width2, height2)
|
|
}
|
|
|
|
/// Scale this image down to a specific size.
|
|
/// Returns a new image. Does not preserve aspect ratio.
|
|
/// `nwidth` and `nheight` are the new image's dimensions.
|
|
/// This method uses a fast integer algorithm where each source
|
|
/// pixel contributes to exactly one target pixel.
|
|
/// May give aliasing artifacts if new size is close to old size.
|
|
pub fn thumbnail_exact(&self, nwidth: u32, nheight: u32) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::thumbnail(p, nwidth, nheight))
|
|
}
|
|
|
|
/// Resize this image using the specified filter algorithm.
|
|
/// Returns a new image. The image's aspect ratio is preserved.
|
|
/// The image is scaled to the maximum possible size that fits
|
|
/// within the larger (relative to aspect ratio) of the bounds
|
|
/// specified by `nwidth` and `nheight`, then cropped to
|
|
/// fit within the other bound.
|
|
pub fn resize_to_fill(
|
|
&self,
|
|
nwidth: u32,
|
|
nheight: u32,
|
|
filter: imageops::FilterType,
|
|
) -> DynamicImage {
|
|
let (width2, height2) =
|
|
resize_dimensions(self.width(), self.height(), nwidth, nheight, true);
|
|
|
|
let mut intermediate = self.resize_exact(width2, height2, filter);
|
|
let (iwidth, iheight) = intermediate.dimensions();
|
|
let ratio = u64::from(iwidth) * u64::from(nheight);
|
|
let nratio = u64::from(nwidth) * u64::from(iheight);
|
|
|
|
if nratio > ratio {
|
|
intermediate.crop(0, (iheight - nheight) / 2, nwidth, nheight)
|
|
} else {
|
|
intermediate.crop((iwidth - nwidth) / 2, 0, nwidth, nheight)
|
|
}
|
|
}
|
|
|
|
/// Performs a Gaussian blur on this image.
|
|
/// `sigma` is a measure of how much to blur by.
|
|
pub fn blur(&self, sigma: f32) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::blur(p, sigma))
|
|
}
|
|
|
|
/// Performs an unsharpen mask on this image.
|
|
/// `sigma` is the amount to blur the image by.
|
|
/// `threshold` is a control of how much to sharpen.
|
|
///
|
|
/// See <https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking>
|
|
pub fn unsharpen(&self, sigma: f32, threshold: i32) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::unsharpen(p, sigma, threshold))
|
|
}
|
|
|
|
/// Filters this image with the specified 3x3 kernel.
|
|
pub fn filter3x3(&self, kernel: &[f32]) -> DynamicImage {
|
|
if kernel.len() != 9 {
|
|
panic!("filter must be 3 x 3")
|
|
}
|
|
|
|
dynamic_map!(*self, ref p => imageops::filter3x3(p, kernel))
|
|
}
|
|
|
|
/// Adjust the contrast of this image.
|
|
/// `contrast` is the amount to adjust the contrast by.
|
|
/// Negative values decrease the contrast and positive values increase the contrast.
|
|
pub fn adjust_contrast(&self, c: f32) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::contrast(p, c))
|
|
}
|
|
|
|
/// Brighten the pixels of this image.
|
|
/// `value` is the amount to brighten each pixel by.
|
|
/// Negative values decrease the brightness and positive values increase it.
|
|
pub fn brighten(&self, value: i32) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::brighten(p, value))
|
|
}
|
|
|
|
/// Hue rotate the supplied image.
|
|
/// `value` is the degrees to rotate each pixel by.
|
|
/// 0 and 360 do nothing, the rest rotates by the given degree value.
|
|
/// just like the css webkit filter hue-rotate(180)
|
|
pub fn huerotate(&self, value: i32) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::huerotate(p, value))
|
|
}
|
|
|
|
/// Flip this image vertically
|
|
pub fn flipv(&self) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::flip_vertical(p))
|
|
}
|
|
|
|
/// Flip this image horizontally
|
|
pub fn fliph(&self) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::flip_horizontal(p))
|
|
}
|
|
|
|
/// Rotate this image 90 degrees clockwise.
|
|
pub fn rotate90(&self) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::rotate90(p))
|
|
}
|
|
|
|
/// Rotate this image 180 degrees clockwise.
|
|
pub fn rotate180(&self) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::rotate180(p))
|
|
}
|
|
|
|
/// Rotate this image 270 degrees clockwise.
|
|
pub fn rotate270(&self) -> DynamicImage {
|
|
dynamic_map!(*self, ref p => imageops::rotate270(p))
|
|
}
|
|
|
|
/// Encode this image and write it to ```w```.
|
|
///
|
|
/// Assumes the writer is buffered. In most cases,
|
|
/// you should wrap your writer in a `BufWriter` for best performance.
|
|
pub fn write_to<W: Write + Seek, F: Into<ImageOutputFormat>>(
|
|
&self,
|
|
w: &mut W,
|
|
format: F,
|
|
) -> ImageResult<()> {
|
|
#[allow(unused_variables)]
|
|
// When no features are supported
|
|
let w = w;
|
|
#[allow(unused_variables, unused_mut)]
|
|
let mut bytes = self.inner_bytes();
|
|
#[allow(unused_variables)]
|
|
let (width, height) = self.dimensions();
|
|
#[allow(unused_variables, unused_mut)]
|
|
let mut color = self.color();
|
|
let format = format.into();
|
|
|
|
// TODO do not repeat this match statement across the crate
|
|
|
|
#[allow(deprecated)]
|
|
match format {
|
|
#[cfg(feature = "png")]
|
|
image::ImageOutputFormat::Png => {
|
|
let p = png::PngEncoder::new(w);
|
|
p.write_image(bytes, width, height, color)?;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "pnm")]
|
|
image::ImageOutputFormat::Pnm(subtype) => {
|
|
let p = pnm::PnmEncoder::new(w).with_subtype(subtype);
|
|
p.write_image(bytes, width, height, color)?;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "gif")]
|
|
image::ImageOutputFormat::Gif => {
|
|
let mut g = gif::GifEncoder::new(w);
|
|
g.encode_frame(crate::animation::Frame::new(self.to_rgba8()))?;
|
|
Ok(())
|
|
}
|
|
|
|
format => write_buffer_with_format(w, bytes, width, height, color, format),
|
|
}
|
|
}
|
|
|
|
/// Saves the buffer to a file at the path specified.
|
|
///
|
|
/// The image format is derived from the file extension.
|
|
pub fn save<Q>(&self, path: Q) -> ImageResult<()>
|
|
where
|
|
Q: AsRef<Path>,
|
|
{
|
|
dynamic_map!(*self, |ref p| p.save(path))
|
|
}
|
|
|
|
/// Saves the buffer to a file at the specified path in
|
|
/// the specified format.
|
|
///
|
|
/// See [`save_buffer_with_format`](fn.save_buffer_with_format.html) for
|
|
/// supported types.
|
|
pub fn save_with_format<Q>(&self, path: Q, format: ImageFormat) -> ImageResult<()>
|
|
where
|
|
Q: AsRef<Path>,
|
|
{
|
|
dynamic_map!(*self, |ref p| p.save_with_format(path, format))
|
|
}
|
|
}
|
|
|
|
impl From<GrayImage> for DynamicImage {
|
|
fn from(image: GrayImage) -> Self {
|
|
DynamicImage::ImageLuma8(image)
|
|
}
|
|
}
|
|
|
|
impl From<GrayAlphaImage> for DynamicImage {
|
|
fn from(image: GrayAlphaImage) -> Self {
|
|
DynamicImage::ImageLumaA8(image)
|
|
}
|
|
}
|
|
|
|
impl From<RgbImage> for DynamicImage {
|
|
fn from(image: RgbImage) -> Self {
|
|
DynamicImage::ImageRgb8(image)
|
|
}
|
|
}
|
|
|
|
impl From<RgbaImage> for DynamicImage {
|
|
fn from(image: RgbaImage) -> Self {
|
|
DynamicImage::ImageRgba8(image)
|
|
}
|
|
}
|
|
|
|
impl From<Gray16Image> for DynamicImage {
|
|
fn from(image: Gray16Image) -> Self {
|
|
DynamicImage::ImageLuma16(image)
|
|
}
|
|
}
|
|
|
|
impl From<GrayAlpha16Image> for DynamicImage {
|
|
fn from(image: GrayAlpha16Image) -> Self {
|
|
DynamicImage::ImageLumaA16(image)
|
|
}
|
|
}
|
|
|
|
impl From<Rgb16Image> for DynamicImage {
|
|
fn from(image: Rgb16Image) -> Self {
|
|
DynamicImage::ImageRgb16(image)
|
|
}
|
|
}
|
|
|
|
impl From<Rgba16Image> for DynamicImage {
|
|
fn from(image: Rgba16Image) -> Self {
|
|
DynamicImage::ImageRgba16(image)
|
|
}
|
|
}
|
|
|
|
impl From<Rgb32FImage> for DynamicImage {
|
|
fn from(image: Rgb32FImage) -> Self {
|
|
DynamicImage::ImageRgb32F(image)
|
|
}
|
|
}
|
|
|
|
impl From<Rgba32FImage> for DynamicImage {
|
|
fn from(image: Rgba32FImage) -> Self {
|
|
DynamicImage::ImageRgba32F(image)
|
|
}
|
|
}
|
|
|
|
impl From<ImageBuffer<Luma<f32>, Vec<f32>>> for DynamicImage {
|
|
fn from(image: ImageBuffer<Luma<f32>, Vec<f32>>) -> Self {
|
|
DynamicImage::ImageRgb32F(image.convert())
|
|
}
|
|
}
|
|
|
|
impl From<ImageBuffer<LumaA<f32>, Vec<f32>>> for DynamicImage {
|
|
fn from(image: ImageBuffer<LumaA<f32>, Vec<f32>>) -> Self {
|
|
DynamicImage::ImageRgba32F(image.convert())
|
|
}
|
|
}
|
|
|
|
#[allow(deprecated)]
|
|
impl GenericImageView for DynamicImage {
|
|
type Pixel = color::Rgba<u8>; // TODO use f32 as default for best precision and unbounded color?
|
|
|
|
fn dimensions(&self) -> (u32, u32) {
|
|
dynamic_map!(*self, |ref p| p.dimensions())
|
|
}
|
|
|
|
fn bounds(&self) -> (u32, u32, u32, u32) {
|
|
dynamic_map!(*self, |ref p| p.bounds())
|
|
}
|
|
|
|
fn get_pixel(&self, x: u32, y: u32) -> color::Rgba<u8> {
|
|
dynamic_map!(*self, |ref p| p.get_pixel(x, y).to_rgba().into_color())
|
|
}
|
|
}
|
|
|
|
#[allow(deprecated)]
|
|
impl GenericImage for DynamicImage {
|
|
fn put_pixel(&mut self, x: u32, y: u32, pixel: color::Rgba<u8>) {
|
|
match *self {
|
|
DynamicImage::ImageLuma8(ref mut p) => p.put_pixel(x, y, pixel.to_luma()),
|
|
DynamicImage::ImageLumaA8(ref mut p) => p.put_pixel(x, y, pixel.to_luma_alpha()),
|
|
DynamicImage::ImageRgb8(ref mut p) => p.put_pixel(x, y, pixel.to_rgb()),
|
|
DynamicImage::ImageRgba8(ref mut p) => p.put_pixel(x, y, pixel),
|
|
DynamicImage::ImageLuma16(ref mut p) => p.put_pixel(x, y, pixel.to_luma().into_color()),
|
|
DynamicImage::ImageLumaA16(ref mut p) => {
|
|
p.put_pixel(x, y, pixel.to_luma_alpha().into_color())
|
|
}
|
|
DynamicImage::ImageRgb16(ref mut p) => p.put_pixel(x, y, pixel.to_rgb().into_color()),
|
|
DynamicImage::ImageRgba16(ref mut p) => p.put_pixel(x, y, pixel.into_color()),
|
|
DynamicImage::ImageRgb32F(ref mut p) => p.put_pixel(x, y, pixel.to_rgb().into_color()),
|
|
DynamicImage::ImageRgba32F(ref mut p) => p.put_pixel(x, y, pixel.into_color()),
|
|
}
|
|
}
|
|
|
|
fn blend_pixel(&mut self, x: u32, y: u32, pixel: color::Rgba<u8>) {
|
|
match *self {
|
|
DynamicImage::ImageLuma8(ref mut p) => p.blend_pixel(x, y, pixel.to_luma()),
|
|
DynamicImage::ImageLumaA8(ref mut p) => p.blend_pixel(x, y, pixel.to_luma_alpha()),
|
|
DynamicImage::ImageRgb8(ref mut p) => p.blend_pixel(x, y, pixel.to_rgb()),
|
|
DynamicImage::ImageRgba8(ref mut p) => p.blend_pixel(x, y, pixel),
|
|
DynamicImage::ImageLuma16(ref mut p) => {
|
|
p.blend_pixel(x, y, pixel.to_luma().into_color())
|
|
}
|
|
DynamicImage::ImageLumaA16(ref mut p) => {
|
|
p.blend_pixel(x, y, pixel.to_luma_alpha().into_color())
|
|
}
|
|
DynamicImage::ImageRgb16(ref mut p) => p.blend_pixel(x, y, pixel.to_rgb().into_color()),
|
|
DynamicImage::ImageRgba16(ref mut p) => p.blend_pixel(x, y, pixel.into_color()),
|
|
DynamicImage::ImageRgb32F(ref mut p) => {
|
|
p.blend_pixel(x, y, pixel.to_rgb().into_color())
|
|
}
|
|
DynamicImage::ImageRgba32F(ref mut p) => p.blend_pixel(x, y, pixel.into_color()),
|
|
}
|
|
}
|
|
|
|
/// Do not use is function: It is unimplemented!
|
|
fn get_pixel_mut(&mut self, _: u32, _: u32) -> &mut color::Rgba<u8> {
|
|
unimplemented!()
|
|
}
|
|
}
|
|
|
|
impl Default for DynamicImage {
|
|
fn default() -> Self {
|
|
Self::ImageRgba8(Default::default())
|
|
}
|
|
}
|
|
|
|
/// Decodes an image and stores it into a dynamic image
|
|
fn decoder_to_image<'a, I: ImageDecoder<'a>>(decoder: I) -> ImageResult<DynamicImage> {
|
|
let (w, h) = decoder.dimensions();
|
|
let color_type = decoder.color_type();
|
|
|
|
let image = match color_type {
|
|
color::ColorType::Rgb8 => {
|
|
let buf = image::decoder_to_vec(decoder)?;
|
|
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb8)
|
|
}
|
|
|
|
color::ColorType::Rgba8 => {
|
|
let buf = image::decoder_to_vec(decoder)?;
|
|
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba8)
|
|
}
|
|
|
|
color::ColorType::L8 => {
|
|
let buf = image::decoder_to_vec(decoder)?;
|
|
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLuma8)
|
|
}
|
|
|
|
color::ColorType::La8 => {
|
|
let buf = image::decoder_to_vec(decoder)?;
|
|
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLumaA8)
|
|
}
|
|
|
|
color::ColorType::Rgb16 => {
|
|
let buf = image::decoder_to_vec(decoder)?;
|
|
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb16)
|
|
}
|
|
|
|
color::ColorType::Rgba16 => {
|
|
let buf = image::decoder_to_vec(decoder)?;
|
|
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba16)
|
|
}
|
|
|
|
color::ColorType::Rgb32F => {
|
|
let buf = image::decoder_to_vec(decoder)?;
|
|
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb32F)
|
|
}
|
|
|
|
color::ColorType::Rgba32F => {
|
|
let buf = image::decoder_to_vec(decoder)?;
|
|
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba32F)
|
|
}
|
|
|
|
color::ColorType::L16 => {
|
|
let buf = image::decoder_to_vec(decoder)?;
|
|
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLuma16)
|
|
}
|
|
|
|
color::ColorType::La16 => {
|
|
let buf = image::decoder_to_vec(decoder)?;
|
|
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLumaA16)
|
|
}
|
|
|
|
// An internal #[non_exhaustive]
|
|
#[allow(unreachable_patterns)]
|
|
_ => {
|
|
return Err(ImageError::Unsupported(
|
|
UnsupportedError::from_format_and_kind(
|
|
ImageFormatHint::Unknown,
|
|
UnsupportedErrorKind::Color(color_type.into()),
|
|
),
|
|
))
|
|
}
|
|
};
|
|
|
|
match image {
|
|
Some(image) => Ok(image),
|
|
None => Err(ImageError::Parameter(ParameterError::from_kind(
|
|
ParameterErrorKind::DimensionMismatch,
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Open the image located at the path specified.
|
|
/// The image's format is determined from the path's file extension.
|
|
///
|
|
/// Try [`io::Reader`] for more advanced uses, including guessing the format based on the file's
|
|
/// content before its path.
|
|
///
|
|
/// [`io::Reader`]: io/struct.Reader.html
|
|
pub fn open<P>(path: P) -> ImageResult<DynamicImage>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
// thin wrapper function to strip generics before calling open_impl
|
|
free_functions::open_impl(path.as_ref())
|
|
}
|
|
|
|
/// Read a tuple containing the (width, height) of the image located at the specified path.
|
|
/// This is faster than fully loading the image and then getting its dimensions.
|
|
///
|
|
/// Try [`io::Reader`] for more advanced uses, including guessing the format based on the file's
|
|
/// content before its path or manually supplying the format.
|
|
///
|
|
/// [`io::Reader`]: io/struct.Reader.html
|
|
pub fn image_dimensions<P>(path: P) -> ImageResult<(u32, u32)>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
// thin wrapper function to strip generics before calling open_impl
|
|
free_functions::image_dimensions_impl(path.as_ref())
|
|
}
|
|
|
|
/// Saves the supplied buffer to a file at the path specified.
|
|
///
|
|
/// The image format is derived from the file extension. The buffer is assumed to have
|
|
/// the correct format according to the specified color type.
|
|
///
|
|
/// This will lead to corrupted files if the buffer contains malformed data. Currently only
|
|
/// jpeg, png, ico, pnm, bmp, exr and tiff files are supported.
|
|
pub fn save_buffer<P>(
|
|
path: P,
|
|
buf: &[u8],
|
|
width: u32,
|
|
height: u32,
|
|
color: color::ColorType,
|
|
) -> ImageResult<()>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
// thin wrapper function to strip generics before calling save_buffer_impl
|
|
free_functions::save_buffer_impl(path.as_ref(), buf, width, height, color)
|
|
}
|
|
|
|
/// Saves the supplied buffer to a file at the path specified
|
|
/// in the specified format.
|
|
///
|
|
/// The buffer is assumed to have the correct format according
|
|
/// to the specified color type.
|
|
/// This will lead to corrupted files if the buffer contains
|
|
/// malformed data. Currently only jpeg, png, ico, bmp, exr and
|
|
/// tiff files are supported.
|
|
pub fn save_buffer_with_format<P>(
|
|
path: P,
|
|
buf: &[u8],
|
|
width: u32,
|
|
height: u32,
|
|
color: color::ColorType,
|
|
format: ImageFormat,
|
|
) -> ImageResult<()>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
// thin wrapper function to strip generics
|
|
free_functions::save_buffer_with_format_impl(path.as_ref(), buf, width, height, color, format)
|
|
}
|
|
|
|
/// Writes the supplied buffer to a writer in the specified format.
|
|
///
|
|
/// The buffer is assumed to have the correct format according
|
|
/// to the specified color type.
|
|
/// This will lead to corrupted writers if the buffer contains
|
|
/// malformed data.
|
|
///
|
|
/// See [`ImageOutputFormat`](../enum.ImageOutputFormat.html) for
|
|
/// supported types.
|
|
///
|
|
/// Assumes the writer is buffered. In most cases,
|
|
/// you should wrap your writer in a `BufWriter` for best performance.
|
|
pub fn write_buffer_with_format<W, F>(
|
|
buffered_writer: &mut W,
|
|
buf: &[u8],
|
|
width: u32,
|
|
height: u32,
|
|
color: color::ColorType,
|
|
format: F,
|
|
) -> ImageResult<()>
|
|
where
|
|
W: Write + Seek,
|
|
F: Into<ImageOutputFormat>,
|
|
{
|
|
// thin wrapper function to strip generics
|
|
free_functions::write_buffer_impl(buffered_writer, buf, width, height, color, format.into())
|
|
}
|
|
|
|
/// Create a new image from a byte slice
|
|
///
|
|
/// Makes an educated guess about the image format.
|
|
/// TGA is not supported by this function.
|
|
///
|
|
/// Try [`io::Reader`] for more advanced uses.
|
|
///
|
|
/// [`io::Reader`]: io/struct.Reader.html
|
|
pub fn load_from_memory(buffer: &[u8]) -> ImageResult<DynamicImage> {
|
|
let format = free_functions::guess_format(buffer)?;
|
|
load_from_memory_with_format(buffer, format)
|
|
}
|
|
|
|
/// Create a new image from a byte slice
|
|
///
|
|
/// This is just a simple wrapper that constructs an `std::io::Cursor` around the buffer and then
|
|
/// calls `load` with that reader.
|
|
///
|
|
/// Try [`io::Reader`] for more advanced uses.
|
|
///
|
|
/// [`load`]: fn.load.html
|
|
/// [`io::Reader`]: io/struct.Reader.html
|
|
#[inline(always)]
|
|
pub fn load_from_memory_with_format(buf: &[u8], format: ImageFormat) -> ImageResult<DynamicImage> {
|
|
let b = io::Cursor::new(buf);
|
|
free_functions::load(b, format)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod bench {
|
|
#[cfg(feature = "benchmarks")]
|
|
use test;
|
|
|
|
#[bench]
|
|
#[cfg(feature = "benchmarks")]
|
|
fn bench_conversion(b: &mut test::Bencher) {
|
|
let a = super::DynamicImage::ImageRgb8(crate::ImageBuffer::new(1000, 1000));
|
|
b.iter(|| a.to_luma8());
|
|
b.bytes = 1000 * 1000 * 3
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
#[test]
|
|
fn test_empty_file() {
|
|
assert!(super::load_from_memory(b"").is_err());
|
|
}
|
|
|
|
#[cfg(feature = "jpeg")]
|
|
#[test]
|
|
fn image_dimensions() {
|
|
let im_path = "./tests/images/jpg/progressive/cat.jpg";
|
|
let dims = super::image_dimensions(im_path).unwrap();
|
|
assert_eq!(dims, (320, 240));
|
|
}
|
|
|
|
#[cfg(feature = "png")]
|
|
#[test]
|
|
fn open_16bpc_png() {
|
|
let im_path = "./tests/images/png/16bpc/basn6a16.png";
|
|
let image = super::open(im_path).unwrap();
|
|
assert_eq!(image.color(), super::color::ColorType::Rgba16);
|
|
}
|
|
|
|
fn test_grayscale(mut img: super::DynamicImage, alpha_discarded: bool) {
|
|
use crate::image::{GenericImage, GenericImageView};
|
|
img.put_pixel(0, 0, crate::color::Rgba([255, 0, 0, 100]));
|
|
let expected_alpha = if alpha_discarded { 255 } else { 100 };
|
|
assert_eq!(
|
|
img.grayscale().get_pixel(0, 0),
|
|
crate::color::Rgba([54, 54, 54, expected_alpha])
|
|
);
|
|
}
|
|
|
|
fn test_grayscale_alpha_discarded(img: super::DynamicImage) {
|
|
test_grayscale(img, true);
|
|
}
|
|
|
|
fn test_grayscale_alpha_preserved(img: super::DynamicImage) {
|
|
test_grayscale(img, false);
|
|
}
|
|
|
|
#[test]
|
|
fn test_grayscale_luma8() {
|
|
test_grayscale_alpha_discarded(super::DynamicImage::new_luma8(1, 1));
|
|
}
|
|
|
|
#[test]
|
|
fn test_grayscale_luma_a8() {
|
|
test_grayscale_alpha_preserved(super::DynamicImage::new_luma_a8(1, 1));
|
|
}
|
|
|
|
#[test]
|
|
fn test_grayscale_rgb8() {
|
|
test_grayscale_alpha_discarded(super::DynamicImage::new_rgb8(1, 1));
|
|
}
|
|
|
|
#[test]
|
|
fn test_grayscale_rgba8() {
|
|
test_grayscale_alpha_preserved(super::DynamicImage::new_rgba8(1, 1));
|
|
}
|
|
|
|
#[test]
|
|
fn test_grayscale_luma16() {
|
|
test_grayscale_alpha_discarded(super::DynamicImage::new_luma16(1, 1));
|
|
}
|
|
|
|
#[test]
|
|
fn test_grayscale_luma_a16() {
|
|
test_grayscale_alpha_preserved(super::DynamicImage::new_luma_a16(1, 1));
|
|
}
|
|
|
|
#[test]
|
|
fn test_grayscale_rgb16() {
|
|
test_grayscale_alpha_discarded(super::DynamicImage::new_rgb16(1, 1));
|
|
}
|
|
|
|
#[test]
|
|
fn test_grayscale_rgba16() {
|
|
test_grayscale_alpha_preserved(super::DynamicImage::new_rgba16(1, 1));
|
|
}
|
|
|
|
#[test]
|
|
fn test_grayscale_rgb32f() {
|
|
test_grayscale_alpha_discarded(super::DynamicImage::new_rgb32f(1, 1));
|
|
}
|
|
|
|
#[test]
|
|
fn test_grayscale_rgba32f() {
|
|
test_grayscale_alpha_preserved(super::DynamicImage::new_rgba32f(1, 1));
|
|
}
|
|
|
|
#[test]
|
|
fn test_dynamic_image_default_implementation() {
|
|
// Test that structs wrapping a DynamicImage are able to auto-derive the Default trait
|
|
// ensures that DynamicImage implements Default (if it didn't, this would cause a compile error).
|
|
#[derive(Default)]
|
|
struct Foo {
|
|
_image: super::DynamicImage,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_to_vecu8() {
|
|
let _ = super::DynamicImage::new_luma8(1, 1).into_bytes();
|
|
let _ = super::DynamicImage::new_luma16(1, 1).into_bytes();
|
|
}
|
|
|
|
#[test]
|
|
fn issue_1705_can_turn_16bit_image_into_bytes() {
|
|
let pixels = vec![65535u16; 64 * 64];
|
|
let img = super::ImageBuffer::from_vec(64, 64, pixels).unwrap();
|
|
|
|
let img = super::DynamicImage::ImageLuma16(img.into());
|
|
assert!(img.as_luma16().is_some());
|
|
|
|
let bytes: Vec<u8> = img.into_bytes();
|
|
assert_eq!(bytes, vec![0xFF; 64 * 64 * 2]);
|
|
}
|
|
}
|