use crate::jpeg::comment::CommentHeader;
use crate::jpeg::dct::DctStruct;
use crate::jpeg::dht::DhtHeader;
use crate::jpeg::dqt::DqtHeader;
use crate::jpeg::header::JfifHeader;
use crate::jpeg::huff::Huffman;
use crate::jpeg::obj::JpegObj;
use crate::jpeg::sof::SofHeader;
use crate::jpeg::sos::SosHeader;
use std::error::Error;
use std::fs::File;
use std::io;
use std::io::SeekFrom;
use std::io::{BufReader, ErrorKind, Read, Seek};
const COLOR_RED: &str = "\x1b[91m";
const COLOR_GREEN: &str = "\x1b[92m";
const COLOR_YELLOW: &str = "\x1b[93m";
const COLOR_RESET: &str = "\x1b[0m";
type JpegHeadersResult = Result<
(
Option<JfifHeader>,
Option<CommentHeader>,
Option<DqtHeader>,
Option<SofHeader>,
Option<DhtHeader>,
Option<SosHeader>,
),
Box<dyn Error>,
>;
pub fn read_marker(reader: &mut dyn Read) -> io::Result<u16> {
let mut marker = [0u8; 2];
match reader.read_exact(&mut marker) {
Ok(_) => Ok(u16::from_be_bytes(marker)),
Err(ref e) if e.kind() == ErrorKind::UnexpectedEof => {
eprintln!("Warning: Unexpected end of file while reading marker. Continuing...");
Ok(0)
}
Err(e) => Err(e),
}
}
pub fn read_jpeg_headers(
file_path: &str,
start_chunk: usize,
end_chunk: usize,
num_chunks: usize,
) -> JpegHeadersResult {
let file = File::open(file_path)?;
let mut reader = BufReader::new(file);
let mut jfif_header = None;
let mut dqt_header = None;
let mut sof_header = None;
let mut dht_header = None;
let mut sos_header = None;
let mut image_width = 0;
let mut image_height = 0;
let mut count_chunk = 0;
let mut comment_data = None;
let mut encountered_dqt = false;
reader.seek(SeekFrom::Current(start_chunk as i64))?;
for current_chunk in start_chunk..=end_chunk {
let marker = read_marker(&mut reader)?;
match marker {
0xFFE0 => {
let mut data_length_bytes = [0u8; 2];
reader.read_exact(&mut data_length_bytes)?;
let data_length = u16::from_be_bytes(data_length_bytes);
let mut data = vec![0u8; data_length as usize + 2];
reader.read_exact(&mut data)?;
jfif_header = Some(JfifHeader::new(&data).unwrap());
println!(
"{}JFIF Header: {:?}{}",
COLOR_GREEN,
jfif_header.clone().unwrap(),
COLOR_RESET
);
}
0xFFFE => {
let mut data_length_bytes = [0u8; 2];
reader.read_exact(&mut data_length_bytes)?;
let data_length = u16::from_be_bytes(data_length_bytes);
let mut data = vec![0u8; data_length as usize - 2];
reader.read_exact(&mut data)?;
comment_data = Some(data);
println!(
"{}Comment Header: {:?}{}",
COLOR_YELLOW,
comment_data.clone().unwrap(),
COLOR_RESET
);
}
0xFFDB => {
let mut data_length_bytes = [0u8; 2];
reader.read_exact(&mut data_length_bytes)?;
let data_length = u16::from_be_bytes(data_length_bytes);
let mut data = vec![0u8; data_length as usize - 4];
reader.read_exact(&mut data)?;
let dct_struct = DctStruct::new(&data).unwrap();
dqt_header = Some(DqtHeader::new(dct_struct));
encountered_dqt = true;
println!(
"{}DQT Header for Chunk#{}: {:?}{}",
COLOR_GREEN,
current_chunk,
dqt_header.clone().unwrap(),
COLOR_RESET
);
}
0xFFC0 => {
let mut data_length_bytes = [0u8; 2];
reader.read_exact(&mut data_length_bytes)?;
let data_length = u16::from_be_bytes(data_length_bytes);
let mut data = vec![0u8; data_length as usize - 2];
reader.read_exact(&mut data)?;
let jpeg_obj = process_sof_data(&data);
sof_header = Some(SofHeader::new(jpeg_obj));
image_width = sof_header.clone().unwrap().jpeg_obj.image_width;
image_height = sof_header.clone().unwrap().jpeg_obj.image_height;
println!(
"{}SOF Header for Chunk#{}: {:?}{}",
COLOR_YELLOW,
current_chunk,
sof_header.clone().unwrap(),
COLOR_RESET
);
}
0xFFC4 => {
let mut data_length_bytes = [0u8; 2];
reader.read_exact(&mut data_length_bytes)?;
let data_length = u16::from_be_bytes(data_length_bytes);
let mut data = vec![0u8; data_length as usize - 2];
reader.read_exact(&mut data)?;
let huf_struct = process_dht_data(&data);
dht_header = Some(DhtHeader::new(huf_struct));
println!(
"{}Processing DHT Header for Chunk#{}: {}",
COLOR_RED, current_chunk, COLOR_RESET
);
}
0xFFDA => {
let mut data_length_bytes = [0u8; 2];
reader.read_exact(&mut data_length_bytes)?;
let data_length = u16::from_be_bytes(data_length_bytes);
let mut data = vec![0u8; data_length as usize - 2];
reader.read_exact(&mut data)?;
let jpeg_obj = process_sos_data(&data, image_height, image_width);
sos_header = Some(SosHeader::new(jpeg_obj));
println!(
"{}SOS Header for Chunk#{}: {:?}{}",
COLOR_GREEN,
current_chunk,
sos_header.clone().unwrap(),
COLOR_RESET
);
}
0xFFD9 => {
println!(
"{}End of Headers for Chunk {}{}",
COLOR_RED, current_chunk, COLOR_RESET
);
break;
}
0 => {
println!(
"{}End of Headers for Chunk {}{}",
COLOR_RED, current_chunk, COLOR_RESET
);
break;
}
_ => {
}
}
if current_chunk > end_chunk {
break;
}
count_chunk += 1;
if count_chunk > num_chunks {
break;
}
}
let comment_str = match comment_data {
Some(data) => String::from_utf8_lossy(&data).trim().to_owned(),
None => String::new(),
};
let comment_header = Some(CommentHeader::new(&comment_str));
Ok((
jfif_header,
comment_header,
if encountered_dqt {
Some(
dqt_header
.ok_or_else(|| io::Error::new(ErrorKind::InvalidData, "Missing DQT header"))?,
)
} else {
None
},
sof_header,
dht_header,
sos_header,
))
}
pub fn process_sof_data(data: &[u8]) -> JpegObj {
let precision = data[0];
let image_height = u16::from_be_bytes([data[1], data[2]]);
let image_width = u16::from_be_bytes([data[3], data[4]]);
let number_of_components = data[5];
let mut comp_id = Vec::with_capacity(number_of_components as usize);
let mut hsamp_factor = Vec::with_capacity(number_of_components as usize);
let mut vsamp_factor = Vec::with_capacity(number_of_components as usize);
let mut qtable_number = Vec::with_capacity(number_of_components as usize);
let mut index = 6;
for _ in 0..number_of_components {
comp_id.push(data[index]);
index += 1;
hsamp_factor.push((data[index] >> 4) & 0xF);
vsamp_factor.push(data[index] & 0xF);
index += 1;
qtable_number.push(data[index]);
index += 1;
}
let dctable_number = (1..=number_of_components).collect();
let actable_number = (11..=11 + number_of_components).collect();
let ss = 0x00; let se = 0x3F; let ah = 0x00; let al = 0x00;
JpegObj {
precision,
image_height,
image_width,
number_of_components,
comp_id,
hsamp_factor,
vsamp_factor,
qtable_number,
dctable_number,
actable_number,
ss,
se,
ah,
al,
}
}
pub fn process_dht_data(data: &[u8]) -> Huffman {
let image_height = u16::from_be_bytes([data[0], data[1]]);
let image_width = u16::from_be_bytes([data[2], data[3]]);
let mut huf_struct = Huffman::new(image_width as i32, image_height as i32);
let mut index = 4;
let mut old_index = 4;
for i in 0..4 {
if index < data.len() {
let bytes = data[index];
huf_struct.bits[i][0] = bytes as i32;
index += 1;
} else {
break;
}
for j in 1..17 {
if index < data.len() {
huf_struct.bits[i][j] = data[index] as i32;
index += 1;
} else {
break;
}
}
let bytes = huf_struct.bits[i][0] as usize;
if index + bytes <= data.len() {
let huf_vals: Vec<i32> = data[index..index + bytes]
.iter()
.map(|&x| x as i32)
.collect();
huf_struct.val[i] = huf_vals;
index += bytes;
} else {
}
let mut dht3 = vec![0xFF, 0xC4];
dht3.extend_from_slice(&data[old_index..index]);
old_index = index;
huf_struct.bits[i][2] = ((index - 2) >> 8) as i32;
huf_struct.bits[i][3] = (index - 2) as i32;
}
huf_struct
}
pub fn process_sos_data(data: &[u8], image_height: u16, image_width: u16) -> JpegObj {
let precision = data[0];
let number_of_components = data[5];
let mut comp_id = Vec::with_capacity(number_of_components as usize);
let mut dc_table_number = Vec::with_capacity(number_of_components as usize);
let mut ac_table_number = Vec::with_capacity(number_of_components as usize);
let mut hsamp_factor = Vec::with_capacity(number_of_components as usize);
let mut vsamp_factor = Vec::with_capacity(number_of_components as usize);
let mut qtable_number = Vec::with_capacity(number_of_components as usize);
let mut index = 6;
for _ in 0..number_of_components {
if index + 1 < data.len() {
comp_id.push(data[index]);
index += 1;
dc_table_number.push((data[index] >> 4) & 0xF);
ac_table_number.push(data[index] & 0xF);
index += 1;
hsamp_factor.push((data[index] >> 4) & 0xF);
vsamp_factor.push(data[index] & 0xF);
qtable_number.push(data[index + 1]);
index += 2;
} else {
break;
}
}
let ss = if index < data.len() { data[index] } else { 0 };
let se = if index + 1 < data.len() {
data[index + 1]
} else {
0
};
let ah_al = if index + 2 < data.len() {
data[index + 2]
} else {
0
};
let ah = (ah_al >> 4) & 0xF;
let al = ah_al & 0xF;
JpegObj {
precision,
image_height,
image_width,
number_of_components,
comp_id,
hsamp_factor,
vsamp_factor,
qtable_number,
dctable_number: dc_table_number,
actable_number: ac_table_number,
ss,
se,
ah,
al,
}
}