Home > Android, Code, Internet, Java > Android OpenGL ES – Mipmaps

Android OpenGL ES – Mipmaps

I am currently a little bit experimenting with Android and its SDK as I am proud owner of a HTC Magic as well as I am pretty impressed by the SDK, Android itself and the ideas and principles it follows.
More precisely I am playing around with the graphical functionalities of Android, especially OpenGL. Android contains an OpenGL ES implementation, therefore I am playing around with some OpenGL codes and test the capabilities and results.

So, I was testing some texturing and of course wanted to test the mipmapping possibilities. Here I tripped over some “problems” you have to avoid by going another way.
In a probably classical way you could use glu to build the mipmaps

gluBuild2DMipmaps(GL_TEXTURE_2D, 3, sizeX, sizeY, GL_RGB, GL_UNSIGNED_BYTE, data);

You hand it the width and height and the texture data and it creates all your needed mipmaps. Problem solved! Unfortunately Android only provides a very small GLU implementation with some helper functions and does not provide you that functionality. Therefore, you would have to go another way.
A possibility on an Android system could be to test for version 1.1 of your GL and use the according hint, as 1.1. adds automatic mipmap generation

gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR_MIPMAP_NEAREST);
if(gl instanceof GL11) {
  gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE);
  GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
} else {
  buildMipmap(gl, bitmap);
}

For the emulator this might work but not every mobile device running Android must have a GL11 instance. Therefore, this solution can work but does not necessarily have to.
Mike Miller had a similar problem/idea and suggested the following functionality

private void buildMipmap(GL10 gl, Bitmap bitmap) {
  //
  int level = 0;
  //
  int height = bitmap.getHeight();
  int width = bitmap.getWidth();

  //
  int[] textures = new int[1];
  gl.glGenTextures(1, textures, 0);
  textureID = textures[0];

  //
  gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);

  //
  while(height >= 1 || width >= 1) {
    //First of all, generate the texture from our bitmap and set it to the according level
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);

    //
    if(height == 1 || width == 1) {
      break;
    }

    //Increase the mipmap level
    level++;

    //
    height /= 2;
    width /= 2;
    Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);

    //Clean up
    bitmap.recycle();
    bitmap = bitmap2;
  }

  return textureID;
}

The code is pretty straight forward. It just creates always the mottled bitmap and adds it using GLUtils to the according level, beginning from 0 to whatever level the size 1 for width or height will be.
Unfortunately again, this did not work for me. I tried this in an existing code I had, a special plain project just for mipmap testing, with different AVDs and with different circumstances. Nothing worked! As soon as I added another layer the texture stayed white. I am aware that as soon as you set the texture parameters for mipmapping

gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR_MIPMAP_NEAREST);

it wants all layers. The function posted by Mike provides these but still it did not work for me. I had no idea why it did not work, because that it is a common solution, trivial and eventually should work. (Nevertheless, I solved it. Read at the end of this post why it did not work for me.)
I do not remember why I tried the following, but after many hours and lost hair I replaced the line of GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bmp, 0); with the following, actually resembling what GLUUtils should provide, but based on an older SDK version

ByteBuffer bytebuf = ByteBuffer.allocateDirect(bmp.getHeight() * bmp.getWidth() * 4);
bytebuf.order(ByteOrder.nativeOrder());
IntBuffer pixelbuf = bytebuf.asIntBuffer();

for(int y = 0; y < bmp.getHeight(); y++)
  for(int x = 0; x < bmp.getWidth(); x++) {
    pixelbuf.put(bmp.getPixel(x, y));
  }
pixelbuf.position(0);
bytebuf.position(0);

gl.glTexImage2D(GL10.GL_TEXTURE_2D, level, GL10.GL_RGBA, bmp.getWidth(), bmp.getHeight(), 0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixelbuf);

This basic self-implementation of what should be done by GLUtils works for me. But please note that it is just a working example and does is neither optimized nor necessarily the finite solution to this task.

I also figured out why the GLUtils did not work for me: It was only in the Emulator and it was only on one of my machines. And especially that machine it did not work on has a very basic, cheap, old graphics card. But for a whole overview and if you step into the same trap, I posted everything so you know what could be a reason and how to solve it.

So, now after all these tests I have done I have three implementations that work for me. On the one hand side, the use of GL11 and the self-implementation of a mipmap building method in two ways. In conclusion after all these tests and also minor performance tests I did, use one of the suggested methods from above or let me know of other possibilities. I would start with the GL11 test and then go over to the implementations. GLUtils should always work. Hope this helps…

Bookmark and Share
  1. July 26th, 2010 at 13:11 | #1

    Hey there sir, I’m trying to solve a similar problem and was trying to make use of your buildMipMap function. It seems to work, but only for textures that are square. I’ve been trying to work out why this is but keep getting black for anything non-square. do you know why this is? Do I need to ditch Bitmap as well? I know it incorrectly premultiplies the alpha channel, so maybe that’s for the best anyway.

    Oddly enough, the reason I’m trying to do this is because the test for GL11 isn’t reliable. The HTC Eris and Hero, for example, will report a GL11 instance but will not generate mipmaps correctly. At this point I’m hoping to remove all reliance on the auto-generated mipmaps. :/

  2. July 30th, 2010 at 13:59 | #2

    Hi there

    The thing with the squares comes with MipMaps in general: If you have a look at http://en.wikipedia.org/wiki/Mipmapping a MipMap source has to be square. I do not know if there are any specifics how to “fake” something else, but this is how I learned and know it.

    Regarding the GL11 problem: Good to know. Still hanging on my Magic ^^’ But I already noticed that there are far too many differences in between the different Android Phones as there should be.

    Regards

  1. August 2nd, 2009 at 04:43 | #1