mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-03 06:40:20 -06:00
Keep arbitrary canvas rotation interpolation in ATN export (#7630)
Fixes #7504 Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
cbe6c4e131
commit
fe64e5a2bd
@@ -2135,7 +2135,11 @@ fn rotate_from(d: &Descriptor) -> Mapped {
|
||||
} else if targets_layer(d) {
|
||||
return Mapped::Unsupported { harmless: false };
|
||||
} else {
|
||||
return step("image.rotation.arbitrary", json!({"angle": angle.abs(), "direction": if angle < 0.0 { "ccw" } else { "cw" }}));
|
||||
let mut p = json!({"angle": angle.abs(), "direction": if angle < 0.0 { "ccw" } else { "cw" }});
|
||||
if let Some(i) = get_enum(d, "Intr").and_then(|i| lookup(INTERPOLATION, &i)).filter(|i| matches!(*i, "bicubic" | "bilinear" | "nearest")) {
|
||||
p["interpolation"] = i.into();
|
||||
}
|
||||
return step("image.rotation.arbitrary", p);
|
||||
};
|
||||
none(if targets_layer(d) { layer } else { doc })
|
||||
}
|
||||
@@ -2155,7 +2159,20 @@ fn rotate_to(id: &str, p: &Value) -> Option<Descriptor> {
|
||||
if layer {
|
||||
d = d.with("null", target_ref("Lyr "));
|
||||
}
|
||||
Some(d.with("Angl", ang(angle)))
|
||||
d = d.with("Angl", ang(angle));
|
||||
if id == "image.rotation.arbitrary"
|
||||
&& let Some(i) = p.get("interpolation")
|
||||
{
|
||||
// The command resamples with nearest, bilinear or bicubic; any other value can't be
|
||||
// carried faithfully, so refuse rather than replay with a different interpolation.
|
||||
let i = match i.as_str()? {
|
||||
"nearestNeighbor" => "nearest",
|
||||
i @ ("bicubic" | "bilinear" | "nearest") => i,
|
||||
_ => return None,
|
||||
};
|
||||
d = d.with("Intr", enm("Intp", lookup_back(INTERPOLATION, i)?));
|
||||
}
|
||||
Some(d)
|
||||
}
|
||||
|
||||
fn flip_from(d: &Descriptor) -> Mapped {
|
||||
|
||||
@@ -1199,6 +1199,25 @@ fn fractional_translation_only_transform_keeps_offset_and_interpolation() {
|
||||
assert_eq!(steps[0].1.get("interpolation"), Some(&json!("bilinear")));
|
||||
}
|
||||
|
||||
/// Arbitrary canvas rotation must keep its interpolation through a strict export: dropping it
|
||||
/// silently replays the rotation with the default bicubic resampling.
|
||||
#[test]
|
||||
fn arbitrary_rotation_keeps_interpolation_through_atn() {
|
||||
for params in [
|
||||
json!({"angle": 17, "direction": "ccw", "interpolation": "nearest"}),
|
||||
json!({"angle": 17, "direction": "ccw", "interpolation": "bilinear"}),
|
||||
json!({"angle": 17, "direction": "ccw", "interpolation": "bicubic"}),
|
||||
json!({"angle": 17, "direction": "ccw"}),
|
||||
] {
|
||||
let a = Action { name: "Rot".into(), steps: vec![("image.rotation.arbitrary".into(), params.clone())], ..Default::default() };
|
||||
let (bytes, skipped) = export("Rot", &[&a], &[], false).unwrap();
|
||||
assert!(skipped.is_empty());
|
||||
let imported = import(&bytes).unwrap();
|
||||
assert!(imported.unsupported.is_empty());
|
||||
assert_eq!(imported.actions[0].steps, vec![("image.rotation.arbitrary".to_string(), params)]);
|
||||
}
|
||||
}
|
||||
|
||||
/// A filter recorded against a layer mask must not export as a plain filter that edits the
|
||||
/// layer's pixels on replay: Photoshop's descriptor has no field for PhotoCraft's `target`.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user