Files
2026-07-09 02:59:53 +03:00

153 lines
5.5 KiB
Rust

/// 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<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("Usage: debug_drop_shadow <ref-png-path>");
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::<f32>().unwrap();
let distance = parts[2].strip_prefix('d').unwrap().parse::<f32>().unwrap();
let size = parts[3].strip_prefix('s').unwrap().parse::<f32>().unwrap();
let spread = parts[4].strip_prefix("sp").unwrap().parse::<f32>().unwrap();
let opacity = parts[5].strip_prefix("op").unwrap().parse::<f32>().unwrap() / 100.0;
let noise = parts[6].strip_prefix('n').unwrap().parse::<f32>().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<u8> = {
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::<Rgba<u8>, _>::from_raw(canvas, canvas, out).unwrap();
ours_img.save("_tmp/debug_drop_shadow_ours.png").unwrap();
let ref_buf = ImageBuffer::<Rgba<u8>, _>::from_raw(canvas, canvas, ref_pixels).unwrap();
ref_buf.save("_tmp/debug_drop_shadow_ref.png").unwrap();
let diff_img = ImageBuffer::<Rgba<u8>, _>::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");
}