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.android.gallery3d.glrenderer; 18 19 import android.graphics.Bitmap; 20 import android.graphics.Bitmap.Config; 21 import android.opengl.GLUtils; 22 23 import com.android.gallery3d.common.Utils; 24 25 import java.util.HashMap; 26 27 import javax.microedition.khronos.opengles.GL11; 28 29 // UploadedTextures use a Bitmap for the content of the texture. 30 // 31 // Subclasses should implement onGetBitmap() to provide the Bitmap and 32 // implement onFreeBitmap(mBitmap) which will be called when the Bitmap 33 // is not needed anymore. 34 // 35 // isContentValid() is meaningful only when the isLoaded() returns true. 36 // It means whether the content needs to be updated. 37 // 38 // The user of this class should call recycle() when the texture is not 39 // needed anymore. 40 // 41 // By default an UploadedTexture is opaque (so it can be drawn faster without 42 // blending). The user or subclass can override it using setOpaque(). 43 public abstract class UploadedTexture extends BasicTexture { 44 45 // To prevent keeping allocation the borders, we store those used borders here. 46 // Since the length will be power of two, it won't use too much memory. 47 private static HashMap<BorderKey, Bitmap> sBorderLines = 48 new HashMap<BorderKey, Bitmap>(); 49 private static BorderKey sBorderKey = new BorderKey(); 50 51 @SuppressWarnings("unused") 52 private static final String TAG = "Texture"; 53 private boolean mContentValid = true; 54 55 // indicate this textures is being uploaded in background 56 private boolean mIsUploading = false; 57 private boolean mOpaque = true; 58 private boolean mThrottled = false; 59 private static int sUploadedCount; 60 private static final int UPLOAD_LIMIT = 100; 61 62 protected Bitmap mBitmap; 63 private int mBorder; 64 UploadedTexture()65 protected UploadedTexture() { 66 this(false); 67 } 68 UploadedTexture(boolean hasBorder)69 protected UploadedTexture(boolean hasBorder) { 70 super(null, 0, STATE_UNLOADED); 71 if (hasBorder) { 72 setBorder(true); 73 mBorder = 1; 74 } 75 } 76 setIsUploading(boolean uploading)77 protected void setIsUploading(boolean uploading) { 78 mIsUploading = uploading; 79 } 80 isUploading()81 public boolean isUploading() { 82 return mIsUploading; 83 } 84 85 private static class BorderKey implements Cloneable { 86 public boolean vertical; 87 public Config config; 88 public int length; 89 90 @Override hashCode()91 public int hashCode() { 92 int x = config.hashCode() ^ length; 93 return vertical ? x : -x; 94 } 95 96 @Override equals(Object object)97 public boolean equals(Object object) { 98 if (!(object instanceof BorderKey)) return false; 99 BorderKey o = (BorderKey) object; 100 return vertical == o.vertical 101 && config == o.config && length == o.length; 102 } 103 104 @Override clone()105 public BorderKey clone() { 106 try { 107 return (BorderKey) super.clone(); 108 } catch (CloneNotSupportedException e) { 109 throw new AssertionError(e); 110 } 111 } 112 } 113 setThrottled(boolean throttled)114 protected void setThrottled(boolean throttled) { 115 mThrottled = throttled; 116 } 117 getBorderLine( boolean vertical, Config config, int length)118 private static Bitmap getBorderLine( 119 boolean vertical, Config config, int length) { 120 BorderKey key = sBorderKey; 121 key.vertical = vertical; 122 key.config = config; 123 key.length = length; 124 Bitmap bitmap = sBorderLines.get(key); 125 if (bitmap == null) { 126 bitmap = vertical 127 ? Bitmap.createBitmap(1, length, config) 128 : Bitmap.createBitmap(length, 1, config); 129 sBorderLines.put(key.clone(), bitmap); 130 } 131 return bitmap; 132 } 133 getBitmap()134 private Bitmap getBitmap() { 135 if (mBitmap == null) { 136 mBitmap = onGetBitmap(); 137 int w = mBitmap.getWidth() + mBorder * 2; 138 int h = mBitmap.getHeight() + mBorder * 2; 139 if (mWidth == UNSPECIFIED) { 140 setSize(w, h); 141 } 142 } 143 return mBitmap; 144 } 145 freeBitmap()146 private void freeBitmap() { 147 Utils.assertTrue(mBitmap != null); 148 onFreeBitmap(mBitmap); 149 mBitmap = null; 150 } 151 152 @Override getWidth()153 public int getWidth() { 154 if (mWidth == UNSPECIFIED) getBitmap(); 155 return mWidth; 156 } 157 158 @Override getHeight()159 public int getHeight() { 160 if (mWidth == UNSPECIFIED) getBitmap(); 161 return mHeight; 162 } 163 onGetBitmap()164 protected abstract Bitmap onGetBitmap(); 165 onFreeBitmap(Bitmap bitmap)166 protected abstract void onFreeBitmap(Bitmap bitmap); 167 invalidateContent()168 protected void invalidateContent() { 169 if (mBitmap != null) freeBitmap(); 170 mContentValid = false; 171 mWidth = UNSPECIFIED; 172 mHeight = UNSPECIFIED; 173 } 174 175 /** 176 * Whether the content on GPU is valid. 177 */ isContentValid()178 public boolean isContentValid() { 179 return isLoaded() && mContentValid; 180 } 181 182 /** 183 * Updates the content on GPU's memory. 184 * @param canvas 185 */ updateContent(GLCanvas canvas)186 public void updateContent(GLCanvas canvas) { 187 if (!isLoaded()) { 188 if (mThrottled && ++sUploadedCount > UPLOAD_LIMIT) { 189 return; 190 } 191 uploadToCanvas(canvas); 192 } else if (!mContentValid) { 193 Bitmap bitmap = getBitmap(); 194 int format = GLUtils.getInternalFormat(bitmap); 195 int type = GLUtils.getType(bitmap); 196 canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type); 197 freeBitmap(); 198 mContentValid = true; 199 } 200 } 201 resetUploadLimit()202 public static void resetUploadLimit() { 203 sUploadedCount = 0; 204 } 205 uploadLimitReached()206 public static boolean uploadLimitReached() { 207 return sUploadedCount > UPLOAD_LIMIT; 208 } 209 uploadToCanvas(GLCanvas canvas)210 private void uploadToCanvas(GLCanvas canvas) { 211 212 Bitmap bitmap = getBitmap(); 213 if (bitmap != null) { 214 try { 215 int bWidth = bitmap.getWidth(); 216 int bHeight = bitmap.getHeight(); 217 int width = bWidth + mBorder * 2; 218 int height = bHeight + mBorder * 2; 219 int texWidth = getTextureWidth(); 220 int texHeight = getTextureHeight(); 221 222 Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight); 223 224 // Upload the bitmap to a new texture. 225 mId = canvas.getGLId().generateTexture(); 226 canvas.setTextureParameters(this); 227 228 if (bWidth == texWidth && bHeight == texHeight) { 229 canvas.initializeTexture(this, bitmap); 230 } else { 231 int format = GLUtils.getInternalFormat(bitmap); 232 int type = GLUtils.getType(bitmap); 233 Config config = bitmap.getConfig(); 234 235 canvas.initializeTextureSize(this, format, type); 236 canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type); 237 238 if (mBorder > 0) { 239 // Left border 240 Bitmap line = getBorderLine(true, config, texHeight); 241 canvas.texSubImage2D(this, 0, 0, line, format, type); 242 243 // Top border 244 line = getBorderLine(false, config, texWidth); 245 canvas.texSubImage2D(this, 0, 0, line, format, type); 246 } 247 248 // Right border 249 if (mBorder + bWidth < texWidth) { 250 Bitmap line = getBorderLine(true, config, texHeight); 251 canvas.texSubImage2D(this, mBorder + bWidth, 0, line, format, type); 252 } 253 254 // Bottom border 255 if (mBorder + bHeight < texHeight) { 256 Bitmap line = getBorderLine(false, config, texWidth); 257 canvas.texSubImage2D(this, 0, mBorder + bHeight, line, format, type); 258 } 259 } 260 } finally { 261 freeBitmap(); 262 } 263 // Update texture state. 264 setAssociatedCanvas(canvas); 265 mState = STATE_LOADED; 266 mContentValid = true; 267 } else { 268 mState = STATE_ERROR; 269 throw new RuntimeException("Texture load fail, no bitmap"); 270 } 271 } 272 273 @Override onBind(GLCanvas canvas)274 protected boolean onBind(GLCanvas canvas) { 275 updateContent(canvas); 276 return isContentValid(); 277 } 278 279 @Override getTarget()280 protected int getTarget() { 281 return GL11.GL_TEXTURE_2D; 282 } 283 setOpaque(boolean isOpaque)284 public void setOpaque(boolean isOpaque) { 285 mOpaque = isOpaque; 286 } 287 288 @Override isOpaque()289 public boolean isOpaque() { 290 return mOpaque; 291 } 292 293 @Override recycle()294 public void recycle() { 295 super.recycle(); 296 if (mBitmap != null) freeBitmap(); 297 } 298 } 299