Reputation: 4796
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.
Upvotes: 0
Views: 2052
Reputation: 4796
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(Color a, Color b, double t) {
var o = lerp(a.getOpacity(), b.getOpacity(), t);
return o == 0.0 ? Color.TRANSPARENT : Color.color(
lerp(a.getRed() * a.getOpacity(), b.getRed() * b.getOpacity(), t) / o,
lerp(a.getGreen() * a.getOpacity(), b.getGreen() * b.getOpacity(), t) / o,
lerp(a.getBlue() * a.getOpacity(), b.getBlue() * b.getOpacity(), t) / o,
o
);
}
public double lerp(double a, double b, double t) {
return a + (b - a) * t;
}
Or you can use some utility conversions:
public Color interpolatePreMultiplied(Color a, Color b, double t) {
var pa = toPreMutliplied(a);
var pb = toPreMultiplied(b);
return toStraightAlpha(pa.interpolate(pb, t));
}
public Color toPreMultiplied(Color c) {
return Color.color(
c.getRed() * c.getOpacity(),
c.getGreen() * c.getOpacity(),
c.getBlue() * c.getOpacity(),
c.getOpacity()
);
}
public Color toStraightAlpha(Color c) {
return c.getOpacity() == 0.0 ? Color.TRANSPARENT : Color.color(
c.getRed() / c.getOpacity(),
c.getGreen() / c.getOpacity(),
c.getBlue() / c.getOpacity(),
c.getOpacity()
);
}
Upvotes: 2