use alloc::vec;
use alloc::vec::Vec;
use super::RegionBitmap;
use super::pattern::PatternDictionary;
use super::{CombinationOperator, RegionSegmentInfo, Template, parse_region_segment_info};
use crate::bitmap::Bitmap;
use crate::error::{ParseError, RegionError, Result};
use crate::gray_scale::{GrayScaleParams, decode_gray_scale_image};
use crate::reader::Reader;
pub(crate) fn decode(
header: &HalftoneRegionHeader<'_>,
pattern_dict: &PatternDictionary,
) -> Result<RegionBitmap> {
let region = &header.region_info;
let mut htreg = Bitmap::new_with(
region.width,
region.height,
region.x_location,
region.y_location,
header.flags.initial_pixel_color,
);
decode_into(header, pattern_dict, &mut htreg)?;
Ok(RegionBitmap {
bitmap: htreg,
combination_operator: region.combination_operator,
})
}
pub(crate) fn decode_into(
header: &HalftoneRegionHeader<'_>,
pattern_dict: &PatternDictionary,
htreg: &mut Bitmap,
) -> Result<()> {
let skip_bitmap = if header.flags.enable_skip {
Some(compute_skip_bitmap(header, pattern_dict, htreg)?)
} else {
None
};
let bits_per_pixel = (pattern_dict.patterns.len() as u32)
.saturating_sub(1)
.checked_ilog2()
.map_or(1, |n| n + 1);
let gs_params = GrayScaleParams {
use_mmr: header.flags.mmr,
bits_per_pixel,
width: header.grid_position_and_size.width,
height: header.grid_position_and_size.height,
template: header.flags.template,
skip_mask: skip_bitmap.as_deref(),
};
let gi = decode_gray_scale_image(header.data, &gs_params)?;
render_patterns(htreg, &gi, header, pattern_dict)?;
Ok(())
}
pub(crate) fn parse<'a>(reader: &mut Reader<'a>) -> Result<HalftoneRegionHeader<'a>> {
let region_info = parse_region_segment_info(reader)?;
let flags_byte = reader.read_byte().ok_or(ParseError::UnexpectedEof)?;
let mmr = flags_byte & 0x01 != 0;
let template = Template::from_byte(flags_byte >> 1);
let enable_skip = flags_byte & 0x08 != 0;
let combination_operator = CombinationOperator::from_value(flags_byte >> 4)?;
let initial_pixel_color = flags_byte & 0x80 != 0;
let flags = HalftoneRegionFlags {
mmr,
template,
enable_skip,
combination_operator,
initial_pixel_color,
};
let grid_width = reader.read_u32().ok_or(ParseError::UnexpectedEof)?;
let grid_height = reader.read_u32().ok_or(ParseError::UnexpectedEof)?;
let grid_horizontal_offset = reader.read_i32().ok_or(ParseError::UnexpectedEof)?;
let grid_vertical_offset = reader.read_i32().ok_or(ParseError::UnexpectedEof)?;
let grid_position_and_size = HalftoneGridPositionAndSize {
width: grid_width,
height: grid_height,
horizontal_offset: grid_horizontal_offset,
vertical_offset: grid_vertical_offset,
};
let grid_x_vector = reader.read_u16().ok_or(ParseError::UnexpectedEof)?;
let grid_y_vector = reader.read_u16().ok_or(ParseError::UnexpectedEof)?;
let grid_vector = HalftoneGridVector {
x_vector: grid_x_vector,
y_vector: grid_y_vector,
};
let data = reader.tail().ok_or(ParseError::UnexpectedEof)?;
Ok(HalftoneRegionHeader {
region_info,
flags,
grid_position_and_size,
grid_vector,
data,
})
}
#[derive(Debug, Clone)]
pub(crate) struct HalftoneRegionFlags {
pub(crate) mmr: bool,
pub(crate) template: Template,
pub(crate) enable_skip: bool,
pub(crate) combination_operator: CombinationOperator,
pub(crate) initial_pixel_color: bool,
}
#[derive(Debug, Clone)]
pub(crate) struct HalftoneGridPositionAndSize {
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) horizontal_offset: i32,
pub(crate) vertical_offset: i32,
}
#[derive(Debug, Clone)]
pub(crate) struct HalftoneGridVector {
pub(crate) x_vector: u16,
pub(crate) y_vector: u16,
}
#[derive(Debug, Clone)]
pub(crate) struct HalftoneRegionHeader<'a> {
pub(crate) region_info: RegionSegmentInfo,
pub(crate) flags: HalftoneRegionFlags,
pub(crate) grid_position_and_size: HalftoneGridPositionAndSize,
pub(crate) grid_vector: HalftoneGridVector,
pub(crate) data: &'a [u8],
}
struct GridCoords {
x: i64,
y: i64,
row_x: i64,
row_y: i64,
hrx: i64,
hry: i64,
}
impl GridCoords {
fn new(grid: &HalftoneGridPositionAndSize, vector: &HalftoneGridVector) -> Self {
Self {
x: grid.horizontal_offset as i64,
y: grid.vertical_offset as i64,
row_x: grid.horizontal_offset as i64,
row_y: grid.vertical_offset as i64,
hrx: vector.x_vector as i64,
hry: vector.y_vector as i64,
}
}
#[inline]
fn get(&self) -> (i32, i32) {
((self.x >> 8) as i32, (self.y >> 8) as i32)
}
#[inline]
fn advance_col(&mut self) {
self.x += self.hrx;
self.y -= self.hry;
}
#[inline]
fn advance_row(&mut self) {
self.row_x += self.hry;
self.row_y += self.hrx;
self.x = self.row_x;
self.y = self.row_y;
}
}
fn compute_skip_bitmap(
header: &HalftoneRegionHeader<'_>,
pattern_dict: &PatternDictionary,
htreg: &Bitmap,
) -> Result<Vec<u32>> {
let grid = &header.grid_position_and_size;
let pattern_width = pattern_dict.pattern_width as i32;
let pattern_height = pattern_dict.pattern_height as i32;
let region_width = htreg.width as i32;
let region_height = htreg.height as i32;
let stride = grid.width.div_ceil(32);
let mut hskip = vec![0_u32; (stride * grid.height) as usize];
let mut coords = GridCoords::new(grid, &header.grid_vector);
for m_g in 0..grid.height {
for n_g in 0..grid.width {
let (x, y) = coords.get();
let skip = (x + pattern_width <= 0)
|| (x >= region_width)
|| (y + pattern_height <= 0)
|| (y >= region_height);
if skip {
let word_idx = (m_g * stride + n_g / 32) as usize;
let bit_pos = 31 - (n_g % 32);
hskip[word_idx] |= 1 << bit_pos;
}
coords.advance_col();
}
coords.advance_row();
}
Ok(hskip)
}
fn render_patterns(
region: &mut Bitmap,
gi: &[u32],
header: &HalftoneRegionHeader<'_>,
pattern_dict: &PatternDictionary,
) -> Result<()> {
let grid = &header.grid_position_and_size;
let mut coords = GridCoords::new(grid, &header.grid_vector);
let mut gi_idx = 0;
for _ in 0..grid.height {
for _ in 0..grid.width {
let (x, y) = coords.get();
let pattern_index = gi[gi_idx] as usize;
gi_idx += 1;
let pattern = pattern_dict
.patterns
.get(pattern_index)
.ok_or(RegionError::InvalidDimension)?;
region.combine(pattern, x, y, header.flags.combination_operator);
coords.advance_col();
}
coords.advance_row();
}
Ok(())
}