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