#![allow(dead_code)]
use crate::error::{Error, Result};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScanInfo {
pub comps_in_scan: u8,
pub component_index: [u8; 4],
pub ss: u8,
pub se: u8,
pub ah: u8,
pub al: u8,
}
impl ScanInfo {
pub fn new(
comps_in_scan: u8,
component_index: [u8; 4],
ss: u8,
se: u8,
ah: u8,
al: u8,
) -> Self {
Self {
comps_in_scan,
component_index,
ss,
se,
ah,
al,
}
}
#[inline]
pub fn is_dc_scan(&self) -> bool {
self.ss == 0 && self.se == 0
}
#[inline]
pub fn is_ac_scan(&self) -> bool {
self.ss > 0
}
#[inline]
pub fn is_first_pass(&self) -> bool {
self.ah == 0
}
#[inline]
pub fn is_refinement(&self) -> bool {
self.ah != 0
}
}
pub fn validate_scan_script(scans: &[ScanInfo], num_components: u8) -> Result<()> {
if scans.is_empty() {
return Err(Error::invalid_scan_script(
"Scan script must contain at least one scan".into(),
));
}
let mut dc_encoded: Vec<(bool, Option<u8>)> = vec![(false, None); num_components as usize];
let mut ac_encoded: Vec<Vec<(bool, Option<u8>)>> =
vec![vec![(false, None); 64]; num_components as usize];
for (scan_idx, scan) in scans.iter().enumerate() {
if scan.comps_in_scan == 0 || scan.comps_in_scan > 4 {
return Err(Error::invalid_scan_script(format!(
"Scan {}: comps_in_scan {} must be 1-4",
scan_idx, scan.comps_in_scan
)));
}
if scan.comps_in_scan > num_components {
return Err(Error::invalid_scan_script(format!(
"Scan {}: comps_in_scan {} exceeds num_components {}",
scan_idx, scan.comps_in_scan, num_components
)));
}
if scan.se > 63 {
return Err(Error::invalid_scan_script(format!(
"Scan {}: Se {} must be <= 63",
scan_idx, scan.se
)));
}
if scan.ss > scan.se {
return Err(Error::invalid_scan_script(format!(
"Scan {}: Ss {} must be <= Se {}",
scan_idx, scan.ss, scan.se
)));
}
let mut seen_components = [false; 4];
for i in 0..scan.comps_in_scan as usize {
let c = scan.component_index[i];
if c >= num_components {
return Err(Error::invalid_scan_script(format!(
"Scan {}: component index {} >= num_components {}",
scan_idx, c, num_components
)));
}
if seen_components[c as usize] {
return Err(Error::invalid_scan_script(format!(
"Scan {}: duplicate component index {}",
scan_idx, c
)));
}
seen_components[c as usize] = true;
if i > 0 && scan.component_index[i] <= scan.component_index[i - 1] {
return Err(Error::invalid_scan_script(format!(
"Scan {}: components must be in ascending order",
scan_idx
)));
}
}
if scan.ss > 0 && scan.comps_in_scan > 1 {
return Err(Error::invalid_scan_script(format!(
"Scan {}: AC scans (Ss={}) must have exactly one component, got {}",
scan_idx, scan.ss, scan.comps_in_scan
)));
}
for i in 0..scan.comps_in_scan as usize {
let c = scan.component_index[i] as usize;
if scan.ss == 0 {
let (first_done, last_al) = dc_encoded[c];
if scan.is_first_pass() {
if first_done {
return Err(Error::invalid_scan_script(format!(
"Scan {}: DC first pass for component {} already done",
scan_idx, c
)));
}
dc_encoded[c] = (true, Some(scan.al));
} else {
if let Some(prev_al) = last_al {
if scan.ah != prev_al {
return Err(Error::invalid_scan_script(format!(
"Scan {}: DC refinement Ah {} must match previous Al {}",
scan_idx, scan.ah, prev_al
)));
}
} else {
return Err(Error::invalid_scan_script(format!(
"Scan {}: DC refinement before first pass for component {}",
scan_idx, c
)));
}
if scan.al >= scan.ah {
return Err(Error::invalid_scan_script(format!(
"Scan {}: DC refinement Al {} must be < Ah {}",
scan_idx, scan.al, scan.ah
)));
}
dc_encoded[c].1 = Some(scan.al);
}
}
if scan.se > 0 {
if scan.ss > 0 && !dc_encoded[c].0 {
return Err(Error::invalid_scan_script(format!(
"Scan {}: AC scan before DC for component {}",
scan_idx, c
)));
}
let ac_start = if scan.ss == 0 { 1 } else { scan.ss };
for k in ac_start..=scan.se {
let (first_done, last_al) = ac_encoded[c][k as usize];
if scan.is_first_pass() {
if first_done {
return Err(Error::invalid_scan_script(format!(
"Scan {}: AC coefficient {} for component {} already encoded",
scan_idx, k, c
)));
}
ac_encoded[c][k as usize] = (true, Some(scan.al));
} else {
if let Some(prev_al) = last_al {
if scan.ah != prev_al {
return Err(Error::invalid_scan_script(format!(
"Scan {}: AC refinement Ah {} must match previous Al {} for coef {}",
scan_idx, scan.ah, prev_al, k
)));
}
} else {
return Err(Error::invalid_scan_script(format!(
"Scan {}: AC refinement before first pass for coef {}",
scan_idx, k
)));
}
ac_encoded[c][k as usize].1 = Some(scan.al);
}
}
}
}
}
Ok(())
}
pub fn parse_scan_script_text(text: &str) -> Result<Vec<ScanInfo>> {
let mut scans = Vec::new();
for line in text.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let line = line.trim_end_matches(';').trim();
if line.is_empty() {
continue;
}
let mut components = [0u8; 4];
let mut count = 0;
for part in line.split_whitespace() {
if count >= 4 {
return Err(Error::invalid_scan_script(
"Too many components in scan (max 4)".into(),
));
}
components[count] = part.parse().map_err(|_| {
Error::invalid_scan_script(format!("Invalid component index: {}", part))
})?;
count += 1;
}
if count == 0 {
continue;
}
scans.push(ScanInfo::new(
count as u8,
components,
0, 0, 0, 0, ));
}
Ok(scans)
}
#[cfg(test)]
mod tests {
use super::*;
fn make_scan(comps: &[u8], ss: u8, se: u8, ah: u8, al: u8) -> ScanInfo {
let mut component_index = [0u8; 4];
for (i, &c) in comps.iter().enumerate() {
component_index[i] = c;
}
ScanInfo::new(comps.len() as u8, component_index, ss, se, ah, al)
}
#[test]
fn test_valid_baseline_script() {
let scans = vec![make_scan(&[0], 0, 63, 0, 0)];
assert!(validate_scan_script(&scans, 1).is_ok());
}
#[test]
fn test_valid_progressive_script() {
let scans = vec![
make_scan(&[0, 1, 2], 0, 0, 0, 0), make_scan(&[0], 1, 63, 0, 0), make_scan(&[1], 1, 63, 0, 0), make_scan(&[2], 1, 63, 0, 0), ];
assert!(validate_scan_script(&scans, 3).is_ok());
}
#[test]
fn test_valid_non_interleaved() {
let scans = vec![
make_scan(&[0], 0, 63, 0, 0),
make_scan(&[1], 0, 63, 0, 0),
make_scan(&[2], 0, 63, 0, 0),
];
assert!(validate_scan_script(&scans, 3).is_ok());
}
#[test]
fn test_invalid_empty_script() {
let scans: Vec<ScanInfo> = vec![];
assert!(validate_scan_script(&scans, 1).is_err());
}
#[test]
fn test_invalid_too_many_components_in_scan() {
let scans = vec![make_scan(&[0, 1], 0, 63, 0, 0)];
assert!(validate_scan_script(&scans, 1).is_err());
}
#[test]
fn test_invalid_comps_in_scan_too_large() {
let mut scan = make_scan(&[0], 0, 63, 0, 0);
scan.comps_in_scan = 5;
let scans = vec![scan];
assert!(validate_scan_script(&scans, 5).is_err());
}
#[test]
fn test_invalid_duplicate_component() {
let scans = vec![make_scan(&[0, 0], 0, 63, 0, 0)];
assert!(validate_scan_script(&scans, 2).is_err());
}
#[test]
fn test_invalid_component_order() {
let scans = vec![make_scan(&[1, 0], 0, 63, 0, 0)];
assert!(validate_scan_script(&scans, 2).is_err());
}
#[test]
fn test_invalid_se_too_large() {
let scans = vec![make_scan(&[0], 0, 64, 0, 0)];
assert!(validate_scan_script(&scans, 1).is_err());
}
#[test]
fn test_invalid_ss_greater_than_se() {
let scans = vec![make_scan(&[0], 2, 1, 0, 0)];
assert!(validate_scan_script(&scans, 1).is_err());
}
#[test]
fn test_invalid_missing_dc_for_component() {
let _scans = [
make_scan(&[0], 0, 63, 0, 0),
make_scan(&[1], 0, 0, 0, 0),
make_scan(&[1], 1, 63, 0, 0), ];
}
#[test]
fn test_invalid_spectral_gap() {
let _scans = [
make_scan(&[0], 0, 1, 0, 0), make_scan(&[0], 2, 63, 0, 0), ];
}
#[test]
fn test_invalid_ac_interleaved() {
let scans = vec![
make_scan(&[0, 1], 0, 0, 0, 0), make_scan(&[0, 1], 1, 63, 0, 0), ];
assert!(validate_scan_script(&scans, 2).is_err());
}
#[test]
fn test_invalid_ac_before_dc() {
let scans = vec![
make_scan(&[0], 1, 63, 0, 0), make_scan(&[0], 0, 0, 0, 0), ];
assert!(validate_scan_script(&scans, 1).is_err());
}
#[test]
fn test_invalid_sa_first_pass() {
let scans = vec![
make_scan(&[0], 0, 0, 10, 1), make_scan(&[0], 0, 0, 1, 0), make_scan(&[0], 1, 63, 0, 0), ];
assert!(validate_scan_script(&scans, 1).is_err());
}
#[test]
fn test_invalid_sa_ah_mismatch() {
let scans = vec![
make_scan(&[0], 0, 0, 0, 2), make_scan(&[0], 0, 0, 1, 0), make_scan(&[0], 0, 0, 2, 1), make_scan(&[0], 1, 63, 0, 0), ];
assert!(validate_scan_script(&scans, 1).is_err());
}
#[test]
fn test_parse_non_interleaved() {
let text = "0;\n1;\n2;";
let scans = parse_scan_script_text(text).unwrap();
assert_eq!(scans.len(), 3);
assert_eq!(scans[0].comps_in_scan, 1);
assert_eq!(scans[0].component_index[0], 0);
assert_eq!(scans[1].component_index[0], 1);
assert_eq!(scans[2].component_index[0], 2);
}
#[test]
fn test_parse_partially_interleaved() {
let text = "0;\n1 2;";
let scans = parse_scan_script_text(text).unwrap();
assert_eq!(scans.len(), 2);
assert_eq!(scans[0].comps_in_scan, 1);
assert_eq!(scans[1].comps_in_scan, 2);
assert_eq!(scans[1].component_index[0], 1);
assert_eq!(scans[1].component_index[1], 2);
}
}