use crate::encode::tables::ZIGZAG_ORDER;
pub fn write_soi(buf: &mut Vec<u8>) {
buf.push(0xFF);
buf.push(0xD8);
}
pub fn write_app0_jfif(buf: &mut Vec<u8>) {
buf.push(0xFF);
buf.push(0xE0);
let length: u16 = 16; buf.extend_from_slice(&length.to_be_bytes());
buf.extend_from_slice(b"JFIF\0");
buf.push(1); buf.push(1);
buf.push(1);
buf.extend_from_slice(&72u16.to_be_bytes());
buf.extend_from_slice(&72u16.to_be_bytes());
buf.push(0);
buf.push(0);
}
pub fn write_dqt(buf: &mut Vec<u8>, table_id: u8, table: &[u16; 64]) {
buf.push(0xFF);
buf.push(0xDB);
let is_16bit = table.iter().any(|&v| v > 255);
let precision = if is_16bit { 1u8 } else { 0u8 };
let table_bytes = if is_16bit { 128 } else { 64 };
let length: u16 = 2 + 1 + table_bytes; buf.extend_from_slice(&length.to_be_bytes());
buf.push((precision << 4) | (table_id & 0x0F));
for &natural_idx in &ZIGZAG_ORDER[..64] {
if is_16bit {
buf.extend_from_slice(&table[natural_idx].to_be_bytes());
} else {
buf.push(table[natural_idx] as u8);
}
}
}
pub fn write_sof0(buf: &mut Vec<u8>, width: u16, height: u16, components: &[(u8, u8, u8, u8)]) {
buf.push(0xFF);
buf.push(0xC0);
let length: u16 = 2 + 1 + 2 + 2 + 1 + (components.len() as u16 * 3);
buf.extend_from_slice(&length.to_be_bytes());
buf.push(8);
buf.extend_from_slice(&height.to_be_bytes());
buf.extend_from_slice(&width.to_be_bytes());
buf.push(components.len() as u8);
for &(id, h_samp, v_samp, quant_tbl_id) in components {
buf.push(id);
buf.push((h_samp << 4) | v_samp);
buf.push(quant_tbl_id);
}
}
pub fn write_dht(buf: &mut Vec<u8>, class: u8, id: u8, bits: &[u8; 17], values: &[u8]) {
buf.push(0xFF);
buf.push(0xC4);
let num_symbols: usize = bits[1..].iter().map(|&b| b as usize).sum();
let length: u16 = (2 + 1 + 16 + num_symbols) as u16;
buf.extend_from_slice(&length.to_be_bytes());
buf.push((class << 4) | (id & 0x0F));
buf.extend_from_slice(&bits[1..17]);
buf.extend_from_slice(&values[..num_symbols]);
}
pub fn write_sos(buf: &mut Vec<u8>, components: &[(u8, u8, u8)]) {
buf.push(0xFF);
buf.push(0xDA);
let length: u16 = 2 + 1 + (components.len() as u16 * 2) + 3;
buf.extend_from_slice(&length.to_be_bytes());
buf.push(components.len() as u8);
for &(id, dc_tbl, ac_tbl) in components {
buf.push(id);
buf.push((dc_tbl << 4) | ac_tbl);
}
buf.push(0); buf.push(63); buf.push(0); }
pub fn write_sof2(buf: &mut Vec<u8>, width: u16, height: u16, components: &[(u8, u8, u8, u8)]) {
buf.push(0xFF);
buf.push(0xC2);
let length: u16 = 2 + 1 + 2 + 2 + 1 + (components.len() as u16 * 3);
buf.extend_from_slice(&length.to_be_bytes());
buf.push(8); buf.extend_from_slice(&height.to_be_bytes());
buf.extend_from_slice(&width.to_be_bytes());
buf.push(components.len() as u8);
for &(id, h_samp, v_samp, quant_tbl_id) in components {
buf.push(id);
buf.push((h_samp << 4) | v_samp);
buf.push(quant_tbl_id);
}
}
pub fn write_sof9(buf: &mut Vec<u8>, width: u16, height: u16, components: &[(u8, u8, u8, u8)]) {
buf.push(0xFF);
buf.push(0xC9);
let length: u16 = 2 + 1 + 2 + 2 + 1 + (components.len() as u16 * 3);
buf.extend_from_slice(&length.to_be_bytes());
buf.push(8); buf.extend_from_slice(&height.to_be_bytes());
buf.extend_from_slice(&width.to_be_bytes());
buf.push(components.len() as u8);
for &(id, h_samp, v_samp, quant_tbl_id) in components {
buf.push(id);
buf.push((h_samp << 4) | v_samp);
buf.push(quant_tbl_id);
}
}
pub fn write_sof10(buf: &mut Vec<u8>, width: u16, height: u16, components: &[(u8, u8, u8, u8)]) {
buf.push(0xFF);
buf.push(0xCA);
let length: u16 = 2 + 1 + 2 + 2 + 1 + (components.len() as u16 * 3);
buf.extend_from_slice(&length.to_be_bytes());
buf.push(8); buf.extend_from_slice(&height.to_be_bytes());
buf.extend_from_slice(&width.to_be_bytes());
buf.push(components.len() as u8);
for &(id, h_samp, v_samp, quant_tbl_id) in components {
buf.push(id);
buf.push((h_samp << 4) | v_samp);
buf.push(quant_tbl_id);
}
}
pub fn write_sos_progressive(
buf: &mut Vec<u8>,
components: &[(u8, u8, u8)],
ss: u8,
se: u8,
ah: u8,
al: u8,
) {
buf.push(0xFF);
buf.push(0xDA);
let length: u16 = 2 + 1 + (components.len() as u16 * 2) + 3;
buf.extend_from_slice(&length.to_be_bytes());
buf.push(components.len() as u8);
for &(id, dc_tbl, ac_tbl) in components {
buf.push(id);
buf.push((dc_tbl << 4) | ac_tbl);
}
buf.push(ss);
buf.push(se);
buf.push((ah << 4) | al);
}
pub fn write_dac(
buf: &mut Vec<u8>,
num_dc: usize,
dc_params: &[(u8, u8)],
num_ac: usize,
ac_params: &[u8],
) {
buf.push(0xFF);
buf.push(0xCC);
let num_entries = num_dc + num_ac;
let length: u16 = 2 + (num_entries as u16 * 2);
buf.extend_from_slice(&length.to_be_bytes());
for (i, &(l, u)) in dc_params[..num_dc].iter().enumerate() {
buf.push(i as u8); buf.push((u << 4) | l);
}
for (i, &val) in ac_params[..num_ac].iter().enumerate() {
buf.push(0x10 | i as u8); buf.push(val);
}
}
pub fn write_app1_exif(buf: &mut Vec<u8>, tiff_data: &[u8]) {
let header = b"Exif\0\0";
let marker_len: u16 = (2 + header.len() + tiff_data.len()) as u16;
buf.push(0xFF);
buf.push(0xE1); buf.extend_from_slice(&marker_len.to_be_bytes());
buf.extend_from_slice(header);
buf.extend_from_slice(tiff_data);
}
pub fn write_app2_icc(buf: &mut Vec<u8>, profile: &[u8]) {
const ICC_OVERHEAD: usize = 14; const MAX_DATA: usize = 65533 - ICC_OVERHEAD;
let num_markers = profile.len().div_ceil(MAX_DATA);
let mut offset = 0;
for seq in 1..=num_markers {
let chunk_len = (profile.len() - offset).min(MAX_DATA);
let marker_len: u16 = (ICC_OVERHEAD + chunk_len) as u16 + 2;
buf.push(0xFF);
buf.push(0xE2); buf.extend_from_slice(&marker_len.to_be_bytes());
buf.extend_from_slice(b"ICC_PROFILE\0");
buf.push(seq as u8);
buf.push(num_markers as u8);
buf.extend_from_slice(&profile[offset..offset + chunk_len]);
offset += chunk_len;
}
}
pub fn write_app14_adobe(buf: &mut Vec<u8>, transform: u8) {
buf.push(0xFF);
buf.push(0xEE); let length: u16 = 14; buf.extend_from_slice(&length.to_be_bytes());
buf.extend_from_slice(b"Adobe"); buf.extend_from_slice(&100u16.to_be_bytes()); buf.extend_from_slice(&0u16.to_be_bytes()); buf.extend_from_slice(&0u16.to_be_bytes()); buf.push(transform); }
pub fn write_sof3(
buf: &mut Vec<u8>,
width: u16,
height: u16,
precision: u8,
components: &[(u8, u8, u8, u8)],
) {
buf.push(0xFF);
buf.push(0xC3); let length: u16 = 2 + 1 + 2 + 2 + 1 + (components.len() as u16 * 3);
buf.extend_from_slice(&length.to_be_bytes());
buf.push(precision);
buf.extend_from_slice(&height.to_be_bytes());
buf.extend_from_slice(&width.to_be_bytes());
buf.push(components.len() as u8);
for &(id, h_samp, v_samp, qt_idx) in components {
buf.push(id);
buf.push((h_samp << 4) | v_samp);
buf.push(qt_idx);
}
}
pub fn write_sof11(
buf: &mut Vec<u8>,
width: u16,
height: u16,
precision: u8,
components: &[(u8, u8, u8, u8)],
) {
buf.push(0xFF);
buf.push(0xCB); let length: u16 = 2 + 1 + 2 + 2 + 1 + (components.len() as u16 * 3);
buf.extend_from_slice(&length.to_be_bytes());
buf.push(precision);
buf.extend_from_slice(&height.to_be_bytes());
buf.extend_from_slice(&width.to_be_bytes());
buf.push(components.len() as u8);
for &(id, h_samp, v_samp, qt_idx) in components {
buf.push(id);
buf.push((h_samp << 4) | v_samp);
buf.push(qt_idx);
}
}
pub fn write_sos_lossless(
buf: &mut Vec<u8>,
components: &[(u8, u8)],
predictor: u8,
point_transform: u8,
) {
buf.push(0xFF);
buf.push(0xDA); let length: u16 = 2 + 1 + (components.len() as u16 * 2) + 3;
buf.extend_from_slice(&length.to_be_bytes());
buf.push(components.len() as u8);
for &(id, dc_tbl) in components {
buf.push(id);
buf.push(dc_tbl << 4); }
buf.push(predictor); buf.push(0); buf.push(point_transform & 0x0F); }
pub fn write_com(buf: &mut Vec<u8>, text: &str) {
buf.push(0xFF);
buf.push(0xFE);
let length: u16 = (2 + text.len()) as u16;
buf.extend_from_slice(&length.to_be_bytes());
buf.extend_from_slice(text.as_bytes());
}
pub fn write_dri(buf: &mut Vec<u8>, interval: u16) {
buf.push(0xFF);
buf.push(0xDD);
buf.extend_from_slice(&4u16.to_be_bytes());
buf.extend_from_slice(&interval.to_be_bytes());
}
pub fn write_marker(buf: &mut Vec<u8>, code: u8, data: &[u8]) {
buf.push(0xFF);
buf.push(code);
let length: u16 = (2 + data.len()) as u16;
buf.extend_from_slice(&length.to_be_bytes());
buf.extend_from_slice(data);
}
pub fn write_app0_jfif_with_density(buf: &mut Vec<u8>, unit: u8, x_density: u16, y_density: u16) {
buf.push(0xFF);
buf.push(0xE0);
let length: u16 = 16;
buf.extend_from_slice(&length.to_be_bytes());
buf.extend_from_slice(b"JFIF\0");
buf.push(1);
buf.push(1);
buf.push(unit);
buf.extend_from_slice(&x_density.to_be_bytes());
buf.extend_from_slice(&y_density.to_be_bytes());
buf.push(0);
buf.push(0);
}
pub fn write_eoi(buf: &mut Vec<u8>) {
buf.push(0xFF);
buf.push(0xD9);
}
pub struct MarkerStreamWriter {
marker_type: u8,
data: Vec<u8>,
}
impl MarkerStreamWriter {
pub fn new(marker_type: u8) -> Self {
Self {
marker_type,
data: Vec::new(),
}
}
pub fn write_byte(&mut self, byte: u8) {
self.data.push(byte);
}
pub fn write_bytes(&mut self, bytes: &[u8]) {
self.data.extend_from_slice(bytes);
}
pub fn finish(self) -> Vec<u8> {
let length: u16 = (2 + self.data.len()) as u16;
let mut segment: Vec<u8> = Vec::with_capacity(4 + self.data.len());
segment.push(0xFF);
segment.push(self.marker_type);
segment.extend_from_slice(&length.to_be_bytes());
segment.extend_from_slice(&self.data);
segment
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn soi_marker() {
let mut buf = Vec::new();
write_soi(&mut buf);
assert_eq!(buf, [0xFF, 0xD8]);
}
#[test]
fn eoi_marker() {
let mut buf = Vec::new();
write_eoi(&mut buf);
assert_eq!(buf, [0xFF, 0xD9]);
}
#[test]
fn app0_jfif_marker() {
let mut buf = Vec::new();
write_app0_jfif(&mut buf);
assert_eq!(buf[0], 0xFF);
assert_eq!(buf[1], 0xE0);
let length = u16::from_be_bytes([buf[2], buf[3]]);
assert_eq!(length, 16);
assert_eq!(&buf[4..9], b"JFIF\0");
assert_eq!(buf[9], 1);
assert_eq!(buf[10], 1);
assert_eq!(buf.len(), 18); }
#[test]
fn dqt_8bit_precision() {
let table = [16u16; 64];
let mut buf = Vec::new();
write_dqt(&mut buf, 0, &table);
assert_eq!(buf[0], 0xFF);
assert_eq!(buf[1], 0xDB);
let length = u16::from_be_bytes([buf[2], buf[3]]);
assert_eq!(length, 67); assert_eq!(buf[4], 0x00);
for i in 0..64 {
assert_eq!(buf[5 + i], 16);
}
}
#[test]
fn dqt_16bit_precision() {
let mut table = [16u16; 64];
table[0] = 300; let mut buf = Vec::new();
write_dqt(&mut buf, 1, &table);
assert_eq!(buf[4], 0x11); let length = u16::from_be_bytes([buf[2], buf[3]]);
assert_eq!(length, 131); }
#[test]
fn sof0_marker_rgb() {
let mut buf = Vec::new();
let components = vec![
(1, 2, 2, 0), (2, 1, 1, 1), (3, 1, 1, 1), ];
write_sof0(&mut buf, 640, 480, &components);
assert_eq!(buf[0], 0xFF);
assert_eq!(buf[1], 0xC0);
assert_eq!(buf[4], 8);
assert_eq!(u16::from_be_bytes([buf[5], buf[6]]), 480);
assert_eq!(u16::from_be_bytes([buf[7], buf[8]]), 640);
assert_eq!(buf[9], 3);
}
#[test]
fn sos_marker() {
let mut buf = Vec::new();
let components = vec![
(1, 0, 0), (2, 1, 1), (3, 1, 1), ];
write_sos(&mut buf, &components);
assert_eq!(buf[0], 0xFF);
assert_eq!(buf[1], 0xDA);
assert_eq!(buf[4], 3);
let offset = 4 + 1 + 3 * 2;
assert_eq!(buf[offset], 0);
assert_eq!(buf[offset + 1], 63);
assert_eq!(buf[offset + 2], 0);
}
#[test]
fn dht_marker() {
let bits: [u8; 17] = [0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0];
let values: [u8; 12] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
let mut buf = Vec::new();
write_dht(&mut buf, 0, 0, &bits, &values);
assert_eq!(buf[0], 0xFF);
assert_eq!(buf[1], 0xC4);
assert_eq!(buf[4], 0x00);
let length = u16::from_be_bytes([buf[2], buf[3]]);
assert_eq!(length, 31);
}
}