use super::{
dictionary::SymbolDescriptor,
integer::BITMAP_BASE,
mq::{ArithmeticSnapshot, MqDecoder},
unsupported,
};
use crate::fallible::reserve_exact;
use crate::{Cancellation, Error, Limits, Result};
use std::mem;
const CONTEXT_COUNT: usize = 1024;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RefinementReference {
pub store_base: u64,
pub symbol: SymbolDescriptor,
}
#[derive(Clone, Copy, Debug)]
pub enum ReferenceStore<'r> {
Other(&'r [u8]),
Output,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RefinementRequest {
pub width: u32,
pub height: u32,
pub template: u8,
pub typical_prediction: bool,
pub reference_dx: i32,
pub reference_dy: i32,
pub reference: RefinementReference,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct RefinementProgress {
pub completed_bitmaps: u32,
pub rows_written: u64,
pub pixels_decoded: u64,
pub output_bytes_written: u64,
pub mq: Option<ArithmeticSnapshot>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RefinementReport {
pub target: SymbolDescriptor,
pub progress: RefinementProgress,
}
#[derive(Clone, Copy)]
struct Geometry {
reference_offset: u64,
reference_width: u32,
reference_height: u32,
reference_stride: usize,
target_stride: usize,
pixels: u64,
bytes: u64,
}
pub struct RefinementDecoder<'a, 'mq, C: Cancellation> {
mq: &'a mut MqDecoder<'mq>,
output: &'a mut Vec<u8>,
limits: &'a Limits,
cancellation: &'a C,
progress: RefinementProgress,
}
impl<'a, 'mq, C: Cancellation> RefinementDecoder<'a, 'mq, C> {
pub fn new(
mq: &'a mut MqDecoder<'mq>,
output: &'a mut Vec<u8>,
limits: &'a Limits,
cancellation: &'a C,
) -> Result<Self> {
Self::new_continuing(
mq,
output,
limits,
cancellation,
RefinementProgress::default(),
)
}
pub(crate) fn new_continuing(
mq: &'a mut MqDecoder<'mq>,
output: &'a mut Vec<u8>,
limits: &'a Limits,
cancellation: &'a C,
previous: RefinementProgress,
) -> Result<Self> {
if mq.context_count() < BITMAP_BASE + CONTEXT_COUNT {
return Err(Error::invalid("coding unit lacks the GR context range"));
}
Ok(Self {
mq,
output,
limits,
cancellation,
progress: previous,
})
}
pub fn progress(&self) -> RefinementProgress {
let mut progress = self.progress;
progress.mq = Some(self.mq.snapshot());
progress
}
pub fn mq_mut(&mut self) -> &mut MqDecoder<'mq> {
self.mq
}
fn check_cancelled(&self) -> Result<()> {
if self.cancellation.is_cancelled() {
Err(Error::cancelled())
} else {
Ok(())
}
}
fn cap(&self, resource: &'static str, maximum: u64, attempted: u64) -> Result<()> {
if attempted > maximum {
Err(Error::limit(resource, maximum, attempted))
} else {
Ok(())
}
}
fn geometry(&self, reference_size: u64, request: RefinementRequest) -> Result<Geometry> {
let next = u64::from(self.progress.completed_bitmaps) + 1;
self.cap("completed bitmaps", u64::from(u32::MAX), next)?;
if request.template != 1 {
return Err(unsupported("refinement template"));
}
if request.typical_prediction {
return Err(unsupported("TPGRON"));
}
let symbol = request.reference.symbol;
if request.width == 0 || request.height == 0 || symbol.width == 0 || symbol.height == 0 {
return Err(unsupported("zero bitmap dimension"));
}
let target_stride = u64::from(request.width).div_ceil(8);
let reference_stride = u64::from(symbol.width).div_ceil(8);
if u64::from(symbol.row_stride) != reference_stride {
return Err(Error::invalid("noncanonical reference row stride"));
}
let reference_bytes = reference_stride * u64::from(symbol.height);
if symbol.stored_bytes != reference_bytes {
return Err(Error::invalid("reference stored byte length"));
}
let reference_offset = request
.reference
.store_base
.checked_add(symbol.relative_store_offset)
.ok_or_else(|| Error::invalid("reference offset overflows u64"))?;
let reference_end = reference_offset
.checked_add(reference_bytes)
.ok_or_else(|| Error::invalid("reference end overflows u64"))?;
if reference_end > reference_size {
return Err(Error::invalid("reference outside its store"));
}
let pixels = u64::from(request.width) * u64::from(request.height);
let bytes = target_stride * u64::from(request.height);
self.cap("pixels per bitmap", self.limits.max_image_pixels, pixels)?;
let target_stride = target_stride as usize;
let reference_stride = reference_stride as usize;
self.limits
.check_allocation(target_stride as u64)
.map_err(|_| {
Error::limit(
"target row allocation",
self.limits.max_allocation_bytes,
target_stride as u64,
)
})?;
self.limits
.check_allocation(reference_stride as u64)
.map_err(|_| {
Error::limit(
"reference row allocation",
self.limits.max_allocation_bytes,
reference_stride as u64,
)
})?;
Ok(Geometry {
reference_offset,
reference_width: symbol.width,
reference_height: symbol.height,
reference_stride,
target_stride,
pixels,
bytes,
})
}
fn row(&self, length: usize) -> Result<Vec<u8>> {
let mut row = Vec::new();
let failed = self
.limits
.allocation_refused("refinement row bytes", length as u64);
reserve_exact(&mut row, length, failed)?;
row.resize(length, 0);
Ok(row)
}
fn reserve_output(&mut self, bytes: u64) -> Result<()> {
let attempted = (self.output.len() as u64).saturating_add(bytes);
self.limits.check_allocation(attempted).map_err(|_| {
Error::limit(
"refinement store bytes",
self.limits.max_allocation_bytes,
attempted,
)
})?;
self.output.try_reserve(bytes as usize).map_err(|_| {
self.limits
.allocation_refused("refinement store bytes", attempted)
})
}
pub fn decode_bitmap(
&mut self,
reference: ReferenceStore<'_>,
request: RefinementRequest,
) -> Result<RefinementReport> {
let reference_size = match reference {
ReferenceStore::Other(bytes) => bytes.len(),
ReferenceStore::Output => self.output.len(),
} as u64;
let geometry = self.geometry(reference_size, request)?;
self.reserve_output(geometry.bytes)?;
let start_offset = self.progress.output_bytes_written;
let mut previous = self.row(geometry.target_stride)?;
let mut current = self.row(geometry.target_stride)?;
let mut reference_rows = [
self.row(geometry.reference_stride)?,
self.row(geometry.reference_stride)?,
self.row(geometry.reference_stride)?,
];
let mut cached: [Option<i64>; 3] = [None; 3];
for y in 0..request.height {
self.check_cancelled()?;
current.fill(0);
let reference_y = i64::from(y) - i64::from(request.reference_dy);
let needed = [reference_y - 1, reference_y, reference_y + 1];
for row_id in needed {
let slot = cache_slot(row_id);
if row_id < 0
|| row_id >= i64::from(geometry.reference_height)
|| cached[slot] == Some(row_id)
{
continue;
}
let start = (geometry.reference_offset
+ row_id as u64 * geometry.reference_stride as u64)
as usize;
let store = match reference {
ReferenceStore::Other(bytes) => bytes,
ReferenceStore::Output => self.output.as_slice(),
};
reference_rows[slot]
.copy_from_slice(&store[start..start + geometry.reference_stride]);
cached[slot] = Some(row_id);
}
for x in 0..request.width {
let reference_x = i64::from(x) - i64::from(request.reference_dx);
let context = template1_context(
&previous,
¤t,
request.width,
&reference_rows,
&cached,
geometry.reference_width,
reference_x,
reference_y,
x,
);
let bit = self.mq.decode_bit(BITMAP_BASE + context)?;
if bit {
current[(x / 8) as usize] |= 0x80 >> (x % 8);
}
self.progress.pixels_decoded += 1;
}
self.output.extend_from_slice(¤t);
self.progress.output_bytes_written += current.len() as u64;
self.progress.rows_written += 1;
mem::swap(&mut previous, &mut current);
}
let target = SymbolDescriptor {
width: request.width,
height: request.height,
row_stride: geometry.target_stride as u32,
relative_store_offset: start_offset,
stored_bytes: geometry.bytes,
};
self.progress.completed_bitmaps += 1;
debug_assert_eq!(
self.progress.output_bytes_written - start_offset,
geometry.bytes
);
debug_assert_eq!(
geometry.pixels,
u64::from(request.width) * u64::from(request.height)
);
Ok(RefinementReport {
target,
progress: self.progress(),
})
}
}
fn packed_pixel(row: &[u8], x: i64, width: u32) -> usize {
if x < 0 || x >= i64::from(width) {
0
} else {
let x = x as usize;
usize::from(row[x / 8] & (0x80 >> (x % 8)) != 0)
}
}
fn reference_pixel(
rows: &[Vec<u8>; 3],
cached: &[Option<i64>; 3],
width: u32,
x: i64,
y: i64,
) -> usize {
let slot = cache_slot(y);
if cached[slot] == Some(y) {
packed_pixel(&rows[slot], x, width)
} else {
0
}
}
fn cache_slot(y: i64) -> usize {
y.rem_euclid(3) as usize
}
#[allow(clippy::too_many_arguments)]
fn template1_context(
target_above: &[u8],
target_current: &[u8],
target_width: u32,
reference_rows: &[Vec<u8>; 3],
cached: &[Option<i64>; 3],
reference_width: u32,
reference_x: i64,
reference_y: i64,
target_x: u32,
) -> usize {
let x = i64::from(target_x);
let pixels = [
packed_pixel(target_above, x - 1, target_width),
packed_pixel(target_above, x, target_width),
packed_pixel(target_above, x + 1, target_width),
packed_pixel(target_current, x - 1, target_width),
reference_pixel(
reference_rows,
cached,
reference_width,
reference_x,
reference_y - 1,
),
reference_pixel(
reference_rows,
cached,
reference_width,
reference_x - 1,
reference_y,
),
reference_pixel(
reference_rows,
cached,
reference_width,
reference_x,
reference_y,
),
reference_pixel(
reference_rows,
cached,
reference_width,
reference_x + 1,
reference_y,
),
reference_pixel(
reference_rows,
cached,
reference_width,
reference_x,
reference_y + 1,
),
reference_pixel(
reference_rows,
cached,
reference_width,
reference_x + 1,
reference_y + 1,
),
];
pixels
.into_iter()
.fold(0, |context, pixel| (context << 1) | pixel)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ErrorKind;
use crate::NeverCancel;
use crate::jbig2::iaid::IAID_BASE;
use crate::jbig2::mq::{CodedSpan, ContextBank, MqTable};
#[test]
fn the_bitmap_index_cannot_pass_u32_max() {
let limits = Limits::default();
let table = MqTable::standard();
let mut banks = ContextBank::new(IAID_BASE + 2, &limits).unwrap();
let bytes = [0, 0xff, 0xac];
let mut mq = MqDecoder::new(
(&bytes[..]).into(),
CodedSpan {
offset: 0,
length: 3,
},
&table,
&mut banks,
&limits,
)
.unwrap();
let mut sink = Vec::new();
let mut decoder =
RefinementDecoder::new(&mut mq, &mut sink, &limits, &NeverCancel).unwrap();
decoder.progress.completed_bitmaps = u32::MAX;
let symbol = SymbolDescriptor {
width: 1,
height: 1,
row_stride: 1,
relative_store_offset: 0,
stored_bytes: 1,
};
let request = RefinementRequest {
width: 1,
height: 1,
template: 1,
typical_prediction: false,
reference_dx: 0,
reference_dy: 0,
reference: RefinementReference {
store_base: 0,
symbol,
},
};
let error = decoder
.decode_bitmap(ReferenceStore::Other(&[0]), request)
.unwrap_err();
assert!(matches!(error, Error { kind: ErrorKind::LimitExceeded {
resource: "completed bitmaps",
limit,
attempted,
}, .. } if limit == u64::from(u32::MAX) && attempted == limit + 1));
}
#[test]
fn figure_13_each_of_ten_pixels_has_one_distinct_context_bit() {
let mappings = [
(0, 0, 0x80),
(0, 0, 0x40),
(0, 0, 0x20),
(1, 0, 0x80),
(2, 0, 0x40),
(2, 1, 0x80),
(2, 1, 0x40),
(2, 1, 0x20),
(2, 2, 0x40),
(2, 2, 0x20),
];
for (bit, &(plane, row, mask)) in mappings.iter().enumerate() {
let mut above = [0u8];
let mut current = [0u8];
let mut reference = [vec![0], vec![0], vec![0]];
let target = match plane {
0 => &mut above[0],
1 => &mut current[0],
_ => &mut reference[row][0],
};
*target = mask;
let actual = template1_context(
&above,
¤t,
3,
&reference,
&[Some(0), Some(1), Some(2)],
3,
1,
1,
1,
);
assert_eq!(actual, 1 << (9 - bit), "tap {bit}");
}
}
#[test]
fn figure_13_zero_extends_all_edges() {
let above = [0xff];
let current = [0xff];
let reference = [vec![0xff], vec![0xff], vec![0xff]];
let cached = [Some(0), Some(1), Some(2)];
let left = template1_context(&above, ¤t, 1, &reference, &cached, 1, 0, 1, 0);
assert_eq!(left & ((1 << 9) | (1 << 6)), 0);
assert_eq!(left & (1 << 7), 0);
for reference_x in [-2, 2] {
let context = template1_context(
&above,
¤t,
1,
&reference,
&cached,
1,
reference_x,
1,
0,
);
assert_eq!(context & 0x3f, 0);
}
let top = template1_context(&above, ¤t, 1, &reference, &cached, 1, 0, -1, 0);
assert_eq!(top & 0x3f, 1 << 1); let bottom = template1_context(&above, ¤t, 1, &reference, &cached, 1, 0, 3, 0);
assert_eq!(bottom & 0x3f, 1 << 5); }
#[test]
fn the_three_rows_around_any_reference_row_use_distinct_cache_slots() {
let extremes = [-(1_i64 << 31), (1_i64 << 32) + (1 << 31)];
for center in (-7..=7).chain(extremes) {
let mut slots = [center - 1, center, center + 1].map(super::cache_slot);
slots.sort_unstable();
assert_eq!(slots, [0, 1, 2], "row {center}");
}
}
}