use cast;
use nom::IResult;
use safemem::write_bytes;
use errors::*;
use util::BytesFormatter;
#[derive(Debug)]
pub struct Size {
pub w: usize,
pub h: usize,
}
#[derive(Debug)]
struct Rle {
cnt: u16,
val: u8,
}
named!(count<(&[u8], usize), u16>,
alt!(
value!(0, tag_bits!(u16, 14, 0)) |
preceded!(tag_bits!(u8, 6, 0), take_bits!(u16, 8)) |
preceded!(tag_bits!(u8, 4, 0), take_bits!(u16, 6)) |
preceded!(tag_bits!(u8, 2, 0), take_bits!(u16, 4)) |
take_bits!(u16, 2)
)
);
named!(rle<(&[u8], usize), Rle>,
do_parse!(
cnt: call!(count) >>
val: take_bits!(u8, 2) >>
(Rle { cnt: cnt, val: val })
)
);
fn scan_line(input: &[u8], output: &mut [u8]) -> Result<usize> {
trace!("scan line starting with {:?}", BytesFormatter(input));
let width = output.len();
let mut x = 0;
let mut pos = (input, 0);
while x < width {
match rle(pos) {
IResult::Done(new_pos, run) => {
pos = new_pos;
let count = if run.cnt == 0 {
width - x
} else {
cast::usize(run.cnt)
};
if x+count > output.len() {
return Err("scan line is too long".into());
}
write_bytes(&mut output[x..x+count], run.val);
x += count;
}
IResult::Error(err) => {
return Err(format!("error parsing subtitle scan line: {:?}",
err).into());
}
IResult::Incomplete(needed) => {
return Err(format!("not enough bytes parsing subtitle scan \
line: {:?}",
needed).into());
}
}
}
if x > width {
return Err("decoded scan line is too long".into());
}
if pos.1 > 0 {
pos = (&pos.0[1..], 0);
}
Ok(input.len() - pos.0.len())
}
pub fn decompress(size: Size, data: [&[u8]; 2])
-> Result<Vec<u8>> {
trace!("decompressing image {:?}, max: [0x{:x}, 0x{:x}]",
&size, data[0].len(), data[1].len());
let mut img = vec![0; size.w * size.h];
let mut offsets = [0; 2];
for y in 0..size.h {
let odd = y % 2;
trace!("line {:?}, offset 0x{:x}", y, offsets[odd]);
let consumed = scan_line(&data[odd][offsets[odd]..],
&mut img[y*size.w..(y+1)*size.w])?;
offsets[odd] += consumed;
}
Ok(img)
}