mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 09:57:08 -06:00
Refuse strict ATN export of Select and Mask with brush strokes
Fixes #7539 Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
3a896a5efc
commit
8de2db8764
@@ -2624,6 +2624,15 @@ macro_rules! adjustment_event {
|
||||
|
||||
const RIPPLE_SIZE: &[(&str, &str)] = &[("Sml", "small"), ("Mdm", "medium"), ("Lrg", "large")];
|
||||
|
||||
/// Select and Mask to Photoshop. The descriptor has no field for the Refine Edge Brush, so a
|
||||
/// step with `refineStrokes` can't be exported faithfully.
|
||||
fn refine_edge_to(_: &str, p: &Value) -> Option<Descriptor> {
|
||||
if !pixel_target(p) || p.get("refineStrokes").is_some_and(|v| !v.is_null() && v.as_array().is_none_or(|a| !a.is_empty())) {
|
||||
return None;
|
||||
}
|
||||
fields_to(p, REFINE_EDGE, desc("null"))
|
||||
}
|
||||
|
||||
/// Select and Mask (`refineSelectionEdge`). Smart Radius, decontamination and the output follow
|
||||
/// Photoshop's; the edge itself is PhotoCraft's own matting, so results differ in detail.
|
||||
const REFINE_EDGE: &[F] = &[
|
||||
@@ -3000,7 +3009,17 @@ const EVENTS: &[Event] = &[
|
||||
ev!("Brdr", "border", "Border", "select.modify.border", &[F("Wdth", "radius", Kind::Px)]),
|
||||
ev!("Grow", "grow", "Grow", "select.grow", &[F("Tlrn", "tolerance", Kind::Long)]),
|
||||
ev!("Smlr", "similar", "Similar", "select.similar", &[F("Tlrn", "tolerance", Kind::Long)]),
|
||||
ev!("", "refineSelectionEdge", "Select and Mask", "select.refineEdge", REFINE_EDGE),
|
||||
Event {
|
||||
code: "",
|
||||
name: "refineSelectionEdge",
|
||||
label: "Select and Mask",
|
||||
ids: &["select.refineEdge"],
|
||||
from: |d| match fields_from(d, REFINE_EDGE) {
|
||||
Some(m) => step("select.refineEdge", Value::Object(m)),
|
||||
None => Mapped::Unsupported { harmless: false },
|
||||
},
|
||||
to: refine_edge_to,
|
||||
},
|
||||
// Edit.
|
||||
Event { code: "Fl ", name: "fill", label: "Fill", ids: &["edit.fill", "paint.bucket"], from: fill_from, to: fill_to },
|
||||
ev!("Strk", "stroke", "Stroke", "edit.stroke", STROKE),
|
||||
|
||||
@@ -1387,3 +1387,26 @@ fn function_keys_keep_shift_and_command() {
|
||||
}
|
||||
assert_eq!(super::function_key_name(7, false, false), "F7");
|
||||
}
|
||||
|
||||
/// Photoshop's Select and Mask descriptor as mapped has no field for the Refine Edge Brush,
|
||||
/// so a step with brush strokes must not export as a plain Select and Mask without them.
|
||||
#[test]
|
||||
fn refine_edge_with_brush_strokes_is_not_exported_without_them() {
|
||||
for mode in ["add", "subtract"] {
|
||||
let brushed = Action {
|
||||
name: "Probe".into(),
|
||||
steps: vec![("select.refineEdge".into(), json!({"radius": 1, "refineStrokes": [{"points": [[5, 4]], "size": 4, "mode": mode}]}))],
|
||||
..Default::default()
|
||||
};
|
||||
assert!(export("Audit", &[&brushed], &[], false).is_err(), "{mode}");
|
||||
let (_, skipped) = export("Audit", &[&brushed], &[], true).unwrap();
|
||||
assert_eq!(skipped, vec![Skipped { action: "Probe".into(), step: 0, id: "select.refineEdge".into() }]);
|
||||
}
|
||||
for params in [json!({"radius": 1}), json!({"radius": 1, "refineStrokes": []}), json!({"radius": 1, "refineStrokes": null})] {
|
||||
let plain = Action { name: "Plain".into(), steps: vec![("select.refineEdge".into(), params)], ..Default::default() };
|
||||
let (bytes, skipped) = export("Audit", &[&plain], &[], false).unwrap();
|
||||
assert!(skipped.is_empty());
|
||||
let imported = import(&bytes).unwrap();
|
||||
assert_eq!(imported.actions[0].steps, vec![("select.refineEdge".to_string(), json!({"radius": 1}))]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user