use super::super::arithmetic_decoder::{ArithmeticDecoder, ArithmeticDecoderContext};
use super::flags::{
set_significant, sign_coding_label, zero_coding_label, zero_coding_table, MU_THIS, PI_ALL,
PI_THIS, SIGMA_NEIGHBOURS, SIGMA_THIS,
};
use super::state::{BitPlaneDecodeContext, Coefficient};
const ROW_BITS: u32 = 0x249;
#[expect(clippy::inline_always, reason = "Tier-1 coefficient-loop hot path")]
#[inline(always)]
fn uncoded_rows(own: u32) -> u32 {
!(own >> SIGMA_THIS.trailing_zeros() | own >> PI_THIS.trailing_zeros()) & ROW_BITS
}
#[expect(clippy::inline_always, reason = "Tier-1 coefficient-loop hot path")]
#[inline(always)]
fn propagation_rows(own: u32) -> u32 {
let any_in_row = own | own >> 1 | own >> 2;
let neighbors = any_in_row | any_in_row >> 6 | own >> 3 | own >> 5;
neighbors & uncoded_rows(own)
}
struct Scan<'c, 'd> {
decoder: ArithmeticDecoder<'d>,
contexts: &'c mut [ArithmeticDecoderContext; 256],
flags: &'c mut [u32],
coefficients: &'c mut [Coefficient],
zero_coding: &'static [u8; 512],
width: usize,
height: usize,
stride: usize,
bit_position: u8,
}
impl<'c, 'd> Scan<'c, 'd> {
fn new(ctx: &'c mut BitPlaneDecodeContext, decoder: &ArithmeticDecoder<'d>) -> Self {
Self {
decoder: decoder.clone(),
zero_coding: zero_coding_table(ctx.sub_band_type),
width: ctx.width as usize,
height: ctx.height as usize,
stride: ctx.padded_width as usize,
bit_position: ctx.current_bit_position,
contexts: &mut ctx.contexts,
flags: &mut ctx.flags,
coefficients: &mut ctx.coefficients,
}
}
fn finish(self, decoder: &mut ArithmeticDecoder<'d>) {
*decoder = self.decoder;
}
fn stripe_start(&self, stripe: usize) -> (usize, usize, u32) {
let rows = (self.height - 4 * stripe).min(4);
(
(stripe + 1) * self.stride + 1,
(4 * stripe + 1) * self.stride + 1,
ROW_BITS & ((1 << (3 * rows)) - 1),
)
}
#[expect(clippy::inline_always, reason = "per-symbol MQ decode")]
#[inline(always)]
fn read(&mut self, label: u8) -> u32 {
self.decoder
.read_bit(&mut self.contexts[usize::from(label)])
}
#[expect(clippy::inline_always, reason = "Tier-1 coefficient-loop hot path")]
#[inline(always)]
fn become_significant(&mut self, own: &mut u32, flag_index: usize, top: usize, shift: u32) {
let index = top + (shift / 3) as usize * self.stride;
self.coefficients[index].push_bit_at(1, self.bit_position);
let (label, xor) = sign_coding_label(
*own,
self.flags[flag_index - 1],
self.flags[flag_index + 1],
shift,
);
let sign = self.read(label) ^ u32::from(xor);
self.coefficients[index].set_sign(u8::from(sign != 0));
set_significant(self.flags, own, flag_index, self.stride, shift, sign);
}
#[expect(clippy::inline_always, reason = "Tier-1 coefficient-loop hot path")]
#[inline(always)]
fn zero_code(&mut self, own: &mut u32, flag_index: usize, top: usize, shift: u32) -> bool {
let significant = self.read(zero_coding_label(self.zero_coding, *own, shift)) != 0;
if significant {
self.become_significant(own, flag_index, top, shift);
}
significant
}
}
pub(super) fn significance_propagation_pass_flags(
ctx: &mut BitPlaneDecodeContext,
decoder: &mut ArithmeticDecoder<'_>,
) {
let mut scan = Scan::new(ctx, decoder);
for stripe in 0..scan.height.div_ceil(4) {
let (flag_row, top_row, rows) = scan.stripe_start(stripe);
for x in 0..scan.width {
let flag_index = flag_row + x;
let mut own = scan.flags[flag_index];
let mut candidates = propagation_rows(own) & rows;
if candidates == 0 {
continue;
}
while candidates != 0 {
let shift = candidates.trailing_zeros();
own |= PI_THIS << shift;
if scan.zero_code(&mut own, flag_index, top_row + x, shift) {
candidates = propagation_rows(own) & rows & (u32::MAX << (shift + 1));
} else {
candidates &= candidates - 1;
}
}
scan.flags[flag_index] = own;
}
}
scan.finish(decoder);
}
pub(super) fn magnitude_refinement_pass_flags(
ctx: &mut BitPlaneDecodeContext,
decoder: &mut ArithmeticDecoder<'_>,
) {
let mut scan = Scan::new(ctx, decoder);
for stripe in 0..scan.height.div_ceil(4) {
let (flag_row, top_row, rows) = scan.stripe_start(stripe);
for x in 0..scan.width {
let flag_index = flag_row + x;
let mut own = scan.flags[flag_index];
let mut candidates =
(own >> SIGMA_THIS.trailing_zeros()) & !(own >> PI_THIS.trailing_zeros()) & rows;
if candidates == 0 {
continue;
}
while candidates != 0 {
let shift = candidates.trailing_zeros();
let window = own >> shift;
let label = if window & MU_THIS != 0 {
16
} else if window & SIGMA_NEIGHBOURS != 0 {
15
} else {
14
};
let bit = scan.read(label);
scan.coefficients[top_row + x + (shift / 3) as usize * scan.stride]
.push_bit_at(bit, scan.bit_position);
own |= MU_THIS << shift;
candidates &= candidates - 1;
}
scan.flags[flag_index] = own;
}
}
scan.finish(decoder);
}
pub(super) fn cleanup_pass_flags(
ctx: &mut BitPlaneDecodeContext,
decoder: &mut ArithmeticDecoder<'_>,
) {
let mut scan = Scan::new(ctx, decoder);
let mut run_length = scan.contexts[17];
let mut uniform = scan.contexts[18];
for stripe in 0..scan.height.div_ceil(4) {
let (flag_row, top_row, rows) = scan.stripe_start(stripe);
for x in 0..scan.width {
let flag_index = flag_row + x;
let top = top_row + x;
let mut own = scan.flags[flag_index];
if rows == ROW_BITS && own == 0 {
if scan.decoder.read_bit(&mut run_length) == 0 {
continue;
}
let first = (scan.decoder.read_bit(&mut uniform) << 1)
| scan.decoder.read_bit(&mut uniform);
scan.become_significant(&mut own, flag_index, top, 3 * first);
for row in first + 1..4 {
scan.zero_code(&mut own, flag_index, top, 3 * row);
}
} else {
let mut candidates = uncoded_rows(own) & rows;
while candidates != 0 {
scan.zero_code(&mut own, flag_index, top, candidates.trailing_zeros());
candidates &= candidates - 1;
}
}
scan.flags[flag_index] = own & !PI_ALL;
}
}
scan.contexts[17] = run_length;
scan.contexts[18] = uniform;
scan.finish(decoder);
}