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 17package com.android.gallery3d.glrenderer; 18 19import android.util.Log; 20 21import com.android.gallery3d.common.Utils; 22 23import java.util.WeakHashMap; 24 25// BasicTexture is a Texture corresponds to a real GL texture. 26// The state of a BasicTexture indicates whether its data is loaded to GL memory. 27// If a BasicTexture is loaded into GL memory, it has a GL texture id. 28public abstract class BasicTexture implements Texture { 29 30 @SuppressWarnings("unused") 31 private static final String TAG = "BasicTexture"; 32 protected static final int UNSPECIFIED = -1; 33 34 protected static final int STATE_UNLOADED = 0; 35 protected static final int STATE_LOADED = 1; 36 protected static final int STATE_ERROR = -1; 37 38 // Log a warning if a texture is larger along a dimension 39 private static final int MAX_TEXTURE_SIZE = 4096; 40 41 protected int mId = -1; 42 protected int mState; 43 44 protected int mWidth = UNSPECIFIED; 45 protected int mHeight = UNSPECIFIED; 46 47 protected int mTextureWidth; 48 protected int mTextureHeight; 49 50 private boolean mHasBorder; 51 52 protected GLCanvas mCanvasRef = null; 53 private static WeakHashMap<BasicTexture, Object> sAllTextures 54 = new WeakHashMap<BasicTexture, Object>(); 55 private static ThreadLocal sInFinalizer = new ThreadLocal(); 56 57 protected BasicTexture(GLCanvas canvas, int id, int state) { 58 setAssociatedCanvas(canvas); 59 mId = id; 60 mState = state; 61 synchronized (sAllTextures) { 62 sAllTextures.put(this, null); 63 } 64 } 65 66 protected BasicTexture() { 67 this(null, 0, STATE_UNLOADED); 68 } 69 70 protected void setAssociatedCanvas(GLCanvas canvas) { 71 mCanvasRef = canvas; 72 } 73 74 /** 75 * Sets the content size of this texture. In OpenGL, the actual texture 76 * size must be of power of 2, the size of the content may be smaller. 77 */ 78 public void setSize(int width, int height) { 79 mWidth = width; 80 mHeight = height; 81 mTextureWidth = width > 0 ? Utils.nextPowerOf2(width) : 0; 82 mTextureHeight = height > 0 ? Utils.nextPowerOf2(height) : 0; 83 if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) { 84 Log.w(TAG, String.format("texture is too large: %d x %d", 85 mTextureWidth, mTextureHeight), new Exception()); 86 } 87 } 88 89 public boolean isFlippedVertically() { 90 return false; 91 } 92 93 public int getId() { 94 return mId; 95 } 96 97 @Override 98 public int getWidth() { 99 return mWidth; 100 } 101 102 @Override 103 public int getHeight() { 104 return mHeight; 105 } 106 107 // Returns the width rounded to the next power of 2. 108 public int getTextureWidth() { 109 return mTextureWidth; 110 } 111 112 // Returns the height rounded to the next power of 2. 113 public int getTextureHeight() { 114 return mTextureHeight; 115 } 116 117 // Returns true if the texture has one pixel transparent border around the 118 // actual content. This is used to avoid jigged edges. 119 // 120 // The jigged edges appear because we use GL_CLAMP_TO_EDGE for texture wrap 121 // mode (GL_CLAMP is not available in OpenGL ES), so a pixel partially 122 // covered by the texture will use the color of the edge texel. If we add 123 // the transparent border, the color of the edge texel will be mixed with 124 // appropriate amount of transparent. 125 // 126 // Currently our background is black, so we can draw the thumbnails without 127 // enabling blending. 128 public boolean hasBorder() { 129 return mHasBorder; 130 } 131 132 protected void setBorder(boolean hasBorder) { 133 mHasBorder = hasBorder; 134 } 135 136 @Override 137 public void draw(GLCanvas canvas, int x, int y) { 138 canvas.drawTexture(this, x, y, getWidth(), getHeight()); 139 } 140 141 @Override 142 public void draw(GLCanvas canvas, int x, int y, int w, int h) { 143 canvas.drawTexture(this, x, y, w, h); 144 } 145 146 // onBind is called before GLCanvas binds this texture. 147 // It should make sure the data is uploaded to GL memory. 148 abstract protected boolean onBind(GLCanvas canvas); 149 150 // Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D). 151 abstract protected int getTarget(); 152 153 public boolean isLoaded() { 154 return mState == STATE_LOADED; 155 } 156 157 // recycle() is called when the texture will never be used again, 158 // so it can free all resources. 159 public void recycle() { 160 freeResource(); 161 } 162 163 // yield() is called when the texture will not be used temporarily, 164 // so it can free some resources. 165 // The default implementation unloads the texture from GL memory, so 166 // the subclass should make sure it can reload the texture to GL memory 167 // later, or it will have to override this method. 168 public void yield() { 169 freeResource(); 170 } 171 172 private void freeResource() { 173 GLCanvas canvas = mCanvasRef; 174 if (canvas != null && mId != -1) { 175 canvas.unloadTexture(this); 176 mId = -1; // Don't free it again. 177 } 178 mState = STATE_UNLOADED; 179 setAssociatedCanvas(null); 180 } 181 182 @Override 183 protected void finalize() { 184 sInFinalizer.set(BasicTexture.class); 185 recycle(); 186 sInFinalizer.set(null); 187 } 188 189 // This is for deciding if we can call Bitmap's recycle(). 190 // We cannot call Bitmap's recycle() in finalizer because at that point 191 // the finalizer of Bitmap may already be called so recycle() will crash. 192 public static boolean inFinalizer() { 193 return sInFinalizer.get() != null; 194 } 195 196 public static void yieldAllTextures() { 197 synchronized (sAllTextures) { 198 for (BasicTexture t : sAllTextures.keySet()) { 199 t.yield(); 200 } 201 } 202 } 203 204 public static void invalidateAllTextures() { 205 synchronized (sAllTextures) { 206 for (BasicTexture t : sAllTextures.keySet()) { 207 t.mState = STATE_UNLOADED; 208 t.setAssociatedCanvas(null); 209 } 210 } 211 } 212} 213