Curves: points outside 0..255 are refused, as the message says (#4868)

A point outside 0..255 was moved onto the edge without a word, while the
error for bad points says they must be in 0..255; Photoshop refuses
them too. The error also named the command twice-quoted (`adjustment
`curves``); it is now `curves adjustment`.

Fixes #4867
This commit is contained in:
Gert Pellin - GPE 2026-10-13 18:45:35 +02:00 committed by GitHub
parent 911881880d
commit 0bdaf905cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 16 additions and 3 deletions

View File

@ -73,7 +73,7 @@ pub fn default_for(kind: &str, mode: ColorMode) -> Result<Adjustment> {
/// None or of another kind). `mode` picks the Levels/Curves channel space when neither the params
/// nor the base name one.
pub fn from_params(kind: &str, p: &Value, base: Option<&Adjustment>, mode: ColorMode) -> Result<Adjustment> {
let cmd = format!("adjustment `{kind}`");
let cmd = format!("{kind} adjustment");
if !p.is_object() && !p.is_null() {
return Err(bad(&cmd, "params must be an object"));
}
@ -952,8 +952,10 @@ pub fn parse_curve(cmd: &str, key: &str, v: &Value) -> Result<Vec<CurvePoint>> {
let mut pts = Vec::with_capacity(a.len());
for q in a {
let q = q.as_array().filter(|q| q.len() == 2).ok_or_else(err)?;
let x = q.first().and_then(Value::as_f64).filter(|x| x.is_finite()).ok_or_else(err)?;
let y = q.get(1).and_then(Value::as_f64).filter(|y| y.is_finite()).ok_or_else(err)?;
// Like Photoshop, a point outside 0..255 is refused rather than moved onto the edge.
let level = |v: Option<&Value>| v.and_then(Value::as_f64).filter(|v| (-1e-6..=255.0 + 1e-6).contains(v));
let x = level(q.first()).ok_or_else(err)?;
let y = level(q.get(1)).ok_or_else(err)?;
pts.push(CurvePoint { input: (x as f32).clamp(0.0, 255.0) / 255.0, output: (y as f32).clamp(0.0, 255.0) / 255.0 });
}
pts.sort_by(|a, b| a.input.total_cmp(&b.input));
@ -1212,6 +1214,17 @@ mod tests {
assert!(matches!(a, Adjustment::Levels { master, .. } if master.in_white > master.in_black));
}
#[test]
fn curve_points_outside_0_255_are_refused_as_the_message_says() {
// Photoshop refuses them too ("the settings in the file are invalid"); they used to be
// moved onto the edge silently.
for pts in [json!([[0, 0], [300, 255]]), json!([[0, -1], [255, 255]]), json!([[0, 0]])] {
let e = from_params("curves", &json!({ "points": pts }), None, ColorMode::Rgb).unwrap_err().to_string();
assert!(e.contains("[input, output] pairs in 0..255") && e.contains("`curves adjustment`"), "{e}");
}
assert!(from_params("curves", &json!({"points": [[0, 0], [128.5, 140], [255, 255]]}), None, ColorMode::Rgb).is_ok());
}
fn curves_parts(a: &Adjustment) -> (&[CurvePoint], &[Vec<CurvePoint>; 3]) {
match a {
Adjustment::Curves { master, per_channel, .. } => (master, per_channel),