feat: Enhance Bevel & Emboss effect with contour support and algorithm improvements

- Implemented contour lookup functionality for Bevel & Emboss effect to allow for custom contour curves.
- Added contour parameter to LayerStyle and updated related structures and functions.
- Improved emboss algorithm by fixing height field calculations and ensuring proper handling of outside pixels.
- Introduced tilt component for 3D emboss effect, enhancing visual depth.
- Optimized MAE grid search parameters for better performance and accuracy.
- Expanded Iced GUI panel for Bevel & Emboss to include contour selection and additional styling options.
- Updated example scripts to reflect changes in the API and new features.
This commit is contained in:
2026-07-14 00:36:09 +03:00
parent 5e03065165
commit 58599bf8f9
11 changed files with 521 additions and 81 deletions
+182 -56
View File
@@ -1,7 +1,4 @@
use std::fs;
use hcie_io::psd::PsdLoader;
use hcie_fx::generate_bevel_tuned;
use image::{RgbaImage, GenericImageView};
use std::path::Path;
fn main() {
@@ -16,85 +13,214 @@ fn main() {
}
}
let mut inner_mae = 0.0;
let mut inner_count = 0;
let mut outer_mae = 0.0;
let mut outer_count = 0;
let mut emboss_mae = 0.0;
let mut emboss_count = 0;
let mut inner_mae = 0.0f64;
let mut inner_count = 0usize;
let mut outer_mae = 0.0f64;
let mut outer_count = 0usize;
let mut emboss_mae = 0.0f64;
let mut emboss_count = 0usize;
let lb = 15;
let hl = 1.0;
let sh = 1.0;
let lb = 5;
let hl = 0.05;
let sh = 2.5;
let tilt = 0.05;
println!("Evaluating {} samples", samples.len());
for (name, png_path) in samples {
let psd_name = name.split("_").next().unwrap().to_string() + ".psd";
let psd_name = name.split('_').next().unwrap().to_string() + ".psd";
let psd_path = format!("_tmp/psd_tunes/{}", psd_name);
if !Path::new(&psd_path).exists() { continue; }
if !Path::new(&psd_path).exists() {
continue;
}
let target = image::open(&png_path).unwrap().to_rgba8();
let mut psd = PsdLoader::new(&psd_path).unwrap();
let layers = psd.get_layers();
let layers = hcie_psd::import_psd(std::path::Path::new(&psd_path)).unwrap();
let mut layer = None;
for l in layers {
if l.effects.is_some() && l.effects.as_ref().unwrap().bevel_emboss.is_some() {
layer = Some(l);
for l in &layers {
for e in &l.effects {
if let hcie_protocol::effects::LayerEffect::BevelEmboss {
enabled: true, ..
} = e
{
layer = Some(l);
break;
}
}
if layer.is_some() {
break;
}
}
if layer.is_none() { continue; }
if layer.is_none() {
continue;
}
let layer = layer.unwrap();
let fx = layer.effects.unwrap();
let bevel = fx.bevel_emboss.unwrap();
use hcie_fx::types::*;
let mut style = BevelStyle::InnerBevel;
if name.contains("InnerBevel") { style = BevelStyle::InnerBevel; }
if name.contains("OuterBevel") { style = BevelStyle::OuterBevel; }
if name.contains("Emboss") { style = BevelStyle::Emboss; }
let (depth, size, soften, angle, altitude, direction, technique, style) = {
let mut d = 0.0f32;
let mut sz = 0.0f32;
let mut sof = 0.0f32;
let mut ang = 0.0f32;
let mut alt = 0.0f32;
let mut dir = hcie_protocol::effects::Direction::Up;
let mut tech = hcie_protocol::effects::Technique::Smooth;
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,
..
} = e
{
d = *depth;
sz = *size;
sof = *soften;
ang = *angle;
alt = *altitude;
dir = direction.clone();
tech = technique.clone();
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,
},
)
};
let (out_w, out_h, out_pixels) = generate_bevel_tuned(
layer.width as usize, layer.height as usize, &layer.rgba,
bevel.size as f32, bevel.depth as f32, bevel.soften as f32, bevel.angle as f32, bevel.altitude as f32,
style, lb, hl, sh
let w = layer.width;
let h = layer.height;
let px = (w * h) as usize;
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,
);
let mut diff_sum = 0.0;
let mut px_count = 0;
let offset_x = (out_w as i32 - target.width() as i32) / 2;
let offset_y = (out_h as i32 - target.height() as i32) / 2;
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 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;
let mut px_count = 0usize;
let target_raw = target.as_raw();
let offset_x = (w as i32 - target.width() as i32) / 2;
let offset_y = (h as i32 - target.height() as i32) / 2;
for y in 0..target.height() {
for x in 0..target.width() {
let sx = x as i32 + offset_x;
let sy = y as i32 + offset_y;
if sx >= 0 && sx < out_w as i32 && sy >= 0 && sy < out_h as i32 {
let s_idx = ((sy as usize) * out_w + (sx as usize)) * 4;
let t_idx = ((y as usize) * target.width() as usize + (x as usize)) * 4;
let src_a = layer.rgba[s_idx+3];
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 src_a = layer.pixels[s_idx + 3];
if src_a > 0 {
diff_sum += (out_pixels[s_idx] as f32 - target[t_idx] as f32).abs();
diff_sum += (out_pixels[s_idx+1] as f32 - target[t_idx+1] as f32).abs();
diff_sum += (out_pixels[s_idx+2] as f32 - target[t_idx+2] as f32).abs();
px_count += 3; // R, G, B
diff_sum += (out[s_idx] as f64 - target_raw[t_idx] as f64).abs();
diff_sum += (out[s_idx + 1] as f64 - target_raw[t_idx + 1] as f64).abs();
diff_sum += (out[s_idx + 2] as f64 - target_raw[t_idx + 2] as f64).abs();
px_count += 3;
}
}
}
}
if px_count > 0 {
let mae = diff_sum / px_count as f32;
if name.contains("InnerBevel") { inner_mae += mae; inner_count += 1; }
if name.contains("OuterBevel") { outer_mae += mae; outer_count += 1; }
if name.contains("Emboss") { emboss_mae += mae; emboss_count += 1; }
let mae = diff_sum / px_count as f64;
if name.contains("InnerBevel") {
inner_mae += mae;
inner_count += 1;
}
if name.contains("OuterBevel") {
outer_mae += mae;
outer_count += 1;
}
if name.contains("Emboss") {
emboss_mae += mae;
emboss_count += 1;
}
}
}
println!("Inner Bevel MAE: {:.4} ({} samples)", inner_mae / inner_count as f32, inner_count);
println!("Outer Bevel MAE: {:.4} ({} samples)", outer_mae / outer_count as f32, outer_count);
println!("Emboss MAE: {:.4} ({} samples)", emboss_mae / emboss_count as f32, emboss_count);
if inner_count > 0 {
println!(
"Inner Bevel MAE: {:.4} ({} samples)",
inner_mae / inner_count as f64,
inner_count
);
}
if outer_count > 0 {
println!(
"Outer Bevel MAE: {:.4} ({} samples)",
outer_mae / outer_count as f64,
outer_count
);
}
if emboss_count > 0 {
println!(
"Emboss MAE: {:.4} ({} samples)",
emboss_mae / emboss_count as f64,
emboss_count
);
}
}
+1 -1
View File
@@ -203,7 +203,7 @@ fn search(
let (hl_buf, sh_buf) = hcie_fx::tuned::generate_bevel_tuned(
alpha, w, h, depth, size, soften, angle, altitude,
direction, technique, style,
*blur, *hl_scale, *sh_scale, 1.0,
*blur, *hl_scale, *sh_scale, 1.0, 0.05,
);
let mut out = src_pixels.to_vec();