Issue
I'm trying to figure out how to replicate how JavaFX gradients are interpolated/blended when transparency is involved. If I try a naive approach of interpolating between the colors using the built in Color#interpolate
method, I get quite a different result. as you can see here:
Both gradients are a simple [red @ 0%, rgba(0, 0, 255, 0) @ 50%, green @ 100%], however the blue isn't visible at all in the JavaFX gradient (top) and is very visible in the naive approach (bottom). What is the actual formula for how colors are interpolated in gradients, and where can it be found (i.e. what method in what class)?
Here's an example showing semi-transparency (going from rgba(255, 0, 0, 1) to rgba(0, 0, 255, 0.5)):
The graph shows the sampled components of the resulting color (white represents opacity). Notice how the color channels interpolate non-linearly. I'm looking for the equation that would adjust the curve as shown.
Solution
Credit goes to @fabian for this answer
When rendering gradients, Prism uses pre-multiplied colors.
To achieve the same result, you can manually premultiply like so:
public Color interpolatePreMultiplied(double ratio, Color origin, Color target) {
var opacity = lerp(ratio, origin.getOpacity(), target.getOpacity());
return opacity == 0.0 ? Color.TRANSPARENT : Color.color(
lerp(ratio, origin.getRed() * origin.getOpacity(), target.getRed() * target.getOpacity()) / opacity,
lerp(ratio, origin.getRed() * origin.getOpacity(), target.getRed() * target.getOpacity()) / opacity,
lerp(ratio, origin.getBlue() * origin.getOpacity(), target.getBlue() * target.getOpacity()) / opacity,
opacity
);
}
public double lerp(double ratio, double origin, double target) {
return origin + (target - origin) * ratio;
}
Or you can use some utility conversions:
public Color interpolatePreMultiplied(double ratio, Color origin, Color target) {
var po = toPreMutliplied(origin);
var pt = toPreMultiplied(target);
return toStraightAlpha(po.interpolate(pt, ratio));
}
public Color toPreMultiplied(Color color) {
return Color.color(
color.getRed() * color.getOpacity(),
color.getGreen() * color.getOpacity(),
color.getBlue() * color.getOpacity(),
color.getOpacity()
);
}
public Color toStraightAlpha(Color color) {
return color.getOpacity() == 0.0 ? Color.TRANSPARENT : Color.color(
color.getRed() / color.getOpacity(),
color.getGreen() / color.getOpacity(),
color.getBlue() / color.getOpacity(),
color.getOpacity()
);
}
Answered By - Ruckus T-Boom