Home | History | Annotate | Download | only in ui
      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.ui;
     18 
     19 import android.graphics.Bitmap;
     20 import android.graphics.Bitmap.Config;
     21 import android.opengl.GLUtils;
     22 
     23 import com.android.gallery3d.common.Utils;
     24 
     25 import java.util.HashMap;
     26 
     27 import javax.microedition.khronos.opengles.GL11;
     28 import javax.microedition.khronos.opengles.GL11Ext;
     29 
     30 // UploadedTextures use a Bitmap for the content of the texture.
     31 //
     32 // Subclasses should implement onGetBitmap() to provide the Bitmap and
     33 // implement onFreeBitmap(mBitmap) which will be called when the Bitmap
     34 // is not needed anymore.
     35 //
     36 // isContentValid() is meaningful only when the isLoaded() returns true.
     37 // It means whether the content needs to be updated.
     38 //
     39 // The user of this class should call recycle() when the texture is not
     40 // needed anymore.
     41 //
     42 // By default an UploadedTexture is opaque (so it can be drawn faster without
     43 // blending). The user or subclass can override it using setOpaque().
     44 abstract class UploadedTexture extends BasicTexture {
     45 
     46     // To prevent keeping allocation the borders, we store those used borders here.
     47     // Since the length will be power of two, it won't use too much memory.
     48     private static HashMap<BorderKey, Bitmap> sBorderLines =
     49             new HashMap<BorderKey, Bitmap>();
     50     private static BorderKey sBorderKey = new BorderKey();
     51 
     52     @SuppressWarnings("unused")
     53     private static final String TAG = "Texture";
     54     private boolean mContentValid = true;
     55 
     56     // indicate this textures is being uploaded in background
     57     private boolean mIsUploading = false;
     58     private boolean mOpaque = true;
     59     private boolean mThrottled = false;
     60     private static int sUploadedCount;
     61     private static final int UPLOAD_LIMIT = 100;
     62 
     63     protected Bitmap mBitmap;
     64     private int mBorder;
     65 
     66     protected UploadedTexture() {
     67         this(false);
     68     }
     69 
     70     protected UploadedTexture(boolean hasBorder) {
     71         super(null, 0, STATE_UNLOADED);
     72         if (hasBorder) {
     73             setBorder(true);
     74             mBorder = 1;
     75         }
     76     }
     77 
     78     protected void setIsUploading(boolean uploading) {
     79         mIsUploading = uploading;
     80     }
     81 
     82     public boolean isUploading() {
     83         return mIsUploading;
     84     }
     85 
     86     private static class BorderKey implements Cloneable {
     87         public boolean vertical;
     88         public Config config;
     89         public int length;
     90 
     91         @Override
     92         public int hashCode() {
     93             int x = config.hashCode() ^ length;
     94             return vertical ? x : -x;
     95         }
     96 
     97         @Override
     98         public boolean equals(Object object) {
     99             if (!(object instanceof BorderKey)) return false;
    100             BorderKey o = (BorderKey) object;
    101             return vertical == o.vertical
    102                     && config == o.config && length == o.length;
    103         }
    104 
    105         @Override
    106         public BorderKey clone() {
    107             try {
    108                 return (BorderKey) super.clone();
    109             } catch (CloneNotSupportedException e) {
    110                 throw new AssertionError(e);
    111             }
    112         }
    113     }
    114 
    115     protected void setThrottled(boolean throttled) {
    116         mThrottled = throttled;
    117     }
    118 
    119     private static Bitmap getBorderLine(
    120             boolean vertical, Config config, int length) {
    121         BorderKey key = sBorderKey;
    122         key.vertical = vertical;
    123         key.config = config;
    124         key.length = length;
    125         Bitmap bitmap = sBorderLines.get(key);
    126         if (bitmap == null) {
    127             bitmap = vertical
    128                     ? Bitmap.createBitmap(1, length, config)
    129                     : Bitmap.createBitmap(length, 1, config);
    130             sBorderLines.put(key.clone(), bitmap);
    131         }
    132         return bitmap;
    133     }
    134 
    135     private Bitmap getBitmap() {
    136         if (mBitmap == null) {
    137             mBitmap = onGetBitmap();
    138             int w = mBitmap.getWidth() + mBorder * 2;
    139             int h = mBitmap.getHeight() + mBorder * 2;
    140             if (mWidth == UNSPECIFIED) {
    141                 setSize(w, h);
    142             }
    143         }
    144         return mBitmap;
    145     }
    146 
    147     private void freeBitmap() {
    148         Utils.assertTrue(mBitmap != null);
    149         onFreeBitmap(mBitmap);
    150         mBitmap = null;
    151     }
    152 
    153     @Override
    154     public int getWidth() {
    155         if (mWidth == UNSPECIFIED) getBitmap();
    156         return mWidth;
    157     }
    158 
    159     @Override
    160     public int getHeight() {
    161         if (mWidth == UNSPECIFIED) getBitmap();
    162         return mHeight;
    163     }
    164 
    165     protected abstract Bitmap onGetBitmap();
    166 
    167     protected abstract void onFreeBitmap(Bitmap bitmap);
    168 
    169     protected void invalidateContent() {
    170         if (mBitmap != null) freeBitmap();
    171         mContentValid = false;
    172         mWidth = UNSPECIFIED;
    173         mHeight = UNSPECIFIED;
    174     }
    175 
    176     /**
    177      * Whether the content on GPU is valid.
    178      */
    179     public boolean isContentValid() {
    180         return isLoaded() && mContentValid;
    181     }
    182 
    183     /**
    184      * Updates the content on GPU's memory.
    185      * @param canvas
    186      */
    187     public void updateContent(GLCanvas canvas) {
    188         if (!isLoaded()) {
    189             if (mThrottled && ++sUploadedCount > UPLOAD_LIMIT) {
    190                 return;
    191             }
    192             uploadToCanvas(canvas);
    193         } else if (!mContentValid) {
    194             Bitmap bitmap = getBitmap();
    195             int format = GLUtils.getInternalFormat(bitmap);
    196             int type = GLUtils.getType(bitmap);
    197             canvas.getGLInstance().glBindTexture(GL11.GL_TEXTURE_2D, mId);
    198             GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0, mBorder, mBorder,
    199                     bitmap, format, type);
    200             freeBitmap();
    201             mContentValid = true;
    202         }
    203     }
    204 
    205     public static void resetUploadLimit() {
    206         sUploadedCount = 0;
    207     }
    208 
    209     public static boolean uploadLimitReached() {
    210         return sUploadedCount > UPLOAD_LIMIT;
    211     }
    212 
    213     static int[] sTextureId = new int[1];
    214     static float[] sCropRect = new float[4];
    215 
    216     private void uploadToCanvas(GLCanvas canvas) {
    217         GL11 gl = canvas.getGLInstance();
    218 
    219         Bitmap bitmap = getBitmap();
    220         if (bitmap != null) {
    221             try {
    222                 int bWidth = bitmap.getWidth();
    223                 int bHeight = bitmap.getHeight();
    224                 int width = bWidth + mBorder * 2;
    225                 int height = bHeight + mBorder * 2;
    226                 int texWidth = getTextureWidth();
    227                 int texHeight = getTextureHeight();
    228 
    229                 Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
    230 
    231                 // Define a vertically flipped crop rectangle for
    232                 // OES_draw_texture.
    233                 // The four values in sCropRect are: left, bottom, width, and
    234                 // height. Negative value of width or height means flip.
    235                 sCropRect[0] = mBorder;
    236                 sCropRect[1] = mBorder + bHeight;
    237                 sCropRect[2] = bWidth;
    238                 sCropRect[3] = -bHeight;
    239 
    240                 // Upload the bitmap to a new texture.
    241                 GLId.glGenTextures(1, sTextureId, 0);
    242                 gl.glBindTexture(GL11.GL_TEXTURE_2D, sTextureId[0]);
    243                 gl.glTexParameterfv(GL11.GL_TEXTURE_2D,
    244                         GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0);
    245                 gl.glTexParameteri(GL11.GL_TEXTURE_2D,
    246                         GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
    247                 gl.glTexParameteri(GL11.GL_TEXTURE_2D,
    248                         GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
    249                 gl.glTexParameterf(GL11.GL_TEXTURE_2D,
    250                         GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
    251                 gl.glTexParameterf(GL11.GL_TEXTURE_2D,
    252                         GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
    253 
    254                 if (bWidth == texWidth && bHeight == texHeight) {
    255                     GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, bitmap, 0);
    256                 } else {
    257                     int format = GLUtils.getInternalFormat(bitmap);
    258                     int type = GLUtils.getType(bitmap);
    259                     Config config = bitmap.getConfig();
    260 
    261                     gl.glTexImage2D(GL11.GL_TEXTURE_2D, 0, format,
    262                             texWidth, texHeight, 0, format, type, null);
    263                     GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
    264                             mBorder, mBorder, bitmap, format, type);
    265 
    266                     if (mBorder > 0) {
    267                         // Left border
    268                         Bitmap line = getBorderLine(true, config, texHeight);
    269                         GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
    270                                 0, 0, line, format, type);
    271 
    272                         // Top border
    273                         line = getBorderLine(false, config, texWidth);
    274                         GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
    275                                 0, 0, line, format, type);
    276                     }
    277 
    278                     // Right border
    279                     if (mBorder + bWidth < texWidth) {
    280                         Bitmap line = getBorderLine(true, config, texHeight);
    281                         GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
    282                                 mBorder + bWidth, 0, line, format, type);
    283                     }
    284 
    285                     // Bottom border
    286                     if (mBorder + bHeight < texHeight) {
    287                         Bitmap line = getBorderLine(false, config, texWidth);
    288                         GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0,
    289                                 0, mBorder + bHeight, line, format, type);
    290                     }
    291                 }
    292             } finally {
    293                 freeBitmap();
    294             }
    295             // Update texture state.
    296             setAssociatedCanvas(canvas);
    297             mId = sTextureId[0];
    298             mState = STATE_LOADED;
    299             mContentValid = true;
    300         } else {
    301             mState = STATE_ERROR;
    302             throw new RuntimeException("Texture load fail, no bitmap");
    303         }
    304     }
    305 
    306     @Override
    307     protected boolean onBind(GLCanvas canvas) {
    308         updateContent(canvas);
    309         return isContentValid();
    310     }
    311 
    312     @Override
    313     protected int getTarget() {
    314         return GL11.GL_TEXTURE_2D;
    315     }
    316 
    317     public void setOpaque(boolean isOpaque) {
    318         mOpaque = isOpaque;
    319     }
    320 
    321     public boolean isOpaque() {
    322         return mOpaque;
    323     }
    324 
    325     @Override
    326     public void recycle() {
    327         super.recycle();
    328         if (mBitmap != null) freeBitmap();
    329     }
    330 }
    331