use anyhow::{Context, Result};
use std::cmp::{self, max};
use std::io::Read;
use crate::helpers::here;
use super::bit_reader::BitReader;
use super::block_based_image::{AlignedBlock, BlockBasedImage};
use super::jpeg_position_state::JpegPositionState;
use super::lepton_format::LeptonHeader;
use super::thread_handoff::ThreadHandoff;
use crate::lepton_error::ExitCode;
use crate::consts::*;
use crate::helpers::*;
use super::jpeg_header::HuffTree;
pub fn read_scan<R: Read>(
lp: &mut LeptonHeader,
reader: &mut R,
thread_handoff: &mut Vec<ThreadHandoff>,
image_data: &mut [BlockBasedImage],
) -> Result<()> {
let mut bit_reader = BitReader::new(reader);
let mut state = JpegPositionState::new(&lp.jpeg_header, 0);
let mut do_handoff = true;
let mut sta = JPegDecodeStatus::DecodeInProgress;
while sta != JPegDecodeStatus::ScanCompleted {
let jf = &lp.jpeg_header;
state.reset_rstw(jf);
if jf.jpeg_type == JPegType::Sequential {
sta = decode_baseline_rst(
&mut state,
lp,
thread_handoff,
&mut bit_reader,
image_data,
&mut do_handoff,
)
.context(here!())?;
} else if jf.cs_to == 0 && jf.cs_sah == 0 {
jf.verify_huffman_table(true, false).context(here!())?;
let mut last_dc = [0i16; 4];
while sta == JPegDecodeStatus::DecodeInProgress {
let current_block = image_data[state.get_cmp()].get_block_mut(state.get_dpos());
if do_handoff {
crystallize_thread_handoff(&state, lp, &bit_reader, thread_handoff, last_dc);
do_handoff = false;
}
let coef = read_dc(&mut bit_reader, jf.get_huff_dc_tree(state.get_cmp()))?;
let v = coef.wrapping_add(last_dc[state.get_cmp()]);
last_dc[state.get_cmp()] = v;
current_block.set_coefficient_zigzag(0, v << jf.cs_sal);
let old_mcu = state.get_mcu();
sta = state.next_mcu_pos(jf);
if state.get_mcu() % lp.jpeg_header.mcuh == 0 && old_mcu != state.get_mcu() {
do_handoff = true;
}
}
} else {
return err_exit_code(
ExitCode::UnsupportedJpeg,
"progress must start with DC stage",
)
.context(here!());
}
bit_reader
.read_and_verify_fill_bits(&mut lp.pad_bit)
.context(here!())?;
if sta == JPegDecodeStatus::RestartIntervalExpired {
bit_reader.verify_reset_code().context(here!())?;
sta = JPegDecodeStatus::DecodeInProgress;
}
}
lp.scnc += 1; Ok(())
}
fn crystallize_thread_handoff<R: Read>(
state: &JpegPositionState,
lp: &LeptonHeader,
bit_reader: &BitReader<R>,
thread_handoff: &mut Vec<ThreadHandoff>,
lastdc: [i16; 4],
) {
let mcu_y = state.get_mcu() / lp.jpeg_header.mcuh;
let luma_mul = lp.jpeg_header.cmp_info[0].bcv / lp.jpeg_header.mcuv;
let (bits_already_read, byte_being_read) = bit_reader.overhang();
let pos = bit_reader.get_stream_position();
let retval = ThreadHandoff {
segment_offset_in_file: pos,
luma_y_start: luma_mul * mcu_y,
luma_y_end: luma_mul * (mcu_y + 1),
overhang_byte: byte_being_read,
num_overhang_bits: bits_already_read,
last_dc: lastdc,
segment_size: 0, };
thread_handoff.push(retval);
}
pub fn read_progressive_scan<R: Read>(
lp: &mut LeptonHeader,
reader: &mut R,
image_data: &mut [BlockBasedImage],
) -> Result<()> {
lp.max_sah = max(
lp.max_sah,
max(lp.jpeg_header.cs_sal, lp.jpeg_header.cs_sah),
);
let mut bit_reader = BitReader::new(reader);
let mut state = JpegPositionState::new(&lp.jpeg_header, 0);
let mut sta = JPegDecodeStatus::DecodeInProgress;
while sta != JPegDecodeStatus::ScanCompleted {
let jf = &lp.jpeg_header;
state.reset_rstw(jf);
if jf.cs_to == 0 {
if jf.cs_sah == 0 {
return err_exit_code(
ExitCode::UnsupportedJpeg,
"progress can't have two DC first stages",
)
.context(here!());
}
jf.verify_huffman_table(true, false).context(here!())?;
while sta == JPegDecodeStatus::DecodeInProgress {
let current_block = image_data[state.get_cmp()].get_block_mut(state.get_dpos());
let value = bit_reader.read(1)? as i16;
current_block.set_coefficient_zigzag(
0,
current_block
.get_coefficient_zigzag(0)
.wrapping_add(value << jf.cs_sal),
);
sta = state.next_mcu_pos(jf);
}
} else {
if jf.cs_from == 0 || jf.cs_to >= 64 || jf.cs_from >= jf.cs_to {
return err_exit_code(
ExitCode::UnsupportedJpeg,
format!(
"progressive encoding range was invalid {0} to {1}",
jf.cs_from, jf.cs_to
)
.as_str(),
);
}
jf.verify_huffman_table(false, true).context(here!())?;
if jf.cs_sah == 0 {
if jf.cs_cmpc != 1 {
return err_exit_code(
ExitCode::UnsupportedJpeg,
"Progressive AC encoding cannot be interleaved",
);
}
let mut block = [0; 64];
while sta == JPegDecodeStatus::DecodeInProgress {
let current_block = image_data[state.get_cmp()].get_block_mut(state.get_dpos());
if state.eobrun == 0 {
let eob = decode_ac_prg_fs(
&mut bit_reader,
jf.get_huff_ac_tree(state.get_cmp()),
&mut block,
&mut state,
jf.cs_from,
jf.cs_to,
)
.context(here!())?;
state
.check_optimal_eobrun(
eob == jf.cs_from,
jf.get_huff_ac_codes(state.get_cmp()),
)
.context(here!())?;
for bpos in jf.cs_from..eob {
current_block.set_coefficient_zigzag(
usize::from(bpos),
block[usize::from(bpos)] << jf.cs_sal,
);
}
}
sta = state.skip_eobrun(&jf).context(here!())?;
if sta == JPegDecodeStatus::DecodeInProgress {
sta = state.next_mcu_pos(jf);
}
}
} else {
let mut block = [0; 64];
while sta == JPegDecodeStatus::DecodeInProgress {
let current_block = image_data[state.get_cmp()].get_block_mut(state.get_dpos());
for bpos in jf.cs_from..jf.cs_to + 1 {
block[usize::from(bpos)] =
current_block.get_coefficient_zigzag(usize::from(bpos));
}
if state.eobrun == 0 {
let eob = decode_ac_prg_sa(
&mut bit_reader,
jf.get_huff_ac_tree(state.get_cmp()),
&mut block,
&mut state,
jf.cs_from,
jf.cs_to,
)
.context(here!())?;
state
.check_optimal_eobrun(
eob == jf.cs_from,
jf.get_huff_ac_codes(state.get_cmp()),
)
.context(here!())?;
} else {
decode_eobrun_sa(
&mut bit_reader,
&mut block,
&mut state,
jf.cs_from,
jf.cs_to,
)
.context(here!())?;
}
for bpos in jf.cs_from..jf.cs_to + 1 {
current_block.set_coefficient_zigzag(
usize::from(bpos),
current_block
.get_coefficient_zigzag(usize::from(bpos))
.wrapping_add(block[usize::from(bpos)] << jf.cs_sal),
);
}
sta = state.next_mcu_pos(jf);
}
}
}
bit_reader
.read_and_verify_fill_bits(&mut lp.pad_bit)
.context(here!())?;
if sta == JPegDecodeStatus::RestartIntervalExpired {
bit_reader.verify_reset_code().context(here!())?;
sta = JPegDecodeStatus::DecodeInProgress;
}
}
lp.scnc += 1; Ok(())
}
fn decode_baseline_rst<R: Read>(
state: &mut JpegPositionState,
lp: &mut LeptonHeader,
thread_handoff: &mut Vec<ThreadHandoff>,
bit_reader: &mut BitReader<R>,
image_data: &mut [BlockBasedImage],
do_handoff: &mut bool,
) -> Result<JPegDecodeStatus> {
lp.jpeg_header
.verify_huffman_table(true, true)
.context(here!())?;
let mut sta = JPegDecodeStatus::DecodeInProgress;
let mut lastdc = [0i16; 4];
while sta == JPegDecodeStatus::DecodeInProgress {
if *do_handoff {
crystallize_thread_handoff(state, lp, bit_reader, thread_handoff, lastdc);
*do_handoff = false;
}
if !bit_reader.is_eof() {
lp.max_dpos[state.get_cmp()] = cmp::max(state.get_dpos(), lp.max_dpos[state.get_cmp()]);
}
let mut block = [0i16; 64];
let eob = decode_block_seq(
bit_reader,
&lp.jpeg_header.get_huff_dc_tree(state.get_cmp()),
&lp.jpeg_header.get_huff_ac_tree(state.get_cmp()),
&mut block,
)?;
if eob > 1 && (block[eob - 1] == 0) {
return err_exit_code(
ExitCode::UnsupportedJpeg,
"cannot encode image with eob after last 0",
);
}
block[0] = block[0].wrapping_add(lastdc[state.get_cmp()]);
lastdc[state.get_cmp()] = block[0];
let mut zzblock = [0i16; 64];
for bpos in 0..eob {
AlignedBlock::set_coefficient_zigzag_block(&mut zzblock, bpos as u8, block[bpos]);
}
image_data[state.get_cmp()].set_block_data(state.get_dpos(), &zzblock);
let old_mcu = state.get_mcu();
sta = state.next_mcu_pos(&lp.jpeg_header);
if state.get_mcu() % lp.jpeg_header.mcuh == 0 && old_mcu != state.get_mcu() {
*do_handoff = true;
}
if bit_reader.is_eof() {
sta = JPegDecodeStatus::ScanCompleted;
lp.early_eof_encountered = true;
}
}
return Ok(sta);
}
fn decode_block_seq<R: Read>(
bit_reader: &mut BitReader<R>,
dctree: &HuffTree,
actree: &HuffTree,
block: &mut [i16; 64],
) -> Result<usize> {
let mut eob = 64;
block[0] = read_dc(bit_reader, dctree)?;
let mut eof_fixup = false;
let mut bpos: usize = 1;
while bpos < 64 {
if let Some((z, coef)) = read_coef(bit_reader, actree)? {
if (z + bpos) >= 64 {
eof_fixup = true;
break;
}
for _i in 0..z {
block[bpos] = 0;
bpos += 1;
}
block[bpos] = coef;
bpos += 1;
} else {
eob = bpos;
break;
}
}
if eof_fixup {
if !bit_reader.is_eof() {
return err_exit_code(
ExitCode::UnsupportedJpeg,
"If 0run is longer than the block must be truncated",
);
}
while bpos < eob {
block[bpos] = 0;
bpos += 1;
}
if eob > 0 {
block[eob - 1] = 1; }
}
return Ok(eob);
}
fn next_huff_code<R: Read>(bit_reader: &mut BitReader<R>, ctree: &HuffTree) -> Result<u8> {
let mut node: u16 = 0;
while node < 256 {
node = ctree.node[usize::from(node)][usize::from(bit_reader.read(1)?)];
}
if node == 0xffff {
err_exit_code(ExitCode::UnsupportedJpeg, "illegal Huffman code detected")
} else {
Ok((node - 256) as u8)
}
}
fn read_dc<R: Read>(bit_reader: &mut BitReader<R>, tree: &HuffTree) -> Result<i16> {
let (z, coef) = read_coef(bit_reader, tree)?.unwrap_or((0, 0));
if z != 0 {
err_exit_code(
ExitCode::UnsupportedJpeg,
"not expecting non-zero run in DC coefficient",
)
} else {
Ok(coef)
}
}
#[inline(always)]
fn read_coef<R: Read>(
bit_reader: &mut BitReader<R>,
tree: &HuffTree,
) -> Result<Option<(usize, i16)>> {
let hc = next_huff_code(bit_reader, tree)?;
if hc != 0 {
let z = usize::from(lbits(hc, 4));
let s = rbits(hc, 4);
let value = bit_reader.read(s)?;
Ok(Some((z, devli(s, value))))
} else {
Ok(None)
}
}
fn decode_ac_prg_fs<R: Read>(
bit_reader: &mut BitReader<R>,
actree: &HuffTree,
block: &mut [i16; 64],
state: &mut JpegPositionState,
from: u8,
to: u8,
) -> Result<u8> {
debug_assert!(state.eobrun == 0);
let mut bpos = from;
while bpos <= to {
let hc = next_huff_code(bit_reader, actree)?;
let l = lbits(hc, 4);
let r = rbits(hc, 4);
if (l == 15) || (r > 0) {
let mut z = l;
let s = r;
let n = bit_reader.read(s)?;
if (z + bpos) > to {
return err_exit_code(ExitCode::UnsupportedJpeg, "run is too long");
}
while z > 0 {
block[usize::from(bpos)] = 0;
z -= 1;
bpos += 1;
}
block[usize::from(bpos)] = devli(s, n); bpos += 1;
} else {
let s = l;
let n = bit_reader.read(s)? as u16;
state.eobrun = decode_eobrun_bits(s, n);
state.eobrun -= 1;
break;
}
}
return Ok(bpos);
}
fn decode_ac_prg_sa<R: Read>(
bit_reader: &mut BitReader<R>,
actree: &HuffTree,
block: &mut [i16; 64],
state: &mut JpegPositionState,
from: u8,
to: u8,
) -> Result<u8> {
debug_assert!(state.eobrun == 0);
let mut bpos = from;
let mut eob = to;
while bpos <= to {
let hc = next_huff_code(bit_reader, actree)?;
let l = lbits(hc, 4);
let r = rbits(hc, 4);
if (l == 15) || (r > 0) {
let mut z = l;
let s = r;
let v;
if s == 0 {
v = 0;
} else if s == 1 {
let n = bit_reader.read(1)?;
v = if n == 0 { -1 } else { 1 }; } else {
return err_exit_code(ExitCode::UnsupportedJpeg, "decoding error").context(here!());
}
loop {
if block[usize::from(bpos)] == 0 {
if z > 0 {
z -= 1;
} else {
block[usize::from(bpos)] = v;
bpos += 1;
break;
}
} else {
let n = bit_reader.read(1)? as i16;
block[usize::from(bpos)] = if block[usize::from(bpos)] > 0 { n } else { -n };
}
if bpos >= to {
return err_exit_code(ExitCode::UnsupportedJpeg, "decoding error")
.context(here!());
}
bpos += 1;
}
} else {
eob = bpos;
let s = l;
let n = bit_reader.read(s)? as u16;
state.eobrun = decode_eobrun_bits(s, n);
decode_eobrun_sa(bit_reader, block, state, bpos, to)?;
break;
}
}
return Ok(eob);
}
fn decode_eobrun_sa<R: Read>(
bit_reader: &mut BitReader<R>,
block: &mut [i16; 64],
state: &mut JpegPositionState,
from: u8,
to: u8,
) -> Result<()> {
debug_assert!(state.eobrun > 0);
for bpos in usize::from(from)..usize::from(to + 1) {
if block[bpos] != 0 {
let n = bit_reader.read(1)? as i16;
block[bpos] = if block[bpos] > 0 { n } else { -n };
}
}
state.eobrun -= 1;
Ok(())
}
fn decode_eobrun_bits(s: u8, n: u16) -> u16 {
n + (1 << s)
}