2

I want to read monochrome image data from disk in a binary format (unsigned byte) and display it as an OpenGL ES 2 texture in Android. I am currently using Eclipse and the AVD emulator.

I am able to read the data from disk using an InputStream, and then convert the byte data to int to allow me to use the createBitmap method.

My hope was to create a monochrome bitmap by using ALPHA_8 as the bitmap format, but if I do that the texture appears as solid black when rendered. If I change the bitmap format to RGB_565 I can see parts of the image but of course the color is all scrambled because it is the wrong data format.

I have tried adding extra parameters to texImage2D() to try to force the texture format and source data type, but Eclipse shows an error if I use any of the opengl texture format codes in the texImage2D arguments.

I'm at a loss, can anyone tell me how to edit this to get a monochrome texture into OpenGL ES?

    int w = 640;
    int h = 512;
    int nP = w * h; //no. of pixels
    
    //load the binary data
    byte[] byteArray = new byte[nP];
    try {
        InputStream fis = mContext.getResources()
                .openRawResource(R.raw.testimage); //testimage is a binary file of U8 image data
        fis.read(byteArray);
        fis.close();
    } catch(IOException e) {
            // Ignore.
    }

    System.out.println(byteArray[1]);

    //convert byte to int to work with createBitmap (is there a better way to do this?)
    int[] intArray = new int[nP];
    for (int i=0; i < nP; i++)
    {
        intArray[i] = byteArray[i];
    }
    
    //create bitmap from intArray and send to texture
    Bitmap img = Bitmap.createBitmap(intArray, w, h, Bitmap.Config.ALPHA_8);
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, img, 0);
    img.recycle();
    //as the code is the image is black, if I change ALPHA_8 to RGB_565 then I see a corrupted image
genpfault
  • 51,148
  • 11
  • 85
  • 139
Luke AP
  • 95
  • 2
  • 11

2 Answers2

1

Once you have loaded Bitmap into byte array you can also use glTexImage2D directly with your byte array. It would be something along these lines;

byte data[bitmapLength] = your_byte_data;
ByteBuffer buffer = ByteBuffer.allocateDirect(bitmapLength);
buffer.put(data);
buffer.position(0);

GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
                    bitmapWidth, bitmapHeight, 0, GLES20.GL_LUMINANCE,
                    GLES20.GL_UNSIGNED_BYTE, buffer);

This should assign each byte value into RGB, same value for each, plus alpha is set to one.

harism
  • 6,011
  • 1
  • 36
  • 31
  • Yes! This works a treat. I have a question though, (it may just reveal my ignorance regarding disk access): I am now loading the data to an InputStream and then from there into a ByteBuffer. Is that a necessary step or could I go straight from disk to the ByteBuffer? I ask because ultimately I would like to do this operation at video speeds so I'd like it to be efficient. – Luke AP Jan 20 '13 at 11:19
  • I'm afraid this isn't possible. But you're forced to load byte array first and then create ByteBuffer out of it. – harism Jan 20 '13 at 11:44
  • Hmmm, so this works on the virtual device but I have now tried running it on a Motorola Milestone with Android 2.2.1, and the texture is black again. Is the likely to be a hardware capability problem? – Luke AP Jan 20 '13 at 16:28
  • Hmm.. GL_LUMINANCE should be supported by all devices running OpenGL ES 2.0 AFAIK. But it might be the case this is not true. Are you targetting devices all the way down to API 8? – harism Jan 21 '13 at 08:36
  • By playing about with a bit of fragment shader code I think I have confirmed that the emulator ends up with a texture with the elements [n,n,n,1], where n is the correct pixel value. The Milestone comes out with [0,0,0,1]. I don't need to target API8 in the long run, it is just the only hardware I have to hand until I get a devkit I'm waiting for through, which may take a few weeks. – Luke AP Jan 21 '13 at 18:24
0

According to the createBitmap docs, that int array is interpreted as an array of Color, which is "(alpha << 24) | (red << 16) | (green << 8) | blue". So, when you're loading those bytes and populating your int array, you're currently putting the data in the blue slot instead of the alpha slot. As such, your alpha values are all zero, which I'd actually expect to result in a clear texture. I believe you want

intArray[i] = byteArray[i] << 24;
Rob Starling
  • 3,868
  • 3
  • 23
  • 40
  • Thanks, Rob that doesn't seem to work as is, but I did try stuffing the data into each channel of the 8888 texture and just about got it to work. However, using the Luminance texture and byte direct seems like a 4x memory saving, which can't be bad! – Luke AP Jan 20 '13 at 11:30
  • For the record, I have made this work perfectly using: intArray[i] = byteArray[i]; and using the fragment shader to copy the R channel in to the G and B channels. – Luke AP Jan 22 '13 at 08:12