use super::constants::{
COLOR_TYPE_CMYK8, COLOR_TYPE_GRAYSCALE, COLOR_TYPE_L8, COLOR_TYPE_RGB8, JPEG_EOI, JPEG_TEM,
SUBSAMPLING_411, SUBSAMPLING_420, SUBSAMPLING_422, SUBSAMPLING_444, SUBSAMPLING_UNKNOWN,
has_jpeg_signature, is_jpeg_rst_marker, is_jpeg_sof_marker,
};
use log::warn;
pub fn extract_subsampling(jpeg_data: &[u8]) -> Option<String> {
jpeg_chroma_subsampling(jpeg_data)
}
pub fn extract_compression(_jpeg_data: &[u8]) -> String {
"JPEG".to_string()
}
pub fn extract_bit_depth(jpeg_data: &[u8]) -> Option<u32> {
if !has_jpeg_signature(jpeg_data) {
return None;
}
let mut i = 2usize;
while i + 4 <= jpeg_data.len() {
if jpeg_data[i] != 0xFF {
i += 1;
continue;
}
while i < jpeg_data.len() && jpeg_data[i] == 0xFF {
i += 1;
}
if i >= jpeg_data.len() {
break;
}
let marker = jpeg_data[i];
i += 1;
match marker {
marker if marker == JPEG_EOI => break,
marker if is_jpeg_rst_marker(marker) => continue,
marker if marker == JPEG_TEM => continue,
_ => {}
}
if i + 2 > jpeg_data.len() {
break;
}
let seg_len = u16::from_be_bytes([jpeg_data[i], jpeg_data[i + 1]]) as usize;
i += 2;
let is_sof = is_jpeg_sof_marker(marker);
if is_sof && seg_len >= 3 && i < jpeg_data.len() {
let precision = jpeg_data[i];
return Some(precision as u32);
}
if seg_len < 2 || i + (seg_len - 2) > jpeg_data.len() {
break;
}
i += seg_len - 2;
}
Some(8)
}
pub fn extract_color_type(jpeg_data: &[u8]) -> Option<String> {
if !has_jpeg_signature(jpeg_data) {
return None;
}
let mut i = 2usize;
while i + 4 <= jpeg_data.len() {
if jpeg_data[i] != 0xFF {
i += 1;
continue;
}
while i < jpeg_data.len() && jpeg_data[i] == 0xFF {
i += 1;
}
if i >= jpeg_data.len() {
break;
}
let marker = jpeg_data[i];
i += 1;
match marker {
marker if marker == JPEG_EOI => break,
marker if is_jpeg_rst_marker(marker) => continue,
marker if marker == JPEG_TEM => continue,
_ => {}
}
if i + 2 > jpeg_data.len() {
break;
}
let seg_len = u16::from_be_bytes([jpeg_data[i], jpeg_data[i + 1]]) as usize;
i += 2;
let is_sof = is_jpeg_sof_marker(marker);
if is_sof && seg_len >= 6 && i + 5 <= jpeg_data.len() {
let num_components = jpeg_data[i + 5];
return match num_components {
1 => Some(COLOR_TYPE_L8.to_string()), 3 => Some(COLOR_TYPE_RGB8.to_string()), 4 => Some(COLOR_TYPE_CMYK8.to_string()), _ => Some(format!("Unknown({num_components})")),
};
}
if seg_len < 2 || i + (seg_len - 2) > jpeg_data.len() {
break;
}
i += seg_len - 2;
}
Some(COLOR_TYPE_RGB8.to_string())
}
fn jpeg_chroma_subsampling(jpeg_data: &[u8]) -> Option<String> {
if jpeg_data.len() < 4 || jpeg_data[0] != 0xFF || jpeg_data[1] != 0xD8 {
return None;
}
let mut i = 2usize;
while i + 4 <= jpeg_data.len() {
if jpeg_data[i] != 0xFF {
i += 1;
continue;
}
while i < jpeg_data.len() && jpeg_data[i] == 0xFF {
i += 1;
}
if i >= jpeg_data.len() {
break;
}
let marker = jpeg_data[i];
i += 1;
match marker {
marker if marker == JPEG_EOI => break,
marker if is_jpeg_rst_marker(marker) => continue,
marker if marker == JPEG_TEM => continue,
_ => {}
}
if i + 2 > jpeg_data.len() {
break;
}
let seg_len = u16::from_be_bytes([jpeg_data[i], jpeg_data[i + 1]]) as usize;
i += 2;
if seg_len < 2 || i + (seg_len - 2) > jpeg_data.len() {
break;
}
let is_sof = is_jpeg_sof_marker(marker);
if is_sof {
let seg = &jpeg_data[i..i + (seg_len - 2)];
if seg.len() < 6 {
return None;
}
let nf = seg[5] as usize;
if nf == 0 {
return None;
}
if nf == 1 {
return Some(COLOR_TYPE_GRAYSCALE.to_string());
}
if seg.len() < 6 + nf * 3 {
return None;
}
let mut y: Option<(u8, u8)> = None;
let mut cb: Option<(u8, u8)> = None;
let mut cr: Option<(u8, u8)> = None;
for c in 0..nf {
let base = 6 + c * 3;
let cid = seg[base];
let hv = seg[base + 1];
let h = hv >> 4;
let v = hv & 0x0F;
match cid {
1 => y = Some((h, v)),
2 => cb = Some((h, v)),
3 => cr = Some((h, v)),
_ => {}
}
}
let (yh, yv) = y?;
let cb = cb.or(cr);
let (ch, cv) = cb?;
let out = match (yh, yv, ch, cv) {
(1, 1, 1, 1) => SUBSAMPLING_444,
(2, 1, 1, 1) => SUBSAMPLING_422,
(2, 2, 1, 1) => SUBSAMPLING_420,
(4, 1, 1, 1) => SUBSAMPLING_411,
_ => {
warn!("Unknown JPEG sampling factors: Y={yh}x{yv}, C={ch}x{cv}");
return Some(SUBSAMPLING_UNKNOWN.to_string());
}
};
return Some(out.to_string());
}
i += seg_len - 2;
}
None
}