• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
23 import com.android.gallery3d.common.Utils;
24 import com.android.launcher3.util.Thunk;
25 
26 import java.util.HashMap;
27 
28 import javax.microedition.khronos.opengles.GL11;
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 public 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 
UploadedTexture()66     protected UploadedTexture() {
67         this(false);
68     }
69 
UploadedTexture(boolean hasBorder)70     protected UploadedTexture(boolean hasBorder) {
71         super(null, 0, STATE_UNLOADED);
72         if (hasBorder) {
73             setBorder(true);
74             mBorder = 1;
75         }
76     }
77 
setIsUploading(boolean uploading)78     protected void setIsUploading(boolean uploading) {
79         mIsUploading = uploading;
80     }
81 
isUploading()82     public boolean isUploading() {
83         return mIsUploading;
84     }
85 
86     @Thunk static class BorderKey implements Cloneable {
87         public boolean vertical;
88         public Config config;
89         public int length;
90 
91         @Override
hashCode()92         public int hashCode() {
93             int x = config.hashCode() ^ length;
94             return vertical ? x : -x;
95         }
96 
97         @Override
equals(Object object)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
clone()106         public BorderKey clone() {
107             try {
108                 return (BorderKey) super.clone();
109             } catch (CloneNotSupportedException e) {
110                 throw new AssertionError(e);
111             }
112         }
113     }
114 
setThrottled(boolean throttled)115     protected void setThrottled(boolean throttled) {
116         mThrottled = throttled;
117     }
118 
getBorderLine( boolean vertical, Config config, int length)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 
getBitmap()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 
freeBitmap()147     private void freeBitmap() {
148         Utils.assertTrue(mBitmap != null);
149         onFreeBitmap(mBitmap);
150         mBitmap = null;
151     }
152 
153     @Override
getWidth()154     public int getWidth() {
155         if (mWidth == UNSPECIFIED) getBitmap();
156         return mWidth;
157     }
158 
159     @Override
getHeight()160     public int getHeight() {
161         if (mWidth == UNSPECIFIED) getBitmap();
162         return mHeight;
163     }
164 
onGetBitmap()165     protected abstract Bitmap onGetBitmap();
166 
onFreeBitmap(Bitmap bitmap)167     protected abstract void onFreeBitmap(Bitmap bitmap);
168 
invalidateContent()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      */
isContentValid()179     public boolean isContentValid() {
180         return isLoaded() && mContentValid;
181     }
182 
183     /**
184      * Updates the content on GPU's memory.
185      * @param canvas
186      */
updateContent(GLCanvas canvas)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.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
198             freeBitmap();
199             mContentValid = true;
200         }
201     }
202 
resetUploadLimit()203     public static void resetUploadLimit() {
204         sUploadedCount = 0;
205     }
206 
uploadLimitReached()207     public static boolean uploadLimitReached() {
208         return sUploadedCount > UPLOAD_LIMIT;
209     }
210 
uploadToCanvas(GLCanvas canvas)211     private void uploadToCanvas(GLCanvas canvas) {
212 
213         Bitmap bitmap = getBitmap();
214         if (bitmap != null) {
215             try {
216                 int bWidth = bitmap.getWidth();
217                 int bHeight = bitmap.getHeight();
218                 int width = bWidth + mBorder * 2;
219                 int height = bHeight + mBorder * 2;
220                 int texWidth = getTextureWidth();
221                 int texHeight = getTextureHeight();
222 
223                 Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
224 
225                 // Upload the bitmap to a new texture.
226                 mId = canvas.getGLId().generateTexture();
227                 canvas.setTextureParameters(this);
228 
229                 if (bWidth == texWidth && bHeight == texHeight) {
230                     canvas.initializeTexture(this, bitmap);
231                 } else {
232                     int format = GLUtils.getInternalFormat(bitmap);
233                     int type = GLUtils.getType(bitmap);
234                     Config config = bitmap.getConfig();
235 
236                     canvas.initializeTextureSize(this, format, type);
237                     canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
238 
239                     if (mBorder > 0) {
240                         // Left border
241                         Bitmap line = getBorderLine(true, config, texHeight);
242                         canvas.texSubImage2D(this, 0, 0, line, format, type);
243 
244                         // Top border
245                         line = getBorderLine(false, config, texWidth);
246                         canvas.texSubImage2D(this, 0, 0, line, format, type);
247                     }
248 
249                     // Right border
250                     if (mBorder + bWidth < texWidth) {
251                         Bitmap line = getBorderLine(true, config, texHeight);
252                         canvas.texSubImage2D(this, mBorder + bWidth, 0, line, format, type);
253                     }
254 
255                     // Bottom border
256                     if (mBorder + bHeight < texHeight) {
257                         Bitmap line = getBorderLine(false, config, texWidth);
258                         canvas.texSubImage2D(this, 0, mBorder + bHeight, line, format, type);
259                     }
260                 }
261             } finally {
262                 freeBitmap();
263             }
264             // Update texture state.
265             setAssociatedCanvas(canvas);
266             mState = STATE_LOADED;
267             mContentValid = true;
268         } else {
269             mState = STATE_ERROR;
270             throw new RuntimeException("Texture load fail, no bitmap");
271         }
272     }
273 
274     @Override
onBind(GLCanvas canvas)275     protected boolean onBind(GLCanvas canvas) {
276         updateContent(canvas);
277         return isContentValid();
278     }
279 
280     @Override
getTarget()281     protected int getTarget() {
282         return GL11.GL_TEXTURE_2D;
283     }
284 
setOpaque(boolean isOpaque)285     public void setOpaque(boolean isOpaque) {
286         mOpaque = isOpaque;
287     }
288 
289     @Override
isOpaque()290     public boolean isOpaque() {
291         return mOpaque;
292     }
293 
294     @Override
recycle()295     public void recycle() {
296         super.recycle();
297         if (mBitmap != null) freeBitmap();
298     }
299 }
300