use crate::bio::{BioReader, BioWriter};
use crate::error::{Jp2Error, Result};
const SOP_MARKER: u16 = 0xFF91;
const EPH_MARKER: u16 = 0xFF92;
#[derive(Debug, Clone)]
pub struct CblkContribution {
pub data: Vec<u8>,
pub num_passes: u32,
pub zero_bitplanes: u32,
pub included: bool,
}
#[derive(Debug, Clone)]
pub struct PacketData {
pub is_empty: bool,
pub cblk_data: Vec<CblkContribution>,
}
fn encode_num_passes(bio: &mut BioWriter, num_passes: u32) {
if num_passes == 1 {
bio.putbit(0);
} else if num_passes == 2 {
bio.putbit(1);
bio.putbit(0);
} else if num_passes <= 5 {
bio.write(0b1100, 4);
bio.write(num_passes - 3, 2);
} else if num_passes <= 36 {
bio.write(0b1101, 4);
bio.write(num_passes - 6, 5);
} else {
bio.write(0b11100000, 8);
bio.write(num_passes - 37, 7);
}
}
fn decode_num_passes(bio: &mut BioReader) -> Result<u32> {
let bit0 = bio.getbit()?;
if bit0 == 0 {
return Ok(1);
}
let bit1 = bio.getbit()?;
if bit1 == 0 {
return Ok(2);
}
let bit2 = bio.getbit()?;
let bit3 = bio.getbit()?;
if bit2 == 0 && bit3 == 0 {
let v = bio.read(2)?;
Ok(3 + v)
} else if bit2 == 0 && bit3 == 1 {
let v = bio.read(5)?;
Ok(6 + v)
} else {
let _extra = bio.read(4)?; let v = bio.read(7)?;
Ok(37 + v)
}
}
fn length_num_bytes(len: usize) -> u32 {
if len < 256 {
1
} else if len < 65536 {
2
} else if len < 16_777_216 {
3
} else {
4
}
}
pub fn encode_packet(
contributions: &[CblkContribution],
use_sop: bool,
use_eph: bool,
) -> Vec<u8> {
let mut output = Vec::new();
if use_sop {
output.push((SOP_MARKER >> 8) as u8);
output.push((SOP_MARKER & 0xFF) as u8);
output.push(0x00);
output.push(0x04); output.push(0x00);
output.push(0x00); }
let has_data = contributions.iter().any(|c| c.included && !c.data.is_empty());
let mut bio = BioWriter::new();
if !has_data {
bio.putbit(0);
let _ = bio.flush();
output.extend_from_slice(bio.as_slice());
if use_eph {
output.push((EPH_MARKER >> 8) as u8);
output.push((EPH_MARKER & 0xFF) as u8);
}
return output;
}
bio.putbit(1);
for contrib in contributions {
if !contrib.included {
bio.putbit(0);
continue;
}
bio.putbit(1);
bio.write(contrib.zero_bitplanes, 8);
encode_num_passes(&mut bio, contrib.num_passes);
let num_len_bytes = length_num_bytes(contrib.data.len());
bio.write(num_len_bytes, 2); bio.write(contrib.data.len() as u32, num_len_bytes * 8);
}
let _ = bio.flush();
output.extend_from_slice(bio.as_slice());
if use_eph {
output.push((EPH_MARKER >> 8) as u8);
output.push((EPH_MARKER & 0xFF) as u8);
}
for contrib in contributions {
if contrib.included {
output.extend_from_slice(&contrib.data);
}
}
output
}
pub fn decode_packet(
data: &[u8],
num_cblks: usize,
_first_inclusion: &[bool],
) -> Result<(PacketData, usize)> {
let mut offset = 0usize;
if data.len() >= offset + 2
&& data[offset] == (SOP_MARKER >> 8) as u8
&& data[offset + 1] == (SOP_MARKER & 0xFF) as u8
{
offset += 6;
}
if offset >= data.len() {
return Err(Jp2Error::OutOfBounds {
offset,
len: 1,
});
}
let mut bio = BioReader::new(&data[offset..]);
let non_empty = bio.getbit()?;
if non_empty == 0 {
let _ = bio.inalign();
let header_bytes = bio.numbytes();
offset += header_bytes;
if data.len() >= offset + 2
&& data[offset] == (EPH_MARKER >> 8) as u8
&& data[offset + 1] == (EPH_MARKER & 0xFF) as u8
{
offset += 2;
}
return Ok((
PacketData {
is_empty: true,
cblk_data: Vec::new(),
},
offset,
));
}
let mut contrib_infos: Vec<(bool, u32, u32, usize)> = Vec::with_capacity(num_cblks);
for _i in 0..num_cblks {
let included = bio.getbit()?;
if included == 0 {
contrib_infos.push((false, 0, 0, 0));
continue;
}
let zero_bp = bio.read(8)?;
let num_passes = decode_num_passes(&mut bio)?;
let num_len_bytes = bio.read(2)?;
let data_len = bio.read(num_len_bytes * 8)? as usize;
contrib_infos.push((true, zero_bp, num_passes, data_len));
}
let _ = bio.inalign();
let header_bytes = bio.numbytes();
offset += header_bytes;
if data.len() >= offset + 2
&& data[offset] == (EPH_MARKER >> 8) as u8
&& data[offset + 1] == (EPH_MARKER & 0xFF) as u8
{
offset += 2;
}
let mut contributions = Vec::with_capacity(num_cblks);
for (included, zero_bp, num_passes, data_len) in contrib_infos {
if !included {
contributions.push(CblkContribution {
data: Vec::new(),
num_passes: 0,
zero_bitplanes: 0,
included: false,
});
continue;
}
if offset + data_len > data.len() {
return Err(Jp2Error::OutOfBounds {
offset,
len: data_len,
});
}
let cblk_data = data[offset..offset + data_len].to_vec();
offset += data_len;
contributions.push(CblkContribution {
data: cblk_data,
num_passes,
zero_bitplanes: zero_bp,
included: true,
});
}
Ok((
PacketData {
is_empty: false,
cblk_data: contributions,
},
offset,
))
}