mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-19 02:05:19 +02:00
* Add the Remove tool with non-local patch completion Tool::Remove (J flyout) paints a hole that paint.remove fills from its surroundings as a background job, in one undo step. A stroke around an object also removes what it encloses; the selection limits the hole. The fill (photocraft_algo::nonlocal) follows Newson, Almansa, Gousseau and Pérez (IPOL 2017): texture features in the patch distance, onion-peel initialisation, match fields upsampled between levels and a best-patch final copy, plus gradient-domain hiding of edge and interior seams. Sparse holes are filled in tiles. inpaint::complete (Content-Aware Fill, Spot Healing, Content-Aware Move) is unchanged. On 20 holes in public-domain photos (remove_quality) the fill keeps 0.89 of the texture it replaces, against 0.54 with the Wexler/PatchMatch completion. 24 MP (remove_bench): 100 px ring 120 ms, 1000 px ring 2.5 s. * Translate the Remove tool's label and tighten comments Adds "Remove Tool" to every UI catalog, following each one's pattern for tool names and its existing word for Remove. Corrects comments that no longer matched the code: the benchmark's description, the tile and margin minimums, and the reasons given for two parameters. * Put the Remove tool first in the J group The J flyout's slot now shows Remove until another J tool is picked, and J from another group selects it; repeated J goes on to Spot Healing Brush, Healing Brush, Patch, Content-Aware Move and Red Eye. --------- Co-authored-by: Brandon Thomas <echelon@gmail.com>
36 lines
1.7 KiB
Rust
36 lines
1.7 KiB
Rust
//! Timing: the Remove Tool (`paint.remove`) on a 6000×4000 (24 MP) document, through the full
|
||
//! command path (`Session::execute`: window read, hole, fill, write, undo snapshot).
|
||
//! `cargo run --release -p photocraft-engine --example remove_bench [depth]`
|
||
//!
|
||
//! Rings around objects of 100, 300 and 1000 px (one completion each) and a 3000 px thin line
|
||
//! (completed tile by tile), each undone before the next.
|
||
use std::time::Instant;
|
||
|
||
use photocraft_engine::Session;
|
||
use serde_json::json;
|
||
|
||
fn ring(cx: f64, cy: f64, r: f64) -> Vec<[f64; 2]> {
|
||
(0..=96).map(|i| f64::from(i) / 96.0 * std::f64::consts::TAU).map(|t| [cx + r * t.cos(), cy + r * t.sin()]).collect()
|
||
}
|
||
|
||
fn main() {
|
||
let depth: u64 = std::env::args().nth(1).and_then(|v| v.parse().ok()).unwrap_or(8);
|
||
let mut s = Session::new();
|
||
s.execute("file.new", json!({"width": 6000, "height": 4000, "depth": depth})).unwrap();
|
||
s.execute("filter.render.clouds", json!({"seed": 3})).unwrap();
|
||
s.execute("filter.noise.addNoise", json!({"amount": 8, "seed": 1})).unwrap();
|
||
println!("document 6000x4000, {depth}-bit RGB");
|
||
let cases: Vec<(String, serde_json::Value)> = vec![
|
||
("ring 100 px".into(), json!({"points": ring(3000.0, 2000.0, 60.0), "size": 16})),
|
||
("ring 300 px".into(), json!({"points": ring(3000.0, 2000.0, 160.0), "size": 24})),
|
||
("ring 1000 px".into(), json!({"points": ring(3000.0, 2000.0, 510.0), "size": 32})),
|
||
("line 3000 px, 4 px wide".into(), json!({"points": [[1500, 1000], [4500, 3000]], "size": 2})),
|
||
];
|
||
for (name, p) in cases {
|
||
let t = Instant::now();
|
||
s.execute("paint.remove", p).unwrap();
|
||
println!("{name:<26} {:>8.1?}", t.elapsed());
|
||
s.execute("edit.undo", json!({})).unwrap();
|
||
}
|
||
}
|