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