use crate::error::bail;
use crate::{DecodingError, Result, ValidationError};
#[must_use]
pub fn idwt_band_index(origin: u32, local_coord: u32, low_pass: bool) -> u32 {
let global = u64::from(origin) + u64::from(local_coord);
let origin = u64::from(origin);
let index = if low_pass {
global.div_ceil(2).saturating_sub(origin.div_ceil(2))
} else {
(global / 2).saturating_sub(origin / 2)
};
u32::try_from(index).unwrap_or(u32::MAX)
}
pub(crate) fn add_roi_shift_to_bitplanes(
bitplanes: u8,
roi_shift: u8,
max_bitplanes: u8,
) -> Result<u8> {
let Some(coded_bitplanes) = bitplanes.checked_add(roi_shift) else {
bail!(DecodingError::TooManyBitplanes);
};
if coded_bitplanes > max_bitplanes {
bail!(DecodingError::TooManyBitplanes);
}
Ok(coded_bitplanes)
}
pub(crate) fn apply_roi_maxshift_inverse_i64(coefficient: i64, roi_shift: u8) -> i64 {
if roi_shift == 0 || coefficient == 0 {
return coefficient;
}
let magnitude = coefficient.unsigned_abs();
let threshold = 1_u64.checked_shl(u32::from(roi_shift)).unwrap_or(u64::MAX);
if magnitude < threshold {
return coefficient;
}
let shifted = magnitude >> roi_shift;
let shifted = i64::try_from(shifted).unwrap_or(i64::MAX);
if coefficient < 0 {
-shifted
} else {
shifted
}
}
pub(crate) fn apply_roi_maxshift_inverse_i32(coefficient: i32, roi_shift: u8) -> i32 {
let shifted = apply_roi_maxshift_inverse_i64(i64::from(coefficient), roi_shift);
i32::try_from(shifted).unwrap_or(if shifted < 0 { i32::MIN } else { i32::MAX })
}
pub(crate) fn validate_roi(dims: (u32, u32), roi: (u32, u32, u32, u32)) -> Result<()> {
let (image_width, image_height) = dims;
let (x, y, width, height) = roi;
let x_end = x
.checked_add(width)
.ok_or(ValidationError::InvalidDimensions)?;
let y_end = y
.checked_add(height)
.ok_or(ValidationError::InvalidDimensions)?;
if x_end > image_width || y_end > image_height {
return Err(ValidationError::InvalidDimensions.into());
}
Ok(())
}