1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.replica.replicaisland; 18 19 import java.io.IOException; 20 import java.io.InputStream; 21 22 import javax.microedition.khronos.opengles.GL10; 23 import javax.microedition.khronos.opengles.GL11; 24 import javax.microedition.khronos.opengles.GL11Ext; 25 26 import android.content.Context; 27 import android.graphics.Bitmap; 28 import android.graphics.BitmapFactory; 29 import android.opengl.GLU; 30 import android.opengl.GLUtils; 31 32 /** 33 * The Texture Library manages all textures in the game. Textures are pooled and handed out to 34 * requesting parties via allocateTexture(). However, the texture data itself is not immediately 35 * loaded at that time; it may have already been loaded or it may be loaded in the future via 36 * a call to loadTexture() or loadAllTextures(). This allows Texture objects to be dispersed to 37 * various game systems and while the texture data itself is streamed in or loaded as necessary. 38 */ 39 public class TextureLibrary extends BaseObject { 40 // Textures are stored in a simple hash. This class implements its own array-based hash rather 41 // than using HashMap for performance. 42 Texture[] mTextureHash; 43 int[] mTextureNameWorkspace; 44 int[] mCropWorkspace; 45 static final int DEFAULT_SIZE = 512; 46 static BitmapFactory.Options sBitmapOptions = new BitmapFactory.Options(); 47 48 public TextureLibrary() { 49 super(); 50 mTextureHash = new Texture[DEFAULT_SIZE]; 51 for (int x = 0; x < mTextureHash.length; x++) { 52 mTextureHash[x] = new Texture(); 53 } 54 55 mTextureNameWorkspace = new int[1]; 56 mCropWorkspace = new int[4]; 57 58 sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565; 59 } 60 61 @Override 62 public void reset() { 63 removeAll(); 64 } 65 66 /** 67 * Creates a Texture object that is mapped to the passed resource id. If a texture has already 68 * been allocated for this id, the previously allocated Texture object is returned. 69 * @param resourceID 70 * @return 71 */ 72 public Texture allocateTexture(int resourceID) { 73 Texture texture = getTextureByResource(resourceID); 74 if (texture == null) { 75 texture = addTexture(resourceID, -1, 0, 0); 76 } 77 78 return texture; 79 } 80 81 /** Loads a single texture into memory. Does nothing if the texture is already loaded. */ 82 public Texture loadTexture(Context context, GL10 gl, int resourceID) { 83 Texture texture = allocateTexture(resourceID); 84 texture = loadBitmap(context, gl, texture); 85 return texture; 86 } 87 88 /** Loads all unloaded textures into OpenGL memory. Already-loaded textures are ignored. */ 89 public void loadAll(Context context, GL10 gl) { 90 for (int x = 0; x < mTextureHash.length; x++) { 91 if (mTextureHash[x].resource != -1 && mTextureHash[x].loaded == false) { 92 loadBitmap(context, gl, mTextureHash[x]); 93 } 94 } 95 } 96 97 /** Flushes all textures from OpenGL memory */ 98 public void deleteAll(GL10 gl) { 99 for (int x = 0; x < mTextureHash.length; x++) { 100 if (mTextureHash[x].resource != -1 && mTextureHash[x].loaded) { 101 assert mTextureHash[x].name != -1; 102 mTextureNameWorkspace[0] = mTextureHash[x].name; 103 mTextureHash[x].name = -1; 104 mTextureHash[x].loaded = false; 105 gl.glDeleteTextures(1, mTextureNameWorkspace, 0); 106 int error = gl.glGetError(); 107 if (error != GL10.GL_NO_ERROR) { 108 DebugLog.d("Texture Delete", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + mTextureHash[x].resource); 109 } 110 111 assert error == GL10.GL_NO_ERROR; 112 } 113 } 114 } 115 116 /** Marks all textures as unloaded */ 117 public void invalidateAll() { 118 for (int x = 0; x < mTextureHash.length; x++) { 119 if (mTextureHash[x].resource != -1 && mTextureHash[x].loaded) { 120 mTextureHash[x].name = -1; 121 mTextureHash[x].loaded = false; 122 } 123 } 124 } 125 126 /** Loads a bitmap into OpenGL and sets up the common parameters for 2D texture maps. */ 127 protected Texture loadBitmap(Context context, GL10 gl, Texture texture) { 128 assert gl != null; 129 assert context != null; 130 assert texture != null; 131 if (texture.loaded == false && texture.resource != -1) { 132 gl.glGenTextures(1, mTextureNameWorkspace, 0); 133 134 int error = gl.glGetError(); 135 if (error != GL10.GL_NO_ERROR) { 136 DebugLog.d("Texture Load 1", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + texture.resource); 137 } 138 139 assert error == GL10.GL_NO_ERROR; 140 141 int textureName = mTextureNameWorkspace[0]; 142 143 gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName); 144 145 error = gl.glGetError(); 146 if (error != GL10.GL_NO_ERROR) { 147 DebugLog.d("Texture Load 2", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + texture.resource); 148 } 149 150 assert error == GL10.GL_NO_ERROR; 151 152 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); 153 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); 154 155 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); 156 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); 157 158 gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE); //GL10.GL_REPLACE); 159 160 InputStream is = context.getResources().openRawResource(texture.resource); 161 Bitmap bitmap; 162 try { 163 bitmap = BitmapFactory.decodeStream(is); 164 } finally { 165 try { 166 is.close(); 167 } catch (IOException e) { 168 e.printStackTrace(); 169 // Ignore. 170 } 171 } 172 173 GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); 174 175 error = gl.glGetError(); 176 if (error != GL10.GL_NO_ERROR) { 177 DebugLog.d("Texture Load 3", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + texture.resource); 178 } 179 180 assert error == GL10.GL_NO_ERROR; 181 182 mCropWorkspace[0] = 0; 183 mCropWorkspace[1] = bitmap.getHeight(); 184 mCropWorkspace[2] = bitmap.getWidth(); 185 mCropWorkspace[3] = -bitmap.getHeight(); 186 187 ((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES, 188 mCropWorkspace, 0); 189 190 texture.name = textureName; 191 texture.width = bitmap.getWidth(); 192 texture.height = bitmap.getHeight(); 193 194 bitmap.recycle(); 195 196 error = gl.glGetError(); 197 if (error != GL10.GL_NO_ERROR) { 198 DebugLog.d("Texture Load 4", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + texture.resource); 199 } 200 201 assert error == GL10.GL_NO_ERROR; 202 203 texture.loaded = true; 204 205 } 206 207 return texture; 208 } 209 210 public boolean isTextureLoaded(int resourceID) { 211 return getTextureByResource(resourceID) != null; 212 } 213 214 /** 215 * Returns the texture associated with the passed Android resource ID. 216 * @param resourceID The resource ID of a bitmap defined in R.java. 217 * @return An associated Texture object, or null if there is no associated 218 * texture in the library. 219 */ 220 public Texture getTextureByResource(int resourceID) { 221 int index = getHashIndex(resourceID); 222 int realIndex = findFirstKey(index, resourceID); 223 Texture texture = null; 224 if (realIndex != -1) { 225 texture = mTextureHash[realIndex]; 226 } 227 return texture; 228 } 229 230 private int getHashIndex(int id) { 231 return id % mTextureHash.length; 232 } 233 234 /** 235 * Locates the texture in the hash. This hash uses a simple linear probe chaining mechanism: 236 * if the hash slot is occupied by some other entry, the next empty array index is used. 237 * This is O(n) for the worst case (every slot is a cache miss) but the average case is 238 * constant time. 239 * @param startIndex 240 * @param key 241 * @return 242 */ 243 private int findFirstKey(int startIndex, int key) { 244 int index = -1; 245 for (int x = 0; x < mTextureHash.length; x++) { 246 final int actualIndex = (startIndex + x) % mTextureHash.length; 247 if (mTextureHash[actualIndex].resource == key) { 248 index = actualIndex; 249 break; 250 } else if (mTextureHash[actualIndex].resource == -1) { 251 break; 252 } 253 } 254 return index; 255 } 256 257 /** Inserts a texture into the hash */ 258 protected Texture addTexture(int id, int name, int width, int height) { 259 int index = findFirstKey(getHashIndex(id), -1); 260 Texture texture = null; 261 assert index != -1; 262 263 if (index != -1) { 264 mTextureHash[index].resource = id; 265 mTextureHash[index].name = name; 266 mTextureHash[index].width = width; 267 mTextureHash[index].height = height; 268 texture = mTextureHash[index]; 269 } 270 271 return texture; 272 } 273 274 public void removeAll() { 275 for (int x = 0; x < mTextureHash.length; x++) { 276 mTextureHash[x].reset(); 277 } 278 } 279 280 } 281