MasDennis/Rajawali — pull request #311 by AndrewJo (2013-01-11 17:30:41+00:00)
Title: Texture compression

Changes
-------------
### TextureInfo class ###
Several fields and their getters and setters have been added to support extra information needed when using compressed textures:
```java
protected CompressionType mCompressionType;
protected int mInternalFormat;
protected ByteBuffer mBuffer;
```

### TextureManager class ###
Several enumerations added in `TextureManager`:

* `CompressionType` : Determines the type of compression to use.
* `NONE` : Use uncompressed bitmaps
* `ETC1` : Use Ericsson texture compression
* `ATC` : Use AMD texture compression
* `PVRTC` : Use PowerVR texture compression
* `PaletteConfig` : Currently unused but will be used for [paletted texture compression](http://developer.android.com/guide/topics/manifest/supports-gl-texture-element.html)
* `AtcFormat` : Determine the alpha transparency format in AMD texture compression scheme
* `RGB` : No alpha transparency
* `RGBA_EXPLICIT` : Explicit alpha transparency - most people will use this type
* `RGBA_INTERPOLATED` : Interpolated alpha transparency
* `PvrtcFormat` : Determine whether alpha transparency will be used and whether to use 2 bytes per pixel or 4 bytes per pixel
* `RGB_2BPP` : 2 bytes per pixel with no alpha channel
* `RGB_4BPP` : 4 bytes per pixel with no alpha channel
* `RGBA_2BPP` : 2 bytes per pixel with alpha channel
* `RGBA_4BPP` : 4 bytes per pixel with alpha channel

Underlying `addTexture` method has been modified in `TextureManager` class to add support for compressed textures. Two arguments, `compressionType` and `compressionFormat` have been added. The method without the two arguments is still available for backwards compatibility. Omitting the compression arguments just adds uncompressed texture.

When `compressionType` is specified to anything other than `NONE`, __automatic mipmap generation is disabled__. This is due to limitations in OpenGL ES 2.0 itself.

```java
public TextureInfo addTexture(ByteBuffer buffer, Bitmap texture, int width, int height, TextureType textureType, Config bitmapConfig, boolean mipmap, boolean recycle, boolean isExistingTexture, WrapType wrapType, FilterType filterType, CompressionType compressionType, int compressionFormat) {
...
if(mipmap && compressionType == CompressionType.NONE){
if(filterType==FilterType.LINEAR)
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR);
else
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST_MIPMAP_NEAREST);
} else {
if(filterType==FilterType.LINEAR)
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
else
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
}
...
if(mipmap && compressionType == CompressionType.NONE)
GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
...
}
```

Several methods have been added each respective to compression types:
* ```addEtc1Texture``` : Shortcut methods for adding ETC1 textures
* ```addAtcTexture``` : Shortcut methods for adding ATC textures
* ```addPvrtcTexture``` : Shortcut methods for adding PowerVR textures

To Do
---------
While ETC1 support is complete and can be used right away, there are still few more things to accomplish:
* Add support for remaining texture compression methods listed [here](http://developer.android.com/guide/topics/manifest/supports-gl-texture-element.html).
* Add utility classes for compression schemes other than ETC1 similar to `android.opengl.ETC1Util` to help load ATC and PowerVR textures more easily.

Example
-------------
Addressing issue #254, the following example demonstrates loading a PKM texture with graceful fallback to PNG images.

First, generate a PKM texture from any image sources. [Mali Texture Compression Tool](http://malideveloper.arm.com/develop-for-mali/mali-gpu-texture-compression-tool/) by ARM is an excellent tool to compress images to ETC1 based PKM/KTX files as well as one of the easiest tools to use.

Then, add the texture file in the `res/raw` folder and your uncompressed image file in `res/drawable-nodpi` folder. The latter bitmap is used as a graceful fallback in case the compressed texture fails to load for any reason.

The example below assumes you have a file name compressedimage.pkm in your `res/raw` folder and the uncompressedimage.png in your `res/drawables-nodpi` folder.

```java
public class Renderer extends RajawaliRenderer {
protected Plane mPlane;

public Renderer(Context context) {
super(context);
setCamera(new Camera2D());
setFramerate(60);
}

@Override
public void initScene() {
mPlane = new Plane();
TextureInfo texture = mTextureManager.addEtc1Texture(mContext.getResources().openRawResource(R.raw.compressedimage), BitmapFactory.decodeResource(mContext.getResources(), R.drawable.uncompressedimage));
mPlane.setMaterial(new SimpleMaterial());
mPlane.addTexture(texture);
addChild(mPlane);
}
}
```

Using ETC1 texture gives you performance boost for free. You should see at least around 10-15fps by using ETC1 texture over PNG.

**Resources**
* Excellent blog post about why you should say yes to texture compression:
http://androidworks-kea.blogspot.kr/2012/05/developers-notes-about-opengl-es.html
* Intel article on different texture compression schemes on Android:
http://software.intel.com/en-us/articles/android-texture-compression
* Where to find constant values for some of the OpenGL ES 2.0 extensions:
http://www.khronos.org/registry/gles/api/2.0/gl2ext.h

Questions regarding design decisions I mentioned at #310 is no longer an issue. =)

I just spent the last 12 hours coding and testing this and I think my brain has turned to a mush. I'm opening an ice cold can of lager and mindlessly channel surf now.

-- Andrew