use alloc::vec::Vec;
use super::super::build::Decomposition;
use super::super::codestream::WaveletTransform;
use super::super::decode::DecompositionStorage;
use super::super::rect::IntRect;
use super::filter_common::periodic_symmetric_extension_left;
use super::horizontal::filter_row;
use super::model::CoefficientSource;
use super::roi::interleave_samples_roi;
use super::vertical::{
irreversible_row_scale, lift_one_row, scale_one_row, split_rows, vertical_step_count,
vertical_steps, VerticalStep,
};
use crate::error::{DecodingError, ValidationError};
use crate::Result;
pub(crate) const fn ring_rows(transform: WaveletTransform) -> usize {
vertical_step_count(transform) + 2
}
pub(crate) fn ring_samples(
windows: impl IntoIterator<Item = IntRect>,
transform: WaveletTransform,
) -> Result<usize> {
windows.into_iter().try_fold(0usize, |total, window| {
(window.width() as usize)
.checked_mul(ring_rows(transform))
.and_then(|samples| total.checked_add(samples))
.ok_or(ValidationError::ImageTooLarge.into())
})
}
struct LevelRows<'r> {
window: IntRect,
decomposition: &'r Decomposition,
ring: &'r mut [f32],
fed: usize,
finished: [usize; 4],
}
impl LevelRows<'_> {
fn width(&self) -> usize {
self.window.width() as usize
}
fn height(&self) -> usize {
self.window.height() as usize
}
fn slot(&self, row: usize) -> usize {
row % (self.ring.len() / self.width())
}
fn row(&self, row: usize) -> &[f32] {
let width = self.width();
&self.ring[self.slot(row) * width..][..width]
}
fn done(&self, transform: WaveletTransform) -> usize {
if self.height() == 1 {
self.fed
} else {
self.finished[vertical_step_count(transform) - 1]
}
}
}
struct Inputs<'s, 'a> {
storage: &'s DecompositionStorage<'a>,
ll_sub_band: CoefficientSource<'s>,
transform: WaveletTransform,
}
#[expect(
clippy::too_many_arguments,
reason = "the streamed IDWT keeps its plan, storage, scratch, and output explicit"
)]
pub(super) fn reconstruct_rows(
storage: &DecompositionStorage<'_>,
decompositions: &[Decomposition],
windows: &[IntRect],
ll_sub_band: CoefficientSource<'_>,
transform: WaveletTransform,
ring: &mut [f32],
copy: IntRect,
write_row: &mut dyn FnMut(u32, &[f32]) -> Result<()>,
output_samples: &mut usize,
) -> Result<()> {
if windows.is_empty() || windows.len() != decompositions.len() {
return Err(DecodingError::CodeBlockDecodeFailure.into());
}
let mut levels = Vec::new();
levels
.try_reserve_exact(windows.len())
.map_err(|_| DecodingError::HostAllocationFailed)?;
let mut rest = ring;
for (window, decomposition) in windows.iter().zip(decompositions) {
let len = ring_samples([*window], transform)?;
if window.is_empty() || rest.len() < len {
return Err(DecodingError::CodeBlockDecodeFailure.into());
}
let (level_ring, tail) = core::mem::take(&mut rest).split_at_mut(len);
rest = tail;
levels.push(LevelRows {
window: *window,
decomposition,
ring: level_ring,
fed: 0,
finished: [0; 4],
});
}
let last = levels.len() - 1;
let window = levels[last].window;
if copy.is_empty() {
return Ok(());
}
if copy.x0 < window.x0 || copy.x1 > window.x1 || copy.y0 < window.y0 || copy.y1 > window.y1 {
return Err(DecodingError::CodeBlockDecodeFailure.into());
}
let inputs = Inputs {
storage,
ll_sub_band,
transform,
};
let columns = (copy.x0 - window.x0) as usize..(copy.x1 - window.x0) as usize;
let result = (|| {
for y in copy.y0..copy.y1 {
let row = (y - window.y0) as usize;
finish_row(&mut levels, last, row, &inputs)?;
write_row(y, &levels[last].row(row)[columns.clone()])?;
}
Ok(())
})();
for level in &levels {
*output_samples = output_samples.saturating_add(level.fed.saturating_mul(level.width()));
}
result
}
fn finish_row(
levels: &mut [LevelRows<'_>],
level: usize,
row: usize,
inputs: &Inputs<'_, '_>,
) -> Result<()> {
let ring_rows = ring_rows(inputs.transform);
while levels[level].done(inputs.transform) <= row {
let current = &levels[level];
let oldest_read = current.done(inputs.transform).saturating_sub(1);
if current.fed >= current.height()
|| (current.fed >= ring_rows && current.fed - ring_rows >= oldest_read)
{
return Err(DecodingError::CodeBlockDecodeFailure.into());
}
feed_row(levels, level, inputs)?;
advance_steps(&mut levels[level], inputs.transform);
}
if levels[level].fed > row + ring_rows {
return Err(DecodingError::CodeBlockDecodeFailure.into());
}
Ok(())
}
fn feed_row(levels: &mut [LevelRows<'_>], level: usize, inputs: &Inputs<'_, '_>) -> Result<()> {
let (lower, upper) = levels.split_at_mut(level);
let current = &mut upper[0];
let local = current.fed;
let y = current.window.y0 + u32::try_from(local).map_err(|_| ValidationError::ImageTooLarge)?;
let band_y = y / 2;
let ll = match lower.len().checked_sub(1) {
None => inputs.ll_sub_band,
Some(below) => {
let below_window = lower[below].window;
if y.is_multiple_of(2) && band_y >= below_window.y0 && band_y < below_window.y1 {
let below_row = (band_y - below_window.y0) as usize;
finish_row(lower, below, below_row, inputs)?;
CoefficientSource::new(
lower[below].row(below_row),
IntRect::from_ltrb(below_window.x0, band_y, below_window.x1, band_y + 1),
below_window.width(),
)
} else {
CoefficientSource::new(&[], IntRect::from_xywh(0, 0, 0, 0), 0)
}
}
};
let storage = inputs.storage;
let band = |index: usize| CoefficientSource::from_sub_band(&storage.sub_bands[index], storage);
let sub_bands = current.decomposition.sub_bands;
let window = current.window;
let height = current.height();
let width = current.width();
let slot = current.slot(local);
let target = &mut current.ring[slot * width..][..width];
interleave_samples_roi(
ll,
band(sub_bands[0]),
band(sub_bands[1]),
band(sub_bands[2]),
target,
IntRect::from_ltrb(window.x0, y, window.x1, y + 1),
);
filter_row(
target,
width,
window.x0 as usize,
inputs.transform,
super::OPENJPEG_NORMALIZED_HIGH_PASS_F32,
);
if height == 1 {
if !y.is_multiple_of(2) {
scale_one_row(target, 0.5);
}
} else if inputs.transform == WaveletTransform::Irreversible97 {
scale_one_row(
target,
irreversible_row_scale(
window.y0,
local.is_multiple_of(2),
super::OPENJPEG_NORMALIZED_HIGH_PASS_F32,
),
);
}
current.fed += 1;
Ok(())
}
fn advance_steps(level: &mut LevelRows<'_>, transform: WaveletTransform) {
let height = level.height();
if height == 1 {
return;
}
let steps = vertical_steps(transform, level.window.y0);
for (index, &(step, even_offsets)) in steps
.iter()
.take(vertical_step_count(transform))
.enumerate()
{
let available = if index == 0 {
level.fed
} else {
level.finished[index - 1]
};
while level.finished[index] < height {
let row = level.finished[index];
if row.is_multiple_of(2) == even_offsets {
let above = periodic_symmetric_extension_left(row, 1);
let below = if row + 1 < height {
row + 1
} else {
height - 2
};
if row.max(above).max(below) >= available {
break;
}
lift_level_row(level, step, row, above, below);
} else if row >= available {
break;
}
level.finished[index] += 1;
}
}
}
fn lift_level_row(
level: &mut LevelRows<'_>,
step: VerticalStep,
row: usize,
above: usize,
below: usize,
) {
let width = level.width();
let (target, above, below) = (level.slot(row), level.slot(above), level.slot(below));
let (row_samples, above, below) = split_rows(level.ring, width, target, above, below);
lift_one_row(step, row_samples, above, below);
}