| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.gallery3d.glrenderer; |
| |
| import android.util.Log; |
| |
| import com.android.gallery3d.common.Utils; |
| |
| import java.util.WeakHashMap; |
| |
| // BasicTexture is a Texture corresponds to a real GL texture. |
| // The state of a BasicTexture indicates whether its data is loaded to GL memory. |
| // If a BasicTexture is loaded into GL memory, it has a GL texture id. |
| public abstract class BasicTexture implements Texture { |
| |
| @SuppressWarnings("unused") |
| private static final String TAG = "BasicTexture"; |
| protected static final int UNSPECIFIED = -1; |
| |
| protected static final int STATE_UNLOADED = 0; |
| protected static final int STATE_LOADED = 1; |
| protected static final int STATE_ERROR = -1; |
| |
| // Log a warning if a texture is larger along a dimension |
| private static final int MAX_TEXTURE_SIZE = 4096; |
| |
| protected int mId = -1; |
| protected int mState; |
| |
| protected int mWidth = UNSPECIFIED; |
| protected int mHeight = UNSPECIFIED; |
| |
| protected int mTextureWidth; |
| protected int mTextureHeight; |
| |
| private boolean mHasBorder; |
| |
| protected GLCanvas mCanvasRef = null; |
| private static WeakHashMap<BasicTexture, Object> sAllTextures |
| = new WeakHashMap<BasicTexture, Object>(); |
| private static ThreadLocal sInFinalizer = new ThreadLocal(); |
| |
| protected BasicTexture(GLCanvas canvas, int id, int state) { |
| setAssociatedCanvas(canvas); |
| mId = id; |
| mState = state; |
| synchronized (sAllTextures) { |
| sAllTextures.put(this, null); |
| } |
| } |
| |
| protected BasicTexture() { |
| this(null, 0, STATE_UNLOADED); |
| } |
| |
| protected void setAssociatedCanvas(GLCanvas canvas) { |
| mCanvasRef = canvas; |
| } |
| |
| /** |
| * Sets the content size of this texture. In OpenGL, the actual texture |
| * size must be of power of 2, the size of the content may be smaller. |
| */ |
| public void setSize(int width, int height) { |
| mWidth = width; |
| mHeight = height; |
| mTextureWidth = width > 0 ? Utils.nextPowerOf2(width) : 0; |
| mTextureHeight = height > 0 ? Utils.nextPowerOf2(height) : 0; |
| if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) { |
| Log.w(TAG, String.format("texture is too large: %d x %d", |
| mTextureWidth, mTextureHeight), new Exception()); |
| } |
| } |
| |
| public boolean isFlippedVertically() { |
| return false; |
| } |
| |
| public int getId() { |
| return mId; |
| } |
| |
| @Override |
| public int getWidth() { |
| return mWidth; |
| } |
| |
| @Override |
| public int getHeight() { |
| return mHeight; |
| } |
| |
| // Returns the width rounded to the next power of 2. |
| public int getTextureWidth() { |
| return mTextureWidth; |
| } |
| |
| // Returns the height rounded to the next power of 2. |
| public int getTextureHeight() { |
| return mTextureHeight; |
| } |
| |
| // Returns true if the texture has one pixel transparent border around the |
| // actual content. This is used to avoid jigged edges. |
| // |
| // The jigged edges appear because we use GL_CLAMP_TO_EDGE for texture wrap |
| // mode (GL_CLAMP is not available in OpenGL ES), so a pixel partially |
| // covered by the texture will use the color of the edge texel. If we add |
| // the transparent border, the color of the edge texel will be mixed with |
| // appropriate amount of transparent. |
| // |
| // Currently our background is black, so we can draw the thumbnails without |
| // enabling blending. |
| public boolean hasBorder() { |
| return mHasBorder; |
| } |
| |
| protected void setBorder(boolean hasBorder) { |
| mHasBorder = hasBorder; |
| } |
| |
| @Override |
| public void draw(GLCanvas canvas, int x, int y) { |
| canvas.drawTexture(this, x, y, getWidth(), getHeight()); |
| } |
| |
| @Override |
| public void draw(GLCanvas canvas, int x, int y, int w, int h) { |
| canvas.drawTexture(this, x, y, w, h); |
| } |
| |
| // onBind is called before GLCanvas binds this texture. |
| // It should make sure the data is uploaded to GL memory. |
| abstract protected boolean onBind(GLCanvas canvas); |
| |
| // Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D). |
| abstract protected int getTarget(); |
| |
| public boolean isLoaded() { |
| return mState == STATE_LOADED; |
| } |
| |
| // recycle() is called when the texture will never be used again, |
| // so it can free all resources. |
| public void recycle() { |
| freeResource(); |
| } |
| |
| // yield() is called when the texture will not be used temporarily, |
| // so it can free some resources. |
| // The default implementation unloads the texture from GL memory, so |
| // the subclass should make sure it can reload the texture to GL memory |
| // later, or it will have to override this method. |
| public void yield() { |
| freeResource(); |
| } |
| |
| private void freeResource() { |
| GLCanvas canvas = mCanvasRef; |
| if (canvas != null && mId != -1) { |
| canvas.unloadTexture(this); |
| mId = -1; // Don't free it again. |
| } |
| mState = STATE_UNLOADED; |
| setAssociatedCanvas(null); |
| } |
| |
| @Override |
| protected void finalize() { |
| sInFinalizer.set(BasicTexture.class); |
| recycle(); |
| sInFinalizer.set(null); |
| } |
| |
| // This is for deciding if we can call Bitmap's recycle(). |
| // We cannot call Bitmap's recycle() in finalizer because at that point |
| // the finalizer of Bitmap may already be called so recycle() will crash. |
| public static boolean inFinalizer() { |
| return sInFinalizer.get() != null; |
| } |
| |
| public static void yieldAllTextures() { |
| synchronized (sAllTextures) { |
| for (BasicTexture t : sAllTextures.keySet()) { |
| t.yield(); |
| } |
| } |
| } |
| |
| public static void invalidateAllTextures() { |
| synchronized (sAllTextures) { |
| for (BasicTexture t : sAllTextures.keySet()) { |
| t.mState = STATE_UNLOADED; |
| t.setAssociatedCanvas(null); |
| } |
| } |
| } |
| } |