#![forbid(unsafe_code)]
use crate::c_types::{GifByteType, GifFileType, GifPixelType, GifPrefixType};
use crate::err::GifError;
use crate::private_c_types::{LZ_BITS, LZ_MAX_CODE, NO_SUCH_CODE};
use core::ffi::c_int;
pub(crate) fn dgif_setup_decompress(gif: &mut GifFileType) -> Result<(), GifError> {
let mut cs_buf = [0u8; 1];
gif.read_exact(&mut cs_buf)?;
let code_size = cs_buf[0];
let bits_per_pixel = code_size as c_int;
if bits_per_pixel > 8 {
return Err(GifError::DReadFailed);
}
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.stack_ptr = 0;
gif.Private.lzw.last_code = NO_SUCH_CODE;
gif.Private.lzw.crnt_shift_state = 0;
gif.Private.lzw.crnt_shift_dword = 0;
gif.Private.lzw.prefix.fill(NO_SUCH_CODE as GifPrefixType);
Ok(())
}
pub(crate) fn dgif_decompress_line(
gif: &mut GifFileType,
line: &mut [GifPixelType],
) -> Result<(), GifError> {
let line_len = line.len();
let mut i: usize = 0;
let mut stack_ptr = gif.Private.lzw.stack_ptr;
let eof_code = gif.Private.lzw.eof_code;
let clear_code = gif.Private.lzw.clear_code;
let mut last_code = gif.Private.lzw.last_code;
if stack_ptr > LZ_MAX_CODE as c_int {
return Err(GifError::DImageDefect);
}
while stack_ptr != 0 && i < line_len {
stack_ptr -= 1;
line[i] = gif.Private.lzw.stack[stack_ptr as usize];
i += 1;
}
while i < line_len {
let mut crnt_code: c_int = 0;
dgif_decompress_input(gif, &mut crnt_code)?;
if crnt_code == eof_code {
line[i..].fill(0);
return Err(GifError::DEofTooSoon);
} else if crnt_code == clear_code {
gif.Private.lzw.prefix.fill(NO_SUCH_CODE as GifPrefixType);
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;
last_code = NO_SUCH_CODE;
gif.Private.lzw.last_code = NO_SUCH_CODE;
} else {
let crnt_prefix: c_int;
if crnt_code < clear_code {
line[i] = crnt_code as GifPixelType;
i += 1;
} else {
if gif.Private.lzw.prefix[crnt_code as usize] == NO_SUCH_CODE as GifPrefixType {
crnt_prefix = last_code;
let rc_idx = gif.Private.lzw.running_code - 2;
let trace_from = if crnt_code == gif.Private.lzw.running_code - 2 {
last_code
} else {
crnt_code
};
let pc = dgif_get_prefix_char(&*gif.Private.lzw.prefix, trace_from, clear_code);
gif.Private.lzw.suffix[rc_idx as usize] = pc as GifByteType;
gif.Private.lzw.stack[stack_ptr as usize] = pc as GifByteType;
stack_ptr += 1;
} else {
crnt_prefix = crnt_code;
}
let mut cp = crnt_prefix;
while (stack_ptr as usize) < LZ_MAX_CODE
&& cp > clear_code
&& cp <= LZ_MAX_CODE as c_int
{
gif.Private.lzw.stack[stack_ptr as usize] = gif.Private.lzw.suffix[cp as usize];
stack_ptr += 1;
cp = gif.Private.lzw.prefix[cp as usize] as c_int;
}
if stack_ptr as usize >= LZ_MAX_CODE || cp > LZ_MAX_CODE as c_int {
return Err(GifError::DImageDefect);
}
gif.Private.lzw.stack[stack_ptr as usize] = cp as GifByteType;
stack_ptr += 1;
while stack_ptr != 0 && i < line_len {
stack_ptr -= 1;
line[i] = gif.Private.lzw.stack[stack_ptr as usize];
i += 1;
}
}
let rc_idx = gif.Private.lzw.running_code - 2;
if last_code != NO_SUCH_CODE
&& rc_idx >= 0
&& (rc_idx as usize) < LZ_MAX_CODE + 1
&& gif.Private.lzw.prefix[rc_idx as usize] == NO_SUCH_CODE as GifPrefixType
{
gif.Private.lzw.prefix[rc_idx as usize] = last_code as GifPrefixType;
let trace_from = if crnt_code == gif.Private.lzw.running_code - 2 {
last_code
} else {
crnt_code
};
gif.Private.lzw.suffix[rc_idx as usize] =
dgif_get_prefix_char(&*gif.Private.lzw.prefix, trace_from, clear_code)
as GifByteType;
}
last_code = crnt_code;
}
}
gif.Private.lzw.last_code = last_code;
gif.Private.lzw.stack_ptr = stack_ptr;
Ok(())
}
#[inline(always)]
fn dgif_get_prefix_char(prefix: &[GifPrefixType], mut code: c_int, clear_code: c_int) -> c_int {
let mut i = 0;
while code > clear_code && i <= LZ_MAX_CODE {
if code > LZ_MAX_CODE as c_int {
return NO_SUCH_CODE;
}
code = prefix[code as usize] as c_int;
i += 1;
}
code
}
#[inline(always)]
pub(crate) fn dgif_decompress_input(
gif: &mut GifFileType,
code: &mut c_int,
) -> Result<(), GifError> {
let running_bits = gif.Private.lzw.running_bits;
if running_bits > LZ_BITS {
return Err(GifError::DImageDefect);
}
while gif.Private.lzw.crnt_shift_state < running_bits {
let mut next_byte: GifByteType = 0;
dgif_buffered_input(gif, &mut next_byte)?;
gif.Private.lzw.crnt_shift_dword |=
(next_byte as u64) << gif.Private.lzw.crnt_shift_state as u64;
gif.Private.lzw.crnt_shift_state += 8;
}
let code_mask = (1u64 << running_bits as u64) - 1;
*code = (gif.Private.lzw.crnt_shift_dword & code_mask) as c_int;
gif.Private.lzw.crnt_shift_dword >>= running_bits as u64;
gif.Private.lzw.crnt_shift_state -= running_bits;
if gif.Private.lzw.running_code < LZ_MAX_CODE as c_int + 2 {
gif.Private.lzw.running_code += 1;
if gif.Private.lzw.running_code > gif.Private.lzw.max_code1 && running_bits < LZ_BITS {
gif.Private.lzw.max_code1 <<= 1;
gif.Private.lzw.running_bits += 1;
}
}
Ok(())
}
#[inline(always)]
fn dgif_buffered_input(gif: &mut GifFileType, next_byte: &mut GifByteType) -> Result<(), GifError> {
const BUF_READ_POS: usize = 1;
if gif.Private.lzw.buf[0] == 0 {
std::hint::cold_path();
let mut len_buf = [0u8; 1];
gif.read_exact(&mut len_buf)?;
gif.Private.lzw.buf[0] = len_buf[0];
if gif.Private.lzw.buf[0] == 0 {
return Err(GifError::DImageDefect);
}
let block_len = gif.Private.lzw.buf[0] as usize;
let gif_ptr = gif as *mut GifFileType;
if gif.Private.io.read(gif_ptr, &mut gif.Private.lzw.buf[1..1 + block_len]) != block_len {
return Err(GifError::DReadFailed);
}
*next_byte = gif.Private.lzw.buf[BUF_READ_POS];
gif.Private.lzw.buf[BUF_READ_POS] = 2; gif.Private.lzw.buf[0] -= 1;
} else {
*next_byte = gif.Private.lzw.buf[gif.Private.lzw.buf[BUF_READ_POS] as usize];
gif.Private.lzw.buf[BUF_READ_POS] = gif.Private.lzw.buf[BUF_READ_POS].wrapping_add(1);
gif.Private.lzw.buf[0] -= 1;
}
Ok(())
}