如何更改具有抗锯齿功能的位图的颜色?

How to change color of a Bitmap that has anti-aliasing?

我有一个 drawable 代表一个带有抗锯齿的白色圆圈,需要在运行时着色。

这是它的缩放图像:

如您所见,几乎没有半透明像素。

如果我尝试以快速方式为它们着色(对于 192x192 像素的可绘制对象大约需要 6-9 毫秒),我将遇到半透明的问题。

public static void changeBitmapColor(@NonNull Bitmap src, @ColorInt int newColor) {
    Paint paint = new Paint();
    ColorFilter filter = new PorterDuffColorFilter(newColor, PorterDuff.Mode.SRC_IN);
    paint.setColorFilter(filter);

    Canvas canvas = new Canvas(src);
    canvas.drawBitmap(src, 0, 0, paint);
}

这是通过设置 ColorFilter:

着色后的可绘制对象

如果我使用蛮力算法执行此操作,遍历所有像素并将 alpha 参数应用于新颜色大约需要 100 毫秒:

public static void changeBitmapColor2(@NonNull Bitmap src, @ColorInt int newColor) {
    int w = src.getWidth();
    int h = src.getHeight();

    for (int x = 0; x < w; x++) {
        for (int y = 0; y < h; y++) {
            int color = src.getPixel(x, y);

            int alpha = color >>> 24;
            if (alpha == 0) {
                continue;
            }
            color = (newColor & 0x00ffffff) | (alpha << 24);
            src.setPixel(x, y, color);
        }
    }
}

第二种算法的结果图像:

我可以用第一种算法做些什么来在不牺牲性能的情况下获得更好的着色质量?

事实证明,第二种算法的想法是正确的,但执行起来很糟糕。关键是批量像素检索和设置,即使用像素数组:

public static void changeBitmapColor2(@NonNull Bitmap src, @ColorInt int newColor) {
    int width = src.getWidth();
    int height = src.getHeight();

    int[] pixels = new int[height * width];
    src.getPixels(pixels, 0, width, 0, 0, width, height);

    int newColorNoAlpha = newColor & 0x00ffffff;
    for (int i = 0; i < width; i++) {
        for (int j = 0; j < height; j++) {
            int currentPixel = i * width + j;

            int color = pixels[currentPixel];

            int alpha = color >>> 24;
            if (alpha == 0) {
                continue;
            }
            pixels[currentPixel] = newColorNoAlpha | (alpha << 24);
        }
    }
    src.setPixels(pixels, 0, width, 0, 0, width, height);
}

此批处理将 200x200 图像的颜色更改时间从 100-120 毫秒减少到 1-4 毫秒:)