use crate::error::{FormatError, RawError, RawResult};
#[allow(dead_code)]
mod markers {
pub const SOI: u16 = 0xFFD8; pub const EOI: u16 = 0xFFD9; pub const SOF3: u16 = 0xFFC3; pub const DHT: u16 = 0xFFC4; pub const SOS: u16 = 0xFFDA; pub const DRI: u16 = 0xFFDD; pub const RST0: u16 = 0xFFD0; pub const RST7: u16 = 0xFFD7; pub const APP0: u16 = 0xFFE0; pub const APP15: u16 = 0xFFEF; pub const COM: u16 = 0xFFFE; }
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct Component {
pub id: u8,
pub h_samp: u8, pub v_samp: u8, pub quant_table: u8,
pub dc_table: u8, }
#[derive(Debug, Clone, Default)]
pub struct FrameInfo {
pub precision: u8, pub height: u16, pub width: u16, pub components: Vec<Component>,
}
#[derive(Debug, Clone)]
pub struct HuffmanTable {
pub bits: [u8; 17],
pub huffval: Vec<u8>,
pub lookup: Vec<(i32, u8)>,
pub maxcode: [i32; 18],
pub mincode: [i32; 17],
pub valptr: [i32; 17],
}
impl Default for HuffmanTable {
fn default() -> Self {
Self {
bits: [0; 17],
huffval: Vec::new(),
lookup: Vec::new(),
maxcode: [-1; 18],
mincode: [0; 17],
valptr: [0; 17],
}
}
}
impl HuffmanTable {
pub fn build_tables(&mut self) {
let mut code = 0i32;
let mut si = 1usize;
let mut j = 0usize;
while si <= 16 {
if self.bits[si] == 0 {
self.maxcode[si] = -1;
} else {
self.valptr[si] = j as i32;
self.mincode[si] = code;
code += self.bits[si] as i32;
self.maxcode[si] = code - 1;
j += self.bits[si] as usize;
}
code <<= 1;
si += 1;
}
self.maxcode[17] = 0x7FFFFFFF;
self.lookup = vec![(0, 0); 256];
let mut code = 0u32;
let mut idx = 0usize;
for bits in 1..=8 {
for _ in 0..self.bits[bits] {
if idx < self.huffval.len() {
let symbol = self.huffval[idx] as i32;
let fill_bits = 8 - bits;
let fill_count = 1 << fill_bits;
let base = (code << fill_bits) as usize;
for f in 0..fill_count {
if base + f < 256 {
self.lookup[base + f] = (symbol, bits as u8);
}
}
idx += 1;
}
code += 1;
}
code <<= 1;
}
}
}
pub use super::bit_pump::BitPump;
pub struct LjpegDecoder {
frame: FrameInfo,
huffman_dc: [HuffmanTable; 4],
restart_interval: u16,
predictor: u8,
point_transform: u8,
real_width: Option<u32>,
real_height: Option<u32>,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct LjpegDecoderBuilder {
width: Option<u32>,
height: Option<u32>,
}
#[allow(dead_code)]
impl LjpegDecoderBuilder {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn dimensions(mut self, width: u32, height: u32) -> Self {
self.width = Some(width);
self.height = Some(height);
self
}
#[must_use]
pub fn build(self) -> LjpegDecoder {
let mut decoder = LjpegDecoder::new();
if let (Some(w), Some(h)) = (self.width, self.height) {
decoder.set_dimensions(w, h);
}
decoder
}
pub fn decode(self, data: &[u8]) -> RawResult<Vec<u16>> {
self.build().decode(data)
}
}
impl LjpegDecoder {
pub fn new() -> Self {
Self {
frame: FrameInfo::default(),
huffman_dc: [
HuffmanTable::default(),
HuffmanTable::default(),
HuffmanTable::default(),
HuffmanTable::default(),
],
restart_interval: 0,
predictor: 1,
point_transform: 0,
real_width: None,
real_height: None,
}
}
#[allow(dead_code)]
pub fn builder() -> LjpegDecoderBuilder {
LjpegDecoderBuilder::new()
}
pub fn set_dimensions(&mut self, width: u32, height: u32) {
self.real_width = Some(width);
self.real_height = Some(height);
}
#[allow(dead_code)]
pub fn frame_info(&self) -> &FrameInfo {
&self.frame
}
pub fn decode(&mut self, data: &[u8]) -> RawResult<Vec<u16>> {
let mut pos;
let mut scan_data_start = 0;
let mut scan_data_end = data.len();
if data.len() < 2 || data[0] != 0xFF || data[1] != 0xD8 {
return Err(RawError::Format(FormatError::Decompression(
"Missing JPEG SOI marker".into(),
)));
}
pos = 2;
while pos + 2 <= data.len() {
if data[pos] != 0xFF {
pos += 1;
continue;
}
let marker = u16::from_be_bytes([data[pos], data[pos + 1]]);
pos += 2;
match marker {
markers::SOF3 => {
self.parse_sof3(&data[pos..])?;
let len = u16::from_be_bytes([data[pos], data[pos + 1]]) as usize;
pos += len;
}
markers::DHT => {
let len = u16::from_be_bytes([data[pos], data[pos + 1]]) as usize;
self.parse_dht(&data[pos..pos + len])?;
pos += len;
}
markers::DRI => {
let len = u16::from_be_bytes([data[pos], data[pos + 1]]) as usize;
if len >= 4 {
self.restart_interval = u16::from_be_bytes([data[pos + 2], data[pos + 3]]);
}
pos += len;
}
markers::SOS => {
let len = u16::from_be_bytes([data[pos], data[pos + 1]]) as usize;
self.parse_sos(&data[pos..pos + len])?;
pos += len;
scan_data_start = pos;
while pos + 1 < data.len() {
if data[pos] == 0xFF
&& data[pos + 1] != 0x00
&& !(data[pos + 1] >= 0xD0 && data[pos + 1] <= 0xD7)
{
scan_data_end = pos;
break;
}
pos += 1;
}
break; }
markers::EOI => {
break;
}
m if (markers::APP0..=markers::APP15).contains(&m) => {
let len = u16::from_be_bytes([data[pos], data[pos + 1]]) as usize;
pos += len;
}
markers::COM => {
let len = u16::from_be_bytes([data[pos], data[pos + 1]]) as usize;
pos += len;
}
_ => {
if pos + 2 <= data.len() {
let len = u16::from_be_bytes([data[pos], data[pos + 1]]) as usize;
pos += len;
}
}
}
}
let scan_data = &data[scan_data_start..scan_data_end];
self.decode_scan(scan_data)
}
fn parse_sof3(&mut self, data: &[u8]) -> RawResult<()> {
if data.len() < 8 {
return Err(RawError::Format(FormatError::Decompression(
"SOF3 too short".into(),
)));
}
let _len = u16::from_be_bytes([data[0], data[1]]);
self.frame.precision = data[2];
self.frame.height = u16::from_be_bytes([data[3], data[4]]);
self.frame.width = u16::from_be_bytes([data[5], data[6]]);
let num_components = data[7] as usize;
if data.len() < 8 + num_components * 3 {
return Err(RawError::Format(FormatError::Decompression(
"SOF3 component data too short".into(),
)));
}
self.frame.components.clear();
for i in 0..num_components {
let offset = 8 + i * 3;
let comp = Component {
id: data[offset],
h_samp: (data[offset + 1] >> 4) & 0x0F,
v_samp: data[offset + 1] & 0x0F,
quant_table: data[offset + 2],
dc_table: 0, };
self.frame.components.push(comp);
}
Ok(())
}
fn parse_dht(&mut self, data: &[u8]) -> RawResult<()> {
let len = u16::from_be_bytes([data[0], data[1]]) as usize;
let mut pos = 2;
while pos < len {
let info = data[pos];
let table_class = (info >> 4) & 0x0F; let table_id = (info & 0x0F) as usize;
pos += 1;
if table_id >= 4 {
return Err(RawError::Format(FormatError::Decompression(format!(
"Invalid Huffman table ID: {}",
table_id
))));
}
let mut table = HuffmanTable::default();
let mut total_codes = 0usize;
for i in 1..=16 {
if pos >= len {
return Err(RawError::Format(FormatError::Decompression(
"DHT truncated".into(),
)));
}
table.bits[i] = data[pos];
total_codes += data[pos] as usize;
pos += 1;
}
if pos + total_codes > len {
return Err(RawError::Format(FormatError::Decompression(
"DHT HUFFVAL truncated".into(),
)));
}
table.huffval = data[pos..pos + total_codes].to_vec();
pos += total_codes;
table.build_tables();
if table_class == 0 {
self.huffman_dc[table_id] = table;
}
}
Ok(())
}
fn parse_sos(&mut self, data: &[u8]) -> RawResult<()> {
if data.len() < 3 {
return Err(RawError::Format(FormatError::Decompression(
"SOS too short".into(),
)));
}
let _len = u16::from_be_bytes([data[0], data[1]]);
let num_components = data[2] as usize;
if data.len() < 3 + num_components * 2 + 3 {
return Err(RawError::Format(FormatError::Decompression(
"SOS data too short".into(),
)));
}
for i in 0..num_components {
let offset = 3 + i * 2;
let comp_id = data[offset];
let tables = data[offset + 1];
let dc_table = (tables >> 4) & 0x0F;
for comp in &mut self.frame.components {
if comp.id == comp_id {
comp.dc_table = dc_table;
break;
}
}
}
let ss_offset = 3 + num_components * 2;
self.predictor = data[ss_offset];
self.point_transform = data[ss_offset + 2] & 0x0F;
Ok(())
}
#[inline]
fn decode_huffman(&self, pump: &mut BitPump, table: &HuffmanTable) -> i32 {
let peek8 = (pump.peek(8) & 0xFF) as usize;
if peek8 < table.lookup.len() {
let (symbol, bits) = table.lookup[peek8];
if bits > 0 {
pump.consume(bits as u32);
return symbol;
}
}
let mut code = 0i32;
for bits in 1..=16 {
code = (code << 1) | (pump.get_bits(1) as i32);
if code <= table.maxcode[bits] {
let idx = (table.valptr[bits] + code - table.mincode[bits]) as usize;
if idx < table.huffval.len() {
return table.huffval[idx] as i32;
}
}
}
0 }
#[inline]
fn extend(v: i32, t: i32) -> i32 {
if t == 0 {
return 0;
}
let vt = 1 << (t - 1);
if v < vt { v + (-1 << t) + 1 } else { v }
}
#[inline]
fn predict(&self, ra: i32, rb: i32, rc: i32) -> i32 {
match self.predictor {
0 => 0, 1 => ra, 2 => rb, 3 => rc, 4 => ra + rb - rc, 5 => ra + ((rb - rc) >> 1), 6 => rb + ((ra - rc) >> 1), 7 => (ra + rb) >> 1, _ => ra, }
}
fn decode_scan(&mut self, scan_data: &[u8]) -> RawResult<Vec<u16>> {
let out_width = self.real_width.unwrap_or(self.frame.width as u32) as usize;
let out_height = self.real_height.unwrap_or(self.frame.height as u32) as usize;
let num_components = self.frame.components.len();
if num_components == 0 {
return Err(RawError::Format(FormatError::Decompression(
"No components defined".into(),
)));
}
let total_pixels = out_width * out_height;
let mut output: Vec<u16> = vec![0; total_pixels];
let mut pump = BitPump::new(scan_data);
let initial = 1i32
<< (self
.frame
.precision
.saturating_sub(self.point_transform)
.saturating_sub(1));
let max_val = (1u32 << self.frame.precision) - 1;
if num_components == 4 {
let super_width = out_width / 2;
let super_height = out_height / 2;
let mut prev = [initial; 4];
let mut col0_prev = [initial; 4];
for sy in 0..super_height {
for sx in 0..super_width {
for c in 0..4 {
let table_idx = self.frame.components[c].dc_table as usize;
let table = &self.huffman_dc[table_idx.min(3)];
let category = self.decode_huffman(&mut pump, table);
let diff = if category == 0 {
0
} else {
let bits = pump.get_bits(category as u32) as i32;
Self::extend(bits, category)
};
let predicted = if sx == 0 {
if sy == 0 { initial } else { col0_prev[c] }
} else {
prev[c]
};
let value = (predicted + diff).clamp(0, max_val as i32) as u16;
prev[c] = value as i32;
if sx == 0 {
col0_prev[c] = value as i32;
}
let (dx, dy) = match c {
0 => (0, 0),
1 => (1, 0),
2 => (0, 1),
3 => (1, 1),
_ => (0, 0),
};
let x = sx * 2 + dx;
let y = sy * 2 + dy;
if x < out_width && y < out_height {
output[y * out_width + x] = value;
}
}
}
}
} else if num_components == 1 {
let table = &self.huffman_dc[self.frame.components[0].dc_table as usize];
for y in 0..out_height {
for x in 0..out_width {
let ra = if x > 0 {
output[y * out_width + x - 1] as i32
} else {
initial
};
let rb = if y > 0 {
output[(y - 1) * out_width + x] as i32
} else {
initial
};
let rc = if x > 0 && y > 0 {
output[(y - 1) * out_width + x - 1] as i32
} else {
initial
};
let predicted = if x == 0 && y == 0 {
initial
} else if y == 0 {
ra
} else if x == 0 {
rb
} else {
self.predict(ra, rb, rc)
};
let category = self.decode_huffman(&mut pump, table);
let diff = if category == 0 {
0
} else {
let bits = pump.get_bits(category as u32) as i32;
Self::extend(bits, category)
};
let value = ((predicted + diff) as u32).min(max_val) as u16;
output[y * out_width + x] = value;
}
}
} else {
return Err(RawError::Format(FormatError::Decompression(format!(
"Unsupported component count: {}",
num_components
))));
}
if self.point_transform > 0 {
for pixel in output.iter_mut() {
*pixel <<= self.point_transform;
}
}
Ok(output)
}
}
impl Default for LjpegDecoder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_huffman_extend() {
assert_eq!(LjpegDecoder::extend(0, 0), 0);
assert_eq!(LjpegDecoder::extend(0, 1), -1);
assert_eq!(LjpegDecoder::extend(1, 1), 1);
assert_eq!(LjpegDecoder::extend(0, 2), -3);
assert_eq!(LjpegDecoder::extend(1, 2), -2);
assert_eq!(LjpegDecoder::extend(2, 2), 2);
assert_eq!(LjpegDecoder::extend(3, 2), 3);
}
#[test]
fn test_huffman_table_build() {
let mut table = HuffmanTable::default();
table.bits[1] = 2; table.huffval = vec![0, 1];
table.build_tables();
assert_eq!(table.maxcode[1], 1);
assert_eq!(table.mincode[1], 0);
}
}