use std::collections::HashMap;
use super::bitreader::J2kBitReader;
use super::box_parser::{is_jp2_container, parse_jp2, Jp2ColorSpace};
use super::markers::{parse_codestream, CodMarker, MarkerSegment, QcdMarker, SizMarker};
use super::tier1::decode_code_block;
use super::tier2::parse_packet_header;
use super::wavelet::{
reconstruct_levels, reconstruct_levels_97, SubbandLevel, SubbandLevel97, SubbandTree,
SubbandTree97,
};
use super::{Jp2Error, Jp2Result};
#[derive(Debug, Clone)]
pub struct DecodedImage {
pub width: u32,
pub height: u32,
pub num_components: u16,
pub bit_depth: u8,
pub samples: Vec<Vec<u16>>,
}
pub struct Jpeg2000Decoder;
impl Jpeg2000Decoder {
#[must_use]
pub fn new() -> Self {
Self
}
pub fn decode(data: &[u8]) -> Jp2Result<DecodedImage> {
if is_jp2_container(data) {
let (_header, codestream) = parse_jp2(data)?;
decode_codestream(codestream)
} else {
decode_codestream(data)
}
}
}
fn decode_codestream(data: &[u8]) -> Jp2Result<DecodedImage> {
let segments = parse_codestream(data)?;
let siz = find_siz(&segments)?;
let cod = find_cod(&segments)?;
let qcd = find_qcd(&segments);
let tile_map = collect_tile_map(&segments)?;
if !cod.is_lossless_wavelet() && !cod.is_irreversible_97() {
return Err(Jp2Error::Unsupported(format!(
"unknown wavelet filter {} — only 5-3 (filter=1) and 9-7 (filter=0) are supported",
cod.wavelet_filter
)));
}
if cod.num_layers != 1 {
return Err(Jp2Error::MultiTileOrLayer);
}
let width = siz.image_width() as usize;
let height = siz.image_height() as usize;
let num_comp = siz.csiz as usize;
let num_levels = usize::from(cod.num_decomp_levels);
if width == 0 || height == 0 {
return Err(Jp2Error::Unsupported("zero-dimension image".to_string()));
}
if num_comp == 0 {
return Err(Jp2Error::Unsupported("zero-component image".to_string()));
}
let num_tiles_x = siz.num_tiles_x() as usize;
let num_tiles_y = siz.num_tiles_y() as usize;
let num_tiles = num_tiles_x * num_tiles_y;
let mut full_samples: Vec<Vec<u16>> = (0..num_comp)
.map(|c| {
let xr = siz.components[c].xr_siz as usize;
let yr = siz.components[c].yr_siz as usize;
let comp_w = (width + xr - 1) / xr;
let comp_h = (height + yr - 1) / yr;
vec![0u16; comp_w * comp_h]
})
.collect();
for tile_idx in 0..num_tiles {
let tile_data = tile_map.get(&(tile_idx as u16)).ok_or_else(|| {
Jp2Error::Unsupported(format!("missing tile data for tile {tile_idx}"))
})?;
let (_tx, _ty, tw, th) = siz.tile_rect(tile_idx as u32);
let tile_w = tw as usize;
let tile_h = th as usize;
if tile_w == 0 || tile_h == 0 {
continue;
}
for comp_idx in 0..num_comp {
let bit_depth = siz.components[comp_idx].bit_depth();
let xr = siz.components[comp_idx].xr_siz as usize;
let yr = siz.components[comp_idx].yr_siz as usize;
let comp_tw = (tile_w + xr - 1) / xr;
let comp_th = (tile_h + yr - 1) / yr;
let tile_comp_samples = if cod.is_lossless_wavelet() {
decode_component_53(
tile_data, comp_tw, comp_th, num_levels, &cod, bit_depth, comp_idx, num_comp,
)?
} else {
decode_component_97(
tile_data, comp_tw, comp_th, num_levels, &cod, qcd, bit_depth, comp_idx,
num_comp,
)?
};
let (tx, ty, _tw2, _th2) = siz.tile_rect(tile_idx as u32);
let comp_tx = tx as usize / xr;
let comp_ty = ty as usize / yr;
let full_comp_w = (width + xr - 1) / xr;
for row in 0..comp_th {
let src_start = row * comp_tw;
let src_end = src_start + comp_tw;
let dst_start = (comp_ty + row) * full_comp_w + comp_tx;
full_samples[comp_idx][dst_start..dst_start + comp_tw]
.copy_from_slice(&tile_comp_samples[src_start..src_end]);
}
}
}
let bit_depth = if num_comp > 0 {
siz.components[0].bit_depth()
} else {
8
};
Ok(DecodedImage {
width: siz.image_width(),
height: siz.image_height(),
num_components: siz.csiz,
bit_depth,
samples: full_samples,
})
}
#[allow(clippy::too_many_arguments)]
fn decode_component_53(
tile_data: &[u8],
tile_w: usize,
tile_h: usize,
num_levels: usize,
cod: &CodMarker,
bit_depth: u8,
comp_idx: usize,
num_comp: usize,
) -> Jp2Result<Vec<u16>> {
let width = tile_w;
let height = tile_h;
let cb_w = 1usize << (usize::from(cod.xcb) + 2);
let cb_h = 1usize << (usize::from(cod.ycb) + 2);
let subband_dims = compute_subband_dims(width, height, num_levels);
let mut t2_reader = J2kBitReader::new(tile_data);
let total_blocks_per_level: Vec<usize> = subband_dims
.iter()
.map(|(sw, sh)| {
let num_cb_h = (sw + cb_w - 1) / cb_w;
let num_cb_v = (sh + cb_h - 1) / cb_h;
num_cb_h * num_cb_v
})
.collect();
let total_blocks_per_comp: usize = {
let ll_dims = coarsest_ll_dims(width, height, num_levels);
let ll_blocks = {
let (llw, llh) = ll_dims;
let nh = (llw + cb_w - 1) / cb_w;
let nv = (llh + cb_h - 1) / cb_h;
nh * nv
};
ll_blocks + total_blocks_per_level.iter().sum::<usize>() * 3
};
let header = parse_packet_header(&mut t2_reader, total_blocks_per_comp)?;
t2_reader.align_to_byte();
let data_start = tile_data.len() - t2_reader.remaining();
let num_bit_planes = bit_depth;
let subband_coeffs = decode_all_subbands_53(
&tile_data[data_start..],
&header,
&subband_dims,
coarsest_ll_dims(width, height, num_levels),
cb_w,
cb_h,
num_bit_planes,
comp_idx,
num_comp,
)?;
let reconstructed = reconstruct_levels(&subband_coeffs, num_levels, width, height)?;
let max_val = (1u32 << bit_depth) - 1;
let samples: Vec<u16> = reconstructed
.iter()
.map(|&v| v.max(0).min(max_val as i32) as u16)
.collect();
Ok(samples)
}
#[allow(clippy::too_many_arguments)]
fn decode_component_97(
tile_data: &[u8],
tile_w: usize,
tile_h: usize,
num_levels: usize,
cod: &CodMarker,
qcd: Option<&QcdMarker>,
bit_depth: u8,
comp_idx: usize,
num_comp: usize,
) -> Jp2Result<Vec<u16>> {
let width = tile_w;
let height = tile_h;
let cb_w = 1usize << (usize::from(cod.xcb) + 2);
let cb_h = 1usize << (usize::from(cod.ycb) + 2);
let subband_dims = compute_subband_dims(width, height, num_levels);
let mut t2_reader = J2kBitReader::new(tile_data);
let total_blocks_per_level: Vec<usize> = subband_dims
.iter()
.map(|(sw, sh)| {
let num_cb_h = (sw + cb_w - 1) / cb_w;
let num_cb_v = (sh + cb_h - 1) / cb_h;
num_cb_h * num_cb_v
})
.collect();
let total_blocks_per_comp: usize = {
let ll_dims = coarsest_ll_dims(width, height, num_levels);
let ll_blocks = {
let (llw, llh) = ll_dims;
let nh = (llw + cb_w - 1) / cb_w;
let nv = (llh + cb_h - 1) / cb_h;
nh * nv
};
ll_blocks + total_blocks_per_level.iter().sum::<usize>() * 3
};
let header = parse_packet_header(&mut t2_reader, total_blocks_per_comp)?;
t2_reader.align_to_byte();
let data_start = tile_data.len() - t2_reader.remaining();
let num_bit_planes = bit_depth;
let guard_bits = qcd.map(|q| q.guard_bits()).unwrap_or(2);
let subband_coeffs_97 = decode_all_subbands_97(
&tile_data[data_start..],
&header,
&subband_dims,
coarsest_ll_dims(width, height, num_levels),
cb_w,
cb_h,
num_bit_planes,
comp_idx,
num_comp,
qcd,
bit_depth,
guard_bits,
)?;
let reconstructed = reconstruct_levels_97(&subband_coeffs_97, num_levels, width, height)?;
let max_val = ((1u32 << bit_depth) - 1) as f64;
let samples: Vec<u16> = reconstructed
.iter()
.map(|&v| {
let clipped = (v + 0.5).floor().max(0.0).min(max_val);
clipped as u16
})
.collect();
Ok(samples)
}
fn compute_subband_dims(width: usize, height: usize, num_levels: usize) -> Vec<(usize, usize)> {
let mut dims = Vec::with_capacity(num_levels);
let mut w = width;
let mut h = height;
for _ in 0..num_levels {
let sub_w = w / 2;
let sub_h = h / 2;
dims.push((sub_w.max(1), sub_h.max(1)));
w = (w + 1) / 2;
h = (h + 1) / 2;
}
dims.reverse();
dims
}
fn coarsest_ll_dims(width: usize, height: usize, num_levels: usize) -> (usize, usize) {
let mut w = width;
let mut h = height;
for _ in 0..num_levels {
w = (w + 1) / 2;
h = (h + 1) / 2;
}
(w.max(1), h.max(1))
}
#[allow(clippy::too_many_arguments)]
fn decode_all_subbands_53(
data: &[u8],
header: &super::tier2::PacketHeader,
subband_dims: &[(usize, usize)],
ll_dims: (usize, usize),
cb_w: usize,
cb_h: usize,
num_bit_planes: u8,
_comp_idx: usize,
_num_comp: usize,
) -> Jp2Result<SubbandTree> {
let num_levels = subband_dims.len();
let mut data_offset = 0usize;
let mut block_idx = 0usize;
let (ll_w, ll_h) = ll_dims;
let ll_cb_h = (ll_w + cb_w - 1) / cb_w;
let ll_cb_v = (ll_h + cb_h - 1) / cb_h;
let ll_num_blocks = ll_cb_h * ll_cb_v;
let mut ll_coeffs = vec![0i32; ll_w * ll_h];
for block_row in 0..ll_cb_v {
for block_col in 0..ll_cb_h {
let cur_cb_w = (cb_w).min(ll_w - block_col * cb_w);
let cur_cb_h = (cb_h).min(ll_h - block_row * cb_h);
let included =
block_idx < header.included_blocks.len() && header.included_blocks[block_idx];
let len = if block_idx < header.data_lengths.len() {
header.data_lengths[block_idx]
} else {
0
};
if included && len > 0 {
let end = (data_offset + len).min(data.len());
let block_data = &data[data_offset..end];
let block = decode_code_block(block_data, cur_cb_w, cur_cb_h, num_bit_planes)?;
for r in 0..cur_cb_h {
let src_row_start = r * cur_cb_w;
let dst_row_start = (block_row * cb_h + r) * ll_w + block_col * cb_w;
let copy_len = cur_cb_w.min(ll_w - block_col * cb_w);
if dst_row_start + copy_len <= ll_coeffs.len() {
ll_coeffs[dst_row_start..dst_row_start + copy_len].copy_from_slice(
&block.coeffs[src_row_start..src_row_start + copy_len],
);
}
}
data_offset += len;
}
block_idx += 1;
}
}
let _ = ll_num_blocks;
let mut levels: Vec<SubbandLevel> = Vec::with_capacity(num_levels);
for level_idx in 0..num_levels {
let (sub_w, sub_h) = subband_dims[level_idx];
let sub_cb_h = (sub_w + cb_w - 1) / cb_w;
let sub_cb_v = (sub_h + cb_h - 1) / cb_h;
let mut hl = vec![0i32; sub_w * sub_h];
let mut lh = vec![0i32; sub_w * sub_h];
let mut hh = vec![0i32; sub_w * sub_h];
for (subband_coeffs, _name) in [(&mut hl, "HL"), (&mut lh, "LH"), (&mut hh, "HH")] {
for block_row in 0..sub_cb_v {
for block_col in 0..sub_cb_h {
let cur_cb_w = cb_w.min(sub_w - block_col * cb_w);
let cur_cb_h = cb_h.min(sub_h - block_row * cb_h);
let included = block_idx < header.included_blocks.len()
&& header.included_blocks[block_idx];
let len = if block_idx < header.data_lengths.len() {
header.data_lengths[block_idx]
} else {
0
};
if included && len > 0 {
let end = (data_offset + len).min(data.len());
let block_data = &data[data_offset..end];
let block =
decode_code_block(block_data, cur_cb_w, cur_cb_h, num_bit_planes)?;
for r in 0..cur_cb_h {
let src_start = r * cur_cb_w;
let dst_start = (block_row * cb_h + r) * sub_w + block_col * cb_w;
let copy_len = cur_cb_w.min(sub_w - block_col * cb_w);
if dst_start + copy_len <= subband_coeffs.len() {
subband_coeffs[dst_start..dst_start + copy_len].copy_from_slice(
&block.coeffs[src_start..src_start + copy_len],
);
}
}
data_offset += len;
}
block_idx += 1;
}
}
}
levels.push(SubbandLevel {
hl,
lh,
hh,
width: sub_w,
height: sub_h,
});
}
Ok(SubbandTree {
ll: ll_coeffs,
ll_width: ll_w,
ll_height: ll_h,
levels,
})
}
#[allow(clippy::too_many_arguments)]
fn decode_all_subbands_97(
data: &[u8],
header: &super::tier2::PacketHeader,
subband_dims: &[(usize, usize)],
ll_dims: (usize, usize),
cb_w: usize,
cb_h: usize,
num_bit_planes: u8,
_comp_idx: usize,
_num_comp: usize,
qcd: Option<&QcdMarker>,
bit_depth: u8,
_guard_bits: u8,
) -> Jp2Result<SubbandTree97> {
let num_levels = subband_dims.len();
let mut data_offset = 0usize;
let mut block_idx = 0usize;
let mut subband_qcd_idx = 0usize;
let (ll_w, ll_h) = ll_dims;
let ll_cb_h = (ll_w + cb_w - 1) / cb_w;
let ll_cb_v = (ll_h + cb_h - 1) / cb_h;
let step_ll = qcd
.map(|q| q.step_size_for_subband(subband_qcd_idx, bit_depth))
.unwrap_or(1.0);
subband_qcd_idx += 1;
let mut ll_coeffs = vec![0.0f64; ll_w * ll_h];
for block_row in 0..ll_cb_v {
for block_col in 0..ll_cb_h {
let cur_cb_w = cb_w.min(ll_w - block_col * cb_w);
let cur_cb_h = cb_h.min(ll_h - block_row * cb_h);
let included =
block_idx < header.included_blocks.len() && header.included_blocks[block_idx];
let len = if block_idx < header.data_lengths.len() {
header.data_lengths[block_idx]
} else {
0
};
if included && len > 0 {
let end = (data_offset + len).min(data.len());
let block_data = &data[data_offset..end];
let block = decode_code_block(block_data, cur_cb_w, cur_cb_h, num_bit_planes)?;
let dq = block.dequantize(step_ll, usize::from(num_bit_planes));
for r in 0..cur_cb_h {
let src_start = r * cur_cb_w;
let dst_start = (block_row * cb_h + r) * ll_w + block_col * cb_w;
let copy_len = cur_cb_w.min(ll_w - block_col * cb_w);
if dst_start + copy_len <= ll_coeffs.len() {
ll_coeffs[dst_start..dst_start + copy_len]
.copy_from_slice(&dq[src_start..src_start + copy_len]);
}
}
data_offset += len;
}
block_idx += 1;
}
}
let mut levels: Vec<SubbandLevel97> = Vec::with_capacity(num_levels);
for level_idx in 0..num_levels {
let (sub_w, sub_h) = subband_dims[level_idx];
let sub_cb_h = (sub_w + cb_w - 1) / cb_w;
let sub_cb_v = (sub_h + cb_h - 1) / cb_h;
let step_hl = qcd
.map(|q| q.step_size_for_subband(subband_qcd_idx, bit_depth))
.unwrap_or(1.0);
subband_qcd_idx += 1;
let step_lh = qcd
.map(|q| q.step_size_for_subband(subband_qcd_idx, bit_depth))
.unwrap_or(1.0);
subband_qcd_idx += 1;
let step_hh = qcd
.map(|q| q.step_size_for_subband(subband_qcd_idx, bit_depth))
.unwrap_or(1.0);
subband_qcd_idx += 1;
let mut hl = vec![0.0f64; sub_w * sub_h];
let mut lh = vec![0.0f64; sub_w * sub_h];
let mut hh = vec![0.0f64; sub_w * sub_h];
for (subband_coeffs, step) in [(&mut hl, step_hl), (&mut lh, step_lh), (&mut hh, step_hh)] {
for block_row in 0..sub_cb_v {
for block_col in 0..sub_cb_h {
let cur_cb_w = cb_w.min(sub_w - block_col * cb_w);
let cur_cb_h = cb_h.min(sub_h - block_row * cb_h);
let included = block_idx < header.included_blocks.len()
&& header.included_blocks[block_idx];
let len = if block_idx < header.data_lengths.len() {
header.data_lengths[block_idx]
} else {
0
};
if included && len > 0 {
let end = (data_offset + len).min(data.len());
let block_data = &data[data_offset..end];
let block =
decode_code_block(block_data, cur_cb_w, cur_cb_h, num_bit_planes)?;
let dq = block.dequantize(step, usize::from(num_bit_planes));
for r in 0..cur_cb_h {
let src_start = r * cur_cb_w;
let dst_start = (block_row * cb_h + r) * sub_w + block_col * cb_w;
let copy_len = cur_cb_w.min(sub_w - block_col * cb_w);
if dst_start + copy_len <= subband_coeffs.len() {
subband_coeffs[dst_start..dst_start + copy_len]
.copy_from_slice(&dq[src_start..src_start + copy_len]);
}
}
data_offset += len;
}
block_idx += 1;
}
}
}
levels.push(SubbandLevel97 {
hl,
lh,
hh,
width: sub_w,
height: sub_h,
});
}
Ok(SubbandTree97 {
ll: ll_coeffs,
ll_width: ll_w,
ll_height: ll_h,
levels,
})
}
fn find_siz(segments: &[MarkerSegment]) -> Jp2Result<&SizMarker> {
for seg in segments {
if let MarkerSegment::Siz(siz) = seg {
return Ok(siz);
}
}
Err(Jp2Error::Unsupported(
"codestream missing SIZ marker".to_string(),
))
}
fn find_cod(segments: &[MarkerSegment]) -> Jp2Result<&CodMarker> {
for seg in segments {
if let MarkerSegment::Cod(cod) = seg {
return Ok(cod);
}
}
Err(Jp2Error::Unsupported(
"codestream missing COD marker".to_string(),
))
}
fn find_qcd(segments: &[MarkerSegment]) -> Option<&QcdMarker> {
segments.iter().find_map(|seg| {
if let MarkerSegment::Qcd(qcd) = seg {
Some(qcd)
} else {
None
}
})
}
fn collect_tile_map(segments: &[MarkerSegment]) -> Jp2Result<HashMap<u16, Vec<u8>>> {
let mut map: HashMap<u16, Vec<u8>> = HashMap::new();
let mut current_tile: Option<u16> = None;
for seg in segments {
match seg {
MarkerSegment::Sot(sot) => {
current_tile = Some(sot.isot);
}
MarkerSegment::Sod { data } => {
let tile_idx = current_tile.ok_or_else(|| {
Jp2Error::Unsupported("SOD without preceding SOT".to_string())
})?;
map.entry(tile_idx).or_default().extend_from_slice(data);
current_tile = None;
}
_ => {}
}
}
if map.is_empty() {
return Err(Jp2Error::Unsupported(
"codestream missing SOD (tile data)".to_string(),
));
}
Ok(map)
}
pub fn build_constant_j2k(
width: u16,
height: u16,
value: u8,
num_decomp_levels: u8,
) -> Jp2Result<Vec<u8>> {
use super::markers::{COD, EOC, QCD, SIZ, SOC, SOD, SOT};
let mut v: Vec<u8> = Vec::new();
v.extend_from_slice(&SOC.to_be_bytes());
let siz_len: u16 = 2 + 36 + 3; v.extend_from_slice(&SIZ.to_be_bytes());
v.extend_from_slice(&siz_len.to_be_bytes());
v.extend_from_slice(&0u16.to_be_bytes()); v.extend_from_slice(&(width as u32).to_be_bytes()); v.extend_from_slice(&(height as u32).to_be_bytes()); v.extend_from_slice(&0u32.to_be_bytes()); v.extend_from_slice(&0u32.to_be_bytes()); v.extend_from_slice(&(width as u32).to_be_bytes()); v.extend_from_slice(&(height as u32).to_be_bytes()); v.extend_from_slice(&0u32.to_be_bytes()); v.extend_from_slice(&0u32.to_be_bytes()); v.extend_from_slice(&1u16.to_be_bytes()); v.push(7); v.push(1); v.push(1);
let cod_len: u16 = 2 + 10;
v.extend_from_slice(&COD.to_be_bytes());
v.extend_from_slice(&cod_len.to_be_bytes());
v.push(0); v.push(0); v.extend_from_slice(&1u16.to_be_bytes()); v.push(0); v.push(num_decomp_levels); v.push(2); v.push(2); v.push(0); v.push(1);
let num_subbands: usize = 1 + 3 * usize::from(num_decomp_levels);
let qcd_len: u16 = 2 + 1 + num_subbands as u16;
v.extend_from_slice(&QCD.to_be_bytes());
v.extend_from_slice(&qcd_len.to_be_bytes());
v.push(0); for _ in 0..num_subbands {
v.push(0x00); }
let sot_len: u16 = 10;
v.extend_from_slice(&SOT.to_be_bytes());
v.extend_from_slice(&sot_len.to_be_bytes());
v.extend_from_slice(&0u16.to_be_bytes()); v.extend_from_slice(&0u32.to_be_bytes()); v.push(0); v.push(1);
v.extend_from_slice(&SOD.to_be_bytes());
let tile_data =
build_constant_tile_data(width as usize, height as usize, value, num_decomp_levels);
v.extend_from_slice(&tile_data);
v.extend_from_slice(&EOC.to_be_bytes());
Ok(v)
}
fn build_constant_tile_data(
width: usize,
height: usize,
value: u8,
num_decomp_levels: u8,
) -> Vec<u8> {
let _ = (width, height, value, num_decomp_levels);
vec![0x00] }
#[cfg(test)]
mod tests {
use super::*;
use crate::jpeg2000::markers::{parse_codestream, MarkerSegment};
#[test]
fn decoder_new() {
let _dec = Jpeg2000Decoder::new();
}
#[test]
fn decode_accepts_97_wavelet() {
let data = build_minimal_codestream_wavelet_filter(0);
let result = Jpeg2000Decoder::decode(&data);
assert!(
result.is_ok(),
"Expected Ok for 9-7 wavelet filter, got {result:?}"
);
}
#[test]
fn decode_rejects_unknown_wavelet_filter() {
let data = build_minimal_codestream_wavelet_filter(2);
let result = Jpeg2000Decoder::decode(&data);
assert!(
result.is_err(),
"Expected error for unknown wavelet filter 2, got Ok"
);
}
#[test]
fn decode_rejects_multi_layer() {
let data = build_minimal_codestream_num_layers(2);
let result = Jpeg2000Decoder::decode(&data);
assert!(
result.is_err(),
"Expected error for multi-layer stream, got Ok"
);
}
#[test]
fn build_constant_j2k_produces_valid_header() {
let j2k = build_constant_j2k(16, 16, 128, 1).expect("build");
assert_eq!(&j2k[0..2], &[0xFF, 0x4F]);
assert_eq!(&j2k[j2k.len() - 2..], &[0xFF, 0xD9]);
let segments = parse_codestream(&j2k).expect("parse");
let has_siz = segments.iter().any(|s| matches!(s, MarkerSegment::Siz(_)));
let has_cod = segments.iter().any(|s| matches!(s, MarkerSegment::Cod(_)));
assert!(has_siz);
assert!(has_cod);
}
fn build_minimal_codestream_wavelet_filter(filter: u8) -> Vec<u8> {
use crate::jpeg2000::markers::{COD, EOC, QCD, SIZ, SOC, SOD, SOT};
let mut v = Vec::new();
v.extend_from_slice(&SOC.to_be_bytes());
let siz_len: u16 = 2 + 36 + 3;
v.extend_from_slice(&SIZ.to_be_bytes());
v.extend_from_slice(&siz_len.to_be_bytes());
v.extend_from_slice(&0u16.to_be_bytes());
v.extend_from_slice(&4u32.to_be_bytes()); v.extend_from_slice(&4u32.to_be_bytes()); v.extend_from_slice(&0u32.to_be_bytes());
v.extend_from_slice(&0u32.to_be_bytes());
v.extend_from_slice(&4u32.to_be_bytes());
v.extend_from_slice(&4u32.to_be_bytes());
v.extend_from_slice(&0u32.to_be_bytes());
v.extend_from_slice(&0u32.to_be_bytes());
v.extend_from_slice(&1u16.to_be_bytes());
v.push(7);
v.push(1);
v.push(1);
let cod_len: u16 = 12;
v.extend_from_slice(&COD.to_be_bytes());
v.extend_from_slice(&cod_len.to_be_bytes());
v.push(0);
v.push(0);
v.extend_from_slice(&1u16.to_be_bytes());
v.push(0);
v.push(1);
v.push(2);
v.push(2);
v.push(0);
v.push(filter); let qcd_len: u16 = 2 + 1 + 4;
v.extend_from_slice(&QCD.to_be_bytes());
v.extend_from_slice(&qcd_len.to_be_bytes());
v.push(0);
v.extend_from_slice(&[0u8; 4]);
let sot_len: u16 = 10;
v.extend_from_slice(&SOT.to_be_bytes());
v.extend_from_slice(&sot_len.to_be_bytes());
v.extend_from_slice(&0u16.to_be_bytes());
v.extend_from_slice(&0u32.to_be_bytes());
v.push(0);
v.push(1);
v.extend_from_slice(&SOD.to_be_bytes());
v.push(0x00);
v.extend_from_slice(&EOC.to_be_bytes());
v
}
fn build_minimal_codestream_num_layers(num_layers: u16) -> Vec<u8> {
use crate::jpeg2000::markers::{COD, EOC, QCD, SIZ, SOC, SOD, SOT};
let mut v = Vec::new();
v.extend_from_slice(&SOC.to_be_bytes());
let siz_len: u16 = 2 + 36 + 3;
v.extend_from_slice(&SIZ.to_be_bytes());
v.extend_from_slice(&siz_len.to_be_bytes());
v.extend_from_slice(&0u16.to_be_bytes());
v.extend_from_slice(&4u32.to_be_bytes());
v.extend_from_slice(&4u32.to_be_bytes());
v.extend_from_slice(&0u32.to_be_bytes());
v.extend_from_slice(&0u32.to_be_bytes());
v.extend_from_slice(&4u32.to_be_bytes());
v.extend_from_slice(&4u32.to_be_bytes());
v.extend_from_slice(&0u32.to_be_bytes());
v.extend_from_slice(&0u32.to_be_bytes());
v.extend_from_slice(&1u16.to_be_bytes());
v.push(7);
v.push(1);
v.push(1);
let cod_len: u16 = 12;
v.extend_from_slice(&COD.to_be_bytes());
v.extend_from_slice(&cod_len.to_be_bytes());
v.push(0);
v.push(0);
v.extend_from_slice(&num_layers.to_be_bytes());
v.push(0);
v.push(1);
v.push(2);
v.push(2);
v.push(0);
v.push(1); let qcd_len: u16 = 2 + 1 + 4;
v.extend_from_slice(&QCD.to_be_bytes());
v.extend_from_slice(&qcd_len.to_be_bytes());
v.push(0);
v.extend_from_slice(&[0u8; 4]);
let sot_len: u16 = 10;
v.extend_from_slice(&SOT.to_be_bytes());
v.extend_from_slice(&sot_len.to_be_bytes());
v.extend_from_slice(&0u16.to_be_bytes());
v.extend_from_slice(&0u32.to_be_bytes());
v.push(0);
v.push(1);
v.extend_from_slice(&SOD.to_be_bytes());
v.push(0x00);
v.extend_from_slice(&EOC.to_be_bytes());
v
}
}