use crate::error::{JpxError, Result};
use crate::markers::{CodingStyle, Siz, SizComponent};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) struct Rect {
pub x0: u32,
pub y0: u32,
pub x1: u32,
pub y1: u32,
}
impl Rect {
pub(crate) fn width(&self) -> u32 {
self.x1.saturating_sub(self.x0)
}
pub(crate) fn height(&self) -> u32 {
self.y1.saturating_sub(self.y0)
}
pub(crate) fn is_empty(&self) -> bool {
self.x1 <= self.x0 || self.y1 <= self.y0
}
}
pub(crate) fn ceil_div(numerator: u64, denominator: u64) -> u64 {
numerator / denominator + u64::from(!numerator.is_multiple_of(denominator))
}
pub(crate) fn tile_grid(siz: &Siz) -> Result<(u32, u32)> {
if siz.xtsiz == 0 || siz.ytsiz == 0 {
return Err(JpxError::Malformed("SIZ: zero tile size".into()));
}
if siz.xtosiz > siz.xosiz || siz.ytosiz > siz.yosiz {
return Err(JpxError::Malformed(
"SIZ: tile offset exceeds image offset (B-3)".into(),
));
}
if siz.xsiz <= siz.xosiz || siz.ysiz <= siz.yosiz {
return Err(JpxError::Malformed(
"SIZ: empty image area (Xsiz <= XOsiz or Ysiz <= YOsiz)".into(),
));
}
let wide = ceil_div(
u64::from(siz.xsiz) - u64::from(siz.xtosiz),
u64::from(siz.xtsiz),
);
let high = ceil_div(
u64::from(siz.ysiz) - u64::from(siz.ytosiz),
u64::from(siz.ytsiz),
);
Ok((wide as u32, high as u32))
}
pub(crate) fn tile_rect(siz: &Siz, p: u32, q: u32) -> Rect {
let clamp_x = |v: u64| v.min(u64::from(siz.xsiz)) as u32;
let clamp_y = |v: u64| v.min(u64::from(siz.ysiz)) as u32;
let tx0 = clamp_x(
(u64::from(siz.xtosiz) + u64::from(p) * u64::from(siz.xtsiz)).max(u64::from(siz.xosiz)),
);
let ty0 = clamp_y(
(u64::from(siz.ytosiz) + u64::from(q) * u64::from(siz.ytsiz)).max(u64::from(siz.yosiz)),
);
let tx1 = clamp_x(u64::from(siz.xtosiz) + (u64::from(p) + 1) * u64::from(siz.xtsiz));
let ty1 = clamp_y(u64::from(siz.ytosiz) + (u64::from(q) + 1) * u64::from(siz.ytsiz));
Rect {
x0: tx0,
y0: ty0,
x1: tx1,
y1: ty1,
}
}
pub(crate) fn component_rect(area: Rect, xrsiz: u8, yrsiz: u8) -> Result<Rect> {
if xrsiz == 0 || yrsiz == 0 {
return Err(JpxError::Malformed(
"SIZ: zero component separation (XRsiz/YRsiz)".into(),
));
}
let dx = u64::from(xrsiz);
let dy = u64::from(yrsiz);
Ok(Rect {
x0: ceil_div(u64::from(area.x0), dx) as u32,
y0: ceil_div(u64::from(area.y0), dy) as u32,
x1: ceil_div(u64::from(area.x1), dx) as u32,
y1: ceil_div(u64::from(area.y1), dy) as u32,
})
}
#[allow(dead_code)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum BandKind {
Ll,
Hl,
Lh,
Hh,
}
#[allow(dead_code)]
#[derive(Debug)]
pub(crate) struct PrecinctBand {
pub blocks_wide: u32,
pub blocks_high: u32,
pub blocks: Vec<Rect>,
}
#[allow(dead_code)]
#[derive(Debug)]
pub(crate) struct BandGeometry {
pub kind: BandKind,
pub level: u8,
pub rect: Rect,
pub precincts: Vec<PrecinctBand>,
}
#[allow(dead_code)]
#[derive(Debug)]
pub(crate) struct ResolutionGeometry {
pub rect: Rect,
pub ppx: u8,
pub ppy: u8,
pub precincts_wide: u32,
pub precincts_high: u32,
pub bands: Vec<BandGeometry>,
}
#[allow(dead_code)]
#[derive(Debug)]
pub(crate) struct TileComponentGeometry {
pub rect: Rect,
pub levels: u8,
pub resolutions: Vec<ResolutionGeometry>,
}
pub(crate) fn tile_component_geometry(
tile: Rect,
component: &SizComponent,
style: &CodingStyle,
) -> Result<TileComponentGeometry> {
let rect = component_rect(tile, component.xrsiz, component.yrsiz)?;
let levels = style.decomposition_levels;
if levels > 32 {
return Err(JpxError::Malformed(
"COD/COC: more than 32 decomposition levels (Table A.15)".into(),
));
}
if !style.precincts.is_empty() && style.precincts.len() != usize::from(levels) + 1 {
return Err(JpxError::Malformed(
"COD/COC: precinct size list is not NL + 1 entries (Table A.21)".into(),
));
}
let xcb = style.code_block_width_exp;
let ycb = style.code_block_height_exp;
if !(2..=10).contains(&xcb) || !(2..=10).contains(&ycb) {
return Err(JpxError::Malformed(
"COD/COC: code-block exponent outside 2..=10 (Table A.18)".into(),
));
}
let mut resolutions = Vec::with_capacity(usize::from(levels) + 1);
for r in 0..=levels {
resolutions.push(resolution_geometry(rect, levels, r, style)?);
}
Ok(TileComponentGeometry {
rect,
levels,
resolutions,
})
}
fn band_offsets(kind: BandKind) -> (u64, u64) {
match kind {
BandKind::Ll => (0, 0),
BandKind::Hl => (1, 0),
BandKind::Lh => (0, 1),
BandKind::Hh => (1, 1),
}
}
fn band_coord(a: u32, ob: u64, half: u64, full: u64) -> u32 {
let a = u64::from(a);
if ob == 0 {
ceil_div(a, full) as u32
} else {
((a + half - 1) / full) as u32
}
}
fn band_rect(tc: Rect, level: u8, kind: BandKind) -> Rect {
if level == 0 {
return tc;
}
let half = 1u64 << (u32::from(level) - 1);
let full = half << 1;
let (xob, yob) = band_offsets(kind);
Rect {
x0: band_coord(tc.x0, xob, half, full),
y0: band_coord(tc.y0, yob, half, full),
x1: band_coord(tc.x1, xob, half, full),
y1: band_coord(tc.y1, yob, half, full),
}
}
fn precinct_exponents(style: &CodingStyle, r: u8) -> Result<(u8, u8)> {
if style.precincts.is_empty() {
return Ok((15, 15));
}
let entry = &style.precincts[usize::from(r)];
if r > 0 && (entry.ppx == 0 || entry.ppy == 0) {
return Err(JpxError::Malformed(
"COD/COC: zero precinct exponent above resolution level 0 (B.6)".into(),
));
}
Ok((entry.ppx, entry.ppy))
}
fn precinct_count(tr0: u32, tr1: u32, pp: u8) -> u32 {
if tr1 <= tr0 {
return 0;
}
(ceil_div(u64::from(tr1), 1u64 << pp) - (u64::from(tr0) >> pp)) as u32
}
struct ResolutionContext {
rect: Rect,
r: u8,
ppx: u8,
ppy: u8,
precincts_wide: u32,
precincts_high: u32,
xcb: u8,
ycb: u8,
}
impl ResolutionContext {
fn band_precinct_shifts(&self) -> (u32, u32) {
if self.r == 0 {
(u32::from(self.ppx), u32::from(self.ppy))
} else {
(u32::from(self.ppx) - 1, u32::from(self.ppy) - 1)
}
}
}
fn precinct_band(band: Rect, ctx: &ResolutionContext, i: u32, j: u32) -> PrecinctBand {
let (shift_x, shift_y) = ctx.band_precinct_shifts();
let column = u64::from(ctx.rect.x0 >> ctx.ppx) + u64::from(i);
let row = u64::from(ctx.rect.y0 >> ctx.ppy) + u64::from(j);
let ix0 = (column << shift_x).max(u64::from(band.x0));
let ix1 = ((column + 1) << shift_x).min(u64::from(band.x1));
let iy0 = (row << shift_y).max(u64::from(band.y0));
let iy1 = ((row + 1) << shift_y).min(u64::from(band.y1));
if ix0 >= ix1 || iy0 >= iy1 {
return PrecinctBand {
blocks_wide: 0,
blocks_high: 0,
blocks: Vec::new(),
};
}
let span_x = 1u64 << ctx.xcb;
let span_y = 1u64 << ctx.ycb;
let first_column = ix0 / span_x;
let end_column = ceil_div(ix1, span_x);
let first_row = iy0 / span_y;
let end_row = ceil_div(iy1, span_y);
let blocks_wide = (end_column - first_column) as u32;
let blocks_high = (end_row - first_row) as u32;
let mut blocks = Vec::with_capacity((blocks_wide as usize) * (blocks_high as usize));
for block_row in first_row..end_row {
for block_column in first_column..end_column {
blocks.push(Rect {
x0: (block_column * span_x).max(ix0) as u32,
y0: (block_row * span_y).max(iy0) as u32,
x1: ((block_column + 1) * span_x).min(ix1) as u32,
y1: ((block_row + 1) * span_y).min(iy1) as u32,
});
}
}
PrecinctBand {
blocks_wide,
blocks_high,
blocks,
}
}
fn band_geometry(tc: Rect, level: u8, kind: BandKind, ctx: &ResolutionContext) -> BandGeometry {
let rect = band_rect(tc, level, kind);
let count = (ctx.precincts_wide as usize) * (ctx.precincts_high as usize);
let mut precincts = Vec::with_capacity(count);
for j in 0..ctx.precincts_high {
for i in 0..ctx.precincts_wide {
precincts.push(precinct_band(rect, ctx, i, j));
}
}
BandGeometry {
kind,
level,
rect,
precincts,
}
}
const PRECINCT_METADATA_BYTES: u64 = 64;
const BLOCK_METADATA_BYTES: u64 = 64;
fn partition_cells(a0: u32, a1: u32, e: u8) -> u64 {
if a1 <= a0 {
return 0;
}
ceil_div(u64::from(a1), 1u64 << e) - (u64::from(a0) >> e)
}
pub(crate) fn partition_metadata_cost(
tile: Rect,
component: &SizComponent,
style: &CodingStyle,
) -> Result<u64> {
let rect = component_rect(tile, component.xrsiz, component.yrsiz)?;
let levels = style.decomposition_levels;
if levels > 32 {
return Err(JpxError::Malformed(
"COD/COC: more than 32 decomposition levels (Table A.15)".into(),
));
}
if !style.precincts.is_empty() && style.precincts.len() != usize::from(levels) + 1 {
return Err(JpxError::Malformed(
"COD/COC: precinct size list is not NL + 1 entries (Table A.21)".into(),
));
}
let mut cost = 0u64;
for r in 0..=levels {
let divisor = 1u64 << u32::from(levels - r);
let res = Rect {
x0: ceil_div(u64::from(rect.x0), divisor) as u32,
y0: ceil_div(u64::from(rect.y0), divisor) as u32,
x1: ceil_div(u64::from(rect.x1), divisor) as u32,
y1: ceil_div(u64::from(rect.y1), divisor) as u32,
};
let (ppx, ppy) = precinct_exponents(style, r)?;
let precincts = u64::from(precinct_count(res.x0, res.x1, ppx))
* u64::from(precinct_count(res.y0, res.y1, ppy));
let (xcb, ycb, level, kinds): (u8, u8, u8, &[BandKind]) = if r == 0 {
(
style.code_block_width_exp.min(ppx),
style.code_block_height_exp.min(ppy),
levels,
&[BandKind::Ll],
)
} else {
(
style.code_block_width_exp.min(ppx - 1),
style.code_block_height_exp.min(ppy - 1),
levels - r + 1,
&[BandKind::Hl, BandKind::Lh, BandKind::Hh],
)
};
for kind in kinds {
let band = band_rect(rect, level, *kind);
let blocks =
partition_cells(band.x0, band.x1, xcb) * partition_cells(band.y0, band.y1, ycb);
cost = cost
.saturating_add(precincts.saturating_mul(PRECINCT_METADATA_BYTES))
.saturating_add(blocks.saturating_mul(BLOCK_METADATA_BYTES));
}
}
Ok(cost)
}
fn resolution_geometry(
tc: Rect,
levels: u8,
r: u8,
style: &CodingStyle,
) -> Result<ResolutionGeometry> {
let divisor = 1u64 << u32::from(levels - r);
let rect = Rect {
x0: ceil_div(u64::from(tc.x0), divisor) as u32,
y0: ceil_div(u64::from(tc.y0), divisor) as u32,
x1: ceil_div(u64::from(tc.x1), divisor) as u32,
y1: ceil_div(u64::from(tc.y1), divisor) as u32,
};
let (ppx, ppy) = precinct_exponents(style, r)?;
let ctx = ResolutionContext {
rect,
r,
ppx,
ppy,
precincts_wide: precinct_count(rect.x0, rect.x1, ppx),
precincts_high: precinct_count(rect.y0, rect.y1, ppy),
xcb: if r == 0 {
style.code_block_width_exp.min(ppx)
} else {
style.code_block_width_exp.min(ppx - 1)
},
ycb: if r == 0 {
style.code_block_height_exp.min(ppy)
} else {
style.code_block_height_exp.min(ppy - 1)
},
};
let (level, kinds): (u8, &[BandKind]) = if r == 0 {
(levels, &[BandKind::Ll])
} else {
(levels - r + 1, &[BandKind::Hl, BandKind::Lh, BandKind::Hh])
};
let bands = kinds
.iter()
.map(|kind| band_geometry(tc, level, *kind, &ctx))
.collect();
Ok(ResolutionGeometry {
rect,
ppx,
ppy,
precincts_wide: ctx.precincts_wide,
precincts_high: ctx.precincts_high,
bands,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::markers::{PrecinctExponents, WaveletKind};
fn rect(x0: u32, y0: u32, x1: u32, y1: u32) -> Rect {
Rect { x0, y0, x1, y1 }
}
fn unit_component() -> SizComponent {
SizComponent {
depth: 8,
signed: false,
xrsiz: 1,
yrsiz: 1,
}
}
fn coding_style(levels: u8, xcb: u8, ycb: u8, precincts: &[(u8, u8)]) -> CodingStyle {
CodingStyle {
decomposition_levels: levels,
code_block_width_exp: xcb,
code_block_height_exp: ycb,
code_block_style: 0,
wavelet: WaveletKind::Reversible53,
precincts: precincts
.iter()
.map(|&(ppx, ppy)| PrecinctExponents { ppx, ppy })
.collect(),
}
}
fn check_invariants(geometry: &TileComponentGeometry) {
assert_eq!(geometry.resolutions.len(), usize::from(geometry.levels) + 1);
for (r, resolution) in geometry.resolutions.iter().enumerate() {
let expected_kinds: &[BandKind] = if r == 0 {
&[BandKind::Ll]
} else {
&[BandKind::Hl, BandKind::Lh, BandKind::Hh]
};
let kinds: Vec<BandKind> = resolution.bands.iter().map(|b| b.kind).collect();
assert_eq!(kinds, expected_kinds, "band order at r={r} (B.9)");
let expected_level = if r == 0 {
geometry.levels
} else {
geometry.levels - r as u8 + 1
};
let precinct_count =
resolution.precincts_wide as usize * resolution.precincts_high as usize;
for band in &resolution.bands {
assert_eq!(band.level, expected_level, "band level at r={r}");
assert_eq!(band.precincts.len(), precinct_count, "precincts at r={r}");
for precinct in &band.precincts {
assert_eq!(
precinct.blocks.len(),
precinct.blocks_wide as usize * precinct.blocks_high as usize
);
for block in &precinct.blocks {
assert!(!block.is_empty(), "clipped-empty block emitted at r={r}");
assert!(block.x0 >= band.rect.x0 && block.x1 <= band.rect.x1);
assert!(block.y0 >= band.rect.y0 && block.y1 <= band.rect.y1);
}
}
}
}
}
fn single_blocks(band: &BandGeometry) -> Vec<Rect> {
band.precincts
.iter()
.map(|p| {
assert_eq!((p.blocks_wide, p.blocks_high), (1, 1));
p.blocks[0]
})
.collect()
}
fn tiled_zoo_siz() -> Siz {
Siz {
rsiz: 0,
xsiz: 523,
ysiz: 311,
xosiz: 0,
yosiz: 0,
xtsiz: 128,
ytsiz: 128,
xtosiz: 0,
ytosiz: 0,
components: vec![unit_component(); 3],
}
}
#[test]
fn tiled_zoo_interior_tile_partition() {
let siz = tiled_zoo_siz();
assert_eq!(tile_grid(&siz).unwrap(), (5, 3));
let tile = tile_rect(&siz, 0, 0);
assert_eq!(tile, rect(0, 0, 128, 128));
let style = coding_style(5, 6, 6, &[]);
let geometry = tile_component_geometry(tile, &siz.components[0], &style).unwrap();
check_invariants(&geometry);
assert_eq!(geometry.rect, rect(0, 0, 128, 128));
assert_eq!(geometry.levels, 5);
let sides = [4u32, 8, 16, 32, 64, 128];
for (r, side) in sides.iter().enumerate() {
let resolution = &geometry.resolutions[r];
assert_eq!(resolution.rect, rect(0, 0, *side, *side), "r={r}");
assert_eq!((resolution.ppx, resolution.ppy), (15, 15));
assert_eq!(
(resolution.precincts_wide, resolution.precincts_high),
(1, 1)
);
}
assert_eq!(geometry.resolutions[0].bands[0].rect, rect(0, 0, 4, 4));
for (r, side) in [(1u32, 4u32), (2, 8), (3, 16), (4, 32), (5, 64)] {
for band in &geometry.resolutions[r as usize].bands {
assert_eq!(band.rect, rect(0, 0, side, side), "r={r}");
assert_eq!(single_blocks(band), vec![band.rect], "r={r}");
}
}
assert_eq!(
single_blocks(&geometry.resolutions[0].bands[0]),
vec![rect(0, 0, 4, 4)]
);
}
#[test]
fn tiled_zoo_edge_tile_partition() {
let siz = tiled_zoo_siz();
let tile = tile_rect(&siz, 4, 2);
assert_eq!(tile, rect(512, 256, 523, 311));
let style = coding_style(5, 6, 6, &[]);
let geometry = tile_component_geometry(tile, &siz.components[0], &style).unwrap();
check_invariants(&geometry);
let expected_resolutions = [
rect(16, 8, 17, 10),
rect(32, 16, 33, 20),
rect(64, 32, 66, 39),
rect(128, 64, 131, 78),
rect(256, 128, 262, 156),
rect(512, 256, 523, 311),
];
for (r, expected) in expected_resolutions.iter().enumerate() {
assert_eq!(&geometry.resolutions[r].rect, expected, "r={r}");
assert_eq!(
(
geometry.resolutions[r].precincts_wide,
geometry.resolutions[r].precincts_high
),
(1, 1),
"r={r}"
);
}
let bands1 = &geometry.resolutions[1].bands;
assert_eq!(bands1[0].rect, rect(16, 8, 16, 10));
assert!(bands1[0].rect.is_empty());
assert_eq!(bands1[1].rect, rect(16, 8, 17, 10));
assert_eq!(bands1[2].rect, rect(16, 8, 16, 10));
assert_eq!(bands1[0].precincts.len(), 1);
assert_eq!(
(
bands1[0].precincts[0].blocks_wide,
bands1[0].precincts[0].blocks_high
),
(0, 0)
);
assert!(bands1[0].precincts[0].blocks.is_empty());
assert_eq!(single_blocks(&bands1[1]), vec![rect(16, 8, 17, 10)]);
let bands2 = &geometry.resolutions[2].bands;
assert_eq!(bands2[0].rect, rect(32, 16, 33, 20));
assert_eq!(bands2[1].rect, rect(32, 16, 33, 19));
assert_eq!(bands2[2].rect, rect(32, 16, 33, 19));
let bands5 = &geometry.resolutions[5].bands;
assert_eq!(bands5[0].rect, rect(256, 128, 261, 156));
assert_eq!(bands5[1].rect, rect(256, 128, 262, 155));
assert_eq!(bands5[2].rect, rect(256, 128, 261, 155));
assert_eq!(single_blocks(&bands5[0]), vec![rect(256, 128, 261, 156)]);
assert_eq!(single_blocks(&bands5[1]), vec![rect(256, 128, 262, 155)]);
}
#[test]
fn offset_odd_tile_component_partition() {
let tile = rect(5, 3, 101, 69);
let style = coding_style(2, 3, 2, &[(3, 3), (4, 4), (4, 3)]);
let geometry = tile_component_geometry(tile, &unit_component(), &style).unwrap();
check_invariants(&geometry);
assert_eq!(geometry.rect, rect(5, 3, 101, 69));
assert_eq!(geometry.resolutions[0].rect, rect(2, 1, 26, 18));
assert_eq!(geometry.resolutions[1].rect, rect(3, 2, 51, 35));
assert_eq!(geometry.resolutions[2].rect, rect(5, 3, 101, 69));
let grids = [(3u8, 3u8, 4u32, 3u32), (4, 4, 4, 3), (4, 3, 7, 9)];
for (r, (ppx, ppy, wide, high)) in grids.iter().enumerate() {
let resolution = &geometry.resolutions[r];
assert_eq!((resolution.ppx, resolution.ppy), (*ppx, *ppy), "r={r}");
assert_eq!(
(resolution.precincts_wide, resolution.precincts_high),
(*wide, *high),
"r={r}"
);
}
assert_eq!(geometry.resolutions[0].bands[0].rect, rect(2, 1, 26, 18));
let bands1 = &geometry.resolutions[1].bands;
assert_eq!(bands1[0].rect, rect(1, 1, 25, 18));
assert_eq!(bands1[1].rect, rect(2, 1, 26, 17));
assert_eq!(bands1[2].rect, rect(1, 1, 25, 17));
let bands2 = &geometry.resolutions[2].bands;
assert_eq!(bands2[0].rect, rect(2, 2, 50, 35));
assert_eq!(bands2[1].rect, rect(3, 1, 51, 34));
assert_eq!(bands2[2].rect, rect(2, 1, 50, 34));
let ll = &geometry.resolutions[0].bands[0];
let p0 = &ll.precincts[0];
assert_eq!((p0.blocks_wide, p0.blocks_high), (1, 2));
assert_eq!(p0.blocks, vec![rect(2, 1, 8, 4), rect(2, 4, 8, 8)]);
let p5 = &ll.precincts[5];
assert_eq!((p5.blocks_wide, p5.blocks_high), (1, 2));
assert_eq!(p5.blocks, vec![rect(8, 8, 16, 12), rect(8, 12, 16, 16)]);
let p11 = &ll.precincts[11];
assert_eq!((p11.blocks_wide, p11.blocks_high), (1, 1));
assert_eq!(p11.blocks, vec![rect(24, 16, 26, 18)]);
let hl1 = &bands1[0];
assert_eq!(
hl1.precincts[0].blocks,
vec![rect(1, 1, 8, 4), rect(1, 4, 8, 8)]
);
assert_eq!(hl1.precincts[11].blocks, vec![rect(24, 16, 25, 18)]);
let hl2 = &bands2[0];
assert_eq!(hl2.precincts[0].blocks, vec![rect(2, 2, 8, 4)]);
assert_eq!(hl2.precincts[62].blocks, vec![rect(48, 32, 50, 35)]);
assert_eq!(bands2[1].precincts[62].blocks, vec![rect(48, 32, 51, 34)]);
for band in bands2 {
assert_eq!(single_blocks(band).len(), 63);
}
}
#[test]
fn precinct_zoo_partition() {
let tile = rect(0, 0, 523, 311);
let exps = [(2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7)];
let style = coding_style(5, 6, 6, &exps);
let geometry = tile_component_geometry(tile, &unit_component(), &style).unwrap();
check_invariants(&geometry);
let far = [
(17u32, 10u32),
(33, 20),
(66, 39),
(131, 78),
(262, 156),
(523, 311),
];
for (r, (x1, y1)) in far.iter().enumerate() {
let resolution = &geometry.resolutions[r];
assert_eq!(resolution.rect, rect(0, 0, *x1, *y1), "r={r}");
let (ppx, ppy) = exps[r];
assert_eq!((resolution.ppx, resolution.ppy), (ppx, ppy), "r={r}");
assert_eq!(
(resolution.precincts_wide, resolution.precincts_high),
(5, 3),
"r={r}"
);
}
let ll = &geometry.resolutions[0].bands[0];
let ll_blocks = single_blocks(ll);
assert_eq!(ll_blocks.len(), 15);
assert_eq!(ll_blocks[0], rect(0, 0, 4, 4));
assert_eq!(ll_blocks[4], rect(16, 0, 17, 4));
assert_eq!(ll_blocks[14], rect(16, 8, 17, 10));
let bands1 = &geometry.resolutions[1].bands;
assert_eq!(bands1[0].rect, rect(0, 0, 16, 10));
assert_eq!(bands1[1].rect, rect(0, 0, 17, 10));
assert_eq!(bands1[2].rect, rect(0, 0, 16, 10));
for band in [&bands1[0], &bands1[2]] {
for idx in [4usize, 9, 14] {
assert_eq!(band.precincts[idx].blocks_wide, 0, "idx={idx}");
assert!(band.precincts[idx].blocks.is_empty(), "idx={idx}");
}
assert_eq!(band.precincts[13].blocks, vec![rect(12, 8, 16, 10)]);
}
assert_eq!(bands1[1].precincts[4].blocks, vec![rect(16, 0, 17, 4)]);
assert_eq!(bands1[1].precincts[14].blocks, vec![rect(16, 8, 17, 10)]);
let bands5 = &geometry.resolutions[5].bands;
assert_eq!(bands5[0].rect, rect(0, 0, 261, 156));
assert_eq!(bands5[1].rect, rect(0, 0, 262, 155));
assert_eq!(bands5[2].rect, rect(0, 0, 261, 155));
assert_eq!(
bands5[0].precincts[14].blocks,
vec![rect(256, 128, 261, 156)]
);
assert_eq!(
bands5[1].precincts[14].blocks,
vec![rect(256, 128, 262, 155)]
);
assert_eq!(
bands5[2].precincts[14].blocks,
vec![rect(256, 128, 261, 155)]
);
}
#[test]
fn precinct_anchor_is_absolute() {
let tile = rect(100, 70, 120, 80);
let style = coding_style(0, 2, 2, &[(3, 3)]);
let geometry = tile_component_geometry(tile, &unit_component(), &style).unwrap();
check_invariants(&geometry);
assert_eq!(geometry.levels, 0);
assert_eq!(geometry.resolutions.len(), 1);
let resolution = &geometry.resolutions[0];
assert_eq!(resolution.rect, rect(100, 70, 120, 80));
assert_eq!(
(resolution.precincts_wide, resolution.precincts_high),
(3, 2)
);
let ll = &resolution.bands[0];
assert_eq!(ll.kind, BandKind::Ll);
assert_eq!(ll.level, 0);
assert_eq!(ll.rect, rect(100, 70, 120, 80));
assert_eq!(ll.precincts[0].blocks, vec![rect(100, 70, 104, 72)]);
assert_eq!(
(ll.precincts[1].blocks_wide, ll.precincts[1].blocks_high),
(2, 1)
);
assert_eq!(
ll.precincts[1].blocks,
vec![rect(104, 70, 108, 72), rect(108, 70, 112, 72)]
);
assert_eq!(
ll.precincts[5].blocks,
vec![
rect(112, 72, 116, 76),
rect(116, 72, 120, 76),
rect(112, 76, 116, 80),
rect(116, 76, 120, 80),
]
);
}
#[test]
fn degenerate_single_sample_lands_in_hh() {
let tile = rect(5, 3, 6, 4);
let style = coding_style(2, 6, 6, &[]);
let geometry = tile_component_geometry(tile, &unit_component(), &style).unwrap();
check_invariants(&geometry);
assert_eq!(geometry.resolutions[0].rect, rect(2, 1, 2, 1));
assert!(geometry.resolutions[0].rect.is_empty());
assert_eq!(
(
geometry.resolutions[0].precincts_wide,
geometry.resolutions[0].precincts_high
),
(0, 0)
);
assert!(geometry.resolutions[0].bands[0].precincts.is_empty());
assert_eq!(
(
geometry.resolutions[1].precincts_wide,
geometry.resolutions[1].precincts_high
),
(0, 0)
);
for band in &geometry.resolutions[1].bands {
assert!(band.rect.is_empty());
assert!(band.precincts.is_empty());
}
let resolution2 = &geometry.resolutions[2];
assert_eq!(resolution2.rect, rect(5, 3, 6, 4));
assert_eq!(
(resolution2.precincts_wide, resolution2.precincts_high),
(1, 1)
);
let bands2 = &resolution2.bands;
assert_eq!(bands2[0].rect, rect(2, 2, 3, 2));
assert!(bands2[0].rect.is_empty());
assert!(bands2[1].rect.is_empty());
assert_eq!(bands2[2].rect, rect(2, 1, 3, 2));
assert_eq!(bands2[0].precincts[0].blocks_wide, 0);
assert!(bands2[0].precincts[0].blocks.is_empty());
assert_eq!(bands2[2].precincts[0].blocks, vec![rect(2, 1, 3, 2)]);
}
#[test]
fn extreme_coordinates_do_not_overflow() {
let tile = rect(4294967000, 4294967293, 4294967295, 4294967295);
let style = coding_style(5, 6, 6, &[]);
let geometry = tile_component_geometry(tile, &unit_component(), &style).unwrap();
check_invariants(&geometry);
let hl5 = &geometry.resolutions[5].bands[0];
assert_eq!(
hl5.rect,
rect(2147483500, 2147483647, 2147483647, 2147483648)
);
let p0 = &hl5.precincts[0];
assert_eq!((p0.blocks_wide, p0.blocks_high), (3, 1));
assert_eq!(
p0.blocks,
vec![
rect(2147483500, 2147483647, 2147483520, 2147483648),
rect(2147483520, 2147483647, 2147483584, 2147483648),
rect(2147483584, 2147483647, 2147483647, 2147483648),
]
);
}
#[test]
fn rejects_zero_precinct_exponent_above_r0() {
let tile = rect(0, 0, 64, 64);
let style = coding_style(2, 6, 6, &[(0, 0), (0, 3), (3, 3)]);
let outcome = tile_component_geometry(tile, &unit_component(), &style);
assert!(matches!(outcome, Err(JpxError::Malformed(msg)) if msg.contains("precinct")));
}
#[test]
fn rejects_short_precinct_list() {
let tile = rect(0, 0, 64, 64);
let style = coding_style(2, 6, 6, &[(3, 3)]);
let outcome = tile_component_geometry(tile, &unit_component(), &style);
assert!(matches!(outcome, Err(JpxError::Malformed(msg)) if msg.contains("precinct")));
}
fn example_siz() -> Siz {
Siz {
rsiz: 0,
xsiz: 1432,
ysiz: 954,
xosiz: 152,
yosiz: 234,
xtsiz: 396,
ytsiz: 297,
xtosiz: 0,
ytosiz: 0,
components: vec![
SizComponent {
depth: 8,
signed: false,
xrsiz: 1,
yrsiz: 1,
},
SizComponent {
depth: 8,
signed: false,
xrsiz: 2,
yrsiz: 2,
},
],
}
}
#[test]
fn ceil_div_matches_the_b4_example() {
assert_eq!(ceil_div(1432, 396), 4);
assert_eq!(ceil_div(954, 297), 4);
assert_eq!(ceil_div(792, 396), 2);
assert_eq!(ceil_div(0, 7), 0);
assert_eq!(ceil_div(u64::MAX, 1), u64::MAX);
}
#[test]
fn rect_dimensions_follow_b2() {
let rect = Rect {
x0: 152,
y0: 234,
x1: 396,
y1: 297,
};
assert_eq!(rect.width(), 244);
assert_eq!(rect.height(), 63);
assert!(!rect.is_empty());
let degenerate = Rect {
x0: 5,
y0: 5,
x1: 5,
y1: 9,
};
assert_eq!(degenerate.width(), 0);
assert!(degenerate.is_empty());
}
#[test]
fn tile_grid_matches_the_b4_example() {
assert_eq!(tile_grid(&example_siz()).unwrap(), (4, 4));
}
#[test]
fn tile_grid_rejects_hostile_siz_fields() {
let mut siz = example_siz();
siz.xtsiz = 0;
assert!(matches!(tile_grid(&siz), Err(JpxError::Malformed(msg)) if msg.contains("tile")));
}
#[test]
fn tile_rects_match_the_b4_example() {
let siz = example_siz();
assert_eq!(
tile_rect(&siz, 0, 0),
Rect {
x0: 152,
y0: 234,
x1: 396,
y1: 297
}
);
assert_eq!(
tile_rect(&siz, 1, 1),
Rect {
x0: 396,
y0: 297,
x1: 792,
y1: 594
}
);
assert_eq!(
tile_rect(&siz, 3, 3),
Rect {
x0: 1188,
y0: 891,
x1: 1432,
y1: 954
}
);
}
#[test]
fn component_rect_matches_the_b4_example() {
let siz = example_siz();
let image = Rect {
x0: siz.xosiz,
y0: siz.yosiz,
x1: siz.xsiz,
y1: siz.ysiz,
};
let comp1 = component_rect(image, 2, 2).unwrap();
assert_eq!(
comp1,
Rect {
x0: 76,
y0: 117,
x1: 716,
y1: 477
}
);
assert_eq!(comp1.width(), 640);
assert_eq!(comp1.height(), 360);
let tile00 = component_rect(tile_rect(&siz, 0, 0), 2, 2).unwrap();
assert_eq!(
tile00,
Rect {
x0: 76,
y0: 117,
x1: 198,
y1: 149
}
);
let tile12 = component_rect(tile_rect(&siz, 1, 2), 2, 2).unwrap();
assert_eq!(
tile12,
Rect {
x0: 198,
y0: 297,
x1: 396,
y1: 446
}
);
assert_eq!(tile12.width(), 198);
assert_eq!(tile12.height(), 149);
}
#[test]
fn component_rect_rejects_zero_separation() {
let area = Rect {
x0: 0,
y0: 0,
x1: 8,
y1: 8,
};
assert!(component_rect(area, 0, 1).is_err());
}
fn measured_metadata_cost(geometry: &TileComponentGeometry) -> u64 {
let mut cost = 0u64;
for resolution in &geometry.resolutions {
for band in &resolution.bands {
for precinct in &band.precincts {
cost += PRECINCT_METADATA_BYTES;
cost += BLOCK_METADATA_BYTES * precinct.blocks.len() as u64;
}
}
}
cost
}
#[test]
fn partition_metadata_cost_matches_the_built_partition() {
let subsampled = SizComponent {
depth: 8,
signed: false,
xrsiz: 2,
yrsiz: 2,
};
let cases = [
(
rect(0, 0, 128, 128),
unit_component(),
coding_style(5, 6, 6, &[]),
),
(
rect(512, 256, 523, 311),
unit_component(),
coding_style(5, 6, 6, &[]),
),
(
rect(5, 3, 101, 69),
unit_component(),
coding_style(2, 3, 2, &[(3, 3), (4, 4), (4, 3)]),
),
(
rect(152, 234, 548, 531),
subsampled,
coding_style(3, 5, 3, &[(0, 0), (6, 5), (6, 5), (7, 7)]),
),
(
rect(0, 0, 97, 61),
unit_component(),
coding_style(0, 6, 6, &[(2, 2)]),
),
];
for (tile, component, style) in cases {
let cost = partition_metadata_cost(tile, &component, &style).unwrap();
let geometry = tile_component_geometry(tile, &component, &style).unwrap();
assert_eq!(cost, measured_metadata_cost(&geometry), "tile {tile:?}");
}
}
#[test]
fn partition_metadata_cost_flags_the_1x1_precinct_bomb() {
let style = coding_style(0, 4, 4, &[(0, 0)]);
let cost =
partition_metadata_cost(rect(0, 0, 8192, 8192), &unit_component(), &style).unwrap();
assert_eq!(cost, 2 * 64 * 8192 * 8192);
assert!(cost > crate::DecodeLimits::default().max_decoded_bytes);
}
}