public class

BitmapTexture

extends Texture
package org.andengine.opengl.texture.bitmap;

import java.io.IOException;
import java.io.InputStream;

import org.andengine.opengl.texture.PixelFormat;
import org.andengine.opengl.texture.Texture;
import org.andengine.opengl.texture.TextureManager;
import org.andengine.opengl.texture.TextureOptions;
import org.andengine.opengl.util.GLState;
import org.andengine.util.StreamUtils;
import org.andengine.util.adt.io.in.IInputStreamOpener;
import org.andengine.util.exception.NullBitmapException;
import org.andengine.util.math.MathUtils;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;

/**
 * (c) Zynga 2011
 *
 * @author Nicolas Gramlich <ngramlich@zynga.com>
 * @since 16:16:25 - 30.07.2011
 */
public class BitmapTexture extends Texture {
	// ===========================================================
	// Constants
	// ===========================================================

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

	private final int mWidth;
	private final int mHeight;
	private final IInputStreamOpener mInputStreamOpener;
	private final BitmapTextureFormat mBitmapTextureFormat;

	// ===========================================================
	// Constructors
	// ===========================================================

	public BitmapTexture(final TextureManager pTextureManager, final IInputStreamOpener pInputStreamOpener) throws IOException {
		this(pTextureManager, pInputStreamOpener, BitmapTextureFormat.RGBA_8888, TextureOptions.DEFAULT, null);
	}

	public BitmapTexture(final TextureManager pTextureManager, final IInputStreamOpener pInputStreamOpener, final BitmapTextureFormat pBitmapTextureFormat) throws IOException {
		this(pTextureManager, pInputStreamOpener, pBitmapTextureFormat, TextureOptions.DEFAULT, null);
	}

	public BitmapTexture(final TextureManager pTextureManager, final IInputStreamOpener pInputStreamOpener, final TextureOptions pTextureOptions) throws IOException {
		this(pTextureManager, pInputStreamOpener, BitmapTextureFormat.RGBA_8888, pTextureOptions, null);
	}

	public BitmapTexture(final TextureManager pTextureManager, final IInputStreamOpener pInputStreamOpener, final BitmapTextureFormat pBitmapTextureFormat, final TextureOptions pTextureOptions) throws IOException {
		this(pTextureManager, pInputStreamOpener, pBitmapTextureFormat, pTextureOptions, null);
	}

	public BitmapTexture(final TextureManager pTextureManager, final IInputStreamOpener pInputStreamOpener, final BitmapTextureFormat pBitmapTextureFormat, final TextureOptions pTextureOptions, final ITextureStateListener pTextureStateListener) throws IOException {
		super(pTextureManager, pBitmapTextureFormat.getPixelFormat(), pTextureOptions, pTextureStateListener);

		this.mInputStreamOpener = pInputStreamOpener;
		this.mBitmapTextureFormat = pBitmapTextureFormat;

		final BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
		decodeOptions.inJustDecodeBounds = true;

		final InputStream in = null;
		try {
			BitmapFactory.decodeStream(pInputStreamOpener.open(), null, decodeOptions);
		} finally {
			StreamUtils.close(in);
		}

		this.mWidth = decodeOptions.outWidth;
		this.mHeight = decodeOptions.outHeight;
	}

	// ===========================================================
	// Getter & Setter
	// ===========================================================

	@Override
	public int getWidth() {
		return this.mWidth;
	}

	@Override
	public int getHeight() {
		return this.mHeight;
	}

	// ===========================================================
	// Methods for/from SuperClass/Interfaces
	// ===========================================================

	@Override
	protected void writeTextureToHardware(final GLState pGLState) throws IOException {
		final Config bitmapConfig = this.mBitmapTextureFormat.getBitmapConfig();
		final Bitmap bitmap = this.onGetBitmap(bitmapConfig);

		if(bitmap == null) {
			throw new NullBitmapException("Caused by: '" + this.toString() + "'.");
		}

		final boolean useDefaultAlignment = MathUtils.isPowerOfTwo(bitmap.getWidth()) && MathUtils.isPowerOfTwo(bitmap.getHeight()) && (this.mPixelFormat == PixelFormat.RGBA_8888);
		if(!useDefaultAlignment) {
			/* Adjust unpack alignment. */
			GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
		}

		final boolean preMultipyAlpha = this.mTextureOptions.mPreMultiplyAlpha;
		if(preMultipyAlpha) {
			GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
		} else {
			pGLState.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0, this.mPixelFormat);
		}

		if(!useDefaultAlignment) {
			/* Restore default unpack alignment. */
			GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, GLState.GL_UNPACK_ALIGNMENT_DEFAULT);
		}

		bitmap.recycle();
	}

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

	protected Bitmap onGetBitmap(final Config pBitmapConfig) throws IOException {
		final BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
		decodeOptions.inPreferredConfig = pBitmapConfig;

		return BitmapFactory.decodeStream(this.mInputStreamOpener.open(), null, decodeOptions);
	}

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

	public static enum BitmapTextureFormat {
		// ===========================================================
		// Elements
		// ===========================================================

		RGBA_8888(Config.ARGB_8888, PixelFormat.RGBA_8888),
		RGB_565(Config.RGB_565, PixelFormat.RGB_565),
		RGBA_4444(Config.ARGB_4444, PixelFormat.RGBA_4444),
		A_8(Config.ALPHA_8, PixelFormat.A_8); // TODO

		// ===========================================================
		// Constants
		// ===========================================================

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

		private final Config mBitmapConfig;
		private final PixelFormat mPixelFormat;

		// ===========================================================
		// Constructors
		// ===========================================================

		private BitmapTextureFormat(final Config pBitmapConfig, final PixelFormat pPixelFormat) {
			this.mBitmapConfig = pBitmapConfig;
			this.mPixelFormat = pPixelFormat;
		}

		public static BitmapTextureFormat fromPixelFormat(final PixelFormat pPixelFormat) {
			switch(pPixelFormat) {
				case RGBA_8888:
					return RGBA_8888;
				case RGBA_4444:
					return RGBA_4444;
				case RGB_565:
					return RGB_565;
				case A_8:
					return A_8;
				default:
					throw new IllegalArgumentException("Unsupported " + PixelFormat.class.getName() + ": '" + pPixelFormat + "'.");
			}
		}

		// ===========================================================
		// Getter & Setter
		// ===========================================================

		public Config getBitmapConfig() {
			return this.mBitmapConfig;
		}

		public PixelFormat getPixelFormat() {
			return this.mPixelFormat;
		}

		// ===========================================================
		// Methods from SuperClass/Interfaces
		// ===========================================================

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

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