1104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/*
2104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Copyright (C) 2010 The Android Open Source Project
3104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
4104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * you may not use this file except in compliance with the License.
6104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * You may obtain a copy of the License at
7104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
8104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
10104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Unless required by applicable law or agreed to in writing, software
11104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * See the License for the specific language governing permissions and
14104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * limitations under the License.
15104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
16104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
17104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapackage com.android.gallery3d.glrenderer;
18104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
19104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.util.Log;
20104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
21104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport com.android.gallery3d.common.Utils;
22104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
23104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.util.WeakHashMap;
24104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
25104c45677660586026a7e74ef8c47d396403d50eMichael Jurka// BasicTexture is a Texture corresponds to a real GL texture.
26104c45677660586026a7e74ef8c47d396403d50eMichael Jurka// The state of a BasicTexture indicates whether its data is loaded to GL memory.
27104c45677660586026a7e74ef8c47d396403d50eMichael Jurka// If a BasicTexture is loaded into GL memory, it has a GL texture id.
28104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapublic abstract class BasicTexture implements Texture {
29104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
30104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @SuppressWarnings("unused")
31104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String TAG = "BasicTexture";
32104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static final int UNSPECIFIED = -1;
33104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
34104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static final int STATE_UNLOADED = 0;
35104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static final int STATE_LOADED = 1;
36104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected static final int STATE_ERROR = -1;
37104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
38104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Log a warning if a texture is larger along a dimension
39104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int MAX_TEXTURE_SIZE = 4096;
40104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
41104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int mId = -1;
42104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int mState;
43104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
44104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int mWidth = UNSPECIFIED;
45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int mHeight = UNSPECIFIED;
46104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
47104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int mTextureWidth;
48104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected int mTextureHeight;
49104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
50104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private boolean mHasBorder;
51104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
52104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected GLCanvas mCanvasRef = null;
53104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static WeakHashMap<BasicTexture, Object> sAllTextures
54104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            = new WeakHashMap<BasicTexture, Object>();
55104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static ThreadLocal sInFinalizer = new ThreadLocal();
56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
57104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected BasicTexture(GLCanvas canvas, int id, int state) {
58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setAssociatedCanvas(canvas);
59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mId = id;
60104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mState = state;
61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        synchronized (sAllTextures) {
62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            sAllTextures.put(this, null);
63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
66104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected BasicTexture() {
67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        this(null, 0, STATE_UNLOADED);
68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
69104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void setAssociatedCanvas(GLCanvas canvas) {
71104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mCanvasRef = canvas;
72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
73104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
74104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
75104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Sets the content size of this texture. In OpenGL, the actual texture
76104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * size must be of power of 2, the size of the content may be smaller.
77104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
78104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void setSize(int width, int height) {
79104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mWidth = width;
80104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mHeight = height;
81104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTextureWidth = width > 0 ? Utils.nextPowerOf2(width) : 0;
82104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mTextureHeight = height > 0 ? Utils.nextPowerOf2(height) : 0;
83104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) {
84104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.w(TAG, String.format("texture is too large: %d x %d",
85104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mTextureWidth, mTextureHeight), new Exception());
86104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
87104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
88104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
89104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public boolean isFlippedVertically() {
90104c45677660586026a7e74ef8c47d396403d50eMichael Jurka      return false;
91104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
92104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
93104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public int getId() {
94104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mId;
95104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
96104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
97104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
98104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public int getWidth() {
99104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mWidth;
100104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
101104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
102104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
103104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public int getHeight() {
104104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mHeight;
105104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
106104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
107104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Returns the width rounded to the next power of 2.
108104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public int getTextureWidth() {
109104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTextureWidth;
110104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
111104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
112104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Returns the height rounded to the next power of 2.
113104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public int getTextureHeight() {
114104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mTextureHeight;
115104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
116104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
117104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Returns true if the texture has one pixel transparent border around the
118104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // actual content. This is used to avoid jigged edges.
119104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    //
120104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // The jigged edges appear because we use GL_CLAMP_TO_EDGE for texture wrap
121104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // mode (GL_CLAMP is not available in OpenGL ES), so a pixel partially
122104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // covered by the texture will use the color of the edge texel. If we add
123104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // the transparent border, the color of the edge texel will be mixed with
124104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // appropriate amount of transparent.
125104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    //
126104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Currently our background is black, so we can draw the thumbnails without
127104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // enabling blending.
128104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public boolean hasBorder() {
129104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mHasBorder;
130104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
131104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
132104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void setBorder(boolean hasBorder) {
133104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mHasBorder = hasBorder;
134104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
135104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
136104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
137104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void draw(GLCanvas canvas, int x, int y) {
138104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        canvas.drawTexture(this, x, y, getWidth(), getHeight());
139104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
140104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
141104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
142104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void draw(GLCanvas canvas, int x, int y, int w, int h) {
143104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        canvas.drawTexture(this, x, y, w, h);
144104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
145104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
146104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // onBind is called before GLCanvas binds this texture.
147104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // It should make sure the data is uploaded to GL memory.
148104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    abstract protected boolean onBind(GLCanvas canvas);
149104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
150104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D).
151104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    abstract protected int getTarget();
152104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
153104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public boolean isLoaded() {
154104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mState == STATE_LOADED;
155104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
156104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
157104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // recycle() is called when the texture will never be used again,
158104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // so it can free all resources.
159104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void recycle() {
160104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        freeResource();
161104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
162104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
163104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // yield() is called when the texture will not be used temporarily,
164104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // so it can free some resources.
165104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // The default implementation unloads the texture from GL memory, so
166104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // the subclass should make sure it can reload the texture to GL memory
167104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // later, or it will have to override this method.
168104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void yield() {
169104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        freeResource();
170104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
171104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
172104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private void freeResource() {
173104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        GLCanvas canvas = mCanvasRef;
174104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (canvas != null && mId != -1) {
175104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            canvas.unloadTexture(this);
176104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mId = -1; // Don't free it again.
177104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
178104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mState = STATE_UNLOADED;
179104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setAssociatedCanvas(null);
180104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
181104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
182104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
183104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void finalize() {
184104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        sInFinalizer.set(BasicTexture.class);
185104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        recycle();
186104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        sInFinalizer.set(null);
187104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
188104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
189104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // This is for deciding if we can call Bitmap's recycle().
190104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // We cannot call Bitmap's recycle() in finalizer because at that point
191104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // the finalizer of Bitmap may already be called so recycle() will crash.
192104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static boolean inFinalizer() {
193104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return sInFinalizer.get() != null;
194104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
195104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
196104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static void yieldAllTextures() {
197104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        synchronized (sAllTextures) {
198104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            for (BasicTexture t : sAllTextures.keySet()) {
199104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                t.yield();
200104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
201104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
202104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
203104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
204104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static void invalidateAllTextures() {
205104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        synchronized (sAllTextures) {
206104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            for (BasicTexture t : sAllTextures.keySet()) {
207104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                t.mState = STATE_UNLOADED;
208104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                t.setAssociatedCanvas(null);
209104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
210104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
211104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
212104c45677660586026a7e74ef8c47d396403d50eMichael Jurka}
213