use crate::{Error, Result};
const FORMAT: &str = "Gen 1 pic";
const MAX_RUN_BITS: u32 = 16;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Pic {
pub width: u8,
pub height: u8,
pub pixels: Vec<u8>,
}
pub fn decompress(bytes: &[u8]) -> Result<Pic> {
let mut bits = Bits { bytes, position: 0 };
let (width, height) = (bits.read(4)? as u8, bits.read(4)? as u8);
if width == 0 || height == 0 {
return Err(Error::malformed(FORMAT, 0, "empty picture"));
}
let size = Size {
width: usize::from(width) * 8,
height: usize::from(height) * 8,
};
let mut planes = [
vec![0; size.width * size.height],
vec![0; size.width * size.height],
];
let first = bits.read(1)? as usize;
let second = first ^ 1;
fill(&mut bits, &mut planes[first], size)?;
let mode = match bits.read(1)? {
0 => 0,
_ => 1 + bits.read(1)?,
};
fill(&mut bits, &mut planes[second], size)?;
match mode {
0 => planes.iter_mut().for_each(|plane| undo_delta(plane, size)),
1 => undo_delta(&mut planes[first], size),
_ => {
undo_delta(&mut planes[second], size);
undo_delta(&mut planes[first], size);
}
}
if mode != 0 {
let [low, high] = &mut planes;
let (from, into) = if first == 0 { (low, high) } else { (high, low) };
into.iter_mut()
.zip(from.iter())
.for_each(|(bit, from)| *bit ^= from);
}
let [low, high] = planes;
Ok(Pic {
width,
height,
pixels: low
.iter()
.zip(&high)
.map(|(low, high)| low | high << 1)
.collect(),
})
}
#[derive(Debug, Clone, Copy)]
struct Size {
width: usize,
height: usize,
}
fn fill(bits: &mut Bits, plane: &mut [u8], size: Size) -> Result<()> {
let pairs = plane.len() / 2;
let mut pair = 0;
let mut zeros = bits.read(1)? == 0;
while pair < pairs {
if zeros {
let start = bits.position;
let mut ones = 0;
while bits.read(1)? == 1 {
ones += 1;
if ones >= MAX_RUN_BITS {
return Err(bits.error_at(start, "zero run too long"));
}
}
pair += (2usize << ones) - 1 + bits.read(ones + 1)? as usize;
if pair > pairs {
return Err(bits.error_at(start, "zero run past the end of the plane"));
}
} else {
while pair < pairs {
let value = bits.read(2)? as u8;
if value == 0 {
break;
}
let (x, y) = (pair / size.height * 2, pair % size.height);
plane[y * size.width + x] = value >> 1;
plane[y * size.width + x + 1] = value & 1;
pair += 1;
}
}
zeros = !zeros;
}
Ok(())
}
fn undo_delta(plane: &mut [u8], size: Size) {
for row in plane.chunks_exact_mut(size.width) {
let mut color = 0;
for bit in row {
color ^= *bit;
*bit = color;
}
}
}
struct Bits<'a> {
bytes: &'a [u8],
position: usize,
}
impl Bits<'_> {
fn read(&mut self, count: u32) -> Result<u32> {
let mut value = 0;
for _ in 0..count {
let byte = self
.bytes
.get(self.position / 8)
.ok_or_else(|| self.error_at(self.position, "data ends early"))?;
value = value << 1 | u32::from(byte >> (7 - self.position % 8) & 1);
self.position += 1;
}
Ok(value)
}
fn error_at(&self, bit: usize, reason: &'static str) -> Error {
Error::malformed(FORMAT, (bit / 8) as u64, reason)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn pack(bits: &str) -> Vec<u8> {
let bits: Vec<u8> = bits.bytes().filter(|b| b"01".contains(b)).collect();
bits.chunks(8)
.map(|chunk| {
(0..8).fold(0, |byte, index| {
byte << 1 | chunk.get(index).map_or(0, |bit| bit - b'0')
})
})
.collect()
}
#[test]
fn decompresses_runs_literals_and_deltas() {
let bytes = pack("0001 0001 0 0 11110 00001 0 1 01 00 11110 00000");
let pic = decompress(&bytes).unwrap();
assert_eq!((pic.width, pic.height), (1, 1));
assert_eq!(pic.pixels[..8], [0, 2, 2, 2, 2, 2, 2, 2]);
assert!(pic.pixels[8..].iter().all(|&pixel| pixel == 0));
}
#[test]
fn rejects_truncated_and_overlong_data() {
assert!(decompress(&pack("0001 0001 0 0 11110")).is_err());
assert!(decompress(&pack("0001 0001 0 0 111110 000000")).is_err());
assert!(decompress(&pack("0000 0001")).is_err());
assert!(decompress(&[0xFF; 64]).is_err());
}
}