如何正确混合两种整型颜色

5

我正在尝试混合两种以整数编码的颜色。这是我的小函数:

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

一切似乎都运作正常,但某些参数却会产生错误的颜色。例如:

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

我的猜测是,使用浮点数比率相乘或转换类型可能是有问题的原因,但我无法确定出错在哪里...
那么在Java中合并两种颜色的正确方法是什么?

那么为什么不使用 ByteBuffer 来获取 byte[] 数组,并从 byte[] 数组生成 int 值呢? - user2511414
为了提高性能。如果我说错了,请纠正我,但是创建原语比实例化对象要快得多。这段代码必须每秒运行几百次。 - Maybe Julius
我使用位运算找到的最佳答案在这里:https://dev59.com/XU3Sa4cB1Zd3GeqPsyck#4787257 - Dr. Andrew Burnett-Thompson
6个回答

5
我的猜测是将强制类型转换为整型应该在加法之后进行。像这样:
int a = (int)((aA * iRatio) + (bA * ratio));

我建议在使用变量时遵循Java命名规范。只有常量应该采用全大写的方式表述。

帮了大忙。我真蠢,为什么我没像那样转换类型 :/ - Maybe Julius
顺便问一下,你会给变量起什么名字?我知道大写是为了表示最终静态变量,但这样做非常易读和逻辑清晰... - Maybe Julius
也许可以使用类似于“redHex”、“greenHex”和“blueHex”的命名,因为它们代表了相应颜色的十六进制值。 :) - dARKpRINCE

5

感谢JuliusB和dARKpRINCE。我已将其改编为接受java.awt.Color,修复了强制转换并将变量重命名得更像Java标准。它运行良好。再次感谢!

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

4

感谢JuliusB、dARKpRINCE和bmauter的帮助。
根据你们的输入,我创建了以下函数,它可以将n种颜色以相等比例混合:

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

1
最简单的答案可能是:

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

1

@dARKpRINCE的回答是正确的,但我有一些小建议:

  1. 由于您的函数不依赖任何对象字段,因此应将其设置为静态。

  2. 提取颜色的alpha组件时,可以通过执行x >>> 24而不是(x >> 24) & 0xFF来节省一次操作。

  3. 任何形式为:

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

    都可以写成:

    a + (b - a) * ratio
    

    这样可以减少需要的乘法数量。


0

如果有人对在LibGDX中混合颜色感兴趣(基于上面的解决方案,但专为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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接