diff options
Diffstat (limited to 'src/com/android/gallery3d/ui/UploadedTexture.java')
-rw-r--r-- | src/com/android/gallery3d/ui/UploadedTexture.java | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/ui/UploadedTexture.java b/src/com/android/gallery3d/ui/UploadedTexture.java new file mode 100644 index 000000000..b063824d2 --- /dev/null +++ b/src/com/android/gallery3d/ui/UploadedTexture.java @@ -0,0 +1,285 @@ +/* + * 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.ui; + +import com.android.gallery3d.common.Utils; + +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.opengl.GLUtils; + +import java.util.HashMap; +import javax.microedition.khronos.opengles.GL11; +import javax.microedition.khronos.opengles.GL11Ext; + +// 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(). +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; + private boolean mOpaque = true; + private boolean mThrottled = false; + private static int sUploadedCount; + private static final int UPLOAD_LIMIT = 100; + + protected Bitmap mBitmap; + + protected UploadedTexture() { + super(null, 0, STATE_UNLOADED); + } + + 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(); + if (mWidth == UNSPECIFIED) { + setSize(mBitmap.getWidth(), mBitmap.getHeight()); + } else if (mWidth != mBitmap.getWidth() + || mHeight != mBitmap.getHeight()) { + throw new IllegalStateException(String.format( + "cannot change size: this = %s, orig = %sx%s, new = %sx%s", + toString(), mWidth, mHeight, mBitmap.getWidth(), + mBitmap.getHeight())); + } + } + return mBitmap; + } + + private void freeBitmap() { + Utils.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; + } + + /** + * Whether the content on GPU is valid. + */ + public boolean isContentValid(GLCanvas canvas) { + return isLoaded(canvas) && mContentValid; + } + + /** + * Updates the content on GPU's memory. + * @param canvas + */ + public void updateContent(GLCanvas canvas) { + if (!isLoaded(canvas)) { + 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.getGLInstance().glBindTexture(GL11.GL_TEXTURE_2D, mId); + GLUtils.texSubImage2D( + GL11.GL_TEXTURE_2D, 0, 0, 0, bitmap, format, type); + freeBitmap(); + mContentValid = true; + } + } + + public static void resetUploadLimit() { + sUploadedCount = 0; + } + + public static boolean uploadLimitReached() { + return sUploadedCount > UPLOAD_LIMIT; + } + + static int[] sTextureId = new int[1]; + static float[] sCropRect = new float[4]; + + private void uploadToCanvas(GLCanvas canvas) { + GL11 gl = canvas.getGLInstance(); + + Bitmap bitmap = getBitmap(); + if (bitmap != null) { + try { + // Define a vertically flipped crop rectangle for + // OES_draw_texture. + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + sCropRect[0] = 0; + sCropRect[1] = height; + sCropRect[2] = width; + sCropRect[3] = -height; + + // Upload the bitmap to a new texture. + gl.glGenTextures(1, sTextureId, 0); + gl.glBindTexture(GL11.GL_TEXTURE_2D, sTextureId[0]); + gl.glTexParameterfv(GL11.GL_TEXTURE_2D, + GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0); + gl.glTexParameteri(GL11.GL_TEXTURE_2D, + GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE); + gl.glTexParameteri(GL11.GL_TEXTURE_2D, + GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE); + gl.glTexParameterf(GL11.GL_TEXTURE_2D, + GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + gl.glTexParameterf(GL11.GL_TEXTURE_2D, + GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); + + if (width == getTextureWidth() && height == getTextureHeight()) { + GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, bitmap, 0); + } else { + int format = GLUtils.getInternalFormat(bitmap); + int type = GLUtils.getType(bitmap); + Config config = bitmap.getConfig(); + + gl.glTexImage2D(GL11.GL_TEXTURE_2D, 0, format, + getTextureWidth(), getTextureHeight(), + 0, format, type, null); + GLUtils.texSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, bitmap, + format, type); + + if (width != getTextureWidth()) { + Bitmap line = getBorderLine(true, config, getTextureHeight()); + GLUtils.texSubImage2D( + GL11.GL_TEXTURE_2D, 0, width, 0, line, format, type); + } + + if (height != getTextureHeight()) { + Bitmap line = getBorderLine(false, config, getTextureWidth()); + GLUtils.texSubImage2D( + GL11.GL_TEXTURE_2D, 0, 0, height, line, format, type); + } + + } + } finally { + freeBitmap(); + } + // Update texture state. + setAssociatedCanvas(canvas); + mId = sTextureId[0]; + mState = UploadedTexture.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(canvas); + } + + public void setOpaque(boolean isOpaque) { + mOpaque = isOpaque; + } + + public boolean isOpaque() { + return mOpaque; + } + + @Override + public void recycle() { + super.recycle(); + if (mBitmap != null) freeBitmap(); + } +} |