android - GLES20.glReadPixel too slow


Keywords:android 


Question: 

It is an old question but I would like to have a reply with a code.

The following is too slow for real-time. I intend to use it later with OpenTOK screen sharing. Any fast substitute?

ByteBuffer bb = ByteBuffer.allocateDirect(screenshotSize * 4);
bb.order(ByteOrder.nativeOrder());
GLES20.glReadPixels(0, 0, width, height, GL_RGBA,
        GL10.GL_UNSIGNED_BYTE, bb);

int pixelsBuffer[] = new int[screenshotSize];
bb.asIntBuffer().get(pixelsBuffer);
final Bitmap bitmap = Bitmap.createBitmap(width, height,
        Bitmap.Config.RGB_565);
bitmap.setPixels(pixelsBuffer, screenshotSize - width, -width,
        0, 0, width, height);
pixelsBuffer = null;

short sBuffer[] = new short[screenshotSize];
ShortBuffer sb = ShortBuffer.wrap(sBuffer);
bitmap.copyPixelsToBuffer(sb);

for (int i = 0; i < screenshotSize; ++i) {
    short v = sBuffer[i];
    sBuffer[i] = (short) (((v & 0x1f) << 11) | (v & 0x7e0) | ((v & 0xf800) >> 11));
}
sb.rewind();
bitmap.copyPixelsFromBuffer(sb);

PS: I already tried GL_RGB and GL_BGRA but it is still slow and I get black screen only.


1 Answer: 

First off, the glReadPixels isn't what is causing your code to slow down. All the allocating of buffers and converting the image to another format is.

Reuse buffers. Allocate them once and reuse them.

ByteBuffer bb = ByteBuffer.allocateDirect(screenshotSize * 4);
bb.order(ByteOrder.nativeOrder());

Not a problem with this bit.

GLES20.glReadPixels(0, 0, width, height, GL_RGBA,
        GL10.GL_UNSIGNED_BYTE, bb);

Now you're preparing to convert to another format which has a lot of overhead. Stick with the format you receive. And again, you're allocating buffers instead of reusing.

However, the Bitmap can be allocated once and reused.

int pixelsBuffer[] = new int[screenshotSize];
bb.asIntBuffer().get(pixelsBuffer);
final Bitmap bitmap = Bitmap.createBitmap(width, height,
        Bitmap.Config.RGB_565);
bitmap.setPixels(pixelsBuffer, screenshotSize - width, -width,
        0, 0, width, height);
pixelsBuffer = null;

short sBuffer[] = new short[screenshotSize];
ShortBuffer sb = ShortBuffer.wrap(sBuffer);
bitmap.copyPixelsToBuffer(sb);

You then won't need this unnecessary conversion.

for (int i = 0; i < screenshotSize; ++i) {
    short v = sBuffer[i];
    sBuffer[i] = (short) (((v & 0x1f) << 11) | (v & 0x7e0) | ((v & 0xf800) >> 11));
}

The copyPixelsFromBuffer() can then be used as long as your Bitmap is same format.

bitmap.copyPixelsFromBuffer(bb);

Generally Android Bitmaps are the same format as GL_RGBA, so it's unlikely you will need to convert. The above reduces everything down to just a read and copy.