public class

GLHelper

extends Object
package org.anddev.andengine.opengl.util;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;

import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;

import org.anddev.andengine.engine.options.RenderOptions;
import org.anddev.andengine.opengl.texture.Texture.PixelFormat;
import org.anddev.andengine.util.Debug;

import android.graphics.Bitmap;
import android.opengl.GLException;
import android.opengl.GLUtils;
import android.os.Build;

/**
 * (c) 2010 Nicolas Gramlich
 * (c) 2011 Zynga Inc.
 *
 * @author Nicolas Gramlich
 * @since 18:00:43 - 08.03.2010
 */
public class GLHelper {
	// ===========================================================
	// Constants
	// ===========================================================

	public static final int BYTES_PER_FLOAT = 4;
	public static final int BYTES_PER_PIXEL_RGBA = 4;

	private static final boolean IS_LITTLE_ENDIAN = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN);

	private static final int[] HARDWARETEXTUREID_CONTAINER = new int[1];
	private static final int[] HARDWAREBUFFERID_CONTAINER = new int[1];

	// ===========================================================
	// Fields
	// ===========================================================

	private static int sCurrentHardwareBufferID = -1;
	private static int sCurrentHardwareTextureID = -1;
	private static int sCurrentMatrix = -1;

	private static int sCurrentSourceBlendMode = -1;
	private static int sCurrentDestinationBlendMode = -1;

	private static FastFloatBuffer sCurrentVertexFloatBuffer = null;
	private static FastFloatBuffer sCurrentTextureFloatBuffer = null;

	private static boolean sEnableDither = true;
	private static boolean sEnableLightning = true;
	private static boolean sEnableDepthTest = true;
	private static boolean sEnableMultisample = true;

	private static boolean sEnableScissorTest = false;
	private static boolean sEnableBlend = false;
	private static boolean sEnableCulling = false;
	private static boolean sEnableTextures = false;
	private static boolean sEnableTexCoordArray = false;
	private static boolean sEnableVertexArray = false;

	private static float sLineWidth = 1;

	private static float sRed = -1;
	private static float sGreen = -1;
	private static float sBlue = -1;
	private static float sAlpha = -1;

	public static boolean EXTENSIONS_VERTEXBUFFEROBJECTS = false;
	public static boolean EXTENSIONS_DRAWTEXTURE = false;
	public static boolean EXTENSIONS_TEXTURE_NON_POWER_OF_TWO = false;

	// ===========================================================
	// Methods
	// ===========================================================

	public static void reset(final GL10 pGL) {
		GLHelper.sCurrentHardwareBufferID = -1;
		GLHelper.sCurrentHardwareTextureID = -1;
		GLHelper.sCurrentMatrix = -1;

		GLHelper.sCurrentSourceBlendMode = -1;
		GLHelper.sCurrentDestinationBlendMode = -1;

		GLHelper.sCurrentVertexFloatBuffer = null;
		GLHelper.sCurrentTextureFloatBuffer = null;

		GLHelper.enableDither(pGL);
		GLHelper.enableLightning(pGL);
		GLHelper.enableDepthTest(pGL);
		GLHelper.enableMultisample(pGL);

		GLHelper.disableBlend(pGL);
		GLHelper.disableCulling(pGL);
		GLHelper.disableTextures(pGL);
		GLHelper.disableTexCoordArray(pGL);
		GLHelper.disableVertexArray(pGL);

		GLHelper.sLineWidth = 1;

		GLHelper.sRed = -1;
		GLHelper.sGreen = -1;
		GLHelper.sBlue = -1;
		GLHelper.sAlpha = -1;

		GLHelper.EXTENSIONS_VERTEXBUFFEROBJECTS = false;
		GLHelper.EXTENSIONS_DRAWTEXTURE = false;
		GLHelper.EXTENSIONS_TEXTURE_NON_POWER_OF_TWO = false;
	}

	public static void enableExtensions(final GL10 pGL, final RenderOptions pRenderOptions) {
		final String version = pGL.glGetString(GL10.GL_VERSION);
		final String renderer = pGL.glGetString(GL10.GL_RENDERER);
		final String extensions = pGL.glGetString(GL10.GL_EXTENSIONS);

		Debug.d("RENDERER: " + renderer);
		Debug.d("VERSION: " + version);
		Debug.d("EXTENSIONS: " + extensions);

		final boolean isOpenGL10 = version.contains("1.0");
		final boolean isOpenGL2X = version.contains("2.");
		final boolean isSoftwareRenderer = renderer.contains("PixelFlinger");
		final boolean isVBOCapable = extensions.contains("_vertex_buffer_object");
		final boolean isDrawTextureCapable = extensions.contains("draw_texture");
		final boolean isTextureNonPowerOfTwoCapable = extensions.contains("texture_npot");

		GLHelper.EXTENSIONS_VERTEXBUFFEROBJECTS = !pRenderOptions.isDisableExtensionVertexBufferObjects() && !isSoftwareRenderer && (isVBOCapable || !isOpenGL10);
		GLHelper.EXTENSIONS_DRAWTEXTURE = !pRenderOptions.isDisableExtensionVertexBufferObjects() && (isDrawTextureCapable || !isOpenGL10);
		GLHelper.EXTENSIONS_TEXTURE_NON_POWER_OF_TWO = isTextureNonPowerOfTwoCapable || isOpenGL2X;

		GLHelper.hackBrokenDevices();
		Debug.d("EXTENSIONS_VERXTEXBUFFEROBJECTS = " + GLHelper.EXTENSIONS_VERTEXBUFFEROBJECTS);
		Debug.d("EXTENSIONS_DRAWTEXTURE = " + GLHelper.EXTENSIONS_DRAWTEXTURE);
	}

	private static void hackBrokenDevices() {
		if (Build.PRODUCT.contains("morrison")) {
			// This is the Motorola Cliq. This device LIES and says it supports
			// VBOs, which it actually does not (or, more likely, the extensions string
			// is correct and the GL JNI glue is broken).
			GLHelper.EXTENSIONS_VERTEXBUFFEROBJECTS = false;
			// TODO: if Motorola fixes this, I should switch to using the fingerprint
			// (blur/morrison/morrison/morrison:1.5/CUPCAKE/091007:user/ota-rel-keys,release-keys)
			// instead of the product name so that newer versions use VBOs
		}
	}

	public static void setColor(final GL10 pGL, final float pRed, final float pGreen, final float pBlue, final float pAlpha) {
		if(pAlpha != GLHelper.sAlpha || pRed != GLHelper.sRed || pGreen != GLHelper.sGreen || pBlue != GLHelper.sBlue) {
			GLHelper.sAlpha = pAlpha;
			GLHelper.sRed = pRed;
			GLHelper.sGreen = pGreen;
			GLHelper.sBlue = pBlue;
			pGL.glColor4f(pRed, pGreen, pBlue, pAlpha);
		}
	}

	public static void enableVertexArray(final GL10 pGL) {
		if(!GLHelper.sEnableVertexArray) {
			GLHelper.sEnableVertexArray = true;
			pGL.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		}
	}
	public static void disableVertexArray(final GL10 pGL) {
		if(GLHelper.sEnableVertexArray) {
			GLHelper.sEnableVertexArray = false;
			pGL.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		}
	}

	public static void enableTexCoordArray(final GL10 pGL) {
		if(!GLHelper.sEnableTexCoordArray) {
			GLHelper.sEnableTexCoordArray = true;
			pGL.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
		}
	}
	public static void disableTexCoordArray(final GL10 pGL) {
		if(GLHelper.sEnableTexCoordArray) {
			GLHelper.sEnableTexCoordArray = false;
			pGL.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
		}
	}

	public static void enableScissorTest(final GL10 pGL) {
		if(!GLHelper.sEnableScissorTest) {
			GLHelper.sEnableScissorTest = true;
			pGL.glEnable(GL10.GL_SCISSOR_TEST);
		}
	}
	public static void disableScissorTest(final GL10 pGL) {
		if(GLHelper.sEnableScissorTest) {
			GLHelper.sEnableScissorTest = false;
			pGL.glDisable(GL10.GL_SCISSOR_TEST);
		}
	}

	public static void enableBlend(final GL10 pGL) {
		if(!GLHelper.sEnableBlend) {
			GLHelper.sEnableBlend = true;
			pGL.glEnable(GL10.GL_BLEND);
		}
	}
	public static void disableBlend(final GL10 pGL) {
		if(GLHelper.sEnableBlend) {
			GLHelper.sEnableBlend = false;
			pGL.glDisable(GL10.GL_BLEND);
		}
	}

	public static void enableCulling(final GL10 pGL) {
		if(!GLHelper.sEnableCulling) {
			GLHelper.sEnableCulling = true;
			pGL.glEnable(GL10.GL_CULL_FACE);
		}
	}
	public static void disableCulling(final GL10 pGL) {
		if(GLHelper.sEnableCulling) {
			GLHelper.sEnableCulling = false;
			pGL.glDisable(GL10.GL_CULL_FACE);
		}
	}

	public static void enableTextures(final GL10 pGL) {
		if(!GLHelper.sEnableTextures) {
			GLHelper.sEnableTextures = true;
			pGL.glEnable(GL10.GL_TEXTURE_2D);
		}
	}
	public static void disableTextures(final GL10 pGL) {
		if(GLHelper.sEnableTextures) {
			GLHelper.sEnableTextures = false;
			pGL.glDisable(GL10.GL_TEXTURE_2D);
		}
	}

	public static void enableLightning(final GL10 pGL) {
		if(!GLHelper.sEnableLightning) {
			GLHelper.sEnableLightning = true;
			pGL.glEnable(GL10.GL_LIGHTING);
		}
	}
	public static void disableLightning(final GL10 pGL) {
		if(GLHelper.sEnableLightning) {
			GLHelper.sEnableLightning = false;
			pGL.glDisable(GL10.GL_LIGHTING);
		}
	}

	public static void enableDither(final GL10 pGL) {
		if(!GLHelper.sEnableDither) {
			GLHelper.sEnableDither = true;
			pGL.glEnable(GL10.GL_DITHER);
		}
	}
	public static void disableDither(final GL10 pGL) {
		if(GLHelper.sEnableDither) {
			GLHelper.sEnableDither = false;
			pGL.glDisable(GL10.GL_DITHER);
		}
	}

	public static void enableDepthTest(final GL10 pGL) {
		if(!GLHelper.sEnableDepthTest) {
			GLHelper.sEnableDepthTest = true;
			pGL.glEnable(GL10.GL_DEPTH_TEST);
		}
	}
	public static void disableDepthTest(final GL10 pGL) {
		if(GLHelper.sEnableDepthTest) {
			GLHelper.sEnableDepthTest = false;
			pGL.glDisable(GL10.GL_DEPTH_TEST);
		}
	}

	public static void enableMultisample(final GL10 pGL) {
		if(!GLHelper.sEnableMultisample) {
			GLHelper.sEnableMultisample = true;
			pGL.glEnable(GL10.GL_MULTISAMPLE);
		}
	}
	public static void disableMultisample(final GL10 pGL) {
		if(GLHelper.sEnableMultisample) {
			GLHelper.sEnableMultisample = false;
			pGL.glDisable(GL10.GL_MULTISAMPLE);
		}
	}

	public static void bindBuffer(final GL11 pGL11, final int pHardwareBufferID) {
		/* Reduce unnecessary buffer switching calls. */
		if(GLHelper.sCurrentHardwareBufferID != pHardwareBufferID) {
			GLHelper.sCurrentHardwareBufferID = pHardwareBufferID;
			pGL11.glBindBuffer(GL11.GL_ARRAY_BUFFER, pHardwareBufferID);
		}
	}

	public static void deleteBuffer(final GL11 pGL11, final int pHardwareBufferID) {
		GLHelper.HARDWAREBUFFERID_CONTAINER[0] = pHardwareBufferID;
		pGL11.glDeleteBuffers(1, GLHelper.HARDWAREBUFFERID_CONTAINER, 0);
	}

	/**
	 * @see {@link GLHelper#forceBindTexture(GL10, int)}
	 * @param pGL
	 * @param pHardwareTextureID
	 */
	public static void bindTexture(final GL10 pGL, final int pHardwareTextureID) {
		/* Reduce unnecessary texture switching calls. */
		if(GLHelper.sCurrentHardwareTextureID != pHardwareTextureID) {
			GLHelper.sCurrentHardwareTextureID = pHardwareTextureID;
			pGL.glBindTexture(GL10.GL_TEXTURE_2D, pHardwareTextureID);
		}
	}

	/**
	 * @see {@link GLHelper#bindTexture(GL10, int)}
	 * @param pGL
	 * @param pHardwareTextureID
	 */
	public static void forceBindTexture(final GL10 pGL, final int pHardwareTextureID) {
		GLHelper.sCurrentHardwareTextureID = pHardwareTextureID;
		pGL.glBindTexture(GL10.GL_TEXTURE_2D, pHardwareTextureID);
	}

	public static void deleteTexture(final GL10 pGL, final int pHardwareTextureID) {
		GLHelper.HARDWARETEXTUREID_CONTAINER[0] = pHardwareTextureID;
		pGL.glDeleteTextures(1, GLHelper.HARDWARETEXTUREID_CONTAINER, 0);
	}

	public static void texCoordPointer(final GL10 pGL, final FastFloatBuffer pTextureFloatBuffer) {
		if(GLHelper.sCurrentTextureFloatBuffer  != pTextureFloatBuffer) {
			GLHelper.sCurrentTextureFloatBuffer = pTextureFloatBuffer;
			pGL.glTexCoordPointer(2, GL10.GL_FLOAT, 0, pTextureFloatBuffer.mByteBuffer);
		}
	}

	public static void texCoordZeroPointer(final GL11 pGL11) {
		pGL11.glTexCoordPointer(2, GL10.GL_FLOAT, 0, 0);
	}

	public static void vertexPointer(final GL10 pGL, final FastFloatBuffer pVertexFloatBuffer) {
		if(GLHelper.sCurrentVertexFloatBuffer != pVertexFloatBuffer) {
			GLHelper.sCurrentVertexFloatBuffer = pVertexFloatBuffer;
			pGL.glVertexPointer(2, GL10.GL_FLOAT, 0, pVertexFloatBuffer.mByteBuffer);
		}
	}

	public static void vertexZeroPointer(final GL11 pGL11) {
		pGL11.glVertexPointer(2, GL10.GL_FLOAT, 0, 0);
	}

	public static void blendFunction(final GL10 pGL, final int pSourceBlendMode, final int pDestinationBlendMode) {
		if(GLHelper.sCurrentSourceBlendMode != pSourceBlendMode || GLHelper.sCurrentDestinationBlendMode != pDestinationBlendMode) {
			GLHelper.sCurrentSourceBlendMode = pSourceBlendMode;
			GLHelper.sCurrentDestinationBlendMode = pDestinationBlendMode;
			pGL.glBlendFunc(pSourceBlendMode, pDestinationBlendMode);
		}
	}

	public static void lineWidth(final GL10 pGL, final float pLineWidth) {
		if(GLHelper.sLineWidth  != pLineWidth) {
			GLHelper.sLineWidth = pLineWidth;
			pGL.glLineWidth(pLineWidth);
		}
	}

	public static void switchToModelViewMatrix(final GL10 pGL) {
		/* Reduce unnecessary matrix switching calls. */
		if(GLHelper.sCurrentMatrix != GL10.GL_MODELVIEW) {
			GLHelper.sCurrentMatrix = GL10.GL_MODELVIEW;
			pGL.glMatrixMode(GL10.GL_MODELVIEW);
		}
	}

	public static void switchToProjectionMatrix(final GL10 pGL) {
		/* Reduce unnecessary matrix switching calls. */
		if(GLHelper.sCurrentMatrix != GL10.GL_PROJECTION) {
			GLHelper.sCurrentMatrix = GL10.GL_PROJECTION;
			pGL.glMatrixMode(GL10.GL_PROJECTION);
		}
	}

	public static void setProjectionIdentityMatrix(final GL10 pGL) {
		GLHelper.switchToProjectionMatrix(pGL);
		pGL.glLoadIdentity();
	}

	public static void setModelViewIdentityMatrix(final GL10 pGL) {
		GLHelper.switchToModelViewMatrix(pGL);
		pGL.glLoadIdentity();
	}

	public static void setShadeModelFlat(final GL10 pGL) {
		pGL.glShadeModel(GL10.GL_FLAT);
	}

	public static void setPerspectiveCorrectionHintFastest(final GL10 pGL) {
		pGL.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
	}

	public static void bufferData(final GL11 pGL11, final ByteBuffer pByteBuffer, final int pUsage) {
		pGL11.glBufferData(GL11.GL_ARRAY_BUFFER, pByteBuffer.capacity(), pByteBuffer, pUsage);
	}

	/**
	 * <b>Note:</b> does not pre-multiply the alpha channel!</br>
	 * Except that difference, same as: {@link GLUtils#texSubImage2D(int, int, int, int, Bitmap, int, int)}</br>
	 * </br>
	 * See topic: '<a href="http://groups.google.com/group/android-developers/browse_thread/thread/baa6c33e63f82fca">PNG loading that doesn't premultiply alpha?</a>'
	 * @param pBorder
	 */
	public static void glTexImage2D(final GL10 pGL, final int pTarget, final int pLevel, final Bitmap pBitmap, final int pBorder, final PixelFormat pPixelFormat) {
		final Buffer pixelBuffer = GLHelper.getPixels(pBitmap, pPixelFormat);

		pGL.glTexImage2D(pTarget, pLevel, pPixelFormat.getGLFormat(), pBitmap.getWidth(), pBitmap.getHeight(), pBorder, pPixelFormat.getGLFormat(), pPixelFormat.getGLType(), pixelBuffer);
	}

	/**
	 * <b>Note:</b> does not pre-multiply the alpha channel!</br>
	 * Except that difference, same as: {@link GLUtils#texSubImage2D(int, int, int, int, Bitmap, int, int)}</br>
	 * </br>
	 * See topic: '<a href="http://groups.google.com/group/android-developers/browse_thread/thread/baa6c33e63f82fca">PNG loading that doesn't premultiply alpha?</a>'
	 */
	public static void glTexSubImage2D(final GL10 pGL, final int pTarget, final int pLevel, final int pXOffset, final int pYOffset, final Bitmap pBitmap, final PixelFormat pPixelFormat) {
		final Buffer pixelBuffer = GLHelper.getPixels(pBitmap, pPixelFormat);

		pGL.glTexSubImage2D(pTarget, pLevel, pXOffset, pYOffset, pBitmap.getWidth(), pBitmap.getHeight(), pPixelFormat.getGLFormat(), pPixelFormat.getGLType(), pixelBuffer);
	}

	private static Buffer getPixels(final Bitmap pBitmap, final PixelFormat pPixelFormat) {
		final int[] pixelsARGB_8888 = GLHelper.getPixelsARGB_8888(pBitmap);

		switch(pPixelFormat) {
			case RGB_565:
				return ByteBuffer.wrap(GLHelper.convertARGB_8888toRGB_565(pixelsARGB_8888));
			case RGBA_8888:
				return IntBuffer.wrap(GLHelper.convertARGB_8888toRGBA_8888(pixelsARGB_8888));
			case RGBA_4444:
				return ByteBuffer.wrap(GLHelper.convertARGB_8888toARGB_4444(pixelsARGB_8888));
			case A_8:
				return ByteBuffer.wrap(GLHelper.convertARGB_8888toA_8(pixelsARGB_8888));
			default:
				throw new IllegalArgumentException("Unexpected " + PixelFormat.class.getSimpleName() + ": '" + pPixelFormat + "'.");
		}
	}

	private static int[] convertARGB_8888toRGBA_8888(final int[] pPixelsARGB_8888) {
		if(GLHelper.IS_LITTLE_ENDIAN) {
			for(int i = pPixelsARGB_8888.length - 1; i >= 0; i--) {
				final int pixel = pPixelsARGB_8888[i];
				/* ARGB to ABGR */
				pPixelsARGB_8888[i] = pixel & 0xFF00FF00 | (pixel & 0x000000FF) << 16 | (pixel & 0x00FF0000) >> 16;
			}
		} else {
			for(int i = pPixelsARGB_8888.length - 1; i >= 0; i--) {
				final int pixel = pPixelsARGB_8888[i];
				/* ARGB to RGBA */
				pPixelsARGB_8888[i] = (pixel & 0x00FFFFFF) << 8 | (pixel & 0xFF000000) >> 24;
			}
		}
		return pPixelsARGB_8888;
	}

	private static byte[] convertARGB_8888toRGB_565(final int[] pPixelsARGB_8888) {
		final byte[] pixelsRGB_565 = new byte[pPixelsARGB_8888.length * 2];
		if(GLHelper.IS_LITTLE_ENDIAN) {
			for(int i = pPixelsARGB_8888.length - 1, j = pixelsRGB_565.length - 1; i >= 0; i--) {
				final int pixel = pPixelsARGB_8888[i];

				final int red = ((pixel >> 16) & 0xFF);
				final int green = ((pixel >> 8) & 0xFF);
				final int blue = ((pixel) & 0xFF);

				/* Byte1: [R1 R2 R3 R4 R5 G1 G2 G3]
				 * Byte2: [G4 G5 G6 B1 B2 B3 B4 B5] */

				pixelsRGB_565[j--] = (byte)((red & 0xF8) | (green >> 5));
				pixelsRGB_565[j--] = (byte)(((green << 3) & 0xE0) | (blue >> 3));
			}
		} else {
			for(int i = pPixelsARGB_8888.length - 1, j = pixelsRGB_565.length - 1; i >= 0; i--) {
				final int pixel = pPixelsARGB_8888[i];

				final int red = ((pixel >> 16) & 0xFF);
				final int green = ((pixel >> 8) & 0xFF);
				final int blue = ((pixel) & 0xFF);

				/* Byte2: [G4 G5 G6 B1 B2 B3 B4 B5]
				 * Byte1: [R1 R2 R3 R4 R5 G1 G2 G3]*/

				pixelsRGB_565[j--] = (byte)(((green << 3) & 0xE0) | (blue >> 3));
				pixelsRGB_565[j--] = (byte)((red & 0xF8) | (green >> 5));
			}
		}
		return pixelsRGB_565;
	}

	private static byte[] convertARGB_8888toARGB_4444(final int[] pPixelsARGB_8888) {
		final byte[] pixelsARGB_4444 = new byte[pPixelsARGB_8888.length * 2];
		if(GLHelper.IS_LITTLE_ENDIAN) {
			for(int i = pPixelsARGB_8888.length - 1, j = pixelsARGB_4444.length - 1; i >= 0; i--) {
				final int pixel = pPixelsARGB_8888[i];

				final int alpha = ((pixel >> 28) & 0x0F);
				final int red = ((pixel >> 16) & 0xF0);
				final int green = ((pixel >> 8) & 0xF0);
				final int blue = ((pixel) & 0x0F);

				/* Byte1: [A1 A2 A3 A4 R1 R2 R3 R4]
				 * Byte2: [G1 G2 G3 G4 G2 G2 G3 G4] */

				pixelsARGB_4444[j--] = (byte)(alpha | red);
				pixelsARGB_4444[j--] = (byte)(green | blue);
			}
		} else {
			for(int i = pPixelsARGB_8888.length - 1, j = pixelsARGB_4444.length - 1; i >= 0; i--) {
				final int pixel = pPixelsARGB_8888[i];

				final int alpha = ((pixel >> 28) & 0x0F);
				final int red = ((pixel >> 16) & 0xF0);
				final int green = ((pixel >> 8) & 0xF0);
				final int blue = ((pixel) & 0x0F);

				/* Byte2: [G1 G2 G3 G4 G2 G2 G3 G4]
				 * Byte1: [A1 A2 A3 A4 R1 R2 R3 R4] */

				pixelsARGB_4444[j--] = (byte)(green | blue);
				pixelsARGB_4444[j--] = (byte)(alpha | red);
			}
		}
		return pixelsARGB_4444;
	}

	private static byte[] convertARGB_8888toA_8(final int[] pPixelsARGB_8888) {
		final byte[] pixelsA_8 = new byte[pPixelsARGB_8888.length];
		if(GLHelper.IS_LITTLE_ENDIAN) {
			for(int i = pPixelsARGB_8888.length - 1; i >= 0; i--) {
				pixelsA_8[i] = (byte) (pPixelsARGB_8888[i] >> 24);
			}
		} else {
			for(int i = pPixelsARGB_8888.length - 1; i >= 0; i--) {
				pixelsA_8[i] = (byte) (pPixelsARGB_8888[i] & 0xFF);
			}
		}
		return pixelsA_8;
	}

	public static int[] getPixelsARGB_8888(final Bitmap pBitmap) {
		final int w = pBitmap.getWidth();
		final int h = pBitmap.getHeight();

		final int[] pixelsARGB_8888 = new int[w * h];
		pBitmap.getPixels(pixelsARGB_8888, 0, w, 0, 0, w, h);

		return pixelsARGB_8888;
	}

	public static void checkGLError(final GL10 pGL) throws GLException { // TODO Use more often!
		final int err = pGL.glGetError();
		if (err != GL10.GL_NO_ERROR) {
			throw new GLException(err);
		}
	}

	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================
}