use crate::codec::jpeg::dct::DctGrid;
use crate::codec::jpeg::zigzag::NATURAL_TO_ZIGZAG;
use crate::stego::cost::CostMap;
use super::embedding::MAX_ARMOR_ZIGZAG;
const STABLE_COST: f32 = 1.0;
pub fn compute_stability_map(grid: &DctGrid, _qt: &crate::codec::jpeg::dct::QuantTable) -> CostMap {
compute_stability_map_freq_only(grid)
}
fn compute_stability_map_freq_only(grid: &DctGrid) -> CostMap {
let bw = grid.blocks_wide();
let bt = grid.blocks_tall();
let mut cost_map = CostMap::new(bw, bt);
for br in 0..bt {
for bc in 0..bw {
for i in 0..8 {
for j in 0..8 {
if i == 0 && j == 0 { continue; }
let freq_idx = i * 8 + j;
let zz = NATURAL_TO_ZIGZAG[freq_idx];
if (1..=MAX_ARMOR_ZIGZAG).contains(&zz) {
cost_map.set(br, bc, i, j, STABLE_COST);
}
}
}
}
}
cost_map
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codec::jpeg::dct::{DctGrid, QuantTable};
use crate::stego::cost::WET_COST;
#[test]
fn stability_map_dc_is_wet_lowfreq_ac_is_stable() {
let mut grid = DctGrid::new(2, 2);
let qt = QuantTable::new([8; 64]);
grid.set(0, 0, 0, 0, 100);
let cost_map = compute_stability_map(&grid, &qt);
for br in 0..2 {
for bc in 0..2 {
assert_eq!(
cost_map.get(br, bc, 0, 0),
WET_COST,
"DC ({br},{bc},0,0) should be WET"
);
assert_eq!(
cost_map.get(br, bc, 0, 1),
STABLE_COST,
"AC ({br},{bc},0,1) should be STABLE (zigzag 1)"
);
assert_eq!(
cost_map.get(br, bc, 1, 0),
STABLE_COST,
"AC ({br},{bc},1,0) should be STABLE (zigzag 2)"
);
}
}
}
#[test]
fn stability_map_excludes_high_freq() {
let mut grid = DctGrid::new(1, 1);
let qt = QuantTable::new([8; 64]);
grid.set(0, 0, 1, 0, 50);
grid.set(0, 0, 0, 1, 1);
let cost_map = compute_stability_map(&grid, &qt);
assert_eq!(cost_map.get(0, 0, 1, 0), STABLE_COST); assert_eq!(cost_map.get(0, 0, 0, 1), STABLE_COST); assert_eq!(cost_map.get(0, 0, 3, 1), STABLE_COST, "zigzag 11 should be STABLE");
assert_eq!(cost_map.get(0, 0, 2, 2), STABLE_COST, "zigzag 12 should be STABLE");
assert_eq!(cost_map.get(0, 0, 7, 7), WET_COST);
assert_eq!(cost_map.get(0, 0, 0, 0), WET_COST);
}
}