• 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.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 
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     private 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.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 
resetUploadLimit()205     public static void resetUploadLimit() {
206         sUploadedCount = 0;
207     }
208 
uploadLimitReached()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 
uploadToCanvas(GLCanvas canvas)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
onBind(GLCanvas canvas)307     protected boolean onBind(GLCanvas canvas) {
308         updateContent(canvas);
309         return isContentValid();
310     }
311 
312     @Override
getTarget()313     protected int getTarget() {
314         return GL11.GL_TEXTURE_2D;
315     }
316 
setOpaque(boolean isOpaque)317     public void setOpaque(boolean isOpaque) {
318         mOpaque = isOpaque;
319     }
320 
isOpaque()321     public boolean isOpaque() {
322         return mOpaque;
323     }
324 
325     @Override
recycle()326     public void recycle() {
327         super.recycle();
328         if (mBitmap != null) freeBitmap();
329     }
330 }
331