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 import android.util.Pair; 23 24 import com.android.gallery3d.common.Utils; 25 26 import java.util.HashMap; 27 28 // UploadedTextures use a Bitmap for the content of the texture. 29 // 30 // Subclasses should implement onGetBitmap() to provide the Bitmap and 31 // implement onFreeBitmap(mBitmap) which will be called when the Bitmap 32 // is not needed anymore. 33 // 34 // isContentValid() is meaningful only when the isLoaded() returns true. 35 // It means whether the content needs to be updated. 36 // 37 // The user of this class should call recycle() when the texture is not 38 // needed anymore. 39 // 40 // By default an UploadedTexture is opaque (so it can be drawn faster without 41 // blending). The user or subclass can override it using setOpaque(). 42 public abstract class UploadedTexture extends BasicTexture { 43 44 // To prevent keeping allocation the borders, we store those used borders here. 45 // Since the length will be power of two, it won't use too much memory. 46 private static HashMap<BorderKey, Bitmap> sBorderLines = new HashMap<BorderKey, Bitmap>(); 47 48 private static class BorderKey extends Pair<Config, Integer> { 49 public BorderKey(Config config, boolean vertical, int length) { 50 super(config, vertical ? length : -length); 51 } 52 } 53 54 private boolean mContentValid = true; 55 protected Bitmap mBitmap; 56 57 protected UploadedTexture() { 58 super(null, 0, STATE_UNLOADED); 59 } 60 61 private static Bitmap getBorderLine(boolean vertical, Config config, int length) { 62 BorderKey key = new BorderKey(config, vertical, length); 63 Bitmap bitmap = sBorderLines.get(key); 64 if (bitmap == null) { 65 bitmap = vertical 66 ? Bitmap.createBitmap(1, length, config) 67 : Bitmap.createBitmap(length, 1, config); 68 sBorderLines.put(key, bitmap); 69 } 70 return bitmap; 71 } 72 73 private Bitmap getBitmap() { 74 if (mBitmap == null) { 75 mBitmap = onGetBitmap(); 76 int w = mBitmap.getWidth(); 77 int h = mBitmap.getHeight(); 78 if (mWidth == UNSPECIFIED) { 79 setSize(w, h); 80 } 81 } 82 return mBitmap; 83 } 84 85 private void freeBitmap() { 86 Utils.assertTrue(mBitmap != null); 87 onFreeBitmap(mBitmap); 88 mBitmap = null; 89 } 90 91 @Override 92 public int getWidth() { 93 if (mWidth == UNSPECIFIED) getBitmap(); 94 return mWidth; 95 } 96 97 @Override 98 public int getHeight() { 99 if (mWidth == UNSPECIFIED) getBitmap(); 100 return mHeight; 101 } 102 103 protected abstract Bitmap onGetBitmap(); 104 105 protected abstract void onFreeBitmap(Bitmap bitmap); 106 107 protected void invalidateContent() { 108 if (mBitmap != null) freeBitmap(); 109 mContentValid = false; 110 mWidth = UNSPECIFIED; 111 mHeight = UNSPECIFIED; 112 } 113 114 /** 115 * Whether the content on GPU is valid. 116 */ 117 public boolean isContentValid() { 118 return isLoaded() && mContentValid; 119 } 120 121 /** 122 * Updates the content on GPU's memory. 123 * @param canvas 124 */ 125 public void updateContent(GLCanvas canvas) { 126 if (!isLoaded()) { 127 uploadToCanvas(canvas); 128 } else if (!mContentValid) { 129 Bitmap bitmap = getBitmap(); 130 int format = GLUtils.getInternalFormat(bitmap); 131 int type = GLUtils.getType(bitmap); 132 canvas.texSubImage2D(this, 0, 0, bitmap, format, type); 133 freeBitmap(); 134 mContentValid = true; 135 } 136 } 137 138 private void uploadToCanvas(GLCanvas canvas) { 139 Bitmap bitmap = getBitmap(); 140 if (bitmap != null) { 141 try { 142 int bWidth = bitmap.getWidth(); 143 int bHeight = bitmap.getHeight(); 144 int texWidth = getTextureWidth(); 145 int texHeight = getTextureHeight(); 146 147 Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight); 148 149 // Upload the bitmap to a new texture. 150 mId = canvas.getGLId().generateTexture(); 151 canvas.setTextureParameters(this); 152 153 if (bWidth == texWidth && bHeight == texHeight) { 154 canvas.initializeTexture(this, bitmap); 155 } else { 156 int format = GLUtils.getInternalFormat(bitmap); 157 int type = GLUtils.getType(bitmap); 158 Config config = bitmap.getConfig(); 159 160 canvas.initializeTextureSize(this, format, type); 161 canvas.texSubImage2D(this, 0, 0, bitmap, format, type); 162 163 // Right border 164 if (bWidth < texWidth) { 165 Bitmap line = getBorderLine(true, config, texHeight); 166 canvas.texSubImage2D(this, bWidth, 0, line, format, type); 167 } 168 169 // Bottom border 170 if (bHeight < texHeight) { 171 Bitmap line = getBorderLine(false, config, texWidth); 172 canvas.texSubImage2D(this, 0, bHeight, line, format, type); 173 } 174 } 175 } finally { 176 freeBitmap(); 177 } 178 // Update texture state. 179 setAssociatedCanvas(canvas); 180 mState = STATE_LOADED; 181 mContentValid = true; 182 } else { 183 mState = STATE_ERROR; 184 throw new RuntimeException("Texture load fail, no bitmap"); 185 } 186 } 187 188 @Override 189 protected boolean onBind(GLCanvas canvas) { 190 updateContent(canvas); 191 return isContentValid(); 192 } 193 194 @Override 195 public void recycle() { 196 super.recycle(); 197 if (mBitmap != null) freeBitmap(); 198 } 199 } 200