use std::io;
use std::mem;
use std::iter;
use std::ascii::OwnedAsciiExt;
use ppm;
use gif;
use webp;
use jpeg;
use png;
use tiff;
use color;
use color::Pixel;
use imageops;
use image;
use image:: {
ImageBuf,
GenericImage,
ImageDecoder,
ImageResult,
ImageFormat,
};
#[deriving(Clone)]
pub enum DynamicImage {
ImageLuma8(ImageBuf<color::Luma<u8>>),
ImageLumaA8(ImageBuf<color::LumaA<u8>>),
ImageRgb8(ImageBuf<color::Rgb<u8>>),
ImageRgba8(ImageBuf<color::Rgba<u8>>),
}
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 to_rgb(&self) -> ImageBuf<color::Rgb<u8>> {
dynamic_map!(*self, ref p -> {
let (w, h) = p.dimensions();
let mut pixels = Vec::with_capacity(w as uint * h as uint);
for (_, _, pix) in p.pixels() {
pixels.push(pix.to_rgb());
}
ImageBuf::from_pixels(pixels, w, h)
})
}
pub fn to_rgba(&self) -> ImageBuf<color::Rgba<u8>> {
dynamic_map!(*self, ref p -> {
let (w, h) = p.dimensions();
let mut pixels = Vec::with_capacity(w as uint * h as uint);
for (_, _, pix) in p.pixels() {
pixels.push(pix.to_rgba());
}
ImageBuf::from_pixels(pixels, w, h)
})
}
pub fn to_luma(&self) -> ImageBuf<color::Luma<u8>> {
dynamic_map!(*self, ref p -> {
let (w, h) = p.dimensions();
let mut pixels = Vec::with_capacity(w as uint * h as uint);
for (_, _, pix) in p.pixels() {
pixels.push(pix.to_luma());
}
ImageBuf::from_pixels(pixels, w, h)
})
}
pub fn to_luma_alpha(&self) -> ImageBuf<color::LumaA<u8>> {
dynamic_map!(*self, ref p -> {
let (w, h) = p.dimensions();
let mut pixels = Vec::with_capacity(w as uint * h as uint);
for (_, _, pix) in p.pixels() {
pixels.push(pix.to_luma_alpha());
}
ImageBuf::from_pixels(pixels, w, h)
})
}
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<&ImageBuf<color::Rgb<u8>>> {
match *self {
DynamicImage::ImageRgb8(ref p) => Some(p),
_ => None
}
}
pub fn as_mut_rgb8(&mut self) -> Option<&mut ImageBuf<color::Rgb<u8>>> {
match *self {
DynamicImage::ImageRgb8(ref mut p) => Some(p),
_ => None
}
}
pub fn as_rgba8(&self) -> Option<& ImageBuf<color::Rgba<u8>>> {
match *self {
DynamicImage::ImageRgba8(ref p) => Some(p),
_ => None
}
}
pub fn as_mut_rgba8(&mut self) -> Option<&mut ImageBuf<color::Rgba<u8>>> {
match *self {
DynamicImage::ImageRgba8(ref mut p) => Some(p),
_ => None
}
}
pub fn as_luma8(& self) -> Option<& ImageBuf<color::Luma<u8>>> {
match *self {
DynamicImage::ImageLuma8(ref p) => Some(p),
_ => None
}
}
pub fn as_mut_luma8(&mut self) -> Option<&mut ImageBuf<color::Luma<u8>>> {
match *self {
DynamicImage::ImageLuma8(ref mut p) => Some(p),
_ => None
}
}
pub fn as_luma_alpha8(&self) -> Option<& ImageBuf<color::LumaA<u8>>> {
match *self {
DynamicImage::ImageLumaA8(ref p) => Some(p),
_ => None
}
}
pub fn as_mut_luma_alpha8(&mut self) -> Option<&mut ImageBuf<color::LumaA<u8>>> {
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::Grey(8),
DynamicImage::ImageLumaA8(_) => color::ColorType::GreyA(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 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: Writer>(&self, w: W, format: ImageFormat) -> io::IoResult<ImageResult<()>> {
let bytes = self.raw_pixels();
let (width, height) = self.dimensions();
let color = self.color();
let r = match format {
image::ImageFormat::PNG => {
let mut p = png::PNGEncoder::new(w);
try!(p.encode(bytes.as_slice(), width, height, color))
Ok(())
}
image::ImageFormat::PPM => {
let mut p = ppm::PPMEncoder::new(w);
try!(p.encode(bytes.as_slice(), width, height, color))
Ok(())
}
image::ImageFormat::JPEG => {
let mut j = jpeg::JPEGEncoder::new(w);
try!(j.encode(bytes.as_slice(), width, height, color))
Ok(())
}
_ => Err(image::ImageError::UnsupportedError(
format!("An encoder for {} is not available.", format))
),
};
Ok(r)
}
}
impl GenericImage<color::Rgba<u8>> for DynamicImage {
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 transmute_vec<T: Send + color::SafeToTransmute>(mut vec: Vec<u8>) -> Vec<T> {
let new_elem_size = mem::size_of::<T>();
let len = vec.len();
assert!(len % new_elem_size == 0);
let new_len = len/new_elem_size;
let cap = vec.capacity();
let new_cap = match cap % new_elem_size {
0 => { cap/new_elem_size },
n => {
vec.reserve_exact(cap + new_elem_size - n);
let cap = vec.capacity();
assert!(cap % new_elem_size == 0); cap/new_elem_size
}
};
let p = vec.as_mut_ptr();
unsafe {
mem::forget(vec);
Vec::from_raw_parts(mem::transmute(p), new_len, new_cap)
}
}
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 {
color::ColorType::RGB(8) => {
DynamicImage::ImageRgb8(ImageBuf::from_pixels(transmute_vec(buf), w, h))
}
color::ColorType::RGBA(8) => {
DynamicImage::ImageRgba8(ImageBuf::from_pixels(transmute_vec(buf), w, h))
}
color::ColorType::Grey(8) => {
DynamicImage::ImageLuma8(ImageBuf::from_pixels(transmute_vec(buf), w, h))
}
color::ColorType::GreyA(8) => {
DynamicImage::ImageLumaA8(ImageBuf::from_pixels(transmute_vec(buf), w, h))
}
color::ColorType::Grey(bit_depth) if bit_depth == 1 || bit_depth == 2 || bit_depth == 4 => {
let mask = (1u8 << bit_depth as uint) - 1;
let scaling_factor = (255)/((1 << bit_depth as uint) - 1);
let skip = (w % 8)/bit_depth as u32;
let row_len = w + skip;
let p = buf.as_slice()
.iter()
.flat_map(|&v|
iter::range_step_inclusive(8i8-(bit_depth as i8), 0, -(bit_depth as i8))
.zip(iter::iterate(
v, |v| v
)
))
.enumerate().filter(|&(i, _)| i % (row_len as uint) < (w as uint) ).map(|(_, p)| p)
.map(|(shift, pixel)|
(pixel & mask << shift as uint) >> shift as uint
)
.map(|pixel| color::Luma::<u8>(pixel * scaling_factor))
.collect();
DynamicImage::ImageLuma8(ImageBuf::from_pixels(p, w, h))
},
_ => return Err(image::ImageError::UnsupportedColor(color))
};
Ok(image)
}
fn image_to_bytes(image: &DynamicImage) -> Vec<u8> {
let mut r = Vec::new();
match *image {
DynamicImage::ImageLuma8(ref a) => {
for & i in a.pixelbuf().iter() {
r.push(i.channel());
}
}
DynamicImage::ImageLumaA8(ref a) => {
for & i in a.pixelbuf().iter() {
let (l, a) = i.channels();
r.push(l);
r.push(a);
}
}
DynamicImage::ImageRgb8(ref a) => {
for & i in a.pixelbuf().iter() {
let (red, g, b) = i.channels();
r.push(red);
r.push(g);
r.push(b);
}
}
DynamicImage::ImageRgba8(ref a) => {
for & i in a.pixelbuf().iter() {
let (red, g, b, alpha) = i.channels();
r.push(red);
r.push(g);
r.push(b);
r.push(alpha);
}
}
}
r
}
pub fn open(path: &Path) -> ImageResult<DynamicImage> {
let fin = match io::File::open(path) {
Ok(f) => f,
Err(err) => return Err(image::ImageError::IoError(err))
};
let ext = path.extension_str()
.map_or("".to_string(), | s | s.to_string().into_ascii_lower());
let format = match ext.as_slice() {
"jpg" |
"jpeg" => image::ImageFormat::JPEG,
"png" => image::ImageFormat::PNG,
"gif" => image::ImageFormat::GIF,
"webp" => image::ImageFormat::WEBP,
"tif" |
"tiff" => image::ImageFormat::TIFF,
format => return Err(image::ImageError::UnsupportedError(format!(
"Image format image/{} is not supported.",
format
)))
};
load(fin, format)
}
pub fn save_buffer(path: &Path, buf: &[u8], width: u32, height: u32, color: color::ColorType) -> io::IoResult<()> {
let fout = try!(io::File::create(path));
let ext = path.extension_str()
.map_or("".to_string(), | s | s.to_string().into_ascii_lower());
match ext.as_slice() {
"jpg" |
"jpeg" => jpeg::JPEGEncoder::new(fout).encode(buf, width, height, color),
"png" => png::PNGEncoder::new(fout).encode(buf, width, height, color),
format => Err(io::IoError {
kind: io::InvalidInput,
desc: "Unsupported image format.",
detail: Some(format!(
"Image format image/{} is not supported.",
format
))
})
}
}
pub fn load<R: Reader+Seek>(r: R, format: ImageFormat) -> ImageResult<DynamicImage> {
match format {
image::ImageFormat::PNG => decoder_to_image(png::PNGDecoder::new(io::BufferedReader::new(r))),
image::ImageFormat::GIF => decoder_to_image(gif::GIFDecoder::new(io::BufferedReader::new(r))),
image::ImageFormat::JPEG => decoder_to_image(jpeg::JPEGDecoder::new(io::BufferedReader::new(r))),
image::ImageFormat::WEBP => decoder_to_image(webp::WebpDecoder::new(io::BufferedReader::new(r))),
image::ImageFormat::TIFF => decoder_to_image(try!(tiff::TIFFDecoder::new(r))),
_ => Err(image::ImageError::UnsupportedError(format!("A decoder for {} is not available.", format))),
}
}
pub fn load_from_memory(buf: &[u8], format: ImageFormat) -> ImageResult<DynamicImage> {
let b = io::BufReader::new(buf);
load(b, format)
}