use hcie_protocol::Layer; use hcie_protocol::effects::LayerEffect; use std::path::Path; fn main() { let output_dir = Path::new("_tmp/psd_test_output"); std::fs::create_dir_all(output_dir).ok(); println!("=== Generate Test PSDs ==="); // Test 1: Simple layer, no effects { let layers = vec![ Layer::new_blank("Background", 256, 256), ]; let composited = vec![255u8; 256 * 256 * 4]; let path = output_dir.join("test_simple.psd"); hcie_psd::save_psd(&layers, 256, 256, &composited, &path).unwrap(); verify_psd(&path, "Simple (no effects)"); } // Test 2: Layer with InnerShadow effect { let mut layer = Layer::new_blank("Effect Layer", 256, 256); layer.effects.push(LayerEffect::InnerShadow { enabled: true, blend_mode: "normal".to_string(), color: [0, 0, 0, 255], opacity: 0.75, angle: 120.0, distance: 10.0, choke: 5.0, size: 15.0, noise: 0.0, contour: None, }); let layers = vec![layer]; let composited = vec![255u8; 256 * 256 * 4]; let path = output_dir.join("test_inner_shadow.psd"); hcie_psd::save_psd(&layers, 256, 256, &composited, &path).unwrap(); verify_psd(&path, "InnerShadow"); } // Test 3: Two layers, one with effects { let bg = Layer::new_blank("Background", 256, 256); let mut fg = Layer::new_blank("Foreground", 256, 256); fg.effects.push(LayerEffect::DropShadow { enabled: true, blend_mode: "normal".to_string(), color: [0, 0, 0, 255], opacity: 0.5, angle: 120.0, distance: 5.0, spread: 0.0, size: 10.0, noise: 0.0, contour: None, }); let layers = vec![bg, fg]; let composited = vec![255u8; 256 * 256 * 4]; let path = output_dir.join("test_two_layers.psd"); hcie_psd::save_psd(&layers, 256, 256, &composited, &path).unwrap(); verify_psd(&path, "Two layers + DropShadow"); } // Test 4: Roundtrip the reference PSD { let ref_path = Path::new("_images/_psd_stil_test/inner_shadow/black_triangle_green_inner_shadow_normal_blend.psd"); if ref_path.exists() { let layers = hcie_psd::import_psd(ref_path).unwrap(); let orig_bytes = std::fs::read(ref_path).unwrap(); let psd = hcie_psd::Psd::from_bytes(&orig_bytes).unwrap(); let composited = psd.rgba(); let path = output_dir.join("test_roundtrip.psd"); hcie_psd::save_psd(&layers, psd.width(), psd.height(), &composited, &path).unwrap(); verify_psd(&path, "Roundtrip from reference PSD"); } } // Test 5: Verify pixel data round-trip { println!("\n=== Pixel Data Verification ==="); let ref_path = Path::new("_images/_psd_stil_test/inner_shadow/black_triangle_green_inner_shadow_normal_blend.psd"); if ref_path.exists() { let orig_bytes = std::fs::read(ref_path).unwrap(); let orig_psd = hcie_psd::Psd::from_bytes(&orig_bytes).unwrap(); let orig_rgba = orig_psd.rgba(); let saved_path = output_dir.join("test_roundtrip.psd"); let saved_bytes = std::fs::read(&saved_path).unwrap(); let saved_psd = hcie_psd::Psd::from_bytes(&saved_bytes).unwrap(); let saved_rgba = saved_psd.rgba(); let mut diff_count = 0; let mut max_diff: u8 = 0; for i in 0..orig_rgba.len().min(saved_rgba.len()) { let d = (orig_rgba[i] as i16 - saved_rgba[i] as i16).unsigned_abs() as u8; if d > 0 { diff_count += 1; max_diff = max_diff.max(d); } } println!("Original RGBA: {} bytes", orig_rgba.len()); println!("Saved RGBA: {} bytes", saved_rgba.len()); println!("Differing bytes: {} / {}", diff_count, orig_rgba.len()); println!("Max per-byte diff: {}", max_diff); println!("MAE: {:.2}", diff_count as f64 / orig_rgba.len() as f64); } } } fn verify_psd(path: &Path, label: &str) { let bytes = std::fs::read(path).unwrap(); match hcie_psd::Psd::from_bytes(&bytes) { Ok(psd) => { println!("[OK] {}: {} bytes, {}x{}, {} layers", label, bytes.len(), psd.width(), psd.height(), psd.layers().len()); } Err(e) => { println!("[FAIL] {}: {} bytes, error: {:?}", label, bytes.len(), e); } } }