191 lines
8.1 KiB
Rust
191 lines
8.1 KiB
Rust
use std::io::Write;
|
|
|
|
const SHADOW_RS: &str = "hcie-fx/src/outer_glow.rs";
|
|
const PSD_PATH: &str = "_images/_psd_stil_test/outer_glow_3.psd";
|
|
const REF_PATH: &str = "_images/_psd_stil_test/outer_glow_3.png";
|
|
const RESULTS_FILE: &str = "/mnt/extra/00_PROJECTS/hcie-rust-v4/outer_glow_results.txt";
|
|
|
|
fn main() {
|
|
let original = std::fs::read_to_string(SHADOW_RS).expect("read shadow.rs");
|
|
|
|
let start_tag = "// BEGIN_GLOW_TUNING_ZONE";
|
|
let end_tag = "// END_GLOW_TUNING_ZONE";
|
|
|
|
let zone_start = original.find(start_tag).unwrap();
|
|
let zone_end = original.find(end_tag).unwrap();
|
|
let _before = &original[..zone_start];
|
|
let _after = &original[zone_end + end_tag.len()..];
|
|
|
|
let ref_img = image::open(REF_PATH)
|
|
.expect("open outer_glow_3.png")
|
|
.to_rgba8();
|
|
let (w, h) = (ref_img.width(), ref_img.height());
|
|
let px = (w * h) as usize;
|
|
let ref_pixels = ref_img.into_raw();
|
|
|
|
let layers = hcie_psd::import_psd(std::path::Path::new(PSD_PATH))
|
|
.expect("import outer_glow_3.psd");
|
|
let layer = &layers[0];
|
|
let alpha: Vec<u8> = layer.pixels.iter().skip(3).step_by(4).copied().collect();
|
|
|
|
let (spread, size, color, blend_mode_str, opacity) = {
|
|
let mut sp = 0.0f32;
|
|
let mut sz = 0.0f32;
|
|
let mut col = [0u8; 4];
|
|
let mut bm = String::new();
|
|
let mut op = 0.0f32;
|
|
for e in &layer.effects {
|
|
if let hcie_protocol::effects::LayerEffect::OuterGlow {
|
|
enabled: true, spread, size, color, blend_mode, opacity, ..
|
|
} = e
|
|
{
|
|
sp = *spread;
|
|
sz = *size;
|
|
col = *color;
|
|
bm = blend_mode.clone();
|
|
op = *opacity;
|
|
}
|
|
}
|
|
(sp, sz, col, bm, op)
|
|
};
|
|
|
|
let _bm_parsed = hcie_fx::types::blend_mode_from_string(&blend_mode_str);
|
|
|
|
println!("OuterGlow: spread={}, size={}, color={:?}, blend={}, opacity={}", spread, size, color, blend_mode_str, opacity);
|
|
println!("Image: {}x{}", w, h);
|
|
std::io::stdout().flush().ok();
|
|
|
|
let mut best_mae = f64::MAX;
|
|
let mut best_params = (0.0f32, 0i32, 0.0f32, 0.0f32, false);
|
|
|
|
let blur_factors = &[0.2, 0.4, 0.6, 0.8, 1.0, 1.5, 2.0, 3.0, 5.0, 8.0];
|
|
let passes_list = &[1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 15, 20];
|
|
let alpha_scales = &[-1.0, -0.5, -0.25, -0.1, 0.0, 0.05, 0.1, 0.2, 0.3, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0];
|
|
let glow_boosts = &[0.05, 0.1, 0.2, 0.3, 0.5, 0.75, 1.0, 1.25, 1.5, 2.0, 2.5, 3.0, 5.0, 10.0];
|
|
let spread_before_blurs = &[true, false];
|
|
|
|
let total = blur_factors.len() * passes_list.len() * alpha_scales.len() * glow_boosts.len() * spread_before_blurs.len();
|
|
println!("Grid search: {} combos", total);
|
|
std::io::stdout().flush().ok();
|
|
|
|
let mut count = 0usize;
|
|
for blur_factor in blur_factors {
|
|
for passes in passes_list {
|
|
for alpha_scale in alpha_scales {
|
|
for glow_boost in glow_boosts {
|
|
for spread_before_blur in spread_before_blurs {
|
|
count += 1;
|
|
if count % 1000 == 0 {
|
|
println!(" [{}/{}] best={:.3}", count, total, best_mae);
|
|
std::io::stdout().flush().ok();
|
|
}
|
|
|
|
let glow = hcie_fx::tuned::generate_glow_tuned(
|
|
&alpha, w, h, spread, size, color, false,
|
|
*blur_factor, *passes, *alpha_scale, *glow_boost, *spread_before_blur,
|
|
);
|
|
|
|
// Correct blending matching shadow.rs apply_layer_effects logic:
|
|
// 1. Glow is rendered onto a transparent canvas (behind),
|
|
// which is equivalent to [color_rgb, sa * opacity] since destination is [0,0,0,0].
|
|
// 2. The original layer (inside) is composited on top using Normal blend mode at 1.0 opacity.
|
|
let mut out = vec![0u8; px * 4];
|
|
for i in 0..px {
|
|
let sa = glow[i * 4 + 3];
|
|
let behind_a = (sa as f32 * opacity).round() as u8;
|
|
let behind = [glow[i * 4], glow[i * 4 + 1], glow[i * 4 + 2], behind_a];
|
|
|
|
let orig_px = [
|
|
layer.pixels[i * 4],
|
|
layer.pixels[i * 4 + 1],
|
|
layer.pixels[i * 4 + 2],
|
|
layer.pixels[i * 4 + 3],
|
|
];
|
|
|
|
let blended = if orig_px[3] == 0 {
|
|
behind
|
|
} else if orig_px[3] == 255 {
|
|
orig_px
|
|
} else {
|
|
hcie_blend::blend_pixels(behind, orig_px, hcie_blend::BlendMode::Normal, 1.0)
|
|
};
|
|
out[i * 4..i * 4 + 4].copy_from_slice(&blended);
|
|
}
|
|
|
|
let mut diff_sum = 0.0f64;
|
|
for i in 0..px {
|
|
let d = (0..4).map(|c| {
|
|
(out[i * 4 + c] as i32 - ref_pixels[i * 4 + c] as i32).abs() as f64
|
|
}).sum::<f64>();
|
|
diff_sum += d;
|
|
}
|
|
let mae = diff_sum / px as f64 / 4.0;
|
|
|
|
if mae < best_mae {
|
|
best_mae = mae;
|
|
best_params = (*blur_factor, *passes, *alpha_scale, *glow_boost, *spread_before_blur);
|
|
println!("*** NEW BEST: {:.3} (blur_factor={:.1}, passes={}, alpha_scale={:.2}, glow_boost={:.2}, spread_before_blur={})",
|
|
best_mae, blur_factor, passes, alpha_scale, glow_boost, spread_before_blur);
|
|
std::io::stdout().flush().ok();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if best_mae < f64::MAX {
|
|
let (bbf, bp, bas, bgb, bsbb) = best_params;
|
|
let spread_before_code = if bsbb {
|
|
" if spread > 0.0 {
|
|
let factor = 1.0 / (1.0 - spread / 100.0);
|
|
for a in ga.iter_mut() {
|
|
*a = (*a * factor).round().min(255.0);
|
|
}
|
|
}"
|
|
} else {
|
|
""
|
|
};
|
|
let spread_after_code = if !bsbb {
|
|
" if spread > 0.0 {
|
|
let factor = 1.0 / (1.0 - spread / 100.0);
|
|
for a in ga.iter_mut() {
|
|
*a = (*a * factor).round().min(255.0);
|
|
}
|
|
}"
|
|
} else {
|
|
""
|
|
};
|
|
|
|
let zone_code = format!(
|
|
" // BEGIN_GLOW_TUNING_ZONE
|
|
{}
|
|
if radius > 0 {{
|
|
let gaussian_r = (radius as f32 / {:.1}).round().max(1.0) as i32;
|
|
for _ in 0..{} {{
|
|
ga = box_blur_f32(&ga, w, h, gaussian_r);
|
|
}}
|
|
}}
|
|
{}
|
|
for i in 0..px {{
|
|
let mask = 255.0 - alpha[i] as f32;
|
|
ga[i] = (ga[i] - alpha[i] as f32 * {:.2}).max(0.0) * {:.2} * mask / 255.0;
|
|
}}
|
|
// END_GLOW_TUNING_ZONE",
|
|
spread_before_code, bbf, bp, spread_after_code, bas, bgb
|
|
);
|
|
let mut patched = String::with_capacity(original.len());
|
|
patched.push_str(&original[..zone_start]);
|
|
patched.push_str(&zone_code);
|
|
patched.push_str(&original[zone_end + end_tag.len()..]);
|
|
std::fs::write(SHADOW_RS, &patched).expect("write final best");
|
|
println!("\nCompleted. Best code saved with MAE = {:.3} (blur_factor={:.1}, passes={}, alpha_scale={:.2}, glow_boost={:.2}, spread_before_blur={})",
|
|
best_mae, bbf, bp, bas, bgb, bsbb);
|
|
if let Ok(mut file) = std::fs::OpenOptions::new().create(true).write(true).truncate(true).open(RESULTS_FILE) {
|
|
let _ = writeln!(file, "=== Outer Glow Tuned ===\n Params: blur_factor={:.1}, passes={}, alpha_scale={:.2}, glow_boost={:.2}, spread_before_blur={}\n MAE: {:.3}",
|
|
bbf, bp, bas, bgb, bsbb, best_mae);
|
|
}
|
|
} else {
|
|
std::fs::write(SHADOW_RS, &original).expect("restore original");
|
|
}
|
|
} |