use std::io::Read;
use gif::{self, SetParameter};
use crate::{
buffer::{cast, Buffer},
color,
error::{self, Error},
pixel,
};
enum State<R: Read> {
Decoder(gif::Decoder<R>),
Reader(gif::Reader<R>),
}
pub struct Decoder<R: Read> {
state: Option<State<R>>,
}
impl<R: Read> Decoder<R> {
#[inline]
pub fn new(input: R) -> Self {
let mut decoder = gif::Decoder::new(input);
decoder.set(gif::ColorOutput::RGBA);
Decoder {
state: Some(State::Decoder(decoder)),
}
}
pub fn reader(&mut self) -> error::Result<&mut gif::Reader<R>> {
let inner = self.state.take();
match inner {
Some(State::Decoder(decoder)) => {
self.state = Some(State::Reader(decoder.read_info()?));
}
Some(State::Reader(reader)) => {
self.state = Some(State::Reader(reader));
}
None => {
unreachable!()
}
}
if let Some(&mut State::Reader(ref mut reader)) = self.state.as_mut() {
Ok(reader)
}
else {
unreachable!();
}
}
}
impl<P, C, R> super::Decoder<P, C> for Decoder<R>
where
P: pixel::Write<C>,
P: From<color::Rgb> + From<color::Rgba> + From<color::Luma> + From<color::Lumaa>,
C: pixel::Channel,
R: Read,
{
#[inline]
fn frame(&mut self) -> error::Result<Buffer<P, C, Vec<C>>> {
let frame = self
.reader()?
.read_next_frame()?
.ok_or(Error::Format("no frames".into()))?;
Ok(cast::Into::<P, C>::into(Buffer::<color::Rgba, u8, _>::from_raw(
frame.width as u32,
frame.height as u32,
frame.buffer.clone().into_owned(),
)
.map_err(|_| Error::Format("wrong dimensions".into()))?))
}
}