use std::io::{Cursor,Read,Seek,SeekFrom,Write};
use byteorder::LittleEndian as LE;
use byteorder::{ReadBytesExt,WriteBytesExt};
use ::{FlicError,FlicResult,Raster,RasterMut};
use super::{Group,GroupByLC};
pub const FLI_LC: u16 = 12;
#[derive(Clone,Copy,Debug)]
enum LcOp {
Skip(usize),
Memset(usize, usize),
Memcpy(usize, usize),
}
pub fn decode_fli_lc(src: &[u8], dst: &mut RasterMut)
-> FlicResult<()> {
let mut r = Cursor::new(src);
let y0 = r.read_u16::<LE>()? as usize;
let hh = r.read_u16::<LE>()? as usize;
let start = dst.stride * (dst.y + y0);
let end = dst.stride * (dst.y + y0 + hh);
for row in dst.buf[start..end].chunks_mut(dst.stride) {
let count = r.read_u8()?;
let mut x0 = dst.x;
for _ in 0..count {
let nskip = r.read_u8()? as usize;
let signed_length = r.read_i8()? as i32;
if signed_length >= 0 {
let start = x0 + nskip;
let end = start + signed_length as usize;
if end > row.len() {
return Err(FlicError::Corrupted);
}
r.read_exact(&mut row[start..end])?;
x0 = end;
} else {
let start = x0 + nskip;
let end = start + (-signed_length) as usize;
if end > row.len() {
return Err(FlicError::Corrupted);
}
let c = r.read_u8()?;
for e in &mut row[start..end] {
*e = c;
}
x0 = end;
}
}
}
Ok(())
}
pub fn encode_fli_lc<W: Write + Seek>(
prev: &Raster, next: &Raster, w: &mut W)
-> FlicResult<usize> {
if (prev.w != next.w) || (prev.h != next.h) {
return Err(FlicError::WrongResolution);
}
let prev_start = prev.stride * prev.y;
let prev_end = prev.stride * (prev.y + prev.h);
let next_start = next.stride * next.y;
let next_end = next.stride * (next.y + next.h);
let y0 = prev.buf[prev_start..prev_end].chunks(prev.stride)
.zip(next.buf[next_start..next_end].chunks(next.stride))
.take_while(|&(p, n)| &p[prev.x..(prev.x + prev.w)] == &n[next.x..(next.x + next.w)])
.count();
if y0 >= next.h {
return Ok(0);
}
let y1 = next.h - prev.buf[prev_start..prev_end].chunks(prev.stride)
.zip(next.buf[next_start..next_end].chunks(next.stride))
.rev()
.take_while(|&(p, n)| &p[prev.x..(prev.x + prev.w)] == &n[next.x..(next.x + next.w)])
.count();
if y1 <= y0 {
return Ok(0);
}
let hh = y1 - y0;
if (y0 > ::std::u16::MAX as usize) || (hh > ::std::u16::MAX as usize) {
return Err(FlicError::ExceededLimit);
}
let max_size = (next.w * next.h) as u64;
let pos0 = w.seek(SeekFrom::Current(0))?;
w.write_u16::<LE>(y0 as u16)?;
w.write_u16::<LE>(hh as u16)?;
let prev_start = prev.stride * y0;
let prev_end = prev.stride * y1;
let next_start = next.stride * y0;
let next_end = next.stride * y1;
for (p, n) in prev.buf[prev_start..prev_end].chunks(prev.stride)
.zip(next.buf[next_start..next_end].chunks(next.stride)) {
let p = &p[prev.x..(prev.x + prev.w)];
let n = &n[next.x..(next.x + next.w)];
let pos1 = w.seek(SeekFrom::Current(0))?;
w.write_u8(0)?;
let mut state = LcOp::Skip(0);
let mut count = 0;
for g in GroupByLC::new_lc(p, n)
.set_prepend_same_run()
.set_ignore_final_same_run() {
if let Some(new_state) = combine_packets(state, g) {
state = new_state;
} else {
let new_state = convert_packet(g);
count = write_packet(state, count, n, w)?;
match (state, new_state) {
(LcOp::Skip(_), _) => {},
(_, LcOp::Skip(_)) => {},
_ => count = write_packet(LcOp::Skip(0), count, n, w)?,
}
if count > 2 * ::std::u8::MAX as usize {
return Err(FlicError::ExceededLimit);
}
state = new_state;
}
}
if let LcOp::Skip(_) = state {
} else {
count = write_packet(state, count, n, w)?;
}
assert!(count % 2 == 0);
if count > 2 * ::std::u8::MAX as usize {
return Err(FlicError::ExceededLimit);
}
let pos2 = w.seek(SeekFrom::Current(0))?;
if pos2 - pos0 > max_size {
return Err(FlicError::ExceededLimit);
}
w.seek(SeekFrom::Start(pos1))?;
w.write_u8((count / 2) as u8)?;
w.seek(SeekFrom::Start(pos2))?;
}
let mut pos1 = w.seek(SeekFrom::Current(0))?;
if (pos1 - pos0) % 2 == 1 {
w.write_u8(0)?;
pos1 = pos1 + 1;
}
Ok((pos1 - pos0) as usize)
}
fn combine_packets(s0: LcOp, s1: Group)
-> Option<LcOp> {
match (s0, s1) {
(LcOp::Skip(a), Group::Same(_, b)) => return Some(LcOp::Skip(a + b)),
(LcOp::Skip(_), Group::Diff(..)) => return None,
(LcOp::Memset(idx, a), Group::Same(_, b)) =>
if a + b < 3 {
return Some(LcOp::Memcpy(idx, a + b));
},
(LcOp::Memset(idx, a), Group::Diff(_, b)) =>
if a + b <= 4 {
return Some(LcOp::Memcpy(idx, a + b));
},
(LcOp::Memcpy(idx, a), Group::Same(_, b)) =>
if b < 2 {
return Some(LcOp::Memcpy(idx, a + b));
},
(LcOp::Memcpy(idx, a), Group::Diff(_, b)) =>
if b < 5 {
return Some(LcOp::Memcpy(idx, a + b));
},
}
None
}
fn convert_packet(g: Group)
-> LcOp {
match g {
Group::Same(_, len) => LcOp::Skip(len),
Group::Diff(start, len) => LcOp::Memset(start, len),
}
}
fn write_packet<W: Write>(
op: LcOp, count: usize, buf: &[u8], w: &mut W)
-> FlicResult<usize> {
let mut count = count;
match op {
LcOp::Skip(mut len) => {
let max = ::std::u8::MAX as usize;
while len > max {
w.write_u8(max as u8)?;
w.write_i8(0)?;
len = len - max;
count = count + 2;
}
w.write_u8(len as u8)?;
count = count + 1;
},
LcOp::Memset(idx, mut len) => {
let max = (-(::std::i8::MIN as i32)) as usize;
while len > max {
w.write_i8(max as i8)?;
w.write_u8(buf[idx])?;
w.write_u8(0)?;
len = len - max;
count = count + 2;
}
w.write_i8(-(len as i32) as i8)?;
w.write_u8(buf[idx])?;
count = count + 1;
},
LcOp::Memcpy(mut idx, mut len) => {
let max = ::std::i8::MAX as usize;
while len > max {
w.write_i8(max as i8)?;
w.write_all(&buf[idx..(idx + max)])?;
w.write_u8(0)?;
idx = idx + max;
len = len - max;
count = count + 2;
}
w.write_u8(len as u8)?;
w.write_all(&buf[idx..(idx + len)])?;
count = count + 1;
},
}
Ok(count)
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use ::{Raster,RasterMut};
use super::*;
#[test]
fn test_decode_fli_lc() {
let src = [
0x02, 0x00, 0x01, 0x00, 0x02, 3, 5, 0x01, 0x23, 0x45, 0x67, 0x89,
2, (-4i8) as u8, 0xAB ];
let expected = [
0x00, 0x00, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89,
0x00, 0x00, 0xAB, 0xAB, 0xAB, 0xAB,
0x00, 0x00 ];
const SCREEN_W: usize = 320;
const SCREEN_H: usize = 200;
let mut buf = [0; SCREEN_W * SCREEN_H];
let mut pal = [0; 3 * 256];
let res = decode_fli_lc(&src,
&mut RasterMut::new(SCREEN_W, SCREEN_H, &mut buf, &mut pal));
assert!(res.is_ok());
assert_eq!(&buf[(SCREEN_W * 2)..(SCREEN_W * 2 + 16)], &expected[..]);
}
#[test]
fn test_encode_fli_lc() {
let src1 = [
0x00, 0x00,
0x01, 0x23, 0x45, 0x67, 0x89,
0x00, 0x00, 0xAB, 0xAB, 0xAB, 0xAB,
0x00, 0x00, 0x00 ];
let src2 = [
0x01, 0x00, 0x45, 0x00, 0x89,
0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB,
0x00, 0x00, 0x00 ];
let expected = [
0x02, 0x00, 0x03, 0x00, 2, 2, 5, 0x01, 0x23, 0x45, 0x67, 0x89,
2, (-4i8) as u8, 0xAB,
0, 2, 0, 5, 0x01, 0x00, 0x45, 0x00, 0x89,
0, (-8i8) as u8, 0xAB,
0x00 ];
const SCREEN_W: usize = 32;
const SCREEN_H: usize = 8;
let buf1 = [0; SCREEN_W * SCREEN_H];
let mut buf2 = [0; SCREEN_W * SCREEN_H];
let pal = [0; 3 * 256];
buf2[(SCREEN_W * 2)..(SCREEN_W * 2 + 16)].copy_from_slice(&src1[..]);
buf2[(SCREEN_W * 4)..(SCREEN_W * 4 + 16)].copy_from_slice(&src2[..]);
let mut enc: Cursor<Vec<u8>> = Cursor::new(Vec::new());
let prev = Raster::new(SCREEN_W, SCREEN_H, &buf1, &pal);
let next = Raster::new(SCREEN_W, SCREEN_H, &buf2, &pal);
let res = encode_fli_lc(&prev, &next, &mut enc);
assert!(res.is_ok());
assert_eq!(&enc.get_ref()[..], &expected[..]);
}
}