#![cfg_attr(feature = "cargo-clippy", allow(while_let_loop))]
extern crate gif;
extern crate num_rational;
use std::clone::Clone;
use std::io::{self, Cursor, Read, Write};
use std::marker::PhantomData;
use std::mem;
use self::gif::{ColorOutput, SetParameter};
pub use self::gif::{DisposalMethod, Frame};
use animation;
use buffer::{ImageBuffer, Pixel};
use color;
use color::Rgba;
use image::{AnimationDecoder, ImageDecoder, ImageError, ImageResult};
use num_rational::Ratio;
pub struct Decoder<R: Read> {
reader: gif::Reader<R>,
}
impl<R: Read> Decoder<R> {
pub fn new(r: R) -> ImageResult<Decoder<R>> {
let mut decoder = gif::Decoder::new(r);
decoder.set(ColorOutput::RGBA);
Ok(Decoder {
reader: decoder.read_info()?,
})
}
}
pub struct GifReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for GifReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
if self.0.position() == 0 && buf.is_empty() {
mem::swap(buf, self.0.get_mut());
Ok(buf.len())
} else {
self.0.read_to_end(buf)
}
}
}
impl<'a, R: 'a + Read> ImageDecoder<'a> for Decoder<R> {
type Reader = GifReader<R>;
fn dimensions(&self) -> (u64, u64) {
(self.reader.width() as u64, self.reader.height() as u64)
}
fn colortype(&self) -> color::ColorType {
color::ColorType::RGBA(8)
}
fn into_reader(self) -> ImageResult<Self::Reader> {
Ok(GifReader(Cursor::new(self.read_image()?), PhantomData))
}
fn read_image(mut self) -> ImageResult<Vec<u8>> {
if self.reader.next_frame_info()?.is_some() {
let mut buf = vec![0; self.reader.buffer_size()];
self.reader.read_into_buffer(&mut buf)?;
Ok(buf)
} else {
Err(ImageError::ImageEnd)
}
}
}
struct GifFrameIterator<R: Read> {
reader: gif::Reader<R>,
width: u32,
height: u32,
background_img: ImageBuffer<Rgba<u8>, Vec<u8>>,
non_disposed_frame: ImageBuffer<Rgba<u8>, Vec<u8>>,
left: u32,
top: u32,
delay: Ratio<u16>,
dispose: DisposalMethod,
}
impl<R: Read> GifFrameIterator<R> {
fn new(decoder: Decoder<R>) -> GifFrameIterator<R> {
let (width, height) = decoder.dimensions();
let (width, height) = (width as u32, height as u32);
let background_color_option = decoder.reader.bg_color();
let mut background_color = vec![0; 4];
let background_pixel = {
let global_palette = decoder.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 non_disposed_frame = background_img.clone();
GifFrameIterator {
reader: decoder.reader,
width,
height,
background_img,
non_disposed_frame,
left: 0,
top: 0,
delay: Ratio::new(0, 1),
dispose: DisposalMethod::Any
}
}
}
impl<R: Read> Iterator for GifFrameIterator<R> {
type Item = ImageResult<animation::Frame>;
fn next(&mut self) -> Option<ImageResult<animation::Frame>> {
match self.reader.next_frame_info() {
Ok(frame_info) => {
if let Some(frame) = frame_info {
self.left = u32::from(frame.left);
self.top = u32::from(frame.top);
self.delay = Ratio::new(frame.delay * 10, 1);
self.dispose = frame.dispose;
} else {
return None;
}
},
Err(err) => return Some(Err(err.into())),
}
let mut vec = vec![0; self.reader.buffer_size()];
if let Err(err) = self.reader.fill_buffer(&mut vec) {
return Some(Err(err.into()));
}
let mut image_buffer = match ImageBuffer::from_raw(self.width, self.height, vec) {
Some(buffer) => buffer,
None => return Some(Err(ImageError::UnsupportedError(
"Unknown error occured while reading gif frame".into()
))),
};
for (x, y, pixel) in image_buffer.enumerate_pixels_mut() {
let previous_img_buffer = &self.non_disposed_frame;
let 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(), self.left, self.top, self.delay
);
match self.dispose {
DisposalMethod::Any => {
}
DisposalMethod::Keep => {
self.non_disposed_frame = image_buffer;
}
DisposalMethod::Background => {
self.non_disposed_frame = self.background_img.clone();
}
DisposalMethod::Previous => {
}
};
Some(Ok(frame))
}
}
impl<'a, R: Read + 'a> AnimationDecoder<'a> for Decoder<R> {
fn into_frames(self) -> animation::Frames<'a> {
animation::Frames::new(Box::new(GifFrameIterator::new(self)))
}
}
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 = 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<F>(&mut self, frames: F) -> ImageResult<()>
where
F: IntoIterator<Item = animation::Frame>
{
for img_frame in frames {
self.encode_single_frame(img_frame)?;
}
Ok(())
}
pub fn try_encode_frames<F>(&mut self, frames: F) -> ImageResult<()>
where
F: IntoIterator<Item = ImageResult<animation::Frame>>
{
for img_frame in frames {
self.encode_single_frame(img_frame?)?;
}
Ok(())
}
fn encode_single_frame(&mut self, img_frame: animation::Frame) -> ImageResult<()> {
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;
self.encode(&frame)
}
}
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),
}
}
}