#![allow(dead_code, unused_imports, unused_variables, unused_macros)] use hcie_blend; use hcie_composite; use std::path::Path; fn main() { let psd_path = "_images/Forest Text Effect/Text.psd"; let layers: Vec = match hcie_psd::import_psd(Path::new(psd_path)) { Ok(l) => l, Err(e) => { eprintln!("import_psd failed: {}", e); return; } }; if layers.is_empty() { eprintln!("No layers imported."); return; } println!("=== Layer Summary ==="); for (i, layer) in layers.iter().enumerate() { let fx_summary: Vec = layer.effects.iter() .map(|e| format!("{}({})", e.effect_name(), if e.is_enabled() { "on" } else { "off" })) .collect(); println!("{:2}: name='{}', blend={:?}, opacity={:.3}, visible={}, fx=[{}]", i, layer.name, layer.blend_mode, layer.opacity, layer.visible, fx_summary.join(", ")); } println!("=== End Summary ==="); fn map_blend(mode: hcie_protocol::BlendMode) -> hcie_blend::BlendMode { match mode { hcie_protocol::BlendMode::Normal => hcie_blend::BlendMode::Normal, hcie_protocol::BlendMode::Dissolve => hcie_blend::BlendMode::Dissolve, hcie_protocol::BlendMode::Darken => hcie_blend::BlendMode::Darken, hcie_protocol::BlendMode::Multiply => hcie_blend::BlendMode::Multiply, hcie_protocol::BlendMode::ColorBurn => hcie_blend::BlendMode::ColorBurn, hcie_protocol::BlendMode::LinearBurn => hcie_blend::BlendMode::LinearBurn, hcie_protocol::BlendMode::DarkerColor => hcie_blend::BlendMode::DarkerColor, hcie_protocol::BlendMode::Lighten => hcie_blend::BlendMode::Lighten, hcie_protocol::BlendMode::Screen => hcie_blend::BlendMode::Screen, hcie_protocol::BlendMode::ColorDodge => hcie_blend::BlendMode::ColorDodge, hcie_protocol::BlendMode::LinearDodge => hcie_blend::BlendMode::LinearDodge, hcie_protocol::BlendMode::LighterColor => hcie_blend::BlendMode::LighterColor, hcie_protocol::BlendMode::Overlay => hcie_blend::BlendMode::Overlay, hcie_protocol::BlendMode::SoftLight => hcie_blend::BlendMode::SoftLight, hcie_protocol::BlendMode::HardLight => hcie_blend::BlendMode::HardLight, hcie_protocol::BlendMode::VividLight => hcie_blend::BlendMode::VividLight, hcie_protocol::BlendMode::LinearLight => hcie_blend::BlendMode::LinearLight, hcie_protocol::BlendMode::PinLight => hcie_blend::BlendMode::PinLight, hcie_protocol::BlendMode::HardMix => hcie_blend::BlendMode::HardMix, hcie_protocol::BlendMode::Difference => hcie_blend::BlendMode::Difference, hcie_protocol::BlendMode::Exclusion => hcie_blend::BlendMode::Exclusion, hcie_protocol::BlendMode::Subtract => hcie_blend::BlendMode::Subtract, hcie_protocol::BlendMode::Divide => hcie_blend::BlendMode::Divide, hcie_protocol::BlendMode::Hue => hcie_blend::BlendMode::Hue, hcie_protocol::BlendMode::Saturation => hcie_blend::BlendMode::Saturation, hcie_protocol::BlendMode::Color => hcie_blend::BlendMode::Color, hcie_protocol::BlendMode::Luminosity => hcie_blend::BlendMode::Luminosity, hcie_protocol::BlendMode::PassThrough => hcie_blend::BlendMode::PassThrough, } } 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, 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; println!("\nCompositing {} layers at {}x{}", comp_layers.len(), canvas_w, canvas_h); let output = hcie_composite::composite_layers(&comp_layers, canvas_w, canvas_h); let out_path = "/tmp/text_composite_output.png"; { let img = image::RgbaImage::from_raw(canvas_w, canvas_h, output) .expect("Failed to create image from buffer"); img.save(out_path).expect("Failed to save output image"); } println!("Saved composite to {}", out_path); let out_dir = std::path::Path::new("/tmp/text_layers"); let _ = std::fs::create_dir_all(out_dir); for (i, layer) in comp_layers.iter().enumerate() { if layer.visible { let path = out_dir.join(format!("{}_{}.png", i, layer.name.replace(['/', '\\', ' '], "_"))); let img = image::RgbaImage::from_raw(layer.width, layer.height, layer.pixels.clone()) .expect("Failed to create image from buffer"); img.save(&path).expect("Failed to save layer"); println!(" Layer {} ({}) -> {:?}", i, layer.name, path); } } let ref_path = "_images/Forest Text Effect/Text.png"; if Path::new(ref_path).exists() { let ref_img = image::open(ref_path).unwrap().to_rgba8(); let ref_w = ref_img.width(); let ref_h = ref_img.height(); let ref_pixels = ref_img.as_raw(); if ref_w == canvas_w && ref_h == canvas_h { let comp_img = image::open(out_path).unwrap().to_rgba8(); let comp_pixels = comp_img.as_raw(); let mut diff_sum = 0.0f64; let mut max_diff = 0u32; let mut diff_count = 0u64; for j in 0..(canvas_w * canvas_h) as usize { let idx = j * 4; let d = (0..4).map(|k| (comp_pixels[idx + k] as i32 - ref_pixels[idx + k] as i32).abs()).sum::() as u32; if d > 0 { diff_count += 1; } diff_sum += d as f64; max_diff = max_diff.max(d); } let px_cnt = (canvas_w * canvas_h) as f64; let total_mae = (diff_sum / px_cnt) / 4.0; let pct_diff = (diff_count as f64 / px_cnt) * 100.0; println!("\n=== Diff vs {} ===", ref_path); println!("Avg diff per channel (MAE): {:.4}", total_mae); println!("Max diff per pixel: {}", max_diff); println!("Pixels with any diff: {} / {} ({:.2}%)", diff_count, px_cnt as u64, pct_diff); } else { println!("Reference image dimensions differ: ref {}x{} vs canvas {}x{}", ref_w, ref_h, canvas_w, canvas_h); } } else { println!("Reference PNG not found at {}", ref_path); } }