use std::io;
use std::io::{Write, Seek, BufRead, BufReader, BufWriter};
use std::path::Path;
use std::fs::File;
use std::iter;
use std::ascii::AsciiExt;
use num_iter;
#[cfg(feature = "pnm")]
use pnm;
#[cfg(feature = "ppm")]
use ppm;
#[cfg(feature = "gif_codec")]
use gif;
#[cfg(feature = "webp")]
use webp;
#[cfg(feature = "jpeg")]
use jpeg;
#[cfg(feature = "png_codec")]
use png;
#[cfg(feature = "tiff")]
use tiff;
#[cfg(feature = "tga")]
use tga;
#[cfg(feature = "bmp")]
use bmp;
#[cfg(feature = "ico")]
use ico;
#[cfg(feature = "hdr")]
use hdr;
use color;
use buffer::{ImageBuffer, ConvertBuffer, Pixel, GrayImage, GrayAlphaImage, RgbImage, RgbaImage};
use imageops;
use image;
use image:: {
GenericImage,
ImageDecoder,
ImageResult,
ImageFormat,
};
use image::DecodingResult::{U8};
#[derive(Clone)]
pub enum DynamicImage {
ImageLuma8(GrayImage),
ImageLumaA8(GrayAlphaImage),
ImageRgb8(RgbImage),
ImageRgba8(RgbaImage),
}
macro_rules! dynamic_map(
($dynimage: expr, ref $image: ident => $action: expr) => (
match $dynimage {
DynamicImage::ImageLuma8(ref $image) => DynamicImage::ImageLuma8($action),
DynamicImage::ImageLumaA8(ref $image) => DynamicImage::ImageLumaA8($action),
DynamicImage::ImageRgb8(ref $image) => DynamicImage::ImageRgb8($action),
DynamicImage::ImageRgba8(ref $image) => DynamicImage::ImageRgba8($action),
}
);
($dynimage: expr, ref mut $image: ident => $action: expr) => (
match $dynimage {
DynamicImage::ImageLuma8(ref mut $image) => DynamicImage::ImageLuma8($action),
DynamicImage::ImageLumaA8(ref mut $image) => DynamicImage::ImageLumaA8($action),
DynamicImage::ImageRgb8(ref mut $image) => DynamicImage::ImageRgb8($action),
DynamicImage::ImageRgba8(ref mut $image) => DynamicImage::ImageRgba8($action),
}
);
($dynimage: expr, ref $image: ident -> $action: expr) => (
match $dynimage {
DynamicImage::ImageLuma8(ref $image) => $action,
DynamicImage::ImageLumaA8(ref $image) => $action,
DynamicImage::ImageRgb8(ref $image) => $action,
DynamicImage::ImageRgba8(ref $image) => $action,
}
);
($dynimage: expr, ref mut $image: ident -> $action: expr) => (
match $dynimage {
DynamicImage::ImageLuma8(ref mut $image) => $action,
DynamicImage::ImageLumaA8(ref mut $image) => $action,
DynamicImage::ImageRgb8(ref mut $image) => $action,
DynamicImage::ImageRgba8(ref mut $image) => $action,
}
);
);
impl DynamicImage {
pub fn new_luma8(w: u32, h: u32) -> DynamicImage {
DynamicImage::ImageLuma8(ImageBuffer::new(w, h))
}
pub fn new_luma_a8(w: u32, h: u32) -> DynamicImage {
DynamicImage::ImageLumaA8(ImageBuffer::new(w, h))
}
pub fn new_rgb8(w: u32, h: u32) -> DynamicImage {
DynamicImage::ImageRgb8(ImageBuffer::new(w, h))
}
pub fn new_rgba8(w: u32, h: u32) -> DynamicImage {
DynamicImage::ImageRgba8(ImageBuffer::new(w, h))
}
pub fn to_rgb(&self) -> RgbImage {
dynamic_map!(*self, ref p -> {
p.convert()
})
}
pub fn to_rgba(&self) -> RgbaImage {
dynamic_map!(*self, ref p -> {
p.convert()
})
}
pub fn to_luma(&self) -> GrayImage {
dynamic_map!(*self, ref p -> {
p.convert()
})
}
pub fn to_luma_alpha(&self) -> GrayAlphaImage {
dynamic_map!(*self, ref p -> {
p.convert()
})
}
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())
}
pub fn as_rgb8(&self) -> Option<&RgbImage> {
match *self {
DynamicImage::ImageRgb8(ref p) => Some(p),
_ => None
}
}
pub fn as_mut_rgb8(&mut self) -> Option<&mut RgbImage> {
match *self {
DynamicImage::ImageRgb8(ref mut p) => Some(p),
_ => None
}
}
pub fn as_rgba8(&self) -> Option<& RgbaImage> {
match *self {
DynamicImage::ImageRgba8(ref p) => Some(p),
_ => None
}
}
pub fn as_mut_rgba8(&mut self) -> Option<&mut RgbaImage> {
match *self {
DynamicImage::ImageRgba8(ref mut p) => Some(p),
_ => None
}
}
pub fn as_luma8(& self) -> Option<& GrayImage> {
match *self {
DynamicImage::ImageLuma8(ref p) => Some(p),
_ => None
}
}
pub fn as_mut_luma8(&mut self) -> Option<&mut GrayImage> {
match *self {
DynamicImage::ImageLuma8(ref mut p) => Some(p),
_ => None
}
}
pub fn as_luma_alpha8(&self) -> Option<& GrayAlphaImage> {
match *self {
DynamicImage::ImageLumaA8(ref p) => Some(p),
_ => None
}
}
pub fn as_mut_luma_alpha8(&mut self) -> Option<&mut GrayAlphaImage> {
match *self {
DynamicImage::ImageLumaA8(ref mut p) => Some(p),
_ => None
}
}
pub fn raw_pixels(&self) -> Vec<u8> {
image_to_bytes(self)
}
pub fn color(&self) -> color::ColorType {
match *self {
DynamicImage::ImageLuma8(_) => color::ColorType::Gray(8),
DynamicImage::ImageLumaA8(_) => color::ColorType::GrayA(8),
DynamicImage::ImageRgb8(_) => color::ColorType::RGB(8),
DynamicImage::ImageRgba8(_) => color::ColorType::RGBA(8),
}
}
pub fn grayscale(&self) -> DynamicImage {
match *self {
DynamicImage::ImageLuma8(ref p) => DynamicImage::ImageLuma8(p.clone()),
DynamicImage::ImageLumaA8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
DynamicImage::ImageRgb8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
DynamicImage::ImageRgba8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
}
}
pub fn invert(&mut self) {
dynamic_map!(*self, ref mut p -> imageops::invert(p))
}
pub fn resize(&self,
nwidth: u32,
nheight: u32,
filter: imageops::FilterType) -> DynamicImage {
let (width, height) = self.dimensions();
let ratio = width as f32 / height as f32;
let nratio = nwidth as f32 / nheight as f32;
let scale = if nratio > ratio {
nheight as f32 / height as f32
} else {
nwidth as f32 / width as f32
};
let width2 = (width as f32 * scale) as u32;
let height2 = (height as f32 * scale) as u32;
self.resize_exact(width2, height2, filter)
}
pub fn resize_exact(&self,
nwidth: u32,
nheight: u32,
filter: imageops::FilterType) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::resize(p, nwidth, nheight, filter))
}
pub fn blur(&self, sigma: f32) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::blur(p, sigma))
}
pub fn unsharpen(&self, sigma: f32, threshold: i32) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::unsharpen(p, sigma, threshold))
}
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))
}
pub fn adjust_contrast(&self, c: f32) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::contrast(p, c))
}
pub fn brighten(&self, value: i32) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::brighten(p, value))
}
pub fn huerotate(&self, value: i32) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::huerotate(p, value))
}
pub fn flipv(&self) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::flip_vertical(p))
}
pub fn fliph(&self) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::flip_horizontal(p))
}
pub fn rotate90(&self) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::rotate90(p))
}
pub fn rotate180(&self) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::rotate180(p))
}
pub fn rotate270(&self) -> DynamicImage {
dynamic_map!(*self, ref p => imageops::rotate270(p))
}
pub fn save<W: Write>(&self, w: &mut W, format: ImageFormat) -> ImageResult<()> {
let bytes = self.raw_pixels();
let (width, height) = self.dimensions();
let color = self.color();
match format {
#[cfg(feature = "png_codec")]
image::ImageFormat::PNG => {
let p = png::PNGEncoder::new(w);
try!(p.encode(&bytes, width, height, color));
Ok(())
}
#[cfg(feature = "ppm")]
image::ImageFormat::PPM => {
let mut p = ppm::PPMEncoder::new(w);
try!(p.encode(&bytes, width, height, color));
Ok(())
}
#[cfg(feature = "jpeg")]
image::ImageFormat::JPEG => {
let mut j = jpeg::JPEGEncoder::new(w);
try!(j.encode(&bytes, width, height, color));
Ok(())
}
#[cfg(feature = "gif_codec")]
image::ImageFormat::GIF => {
let g = gif::Encoder::new(w);
try!(g.encode(gif::Frame::from_rgba(
width as u16,
height as u16,
&mut *self.to_rgba().iter().cloned().collect::<Vec<u8>>()
)));
Ok(())
}
#[cfg(feature = "ico")]
image::ImageFormat::ICO => {
let i = ico::ICOEncoder::new(w);
try!(i.encode(&bytes, width, height, color));
Ok(())
}
#[cfg(feature = "bmp")]
image::ImageFormat::BMP => {
let mut b = bmp::BMPEncoder::new(w);
try!(b.encode(&bytes, width, height, color));
Ok(())
}
_ => Err(image::ImageError::UnsupportedError(
format!("An encoder for {:?} is not available.", format))
),
}
}
}
#[allow(deprecated)]
impl GenericImage for DynamicImage {
type Pixel = color::Rgba<u8>;
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())
}
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),
}
}
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),
}
}
fn get_pixel_mut(&mut self, _: u32, _: u32) -> &mut color::Rgba<u8> {
unimplemented!()
}
}
pub fn decoder_to_image<I: ImageDecoder>(codec: I) -> ImageResult<DynamicImage> {
let mut codec = codec;
let color = try!(codec.colortype());
let buf = try!(codec.read_image());
let (w, h) = try!(codec.dimensions());
let image = match (color, buf) {
(color::ColorType::RGB(8), U8(buf)) => {
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb8)
}
(color::ColorType::RGBA(8), U8(buf)) => {
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba8)
}
(color::ColorType::Gray(8), U8(buf)) => {
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLuma8)
}
(color::ColorType::GrayA(8), U8(buf)) => {
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLumaA8)
}
(color::ColorType::Gray(bit_depth), U8(ref buf)) if bit_depth == 1 || bit_depth == 2 || bit_depth == 4 => {
let mask = (1u8 << bit_depth as usize) - 1;
let scaling_factor = 255/((1 << bit_depth as usize) - 1);
let skip = (w % 8)/u32::from(bit_depth);
let row_len = w + skip;
let p = buf
.iter()
.flat_map(|&v|
num_iter::range_step_inclusive(8i8-(bit_depth as i8), 0, -(bit_depth as i8))
.zip(iter::repeat(v))
)
.enumerate().filter(|&(i, _)| i % (row_len as usize) < (w as usize) ).map(|(_, p)| p)
.map(|(shift, pixel)|
(pixel & mask << shift as usize) >> shift as usize
)
.map(|pixel| pixel * scaling_factor)
.collect();
ImageBuffer::from_raw(w, h, p).map(DynamicImage::ImageLuma8)
},
_ => return Err(image::ImageError::UnsupportedColor(color))
};
match image {
Some(image) => Ok(image),
None => Err(image::ImageError::DimensionError)
}
}
#[allow(deprecated)]
fn image_to_bytes(image: &DynamicImage) -> Vec<u8> {
match *image {
DynamicImage::ImageLuma8(ref a) => {
a.iter().cloned().collect()
}
DynamicImage::ImageLumaA8(ref a) => {
a.iter().cloned().collect()
}
DynamicImage::ImageRgb8(ref a) => {
a.iter().cloned().collect()
}
DynamicImage::ImageRgba8(ref a) => {
a.iter().cloned().collect()
}
}
}
pub fn open<P>(path: P) -> ImageResult<DynamicImage> where P: AsRef<Path> {
open_impl(path.as_ref())
}
fn open_impl(path: &Path) -> ImageResult<DynamicImage> {
let fin = match File::open(path) {
Ok(f) => f,
Err(err) => return Err(image::ImageError::IoError(err))
};
let fin = BufReader::new(fin);
let ext = path.extension().and_then(|s| s.to_str())
.map_or("".to_string(), |s| s.to_ascii_lowercase());
let format = match &ext[..] {
"jpg" |
"jpeg" => image::ImageFormat::JPEG,
"png" => image::ImageFormat::PNG,
"gif" => image::ImageFormat::GIF,
"webp" => image::ImageFormat::WEBP,
"tif" |
"tiff" => image::ImageFormat::TIFF,
"tga" => image::ImageFormat::TGA,
"bmp" => image::ImageFormat::BMP,
"ico" => image::ImageFormat::ICO,
"hdr" => image::ImageFormat::HDR,
"pbm" |
"pam" |
"pgm" => image::ImageFormat::PNM,
"ppm" => image::ImageFormat::PPM,
format => return Err(image::ImageError::UnsupportedError(format!(
"Image format image/{:?} is not supported.",
format
)))
};
load(fin, format)
}
pub fn save_buffer<P>(path: P, buf: &[u8], width: u32, height: u32, color: color::ColorType)
-> io::Result<()> where P: AsRef<Path> {
save_buffer_impl(path.as_ref(), buf, width, height, color)
}
fn save_buffer_impl(path: &Path, buf: &[u8], width: u32, height: u32, color: color::ColorType)
-> io::Result<()> {
let fout = &mut BufWriter::new(try!(File::create(path)));
let ext = path.extension().and_then(|s| s.to_str())
.map_or("".to_string(), |s| s.to_ascii_lowercase());
match &*ext {
#[cfg(feature = "ico")]
"ico" => ico::ICOEncoder::new(fout).encode(buf, width, height, color),
#[cfg(feature = "jpeg")]
"jpg" |
"jpeg" => jpeg::JPEGEncoder::new(fout).encode(buf, width, height, color),
#[cfg(feature = "png_codec")]
"png" => png::PNGEncoder::new(fout).encode(buf, width, height, color),
#[cfg(feature = "ppm")]
"ppm" => ppm::PPMEncoder::new(fout).encode(buf, width, height, color),
#[cfg(feature = "bmp")]
"bmp" => bmp::BMPEncoder::new(fout).encode(buf, width, height, color),
format => Err(io::Error::new(
io::ErrorKind::InvalidInput,
&format!("Unsupported image format image/{:?}", format)[..],
))
}
}
pub fn load<R: BufRead+Seek>(r: R, format: ImageFormat) -> ImageResult<DynamicImage> {
match format {
#[cfg(feature = "png_codec")]
image::ImageFormat::PNG => decoder_to_image(png::PNGDecoder::new(r)),
#[cfg(feature = "gif_codec")]
image::ImageFormat::GIF => decoder_to_image(gif::Decoder::new(r)),
#[cfg(feature = "jpeg")]
image::ImageFormat::JPEG => decoder_to_image(jpeg::JPEGDecoder::new(r)),
#[cfg(feature = "webp")]
image::ImageFormat::WEBP => decoder_to_image(webp::WebpDecoder::new(r)),
#[cfg(feature = "tiff")]
image::ImageFormat::TIFF => decoder_to_image(try!(tiff::TIFFDecoder::new(r))),
#[cfg(feature = "tga")]
image::ImageFormat::TGA => decoder_to_image(tga::TGADecoder::new(r)),
#[cfg(feature = "bmp")]
image::ImageFormat::BMP => decoder_to_image(bmp::BMPDecoder::new(r)),
#[cfg(feature = "ico")]
image::ImageFormat::ICO => decoder_to_image(try!(ico::ICODecoder::new(r))),
#[cfg(feature = "hdr")]
image::ImageFormat::HDR => decoder_to_image(try!(hdr::HDRAdapter::new(BufReader::new(r)))),
#[cfg(feature = "ppm")]
image::ImageFormat::PPM => decoder_to_image(try!(ppm::PPMDecoder::new(BufReader::new(r)))),
#[cfg(feature = "pnm")]
image::ImageFormat::PNM => decoder_to_image(try!(pnm::PNMDecoder::new(BufReader::new(r)))),
_ => Err(image::ImageError::UnsupportedError(format!("A decoder for {:?} is not available.", format))),
}
}
static MAGIC_BYTES: [(&'static [u8], ImageFormat); 17] = [
(b"\x89PNG\r\n\x1a\n", ImageFormat::PNG),
(&[0xff, 0xd8, 0xff], ImageFormat::JPEG),
(b"GIF89a", ImageFormat::GIF),
(b"GIF87a", ImageFormat::GIF),
(b"WEBP", ImageFormat::WEBP),
(b"MM.*", ImageFormat::TIFF),
(b"II*.", ImageFormat::TIFF),
(b"BM", ImageFormat::BMP),
(&[0, 0, 1, 0], ImageFormat::ICO),
(b"#?RADIANCE", ImageFormat::HDR),
(b"P1", ImageFormat::PNM),
(b"P2", ImageFormat::PNM),
(b"P3", ImageFormat::PNM),
(b"P4", ImageFormat::PNM),
(b"P5", ImageFormat::PNM),
(b"P6", ImageFormat::PPM),
(b"P7", ImageFormat::PNM),
];
pub fn load_from_memory(buffer: &[u8]) -> ImageResult<DynamicImage> {
load_from_memory_with_format(buffer, try!(guess_format(buffer)))
}
#[inline(always)]
pub fn load_from_memory_with_format(buf: &[u8], format: ImageFormat) -> ImageResult<DynamicImage> {
let b = io::Cursor::new(buf);
load(b, format)
}
pub fn guess_format(buffer: &[u8]) -> ImageResult<ImageFormat> {
for &(signature, format) in &MAGIC_BYTES {
if buffer.starts_with(signature) {
return Ok(format);
}
}
Err(image::ImageError::UnsupportedError(
"Unsupported image format".to_string())
)
}
#[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(::ImageBuffer::new(1000, 1000));
b.iter(|| {
a.to_luma()
});
b.bytes = 1000*1000*3
}
}
#[cfg(test)]
mod test {
#[test]
fn test_empty_file() {
assert!(super::load_from_memory(b"").is_err());
}
}