How to mix two int colors correctly

流过昼夜 提交于 2021-01-27 05:32:01

问题


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?


回答1:


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.




回答2:


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 );
}



回答3:


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);
}



回答4:


@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.




回答5:


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);
}



回答6:


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);
}


来源:https://stackoverflow.com/questions/19398238/how-to-mix-two-int-colors-correctly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!