194 lines
7.3 KiB
Rust
194 lines
7.3 KiB
Rust
|
|
/// Generate an expanded set of stroke PSDs with asymmetric shapes.
|
||
|
|
///
|
||
|
|
/// Shapes: square, tall-rect, wide-rect, L, T, cross, triangle, star.
|
||
|
|
/// This tests stroke rendering on non-uniform geometry and sharp corners.
|
||
|
|
/// Output: `_tmp/stroke_v2_psd_set/`
|
||
|
|
use hcie_protocol::{Layer, LayerType, LayerData, BlendMode, effects::{LayerEffect, StrokePosition}};
|
||
|
|
use std::sync::atomic::{AtomicBool};
|
||
|
|
use std::sync::Mutex;
|
||
|
|
|
||
|
|
fn make_shape(shape: &str, canvas: u32) -> Vec<u8> {
|
||
|
|
let mut pixels = vec![0u8; (canvas * canvas * 4) as usize];
|
||
|
|
let cx = canvas / 2;
|
||
|
|
let cy = canvas / 2;
|
||
|
|
|
||
|
|
for y in 0..canvas {
|
||
|
|
for x in 0..canvas {
|
||
|
|
let i = (y * canvas + x) as usize * 4;
|
||
|
|
let inside = match shape {
|
||
|
|
"rect" => {
|
||
|
|
x >= 64 && x < canvas - 64 && y >= 64 && y < canvas - 64
|
||
|
|
}
|
||
|
|
"tall" => {
|
||
|
|
x >= 96 && x < canvas - 96 && y >= 32 && y < canvas - 32
|
||
|
|
}
|
||
|
|
"wide" => {
|
||
|
|
x >= 32 && x < canvas - 32 && y >= 96 && y < canvas - 96
|
||
|
|
}
|
||
|
|
"L" => {
|
||
|
|
// left vertical bar + bottom horizontal bar
|
||
|
|
(x >= 64 && x < 96 && y >= 64 && y < canvas - 64)
|
||
|
|
|| (x >= 64 && x < canvas - 64 && y >= canvas - 96 && y < canvas - 64)
|
||
|
|
}
|
||
|
|
"T" => {
|
||
|
|
// top horizontal + center vertical
|
||
|
|
(y >= 64 && y < 96 && x >= 64 && x < canvas - 64)
|
||
|
|
|| (x >= 112 && x < 144 && y >= 64 && y < canvas - 64)
|
||
|
|
}
|
||
|
|
"cross" => {
|
||
|
|
// vertical + horizontal bar
|
||
|
|
(x >= 112 && x < 144 && y >= 48 && y < canvas - 48)
|
||
|
|
|| (y >= 112 && y < 144 && x >= 48 && x < canvas - 48)
|
||
|
|
}
|
||
|
|
"triangle" => {
|
||
|
|
// filled triangle pointing up
|
||
|
|
let dx = (x as i32 - cx as i32).abs();
|
||
|
|
let top = 64i32;
|
||
|
|
let bottom = (canvas - 64) as i32;
|
||
|
|
if (y as i32) < top || (y as i32) > bottom { false } else {
|
||
|
|
let y_span = ((y as i32) - top) as f32 / (bottom - top) as f32;
|
||
|
|
let half_width_at_y = (canvas - 128) as f32 / 2.0 * (1.0 - y_span);
|
||
|
|
(dx as f32) <= half_width_at_y
|
||
|
|
}
|
||
|
|
}
|
||
|
|
"star" => {
|
||
|
|
// simple 5-point star via radial test
|
||
|
|
let dx = x as f32 - cx as f32;
|
||
|
|
let dy = y as f32 - cy as f32;
|
||
|
|
let r = (dx * dx + dy * dy).sqrt();
|
||
|
|
if r > 96.0 || r < 20.0 { false } else {
|
||
|
|
let angle = dy.atan2(dx);
|
||
|
|
let points = 5.0f32;
|
||
|
|
let sector = (angle * points).cos();
|
||
|
|
let limit = 64.0 + sector * 32.0;
|
||
|
|
r <= limit
|
||
|
|
}
|
||
|
|
}
|
||
|
|
_ => false,
|
||
|
|
};
|
||
|
|
if inside {
|
||
|
|
pixels[i] = 0;
|
||
|
|
pixels[i + 1] = 0;
|
||
|
|
pixels[i + 2] = 0;
|
||
|
|
pixels[i + 3] = 255;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
pixels
|
||
|
|
}
|
||
|
|
|
||
|
|
fn main() {
|
||
|
|
let out_dir = std::path::Path::new("_tmp/stroke_v2_psd_set");
|
||
|
|
std::fs::create_dir_all(out_dir).expect("create output dir");
|
||
|
|
|
||
|
|
let canvas: u32 = 256;
|
||
|
|
|
||
|
|
let shapes: &[(&str, &str)] = &[
|
||
|
|
("rect", "rect"),
|
||
|
|
("tall", "tall"),
|
||
|
|
("wide", "wide"),
|
||
|
|
("L", "L"),
|
||
|
|
("T", "T"),
|
||
|
|
("cross", "cross"),
|
||
|
|
("triangle", "triangle"),
|
||
|
|
("star", "star"),
|
||
|
|
];
|
||
|
|
let positions: &[(StrokePosition, &str)] = &[
|
||
|
|
(StrokePosition::Inside, "inside"),
|
||
|
|
(StrokePosition::Center, "center"),
|
||
|
|
(StrokePosition::Outside, "outside"),
|
||
|
|
];
|
||
|
|
let sizes: &[f32] = &[5.0, 15.0];
|
||
|
|
let fx_opacities: &[f32] = &[0.75, 1.0];
|
||
|
|
let stroke_colors: &[([u8; 4], &str)] = &[
|
||
|
|
([220, 30, 30, 255], "red"),
|
||
|
|
([30, 180, 30, 255], "green"),
|
||
|
|
([30, 60, 220, 255], "blue"),
|
||
|
|
([220, 220, 220, 255], "white"),
|
||
|
|
];
|
||
|
|
|
||
|
|
let total = shapes.len() * positions.len() * sizes.len() * fx_opacities.len() * stroke_colors.len();
|
||
|
|
println!("Generating {} stroke PSDs...", total);
|
||
|
|
|
||
|
|
let mut count = 0usize;
|
||
|
|
let mut errors = 0usize;
|
||
|
|
|
||
|
|
for &(shape_name, shape_tag) in shapes {
|
||
|
|
for &(position, pos_name) in positions {
|
||
|
|
for &size in sizes {
|
||
|
|
for &opacity in fx_opacities {
|
||
|
|
for &(color, color_name) in stroke_colors {
|
||
|
|
count += 1;
|
||
|
|
let filename = format!(
|
||
|
|
"st_{}_{}_sz{}_op{}_{}.psd",
|
||
|
|
shape_tag, pos_name, size as u32,
|
||
|
|
(opacity * 100.0) as u32, color_name
|
||
|
|
);
|
||
|
|
let path = out_dir.join(&filename);
|
||
|
|
if path.exists() {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
let pixels = make_shape(shape_name, canvas);
|
||
|
|
|
||
|
|
let effect = LayerEffect::Stroke {
|
||
|
|
enabled: true,
|
||
|
|
position,
|
||
|
|
blend_mode: "normal".to_string(),
|
||
|
|
color,
|
||
|
|
opacity,
|
||
|
|
size,
|
||
|
|
fill_type: hcie_protocol::effects::StrokeFillType::Color,
|
||
|
|
};
|
||
|
|
|
||
|
|
let layer = Layer {
|
||
|
|
name: "shape".to_string(),
|
||
|
|
layer_type: LayerType::Raster,
|
||
|
|
data: LayerData::Raster,
|
||
|
|
pixels: pixels.clone(),
|
||
|
|
width: canvas,
|
||
|
|
height: canvas,
|
||
|
|
visible: true,
|
||
|
|
opacity: 1.0,
|
||
|
|
blend_mode: BlendMode::Normal,
|
||
|
|
locked: false,
|
||
|
|
dirty: false,
|
||
|
|
id: 1,
|
||
|
|
parent_id: None,
|
||
|
|
styles: vec![],
|
||
|
|
clipping_mask: false,
|
||
|
|
collapsed: false,
|
||
|
|
adjustment: None,
|
||
|
|
mask_pixels: None,
|
||
|
|
mask_bounds: None,
|
||
|
|
mask_default_color: 0,
|
||
|
|
curve_points: vec![],
|
||
|
|
fill_opacity: 1.0,
|
||
|
|
effects: vec![effect],
|
||
|
|
effects_dirty: AtomicBool::new(false),
|
||
|
|
effects_cache: Mutex::new(None),
|
||
|
|
adjustment_raw: None,
|
||
|
|
is_section_divider: false,
|
||
|
|
image_resources_raw: None,
|
||
|
|
};
|
||
|
|
|
||
|
|
match hcie_psd::save_psd(&[layer], canvas, canvas, &pixels, &path) {
|
||
|
|
Ok(()) => {}
|
||
|
|
Err(e) => {
|
||
|
|
eprintln!(" ERROR {}: {}", filename, e);
|
||
|
|
errors += 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if count % 20 == 0 {
|
||
|
|
println!(" [{}/{}] errors={}", count, total, errors);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
println!("Done. {} PSDs generated, {} errors.", count - errors, errors);
|
||
|
|
}
|