/// Debug drop shadow: render one sample and compare to Photoshop PNG. /// /// Usage: /// cargo run --example debug_drop_shadow -p hcie-io -- _tmp/drop_shadow_png_set/ds_a120_d15_s10_sp0_op100_n0_normal_red.png /// /// Writes: /// _tmp/debug_drop_shadow_ours.png /// _tmp/debug_drop_shadow_ref.png /// _tmp/debug_drop_shadow_diff.png use image::{ImageBuffer, Rgba}; fn main() { let args: Vec = std::env::args().collect(); if args.len() < 2 { eprintln!("Usage: debug_drop_shadow "); std::process::exit(1); } let ref_path = &args[1]; // Parse filename let stem = std::path::Path::new(ref_path).file_stem().unwrap().to_string_lossy().to_string(); let parts: Vec<&str> = stem.split('_').collect(); if parts.len() < 9 || parts[0] != "ds" { eprintln!("Unexpected filename format: {}", stem); std::process::exit(1); } let angle = parts[1].strip_prefix('a').unwrap().parse::().unwrap(); let distance = parts[2].strip_prefix('d').unwrap().parse::().unwrap(); let size = parts[3].strip_prefix('s').unwrap().parse::().unwrap(); let spread = parts[4].strip_prefix("sp").unwrap().parse::().unwrap(); let opacity = parts[5].strip_prefix("op").unwrap().parse::().unwrap() / 100.0; let noise = parts[6].strip_prefix('n').unwrap().parse::().unwrap(); let _blend = parts[7].to_string(); let color_name = parts[8]; let color = match color_name { "red" => [220u8, 30, 30, 255], "green" => [30, 180, 30, 255], "blue" => [30, 60, 220, 255], "white" => [220, 220, 220, 255], _ => [220, 30, 30, 255], }; println!("Sample: angle={} distance={} size={} spread={} opacity={} noise={} color={}", angle, distance, size, spread, opacity, noise, color_name); let canvas: u32 = 256; let margin: u32 = 64; let px = (canvas * canvas) as usize; let rect_alpha: Vec = { let mut a = vec![0u8; px]; for y in 0..canvas { for x in 0..canvas { if x >= margin && x < canvas - margin && y >= margin && y < canvas - margin { a[(y * canvas + x) as usize] = 255; } } } a }; let shadow = hcie_fx::tuned::generate_shadow_tuned( &rect_alpha, canvas, canvas, angle, distance, spread, size, color, false, 1.5, 5, 0.0, ); let mut out = vec![0u8; px * 4]; for i in 0..px { if rect_alpha[i] > 0 { out[i * 4] = 0; out[i * 4 + 1] = 0; out[i * 4 + 2] = 0; out[i * 4 + 3] = 255; } } for i in 0..px { let sa = shadow[i * 4 + 3]; if sa == 0 { continue; } let dst = [out[i * 4], out[i * 4 + 1], out[i * 4 + 2], out[i * 4 + 3]]; let src = [shadow[i * 4], shadow[i * 4 + 1], shadow[i * 4 + 2], sa]; let blended = hcie_blend::blend_pixels(dst, src, hcie_blend::BlendMode::Normal, opacity); out[i * 4..i * 4 + 4].copy_from_slice(&blended); } // Load reference let ref_img = image::open(ref_path).expect("open ref png").to_rgba8(); let ref_pixels = ref_img.into_raw(); // Compute diff let mut diff_sum_all = 0.0f64; let mut diff_sum_shadow_only = 0.0f64; let mut all_pixels = 0u64; let mut shadow_pixels = 0u64; let mut max_diff = 0u32; let mut max_diff_idx = 0usize; let mut diff_buf = vec![0u8; px * 4]; for i in 0..px { let is_shape = rect_alpha[i] > 0; let is_ref_alpha = ref_pixels[i * 4 + 3] > 0; if is_ref_alpha { all_pixels += 1; let mut d = 0u32; for c in 0..4 { d += (out[i * 4 + c] as i32 - ref_pixels[i * 4 + c] as i32).abs() as u32; } diff_sum_all += d as f64; if d > max_diff { max_diff = d; max_diff_idx = i; } if !is_shape { shadow_pixels += 1; diff_sum_shadow_only += d as f64; } // Visualize diff: red = large diff, green = small let scaled = (d.min(255)) as u8; diff_buf[i * 4] = scaled; diff_buf[i * 4 + 1] = 0; diff_buf[i * 4 + 2] = 0; diff_buf[i * 4 + 3] = 255; } } let mae_all = diff_sum_all / all_pixels as f64 / 4.0; let mae_shadow = if shadow_pixels > 0 { diff_sum_shadow_only / shadow_pixels as f64 / 4.0 } else { 0.0 }; println!("MAE all pixels: {:.4}", mae_all); println!("MAE shadow-only pixels: {:.4}", mae_shadow); println!("Max diff: {} at pixel {}", max_diff, max_diff_idx); println!(" ours={:?}", &out[max_diff_idx * 4..max_diff_idx * 4 + 4]); println!(" ref ={:?}", &ref_pixels[max_diff_idx * 4..max_diff_idx * 4 + 4]); // Write debug images std::fs::create_dir_all("_tmp").ok(); let ours_img = ImageBuffer::, _>::from_raw(canvas, canvas, out).unwrap(); ours_img.save("_tmp/debug_drop_shadow_ours.png").unwrap(); let ref_buf = ImageBuffer::, _>::from_raw(canvas, canvas, ref_pixels).unwrap(); ref_buf.save("_tmp/debug_drop_shadow_ref.png").unwrap(); let diff_img = ImageBuffer::, _>::from_raw(canvas, canvas, diff_buf).unwrap(); diff_img.save("_tmp/debug_drop_shadow_diff.png").unwrap(); println!("Wrote _tmp/debug_drop_shadow_ours.png"); println!("Wrote _tmp/debug_drop_shadow_ref.png"); println!("Wrote _tmp/debug_drop_shadow_diff.png"); }