//! 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 = 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::>().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; } }