mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-14 11:16:53 -04:00
Opacity and Fill are fractions in layer.setProps but percentages in the Layer Style commands, and a value above 1 was clamped silently: 55 gave an opaque layer. Out-of-range or non-numeric values now fail with an error that names the range, and the layer is left alone.
This commit is contained in:
1 parent
15f7618504
commit
7e28900df3
1 file changed
+41
-4
@@ -681,6 +681,16 @@ fn build() -> Vec<CommandSpec> {
|
||||
has_layer,
|
||||
|s, p| {
|
||||
let id = layer_param(s, p)?;
|
||||
// Opacity and Fill are fractions here, while the Layer Style commands take
|
||||
// percentages: a value outside 0..1 is refused, never clamped to 100 % (#5242).
|
||||
let unit = |key: &str| -> Result<Option<f32>> {
|
||||
let Some(v) = p.get(key) else { return Ok(None) };
|
||||
match v.as_f64() {
|
||||
Some(x) if (0.0..=1.0).contains(&x) => Ok(Some(x as f32)),
|
||||
_ => Err(bad("layer.setProps", format!("{key} must be a number in 0..1 (1 = 100 %), got {v}"))),
|
||||
}
|
||||
};
|
||||
let (opacity, fill) = (unit("opacity")?, unit("fill")?);
|
||||
let only_visibility =
|
||||
p.as_object().is_some_and(|o| o.contains_key("visible") && o.keys().all(|k| matches!(k.as_str(), "layer" | "visible" | "coalesce")));
|
||||
let label = if only_visibility { "Layer Visibility" } else { "Layer Properties" };
|
||||
@@ -702,11 +712,11 @@ fn build() -> Vec<CommandSpec> {
|
||||
if let Some(v) = p.get("visible").and_then(Value::as_bool) {
|
||||
l.visible = v;
|
||||
}
|
||||
if let Some(v) = p.get("opacity").and_then(Value::as_f64) {
|
||||
l.opacity = (v as f32).clamp(0.0, 1.0);
|
||||
if let Some(v) = opacity {
|
||||
l.opacity = v;
|
||||
}
|
||||
if let Some(v) = p.get("fill").and_then(Value::as_f64) {
|
||||
l.fill_opacity = (v as f32).clamp(0.0, 1.0);
|
||||
if let Some(v) = fill {
|
||||
l.fill_opacity = v;
|
||||
}
|
||||
if let Some(v) = p.get("blend").and_then(Value::as_str) {
|
||||
l.blend = blend_from_str(v).ok_or_else(|| bad("layer.setProps", format!("unknown blend mode `{v}`")))?;
|
||||
@@ -1554,3 +1564,30 @@ mod registry_tests {
|
||||
assert_eq!(registry.specs.len(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod set_props_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn opacity_and_fill_outside_zero_to_one_are_refused_not_clamped() {
|
||||
// #5242: `55` (a percentage, as the Layer Style commands take) used to become 100 %.
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", json!({"width": 8, "height": 8})).unwrap();
|
||||
s.execute("layer.new.layer", json!({})).unwrap();
|
||||
s.execute("layer.setProps", json!({"opacity": 0.55, "fill": 0.4})).unwrap();
|
||||
let layer = |s: &Session| {
|
||||
let d = s.active().unwrap();
|
||||
let l = d.doc.layer(d.active_layer.unwrap()).unwrap();
|
||||
(l.opacity, l.fill_opacity)
|
||||
};
|
||||
assert_eq!(layer(&s), (0.55, 0.4));
|
||||
for bad in [json!({"opacity": 55}), json!({"opacity": -0.1}), json!({"fill": 1.5}), json!({"opacity": "50%"}), json!({"opacity": 0.5, "fill": 40})] {
|
||||
let e = s.execute("layer.setProps", bad.clone()).unwrap_err().to_string();
|
||||
assert!(e.contains("0..1"), "{bad}: {e}");
|
||||
assert_eq!(layer(&s), (0.55, 0.4), "{bad} left the layer alone");
|
||||
}
|
||||
s.execute("layer.setProps", json!({"opacity": 0, "fill": 1})).unwrap();
|
||||
assert_eq!(layer(&s), (0.0, 1.0));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user