Implement stable serialization and restoration for dock layouts, including auto-hidden and floating panels
- Add `persistence.rs` for stable serialization of dock layouts without runtime identifiers. - Introduce `preview.rs` for geometry handling of dock drop previews. - Create `sizing.rs` to manage content-aware sizing policies and constraint solving for dock panels. - Implement `welcome.rs` to provide a welcome surface when no documents are open, featuring New and Open actions.
This commit is contained in:
@@ -39,10 +39,7 @@ fn main() {
|
||||
let mut layer = None;
|
||||
for l in &layers {
|
||||
for e in &l.effects {
|
||||
if let hcie_protocol::effects::LayerEffect::BevelEmboss {
|
||||
enabled: true, ..
|
||||
} = e
|
||||
{
|
||||
if let hcie_protocol::effects::LayerEffect::BevelEmboss { enabled: true, .. } = e {
|
||||
layer = Some(l);
|
||||
break;
|
||||
}
|
||||
@@ -113,7 +110,9 @@ fn main() {
|
||||
hcie_protocol::effects::BevelStyle::OuterBevel => {
|
||||
hcie_fx::types::BevelStyle::OuterBevel
|
||||
}
|
||||
hcie_protocol::effects::BevelStyle::Emboss => hcie_fx::types::BevelStyle::Emboss,
|
||||
hcie_protocol::effects::BevelStyle::Emboss => {
|
||||
hcie_fx::types::BevelStyle::Emboss
|
||||
}
|
||||
hcie_protocol::effects::BevelStyle::PillowEmboss => {
|
||||
hcie_fx::types::BevelStyle::PillowEmboss
|
||||
}
|
||||
@@ -129,30 +128,27 @@ fn main() {
|
||||
let alpha: Vec<u8> = layer.pixels.iter().skip(3).step_by(4).copied().collect();
|
||||
|
||||
let (hl_buf, sh_buf) = hcie_fx::tuned::generate_bevel_tuned(
|
||||
&alpha,
|
||||
w,
|
||||
h,
|
||||
depth,
|
||||
size,
|
||||
soften,
|
||||
angle,
|
||||
altitude,
|
||||
&direction,
|
||||
&technique,
|
||||
&style,
|
||||
lb,
|
||||
hl,
|
||||
sh,
|
||||
1.0,
|
||||
tilt,
|
||||
&alpha, w, h, depth, size, soften, angle, altitude, &direction, &technique, &style, lb,
|
||||
hl, sh, 1.0, tilt,
|
||||
);
|
||||
|
||||
let mut out = layer.pixels.clone();
|
||||
for i in 0..px {
|
||||
let dst = [out[i * 4], out[i * 4 + 1], out[i * 4 + 2], out[i * 4 + 3]];
|
||||
let hl_src = [hl_buf[i * 4], hl_buf[i * 4 + 1], hl_buf[i * 4 + 2], hl_buf[i * 4 + 3]];
|
||||
let sh_src = [sh_buf[i * 4], sh_buf[i * 4 + 1], sh_buf[i * 4 + 2], sh_buf[i * 4 + 3]];
|
||||
let blended = hcie_blend::blend_pixels(dst, hl_src, hcie_blend::BlendMode::Screen, 0.75);
|
||||
let hl_src = [
|
||||
hl_buf[i * 4],
|
||||
hl_buf[i * 4 + 1],
|
||||
hl_buf[i * 4 + 2],
|
||||
hl_buf[i * 4 + 3],
|
||||
];
|
||||
let sh_src = [
|
||||
sh_buf[i * 4],
|
||||
sh_buf[i * 4 + 1],
|
||||
sh_buf[i * 4 + 2],
|
||||
sh_buf[i * 4 + 3],
|
||||
];
|
||||
let blended =
|
||||
hcie_blend::blend_pixels(dst, hl_src, hcie_blend::BlendMode::Screen, 0.75);
|
||||
let blended =
|
||||
hcie_blend::blend_pixels(blended, sh_src, hcie_blend::BlendMode::Multiply, 0.75);
|
||||
out[i * 4..i * 4 + 4].copy_from_slice(&blended);
|
||||
@@ -171,8 +167,7 @@ fn main() {
|
||||
let sy = y as i32 + offset_y;
|
||||
if sx >= 0 && sx < w as i32 && sy >= 0 && sy < h as i32 {
|
||||
let s_idx = ((sy as usize) * w as usize + (sx as usize)) * 4;
|
||||
let t_idx =
|
||||
((y as usize) * target.width() as usize + (x as usize)) * 4;
|
||||
let t_idx = ((y as usize) * target.width() as usize + (x as usize)) * 4;
|
||||
|
||||
let src_a = layer.pixels[s_idx + 3];
|
||||
if src_a > 0 {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use image::GenericImageView;
|
||||
|
||||
fn main() {
|
||||
let ref_img = image::open("_images/_psd_stil_test/test_2/Emboss.png").unwrap().to_rgba8();
|
||||
let ref_img = image::open("_images/_psd_stil_test/test_2/Emboss.png")
|
||||
.unwrap()
|
||||
.to_rgba8();
|
||||
let test_img = image::open("/tmp/test2_Emboss.png").unwrap().to_rgba8();
|
||||
|
||||
let mut diff_sum = 0.0;
|
||||
@@ -15,10 +17,10 @@ fn main() {
|
||||
let rp = ref_img.get_pixel(x, y);
|
||||
let tp = test_img.get_pixel(x, y);
|
||||
|
||||
let d = (rp[0] as i32 - tp[0] as i32).abs() +
|
||||
(rp[1] as i32 - tp[1] as i32).abs() +
|
||||
(rp[2] as i32 - tp[2] as i32).abs() +
|
||||
(rp[3] as i32 - tp[3] as i32).abs();
|
||||
let d = (rp[0] as i32 - tp[0] as i32).abs()
|
||||
+ (rp[1] as i32 - tp[1] as i32).abs()
|
||||
+ (rp[2] as i32 - tp[2] as i32).abs()
|
||||
+ (rp[3] as i32 - tp[3] as i32).abs();
|
||||
|
||||
diff_sum += d as f64;
|
||||
max_diff = max_diff.max(d);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![allow(dead_code, unused_imports, unused_variables, unused_macros)]
|
||||
use hcie_blend::BlendMode;
|
||||
/// Visual inspection tool for Bevel & Emboss rendering.
|
||||
///
|
||||
/// Loads a PSD from `_tmp/psd_tunes/bevel_emboss_psd_set/`, applies the
|
||||
@@ -11,9 +12,7 @@
|
||||
/// cargo run --example inspect_emboss -p hcie-io -- be_L_emboss_d100_sz10_sf0_down_alt30_smooth
|
||||
///
|
||||
/// If no stem is given, runs a small representative subset.
|
||||
|
||||
use hcie_fx::types::{BevelStyle, Direction, Technique};
|
||||
use hcie_blend::BlendMode;
|
||||
|
||||
const PSD_DIR: &str = "_tmp/psd_tunes/bevel_emboss_psd_set";
|
||||
const PNG_DIR: &str = "_tmp/psd_tunes/bevel_emboss_png_set";
|
||||
@@ -22,7 +21,9 @@ const OUT_DIR: &str = "_tmp/emboss_inspect";
|
||||
fn parse_stem(stem: &str) -> Option<(String, String, f32, f32, f32, String, f32, String)> {
|
||||
// be_{shape}_{style}_d{depth}_sz{size}_sf{soften}_{dir}_alt{altitude}_{technique}
|
||||
let parts: Vec<&str> = stem.split('_').collect();
|
||||
if parts.len() < 9 || parts[0] != "be" { return None; }
|
||||
if parts.len() < 9 || parts[0] != "be" {
|
||||
return None;
|
||||
}
|
||||
let shape = parts[1].to_string();
|
||||
let style = parts[2].to_string();
|
||||
let depth = parts[3].strip_prefix('d')?.parse::<f32>().ok()?;
|
||||
@@ -31,13 +32,18 @@ fn parse_stem(stem: &str) -> Option<(String, String, f32, f32, f32, String, f32,
|
||||
let direction = parts[6].to_string();
|
||||
let altitude = parts[7].strip_prefix("alt")?.parse::<f32>().ok()?;
|
||||
let technique = parts[8].to_string();
|
||||
Some((shape, style, depth, size, soften, direction, altitude, technique))
|
||||
Some((
|
||||
shape, style, depth, size, soften, direction, altitude, technique,
|
||||
))
|
||||
}
|
||||
|
||||
fn render_one(stem: &str) {
|
||||
let params = match parse_stem(stem) {
|
||||
Some(p) => p,
|
||||
None => { eprintln!(" cannot parse stem: {}", stem); return; }
|
||||
None => {
|
||||
eprintln!(" cannot parse stem: {}", stem);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let (shape, style_str, depth, size, soften, direction_str, altitude, _technique) = params;
|
||||
|
||||
@@ -56,13 +62,19 @@ fn render_one(stem: &str) {
|
||||
|
||||
let layers = match hcie_psd::import_psd(std::path::Path::new(&psd_path)) {
|
||||
Ok(l) => l,
|
||||
Err(e) => { eprintln!(" SKIP {}: {}", stem, e); return; }
|
||||
Err(e) => {
|
||||
eprintln!(" SKIP {}: {}", stem, e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Find the layer with effects
|
||||
let layer = match layers.iter().find(|l| !l.effects.is_empty()) {
|
||||
Some(l) => l,
|
||||
None => { eprintln!(" SKIP {}: no effects layer", stem); return; }
|
||||
None => {
|
||||
eprintln!(" SKIP {}: no effects layer", stem);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let w = layer.width;
|
||||
@@ -83,7 +95,10 @@ fn render_one(stem: &str) {
|
||||
let mut sh_color = [0u8; 4];
|
||||
for e in &layer.effects {
|
||||
if let hcie_protocol::effects::LayerEffect::BevelEmboss {
|
||||
angle: a, highlight_color, shadow_color, ..
|
||||
angle: a,
|
||||
highlight_color,
|
||||
shadow_color,
|
||||
..
|
||||
} = e
|
||||
{
|
||||
angle = *a;
|
||||
@@ -95,21 +110,22 @@ fn render_one(stem: &str) {
|
||||
};
|
||||
|
||||
// Convert protocol effects to hcie-fx effects and run the full pipeline
|
||||
let fx_effects: Vec<hcie_fx::types::LayerEffect> = layer.effects.iter().map(|e| {
|
||||
hcie_fx::types::protocol_to_hcie_fx_effect(e)
|
||||
}).collect();
|
||||
let fx_effects: Vec<hcie_fx::types::LayerEffect> = layer
|
||||
.effects
|
||||
.iter()
|
||||
.map(|e| hcie_fx::types::protocol_to_hcie_fx_effect(e))
|
||||
.collect();
|
||||
|
||||
// Use apply_layer_effects for the full rendering pipeline
|
||||
let out = hcie_fx::apply_layer_effects(
|
||||
&layer.pixels, w, h,
|
||||
&fx_effects,
|
||||
layer.fill_opacity,
|
||||
);
|
||||
let out = hcie_fx::apply_layer_effects(&layer.pixels, w, h, &fx_effects, layer.fill_opacity);
|
||||
|
||||
// Compute MAE against reference
|
||||
let ref_img = match image::open(&png_path) {
|
||||
Ok(i) => i.to_rgba8(),
|
||||
Err(e) => { eprintln!(" SKIP ref {}: {}", stem, e); return; }
|
||||
Err(e) => {
|
||||
eprintln!(" SKIP ref {}: {}", stem, e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let (rw, rh) = (ref_img.width(), ref_img.height());
|
||||
let ref_raw = ref_img.into_raw();
|
||||
@@ -145,14 +161,22 @@ fn render_one(stem: &str) {
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let i = (y * w + x) as usize * 4;
|
||||
combined.put_pixel(x, y, image::Rgba([out[i], out[i+1], out[i+2], out[i+3]]));
|
||||
combined.put_pixel(
|
||||
x,
|
||||
y,
|
||||
image::Rgba([out[i], out[i + 1], out[i + 2], out[i + 3]]),
|
||||
);
|
||||
}
|
||||
}
|
||||
// ref (right, after 4px gap)
|
||||
for y in 0..rh {
|
||||
for x in 0..rw {
|
||||
let i = (y * rw + x) as usize * 4;
|
||||
combined.put_pixel(w as u32 + 4 + x, y, image::Rgba([ref_raw[i], ref_raw[i+1], ref_raw[i+2], ref_raw[i+3]]));
|
||||
combined.put_pixel(
|
||||
w as u32 + 4 + x,
|
||||
y,
|
||||
image::Rgba([ref_raw[i], ref_raw[i + 1], ref_raw[i + 2], ref_raw[i + 3]]),
|
||||
);
|
||||
}
|
||||
}
|
||||
let cmp_path = format!("{}/{}_cmp.png", OUT_DIR, stem);
|
||||
@@ -184,9 +208,13 @@ fn main() {
|
||||
"be_star_emboss_d200_sz25_sf5_up_alt60_smooth",
|
||||
"be_star_inner_d200_sz25_sf5_up_alt60_smooth",
|
||||
];
|
||||
println!("Rendering {} representative samples to {}...", subset.len(), OUT_DIR);
|
||||
println!(
|
||||
"Rendering {} representative samples to {}...",
|
||||
subset.len(),
|
||||
OUT_DIR
|
||||
);
|
||||
for stem in &subset {
|
||||
render_one(stem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![allow(dead_code, unused_imports, unused_variables, unused_macros)]
|
||||
use rayon::prelude::*;
|
||||
/// Grid-search Bevel & Emboss MAE tuning against Photoshop ground-truth PNGs.
|
||||
///
|
||||
/// Expects `_tmp/psd_tunes/bevel_emboss_png_set/` with transparent PNGs exported
|
||||
@@ -9,9 +10,7 @@
|
||||
///
|
||||
/// Usage:
|
||||
/// cargo run --example tune_mae_bevel_emboss_grid -p hcie-io --release
|
||||
|
||||
use std::fs;
|
||||
use rayon::prelude::*;
|
||||
use std::io::Write;
|
||||
|
||||
const GT_DIR: &str = "_tmp/psd_tunes/bevel_emboss_png_set";
|
||||
@@ -56,14 +55,20 @@ fn main() {
|
||||
|
||||
let params = match parse_filename(&stem) {
|
||||
Some(p) => p,
|
||||
None => { eprintln!(" SKIP: {}", stem); continue; }
|
||||
None => {
|
||||
eprintln!(" SKIP: {}", stem);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let (_shape, style, depth, size, soften, direction, altitude, _technique) = params;
|
||||
|
||||
let img = match image::open(&path) {
|
||||
Ok(i) => i,
|
||||
Err(e) => { eprintln!(" SKIP {}: {}", stem, e); continue; }
|
||||
Err(e) => {
|
||||
eprintln!(" SKIP {}: {}", stem, e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let rgba = img.to_rgba8();
|
||||
@@ -77,17 +82,24 @@ fn main() {
|
||||
}
|
||||
|
||||
let psd_path_str = format!("_tmp/psd_tunes/bevel_emboss_psd_set/{}.psd", stem);
|
||||
let (layer_pixels, fx_effects, fill_opacity, angle) = if let Ok(layers) = hcie_psd::import_psd(std::path::Path::new(&psd_path_str)) {
|
||||
let (layer_pixels, fx_effects, fill_opacity, angle) = if let Ok(layers) =
|
||||
hcie_psd::import_psd(std::path::Path::new(&psd_path_str))
|
||||
{
|
||||
let mut found = None;
|
||||
for l in layers {
|
||||
if !l.effects.is_empty() {
|
||||
let fx_effects: Vec<hcie_fx::types::LayerEffect> = l.effects.iter()
|
||||
let fx_effects: Vec<hcie_fx::types::LayerEffect> = l
|
||||
.effects
|
||||
.iter()
|
||||
.map(|e| hcie_fx::types::protocol_to_hcie_fx_effect(e))
|
||||
.collect();
|
||||
// Extract angle from BevelEmboss effect
|
||||
let mut angle = 120.0f32;
|
||||
for e in &l.effects {
|
||||
if let hcie_protocol::effects::LayerEffect::BevelEmboss { angle: a, .. } = e {
|
||||
if let hcie_protocol::effects::LayerEffect::BevelEmboss {
|
||||
angle: a, ..
|
||||
} = e
|
||||
{
|
||||
angle = *a;
|
||||
}
|
||||
}
|
||||
@@ -97,7 +109,10 @@ fn main() {
|
||||
}
|
||||
match found {
|
||||
Some(v) => v,
|
||||
None => { eprintln!(" SKIP no effects layer: {}", stem); continue; }
|
||||
None => {
|
||||
eprintln!(" SKIP no effects layer: {}", stem);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!(" SKIP PSD missing for {}: {}", stem, psd_path_str);
|
||||
@@ -106,14 +121,20 @@ fn main() {
|
||||
|
||||
samples.push(Sample {
|
||||
name: stem,
|
||||
style, depth, size, soften, direction, altitude,
|
||||
style,
|
||||
depth,
|
||||
size,
|
||||
soften,
|
||||
direction,
|
||||
altitude,
|
||||
angle,
|
||||
ref_pixels: raw,
|
||||
content_mask,
|
||||
layer_pixels,
|
||||
fx_effects,
|
||||
fill_opacity,
|
||||
w, h,
|
||||
w,
|
||||
h,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -138,7 +159,12 @@ fn main() {
|
||||
let tilt_scales: &[f32] = &[0.05, 0.1, 0.2, 0.3, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0, 5.0, 8.0];
|
||||
|
||||
let total_combos = lighting_blurs.len() * hl_scales.len() * sh_scales.len() * tilt_scales.len();
|
||||
println!("Grid: {} combos x {} samples = {} evals", total_combos, samples.len(), total_combos * samples.len());
|
||||
println!(
|
||||
"Grid: {} combos x {} samples = {} evals",
|
||||
total_combos,
|
||||
samples.len(),
|
||||
total_combos * samples.len()
|
||||
);
|
||||
std::io::stdout().flush().ok();
|
||||
|
||||
let mut best_avg_mae = f64::MAX;
|
||||
@@ -149,133 +175,206 @@ fn main() {
|
||||
for &hl_scale in hl_scales {
|
||||
for &sh_scale in sh_scales {
|
||||
for &tilt_scale in tilt_scales {
|
||||
let total_mae: f64 = samples.par_iter().map(|s| {
|
||||
// Use generate_bevel_tuned to get highlight/shadow buffers
|
||||
let style_enum = match s.style.as_str() {
|
||||
"emboss" => hcie_fx::types::BevelStyle::Emboss,
|
||||
"outer" => hcie_fx::types::BevelStyle::OuterBevel,
|
||||
_ => hcie_fx::types::BevelStyle::InnerBevel,
|
||||
};
|
||||
let dir_enum = match s.direction.as_str() {
|
||||
"down" => hcie_fx::types::Direction::Down,
|
||||
_ => hcie_fx::types::Direction::Up,
|
||||
};
|
||||
let total_mae: f64 = samples
|
||||
.par_iter()
|
||||
.map(|s| {
|
||||
// Use generate_bevel_tuned to get highlight/shadow buffers
|
||||
let style_enum = match s.style.as_str() {
|
||||
"emboss" => hcie_fx::types::BevelStyle::Emboss,
|
||||
"outer" => hcie_fx::types::BevelStyle::OuterBevel,
|
||||
_ => hcie_fx::types::BevelStyle::InnerBevel,
|
||||
};
|
||||
let dir_enum = match s.direction.as_str() {
|
||||
"down" => hcie_fx::types::Direction::Down,
|
||||
_ => hcie_fx::types::Direction::Up,
|
||||
};
|
||||
|
||||
let (highlight, shadow) = hcie_fx::tuned::generate_bevel_tuned(
|
||||
&s.layer_pixels.iter().skip(3).step_by(4).copied().collect::<Vec<u8>>(),
|
||||
s.w, s.h,
|
||||
s.depth, s.size, s.soften, s.angle, s.altitude,
|
||||
&dir_enum, &hcie_fx::types::Technique::Smooth, &style_enum,
|
||||
lighting_blur, hl_scale, sh_scale, 1.0, tilt_scale,
|
||||
);
|
||||
let (highlight, shadow) = hcie_fx::tuned::generate_bevel_tuned(
|
||||
&s.layer_pixels
|
||||
.iter()
|
||||
.skip(3)
|
||||
.step_by(4)
|
||||
.copied()
|
||||
.collect::<Vec<u8>>(),
|
||||
s.w,
|
||||
s.h,
|
||||
s.depth,
|
||||
s.size,
|
||||
s.soften,
|
||||
s.angle,
|
||||
s.altitude,
|
||||
&dir_enum,
|
||||
&hcie_fx::types::Technique::Smooth,
|
||||
&style_enum,
|
||||
lighting_blur,
|
||||
hl_scale,
|
||||
sh_scale,
|
||||
1.0,
|
||||
tilt_scale,
|
||||
);
|
||||
|
||||
let px = (s.w * s.h) as usize;
|
||||
let mut out = vec![0u8; px * 4];
|
||||
let px = (s.w * s.h) as usize;
|
||||
let mut out = vec![0u8; px * 4];
|
||||
|
||||
// Base shape: fill with layer pixels (black fill)
|
||||
for i in 0..px {
|
||||
if s.layer_pixels[i * 4 + 3] > 0 {
|
||||
out[i * 4] = s.layer_pixels[i * 4];
|
||||
out[i * 4 + 1] = s.layer_pixels[i * 4 + 1];
|
||||
out[i * 4 + 2] = s.layer_pixels[i * 4 + 2];
|
||||
out[i * 4 + 3] = s.layer_pixels[i * 4 + 3];
|
||||
}
|
||||
}
|
||||
|
||||
// For emboss/outerbevel: outside highlight/shadow go to empty canvas (behind)
|
||||
let has_outer = matches!(style_enum, hcie_fx::types::BevelStyle::Emboss | hcie_fx::types::BevelStyle::OuterBevel);
|
||||
|
||||
// Get opacity and blend mode from fx_effects
|
||||
let (hl_op, sh_op, hl_bm, sh_bm) = {
|
||||
let mut hl_op = 0.75f32;
|
||||
let mut sh_op = 0.75f32;
|
||||
let mut hl_bm = hcie_blend::BlendMode::Screen;
|
||||
let mut sh_bm = hcie_blend::BlendMode::Multiply;
|
||||
for e in &s.fx_effects {
|
||||
if let hcie_fx::types::LayerEffect::BevelEmboss {
|
||||
highlight_opacity, shadow_opacity,
|
||||
highlight_blend, shadow_blend, ..
|
||||
} = e {
|
||||
hl_op = *highlight_opacity / 100.0;
|
||||
sh_op = *shadow_opacity / 100.0;
|
||||
hl_bm = *highlight_blend;
|
||||
sh_bm = *shadow_blend;
|
||||
// Base shape: fill with layer pixels (black fill)
|
||||
for i in 0..px {
|
||||
if s.layer_pixels[i * 4 + 3] > 0 {
|
||||
out[i * 4] = s.layer_pixels[i * 4];
|
||||
out[i * 4 + 1] = s.layer_pixels[i * 4 + 1];
|
||||
out[i * 4 + 2] = s.layer_pixels[i * 4 + 2];
|
||||
out[i * 4 + 3] = s.layer_pixels[i * 4 + 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
(hl_op, sh_op, hl_bm, sh_bm)
|
||||
};
|
||||
|
||||
// Composite highlight — inside on fill, outside on empty canvas
|
||||
for i in 0..px {
|
||||
let sa = highlight[i * 4 + 3];
|
||||
if sa > 0 {
|
||||
let is_inner = s.layer_pixels[i * 4 + 3] > 0;
|
||||
if !has_outer || is_inner {
|
||||
let dst = [out[i * 4], out[i * 4 + 1], out[i * 4 + 2], out[i * 4 + 3]];
|
||||
let src = [highlight[i * 4], highlight[i * 4 + 1], highlight[i * 4 + 2], sa];
|
||||
let blended = hcie_blend::blend_pixels(dst, src, hl_bm, hl_op);
|
||||
out[i * 4..i * 4 + 4].copy_from_slice(&blended);
|
||||
// For emboss/outerbevel: outside highlight/shadow go to empty canvas (behind)
|
||||
let has_outer = matches!(
|
||||
style_enum,
|
||||
hcie_fx::types::BevelStyle::Emboss
|
||||
| hcie_fx::types::BevelStyle::OuterBevel
|
||||
);
|
||||
|
||||
// Get opacity and blend mode from fx_effects
|
||||
let (hl_op, sh_op, hl_bm, sh_bm) = {
|
||||
let mut hl_op = 0.75f32;
|
||||
let mut sh_op = 0.75f32;
|
||||
let mut hl_bm = hcie_blend::BlendMode::Screen;
|
||||
let mut sh_bm = hcie_blend::BlendMode::Multiply;
|
||||
for e in &s.fx_effects {
|
||||
if let hcie_fx::types::LayerEffect::BevelEmboss {
|
||||
highlight_opacity,
|
||||
shadow_opacity,
|
||||
highlight_blend,
|
||||
shadow_blend,
|
||||
..
|
||||
} = e
|
||||
{
|
||||
hl_op = *highlight_opacity / 100.0;
|
||||
sh_op = *shadow_opacity / 100.0;
|
||||
hl_bm = *highlight_blend;
|
||||
sh_bm = *shadow_blend;
|
||||
}
|
||||
}
|
||||
(hl_op, sh_op, hl_bm, sh_bm)
|
||||
};
|
||||
|
||||
// Composite highlight — inside on fill, outside on empty canvas
|
||||
for i in 0..px {
|
||||
let sa = highlight[i * 4 + 3];
|
||||
if sa > 0 {
|
||||
let is_inner = s.layer_pixels[i * 4 + 3] > 0;
|
||||
if !has_outer || is_inner {
|
||||
let dst = [
|
||||
out[i * 4],
|
||||
out[i * 4 + 1],
|
||||
out[i * 4 + 2],
|
||||
out[i * 4 + 3],
|
||||
];
|
||||
let src = [
|
||||
highlight[i * 4],
|
||||
highlight[i * 4 + 1],
|
||||
highlight[i * 4 + 2],
|
||||
sa,
|
||||
];
|
||||
let blended =
|
||||
hcie_blend::blend_pixels(dst, src, hl_bm, hl_op);
|
||||
out[i * 4..i * 4 + 4].copy_from_slice(&blended);
|
||||
} else {
|
||||
// Outside: composite highlight on empty canvas
|
||||
let dst = [0u8, 0, 0, 0];
|
||||
let src = [
|
||||
highlight[i * 4],
|
||||
highlight[i * 4 + 1],
|
||||
highlight[i * 4 + 2],
|
||||
sa,
|
||||
];
|
||||
let blended =
|
||||
hcie_blend::blend_pixels(dst, src, hl_bm, hl_op);
|
||||
out[i * 4..i * 4 + 4].copy_from_slice(&blended);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Composite shadow
|
||||
for i in 0..px {
|
||||
let sa = shadow[i * 4 + 3];
|
||||
if sa > 0 {
|
||||
let is_inner = s.layer_pixels[i * 4 + 3] > 0;
|
||||
if !has_outer || is_inner {
|
||||
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, sh_bm, sh_op);
|
||||
out[i * 4..i * 4 + 4].copy_from_slice(&blended);
|
||||
} else {
|
||||
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, sh_bm, sh_op);
|
||||
out[i * 4..i * 4 + 4].copy_from_slice(&blended);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut diff_sum = 0.0f64;
|
||||
let mut content_pixels = 0u64;
|
||||
for i in 0..px {
|
||||
if !s.content_mask[i] {
|
||||
continue;
|
||||
}
|
||||
content_pixels += 1;
|
||||
for c in 0..4 {
|
||||
diff_sum +=
|
||||
(out[i * 4 + c] as i32 - s.ref_pixels[i * 4 + c] as i32)
|
||||
.abs() as f64;
|
||||
}
|
||||
}
|
||||
if content_pixels > 0 {
|
||||
diff_sum / content_pixels as f64 / 4.0
|
||||
} else {
|
||||
// Outside: composite highlight on empty canvas
|
||||
let dst = [0u8, 0, 0, 0];
|
||||
let src = [highlight[i * 4], highlight[i * 4 + 1], highlight[i * 4 + 2], sa];
|
||||
let blended = hcie_blend::blend_pixels(dst, src, hl_bm, hl_op);
|
||||
out[i * 4..i * 4 + 4].copy_from_slice(&blended);
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
// Composite shadow
|
||||
for i in 0..px {
|
||||
let sa = shadow[i * 4 + 3];
|
||||
if sa > 0 {
|
||||
let is_inner = s.layer_pixels[i * 4 + 3] > 0;
|
||||
if !has_outer || is_inner {
|
||||
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, sh_bm, sh_op);
|
||||
out[i * 4..i * 4 + 4].copy_from_slice(&blended);
|
||||
} else {
|
||||
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, sh_bm, sh_op);
|
||||
out[i * 4..i * 4 + 4].copy_from_slice(&blended);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.sum();
|
||||
|
||||
let mut diff_sum = 0.0f64;
|
||||
let mut content_pixels = 0u64;
|
||||
for i in 0..px {
|
||||
if !s.content_mask[i] { continue; }
|
||||
content_pixels += 1;
|
||||
for c in 0..4 {
|
||||
diff_sum += (out[i * 4 + c] as i32 - s.ref_pixels[i * 4 + c] as i32).abs() as f64;
|
||||
}
|
||||
}
|
||||
if content_pixels > 0 {
|
||||
diff_sum / content_pixels as f64 / 4.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}).sum();
|
||||
eval_count += 1;
|
||||
let avg_mae = total_mae / samples.len() as f64;
|
||||
|
||||
eval_count += 1;
|
||||
let avg_mae = total_mae / samples.len() as f64;
|
||||
|
||||
if avg_mae < best_avg_mae {
|
||||
best_avg_mae = avg_mae;
|
||||
best_params = (lighting_blur, hl_scale, sh_scale, tilt_scale);
|
||||
println!("*** [{}/{}] NEW BEST avg MAE = {:.4} (lb={}, hl={:.3}, sh={:.2}, tilt={:.2})",
|
||||
if avg_mae < best_avg_mae {
|
||||
best_avg_mae = avg_mae;
|
||||
best_params = (lighting_blur, hl_scale, sh_scale, tilt_scale);
|
||||
println!("*** [{}/{}] NEW BEST avg MAE = {:.4} (lb={}, hl={:.3}, sh={:.2}, tilt={:.2})",
|
||||
eval_count, total_combos, best_avg_mae, lighting_blur, hl_scale, sh_scale, tilt_scale);
|
||||
std::io::stdout().flush().ok();
|
||||
} else if eval_count % 100 == 0 {
|
||||
println!(" [{}/{}] avg={:.4} best={:.4}", eval_count, total_combos, avg_mae, best_avg_mae);
|
||||
std::io::stdout().flush().ok();
|
||||
std::io::stdout().flush().ok();
|
||||
} else if eval_count % 100 == 0 {
|
||||
println!(
|
||||
" [{}/{}] avg={:.4} best={:.4}",
|
||||
eval_count, total_combos, avg_mae, best_avg_mae
|
||||
);
|
||||
std::io::stdout().flush().ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (lb, hl, sh, ts) = best_params;
|
||||
println!("\n=== RESULT ===");
|
||||
@@ -286,19 +385,32 @@ fn main() {
|
||||
println!(" tilt_scale = {:.2}", ts);
|
||||
|
||||
if let Ok(mut file) = std::fs::OpenOptions::new()
|
||||
.create(true).write(true).truncate(true).open(RESULTS_FILE)
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(RESULTS_FILE)
|
||||
{
|
||||
let _ = writeln!(file,
|
||||
let _ = writeln!(
|
||||
file,
|
||||
"lighting_blur={} hl_scale={:.3} sh_scale={:.2} tilt_scale={:.2}\n\
|
||||
avg_mae={:.4} samples={} combos={}",
|
||||
lb, hl, sh, ts, best_avg_mae, samples.len(), total_combos);
|
||||
lb,
|
||||
hl,
|
||||
sh,
|
||||
ts,
|
||||
best_avg_mae,
|
||||
samples.len(),
|
||||
total_combos
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_filename(name: &str) -> Option<(String, String, f32, f32, f32, String, f32, String)> {
|
||||
// be_{shape}_{style}_d{depth}_sz{size}_sf{soften}_{dir}_alt{altitude}_{technique}
|
||||
let parts: Vec<&str> = name.split('_').collect();
|
||||
if parts.len() < 9 || parts[0] != "be" { return None; }
|
||||
if parts.len() < 9 || parts[0] != "be" {
|
||||
return None;
|
||||
}
|
||||
let shape = parts[1].to_string();
|
||||
let style = parts[2].to_string();
|
||||
let depth = parts[3].strip_prefix('d')?.parse::<f32>().ok()?;
|
||||
@@ -307,5 +419,7 @@ fn parse_filename(name: &str) -> Option<(String, String, f32, f32, f32, String,
|
||||
let direction = parts[6].to_string();
|
||||
let altitude = parts[7].strip_prefix("alt")?.parse::<f32>().ok()?;
|
||||
let technique = parts[8].to_string();
|
||||
Some((shape, style, depth, size, soften, direction, altitude, technique))
|
||||
}
|
||||
Some((
|
||||
shape, style, depth, size, soften, direction, altitude, technique,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
#![allow(dead_code, unused_imports, unused_variables, unused_macros, unreachable_patterns)]
|
||||
#![allow(
|
||||
dead_code,
|
||||
unused_imports,
|
||||
unused_variables,
|
||||
unused_macros,
|
||||
unreachable_patterns
|
||||
)]
|
||||
use std::io::Write;
|
||||
|
||||
const SHADOW_RS: &str = "hcie-fx/src/emboss.rs";
|
||||
@@ -39,7 +45,16 @@ fn main() {
|
||||
let mut sty = hcie_protocol::effects::BevelStyle::InnerBevel;
|
||||
for e in &layer.effects {
|
||||
if let hcie_protocol::effects::LayerEffect::BevelEmboss {
|
||||
enabled: true, depth, size, soften, angle, altitude, direction, technique, style, ..
|
||||
enabled: true,
|
||||
depth,
|
||||
size,
|
||||
soften,
|
||||
angle,
|
||||
altitude,
|
||||
direction,
|
||||
technique,
|
||||
style,
|
||||
..
|
||||
} = e
|
||||
{
|
||||
d = *depth;
|
||||
@@ -52,19 +67,43 @@ fn main() {
|
||||
sty = style.clone();
|
||||
}
|
||||
}
|
||||
(d, sz, sof, ang, alt,
|
||||
match dir { hcie_protocol::effects::Direction::Up => hcie_fx::types::Direction::Up, _ => hcie_fx::types::Direction::Down },
|
||||
match tech { hcie_protocol::effects::Technique::Smooth => hcie_fx::types::Technique::Smooth, hcie_protocol::effects::Technique::ChiselHard => hcie_fx::types::Technique::ChiselHard, _ => hcie_fx::types::Technique::ChiselSoft },
|
||||
match sty {
|
||||
hcie_protocol::effects::BevelStyle::InnerBevel => hcie_fx::types::BevelStyle::InnerBevel,
|
||||
hcie_protocol::effects::BevelStyle::OuterBevel => hcie_fx::types::BevelStyle::OuterBevel,
|
||||
hcie_protocol::effects::BevelStyle::Emboss => hcie_fx::types::BevelStyle::Emboss,
|
||||
hcie_protocol::effects::BevelStyle::PillowEmboss => hcie_fx::types::BevelStyle::PillowEmboss,
|
||||
_ => hcie_fx::types::BevelStyle::StrokeEmboss,
|
||||
})
|
||||
(
|
||||
d,
|
||||
sz,
|
||||
sof,
|
||||
ang,
|
||||
alt,
|
||||
match dir {
|
||||
hcie_protocol::effects::Direction::Up => hcie_fx::types::Direction::Up,
|
||||
_ => hcie_fx::types::Direction::Down,
|
||||
},
|
||||
match tech {
|
||||
hcie_protocol::effects::Technique::Smooth => hcie_fx::types::Technique::Smooth,
|
||||
hcie_protocol::effects::Technique::ChiselHard => {
|
||||
hcie_fx::types::Technique::ChiselHard
|
||||
}
|
||||
_ => hcie_fx::types::Technique::ChiselSoft,
|
||||
},
|
||||
match sty {
|
||||
hcie_protocol::effects::BevelStyle::InnerBevel => {
|
||||
hcie_fx::types::BevelStyle::InnerBevel
|
||||
}
|
||||
hcie_protocol::effects::BevelStyle::OuterBevel => {
|
||||
hcie_fx::types::BevelStyle::OuterBevel
|
||||
}
|
||||
hcie_protocol::effects::BevelStyle::Emboss => hcie_fx::types::BevelStyle::Emboss,
|
||||
hcie_protocol::effects::BevelStyle::PillowEmboss => {
|
||||
hcie_fx::types::BevelStyle::PillowEmboss
|
||||
}
|
||||
_ => hcie_fx::types::BevelStyle::StrokeEmboss,
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
println!("Emboss params: depth={}, size={}, soften={}, angle={}, altitude={}", depth, size, soften, angle, altitude);
|
||||
println!(
|
||||
"Emboss params: depth={}, size={}, soften={}, angle={}, altitude={}",
|
||||
depth, size, soften, angle, altitude
|
||||
);
|
||||
println!("Image: {}x{}", w, h);
|
||||
std::io::stdout().flush().ok();
|
||||
|
||||
@@ -81,28 +120,80 @@ fn main() {
|
||||
println!("Phase 1: coarse grid ({} combos)", total);
|
||||
std::io::stdout().flush().ok();
|
||||
|
||||
search(original.as_str(), before, after, &alpha, w, h, px, &ref_pixels, &layer.pixels,
|
||||
depth, size, soften, angle, altitude, &direction, &technique, &style,
|
||||
coarse_blur, coarse_hl, coarse_sh,
|
||||
&mut best_mae, &mut best_code, &mut best_params);
|
||||
search(
|
||||
original.as_str(),
|
||||
before,
|
||||
after,
|
||||
&alpha,
|
||||
w,
|
||||
h,
|
||||
px,
|
||||
&ref_pixels,
|
||||
&layer.pixels,
|
||||
depth,
|
||||
size,
|
||||
soften,
|
||||
angle,
|
||||
altitude,
|
||||
&direction,
|
||||
&technique,
|
||||
&style,
|
||||
coarse_blur,
|
||||
coarse_hl,
|
||||
coarse_sh,
|
||||
&mut best_mae,
|
||||
&mut best_code,
|
||||
&mut best_params,
|
||||
);
|
||||
|
||||
// Phase 2: fine grid around best
|
||||
let (bb, bhl, bsh) = best_params;
|
||||
let fine_blur: Vec<i32> = (bb - 4 ..= bb + 4).filter(|&v| v > 0).collect();
|
||||
let fine_hl: Vec<f32> = ((bhl * 100.0 - 50.0) as i32 ..= (bhl * 100.0 + 50.0) as i32)
|
||||
.step_by(5).map(|v| v as f32 / 100.0).filter(|&v| v > 0.0).collect();
|
||||
let fine_sh: Vec<f32> = ((bsh * 100.0 - 50.0) as i32 ..= (bsh * 100.0 + 50.0) as i32)
|
||||
.step_by(5).map(|v| v as f32 / 100.0).filter(|&v| v > 0.0).collect();
|
||||
let fine_blur: Vec<i32> = (bb - 4..=bb + 4).filter(|&v| v > 0).collect();
|
||||
let fine_hl: Vec<f32> = ((bhl * 100.0 - 50.0) as i32..=(bhl * 100.0 + 50.0) as i32)
|
||||
.step_by(5)
|
||||
.map(|v| v as f32 / 100.0)
|
||||
.filter(|&v| v > 0.0)
|
||||
.collect();
|
||||
let fine_sh: Vec<f32> = ((bsh * 100.0 - 50.0) as i32..=(bsh * 100.0 + 50.0) as i32)
|
||||
.step_by(5)
|
||||
.map(|v| v as f32 / 100.0)
|
||||
.filter(|&v| v > 0.0)
|
||||
.collect();
|
||||
|
||||
println!("\nPhase 2: fine grid around best ({} x {} x {} = {} combos)",
|
||||
fine_blur.len(), fine_hl.len(), fine_sh.len(),
|
||||
fine_blur.len() * fine_hl.len() * fine_sh.len());
|
||||
println!(
|
||||
"\nPhase 2: fine grid around best ({} x {} x {} = {} combos)",
|
||||
fine_blur.len(),
|
||||
fine_hl.len(),
|
||||
fine_sh.len(),
|
||||
fine_blur.len() * fine_hl.len() * fine_sh.len()
|
||||
);
|
||||
std::io::stdout().flush().ok();
|
||||
|
||||
search(original.as_str(), before, after, &alpha, w, h, px, &ref_pixels, &layer.pixels,
|
||||
depth, size, soften, angle, altitude, &direction, &technique, &style,
|
||||
&fine_blur, &fine_hl, &fine_sh,
|
||||
&mut best_mae, &mut best_code, &mut best_params);
|
||||
search(
|
||||
original.as_str(),
|
||||
before,
|
||||
after,
|
||||
&alpha,
|
||||
w,
|
||||
h,
|
||||
px,
|
||||
&ref_pixels,
|
||||
&layer.pixels,
|
||||
depth,
|
||||
size,
|
||||
soften,
|
||||
angle,
|
||||
altitude,
|
||||
&direction,
|
||||
&technique,
|
||||
&style,
|
||||
&fine_blur,
|
||||
&fine_hl,
|
||||
&fine_sh,
|
||||
&mut best_mae,
|
||||
&mut best_code,
|
||||
&mut best_params,
|
||||
);
|
||||
|
||||
if best_mae < f64::MAX {
|
||||
let (bb, bhl, bsh) = best_params;
|
||||
@@ -167,7 +258,12 @@ fn main() {
|
||||
std::fs::write(SHADOW_RS, &patched).expect("write final best");
|
||||
println!("\nCompleted. Best code saved with MAE = {:.3} (blur={}, hl_scale={:.2}, sh_scale={:.2})",
|
||||
best_mae, bb, bhl, bsh);
|
||||
if let Ok(mut file) = std::fs::OpenOptions::new().create(true).write(true).truncate(true).open(RESULTS_FILE) {
|
||||
if let Ok(mut file) = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(RESULTS_FILE)
|
||||
{
|
||||
let _ = writeln!(file, "=== Emboss Tuned ===\n Params: blur={}, hl_scale={:.2}, sh_scale={:.2}\n MAE: {:.3}",
|
||||
bb, bhl, bsh, best_mae);
|
||||
}
|
||||
@@ -177,15 +273,28 @@ fn main() {
|
||||
}
|
||||
|
||||
fn search(
|
||||
_original: &str, _before: &str, _after: &str,
|
||||
alpha: &[u8], w: u32, h: u32, px: usize, ref_pixels: &[u8],
|
||||
_original: &str,
|
||||
_before: &str,
|
||||
_after: &str,
|
||||
alpha: &[u8],
|
||||
w: u32,
|
||||
h: u32,
|
||||
px: usize,
|
||||
ref_pixels: &[u8],
|
||||
src_pixels: &[u8],
|
||||
depth: f32, size: f32, soften: f32, angle: f32, altitude: f32,
|
||||
depth: f32,
|
||||
size: f32,
|
||||
soften: f32,
|
||||
angle: f32,
|
||||
altitude: f32,
|
||||
direction: &hcie_fx::types::Direction,
|
||||
technique: &hcie_fx::types::Technique,
|
||||
style: &hcie_fx::types::BevelStyle,
|
||||
blur_list: &[i32], hl_list: &[f32], sh_list: &[f32],
|
||||
best_mae: &mut f64, _best_code: &mut String,
|
||||
blur_list: &[i32],
|
||||
hl_list: &[f32],
|
||||
sh_list: &[f32],
|
||||
best_mae: &mut f64,
|
||||
_best_code: &mut String,
|
||||
best_params: &mut (i32, f32, f32),
|
||||
) {
|
||||
let total = blur_list.len() * hl_list.len() * sh_list.len();
|
||||
@@ -201,38 +310,57 @@ fn search(
|
||||
}
|
||||
|
||||
let (hl_buf, sh_buf) = hcie_fx::tuned::generate_bevel_tuned(
|
||||
alpha, w, h, depth, size, soften, angle, altitude,
|
||||
direction, technique, style,
|
||||
alpha, w, h, depth, size, soften, angle, altitude, direction, technique, style,
|
||||
*blur, *hl_scale, *sh_scale, 1.0, 0.05,
|
||||
);
|
||||
|
||||
let mut out = src_pixels.to_vec();
|
||||
for i in 0..px {
|
||||
let dst = [out[i*4], out[i*4+1], out[i*4+2], out[i*4+3]];
|
||||
let hl_src = [hl_buf[i*4], hl_buf[i*4+1], hl_buf[i*4+2], hl_buf[i*4+3]];
|
||||
let sh_src = [sh_buf[i*4], sh_buf[i*4+1], sh_buf[i*4+2], sh_buf[i*4+3]];
|
||||
let blended = hcie_blend::blend_pixels(dst, hl_src, hcie_blend::BlendMode::Screen, 0.75);
|
||||
let blended = hcie_blend::blend_pixels(blended, sh_src, hcie_blend::BlendMode::Multiply, 0.75);
|
||||
out[i*4..i*4+4].copy_from_slice(&blended);
|
||||
let dst = [out[i * 4], out[i * 4 + 1], out[i * 4 + 2], out[i * 4 + 3]];
|
||||
let hl_src = [
|
||||
hl_buf[i * 4],
|
||||
hl_buf[i * 4 + 1],
|
||||
hl_buf[i * 4 + 2],
|
||||
hl_buf[i * 4 + 3],
|
||||
];
|
||||
let sh_src = [
|
||||
sh_buf[i * 4],
|
||||
sh_buf[i * 4 + 1],
|
||||
sh_buf[i * 4 + 2],
|
||||
sh_buf[i * 4 + 3],
|
||||
];
|
||||
let blended =
|
||||
hcie_blend::blend_pixels(dst, hl_src, hcie_blend::BlendMode::Screen, 0.75);
|
||||
let blended = hcie_blend::blend_pixels(
|
||||
blended,
|
||||
sh_src,
|
||||
hcie_blend::BlendMode::Multiply,
|
||||
0.75,
|
||||
);
|
||||
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>();
|
||||
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, *hl_scale, *sh_scale);
|
||||
println!("*** NEW BEST: {:.3} (blur={}, hl_scale={:.2}, sh_scale={:.2})",
|
||||
best_mae, blur, hl_scale, sh_scale);
|
||||
std::io::stdout().flush().ok();
|
||||
}
|
||||
*best_mae = mae;
|
||||
*best_params = (*blur, *hl_scale, *sh_scale);
|
||||
println!(
|
||||
"*** NEW BEST: {:.3} (blur={}, hl_scale={:.2}, sh_scale={:.2})",
|
||||
best_mae, blur, hl_scale, sh_scale
|
||||
);
|
||||
std::io::stdout().flush().ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user