153 lines
5.5 KiB
Rust
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");
|
||
|
|
}
|