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