use super::byte_stream::ByteStream;
use super::huffman::HuffTable;
use super::*;
use decompressors::*;
mod decompressors;
#[allow(clippy::upper_case_acronyms)]
enum Marker {
Stuff = 0x00,
SOF3 = 0xc3, DHT = 0xc4, SOI = 0xd8, EOI = 0xd9, SOS = 0xda, DQT = 0xdb, Fill = 0xff,
}
fn m(marker: Marker) -> u8 {
marker as u8
}
#[derive(Debug, Copy, Clone)]
struct JpegComponentInfo {
id: usize,
dc_tbl_num: usize,
super_h: usize, super_v: usize, }
#[derive(Debug, Clone)]
struct SOFInfo {
width: usize,
height: usize,
cps: usize,
precision: usize,
components: Vec<JpegComponentInfo>,
csfix: bool,
}
impl SOFInfo {
fn empty(csfix: bool) -> SOFInfo {
SOFInfo {
width: 0,
height: 0,
cps: 0,
precision: 0,
components: Vec::new(),
csfix,
}
}
fn parse_sof(&mut self, input: &mut ByteStream) -> Result<(), DecodingError> {
let header_length = input.get_u16() as usize;
self.precision = input.get_u8() as usize;
self.height = input.get_u16() as usize;
self.width = input.get_u16() as usize;
self.cps = input.get_u8() as usize;
if self.precision > 16 {
return Err(DecodingError::LJpegError("ljpeg: More than 16 bits per channel is not supported."));
}
if self.cps > 4 || self.cps < 1 {
return Err(DecodingError::LJpegError("ljpeg: Only from 1 to 4 components are supported."));
}
if header_length != 8 + self.cps * 3 {
return Err(DecodingError::LJpegError("ljpeg: Header size mismatch."));
}
for _ in 0..self.cps {
let id = input.get_u8() as usize;
let subs = input.get_u8() as usize;
input.get_u8();
self.components.push(JpegComponentInfo {
id,
dc_tbl_num: 0,
super_v: subs & 0xf,
super_h: subs >> 4,
});
}
Ok(())
}
fn parse_sos(&mut self, input: &mut ByteStream) -> Result<(usize, usize), DecodingError> {
if self.width == 0 {
return Err(DecodingError::LJpegError("ljpeg: Trying to parse SOS before SOF"));
}
input.get_u16(); let soscps = input.get_u8() as usize;
if self.cps != soscps {
return Err(DecodingError::LJpegError("ljpeg: component number mismatch in SOS"));
}
for cs in 0..self.cps {
let readcs = input.get_u8() as usize;
let cs = if self.csfix { cs } else { readcs };
let component = match self.components.iter_mut().find(|&&mut c| c.id == cs) {
Some(val) => val,
None => return Err(DecodingError::LJpegError("ljpeg: invalid component selector")),
};
let td = (input.get_u8() as usize) >> 4;
if td > 3 {
return Err(DecodingError::LJpegError("ljpeg: Invalid Huffman table selection"));
}
component.dc_tbl_num = td;
}
let pred = input.get_u8() as usize;
input.get_u8(); let pt = (input.get_u8() as usize) & 0xf; Ok((pred, pt))
}
}
#[derive(Debug)]
pub(in super::super) struct LjpegDecompressor<'a> {
buffer: &'a [u8],
sof: SOFInfo,
predictor: usize,
point_transform: usize,
dhts: Vec<HuffTable>,
}
impl<'a> LjpegDecompressor<'a> {
pub(in super::super) fn new(src: &'a [u8]) -> Result<LjpegDecompressor, DecodingError> {
LjpegDecompressor::new_full(src, false, false)
}
pub(in super::super) fn new_full(src: &'a [u8], dng_bug: bool, csfix: bool) -> Result<LjpegDecompressor, DecodingError> {
let mut input = ByteStream::new(src, false);
if LjpegDecompressor::get_next_marker(&mut input, false)? != m(Marker::SOI) {
return Err(DecodingError::LJpegError("ljpeg: Image did not start with SOI. Probably not LJPEG"));
}
let mut sof = SOFInfo::empty(csfix);
let mut dht_init = [false; 4];
let mut dht_bits = [[0u32; 17]; 4];
let mut dht_huffval = [[0u32; 256]; 4];
let pred;
let pt;
loop {
let marker = LjpegDecompressor::get_next_marker(&mut input, true)?;
if marker == m(Marker::SOF3) {
sof.parse_sof(&mut input)?;
if sof.precision > 16 || sof.precision < 12 {
return Err(DecodingError::LJpegError("sof.precision"));
}
} else if marker == m(Marker::DHT) {
LjpegDecompressor::parse_dht(&mut input, &mut dht_init, &mut dht_bits, &mut dht_huffval)?;
} else if marker == m(Marker::SOS) {
let (a, b) = sof.parse_sos(&mut input)?;
pred = a;
pt = b;
break;
} else if marker == m(Marker::EOI) {
return Err(DecodingError::LJpegError("ljpeg: reached EOI before SOS"));
} else if marker == m(Marker::DQT) {
return Err(DecodingError::LJpegError("ljpeg: not a valid raw file, found DQT"));
}
}
let mut dhts = Vec::new();
for i in 0..4 {
dhts.push(if dht_init[i] {
HuffTable::new(dht_bits[i], dht_huffval[i], dng_bug)
} else {
HuffTable::empty()
});
}
let offset = input.get_pos();
Ok(LjpegDecompressor {
buffer: &src[offset..],
sof,
predictor: pred,
point_transform: pt,
dhts,
})
}
fn get_next_marker(input: &mut ByteStream, allowskip: bool) -> Result<u8, DecodingError> {
if !allowskip {
if input.get_u8() != 0xff {
return Err(DecodingError::LJpegError("ljpeg: (noskip) expected marker not found"));
}
let mark = input.get_u8();
if mark == m(Marker::Stuff) || mark == m(Marker::Fill) {
return Err(DecodingError::LJpegError("ljpeg: (noskip) expected marker but found stuff or fill"));
}
return Ok(mark);
}
input.skip_to_marker()?;
Ok(input.get_u8())
}
fn parse_dht(
input: &mut ByteStream,
init: &mut [bool; 4],
bits: &mut [[u32; 17]; 4],
huffval: &mut [[u32; 256]; 4],
) -> Result<(), DecodingError> {
let mut length = (input.get_u16() as usize) - 2;
while length > 0 {
let b = input.get_u8() as usize;
let tc = b >> 4;
let th = b & 0xf;
if tc != 0 {
return Err(DecodingError::LJpegError("ljpeg: unsupported table class in DHT"));
}
if th > 3 {
return Err(DecodingError::LJpegError("ljpeg: unsupported table id"));
}
let mut acc: usize = 0;
for i in 0..16 {
bits[th][i + 1] = input.get_u8() as u32;
acc += bits[th][i + 1] as usize;
}
bits[th][0] = 0;
if acc > 256 {
return Err(DecodingError::LJpegError("ljpeg: invalid DHT table"));
}
if length < 1 + 16 + acc {
return Err(DecodingError::LJpegError("ljpeg: invalid DHT table length"));
}
for i in 0..acc {
huffval[th][i] = input.get_u8() as u32;
}
init[th] = true;
length -= 1 + 16 + acc;
}
Ok(())
}
pub(in super::super) fn decode(
&self,
out: &mut [u16],
x: usize,
stripwidth: usize,
width: usize,
height: usize,
dummy: bool,
) -> Result<(), DecodingError> {
if dummy {
return Ok(());
}
if self.sof.components[0].super_h == 2 && self.sof.components[0].super_v == 2 {
return decode_ljpeg_420(self, out, width, height);
} else if self.sof.components[0].super_h == 2 && self.sof.components[0].super_v == 1 {
return decode_ljpeg_422(self, out, width, height);
}
match self.predictor {
1 => match self.sof.cps {
2 => decode_ljpeg_2components(self, out, x, stripwidth, width, height),
3 => decode_ljpeg_3components(self, out, x, stripwidth, width, height),
4 => decode_ljpeg_4components(self, out, width, height),
c => Err(DecodingError::LJpegComponentFilesNotSupported(c)),
},
8 => decode_hasselblad(self, out, width),
p => Err(DecodingError::LJpegPredictorNotSupported(p)),
}
}
}