blob: e0254437eecea7b618b694ff2be8db0be37f1b7c [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.replica.replicaisland;
import java.io.IOException;
import java.io.InputStream;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11Ext;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLU;
import android.opengl.GLUtils;
/**
* The Texture Library manages all textures in the game. Textures are pooled and handed out to
* requesting parties via allocateTexture(). However, the texture data itself is not immediately
* loaded at that time; it may have already been loaded or it may be loaded in the future via
* a call to loadTexture() or loadAllTextures(). This allows Texture objects to be dispersed to
* various game systems and while the texture data itself is streamed in or loaded as necessary.
*/
public class TextureLibrary extends BaseObject {
// Textures are stored in a simple hash. This class implements its own array-based hash rather
// than using HashMap for performance.
Texture[] mTextureHash;
int[] mTextureNameWorkspace;
int[] mCropWorkspace;
static final int DEFAULT_SIZE = 512;
static BitmapFactory.Options sBitmapOptions = new BitmapFactory.Options();
public TextureLibrary() {
super();
mTextureHash = new Texture[DEFAULT_SIZE];
for (int x = 0; x < mTextureHash.length; x++) {
mTextureHash[x] = new Texture();
}
mTextureNameWorkspace = new int[1];
mCropWorkspace = new int[4];
sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
}
@Override
public void reset() {
removeAll();
}
/**
* Creates a Texture object that is mapped to the passed resource id. If a texture has already
* been allocated for this id, the previously allocated Texture object is returned.
* @param resourceID
* @return
*/
public Texture allocateTexture(int resourceID) {
Texture texture = getTextureByResource(resourceID);
if (texture == null) {
texture = addTexture(resourceID, -1, 0, 0);
}
return texture;
}
/** Loads a single texture into memory. Does nothing if the texture is already loaded. */
public Texture loadTexture(Context context, GL10 gl, int resourceID) {
Texture texture = allocateTexture(resourceID);
texture = loadBitmap(context, gl, texture);
return texture;
}
/** Loads all unloaded textures into OpenGL memory. Already-loaded textures are ignored. */
public void loadAll(Context context, GL10 gl) {
for (int x = 0; x < mTextureHash.length; x++) {
if (mTextureHash[x].resource != -1 && mTextureHash[x].loaded == false) {
loadBitmap(context, gl, mTextureHash[x]);
}
}
}
/** Flushes all textures from OpenGL memory */
public void deleteAll(GL10 gl) {
for (int x = 0; x < mTextureHash.length; x++) {
if (mTextureHash[x].resource != -1 && mTextureHash[x].loaded) {
assert mTextureHash[x].name != -1;
mTextureNameWorkspace[0] = mTextureHash[x].name;
mTextureHash[x].name = -1;
mTextureHash[x].loaded = false;
gl.glDeleteTextures(1, mTextureNameWorkspace, 0);
int error = gl.glGetError();
if (error != GL10.GL_NO_ERROR) {
DebugLog.d("Texture Delete", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + mTextureHash[x].resource);
}
assert error == GL10.GL_NO_ERROR;
}
}
}
/** Marks all textures as unloaded */
public void invalidateAll() {
for (int x = 0; x < mTextureHash.length; x++) {
if (mTextureHash[x].resource != -1 && mTextureHash[x].loaded) {
mTextureHash[x].name = -1;
mTextureHash[x].loaded = false;
}
}
}
/** Loads a bitmap into OpenGL and sets up the common parameters for 2D texture maps. */
protected Texture loadBitmap(Context context, GL10 gl, Texture texture) {
assert gl != null;
assert context != null;
assert texture != null;
if (texture.loaded == false && texture.resource != -1) {
gl.glGenTextures(1, mTextureNameWorkspace, 0);
int error = gl.glGetError();
if (error != GL10.GL_NO_ERROR) {
DebugLog.d("Texture Load 1", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + texture.resource);
}
assert error == GL10.GL_NO_ERROR;
int textureName = mTextureNameWorkspace[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);
error = gl.glGetError();
if (error != GL10.GL_NO_ERROR) {
DebugLog.d("Texture Load 2", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + texture.resource);
}
assert error == GL10.GL_NO_ERROR;
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE); //GL10.GL_REPLACE);
InputStream is = context.getResources().openRawResource(texture.resource);
Bitmap bitmap;
try {
bitmap = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
// Ignore.
}
}
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
error = gl.glGetError();
if (error != GL10.GL_NO_ERROR) {
DebugLog.d("Texture Load 3", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + texture.resource);
}
assert error == GL10.GL_NO_ERROR;
mCropWorkspace[0] = 0;
mCropWorkspace[1] = bitmap.getHeight();
mCropWorkspace[2] = bitmap.getWidth();
mCropWorkspace[3] = -bitmap.getHeight();
((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES,
mCropWorkspace, 0);
texture.name = textureName;
texture.width = bitmap.getWidth();
texture.height = bitmap.getHeight();
bitmap.recycle();
error = gl.glGetError();
if (error != GL10.GL_NO_ERROR) {
DebugLog.d("Texture Load 4", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + texture.resource);
}
assert error == GL10.GL_NO_ERROR;
texture.loaded = true;
}
return texture;
}
public boolean isTextureLoaded(int resourceID) {
return getTextureByResource(resourceID) != null;
}
/**
* Returns the texture associated with the passed Android resource ID.
* @param resourceID The resource ID of a bitmap defined in R.java.
* @return An associated Texture object, or null if there is no associated
* texture in the library.
*/
public Texture getTextureByResource(int resourceID) {
int index = getHashIndex(resourceID);
int realIndex = findFirstKey(index, resourceID);
Texture texture = null;
if (realIndex != -1) {
texture = mTextureHash[realIndex];
}
return texture;
}
private int getHashIndex(int id) {
return id % mTextureHash.length;
}
/**
* Locates the texture in the hash. This hash uses a simple linear probe chaining mechanism:
* if the hash slot is occupied by some other entry, the next empty array index is used.
* This is O(n) for the worst case (every slot is a cache miss) but the average case is
* constant time.
* @param startIndex
* @param key
* @return
*/
private int findFirstKey(int startIndex, int key) {
int index = -1;
for (int x = 0; x < mTextureHash.length; x++) {
final int actualIndex = (startIndex + x) % mTextureHash.length;
if (mTextureHash[actualIndex].resource == key) {
index = actualIndex;
break;
} else if (mTextureHash[actualIndex].resource == -1) {
break;
}
}
return index;
}
/** Inserts a texture into the hash */
protected Texture addTexture(int id, int name, int width, int height) {
int index = findFirstKey(getHashIndex(id), -1);
Texture texture = null;
assert index != -1;
if (index != -1) {
mTextureHash[index].resource = id;
mTextureHash[index].name = name;
mTextureHash[index].width = width;
mTextureHash[index].height = height;
texture = mTextureHash[index];
}
return texture;
}
public void removeAll() {
for (int x = 0; x < mTextureHash.length; x++) {
mTextureHash[x].reset();
}
}
}