use std::io::Write; fn test_file(psd_path: &str, ref_path: &str, out_path: &str, writer: &mut std::io::BufWriter) { let name = std::path::Path::new(psd_path).file_name().unwrap().to_str().unwrap(); println!("\n=== {} ===", name); writeln!(writer, "=== {} ===", name).unwrap(); let layers = match hcie_psd::import_psd(std::path::Path::new(psd_path)) { Ok(l) => l, Err(e) => { println!("import_psd failed: {}", e); return; } }; if layers.is_empty() { println!("No layers imported."); return; } for (i, layer) in layers.iter().enumerate() { let effs = if layer.effects.is_empty() { "no effects".to_string() } else { let mut parts = Vec::new(); for e in &layer.effects { if let hcie_protocol::effects::LayerEffect::BevelEmboss { enabled, style, technique, depth, direction, size, soften, angle, altitude, highlight_blend, highlight_color, highlight_opacity, shadow_blend, shadow_color, shadow_opacity, .. } = e { parts.push(format!( "BevelEmboss(enabled={}, style={:?}, tech={:?}, depth={}, dir={:?}, size={}, soften={}, angle={}, altitude={}, hl_blend={:?}, hl_color={:?}, hl_op={}, sh_blend={:?}, sh_color={:?}, sh_op={})", enabled, style, technique, depth, direction, size, soften, angle, altitude, highlight_blend, highlight_color, highlight_opacity, shadow_blend, shadow_color, shadow_opacity )); } else { parts.push(format!("{}({})", e.effect_name(), if e.is_enabled() {"on"} else {"off"})); } } format!("{} effects: {}", layer.effects.len(), parts.join(", ")) }; println!(" Layer {}: '{}' {}x{} visible={} {}", i, layer.name, layer.width, layer.height, layer.visible, effs); } let comp_layers: Vec = layers.iter().map(|l| hcie_composite::Layer { name: l.name.clone(), layer_type: l.layer_type.clone(), data: l.data.clone(), pixels: l.pixels.clone(), width: l.width, height: l.height, visible: l.visible, opacity: l.opacity, blend_mode: l.blend_mode.clone(), locked: l.locked, dirty: l.dirty, id: l.id, parent_id: l.parent_id, styles: l.styles.clone(), clipping_mask: l.clipping_mask, collapsed: l.collapsed, adjustment: l.adjustment.clone(), mask_pixels: l.mask_pixels.clone(), mask_bounds: l.mask_bounds, mask_default_color: l.mask_default_color, curve_points: l.curve_points.clone(), fill_opacity: l.fill_opacity, effects: l.effects.clone(), effects_dirty: std::sync::atomic::AtomicBool::new(false), effects_cache: std::sync::Mutex::new(None), adjustment_raw: None, is_section_divider: false, image_resources_raw: None, }).collect(); let canvas_w = comp_layers[0].width; let canvas_h = comp_layers[0].height; let output = hcie_composite::composite_layers(&comp_layers, canvas_w, canvas_h); { let orig_img = image::RgbaImage::from_raw(canvas_w, canvas_h, comp_layers[0].pixels.clone()) .expect("Failed to create orig image from buffer"); let orig_path = out_path.replace(".png", "_orig.png"); orig_img.save(orig_path).expect("Failed to save orig image"); let img = image::RgbaImage::from_raw(canvas_w, canvas_h, output.clone()) .expect("Failed to create image from buffer"); img.save(out_path).expect("Failed to save output image"); } if std::path::Path::new(ref_path).exists() { let ref_img = image::open(ref_path).unwrap().to_rgba8(); if ref_img.width() == canvas_w && ref_img.height() == canvas_h { let mut diff_sum = 0.0f64; let mut max_diff = 0u32; let comp_img = image::open(out_path).unwrap().to_rgba8(); for (p1, p2) in comp_img.pixels().zip(ref_img.pixels()) { let d = p1.0.iter().zip(p2.0.iter()) .map(|(a, b)| (*a as i32 - *b as i32).abs() as f64) .sum::(); diff_sum += d; max_diff = max_diff.max(d as u32); } let px_cnt = (canvas_w * canvas_h) as f64; writeln!(writer, " Avg diff per channel: {:.2}", diff_sum / px_cnt).unwrap(); writeln!(writer, " Max diff per pixel: {}", max_diff).unwrap(); } else { println!("Reference image dimensions differ: ours={}x{} ref={}x{}", canvas_w, canvas_h, ref_img.width(), ref_img.height()); } } else { writeln!(writer, " Reference not found: {}", ref_path).unwrap(); } } fn main() { use std::fs::File; use std::io::BufWriter; let out_file = std::env::var("SULTAN_RESULTS_FILE").unwrap_or_else(|_| "sultan_results.txt".to_string()); let mut writer = BufWriter::new(File::create(out_file).unwrap()); let base = "_images/_psd_stil_test/sultan_test"; let sultan_dir = format!("{}/sultan", base); let layers = [ ("Layer 1.psd", "Layer 1.png"), ("Layer 4.psd", "Layer 4.png"), ("Layer 3.psd", "Layer 3.png"), ("Layer 2.psd", "Layer 2.png"), ]; println!("=== Individual Layers ==="); for (psd_name, ref_name) in &layers { let psd = format!("{}/{}", sultan_dir, psd_name); let ref_p = if ref_name.is_empty() { String::new() } else { format!("{}/{}", sultan_dir, ref_name) }; let out = format!("/tmp/sultan_{}.png", ref_name.replace(".png", "")); test_file(&psd, &ref_p, &out, &mut writer); } println!("\n=== Sultan Composite ==="); test_file( &format!("{}/sultan.psd", base), &format!("{}/sultan.png", base), "/tmp/sultan_composite.png", &mut writer, ); }