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:
+182
-56
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user