use crate::error::{Jp2Error, Result};
use crate::stream::{SliceReader, VecWriter};
pub const JP2_JP: u32 = 0x6A502020; pub const JP2_FTYP: u32 = 0x66747970; pub const JP2_JP2H: u32 = 0x6A703268; pub const JP2_IHDR: u32 = 0x69686472; pub const JP2_COLR: u32 = 0x636F6C72; pub const JP2_JP2C: u32 = 0x6A703263; pub const JP2_PCLR: u32 = 0x70636C72; pub const JP2_CMAP: u32 = 0x636D6170; pub const JP2_CDEF: u32 = 0x63646566; pub const JP2_RES: u32 = 0x72657320;
pub const JP2_SIGNATURE: u32 = 0x0D0A870A;
pub const JP2_BRAND: u32 = 0x6A703220;
#[derive(Debug, Clone)]
pub struct BoxHeader {
pub box_type: u32,
pub length: u64,
pub header_size: u8,
}
pub fn read_box_header(reader: &mut SliceReader) -> Result<BoxHeader> {
let lbox = reader.read_u32_be()? as u64;
let tbox = reader.read_u32_be()?;
let (length, header_size) = if lbox == 1 {
let xl = reader.read_u64_be()?;
(xl, 16u8)
} else if lbox == 0 {
(0u64, 8u8)
} else {
(lbox, 8u8)
};
Ok(BoxHeader {
box_type: tbox,
length,
header_size,
})
}
pub fn write_box_header(writer: &mut VecWriter, box_type: u32, data_len: u64) {
let total = data_len + 8;
writer.write_u32_be(total as u32);
writer.write_u32_be(box_type);
}
pub fn write_box_header_xl(writer: &mut VecWriter, box_type: u32, data_len: u64) {
let total = data_len + 16;
writer.write_u32_be(1); writer.write_u32_be(box_type);
writer.write_u64_be(total);
}
#[derive(Debug, Clone)]
pub struct IhdrBox {
pub height: u32,
pub width: u32,
pub num_comps: u16,
pub bpc: u8,
pub compression: u8,
pub unk_colorspace: u8,
pub ipr: u8,
}
pub fn read_ihdr(reader: &mut SliceReader) -> Result<IhdrBox> {
let height = reader.read_u32_be()?;
let width = reader.read_u32_be()?;
let num_comps = reader.read_u16_be()?;
let bpc = reader.read_u8()?;
let compression = reader.read_u8()?;
let unk_colorspace = reader.read_u8()?;
let ipr = reader.read_u8()?;
if compression != 7 {
return Err(Jp2Error::UnsupportedFeature(format!(
"IHDR compression type {compression}, expected 7"
)));
}
Ok(IhdrBox {
height,
width,
num_comps,
bpc,
compression,
unk_colorspace,
ipr,
})
}
pub fn write_ihdr(writer: &mut VecWriter, ihdr: &IhdrBox) {
writer.write_u32_be(ihdr.height);
writer.write_u32_be(ihdr.width);
writer.write_u16_be(ihdr.num_comps);
writer.write_u8(ihdr.bpc);
writer.write_u8(ihdr.compression);
writer.write_u8(ihdr.unk_colorspace);
writer.write_u8(ihdr.ipr);
}
#[derive(Debug, Clone)]
pub struct ColrBox {
pub method: u8,
pub precedence: u8,
pub approx: u8,
pub enum_cs: Option<u32>,
pub icc_profile: Option<Vec<u8>>,
}
pub const CS_SRGB: u32 = 16;
pub const CS_GRAYSCALE: u32 = 17;
pub const CS_YCC: u32 = 18;
pub fn read_colr(reader: &mut SliceReader, payload_len: usize) -> Result<ColrBox> {
let method = reader.read_u8()?;
let precedence = reader.read_u8()?;
let approx = reader.read_u8()?;
match method {
1 => {
let enum_cs = reader.read_u32_be()?;
Ok(ColrBox {
method,
precedence,
approx,
enum_cs: Some(enum_cs),
icc_profile: None,
})
}
2 => {
let profile_len = payload_len.saturating_sub(3);
let profile = reader.read_bytes(profile_len)?.to_vec();
Ok(ColrBox {
method,
precedence,
approx,
enum_cs: None,
icc_profile: Some(profile),
})
}
_ => Err(Jp2Error::UnsupportedFeature(format!(
"COLR method {method}"
))),
}
}
pub fn write_colr(writer: &mut VecWriter, colr: &ColrBox) {
writer.write_u8(colr.method);
writer.write_u8(colr.precedence);
writer.write_u8(colr.approx);
match colr.method {
1 => {
if let Some(cs) = colr.enum_cs {
writer.write_u32_be(cs);
}
}
2 => {
if let Some(ref icc) = colr.icc_profile {
writer.write_bytes(icc);
}
}
_ => {}
}
}