سؤال

I'm trying to blend two colors that are coded as integers. Here is my little function:

int blend (int a, int b, float ratio) {
    if (ratio > 1f) {
        ratio = 1f;
    } else if (ratio < 0f) {
        ratio = 0f;
    }
    float iRatio = 1.0f - ratio;

    int aA = (a >> 24 & 0xff);
    int aR = ((a & 0xff0000) >> 16);
    int aG = ((a & 0xff00) >> 8);
    int aB = (a & 0xff);

    int bA = (b >> 24 & 0xff);
    int bR = ((b & 0xff0000) >> 16);
    int bG = ((b & 0xff00) >> 8);
    int bB = (b & 0xff);

    int A = ((int)(aA * iRatio) + (int)(bA * ratio));
    int R = ((int)(aR * iRatio) + (int)(bR * ratio));
    int G = ((int)(aG * iRatio) + (int)(bG * ratio));
    int B = ((int)(aB * iRatio) + (int)(bB * ratio));

    return A << 24 | R << 16 | G << 8 | B;
}

Everything seems to work fine, but certain arguments produce wrong colors. For example:

    int a = 0xbbccdd;
    int b = 0xbbccdd;
    int c = blend(a, b, 0.5f); // gives 0xbaccdc, although it should be 0xbbccdd

My guess is that either multiplication by float ratios or casting are to blame here, but I can't figure out what's wrong with them...

So what's the correct way to blend two colors in java?

هل كانت مفيدة؟

المحلول

My guess is that the casting to int should be done after the addition. Like this

int a = (int)((aA * iRatio) + (bA * ratio));

I would also suggest using Java naming conventions when using variables. Only constants should be caps.

نصائح أخرى

Thanks JuliusB and dARKpRINCE. I've adapted it to accept java.awt.Color, fixed the cast and renamed the variables somewhat more like the Java standard. It works well. Thanks again!

Color blend( Color c1, Color c2, float ratio ) {
    if ( ratio > 1f ) ratio = 1f;
    else if ( ratio < 0f ) ratio = 0f;
    float iRatio = 1.0f - ratio;

    int i1 = c1.getRGB();
    int i2 = c2.getRGB();

    int a1 = (i1 >> 24 & 0xff);
    int r1 = ((i1 & 0xff0000) >> 16);
    int g1 = ((i1 & 0xff00) >> 8);
    int b1 = (i1 & 0xff);

    int a2 = (i2 >> 24 & 0xff);
    int r2 = ((i2 & 0xff0000) >> 16);
    int g2 = ((i2 & 0xff00) >> 8);
    int b2 = (i2 & 0xff);

    int a = (int)((a1 * iRatio) + (a2 * ratio));
    int r = (int)((r1 * iRatio) + (r2 * ratio));
    int g = (int)((g1 * iRatio) + (g2 * ratio));
    int b = (int)((b1 * iRatio) + (b2 * ratio));

    return new Color( a << 24 | r << 16 | g << 8 | b );
}

Thanks JuliusB, dARKpRINCE and bmauter.
Based on your input I've created the following function which blends n Colors with equal ratio:

public static Color blend(Color... c) {
    if (c == null || c.length <= 0) {
        return null;
    }
    float ratio = 1f / ((float) c.length);

    int a = 0;
    int r = 0;
    int g = 0;
    int b = 0;

    for (int i = 0; i < c.length; i++) {
        int rgb = c[i].getRGB();
        int a1 = (rgb >> 24 & 0xff);
        int r1 = ((rgb & 0xff0000) >> 16);
        int g1 = ((rgb & 0xff00) >> 8);
        int b1 = (rgb & 0xff);
        a += ((int) a1 * ratio);
        r += ((int) r1 * ratio);
        g += ((int) g1 * ratio);
        b += ((int) b1 * ratio);
    }

    return new Color(a << 24 | r << 16 | g << 8 | b);
}

@dARKpRINCE's answer is correct, but I have a few minor tips:

  1. Your function should be static, since it doesn't depend on any object fields.

  2. You can save one operation when extracting a color's alpha component by doing x >>> 24 instead of (x >> 24) & 0xFF.

  3. Anything of the form:

    (a * (1 - ratio)) + (b * ratio)
    

    can be written as:

    a + (b - a) * ratio
    

    which halves the number of multiplications you need.

The simplest answer might be:

public static Color mixColors(Color... colors) {
    float ratio = 1f / ((float) colors.length);
    int r = 0, g = 0, b = 0, a = 0;
    for (Color color : colors) {
        r += color.getRed() * ratio;
        g += color.getGreen() * ratio;
        b += color.getBlue() * ratio;
        a += color.getAlpha() * ratio;
    }
    return new Color(r, g, b, a);
}

In case anyone is interested in blending colors in LibGDX (based on the solution above, but tailored for LibGDX API):

static Color blend( Color c1, Color c2, float ratio ) {
    if ( ratio > 1f ) ratio = 1f;
    else if ( ratio < 0f ) ratio = 0f;
    float iRatio = 1.0f - ratio;

    int i1 = Color.argb8888(c1);
    int i2 = Color.argb8888(c2);

    int a1 = (i1 >> 24 & 0xff);
    int r1 = ((i1 & 0xff0000) >> 16);
    int g1 = ((i1 & 0xff00) >> 8);
    int b1 = (i1 & 0xff);

    int a2 = (i2 >> 24 & 0xff);
    int r2 = ((i2 & 0xff0000) >> 16);
    int g2 = ((i2 & 0xff00) >> 8);
    int b2 = (i2 & 0xff);

    int a = (int)((a1 * iRatio) + (a2 * ratio));
    int r = (int)((r1 * iRatio) + (r2 * ratio));
    int g = (int)((g1 * iRatio) + (g2 * ratio));
    int b = (int)((b1 * iRatio) + (b2 * ratio));

    return new Color(r << 24 | g << 16 | b << 8 | a);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top