I have a drawable that represents a white circle with anti-aliasing that needs to be coloured in runtime.
Here's a scaled image of it:
As you can see, there are few semi-transparent pixels.
If I try to color them the fast way (which takes roughly 6-9 ms for 192x192 px drawable), I will have troubles with semi-transparent.
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);
}
Here's the drawable after being coloured by setting ColorFilter
:
If I do it using brute-force algorithm, it takes roughly 100ms to go over all pixels and apply alpha parameter to a new color:
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);
}
}
}
The resulting image of 2nd algorithm:
Is there anything I could do with 1st algorithm that it will result in a better quality colouring without sacrificing the performance?
Turns out, the 2nd algorithm had the right idea, but poor implementation. The key was to batch pixel retrieval and set, i.e. using pixel array:
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);
}
This batching has reduced time to change color of 200x200 image from 100-120ms to 1-4 ms :)