8e88cb8478
Change-Id: Id79d11ed08085c4fe356739498a8b77952e30392
299 lines
9.0 KiB
Java
299 lines
9.0 KiB
Java
/*
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.gallery3d.glrenderer;
|
|
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.Bitmap.Config;
|
|
import android.opengl.GLUtils;
|
|
|
|
import junit.framework.Assert;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import javax.microedition.khronos.opengles.GL11;
|
|
|
|
// UploadedTextures use a Bitmap for the content of the texture.
|
|
//
|
|
// Subclasses should implement onGetBitmap() to provide the Bitmap and
|
|
// implement onFreeBitmap(mBitmap) which will be called when the Bitmap
|
|
// is not needed anymore.
|
|
//
|
|
// isContentValid() is meaningful only when the isLoaded() returns true.
|
|
// It means whether the content needs to be updated.
|
|
//
|
|
// The user of this class should call recycle() when the texture is not
|
|
// needed anymore.
|
|
//
|
|
// By default an UploadedTexture is opaque (so it can be drawn faster without
|
|
// blending). The user or subclass can override it using setOpaque().
|
|
public abstract class UploadedTexture extends BasicTexture {
|
|
|
|
// To prevent keeping allocation the borders, we store those used borders here.
|
|
// Since the length will be power of two, it won't use too much memory.
|
|
private static HashMap<BorderKey, Bitmap> sBorderLines =
|
|
new HashMap<BorderKey, Bitmap>();
|
|
private static BorderKey sBorderKey = new BorderKey();
|
|
|
|
@SuppressWarnings("unused")
|
|
private static final String TAG = "Texture";
|
|
private boolean mContentValid = true;
|
|
|
|
// indicate this textures is being uploaded in background
|
|
private boolean mIsUploading = false;
|
|
private boolean mOpaque = true;
|
|
private boolean mThrottled = false;
|
|
private static int sUploadedCount;
|
|
private static final int UPLOAD_LIMIT = 100;
|
|
|
|
protected Bitmap mBitmap;
|
|
private int mBorder;
|
|
|
|
protected UploadedTexture() {
|
|
this(false);
|
|
}
|
|
|
|
protected UploadedTexture(boolean hasBorder) {
|
|
super(null, 0, STATE_UNLOADED);
|
|
if (hasBorder) {
|
|
setBorder(true);
|
|
mBorder = 1;
|
|
}
|
|
}
|
|
|
|
protected void setIsUploading(boolean uploading) {
|
|
mIsUploading = uploading;
|
|
}
|
|
|
|
public boolean isUploading() {
|
|
return mIsUploading;
|
|
}
|
|
|
|
private static class BorderKey implements Cloneable {
|
|
public boolean vertical;
|
|
public Config config;
|
|
public int length;
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
int x = config.hashCode() ^ length;
|
|
return vertical ? x : -x;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object object) {
|
|
if (!(object instanceof BorderKey)) return false;
|
|
BorderKey o = (BorderKey) object;
|
|
return vertical == o.vertical
|
|
&& config == o.config && length == o.length;
|
|
}
|
|
|
|
@Override
|
|
public BorderKey clone() {
|
|
try {
|
|
return (BorderKey) super.clone();
|
|
} catch (CloneNotSupportedException e) {
|
|
throw new AssertionError(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void setThrottled(boolean throttled) {
|
|
mThrottled = throttled;
|
|
}
|
|
|
|
private static Bitmap getBorderLine(
|
|
boolean vertical, Config config, int length) {
|
|
BorderKey key = sBorderKey;
|
|
key.vertical = vertical;
|
|
key.config = config;
|
|
key.length = length;
|
|
Bitmap bitmap = sBorderLines.get(key);
|
|
if (bitmap == null) {
|
|
bitmap = vertical
|
|
? Bitmap.createBitmap(1, length, config)
|
|
: Bitmap.createBitmap(length, 1, config);
|
|
sBorderLines.put(key.clone(), bitmap);
|
|
}
|
|
return bitmap;
|
|
}
|
|
|
|
private Bitmap getBitmap() {
|
|
if (mBitmap == null) {
|
|
mBitmap = onGetBitmap();
|
|
int w = mBitmap.getWidth() + mBorder * 2;
|
|
int h = mBitmap.getHeight() + mBorder * 2;
|
|
if (mWidth == UNSPECIFIED) {
|
|
setSize(w, h);
|
|
}
|
|
}
|
|
return mBitmap;
|
|
}
|
|
|
|
private void freeBitmap() {
|
|
Assert.assertTrue(mBitmap != null);
|
|
onFreeBitmap(mBitmap);
|
|
mBitmap = null;
|
|
}
|
|
|
|
@Override
|
|
public int getWidth() {
|
|
if (mWidth == UNSPECIFIED) getBitmap();
|
|
return mWidth;
|
|
}
|
|
|
|
@Override
|
|
public int getHeight() {
|
|
if (mWidth == UNSPECIFIED) getBitmap();
|
|
return mHeight;
|
|
}
|
|
|
|
protected abstract Bitmap onGetBitmap();
|
|
|
|
protected abstract void onFreeBitmap(Bitmap bitmap);
|
|
|
|
protected void invalidateContent() {
|
|
if (mBitmap != null) freeBitmap();
|
|
mContentValid = false;
|
|
mWidth = UNSPECIFIED;
|
|
mHeight = UNSPECIFIED;
|
|
}
|
|
|
|
/**
|
|
* Whether the content on GPU is valid.
|
|
*/
|
|
public boolean isContentValid() {
|
|
return isLoaded() && mContentValid;
|
|
}
|
|
|
|
/**
|
|
* Updates the content on GPU's memory.
|
|
* @param canvas
|
|
*/
|
|
public void updateContent(GLCanvas canvas) {
|
|
if (!isLoaded()) {
|
|
if (mThrottled && ++sUploadedCount > UPLOAD_LIMIT) {
|
|
return;
|
|
}
|
|
uploadToCanvas(canvas);
|
|
} else if (!mContentValid) {
|
|
Bitmap bitmap = getBitmap();
|
|
int format = GLUtils.getInternalFormat(bitmap);
|
|
int type = GLUtils.getType(bitmap);
|
|
canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
|
|
freeBitmap();
|
|
mContentValid = true;
|
|
}
|
|
}
|
|
|
|
public static void resetUploadLimit() {
|
|
sUploadedCount = 0;
|
|
}
|
|
|
|
public static boolean uploadLimitReached() {
|
|
return sUploadedCount > UPLOAD_LIMIT;
|
|
}
|
|
|
|
private void uploadToCanvas(GLCanvas canvas) {
|
|
|
|
Bitmap bitmap = getBitmap();
|
|
if (bitmap != null) {
|
|
try {
|
|
int bWidth = bitmap.getWidth();
|
|
int bHeight = bitmap.getHeight();
|
|
int width = bWidth + mBorder * 2;
|
|
int height = bHeight + mBorder * 2;
|
|
int texWidth = getTextureWidth();
|
|
int texHeight = getTextureHeight();
|
|
|
|
Assert.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
|
|
|
|
// Upload the bitmap to a new texture.
|
|
mId = canvas.getGLId().generateTexture();
|
|
canvas.setTextureParameters(this);
|
|
|
|
if (bWidth == texWidth && bHeight == texHeight) {
|
|
canvas.initializeTexture(this, bitmap);
|
|
} else {
|
|
int format = GLUtils.getInternalFormat(bitmap);
|
|
int type = GLUtils.getType(bitmap);
|
|
Config config = bitmap.getConfig();
|
|
|
|
canvas.initializeTextureSize(this, format, type);
|
|
canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
|
|
|
|
if (mBorder > 0) {
|
|
// Left border
|
|
Bitmap line = getBorderLine(true, config, texHeight);
|
|
canvas.texSubImage2D(this, 0, 0, line, format, type);
|
|
|
|
// Top border
|
|
line = getBorderLine(false, config, texWidth);
|
|
canvas.texSubImage2D(this, 0, 0, line, format, type);
|
|
}
|
|
|
|
// Right border
|
|
if (mBorder + bWidth < texWidth) {
|
|
Bitmap line = getBorderLine(true, config, texHeight);
|
|
canvas.texSubImage2D(this, mBorder + bWidth, 0, line, format, type);
|
|
}
|
|
|
|
// Bottom border
|
|
if (mBorder + bHeight < texHeight) {
|
|
Bitmap line = getBorderLine(false, config, texWidth);
|
|
canvas.texSubImage2D(this, 0, mBorder + bHeight, line, format, type);
|
|
}
|
|
}
|
|
} finally {
|
|
freeBitmap();
|
|
}
|
|
// Update texture state.
|
|
setAssociatedCanvas(canvas);
|
|
mState = STATE_LOADED;
|
|
mContentValid = true;
|
|
} else {
|
|
mState = STATE_ERROR;
|
|
throw new RuntimeException("Texture load fail, no bitmap");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected boolean onBind(GLCanvas canvas) {
|
|
updateContent(canvas);
|
|
return isContentValid();
|
|
}
|
|
|
|
@Override
|
|
protected int getTarget() {
|
|
return GL11.GL_TEXTURE_2D;
|
|
}
|
|
|
|
public void setOpaque(boolean isOpaque) {
|
|
mOpaque = isOpaque;
|
|
}
|
|
|
|
@Override
|
|
public boolean isOpaque() {
|
|
return mOpaque;
|
|
}
|
|
|
|
@Override
|
|
public void recycle() {
|
|
super.recycle();
|
|
if (mBitmap != null) freeBitmap();
|
|
}
|
|
}
|