use crate::error::{Jpeg2000Error, Result};
use byteorder::{BigEndian, ReadBytesExt};
use std::io::Read;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Marker {
Soc = 0xFF4F,
Sot = 0xFF90,
Sod = 0xFF93,
Eoc = 0xFFD9,
Siz = 0xFF51,
Cod = 0xFF52,
Coc = 0xFF53,
Qcd = 0xFF5C,
Qcc = 0xFF5D,
Rgn = 0xFF5E,
Poc = 0xFF5F,
Plm = 0xFF57,
Plt = 0xFF58,
Ppm = 0xFF60,
Ppt = 0xFF61,
Crg = 0xFF63,
Com = 0xFF64,
}
impl Marker {
pub fn from_u16(value: u16) -> Result<Self> {
match value {
0xFF4F => Ok(Self::Soc),
0xFF90 => Ok(Self::Sot),
0xFF93 => Ok(Self::Sod),
0xFFD9 => Ok(Self::Eoc),
0xFF51 => Ok(Self::Siz),
0xFF52 => Ok(Self::Cod),
0xFF53 => Ok(Self::Coc),
0xFF5C => Ok(Self::Qcd),
0xFF5D => Ok(Self::Qcc),
0xFF5E => Ok(Self::Rgn),
0xFF5F => Ok(Self::Poc),
0xFF57 => Ok(Self::Plm),
0xFF58 => Ok(Self::Plt),
0xFF60 => Ok(Self::Ppm),
0xFF61 => Ok(Self::Ppt),
0xFF63 => Ok(Self::Crg),
0xFF64 => Ok(Self::Com),
_ => Err(Jpeg2000Error::InvalidMarker(value)),
}
}
pub fn has_segment(&self) -> bool {
!matches!(self, Self::Soc | Self::Sod | Self::Eoc)
}
}
#[derive(Debug, Clone)]
pub struct ImageSize {
pub width: u32,
pub height: u32,
pub x_offset: u32,
pub y_offset: u32,
pub tile_width: u32,
pub tile_height: u32,
pub tile_x_offset: u32,
pub tile_y_offset: u32,
pub num_components: u16,
pub components: Vec<ComponentSize>,
}
#[derive(Debug, Clone)]
pub struct ComponentSize {
pub precision: u8,
pub is_signed: bool,
pub dx: u8,
pub dy: u8,
}
impl ImageSize {
pub fn parse<R: Read>(reader: &mut R, length: u16) -> Result<Self> {
let _capability = reader.read_u16::<BigEndian>()?;
let width = reader.read_u32::<BigEndian>()?;
let height = reader.read_u32::<BigEndian>()?;
let x_offset = reader.read_u32::<BigEndian>()?;
let y_offset = reader.read_u32::<BigEndian>()?;
let tile_width = reader.read_u32::<BigEndian>()?;
let tile_height = reader.read_u32::<BigEndian>()?;
let tile_x_offset = reader.read_u32::<BigEndian>()?;
let tile_y_offset = reader.read_u32::<BigEndian>()?;
let num_components = reader.read_u16::<BigEndian>()?;
if num_components == 0 {
return Err(Jpeg2000Error::InvalidImageHeader(
"Number of components must be > 0".to_string(),
));
}
let expected_length = 36 + (num_components as usize * 3);
if length as usize != expected_length {
return Err(Jpeg2000Error::InvalidImageHeader(format!(
"Invalid SIZ segment length: expected {}, got {}",
expected_length, length
)));
}
let mut components = Vec::with_capacity(num_components as usize);
for _ in 0..num_components {
let ssiz = reader.read_u8()?;
let dx = reader.read_u8()?;
let dy = reader.read_u8()?;
let is_signed = (ssiz & 0x80) != 0;
let precision = (ssiz & 0x7F) + 1;
components.push(ComponentSize {
precision,
is_signed,
dx,
dy,
});
}
Ok(Self {
width,
height,
x_offset,
y_offset,
tile_width,
tile_height,
tile_x_offset,
tile_y_offset,
num_components,
components,
})
}
pub fn num_tiles_x(&self) -> u32 {
(self.width + self.tile_width - 1 - self.tile_x_offset) / self.tile_width
}
pub fn num_tiles_y(&self) -> u32 {
(self.height + self.tile_height - 1 - self.tile_y_offset) / self.tile_height
}
pub fn num_tiles(&self) -> u32 {
self.num_tiles_x() * self.num_tiles_y()
}
}
#[derive(Debug, Clone)]
pub struct CodingStyle {
pub progression_order: ProgressionOrder,
pub num_layers: u16,
pub use_mct: bool,
pub num_levels: u8,
pub code_block_width: u8,
pub code_block_height: u8,
pub code_block_style: u8,
pub wavelet: WaveletTransform,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProgressionOrder {
Lrcp = 0,
Rlcp = 1,
Rpcl = 2,
Pcrl = 3,
Cprl = 4,
}
impl ProgressionOrder {
pub fn from_u8(value: u8) -> Result<Self> {
match value {
0 => Ok(Self::Lrcp),
1 => Ok(Self::Rlcp),
2 => Ok(Self::Rpcl),
3 => Ok(Self::Pcrl),
4 => Ok(Self::Cprl),
_ => Err(Jpeg2000Error::CodestreamError(format!(
"Invalid progression order: {}",
value
))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WaveletTransform {
Irreversible97,
Reversible53,
}
impl WaveletTransform {
pub fn from_u8(value: u8) -> Result<Self> {
match value {
0 => Ok(Self::Irreversible97),
1 => Ok(Self::Reversible53),
_ => Err(Jpeg2000Error::CodestreamError(format!(
"Invalid wavelet transform: {}",
value
))),
}
}
}
impl CodingStyle {
pub fn parse<R: Read>(reader: &mut R, _length: u16) -> Result<Self> {
let scod = reader.read_u8()?;
let progression_order = ProgressionOrder::from_u8(reader.read_u8()?)?;
let num_layers = reader.read_u16::<BigEndian>()?;
let mct = reader.read_u8()?;
let num_levels = reader.read_u8()?;
let code_block_width = reader.read_u8()?;
let code_block_height = reader.read_u8()?;
let code_block_style = reader.read_u8()?;
let wavelet = WaveletTransform::from_u8(reader.read_u8()?)?;
let use_mct = mct != 0;
if (scod & 0x01) != 0 {
return Err(Jpeg2000Error::UnsupportedFeature(
"Custom precinct sizes not yet supported".to_string(),
));
}
Ok(Self {
progression_order,
num_layers,
use_mct,
num_levels,
code_block_width,
code_block_height,
code_block_style,
wavelet,
})
}
}
#[derive(Debug, Clone)]
pub struct Quantization {
pub style: QuantizationStyle,
pub step_sizes: Vec<u16>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QuantizationStyle {
NoQuantization,
ScalarDerived,
ScalarExpounded,
}
impl QuantizationStyle {
pub fn from_u8(value: u8) -> Result<Self> {
match value & 0x1F {
0 => Ok(Self::NoQuantization),
1 => Ok(Self::ScalarDerived),
2 => Ok(Self::ScalarExpounded),
_ => Err(Jpeg2000Error::CodestreamError(format!(
"Invalid quantization style: {}",
value
))),
}
}
}
impl Quantization {
pub fn parse<R: Read>(reader: &mut R, length: u16) -> Result<Self> {
let sqcd = reader.read_u8()?;
let style = QuantizationStyle::from_u8(sqcd)?;
let mut step_sizes = Vec::new();
let remaining = (length - 1) as usize;
match style {
QuantizationStyle::NoQuantization | QuantizationStyle::ScalarDerived => {
for _ in 0..remaining {
step_sizes.push(u16::from(reader.read_u8()?));
}
}
QuantizationStyle::ScalarExpounded => {
for _ in 0..(remaining / 2) {
step_sizes.push(reader.read_u16::<BigEndian>()?);
}
}
}
Ok(Self { style, step_sizes })
}
}
pub struct CodestreamParser<R> {
reader: R,
}
impl<R: Read> CodestreamParser<R> {
pub fn new(reader: R) -> Self {
Self { reader }
}
pub fn read_marker(&mut self) -> Result<Option<Marker>> {
match self.reader.read_u16::<BigEndian>() {
Ok(value) => Ok(Some(Marker::from_u16(value)?)),
Err(ref e) if e.kind() == std::io::ErrorKind::UnexpectedEof => Ok(None),
Err(e) => Err(e.into()),
}
}
pub fn read_segment_length(&mut self) -> Result<u16> {
let length = self.reader.read_u16::<BigEndian>()?;
if length < 2 {
return Err(Jpeg2000Error::CodestreamError(format!(
"Invalid segment length: {}",
length
)));
}
Ok(length - 2)
}
pub fn skip_segment(&mut self, length: u16) -> Result<()> {
let mut buffer = vec![0u8; length as usize];
self.reader.read_exact(&mut buffer)?;
Ok(())
}
pub fn parse_siz(&mut self) -> Result<ImageSize> {
let length = self.read_segment_length()?;
ImageSize::parse(&mut self.reader, length)
}
pub fn parse_cod(&mut self) -> Result<CodingStyle> {
let length = self.read_segment_length()?;
CodingStyle::parse(&mut self.reader, length)
}
pub fn parse_qcd(&mut self) -> Result<Quantization> {
let length = self.read_segment_length()?;
Quantization::parse(&mut self.reader, length)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_marker_conversion() {
assert_eq!(Marker::from_u16(0xFF4F).ok(), Some(Marker::Soc));
assert_eq!(Marker::from_u16(0xFFD9).ok(), Some(Marker::Eoc));
assert!(Marker::from_u16(0x0000).is_err());
}
#[test]
fn test_progression_order() {
assert_eq!(
ProgressionOrder::from_u8(0).ok(),
Some(ProgressionOrder::Lrcp)
);
assert_eq!(
ProgressionOrder::from_u8(4).ok(),
Some(ProgressionOrder::Cprl)
);
assert!(ProgressionOrder::from_u8(5).is_err());
}
#[test]
fn test_wavelet_transform() {
assert_eq!(
WaveletTransform::from_u8(0).ok(),
Some(WaveletTransform::Irreversible97)
);
assert_eq!(
WaveletTransform::from_u8(1).ok(),
Some(WaveletTransform::Reversible53)
);
assert!(WaveletTransform::from_u8(2).is_err());
}
}