content: /*
   * 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);
              }
          }
      }
  }
  