#![cfg_attr(feature = "cargo-clippy", allow(while_let_loop))]
extern crate gif;
extern crate num_rational;
use std::clone::Clone;
use std::io::{Read, Write};
use self::gif::{ColorOutput, SetParameter};
pub use self::gif::{DisposalMethod, Frame};
use animation;
use buffer::{ImageBuffer, Pixel};
use color;
use color::Rgba;
use image::{DecodingResult, ImageDecoder, ImageError, ImageResult};
use num_rational::Ratio;
enum Either<T, U> {
Left(T),
Right(U),
}
pub struct Decoder<R: Read> {
inner: Option<Either<gif::Decoder<R>, gif::Reader<R>>>,
}
impl<R: Read> Decoder<R> {
pub fn new(r: R) -> Decoder<R> {
let mut decoder = gif::Decoder::new(r);
decoder.set(ColorOutput::RGBA);
Decoder {
inner: Some(Either::Left(decoder)),
}
}
fn get_reader(&mut self) -> Result<&mut gif::Reader<R>, gif::DecodingError> {
let inner = self.inner.take().unwrap();
self.inner = Some(match inner {
Either::Left(decoder) => {
let reader = try!(decoder.read_info());
Either::Right(reader)
}
Either::Right(reader) => Either::Right(reader),
});
match self.inner {
Some(Either::Right(ref mut reader)) => Ok(reader),
_ => unreachable!(),
}
}
}
impl<R: Read> ImageDecoder for Decoder<R> {
fn dimensions(&mut self) -> ImageResult<(u32, u32)> {
let reader = try!(self.get_reader());
Ok((u32::from(reader.width()), u32::from(reader.height())))
}
fn colortype(&mut self) -> ImageResult<color::ColorType> {
Ok(color::ColorType::RGBA(8))
}
fn row_len(&mut self) -> ImageResult<usize> {
let reader = try!(self.get_reader());
Ok(reader.line_length())
}
fn read_scanline(&mut self, buf: &mut [u8]) -> ImageResult<u32> {
let reader = try!(self.get_reader());
let len = reader.line_length();
try!(reader.fill_buffer(&mut buf[..len]));
Ok(len as u32)
}
fn read_image(&mut self) -> ImageResult<DecodingResult> {
let reader = try!(self.get_reader());
if try!(reader.next_frame_info()).is_some() {
let mut buf = vec![0; reader.buffer_size()];
try!(reader.read_into_buffer(&mut buf));
Ok(DecodingResult::U8(buf))
} else {
Err(ImageError::ImageEnd)
}
}
fn is_animated(&mut self) -> ImageResult<bool> {
Ok(true)
}
fn into_frames(mut self) -> ImageResult<animation::Frames> {
let reader = try!(self.get_reader());
let width = u32::from(reader.width());
let height = u32::from(reader.height());
let mut frames = Vec::new();
let background_color_option = reader.bg_color();
let mut background_color = vec![0; 4];
let background_pixel = {
let global_palette = reader.global_palette();
match background_color_option {
Some(index) => {
match global_palette {
Some(slice) => {
background_color.clone_from_slice(&slice[index..(index + 4)]);
Rgba::from_slice(&background_color)
}
None => Rgba::from_slice(&[0, 0, 0, 0]),
}
}
None => Rgba::from_slice(&[0, 0, 0, 0]),
}
};
let background_img = ImageBuffer::from_pixel(width, height, *background_pixel);
let mut non_disposed_frame = background_img.clone();
let mut left: u32;
let mut top: u32;
let mut delay: Ratio<u16>;
let mut dispose: DisposalMethod;
loop {
if let Some(frame) = try!(reader.next_frame_info()) {
left = u32::from(frame.left);
top = u32::from(frame.top);
delay = Ratio::new(frame.delay * 10, 1);
dispose = frame.dispose;
} else {
break;
}
let mut vec = vec![0; reader.buffer_size()];
try!(reader.fill_buffer(&mut vec));
if let Some(mut image_buffer) = ImageBuffer::from_raw(width, height, vec) {
let previous_img_buffer = non_disposed_frame.clone();
for (x, y, pixel) in image_buffer.enumerate_pixels_mut() {
let mut adjusted_pixel: &mut Rgba<u8> = pixel;
let previous_pixel: &Rgba<u8> = previous_img_buffer.get_pixel(x, y);
let pixel_alpha = adjusted_pixel.channels()[3];
if pixel_alpha == 0 {
adjusted_pixel.blend(previous_pixel);
}
}
let frame = animation::Frame::from_parts(image_buffer.clone(), left, top, delay);
frames.push(frame);
match dispose {
DisposalMethod::Any => {
}
DisposalMethod::Keep => {
non_disposed_frame = image_buffer;
}
DisposalMethod::Background => {
non_disposed_frame = background_img.clone();
}
DisposalMethod::Previous => {
}
};
};
}
Ok(animation::Frames::new(frames))
}
}
pub struct Encoder<W: Write> {
w: Option<W>,
gif_encoder: Option<gif::Encoder<W>>,
}
impl<W: Write> Encoder<W> {
pub fn new(w: W) -> Encoder<W> {
Encoder {
w: Some(w),
gif_encoder: None,
}
}
pub fn encode(&mut self, frame: &Frame) -> ImageResult<()> {
let result;
if let Some(ref mut encoder) = self.gif_encoder {
result = encoder.write_frame(frame).map_err(|err| err.into());
} else {
let writer = self.w.take().unwrap();
let mut encoder = try!(gif::Encoder::new(writer, frame.width, frame.height, &[]));
result = encoder.write_frame(&frame).map_err(|err| err.into());
self.gif_encoder = Some(encoder);
}
result
}
pub fn encode_frames(&mut self, frames: animation::Frames) -> ImageResult<()> {
for img_frame in frames {
let frame_delay = img_frame.delay().to_integer();
let rbga_frame = img_frame.into_buffer();
let mut frame = Frame::from_rgba(rbga_frame.width() as u16, rbga_frame.height() as u16, &mut rbga_frame.into_raw());
frame.delay = frame_delay;
if let Err(e) = self.encode(&frame) {
return Err(e);
}
}
Ok(())
}
}
impl From<gif::DecodingError> for ImageError {
fn from(err: gif::DecodingError) -> ImageError {
use self::gif::DecodingError::*;
match err {
Format(desc) | Internal(desc) => ImageError::FormatError(desc.into()),
Io(io_err) => ImageError::IoError(io_err),
}
}
}