Files
Khawar Zamman Wani e3ecd73569 feat(frame): rework the Frame tool's selection, handles and placement (#8920)
Thanks @wanikhawar — merging the Frame Tool rework.

Summary: Frame Tool selection/handles/placement now distinguish frame vs image (border / click / double-click and Layers thumbnails), with independent transforms, shape variants, Properties/options-bar stroke and fit commands, Convert to Frame, CLI/MCP `layer.frame.*` commands, native `.pcraft` persistence, and PSD export via editable groups + `pcFr` metadata (not Photoshop's native frame type). Tests cover selection, transforms, fitting, locks/hostile geometry, and native/PSD round-trips.
2026-11-05 12:39:02 +01:00

59 lines
2.3 KiB
Rust

//! 24 MP frame creation: vector commands versus the previous raster-mask construction.
//! `cargo run --release -p photocraft-engine --example bench_frame`
//! The baseline times only the former mask work; command times include history/validation.
use std::hint::black_box;
use std::time::Instant;
use photocraft_doc::LayerMask;
use photocraft_engine::Session;
use photocraft_geom::Rect;
use serde_json::json;
fn raster_mask(rect: Rect, ellipse: bool) -> LayerMask {
let mut mask = LayerMask::hide_all();
if !ellipse {
mask.surface.fill_rect(rect, &[1.0]);
return mask;
}
let (rx, ry) = (f64::from(rect.width()) / 2., f64::from(rect.height()) / 2.);
let (cx, cy) = (f64::from(rect.x0) + rx, f64::from(rect.y0) + ry);
for y in rect.y0..rect.y1 {
for x in rect.x0..rect.x1 {
let covered = (0..16)
.filter(|i| {
let dx = (f64::from(x) + (f64::from(i % 4) + 0.5) / 4. - cx) / rx;
let dy = (f64::from(y) + (f64::from(i / 4) + 0.5) / 4. - cy) / ry;
dx * dx + dy * dy <= 1.
})
.count();
if covered > 0 {
mask.surface.write_pixel(x, y, &[covered as f32 / 16.]);
}
}
}
mask.surface.prune();
mask
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut session = Session::new();
session.execute("file.new", json!({"width":6000,"height":4000}))?;
let rect = Rect::new(100, 100, 5900, 3900);
for (shape, ellipse) in [("rectangle", false), ("ellipse", true)] {
let mut before = f64::INFINITY;
let mut after = f64::INFINITY;
for _ in 0..3 {
let start = Instant::now();
let mask = black_box(raster_mask(rect, ellipse));
before = before.min(start.elapsed().as_secs_f64() * 1000.);
drop(mask);
let start = Instant::now();
session.execute("layer.new.frame", json!({"x":100,"y":100,"width":5800,"height":3800,"shape":shape}))?;
after = after.min(start.elapsed().as_secs_f64() * 1000.);
session.undo();
}
println!("{shape}, 6000x4000 canvas, best of 3: previous mask {before:.3} ms; vector command {after:.3} ms");
}
Ok(())
}