Home | History | Annotate | Download | only in glrenderer
      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 
     17 package com.android.gallery3d.glrenderer;
     18 
     19 import android.util.Log;
     20 
     21 import com.android.gallery3d.common.Utils;
     22 
     23 import 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.
     28 public 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     private boolean mHasBorder;
     50 
     51     protected GLCanvas mCanvasRef = null;
     52     private static WeakHashMap<BasicTexture, Object> sAllTextures
     53             = new WeakHashMap<BasicTexture, Object>();
     54     private static ThreadLocal sInFinalizer = new ThreadLocal();
     55 
     56     protected BasicTexture(GLCanvas canvas, int id, int state) {
     57         setAssociatedCanvas(canvas);
     58         mId = id;
     59         mState = state;
     60         synchronized (sAllTextures) {
     61             sAllTextures.put(this, null);
     62         }
     63     }
     64 
     65     protected BasicTexture() {
     66         this(null, 0, STATE_UNLOADED);
     67     }
     68 
     69     protected void setAssociatedCanvas(GLCanvas canvas) {
     70         mCanvasRef = canvas;
     71     }
     72 
     73     /**
     74      * Sets the content size of this texture. In OpenGL, the actual texture
     75      * size must be of power of 2, the size of the content may be smaller.
     76      */
     77     public void setSize(int width, int height) {
     78         mWidth = width;
     79         mHeight = height;
     80         mTextureWidth = width > 0 ? Utils.nextPowerOf2(width) : 0;
     81         mTextureHeight = height > 0 ? Utils.nextPowerOf2(height) : 0;
     82         if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) {
     83             Log.w(TAG, String.format("texture is too large: %d x %d",
     84                     mTextureWidth, mTextureHeight), new Exception());
     85         }
     86     }
     87 
     88     public boolean isFlippedVertically() {
     89       return false;
     90     }
     91 
     92     public int getId() {
     93         return mId;
     94     }
     95 
     96     @Override
     97     public int getWidth() {
     98         return mWidth;
     99     }
    100 
    101     @Override
    102     public int getHeight() {
    103         return mHeight;
    104     }
    105 
    106     // Returns the width rounded to the next power of 2.
    107     public int getTextureWidth() {
    108         return mTextureWidth;
    109     }
    110 
    111     // Returns the height rounded to the next power of 2.
    112     public int getTextureHeight() {
    113         return mTextureHeight;
    114     }
    115 
    116     // Returns true if the texture has one pixel transparent border around the
    117     // actual content. This is used to avoid jigged edges.
    118     //
    119     // The jigged edges appear because we use GL_CLAMP_TO_EDGE for texture wrap
    120     // mode (GL_CLAMP is not available in OpenGL ES), so a pixel partially
    121     // covered by the texture will use the color of the edge texel. If we add
    122     // the transparent border, the color of the edge texel will be mixed with
    123     // appropriate amount of transparent.
    124     //
    125     // Currently our background is black, so we can draw the thumbnails without
    126     // enabling blending.
    127     public boolean hasBorder() {
    128         return mHasBorder;
    129     }
    130 
    131     protected void setBorder(boolean hasBorder) {
    132         mHasBorder = hasBorder;
    133     }
    134 
    135     @Override
    136     public void draw(GLCanvas canvas, int x, int y) {
    137         canvas.drawTexture(this, x, y, getWidth(), getHeight());
    138     }
    139 
    140     @Override
    141     public void draw(GLCanvas canvas, int x, int y, int w, int h) {
    142         canvas.drawTexture(this, x, y, w, h);
    143     }
    144 
    145     // onBind is called before GLCanvas binds this texture.
    146     // It should make sure the data is uploaded to GL memory.
    147     abstract protected boolean onBind(GLCanvas canvas);
    148 
    149     // Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D).
    150     abstract protected int getTarget();
    151 
    152     public boolean isLoaded() {
    153         return mState == STATE_LOADED;
    154     }
    155 
    156     // recycle() is called when the texture will never be used again,
    157     // so it can free all resources.
    158     public void recycle() {
    159         freeResource();
    160     }
    161 
    162     // yield() is called when the texture will not be used temporarily,
    163     // so it can free some resources.
    164     // The default implementation unloads the texture from GL memory, so
    165     // the subclass should make sure it can reload the texture to GL memory
    166     // later, or it will have to override this method.
    167     public void yield() {
    168         freeResource();
    169     }
    170 
    171     private void freeResource() {
    172         GLCanvas canvas = mCanvasRef;
    173         if (canvas != null && mId != -1) {
    174             canvas.unloadTexture(this);
    175             mId = -1; // Don't free it again.
    176         }
    177         mState = STATE_UNLOADED;
    178         setAssociatedCanvas(null);
    179     }
    180 
    181     @Override
    182     protected void finalize() {
    183         sInFinalizer.set(BasicTexture.class);
    184         recycle();
    185         sInFinalizer.set(null);
    186     }
    187 
    188     // This is for deciding if we can call Bitmap's recycle().
    189     // We cannot call Bitmap's recycle() in finalizer because at that point
    190     // the finalizer of Bitmap may already be called so recycle() will crash.
    191     public static boolean inFinalizer() {
    192         return sInFinalizer.get() != null;
    193     }
    194 
    195     public static void yieldAllTextures() {
    196         synchronized (sAllTextures) {
    197             for (BasicTexture t : sAllTextures.keySet()) {
    198                 t.yield();
    199             }
    200         }
    201     }
    202 
    203     public static void invalidateAllTextures() {
    204         synchronized (sAllTextures) {
    205             for (BasicTexture t : sAllTextures.keySet()) {
    206                 t.mState = STATE_UNLOADED;
    207                 t.setAssociatedCanvas(null);
    208             }
    209         }
    210     }
    211 }
    212