#![allow(
clippy::cast_possible_truncation,
reason = "pixel packing/unpacking and size arithmetic are bounded by the 14-bit VP8L \
dimensions and 8-bit channels"
)]
use crate::lossless::bit_io::reader::BitReader;
use crate::lossless::color_cache::ColorCache;
use crate::lossless::constants::{
ALPHABET_SIZE, COLOR_INDEXING_TRANSFORM, CROSS_COLOR_TRANSFORM, MAX_CACHE_BITS,
MIN_TRANSFORM_BITS, NUM_LENGTH_CODES, NUM_LITERAL_CODES, NUM_TRANSFORM_BITS,
PREDICTOR_TRANSFORM, SUBTRACT_GREEN_TRANSFORM, VP8L_IMAGE_SIZE_BITS, VP8L_MAGIC_BYTE,
VP8L_VERSION_BITS, subsample_size,
};
use crate::lossless::huffman::decode::{HuffmanTable, read_huffman_code};
use crate::lossless::lz77::{plane_code_to_distance, read_prefix_value};
use crate::lossless::prelude::*;
use crate::lossless::transform::{cross_color, palette, predictor, subtract_green};
use crate::lossless::{Codec, Error, Result};
pub(crate) struct Decoded {
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) argb: Vec<u32>,
}
pub(crate) struct HuffmanGroup {
green: HuffmanTable,
red: HuffmanTable,
blue: HuffmanTable,
alpha: HuffmanTable,
dist: HuffmanTable,
}
pub(crate) struct EntropyImage {
data: Vec<u16>,
xsize: u32,
bits: u32,
}
pub(crate) enum Transform {
Predictor {
bits: u32,
width: u32,
data: Vec<u32>,
},
CrossColor {
bits: u32,
width: u32,
data: Vec<u32>,
},
SubtractGreen,
ColorIndexing {
bits: u32,
dst_width: u32,
palette: Vec<u32>,
},
}
fn read_header(br: &mut BitReader<'_>) -> Result<(u32, u32, bool)> {
if br.read_bits(8) != u32::from(VP8L_MAGIC_BYTE) {
return Err(Error::InvalidBitstream {
codec: Codec::Lossless,
});
}
let width = br.read_bits(VP8L_IMAGE_SIZE_BITS) + 1;
let height = br.read_bits(VP8L_IMAGE_SIZE_BITS) + 1;
let alpha_used = br.read_bit() != 0;
if br.read_bits(VP8L_VERSION_BITS) != 0 {
return Err(Error::InvalidBitstream {
codec: Codec::Lossless,
});
}
Ok((width, height, alpha_used))
}
pub(crate) fn peek_header(payload: &[u8]) -> Result<(u32, u32, bool)> {
let mut br = BitReader::new(payload);
let header = read_header(&mut br)?;
if br.is_eos() {
return Err(Error::Truncated);
}
Ok(header)
}
pub(crate) fn decode(payload: &[u8]) -> Result<Decoded> {
let mut br = BitReader::new(payload);
let (width, height, _alpha_used) = read_header(&mut br)?;
let argb = decode_image_stream(&mut br, width, height, true)?;
if br.is_eos() {
return Err(Error::Truncated);
}
Ok(Decoded {
width,
height,
argb,
})
}
pub(crate) fn decode_alpha_stream(payload: &[u8], width: u32, height: u32) -> Result<Vec<u8>> {
let mut br = BitReader::new(payload);
let argb = decode_image_stream(&mut br, width, height, true)?;
if br.is_eos() {
return Err(Error::Truncated);
}
Ok(argb.iter().map(|&p| (p >> 8) as u8).collect())
}
pub(crate) struct ParsedStream {
pub(crate) transforms: Vec<Transform>,
pub(crate) working_width: u32,
pub(crate) total: usize,
pub(crate) cache_bits: u32,
pub(crate) groups: Vec<HuffmanGroup>,
pub(crate) entropy: Option<EntropyImage>,
}
fn parse_image_stream(
br: &mut BitReader<'_>,
xsize: u32,
ysize: u32,
is_level0: bool,
) -> Result<ParsedStream> {
let mut transforms = Vec::new();
let mut transform_xsize = xsize;
if is_level0 {
let mut seen = 0u8;
while br.read_bit() != 0 {
let (transform, reduced) = read_transform(br, transform_xsize, ysize, &mut seen)?;
transform_xsize = reduced;
transforms.push(transform);
}
}
let cache_bits = if br.read_bit() != 0 {
let bits = br.read_bits(4);
if !(1..=MAX_CACHE_BITS).contains(&bits) {
return Err(Error::InvalidBitstream {
codec: Codec::Lossless,
});
}
bits
} else {
0
};
let (groups, entropy) = read_huffman_codes(br, transform_xsize, ysize, cache_bits, is_level0)?;
let total = (transform_xsize as usize)
.checked_mul(ysize as usize)
.ok_or(Error::InvalidBitstream {
codec: Codec::Lossless,
})?;
Ok(ParsedStream {
transforms,
working_width: transform_xsize,
total,
cache_bits,
groups,
entropy,
})
}
fn decode_image_stream(
br: &mut BitReader<'_>,
xsize: u32,
ysize: u32,
is_level0: bool,
) -> Result<Vec<u32>> {
let ParsedStream {
transforms,
working_width,
total,
cache_bits,
groups,
entropy,
} = parse_image_stream(br, xsize, ysize, is_level0)?;
let argb = decode_image_data(br, working_width, total, cache_bits, groups, entropy)?;
Ok(apply_inverse_transforms(argb, &transforms))
}
pub(crate) fn apply_inverse_transforms(mut argb: Vec<u32>, transforms: &[Transform]) -> Vec<u32> {
for transform in transforms.iter().rev() {
match transform {
Transform::SubtractGreen => subtract_green::inverse(&mut argb),
Transform::Predictor { bits, width, data } => {
predictor::inverse(&mut argb, *width, *bits, data);
},
Transform::CrossColor { bits, width, data } => {
cross_color::inverse(&mut argb, *width, *bits, data);
},
Transform::ColorIndexing {
bits,
dst_width,
palette,
} => {
argb = palette::inverse(&argb, *dst_width, *bits, palette);
},
}
}
argb
}
pub(crate) fn parse_top_level(br: &mut BitReader<'_>) -> Result<((u32, u32, bool), ParsedStream)> {
let header = read_header(br)?;
let stream = parse_image_stream(br, header.0, header.1, true)?;
Ok((header, stream))
}
fn read_transform(
br: &mut BitReader<'_>,
xsize: u32,
ysize: u32,
seen: &mut u8,
) -> Result<(Transform, u32)> {
let ty = br.read_bits(2);
let bit = 1u8 << ty;
if *seen & bit != 0 {
return Err(Error::InvalidBitstream {
codec: Codec::Lossless,
});
}
*seen |= bit;
match ty {
PREDICTOR_TRANSFORM => {
let bits = br.read_bits(NUM_TRANSFORM_BITS) + MIN_TRANSFORM_BITS;
let data = read_tile_image(br, xsize, ysize, bits)?;
Ok((
Transform::Predictor {
bits,
width: xsize,
data,
},
xsize,
))
},
CROSS_COLOR_TRANSFORM => {
let bits = br.read_bits(NUM_TRANSFORM_BITS) + MIN_TRANSFORM_BITS;
let data = read_tile_image(br, xsize, ysize, bits)?;
Ok((
Transform::CrossColor {
bits,
width: xsize,
data,
},
xsize,
))
},
SUBTRACT_GREEN_TRANSFORM => Ok((Transform::SubtractGreen, xsize)),
COLOR_INDEXING_TRANSFORM => {
let num_colors = br.read_bits(8) + 1;
let bits = if num_colors > 16 {
0
} else if num_colors > 4 {
1
} else if num_colors > 2 {
2
} else {
3
};
let raw = decode_image_stream(br, num_colors, 1, false)?;
let palette = palette::expand_color_map(num_colors as usize, &raw, bits);
Ok((
Transform::ColorIndexing {
bits,
dst_width: xsize,
palette,
},
subsample_size(xsize, bits),
))
},
_ => Err(Error::InvalidBitstream {
codec: Codec::Lossless,
}),
}
}
fn read_tile_image(br: &mut BitReader<'_>, xsize: u32, ysize: u32, bits: u32) -> Result<Vec<u32>> {
let cols = subsample_size(xsize, bits);
let rows = subsample_size(ysize, bits);
decode_image_stream(br, cols, rows, false)
}
fn read_huffman_codes(
br: &mut BitReader<'_>,
xsize: u32,
ysize: u32,
color_cache_bits: u32,
allow_recursion: bool,
) -> Result<(Vec<HuffmanGroup>, Option<EntropyImage>)> {
let (mapping, num_used, entropy) = if allow_recursion && br.read_bit() != 0 {
let bits = br.read_bits(3) + 2;
let entropy_cols = subsample_size(xsize, bits);
let entropy_rows = subsample_size(ysize, bits);
let entropy_argb = decode_image_stream(br, entropy_cols, entropy_rows, false)?;
let mut num_groups_max = 1u32;
let raw: Vec<u16> = entropy_argb
.iter()
.map(|&pixel| {
let group = ((pixel >> 8) & 0xffff) as u16;
num_groups_max = num_groups_max.max(u32::from(group) + 1);
group
})
.collect();
let mut mapping: Vec<Option<usize>> = vec![None; num_groups_max as usize];
let mut num_used = 0usize;
let data: Vec<u16> = raw
.iter()
.map(|&group| {
let slot = &mut mapping[group as usize];
let new = *slot.get_or_insert_with(|| {
let new = num_used;
num_used += 1;
new
});
new as u16
})
.collect();
(
mapping,
num_used,
Some(EntropyImage {
data,
xsize: entropy_cols,
bits,
}),
)
} else {
(vec![Some(0usize)], 1usize, None)
};
let cache_codes = if color_cache_bits > 0 {
1usize << color_cache_bits
} else {
0
};
let green_alphabet = ALPHABET_SIZE[0] + cache_codes;
let mut groups: Vec<Option<HuffmanGroup>> = (0..num_used).map(|_| None).collect();
for slot in &mapping {
let group = HuffmanGroup {
green: read_huffman_code(br, green_alphabet).ok_or(Error::InvalidBitstream {
codec: Codec::Lossless,
})?,
red: read_huffman_code(br, ALPHABET_SIZE[1]).ok_or(Error::InvalidBitstream {
codec: Codec::Lossless,
})?,
blue: read_huffman_code(br, ALPHABET_SIZE[2]).ok_or(Error::InvalidBitstream {
codec: Codec::Lossless,
})?,
alpha: read_huffman_code(br, ALPHABET_SIZE[3]).ok_or(Error::InvalidBitstream {
codec: Codec::Lossless,
})?,
dist: read_huffman_code(br, ALPHABET_SIZE[4]).ok_or(Error::InvalidBitstream {
codec: Codec::Lossless,
})?,
};
if let &Some(new) = slot {
groups[new] = Some(group);
}
}
let groups = groups
.into_iter()
.map(|g| {
g.ok_or(Error::InvalidBitstream {
codec: Codec::Lossless,
})
})
.collect::<Result<Vec<_>>>()?;
Ok((groups, entropy))
}
pub(crate) struct PixelCore {
pub(crate) argb: Vec<u32>,
pub(crate) pos: usize,
pub(crate) total: usize,
pub(crate) width: u32,
pub(crate) cache: Option<ColorCache>,
pub(crate) cache_bits: u32,
pub(crate) groups: Vec<HuffmanGroup>,
pub(crate) entropy: Option<EntropyImage>,
}
impl PixelCore {
pub(crate) fn new(
width: u32,
total: usize,
cache_bits: u32,
groups: Vec<HuffmanGroup>,
entropy: Option<EntropyImage>,
) -> Self {
let cache = (cache_bits > 0).then(|| ColorCache::new(cache_bits));
Self {
argb: vec![0u32; total],
pos: 0,
total,
width,
cache,
cache_bits,
groups,
entropy,
}
}
}
fn decode_image_data(
br: &mut BitReader<'_>,
width: u32,
total: usize,
cache_bits: u32,
groups: Vec<HuffmanGroup>,
entropy: Option<EntropyImage>,
) -> Result<Vec<u32>> {
let mut core = PixelCore::new(width, total, cache_bits, groups, entropy);
while core.pos < core.total {
if !decode_one(br, &mut core)? {
return Err(Error::Truncated);
}
}
Ok(core.argb)
}
pub(crate) fn decode_one(br: &mut BitReader<'_>, st: &mut PixelCore) -> Result<bool> {
let cache_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES;
let group = match st.entropy.as_ref() {
None => st.groups.first(),
Some(e) => {
let pos = st.pos as u32;
let idx = select_group(Some(e), pos % st.width, pos / st.width).ok_or(
Error::InvalidBitstream {
codec: Codec::Lossless,
},
)?;
st.groups.get(idx)
},
}
.ok_or(Error::InvalidBitstream {
codec: Codec::Lossless,
})?;
let code = group.green.read_symbol(br) as usize;
if code < NUM_LITERAL_CODES {
let red = group.red.read_symbol(br);
let blue = group.blue.read_symbol(br);
let alpha = group.alpha.read_symbol(br);
if br.is_eos() {
return Ok(false);
}
let pixel = (u32::from(alpha) << 24)
| (u32::from(red) << 16)
| ((code as u32) << 8)
| u32::from(blue);
st.argb[st.pos] = pixel;
if let Some(c) = st.cache.as_mut() {
c.insert(pixel);
}
st.pos += 1;
} else if code < cache_limit {
let length = read_prefix_value((code - NUM_LITERAL_CODES) as u32, br) as usize;
let dist_symbol = group.dist.read_symbol(br);
let dist_code = read_prefix_value(u32::from(dist_symbol), br);
if br.is_eos() {
return Ok(false);
}
let dist = plane_code_to_distance(st.width, dist_code) as usize;
if dist > st.pos || st.pos + length > st.total {
return Err(Error::InvalidBitstream {
codec: Codec::Lossless,
});
}
let end = st.pos + length;
if let Some(c) = st.cache.as_mut() {
while st.pos < end {
let value = st.argb[st.pos - dist];
st.argb[st.pos] = value;
c.insert(value);
st.pos += 1;
}
} else {
while st.pos < end {
st.argb[st.pos] = st.argb[st.pos - dist];
st.pos += 1;
}
}
} else {
if br.is_eos() {
return Ok(false);
}
let key = code - cache_limit;
let cache = st.cache.as_mut().ok_or(Error::InvalidBitstream {
codec: Codec::Lossless,
})?;
if key >= (1usize << st.cache_bits) {
return Err(Error::InvalidBitstream {
codec: Codec::Lossless,
});
}
let value = cache.get(key);
st.argb[st.pos] = value;
cache.insert(value);
st.pos += 1;
}
Ok(true)
}
fn select_group(entropy: Option<&EntropyImage>, x: u32, y: u32) -> Option<usize> {
entropy.map_or(Some(0), |e| {
let block = (y >> e.bits) * e.xsize + (x >> e.bits);
e.data.get(block as usize).map(|&group| group as usize)
})
}
#[cfg(test)]
mod tests {
use super::{PixelCore, decode, decode_one, parse_top_level};
use crate::lossless::bit_io::reader::BitReader;
#[derive(Default)]
struct BitBuf {
bytes: Vec<u8>,
acc: u32,
n: u32,
}
impl BitBuf {
fn put(&mut self, value: u32, bits: u32) {
self.acc |= value << self.n;
self.n += bits;
while self.n >= 8 {
self.bytes.push((self.acc & 0xff) as u8);
self.acc >>= 8;
self.n -= 8;
}
}
fn finish(mut self) -> Vec<u8> {
if self.n > 0 {
self.bytes.push((self.acc & 0xff) as u8);
}
self.bytes
}
}
fn put_simple_code(b: &mut BitBuf, symbol: u32) {
b.put(1, 1); b.put(0, 1); if symbol <= 1 {
b.put(0, 1); b.put(symbol, 1);
} else {
b.put(1, 1); b.put(symbol, 8);
}
}
fn solid_stream(width: u32, height: u32, r: u32, g: u32, b: u32, a: u32) -> Vec<u8> {
let mut buf = BitBuf::default();
buf.put(0x2f, 8); buf.put(width - 1, 14);
buf.put(height - 1, 14);
buf.put(0, 1); buf.put(0, 3); buf.put(0, 1); buf.put(0, 1); buf.put(0, 1); put_simple_code(&mut buf, g); put_simple_code(&mut buf, r); put_simple_code(&mut buf, b); put_simple_code(&mut buf, a); put_simple_code(&mut buf, 0); buf.finish()
}
#[test]
fn decodes_a_1x1_pixel() {
let stream = solid_stream(1, 1, 50, 100, 200, 255);
let out = decode(&stream).unwrap();
assert_eq!((out.width, out.height), (1, 1));
assert_eq!(out.argb, vec![0xff32_64c8]); }
fn argb(a: u32, r: u32, g: u32, b: u32) -> u32 {
(a << 24) | (r << 16) | (g << 8) | b
}
#[test]
fn decodes_a_solid_block() {
let stream = solid_stream(3, 2, 10, 20, 30, 40);
let out = decode(&stream).unwrap();
assert_eq!((out.width, out.height), (3, 2));
assert_eq!(out.argb, vec![argb(40, 10, 20, 30); 6]);
}
#[test]
fn rejects_bad_signature() {
let mut stream = solid_stream(1, 1, 0, 0, 0, 0);
stream[0] = 0x00;
assert!(decode(&stream).is_err());
}
fn solid_stream_subtract_green(
width: u32,
height: u32,
r: u32,
g: u32,
b: u32,
a: u32,
) -> Vec<u8> {
let mut buf = BitBuf::default();
buf.put(0x2f, 8);
buf.put(width - 1, 14);
buf.put(height - 1, 14);
buf.put(0, 1); buf.put(0, 3); buf.put(1, 1); buf.put(2, 2); buf.put(0, 1); buf.put(0, 1); buf.put(0, 1); put_simple_code(&mut buf, g);
put_simple_code(&mut buf, r);
put_simple_code(&mut buf, b);
put_simple_code(&mut buf, a);
put_simple_code(&mut buf, 0); buf.finish()
}
#[test]
fn decodes_with_subtract_green() {
let stream = solid_stream_subtract_green(2, 1, 10, 100, 20, 255);
let out = decode(&stream).unwrap();
assert_eq!((out.width, out.height), (2, 1));
assert_eq!(out.argb, vec![argb(255, 110, 100, 120); 2]);
}
fn put_simple_code2(b: &mut BitBuf, sym0: u32, sym1: u32) {
b.put(1, 1); b.put(1, 1); if sym0 <= 1 {
b.put(0, 1);
b.put(sym0, 1);
} else {
b.put(1, 1);
b.put(sym0, 8);
}
b.put(sym1, 8); }
fn meta_huffman_amplify_stream() -> Vec<u8> {
let mut buf = BitBuf::default();
buf.put(0x2f, 8);
buf.put(0, 14); buf.put(0, 14); buf.put(0, 1); buf.put(0, 3); buf.put(0, 1); buf.put(0, 1); buf.put(1, 1); buf.put(0, 3); buf.put(0, 1); put_simple_code(&mut buf, 255); put_simple_code(&mut buf, 255); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); for _ in 0..65536u32 {
put_simple_code(&mut buf, 0);
put_simple_code(&mut buf, 0);
put_simple_code(&mut buf, 0);
put_simple_code(&mut buf, 0);
put_simple_code(&mut buf, 0);
}
buf.finish()
}
#[test]
fn meta_huffman_group_alloc_is_bounded_by_used_groups() {
let stream = meta_huffman_amplify_stream();
let mut br = BitReader::new(&stream);
let (_, parsed) = parse_top_level(&mut br).unwrap();
assert_eq!(parsed.groups.len(), 1);
}
fn meta_huffman_multigroup_stream() -> Vec<u8> {
let mut buf = BitBuf::default();
buf.put(0x2f, 8);
buf.put(7, 14); buf.put(0, 14); buf.put(0, 1); buf.put(0, 3); buf.put(0, 1); buf.put(0, 1); buf.put(1, 1); buf.put(0, 3); buf.put(0, 1); put_simple_code2(&mut buf, 1, 3); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); buf.put(1, 1); buf.put(0, 1); let mut emit_group = |g: u32, r: u32, b: u32, a: u32| {
put_simple_code(&mut buf, g);
put_simple_code(&mut buf, r);
put_simple_code(&mut buf, b);
put_simple_code(&mut buf, a);
put_simple_code(&mut buf, 0); };
emit_group(0, 0, 0, 0); emit_group(20, 21, 22, 23); emit_group(0, 0, 0, 0); emit_group(30, 31, 32, 33); buf.finish()
}
#[test]
fn meta_huffman_remaps_non_contiguous_groups() {
let stream = meta_huffman_multigroup_stream();
let out = decode(&stream).unwrap();
assert_eq!((out.width, out.height), (8, 1));
let x = argb(33, 31, 30, 32);
let y = argb(23, 21, 20, 22);
assert_eq!(out.argb, vec![x, x, x, x, y, y, y, y]);
}
fn duplicate_subtract_green_stream() -> Vec<u8> {
let mut buf = BitBuf::default();
buf.put(0x2f, 8);
buf.put(0, 14); buf.put(0, 14); buf.put(0, 1); buf.put(0, 3); buf.put(1, 1); buf.put(2, 2); buf.put(1, 1); buf.put(2, 2); buf.put(0, 1); buf.put(0, 1); buf.put(0, 1); put_simple_code(&mut buf, 10);
put_simple_code(&mut buf, 20);
put_simple_code(&mut buf, 30);
put_simple_code(&mut buf, 40);
put_simple_code(&mut buf, 0);
buf.finish()
}
#[test]
fn rejects_a_repeated_transform_type() {
let stream = duplicate_subtract_green_stream();
assert!(decode(&stream).is_err());
}
fn palette_stream(
dst_width: u32,
num_colors: u32,
cmap: (u32, u32, u32, u32),
main_green: u32,
) -> Vec<u8> {
let (cg, cr, cb, ca) = cmap;
let mut buf = BitBuf::default();
buf.put(0x2f, 8);
buf.put(dst_width - 1, 14);
buf.put(0, 14); buf.put(0, 1); buf.put(0, 3); buf.put(1, 1); buf.put(3, 2); buf.put(num_colors - 1, 8);
buf.put(0, 1); put_simple_code(&mut buf, cg);
put_simple_code(&mut buf, cr);
put_simple_code(&mut buf, cb);
put_simple_code(&mut buf, ca);
put_simple_code(&mut buf, 0); buf.put(0, 1); buf.put(0, 1); buf.put(0, 1); put_simple_code(&mut buf, main_green);
put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); buf.finish()
}
#[test]
fn palette_16_colors_uses_two_index_bundling() {
let stream = palette_stream(2, 16, (1, 1, 1, 1), 0x21);
let out = decode(&stream).unwrap();
assert_eq!((out.width, out.height), (2, 1));
assert_eq!(out.argb, vec![0x0202_0202, 0x0303_0303]);
}
#[test]
fn palette_2_colors_uses_eight_index_bundling() {
let stream = palette_stream(8, 2, (1, 1, 1, 1), 0x55);
let out = decode(&stream).unwrap();
assert_eq!((out.width, out.height), (8, 1));
let m0 = 0x0101_0101u32; let m1 = 0x0202_0202u32; assert_eq!(out.argb, vec![m1, m0, m1, m0, m1, m0, m1, m0]);
}
fn predictor_transform_bits_stream() -> Vec<u8> {
let mut buf = BitBuf::default();
buf.put(0x2f, 8);
buf.put(7, 14); buf.put(0, 14); buf.put(0, 1); buf.put(0, 3); buf.put(1, 1); buf.put(0, 2); buf.put(0, 3); buf.put(0, 1); put_simple_code2(&mut buf, 0, 1); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); buf.put(0, 1); buf.put(0, 1); buf.put(0, 1); buf.put(0, 1); buf.put(0, 1); put_simple_code(&mut buf, 10); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); put_simple_code(&mut buf, 0); buf.finish()
}
#[test]
fn predictor_transform_bits_is_sum_not_product() {
let stream = predictor_transform_bits_stream();
let out = decode(&stream).unwrap();
assert_eq!((out.width, out.height), (8, 1));
assert_eq!(
out.argb,
vec![
0xff00_0a00, 0xff00_1400, 0xff00_1e00, 0xff00_2800, 0xff00_3200, 0xff00_3c00, 0xff00_4600, 0xff00_5000, ]
);
}
fn put_lz77_green_code(b: &mut BitBuf) {
b.put(0, 1); b.put(0, 4); b.put(0, 3); b.put(1, 3); b.put(0, 3); b.put(1, 3); b.put(0, 1);
b.put(0, 1); b.put(1, 1);
b.put(127, 7); b.put(1, 1);
b.put(106, 7); b.put(0, 1); b.put(1, 1);
b.put(12, 7); }
fn lz77_literal_then_backref_stream() -> Vec<u8> {
let mut buf = BitBuf::default();
buf.put(0x2f, 8);
buf.put(1, 14); buf.put(0, 14); buf.put(0, 1); buf.put(0, 3); buf.put(0, 1); buf.put(0, 1); buf.put(0, 1); put_lz77_green_code(&mut buf);
put_simple_code(&mut buf, 4); put_simple_code(&mut buf, 8); put_simple_code(&mut buf, 255); put_simple_code(&mut buf, 1); buf.put(0, 1); buf.put(1, 1); buf.finish()
}
#[test]
fn literal_boundary_at_num_literal_codes() {
let stream = lz77_literal_then_backref_stream();
let out = decode(&stream).unwrap();
assert_eq!((out.width, out.height), (2, 1));
assert_eq!(out.argb, vec![0xff04_0008, 0xff04_0008]);
}
fn lz77_backref_out_of_range_stream() -> Vec<u8> {
let mut buf = BitBuf::default();
buf.put(0x2f, 8);
buf.put(0, 14); buf.put(0, 14); buf.put(0, 1); buf.put(0, 3); buf.put(0, 1); buf.put(0, 1); buf.put(0, 1); put_lz77_green_code(&mut buf);
put_simple_code(&mut buf, 4); put_simple_code(&mut buf, 8); put_simple_code(&mut buf, 255); put_simple_code(&mut buf, 1); buf.put(1, 1); buf.finish()
}
#[test]
fn rejects_backreference_distance_past_window() {
let stream = lz77_backref_out_of_range_stream();
assert!(decode(&stream).is_err());
}
#[test]
fn decode_one_commits_exactly_one_literal_pixel() {
let stream = solid_stream(1, 1, 50, 100, 200, 255);
let mut br = BitReader::new(&stream);
let (_, parsed) = parse_top_level(&mut br).unwrap();
let mut core = PixelCore::new(
parsed.working_width,
parsed.total,
parsed.cache_bits,
parsed.groups,
parsed.entropy,
);
assert!(decode_one(&mut br, &mut core).unwrap());
assert_eq!(core.pos, 1);
assert_eq!(core.argb[0], 0xff32_64c8); }
}