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    private static final String TAG = "BasicTexture";
31    protected static final int UNSPECIFIED = -1;
32
33    protected static final int STATE_UNLOADED = 0;
34    protected static final int STATE_LOADED = 1;
35    protected static final int STATE_ERROR = -1;
36
37    // Log a warning if a texture is larger along a dimension
38    private static final int MAX_TEXTURE_SIZE = 4096;
39
40    protected int mId = -1;
41    protected int mState;
42
43    protected int mWidth = UNSPECIFIED;
44    protected int mHeight = UNSPECIFIED;
45
46    protected int mTextureWidth;
47    protected int mTextureHeight;
48
49    protected GLCanvas mCanvasRef = null;
50    private static WeakHashMap<BasicTexture, Object> sAllTextures
51            = new WeakHashMap<BasicTexture, Object>();
52    private static ThreadLocal sInFinalizer = new ThreadLocal();
53
54    protected BasicTexture(GLCanvas canvas, int id, int state) {
55        setAssociatedCanvas(canvas);
56        mId = id;
57        mState = state;
58        synchronized (sAllTextures) {
59            sAllTextures.put(this, null);
60        }
61    }
62
63    protected BasicTexture() {
64        this(null, 0, STATE_UNLOADED);
65    }
66
67    protected void setAssociatedCanvas(GLCanvas canvas) {
68        mCanvasRef = canvas;
69    }
70
71    /**
72     * Sets the content size of this texture. In OpenGL, the actual texture
73     * size must be of power of 2, the size of the content may be smaller.
74     */
75    public void setSize(int width, int height) {
76        mWidth = width;
77        mHeight = height;
78        mTextureWidth = width > 0 ? Utils.nextPowerOf2(width) : 0;
79        mTextureHeight = height > 0 ? Utils.nextPowerOf2(height) : 0;
80        if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) {
81            Log.w(TAG, String.format("texture is too large: %d x %d",
82                    mTextureWidth, mTextureHeight), new Exception());
83        }
84    }
85
86    public int getId() {
87        return mId;
88    }
89
90    @Override
91    public int getWidth() {
92        return mWidth;
93    }
94
95    @Override
96    public int getHeight() {
97        return mHeight;
98    }
99
100    // Returns the width rounded to the next power of 2.
101    public int getTextureWidth() {
102        return mTextureWidth;
103    }
104
105    // Returns the height rounded to the next power of 2.
106    public int getTextureHeight() {
107        return mTextureHeight;
108    }
109
110    @Override
111    public void draw(GLCanvas canvas, int x, int y) {
112        canvas.drawTexture(this, x, y, getWidth(), getHeight());
113    }
114
115    @Override
116    public void draw(GLCanvas canvas, int x, int y, int w, int h) {
117        canvas.drawTexture(this, x, y, w, h);
118    }
119
120    // onBind is called before GLCanvas binds this texture.
121    // It should make sure the data is uploaded to GL memory.
122    abstract protected boolean onBind(GLCanvas canvas);
123
124    public boolean isLoaded() {
125        return mState == STATE_LOADED;
126    }
127
128    // recycle() is called when the texture will never be used again,
129    // so it can free all resources.
130    public void recycle() {
131        freeResource();
132    }
133
134    // yield() is called when the texture will not be used temporarily,
135    // so it can free some resources.
136    // The default implementation unloads the texture from GL memory, so
137    // the subclass should make sure it can reload the texture to GL memory
138    // later, or it will have to override this method.
139    public void yield() {
140        freeResource();
141    }
142
143    private void freeResource() {
144        GLCanvas canvas = mCanvasRef;
145        if (canvas != null && mId != -1) {
146            canvas.unloadTexture(this);
147            mId = -1; // Don't free it again.
148        }
149        mState = STATE_UNLOADED;
150        setAssociatedCanvas(null);
151    }
152
153    @Override
154    protected void finalize() {
155        sInFinalizer.set(BasicTexture.class);
156        recycle();
157        sInFinalizer.set(null);
158    }
159
160    public static void yieldAllTextures() {
161        synchronized (sAllTextures) {
162            for (BasicTexture t : sAllTextures.keySet()) {
163                t.yield();
164            }
165        }
166    }
167
168    public static void invalidateAllTextures() {
169        synchronized (sAllTextures) {
170            for (BasicTexture t : sAllTextures.keySet()) {
171                t.mState = STATE_UNLOADED;
172                t.setAssociatedCanvas(null);
173            }
174        }
175    }
176}
177