use crate::tier1::SubbandType;
#[inline]
fn div_ceil(a: usize, b: usize) -> usize {
if b == 0 { 0 } else { a.div_ceil(b) }
}
pub fn band_dim(size: usize, ob: u32, nb: u32) -> usize {
debug_assert!(nb >= 1, "band_dim requires nb >= 1");
let pow = 1usize << nb;
let half = 1usize << (nb - 1);
let offset = (ob as usize) * half;
if size > offset {
div_ceil(size - offset, pow)
} else {
0
}
}
pub fn resolution_dim(size: usize, num_levels: u32, r: u32) -> usize {
let shift = num_levels - r;
div_ceil(size, 1usize << shift)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SubbandLayout {
pub subband_type: SubbandType,
pub width: usize,
pub height: usize,
pub cblk_nx: usize,
pub cblk_ny: usize,
pub exponent_index: usize,
}
impl SubbandLayout {
pub fn cblk_dims(&self, bx: usize, by: usize, cbw: usize, cbh: usize) -> (usize, usize) {
let w = cbw.min(self.width.saturating_sub(bx * cbw));
let h = cbh.min(self.height.saturating_sub(by * cbh));
(w, h)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolutionLayout {
pub width: usize,
pub height: usize,
pub subbands: Vec<SubbandLayout>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TileComponentLayout {
pub width: usize,
pub height: usize,
pub cbw: usize,
pub cbh: usize,
pub num_levels: u32,
pub resolutions: Vec<ResolutionLayout>,
}
impl TileComponentLayout {
pub fn num_resolutions(&self) -> usize {
self.resolutions.len()
}
}
pub fn build_tile_component_layout(
comp_w: usize,
comp_h: usize,
num_levels: u32,
cbw: usize,
cbh: usize,
) -> TileComponentLayout {
let cbw = cbw.max(1);
let cbh = cbh.max(1);
let mut resolutions = Vec::with_capacity(num_levels as usize + 1);
let ll_w = resolution_dim(comp_w, num_levels, 0);
let ll_h = resolution_dim(comp_h, num_levels, 0);
resolutions.push(ResolutionLayout {
width: ll_w,
height: ll_h,
subbands: vec![SubbandLayout {
subband_type: SubbandType::Ll,
width: ll_w,
height: ll_h,
cblk_nx: div_ceil(ll_w, cbw),
cblk_ny: div_ceil(ll_h, cbh),
exponent_index: 0,
}],
});
for r in 1..=num_levels {
let nb = num_levels - r + 1; let res_w = resolution_dim(comp_w, num_levels, r);
let res_h = resolution_dim(comp_h, num_levels, r);
let base = 1 + 3 * (r as usize - 1);
let mut subbands = Vec::with_capacity(3);
for (offset, (subband_type, obx, oby)) in [
(SubbandType::Hl, 1u32, 0u32),
(SubbandType::Lh, 0u32, 1u32),
(SubbandType::Hh, 1u32, 1u32),
]
.into_iter()
.enumerate()
{
let w = band_dim(comp_w, obx, nb);
let h = band_dim(comp_h, oby, nb);
subbands.push(SubbandLayout {
subband_type,
width: w,
height: h,
cblk_nx: div_ceil(w, cbw),
cblk_ny: div_ceil(h, cbh),
exponent_index: base + offset,
});
}
resolutions.push(ResolutionLayout {
width: res_w,
height: res_h,
subbands,
});
}
TileComponentLayout {
width: comp_w,
height: comp_h,
cbw,
cbh,
num_levels,
resolutions,
}
}
pub fn subband_num_bitplanes(guard_bits: u8, exponent: u8, precision: u8) -> usize {
let mb = i32::from(guard_bits) + i32::from(exponent) - 1;
if mb >= 1 {
mb as usize
} else {
usize::from(precision).max(1) + 1
}
}
pub fn code_block_bitplanes(guard_bits: u8, exponent: u8, precision: u8, zbp: u32) -> usize {
subband_num_bitplanes(guard_bits, exponent, precision)
.saturating_sub(zbp as usize)
.max(1)
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use super::*;
#[test]
fn test_band_dim_basic() {
assert_eq!(band_dim(4, 0, 1), 2);
assert_eq!(band_dim(4, 1, 1), 2);
assert_eq!(band_dim(5, 0, 1), 3);
assert_eq!(band_dim(5, 1, 1), 2);
}
#[test]
fn test_layout_num_levels_zero_single_ll() {
let layout = build_tile_component_layout(4, 4, 0, 16, 16);
assert_eq!(layout.num_resolutions(), 1);
let res0 = &layout.resolutions[0];
assert_eq!(res0.subbands.len(), 1);
let ll = &res0.subbands[0];
assert_eq!(ll.subband_type, SubbandType::Ll);
assert_eq!((ll.width, ll.height), (4, 4));
assert_eq!((ll.cblk_nx, ll.cblk_ny), (1, 1));
assert_eq!(ll.exponent_index, 0);
}
#[test]
fn test_layout_one_level_subbands() {
let layout = build_tile_component_layout(4, 4, 1, 16, 16);
assert_eq!(layout.num_resolutions(), 2);
let res0 = &layout.resolutions[0];
assert_eq!((res0.width, res0.height), (2, 2));
assert_eq!(res0.subbands[0].subband_type, SubbandType::Ll);
assert_eq!((res0.subbands[0].width, res0.subbands[0].height), (2, 2));
let res1 = &layout.resolutions[1];
assert_eq!((res1.width, res1.height), (4, 4));
assert_eq!(res1.subbands.len(), 3);
assert_eq!(res1.subbands[0].subband_type, SubbandType::Hl);
assert_eq!(res1.subbands[1].subband_type, SubbandType::Lh);
assert_eq!(res1.subbands[2].subband_type, SubbandType::Hh);
for sb in &res1.subbands {
assert_eq!((sb.width, sb.height), (2, 2));
}
assert_eq!(res1.subbands[0].exponent_index, 1);
assert_eq!(res1.subbands[1].exponent_index, 2);
assert_eq!(res1.subbands[2].exponent_index, 3);
}
#[test]
fn test_layout_reconstruction_dims_sum() {
let layout = build_tile_component_layout(8, 8, 2, 64, 64);
assert_eq!(layout.num_resolutions(), 3);
assert_eq!(
(layout.resolutions[0].width, layout.resolutions[0].height),
(2, 2)
);
assert_eq!(
(layout.resolutions[1].width, layout.resolutions[1].height),
(4, 4)
);
assert_eq!(
(layout.resolutions[2].width, layout.resolutions[2].height),
(8, 8)
);
let mut total = 0usize;
for res in &layout.resolutions {
for sb in &res.subbands {
total += sb.width * sb.height;
}
}
assert_eq!(total, 64);
}
#[test]
fn test_multiple_code_blocks_grid() {
let layout = build_tile_component_layout(32, 16, 0, 16, 16);
let ll = &layout.resolutions[0].subbands[0];
assert_eq!((ll.cblk_nx, ll.cblk_ny), (2, 1));
assert_eq!(ll.cblk_dims(0, 0, 16, 16), (16, 16));
assert_eq!(ll.cblk_dims(1, 0, 16, 16), (16, 16));
}
#[test]
fn test_code_block_bitplanes_floor() {
assert_eq!(code_block_bitplanes(2, 8, 8, 3), 6);
assert_eq!(subband_num_bitplanes(0, 0, 8), 9);
assert_eq!(code_block_bitplanes(2, 1, 8, 100), 1);
}
}