extern crate color_quant;
use std::borrow::Cow;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum DisposalMethod {
Any = 0,
Keep = 1,
Background = 2,
Previous = 3,
}
impl DisposalMethod {
pub fn from_u8(n: u8) -> Option<DisposalMethod> {
match n {
0 => Some(DisposalMethod::Any),
1 => Some(DisposalMethod::Keep),
2 => Some(DisposalMethod::Background),
3 => Some(DisposalMethod::Previous),
_ => None
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum Block {
Image = 0x2C,
Extension = 0x21,
Trailer = 0x3B
}
impl Block {
pub fn from_u8(n: u8) -> Option<Block> {
match n {
0x2C => Some(Block::Image),
0x21 => Some(Block::Extension),
0x3B => Some(Block::Trailer),
_ => None
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum Extension {
Text = 0x01,
Control = 0xF9,
Comment = 0xFE,
Application = 0xFF
}
impl Extension {
pub fn from_u8(n: u8) -> Option<Extension> {
match n {
0x01 => Some(Extension::Text),
0xF9 => Some(Extension::Control),
0xFE => Some(Extension::Comment),
0xFF => Some(Extension::Application),
_ => None
}
}
}
#[derive(Debug, Clone)]
pub struct Frame<'a> {
pub delay: u16,
pub dispose: DisposalMethod,
pub transparent: Option<u8>,
pub needs_user_input: bool,
pub top: u16,
pub left: u16,
pub width: u16,
pub height: u16,
pub interlaced: bool,
pub palette: Option<Vec<u8>>,
pub buffer: Cow<'a, [u8]>
}
impl<'a> Default for Frame<'a> {
fn default() -> Frame<'a> {
Frame {
delay: 0,
dispose: DisposalMethod::Keep,
transparent: None,
needs_user_input: false,
top: 0,
left: 0,
width: 0,
height: 0,
interlaced: false,
palette: None,
buffer: Cow::Borrowed(&[])
}
}
}
impl Frame<'static> {
pub fn from_rgba(width: u16, height: u16, pixels: &mut [u8]) -> Frame<'static> {
Frame::from_rgba_speed(width, height, pixels, 1)
}
pub fn from_rgba_speed(width: u16, height: u16, pixels: &mut [u8], speed: i32) -> Frame<'static> {
assert_eq!(width as usize * height as usize * 4, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame");
assert!(speed >= 1 && speed <= 30, "speed needs to be in the range [1, 30]");
let mut frame = Frame::default();
let mut transparent = None;
for pix in pixels.chunks_mut(4) {
if pix[3] != 0 {
pix[3] = 0xFF;
} else {
transparent = Some([pix[0], pix[1], pix[2], pix[3]])
}
}
frame.width = width;
frame.height = height;
let nq = color_quant::NeuQuant::new(speed, 256, pixels);
frame.buffer = Cow::Owned(pixels.chunks(4).map(|pix| nq.index_of(pix) as u8).collect());
frame.palette = Some(nq.color_map_rgb());
frame.transparent = if let Some(t) = transparent {
Some(nq.index_of(&t) as u8)
} else {
None
};
frame
}
pub fn from_palette_pixels(width: u16, height: u16, pixels: &[u8], palette: &[u8], transparent: Option<u8>) -> Frame<'static> {
assert_eq!(width as usize * height as usize, pixels.len(), "Too many or too little pixels for the given width and height to create a GIF Frame");
assert!(palette.len() <= 256*3, "Too many palette values to create a GIF Frame");
let mut frame = Frame::default();
frame.width = width;
frame.height = height;
frame.buffer = Cow::Owned(pixels.to_vec());
frame.palette = Some(palette.to_vec());
frame.transparent = transparent;
frame
}
pub fn from_indexed_pixels(width: u16, height: u16, pixels: &[u8], transparent: Option<u8>) -> Frame<'static> {
assert_eq!(width as usize * height as usize, pixels.len(), "Too many or too little pixels for the given width and height to create a GIF Frame");
let mut frame = Frame::default();
frame.width = width;
frame.height = height;
frame.buffer = Cow::Owned(pixels.to_vec());
frame.palette = None;
frame.transparent = transparent;
frame
}
pub fn from_rgb(width: u16, height: u16, pixels: &[u8]) -> Frame<'static> {
Frame::from_rgb_speed(width, height, pixels, 1)
}
pub fn from_rgb_speed(width: u16, height: u16, pixels: &[u8], speed: i32) -> Frame<'static> {
assert_eq!(width as usize * height as usize * 3, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame");
let mut vec: Vec<u8> = Vec::with_capacity(pixels.len() + width as usize * height as usize);
for v in pixels.chunks(3) {
vec.extend([v[0], v[1], v[2], 0xFF].iter().cloned())
}
Frame::from_rgba_speed(width, height, &mut vec, speed)
}
}