use alloc::vec;
use alloc::vec::Vec;
use super::{
AdaptiveTemplatePixel, RefinementTemplate, RegionBitmap, RegionSegmentInfo,
parse_refinement_at_pixels, parse_region_segment_info,
};
use crate::arithmetic_decoder::{ArithmeticDecoder, Context};
use crate::bitmap::Bitmap;
use crate::error::{DecodeError, ParseError, RegionError, Result, bail};
use crate::reader::Reader;
pub(crate) fn decode(
header: &GenericRefinementRegionHeader<'_>,
reference: &Bitmap,
) -> Result<RegionBitmap> {
let mut region = Bitmap::new_with(
header.region_info.width,
header.region_info.height,
header.region_info.x_location,
header.region_info.y_location,
false,
);
decode_into(header, reference, &mut region)?;
Ok(RegionBitmap {
bitmap: region,
combination_operator: header.region_info.combination_operator,
})
}
pub(crate) fn decode_into(
header: &GenericRefinementRegionHeader<'_>,
reference: &Bitmap,
region: &mut Bitmap,
) -> Result<()> {
let data = header.data;
if header.region_info.width > reference.width || header.region_info.height > reference.height {
bail!(RegionError::InvalidDimension);
}
let reference_dx = i32::try_from(reference.x_location)
.ok()
.and_then(|r: i32| {
i32::try_from(header.region_info.x_location)
.ok()
.and_then(|h| r.checked_sub(h))
})
.ok_or(DecodeError::Overflow)?;
let reference_dy = i32::try_from(reference.y_location)
.ok()
.and_then(|r: i32| {
i32::try_from(header.region_info.y_location)
.ok()
.and_then(|h| r.checked_sub(h))
})
.ok_or(DecodeError::Overflow)?;
let mut decoder = ArithmeticDecoder::new(data);
let num_context_bits = header.template.context_bits();
let mut contexts = vec![Context::default(); 1 << num_context_bits];
decode_bitmap(
&mut decoder,
&mut contexts,
region,
reference,
reference_dx,
reference_dy,
header.template,
&header.adaptive_template_pixels,
header.tpgron,
)?;
Ok(())
}
#[derive(Debug, Clone)]
pub(crate) struct GenericRefinementRegionHeader<'a> {
pub(crate) region_info: RegionSegmentInfo,
pub(crate) template: RefinementTemplate,
pub(crate) tpgron: bool,
pub(crate) adaptive_template_pixels: Vec<AdaptiveTemplatePixel>,
pub(crate) data: &'a [u8],
}
pub(crate) fn parse<'a>(reader: &mut Reader<'a>) -> Result<GenericRefinementRegionHeader<'a>> {
let region_info = parse_region_segment_info(reader)?;
let flags = reader.read_byte().ok_or(ParseError::UnexpectedEof)?;
let template = RefinementTemplate::from_byte(flags);
let tpgron = flags & 0x02 != 0;
let adaptive_template_pixels = if template == RefinementTemplate::Template0 {
parse_refinement_at_pixels(reader)?
} else {
Vec::new()
};
let data = reader.tail().ok_or(ParseError::UnexpectedEof)?;
Ok(GenericRefinementRegionHeader {
region_info,
template,
tpgron,
adaptive_template_pixels,
data,
})
}
pub(crate) fn decode_bitmap(
decoder: &mut ArithmeticDecoder<'_>,
contexts: &mut [Context],
region: &mut Bitmap,
reference: &Bitmap,
reference_dx: i32,
reference_dy: i32,
gr_template: RefinementTemplate,
adaptive_template_pixels: &[AdaptiveTemplatePixel],
tpgron: bool,
) -> Result<()> {
let width = region.width;
let height = region.height;
let mut ltp = false;
for y in 0..height {
if tpgron {
let sltp_context: u16 = match gr_template {
RefinementTemplate::Template0 => 0b0000000010000,
RefinementTemplate::Template1 => 0b0000001000,
};
let sltp = decoder.decode(&mut contexts[sltp_context as usize]);
ltp = ltp != (sltp != 0);
}
let mut decode_single =
|x: u32, decoder: &mut ArithmeticDecoder<'_>, region: &mut Bitmap| {
let context = gather_context(
region,
reference,
x,
y,
reference_dx,
reference_dy,
gr_template,
adaptive_template_pixels,
);
let pixel = decoder.decode(&mut contexts[context as usize]);
region.set_pixel(x, y, pixel != 0);
};
if !ltp {
for x in 0..width {
decode_single(x, decoder, region);
}
} else {
for x in 0..width {
let tpgrpix = tpgron && {
let ref_x = x as i32 - reference_dx;
let ref_y = y as i32 - reference_dy;
let mut all_same = true;
let center = get_pixel(reference, ref_x, ref_y);
for dy in -1..=1 {
for dx in -1..=1 {
if get_pixel(reference, ref_x + dx, ref_y + dy) != center {
all_same = false;
break;
}
}
}
all_same
};
if tpgrpix {
let ref_x = x as i32 - reference_dx;
let ref_y = y as i32 - reference_dy;
let tpgrval = get_pixel(reference, ref_x, ref_y);
region.set_pixel(x, y, tpgrval != 0);
} else {
decode_single(x, decoder, region);
}
}
}
}
Ok(())
}
fn gather_context(
region: &Bitmap,
reference: &Bitmap,
x: u32,
y: u32,
reference_dx: i32,
reference_dy: i32,
gr_template: RefinementTemplate,
adaptive_template_pixels: &[AdaptiveTemplatePixel],
) -> u16 {
let x = x as i32;
let y = y as i32;
let ref_x = x - reference_dx;
let ref_y = y - reference_dy;
match gr_template {
RefinementTemplate::Template0 => {
let at1 = adaptive_template_pixels[0];
let at2 = adaptive_template_pixels[1];
let mut context = 0_u16;
context = (context << 1) | get_pixel(region, x + at1.x as i32, y + at1.y as i32);
context = (context << 1) | get_pixel(region, x, y - 1);
context = (context << 1) | get_pixel(region, x + 1, y - 1);
context = (context << 1) | get_pixel(region, x - 1, y);
context =
(context << 1) | get_pixel(reference, ref_x + at2.x as i32, ref_y + at2.y as i32);
context = (context << 1) | get_pixel(reference, ref_x, ref_y - 1);
context = (context << 1) | get_pixel(reference, ref_x + 1, ref_y - 1);
context = (context << 1) | get_pixel(reference, ref_x - 1, ref_y);
context = (context << 1) | get_pixel(reference, ref_x, ref_y);
context = (context << 1) | get_pixel(reference, ref_x + 1, ref_y);
context = (context << 1) | get_pixel(reference, ref_x - 1, ref_y + 1);
context = (context << 1) | get_pixel(reference, ref_x, ref_y + 1);
context = (context << 1) | get_pixel(reference, ref_x + 1, ref_y + 1);
context
}
RefinementTemplate::Template1 => {
let mut context = 0_u16;
context = (context << 1) | get_pixel(region, x - 1, y - 1);
context = (context << 1) | get_pixel(region, x, y - 1);
context = (context << 1) | get_pixel(region, x + 1, y - 1);
context = (context << 1) | get_pixel(region, x - 1, y);
context = (context << 1) | get_pixel(reference, ref_x, ref_y - 1);
context = (context << 1) | get_pixel(reference, ref_x - 1, ref_y);
context = (context << 1) | get_pixel(reference, ref_x, ref_y);
context = (context << 1) | get_pixel(reference, ref_x + 1, ref_y);
context = (context << 1) | get_pixel(reference, ref_x, ref_y + 1);
context = (context << 1) | get_pixel(reference, ref_x + 1, ref_y + 1);
context
}
}
}
#[inline]
fn get_pixel(bitmap: &Bitmap, x: i32, y: i32) -> u16 {
if x < 0 || y < 0 || x >= bitmap.width as i32 || y >= bitmap.height as i32 {
0
} else if bitmap.get_pixel(x as u32, y as u32) {
1
} else {
0
}
}