Integration: keep Wave action steps with more generators than the renderer draws (#10629)

A Wave step recorded with NmbG above 64 (photocraft_algo::MAX_WAVE_GENERATORS)
mapped to filter.distort.wave, but the renderer clamps to 64 generators, so the
step played differently from Photoshop. Add generators 1..MAX_WAVE_GENERATORS to
WAVE_COMMAND_LIMITS: such a step (e.g. NmbG 65, 999, 1000000) now stays a kept
Photoshop step on import, and {"generators": 65} is refused on export, as the
PSD smart-filter mapper does.

Co-authored-by: hectorviov <1501713+hectorviov@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed 2026-11-02 01:58:01 +01:00
1 parent 244082fb2c
commit 363e0f5e89
2 files changed
+24 -10

No files matched your search

@@ -152,10 +152,16 @@ const WAVE: &[Key] = &[
const WAVE_UNDEFINED: &[(&str, &str)] = &[("RptE", "repeat"), ("WrpA", "wrap")];
/// `filter.distort.wave`'s own limits where they are narrower than Photoshop's 1..999. A value
/// Photoshop plays but the command rejects keeps the step on import and refuses it on export,
/// rather than mapping a step that fails when it plays.
const WAVE_COMMAND_LIMITS: &[(&str, f64, f64)] = &[("wavelengthMin", 1.0, 998.0), ("wavelengthMax", 2.0, 999.0), ("amplitudeMin", 1.0, 998.0)];
/// `filter.distort.wave`'s own limits where the command plays narrower than Photoshop's 1..999
/// (the renderer draws at most [`photocraft_algo::MAX_WAVE_GENERATORS`] generators). A value
/// Photoshop plays but the command rejects or plays differently keeps the step on import and
/// refuses it on export, rather than mapping a step that fails or differs when it plays.
const WAVE_COMMAND_LIMITS: &[(&str, f64, f64)] = &[
("generators", 1.0, photocraft_algo::MAX_WAVE_GENERATORS as f64),
("wavelengthMin", 1.0, 998.0),
("wavelengthMax", 2.0, 999.0),
("amplitudeMin", 1.0, 998.0),
];
/// Whether every value `p` sets for a key of [`WAVE_COMMAND_LIMITS`] is within the command's limits.
fn wave_command_takes(p: &Map<String, Value>) -> bool {
+14 -6
View File
@@ -3258,12 +3258,20 @@ fn distort_filter_steps_load_clamp_play_and_save_back() {
assert_eq!(one_step(distort("Wave", desc("null").with("NmbG", D::Integer(0)))).1["generators"], 1);
}
/// Photoshop clamps Wave's number of generators to 1..999 when it plays, so a recorded count far
/// above it loads as 999, as the existing 0 loads as 1 (#10605).
/// PhotoCraft's renderer draws at most `MAX_WAVE_GENERATORS` (64) generators, so a Wave step
/// recorded with more keeps the Photoshop step on import and is refused on export, as the PSD
/// smart-filter mapper does, rather than mapping a step that plays differently (#10605).
#[test]
fn wave_generators_above_photoshops_limit_load_clamped() {
let p = one_step(distort("Wave", desc("null").with("NmbG", D::Integer(1_000_000)))).1;
assert_eq!(p["generators"], 999, "{p}");
fn wave_generators_past_what_photocraft_draws_keep_the_step() {
assert_eq!(photocraft_algo::MAX_WAVE_GENERATORS, 64);
let p = one_step(distort("Wave", desc("null").with("NmbG", D::Integer(64)))).1;
assert_eq!(p["generators"], 64, "{p}");
for n in [65, 999, 1_000_000] {
let m = distort("Wave", desc("null").with("NmbG", D::Integer(n)));
assert!(matches!(m, Mapped::Unsupported { harmless: false }), "NmbG {n}");
}
assert!(export_item("filter.distort.wave", &json!({"generators": 65}), "S").is_none());
assert!(export_item("filter.distort.wave", &json!({"generators": 64}), "S").is_some());
}
/// What the commands cannot play keeps the Photoshop step: an unknown enumeration value, a Wave
@@ -3341,7 +3349,7 @@ fn distort_filter_steps_outside_photoshops_range_are_refused() {
("filter.distort.pinch", json!({"amount": -100})),
("filter.distort.twirl", json!({"angle": 999})),
("filter.distort.zigZag", json!({"ridges": 0})),
("filter.distort.wave", json!({"generators": 999, "wavelengthMin": 1})),
("filter.distort.wave", json!({"generators": 64, "wavelengthMin": 1})),
] {
assert!(export_item(id, &params, "S").is_some(), "{id} {params} refused");
}