use std::cmp::min;
use std::fs::File;
use std::io::{Cursor,Read,Seek,SeekFrom,Write};
use std::path::{Path,PathBuf};
use byteorder::LittleEndian as LE;
use byteorder::{ReadBytesExt,WriteBytesExt};
use ::{FlicError,FlicResult,Raster,RasterMut};
use ::pstamp::{PostageStamp,write_pstamp_data};
use codec::*;
pub const FLIH_MAGIC: u16 = 0xAF11;
pub const FLIHR_MAGIC: u16 = 0xAF12;
pub const LIBFLIC_UPDATER_ID: u32 = 0x464C5253;
#[allow(dead_code)]
pub struct FlicFile {
hdr: FlicHeader,
frame_hdr: Vec<FlicFrame>,
frame: usize,
filename: PathBuf,
file: File,
}
pub struct FlicFileWriter {
hdr: FlicHeader,
offset_frame1: u64,
offset_frame2: u64,
filename: PathBuf,
file: Option<File>,
}
pub const SIZE_OF_FLIC_HEADER: usize = 128;
struct FlicHeader {
magic: u16,
size: u32,
frame_count: u16,
w: u16,
h: u16,
speed_msec: u32,
speed_jiffies: u16,
created: u32,
creator: u32,
updated: u32,
updater: u32,
aspect_x: u16,
aspect_y: u16,
}
pub const FCID_PREFIX: u16 = 0xF100;
pub const FCID_FRAME: u16 = 0xF1FA;
pub const SIZE_OF_FLIC_FRAME: usize = 16;
struct FlicFrame {
chunks: Vec<ChunkId>,
}
pub const SIZE_OF_CHUNK: usize = 6;
struct ChunkId {
offset: u64,
size: u32,
magic: u16,
}
pub struct FlicPlaybackResult {
pub ended: bool,
pub looped: bool,
pub palette_updated: bool,
}
impl FlicFile {
pub fn open(filename: &Path)
-> FlicResult<Self> {
if !filename.exists() {
return Err(FlicError::NoFile);
} else if !filename.is_file() {
return Err(FlicError::NotARegularFile);
}
let mut file = File::open(filename)?;
let hdr = read_flic_header(&mut file)?;
let frame_hdr = read_frame_headers(&mut file, &hdr)?;
Ok(FlicFile {
hdr: hdr,
frame_hdr: frame_hdr,
frame: 0,
filename: filename.to_path_buf(),
file: file,
})
}
pub fn frame(&self) -> u16 {
self.frame as u16
}
pub fn frame_count(&self) -> u16 {
self.hdr.frame_count
}
pub fn width(&self) -> u16 {
self.hdr.w
}
pub fn height(&self) -> u16 {
self.hdr.h
}
pub fn speed_msec(&self) -> u32 {
self.hdr.speed_msec
}
pub fn speed_jiffies(&self) -> u16 {
self.hdr.speed_jiffies
}
pub fn creator(&self) -> u32 {
self.hdr.creator
}
pub fn creation_time(&self) -> u32 {
self.hdr.created
}
pub fn updater(&self) -> u32 {
self.hdr.updater
}
pub fn update_time(&self) -> u32 {
self.hdr.updated
}
pub fn aspect_x(&self) -> u16 {
self.hdr.aspect_x
}
pub fn aspect_y(&self) -> u16 {
self.hdr.aspect_y
}
pub fn read_postage_stamp<'a>(&mut self, dst: &'a mut RasterMut<'a>)
-> FlicResult<()> {
let mut pstamp = PostageStamp::new(
self.hdr.w as usize, self.hdr.h as usize, dst);
for chunk in self.frame_hdr[0].chunks.iter() {
self.file.seek(SeekFrom::Start(chunk.offset))?;
let mut buf = vec![0; chunk.size as usize];
self.file.read_exact(&mut buf)?;
let done = pstamp.feed(chunk.magic, &buf)?;
if done {
break;
}
}
Ok(())
}
pub fn read_next_frame(&mut self, dst: &mut RasterMut)
-> FlicResult<FlicPlaybackResult> {
let mut res = FlicPlaybackResult {
ended: false,
looped: false,
palette_updated: false,
};
if (self.hdr.w as usize != dst.w) || (self.hdr.h as usize != dst.h) {
return Err(FlicError::WrongResolution);
}
let frame = &self.frame_hdr[self.frame];
for chunk in frame.chunks.iter() {
self.file.seek(SeekFrom::Start(chunk.offset))?;
let mut buf = vec![0; chunk.size as usize];
self.file.read_exact(&mut buf)?;
decode_chunk(chunk.magic, &buf, dst)?;
res.palette_updated = res.palette_updated
|| chunk_modifies_palette(chunk.magic);
}
if self.frame + 1 >= self.frame_hdr.len() {
self.frame = 1;
res.looped = true;
} else {
self.frame = self.frame + 1;
}
if self.frame + 1 >= self.frame_hdr.len() {
res.ended = true;
}
Ok(res)
}
}
impl FlicFileWriter {
pub fn create(filename: &Path, w: u16, h: u16, speed_msec: u32)
-> FlicResult<Self> {
let mut file = File::create(filename)?;
file.write_all(&[0; SIZE_OF_FLIC_HEADER])?;
let jiffy_speed = min((speed_msec as u64) * 70 / 1000, ::std::u16::MAX as u64) as u16;
let hdr = FlicHeader {
magic: FLIHR_MAGIC,
size: 0,
frame_count: 0,
w: w,
h: h,
speed_msec: speed_msec,
speed_jiffies: jiffy_speed,
created: 0,
creator: 0,
updated: 0,
updater: LIBFLIC_UPDATER_ID,
aspect_x: 1,
aspect_y: 1,
};
Ok(FlicFileWriter{
hdr: hdr,
offset_frame1: 0,
offset_frame2: 0,
filename: filename.to_path_buf(),
file: Some(file),
})
}
pub fn create_fli(filename: &Path, speed_jiffies: u16)
-> FlicResult<Self> {
let mut file = File::create(filename)?;
file.write_all(&[0; SIZE_OF_FLIC_HEADER])?;
let hdr = FlicHeader {
magic: FLIH_MAGIC,
size: 0,
frame_count: 0,
w: 320,
h: 200,
speed_msec: (speed_jiffies as u32) * 1000 / 70,
speed_jiffies: speed_jiffies,
created: 0,
creator: 0,
updated: 0,
updater: LIBFLIC_UPDATER_ID,
aspect_x: 6,
aspect_y: 5,
};
Ok(FlicFileWriter{
hdr: hdr,
offset_frame1: 0,
offset_frame2: 0,
filename: filename.to_path_buf(),
file: Some(file),
})
}
pub fn set_creator(&mut self, creator: u32, created: u32) {
self.hdr.creator = creator;
self.hdr.created = created;
}
pub fn set_updater(&mut self, updater: u32, updated: u32) {
self.hdr.updater = updater;
self.hdr.updated = updated;
}
pub fn set_aspect_ratio(&mut self, x: u16, y: u16) {
if x > 0 && y > 0 {
self.hdr.aspect_x = x;
self.hdr.aspect_y = y;
} else {
self.hdr.aspect_x = 1;
self.hdr.aspect_y = 1;
}
}
pub fn close(mut self)
-> FlicResult<()> {
if let Some(mut file) = self.file.take() {
if self.hdr.frame_count == 0 {
return Err(FlicError::Corrupted);
} else if self.hdr.frame_count == 1 {
self.offset_frame2 = file.seek(SeekFrom::Current(0))?;
write_empty_frame(&mut file)?;
} else {
self.hdr.frame_count = self.hdr.frame_count - 1;
}
let size = file.seek(SeekFrom::Current(0))?;
if size > ::std::u32::MAX as u64 {
return Err(FlicError::ExceededLimit);
}
self.hdr.size = size as u32;
file.seek(SeekFrom::Start(0))?;
write_flic_header(
&self.hdr, self.offset_frame1, self.offset_frame2,
&mut file)?;
Ok(())
} else {
Err(FlicError::NoFile)
}
}
pub fn write_next_frame(&mut self, prev: Option<&Raster>, next: &Raster)
-> FlicResult<()> {
if let Some(mut file) = self.file.as_ref() {
if (next.w != self.hdr.w as usize) || (next.h != self.hdr.h as usize) {
return Err(FlicError::WrongResolution);
}
if self.hdr.frame_count == ::std::u16::MAX {
return Err(FlicError::ExceededLimit);
}
if self.hdr.frame_count == 0 {
self.offset_frame1 = file.seek(SeekFrom::Current(0))?;
} else if self.hdr.frame_count == 1 {
self.offset_frame2 = file.seek(SeekFrom::Current(0))?;
}
let prev = if self.hdr.frame_count == 0 {
None
} else {
prev
};
write_next_frame(self.hdr.magic, self.hdr.frame_count,
prev, next, &mut file)?;
self.hdr.frame_count = self.hdr.frame_count + 1;
Ok(())
} else {
Err(FlicError::NoFile)
}
}
}
impl Drop for FlicFileWriter {
fn drop(&mut self) {
if self.file.is_some() {
println!("Warning: {} was not closed, may be corrupt.",
self.filename.to_string_lossy());
}
}
}
fn read_flic_header(file: &mut File)
-> FlicResult<FlicHeader> {
let mut buf = [0; SIZE_OF_FLIC_HEADER];
file.read_exact(&mut buf)?;
let mut r = Cursor::new(&buf[..]);
let size = r.read_u32::<LE>()?;
let magic = r.read_u16::<LE>()?;
match magic {
FLIH_MAGIC => read_fli_header(&mut r, size, magic),
FLIHR_MAGIC => read_flc_header(&mut r, size, magic),
_ => Err(FlicError::BadMagic),
}
}
fn read_fli_header(
r: &mut Cursor<&[u8]>, size: u32, magic: u16)
-> FlicResult<FlicHeader> {
assert_eq!(magic, FLIH_MAGIC);
let frame_count = r.read_u16::<LE>()?;
let width = r.read_u16::<LE>()?;
let height = r.read_u16::<LE>()?;
let _bpp = r.read_u16::<LE>()?;
let _flags = r.read_u16::<LE>()?;
let jiffy_speed = r.read_u16::<LE>()?;
match r.seek(SeekFrom::Current(110)) {
Ok(128) => (),
_ => unreachable!(),
};
if width != 320 || height != 200 {
return Err(FlicError::WrongResolution);
}
if frame_count <= 0 {
return Err(FlicError::Corrupted);
}
Ok(FlicHeader{
magic: magic,
size: size,
frame_count: frame_count,
w: width,
h: height,
speed_msec: (jiffy_speed as u32) * 1000 / 70,
speed_jiffies: jiffy_speed,
created: 0,
creator: 0,
updated: 0,
updater: 0,
aspect_x: 6,
aspect_y: 5,
})
}
fn read_flc_header(
r: &mut Cursor<&[u8]>, size: u32, magic: u16)
-> FlicResult<FlicHeader> {
assert_eq!(magic, FLIHR_MAGIC);
let frame_count = r.read_u16::<LE>()?;
let width = r.read_u16::<LE>()?;
let height = r.read_u16::<LE>()?;
let _bpp = r.read_u16::<LE>()?;
let _flags = r.read_u16::<LE>()?;
let speed = r.read_u32::<LE>()?;
r.seek(SeekFrom::Current(2))?;
let created = r.read_u32::<LE>()?;
let creator = r.read_u32::<LE>()?;
let updated = r.read_u32::<LE>()?;
let updater = r.read_u32::<LE>()?;
let mut aspect_x = r.read_u16::<LE>()?;
let mut aspect_y = r.read_u16::<LE>()?;
r.seek(SeekFrom::Current(38))?;
let _oframe1 = r.read_u32::<LE>()?;
let _oframe2 = r.read_u32::<LE>()?;
match r.seek(SeekFrom::Current(40)) {
Ok(128) => (),
_ => unreachable!(),
};
if frame_count <= 0 || width <= 0 || height <= 0 {
return Err(FlicError::Corrupted);
}
let jiffy_speed = min((speed as u64) * 70 / 1000, ::std::u16::MAX as u64) as u16;
if aspect_x <= 0 || aspect_y <= 0 {
aspect_x = 1;
aspect_y = 1;
}
Ok(FlicHeader{
magic: magic,
size: size,
frame_count: frame_count,
w: width,
h: height,
speed_msec: speed,
speed_jiffies: jiffy_speed,
created: created,
creator: creator,
updated: updated,
updater: updater,
aspect_x: aspect_x,
aspect_y: aspect_y,
})
}
fn read_frame_headers(file: &mut File, hdr: &FlicHeader)
-> FlicResult<Vec<FlicFrame>> {
let mut frames = Vec::with_capacity(min(4096, 1 + hdr.frame_count as usize));
let mut offset = SIZE_OF_FLIC_HEADER as u64;
for frame_num in 0..(hdr.frame_count + 1) {
let mut buf = [0; SIZE_OF_FLIC_FRAME];
let mut size;
let mut magic;
let mut num_chunks;
file.seek(SeekFrom::Start(offset))?;
file.read_exact(&mut buf)?;
{
let mut r = Cursor::new(&buf[..]);
size = r.read_u32::<LE>()?;
magic = r.read_u16::<LE>()?;
num_chunks = r.read_u16::<LE>()? as usize;
if size < (SIZE_OF_FLIC_FRAME as u32)
|| offset + (size as u64) > (hdr.size as u64) {
return Err(FlicError::Corrupted);
}
}
if frame_num == 0 && magic == FCID_PREFIX {
offset = offset + size as u64;
file.seek(SeekFrom::Start(offset))?;
file.read_exact(&mut buf)?;
let mut r = Cursor::new(&buf[..]);
size = r.read_u32::<LE>()?;
magic = r.read_u16::<LE>()?;
num_chunks = r.read_u16::<LE>()? as usize;
if size < (SIZE_OF_FLIC_FRAME as u32)
|| offset + (size as u64) > (hdr.size as u64) {
return Err(FlicError::Corrupted);
}
}
if magic != FCID_FRAME {
return Err(FlicError::BadMagic);
}
let chunks = read_chunk_headers(file, hdr,
frame_num, offset, size, num_chunks)?;
assert_eq!(chunks.len(), num_chunks);
if num_chunks > 0 {
let position = chunks[num_chunks - 1].offset + chunks[num_chunks - 1].size as u64;
let expected = offset + size as u64;
if position > expected {
println!("Warning: frame {} reads too much - current offset={}, expected offset={}",
frame_num, position, expected);
} else if position < expected {
println!("Warning: frame {} reads too little - current offset={}, expected offset={}",
frame_num, position, expected);
}
}
frames.push(FlicFrame{
chunks: chunks,
});
offset = offset + size as u64;
}
Ok(frames)
}
fn read_chunk_headers(file: &mut File, hdr: &FlicHeader,
frame_num: u16, frame_offset: u64, frame_size: u32, num_chunks: usize)
-> FlicResult<Vec<ChunkId>> {
let mut chunks = Vec::with_capacity(min(4, num_chunks));
let mut offset = frame_offset + SIZE_OF_FLIC_FRAME as u64;
for _ in 0..num_chunks {
file.seek(SeekFrom::Start(offset))?;
let mut buf = [0; SIZE_OF_CHUNK];
file.read_exact(&mut buf)?;
let mut r = Cursor::new(&buf[..]);
let size = r.read_u32::<LE>()?;
let magic = r.read_u16::<LE>()?;
if !(SIZE_OF_CHUNK as u32 <= size && size <= frame_size) {
return Err(FlicError::Corrupted);
}
let mut size2 = size;
match magic {
FLI_WRUN =>
println!("Warning: frame {} - FLI_WRUN chunk type detected",
frame_num),
FLI_SBSRSC =>
println!("Warning: frame {} - FLI_SBSRSC chunk type detected",
frame_num),
FLI_ICOLORS =>
println!("Warning: frame {} - FLI_ICOLORS chunk type detected",
frame_num),
FLI_COPY => {
if size == hdr.w as u32 * hdr.h as u32 + 4 {
size2 = hdr.w as u32 * hdr.h as u32 + 6;
println!("Warning: frame {} - FLI_COPY has wrong size",
frame_num);
}
},
FLI_COLOR256 | FLI_SS2 | FLI_COLOR64 | FLI_LC | FLI_BLACK | FLI_BRUN | FLI_PSTAMP => (),
_ => println!("Warning: frame {} - unrecognised chunk type {}",
frame_num, magic),
}
chunks.push(ChunkId {
offset: offset + SIZE_OF_CHUNK as u64,
size: size2 - SIZE_OF_CHUNK as u32,
magic: magic,
});
offset = offset + size as u64;
}
Ok(chunks)
}
fn write_flic_header<W: Write + Seek>(
hdr: &FlicHeader, offset_frame1: u64, offset_frame2: u64, w: &mut W)
-> FlicResult<()> {
match hdr.magic {
FLIH_MAGIC => write_fli_header(hdr, w),
FLIHR_MAGIC => write_flc_header(hdr, offset_frame1, offset_frame2, w),
_ => return Err(FlicError::BadMagic),
}
}
fn write_fli_header<W: Write + Seek>(
hdr: &FlicHeader, w: &mut W)
-> FlicResult<()> {
let depth = 8;
let flags = 0;
w.write_u32::<LE>(hdr.size)?;
w.write_u16::<LE>(FLIH_MAGIC)?;
w.write_u16::<LE>(hdr.frame_count)?;
w.write_u16::<LE>(hdr.w)?;
w.write_u16::<LE>(hdr.h)?;
w.write_u16::<LE>(depth)?;
w.write_u16::<LE>(flags)?;
w.write_u16::<LE>(hdr.speed_jiffies)?;
Ok(())
}
fn write_flc_header<W: Write + Seek>(
hdr: &FlicHeader, offset_frame1: u64, offset_frame2: u64, w: &mut W)
-> FlicResult<()> {
let depth = 8;
let flags = 3;
w.write_u32::<LE>(hdr.size)?;
w.write_u16::<LE>(FLIHR_MAGIC)?;
w.write_u16::<LE>(hdr.frame_count)?;
w.write_u16::<LE>(hdr.w)?;
w.write_u16::<LE>(hdr.h)?;
w.write_u16::<LE>(depth)?;
w.write_u16::<LE>(flags)?;
w.write_u32::<LE>(hdr.speed_msec)?;
w.seek(SeekFrom::Current(2))?; w.write_u32::<LE>(hdr.created)?;
w.write_u32::<LE>(hdr.creator)?;
w.write_u32::<LE>(hdr.updated)?;
w.write_u32::<LE>(hdr.updater)?;
w.write_u16::<LE>(hdr.aspect_x)?;
w.write_u16::<LE>(hdr.aspect_y)?;
w.seek(SeekFrom::Current(38))?;
if offset_frame1 < offset_frame2 && offset_frame2 <= ::std::u32::MAX as u64 {
w.write_u32::<LE>(offset_frame1 as u32)?;
w.write_u32::<LE>(offset_frame2 as u32)?;
}
Ok(())
}
fn write_empty_frame<W: Write>(
w: &mut W)
-> FlicResult<()> {
w.write_u32::<LE>(SIZE_OF_FLIC_FRAME as u32)?;
w.write_u16::<LE>(FCID_FRAME)?;
w.write_u16::<LE>(0)?; w.write_all(&[0; 8])?;
Ok(())
}
fn write_next_frame<W: Write + Seek>(
flic_magic: u16, frame_count: u16,
prev: Option<&Raster>, next: &Raster, w: &mut W)
-> FlicResult<usize> {
let pos0 = w.seek(SeekFrom::Current(0))?;
w.write_all(&[0; SIZE_OF_FLIC_FRAME])?;
let size_pstamp =
if flic_magic != FLIH_MAGIC && frame_count == 0 {
match write_pstamp_data(next, w) {
Ok(size) => size,
Err(_) => {
w.seek(SeekFrom::Start(pos0 + SIZE_OF_FLIC_FRAME as u64))?;
0
},
}
} else {
0
};
let size_col = write_color_data(flic_magic, prev, next, w)?;
let size_pix = write_pixel_data(flic_magic, prev, next, w)?;
let size = SIZE_OF_FLIC_FRAME + size_pstamp + size_col + size_pix;
if size > ::std::u32::MAX as usize {
return Err(FlicError::ExceededLimit);
}
let pos1 = w.seek(SeekFrom::Current(0))?;
w.seek(SeekFrom::Start(pos0))?;
if size > 0 {
let num_chunks
= if size_pstamp > 0 { 1 } else { 0 }
+ if size_col > 0 { 1 } else { 0 }
+ if size_pix > 0 { 1 } else { 0 };
assert_eq!(size, (pos1 - pos0) as usize);
w.write_u32::<LE>(size as u32)?;
w.write_u16::<LE>(FCID_FRAME)?;
w.write_u16::<LE>(num_chunks)?;
w.seek(SeekFrom::Start(pos1))?;
Ok(size)
} else {
Ok(0)
}
}
fn write_color_data<W: Write + Seek>(
flic_magic: u16, prev: Option<&Raster>, next: &Raster, w: &mut W)
-> FlicResult<usize> {
let pos0 = w.seek(SeekFrom::Current(0))?;
w.write_all(&[0; SIZE_OF_CHUNK])?;
let (chunk_size, chunk_magic) =
if flic_magic == FLIH_MAGIC {
let size = encode_fli_color64(prev, next, w)?;
(size, FLI_COLOR64)
} else {
let size = encode_fli_color256(prev, next, w)?;
(size, FLI_COLOR256)
};
if SIZE_OF_CHUNK + chunk_size > ::std::u32::MAX as usize {
return Err(FlicError::ExceededLimit);
}
let pos1 = w.seek(SeekFrom::Current(0))?;
w.seek(SeekFrom::Start(pos0))?;
if chunk_size > 0 {
w.write_u32::<LE>((SIZE_OF_CHUNK + chunk_size) as u32)?;
w.write_u16::<LE>(chunk_magic)?;
w.seek(SeekFrom::Start(pos1))?;
Ok(SIZE_OF_CHUNK + chunk_size)
} else {
Ok(0)
}
}
fn write_pixel_data<W: Write + Seek>(
flic_magic: u16, prev: Option<&Raster>, next: &Raster, w: &mut W)
-> FlicResult<usize> {
let pos0 = w.seek(SeekFrom::Current(0))?;
w.write_all(&[0; SIZE_OF_CHUNK])?;
let mut chunk_size = next.w * next.h;
let mut chunk_magic = FLI_COPY;
if chunk_magic == FLI_COPY && prev.is_none() {
if can_encode_fli_black(next) {
chunk_size = 0;
chunk_magic = FLI_BLACK;
}
}
if chunk_magic == FLI_COPY && prev.is_some() {
match encode_fli_lc(prev.unwrap(), next, w) {
Ok(size) =>
if size == 0 {
w.seek(SeekFrom::Start(pos0))?;
return Ok(0);
} else if size < chunk_size {
chunk_size = size;
chunk_magic = FLI_LC;
},
Err(FlicError::ExceededLimit) => (),
Err(e) => return Err(e),
}
if chunk_magic != FLI_LC {
w.seek(SeekFrom::Start(pos0 + SIZE_OF_CHUNK as u64))?;
}
}
if flic_magic == FLIHR_MAGIC && chunk_magic == FLI_COPY && prev.is_some() {
match encode_fli_ss2(prev.unwrap(), next, w) {
Ok(size) =>
if size < chunk_size {
chunk_size = size;
chunk_magic = FLI_SS2;
},
Err(FlicError::ExceededLimit) => {},
Err(e) => return Err(e),
}
if chunk_magic != FLI_SS2 {
w.seek(SeekFrom::Start(pos0 + SIZE_OF_CHUNK as u64))?;
}
}
if chunk_magic == FLI_COPY {
match encode_fli_brun(next, w) {
Ok(size) =>
if size < chunk_size {
chunk_size = size;
chunk_magic = FLI_BRUN;
},
Err(FlicError::ExceededLimit) => (),
Err(e) => return Err(e),
}
if chunk_magic != FLI_BRUN {
w.seek(SeekFrom::Start(pos0 + SIZE_OF_CHUNK as u64))?;
}
}
if chunk_magic == FLI_COPY {
chunk_size = encode_fli_copy(next, w)?;
chunk_magic = FLI_COPY;
}
let pos1 = w.seek(SeekFrom::Current(0))?;
assert_eq!(SIZE_OF_CHUNK + chunk_size, (pos1 - pos0) as usize);
w.seek(SeekFrom::Start(pos0))?;
if pos1 - pos0 > ::std::u32::MAX as u64 {
return Err(FlicError::ExceededLimit);
}
w.write_u32::<LE>((pos1 - pos0) as u32)?;
w.write_u16::<LE>(chunk_magic)?;
w.seek(SeekFrom::Start(pos1))?;
Ok((pos1 - pos0) as usize)
}
#[cfg(test)]
mod tests {
use std::io::{Cursor,Seek,SeekFrom};
use byteorder::LittleEndian as LE;
use byteorder::ReadBytesExt;
use ::Raster;
use ::codec::FLI_COPY;
use super::{FLIH_MAGIC,SIZE_OF_CHUNK,write_pixel_data};
#[test]
fn test_write_pixel_data_fli_copy() {
const SCREEN_W: usize = 1;
const SCREEN_H: usize = 1;
const NUM_COLS: usize = 256;
let expected_size = SIZE_OF_CHUNK + SCREEN_W * SCREEN_H;
let buf = [0xFF; SCREEN_W * SCREEN_H];
let pal = [0; 3 * NUM_COLS];
let next = Raster::new(SCREEN_W, SCREEN_H, &buf, &pal);
let mut w = Cursor::new(Vec::new());
let res = write_pixel_data(FLIH_MAGIC, None, &next, &mut w);
assert_eq!(res.expect("size"), expected_size);
w.seek(SeekFrom::Start(0)).expect("reset");
assert_eq!(w.read_u32::<LE>().expect("size"), expected_size as u32);
assert_eq!(w.read_u16::<LE>().expect("magic"), FLI_COPY);
}
}