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.graphics.Bitmap;
     20 import android.graphics.Bitmap.Config;
     21 import android.opengl.GLUtils;
     22 import android.util.Pair;
     23 
     24 import com.android.gallery3d.common.Utils;
     25 
     26 import java.util.HashMap;
     27 
     28 // UploadedTextures use a Bitmap for the content of the texture.
     29 //
     30 // Subclasses should implement onGetBitmap() to provide the Bitmap and
     31 // implement onFreeBitmap(mBitmap) which will be called when the Bitmap
     32 // is not needed anymore.
     33 //
     34 // isContentValid() is meaningful only when the isLoaded() returns true.
     35 // It means whether the content needs to be updated.
     36 //
     37 // The user of this class should call recycle() when the texture is not
     38 // needed anymore.
     39 //
     40 // By default an UploadedTexture is opaque (so it can be drawn faster without
     41 // blending). The user or subclass can override it using setOpaque().
     42 public abstract class UploadedTexture extends BasicTexture {
     43 
     44     // To prevent keeping allocation the borders, we store those used borders here.
     45     // Since the length will be power of two, it won't use too much memory.
     46     private static HashMap<BorderKey, Bitmap> sBorderLines = new HashMap<BorderKey, Bitmap>();
     47 
     48     private static class BorderKey extends Pair<Config, Integer> {
     49         public BorderKey(Config config, boolean vertical, int length) {
     50             super(config, vertical ? length : -length);
     51         }
     52     }
     53 
     54     private boolean mContentValid = true;
     55     protected Bitmap mBitmap;
     56 
     57     protected UploadedTexture() {
     58         super(null, 0, STATE_UNLOADED);
     59     }
     60 
     61     private static Bitmap getBorderLine(boolean vertical, Config config, int length) {
     62         BorderKey key = new BorderKey(config, vertical, length);
     63         Bitmap bitmap = sBorderLines.get(key);
     64         if (bitmap == null) {
     65             bitmap = vertical
     66                     ? Bitmap.createBitmap(1, length, config)
     67                     : Bitmap.createBitmap(length, 1, config);
     68             sBorderLines.put(key, bitmap);
     69         }
     70         return bitmap;
     71     }
     72 
     73     private Bitmap getBitmap() {
     74         if (mBitmap == null) {
     75             mBitmap = onGetBitmap();
     76             int w = mBitmap.getWidth();
     77             int h = mBitmap.getHeight();
     78             if (mWidth == UNSPECIFIED) {
     79                 setSize(w, h);
     80             }
     81         }
     82         return mBitmap;
     83     }
     84 
     85     private void freeBitmap() {
     86         Utils.assertTrue(mBitmap != null);
     87         onFreeBitmap(mBitmap);
     88         mBitmap = null;
     89     }
     90 
     91     @Override
     92     public int getWidth() {
     93         if (mWidth == UNSPECIFIED) getBitmap();
     94         return mWidth;
     95     }
     96 
     97     @Override
     98     public int getHeight() {
     99         if (mWidth == UNSPECIFIED) getBitmap();
    100         return mHeight;
    101     }
    102 
    103     protected abstract Bitmap onGetBitmap();
    104 
    105     protected abstract void onFreeBitmap(Bitmap bitmap);
    106 
    107     protected void invalidateContent() {
    108         if (mBitmap != null) freeBitmap();
    109         mContentValid = false;
    110         mWidth = UNSPECIFIED;
    111         mHeight = UNSPECIFIED;
    112     }
    113 
    114     /**
    115      * Whether the content on GPU is valid.
    116      */
    117     public boolean isContentValid() {
    118         return isLoaded() && mContentValid;
    119     }
    120 
    121     /**
    122      * Updates the content on GPU's memory.
    123      * @param canvas
    124      */
    125     public void updateContent(GLCanvas canvas) {
    126         if (!isLoaded()) {
    127             uploadToCanvas(canvas);
    128         } else if (!mContentValid) {
    129             Bitmap bitmap = getBitmap();
    130             int format = GLUtils.getInternalFormat(bitmap);
    131             int type = GLUtils.getType(bitmap);
    132             canvas.texSubImage2D(this, 0, 0, bitmap, format, type);
    133             freeBitmap();
    134             mContentValid = true;
    135         }
    136     }
    137 
    138     private void uploadToCanvas(GLCanvas canvas) {
    139         Bitmap bitmap = getBitmap();
    140         if (bitmap != null) {
    141             try {
    142                 int bWidth = bitmap.getWidth();
    143                 int bHeight = bitmap.getHeight();
    144                 int texWidth = getTextureWidth();
    145                 int texHeight = getTextureHeight();
    146 
    147                 Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
    148 
    149                 // Upload the bitmap to a new texture.
    150                 mId = canvas.getGLId().generateTexture();
    151                 canvas.setTextureParameters(this);
    152 
    153                 if (bWidth == texWidth && bHeight == texHeight) {
    154                     canvas.initializeTexture(this, bitmap);
    155                 } else {
    156                     int format = GLUtils.getInternalFormat(bitmap);
    157                     int type = GLUtils.getType(bitmap);
    158                     Config config = bitmap.getConfig();
    159 
    160                     canvas.initializeTextureSize(this, format, type);
    161                     canvas.texSubImage2D(this, 0, 0, bitmap, format, type);
    162 
    163                     // Right border
    164                     if (bWidth < texWidth) {
    165                         Bitmap line = getBorderLine(true, config, texHeight);
    166                         canvas.texSubImage2D(this, bWidth, 0, line, format, type);
    167                     }
    168 
    169                     // Bottom border
    170                     if (bHeight < texHeight) {
    171                         Bitmap line = getBorderLine(false, config, texWidth);
    172                         canvas.texSubImage2D(this, 0, bHeight, line, format, type);
    173                     }
    174                 }
    175             } finally {
    176                 freeBitmap();
    177             }
    178             // Update texture state.
    179             setAssociatedCanvas(canvas);
    180             mState = STATE_LOADED;
    181             mContentValid = true;
    182         } else {
    183             mState = STATE_ERROR;
    184             throw new RuntimeException("Texture load fail, no bitmap");
    185         }
    186     }
    187 
    188     @Override
    189     protected boolean onBind(GLCanvas canvas) {
    190         updateContent(canvas);
    191         return isContentValid();
    192     }
    193 
    194     @Override
    195     public void recycle() {
    196         super.recycle();
    197         if (mBitmap != null) freeBitmap();
    198     }
    199 }
    200