Files
hcie-rust-v3.05/hcie-io/examples/compare_psd.rs
T
2026-07-13 20:33:39 +03:00

142 lines
5.8 KiB
Rust

//! Test: read Example3-mini.psd, then save it as hcie PSD and compare
//! layer records with the original.
fn main() {
let input_path = "/mnt/extra/00_PROJECTS/hcie-rust-_images/_test_images/example3/Example3-mini.psd";
let output_path = "/mnt/extra/00_PROJECTS/hcie-rust-_images/_test_images/example3/Example3-mini-hcie-v2.psd";
println!("Reading original PSD...");
let orig_bytes = std::fs::read(input_path).unwrap();
// Import via hcie-psd
let layers = hcie_psd::psd_import::import_psd(std::path::Path::new(input_path)).unwrap();
println!("Imported {} layers", layers.len());
for (i, l) in layers.iter().enumerate() {
println!(" Layer {}: \"{}\" blend={:?} opacity={} visible={} type={:?} mask={:?} adj={} div={}",
i, l.name, l.blend_mode, l.opacity, l.visible, l.layer_type,
l.mask_bounds.is_some(), l.adjustment_raw.is_some(), l.is_section_divider);
}
// Read canvas from PSD header
let psd = hcie_psd::Psd::from_bytes(&orig_bytes).unwrap();
let canvas_w = psd.width();
let canvas_h = psd.height();
println!("Canvas: {}x{}", canvas_w, canvas_h);
// Create composited image (simple white background for now)
let composited = vec![255u8; (canvas_w * canvas_h * 4) as usize];
// Save
println!("Saving to {}...", output_path);
hcie_psd_saver::save_psd(&layers, canvas_w, canvas_h, &composited, std::path::Path::new(output_path)).unwrap();
println!("Saved!");
// Now compare the saved output with original
let saved_bytes = std::fs::read(output_path).unwrap();
println!("\n=== Comparison ===");
println!("Original: {} bytes", orig_bytes.len());
println!("Saved: {} bytes", saved_bytes.len());
// Compare layer records
println!("\n--- Original ---");
dump_layer_meta(&orig_bytes, "ORIG");
println!("\n--- Saved ---");
dump_layer_meta(&saved_bytes, "SAVE");
}
fn dump_layer_meta(data: &[u8], label: &str) {
let mut pos = 26;
let cm_len = u32::from_be_bytes([data[pos], data[pos+1], data[pos+2], data[pos+3]]) as usize;
pos += 4 + cm_len;
let ir_len = u32::from_be_bytes([data[pos], data[pos+1], data[pos+2], data[pos+3]]) as usize;
pos += 4 + ir_len;
let _lm_len = u32::from_be_bytes([data[pos], data[pos+1], data[pos+2], data[pos+3]]) as usize;
pos += 4;
let _li_len = u32::from_be_bytes([data[pos], data[pos+1], data[pos+2], data[pos+3]]) as usize;
pos += 4;
let layer_count = i16::from_be_bytes([data[pos], data[pos+1]]).unsigned_abs() as usize;
pos += 2;
for idx in 0..layer_count {
pos += 16; // bounds
let ch_count = u16::from_be_bytes([data[pos], data[pos+1]]) as usize;
pos += 2;
let mut channels = Vec::new();
for _ in 0..ch_count {
let id = i16::from_be_bytes([data[pos], data[pos+1]]);
let len = u32::from_be_bytes([data[pos+2], data[pos+3], data[pos+4], data[pos+5]]) as usize;
channels.push((id, len));
pos += 6;
}
pos += 4; // sig
let blend_key = std::str::from_utf8(&data[pos..pos+4]).unwrap_or("????");
pos += 4;
let opacity = data[pos]; pos += 1;
let clipping = data[pos]; pos += 1;
let flags = data[pos]; pos += 1;
pos += 1; // filler
let extra_len = u32::from_be_bytes([data[pos], data[pos+1], data[pos+2], data[pos+3]]) as usize;
pos += 4;
let extra_start = pos;
let mask_len = u32::from_be_bytes([data[pos], data[pos+1], data[pos+2], data[pos+3]]) as usize;
pos += 4;
let mask_hex = if mask_len > 0 {
let show = mask_len.min(30);
let hex: Vec<String> = data[pos..pos+show].iter().map(|b| format!("{:02x}", b)).collect();
hex.join(" ")
} else { "none".to_string() };
pos += mask_len;
let br_len = u32::from_be_bytes([data[pos], data[pos+1], data[pos+2], data[pos+3]]) as usize;
pos += 4 + br_len;
let name_len = data[pos] as usize;
pos += 1;
let name = String::from_utf8_lossy(&data[pos..pos + name_len]).to_string();
pos += name_len;
let pascal_total = 1 + name_len;
let pad = (4 - (pascal_total % 4)) % 4;
pos += pad;
let ch_str = channels.iter().map(|(id, len)| format!("{}:{}", id, len)).collect::<Vec<_>>().join(",");
print!("[{}] L{}: \"{}\" blend={} op={} clip={} flags={:08b} ch=[{}] mask_len={}",
label, idx, name, blend_key, opacity, clipping, flags, ch_str, mask_len);
// Check for lsct
let mut tag_pos = pos;
while tag_pos < extra_start + extra_len {
let tag_sig = &data[tag_pos..tag_pos+4];
if tag_sig != b"8BIM" && tag_sig != b"8B64" { break; }
tag_pos += 4;
let key = std::str::from_utf8(&data[tag_pos..tag_pos+4]).unwrap_or("????");
tag_pos += 4;
let block_len = u32::from_be_bytes([data[tag_pos], data[tag_pos+1], data[tag_pos+2], data[tag_pos+3]]) as usize;
tag_pos += 4;
if key == "lsct" {
let section_type = if block_len >= 4 {
u32::from_be_bytes([data[tag_pos], data[tag_pos+1], data[tag_pos+2], data[tag_pos+3]])
} else { 999 };
let blend = if block_len >= 12 {
std::str::from_utf8(&data[tag_pos+8..tag_pos+12]).unwrap_or("????").to_string()
} else { "n/a".to_string() };
print!(" lsct:type={},blend={}", section_type, blend);
}
tag_pos += block_len;
if block_len % 2 != 0 { tag_pos += 1; }
}
println!();
if mask_len > 0 {
println!(" mask: {}", mask_hex);
}
pos = extra_start + extra_len;
}
}