Plug-ins: a filter's gradient stop plates are clamped into range like its other colors

A filter's or importer's output clamps each gradient stop's offset,
opacity, midpoint, tint and color, but kept its four process plates as
given, so a plate below 0, above 1 or infinite was stored in the
document. Each plate is now clamped to 0-1: a value that is not a
number becomes 0, and an infinite one becomes 0 or 1 by its sign.
Painted live effects still refuse such a stop.

Docs: docs/plugins.md, docs/target-app-parity.md.
This commit is contained in:
dotCooCoo
2026-11-12 16:04:43 -08:00
parent b4505a76de
commit 4bb1df4929
4 changed files with 41 additions and 4 deletions
+10 -2
View File
@@ -579,6 +579,11 @@ fn unit(v: f32) -> f32 {
v.clamp(0.0, 1.0)
}
/// Process plate `v` in 0..1: NaN is 0, infinities go to the nearest end.
fn plate(v: f32) -> f32 {
if v.is_nan() { 0.0 } else { unit(v) }
}
/// `c` with its components in range.
pub(crate) fn clamp_color(c: Color) -> Color {
match c {
@@ -589,8 +594,8 @@ pub(crate) fn clamp_color(c: Color) -> Color {
}
}
/// A paint from plug-in output (`null`: none), with colours, opacities and offsets clamped and
/// gradient and pattern placements checked.
/// A paint from plug-in output (`null`: none), with colors, process plates, opacities and
/// offsets clamped and gradient and pattern placements checked.
fn decode_paint(v: &Value, at: &str) -> Result<Paint> {
if v.is_null() {
return Ok(Paint::None);
@@ -613,6 +618,9 @@ fn decode_paint(v: &Value, at: &str) -> Result<Paint> {
s.midpoint = s.midpoint.clamp(0.13, 0.87);
s.tint = unit(s.tint);
s.color = clamp_color(s.color);
if let Some(plates) = &mut s.plates {
*plates = plates.map(plate);
}
}
stops.sort_by(|a, b| a.offset.total_cmp(&b.offset));
g.angle = g.angle.rem_euclid(360.0);