Files
hcie-rust-v3.05/hcie-io/examples/inspect_emboss.rs
phantom b09795ccff 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.
2026-07-16 22:10:22 +03:00

221 lines
7.3 KiB
Rust

#![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
/// current `generate_bevel_emboss` implementation, composites highlight/shadow
/// over the layer fill, and writes the result to `_tmp/emboss_inspect/` so we
/// can compare side-by-side with the Photoshop ground truth PNG.
///
/// Usage:
/// cargo run --example inspect_emboss -p hcie-io -- <stem>
/// 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};
const PSD_DIR: &str = "_tmp/psd_tunes/bevel_emboss_psd_set";
const PNG_DIR: &str = "_tmp/psd_tunes/bevel_emboss_png_set";
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;
}
let shape = parts[1].to_string();
let style = parts[2].to_string();
let depth = parts[3].strip_prefix('d')?.parse::<f32>().ok()?;
let size = parts[4].strip_prefix("sz")?.parse::<f32>().ok()?;
let soften = parts[5].strip_prefix("sf")?.parse::<f32>().ok()?;
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,
))
}
fn render_one(stem: &str) {
let params = match parse_stem(stem) {
Some(p) => p,
None => {
eprintln!(" cannot parse stem: {}", stem);
return;
}
};
let (shape, style_str, depth, size, soften, direction_str, altitude, _technique) = params;
let style_enum = match style_str.as_str() {
"emboss" => BevelStyle::Emboss,
"outer" => BevelStyle::OuterBevel,
_ => BevelStyle::InnerBevel,
};
let dir_enum = match direction_str.as_str() {
"down" => Direction::Down,
_ => Direction::Up,
};
let psd_path = format!("{}/{}.psd", PSD_DIR, stem);
let png_path = format!("{}/{}.png", PNG_DIR, stem);
let layers = match hcie_psd::import_psd(std::path::Path::new(&psd_path)) {
Ok(l) => l,
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;
}
};
let w = layer.width;
let h = layer.height;
let px = (w * h) as usize;
// Extract alpha from layer pixels
let mut alpha = vec![0u8; px];
for i in 0..px {
alpha[i] = layer.pixels[i * 4 + 3];
}
// Get the bevel params from the parsed effect — but we'll use apply_layer_effects
// for the full pipeline (inside + behind split for emboss/outerbevel)
let _ = {
let mut angle = 120.0f32;
let mut hl_color = [255u8; 4];
let mut sh_color = [0u8; 4];
for e in &layer.effects {
if let hcie_protocol::effects::LayerEffect::BevelEmboss {
angle: a,
highlight_color,
shadow_color,
..
} = e
{
angle = *a;
hl_color = *highlight_color;
sh_color = *shadow_color;
}
}
(angle, hl_color, sh_color)
};
// 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();
// 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);
// 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;
}
};
let (rw, rh) = (ref_img.width(), ref_img.height());
let ref_raw = ref_img.into_raw();
let mut diff_sum = 0.0f64;
let mut content_pixels = 0u64;
for i in 0..px {
let ra = ref_raw[i * 4 + 3];
let oa = out[i * 4 + 3];
if ra > 0 || oa > 0 {
content_pixels += 1;
for c in 0..4 {
diff_sum += (out[i * 4 + c] as i32 - ref_raw[i * 4 + c] as i32).abs() as f64;
}
}
}
let mae = if content_pixels > 0 {
diff_sum / content_pixels as f64 / 4.0
} else {
0.0
};
// Save our rendering
let out_img: image::RgbaImage = image::ImageBuffer::from_raw(w, h, out.clone()).unwrap();
let out_path = format!("{}/{}.png", OUT_DIR, stem);
let _ = out_img.save(&out_path);
// Also save a side-by-side comparison
let combined_w = w + rw + 4;
let combined_h = h.max(rh);
let mut combined = image::RgbaImage::new(combined_w, combined_h);
// our output (left)
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]]),
);
}
}
// 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]]),
);
}
}
let cmp_path = format!("{}/{}_cmp.png", OUT_DIR, stem);
let _ = combined.save(&cmp_path);
println!(" {} : MAE={:.2} (saved {})", stem, mae, cmp_path);
}
fn main() {
let _ = std::fs::create_dir_all(OUT_DIR);
let args: Vec<String> = std::env::args().collect();
if args.len() > 1 {
// Render specific stems
for stem in &args[1..] {
render_one(stem);
}
} else {
// Representative subset: different shapes, styles, depths, sizes
let subset = [
"be_L_emboss_d100_sz10_sf0_down_alt30_smooth",
"be_L_emboss_d100_sz25_sf0_up_alt60_smooth",
"be_L_inner_d100_sz10_sf0_down_alt30_smooth",
"be_L_inner_d100_sz25_sf0_up_alt60_smooth",
"be_rect_emboss_d100_sz10_sf0_down_alt30_smooth",
"be_rect_inner_d100_sz10_sf0_down_alt30_smooth",
"be_circle_emboss_d100_sz10_sf0_down_alt30_smooth",
"be_circle_inner_d100_sz10_sf0_down_alt30_smooth",
"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
);
for stem in &subset {
render_one(stem);
}
}
}