#[derive(Clone, Copy)]
struct QeEntry {
qe: u16, nmps: u8, nlps: u8, switch: u8, }
const QE_TABLE: [QeEntry; 47] = [
QeEntry {
qe: 0x5601,
nmps: 1,
nlps: 1,
switch: 1,
},
QeEntry {
qe: 0x3401,
nmps: 2,
nlps: 6,
switch: 0,
},
QeEntry {
qe: 0x1801,
nmps: 3,
nlps: 9,
switch: 0,
},
QeEntry {
qe: 0x0AC1,
nmps: 4,
nlps: 12,
switch: 0,
},
QeEntry {
qe: 0x0521,
nmps: 5,
nlps: 29,
switch: 0,
},
QeEntry {
qe: 0x0221,
nmps: 38,
nlps: 33,
switch: 0,
},
QeEntry {
qe: 0x5601,
nmps: 7,
nlps: 6,
switch: 1,
},
QeEntry {
qe: 0x5401,
nmps: 8,
nlps: 14,
switch: 0,
},
QeEntry {
qe: 0x4801,
nmps: 9,
nlps: 14,
switch: 0,
},
QeEntry {
qe: 0x3801,
nmps: 10,
nlps: 14,
switch: 0,
},
QeEntry {
qe: 0x3001,
nmps: 11,
nlps: 17,
switch: 0,
},
QeEntry {
qe: 0x2401,
nmps: 12,
nlps: 18,
switch: 0,
},
QeEntry {
qe: 0x1C01,
nmps: 13,
nlps: 20,
switch: 0,
},
QeEntry {
qe: 0x1601,
nmps: 29,
nlps: 21,
switch: 0,
},
QeEntry {
qe: 0x5601,
nmps: 15,
nlps: 14,
switch: 1,
},
QeEntry {
qe: 0x5401,
nmps: 16,
nlps: 14,
switch: 0,
},
QeEntry {
qe: 0x5101,
nmps: 17,
nlps: 15,
switch: 0,
},
QeEntry {
qe: 0x4801,
nmps: 18,
nlps: 16,
switch: 0,
},
QeEntry {
qe: 0x3801,
nmps: 19,
nlps: 17,
switch: 0,
},
QeEntry {
qe: 0x3401,
nmps: 20,
nlps: 18,
switch: 0,
},
QeEntry {
qe: 0x3001,
nmps: 21,
nlps: 19,
switch: 0,
},
QeEntry {
qe: 0x2801,
nmps: 22,
nlps: 19,
switch: 0,
},
QeEntry {
qe: 0x2401,
nmps: 23,
nlps: 20,
switch: 0,
},
QeEntry {
qe: 0x2201,
nmps: 24,
nlps: 21,
switch: 0,
},
QeEntry {
qe: 0x1C01,
nmps: 25,
nlps: 22,
switch: 0,
},
QeEntry {
qe: 0x1801,
nmps: 26,
nlps: 23,
switch: 0,
},
QeEntry {
qe: 0x1601,
nmps: 27,
nlps: 24,
switch: 0,
},
QeEntry {
qe: 0x1401,
nmps: 28,
nlps: 25,
switch: 0,
},
QeEntry {
qe: 0x1201,
nmps: 29,
nlps: 26,
switch: 0,
},
QeEntry {
qe: 0x1101,
nmps: 30,
nlps: 27,
switch: 0,
},
QeEntry {
qe: 0x0AC1,
nmps: 31,
nlps: 28,
switch: 0,
},
QeEntry {
qe: 0x09C1,
nmps: 32,
nlps: 29,
switch: 0,
},
QeEntry {
qe: 0x08A1,
nmps: 33,
nlps: 30,
switch: 0,
},
QeEntry {
qe: 0x0521,
nmps: 34,
nlps: 31,
switch: 0,
},
QeEntry {
qe: 0x0441,
nmps: 35,
nlps: 32,
switch: 0,
},
QeEntry {
qe: 0x02A1,
nmps: 36,
nlps: 33,
switch: 0,
},
QeEntry {
qe: 0x0221,
nmps: 37,
nlps: 34,
switch: 0,
},
QeEntry {
qe: 0x0141,
nmps: 38,
nlps: 35,
switch: 0,
},
QeEntry {
qe: 0x0111,
nmps: 39,
nlps: 36,
switch: 0,
},
QeEntry {
qe: 0x0085,
nmps: 40,
nlps: 37,
switch: 0,
},
QeEntry {
qe: 0x0049,
nmps: 41,
nlps: 38,
switch: 0,
},
QeEntry {
qe: 0x0025,
nmps: 42,
nlps: 39,
switch: 0,
},
QeEntry {
qe: 0x0015,
nmps: 43,
nlps: 40,
switch: 0,
},
QeEntry {
qe: 0x0009,
nmps: 44,
nlps: 41,
switch: 0,
},
QeEntry {
qe: 0x0005,
nmps: 45,
nlps: 42,
switch: 0,
},
QeEntry {
qe: 0x0001,
nmps: 45,
nlps: 43,
switch: 0,
},
QeEntry {
qe: 0x5601,
nmps: 46,
nlps: 46,
switch: 0,
},
];
const NUM_CONTEXTS: usize = 19;
pub struct MqEncoder {
pub output: Vec<u8>,
a: u32,
c: u32,
ct: i32,
b: u8,
cx: [(u8, u8); NUM_CONTEXTS],
}
impl MqEncoder {
pub fn new() -> Self {
let mut enc = Self {
output: Vec::new(),
a: 0x8000,
c: 0,
ct: 12,
b: 0,
cx: [(0u8, 0u8); NUM_CONTEXTS],
};
enc
}
pub fn encode(&mut self, d: u8, cx_idx: usize) {
let (state, mps) = self.cx[cx_idx];
let qe = QE_TABLE[state as usize].qe as u32;
self.a -= qe;
if d == mps {
if self.a < 0x8000 {
if self.a < qe {
self.a = qe;
}
let ns = QE_TABLE[state as usize].nmps;
self.cx[cx_idx] = (ns, mps);
self.renorm_e();
}
} else {
if self.a < qe {
self.c += self.a;
self.a = qe;
} else {
self.c += self.a;
self.a = qe;
if QE_TABLE[state as usize].switch != 0 {
let ns = QE_TABLE[state as usize].nlps;
self.cx[cx_idx] = (ns, 1 - mps);
} else {
let ns = QE_TABLE[state as usize].nlps;
self.cx[cx_idx] = (ns, mps);
}
}
self.renorm_e();
}
}
fn renorm_e(&mut self) {
loop {
self.a <<= 1;
self.c <<= 1;
self.ct -= 1;
if self.ct == 0 {
self.byte_out();
}
if self.a >= 0x8000 {
break;
}
}
}
fn byte_out(&mut self) {
let t = (self.c >> 19) as u8;
self.c &= 0x7FFFF;
self.ct = 8;
if self.b == 0xFF {
self.output.push(0xFF);
self.output.push(t & 0x7F);
self.ct = 7;
} else if t > 0x7F {
if let Some(last) = self.output.last_mut() {
*last += 1;
}
self.output.push(t - 0x80);
} else {
self.output.push(self.b);
}
self.b = t;
}
pub fn flush(mut self) -> Vec<u8> {
let temp_c = self.c + self.a - 1;
let mask = !(self.a - 1);
self.c = temp_c & mask;
if self.c > (temp_c ^ mask) {
self.c = temp_c | !(mask >> 1);
}
for _ in 0..2 {
self.c <<= self.ct;
self.ct -= 8;
self.byte_out();
}
self.output.push(self.b);
self.output
}
}
pub struct MqDecoder<'a> {
data: &'a [u8],
pos: usize,
a: u32,
c: u32,
ct: i32,
cx: [(u8, u8); NUM_CONTEXTS],
}
impl<'a> MqDecoder<'a> {
pub fn new(data: &'a [u8]) -> Self {
let mut dec = Self {
data,
pos: 0,
a: 0,
c: 0,
ct: 0,
cx: [(0, 0); NUM_CONTEXTS],
};
dec.init();
dec
}
pub fn init_standard_j2k_contexts(&mut self) {
self.cx[0] = (4, 0);
self.cx[17] = (3, 0);
self.cx[18] = (46, 0);
}
fn init(&mut self) {
self.c = ((self.current_byte() as u32) ^ 0xFF) << 16;
self.byte_in();
self.c <<= 7;
self.ct -= 7;
self.a = 0x8000;
}
fn current_byte(&self) -> u8 {
self.data.get(self.pos).copied().unwrap_or(0xFF)
}
fn next_byte(&self) -> u8 {
self.data.get(self.pos + 1).copied().unwrap_or(0xFF)
}
fn byte_in(&mut self) {
if self.current_byte() == 0xFF {
let b2 = self.next_byte();
if b2 > 0x8F {
self.ct = 8;
} else {
self.pos += 1;
self.c = self
.c
.wrapping_add(0xFE00)
.wrapping_sub((self.current_byte() as u32) << 9);
self.ct = 7;
}
} else {
self.pos += 1;
self.c = self
.c
.wrapping_add(0xFF00)
.wrapping_sub((self.current_byte() as u32) << 8);
self.ct = 8;
}
}
fn renorm_d(&mut self) {
loop {
if self.ct == 0 {
self.byte_in();
}
self.a <<= 1;
self.c <<= 1;
self.ct -= 1;
if self.a >= 0x8000 {
break;
}
}
}
pub fn decode(&mut self, cx_idx: usize) -> u8 {
let (state, mps) = self.cx[cx_idx];
let qe = QE_TABLE[state as usize].qe as u32;
self.a -= qe;
let d;
if (self.c >> 16) < self.a {
if self.a < 0x8000 {
d = if self.a < qe { 1 - mps } else { mps };
let ns = if self.a < qe {
if QE_TABLE[state as usize].switch != 0 {
self.cx[cx_idx].1 ^= 1;
}
QE_TABLE[state as usize].nlps
} else {
QE_TABLE[state as usize].nmps
};
self.cx[cx_idx].0 = ns;
self.renorm_d();
} else {
d = mps;
}
} else {
self.c -= self.a << 16;
d = if self.a < qe {
self.a = qe;
self.cx[cx_idx].0 = QE_TABLE[state as usize].nmps;
mps
} else {
self.a = qe;
if QE_TABLE[state as usize].switch != 0 {
self.cx[cx_idx].1 ^= 1;
}
self.cx[cx_idx].0 = QE_TABLE[state as usize].nlps;
1 - mps
};
self.renorm_d();
}
d
}
pub fn consumed_bytes(&self) -> usize {
self.pos.min(self.data.len())
}
pub fn debug_state(&self) -> (u32, u32, i32, usize, u8) {
let cur_byte = if self.pos < self.data.len() {
self.data[self.pos]
} else {
0xFF
};
(
self.a,
self.c,
self.ct,
self.pos.min(self.data.len()),
cur_byte,
)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum SigState {
Insignificant = 0,
Significant = 1,
}
mod ctx {
pub const ZERO: usize = 0; pub const SIG: [usize; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9]; pub const SIGN: [usize; 5] = [10, 11, 12, 13, 14]; pub const MAG: [usize; 3] = [15, 16, 17]; pub const CLEANUP: usize = 18; }
mod std_ctx {
pub const SIGN: [usize; 5] = [9, 10, 11, 12, 13];
pub const MAG: [usize; 3] = [14, 15, 16];
pub const RUN: usize = 17;
pub const UNIFORM: usize = 18;
}
pub fn encode_block(coeffs: &[i32], width: usize, height: usize) -> Vec<u8> {
let n = width * height;
debug_assert_eq!(coeffs.len(), n);
let max_mag = coeffs.iter().map(|&c| c.unsigned_abs()).max().unwrap_or(0);
if max_mag == 0 {
let mut enc = MqEncoder::new();
for _ in 0..n {
enc.encode(0, ctx::CLEANUP);
}
return enc.flush();
}
let num_bitplanes = (u32::BITS - max_mag.leading_zeros()) as usize;
let mags: Vec<u32> = coeffs.iter().map(|&c| c.unsigned_abs()).collect();
let signs: Vec<u8> = coeffs.iter().map(|&c| if c < 0 { 1 } else { 0 }).collect();
let mut sig = vec![SigState::Insignificant; n];
let mut enc = MqEncoder::new();
for bp in (0..num_bitplanes).rev() {
let threshold = 1u32 << bp;
for i in 0..n {
if sig[i] == SigState::Insignificant {
let ctx = significance_context(&sig, i, width, height);
if ctx > 0 {
let bit = ((mags[i] >> bp) & 1) as u8;
enc.encode(bit, ctx::SIG[ctx.min(8)]);
if bit == 1 {
sig[i] = SigState::Significant;
let sign_ctx = sign_context(&sig, &signs, i, width, height);
enc.encode(signs[i] ^ sign_ctx.1, sign_ctx.0);
}
}
}
}
for i in 0..n {
if sig[i] == SigState::Significant && mags[i] >= threshold * 2 {
let ctx = mag_refinement_context(&sig, i, width, height, bp, num_bitplanes);
let bit = ((mags[i] >> bp) & 1) as u8;
enc.encode(bit, ctx::MAG[ctx]);
}
}
for i in 0..n {
if sig[i] == SigState::Insignificant {
let ctx = significance_context(&sig, i, width, height);
if ctx == 0 {
let bit = ((mags[i] >> bp) & 1) as u8;
enc.encode(bit, ctx::CLEANUP);
if bit == 1 {
sig[i] = SigState::Significant;
enc.encode(signs[i], ctx::SIGN[0]);
}
}
}
}
}
enc.flush()
}
pub fn decode_block(data: &[u8], width: usize, height: usize, num_bitplanes: usize) -> Vec<i32> {
decode_block_with_consumed(data, width, height, num_bitplanes).0
}
pub fn decode_block_with_consumed(
data: &[u8],
width: usize,
height: usize,
num_bitplanes: usize,
) -> (Vec<i32>, usize) {
let n = width * height;
let mut mags = vec![0u32; n];
let mut signs = vec![0u8; n];
let mut sig = vec![SigState::Insignificant; n];
let mut dec = MqDecoder::new(data);
let debug_cl_stream = std::env::var("JPEG2000_DEBUG_CL_SIG_STREAM")
.ok()
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false);
let debug_cl_stream_max = std::env::var("JPEG2000_DEBUG_CL_SIG_STREAM_MAX")
.ok()
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(48usize);
let mut cl_stream: Vec<(usize, usize, usize, u8)> = Vec::new();
for bp in (0..num_bitplanes).rev() {
let threshold = 1u32 << bp;
for i in 0..n {
if sig[i] == SigState::Insignificant {
let ctx = significance_context(&sig, i, width, height);
if ctx > 0 {
let bit = dec.decode(ctx::SIG[ctx.min(8)]);
if bit == 1 {
mags[i] |= threshold;
sig[i] = SigState::Significant;
let sign_ctx = sign_context(&sig, &signs, i, width, height);
signs[i] = dec.decode(sign_ctx.0) ^ sign_ctx.1;
}
}
}
}
for i in 0..n {
if sig[i] == SigState::Significant && mags[i] >= threshold * 2 {
let ctx = mag_refinement_context(&sig, i, width, height, bp, num_bitplanes);
let bit = dec.decode(ctx::MAG[ctx]);
if bit == 1 {
mags[i] |= threshold;
}
}
}
for i in 0..n {
if sig[i] == SigState::Insignificant {
let ctx = significance_context(&sig, i, width, height);
if ctx == 0 {
let bit = dec.decode(ctx::CLEANUP);
if debug_cl_stream && cl_stream.len() < debug_cl_stream_max {
cl_stream.push((bp, i, ctx::CLEANUP, bit));
}
if bit == 1 {
mags[i] |= threshold;
sig[i] = SigState::Significant;
signs[i] = dec.decode(ctx::SIGN[0]);
}
}
}
}
}
let out = mags
.iter()
.zip(signs.iter())
.map(|(&m, &s)| if s == 0 { m as i32 } else { -(m as i32) })
.collect();
if debug_cl_stream {
eprintln!(
"[cl_sig_stream][legacy] w={} h={} num_bp={} samples={:?}",
width, height, num_bitplanes, cl_stream
);
}
(out, dec.consumed_bytes())
}
pub fn decode_block_standard_j2k(
data: &[u8],
width: usize,
height: usize,
num_bitplanes: usize,
) -> Vec<i32> {
decode_block_standard_j2k_with_probe(
data,
width,
height,
num_bitplanes,
StandardSubbandKind::Ll,
LlPassProbeConfig::default(),
)
}
#[derive(Clone, Copy, Debug, Default)]
pub struct LlPassProbeConfig {
pub disable_sp: bool,
pub disable_mr: bool,
pub disable_cl: bool,
}
fn for_each_stripe_index(width: usize, height: usize, mut action: impl FnMut(usize)) {
for base_row in (0..height).step_by(4) {
for c in 0..width {
for j in 0..4 {
let row = base_row + j;
if row >= height {
break;
}
action(row * width + c);
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StandardSubbandKind {
Ll,
Hl,
Lh,
Hh,
}
pub fn decode_block_standard_j2k_with_probe(
data: &[u8],
width: usize,
height: usize,
num_bitplanes: usize,
subband_kind: StandardSubbandKind,
ll_probe: LlPassProbeConfig,
) -> Vec<i32> {
let n = width * height;
let mut mags = vec![0u32; n];
let mut signs = vec![0u8; n];
let mut sig = vec![false; n]; let mut mag_refined = vec![false; n];
let mut sp_visit = vec![false; n]; let mut dec = MqDecoder::new(data);
dec.init_standard_j2k_contexts();
let trace = width == 64 && height == 64 && num_bitplanes >= 10;
let run_mode_enabled = std::env::var("JPEG2000_STDJK_ENABLE_RUNMODE")
.ok()
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(true);
let debug_cl_stream = std::env::var("JPEG2000_DEBUG_CL_SIG_STREAM")
.ok()
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false);
let debug_cl_stream_max = std::env::var("JPEG2000_DEBUG_CL_SIG_STREAM_MAX")
.ok()
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(48usize);
let mut cl_stream: Vec<(usize, usize, usize, u8)> = Vec::new(); let mut native_symbol_trace_count = 0usize;
let debug_cleanup_trace = std::env::var("JPEG2000_DEBUG_LL_CLEANUP_TRACE")
.ok()
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false);
for bp in (0..num_bitplanes).rev() {
let threshold = 1u32 << bp;
let mut cl_sig_count = 0usize;
let mut sp_sig_count = 0usize;
let mut cl_eligible_pixels = 0usize;
let mut run_eligible_cols = 0usize;
let mut run_agg_one_cols = 0usize;
let mut cl_sig_decode_attempts = 0usize;
for v in sp_visit.iter_mut() {
*v = false;
}
if !ll_probe.disable_sp {
for_each_stripe_index(width, height, |i| {
if !sig[i] {
let ctx_label = zero_coding_context_bool(&sig, i, width, height, subband_kind);
if ctx_label > 0 {
sp_visit[i] = true;
let bit = dec.decode(ctx_label);
if bit == 1 {
mags[i] |= threshold;
sig[i] = true;
sp_sig_count += 1;
let (sctx, flip) = sign_context_bool(&sig, &signs, i, width, height);
signs[i] = dec.decode(sctx) ^ flip;
}
}
}
});
}
if !ll_probe.disable_mr {
for_each_stripe_index(width, height, |i| {
if sig[i] && mags[i] >= threshold * 2 {
let ctx = mag_refinement_context_bool(&sig, &mag_refined, i, width, height);
let bit = dec.decode(std_ctx::MAG[ctx]);
if bit == 1 {
mags[i] |= threshold;
}
mag_refined[i] = true;
}
});
}
if !ll_probe.disable_cl {
let mut band_row = 0usize;
while band_row < height {
let band_end = (band_row + 4).min(height);
let band_h = band_end - band_row;
for c in 0..width {
let any_needs = (0..band_h).any(|j| {
let i = (band_row + j) * width + c;
!sig[i] && !sp_visit[i]
});
if !any_needs {
continue;
}
let run_eligible = run_mode_enabled
&& band_h == 4
&& (0..4).all(|j| {
let i = (band_row + j) * width + c;
!sig[i]
&& !sp_visit[i]
&& zero_coding_context_bool(&sig, i, width, height, subband_kind)
== 0
});
if run_eligible {
run_eligible_cols += 1;
}
if run_eligible {
if debug_cl_stream && native_symbol_trace_count < debug_cl_stream_max {
let (a_before, c_before, ct_before, pos_before, cur_byte_before) =
dec.debug_state();
eprintln!(
"[native_entropy_state][before_run_agg] bp={} idx={} ctx={} a=0x{:04X} c=0x{:08X} ct={} pos={} cur=0x{:02X}",
bp,
band_row * width + c,
std_ctx::RUN,
a_before,
c_before,
ct_before,
pos_before,
cur_byte_before
);
native_symbol_trace_count += 1;
}
let agg = dec.decode(std_ctx::RUN);
if debug_cl_stream && native_symbol_trace_count < debug_cl_stream_max {
let (a_after, c_after, ct_after, pos_after, cur_byte_after) =
dec.debug_state();
eprintln!(
"[native_entropy_state][after_run_agg] bp={} idx={} ctx={} bit={} a=0x{:04X} c=0x{:08X} ct={} pos={} cur=0x{:02X}",
bp,
band_row * width + c,
std_ctx::RUN,
agg,
a_after,
c_after,
ct_after,
pos_after,
cur_byte_after
);
native_symbol_trace_count += 1;
}
if debug_cl_stream && native_symbol_trace_count < debug_cl_stream_max {
eprintln!(
"[native_symbol_trace][cleanup_run_agg] bp={} idx={} ctx={} bit={} x={} y={}",
bp,
band_row * width + c,
std_ctx::RUN,
agg,
c,
band_row
);
native_symbol_trace_count += 1;
}
if agg == 0 {
continue;
}
run_agg_one_cols += 1;
let rp_hi = dec.decode(std_ctx::UNIFORM);
let rp_lo = dec.decode(std_ctx::UNIFORM);
let run_pos = ((rp_hi << 1) | rp_lo) as usize; if debug_cl_stream && native_symbol_trace_count < debug_cl_stream_max {
eprintln!(
"[native_symbol_trace][cleanup_run_pos] bp={} idx={} ctx={} run_pos={} x={} y={}",
bp,
band_row * width + c,
std_ctx::UNIFORM,
run_pos,
c,
band_row
);
native_symbol_trace_count += 1;
}
let first_i = (band_row + run_pos) * width + c;
mags[first_i] |= threshold;
sig[first_i] = true;
cl_sig_count += 1;
let (sctx, flip) = sign_context_bool(&sig, &signs, first_i, width, height);
signs[first_i] = dec.decode(sctx) ^ flip;
for j in (run_pos + 1)..band_h {
let i = (band_row + j) * width + c;
if !sig[i] && !sp_visit[i] {
cl_eligible_pixels += 1;
let ctx =
zero_coding_context_bool(&sig, i, width, height, subband_kind);
cl_sig_decode_attempts += 1;
let decode_ctx = ctx;
if debug_cl_stream
&& native_symbol_trace_count < debug_cl_stream_max
&& bp >= num_bitplanes.saturating_sub(1)
&& i <= 32
{
let (
a_before,
c_before,
ct_before,
pos_before,
cur_byte_before,
) = dec.debug_state();
eprintln!(
"[native_entropy_state][before_cleanup_sym] bp={} idx={} ctx={} a=0x{:04X} c=0x{:08X} ct={} pos={} cur=0x{:02X}",
bp,
i,
decode_ctx,
a_before,
c_before,
ct_before,
pos_before,
cur_byte_before
);
native_symbol_trace_count += 1;
}
let bit = dec.decode(decode_ctx);
if debug_cl_stream
&& native_symbol_trace_count < debug_cl_stream_max
&& bp >= num_bitplanes.saturating_sub(1)
&& i <= 32
{
let (a_after, c_after, ct_after, pos_after, cur_byte_after) =
dec.debug_state();
eprintln!(
"[native_entropy_state][after_cleanup_sym] bp={} idx={} ctx={} bit={} a=0x{:04X} c=0x{:08X} ct={} pos={} cur=0x{:02X}",
bp,
i,
decode_ctx,
bit,
a_after,
c_after,
ct_after,
pos_after,
cur_byte_after
);
native_symbol_trace_count += 1;
}
if debug_cl_stream
&& native_symbol_trace_count < debug_cl_stream_max
{
eprintln!(
"[native_symbol_trace][cleanup] bp={} idx={} ctx={} bit={} x={} y={} use_rl=0",
bp,
i,
decode_ctx,
bit,
i % width,
i / width
);
native_symbol_trace_count += 1;
}
if debug_cl_stream && cl_stream.len() < debug_cl_stream_max {
cl_stream.push((bp, i, decode_ctx, bit));
}
if bit == 1 {
mags[i] |= threshold;
sig[i] = true;
cl_sig_count += 1;
let (sctx2, flip2) =
sign_context_bool(&sig, &signs, i, width, height);
signs[i] = dec.decode(sctx2) ^ flip2;
}
}
}
} else {
for j in 0..band_h {
let i = (band_row + j) * width + c;
if !sig[i] && !sp_visit[i] {
cl_eligible_pixels += 1;
let ctx =
zero_coding_context_bool(&sig, i, width, height, subband_kind);
cl_sig_decode_attempts += 1;
let decode_ctx = ctx;
if debug_cl_stream
&& native_symbol_trace_count < debug_cl_stream_max
&& bp >= num_bitplanes.saturating_sub(1)
&& i <= 32
{
let (
a_before,
c_before,
ct_before,
pos_before,
cur_byte_before,
) = dec.debug_state();
eprintln!(
"[native_entropy_state][before_cleanup_sym] bp={} idx={} ctx={} a=0x{:04X} c=0x{:08X} ct={} pos={} cur=0x{:02X}",
bp,
i,
decode_ctx,
a_before,
c_before,
ct_before,
pos_before,
cur_byte_before
);
native_symbol_trace_count += 1;
}
let bit = dec.decode(decode_ctx);
if debug_cl_stream
&& native_symbol_trace_count < debug_cl_stream_max
&& bp >= num_bitplanes.saturating_sub(1)
&& i <= 32
{
let (a_after, c_after, ct_after, pos_after, cur_byte_after) =
dec.debug_state();
eprintln!(
"[native_entropy_state][after_cleanup_sym] bp={} idx={} ctx={} bit={} a=0x{:04X} c=0x{:08X} ct={} pos={} cur=0x{:02X}",
bp,
i,
decode_ctx,
bit,
a_after,
c_after,
ct_after,
pos_after,
cur_byte_after
);
native_symbol_trace_count += 1;
}
if debug_cl_stream
&& native_symbol_trace_count < debug_cl_stream_max
{
eprintln!(
"[native_symbol_trace][cleanup] bp={} idx={} ctx={} bit={} x={} y={} use_rl=0",
bp,
i,
decode_ctx,
bit,
i % width,
i / width
);
native_symbol_trace_count += 1;
}
if debug_cl_stream && cl_stream.len() < debug_cl_stream_max {
cl_stream.push((bp, i, decode_ctx, bit));
}
if bit == 1 {
mags[i] |= threshold;
sig[i] = true;
cl_sig_count += 1;
let (sctx, flip) =
sign_context_bool(&sig, &signs, i, width, height);
signs[i] = dec.decode(sctx) ^ flip;
}
}
}
}
}
band_row += 4;
}
}
if debug_cleanup_trace {
eprintln!(
"[ll_cleanup_trace] bp={} threshold={} sp_sig={} cl_sig={} cl_eligible_pixels={} cl_sig_decode_attempts={} run_eligible_cols={} run_agg_one_cols={} run_mode_enabled={}",
bp,
threshold,
sp_sig_count,
cl_sig_count,
cl_eligible_pixels,
cl_sig_decode_attempts,
run_eligible_cols,
run_agg_one_cols,
run_mode_enabled
);
}
if trace && bp >= num_bitplanes - 3 {
eprintln!(
"[decode_block_stdjk] bp={} threshold={} sp_sig={} cl_sig={}",
bp, threshold, sp_sig_count, cl_sig_count
);
}
}
let result: Vec<i32> = mags
.iter()
.zip(signs.iter())
.map(|(&m, &s)| if s == 0 { m as i32 } else { -(m as i32) })
.collect();
if debug_cl_stream {
eprintln!(
"[cl_sig_stream][standard] w={} h={} num_bp={} samples={:?}",
width, height, num_bitplanes, cl_stream
);
}
if width == 64 && height == 64 && trace {
eprintln!("[decode_stdjk_result] coeff[0..8]: {:?}", &result[0..8]);
eprintln!("[decode_stdjk_result] coeff[64..72]: {:?}", &result[64..72]);
}
result
}
fn significance_context_bool(sig: &[bool], idx: usize, w: usize, h: usize) -> usize {
let nb = neighbours(idx, w, h);
nb.iter()
.filter_map(|&n| n)
.filter(|&n| sig[n])
.count()
.min(8)
}
#[rustfmt::skip]
const ZERO_CTX_LL_LH_LOOKUP: [usize; 256] = [
0, 3, 1, 3, 5, 7, 6, 7, 1, 3, 2, 3, 6, 7, 6, 7, 5, 7, 6, 7, 8, 8, 8, 8, 6,
7, 6, 7, 8, 8, 8, 8, 1, 3, 2, 3, 6, 7, 6, 7, 2, 3, 2, 3, 6, 7, 6, 7, 6, 7,
6, 7, 8, 8, 8, 8, 6, 7, 6, 7, 8, 8, 8, 8, 3, 4, 3, 4, 7, 7, 7, 7, 3, 4, 3,
4, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 3, 4, 3, 4,
7, 7, 7, 7, 3, 4, 3, 4, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 8,
8, 8, 8, 1, 3, 2, 3, 6, 7, 6, 7, 2, 3, 2, 3, 6, 7, 6, 7, 6, 7, 6, 7, 8, 8,
8, 8, 6, 7, 6, 7, 8, 8, 8, 8, 2, 3, 2, 3, 6, 7, 6, 7, 2, 3, 2, 3, 6, 7, 6,
7, 6, 7, 6, 7, 8, 8, 8, 8, 6, 7, 6, 7, 8, 8, 8, 8, 3, 4, 3, 4, 7, 7, 7, 7,
3, 4, 3, 4, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 3,
4, 3, 4, 7, 7, 7, 7, 3, 4, 3, 4, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7,
7, 7, 8, 8, 8, 8,
];
#[rustfmt::skip]
const ZERO_CTX_HL_LOOKUP: [usize; 256] = [
0, 5, 1, 6, 3, 7, 3, 7, 1, 6, 2, 6, 3, 7, 3, 7, 3, 7, 3, 7, 4, 7, 4, 7, 3,
7, 3, 7, 4, 7, 4, 7, 1, 6, 2, 6, 3, 7, 3, 7, 2, 6, 2, 6, 3, 7, 3, 7, 3, 7,
3, 7, 4, 7, 4, 7, 3, 7, 3, 7, 4, 7, 4, 7, 5, 8, 6, 8, 7, 8, 7, 8, 6, 8, 6,
8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 6, 8, 6, 8,
7, 8, 7, 8, 6, 8, 6, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7,
8, 7, 8, 1, 6, 2, 6, 3, 7, 3, 7, 2, 6, 2, 6, 3, 7, 3, 7, 3, 7, 3, 7, 4, 7,
4, 7, 3, 7, 3, 7, 4, 7, 4, 7, 2, 6, 2, 6, 3, 7, 3, 7, 2, 6, 2, 6, 3, 7, 3,
7, 3, 7, 3, 7, 4, 7, 4, 7, 3, 7, 3, 7, 4, 7, 4, 7, 6, 8, 6, 8, 7, 8, 7, 8,
6, 8, 6, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 6,
8, 6, 8, 7, 8, 7, 8, 6, 8, 6, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8,
7, 8, 7, 8, 7, 8,
];
#[rustfmt::skip]
const ZERO_CTX_HH_LOOKUP: [usize; 256] = [
0, 1, 3, 4, 1, 2, 4, 5, 3, 4, 6, 7, 4, 5, 7, 7, 1, 2, 4, 5, 2, 2, 5, 5, 4,
5, 7, 7, 5, 5, 7, 7, 3, 4, 6, 7, 4, 5, 7, 7, 6, 7, 8, 8, 7, 7, 8, 8, 4, 5,
7, 7, 5, 5, 7, 7, 7, 7, 8, 8, 7, 7, 8, 8, 1, 2, 4, 5, 2, 2, 5, 5, 4, 5, 7,
7, 5, 5, 7, 7, 2, 2, 5, 5, 2, 2, 5, 5, 5, 5, 7, 7, 5, 5, 7, 7, 4, 5, 7, 7,
5, 5, 7, 7, 7, 7, 8, 8, 7, 7, 8, 8, 5, 5, 7, 7, 5, 5, 7, 7, 7, 7, 8, 8, 7,
7, 8, 8, 3, 4, 6, 7, 4, 5, 7, 7, 6, 7, 8, 8, 7, 7, 8, 8, 4, 5, 7, 7, 5, 5,
7, 7, 7, 7, 8, 8, 7, 7, 8, 8, 6, 7, 8, 8, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 7, 7, 8, 8, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 5, 7, 7, 5, 5, 7, 7,
7, 7, 8, 8, 7, 7, 8, 8, 5, 5, 7, 7, 5, 5, 7, 7, 7, 7, 8, 8, 7, 7, 8, 8, 7,
7, 8, 8, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 8, 8, 7, 7, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8,
];
fn neighbor_significance_bits_bool(sig: &[bool], idx: usize, w: usize, h: usize) -> u8 {
let r = idx / w;
let c = idx % w;
let mut bits = 0u8;
if r > 0 && c > 0 && sig[(r - 1) * w + (c - 1)] {
bits |= 1 << 7;
}
if r > 0 && sig[(r - 1) * w + c] {
bits |= 1 << 6;
}
if r > 0 && c + 1 < w && sig[(r - 1) * w + (c + 1)] {
bits |= 1 << 5;
}
if c > 0 && sig[r * w + (c - 1)] {
bits |= 1 << 4;
}
if r + 1 < h && c > 0 && sig[(r + 1) * w + (c - 1)] {
bits |= 1 << 3;
}
if c + 1 < w && sig[r * w + (c + 1)] {
bits |= 1 << 2;
}
if r + 1 < h && c + 1 < w && sig[(r + 1) * w + (c + 1)] {
bits |= 1 << 1;
}
if r + 1 < h && sig[(r + 1) * w + c] {
bits |= 1;
}
bits
}
fn zero_coding_context_bool(
sig: &[bool],
idx: usize,
w: usize,
h: usize,
subband_kind: StandardSubbandKind,
) -> usize {
let neighbors = neighbor_significance_bits_bool(sig, idx, w, h) as usize;
match subband_kind {
StandardSubbandKind::Ll | StandardSubbandKind::Lh => ZERO_CTX_LL_LH_LOOKUP[neighbors],
StandardSubbandKind::Hl => ZERO_CTX_HL_LOOKUP[neighbors],
StandardSubbandKind::Hh => ZERO_CTX_HH_LOOKUP[neighbors],
}
}
fn sign_context_bool(sig: &[bool], signs: &[u8], idx: usize, w: usize, h: usize) -> (usize, u8) {
let r = idx / w;
let c = idx % w;
let h_contrib = {
let left = if c > 0 {
if sig[r * w + c - 1] {
if signs[r * w + c - 1] == 1 {
-1i32
} else {
1
}
} else {
0
}
} else {
0
};
let right = if c + 1 < w {
if sig[r * w + c + 1] {
if signs[r * w + c + 1] == 1 {
-1i32
} else {
1
}
} else {
0
}
} else {
0
};
(left + right).signum()
};
let v_contrib = {
let up = if r > 0 {
if sig[(r - 1) * w + c] {
if signs[(r - 1) * w + c] == 1 {
-1i32
} else {
1
}
} else {
0
}
} else {
0
};
let down = if r + 1 < h {
if sig[(r + 1) * w + c] {
if signs[(r + 1) * w + c] == 1 {
-1i32
} else {
1
}
} else {
0
}
} else {
0
};
(up + down).signum()
};
match (h_contrib, v_contrib) {
(1, 1) => (std_ctx::SIGN[0], 0),
(1, 0) => (std_ctx::SIGN[1], 0),
(1, -1) => (std_ctx::SIGN[2], 0),
(0, 1) => (std_ctx::SIGN[3], 0),
(0, 0) => (std_ctx::SIGN[4], 0),
(0, -1) => (std_ctx::SIGN[3], 1),
(-1, 1) => (std_ctx::SIGN[2], 1),
(-1, 0) => (std_ctx::SIGN[1], 1),
(-1, -1) => (std_ctx::SIGN[0], 1),
_ => (std_ctx::SIGN[4], 0),
}
}
fn mag_refinement_context_bool(
sig: &[bool],
mag_refined: &[bool],
idx: usize,
w: usize,
h: usize,
) -> usize {
if mag_refined[idx] {
2
} else if significance_context_bool(sig, idx, w, h) > 0 {
1
} else {
0
}
}
fn neighbours(idx: usize, w: usize, h: usize) -> [Option<usize>; 8] {
let r = idx / w;
let c = idx % w;
[
if r > 0 && c > 0 {
Some((r - 1) * w + c - 1)
} else {
None
},
if r > 0 { Some((r - 1) * w + c) } else { None },
if r > 0 && c + 1 < w {
Some((r - 1) * w + c + 1)
} else {
None
},
if c > 0 { Some(r * w + c - 1) } else { None },
if c + 1 < w { Some(r * w + c + 1) } else { None },
if r + 1 < h && c > 0 {
Some((r + 1) * w + c - 1)
} else {
None
},
if r + 1 < h {
Some((r + 1) * w + c)
} else {
None
},
if r + 1 < h && c + 1 < w {
Some((r + 1) * w + c + 1)
} else {
None
},
]
}
fn significance_context(sig: &[SigState], idx: usize, w: usize, h: usize) -> usize {
let nb = neighbours(idx, w, h);
let count: usize = nb
.iter()
.filter_map(|&n| n)
.filter(|&n| sig[n] == SigState::Significant)
.count();
count.min(8)
}
fn sign_context(sig: &[SigState], signs: &[u8], idx: usize, w: usize, h: usize) -> (usize, u8) {
(ctx::SIGN[0], 0)
}
fn mag_refinement_context(
sig: &[SigState],
idx: usize,
w: usize,
h: usize,
bp: usize,
total_bp: usize,
) -> usize {
if bp == total_bp.saturating_sub(1) {
0
} else if significance_context(sig, idx, w, h) > 0 {
1
} else {
2
}
}
pub fn quantise(coeffs: &[f64], step_sizes: &[f64]) -> Vec<i32> {
let step = step_sizes.first().copied().unwrap_or(1.0).max(1e-10);
coeffs
.iter()
.map(|&c| {
let q = (c.abs() / step).floor() as i32;
if c < 0.0 {
-q
} else {
q
}
})
.collect()
}
pub fn dequantise(quantised: &[i32], step_sizes: &[f64]) -> Vec<f64> {
let step = step_sizes.first().copied().unwrap_or(1.0);
quantised
.iter()
.map(|&q| {
if q == 0 {
0.0
} else {
let sign = if q < 0 { -1.0 } else { 1.0 };
sign * (q.unsigned_abs() as f64 + 0.5) * step
}
})
.collect()
}
#[cfg(any())]
mod tests {
use super::*;
#[test]
fn mq_encode_decode_zeros() {
let mut enc = MqEncoder::new();
for _ in 0..32 {
enc.encode(0, 0);
}
let bytes = enc.flush();
assert!(!bytes.is_empty());
let mut dec = MqDecoder::new(&bytes);
for _ in 0..32 {
let b = dec.decode(0);
assert!(b == 0 || b == 1); }
}
#[test]
fn mq_encode_decode_alternating() {
let symbols: Vec<u8> = (0..64).map(|i| (i % 2) as u8).collect();
let mut enc = MqEncoder::new();
for &s in &symbols {
enc.encode(s, 1);
}
let bytes = enc.flush();
assert!(!bytes.is_empty());
let mut dec = MqDecoder::new(&bytes);
let mut decoded = Vec::new();
for _ in 0..64 {
decoded.push(dec.decode(1));
}
assert_eq!(decoded, symbols);
}
#[test]
fn block_codec_zero() {
let coeffs = vec![0i32; 64];
let encoded = encode_block(&coeffs, 8, 8);
let decoded = decode_block(&encoded, 8, 8, 1);
assert_eq!(decoded.len(), 64);
}
#[test]
fn block_codec_simple() {
let coeffs: Vec<i32> = (0..64i32).map(|x| x - 32).collect();
let encoded = encode_block(&coeffs, 8, 8);
let num_bp = 7; let decoded = decode_block(&encoded, 8, 8, num_bp);
for (a, b) in coeffs.iter().zip(decoded.iter()) {
assert!((a - b).abs() <= 1, "block codec mismatch: {} vs {}", a, b);
}
}
}