Question

I am converting an RGBA image to RGB. Using naive array copy:

for (int j=0, i=0; i<argbBytes.length; i++){
    if (i%4 < 3) {
        thumbRGB888[j++] = argbBytes[i];
    }
}

But, as expected, it is ultra slow (in comparison to System.arraycopy() method), especially on an Android device. Is there a trick to do it faster?

Était-ce utile?

La solution

Use two indexes and System.arraycopy(), copying 3 elements each time:

for (int i = 0, j = 0; i < argbBytes.length; i += 4, j += 3)
{
    System.arraycopy(argbBytes, i, thumbRGB888, j, 3);
}

Should be significantly faster. Not only does it get rid of the modulo and comparison, System.arraycopy() is implemented with native code, relying on memcpy which is going to be faster than individual assignments.

Autres conseils

Instead of

if (i%4 < 3)

do

for (int j=0, i=0; i<argbBytes.length; i++){
   thumbRGB888[j++] = argbBytes[i++];
   thumbRGB888[j++] = argbBytes[i++];
   thumbRGB888[j++] = argbBytes[i++];
}

This solution is far faster, because modulo operation and compare are more costfull.

You should avoid the if statement, it does 2 operations.
Start with i=0, i%4<3 means: 0,1,2, 4,5,6, 8,9,10. Here's how I'd do:

int div = 4;
int n = argbBytes.length - argbBytes.length%div;
int i=0;
int j=0;

for (; i<n; i+=div){
    // i = 0, 4, 8 ... then i, i+1,i+2 should pass (i%4<3)  
    thumbRGB888[j++] = argbBytes[i];
    thumbRGB888[j++] = argbBytes[i+1];
    thumbRGB888[j++] = argbBytes[i+2];
}

for(;i<argbBytes.length;i++){
    //i=n,n+1,n+2, since n%4==0 then (i%4<3) is passed
    thumbRGB888[j++] = argbBytes[i];
}

-> increments of i are the same
tested with [ 0,1,2,3,4,5,6,7,8,9,10] return [0,1,2,4,5,6,8,9,10] test with array of 10,000,000 elements, run in a loop of 30
original code: 3191ms
modified code: 1653ms
I've tested in C# so maybe the result would be different. Btw, it's may not quite much an improvement.

Not a well defined answer, but a pointer: Never done, and not entirely satisfying your request, but leave the conversion to BitmapFactory. Entirely unsure, but:

Use a BitmapFactory to get an ARGB Bitmap from the bytes. If not mutable, then maybe no new allocation is done. Might also be, that you first need to wrap the bytes into a Buffer.

Then Bitmap.copy should be possible or so.

Some wild ideas:

BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inMutable = true; // Bitmap generated mutable.
opts.inPreferredConfig = Bitmap.Config.ARGB_8888; // I hope not BGRA.
Bitmap bmARGB = BitmapFactory.decodeByteArray(argbBytes, 0, argbBytes.length,
    opts);

bmARGB.setHasAlpha(false);

DisplayMetrics display = ...;
int[] colors = null; // Palette
Bitmap bmRGB = bmARBG.createBitmap(display, colors, width, height,
    Bitmap.Config.RGB_565); // 16 bit colors only!
bmARGB.recycle();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top