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.util.Log;
20 
21 import com.android.gallery3d.common.Utils;
22 
23 import 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.
28 public 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 
BasicTexture(GLCanvas canvas, int id, int state)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 
BasicTexture()66     protected BasicTexture() {
67         this(null, 0, STATE_UNLOADED);
68     }
69 
setAssociatedCanvas(GLCanvas canvas)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      */
setSize(int width, int height)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 
isFlippedVertically()89     public boolean isFlippedVertically() {
90       return false;
91     }
92 
getId()93     public int getId() {
94         return mId;
95     }
96 
97     @Override
getWidth()98     public int getWidth() {
99         return mWidth;
100     }
101 
102     @Override
getHeight()103     public int getHeight() {
104         return mHeight;
105     }
106 
107     // Returns the width rounded to the next power of 2.
getTextureWidth()108     public int getTextureWidth() {
109         return mTextureWidth;
110     }
111 
112     // Returns the height rounded to the next power of 2.
getTextureHeight()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.
hasBorder()128     public boolean hasBorder() {
129         return mHasBorder;
130     }
131 
setBorder(boolean hasBorder)132     protected void setBorder(boolean hasBorder) {
133         mHasBorder = hasBorder;
134     }
135 
136     @Override
draw(GLCanvas canvas, int x, int y)137     public void draw(GLCanvas canvas, int x, int y) {
138         canvas.drawTexture(this, x, y, getWidth(), getHeight());
139     }
140 
141     @Override
draw(GLCanvas canvas, int x, int y, int w, int h)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.
onBind(GLCanvas canvas)148     abstract protected boolean onBind(GLCanvas canvas);
149 
150     // Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D).
getTarget()151     abstract protected int getTarget();
152 
isLoaded()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.
recycle()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.
yield()168     public void yield() {
169         freeResource();
170     }
171 
freeResource()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
finalize()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.
inFinalizer()192     public static boolean inFinalizer() {
193         return sInFinalizer.get() != null;
194     }
195 
yieldAllTextures()196     public static void yieldAllTextures() {
197         synchronized (sAllTextures) {
198             for (BasicTexture t : sAllTextures.keySet()) {
199                 t.yield();
200             }
201         }
202     }
203 
invalidateAllTextures()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