Ruckus T-Boom
Ruckus T-Boom

Reputation: 4796

How are colors interpolated in a JavaFX gradient?

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:

enter image description 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)):

enter image description here

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

Answers (1)

Ruckus T-Boom
Ruckus T-Boom

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

Related Questions