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…

9 thoughts on “Android OpenGL ES – Mipmaps”

  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. 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

  3. Nice post, it helped me get mipmaping to work.

    Regarding the ‘hacked’ version where you don’t use GLUUtils, it has a big-endian/little-endian issue. It’ll work on little-endian phones, but not on big-endian phones. Wrapping the pixels as bytes manually solves that problem.

    The below is also useful as a general way to load textures in OpenGL on Android if one desires to load bitmaps without premultiplied alpha (when using the GLUUtils alpha gets premultiplied (due to behaviour of Bitmap-class), meaning the colors loaded are a*r, a*g, a*b, a, instead of r,g,b,a).

    int[] pixels = GraphicsUtil.extractPixels(bitmap);
    byte[] pixelComponents = new byte[pixels.length*4];
    int byteIndex = 0;
    for (int i = 0; i > 16) & 0xFF); // red
    pixelComponents[byteIndex++] = (byte) ((p >> 8) & 0xFF); // green
    pixelComponents[byteIndex++] = (byte) ((p) & 0xFF); // blue
    pixelComponents[byteIndex++] = (byte) (p >> 24); // alpha
    }
    ByteBuffer pixelBuffer = ByteBuffer.wrap(pixelComponents);

    gl.glTexImage2D(GL10.GL_TEXTURE_2D, level, GL10.GL_RGBA, bitmap.getWidth(), bitmap.getHeight(), 0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixelBuffer);

  4. Hi Alex

    Nice nice, thanks for that info. Haven’t ever thought once about that… ‘doh’…

    Thx very much

  5. Hi guys,
    I’m sorry for little offtopic, but look you’re skilled enough to just point me on right way. I create map application on Android (Locus) and I ‘need’ to use OpenGL for map rendering. OpenGL is still little mystery for me (just warning).
    May you please point me to some information page, or doc of function that help me with this. Problem for me is that I with every map move, load new bitmap image and old recycle etc. and all tutorial about OpenGL I found, have preloaded bitmaps. Best should be to use directly bitmap object, but this is not possible, because bitmap data needs to be in c (thanks to JNI) right? If you find minute to answer, I’ll be very glad! Thanks

  6. Hey there

    I do not know if I got you correctly but if you need help with OpenGL in general you could start here (http://insanitydesign.com/wp/projects/nehe-android-ports/) or jump over to NeHe directly.
    If you need normal streetmaps you could use Googles own Android API to access Google Map.
    If you just want to know in general how you could do a “map” app I would recommend to be “inspired” by the tile map loading every bigger map services does now. You cannot load big images at all as they would probably occupy or crash the memory of your phone, therefore you have to look for a “streaming” reloading of tiles of a map. These single tiles can then be loaded and drawn by OpenGL if you wish to. Basically something similar to classic tile maps with support of all the benefits OpenGL can offer.

    I hope this is the direction you needed help. Otherwise let me know.

    Regards.

  7. I don’t know If I can take some more your time, but I’ll try :)

    So firstly, thanks for answer. Anyway you misunderstand me. I already have fully working application (https://market.android.com/details?id=menion.android.locus&feature=more_from_developer), anyway I now try to rotate map around X axis, and few problems occur (more info here http://stackoverflow.com/questions/6863540/problem-with-draw-bitmap-on-rotated-canvas-around-y-axis together with three images). Looks like some problem with drawing on Canvas, so I’ll probably have to start learn OpenGL ES.

    And problem for me: all tutorials on OpenGL ES, use method that during initialize, they load bitmaps and then use them during rendering. In my case, I need to change map tiles (256×256 bitmaps) dynamically, because as you move with map, new tiles have to be loaded and displayed. And I’m still little bit confused how to do it.

    So I asked for some tip on some sample application that should bring some light on this for me. Anyway ignore my posts if you don’t understand or not have time. Thanks anyway for your work on NeHe tutorials, if you’ll not point me on some way, I’ll probably start from begin with this!

Leave a Reply