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