use crate::error::{JpxError, Result};
use crate::geometry::{BandKind, Rect, TileComponentGeometry};
use crate::markers::{ComponentCoding, QuantizationStyle, SizComponent, WaveletKind};
use crate::t1::{BandCoefficients, CodeBlockCoefficients};
use crate::DecodeLimits;
#[derive(Debug)]
pub(crate) enum CoefficientCanvas {
Reversible(Vec<i32>),
Irreversible(Vec<f32>),
}
#[derive(Debug)]
pub(crate) struct TileComponentCanvas {
pub rect: Rect,
pub levels: u8,
pub samples: CoefficientCanvas,
}
pub(crate) fn dequantize_tile_component(
geometry: &TileComponentGeometry,
coding: &ComponentCoding,
component: &SizComponent,
bands: &[BandCoefficients],
limits: &DecodeLimits,
) -> Result<TileComponentCanvas> {
let _ = component.signed;
let rect = geometry.rect;
let bytes = (u64::from(rect.width()) * u64::from(rect.height()))
.checked_mul(4)
.ok_or_else(|| {
JpxError::Malformed("tile-component canvas exceeds the address space".into())
})?;
if bytes > limits.max_decoded_bytes {
return Err(JpxError::LimitExceeded {
what: "max_decoded_bytes",
actual: bytes,
limit: limits.max_decoded_bytes,
});
}
let count = usize::try_from(bytes / 4).map_err(|error| {
JpxError::Malformed(format!(
"tile-component canvas exceeds the address space: {error}"
))
})?;
let reversible = coding.style.wavelet == WaveletKind::Reversible53
&& matches!(&coding.quant.style, QuantizationStyle::None { .. });
let mut samples = if reversible {
CoefficientCanvas::Reversible(vec![0; count])
} else {
CoefficientCanvas::Irreversible(vec![0.0; count])
};
for band in bands {
let params = band_parameters(coding, component, geometry.levels, band)?;
for block in &band.blocks {
scatter_block(&mut samples, rect, band, block, ¶ms, coding.roi_shift);
}
}
Ok(TileComponentCanvas {
rect,
levels: geometry.levels,
samples,
})
}
struct BandParams {
mb: i32,
delta: f64,
}
fn gain_log2(kind: BandKind) -> i32 {
match kind {
BandKind::Ll => 0,
BandKind::Hl | BandKind::Lh => 1,
BandKind::Hh => 2,
}
}
fn subband_index(kind: BandKind, level: u8, levels: u8) -> Result<usize> {
let orientation = match kind {
BandKind::Ll => return Ok(0),
BandKind::Hl => 1,
BandKind::Lh => 2,
BandKind::Hh => 3,
};
if level == 0 || level > levels {
return Err(JpxError::Malformed(
"sub-band decomposition level outside 1..=NL".into(),
));
}
Ok(3 * usize::from(levels - level) + orientation)
}
fn step_size(rb: i32, eps: i32, mantissa: u16) -> f64 {
f64::from(rb - eps).exp2() * (1.0 + f64::from(mantissa) / 2048.0)
}
fn band_parameters(
coding: &ComponentCoding,
component: &SizComponent,
levels: u8,
band: &BandCoefficients,
) -> Result<BandParams> {
let guard = i32::from(coding.quant.guard_bits);
let rb = i32::from(component.depth) + gain_log2(band.kind);
let index = subband_index(band.kind, band.level, levels)?;
let quant = coding.quant.band_quant(levels, band.level, index);
let eps = quant.exponent.min(u32::from(u8::MAX)) as i32;
let delta = match &coding.quant.style {
QuantizationStyle::None { .. } => 1.0,
QuantizationStyle::ScalarDerived { .. } | QuantizationStyle::ScalarExpounded { .. } => {
step_size(rb, eps, quant.mantissa)
}
};
Ok(BandParams {
mb: guard + eps - 1,
delta,
})
}
fn undo_maxshift(magnitude: u32, planes: u8, shift: u8, mb: i32) -> (u32, u8) {
let scaled = u64::from(magnitude) >> u32::from(shift).min(63);
if scaled != 0 {
let planes = if i32::from(planes) >= mb {
mb.clamp(0, 255) as u8
} else {
planes
};
(scaled as u32, planes)
} else {
let planes = if i32::from(planes) >= mb {
planes.saturating_sub(shift)
} else {
planes
};
(magnitude, planes)
}
}
fn reconstruct_reversible(magnitude: u32, negative: bool, planes: u8, mb: i32) -> i32 {
if magnitude == 0 {
return 0;
}
let mut value = i64::from(magnitude);
let missing = mb - i32::from(planes);
if missing > 0 {
value += 1i64 << (missing - 1).min(61);
}
if negative {
value = -value;
}
value.clamp(i64::from(i32::MIN), i64::from(i32::MAX)) as i32
}
fn reconstruct_irreversible(
magnitude: u32,
negative: bool,
planes: u8,
mb: i32,
delta: f64,
) -> f32 {
if magnitude == 0 {
return 0.0;
}
let mut value = f64::from(magnitude);
let missing = mb - i32::from(planes);
if missing > 0 {
value += 0.5 * f64::from(missing).exp2();
}
if negative {
value = -value;
}
(value * delta) as f32
}
fn canvas_index(rect: Rect, level: u8, kind: BandKind, ub: u32, vb: u32) -> Option<usize> {
if level > 32 {
return None;
}
let (x, y) = if level == 0 {
(u64::from(ub), u64::from(vb))
} else {
let (xob, yob) = match kind {
BandKind::Ll => (0u64, 0u64),
BandKind::Hl => (1, 0),
BandKind::Lh => (0, 1),
BandKind::Hh => (1, 1),
};
let shift = u32::from(level);
(
(u64::from(ub) << shift) + (xob << (shift - 1)),
(u64::from(vb) << shift) + (yob << (shift - 1)),
)
};
if x < u64::from(rect.x0)
|| x >= u64::from(rect.x1)
|| y < u64::from(rect.y0)
|| y >= u64::from(rect.y1)
{
return None;
}
let index = (y - u64::from(rect.y0)) * u64::from(rect.width()) + (x - u64::from(rect.x0));
usize::try_from(index).ok()
}
fn scatter_block(
canvas: &mut CoefficientCanvas,
rect: Rect,
band: &BandCoefficients,
block: &CodeBlockCoefficients,
params: &BandParams,
roi_shift: Option<u8>,
) {
let width = block.rect.width() as usize;
let height = block.rect.height() as usize;
if width == 0 || height == 0 {
return;
}
for (i, &raw) in block.magnitudes.iter().take(width * height).enumerate() {
if raw == 0 {
continue;
}
let ub = block.rect.x0 + (i % width) as u32;
let vb = block.rect.y0 + (i / width) as u32;
let negative = block.negative.get(i).copied().unwrap_or(false);
let planes = block.decoded_planes.get(i).copied().unwrap_or(0);
let (magnitude, planes) = match roi_shift {
Some(shift) => undo_maxshift(raw, planes, shift, params.mb),
None => (raw, planes),
};
let Some(index) = canvas_index(rect, band.level, band.kind, ub, vb) else {
continue;
};
match canvas {
CoefficientCanvas::Reversible(values) => {
values[index] = reconstruct_reversible(magnitude, negative, planes, params.mb);
}
CoefficientCanvas::Irreversible(values) => {
values[index] =
reconstruct_irreversible(magnitude, negative, planes, params.mb, params.delta);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::tile_component_geometry;
use crate::markers::{CodingStyle, QuantStep, Quantization};
fn coding_style(levels: u8, wavelet: WaveletKind) -> CodingStyle {
CodingStyle {
decomposition_levels: levels,
code_block_width_exp: 6,
code_block_height_exp: 6,
code_block_style: 0,
wavelet,
precincts: Vec::new(),
}
}
fn component(depth: u8) -> SizComponent {
SizComponent {
depth,
signed: false,
xrsiz: 1,
yrsiz: 1,
}
}
fn coding(
levels: u8,
wavelet: WaveletKind,
guard_bits: u8,
style: QuantizationStyle,
roi_shift: Option<u8>,
) -> ComponentCoding {
ComponentCoding {
style: coding_style(levels, wavelet),
quant: Quantization { guard_bits, style },
roi_shift,
}
}
fn geometry_for(tile: Rect, levels: u8, wavelet: WaveletKind) -> TileComponentGeometry {
tile_component_geometry(tile, &component(8), &coding_style(levels, wavelet)).unwrap()
}
fn band_rect_of(geometry: &TileComponentGeometry, kind: BandKind, level: u8) -> Rect {
for resolution in &geometry.resolutions {
for band in &resolution.bands {
if band.kind == kind && band.level == level {
return band.rect;
}
}
}
panic!("band {kind:?} level {level} not in geometry");
}
fn zero_block(rect: Rect) -> CodeBlockCoefficients {
let count = (rect.width() * rect.height()) as usize;
CodeBlockCoefficients {
rect,
magnitudes: vec![0; count],
negative: vec![false; count],
decoded_planes: vec![0; count],
corrupt: false,
}
}
fn set_sample(
block: &mut CodeBlockCoefficients,
ub: u32,
vb: u32,
magnitude: u32,
negative: bool,
planes: u8,
) {
let index = ((vb - block.rect.y0) * block.rect.width() + (ub - block.rect.x0)) as usize;
block.magnitudes[index] = magnitude;
block.negative[index] = negative;
block.decoded_planes[index] = planes;
}
fn one_band(
kind: BandKind,
level: u8,
rect: Rect,
block: CodeBlockCoefficients,
) -> BandCoefficients {
BandCoefficients {
kind,
level,
rect,
blocks: vec![block],
}
}
fn floats(canvas: &TileComponentCanvas) -> &[f32] {
match &canvas.samples {
CoefficientCanvas::Irreversible(values) => values,
CoefficientCanvas::Reversible(values) => {
panic!("expected the f32 canvas, got i32 x {}", values.len())
}
}
}
fn ints(canvas: &TileComponentCanvas) -> &[i32] {
match &canvas.samples {
CoefficientCanvas::Reversible(values) => values,
CoefficientCanvas::Irreversible(values) => {
panic!("expected the i32 canvas, got f32 x {}", values.len())
}
}
}
#[test]
fn delta_steps_follow_equation_e3() {
let tile = Rect {
x0: 0,
y0: 0,
x1: 1,
y1: 1,
};
let cases: [(u8, u8, u16, f32); 4] = [
(8, 8, 0, 1.0),
(9, 7, 1024, 6.0),
(10, 12, 512, 0.3125),
(8, 5, 2047, 4095.0 / 256.0),
];
for (depth, exponent, mantissa, expected) in cases {
let geometry = geometry_for(tile, 0, WaveletKind::Irreversible97);
let coding = coding(
0,
WaveletKind::Irreversible97,
2,
QuantizationStyle::ScalarExpounded {
steps: vec![QuantStep { exponent, mantissa }],
},
None,
);
let mb = 2 + exponent - 1; let mut block = zero_block(tile);
set_sample(&mut block, 0, 0, 1, false, mb);
let bands = vec![one_band(BandKind::Ll, 0, tile, block)];
let canvas = dequantize_tile_component(
&geometry,
&coding,
&component(depth),
&bands,
&DecodeLimits::default(),
)
.unwrap();
assert_eq!(
floats(&canvas),
&[expected],
"RI={depth} eps={exponent} mu={mantissa}"
);
}
}
#[test]
fn derived_exponents_follow_equation_e5() {
let tile = Rect {
x0: 0,
y0: 0,
x1: 16,
y1: 16,
};
let geometry = geometry_for(tile, 3, WaveletKind::Irreversible97);
let coding = coding(
3,
WaveletKind::Irreversible97,
2,
QuantizationStyle::ScalarDerived {
exponent: 10,
mantissa: 1024,
},
None,
);
let cases: [(BandKind, u8, u32, u32, f32); 10] = [
(BandKind::Ll, 3, 0, 0, 0.375),
(BandKind::Hl, 3, 4, 0, 0.75),
(BandKind::Lh, 3, 0, 4, 0.75),
(BandKind::Hh, 3, 4, 4, 1.5),
(BandKind::Hl, 2, 2, 0, 1.5),
(BandKind::Lh, 2, 0, 2, 1.5),
(BandKind::Hh, 2, 2, 2, 3.0),
(BandKind::Hl, 1, 1, 0, 3.0),
(BandKind::Lh, 1, 0, 1, 3.0),
(BandKind::Hh, 1, 1, 1, 6.0),
];
let mut bands = Vec::new();
for (kind, level, ..) in cases {
let rect = band_rect_of(&geometry, kind, level);
let mut block = zero_block(rect);
let mb = 2 + (10 - 3 + level) - 1; set_sample(&mut block, rect.x0, rect.y0, 1, false, mb);
bands.push(one_band(kind, level, rect, block));
}
let canvas = dequantize_tile_component(
&geometry,
&coding,
&component(8),
&bands,
&DecodeLimits::default(),
)
.unwrap();
let values = floats(&canvas);
for (kind, level, x, y, delta) in cases {
assert_eq!(
values[(y * 16 + x) as usize],
delta,
"{kind:?} level {level}"
);
}
assert_eq!(values.iter().filter(|value| **value != 0.0).count(), 10);
}
#[test]
fn reversible_reconstruction_is_exact_or_midpoint() {
let tile = Rect {
x0: 0,
y0: 0,
x1: 6,
y1: 1,
};
let geometry = geometry_for(tile, 0, WaveletKind::Reversible53);
let coding = coding(
0,
WaveletKind::Reversible53,
1,
QuantizationStyle::None { exponents: vec![8] },
None,
);
let mut block = zero_block(tile);
set_sample(&mut block, 0, 0, 5, false, 8);
set_sample(&mut block, 1, 0, 5, true, 8);
set_sample(&mut block, 2, 0, 8, false, 5);
set_sample(&mut block, 3, 0, 8, true, 5);
set_sample(&mut block, 4, 0, 0, false, 5);
set_sample(&mut block, 5, 0, 0, true, 0);
let bands = vec![one_band(BandKind::Ll, 0, tile, block)];
let canvas = dequantize_tile_component(
&geometry,
&coding,
&component(8),
&bands,
&DecodeLimits::default(),
)
.unwrap();
assert_eq!(ints(&canvas), &[5, -5, 12, -12, 0, 0]);
}
#[test]
fn irreversible_reconstruction_applies_delta_and_midpoint() {
let tile = Rect {
x0: 0,
y0: 0,
x1: 6,
y1: 1,
};
let geometry = geometry_for(tile, 0, WaveletKind::Irreversible97);
let coding = coding(
0,
WaveletKind::Irreversible97,
1,
QuantizationStyle::ScalarExpounded {
steps: vec![QuantStep {
exponent: 8,
mantissa: 1024,
}],
},
None,
);
let mut block = zero_block(tile);
set_sample(&mut block, 0, 0, 5, false, 8);
set_sample(&mut block, 1, 0, 5, true, 8);
set_sample(&mut block, 2, 0, 8, false, 5);
set_sample(&mut block, 3, 0, 8, true, 5);
set_sample(&mut block, 4, 0, 0, false, 5);
set_sample(&mut block, 5, 0, 0, true, 0);
let bands = vec![one_band(BandKind::Ll, 0, tile, block)];
let canvas = dequantize_tile_component(
&geometry,
&coding,
&component(8),
&bands,
&DecodeLimits::default(),
)
.unwrap();
assert_eq!(floats(&canvas), &[7.5, -7.5, 18.0, -18.0, 0.0, 0.0]);
}
#[test]
fn band_gains_follow_table_e1() {
let tile = Rect {
x0: 0,
y0: 0,
x1: 4,
y1: 4,
};
let geometry = geometry_for(tile, 1, WaveletKind::Irreversible97);
let step = QuantStep {
exponent: 8,
mantissa: 0,
};
let coding = coding(
1,
WaveletKind::Irreversible97,
2,
QuantizationStyle::ScalarExpounded {
steps: vec![step; 4],
},
None,
);
let mb = 2 + 8 - 1; let cases: [(BandKind, usize, f32); 4] = [
(BandKind::Ll, 0, 1.0),
(BandKind::Hl, 1, 2.0),
(BandKind::Lh, 4, 2.0),
(BandKind::Hh, 5, 4.0),
];
let mut bands = Vec::new();
for (kind, ..) in cases {
let rect = band_rect_of(&geometry, kind, 1);
let mut block = zero_block(rect);
set_sample(&mut block, rect.x0, rect.y0, 1, false, mb);
bands.push(one_band(kind, 1, rect, block));
}
let canvas = dequantize_tile_component(
&geometry,
&coding,
&component(8),
&bands,
&DecodeLimits::default(),
)
.unwrap();
let values = floats(&canvas);
for (kind, index, expected) in cases {
assert_eq!(values[index], expected, "{kind:?}");
}
}
#[test]
fn expounded_steps_resolve_in_codestream_band_order() {
let tile = Rect {
x0: 0,
y0: 0,
x1: 4,
y1: 4,
};
let geometry = geometry_for(tile, 1, WaveletKind::Irreversible97);
let mantissas: [u16; 4] = [0, 1024, 512, 2047];
let coding = coding(
1,
WaveletKind::Irreversible97,
2,
QuantizationStyle::ScalarExpounded {
steps: mantissas
.iter()
.map(|&mantissa| QuantStep {
exponent: 8,
mantissa,
})
.collect(),
},
None,
);
let mb = 2 + 8 - 1; let cases: [(BandKind, usize, f32); 4] = [
(BandKind::Ll, 0, 1.0),
(BandKind::Hl, 1, 3.0),
(BandKind::Lh, 4, 2.5),
(BandKind::Hh, 5, 4095.0 / 512.0),
];
let mut bands = Vec::new();
for (kind, ..) in cases {
let rect = band_rect_of(&geometry, kind, 1);
let mut block = zero_block(rect);
set_sample(&mut block, rect.x0, rect.y0, 1, false, mb);
bands.push(one_band(kind, 1, rect, block));
}
let canvas = dequantize_tile_component(
&geometry,
&coding,
&component(8),
&bands,
&DecodeLimits::default(),
)
.unwrap();
let values = floats(&canvas);
for (kind, index, expected) in cases {
assert_eq!(values[index], expected, "{kind:?}");
}
}
#[test]
fn maxshift_undo_follows_h1() {
let tile = Rect {
x0: 0,
y0: 0,
x1: 5,
y1: 1,
};
let geometry = geometry_for(tile, 0, WaveletKind::Reversible53);
let coding = coding(
0,
WaveletKind::Reversible53,
1,
QuantizationStyle::None { exponents: vec![3] },
Some(3),
);
let mut block = zero_block(tile);
set_sample(&mut block, 0, 0, 40, false, 6);
set_sample(&mut block, 1, 0, 5, false, 6);
set_sample(&mut block, 2, 0, 4, false, 4);
set_sample(&mut block, 3, 0, 32, true, 2);
set_sample(&mut block, 4, 0, 0, false, 6);
let bands = vec![one_band(BandKind::Ll, 0, tile, block)];
let canvas = dequantize_tile_component(
&geometry,
&coding,
&component(8),
&bands,
&DecodeLimits::default(),
)
.unwrap();
assert_eq!(ints(&canvas), &[5, 5, 6, -5, 0]);
}
#[test]
fn interleave_places_every_band_at_its_f33_canvas_position() {
let tile = Rect {
x0: 3,
y0: 5,
x1: 11,
y1: 9,
};
let geometry = geometry_for(tile, 2, WaveletKind::Reversible53);
let coding = coding(
2,
WaveletKind::Reversible53,
1,
QuantizationStyle::None {
exponents: vec![8; 7],
},
None,
);
let cases: [(BandKind, u8, Rect, u32, u32, usize, i32); 7] = [
(
BandKind::Ll,
2,
Rect {
x0: 1,
y0: 2,
x1: 3,
y1: 3,
},
1,
2,
25,
1,
),
(
BandKind::Hl,
2,
Rect {
x0: 1,
y0: 2,
x1: 3,
y1: 3,
},
1,
2,
27,
2,
),
(
BandKind::Lh,
2,
Rect {
x0: 1,
y0: 1,
x1: 3,
y1: 2,
},
1,
1,
9,
3,
),
(
BandKind::Hh,
2,
Rect {
x0: 1,
y0: 1,
x1: 3,
y1: 2,
},
1,
1,
11,
4,
),
(
BandKind::Hl,
1,
Rect {
x0: 1,
y0: 3,
x1: 5,
y1: 5,
},
1,
3,
8,
5,
),
(
BandKind::Lh,
1,
Rect {
x0: 2,
y0: 2,
x1: 6,
y1: 4,
},
2,
2,
1,
6,
),
(
BandKind::Hh,
1,
Rect {
x0: 1,
y0: 2,
x1: 5,
y1: 4,
},
1,
2,
0,
7,
),
];
let mut bands = Vec::new();
for (kind, level, rect, ub, vb, _, marker) in cases {
assert_eq!(
band_rect_of(&geometry, kind, level),
rect,
"{kind:?} level {level}"
);
let mut block = zero_block(rect);
set_sample(&mut block, ub, vb, marker as u32, false, 8);
bands.push(one_band(kind, level, rect, block));
}
let canvas = dequantize_tile_component(
&geometry,
&coding,
&component(8),
&bands,
&DecodeLimits::default(),
)
.unwrap();
assert_eq!(canvas.rect, tile);
assert_eq!(canvas.levels, 2);
let values = ints(&canvas);
assert_eq!(values.len(), 32);
for (kind, level, _, _, _, index, marker) in cases {
assert_eq!(values[index], marker, "{kind:?} level {level}");
}
assert_eq!(
values.iter().map(|value| i64::from(*value)).sum::<i64>(),
28
);
assert_eq!(values.iter().filter(|value| **value != 0).count(), 7);
}
#[test]
fn canvas_variant_needs_both_reversible_wavelet_and_no_quantization() {
let tile = Rect {
x0: 0,
y0: 0,
x1: 1,
y1: 1,
};
let geometry = geometry_for(tile, 0, WaveletKind::Reversible53);
let coding_mixed = coding(
0,
WaveletKind::Reversible53,
2,
QuantizationStyle::ScalarExpounded {
steps: vec![QuantStep {
exponent: 8,
mantissa: 0,
}],
},
None,
);
let mut block = zero_block(tile);
set_sample(&mut block, 0, 0, 3, false, 9);
let bands = vec![one_band(BandKind::Ll, 0, tile, block)];
let canvas = dequantize_tile_component(
&geometry,
&coding_mixed,
&component(8),
&bands,
&DecodeLimits::default(),
)
.unwrap();
assert_eq!(floats(&canvas), &[3.0]);
let geometry = geometry_for(tile, 0, WaveletKind::Irreversible97);
let coding_ranged = coding(
0,
WaveletKind::Irreversible97,
1,
QuantizationStyle::None { exponents: vec![8] },
None,
);
let mut block = zero_block(tile);
set_sample(&mut block, 0, 0, 8, false, 5);
let bands = vec![one_band(BandKind::Ll, 0, tile, block)];
let canvas = dequantize_tile_component(
&geometry,
&coding_ranged,
&component(8),
&bands,
&DecodeLimits::default(),
)
.unwrap();
assert_eq!(floats(&canvas), &[12.0]);
}
#[test]
fn missing_expounded_step_derives_from_the_first_entry() {
let tile = Rect {
x0: 0,
y0: 0,
x1: 4,
y1: 4,
};
let geometry = geometry_for(tile, 1, WaveletKind::Irreversible97);
let coding = coding(
1,
WaveletKind::Irreversible97,
2,
QuantizationStyle::ScalarExpounded {
steps: vec![QuantStep {
exponent: 8,
mantissa: 0,
}],
},
None,
);
let rect = band_rect_of(&geometry, BandKind::Hh, 1);
let mut block = zero_block(rect);
set_sample(&mut block, 0, 0, 1, false, 9);
let bands = vec![one_band(BandKind::Hh, 1, rect, block)];
let canvas = dequantize_tile_component(
&geometry,
&coding,
&component(8),
&bands,
&DecodeLimits::default(),
)
.unwrap();
let values = floats(&canvas);
assert_eq!(values.iter().filter(|v| **v != 0.0).count(), 1);
assert!(values.contains(&4.0), "derived Delta drifted: {values:?}");
}
#[test]
fn samples_outside_the_canvas_are_dropped_not_panicked() {
let tile = Rect {
x0: 0,
y0: 0,
x1: 2,
y1: 1,
};
let geometry = geometry_for(tile, 0, WaveletKind::Reversible53);
let coding = coding(
0,
WaveletKind::Reversible53,
1,
QuantizationStyle::None { exponents: vec![8] },
None,
);
let hostile = Rect {
x0: 0,
y0: 0,
x1: 3,
y1: 1,
};
let mut block = zero_block(hostile);
set_sample(&mut block, 0, 0, 1, false, 8);
set_sample(&mut block, 1, 0, 2, false, 8);
set_sample(&mut block, 2, 0, 3, false, 8);
let bands = vec![one_band(BandKind::Ll, 0, hostile, block)];
let canvas = dequantize_tile_component(
&geometry,
&coding,
&component(8),
&bands,
&DecodeLimits::default(),
)
.unwrap();
assert_eq!(ints(&canvas), &[1, 2]);
}
#[test]
fn max_decoded_bytes_bounds_the_canvas_allocation() {
let tile = Rect {
x0: 0,
y0: 0,
x1: 5,
y1: 1,
};
let geometry = geometry_for(tile, 0, WaveletKind::Reversible53);
let coding = coding(
0,
WaveletKind::Reversible53,
1,
QuantizationStyle::None { exponents: vec![8] },
None,
);
let bands = vec![one_band(BandKind::Ll, 0, tile, zero_block(tile))];
let tight = DecodeLimits {
max_decoded_bytes: 19,
..DecodeLimits::default()
};
assert!(matches!(
dequantize_tile_component(&geometry, &coding, &component(8), &bands, &tight),
Err(JpxError::LimitExceeded {
what: "max_decoded_bytes",
actual: 20,
limit: 19,
})
));
let exact = DecodeLimits {
max_decoded_bytes: 20,
..DecodeLimits::default()
};
dequantize_tile_component(&geometry, &coding, &component(8), &bands, &exact).unwrap();
}
#[test]
fn rgn_streams_compose_across_the_tier2_and_dequant_seams() {
let shift = 3u8;
let coding = coding(
0,
WaveletKind::Reversible53,
1,
QuantizationStyle::None { exponents: vec![3] },
Some(shift),
);
let coded_planes = crate::packet::band_magnitude_bits(&coding, 0, 0, 0);
assert_eq!(coded_planes, 6);
let tile = Rect {
x0: 0,
y0: 0,
x1: 2,
y1: 1,
};
let geometry = geometry_for(tile, 0, WaveletKind::Reversible53);
let mut block = zero_block(tile);
set_sample(&mut block, 0, 0, 5 << shift, false, coded_planes);
set_sample(&mut block, 1, 0, 5, true, coded_planes);
let bands = vec![one_band(BandKind::Ll, 0, tile, block)];
let canvas = dequantize_tile_component(
&geometry,
&coding,
&component(8),
&bands,
&DecodeLimits::default(),
)
.unwrap();
assert_eq!(ints(&canvas), &[5, -5]);
}
}