use crate::arith::ArithState;
use crate::arith_iaid::ArithIaidCtx;
use crate::arith_int::ArithIntCtx;
use crate::error::{Jbig2Error, Result};
use crate::image::{ComposeOp, Jbig2Image};
use crate::symbol_dict::SymbolDict;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum RefCorner {
BottomLeft = 0,
TopLeft = 1,
BottomRight = 2,
TopRight = 3,
}
impl RefCorner {
pub fn from_u8(v: u8) -> Self {
match v & 3 {
0 => Self::BottomLeft,
1 => Self::TopLeft,
2 => Self::BottomRight,
3 => Self::TopRight,
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone)]
pub struct TextRegionParams {
pub sbhuff: bool,
pub sbrefine: bool,
pub sbdefpixel: bool,
pub sbcombop: ComposeOp,
pub transposed: bool,
pub refcorner: RefCorner,
pub sbdsoffset: i32,
pub sbnuminstances: u32,
pub logsbstrips: u8,
pub sbstrips: u32,
pub sbrtemplate: u8,
pub sbrat: [i8; 4],
}
impl TextRegionParams {
pub fn parse(data: &[u8]) -> Option<(Self, usize)> {
if data.len() < 2 {
return None;
}
let flags = u16::from_be_bytes([data[0], data[1]]);
let sbhuff = flags & 0x0001 != 0;
let sbrefine = (flags & 0x0002) != 0;
let logsbstrips = ((flags & 0x000C) >> 2) as u8;
let sbstrips = 1u32 << logsbstrips;
let refcorner = RefCorner::from_u8(((flags >> 4) & 3) as u8);
let transposed = (flags & 0x0040) != 0;
let sbcombop = match (flags >> 7) & 3 {
0 => ComposeOp::Or,
1 => ComposeOp::And,
2 => ComposeOp::Xor,
3 => ComposeOp::Xnor,
_ => ComposeOp::Or,
};
let sbdefpixel = (flags & 0x0200) != 0;
let raw_offset = ((flags >> 10) & 0x1F) as i32;
let sbdsoffset = if raw_offset > 15 { raw_offset - 32 } else { raw_offset };
let sbrtemplate = ((flags >> 15) & 1) as u8;
let mut offset = 2;
if sbhuff {
if data.len() < offset + 2 {
return None;
}
offset += 2;
}
let mut sbrat = [0i8; 4];
if !sbhuff && sbrefine && sbrtemplate == 0 {
if data.len() < offset + 4 {
return None;
}
for i in 0..4 {
sbrat[i] = data[offset + i] as i8;
}
offset += 4;
}
if data.len() < offset + 4 {
return None;
}
let sbnuminstances = u32::from_be_bytes([
data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
]);
offset += 4;
Some((
TextRegionParams {
sbhuff,
sbrefine,
sbdefpixel,
sbcombop,
transposed,
refcorner,
sbdsoffset,
sbnuminstances,
logsbstrips,
sbstrips,
sbrtemplate,
sbrat,
},
offset,
))
}
}
pub fn decode_text_region(
params: &TextRegionParams,
as_: &mut ArithState,
image: &mut Jbig2Image,
dicts: &[&SymbolDict],
sbnumsyms: u32,
) -> Result<()> {
if params.sbhuff {
return Err(Jbig2Error::UnsupportedFeature(
"huffman text region decoding".into(),
));
}
if params.sbdefpixel {
image.clear(1);
}
let mut iadt = ArithIntCtx::new();
let mut iafs = ArithIntCtx::new();
let mut iads = ArithIntCtx::new();
let mut iait = ArithIntCtx::new();
let mut iari = ArithIntCtx::new();
let sbsymcodelen = {
let mut n = 0u8;
while (1u64 << n) < sbnumsyms as u64 {
n += 1;
}
n
};
let mut iaid = ArithIaidCtx::new(sbsymcodelen)?;
let mut stript: i32 = 0;
let mut firsts: i32 = 0;
let mut ninstances: u32 = 0;
while ninstances < params.sbnuminstances {
let dt = iadt.decode(as_)?.unwrap_or(0);
stript += dt * params.sbstrips as i32;
let mut first_s = true;
let mut curs: i32 = 0;
loop {
if first_s {
let dfs = iafs.decode(as_)?.unwrap_or(0);
firsts += dfs;
curs = firsts;
first_s = false;
} else {
match iads.decode(as_)? {
None => break, Some(ids) => {
curs += ids + params.sbdsoffset;
}
}
}
let curt = if params.sbstrips == 1 {
0i32
} else {
iait.decode(as_)?.unwrap_or(0)
};
let t = stript + curt;
let id = iaid.decode(as_)?;
let ib = lookup_glyph(dicts, id);
let ri = if params.sbrefine {
iari.decode(as_)?.unwrap_or(0) != 0
} else {
false
};
let _ = ri;
if let Some(ref g) = ib {
if !params.transposed && params.refcorner as u8 > 1 {
curs += g.width as i32 - 1;
} else if params.transposed && (params.refcorner as u8 & 1) == 0 {
curs += g.height as i32 - 1;
}
}
let s = curs;
if let Some(ref g) = ib {
let (x, y) = compute_placement(params, s, t, g.width, g.height);
image.compose(g, x, y, params.sbcombop)?;
}
if let Some(ref g) = ib {
if !params.transposed && (params.refcorner as u8) < 2 {
curs += g.width as i32 - 1;
} else if params.transposed && (params.refcorner as u8 & 1) != 0 {
curs += g.height as i32 - 1;
}
}
ninstances += 1;
if ninstances >= params.sbnuminstances {
break;
}
}
}
Ok(())
}
fn lookup_glyph(dicts: &[&SymbolDict], id: u32) -> Option<Jbig2Image> {
let mut remaining = id;
for dict in dicts {
if remaining < dict.n_symbols() {
return dict.glyph(remaining).cloned();
}
remaining -= dict.n_symbols();
}
None
}
fn compute_placement(
params: &TextRegionParams,
s: i32,
t: i32,
w: u32,
h: u32,
) -> (i32, i32) {
let w = w as i32;
let h = h as i32;
if !params.transposed {
match params.refcorner {
RefCorner::TopLeft => (s, t),
RefCorner::TopRight => (s - w + 1, t),
RefCorner::BottomLeft => (s, t - h + 1),
RefCorner::BottomRight => (s - w + 1, t - h + 1),
}
} else {
match params.refcorner {
RefCorner::TopLeft => (t, s),
RefCorner::TopRight => (t - w + 1, s),
RefCorner::BottomLeft => (t, s - h + 1),
RefCorner::BottomRight => (t - w + 1, s - h + 1),
}
}
}