use std::cmp;
use std::default::Default;
use std::error;
use std::fmt;
use std::io;
use std::mem;
use crate::gif::common::{AnyExtension, Block, Extension, Frame};
pub const PLTE_CHANNELS: usize = 3;
#[derive(Debug)]
pub enum DecodingError {
Format,
Io(io::Error),
}
impl DecodingError {
#[inline]
pub(crate) fn format(_: &'static str) -> Self {
DecodingError::Format
}
}
impl fmt::Display for DecodingError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
DecodingError::Format => "malformed GIF file".fmt(fmt),
DecodingError::Io(ref err) => err.fmt(fmt),
}
}
}
impl error::Error for DecodingError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
DecodingError::Format => None,
DecodingError::Io(ref err) => Some(err),
}
}
}
impl From<io::Error> for DecodingError {
fn from(err: io::Error) -> Self {
DecodingError::Io(err)
}
}
#[derive(Debug)]
pub enum Decoded<'a> {
Nothing,
GlobalPalette,
BackgroundColor,
Trailer,
BlockStart(Block),
SubBlockFinished,
BlockFinished,
Frame(&'a Frame),
SomeData,
DataEnd,
}
#[derive(Debug)]
enum State {
Magic(u8, [u8; 6]),
U16Byte1(U16Value, u8),
U16(U16Value),
Byte(ByteValue),
GlobalPalette(usize),
BlockStart(Option<Block>),
BlockEnd(u8),
ExtensionBlock(AnyExtension),
SkipBlock(usize),
LocalPalette(usize),
LzwInit(u8),
DecodeSubBlock(usize),
FrameDecoded,
Trailer,
}
use self::State::*;
#[derive(Debug)]
enum U16Value {
ScreenWidth,
ScreenHeight,
Delay,
ImageLeft,
ImageTop,
ImageWidth,
ImageHeight,
}
#[derive(Debug)]
enum ByteValue {
GlobalFlags,
Background { table_size: usize },
AspectRatio { table_size: usize },
ControlFlags,
ImageFlags,
TransparentIdx,
CodeSize,
}
pub struct StreamingDecoder {
state: Option<State>,
version: Version,
width: u16,
height: u16,
current: Option<Frame>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Version {
V87a,
V89a,
}
impl StreamingDecoder {
pub(crate) fn with_options() -> Self {
StreamingDecoder {
state: Some(Magic(0, [0; 6])),
version: Version::V87a,
width: 0,
height: 0,
current: None,
}
}
pub fn update<'a>(&'a mut self, mut buf: &[u8]) -> Result<(usize, Decoded<'a>), DecodingError> {
let len = buf.len();
while !buf.is_empty() && self.state.is_some() {
match self.next_state(buf) {
Ok((bytes, Decoded::Nothing)) => {
buf = &buf[bytes..];
}
Ok((bytes, Decoded::Trailer)) => {
buf = &buf[bytes..];
break;
}
Ok((bytes, result)) => {
buf = &buf[bytes..];
return Ok(
(len-buf.len(),
unsafe {
mem::transmute::<Decoded, Decoded>(result)
}
))
}
Err(err) => return Err(err)
}
}
Ok((len-buf.len(), Decoded::Nothing))
}
#[inline(always)]
pub fn current_frame_mut(&mut self) -> &mut Frame {
self.current.as_mut().unwrap()
}
fn next_state<'a>(&'a mut self, buf: &[u8]) -> Result<(usize, Decoded<'a>), DecodingError> {
macro_rules! goto (
($n:expr, $state:expr) => ({
self.state = Some($state);
Ok(($n, Decoded::Nothing))
});
($state:expr) => ({
self.state = Some($state);
Ok((1, Decoded::Nothing))
});
($n:expr, $state:expr, emit $res:expr) => ({
self.state = Some($state);
Ok(($n, $res))
});
($state:expr, emit $res:expr) => ({
self.state = Some($state);
Ok((1, $res))
})
);
let b = buf[0];
let state = self.state.take().unwrap();
match state {
Magic(i, mut version) => if i < 6 {
version[i as usize] = b;
goto!(Magic(i+1, version))
} else if &version[..3] == b"GIF" {
self.version = match &version[3..] {
b"87a" => Version::V87a,
b"89a" => Version::V89a,
_ => return Err(DecodingError::format("unsupported GIF version"))
};
goto!(U16Byte1(U16Value::ScreenWidth, b))
} else {
Err(DecodingError::format("malformed GIF header"))
},
U16(next) => goto!(U16Byte1(next, b)),
U16Byte1(next, value) => {
use self::U16Value::*;
let value = (u16::from(b) << 8) | u16::from(value);
match (next, value) {
(ScreenWidth, width) => {
self.width = width;
goto!(U16(U16Value::ScreenHeight))
},
(ScreenHeight, height) => {
self.height = height;
goto!(Byte(ByteValue::GlobalFlags))
},
(Delay, _delay) => {
goto!(Byte(ByteValue::TransparentIdx))
},
(ImageLeft, left) => {
self.current_frame_mut().left = left;
goto!(U16(U16Value::ImageTop))
},
(ImageTop, top) => {
self.current_frame_mut().top = top;
goto!(U16(U16Value::ImageWidth))
},
(ImageWidth, width) => {
self.current_frame_mut().width = width;
goto!(U16(U16Value::ImageHeight))
},
(ImageHeight, height) => {
self.current_frame_mut().height = height;
goto!(Byte(ByteValue::ImageFlags))
}
}
}
Byte(value) => {
use self::ByteValue::*;
match value {
GlobalFlags => {
let global_table = b & 0x80 != 0;
let entries = if global_table {
PLTE_CHANNELS*(1 << ((b & 0b111) + 1) as usize)
} else {
0usize
};
goto!(Byte(Background { table_size: entries }))
},
Background { table_size } => {
goto!(
Byte(AspectRatio { table_size }),
emit Decoded::BackgroundColor
)
},
AspectRatio { table_size } => {
goto!(GlobalPalette(table_size))
},
ControlFlags => {
goto!(U16(U16Value::Delay))
}
TransparentIdx => {
goto!(SkipBlock(0))
}
ImageFlags => {
let local_table = (b & 0b1000_0000) != 0;
let interlaced = (b & 0b0100_0000) != 0;
let table_size = b & 0b0000_0111;
self.current_frame_mut().was_interlaced = interlaced;
self.current_frame_mut().is_interlaced = interlaced;
if local_table {
let entries = PLTE_CHANNELS * (1 << (table_size + 1));
goto!(LocalPalette(entries))
} else {
goto!(Byte(CodeSize))
}
},
CodeSize => goto!(LzwInit(b))
}
}
GlobalPalette(left) => {
let n = cmp::min(left, buf.len());
if left > 0 {
goto!(n, GlobalPalette(left - n))
} else {
goto!(BlockStart(Block::from_u8(b)), emit Decoded::GlobalPalette)
}
}
BlockStart(type_) => {
match type_ {
Some(Block::Image) => {
self.add_frame();
goto!(U16Byte1(U16Value::ImageLeft, b), emit Decoded::BlockStart(Block::Image))
}
Some(Block::Extension) => {
goto!(ExtensionBlock(AnyExtension(b)), emit Decoded::BlockStart(Block::Extension))
}
Some(Block::Trailer) => {
goto!(0, State::Trailer, emit Decoded::BlockStart(Block::Trailer))
}
None => {
goto!(SkipBlock(b as usize))
}
}
}
BlockEnd(terminator) => {
if terminator == 0 {
if b == Block::Trailer as u8 {
goto!(0, BlockStart(Some(Block::Trailer)))
} else {
goto!(BlockStart(Block::from_u8(b)))
}
} else {
Err(DecodingError::format(
"expected block terminator not found"
))
}
}
ExtensionBlock(id) => {
use Extension::{Application, Comment, Control, Text};
if let Some(ext) = Extension::from_u8(id.0) {
match ext {
Control => {
goto!(self.read_control_extension(b)?)
}
Text | Comment | Application => {
goto!(SkipBlock(b as usize))
}
}
} else {
Err(DecodingError::format(
"unknown extention block encountered"
))
}
}
SkipBlock(left) => {
let n = cmp::min(left, buf.len());
if left > 0 {
goto!(n, SkipBlock(left - n))
} else if b == 0 {
goto!(BlockEnd(b), emit Decoded::BlockFinished)
} else {
goto!(SkipBlock(b as usize), emit Decoded::SubBlockFinished)
}
}
LocalPalette(left) => {
let n = cmp::min(left, buf.len());
if left > 0 {
goto!(n, LocalPalette(left - n))
} else {
goto!(LzwInit(b))
}
}
LzwInit(_code_size) => {
goto!(DecodeSubBlock(b as usize), emit Decoded::Frame(self.current_frame_mut()))
}
DecodeSubBlock(left) => {
if left > 0 {
let consumed = left.min(buf.len());
goto!(consumed, DecodeSubBlock(left - consumed), emit Decoded::SomeData)
} else if b != 0 { goto!(DecodeSubBlock(b as usize))
} else {
self.current = None;
goto!(0, FrameDecoded, emit Decoded::DataEnd)
}
}
FrameDecoded => {
goto!(BlockEnd(b))
}
Trailer => {
self.state = None;
Ok((1, Decoded::Trailer))
}
}
}
fn read_control_extension(&mut self, b: u8) -> Result<State, DecodingError> {
self.add_frame();
if b != 4 {
return Err(DecodingError::format(
"control extension has wrong length"
))
}
Ok(Byte(ByteValue::ControlFlags))
}
fn add_frame(&mut self) {
if self.current.is_none() {
self.current = Some(Frame::default());
}
}
}