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