Files
hcie-rust-v3.05/hcie-io/examples/composite_test.rs
T

285 lines
13 KiB
Rust

#![allow(unreachable_patterns, unused_assignments, unused_must_use)]
#![allow(dead_code, unused_imports, unused_variables, unused_macros)]
use hcie_blend;
use hcie_composite;
use hcie_io;
use image;
fn main() {
// Load PSD
let psd_path = "_images/_test_images/example3/Example3-mini.psd";
let layers: Vec<hcie_protocol::Layer> = match hcie_psd::import_psd(std::path::Path::new(psd_path)) {
Ok(l) => l,
Err(e) => {
eprintln!("import_psd failed: {}", e);
return;
}
};
if layers.is_empty() {
eprintln!("No layers imported.");
return;
}
// Layer summary for debugging (focus on Rear Light / Jaw Light)
println!("=== Layer Summary ===");
for (i, layer) in layers.iter().enumerate() {
let adj = if let Some(adj) = &layer.adjustment {
match adj {
hcie_blend::Adjustment::Curves { .. } => "Curves",
hcie_blend::Adjustment::GradientMap { .. } => "GradientMap",
hcie_blend::Adjustment::HueSaturation { .. } => "HueSat",
_ => "Other",
}
} else {
"None"
};
let mask_info = if let Some(_) = &layer.mask_pixels {
format!("Mask(default={})", layer.mask_default_color)
} else {
"NoMask".to_string()
};
let alphas: Vec<u8> = layer.pixels.chunks_exact(4).map(|c| c[3]).collect();
let (min_a, max_a, avg_a) = if !alphas.is_empty() {
let min = *alphas.iter().min().unwrap();
let max = *alphas.iter().max().unwrap();
let sum: u64 = alphas.iter().map(|&v| v as u64).sum();
let avg = sum as f64 / alphas.len() as f64;
(min, max, avg)
} else {
(0, 0, 0.0)
};
println!("{:2}: name='{}', blend={:?}, opacity={:.3}, visible={}, clipping={}, adjustment={}, {}, alpha=[min={}, max={}, avg={:.1}]",
i, layer.name, layer.blend_mode, layer.opacity, layer.visible, layer.clipping_mask, adj, mask_info, min_a, max_a, avg_a);
}
println!("=== End Summary ===");
// Debug mask statistics and LUTs for layers with masks
for (i, layer) in layers.iter().enumerate() {
if !layer.effects.is_empty() {
println!(
"Layer {} ('{}') effects ({}):",
i,
layer.name,
layer.effects.len()
);
for fx in &layer.effects {
println!(" {} (enabled={})", fx.effect_name(), fx.is_enabled());
match hcie_fx::protocol_to_hcie_fx_effect(fx) {
hcie_fx::LayerEffect::BevelEmboss {
depth,
size,
angle,
altitude,
highlight_color,
shadow_color,
..
} => {
println!(
" depth={}, size={}, angle={}, altitude={}, hl={:?}, sh={:?}",
depth, size, angle, altitude, highlight_color, shadow_color
);
}
hcie_fx::LayerEffect::OuterGlow {
color,
opacity,
size,
spread,
blend_mode,
..
} => {
println!(
" color={:?}, opacity={}, size={}, spread={}, blend={:?}",
color, opacity, size, spread, blend_mode
);
}
hcie_fx::LayerEffect::InnerGlow {
color,
opacity,
size,
blend_mode,
..
} => {
println!(
" color={:?}, opacity={}, size={}, blend={:?}",
color, opacity, size, blend_mode
);
}
_ => {}
}
}
}
if layer.adjustment.is_some() {
println!("--- Debugging Layer {} ('{}') ---", i, layer.name);
if let Some(adj) = &layer.adjustment {
match adj {
hcie_blend::Adjustment::Curves { lut_r, lut_g, lut_b } => {
println!(
"Curves LUT sample R: [{}, {}, {}, {}, {}], G: [{}, {}, {}, {}, {}], B: [{}, {}, {}, {}, {}]",
lut_r[0], lut_r[64], lut_r[128], lut_r[192], lut_r[255],
lut_g[0], lut_g[64], lut_g[128], lut_g[192], lut_g[255],
lut_b[0], lut_b[64], lut_b[128], lut_b[192], lut_b[255]
);
}
hcie_blend::Adjustment::GradientMap { lut_r, lut_g, lut_b } => {
println!(
"GradientMap LUT sample R: [{}, {}, {}, {}, {}], G: [{}, {}, {}, {}, {}], B: [{}, {}, {}, {}, {}]",
lut_r[0], lut_r[64], lut_r[128], lut_r[192], lut_r[255],
lut_g[0], lut_g[64], lut_g[128], lut_g[192], lut_g[255],
lut_b[0], lut_b[64], lut_b[128], lut_b[192], lut_b[255]
);
}
hcie_blend::Adjustment::HueSaturation { hue, saturation, lightness } => {
println!("HueSat: hue={}, sat={}, light={}", hue, saturation, lightness);
}
}
}
if let Some(mask) = &layer.mask_pixels {
let non_zero: usize = mask.iter().filter(|&&v| v != 0).count();
println!(
"Mask stats: {} non-zero / {} total (default={})",
non_zero,
mask.len(),
layer.mask_default_color
);
}
if !layer.effects.is_empty() {
println!("Effects ({}):", layer.effects.len());
for fx in &layer.effects {
println!(" {} (enabled={})", fx.effect_name(), fx.is_enabled());
}
}
}
}
fn map_blend(mode: hcie_protocol::BlendMode) -> hcie_blend::BlendMode {
match mode {
hcie_protocol::BlendMode::Normal => hcie_blend::BlendMode::Normal,
hcie_protocol::BlendMode::Dissolve => hcie_blend::BlendMode::Dissolve,
hcie_protocol::BlendMode::Darken => hcie_blend::BlendMode::Darken,
hcie_protocol::BlendMode::Multiply => hcie_blend::BlendMode::Multiply,
hcie_protocol::BlendMode::ColorBurn => hcie_blend::BlendMode::ColorBurn,
hcie_protocol::BlendMode::LinearBurn => hcie_blend::BlendMode::LinearBurn,
hcie_protocol::BlendMode::DarkerColor => hcie_blend::BlendMode::DarkerColor,
hcie_protocol::BlendMode::Lighten => hcie_blend::BlendMode::Lighten,
hcie_protocol::BlendMode::Screen => hcie_blend::BlendMode::Screen,
hcie_protocol::BlendMode::ColorDodge => hcie_blend::BlendMode::ColorDodge,
hcie_protocol::BlendMode::LinearDodge => hcie_blend::BlendMode::LinearDodge,
hcie_protocol::BlendMode::LighterColor => hcie_blend::BlendMode::LighterColor,
hcie_protocol::BlendMode::Overlay => hcie_blend::BlendMode::Overlay,
hcie_protocol::BlendMode::SoftLight => hcie_blend::BlendMode::SoftLight,
hcie_protocol::BlendMode::HardLight => hcie_blend::BlendMode::HardLight,
hcie_protocol::BlendMode::VividLight => hcie_blend::BlendMode::VividLight,
hcie_protocol::BlendMode::LinearLight => hcie_blend::BlendMode::LinearLight,
hcie_protocol::BlendMode::PinLight => hcie_blend::BlendMode::PinLight,
hcie_protocol::BlendMode::HardMix => hcie_blend::BlendMode::HardMix,
hcie_protocol::BlendMode::Difference => hcie_blend::BlendMode::Difference,
hcie_protocol::BlendMode::Exclusion => hcie_blend::BlendMode::Exclusion,
hcie_protocol::BlendMode::Subtract => hcie_blend::BlendMode::Subtract,
hcie_protocol::BlendMode::Divide => hcie_blend::BlendMode::Divide,
hcie_protocol::BlendMode::Hue => hcie_blend::BlendMode::Hue,
hcie_protocol::BlendMode::Saturation => hcie_blend::BlendMode::Saturation,
hcie_protocol::BlendMode::Color => hcie_blend::BlendMode::Color,
hcie_protocol::BlendMode::Luminosity => hcie_blend::BlendMode::Luminosity,
hcie_protocol::BlendMode::PassThrough => hcie_blend::BlendMode::PassThrough,
}
}
let comp_layers: Vec<hcie_composite::Layer> = layers
.iter()
.map(|l| hcie_composite::Layer {
name: l.name.clone(),
layer_type: l.layer_type.clone(),
data: l.data.clone(),
pixels: l.pixels.clone(),
width: l.width,
height: l.height,
visible: l.visible,
opacity: l.opacity,
blend_mode: l.blend_mode,
locked: l.locked,
dirty: l.dirty,
id: l.id,
parent_id: l.parent_id,
styles: l.styles.clone(),
clipping_mask: l.clipping_mask,
collapsed: l.collapsed,
adjustment: l.adjustment.clone(),
mask_pixels: l.mask_pixels.clone(),
mask_bounds: l.mask_bounds,
mask_default_color: l.mask_default_color,
curve_points: l.curve_points.clone(),
fill_opacity: l.fill_opacity,
effects: l.effects.clone(),
effects_dirty: std::sync::atomic::AtomicBool::new(false),
effects_cache: std::sync::Mutex::new(None),
adjustment_raw: None,
is_section_divider: false,
image_resources_raw: None,
})
.collect();
let canvas_w = comp_layers[0].width;
let canvas_h = comp_layers[0].height;
// Composite
let output = hcie_composite::composite_layers(&comp_layers, canvas_w, canvas_h);
// Save output for visual inspection
let out_path = "/tmp/composite_output.png";
{
let img = image::RgbaImage::from_raw(canvas_w, canvas_h, output)
.expect("Failed to create image from buffer");
img.save(out_path).expect("Failed to save output image");
}
println!("Saved composite to {}", out_path);
// Diff against reference PNG
let ref_path = "_images/_test_images/example3/Example3-mini.png";
if std::path::Path::new(ref_path).exists() {
let ref_img = image::open(ref_path).unwrap().to_rgba8();
let ref_pixels = ref_img.as_raw();
if ref_img.width() == canvas_w && ref_img.height() == canvas_h {
let mut diff_sum = 0.0f64;
let mut max_diff = 0u32;
let comp_img = image::open(out_path).unwrap().to_rgba8();
for (p1, p2) in comp_img.pixels().zip(ref_img.pixels()) {
let d =
p1.0.iter()
.zip(p2.0.iter())
.map(|(a, b)| (*a as i32 - *b as i32).abs() as f64)
.sum::<f64>();
diff_sum += d;
max_diff = max_diff.max(d as u32);
}
let px_cnt = (canvas_w * canvas_h) as f64;
let total_mae = (diff_sum / px_cnt) / 4.0;
println!("Avg diff per channel (MAE): {:.4}", total_mae);
println!("Max diff per pixel: {}", max_diff);
println!("\n--- Per-layer contribution analysis ---");
for skip_i in 0..comp_layers.len() {
let cl_skip: Vec<_> = comp_layers.iter().enumerate()
.filter(|(idx, _)| *idx != skip_i)
.map(|(_, l)| l.clone())
.collect();
let res_skip = hcie_composite::composite_layers(&cl_skip, canvas_w, canvas_h);
let mut ds_skip = 0.0f64;
for j in 0..(canvas_w * canvas_h) as usize {
let idx = j * 4;
if idx + 3 >= res_skip.len() || idx + 3 >= ref_pixels.len() { continue; }
let dr = (res_skip[idx] as i32 - ref_pixels[idx] as i32).unsigned_abs() as f64;
let dg = (res_skip[idx+1] as i32 - ref_pixels[idx+1] as i32).unsigned_abs() as f64;
let db = (res_skip[idx+2] as i32 - ref_pixels[idx+2] as i32).unsigned_abs() as f64;
let da = (res_skip[idx+3] as i32 - ref_pixels[idx+3] as i32).unsigned_abs() as f64;
ds_skip += (dr + dg + db + da) / 4.0;
}
let mae_skip = ds_skip / px_cnt;
let delta = total_mae - mae_skip;
println!(" Skip [{:2}] {:25}: MAE(w/o)={:.4} delta={:+.4}", skip_i, comp_layers[skip_i].name, mae_skip, delta);
}
} else {
println!("Reference image dimensions differ.");
}
}
}