#![allow(unreachable_patterns, unused_assignments, unused_must_use)] #![allow(dead_code, unused_imports, unused_variables, unused_macros)] use hcie_blend; use hcie_composite; use hcie_io; use image; fn main() { // Load PSD let psd_path = "_images/_test_images/example3/Example3-mini.psd"; let layers: Vec = match hcie_psd::import_psd(std::path::Path::new(psd_path)) { Ok(l) => l, Err(e) => { eprintln!("import_psd failed: {}", e); return; } }; if layers.is_empty() { eprintln!("No layers imported."); return; } // Layer summary for debugging (focus on Rear Light / Jaw Light) println!("=== Layer Summary ==="); for (i, layer) in layers.iter().enumerate() { let adj = if let Some(adj) = &layer.adjustment { match adj { hcie_blend::Adjustment::Curves { .. } => "Curves", hcie_blend::Adjustment::GradientMap { .. } => "GradientMap", hcie_blend::Adjustment::HueSaturation { .. } => "HueSat", _ => "Other", } } else { "None" }; let mask_info = if let Some(_) = &layer.mask_pixels { format!("Mask(default={})", layer.mask_default_color) } else { "NoMask".to_string() }; let alphas: Vec = layer.pixels.chunks_exact(4).map(|c| c[3]).collect(); let (min_a, max_a, avg_a) = if !alphas.is_empty() { let min = *alphas.iter().min().unwrap(); let max = *alphas.iter().max().unwrap(); let sum: u64 = alphas.iter().map(|&v| v as u64).sum(); let avg = sum as f64 / alphas.len() as f64; (min, max, avg) } else { (0, 0, 0.0) }; println!("{:2}: name='{}', blend={:?}, opacity={:.3}, visible={}, clipping={}, adjustment={}, {}, alpha=[min={}, max={}, avg={:.1}]", i, layer.name, layer.blend_mode, layer.opacity, layer.visible, layer.clipping_mask, adj, mask_info, min_a, max_a, avg_a); } println!("=== End Summary ==="); // Debug mask statistics and LUTs for layers with masks for (i, layer) in layers.iter().enumerate() { if !layer.effects.is_empty() { println!( "Layer {} ('{}') effects ({}):", i, layer.name, layer.effects.len() ); for fx in &layer.effects { println!(" {} (enabled={})", fx.effect_name(), fx.is_enabled()); match hcie_fx::protocol_to_hcie_fx_effect(fx) { hcie_fx::LayerEffect::BevelEmboss { depth, size, angle, altitude, highlight_color, shadow_color, .. } => { println!( " depth={}, size={}, angle={}, altitude={}, hl={:?}, sh={:?}", depth, size, angle, altitude, highlight_color, shadow_color ); } hcie_fx::LayerEffect::OuterGlow { color, opacity, size, spread, blend_mode, .. } => { println!( " color={:?}, opacity={}, size={}, spread={}, blend={:?}", color, opacity, size, spread, blend_mode ); } hcie_fx::LayerEffect::InnerGlow { color, opacity, size, blend_mode, .. } => { println!( " color={:?}, opacity={}, size={}, blend={:?}", color, opacity, size, blend_mode ); } _ => {} } } } if layer.adjustment.is_some() { println!("--- Debugging Layer {} ('{}') ---", i, layer.name); if let Some(adj) = &layer.adjustment { match adj { hcie_blend::Adjustment::Curves { lut_r, lut_g, lut_b } => { println!( "Curves LUT sample R: [{}, {}, {}, {}, {}], G: [{}, {}, {}, {}, {}], B: [{}, {}, {}, {}, {}]", lut_r[0], lut_r[64], lut_r[128], lut_r[192], lut_r[255], lut_g[0], lut_g[64], lut_g[128], lut_g[192], lut_g[255], lut_b[0], lut_b[64], lut_b[128], lut_b[192], lut_b[255] ); } hcie_blend::Adjustment::GradientMap { lut_r, lut_g, lut_b } => { println!( "GradientMap LUT sample R: [{}, {}, {}, {}, {}], G: [{}, {}, {}, {}, {}], B: [{}, {}, {}, {}, {}]", lut_r[0], lut_r[64], lut_r[128], lut_r[192], lut_r[255], lut_g[0], lut_g[64], lut_g[128], lut_g[192], lut_g[255], lut_b[0], lut_b[64], lut_b[128], lut_b[192], lut_b[255] ); } hcie_blend::Adjustment::HueSaturation { hue, saturation, lightness } => { println!("HueSat: hue={}, sat={}, light={}", hue, saturation, lightness); } } } if let Some(mask) = &layer.mask_pixels { let non_zero: usize = mask.iter().filter(|&&v| v != 0).count(); println!( "Mask stats: {} non-zero / {} total (default={})", non_zero, mask.len(), layer.mask_default_color ); } if !layer.effects.is_empty() { println!("Effects ({}):", layer.effects.len()); for fx in &layer.effects { println!(" {} (enabled={})", fx.effect_name(), fx.is_enabled()); } } } } 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; // Composite let output = hcie_composite::composite_layers(&comp_layers, canvas_w, canvas_h); // Save output for visual inspection let out_path = "/tmp/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); // Diff against reference PNG let ref_path = "_images/_test_images/example3/Example3-mini.png"; if std::path::Path::new(ref_path).exists() { let ref_img = image::open(ref_path).unwrap().to_rgba8(); let ref_pixels = ref_img.as_raw(); 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; let total_mae = (diff_sum / px_cnt) / 4.0; println!("Avg diff per channel (MAE): {:.4}", total_mae); println!("Max diff per pixel: {}", max_diff); println!("\n--- Per-layer contribution analysis ---"); for skip_i in 0..comp_layers.len() { let cl_skip: Vec<_> = comp_layers.iter().enumerate() .filter(|(idx, _)| *idx != skip_i) .map(|(_, l)| l.clone()) .collect(); let res_skip = hcie_composite::composite_layers(&cl_skip, canvas_w, canvas_h); let mut ds_skip = 0.0f64; for j in 0..(canvas_w * canvas_h) as usize { let idx = j * 4; if idx + 3 >= res_skip.len() || idx + 3 >= ref_pixels.len() { continue; } let dr = (res_skip[idx] as i32 - ref_pixels[idx] as i32).unsigned_abs() as f64; let dg = (res_skip[idx+1] as i32 - ref_pixels[idx+1] as i32).unsigned_abs() as f64; let db = (res_skip[idx+2] as i32 - ref_pixels[idx+2] as i32).unsigned_abs() as f64; let da = (res_skip[idx+3] as i32 - ref_pixels[idx+3] as i32).unsigned_abs() as f64; ds_skip += (dr + dg + db + da) / 4.0; } let mae_skip = ds_skip / px_cnt; let delta = total_mae - mae_skip; println!(" Skip [{:2}] {:25}: MAE(w/o)={:.4} delta={:+.4}", skip_i, comp_layers[skip_i].name, mae_skip, delta); } } else { println!("Reference image dimensions differ."); } } }