#![forbid(unsafe_code)]
use crate::c_types::{GifFileType, GifPixelType};
use crate::err::GifError;
use crate::private_c_types::{FIRST_CODE, FLUSH_OUTPUT, LZ_MAX_CODE};
use core::ffi::c_int;
pub(crate) const HT_SIZE: usize = 8192; const HT_KEY_MASK: u32 = 0x1FFF;
fn ht_get_key(l: u32) -> u32 {
l >> 12
}
fn ht_get_code(l: u32) -> i32 {
(l & 0x0FFF) as i32
}
fn ht_put_key(l: u32) -> u32 {
l << 12
}
fn ht_put_code(l: i32) -> u32 {
(l as u32) & 0x0FFF
}
fn key_item(item: u32) -> u32 {
((item >> 12) ^ item) & HT_KEY_MASK
}
#[derive(Debug)]
pub struct GifHashTableType {
pub h_table: [u32; HT_SIZE],
}
impl Default for GifHashTableType {
fn default() -> Self {
let mut ht = Self { h_table: [0; HT_SIZE] };
ht.clear();
ht
}
}
impl GifHashTableType {
pub fn clear(&mut self) {
self.h_table.fill(0xFFFFFFFF);
}
pub fn insert(&mut self, key: u32, code: i32) {
let mut h_key = key_item(key) as usize;
while ht_get_key(self.h_table[h_key]) != 0xFFFFF {
h_key = (h_key + 1) & (HT_KEY_MASK as usize);
}
self.h_table[h_key] = ht_put_key(key) | ht_put_code(code);
}
pub fn exists(&self, key: u32) -> Option<i32> {
let mut h_key = key_item(key) as usize;
loop {
let ht_key = ht_get_key(self.h_table[h_key]);
if ht_key == 0xFFFFF {
return None;
}
if key == ht_key {
return Some(ht_get_code(self.h_table[h_key]));
}
h_key = (h_key + 1) & (HT_KEY_MASK as usize);
}
}
}
static CODE_MASK: [GifPixelType; 9] = [0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff];
pub(crate) fn egif_setup_compress(gif: &mut GifFileType) -> Result<(), GifError> {
let bits_per_pixel = if let Some(ref cm) = gif.Image.ColorMap {
cm.BitsPerPixel
} else if let Some(ref cm) = gif.SColorMap {
cm.BitsPerPixel
} else {
return Err(GifError::ENoColorMap);
};
let bits_per_pixel = if bits_per_pixel < 2 { 2 } else { bits_per_pixel };
let buf = [bits_per_pixel as u8];
gif.write_exact(&buf)?;
gif.Private.lzw.buf[0] = 0; gif.Private.lzw.bits_per_pixel = bits_per_pixel;
gif.Private.lzw.clear_code = 1 << bits_per_pixel;
gif.Private.lzw.eof_code = gif.Private.lzw.clear_code + 1;
gif.Private.lzw.running_code = gif.Private.lzw.eof_code + 1;
gif.Private.lzw.running_bits = bits_per_pixel + 1; gif.Private.lzw.max_code1 = 1 << gif.Private.lzw.running_bits; gif.Private.lzw.crnt_code = FIRST_CODE; gif.Private.lzw.crnt_shift_state = 0;
gif.Private.lzw.crnt_shift_dword = 0;
let ht = gif.Private.lzw.hash_table.get_or_insert_default();
ht.clear();
if egif_compress_output(gif, gif.Private.lzw.clear_code).is_err() {
return Err(GifError::EDiskIsFull);
}
Ok(())
}
pub(crate) fn egif_compress_line(
gif: &mut GifFileType,
line: &[GifPixelType],
) -> Result<(), GifError> {
let line_len = line.len();
let mut i = 0usize;
let mut crnt_code: c_int = if gif.Private.lzw.crnt_code == FIRST_CODE {
let c = line[i] as c_int;
i += 1;
c
} else {
gif.Private.lzw.crnt_code };
while i < line_len {
let pixel = line[i]; i += 1;
let new_key: u32 = ((crnt_code as u32) << 8) + pixel as u32;
if let Some(new_code) = gif.Private.lzw.hash_table.as_ref().unwrap().exists(new_key) {
crnt_code = new_code;
} else {
egif_compress_output(gif, crnt_code)?;
crnt_code = pixel as c_int;
if gif.Private.lzw.running_code >= LZ_MAX_CODE as c_int {
let clear_code = gif.Private.lzw.clear_code;
egif_compress_output(gif, clear_code)?;
gif.Private.lzw.running_code = gif.Private.lzw.eof_code + 1;
gif.Private.lzw.running_bits = gif.Private.lzw.bits_per_pixel + 1;
gif.Private.lzw.max_code1 = 1 << gif.Private.lzw.running_bits;
gif.Private.lzw.hash_table.as_mut().unwrap().clear();
} else {
let rc = gif.Private.lzw.running_code;
gif.Private.lzw.hash_table.as_mut().unwrap().insert(new_key, rc);
gif.Private.lzw.running_code += 1;
}
}
}
gif.Private.lzw.crnt_code = crnt_code;
if gif.Private.pixel_count == 0 {
egif_compress_output(gif, crnt_code)?;
let eof_code = gif.Private.lzw.eof_code;
egif_compress_output(gif, eof_code)?;
egif_compress_output(gif, FLUSH_OUTPUT)?;
}
Ok(())
}
fn egif_compress_output(gif: &mut GifFileType, code: c_int) -> Result<(), GifError> {
let mut retval = Ok(());
if code == FLUSH_OUTPUT {
while gif.Private.lzw.crnt_shift_state > 0 {
let byte = (gif.Private.lzw.crnt_shift_dword & 0xff) as c_int;
if egif_buffered_output(gif, byte).is_err() {
retval = Err(GifError::EDiskIsFull);
}
gif.Private.lzw.crnt_shift_dword >>= 8;
gif.Private.lzw.crnt_shift_state -= 8;
}
gif.Private.lzw.crnt_shift_state = 0; if egif_buffered_output(gif, FLUSH_OUTPUT).is_err() {
retval = Err(GifError::EDiskIsFull);
}
} else {
gif.Private.lzw.crnt_shift_dword |=
(code as u64) << gif.Private.lzw.crnt_shift_state as u64;
gif.Private.lzw.crnt_shift_state += gif.Private.lzw.running_bits;
while gif.Private.lzw.crnt_shift_state >= 8 {
let byte = (gif.Private.lzw.crnt_shift_dword & 0xff) as c_int;
if egif_buffered_output(gif, byte).is_err() {
retval = Err(GifError::EDiskIsFull);
}
gif.Private.lzw.crnt_shift_dword >>= 8;
gif.Private.lzw.crnt_shift_state -= 8;
}
}
if gif.Private.lzw.running_code >= gif.Private.lzw.max_code1 && code <= 4095 {
gif.Private.lzw.running_bits += 1;
gif.Private.lzw.max_code1 = 1 << gif.Private.lzw.running_bits;
}
retval
}
fn egif_buffered_output(gif: &mut GifFileType, c: c_int) -> Result<(), GifError> {
let gif_ptr = gif as *mut GifFileType;
if c == FLUSH_OUTPUT {
let count = gif.Private.lzw.buf[0] as usize;
if count != 0 {
let written = gif.Private.io.write(gif_ptr, &gif.Private.lzw.buf[..count + 1]);
if written != count + 1 {
return Err(GifError::EWriteFailed);
}
}
gif.Private.lzw.buf[0] = 0;
let written = gif.Private.io.write(gif_ptr, &gif.Private.lzw.buf[..1]);
if written != 1 {
return Err(GifError::EWriteFailed);
}
} else {
if gif.Private.lzw.buf[0] == 255 {
let written = gif.Private.io.write(gif_ptr, &gif.Private.lzw.buf[..256]);
if written != 256 {
return Err(GifError::EWriteFailed);
}
gif.Private.lzw.buf[0] = 0;
}
gif.Private.lzw.buf[0] += 1;
let idx = gif.Private.lzw.buf[0] as usize;
gif.Private.lzw.buf[idx] = c as u8;
}
Ok(())
}
pub(crate) fn get_code_mask(bits_per_pixel: c_int) -> GifPixelType {
CODE_MASK[bits_per_pixel as usize]
}