From 8cb165add72646ba57cf2f072d91351a8544fbc5 Mon Sep 17 00:00:00 2001 From: John Reck Date: Tue, 11 Dec 2012 14:42:27 -0800 Subject: GL packaging refactor Change-Id: I1d7a95c4f0b91a5ba59030d57cf6865c11b497aa --- src/com/android/gallery3d/anim/AlphaAnimation.java | 2 +- .../android/gallery3d/anim/CanvasAnimation.java | 2 +- .../gallery3d/anim/StateTransitionAnimation.java | 4 +- src/com/android/gallery3d/app/ActivityState.java | 2 +- src/com/android/gallery3d/app/AlbumPage.java | 4 +- src/com/android/gallery3d/app/AlbumSetPage.java | 4 +- src/com/android/gallery3d/app/ManageCachePage.java | 2 +- .../android/gallery3d/app/PhotoDataAdapter.java | 2 +- src/com/android/gallery3d/app/SlideshowPage.java | 2 +- .../android/gallery3d/glrenderer/BasicTexture.java | 208 ++++ .../gallery3d/glrenderer/BitmapTexture.java | 54 + .../gallery3d/glrenderer/CanvasTexture.java | 52 + .../android/gallery3d/glrenderer/ColorTexture.java | 63 ++ .../android/gallery3d/glrenderer/ExtTexture.java | 60 ++ .../gallery3d/glrenderer/FadeInTexture.java | 43 + .../gallery3d/glrenderer/FadeOutTexture.java | 42 + .../android/gallery3d/glrenderer/FadeTexture.java | 81 ++ src/com/android/gallery3d/glrenderer/GLCanvas.java | 264 +++++ .../android/gallery3d/glrenderer/GLES11Canvas.java | 1039 +++++++++++++++++++ .../android/gallery3d/glrenderer/GLES20Canvas.java | 1068 ++++++++++++++++++++ src/com/android/gallery3d/glrenderer/GLId.java | 33 + src/com/android/gallery3d/glrenderer/GLIdImpl.java | 68 ++ src/com/android/gallery3d/glrenderer/GLPaint.java | 41 + .../gallery3d/glrenderer/MultiLineTexture.java | 52 + .../gallery3d/glrenderer/NinePatchChunk.java | 82 ++ .../gallery3d/glrenderer/NinePatchTexture.java | 424 ++++++++ .../android/gallery3d/glrenderer/RawTexture.java | 63 ++ .../gallery3d/glrenderer/ResourceTexture.java | 53 + .../gallery3d/glrenderer/StringTexture.java | 88 ++ src/com/android/gallery3d/glrenderer/Texture.java | 44 + .../gallery3d/glrenderer/TextureUploader.java | 105 ++ .../android/gallery3d/glrenderer/TiledTexture.java | 349 +++++++ .../gallery3d/glrenderer/UploadedTexture.java | 298 ++++++ .../android/gallery3d/ui/AbstractSlotRenderer.java | 5 + .../gallery3d/ui/AlbumSetSlidingWindow.java | 4 + .../android/gallery3d/ui/AlbumSetSlotRenderer.java | 7 + .../android/gallery3d/ui/AlbumSlidingWindow.java | 2 + .../android/gallery3d/ui/AlbumSlotRenderer.java | 5 + src/com/android/gallery3d/ui/BasicTexture.java | 206 ---- src/com/android/gallery3d/ui/BitmapScreenNail.java | 3 + src/com/android/gallery3d/ui/BitmapTexture.java | 54 - src/com/android/gallery3d/ui/CanvasTexture.java | 52 - src/com/android/gallery3d/ui/ColorTexture.java | 63 -- src/com/android/gallery3d/ui/EdgeEffect.java | 2 + src/com/android/gallery3d/ui/EdgeView.java | 2 + src/com/android/gallery3d/ui/ExtTexture.java | 61 -- src/com/android/gallery3d/ui/FadeInTexture.java | 42 - src/com/android/gallery3d/ui/FadeOutTexture.java | 41 - src/com/android/gallery3d/ui/FadeTexture.java | 80 -- src/com/android/gallery3d/ui/GLCanvas.java | 264 ----- src/com/android/gallery3d/ui/GLCanvasImpl.java | 1036 ------------------- src/com/android/gallery3d/ui/GLES20Canvas.java | 1068 -------------------- src/com/android/gallery3d/ui/GLId.java | 33 - src/com/android/gallery3d/ui/GLIdImpl.java | 68 -- src/com/android/gallery3d/ui/GLPaint.java | 42 - src/com/android/gallery3d/ui/GLRoot.java | 1 + src/com/android/gallery3d/ui/GLRootView.java | 3 + src/com/android/gallery3d/ui/GLView.java | 1 + .../gallery3d/ui/GalleryEGLConfigChooser.java | 1 + src/com/android/gallery3d/ui/Log.java | 1 + .../android/gallery3d/ui/ManageCacheDrawer.java | 3 + src/com/android/gallery3d/ui/MultiLineTexture.java | 51 - src/com/android/gallery3d/ui/NinePatchChunk.java | 82 -- src/com/android/gallery3d/ui/NinePatchTexture.java | 424 -------- .../android/gallery3d/ui/PhotoFallbackEffect.java | 2 + src/com/android/gallery3d/ui/PhotoView.java | 5 + .../gallery3d/ui/PreparePageFadeoutTexture.java | 2 + src/com/android/gallery3d/ui/ProgressSpinner.java | 2 + src/com/android/gallery3d/ui/RawTexture.java | 63 -- src/com/android/gallery3d/ui/ResourceTexture.java | 52 - src/com/android/gallery3d/ui/ScreenNail.java | 2 + src/com/android/gallery3d/ui/ScrollBarView.java | 3 + src/com/android/gallery3d/ui/SlideshowView.java | 4 +- src/com/android/gallery3d/ui/SlotView.java | 1 + src/com/android/gallery3d/ui/StringTexture.java | 88 -- .../gallery3d/ui/SurfaceTextureScreenNail.java | 2 + src/com/android/gallery3d/ui/Texture.java | 43 - src/com/android/gallery3d/ui/TextureUploader.java | 104 -- src/com/android/gallery3d/ui/TileImageView.java | 2 + src/com/android/gallery3d/ui/TiledScreenNail.java | 2 + src/com/android/gallery3d/ui/TiledTexture.java | 348 ------- src/com/android/gallery3d/ui/UndoBarView.java | 4 + src/com/android/gallery3d/ui/UploadedTexture.java | 298 ------ 83 files changed, 4756 insertions(+), 4676 deletions(-) create mode 100644 src/com/android/gallery3d/glrenderer/BasicTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/BitmapTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/CanvasTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/ColorTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/ExtTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/FadeInTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/FadeOutTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/FadeTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/GLCanvas.java create mode 100644 src/com/android/gallery3d/glrenderer/GLES11Canvas.java create mode 100644 src/com/android/gallery3d/glrenderer/GLES20Canvas.java create mode 100644 src/com/android/gallery3d/glrenderer/GLId.java create mode 100644 src/com/android/gallery3d/glrenderer/GLIdImpl.java create mode 100644 src/com/android/gallery3d/glrenderer/GLPaint.java create mode 100644 src/com/android/gallery3d/glrenderer/MultiLineTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/NinePatchChunk.java create mode 100644 src/com/android/gallery3d/glrenderer/NinePatchTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/RawTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/ResourceTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/StringTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/Texture.java create mode 100644 src/com/android/gallery3d/glrenderer/TextureUploader.java create mode 100644 src/com/android/gallery3d/glrenderer/TiledTexture.java create mode 100644 src/com/android/gallery3d/glrenderer/UploadedTexture.java delete mode 100644 src/com/android/gallery3d/ui/BasicTexture.java delete mode 100644 src/com/android/gallery3d/ui/BitmapTexture.java delete mode 100644 src/com/android/gallery3d/ui/CanvasTexture.java delete mode 100644 src/com/android/gallery3d/ui/ColorTexture.java delete mode 100644 src/com/android/gallery3d/ui/ExtTexture.java delete mode 100644 src/com/android/gallery3d/ui/FadeInTexture.java delete mode 100644 src/com/android/gallery3d/ui/FadeOutTexture.java delete mode 100644 src/com/android/gallery3d/ui/FadeTexture.java delete mode 100644 src/com/android/gallery3d/ui/GLCanvas.java delete mode 100644 src/com/android/gallery3d/ui/GLCanvasImpl.java delete mode 100644 src/com/android/gallery3d/ui/GLES20Canvas.java delete mode 100644 src/com/android/gallery3d/ui/GLId.java delete mode 100644 src/com/android/gallery3d/ui/GLIdImpl.java delete mode 100644 src/com/android/gallery3d/ui/GLPaint.java delete mode 100644 src/com/android/gallery3d/ui/MultiLineTexture.java delete mode 100644 src/com/android/gallery3d/ui/NinePatchChunk.java delete mode 100644 src/com/android/gallery3d/ui/NinePatchTexture.java delete mode 100644 src/com/android/gallery3d/ui/RawTexture.java delete mode 100644 src/com/android/gallery3d/ui/ResourceTexture.java delete mode 100644 src/com/android/gallery3d/ui/StringTexture.java delete mode 100644 src/com/android/gallery3d/ui/Texture.java delete mode 100644 src/com/android/gallery3d/ui/TextureUploader.java delete mode 100644 src/com/android/gallery3d/ui/TiledTexture.java delete mode 100644 src/com/android/gallery3d/ui/UploadedTexture.java (limited to 'src') diff --git a/src/com/android/gallery3d/anim/AlphaAnimation.java b/src/com/android/gallery3d/anim/AlphaAnimation.java index cb17527b8..f9f4cbd2c 100644 --- a/src/com/android/gallery3d/anim/AlphaAnimation.java +++ b/src/com/android/gallery3d/anim/AlphaAnimation.java @@ -17,7 +17,7 @@ package com.android.gallery3d.anim; import com.android.gallery3d.common.Utils; -import com.android.gallery3d.ui.GLCanvas; +import com.android.gallery3d.glrenderer.GLCanvas; public class AlphaAnimation extends CanvasAnimation { private final float mStartAlpha; diff --git a/src/com/android/gallery3d/anim/CanvasAnimation.java b/src/com/android/gallery3d/anim/CanvasAnimation.java index 4c8bcc825..cdc66c6ba 100644 --- a/src/com/android/gallery3d/anim/CanvasAnimation.java +++ b/src/com/android/gallery3d/anim/CanvasAnimation.java @@ -16,7 +16,7 @@ package com.android.gallery3d.anim; -import com.android.gallery3d.ui.GLCanvas; +import com.android.gallery3d.glrenderer.GLCanvas; public abstract class CanvasAnimation extends Animation { diff --git a/src/com/android/gallery3d/anim/StateTransitionAnimation.java b/src/com/android/gallery3d/anim/StateTransitionAnimation.java index cf04d2cc8..bf8a54405 100644 --- a/src/com/android/gallery3d/anim/StateTransitionAnimation.java +++ b/src/com/android/gallery3d/anim/StateTransitionAnimation.java @@ -20,9 +20,9 @@ import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; -import com.android.gallery3d.ui.GLCanvas; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.RawTexture; import com.android.gallery3d.ui.GLView; -import com.android.gallery3d.ui.RawTexture; import com.android.gallery3d.ui.TiledScreenNail; public class StateTransitionAnimation extends Animation { diff --git a/src/com/android/gallery3d/app/ActivityState.java b/src/com/android/gallery3d/app/ActivityState.java index b2e39b1cb..fa5a9bd54 100644 --- a/src/com/android/gallery3d/app/ActivityState.java +++ b/src/com/android/gallery3d/app/ActivityState.java @@ -35,9 +35,9 @@ import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; import com.android.gallery3d.R; import com.android.gallery3d.anim.StateTransitionAnimation; +import com.android.gallery3d.glrenderer.RawTexture; import com.android.gallery3d.ui.GLView; import com.android.gallery3d.ui.PreparePageFadeoutTexture; -import com.android.gallery3d.ui.RawTexture; import com.android.gallery3d.util.GalleryUtils; abstract public class ActivityState { diff --git a/src/com/android/gallery3d/app/AlbumPage.java b/src/com/android/gallery3d/app/AlbumPage.java index 60000903d..4de38c5dd 100644 --- a/src/com/android/gallery3d/app/AlbumPage.java +++ b/src/com/android/gallery3d/app/AlbumPage.java @@ -45,8 +45,6 @@ import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener; import com.android.gallery3d.ui.AlbumSlotRenderer; import com.android.gallery3d.ui.DetailsHelper; import com.android.gallery3d.ui.DetailsHelper.CloseListener; -import com.android.gallery3d.ui.FadeTexture; -import com.android.gallery3d.ui.GLCanvas; import com.android.gallery3d.ui.GLRoot; import com.android.gallery3d.ui.GLView; import com.android.gallery3d.ui.PhotoFallbackEffect; @@ -59,6 +57,8 @@ import com.android.gallery3d.util.GalleryUtils; import com.android.gallery3d.util.MediaSetUtils; import com.android.gallery3d.filtershow.FilterShowActivity; import com.android.gallery3d.filtershow.CropExtras; +import com.android.gallery3d.glrenderer.FadeTexture; +import com.android.gallery3d.glrenderer.GLCanvas; public class AlbumPage extends ActivityState implements GalleryActionBar.ClusterRunner, diff --git a/src/com/android/gallery3d/app/AlbumSetPage.java b/src/com/android/gallery3d/app/AlbumSetPage.java index df68ffbaa..4bafb4737 100644 --- a/src/com/android/gallery3d/app/AlbumSetPage.java +++ b/src/com/android/gallery3d/app/AlbumSetPage.java @@ -41,6 +41,8 @@ import com.android.gallery3d.data.MediaItem; import com.android.gallery3d.data.MediaObject; import com.android.gallery3d.data.MediaSet; import com.android.gallery3d.data.Path; +import com.android.gallery3d.glrenderer.FadeTexture; +import com.android.gallery3d.glrenderer.GLCanvas; import com.android.gallery3d.picasasource.PicasaSource; import com.android.gallery3d.settings.GallerySettings; import com.android.gallery3d.ui.ActionModeHandler; @@ -48,8 +50,6 @@ import com.android.gallery3d.ui.ActionModeHandler.ActionModeListener; import com.android.gallery3d.ui.AlbumSetSlotRenderer; import com.android.gallery3d.ui.DetailsHelper; import com.android.gallery3d.ui.DetailsHelper.CloseListener; -import com.android.gallery3d.ui.FadeTexture; -import com.android.gallery3d.ui.GLCanvas; import com.android.gallery3d.ui.GLRoot; import com.android.gallery3d.ui.GLView; import com.android.gallery3d.ui.SelectionManager; diff --git a/src/com/android/gallery3d/app/ManageCachePage.java b/src/com/android/gallery3d/app/ManageCachePage.java index 37a97626e..4f5c35819 100644 --- a/src/com/android/gallery3d/app/ManageCachePage.java +++ b/src/com/android/gallery3d/app/ManageCachePage.java @@ -35,8 +35,8 @@ import com.android.gallery3d.common.Utils; import com.android.gallery3d.data.MediaObject; import com.android.gallery3d.data.MediaSet; import com.android.gallery3d.data.Path; +import com.android.gallery3d.glrenderer.GLCanvas; import com.android.gallery3d.ui.CacheStorageUsageInfo; -import com.android.gallery3d.ui.GLCanvas; import com.android.gallery3d.ui.GLRoot; import com.android.gallery3d.ui.GLView; import com.android.gallery3d.ui.ManageCacheDrawer; diff --git a/src/com/android/gallery3d/app/PhotoDataAdapter.java b/src/com/android/gallery3d/app/PhotoDataAdapter.java index 8485b25eb..c38d25656 100644 --- a/src/com/android/gallery3d/app/PhotoDataAdapter.java +++ b/src/com/android/gallery3d/app/PhotoDataAdapter.java @@ -30,12 +30,12 @@ import com.android.gallery3d.data.MediaItem; import com.android.gallery3d.data.MediaObject; import com.android.gallery3d.data.MediaSet; import com.android.gallery3d.data.Path; +import com.android.gallery3d.glrenderer.TiledTexture; import com.android.gallery3d.ui.PhotoView; import com.android.gallery3d.ui.ScreenNail; import com.android.gallery3d.ui.SynchronizedHandler; import com.android.gallery3d.ui.TileImageViewAdapter; import com.android.gallery3d.ui.TiledScreenNail; -import com.android.gallery3d.ui.TiledTexture; import com.android.gallery3d.util.Future; import com.android.gallery3d.util.FutureListener; import com.android.gallery3d.util.MediaSetUtils; diff --git a/src/com/android/gallery3d/app/SlideshowPage.java b/src/com/android/gallery3d/app/SlideshowPage.java index 80edb367e..54aae67ab 100644 --- a/src/com/android/gallery3d/app/SlideshowPage.java +++ b/src/com/android/gallery3d/app/SlideshowPage.java @@ -31,7 +31,7 @@ import com.android.gallery3d.data.MediaItem; import com.android.gallery3d.data.MediaObject; import com.android.gallery3d.data.MediaSet; import com.android.gallery3d.data.Path; -import com.android.gallery3d.ui.GLCanvas; +import com.android.gallery3d.glrenderer.GLCanvas; import com.android.gallery3d.ui.GLView; import com.android.gallery3d.ui.SlideshowView; import com.android.gallery3d.ui.SynchronizedHandler; diff --git a/src/com/android/gallery3d/glrenderer/BasicTexture.java b/src/com/android/gallery3d/glrenderer/BasicTexture.java new file mode 100644 index 000000000..48227f6ef --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/BasicTexture.java @@ -0,0 +1,208 @@ +/* + * 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.util.Log; + +import com.android.gallery3d.common.Utils; + +import java.util.WeakHashMap; + +// BasicTexture is a Texture corresponds to a real GL texture. +// The state of a BasicTexture indicates whether its data is loaded to GL memory. +// If a BasicTexture is loaded into GL memory, it has a GL texture id. +public abstract class BasicTexture implements Texture { + + @SuppressWarnings("unused") + private static final String TAG = "BasicTexture"; + protected static final int UNSPECIFIED = -1; + + protected static final int STATE_UNLOADED = 0; + protected static final int STATE_LOADED = 1; + protected static final int STATE_ERROR = -1; + + // Log a warning if a texture is larger along a dimension + private static final int MAX_TEXTURE_SIZE = 4096; + + protected int mId = -1; + protected int mState; + + protected int mWidth = UNSPECIFIED; + protected int mHeight = UNSPECIFIED; + + protected int mTextureWidth; + protected int mTextureHeight; + + private boolean mHasBorder; + + protected GLCanvas mCanvasRef = null; + private static WeakHashMap sAllTextures + = new WeakHashMap(); + private static ThreadLocal sInFinalizer = new ThreadLocal(); + + protected BasicTexture(GLCanvas canvas, int id, int state) { + setAssociatedCanvas(canvas); + mId = id; + mState = state; + synchronized (sAllTextures) { + sAllTextures.put(this, null); + } + } + + protected BasicTexture() { + this(null, 0, STATE_UNLOADED); + } + + protected void setAssociatedCanvas(GLCanvas canvas) { + mCanvasRef = canvas; + } + + /** + * Sets the content size of this texture. In OpenGL, the actual texture + * size must be of power of 2, the size of the content may be smaller. + */ + public void setSize(int width, int height) { + mWidth = width; + mHeight = height; + mTextureWidth = Utils.nextPowerOf2(width); + mTextureHeight = Utils.nextPowerOf2(height); + if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) { + Log.w(TAG, String.format("texture is too large: %d x %d", + mTextureWidth, mTextureHeight), new Exception()); + } + } + + public int getId() { + return mId; + } + + @Override + public int getWidth() { + return mWidth; + } + + @Override + public int getHeight() { + return mHeight; + } + + // Returns the width rounded to the next power of 2. + public int getTextureWidth() { + return mTextureWidth; + } + + // Returns the height rounded to the next power of 2. + public int getTextureHeight() { + return mTextureHeight; + } + + // Returns true if the texture has one pixel transparent border around the + // actual content. This is used to avoid jigged edges. + // + // The jigged edges appear because we use GL_CLAMP_TO_EDGE for texture wrap + // mode (GL_CLAMP is not available in OpenGL ES), so a pixel partially + // covered by the texture will use the color of the edge texel. If we add + // the transparent border, the color of the edge texel will be mixed with + // appropriate amount of transparent. + // + // Currently our background is black, so we can draw the thumbnails without + // enabling blending. + public boolean hasBorder() { + return mHasBorder; + } + + protected void setBorder(boolean hasBorder) { + mHasBorder = hasBorder; + } + + @Override + public void draw(GLCanvas canvas, int x, int y) { + canvas.drawTexture(this, x, y, getWidth(), getHeight()); + } + + @Override + public void draw(GLCanvas canvas, int x, int y, int w, int h) { + canvas.drawTexture(this, x, y, w, h); + } + + // onBind is called before GLCanvas binds this texture. + // It should make sure the data is uploaded to GL memory. + abstract protected boolean onBind(GLCanvas canvas); + + // Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D). + abstract protected int getTarget(); + + public boolean isLoaded() { + return mState == STATE_LOADED; + } + + // recycle() is called when the texture will never be used again, + // so it can free all resources. + public void recycle() { + freeResource(); + } + + // yield() is called when the texture will not be used temporarily, + // so it can free some resources. + // The default implementation unloads the texture from GL memory, so + // the subclass should make sure it can reload the texture to GL memory + // later, or it will have to override this method. + public void yield() { + freeResource(); + } + + private void freeResource() { + GLCanvas canvas = mCanvasRef; + if (canvas != null && mId != -1) { + canvas.unloadTexture(this); + mId = -1; // Don't free it again. + } + mState = STATE_UNLOADED; + setAssociatedCanvas(null); + } + + @Override + protected void finalize() { + sInFinalizer.set(BasicTexture.class); + recycle(); + sInFinalizer.set(null); + } + + // This is for deciding if we can call Bitmap's recycle(). + // We cannot call Bitmap's recycle() in finalizer because at that point + // the finalizer of Bitmap may already be called so recycle() will crash. + public static boolean inFinalizer() { + return sInFinalizer.get() != null; + } + + public static void yieldAllTextures() { + synchronized (sAllTextures) { + for (BasicTexture t : sAllTextures.keySet()) { + t.yield(); + } + } + } + + public static void invalidateAllTextures() { + synchronized (sAllTextures) { + for (BasicTexture t : sAllTextures.keySet()) { + t.mState = STATE_UNLOADED; + t.setAssociatedCanvas(null); + } + } + } +} diff --git a/src/com/android/gallery3d/glrenderer/BitmapTexture.java b/src/com/android/gallery3d/glrenderer/BitmapTexture.java new file mode 100644 index 000000000..100b0b3b9 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/BitmapTexture.java @@ -0,0 +1,54 @@ +/* + * 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 junit.framework.Assert; + +// BitmapTexture is a texture whose content is specified by a fixed Bitmap. +// +// The texture does not own the Bitmap. The user should make sure the Bitmap +// is valid during the texture's lifetime. When the texture is recycled, it +// does not free the Bitmap. +public class BitmapTexture extends UploadedTexture { + protected Bitmap mContentBitmap; + + public BitmapTexture(Bitmap bitmap) { + this(bitmap, false); + } + + public BitmapTexture(Bitmap bitmap, boolean hasBorder) { + super(hasBorder); + Assert.assertTrue(bitmap != null && !bitmap.isRecycled()); + mContentBitmap = bitmap; + } + + @Override + protected void onFreeBitmap(Bitmap bitmap) { + // Do nothing. + } + + @Override + protected Bitmap onGetBitmap() { + return mContentBitmap; + } + + public Bitmap getBitmap() { + return mContentBitmap; + } +} diff --git a/src/com/android/gallery3d/glrenderer/CanvasTexture.java b/src/com/android/gallery3d/glrenderer/CanvasTexture.java new file mode 100644 index 000000000..bff9d4baa --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/CanvasTexture.java @@ -0,0 +1,52 @@ +/* + * 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.graphics.Canvas; + +// CanvasTexture is a texture whose content is the drawing on a Canvas. +// The subclasses should override onDraw() to draw on the bitmap. +// By default CanvasTexture is not opaque. +abstract class CanvasTexture extends UploadedTexture { + protected Canvas mCanvas; + private final Config mConfig; + + public CanvasTexture(int width, int height) { + mConfig = Config.ARGB_8888; + setSize(width, height); + setOpaque(false); + } + + @Override + protected Bitmap onGetBitmap() { + Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, mConfig); + mCanvas = new Canvas(bitmap); + onDraw(mCanvas, bitmap); + return bitmap; + } + + @Override + protected void onFreeBitmap(Bitmap bitmap) { + if (!inFinalizer()) { + bitmap.recycle(); + } + } + + abstract protected void onDraw(Canvas canvas, Bitmap backing); +} diff --git a/src/com/android/gallery3d/glrenderer/ColorTexture.java b/src/com/android/gallery3d/glrenderer/ColorTexture.java new file mode 100644 index 000000000..904c78e1b --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/ColorTexture.java @@ -0,0 +1,63 @@ +/* + * 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 com.android.gallery3d.common.Utils; + +// ColorTexture is a texture which fills the rectangle with the specified color. +public class ColorTexture implements Texture { + + private final int mColor; + private int mWidth; + private int mHeight; + + public ColorTexture(int color) { + mColor = color; + mWidth = 1; + mHeight = 1; + } + + @Override + public void draw(GLCanvas canvas, int x, int y) { + draw(canvas, x, y, mWidth, mHeight); + } + + @Override + public void draw(GLCanvas canvas, int x, int y, int w, int h) { + canvas.fillRect(x, y, w, h, mColor); + } + + @Override + public boolean isOpaque() { + return Utils.isOpaque(mColor); + } + + public void setSize(int width, int height) { + mWidth = width; + mHeight = height; + } + + @Override + public int getWidth() { + return mWidth; + } + + @Override + public int getHeight() { + return mHeight; + } +} diff --git a/src/com/android/gallery3d/glrenderer/ExtTexture.java b/src/com/android/gallery3d/glrenderer/ExtTexture.java new file mode 100644 index 000000000..b1fbd195c --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/ExtTexture.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 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; + +// ExtTexture is a texture whose content comes from a external texture. +// Before drawing, setSize() should be called. +public class ExtTexture extends BasicTexture { + + private int mTarget; + + public ExtTexture(int target) { + GLId glId = GLCanvas.getGLId(); + mId = glId.generateTexture(); + mTarget = target; + } + + private void uploadToCanvas(GLCanvas canvas) { + canvas.setTextureParameters(this); + setAssociatedCanvas(canvas); + mState = STATE_LOADED; + } + + @Override + protected boolean onBind(GLCanvas canvas) { + if (!isLoaded()) { + uploadToCanvas(canvas); + } + + return true; + } + + @Override + public int getTarget() { + return mTarget; + } + + @Override + public boolean isOpaque() { + return true; + } + + @Override + public void yield() { + // we cannot free the texture because we have no backup. + } +} diff --git a/src/com/android/gallery3d/glrenderer/FadeInTexture.java b/src/com/android/gallery3d/glrenderer/FadeInTexture.java new file mode 100644 index 000000000..838d465f5 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/FadeInTexture.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 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; + + +// FadeInTexture is a texture which begins with a color, then gradually animates +// into a given texture. +public class FadeInTexture extends FadeTexture implements Texture { + @SuppressWarnings("unused") + private static final String TAG = "FadeInTexture"; + + private final int mColor; + private final TiledTexture mTexture; + + public FadeInTexture(int color, TiledTexture texture) { + super(texture.getWidth(), texture.getHeight(), texture.isOpaque()); + mColor = color; + mTexture = texture; + } + + @Override + public void draw(GLCanvas canvas, int x, int y, int w, int h) { + if (isAnimating()) { + mTexture.drawMixed(canvas, mColor, getRatio(), x, y, w, h); + } else { + mTexture.draw(canvas, x, y, w, h); + } + } +} diff --git a/src/com/android/gallery3d/glrenderer/FadeOutTexture.java b/src/com/android/gallery3d/glrenderer/FadeOutTexture.java new file mode 100644 index 000000000..b05f3b631 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/FadeOutTexture.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 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; + + +// FadeOutTexture is a texture which begins with a given texture, then gradually animates +// into fading out totally. +public class FadeOutTexture extends FadeTexture { + @SuppressWarnings("unused") + private static final String TAG = "FadeOutTexture"; + + private final BasicTexture mTexture; + + public FadeOutTexture(BasicTexture texture) { + super(texture.getWidth(), texture.getHeight(), texture.isOpaque()); + mTexture = texture; + } + + @Override + public void draw(GLCanvas canvas, int x, int y, int w, int h) { + if (isAnimating()) { + canvas.save(GLCanvas.SAVE_FLAG_ALPHA); + canvas.setAlpha(getRatio()); + mTexture.draw(canvas, x, y, w, h); + canvas.restore(); + } + } +} diff --git a/src/com/android/gallery3d/glrenderer/FadeTexture.java b/src/com/android/gallery3d/glrenderer/FadeTexture.java new file mode 100644 index 000000000..002c90f5c --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/FadeTexture.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2011 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 com.android.gallery3d.common.Utils; +import com.android.gallery3d.ui.AnimationTime; + +// FadeTexture is a texture which fades the given texture along the time. +public abstract class FadeTexture implements Texture { + @SuppressWarnings("unused") + private static final String TAG = "FadeTexture"; + + // The duration of the fading animation in milliseconds + public static final int DURATION = 180; + + private final long mStartTime; + private final int mWidth; + private final int mHeight; + private final boolean mIsOpaque; + private boolean mIsAnimating; + + public FadeTexture(int width, int height, boolean opaque) { + mWidth = width; + mHeight = height; + mIsOpaque = opaque; + mStartTime = now(); + mIsAnimating = true; + } + + @Override + public void draw(GLCanvas canvas, int x, int y) { + draw(canvas, x, y, mWidth, mHeight); + } + + @Override + public boolean isOpaque() { + return mIsOpaque; + } + + @Override + public int getWidth() { + return mWidth; + } + + @Override + public int getHeight() { + return mHeight; + } + + public boolean isAnimating() { + if (mIsAnimating) { + if (now() - mStartTime >= DURATION) { + mIsAnimating = false; + } + } + return mIsAnimating; + } + + protected float getRatio() { + float r = (float)(now() - mStartTime) / DURATION; + return Utils.clamp(1.0f - r, 0.0f, 1.0f); + } + + private long now() { + return AnimationTime.get(); + } +} diff --git a/src/com/android/gallery3d/glrenderer/GLCanvas.java b/src/com/android/gallery3d/glrenderer/GLCanvas.java new file mode 100644 index 000000000..848af75a9 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/GLCanvas.java @@ -0,0 +1,264 @@ +/* + * 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.RectF; + +import com.android.gallery3d.common.ApiHelper; + +import javax.microedition.khronos.opengles.GL11; + +// +// GLCanvas gives a convenient interface to draw using OpenGL. +// +// When a rectangle is specified in this interface, it means the region +// [x, x+width) * [y, y+height) +// +public abstract class GLCanvas { + public enum Blending { + Additive, Mix, + } + + private static GLCanvas sInstance = instantiateCanvas(); + private static GLId sGLId = instantiateGLId(); + + public static GLId getGLId() { + return sGLId; + } + + public static GLCanvas getInstance() { + return sInstance; + } + + private static GLId instantiateGLId() { + return ApiHelper.HAS_GLES20_REQUIRED ? (GLES20Canvas) sInstance : new GLIdImpl(); + } + + private static GLCanvas instantiateCanvas() { + return ApiHelper.HAS_GLES20_REQUIRED ? new GLES20Canvas() : new GLES11Canvas(); + } + + public static int getEGLContextClientVersion() { + return ApiHelper.HAS_GLES20_REQUIRED ? 2 : 1; + } + + public abstract void initialize(GL11 gl); + + // Tells GLCanvas the size of the underlying GL surface. This should be + // called before first drawing and when the size of GL surface is changed. + // This is called by GLRoot and should not be called by the clients + // who only want to draw on the GLCanvas. Both width and height must be + // nonnegative. + public abstract void setSize(int width, int height); + + // Clear the drawing buffers. This should only be used by GLRoot. + public abstract void clearBuffer(); + + public abstract void clearBuffer(float[] argb); + + // Sets and gets the current alpha, alpha must be in [0, 1]. + public abstract void setAlpha(float alpha); + + public abstract float getAlpha(); + + // (current alpha) = (current alpha) * alpha + public abstract void multiplyAlpha(float alpha); + + // Change the current transform matrix. + public abstract void translate(float x, float y, float z); + + public abstract void translate(float x, float y); + + public abstract void scale(float sx, float sy, float sz); + + public abstract void rotate(float angle, float x, float y, float z); + + public abstract void multiplyMatrix(float[] mMatrix, int offset); + + // Pushes the configuration state (matrix, and alpha) onto + // a private stack. + public abstract void save(); + + // Same as save(), but only save those specified in saveFlags. + public abstract void save(int saveFlags); + + public static final int SAVE_FLAG_ALL = 0xFFFFFFFF; + public static final int SAVE_FLAG_ALPHA = 0x01; + public static final int SAVE_FLAG_MATRIX = 0x02; + public static final int SAVE_FLAG_BLEND = 0x04; + + // Pops from the top of the stack as current configuration state (matrix, + // alpha, and clip). This call balances a previous call to save(), and is + // used to remove all modifications to the configuration state since the + // last save call. + public abstract void restore(); + + // Draws a line using the specified paint from (x1, y1) to (x2, y2). + // (Both end points are included). + public abstract void drawLine(float x1, float y1, float x2, float y2, GLPaint paint); + + // Draws a rectangle using the specified paint from (x1, y1) to (x2, y2). + // (Both end points are included). + public abstract void drawRect(float x1, float y1, float x2, float y2, GLPaint paint); + + // Fills the specified rectangle with the specified color. + public abstract void fillRect(float x, float y, float width, float height, int color); + + // Draws a texture to the specified rectangle. + public abstract void drawTexture( + BasicTexture texture, int x, int y, int width, int height); + + public abstract void drawMesh(BasicTexture tex, int x, int y, int xyBuffer, + int uvBuffer, int indexBuffer, int indexCount); + + // Draws the source rectangle part of the texture to the target rectangle. + public abstract void drawTexture(BasicTexture texture, RectF source, RectF target); + + // Draw a texture with a specified texture transform. + public abstract void drawTexture(BasicTexture texture, float[] mTextureTransform, + int x, int y, int w, int h); + + // Draw two textures to the specified rectangle. The actual texture used is + // from * (1 - ratio) + to * ratio + // The two textures must have the same size. + public abstract void drawMixed(BasicTexture from, int toColor, + float ratio, int x, int y, int w, int h); + + // Draw a region of a texture and a specified color to the specified + // rectangle. The actual color used is from * (1 - ratio) + to * ratio. + // The region of the texture is defined by parameter "src". The target + // rectangle is specified by parameter "target". + public abstract void drawMixed(BasicTexture from, int toColor, + float ratio, RectF src, RectF target); + + // Unloads the specified texture from the canvas. The resource allocated + // to draw the texture will be released. The specified texture will return + // to the unloaded state. This function should be called only from + // BasicTexture or its descendant + public abstract boolean unloadTexture(BasicTexture texture); + + // Delete the specified buffer object, similar to unloadTexture. + public abstract void deleteBuffer(int bufferId); + + // Delete the textures and buffers in GL side. This function should only be + // called in the GL thread. + public abstract void deleteRecycledResources(); + + // Dump statistics information and clear the counters. For debug only. + public abstract void dumpStatisticsAndClear(); + + public abstract void beginRenderTarget(RawTexture texture); + + public abstract void endRenderTarget(); + + /** + * Sets texture parameters to use GL_CLAMP_TO_EDGE for both + * GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T. Sets texture parameters to be + * GL_LINEAR for GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER. + * bindTexture() must be called prior to this. + * + * @param texture The texture to set parameters on. + */ + public abstract void setTextureParameters(BasicTexture texture); + + /** + * Initializes the texture to a size by calling texImage2D on it. + * + * @param texture The texture to initialize the size. + * @param format The texture format (e.g. GL_RGBA) + * @param type The texture type (e.g. GL_UNSIGNED_BYTE) + */ + public abstract void initializeTextureSize(BasicTexture texture, int format, int type); + + /** + * Initializes the texture to a size by calling texImage2D on it. + * + * @param texture The texture to initialize the size. + * @param bitmap The bitmap to initialize the bitmap with. + */ + public abstract void initializeTexture(BasicTexture texture, Bitmap bitmap); + + /** + * Calls glTexSubImage2D to upload a bitmap to the texture. + * + * @param texture The target texture to write to. + * @param xOffset Specifies a texel offset in the x direction within the + * texture array. + * @param yOffset Specifies a texel offset in the y direction within the + * texture array. + * @param format The texture format (e.g. GL_RGBA) + * @param type The texture type (e.g. GL_UNSIGNED_BYTE) + */ + public abstract void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, + Bitmap bitmap, + int format, int type); + + /** + * Generates buffers and uploads the buffer data. + * + * @param buffer The buffer to upload + * @return The buffer ID that was generated. + */ + public abstract int uploadBuffer(java.nio.FloatBuffer buffer); + + /** + * Generates buffers and uploads the element array buffer data. + * + * @param buffer The buffer to upload + * @return The buffer ID that was generated. + */ + public abstract int uploadBuffer(java.nio.ByteBuffer buffer); + + /** + * Sets the blending algorithm if a texture is not opaque. + * + * @param blending Either mixing (overlay) or adding a texture. + */ + public abstract void setBlending(Blending blending); + + /** + * Enable stencil test + */ + public abstract void enableStencil(); + + /** + * Disable stencil. + */ + public abstract void disableStencil(); + + /** + * Clears the stencil so that a new stencil can be generated. + */ + public abstract void clearStencilBuffer(); + + /** + * Start/stop updating the stencil buffer. + * + * @param update True if the stencil should be updated, false otherwise. + */ + public abstract void updateStencil(boolean update); + + /** + * Changes how the stencil buffer is used. + * + * @param onlyOutside If true, only the area outside the stencil can be + * changed. If false, the area inside the stencil can be drawn to + * as well. + */ + public abstract void drawOnlyOutsideStencil(boolean onlyOutside); +} diff --git a/src/com/android/gallery3d/glrenderer/GLES11Canvas.java b/src/com/android/gallery3d/glrenderer/GLES11Canvas.java new file mode 100644 index 000000000..c5434215a --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/GLES11Canvas.java @@ -0,0 +1,1039 @@ +/* + * 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.RectF; +import android.opengl.GLU; +import android.opengl.GLUtils; +import android.opengl.Matrix; +import android.util.Log; + +import com.android.gallery3d.common.Utils; +import com.android.gallery3d.util.IntArray; + +import junit.framework.Assert; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.ArrayList; + +import javax.microedition.khronos.opengles.GL10; +import javax.microedition.khronos.opengles.GL11; +import javax.microedition.khronos.opengles.GL11Ext; +import javax.microedition.khronos.opengles.GL11ExtensionPack; + +public class GLES11Canvas extends GLCanvas { + @SuppressWarnings("unused") + private static final String TAG = "GLCanvasImp"; + + private static final float OPAQUE_ALPHA = 0.95f; + + private static final int OFFSET_FILL_RECT = 0; + private static final int OFFSET_DRAW_LINE = 4; + private static final int OFFSET_DRAW_RECT = 6; + private static final float[] BOX_COORDINATES = { + 0, 0, 1, 0, 0, 1, 1, 1, // used for filling a rectangle + 0, 0, 1, 1, // used for drawing a line + 0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle + + private GL11 mGL; + + private final float mMatrixValues[] = new float[16]; + private final float mTextureMatrixValues[] = new float[16]; + + // The results of mapPoints are stored in this buffer, and the order is + // x1, y1, x2, y2. + private final float mMapPointsBuffer[] = new float[4]; + + private final float mTextureColor[] = new float[4]; + + private int mBoxCoords; + + private GLState mGLState; + private final ArrayList mTargetStack = new ArrayList(); + + private float mAlpha; + private final ArrayList mRestoreStack = new ArrayList(); + private ConfigState mRecycledRestoreAction; + + private final RectF mDrawTextureSourceRect = new RectF(); + private final RectF mDrawTextureTargetRect = new RectF(); + private final float[] mTempMatrix = new float[32]; + private final IntArray mUnboundTextures = new IntArray(); + private final IntArray mDeleteBuffers = new IntArray(); + private int mScreenWidth; + private int mScreenHeight; + private boolean mBlendEnabled = true; + private int mFrameBuffer[] = new int[1]; + private static float[] sCropRect = new float[4]; + + private RawTexture mTargetTexture; + private Blending mBlending = Blending.Mix; + + // Drawing statistics + int mCountDrawLine; + int mCountFillRect; + int mCountDrawMesh; + int mCountTextureRect; + int mCountTextureOES; + + GLES11Canvas() { + } + + @Override + public void setSize(int width, int height) { + Assert.assertTrue(width >= 0 && height >= 0); + + if (mTargetTexture == null) { + mScreenWidth = width; + mScreenHeight = height; + } + mAlpha = 1.0f; + + GL11 gl = mGL; + gl.glViewport(0, 0, width, height); + gl.glMatrixMode(GL11.GL_PROJECTION); + gl.glLoadIdentity(); + GLU.gluOrtho2D(gl, 0, width, 0, height); + + gl.glMatrixMode(GL11.GL_MODELVIEW); + gl.glLoadIdentity(); + + float matrix[] = mMatrixValues; + Matrix.setIdentityM(matrix, 0); + // to match the graphic coordinate system in android, we flip it vertically. + if (mTargetTexture == null) { + Matrix.translateM(matrix, 0, 0, height, 0); + Matrix.scaleM(matrix, 0, 1, -1, 1); + } + } + + @Override + public void setAlpha(float alpha) { + Assert.assertTrue(alpha >= 0 && alpha <= 1); + mAlpha = alpha; + } + + @Override + public float getAlpha() { + return mAlpha; + } + + @Override + public void multiplyAlpha(float alpha) { + Assert.assertTrue(alpha >= 0 && alpha <= 1); + mAlpha *= alpha; + } + + private static ByteBuffer allocateDirectNativeOrderBuffer(int size) { + return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); + } + + @Override + public void initialize(GL11 gl) { + mGL = gl; + mGLState = new GLState(gl); + // First create an nio buffer, then create a VBO from it. + int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE; + FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer(); + xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0); + + int[] name = new int[1]; + GLId glId = getGLId(); + glId.glGenBuffers(1, name, 0); + mBoxCoords = name[0]; + + gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords); + gl.glBufferData(GL11.GL_ARRAY_BUFFER, + xyBuffer.capacity() * (Float.SIZE / Byte.SIZE), + xyBuffer, GL11.GL_STATIC_DRAW); + + gl.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); + gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); + + // Enable the texture coordinate array for Texture 1 + gl.glClientActiveTexture(GL11.GL_TEXTURE1); + gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); + gl.glClientActiveTexture(GL11.GL_TEXTURE0); + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + // mMatrixValues and mAlpha will be initialized in setSize() + } + + @Override + public void drawRect(float x, float y, float width, float height, GLPaint paint) { + GL11 gl = mGL; + + mGLState.setColorMode(paint.getColor(), mAlpha); + mGLState.setLineWidth(paint.getLineWidth()); + + saveTransform(); + translate(x, y); + scale(width, height, 1); + + gl.glLoadMatrixf(mMatrixValues, 0); + gl.glDrawArrays(GL11.GL_LINE_LOOP, OFFSET_DRAW_RECT, 4); + + restoreTransform(); + mCountDrawLine++; + } + + @Override + public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) { + GL11 gl = mGL; + + mGLState.setColorMode(paint.getColor(), mAlpha); + mGLState.setLineWidth(paint.getLineWidth()); + + saveTransform(); + translate(x1, y1); + scale(x2 - x1, y2 - y1, 1); + + gl.glLoadMatrixf(mMatrixValues, 0); + gl.glDrawArrays(GL11.GL_LINE_STRIP, OFFSET_DRAW_LINE, 2); + + restoreTransform(); + mCountDrawLine++; + } + + @Override + public void fillRect(float x, float y, float width, float height, int color) { + mGLState.setColorMode(color, mAlpha); + GL11 gl = mGL; + + saveTransform(); + translate(x, y); + scale(width, height, 1); + + gl.glLoadMatrixf(mMatrixValues, 0); + gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4); + + restoreTransform(); + mCountFillRect++; + } + + @Override + public void translate(float x, float y, float z) { + Matrix.translateM(mMatrixValues, 0, x, y, z); + } + + // This is a faster version of translate(x, y, z) because + // (1) we knows z = 0, (2) we inline the Matrix.translateM call, + // (3) we unroll the loop + @Override + public void translate(float x, float y) { + float[] m = mMatrixValues; + m[12] += m[0] * x + m[4] * y; + m[13] += m[1] * x + m[5] * y; + m[14] += m[2] * x + m[6] * y; + m[15] += m[3] * x + m[7] * y; + } + + @Override + public void scale(float sx, float sy, float sz) { + Matrix.scaleM(mMatrixValues, 0, sx, sy, sz); + } + + @Override + public void rotate(float angle, float x, float y, float z) { + if (angle == 0) return; + float[] temp = mTempMatrix; + Matrix.setRotateM(temp, 0, angle, x, y, z); + Matrix.multiplyMM(temp, 16, mMatrixValues, 0, temp, 0); + System.arraycopy(temp, 16, mMatrixValues, 0, 16); + } + + @Override + public void multiplyMatrix(float matrix[], int offset) { + float[] temp = mTempMatrix; + Matrix.multiplyMM(temp, 0, mMatrixValues, 0, matrix, offset); + System.arraycopy(temp, 0, mMatrixValues, 0, 16); + } + + private void textureRect(float x, float y, float width, float height) { + GL11 gl = mGL; + + saveTransform(); + translate(x, y); + scale(width, height, 1); + + gl.glLoadMatrixf(mMatrixValues, 0); + gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4); + + restoreTransform(); + mCountTextureRect++; + } + + @Override + public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer, + int uvBuffer, int indexBuffer, int indexCount) { + float alpha = mAlpha; + if (!bindTexture(tex)) return; + + mGLState.setBlendEnabled(mBlendEnabled + && (!tex.isOpaque() || alpha < OPAQUE_ALPHA)); + mGLState.setTextureAlpha(alpha); + + // Reset the texture matrix. We will set our own texture coordinates + // below. + setTextureCoords(0, 0, 1, 1); + + saveTransform(); + translate(x, y); + + mGL.glLoadMatrixf(mMatrixValues, 0); + + mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, xyBuffer); + mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); + + mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, uvBuffer); + mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); + + mGL.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, indexBuffer); + mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP, + indexCount, GL11.GL_UNSIGNED_BYTE, 0); + + mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords); + mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); + mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); + + restoreTransform(); + mCountDrawMesh++; + } + + // Transforms two points by the given matrix m. The result + // {x1', y1', x2', y2'} are stored in mMapPointsBuffer and also returned. + private float[] mapPoints(float m[], int x1, int y1, int x2, int y2) { + float[] r = mMapPointsBuffer; + + // Multiply m and (x1 y1 0 1) to produce (x3 y3 z3 w3). z3 is unused. + float x3 = m[0] * x1 + m[4] * y1 + m[12]; + float y3 = m[1] * x1 + m[5] * y1 + m[13]; + float w3 = m[3] * x1 + m[7] * y1 + m[15]; + r[0] = x3 / w3; + r[1] = y3 / w3; + + // Same for x2 y2. + float x4 = m[0] * x2 + m[4] * y2 + m[12]; + float y4 = m[1] * x2 + m[5] * y2 + m[13]; + float w4 = m[3] * x2 + m[7] * y2 + m[15]; + r[2] = x4 / w4; + r[3] = y4 / w4; + + return r; + } + + private void drawBoundTexture( + BasicTexture texture, int x, int y, int width, int height) { + // Test whether it has been rotated or flipped, if so, glDrawTexiOES + // won't work + if (isMatrixRotatedOrFlipped(mMatrixValues)) { + if (texture.hasBorder()) { + setTextureCoords( + 1.0f / texture.getTextureWidth(), + 1.0f / texture.getTextureHeight(), + (texture.getWidth() - 1.0f) / texture.getTextureWidth(), + (texture.getHeight() - 1.0f) / texture.getTextureHeight()); + } else { + setTextureCoords(0, 0, + (float) texture.getWidth() / texture.getTextureWidth(), + (float) texture.getHeight() / texture.getTextureHeight()); + } + textureRect(x, y, width, height); + } else { + // draw the rect from bottom-left to top-right + float points[] = mapPoints( + mMatrixValues, x, y + height, x + width, y); + x = (int) (points[0] + 0.5f); + y = (int) (points[1] + 0.5f); + width = (int) (points[2] + 0.5f) - x; + height = (int) (points[3] + 0.5f) - y; + if (width > 0 && height > 0) { + ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height); + mCountTextureOES++; + } + } + } + + @Override + public void drawTexture( + BasicTexture texture, int x, int y, int width, int height) { + drawTexture(texture, x, y, width, height, mAlpha); + } + + private void drawTexture(BasicTexture texture, + int x, int y, int width, int height, float alpha) { + if (width <= 0 || height <= 0) return; + + mGLState.setBlendEnabled(mBlendEnabled + && (!texture.isOpaque() || alpha < OPAQUE_ALPHA)); + if (!bindTexture(texture)) return; + mGLState.setTextureAlpha(alpha); + drawBoundTexture(texture, x, y, width, height); + } + + @Override + public void drawTexture(BasicTexture texture, RectF source, RectF target) { + if (target.width() <= 0 || target.height() <= 0) return; + + // Copy the input to avoid changing it. + mDrawTextureSourceRect.set(source); + mDrawTextureTargetRect.set(target); + source = mDrawTextureSourceRect; + target = mDrawTextureTargetRect; + + mGLState.setBlendEnabled(mBlendEnabled + && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA)); + if (!bindTexture(texture)) return; + convertCoordinate(source, target, texture); + setTextureCoords(source); + mGLState.setTextureAlpha(mAlpha); + textureRect(target.left, target.top, target.width(), target.height()); + } + + @Override + public void drawTexture(BasicTexture texture, float[] mTextureTransform, + int x, int y, int w, int h) { + mGLState.setBlendEnabled(mBlendEnabled + && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA)); + if (!bindTexture(texture)) return; + setTextureCoords(mTextureTransform); + mGLState.setTextureAlpha(mAlpha); + textureRect(x, y, w, h); + } + + // This function changes the source coordinate to the texture coordinates. + // It also clips the source and target coordinates if it is beyond the + // bound of the texture. + private static void convertCoordinate(RectF source, RectF target, + BasicTexture texture) { + + int width = texture.getWidth(); + int height = texture.getHeight(); + int texWidth = texture.getTextureWidth(); + int texHeight = texture.getTextureHeight(); + // Convert to texture coordinates + source.left /= texWidth; + source.right /= texWidth; + source.top /= texHeight; + source.bottom /= texHeight; + + // Clip if the rendering range is beyond the bound of the texture. + float xBound = (float) width / texWidth; + if (source.right > xBound) { + target.right = target.left + target.width() * + (xBound - source.left) / source.width(); + source.right = xBound; + } + float yBound = (float) height / texHeight; + if (source.bottom > yBound) { + target.bottom = target.top + target.height() * + (yBound - source.top) / source.height(); + source.bottom = yBound; + } + } + + @Override + public void drawMixed(BasicTexture from, + int toColor, float ratio, int x, int y, int w, int h) { + drawMixed(from, toColor, ratio, x, y, w, h, mAlpha); + } + + private boolean bindTexture(BasicTexture texture) { + if (!texture.onBind(this)) return false; + int target = texture.getTarget(); + mGLState.setTextureTarget(target); + mGL.glBindTexture(target, texture.getId()); + return true; + } + + private void setTextureColor(float r, float g, float b, float alpha) { + float[] color = mTextureColor; + color[0] = r; + color[1] = g; + color[2] = b; + color[3] = alpha; + } + + private void setMixedColor(int toColor, float ratio, float alpha) { + // + // The formula we want: + // alpha * ((1 - ratio) * from + ratio * to) + // + // The formula that GL supports is in the form of: + // combo * from + (1 - combo) * to * scale + // + // So, we have combo = alpha * (1 - ratio) + // and scale = alpha * ratio / (1 - combo) + // + float combo = alpha * (1 - ratio); + float scale = alpha * ratio / (1 - combo); + + // Specify the interpolation factor via the alpha component of + // GL_TEXTURE_ENV_COLORs. + // RGB component are get from toColor and will used as SRC1 + float colorScale = scale * (toColor >>> 24) / (0xff * 0xff); + setTextureColor(((toColor >>> 16) & 0xff) * colorScale, + ((toColor >>> 8) & 0xff) * colorScale, + (toColor & 0xff) * colorScale, combo); + GL11 gl = mGL; + gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0); + + gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE); + gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE); + gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_RGB, GL11.GL_CONSTANT); + gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_RGB, GL11.GL_SRC_COLOR); + gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_ALPHA, GL11.GL_CONSTANT); + gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_ALPHA, GL11.GL_SRC_ALPHA); + + // Wire up the interpolation factor for RGB. + gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT); + gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA); + + // Wire up the interpolation factor for alpha. + gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT); + gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA); + + } + + @Override + public void drawMixed(BasicTexture from, int toColor, float ratio, + RectF source, RectF target) { + if (target.width() <= 0 || target.height() <= 0) return; + + if (ratio <= 0.01f) { + drawTexture(from, source, target); + return; + } else if (ratio >= 1) { + fillRect(target.left, target.top, target.width(), target.height(), toColor); + return; + } + + float alpha = mAlpha; + + // Copy the input to avoid changing it. + mDrawTextureSourceRect.set(source); + mDrawTextureTargetRect.set(target); + source = mDrawTextureSourceRect; + target = mDrawTextureTargetRect; + + mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque() + || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA)); + + if (!bindTexture(from)) return; + + // Interpolate the RGB and alpha values between both textures. + mGLState.setTexEnvMode(GL11.GL_COMBINE); + setMixedColor(toColor, ratio, alpha); + convertCoordinate(source, target, from); + setTextureCoords(source); + textureRect(target.left, target.top, target.width(), target.height()); + mGLState.setTexEnvMode(GL11.GL_REPLACE); + } + + private void drawMixed(BasicTexture from, int toColor, + float ratio, int x, int y, int width, int height, float alpha) { + // change from 0 to 0.01f to prevent getting divided by zero below + if (ratio <= 0.01f) { + drawTexture(from, x, y, width, height, alpha); + return; + } else if (ratio >= 1) { + fillRect(x, y, width, height, toColor); + return; + } + + mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque() + || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA)); + + final GL11 gl = mGL; + if (!bindTexture(from)) return; + + // Interpolate the RGB and alpha values between both textures. + mGLState.setTexEnvMode(GL11.GL_COMBINE); + setMixedColor(toColor, ratio, alpha); + + drawBoundTexture(from, x, y, width, height); + mGLState.setTexEnvMode(GL11.GL_REPLACE); + } + + // TODO: the code only work for 2D should get fixed for 3D or removed + private static final int MSKEW_X = 4; + private static final int MSKEW_Y = 1; + private static final int MSCALE_X = 0; + private static final int MSCALE_Y = 5; + + private static boolean isMatrixRotatedOrFlipped(float matrix[]) { + final float eps = 1e-5f; + return Math.abs(matrix[MSKEW_X]) > eps + || Math.abs(matrix[MSKEW_Y]) > eps + || matrix[MSCALE_X] < -eps + || matrix[MSCALE_Y] > eps; + } + + private static class GLState { + + private final GL11 mGL; + + private int mTexEnvMode = GL11.GL_REPLACE; + private float mTextureAlpha = 1.0f; + private int mTextureTarget = GL11.GL_TEXTURE_2D; + private boolean mBlendEnabled = true; + private float mLineWidth = 1.0f; + private boolean mLineSmooth = false; + + public GLState(GL11 gl) { + mGL = gl; + + // Disable unused state + gl.glDisable(GL11.GL_LIGHTING); + + // Enable used features + gl.glEnable(GL11.GL_DITHER); + + gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); + gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + gl.glEnable(GL11.GL_TEXTURE_2D); + + gl.glTexEnvf(GL11.GL_TEXTURE_ENV, + GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE); + + // Set the background color + gl.glClearColor(0f, 0f, 0f, 0f); + gl.glClearStencil(0); + + gl.glEnable(GL11.GL_BLEND); + gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); + + // We use 565 or 8888 format, so set the alignment to 2 bytes/pixel. + gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2); + } + + public void setTexEnvMode(int mode) { + if (mTexEnvMode == mode) return; + mTexEnvMode = mode; + mGL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, mode); + } + + public void setLineWidth(float width) { + if (mLineWidth == width) return; + mLineWidth = width; + mGL.glLineWidth(width); + } + + public void setTextureAlpha(float alpha) { + if (mTextureAlpha == alpha) return; + mTextureAlpha = alpha; + if (alpha >= OPAQUE_ALPHA) { + // The alpha is need for those texture without alpha channel + mGL.glColor4f(1, 1, 1, 1); + setTexEnvMode(GL11.GL_REPLACE); + } else { + mGL.glColor4f(alpha, alpha, alpha, alpha); + setTexEnvMode(GL11.GL_MODULATE); + } + } + + public void setColorMode(int color, float alpha) { + setBlendEnabled(!Utils.isOpaque(color) || alpha < OPAQUE_ALPHA); + + // Set mTextureAlpha to an invalid value, so that it will reset + // again in setTextureAlpha(float) later. + mTextureAlpha = -1.0f; + + setTextureTarget(0); + + float prealpha = (color >>> 24) * alpha * 65535f / 255f / 255f; + mGL.glColor4x( + Math.round(((color >> 16) & 0xFF) * prealpha), + Math.round(((color >> 8) & 0xFF) * prealpha), + Math.round((color & 0xFF) * prealpha), + Math.round(255 * prealpha)); + } + + // target is a value like GL_TEXTURE_2D. If target = 0, texturing is disabled. + public void setTextureTarget(int target) { + if (mTextureTarget == target) return; + if (mTextureTarget != 0) { + mGL.glDisable(mTextureTarget); + } + mTextureTarget = target; + if (mTextureTarget != 0) { + mGL.glEnable(mTextureTarget); + } + } + + public void setBlendEnabled(boolean enabled) { + if (mBlendEnabled == enabled) return; + mBlendEnabled = enabled; + if (enabled) { + mGL.glEnable(GL11.GL_BLEND); + } else { + mGL.glDisable(GL11.GL_BLEND); + } + } + } + + @Override + public void clearBuffer(float[] argb) { + if(argb != null && argb.length == 4) { + mGL.glClearColor(argb[1], argb[2], argb[3], argb[0]); + } else { + mGL.glClearColor(0, 0, 0, 1); + } + mGL.glClear(GL10.GL_COLOR_BUFFER_BIT); + } + + @Override + public void clearBuffer() { + clearBuffer(null); + } + + private void setTextureCoords(RectF source) { + setTextureCoords(source.left, source.top, source.right, source.bottom); + } + + private void setTextureCoords(float left, float top, + float right, float bottom) { + mGL.glMatrixMode(GL11.GL_TEXTURE); + mTextureMatrixValues[0] = right - left; + mTextureMatrixValues[5] = bottom - top; + mTextureMatrixValues[10] = 1; + mTextureMatrixValues[12] = left; + mTextureMatrixValues[13] = top; + mTextureMatrixValues[15] = 1; + mGL.glLoadMatrixf(mTextureMatrixValues, 0); + mGL.glMatrixMode(GL11.GL_MODELVIEW); + } + + private void setTextureCoords(float[] mTextureTransform) { + mGL.glMatrixMode(GL11.GL_TEXTURE); + mGL.glLoadMatrixf(mTextureTransform, 0); + mGL.glMatrixMode(GL11.GL_MODELVIEW); + } + + // unloadTexture and deleteBuffer can be called from the finalizer thread, + // so we synchronized on the mUnboundTextures object. + @Override + public boolean unloadTexture(BasicTexture t) { + synchronized (mUnboundTextures) { + if (!t.isLoaded()) return false; + mUnboundTextures.add(t.mId); + return true; + } + } + + @Override + public void deleteBuffer(int bufferId) { + synchronized (mUnboundTextures) { + mDeleteBuffers.add(bufferId); + } + } + + @Override + public void deleteRecycledResources() { + synchronized (mUnboundTextures) { + IntArray ids = mUnboundTextures; + GLId glId = getGLId(); + if (ids.size() > 0) { + glId.glDeleteTextures(mGL, ids.size(), ids.getInternalArray(), 0); + ids.clear(); + } + + ids = mDeleteBuffers; + if (ids.size() > 0) { + glId.glDeleteBuffers(mGL, ids.size(), ids.getInternalArray(), 0); + ids.clear(); + } + } + } + + @Override + public void save() { + save(SAVE_FLAG_ALL); + } + + @Override + public void save(int saveFlags) { + ConfigState config = obtainRestoreConfig(); + + if ((saveFlags & SAVE_FLAG_ALPHA) != 0) { + config.mAlpha = mAlpha; + } else { + config.mAlpha = -1; + } + + if ((saveFlags & SAVE_FLAG_BLEND) != 0) { + config.mBlending = mBlending; + } else { + config.mBlending = null; + } + + if ((saveFlags & SAVE_FLAG_MATRIX) != 0) { + System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16); + } else { + config.mMatrix[0] = Float.NEGATIVE_INFINITY; + } + + mRestoreStack.add(config); + } + + @Override + public void restore() { + if (mRestoreStack.isEmpty()) throw new IllegalStateException(); + ConfigState config = mRestoreStack.remove(mRestoreStack.size() - 1); + config.restore(this); + freeRestoreConfig(config); + } + + private void freeRestoreConfig(ConfigState action) { + action.mNextFree = mRecycledRestoreAction; + mRecycledRestoreAction = action; + } + + private ConfigState obtainRestoreConfig() { + if (mRecycledRestoreAction != null) { + ConfigState result = mRecycledRestoreAction; + mRecycledRestoreAction = result.mNextFree; + return result; + } + return new ConfigState(); + } + + private static class ConfigState { + float mAlpha; + float mMatrix[] = new float[16]; + Blending mBlending; + ConfigState mNextFree; + + public void restore(GLES11Canvas canvas) { + if (mAlpha >= 0) canvas.setAlpha(mAlpha); + if (mMatrix[0] != Float.NEGATIVE_INFINITY) { + System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16); + } + if (mBlending != null) { + canvas.setBlending(mBlending); + } + } + } + + @Override + public void dumpStatisticsAndClear() { + String line = String.format( + "MESH:%d, TEX_OES:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", + mCountDrawMesh, mCountTextureRect, mCountTextureOES, + mCountFillRect, mCountDrawLine); + mCountDrawMesh = 0; + mCountTextureRect = 0; + mCountTextureOES = 0; + mCountFillRect = 0; + mCountDrawLine = 0; + Log.d(TAG, line); + } + + private void saveTransform() { + System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16); + } + + private void restoreTransform() { + System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16); + } + + private void setRenderTarget(RawTexture texture) { + GL11ExtensionPack gl11ep = (GL11ExtensionPack) mGL; + + if (mTargetTexture == null && texture != null) { + GLId glId = getGLId(); + glId.glGenBuffers(1, mFrameBuffer, 0); + gl11ep.glBindFramebufferOES( + GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFrameBuffer[0]); + } + if (mTargetTexture != null && texture == null) { + gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0); + gl11ep.glDeleteFramebuffersOES(1, mFrameBuffer, 0); + } + + mTargetTexture = texture; + if (texture == null) { + setSize(mScreenWidth, mScreenHeight); + } else { + setSize(texture.getWidth(), texture.getHeight()); + + if (!texture.isLoaded()) texture.prepare(this); + + gl11ep.glFramebufferTexture2DOES( + GL11ExtensionPack.GL_FRAMEBUFFER_OES, + GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES, + GL11.GL_TEXTURE_2D, texture.getId(), 0); + + checkFramebufferStatus(gl11ep); + } + } + + @Override + public void endRenderTarget() { + RawTexture texture = mTargetStack.remove(mTargetStack.size() - 1); + setRenderTarget(texture); + restore(); // restore matrix and alpha + } + + @Override + public void beginRenderTarget(RawTexture texture) { + save(); // save matrix and alpha + mTargetStack.add(mTargetTexture); + setRenderTarget(texture); + } + + private static void checkFramebufferStatus(GL11ExtensionPack gl11ep) { + int status = gl11ep.glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES); + if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES) { + String msg = ""; + switch (status) { + case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES: + msg = "FRAMEBUFFER_FORMATS"; + break; + case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES: + msg = "FRAMEBUFFER_ATTACHMENT"; + break; + case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES: + msg = "FRAMEBUFFER_MISSING_ATTACHMENT"; + break; + case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES: + msg = "FRAMEBUFFER_DRAW_BUFFER"; + break; + case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES: + msg = "FRAMEBUFFER_READ_BUFFER"; + break; + case GL11ExtensionPack.GL_FRAMEBUFFER_UNSUPPORTED_OES: + msg = "FRAMEBUFFER_UNSUPPORTED"; + break; + case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES: + msg = "FRAMEBUFFER_INCOMPLETE_DIMENSIONS"; + break; + } + throw new RuntimeException(msg + ":" + Integer.toHexString(status)); + } + } + + @Override + public void setTextureParameters(BasicTexture texture) { + int width = texture.getWidth(); + int height = texture.getHeight(); + // Define a vertically flipped crop rectangle for OES_draw_texture. + // The four values in sCropRect are: left, bottom, width, and + // height. Negative value of width or height means flip. + sCropRect[0] = 0; + sCropRect[1] = height; + sCropRect[2] = width; + sCropRect[3] = -height; + + // Set texture parameters. + int target = texture.getTarget(); + mGL.glBindTexture(target, texture.getId()); + mGL.glTexParameterfv(target, GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0); + mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE); + mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE); + mGL.glTexParameterf(target, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + mGL.glTexParameterf(target, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); + } + + @Override + public void initializeTextureSize(BasicTexture texture, int format, int type) { + int target = texture.getTarget(); + mGL.glBindTexture(target, texture.getId()); + int width = texture.getTextureWidth(); + int height = texture.getTextureHeight(); + mGL.glTexImage2D(target, 0, format, width, height, 0, format, type, null); + } + + @Override + public void initializeTexture(BasicTexture texture, Bitmap bitmap) { + int target = texture.getTarget(); + mGL.glBindTexture(target, texture.getId()); + GLUtils.texImage2D(target, 0, bitmap, 0); + } + + @Override + public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap, + int format, int type) { + int target = texture.getTarget(); + mGL.glBindTexture(target, texture.getId()); + GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type); + } + + @Override + public int uploadBuffer(FloatBuffer buf) { + return uploadBuffer(buf, Float.SIZE / Byte.SIZE); + } + + @Override + public int uploadBuffer(ByteBuffer buf) { + return uploadBuffer(buf, 1); + } + + private int uploadBuffer(Buffer buf, int elementSize) { + int[] bufferIds = new int[1]; + GLId glId = getGLId(); + glId.glGenBuffers(bufferIds.length, bufferIds, 0); + int bufferId = bufferIds[0]; + mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, bufferId); + mGL.glBufferData(GL11.GL_ARRAY_BUFFER, buf.capacity() * elementSize, buf, + GL11.GL_STATIC_DRAW); + return bufferId; + } + + @Override + public void setBlending(Blending blending) { + if (mBlending == blending) { + return; + } + Assert.assertTrue(blending == Blending.Additive || blending == Blending.Mix); + mBlending = blending; + int srcFunc = GL11.GL_ONE; + int dstFunc = (blending == Blending.Additive) ? GL11.GL_ONE : GL11.GL_ONE_MINUS_SRC_ALPHA; + mGL.glBlendFunc(srcFunc, dstFunc); + } + + @Override + public void enableStencil() { + mGL.glEnable(GL11.GL_STENCIL_TEST); + } + + @Override + public void disableStencil() { + mGL.glDisable(GL11.GL_STENCIL_TEST); + } + + @Override + public void clearStencilBuffer() { + mGL.glClear(GL11.GL_STENCIL_BUFFER_BIT); + } + + @Override + public void updateStencil(boolean update) { + int passOp = update ? GL11.GL_REPLACE : GL11.GL_KEEP; + mGL.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, passOp); + } + + @Override + public void drawOnlyOutsideStencil(boolean onlyOutside) { + int func = onlyOutside ? GL11.GL_NOTEQUAL : GL11.GL_ALWAYS; + mGL.glStencilFunc(func, 1, 1); + } +} diff --git a/src/com/android/gallery3d/glrenderer/GLES20Canvas.java b/src/com/android/gallery3d/glrenderer/GLES20Canvas.java new file mode 100644 index 000000000..3c3eebf20 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/GLES20Canvas.java @@ -0,0 +1,1068 @@ +/* + * Copyright (C) 2012 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.RectF; +import android.opengl.GLES20; +import android.opengl.GLUtils; +import android.opengl.Matrix; +import android.util.Log; + +import com.android.gallery3d.common.Utils; +import com.android.gallery3d.util.IntArray; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.Arrays; + +import javax.microedition.khronos.opengles.GL11; +import javax.microedition.khronos.opengles.GL11ExtensionPack; + +public class GLES20Canvas extends GLCanvas implements GLId { + // ************** Constants ********************** + private static final String TAG = GLES20Canvas.class.getSimpleName(); + private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE; + private static final float OPAQUE_ALPHA = 0.95f; + + private static final int COORDS_PER_VERTEX = 2; + private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE; + + private static final int COUNT_FILL_VERTEX = 4; + private static final int COUNT_LINE_VERTEX = 2; + private static final int COUNT_RECT_VERTEX = 4; + private static final int OFFSET_FILL_RECT = 0; + private static final int OFFSET_DRAW_LINE = OFFSET_FILL_RECT + COUNT_FILL_VERTEX; + private static final int OFFSET_DRAW_RECT = OFFSET_DRAW_LINE + COUNT_LINE_VERTEX; + + private static final float[] BOX_COORDINATES = { + 0, 0, // Fill rectangle + 1, 0, + 0, 1, + 1, 1, + 0, 0, // Draw line + 1, 1, + 0, 0, // Draw rectangle outline + 0, 1, + 1, 1, + 1, 0, + }; + + private static final String POSITION_ATTRIBUTE = "aPosition"; + private static final String COLOR_UNIFORM = "uColor"; + private static final String MATRIX_UNIFORM = "uMatrix"; + private static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix"; + private static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler"; + private static final String ALPHA_UNIFORM = "uAlpha"; + private static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate"; + + private static final String DRAW_VERTEX_SHADER = "" + + "uniform mat4 " + MATRIX_UNIFORM + ";\n" + + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n" + + "void main() {\n" + + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n" + + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n" + + "}\n"; + + private static final String DRAW_FRAGMENT_SHADER = "" + + "precision mediump float;\n" + + "uniform vec4 " + COLOR_UNIFORM + ";\n" + + "void main() {\n" + + " gl_FragColor = " + COLOR_UNIFORM + ";\n" + + "}\n"; + + private static final String TEXTURE_VERTEX_SHADER = "" + + "uniform mat4 " + MATRIX_UNIFORM + ";\n" + + "uniform mat4 " + TEXTURE_MATRIX_UNIFORM + ";\n" + + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n" + + "varying vec2 vTextureCoord;\n" + + "void main() {\n" + + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n" + + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n" + + " vTextureCoord = (" + TEXTURE_MATRIX_UNIFORM + " * pos).xy;\n" + + "}\n"; + + private static final String MESH_VERTEX_SHADER = "" + + "uniform mat4 " + MATRIX_UNIFORM + ";\n" + + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n" + + "attribute vec2 " + TEXTURE_COORD_ATTRIBUTE + ";\n" + + "varying vec2 vTextureCoord;\n" + + "void main() {\n" + + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n" + + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n" + + " vTextureCoord = " + TEXTURE_COORD_ATTRIBUTE + ";\n" + + "}\n"; + + private static final String TEXTURE_FRAGMENT_SHADER = "" + + "precision mediump float;\n" + + "varying vec2 vTextureCoord;\n" + + "uniform float " + ALPHA_UNIFORM + ";\n" + + "uniform sampler2D " + TEXTURE_SAMPLER_UNIFORM + ";\n" + + "void main() {\n" + + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n" + + " gl_FragColor.a *= " + ALPHA_UNIFORM + ";\n" + + "}\n"; + + private static final String OES_TEXTURE_FRAGMENT_SHADER = "" + + "#extension GL_OES_EGL_image_external : require\n" + + "precision mediump float;\n" + + "varying vec2 vTextureCoord;\n" + + "uniform float " + ALPHA_UNIFORM + ";\n" + + "uniform samplerExternalOES " + TEXTURE_SAMPLER_UNIFORM + ";\n" + + "void main() {\n" + + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n" + + " gl_FragColor.a *= " + ALPHA_UNIFORM + ";\n" + + "}\n"; + + private static final int INITIAL_RESTORE_STATE_SIZE = 8; + private static final int MATRIX_SIZE = 16; + + // Keep track of restore state + private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE]; + private float[] mAlphas = new float[INITIAL_RESTORE_STATE_SIZE]; + private IntArray mSaveFlags = new IntArray(); + private ArrayList mBlendings = new ArrayList(); + + private int mCurrentAlphaIndex = 0; + private int mCurrentMatrixIndex = 0; + + // Viewport size + private int mWidth; + private int mHeight; + + // Projection matrix + private float[] mProjectionMatrix = new float[MATRIX_SIZE]; + + // Screen size for when we aren't bound to a texture + private int mScreenWidth; + private int mScreenHeight; + + // GL programs + private int mDrawProgram; + private int mTextureProgram; + private int mOesTextureProgram; + private int mMeshProgram; + + // GL buffer containing BOX_COORDINATES + private int mBoxCoordinates; + + // Handle indices -- common + private static final int INDEX_POSITION = 0; + private static final int INDEX_MATRIX = 1; + + // Handle indices -- draw + private static final int INDEX_COLOR = 2; + + // Handle indices -- texture + private static final int INDEX_TEXTURE_MATRIX = 2; + private static final int INDEX_TEXTURE_SAMPLER = 3; + private static final int INDEX_ALPHA = 4; + + // Handle indices -- mesh + private static final int INDEX_TEXTURE_COORD = 2; + + private abstract static class ShaderParameter { + public int handle; + protected final String mName; + + public ShaderParameter(String name) { + mName = name; + } + + public abstract void loadHandle(int program); + } + + private static class UniformShaderParameter extends ShaderParameter { + public UniformShaderParameter(String name) { + super(name); + } + + @Override + public void loadHandle(int program) { + handle = GLES20.glGetUniformLocation(program, mName); + checkError(); + } + } + + private static class AttributeShaderParameter extends ShaderParameter { + public AttributeShaderParameter(String name) { + super(name); + } + + @Override + public void loadHandle(int program) { + handle = GLES20.glGetAttribLocation(program, mName); + checkError(); + } + } + + ShaderParameter[] mDrawParameters = { + new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION + new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX + new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR + }; + ShaderParameter[] mTextureParameters = { + new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION + new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX + new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX + new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER + new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA + }; + ShaderParameter[] mOesTextureParameters = { + new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION + new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX + new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX + new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER + new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA + }; + ShaderParameter[] mMeshParameters = { + new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION + new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX + new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD + new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER + new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA + }; + + private final IntArray mUnboundTextures = new IntArray(); + private final IntArray mDeleteBuffers = new IntArray(); + + // Keep track of statistics for debugging + private int mCountDrawMesh = 0; + private int mCountTextureRect = 0; + private int mCountFillRect = 0; + private int mCountDrawLine = 0; + + private int mNextTextureId = 1; + + // Buffer for framebuffer IDs -- we keep track so we can switch the attached + // texture. + private int[] mFrameBuffer = new int[1]; + + // Bound textures. + private ArrayList mTargetTextures = new ArrayList(); + + // Temporary variables used within calculations + private final float[] mTempMatrix = new float[32]; + private final float[] mTempColor = new float[4]; + private final RectF mTempSourceRect = new RectF(); + private final RectF mTempTargetRect = new RectF(); + private final float[] mTempTextureMatrix = new float[MATRIX_SIZE]; + private final int[] mTempIntArray = new int[1]; + + public GLES20Canvas() { + Matrix.setIdentityM(mTempTextureMatrix, 0); + Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex); + mAlphas[mCurrentAlphaIndex] = 1f; + mTargetTextures.add(null); + } + + @Override + public void initialize(GL11 gl) { + FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES); + mBoxCoordinates = uploadBuffer(boxBuffer); + + int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER); + int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER); + int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER); + int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER); + int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER); + int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, + OES_TEXTURE_FRAGMENT_SHADER); + + mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters); + mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader, + mTextureParameters); + mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader, + mOesTextureParameters); + mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters); + + mBlendings.clear(); + mBlendings.add(null); + setBlending(Blending.Mix); + } + + private static FloatBuffer createBuffer(float[] values) { + // First create an nio buffer, then create a VBO from it. + int size = values.length * FLOAT_SIZE; + FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()) + .asFloatBuffer(); + buffer.put(values, 0, values.length).position(0); + return buffer; + } + + private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) { + int program = GLES20.glCreateProgram(); + checkError(); + if (program == 0) { + throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError()); + } + GLES20.glAttachShader(program, vertexShader); + checkError(); + GLES20.glAttachShader(program, fragmentShader); + checkError(); + GLES20.glLinkProgram(program); + checkError(); + int[] mLinkStatus = mTempIntArray; + GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0); + if (mLinkStatus[0] != GLES20.GL_TRUE) { + Log.e(TAG, "Could not link program: "); + Log.e(TAG, GLES20.glGetProgramInfoLog(program)); + GLES20.glDeleteProgram(program); + program = 0; + } + for (int i = 0; i < params.length; i++) { + params[i].loadHandle(program); + } + return program; + } + + private static int loadShader(int type, String shaderCode) { + // create a vertex shader type (GLES20.GL_VERTEX_SHADER) + // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) + int shader = GLES20.glCreateShader(type); + + // add the source code to the shader and compile it + GLES20.glShaderSource(shader, shaderCode); + checkError(); + GLES20.glCompileShader(shader); + checkError(); + + return shader; + } + + @Override + public void setSize(int width, int height) { + mWidth = width; + mHeight = height; + GLES20.glViewport(0, 0, mWidth, mHeight); + checkError(); + Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex); + Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1); + if (getTargetTexture() == null) { + mScreenWidth = width; + mScreenHeight = height; + Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0); + Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1); + } + } + + @Override + public void clearBuffer() { + GLES20.glClearColor(0f, 0f, 0f, 1f); + checkError(); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + checkError(); + } + + @Override + public void clearBuffer(float[] argb) { + GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]); + checkError(); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + checkError(); + } + + @Override + public float getAlpha() { + return mAlphas[mCurrentAlphaIndex]; + } + + @Override + public void setAlpha(float alpha) { + mAlphas[mCurrentAlphaIndex] = alpha; + } + + @Override + public void multiplyAlpha(float alpha) { + setAlpha(getAlpha() * alpha); + } + + @Override + public void translate(float x, float y, float z) { + Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z); + } + + // This is a faster version of translate(x, y, z) because + // (1) we knows z = 0, (2) we inline the Matrix.translateM call, + // (3) we unroll the loop + @Override + public void translate(float x, float y) { + int index = mCurrentMatrixIndex; + float[] m = mMatrices; + m[index + 12] += m[index + 0] * x + m[index + 4] * y; + m[index + 13] += m[index + 1] * x + m[index + 5] * y; + m[index + 14] += m[index + 2] * x + m[index + 6] * y; + m[index + 15] += m[index + 3] * x + m[index + 7] * y; + } + + @Override + public void scale(float sx, float sy, float sz) { + Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz); + } + + @Override + public void rotate(float angle, float x, float y, float z) { + if (angle == 0f) { + return; + } + float[] temp = mTempMatrix; + Matrix.setRotateM(temp, 0, angle, x, y, z); + float[] matrix = mMatrices; + int index = mCurrentMatrixIndex; + Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0); + System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE); + } + + @Override + public void multiplyMatrix(float[] matrix, int offset) { + float[] temp = mTempMatrix; + float[] currentMatrix = mMatrices; + int index = mCurrentMatrixIndex; + Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset); + System.arraycopy(temp, 0, currentMatrix, index, 16); + } + + @Override + public void save() { + save(SAVE_FLAG_ALL); + } + + @Override + public void save(int saveFlags) { + boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA; + if (saveAlpha) { + float currentAlpha = getAlpha(); + mCurrentAlphaIndex++; + if (mAlphas.length <= mCurrentAlphaIndex) { + mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2); + } + mAlphas[mCurrentAlphaIndex] = currentAlpha; + } + boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX; + if (saveMatrix) { + int currentIndex = mCurrentMatrixIndex; + mCurrentMatrixIndex += MATRIX_SIZE; + if (mMatrices.length <= mCurrentMatrixIndex) { + mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2); + } + System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE); + } + boolean saveBlending = (saveFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND; + if (saveBlending) { + mBlendings.add(mBlendings.get(mBlendings.size() - 1)); + } + mSaveFlags.add(saveFlags); + } + + @Override + public void restore() { + int restoreFlags = mSaveFlags.removeLast(); + boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA; + if (restoreAlpha) { + mCurrentAlphaIndex--; + } + boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX; + if (restoreMatrix) { + mCurrentMatrixIndex -= MATRIX_SIZE; + } + boolean restoreBlending = (restoreFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND; + if (restoreBlending) { + setBlending(mBlendings.get(mBlendings.size() - 2)); + mBlendings.remove(mBlendings.size() - 1); + } + } + + @Override + public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) { + draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1, + paint); + mCountDrawLine++; + } + + @Override + public void drawRect(float x, float y, float width, float height, GLPaint paint) { + draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint); + mCountDrawLine++; + } + + private void draw(int type, int offset, int count, float x, float y, float width, float height, + GLPaint paint) { + draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth()); + } + + private void draw(int type, int offset, int count, float x, float y, float width, float height, + int color, float lineWidth) { + prepareDraw(offset, color, lineWidth); + draw(mDrawParameters, type, count, x, y, width, height); + } + + private void prepareDraw(int offset, int color, float lineWidth) { + GLES20.glUseProgram(mDrawProgram); + checkError(); + if (lineWidth > 0) { + GLES20.glLineWidth(lineWidth); + checkError(); + } + float[] colorArray = getColor(color); + boolean blendingEnabled = (colorArray[3] < 1f); + enableBlending(blendingEnabled); + if (blendingEnabled) { + GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]); + checkError(); + } + + GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0); + setPosition(mDrawParameters, offset); + checkError(); + } + + private float[] getColor(int color) { + float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha(); + float red = ((color >>> 16) & 0xFF) / 255f * alpha; + float green = ((color >>> 8) & 0xFF) / 255f * alpha; + float blue = (color & 0xFF) / 255f * alpha; + mTempColor[0] = red; + mTempColor[1] = green; + mTempColor[2] = blue; + mTempColor[3] = alpha; + return mTempColor; + } + + private void enableBlending(boolean enableBlending) { + if (enableBlending) { + GLES20.glEnable(GLES20.GL_BLEND); + checkError(); + } else { + GLES20.glDisable(GLES20.GL_BLEND); + checkError(); + } + } + + private void setPosition(ShaderParameter[] params, int offset) { + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates); + checkError(); + GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX, + GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE); + checkError(); + } + + private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width, + float height) { + setMatrix(params, x, y, width, height); + int positionHandle = params[INDEX_POSITION].handle; + GLES20.glEnableVertexAttribArray(positionHandle); + checkError(); + GLES20.glDrawArrays(type, 0, count); + checkError(); + GLES20.glDisableVertexAttribArray(positionHandle); + checkError(); + } + + private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) { + Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f); + Matrix.scaleM(mTempMatrix, 0, width, height, 1f); + Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0); + GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE); + checkError(); + } + + @Override + public void fillRect(float x, float y, float width, float height, int color) { + draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height, + color, 0f); + mCountFillRect++; + } + + @Override + public void drawTexture(BasicTexture texture, int x, int y, int width, int height) { + if (width <= 0 || height <= 0) { + return; + } + copyTextureCoordinates(texture, mTempSourceRect); + mTempTargetRect.set(x, y, x + width, y + height); + convertCoordinate(mTempSourceRect, mTempTargetRect, texture); + drawTextureRect(texture, mTempSourceRect, mTempTargetRect); + } + + private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) { + int left = 0; + int top = 0; + int right = texture.getWidth(); + int bottom = texture.getHeight(); + if (texture.hasBorder()) { + left = 1; + top = 1; + right -= 1; + bottom -= 1; + } + outRect.set(left, top, right, bottom); + } + + @Override + public void drawTexture(BasicTexture texture, RectF source, RectF target) { + if (target.width() <= 0 || target.height() <= 0) { + return; + } + mTempSourceRect.set(source); + mTempTargetRect.set(target); + + convertCoordinate(mTempSourceRect, mTempTargetRect, texture); + drawTextureRect(texture, mTempSourceRect, mTempTargetRect); + } + + @Override + public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w, + int h) { + if (w <= 0 || h <= 0) { + return; + } + mTempTargetRect.set(x, y, x + w, y + h); + drawTextureRect(texture, textureTransform, mTempTargetRect); + } + + private void drawTextureRect(BasicTexture texture, RectF source, RectF target) { + setTextureMatrix(source); + drawTextureRect(texture, mTempTextureMatrix, target); + } + + private void setTextureMatrix(RectF source) { + mTempTextureMatrix[0] = source.width(); + mTempTextureMatrix[5] = source.height(); + mTempTextureMatrix[12] = source.left; + mTempTextureMatrix[13] = source.top; + } + + // This function changes the source coordinate to the texture coordinates. + // It also clips the source and target coordinates if it is beyond the + // bound of the texture. + private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) { + int width = texture.getWidth(); + int height = texture.getHeight(); + int texWidth = texture.getTextureWidth(); + int texHeight = texture.getTextureHeight(); + // Convert to texture coordinates + source.left /= texWidth; + source.right /= texWidth; + source.top /= texHeight; + source.bottom /= texHeight; + + // Clip if the rendering range is beyond the bound of the texture. + float xBound = (float) width / texWidth; + if (source.right > xBound) { + target.right = target.left + target.width() * (xBound - source.left) / source.width(); + source.right = xBound; + } + float yBound = (float) height / texHeight; + if (source.bottom > yBound) { + target.bottom = target.top + target.height() * (yBound - source.top) / source.height(); + source.bottom = yBound; + } + } + + private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) { + ShaderParameter[] params = prepareTexture(texture); + setPosition(params, OFFSET_FILL_RECT); + GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0); + checkError(); + draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top, + target.width(), target.height()); + mCountTextureRect++; + } + + private ShaderParameter[] prepareTexture(BasicTexture texture) { + ShaderParameter[] params; + int program; + if (texture.getTarget() == GLES20.GL_TEXTURE_2D) { + params = mTextureParameters; + program = mTextureProgram; + } else { + params = mOesTextureParameters; + program = mOesTextureProgram; + } + prepareTexture(texture, program, params); + return params; + } + + private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) { + GLES20.glUseProgram(program); + checkError(); + enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + checkError(); + texture.onBind(this); + GLES20.glBindTexture(texture.getTarget(), texture.getId()); + checkError(); + GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0); + checkError(); + GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha()); + checkError(); + } + + @Override + public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer, + int indexBuffer, int indexCount) { + prepareTexture(texture, mMeshProgram, mMeshParameters); + + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer); + checkError(); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer); + checkError(); + int positionHandle = mMeshParameters[INDEX_POSITION].handle; + GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, + VERTEX_STRIDE, 0); + checkError(); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer); + checkError(); + int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle; + GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, + false, VERTEX_STRIDE, 0); + checkError(); + + GLES20.glEnableVertexAttribArray(positionHandle); + checkError(); + GLES20.glEnableVertexAttribArray(texCoordHandle); + checkError(); + + setMatrix(mMeshParameters, x, y, 1, 1); + GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_BYTE, 0); + checkError(); + + GLES20.glDisableVertexAttribArray(positionHandle); + checkError(); + GLES20.glDisableVertexAttribArray(texCoordHandle); + checkError(); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); + checkError(); + mCountDrawMesh++; + } + + @Override + public void drawMixed(BasicTexture texture, int toColor, float ratio, int x, int y, int w, int h) { + copyTextureCoordinates(texture, mTempSourceRect); + mTempTargetRect.set(x, y, x + w, y + h); + drawMixed(texture, toColor, ratio, mTempSourceRect, mTempTargetRect); + } + + @Override + public void drawMixed(BasicTexture texture, int toColor, float ratio, RectF source, RectF target) { + if (target.width() <= 0 || target.height() <= 0) { + return; + } + save(SAVE_FLAG_ALPHA); + + float currentAlpha = getAlpha(); + float cappedRatio = Math.min(1f, Math.max(0f, ratio)); + + float textureAlpha = (1f - cappedRatio) * currentAlpha; + setAlpha(textureAlpha); + drawTexture(texture, source, target); + + float colorAlpha = cappedRatio * currentAlpha; + setAlpha(colorAlpha); + fillRect(target.left, target.top, target.width(), target.height(), toColor); + + restore(); + } + + @Override + public boolean unloadTexture(BasicTexture texture) { + boolean unload = texture.isLoaded(); + if (unload) { + synchronized (mUnboundTextures) { + mUnboundTextures.add(texture.getId()); + } + } + return unload; + } + + @Override + public void deleteBuffer(int bufferId) { + synchronized (mUnboundTextures) { + mDeleteBuffers.add(bufferId); + } + } + + @Override + public void deleteRecycledResources() { + synchronized (mUnboundTextures) { + IntArray ids = mUnboundTextures; + if (mUnboundTextures.size() > 0) { + glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0); + ids.clear(); + } + + ids = mDeleteBuffers; + if (ids.size() > 0) { + glDeleteBuffers(null, ids.size(), ids.getInternalArray(), 0); + ids.clear(); + } + } + } + + @Override + public void dumpStatisticsAndClear() { + String line = String.format("MESH:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", mCountDrawMesh, + mCountTextureRect, mCountFillRect, mCountDrawLine); + mCountDrawMesh = 0; + mCountTextureRect = 0; + mCountFillRect = 0; + mCountDrawLine = 0; + Log.d(TAG, line); + } + + @Override + public void endRenderTarget() { + RawTexture oldTexture = mTargetTextures.remove(mTargetTextures.size() - 1); + RawTexture texture = getTargetTexture(); + setRenderTarget(oldTexture, texture); + restore(); // restore matrix and alpha + } + + @Override + public void beginRenderTarget(RawTexture texture) { + save(); // save matrix and alpha and blending + RawTexture oldTexture = getTargetTexture(); + mTargetTextures.add(texture); + setRenderTarget(oldTexture, texture); + } + + private RawTexture getTargetTexture() { + return mTargetTextures.get(mTargetTextures.size() - 1); + } + + private void setRenderTarget(BasicTexture oldTexture, RawTexture texture) { + if (oldTexture == null && texture != null) { + GLES20.glGenFramebuffers(1, mFrameBuffer, 0); + checkError(); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer[0]); + checkError(); + } else if (oldTexture != null && texture == null) { + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + checkError(); + GLES20.glDeleteFramebuffers(1, mFrameBuffer, 0); + checkError(); + } + + if (texture == null) { + setSize(mScreenWidth, mScreenHeight); + } else { + setSize(texture.getWidth(), texture.getHeight()); + + if (!texture.isLoaded()) { + texture.prepare(this); + } + + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, + texture.getTarget(), texture.getId(), 0); + checkError(); + + checkFramebufferStatus(); + } + } + + private static void checkFramebufferStatus() { + int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); + if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) { + String msg = ""; + switch (status) { + case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + msg = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; + break; + case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + msg = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS"; + break; + case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + msg = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + break; + case GLES20.GL_FRAMEBUFFER_UNSUPPORTED: + msg = "GL_FRAMEBUFFER_UNSUPPORTED"; + break; + } + throw new RuntimeException(msg + ":" + Integer.toHexString(status)); + } + } + + @Override + public void setTextureParameters(BasicTexture texture) { + int target = texture.getTarget(); + GLES20.glBindTexture(target, texture.getId()); + checkError(); + GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + } + + @Override + public void initializeTextureSize(BasicTexture texture, int format, int type) { + int target = texture.getTarget(); + GLES20.glBindTexture(target, texture.getId()); + checkError(); + int width = texture.getTextureWidth(); + int height = texture.getTextureHeight(); + GLES20.glTexImage2D(target, 0, format, width, height, 0, format, type, null); + } + + @Override + public void initializeTexture(BasicTexture texture, Bitmap bitmap) { + int target = texture.getTarget(); + GLES20.glBindTexture(target, texture.getId()); + checkError(); + GLUtils.texImage2D(target, 0, bitmap, 0); + } + + @Override + public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap, + int format, int type) { + int target = texture.getTarget(); + GLES20.glBindTexture(target, texture.getId()); + checkError(); + GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type); + } + + @Override + public int uploadBuffer(FloatBuffer buf) { + return uploadBuffer(buf, FLOAT_SIZE); + } + + @Override + public int uploadBuffer(ByteBuffer buf) { + return uploadBuffer(buf, 1); + } + + private int uploadBuffer(Buffer buffer, int elementSize) { + glGenBuffers(1, mTempIntArray, 0); + checkError(); + int bufferId = mTempIntArray[0]; + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId); + checkError(); + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer, + GLES20.GL_STATIC_DRAW); + checkError(); + return bufferId; + } + + @Override + public void setBlending(Blending blending) { + Blending currentBlending = mBlendings.get(mBlendings.size() - 1); + if (currentBlending == blending) { + return; // nothing to change + } + mBlendings.set(mBlendings.size() - 1, blending); + int srcFunc = GLES20.GL_ONE; + int dstFunc; + switch (blending) { + case Additive: + dstFunc = GLES20.GL_ONE; + break; + case Mix: + dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA; + break; + default: + Utils.fail("Unknown blend: " + blending); + dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA; + break; + } + GLES20.glBlendFunc(srcFunc, dstFunc); + checkError(); + } + + @Override + public int generateTexture() { + // Can use anything as a lock. No need to create a new object. + synchronized (mTempIntArray) { + return mNextTextureId++; + } + } + + @Override + public void glGenBuffers(int n, int[] buffers, int offset) { + GLES20.glGenBuffers(n, buffers, offset); + checkError(); + } + + @Override + public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) { + GLES20.glDeleteTextures(n, textures, offset); + checkError(); + } + + @Override + public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) { + GLES20.glDeleteBuffers(n, buffers, offset); + checkError(); + } + + @Override + public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) { + GLES20.glDeleteFramebuffers(n, buffers, offset); + checkError(); + } + + @Override + public void enableStencil() { + GLES20.glEnable(GLES20.GL_STENCIL_TEST); + } + + @Override + public void disableStencil() { + GLES20.glDisable(GLES20.GL_STENCIL_TEST); + } + + @Override + public void clearStencilBuffer() { + GLES20.glClear(GLES20.GL_STENCIL_BUFFER_BIT); + } + + @Override + public void updateStencil(boolean update) { + int passOp = update ? GLES20.GL_REPLACE : GLES20.GL_KEEP; + GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, passOp); + } + + @Override + public void drawOnlyOutsideStencil(boolean onlyOutside) { + int func = onlyOutside ? GLES20.GL_NOTEQUAL : GLES20.GL_ALWAYS; + GLES20.glStencilFunc(func, 1, 1); + } + + private static void checkError() { + int error = GLES20.glGetError(); + if (error != 0) { + Throwable t = new Throwable(); + Log.e(TAG, "GL error: " + error, t); + } + } + + @SuppressWarnings("unused") + private static void printMatrix(String message, float[] m, int offset) { + StringBuilder b = new StringBuilder(message); + for (int i = 0; i < MATRIX_SIZE; i++) { + b.append(' '); + if (i % 4 == 0) { + b.append('\n'); + } + b.append(m[offset + i]); + } + Log.v(TAG, b.toString()); + } + +} diff --git a/src/com/android/gallery3d/glrenderer/GLId.java b/src/com/android/gallery3d/glrenderer/GLId.java new file mode 100644 index 000000000..3cec558f6 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/GLId.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 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 javax.microedition.khronos.opengles.GL11; +import javax.microedition.khronos.opengles.GL11ExtensionPack; + +// This mimics corresponding GL functions. +public interface GLId { + public int generateTexture(); + + public void glGenBuffers(int n, int[] buffers, int offset); + + public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset); + + public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset); + + public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset); +} diff --git a/src/com/android/gallery3d/glrenderer/GLIdImpl.java b/src/com/android/gallery3d/glrenderer/GLIdImpl.java new file mode 100644 index 000000000..92aa58ee4 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/GLIdImpl.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012 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 javax.microedition.khronos.opengles.GL11; +import javax.microedition.khronos.opengles.GL11ExtensionPack; + +/** + * Open GL ES 1.1 implementation for generating and destroying texture IDs and + * buffer IDs + */ +public class GLIdImpl implements GLId { + private static int sNextId = 1; + // Mutex for sNextId + private static Object sLock = new Object(); + + @Override + public int generateTexture() { + synchronized (sLock) { + return sNextId++; + } + } + + @Override + public void glGenBuffers(int n, int[] buffers, int offset) { + synchronized (sLock) { + while (n-- > 0) { + buffers[offset + n] = sNextId++; + } + } + } + + @Override + public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) { + synchronized (sLock) { + gl.glDeleteTextures(n, textures, offset); + } + } + + @Override + public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) { + synchronized (sLock) { + gl.glDeleteBuffers(n, buffers, offset); + } + } + + @Override + public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) { + synchronized (sLock) { + gl11ep.glDeleteFramebuffersOES(n, buffers, offset); + } + } + + +} diff --git a/src/com/android/gallery3d/glrenderer/GLPaint.java b/src/com/android/gallery3d/glrenderer/GLPaint.java new file mode 100644 index 000000000..16b220690 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/GLPaint.java @@ -0,0 +1,41 @@ +/* + * 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 junit.framework.Assert; + +public class GLPaint { + private float mLineWidth = 1f; + private int mColor = 0; + + public void setColor(int color) { + mColor = color; + } + + public int getColor() { + return mColor; + } + + public void setLineWidth(float width) { + Assert.assertTrue(width >= 0); + mLineWidth = width; + } + + public float getLineWidth() { + return mLineWidth; + } +} diff --git a/src/com/android/gallery3d/glrenderer/MultiLineTexture.java b/src/com/android/gallery3d/glrenderer/MultiLineTexture.java new file mode 100644 index 000000000..82839f107 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/MultiLineTexture.java @@ -0,0 +1,52 @@ +/* + * 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.Canvas; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; + + +// MultiLineTexture is a texture shows the content of a specified String. +// +// To create a MultiLineTexture, use the newInstance() method and specify +// the String, the font size, and the color. +class MultiLineTexture extends CanvasTexture { + private final Layout mLayout; + + private MultiLineTexture(Layout layout) { + super(layout.getWidth(), layout.getHeight()); + mLayout = layout; + } + + public static MultiLineTexture newInstance( + String text, int maxWidth, float textSize, int color, + Layout.Alignment alignment) { + TextPaint paint = StringTexture.getDefaultPaint(textSize, color); + Layout layout = new StaticLayout(text, 0, text.length(), paint, + maxWidth, alignment, 1, 0, true, null, 0); + + return new MultiLineTexture(layout); + } + + @Override + protected void onDraw(Canvas canvas, Bitmap backing) { + mLayout.draw(canvas); + } +} diff --git a/src/com/android/gallery3d/glrenderer/NinePatchChunk.java b/src/com/android/gallery3d/glrenderer/NinePatchChunk.java new file mode 100644 index 000000000..9dc326622 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/NinePatchChunk.java @@ -0,0 +1,82 @@ +/* + * 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.Rect; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +// See "frameworks/base/include/utils/ResourceTypes.h" for the format of +// NinePatch chunk. +class NinePatchChunk { + + public static final int NO_COLOR = 0x00000001; + public static final int TRANSPARENT_COLOR = 0x00000000; + + public Rect mPaddings = new Rect(); + + public int mDivX[]; + public int mDivY[]; + public int mColor[]; + + private static void readIntArray(int[] data, ByteBuffer buffer) { + for (int i = 0, n = data.length; i < n; ++i) { + data[i] = buffer.getInt(); + } + } + + private static void checkDivCount(int length) { + if (length == 0 || (length & 0x01) != 0) { + throw new RuntimeException("invalid nine-patch: " + length); + } + } + + public static NinePatchChunk deserialize(byte[] data) { + ByteBuffer byteBuffer = + ByteBuffer.wrap(data).order(ByteOrder.nativeOrder()); + + byte wasSerialized = byteBuffer.get(); + if (wasSerialized == 0) return null; + + NinePatchChunk chunk = new NinePatchChunk(); + chunk.mDivX = new int[byteBuffer.get()]; + chunk.mDivY = new int[byteBuffer.get()]; + chunk.mColor = new int[byteBuffer.get()]; + + checkDivCount(chunk.mDivX.length); + checkDivCount(chunk.mDivY.length); + + // skip 8 bytes + byteBuffer.getInt(); + byteBuffer.getInt(); + + chunk.mPaddings.left = byteBuffer.getInt(); + chunk.mPaddings.right = byteBuffer.getInt(); + chunk.mPaddings.top = byteBuffer.getInt(); + chunk.mPaddings.bottom = byteBuffer.getInt(); + + // skip 4 bytes + byteBuffer.getInt(); + + readIntArray(chunk.mDivX, byteBuffer); + readIntArray(chunk.mDivY, byteBuffer); + readIntArray(chunk.mColor, byteBuffer); + + return chunk; + } +} \ No newline at end of file diff --git a/src/com/android/gallery3d/glrenderer/NinePatchTexture.java b/src/com/android/gallery3d/glrenderer/NinePatchTexture.java new file mode 100644 index 000000000..d0ddc46c3 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/NinePatchTexture.java @@ -0,0 +1,424 @@ +/* + * 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.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Rect; + +import com.android.gallery3d.common.Utils; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +// NinePatchTexture is a texture backed by a NinePatch resource. +// +// getPaddings() returns paddings specified in the NinePatch. +// getNinePatchChunk() returns the layout data specified in the NinePatch. +// +public class NinePatchTexture extends ResourceTexture { + @SuppressWarnings("unused") + private static final String TAG = "NinePatchTexture"; + private NinePatchChunk mChunk; + private SmallCache mInstanceCache + = new SmallCache(); + + public NinePatchTexture(Context context, int resId) { + super(context, resId); + } + + @Override + protected Bitmap onGetBitmap() { + if (mBitmap != null) return mBitmap; + + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + Bitmap bitmap = BitmapFactory.decodeResource( + mContext.getResources(), mResId, options); + mBitmap = bitmap; + setSize(bitmap.getWidth(), bitmap.getHeight()); + byte[] chunkData = bitmap.getNinePatchChunk(); + mChunk = chunkData == null + ? null + : NinePatchChunk.deserialize(bitmap.getNinePatchChunk()); + if (mChunk == null) { + throw new RuntimeException("invalid nine-patch image: " + mResId); + } + return bitmap; + } + + public Rect getPaddings() { + // get the paddings from nine patch + if (mChunk == null) onGetBitmap(); + return mChunk.mPaddings; + } + + public NinePatchChunk getNinePatchChunk() { + if (mChunk == null) onGetBitmap(); + return mChunk; + } + + // This is a simple cache for a small number of things. Linear search + // is used because the cache is small. It also tries to remove less used + // item when the cache is full by moving the often-used items to the front. + private static class SmallCache { + private static final int CACHE_SIZE = 16; + private static final int CACHE_SIZE_START_MOVE = CACHE_SIZE / 2; + private int[] mKey = new int[CACHE_SIZE]; + private V[] mValue = (V[]) new Object[CACHE_SIZE]; + private int mCount; // number of items in this cache + + // Puts a value into the cache. If the cache is full, also returns + // a less used item, otherwise returns null. + public V put(int key, V value) { + if (mCount == CACHE_SIZE) { + V old = mValue[CACHE_SIZE - 1]; // remove the last item + mKey[CACHE_SIZE - 1] = key; + mValue[CACHE_SIZE - 1] = value; + return old; + } else { + mKey[mCount] = key; + mValue[mCount] = value; + mCount++; + return null; + } + } + + public V get(int key) { + for (int i = 0; i < mCount; i++) { + if (mKey[i] == key) { + // Move the accessed item one position to the front, so it + // will less likely to be removed when cache is full. Only + // do this if the cache is starting to get full. + if (mCount > CACHE_SIZE_START_MOVE && i > 0) { + int tmpKey = mKey[i]; + mKey[i] = mKey[i - 1]; + mKey[i - 1] = tmpKey; + + V tmpValue = mValue[i]; + mValue[i] = mValue[i - 1]; + mValue[i - 1] = tmpValue; + } + return mValue[i]; + } + } + return null; + } + + public void clear() { + for (int i = 0; i < mCount; i++) { + mValue[i] = null; // make sure it's can be garbage-collected. + } + mCount = 0; + } + + public int size() { + return mCount; + } + + public V valueAt(int i) { + return mValue[i]; + } + } + + private NinePatchInstance findInstance(GLCanvas canvas, int w, int h) { + int key = w; + key = (key << 16) | h; + NinePatchInstance instance = mInstanceCache.get(key); + + if (instance == null) { + instance = new NinePatchInstance(this, w, h); + NinePatchInstance removed = mInstanceCache.put(key, instance); + if (removed != null) { + removed.recycle(canvas); + } + } + + return instance; + } + + @Override + public void draw(GLCanvas canvas, int x, int y, int w, int h) { + if (!isLoaded()) { + mInstanceCache.clear(); + } + + if (w != 0 && h != 0) { + findInstance(canvas, w, h).draw(canvas, this, x, y); + } + } + + @Override + public void recycle() { + super.recycle(); + GLCanvas canvas = mCanvasRef; + if (canvas == null) return; + int n = mInstanceCache.size(); + for (int i = 0; i < n; i++) { + NinePatchInstance instance = mInstanceCache.valueAt(i); + instance.recycle(canvas); + } + mInstanceCache.clear(); + } +} + +// This keeps data for a specialization of NinePatchTexture with the size +// (width, height). We pre-compute the coordinates for efficiency. +class NinePatchInstance { + + @SuppressWarnings("unused") + private static final String TAG = "NinePatchInstance"; + + // We need 16 vertices for a normal nine-patch image (the 4x4 vertices) + private static final int VERTEX_BUFFER_SIZE = 16 * 2; + + // We need 22 indices for a normal nine-patch image, plus 2 for each + // transparent region. Current there are at most 1 transparent region. + private static final int INDEX_BUFFER_SIZE = 22 + 2; + + private FloatBuffer mXyBuffer; + private FloatBuffer mUvBuffer; + private ByteBuffer mIndexBuffer; + + // Names for buffer names: xy, uv, index. + private int mXyBufferName = -1; + private int mUvBufferName; + private int mIndexBufferName; + + private int mIdxCount; + + public NinePatchInstance(NinePatchTexture tex, int width, int height) { + NinePatchChunk chunk = tex.getNinePatchChunk(); + + if (width <= 0 || height <= 0) { + throw new RuntimeException("invalid dimension"); + } + + // The code should be easily extended to handle the general cases by + // allocating more space for buffers. But let's just handle the only + // use case. + if (chunk.mDivX.length != 2 || chunk.mDivY.length != 2) { + throw new RuntimeException("unsupported nine patch"); + } + + float divX[] = new float[4]; + float divY[] = new float[4]; + float divU[] = new float[4]; + float divV[] = new float[4]; + + int nx = stretch(divX, divU, chunk.mDivX, tex.getWidth(), width); + int ny = stretch(divY, divV, chunk.mDivY, tex.getHeight(), height); + + prepareVertexData(divX, divY, divU, divV, nx, ny, chunk.mColor); + } + + /** + * Stretches the texture according to the nine-patch rules. It will + * linearly distribute the strechy parts defined in the nine-patch chunk to + * the target area. + * + *
+     *                      source
+     *          /--------------^---------------\
+     *         u0    u1       u2  u3     u4   u5
+     * div ---> |fffff|ssssssss|fff|ssssss|ffff| ---> u
+     *          |    div0    div1 div2   div3  |
+     *          |     |       /   /      /    /
+     *          |     |      /   /     /    /
+     *          |     |     /   /    /    /
+     *          |fffff|ssss|fff|sss|ffff| ---> x
+     *         x0    x1   x2  x3  x4   x5
+     *          \----------v------------/
+     *                  target
+     *
+     * f: fixed segment
+     * s: stretchy segment
+     * 
+ * + * @param div the stretch parts defined in nine-patch chunk + * @param source the length of the texture + * @param target the length on the drawing plan + * @param u output, the positions of these dividers in the texture + * coordinate + * @param x output, the corresponding position of these dividers on the + * drawing plan + * @return the number of these dividers. + */ + private static int stretch( + float x[], float u[], int div[], int source, int target) { + int textureSize = Utils.nextPowerOf2(source); + float textureBound = (float) source / textureSize; + + float stretch = 0; + for (int i = 0, n = div.length; i < n; i += 2) { + stretch += div[i + 1] - div[i]; + } + + float remaining = target - source + stretch; + + float lastX = 0; + float lastU = 0; + + x[0] = 0; + u[0] = 0; + for (int i = 0, n = div.length; i < n; i += 2) { + // Make the stretchy segment a little smaller to prevent sampling + // on neighboring fixed segments. + // fixed segment + x[i + 1] = lastX + (div[i] - lastU) + 0.5f; + u[i + 1] = Math.min((div[i] + 0.5f) / textureSize, textureBound); + + // stretchy segment + float partU = div[i + 1] - div[i]; + float partX = remaining * partU / stretch; + remaining -= partX; + stretch -= partU; + + lastX = x[i + 1] + partX; + lastU = div[i + 1]; + x[i + 2] = lastX - 0.5f; + u[i + 2] = Math.min((lastU - 0.5f)/ textureSize, textureBound); + } + // the last fixed segment + x[div.length + 1] = target; + u[div.length + 1] = textureBound; + + // remove segments with length 0. + int last = 0; + for (int i = 1, n = div.length + 2; i < n; ++i) { + if ((x[i] - x[last]) < 1f) continue; + x[++last] = x[i]; + u[last] = u[i]; + } + return last + 1; + } + + private void prepareVertexData(float x[], float y[], float u[], float v[], + int nx, int ny, int[] color) { + /* + * Given a 3x3 nine-patch image, the vertex order is defined as the + * following graph: + * + * (0) (1) (2) (3) + * | /| /| /| + * | / | / | / | + * (4) (5) (6) (7) + * | \ | \ | \ | + * | \| \| \| + * (8) (9) (A) (B) + * | /| /| /| + * | / | / | / | + * (C) (D) (E) (F) + * + * And we draw the triangle strip in the following index order: + * + * index: 04152637B6A5948C9DAEBF + */ + int pntCount = 0; + float xy[] = new float[VERTEX_BUFFER_SIZE]; + float uv[] = new float[VERTEX_BUFFER_SIZE]; + for (int j = 0; j < ny; ++j) { + for (int i = 0; i < nx; ++i) { + int xIndex = (pntCount++) << 1; + int yIndex = xIndex + 1; + xy[xIndex] = x[i]; + xy[yIndex] = y[j]; + uv[xIndex] = u[i]; + uv[yIndex] = v[j]; + } + } + + int idxCount = 1; + boolean isForward = false; + byte index[] = new byte[INDEX_BUFFER_SIZE]; + for (int row = 0; row < ny - 1; row++) { + --idxCount; + isForward = !isForward; + + int start, end, inc; + if (isForward) { + start = 0; + end = nx; + inc = 1; + } else { + start = nx - 1; + end = -1; + inc = -1; + } + + for (int col = start; col != end; col += inc) { + int k = row * nx + col; + if (col != start) { + int colorIdx = row * (nx - 1) + col; + if (isForward) colorIdx--; + if (color[colorIdx] == NinePatchChunk.TRANSPARENT_COLOR) { + index[idxCount] = index[idxCount - 1]; + ++idxCount; + index[idxCount++] = (byte) k; + } + } + + index[idxCount++] = (byte) k; + index[idxCount++] = (byte) (k + nx); + } + } + + mIdxCount = idxCount; + + int size = (pntCount * 2) * (Float.SIZE / Byte.SIZE); + mXyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer(); + mUvBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer(); + mIndexBuffer = allocateDirectNativeOrderBuffer(mIdxCount); + + mXyBuffer.put(xy, 0, pntCount * 2).position(0); + mUvBuffer.put(uv, 0, pntCount * 2).position(0); + mIndexBuffer.put(index, 0, idxCount).position(0); + } + + private static ByteBuffer allocateDirectNativeOrderBuffer(int size) { + return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); + } + + private void prepareBuffers(GLCanvas canvas) { + mXyBufferName = canvas.uploadBuffer(mXyBuffer); + mUvBufferName = canvas.uploadBuffer(mUvBuffer); + mIndexBufferName = canvas.uploadBuffer(mIndexBuffer); + + // These buffers are never used again. + mXyBuffer = null; + mUvBuffer = null; + mIndexBuffer = null; + } + + public void draw(GLCanvas canvas, NinePatchTexture tex, int x, int y) { + if (mXyBufferName == -1) { + prepareBuffers(canvas); + } + canvas.drawMesh(tex, x, y, mXyBufferName, mUvBufferName, mIndexBufferName, mIdxCount); + } + + public void recycle(GLCanvas canvas) { + if (mXyBuffer == null) { + canvas.deleteBuffer(mXyBufferName); + canvas.deleteBuffer(mUvBufferName); + canvas.deleteBuffer(mIndexBufferName); + mXyBufferName = -1; + } + } +} diff --git a/src/com/android/gallery3d/glrenderer/RawTexture.java b/src/com/android/gallery3d/glrenderer/RawTexture.java new file mode 100644 index 000000000..73f2c499a --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/RawTexture.java @@ -0,0 +1,63 @@ +/* + * 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.util.Log; + +import javax.microedition.khronos.opengles.GL11; + +public class RawTexture extends BasicTexture { + private static final String TAG = "RawTexture"; + + private final boolean mOpaque; + + public RawTexture(int width, int height, boolean opaque) { + mOpaque = opaque; + setSize(width, height); + } + + @Override + public boolean isOpaque() { + return mOpaque; + } + + protected void prepare(GLCanvas canvas) { + GLId glId = GLCanvas.getGLId(); + mId = glId.generateTexture(); + canvas.initializeTextureSize(this, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE); + canvas.setTextureParameters(this); + mState = STATE_LOADED; + setAssociatedCanvas(canvas); + } + + @Override + protected boolean onBind(GLCanvas canvas) { + if (isLoaded()) return true; + Log.w(TAG, "lost the content due to context change"); + return false; + } + + @Override + public void yield() { + // we cannot free the texture because we have no backup. + } + + @Override + protected int getTarget() { + return GL11.GL_TEXTURE_2D; + } +} diff --git a/src/com/android/gallery3d/glrenderer/ResourceTexture.java b/src/com/android/gallery3d/glrenderer/ResourceTexture.java new file mode 100644 index 000000000..eb8e8a517 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/ResourceTexture.java @@ -0,0 +1,53 @@ +/* + * 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.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import junit.framework.Assert; + +// ResourceTexture is a texture whose Bitmap is decoded from a resource. +// By default ResourceTexture is not opaque. +public class ResourceTexture extends UploadedTexture { + + protected final Context mContext; + protected final int mResId; + + public ResourceTexture(Context context, int resId) { + Assert.assertNotNull(context); + mContext = context; + mResId = resId; + setOpaque(false); + } + + @Override + protected Bitmap onGetBitmap() { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + return BitmapFactory.decodeResource( + mContext.getResources(), mResId, options); + } + + @Override + protected void onFreeBitmap(Bitmap bitmap) { + if (!inFinalizer()) { + bitmap.recycle(); + } + } +} diff --git a/src/com/android/gallery3d/glrenderer/StringTexture.java b/src/com/android/gallery3d/glrenderer/StringTexture.java new file mode 100644 index 000000000..56ca29753 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/StringTexture.java @@ -0,0 +1,88 @@ +/* + * 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.Canvas; +import android.graphics.Color; +import android.graphics.Paint.FontMetricsInt; +import android.graphics.Typeface; +import android.text.TextPaint; +import android.text.TextUtils; +import android.util.FloatMath; + +// StringTexture is a texture shows the content of a specified String. +// +// To create a StringTexture, use the newInstance() method and specify +// the String, the font size, and the color. +public class StringTexture extends CanvasTexture { + private final String mText; + private final TextPaint mPaint; + private final FontMetricsInt mMetrics; + + private StringTexture(String text, TextPaint paint, + FontMetricsInt metrics, int width, int height) { + super(width, height); + mText = text; + mPaint = paint; + mMetrics = metrics; + } + + public static TextPaint getDefaultPaint(float textSize, int color) { + TextPaint paint = new TextPaint(); + paint.setTextSize(textSize); + paint.setAntiAlias(true); + paint.setColor(color); + paint.setShadowLayer(2f, 0f, 0f, Color.BLACK); + return paint; + } + + public static StringTexture newInstance( + String text, float textSize, int color) { + return newInstance(text, getDefaultPaint(textSize, color)); + } + + public static StringTexture newInstance( + String text, float textSize, int color, + float lengthLimit, boolean isBold) { + TextPaint paint = getDefaultPaint(textSize, color); + if (isBold) { + paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); + } + if (lengthLimit > 0) { + text = TextUtils.ellipsize( + text, paint, lengthLimit, TextUtils.TruncateAt.END).toString(); + } + return newInstance(text, paint); + } + + private static StringTexture newInstance(String text, TextPaint paint) { + FontMetricsInt metrics = paint.getFontMetricsInt(); + int width = (int) FloatMath.ceil(paint.measureText(text)); + int height = metrics.bottom - metrics.top; + // The texture size needs to be at least 1x1. + if (width <= 0) width = 1; + if (height <= 0) height = 1; + return new StringTexture(text, paint, metrics, width, height); + } + + @Override + protected void onDraw(Canvas canvas, Bitmap backing) { + canvas.translate(0, -mMetrics.ascent); + canvas.drawText(mText, 0, 0, mPaint); + } +} diff --git a/src/com/android/gallery3d/glrenderer/Texture.java b/src/com/android/gallery3d/glrenderer/Texture.java new file mode 100644 index 000000000..3dcae4aec --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/Texture.java @@ -0,0 +1,44 @@ +/* + * 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; + + +// Texture is a rectangular image which can be drawn on GLCanvas. +// The isOpaque() function gives a hint about whether the texture is opaque, +// so the drawing can be done faster. +// +// This is the current texture hierarchy: +// +// Texture +// -- ColorTexture +// -- FadeInTexture +// -- BasicTexture +// -- UploadedTexture +// -- BitmapTexture +// -- Tile +// -- ResourceTexture +// -- NinePatchTexture +// -- CanvasTexture +// -- StringTexture +// +public interface Texture { + public int getWidth(); + public int getHeight(); + public void draw(GLCanvas canvas, int x, int y); + public void draw(GLCanvas canvas, int x, int y, int w, int h); + public boolean isOpaque(); +} diff --git a/src/com/android/gallery3d/glrenderer/TextureUploader.java b/src/com/android/gallery3d/glrenderer/TextureUploader.java new file mode 100644 index 000000000..f17ab845c --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/TextureUploader.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2012 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 com.android.gallery3d.ui.GLRoot; +import com.android.gallery3d.ui.GLRoot.OnGLIdleListener; + +import java.util.ArrayDeque; + +public class TextureUploader implements OnGLIdleListener { + private static final int INIT_CAPACITY = 64; + private static final int QUOTA_PER_FRAME = 1; + + private final ArrayDeque mFgTextures = + new ArrayDeque(INIT_CAPACITY); + private final ArrayDeque mBgTextures = + new ArrayDeque(INIT_CAPACITY); + private final GLRoot mGLRoot; + private volatile boolean mIsQueued = false; + + public TextureUploader(GLRoot root) { + mGLRoot = root; + } + + public synchronized void clear() { + while (!mFgTextures.isEmpty()) { + mFgTextures.pop().setIsUploading(false); + } + while (!mBgTextures.isEmpty()) { + mBgTextures.pop().setIsUploading(false); + } + } + + // caller should hold synchronized on "this" + private void queueSelfIfNeed() { + if (mIsQueued) return; + mIsQueued = true; + mGLRoot.addOnGLIdleListener(this); + } + + public synchronized void addBgTexture(UploadedTexture t) { + if (t.isContentValid()) return; + mBgTextures.addLast(t); + t.setIsUploading(true); + queueSelfIfNeed(); + } + + public synchronized void addFgTexture(UploadedTexture t) { + if (t.isContentValid()) return; + mFgTextures.addLast(t); + t.setIsUploading(true); + queueSelfIfNeed(); + } + + private int upload(GLCanvas canvas, ArrayDeque deque, + int uploadQuota, boolean isBackground) { + while (uploadQuota > 0) { + UploadedTexture t; + synchronized (this) { + if (deque.isEmpty()) break; + t = deque.removeFirst(); + t.setIsUploading(false); + if (t.isContentValid()) continue; + + // this has to be protected by the synchronized block + // to prevent the inner bitmap get recycled + t.updateContent(canvas); + } + + // It will took some more time for a texture to be drawn for + // the first time. + // Thus, when scrolling, if a new column appears on screen, + // it may cause a UI jank even these textures are uploaded. + if (isBackground) t.draw(canvas, 0, 0); + --uploadQuota; + } + return uploadQuota; + } + + @Override + public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) { + int uploadQuota = QUOTA_PER_FRAME; + uploadQuota = upload(canvas, mFgTextures, uploadQuota, false); + if (uploadQuota < QUOTA_PER_FRAME) mGLRoot.requestRender(); + upload(canvas, mBgTextures, uploadQuota, true); + synchronized (this) { + mIsQueued = !mFgTextures.isEmpty() || !mBgTextures.isEmpty(); + return mIsQueued; + } + } +} diff --git a/src/com/android/gallery3d/glrenderer/TiledTexture.java b/src/com/android/gallery3d/glrenderer/TiledTexture.java new file mode 100644 index 000000000..6ca1de088 --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/TiledTexture.java @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2012 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.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff.Mode; +import android.graphics.PorterDuffXfermode; +import android.graphics.RectF; +import android.os.SystemClock; + +import com.android.gallery3d.ui.GLRoot; +import com.android.gallery3d.ui.GLRoot.OnGLIdleListener; + +import java.util.ArrayDeque; +import java.util.ArrayList; + +// This class is similar to BitmapTexture, except the bitmap is +// split into tiles. By doing so, we may increase the time required to +// upload the whole bitmap but we reduce the time of uploading each tile +// so it make the animation more smooth and prevents jank. +public class TiledTexture implements Texture { + private static final int CONTENT_SIZE = 254; + private static final int BORDER_SIZE = 1; + private static final int TILE_SIZE = CONTENT_SIZE + 2 * BORDER_SIZE; + private static final int INIT_CAPACITY = 8; + + // We are targeting at 60fps, so we have 16ms for each frame. + // In this 16ms, we use about 4~8 ms to upload tiles. + private static final long UPLOAD_TILE_LIMIT = 4; // ms + + private static Tile sFreeTileHead = null; + private static final Object sFreeTileLock = new Object(); + + private static Bitmap sUploadBitmap; + private static Canvas sCanvas; + private static Paint sBitmapPaint; + private static Paint sPaint; + + private int mUploadIndex = 0; + + private final Tile[] mTiles; // Can be modified in different threads. + // Should be protected by "synchronized." + private final int mWidth; + private final int mHeight; + private final RectF mSrcRect = new RectF(); + private final RectF mDestRect = new RectF(); + + public static class Uploader implements OnGLIdleListener { + private final ArrayDeque mTextures = + new ArrayDeque(INIT_CAPACITY); + + private final GLRoot mGlRoot; + private boolean mIsQueued = false; + + public Uploader(GLRoot glRoot) { + mGlRoot = glRoot; + } + + public synchronized void clear() { + mTextures.clear(); + } + + public synchronized void addTexture(TiledTexture t) { + if (t.isReady()) return; + mTextures.addLast(t); + + if (mIsQueued) return; + mIsQueued = true; + mGlRoot.addOnGLIdleListener(this); + } + + @Override + public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) { + ArrayDeque deque = mTextures; + synchronized (this) { + long now = SystemClock.uptimeMillis(); + long dueTime = now + UPLOAD_TILE_LIMIT; + while (now < dueTime && !deque.isEmpty()) { + TiledTexture t = deque.peekFirst(); + if (t.uploadNextTile(canvas)) { + deque.removeFirst(); + mGlRoot.requestRender(); + } + now = SystemClock.uptimeMillis(); + } + mIsQueued = !mTextures.isEmpty(); + + // return true to keep this listener in the queue + return mIsQueued; + } + } + } + + private static class Tile extends UploadedTexture { + public int offsetX; + public int offsetY; + public Bitmap bitmap; + public Tile nextFreeTile; + public int contentWidth; + public int contentHeight; + + @Override + public void setSize(int width, int height) { + contentWidth = width; + contentHeight = height; + mWidth = width + 2 * BORDER_SIZE; + mHeight = height + 2 * BORDER_SIZE; + mTextureWidth = TILE_SIZE; + mTextureHeight = TILE_SIZE; + } + + @Override + protected Bitmap onGetBitmap() { + int x = BORDER_SIZE - offsetX; + int y = BORDER_SIZE - offsetY; + int r = bitmap.getWidth() + x; + int b = bitmap.getHeight() + y; + sCanvas.drawBitmap(bitmap, x, y, sBitmapPaint); + bitmap = null; + + // draw borders if need + if (x > 0) sCanvas.drawLine(x - 1, 0, x - 1, TILE_SIZE, sPaint); + if (y > 0) sCanvas.drawLine(0, y - 1, TILE_SIZE, y - 1, sPaint); + if (r < CONTENT_SIZE) sCanvas.drawLine(r, 0, r, TILE_SIZE, sPaint); + if (b < CONTENT_SIZE) sCanvas.drawLine(0, b, TILE_SIZE, b, sPaint); + + return sUploadBitmap; + } + + @Override + protected void onFreeBitmap(Bitmap bitmap) { + // do nothing + } + } + + private static void freeTile(Tile tile) { + tile.invalidateContent(); + tile.bitmap = null; + synchronized (sFreeTileLock) { + tile.nextFreeTile = sFreeTileHead; + sFreeTileHead = tile; + } + } + + private static Tile obtainTile() { + synchronized (sFreeTileLock) { + Tile result = sFreeTileHead; + if (result == null) return new Tile(); + sFreeTileHead = result.nextFreeTile; + result.nextFreeTile = null; + return result; + } + } + + private boolean uploadNextTile(GLCanvas canvas) { + if (mUploadIndex == mTiles.length) return true; + + synchronized (mTiles) { + Tile next = mTiles[mUploadIndex++]; + + // Make sure tile has not already been recycled by the time + // this is called (race condition in onGLIdle) + if (next.bitmap != null) { + boolean hasBeenLoad = next.isLoaded(); + next.updateContent(canvas); + + // It will take some time for a texture to be drawn for the first + // time. When scrolling, we need to draw several tiles on the screen + // at the same time. It may cause a UI jank even these textures has + // been uploaded. + if (!hasBeenLoad) next.draw(canvas, 0, 0); + } + } + return mUploadIndex == mTiles.length; + } + + public TiledTexture(Bitmap bitmap) { + mWidth = bitmap.getWidth(); + mHeight = bitmap.getHeight(); + ArrayList list = new ArrayList(); + + for (int x = 0, w = mWidth; x < w; x += CONTENT_SIZE) { + for (int y = 0, h = mHeight; y < h; y += CONTENT_SIZE) { + Tile tile = obtainTile(); + tile.offsetX = x; + tile.offsetY = y; + tile.bitmap = bitmap; + tile.setSize( + Math.min(CONTENT_SIZE, mWidth - x), + Math.min(CONTENT_SIZE, mHeight - y)); + list.add(tile); + } + } + mTiles = list.toArray(new Tile[list.size()]); + } + + public boolean isReady() { + return mUploadIndex == mTiles.length; + } + + // Can be called in UI thread. + public void recycle() { + synchronized (mTiles) { + for (int i = 0, n = mTiles.length; i < n; ++i) { + freeTile(mTiles[i]); + } + } + } + + public static void freeResources() { + sUploadBitmap = null; + sCanvas = null; + sBitmapPaint = null; + sPaint = null; + } + + public static void prepareResources() { + sUploadBitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Config.ARGB_8888); + sCanvas = new Canvas(sUploadBitmap); + sBitmapPaint = new Paint(Paint.FILTER_BITMAP_FLAG); + sBitmapPaint.setXfermode(new PorterDuffXfermode(Mode.SRC)); + sPaint = new Paint(); + sPaint.setXfermode(new PorterDuffXfermode(Mode.SRC)); + sPaint.setColor(Color.TRANSPARENT); + } + + // We want to draw the "source" on the "target". + // This method is to find the "output" rectangle which is + // the corresponding area of the "src". + // (x,y) target + // (x0,y0) source +---------------+ + // +----------+ | | + // | src | | output | + // | +--+ | linear map | +----+ | + // | +--+ | ----------> | | | | + // | | by (scaleX, scaleY) | +----+ | + // +----------+ | | + // Texture +---------------+ + // Canvas + private static void mapRect(RectF output, + RectF src, float x0, float y0, float x, float y, float scaleX, + float scaleY) { + output.set(x + (src.left - x0) * scaleX, + y + (src.top - y0) * scaleY, + x + (src.right - x0) * scaleX, + y + (src.bottom - y0) * scaleY); + } + + // Draws a mixed color of this texture and a specified color onto the + // a rectangle. The used color is: from * (1 - ratio) + to * ratio. + public void drawMixed(GLCanvas canvas, int color, float ratio, + int x, int y, int width, int height) { + RectF src = mSrcRect; + RectF dest = mDestRect; + float scaleX = (float) width / mWidth; + float scaleY = (float) height / mHeight; + synchronized (mTiles) { + for (int i = 0, n = mTiles.length; i < n; ++i) { + Tile t = mTiles[i]; + src.set(0, 0, t.contentWidth, t.contentHeight); + src.offset(t.offsetX, t.offsetY); + mapRect(dest, src, 0, 0, x, y, scaleX, scaleY); + src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY); + canvas.drawMixed(t, color, ratio, mSrcRect, mDestRect); + } + } + } + + // Draws the texture on to the specified rectangle. + @Override + public void draw(GLCanvas canvas, int x, int y, int width, int height) { + RectF src = mSrcRect; + RectF dest = mDestRect; + float scaleX = (float) width / mWidth; + float scaleY = (float) height / mHeight; + synchronized (mTiles) { + for (int i = 0, n = mTiles.length; i < n; ++i) { + Tile t = mTiles[i]; + src.set(0, 0, t.contentWidth, t.contentHeight); + src.offset(t.offsetX, t.offsetY); + mapRect(dest, src, 0, 0, x, y, scaleX, scaleY); + src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY); + canvas.drawTexture(t, mSrcRect, mDestRect); + } + } + } + + // Draws a sub region of this texture on to the specified rectangle. + public void draw(GLCanvas canvas, RectF source, RectF target) { + RectF src = mSrcRect; + RectF dest = mDestRect; + float x0 = source.left; + float y0 = source.top; + float x = target.left; + float y = target.top; + float scaleX = target.width() / source.width(); + float scaleY = target.height() / source.height(); + + synchronized (mTiles) { + for (int i = 0, n = mTiles.length; i < n; ++i) { + Tile t = mTiles[i]; + src.set(0, 0, t.contentWidth, t.contentHeight); + src.offset(t.offsetX, t.offsetY); + if (!src.intersect(source)) continue; + mapRect(dest, src, x0, y0, x, y, scaleX, scaleY); + src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY); + canvas.drawTexture(t, src, dest); + } + } + } + + @Override + public int getWidth() { + return mWidth; + } + + @Override + public int getHeight() { + return mHeight; + } + + @Override + public void draw(GLCanvas canvas, int x, int y) { + draw(canvas, x, y, mWidth, mHeight); + } + + @Override + public boolean isOpaque() { + return false; + } +} diff --git a/src/com/android/gallery3d/glrenderer/UploadedTexture.java b/src/com/android/gallery3d/glrenderer/UploadedTexture.java new file mode 100644 index 000000000..ee55736bb --- /dev/null +++ b/src/com/android/gallery3d/glrenderer/UploadedTexture.java @@ -0,0 +1,298 @@ +/* + * 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 sBorderLines = + new HashMap(); + 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 = GLCanvas.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(); + } +} diff --git a/src/com/android/gallery3d/ui/AbstractSlotRenderer.java b/src/com/android/gallery3d/ui/AbstractSlotRenderer.java index 10b710d2d..729439dc3 100644 --- a/src/com/android/gallery3d/ui/AbstractSlotRenderer.java +++ b/src/com/android/gallery3d/ui/AbstractSlotRenderer.java @@ -20,6 +20,11 @@ import android.content.Context; import android.graphics.Rect; import com.android.gallery3d.R; +import com.android.gallery3d.glrenderer.FadeOutTexture; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.NinePatchTexture; +import com.android.gallery3d.glrenderer.ResourceTexture; +import com.android.gallery3d.glrenderer.Texture; public abstract class AbstractSlotRenderer implements SlotView.SlotRenderer { diff --git a/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java b/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java index 80dfc919f..d5a15b4ac 100644 --- a/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java +++ b/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java @@ -29,6 +29,10 @@ import com.android.gallery3d.data.MediaItem; import com.android.gallery3d.data.MediaObject; import com.android.gallery3d.data.MediaSet; import com.android.gallery3d.data.Path; +import com.android.gallery3d.glrenderer.BitmapTexture; +import com.android.gallery3d.glrenderer.Texture; +import com.android.gallery3d.glrenderer.TextureUploader; +import com.android.gallery3d.glrenderer.TiledTexture; import com.android.gallery3d.util.Future; import com.android.gallery3d.util.FutureListener; import com.android.gallery3d.util.ThreadPool; diff --git a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java index 70d7c273a..5332ef89a 100644 --- a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java +++ b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java @@ -21,6 +21,13 @@ import com.android.gallery3d.app.AbstractGalleryActivity; import com.android.gallery3d.app.AlbumSetDataLoader; import com.android.gallery3d.data.MediaObject; import com.android.gallery3d.data.Path; +import com.android.gallery3d.glrenderer.ColorTexture; +import com.android.gallery3d.glrenderer.FadeInTexture; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.ResourceTexture; +import com.android.gallery3d.glrenderer.Texture; +import com.android.gallery3d.glrenderer.TiledTexture; +import com.android.gallery3d.glrenderer.UploadedTexture; import com.android.gallery3d.ui.AlbumSetSlidingWindow.AlbumSetEntry; public class AlbumSetSlotRenderer extends AbstractSlotRenderer { diff --git a/src/com/android/gallery3d/ui/AlbumSlidingWindow.java b/src/com/android/gallery3d/ui/AlbumSlidingWindow.java index 678c43251..8cd2cf500 100644 --- a/src/com/android/gallery3d/ui/AlbumSlidingWindow.java +++ b/src/com/android/gallery3d/ui/AlbumSlidingWindow.java @@ -27,6 +27,8 @@ import com.android.gallery3d.data.MediaItem; import com.android.gallery3d.data.MediaObject; import com.android.gallery3d.data.Path; import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback; +import com.android.gallery3d.glrenderer.Texture; +import com.android.gallery3d.glrenderer.TiledTexture; import com.android.gallery3d.util.Future; import com.android.gallery3d.util.FutureListener; import com.android.gallery3d.util.JobLimiter; diff --git a/src/com/android/gallery3d/ui/AlbumSlotRenderer.java b/src/com/android/gallery3d/ui/AlbumSlotRenderer.java index ce5b7ac24..dc6c89b0e 100644 --- a/src/com/android/gallery3d/ui/AlbumSlotRenderer.java +++ b/src/com/android/gallery3d/ui/AlbumSlotRenderer.java @@ -20,6 +20,11 @@ import com.android.gallery3d.app.AbstractGalleryActivity; import com.android.gallery3d.app.AlbumDataLoader; import com.android.gallery3d.data.MediaObject; import com.android.gallery3d.data.Path; +import com.android.gallery3d.glrenderer.ColorTexture; +import com.android.gallery3d.glrenderer.FadeInTexture; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.Texture; +import com.android.gallery3d.glrenderer.TiledTexture; public class AlbumSlotRenderer extends AbstractSlotRenderer { @SuppressWarnings("unused") diff --git a/src/com/android/gallery3d/ui/BasicTexture.java b/src/com/android/gallery3d/ui/BasicTexture.java deleted file mode 100644 index 38686d59f..000000000 --- a/src/com/android/gallery3d/ui/BasicTexture.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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 java.util.WeakHashMap; - -// BasicTexture is a Texture corresponds to a real GL texture. -// The state of a BasicTexture indicates whether its data is loaded to GL memory. -// If a BasicTexture is loaded into GL memory, it has a GL texture id. -abstract class BasicTexture implements Texture { - - @SuppressWarnings("unused") - private static final String TAG = "BasicTexture"; - protected static final int UNSPECIFIED = -1; - - protected static final int STATE_UNLOADED = 0; - protected static final int STATE_LOADED = 1; - protected static final int STATE_ERROR = -1; - - // Log a warning if a texture is larger along a dimension - private static final int MAX_TEXTURE_SIZE = 4096; - - protected int mId = -1; - protected int mState; - - protected int mWidth = UNSPECIFIED; - protected int mHeight = UNSPECIFIED; - - protected int mTextureWidth; - protected int mTextureHeight; - - private boolean mHasBorder; - - protected GLCanvas mCanvasRef = null; - private static WeakHashMap sAllTextures - = new WeakHashMap(); - private static ThreadLocal sInFinalizer = new ThreadLocal(); - - protected BasicTexture(GLCanvas canvas, int id, int state) { - setAssociatedCanvas(canvas); - mId = id; - mState = state; - synchronized (sAllTextures) { - sAllTextures.put(this, null); - } - } - - protected BasicTexture() { - this(null, 0, STATE_UNLOADED); - } - - protected void setAssociatedCanvas(GLCanvas canvas) { - mCanvasRef = canvas; - } - - /** - * Sets the content size of this texture. In OpenGL, the actual texture - * size must be of power of 2, the size of the content may be smaller. - */ - protected void setSize(int width, int height) { - mWidth = width; - mHeight = height; - mTextureWidth = Utils.nextPowerOf2(width); - mTextureHeight = Utils.nextPowerOf2(height); - if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) { - Log.w(TAG, String.format("texture is too large: %d x %d", - mTextureWidth, mTextureHeight), new Exception()); - } - } - - public int getId() { - return mId; - } - - @Override - public int getWidth() { - return mWidth; - } - - @Override - public int getHeight() { - return mHeight; - } - - // Returns the width rounded to the next power of 2. - public int getTextureWidth() { - return mTextureWidth; - } - - // Returns the height rounded to the next power of 2. - public int getTextureHeight() { - return mTextureHeight; - } - - // Returns true if the texture has one pixel transparent border around the - // actual content. This is used to avoid jigged edges. - // - // The jigged edges appear because we use GL_CLAMP_TO_EDGE for texture wrap - // mode (GL_CLAMP is not available in OpenGL ES), so a pixel partially - // covered by the texture will use the color of the edge texel. If we add - // the transparent border, the color of the edge texel will be mixed with - // appropriate amount of transparent. - // - // Currently our background is black, so we can draw the thumbnails without - // enabling blending. - public boolean hasBorder() { - return mHasBorder; - } - - protected void setBorder(boolean hasBorder) { - mHasBorder = hasBorder; - } - - @Override - public void draw(GLCanvas canvas, int x, int y) { - canvas.drawTexture(this, x, y, getWidth(), getHeight()); - } - - @Override - public void draw(GLCanvas canvas, int x, int y, int w, int h) { - canvas.drawTexture(this, x, y, w, h); - } - - // onBind is called before GLCanvas binds this texture. - // It should make sure the data is uploaded to GL memory. - abstract protected boolean onBind(GLCanvas canvas); - - // Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D). - abstract protected int getTarget(); - - public boolean isLoaded() { - return mState == STATE_LOADED; - } - - // recycle() is called when the texture will never be used again, - // so it can free all resources. - public void recycle() { - freeResource(); - } - - // yield() is called when the texture will not be used temporarily, - // so it can free some resources. - // The default implementation unloads the texture from GL memory, so - // the subclass should make sure it can reload the texture to GL memory - // later, or it will have to override this method. - public void yield() { - freeResource(); - } - - private void freeResource() { - GLCanvas canvas = mCanvasRef; - if (canvas != null && mId != -1) { - canvas.unloadTexture(this); - mId = -1; // Don't free it again. - } - mState = STATE_UNLOADED; - setAssociatedCanvas(null); - } - - @Override - protected void finalize() { - sInFinalizer.set(BasicTexture.class); - recycle(); - sInFinalizer.set(null); - } - - // This is for deciding if we can call Bitmap's recycle(). - // We cannot call Bitmap's recycle() in finalizer because at that point - // the finalizer of Bitmap may already be called so recycle() will crash. - public static boolean inFinalizer() { - return sInFinalizer.get() != null; - } - - public static void yieldAllTextures() { - synchronized (sAllTextures) { - for (BasicTexture t : sAllTextures.keySet()) { - t.yield(); - } - } - } - - public static void invalidateAllTextures() { - synchronized (sAllTextures) { - for (BasicTexture t : sAllTextures.keySet()) { - t.mState = STATE_UNLOADED; - t.setAssociatedCanvas(null); - } - } - } -} diff --git a/src/com/android/gallery3d/ui/BitmapScreenNail.java b/src/com/android/gallery3d/ui/BitmapScreenNail.java index 741eefbe3..a3d403946 100644 --- a/src/com/android/gallery3d/ui/BitmapScreenNail.java +++ b/src/com/android/gallery3d/ui/BitmapScreenNail.java @@ -19,6 +19,9 @@ package com.android.gallery3d.ui; import android.graphics.Bitmap; import android.graphics.RectF; +import com.android.gallery3d.glrenderer.BitmapTexture; +import com.android.gallery3d.glrenderer.GLCanvas; + public class BitmapScreenNail implements ScreenNail { private final BitmapTexture mBitmapTexture; diff --git a/src/com/android/gallery3d/ui/BitmapTexture.java b/src/com/android/gallery3d/ui/BitmapTexture.java deleted file mode 100644 index 607544907..000000000 --- a/src/com/android/gallery3d/ui/BitmapTexture.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 android.graphics.Bitmap; - -import com.android.gallery3d.common.Utils; - -// BitmapTexture is a texture whose content is specified by a fixed Bitmap. -// -// The texture does not own the Bitmap. The user should make sure the Bitmap -// is valid during the texture's lifetime. When the texture is recycled, it -// does not free the Bitmap. -public class BitmapTexture extends UploadedTexture { - protected Bitmap mContentBitmap; - - public BitmapTexture(Bitmap bitmap) { - this(bitmap, false); - } - - public BitmapTexture(Bitmap bitmap, boolean hasBorder) { - super(hasBorder); - Utils.assertTrue(bitmap != null && !bitmap.isRecycled()); - mContentBitmap = bitmap; - } - - @Override - protected void onFreeBitmap(Bitmap bitmap) { - // Do nothing. - } - - @Override - protected Bitmap onGetBitmap() { - return mContentBitmap; - } - - public Bitmap getBitmap() { - return mContentBitmap; - } -} diff --git a/src/com/android/gallery3d/ui/CanvasTexture.java b/src/com/android/gallery3d/ui/CanvasTexture.java deleted file mode 100644 index a2e9e48ad..000000000 --- a/src/com/android/gallery3d/ui/CanvasTexture.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Canvas; - -// CanvasTexture is a texture whose content is the drawing on a Canvas. -// The subclasses should override onDraw() to draw on the bitmap. -// By default CanvasTexture is not opaque. -abstract class CanvasTexture extends UploadedTexture { - protected Canvas mCanvas; - private final Config mConfig; - - public CanvasTexture(int width, int height) { - mConfig = Config.ARGB_8888; - setSize(width, height); - setOpaque(false); - } - - @Override - protected Bitmap onGetBitmap() { - Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, mConfig); - mCanvas = new Canvas(bitmap); - onDraw(mCanvas, bitmap); - return bitmap; - } - - @Override - protected void onFreeBitmap(Bitmap bitmap) { - if (!inFinalizer()) { - bitmap.recycle(); - } - } - - abstract protected void onDraw(Canvas canvas, Bitmap backing); -} diff --git a/src/com/android/gallery3d/ui/ColorTexture.java b/src/com/android/gallery3d/ui/ColorTexture.java deleted file mode 100644 index 733c05653..000000000 --- a/src/com/android/gallery3d/ui/ColorTexture.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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; - -// ColorTexture is a texture which fills the rectangle with the specified color. -public class ColorTexture implements Texture { - - private final int mColor; - private int mWidth; - private int mHeight; - - public ColorTexture(int color) { - mColor = color; - mWidth = 1; - mHeight = 1; - } - - @Override - public void draw(GLCanvas canvas, int x, int y) { - draw(canvas, x, y, mWidth, mHeight); - } - - @Override - public void draw(GLCanvas canvas, int x, int y, int w, int h) { - canvas.fillRect(x, y, w, h, mColor); - } - - @Override - public boolean isOpaque() { - return Utils.isOpaque(mColor); - } - - public void setSize(int width, int height) { - mWidth = width; - mHeight = height; - } - - @Override - public int getWidth() { - return mWidth; - } - - @Override - public int getHeight() { - return mHeight; - } -} diff --git a/src/com/android/gallery3d/ui/EdgeEffect.java b/src/com/android/gallery3d/ui/EdgeEffect.java index ed369737b..87ff0c5d3 100644 --- a/src/com/android/gallery3d/ui/EdgeEffect.java +++ b/src/com/android/gallery3d/ui/EdgeEffect.java @@ -23,6 +23,8 @@ import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import com.android.gallery3d.R; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.ResourceTexture; // This is copied from android.widget.EdgeEffect with some small modifications: // (1) Copy the images (overscroll_{edge|glow}.png) to local resources. diff --git a/src/com/android/gallery3d/ui/EdgeView.java b/src/com/android/gallery3d/ui/EdgeView.java index 4aff0494d..051de18fa 100644 --- a/src/com/android/gallery3d/ui/EdgeView.java +++ b/src/com/android/gallery3d/ui/EdgeView.java @@ -19,6 +19,8 @@ package com.android.gallery3d.ui; import android.content.Context; import android.opengl.Matrix; +import com.android.gallery3d.glrenderer.GLCanvas; + // EdgeView draws EdgeEffect (blue glow) at four sides of the view. public class EdgeView extends GLView { @SuppressWarnings("unused") diff --git a/src/com/android/gallery3d/ui/ExtTexture.java b/src/com/android/gallery3d/ui/ExtTexture.java deleted file mode 100644 index 180a89dce..000000000 --- a/src/com/android/gallery3d/ui/ExtTexture.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2012 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; - - -// ExtTexture is a texture whose content comes from a external texture. -// Before drawing, setSize() should be called. -public class ExtTexture extends BasicTexture { - - private int mTarget; - - public ExtTexture(int target) { - GLId glId = GLCanvas.getGLId(); - mId = glId.generateTexture(); - mTarget = target; - } - - private void uploadToCanvas(GLCanvas canvas) { - canvas.setTextureParameters(this); - setAssociatedCanvas(canvas); - mState = STATE_LOADED; - } - - @Override - protected boolean onBind(GLCanvas canvas) { - if (!isLoaded()) { - uploadToCanvas(canvas); - } - - return true; - } - - @Override - public int getTarget() { - return mTarget; - } - - @Override - public boolean isOpaque() { - return true; - } - - @Override - public void yield() { - // we cannot free the texture because we have no backup. - } -} diff --git a/src/com/android/gallery3d/ui/FadeInTexture.java b/src/com/android/gallery3d/ui/FadeInTexture.java deleted file mode 100644 index c6a9811f6..000000000 --- a/src/com/android/gallery3d/ui/FadeInTexture.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2011 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; - -// FadeInTexture is a texture which begins with a color, then gradually animates -// into a given texture. -public class FadeInTexture extends FadeTexture implements Texture { - @SuppressWarnings("unused") - private static final String TAG = "FadeInTexture"; - - private final int mColor; - private final TiledTexture mTexture; - - public FadeInTexture(int color, TiledTexture texture) { - super(texture.getWidth(), texture.getHeight(), texture.isOpaque()); - mColor = color; - mTexture = texture; - } - - @Override - public void draw(GLCanvas canvas, int x, int y, int w, int h) { - if (isAnimating()) { - mTexture.drawMixed(canvas, mColor, getRatio(), x, y, w, h); - } else { - mTexture.draw(canvas, x, y, w, h); - } - } -} diff --git a/src/com/android/gallery3d/ui/FadeOutTexture.java b/src/com/android/gallery3d/ui/FadeOutTexture.java deleted file mode 100644 index 7050e535e..000000000 --- a/src/com/android/gallery3d/ui/FadeOutTexture.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2011 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; - -// FadeOutTexture is a texture which begins with a given texture, then gradually animates -// into fading out totally. -public class FadeOutTexture extends FadeTexture { - @SuppressWarnings("unused") - private static final String TAG = "FadeOutTexture"; - - private final BasicTexture mTexture; - - public FadeOutTexture(BasicTexture texture) { - super(texture.getWidth(), texture.getHeight(), texture.isOpaque()); - mTexture = texture; - } - - @Override - public void draw(GLCanvas canvas, int x, int y, int w, int h) { - if (isAnimating()) { - canvas.save(GLCanvas.SAVE_FLAG_ALPHA); - canvas.setAlpha(getRatio()); - mTexture.draw(canvas, x, y, w, h); - canvas.restore(); - } - } -} diff --git a/src/com/android/gallery3d/ui/FadeTexture.java b/src/com/android/gallery3d/ui/FadeTexture.java deleted file mode 100644 index 5236d3639..000000000 --- a/src/com/android/gallery3d/ui/FadeTexture.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2011 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; - -// FadeTexture is a texture which fades the given texture along the time. -public abstract class FadeTexture implements Texture { - @SuppressWarnings("unused") - private static final String TAG = "FadeTexture"; - - // The duration of the fading animation in milliseconds - public static final int DURATION = 180; - - private final long mStartTime; - private final int mWidth; - private final int mHeight; - private final boolean mIsOpaque; - private boolean mIsAnimating; - - public FadeTexture(int width, int height, boolean opaque) { - mWidth = width; - mHeight = height; - mIsOpaque = opaque; - mStartTime = now(); - mIsAnimating = true; - } - - @Override - public void draw(GLCanvas canvas, int x, int y) { - draw(canvas, x, y, mWidth, mHeight); - } - - @Override - public boolean isOpaque() { - return mIsOpaque; - } - - @Override - public int getWidth() { - return mWidth; - } - - @Override - public int getHeight() { - return mHeight; - } - - public boolean isAnimating() { - if (mIsAnimating) { - if (now() - mStartTime >= DURATION) { - mIsAnimating = false; - } - } - return mIsAnimating; - } - - protected float getRatio() { - float r = (float)(now() - mStartTime) / DURATION; - return Utils.clamp(1.0f - r, 0.0f, 1.0f); - } - - private long now() { - return AnimationTime.get(); - } -} diff --git a/src/com/android/gallery3d/ui/GLCanvas.java b/src/com/android/gallery3d/ui/GLCanvas.java deleted file mode 100644 index 1dbee5dd9..000000000 --- a/src/com/android/gallery3d/ui/GLCanvas.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * 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 android.graphics.Bitmap; -import android.graphics.RectF; - -import com.android.gallery3d.common.ApiHelper; - -import javax.microedition.khronos.opengles.GL11; - -// -// GLCanvas gives a convenient interface to draw using OpenGL. -// -// When a rectangle is specified in this interface, it means the region -// [x, x+width) * [y, y+height) -// -public abstract class GLCanvas { - public enum Blending { - Additive, Mix, - } - - private static GLCanvas sInstance = instantiateCanvas(); - private static GLId sGLId = instantiateGLId(); - - public static GLId getGLId() { - return sGLId; - } - - public static GLCanvas getInstance() { - return sInstance; - } - - private static GLId instantiateGLId() { - return ApiHelper.HAS_GLES20_REQUIRED ? (GLES20Canvas) sInstance : new GLIdImpl(); - } - - private static GLCanvas instantiateCanvas() { - return ApiHelper.HAS_GLES20_REQUIRED ? new GLES20Canvas() : new GLCanvasImpl(); - } - - public static int getEGLContextClientVersion() { - return ApiHelper.HAS_GLES20_REQUIRED ? 2 : 1; - } - - public abstract void initialize(GL11 gl); - - // Tells GLCanvas the size of the underlying GL surface. This should be - // called before first drawing and when the size of GL surface is changed. - // This is called by GLRoot and should not be called by the clients - // who only want to draw on the GLCanvas. Both width and height must be - // nonnegative. - public abstract void setSize(int width, int height); - - // Clear the drawing buffers. This should only be used by GLRoot. - public abstract void clearBuffer(); - - public abstract void clearBuffer(float[] argb); - - // Sets and gets the current alpha, alpha must be in [0, 1]. - public abstract void setAlpha(float alpha); - - public abstract float getAlpha(); - - // (current alpha) = (current alpha) * alpha - public abstract void multiplyAlpha(float alpha); - - // Change the current transform matrix. - public abstract void translate(float x, float y, float z); - - public abstract void translate(float x, float y); - - public abstract void scale(float sx, float sy, float sz); - - public abstract void rotate(float angle, float x, float y, float z); - - public abstract void multiplyMatrix(float[] mMatrix, int offset); - - // Pushes the configuration state (matrix, and alpha) onto - // a private stack. - public abstract void save(); - - // Same as save(), but only save those specified in saveFlags. - public abstract void save(int saveFlags); - - public static final int SAVE_FLAG_ALL = 0xFFFFFFFF; - public static final int SAVE_FLAG_ALPHA = 0x01; - public static final int SAVE_FLAG_MATRIX = 0x02; - public static final int SAVE_FLAG_BLEND = 0x04; - - // Pops from the top of the stack as current configuration state (matrix, - // alpha, and clip). This call balances a previous call to save(), and is - // used to remove all modifications to the configuration state since the - // last save call. - public abstract void restore(); - - // Draws a line using the specified paint from (x1, y1) to (x2, y2). - // (Both end points are included). - public abstract void drawLine(float x1, float y1, float x2, float y2, GLPaint paint); - - // Draws a rectangle using the specified paint from (x1, y1) to (x2, y2). - // (Both end points are included). - public abstract void drawRect(float x1, float y1, float x2, float y2, GLPaint paint); - - // Fills the specified rectangle with the specified color. - public abstract void fillRect(float x, float y, float width, float height, int color); - - // Draws a texture to the specified rectangle. - public abstract void drawTexture( - BasicTexture texture, int x, int y, int width, int height); - - public abstract void drawMesh(BasicTexture tex, int x, int y, int xyBuffer, - int uvBuffer, int indexBuffer, int indexCount); - - // Draws the source rectangle part of the texture to the target rectangle. - public abstract void drawTexture(BasicTexture texture, RectF source, RectF target); - - // Draw a texture with a specified texture transform. - public abstract void drawTexture(BasicTexture texture, float[] mTextureTransform, - int x, int y, int w, int h); - - // Draw two textures to the specified rectangle. The actual texture used is - // from * (1 - ratio) + to * ratio - // The two textures must have the same size. - public abstract void drawMixed(BasicTexture from, int toColor, - float ratio, int x, int y, int w, int h); - - // Draw a region of a texture and a specified color to the specified - // rectangle. The actual color used is from * (1 - ratio) + to * ratio. - // The region of the texture is defined by parameter "src". The target - // rectangle is specified by parameter "target". - public abstract void drawMixed(BasicTexture from, int toColor, - float ratio, RectF src, RectF target); - - // Unloads the specified texture from the canvas. The resource allocated - // to draw the texture will be released. The specified texture will return - // to the unloaded state. This function should be called only from - // BasicTexture or its descendant - public abstract boolean unloadTexture(BasicTexture texture); - - // Delete the specified buffer object, similar to unloadTexture. - public abstract void deleteBuffer(int bufferId); - - // Delete the textures and buffers in GL side. This function should only be - // called in the GL thread. - public abstract void deleteRecycledResources(); - - // Dump statistics information and clear the counters. For debug only. - public abstract void dumpStatisticsAndClear(); - - public abstract void beginRenderTarget(RawTexture texture); - - public abstract void endRenderTarget(); - - /** - * Sets texture parameters to use GL_CLAMP_TO_EDGE for both - * GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T. Sets texture parameters to be - * GL_LINEAR for GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER. - * bindTexture() must be called prior to this. - * - * @param texture The texture to set parameters on. - */ - public abstract void setTextureParameters(BasicTexture texture); - - /** - * Initializes the texture to a size by calling texImage2D on it. - * - * @param texture The texture to initialize the size. - * @param format The texture format (e.g. GL_RGBA) - * @param type The texture type (e.g. GL_UNSIGNED_BYTE) - */ - public abstract void initializeTextureSize(BasicTexture texture, int format, int type); - - /** - * Initializes the texture to a size by calling texImage2D on it. - * - * @param texture The texture to initialize the size. - * @param bitmap The bitmap to initialize the bitmap with. - */ - public abstract void initializeTexture(BasicTexture texture, Bitmap bitmap); - - /** - * Calls glTexSubImage2D to upload a bitmap to the texture. - * - * @param texture The target texture to write to. - * @param xOffset Specifies a texel offset in the x direction within the - * texture array. - * @param yOffset Specifies a texel offset in the y direction within the - * texture array. - * @param format The texture format (e.g. GL_RGBA) - * @param type The texture type (e.g. GL_UNSIGNED_BYTE) - */ - public abstract void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, - Bitmap bitmap, - int format, int type); - - /** - * Generates buffers and uploads the buffer data. - * - * @param buffer The buffer to upload - * @return The buffer ID that was generated. - */ - public abstract int uploadBuffer(java.nio.FloatBuffer buffer); - - /** - * Generates buffers and uploads the element array buffer data. - * - * @param buffer The buffer to upload - * @return The buffer ID that was generated. - */ - public abstract int uploadBuffer(java.nio.ByteBuffer buffer); - - /** - * Sets the blending algorithm if a texture is not opaque. - * - * @param blending Either mixing (overlay) or adding a texture. - */ - public abstract void setBlending(Blending blending); - - /** - * Enable stencil test - */ - public abstract void enableStencil(); - - /** - * Disable stencil. - */ - public abstract void disableStencil(); - - /** - * Clears the stencil so that a new stencil can be generated. - */ - public abstract void clearStencilBuffer(); - - /** - * Start/stop updating the stencil buffer. - * - * @param update True if the stencil should be updated, false otherwise. - */ - public abstract void updateStencil(boolean update); - - /** - * Changes how the stencil buffer is used. - * - * @param onlyOutside If true, only the area outside the stencil can be - * changed. If false, the area inside the stencil can be drawn to - * as well. - */ - public abstract void drawOnlyOutsideStencil(boolean onlyOutside); -} diff --git a/src/com/android/gallery3d/ui/GLCanvasImpl.java b/src/com/android/gallery3d/ui/GLCanvasImpl.java deleted file mode 100644 index 54c231c3b..000000000 --- a/src/com/android/gallery3d/ui/GLCanvasImpl.java +++ /dev/null @@ -1,1036 +0,0 @@ -/* - * 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 android.graphics.Bitmap; -import android.graphics.RectF; -import android.opengl.GLU; -import android.opengl.GLUtils; -import android.opengl.Matrix; - -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.util.IntArray; - -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.util.ArrayList; - -import javax.microedition.khronos.opengles.GL10; -import javax.microedition.khronos.opengles.GL11; -import javax.microedition.khronos.opengles.GL11Ext; -import javax.microedition.khronos.opengles.GL11ExtensionPack; - -public class GLCanvasImpl extends GLCanvas { - @SuppressWarnings("unused") - private static final String TAG = "GLCanvasImp"; - - private static final float OPAQUE_ALPHA = 0.95f; - - private static final int OFFSET_FILL_RECT = 0; - private static final int OFFSET_DRAW_LINE = 4; - private static final int OFFSET_DRAW_RECT = 6; - private static final float[] BOX_COORDINATES = { - 0, 0, 1, 0, 0, 1, 1, 1, // used for filling a rectangle - 0, 0, 1, 1, // used for drawing a line - 0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle - - private GL11 mGL; - - private final float mMatrixValues[] = new float[16]; - private final float mTextureMatrixValues[] = new float[16]; - - // The results of mapPoints are stored in this buffer, and the order is - // x1, y1, x2, y2. - private final float mMapPointsBuffer[] = new float[4]; - - private final float mTextureColor[] = new float[4]; - - private int mBoxCoords; - - private GLState mGLState; - private final ArrayList mTargetStack = new ArrayList(); - - private float mAlpha; - private final ArrayList mRestoreStack = new ArrayList(); - private ConfigState mRecycledRestoreAction; - - private final RectF mDrawTextureSourceRect = new RectF(); - private final RectF mDrawTextureTargetRect = new RectF(); - private final float[] mTempMatrix = new float[32]; - private final IntArray mUnboundTextures = new IntArray(); - private final IntArray mDeleteBuffers = new IntArray(); - private int mScreenWidth; - private int mScreenHeight; - private boolean mBlendEnabled = true; - private int mFrameBuffer[] = new int[1]; - private static float[] sCropRect = new float[4]; - - private RawTexture mTargetTexture; - private Blending mBlending = Blending.Mix; - - // Drawing statistics - int mCountDrawLine; - int mCountFillRect; - int mCountDrawMesh; - int mCountTextureRect; - int mCountTextureOES; - - GLCanvasImpl() { - } - - @Override - public void setSize(int width, int height) { - Utils.assertTrue(width >= 0 && height >= 0); - - if (mTargetTexture == null) { - mScreenWidth = width; - mScreenHeight = height; - } - mAlpha = 1.0f; - - GL11 gl = mGL; - gl.glViewport(0, 0, width, height); - gl.glMatrixMode(GL11.GL_PROJECTION); - gl.glLoadIdentity(); - GLU.gluOrtho2D(gl, 0, width, 0, height); - - gl.glMatrixMode(GL11.GL_MODELVIEW); - gl.glLoadIdentity(); - - float matrix[] = mMatrixValues; - Matrix.setIdentityM(matrix, 0); - // to match the graphic coordinate system in android, we flip it vertically. - if (mTargetTexture == null) { - Matrix.translateM(matrix, 0, 0, height, 0); - Matrix.scaleM(matrix, 0, 1, -1, 1); - } - } - - @Override - public void setAlpha(float alpha) { - Utils.assertTrue(alpha >= 0 && alpha <= 1); - mAlpha = alpha; - } - - @Override - public float getAlpha() { - return mAlpha; - } - - @Override - public void multiplyAlpha(float alpha) { - Utils.assertTrue(alpha >= 0 && alpha <= 1); - mAlpha *= alpha; - } - - private static ByteBuffer allocateDirectNativeOrderBuffer(int size) { - return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); - } - - @Override - public void initialize(GL11 gl) { - mGL = gl; - mGLState = new GLState(gl); - // First create an nio buffer, then create a VBO from it. - int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE; - FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer(); - xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0); - - int[] name = new int[1]; - GLId glId = getGLId(); - glId.glGenBuffers(1, name, 0); - mBoxCoords = name[0]; - - gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords); - gl.glBufferData(GL11.GL_ARRAY_BUFFER, - xyBuffer.capacity() * (Float.SIZE / Byte.SIZE), - xyBuffer, GL11.GL_STATIC_DRAW); - - gl.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); - gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); - - // Enable the texture coordinate array for Texture 1 - gl.glClientActiveTexture(GL11.GL_TEXTURE1); - gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); - gl.glClientActiveTexture(GL11.GL_TEXTURE0); - gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); - - // mMatrixValues and mAlpha will be initialized in setSize() - } - - @Override - public void drawRect(float x, float y, float width, float height, GLPaint paint) { - GL11 gl = mGL; - - mGLState.setColorMode(paint.getColor(), mAlpha); - mGLState.setLineWidth(paint.getLineWidth()); - - saveTransform(); - translate(x, y); - scale(width, height, 1); - - gl.glLoadMatrixf(mMatrixValues, 0); - gl.glDrawArrays(GL11.GL_LINE_LOOP, OFFSET_DRAW_RECT, 4); - - restoreTransform(); - mCountDrawLine++; - } - - @Override - public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) { - GL11 gl = mGL; - - mGLState.setColorMode(paint.getColor(), mAlpha); - mGLState.setLineWidth(paint.getLineWidth()); - - saveTransform(); - translate(x1, y1); - scale(x2 - x1, y2 - y1, 1); - - gl.glLoadMatrixf(mMatrixValues, 0); - gl.glDrawArrays(GL11.GL_LINE_STRIP, OFFSET_DRAW_LINE, 2); - - restoreTransform(); - mCountDrawLine++; - } - - @Override - public void fillRect(float x, float y, float width, float height, int color) { - mGLState.setColorMode(color, mAlpha); - GL11 gl = mGL; - - saveTransform(); - translate(x, y); - scale(width, height, 1); - - gl.glLoadMatrixf(mMatrixValues, 0); - gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4); - - restoreTransform(); - mCountFillRect++; - } - - @Override - public void translate(float x, float y, float z) { - Matrix.translateM(mMatrixValues, 0, x, y, z); - } - - // This is a faster version of translate(x, y, z) because - // (1) we knows z = 0, (2) we inline the Matrix.translateM call, - // (3) we unroll the loop - @Override - public void translate(float x, float y) { - float[] m = mMatrixValues; - m[12] += m[0] * x + m[4] * y; - m[13] += m[1] * x + m[5] * y; - m[14] += m[2] * x + m[6] * y; - m[15] += m[3] * x + m[7] * y; - } - - @Override - public void scale(float sx, float sy, float sz) { - Matrix.scaleM(mMatrixValues, 0, sx, sy, sz); - } - - @Override - public void rotate(float angle, float x, float y, float z) { - if (angle == 0) return; - float[] temp = mTempMatrix; - Matrix.setRotateM(temp, 0, angle, x, y, z); - Matrix.multiplyMM(temp, 16, mMatrixValues, 0, temp, 0); - System.arraycopy(temp, 16, mMatrixValues, 0, 16); - } - - @Override - public void multiplyMatrix(float matrix[], int offset) { - float[] temp = mTempMatrix; - Matrix.multiplyMM(temp, 0, mMatrixValues, 0, matrix, offset); - System.arraycopy(temp, 0, mMatrixValues, 0, 16); - } - - private void textureRect(float x, float y, float width, float height) { - GL11 gl = mGL; - - saveTransform(); - translate(x, y); - scale(width, height, 1); - - gl.glLoadMatrixf(mMatrixValues, 0); - gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4); - - restoreTransform(); - mCountTextureRect++; - } - - @Override - public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer, - int uvBuffer, int indexBuffer, int indexCount) { - float alpha = mAlpha; - if (!bindTexture(tex)) return; - - mGLState.setBlendEnabled(mBlendEnabled - && (!tex.isOpaque() || alpha < OPAQUE_ALPHA)); - mGLState.setTextureAlpha(alpha); - - // Reset the texture matrix. We will set our own texture coordinates - // below. - setTextureCoords(0, 0, 1, 1); - - saveTransform(); - translate(x, y); - - mGL.glLoadMatrixf(mMatrixValues, 0); - - mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, xyBuffer); - mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); - - mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, uvBuffer); - mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); - - mGL.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, indexBuffer); - mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP, - indexCount, GL11.GL_UNSIGNED_BYTE, 0); - - mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords); - mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); - mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); - - restoreTransform(); - mCountDrawMesh++; - } - - // Transforms two points by the given matrix m. The result - // {x1', y1', x2', y2'} are stored in mMapPointsBuffer and also returned. - private float[] mapPoints(float m[], int x1, int y1, int x2, int y2) { - float[] r = mMapPointsBuffer; - - // Multiply m and (x1 y1 0 1) to produce (x3 y3 z3 w3). z3 is unused. - float x3 = m[0] * x1 + m[4] * y1 + m[12]; - float y3 = m[1] * x1 + m[5] * y1 + m[13]; - float w3 = m[3] * x1 + m[7] * y1 + m[15]; - r[0] = x3 / w3; - r[1] = y3 / w3; - - // Same for x2 y2. - float x4 = m[0] * x2 + m[4] * y2 + m[12]; - float y4 = m[1] * x2 + m[5] * y2 + m[13]; - float w4 = m[3] * x2 + m[7] * y2 + m[15]; - r[2] = x4 / w4; - r[3] = y4 / w4; - - return r; - } - - private void drawBoundTexture( - BasicTexture texture, int x, int y, int width, int height) { - // Test whether it has been rotated or flipped, if so, glDrawTexiOES - // won't work - if (isMatrixRotatedOrFlipped(mMatrixValues)) { - if (texture.hasBorder()) { - setTextureCoords( - 1.0f / texture.getTextureWidth(), - 1.0f / texture.getTextureHeight(), - (texture.getWidth() - 1.0f) / texture.getTextureWidth(), - (texture.getHeight() - 1.0f) / texture.getTextureHeight()); - } else { - setTextureCoords(0, 0, - (float) texture.getWidth() / texture.getTextureWidth(), - (float) texture.getHeight() / texture.getTextureHeight()); - } - textureRect(x, y, width, height); - } else { - // draw the rect from bottom-left to top-right - float points[] = mapPoints( - mMatrixValues, x, y + height, x + width, y); - x = (int) (points[0] + 0.5f); - y = (int) (points[1] + 0.5f); - width = (int) (points[2] + 0.5f) - x; - height = (int) (points[3] + 0.5f) - y; - if (width > 0 && height > 0) { - ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height); - mCountTextureOES++; - } - } - } - - @Override - public void drawTexture( - BasicTexture texture, int x, int y, int width, int height) { - drawTexture(texture, x, y, width, height, mAlpha); - } - - private void drawTexture(BasicTexture texture, - int x, int y, int width, int height, float alpha) { - if (width <= 0 || height <= 0) return; - - mGLState.setBlendEnabled(mBlendEnabled - && (!texture.isOpaque() || alpha < OPAQUE_ALPHA)); - if (!bindTexture(texture)) return; - mGLState.setTextureAlpha(alpha); - drawBoundTexture(texture, x, y, width, height); - } - - @Override - public void drawTexture(BasicTexture texture, RectF source, RectF target) { - if (target.width() <= 0 || target.height() <= 0) return; - - // Copy the input to avoid changing it. - mDrawTextureSourceRect.set(source); - mDrawTextureTargetRect.set(target); - source = mDrawTextureSourceRect; - target = mDrawTextureTargetRect; - - mGLState.setBlendEnabled(mBlendEnabled - && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA)); - if (!bindTexture(texture)) return; - convertCoordinate(source, target, texture); - setTextureCoords(source); - mGLState.setTextureAlpha(mAlpha); - textureRect(target.left, target.top, target.width(), target.height()); - } - - @Override - public void drawTexture(BasicTexture texture, float[] mTextureTransform, - int x, int y, int w, int h) { - mGLState.setBlendEnabled(mBlendEnabled - && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA)); - if (!bindTexture(texture)) return; - setTextureCoords(mTextureTransform); - mGLState.setTextureAlpha(mAlpha); - textureRect(x, y, w, h); - } - - // This function changes the source coordinate to the texture coordinates. - // It also clips the source and target coordinates if it is beyond the - // bound of the texture. - private static void convertCoordinate(RectF source, RectF target, - BasicTexture texture) { - - int width = texture.getWidth(); - int height = texture.getHeight(); - int texWidth = texture.getTextureWidth(); - int texHeight = texture.getTextureHeight(); - // Convert to texture coordinates - source.left /= texWidth; - source.right /= texWidth; - source.top /= texHeight; - source.bottom /= texHeight; - - // Clip if the rendering range is beyond the bound of the texture. - float xBound = (float) width / texWidth; - if (source.right > xBound) { - target.right = target.left + target.width() * - (xBound - source.left) / source.width(); - source.right = xBound; - } - float yBound = (float) height / texHeight; - if (source.bottom > yBound) { - target.bottom = target.top + target.height() * - (yBound - source.top) / source.height(); - source.bottom = yBound; - } - } - - @Override - public void drawMixed(BasicTexture from, - int toColor, float ratio, int x, int y, int w, int h) { - drawMixed(from, toColor, ratio, x, y, w, h, mAlpha); - } - - private boolean bindTexture(BasicTexture texture) { - if (!texture.onBind(this)) return false; - int target = texture.getTarget(); - mGLState.setTextureTarget(target); - mGL.glBindTexture(target, texture.getId()); - return true; - } - - private void setTextureColor(float r, float g, float b, float alpha) { - float[] color = mTextureColor; - color[0] = r; - color[1] = g; - color[2] = b; - color[3] = alpha; - } - - private void setMixedColor(int toColor, float ratio, float alpha) { - // - // The formula we want: - // alpha * ((1 - ratio) * from + ratio * to) - // - // The formula that GL supports is in the form of: - // combo * from + (1 - combo) * to * scale - // - // So, we have combo = alpha * (1 - ratio) - // and scale = alpha * ratio / (1 - combo) - // - float combo = alpha * (1 - ratio); - float scale = alpha * ratio / (1 - combo); - - // Specify the interpolation factor via the alpha component of - // GL_TEXTURE_ENV_COLORs. - // RGB component are get from toColor and will used as SRC1 - float colorScale = scale * (toColor >>> 24) / (0xff * 0xff); - setTextureColor(((toColor >>> 16) & 0xff) * colorScale, - ((toColor >>> 8) & 0xff) * colorScale, - (toColor & 0xff) * colorScale, combo); - GL11 gl = mGL; - gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0); - - gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE); - gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE); - gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_RGB, GL11.GL_CONSTANT); - gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_RGB, GL11.GL_SRC_COLOR); - gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_ALPHA, GL11.GL_CONSTANT); - gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_ALPHA, GL11.GL_SRC_ALPHA); - - // Wire up the interpolation factor for RGB. - gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT); - gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA); - - // Wire up the interpolation factor for alpha. - gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT); - gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA); - - } - - @Override - public void drawMixed(BasicTexture from, int toColor, float ratio, - RectF source, RectF target) { - if (target.width() <= 0 || target.height() <= 0) return; - - if (ratio <= 0.01f) { - drawTexture(from, source, target); - return; - } else if (ratio >= 1) { - fillRect(target.left, target.top, target.width(), target.height(), toColor); - return; - } - - float alpha = mAlpha; - - // Copy the input to avoid changing it. - mDrawTextureSourceRect.set(source); - mDrawTextureTargetRect.set(target); - source = mDrawTextureSourceRect; - target = mDrawTextureTargetRect; - - mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque() - || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA)); - - if (!bindTexture(from)) return; - - // Interpolate the RGB and alpha values between both textures. - mGLState.setTexEnvMode(GL11.GL_COMBINE); - setMixedColor(toColor, ratio, alpha); - convertCoordinate(source, target, from); - setTextureCoords(source); - textureRect(target.left, target.top, target.width(), target.height()); - mGLState.setTexEnvMode(GL11.GL_REPLACE); - } - - private void drawMixed(BasicTexture from, int toColor, - float ratio, int x, int y, int width, int height, float alpha) { - // change from 0 to 0.01f to prevent getting divided by zero below - if (ratio <= 0.01f) { - drawTexture(from, x, y, width, height, alpha); - return; - } else if (ratio >= 1) { - fillRect(x, y, width, height, toColor); - return; - } - - mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque() - || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA)); - - final GL11 gl = mGL; - if (!bindTexture(from)) return; - - // Interpolate the RGB and alpha values between both textures. - mGLState.setTexEnvMode(GL11.GL_COMBINE); - setMixedColor(toColor, ratio, alpha); - - drawBoundTexture(from, x, y, width, height); - mGLState.setTexEnvMode(GL11.GL_REPLACE); - } - - // TODO: the code only work for 2D should get fixed for 3D or removed - private static final int MSKEW_X = 4; - private static final int MSKEW_Y = 1; - private static final int MSCALE_X = 0; - private static final int MSCALE_Y = 5; - - private static boolean isMatrixRotatedOrFlipped(float matrix[]) { - final float eps = 1e-5f; - return Math.abs(matrix[MSKEW_X]) > eps - || Math.abs(matrix[MSKEW_Y]) > eps - || matrix[MSCALE_X] < -eps - || matrix[MSCALE_Y] > eps; - } - - private static class GLState { - - private final GL11 mGL; - - private int mTexEnvMode = GL11.GL_REPLACE; - private float mTextureAlpha = 1.0f; - private int mTextureTarget = GL11.GL_TEXTURE_2D; - private boolean mBlendEnabled = true; - private float mLineWidth = 1.0f; - private boolean mLineSmooth = false; - - public GLState(GL11 gl) { - mGL = gl; - - // Disable unused state - gl.glDisable(GL11.GL_LIGHTING); - - // Enable used features - gl.glEnable(GL11.GL_DITHER); - - gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); - gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); - gl.glEnable(GL11.GL_TEXTURE_2D); - - gl.glTexEnvf(GL11.GL_TEXTURE_ENV, - GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE); - - // Set the background color - gl.glClearColor(0f, 0f, 0f, 0f); - gl.glClearStencil(0); - - gl.glEnable(GL11.GL_BLEND); - gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); - - // We use 565 or 8888 format, so set the alignment to 2 bytes/pixel. - gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2); - } - - public void setTexEnvMode(int mode) { - if (mTexEnvMode == mode) return; - mTexEnvMode = mode; - mGL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, mode); - } - - public void setLineWidth(float width) { - if (mLineWidth == width) return; - mLineWidth = width; - mGL.glLineWidth(width); - } - - public void setTextureAlpha(float alpha) { - if (mTextureAlpha == alpha) return; - mTextureAlpha = alpha; - if (alpha >= OPAQUE_ALPHA) { - // The alpha is need for those texture without alpha channel - mGL.glColor4f(1, 1, 1, 1); - setTexEnvMode(GL11.GL_REPLACE); - } else { - mGL.glColor4f(alpha, alpha, alpha, alpha); - setTexEnvMode(GL11.GL_MODULATE); - } - } - - public void setColorMode(int color, float alpha) { - setBlendEnabled(!Utils.isOpaque(color) || alpha < OPAQUE_ALPHA); - - // Set mTextureAlpha to an invalid value, so that it will reset - // again in setTextureAlpha(float) later. - mTextureAlpha = -1.0f; - - setTextureTarget(0); - - float prealpha = (color >>> 24) * alpha * 65535f / 255f / 255f; - mGL.glColor4x( - Math.round(((color >> 16) & 0xFF) * prealpha), - Math.round(((color >> 8) & 0xFF) * prealpha), - Math.round((color & 0xFF) * prealpha), - Math.round(255 * prealpha)); - } - - // target is a value like GL_TEXTURE_2D. If target = 0, texturing is disabled. - public void setTextureTarget(int target) { - if (mTextureTarget == target) return; - if (mTextureTarget != 0) { - mGL.glDisable(mTextureTarget); - } - mTextureTarget = target; - if (mTextureTarget != 0) { - mGL.glEnable(mTextureTarget); - } - } - - public void setBlendEnabled(boolean enabled) { - if (mBlendEnabled == enabled) return; - mBlendEnabled = enabled; - if (enabled) { - mGL.glEnable(GL11.GL_BLEND); - } else { - mGL.glDisable(GL11.GL_BLEND); - } - } - } - - @Override - public void clearBuffer(float[] argb) { - if(argb != null && argb.length == 4) { - mGL.glClearColor(argb[1], argb[2], argb[3], argb[0]); - } else { - mGL.glClearColor(0, 0, 0, 1); - } - mGL.glClear(GL10.GL_COLOR_BUFFER_BIT); - } - - @Override - public void clearBuffer() { - clearBuffer(null); - } - - private void setTextureCoords(RectF source) { - setTextureCoords(source.left, source.top, source.right, source.bottom); - } - - private void setTextureCoords(float left, float top, - float right, float bottom) { - mGL.glMatrixMode(GL11.GL_TEXTURE); - mTextureMatrixValues[0] = right - left; - mTextureMatrixValues[5] = bottom - top; - mTextureMatrixValues[10] = 1; - mTextureMatrixValues[12] = left; - mTextureMatrixValues[13] = top; - mTextureMatrixValues[15] = 1; - mGL.glLoadMatrixf(mTextureMatrixValues, 0); - mGL.glMatrixMode(GL11.GL_MODELVIEW); - } - - private void setTextureCoords(float[] mTextureTransform) { - mGL.glMatrixMode(GL11.GL_TEXTURE); - mGL.glLoadMatrixf(mTextureTransform, 0); - mGL.glMatrixMode(GL11.GL_MODELVIEW); - } - - // unloadTexture and deleteBuffer can be called from the finalizer thread, - // so we synchronized on the mUnboundTextures object. - @Override - public boolean unloadTexture(BasicTexture t) { - synchronized (mUnboundTextures) { - if (!t.isLoaded()) return false; - mUnboundTextures.add(t.mId); - return true; - } - } - - @Override - public void deleteBuffer(int bufferId) { - synchronized (mUnboundTextures) { - mDeleteBuffers.add(bufferId); - } - } - - @Override - public void deleteRecycledResources() { - synchronized (mUnboundTextures) { - IntArray ids = mUnboundTextures; - GLId glId = getGLId(); - if (ids.size() > 0) { - glId.glDeleteTextures(mGL, ids.size(), ids.getInternalArray(), 0); - ids.clear(); - } - - ids = mDeleteBuffers; - if (ids.size() > 0) { - glId.glDeleteBuffers(mGL, ids.size(), ids.getInternalArray(), 0); - ids.clear(); - } - } - } - - @Override - public void save() { - save(SAVE_FLAG_ALL); - } - - @Override - public void save(int saveFlags) { - ConfigState config = obtainRestoreConfig(); - - if ((saveFlags & SAVE_FLAG_ALPHA) != 0) { - config.mAlpha = mAlpha; - } else { - config.mAlpha = -1; - } - - if ((saveFlags & SAVE_FLAG_BLEND) != 0) { - config.mBlending = mBlending; - } else { - config.mBlending = null; - } - - if ((saveFlags & SAVE_FLAG_MATRIX) != 0) { - System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16); - } else { - config.mMatrix[0] = Float.NEGATIVE_INFINITY; - } - - mRestoreStack.add(config); - } - - @Override - public void restore() { - if (mRestoreStack.isEmpty()) throw new IllegalStateException(); - ConfigState config = mRestoreStack.remove(mRestoreStack.size() - 1); - config.restore(this); - freeRestoreConfig(config); - } - - private void freeRestoreConfig(ConfigState action) { - action.mNextFree = mRecycledRestoreAction; - mRecycledRestoreAction = action; - } - - private ConfigState obtainRestoreConfig() { - if (mRecycledRestoreAction != null) { - ConfigState result = mRecycledRestoreAction; - mRecycledRestoreAction = result.mNextFree; - return result; - } - return new ConfigState(); - } - - private static class ConfigState { - float mAlpha; - float mMatrix[] = new float[16]; - Blending mBlending; - ConfigState mNextFree; - - public void restore(GLCanvasImpl canvas) { - if (mAlpha >= 0) canvas.setAlpha(mAlpha); - if (mMatrix[0] != Float.NEGATIVE_INFINITY) { - System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16); - } - if (mBlending != null) { - canvas.setBlending(mBlending); - } - } - } - - @Override - public void dumpStatisticsAndClear() { - String line = String.format( - "MESH:%d, TEX_OES:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", - mCountDrawMesh, mCountTextureRect, mCountTextureOES, - mCountFillRect, mCountDrawLine); - mCountDrawMesh = 0; - mCountTextureRect = 0; - mCountTextureOES = 0; - mCountFillRect = 0; - mCountDrawLine = 0; - Log.d(TAG, line); - } - - private void saveTransform() { - System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16); - } - - private void restoreTransform() { - System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16); - } - - private void setRenderTarget(RawTexture texture) { - GL11ExtensionPack gl11ep = (GL11ExtensionPack) mGL; - - if (mTargetTexture == null && texture != null) { - GLId glId = getGLId(); - glId.glGenBuffers(1, mFrameBuffer, 0); - gl11ep.glBindFramebufferOES( - GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFrameBuffer[0]); - } - if (mTargetTexture != null && texture == null) { - gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0); - gl11ep.glDeleteFramebuffersOES(1, mFrameBuffer, 0); - } - - mTargetTexture = texture; - if (texture == null) { - setSize(mScreenWidth, mScreenHeight); - } else { - setSize(texture.getWidth(), texture.getHeight()); - - if (!texture.isLoaded()) texture.prepare(this); - - gl11ep.glFramebufferTexture2DOES( - GL11ExtensionPack.GL_FRAMEBUFFER_OES, - GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES, - GL11.GL_TEXTURE_2D, texture.getId(), 0); - - checkFramebufferStatus(gl11ep); - } - } - - @Override - public void endRenderTarget() { - RawTexture texture = mTargetStack.remove(mTargetStack.size() - 1); - setRenderTarget(texture); - restore(); // restore matrix and alpha - } - - @Override - public void beginRenderTarget(RawTexture texture) { - save(); // save matrix and alpha - mTargetStack.add(mTargetTexture); - setRenderTarget(texture); - } - - private static void checkFramebufferStatus(GL11ExtensionPack gl11ep) { - int status = gl11ep.glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES); - if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES) { - String msg = ""; - switch (status) { - case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES: - msg = "FRAMEBUFFER_FORMATS"; - break; - case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES: - msg = "FRAMEBUFFER_ATTACHMENT"; - break; - case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES: - msg = "FRAMEBUFFER_MISSING_ATTACHMENT"; - break; - case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES: - msg = "FRAMEBUFFER_DRAW_BUFFER"; - break; - case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES: - msg = "FRAMEBUFFER_READ_BUFFER"; - break; - case GL11ExtensionPack.GL_FRAMEBUFFER_UNSUPPORTED_OES: - msg = "FRAMEBUFFER_UNSUPPORTED"; - break; - case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES: - msg = "FRAMEBUFFER_INCOMPLETE_DIMENSIONS"; - break; - } - throw new RuntimeException(msg + ":" + Integer.toHexString(status)); - } - } - - @Override - public void setTextureParameters(BasicTexture texture) { - int width = texture.getWidth(); - int height = texture.getHeight(); - // Define a vertically flipped crop rectangle for OES_draw_texture. - // The four values in sCropRect are: left, bottom, width, and - // height. Negative value of width or height means flip. - sCropRect[0] = 0; - sCropRect[1] = height; - sCropRect[2] = width; - sCropRect[3] = -height; - - // Set texture parameters. - int target = texture.getTarget(); - mGL.glBindTexture(target, texture.getId()); - mGL.glTexParameterfv(target, GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0); - mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE); - mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE); - mGL.glTexParameterf(target, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); - mGL.glTexParameterf(target, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); - } - - @Override - public void initializeTextureSize(BasicTexture texture, int format, int type) { - int target = texture.getTarget(); - mGL.glBindTexture(target, texture.getId()); - int width = texture.getTextureWidth(); - int height = texture.getTextureHeight(); - mGL.glTexImage2D(target, 0, format, width, height, 0, format, type, null); - } - - @Override - public void initializeTexture(BasicTexture texture, Bitmap bitmap) { - int target = texture.getTarget(); - mGL.glBindTexture(target, texture.getId()); - GLUtils.texImage2D(target, 0, bitmap, 0); - } - - @Override - public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap, - int format, int type) { - int target = texture.getTarget(); - mGL.glBindTexture(target, texture.getId()); - GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type); - } - - @Override - public int uploadBuffer(FloatBuffer buf) { - return uploadBuffer(buf, Float.SIZE / Byte.SIZE); - } - - @Override - public int uploadBuffer(ByteBuffer buf) { - return uploadBuffer(buf, 1); - } - - private int uploadBuffer(Buffer buf, int elementSize) { - int[] bufferIds = new int[1]; - GLId glId = getGLId(); - glId.glGenBuffers(bufferIds.length, bufferIds, 0); - int bufferId = bufferIds[0]; - mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, bufferId); - mGL.glBufferData(GL11.GL_ARRAY_BUFFER, buf.capacity() * elementSize, buf, - GL11.GL_STATIC_DRAW); - return bufferId; - } - - @Override - public void setBlending(Blending blending) { - if (mBlending == blending) { - return; - } - Utils.assertTrue(blending == Blending.Additive || blending == Blending.Mix); - mBlending = blending; - int srcFunc = GL11.GL_ONE; - int dstFunc = (blending == Blending.Additive) ? GL11.GL_ONE : GL11.GL_ONE_MINUS_SRC_ALPHA; - mGL.glBlendFunc(srcFunc, dstFunc); - } - - @Override - public void enableStencil() { - mGL.glEnable(GL11.GL_STENCIL_TEST); - } - - @Override - public void disableStencil() { - mGL.glDisable(GL11.GL_STENCIL_TEST); - } - - @Override - public void clearStencilBuffer() { - mGL.glClear(GL11.GL_STENCIL_BUFFER_BIT); - } - - @Override - public void updateStencil(boolean update) { - int passOp = update ? GL11.GL_REPLACE : GL11.GL_KEEP; - mGL.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, passOp); - } - - @Override - public void drawOnlyOutsideStencil(boolean onlyOutside) { - int func = onlyOutside ? GL11.GL_NOTEQUAL : GL11.GL_ALWAYS; - mGL.glStencilFunc(func, 1, 1); - } -} diff --git a/src/com/android/gallery3d/ui/GLES20Canvas.java b/src/com/android/gallery3d/ui/GLES20Canvas.java deleted file mode 100644 index b720a773b..000000000 --- a/src/com/android/gallery3d/ui/GLES20Canvas.java +++ /dev/null @@ -1,1068 +0,0 @@ -/* - * Copyright (C) 2012 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 android.graphics.Bitmap; -import android.graphics.RectF; -import android.opengl.GLES20; -import android.opengl.GLUtils; -import android.opengl.Matrix; -import android.util.Log; - -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.util.IntArray; - -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.Arrays; - -import javax.microedition.khronos.opengles.GL11; -import javax.microedition.khronos.opengles.GL11ExtensionPack; - -public class GLES20Canvas extends GLCanvas implements GLId { - // ************** Constants ********************** - private static final String TAG = GLES20Canvas.class.getSimpleName(); - private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE; - private static final float OPAQUE_ALPHA = 0.95f; - - private static final int COORDS_PER_VERTEX = 2; - private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE; - - private static final int COUNT_FILL_VERTEX = 4; - private static final int COUNT_LINE_VERTEX = 2; - private static final int COUNT_RECT_VERTEX = 4; - private static final int OFFSET_FILL_RECT = 0; - private static final int OFFSET_DRAW_LINE = OFFSET_FILL_RECT + COUNT_FILL_VERTEX; - private static final int OFFSET_DRAW_RECT = OFFSET_DRAW_LINE + COUNT_LINE_VERTEX; - - private static final float[] BOX_COORDINATES = { - 0, 0, // Fill rectangle - 1, 0, - 0, 1, - 1, 1, - 0, 0, // Draw line - 1, 1, - 0, 0, // Draw rectangle outline - 0, 1, - 1, 1, - 1, 0, - }; - - private static final String POSITION_ATTRIBUTE = "aPosition"; - private static final String COLOR_UNIFORM = "uColor"; - private static final String MATRIX_UNIFORM = "uMatrix"; - private static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix"; - private static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler"; - private static final String ALPHA_UNIFORM = "uAlpha"; - private static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate"; - - private static final String DRAW_VERTEX_SHADER = "" - + "uniform mat4 " + MATRIX_UNIFORM + ";\n" - + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n" - + "void main() {\n" - + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n" - + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n" - + "}\n"; - - private static final String DRAW_FRAGMENT_SHADER = "" - + "precision mediump float;\n" - + "uniform vec4 " + COLOR_UNIFORM + ";\n" - + "void main() {\n" - + " gl_FragColor = " + COLOR_UNIFORM + ";\n" - + "}\n"; - - private static final String TEXTURE_VERTEX_SHADER = "" - + "uniform mat4 " + MATRIX_UNIFORM + ";\n" - + "uniform mat4 " + TEXTURE_MATRIX_UNIFORM + ";\n" - + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n" - + "varying vec2 vTextureCoord;\n" - + "void main() {\n" - + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n" - + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n" - + " vTextureCoord = (" + TEXTURE_MATRIX_UNIFORM + " * pos).xy;\n" - + "}\n"; - - private static final String MESH_VERTEX_SHADER = "" - + "uniform mat4 " + MATRIX_UNIFORM + ";\n" - + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n" - + "attribute vec2 " + TEXTURE_COORD_ATTRIBUTE + ";\n" - + "varying vec2 vTextureCoord;\n" - + "void main() {\n" - + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n" - + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n" - + " vTextureCoord = " + TEXTURE_COORD_ATTRIBUTE + ";\n" - + "}\n"; - - private static final String TEXTURE_FRAGMENT_SHADER = "" - + "precision mediump float;\n" - + "varying vec2 vTextureCoord;\n" - + "uniform float " + ALPHA_UNIFORM + ";\n" - + "uniform sampler2D " + TEXTURE_SAMPLER_UNIFORM + ";\n" - + "void main() {\n" - + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n" - + " gl_FragColor.a *= " + ALPHA_UNIFORM + ";\n" - + "}\n"; - - private static final String OES_TEXTURE_FRAGMENT_SHADER = "" - + "#extension GL_OES_EGL_image_external : require\n" - + "precision mediump float;\n" - + "varying vec2 vTextureCoord;\n" - + "uniform float " + ALPHA_UNIFORM + ";\n" - + "uniform samplerExternalOES " + TEXTURE_SAMPLER_UNIFORM + ";\n" - + "void main() {\n" - + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n" - + " gl_FragColor.a *= " + ALPHA_UNIFORM + ";\n" - + "}\n"; - - private static final int INITIAL_RESTORE_STATE_SIZE = 8; - private static final int MATRIX_SIZE = 16; - - // Keep track of restore state - private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE]; - private float[] mAlphas = new float[INITIAL_RESTORE_STATE_SIZE]; - private IntArray mSaveFlags = new IntArray(); - private ArrayList mBlendings = new ArrayList(); - - private int mCurrentAlphaIndex = 0; - private int mCurrentMatrixIndex = 0; - - // Viewport size - private int mWidth; - private int mHeight; - - // Projection matrix - private float[] mProjectionMatrix = new float[MATRIX_SIZE]; - - // Screen size for when we aren't bound to a texture - private int mScreenWidth; - private int mScreenHeight; - - // GL programs - private int mDrawProgram; - private int mTextureProgram; - private int mOesTextureProgram; - private int mMeshProgram; - - // GL buffer containing BOX_COORDINATES - private int mBoxCoordinates; - - // Handle indices -- common - private static final int INDEX_POSITION = 0; - private static final int INDEX_MATRIX = 1; - - // Handle indices -- draw - private static final int INDEX_COLOR = 2; - - // Handle indices -- texture - private static final int INDEX_TEXTURE_MATRIX = 2; - private static final int INDEX_TEXTURE_SAMPLER = 3; - private static final int INDEX_ALPHA = 4; - - // Handle indices -- mesh - private static final int INDEX_TEXTURE_COORD = 2; - - private abstract static class ShaderParameter { - public int handle; - protected final String mName; - - public ShaderParameter(String name) { - mName = name; - } - - public abstract void loadHandle(int program); - } - - private static class UniformShaderParameter extends ShaderParameter { - public UniformShaderParameter(String name) { - super(name); - } - - @Override - public void loadHandle(int program) { - handle = GLES20.glGetUniformLocation(program, mName); - checkError(); - } - } - - private static class AttributeShaderParameter extends ShaderParameter { - public AttributeShaderParameter(String name) { - super(name); - } - - @Override - public void loadHandle(int program) { - handle = GLES20.glGetAttribLocation(program, mName); - checkError(); - } - } - - ShaderParameter[] mDrawParameters = { - new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION - new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX - new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR - }; - ShaderParameter[] mTextureParameters = { - new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION - new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX - new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX - new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER - new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA - }; - ShaderParameter[] mOesTextureParameters = { - new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION - new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX - new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX - new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER - new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA - }; - ShaderParameter[] mMeshParameters = { - new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION - new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX - new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD - new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER - new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA - }; - - private final IntArray mUnboundTextures = new IntArray(); - private final IntArray mDeleteBuffers = new IntArray(); - - // Keep track of statistics for debugging - private int mCountDrawMesh = 0; - private int mCountTextureRect = 0; - private int mCountFillRect = 0; - private int mCountDrawLine = 0; - - private int mNextTextureId = 1; - - // Buffer for framebuffer IDs -- we keep track so we can switch the attached - // texture. - private int[] mFrameBuffer = new int[1]; - - // Bound textures. - private ArrayList mTargetTextures = new ArrayList(); - - // Temporary variables used within calculations - private final float[] mTempMatrix = new float[32]; - private final float[] mTempColor = new float[4]; - private final RectF mTempSourceRect = new RectF(); - private final RectF mTempTargetRect = new RectF(); - private final float[] mTempTextureMatrix = new float[MATRIX_SIZE]; - private final int[] mTempIntArray = new int[1]; - - public GLES20Canvas() { - Matrix.setIdentityM(mTempTextureMatrix, 0); - Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex); - mAlphas[mCurrentAlphaIndex] = 1f; - mTargetTextures.add(null); - } - - @Override - public void initialize(GL11 gl) { - FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES); - mBoxCoordinates = uploadBuffer(boxBuffer); - - int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER); - int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER); - int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER); - int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER); - int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER); - int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, - OES_TEXTURE_FRAGMENT_SHADER); - - mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters); - mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader, - mTextureParameters); - mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader, - mOesTextureParameters); - mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters); - - mBlendings.clear(); - mBlendings.add(null); - setBlending(Blending.Mix); - } - - private static FloatBuffer createBuffer(float[] values) { - // First create an nio buffer, then create a VBO from it. - int size = values.length * FLOAT_SIZE; - FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()) - .asFloatBuffer(); - buffer.put(values, 0, values.length).position(0); - return buffer; - } - - private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) { - int program = GLES20.glCreateProgram(); - checkError(); - if (program == 0) { - throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError()); - } - GLES20.glAttachShader(program, vertexShader); - checkError(); - GLES20.glAttachShader(program, fragmentShader); - checkError(); - GLES20.glLinkProgram(program); - checkError(); - int[] mLinkStatus = mTempIntArray; - GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0); - if (mLinkStatus[0] != GLES20.GL_TRUE) { - Log.e(TAG, "Could not link program: "); - Log.e(TAG, GLES20.glGetProgramInfoLog(program)); - GLES20.glDeleteProgram(program); - program = 0; - } - for (int i = 0; i < params.length; i++) { - params[i].loadHandle(program); - } - return program; - } - - private static int loadShader(int type, String shaderCode) { - // create a vertex shader type (GLES20.GL_VERTEX_SHADER) - // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) - int shader = GLES20.glCreateShader(type); - - // add the source code to the shader and compile it - GLES20.glShaderSource(shader, shaderCode); - checkError(); - GLES20.glCompileShader(shader); - checkError(); - - return shader; - } - - @Override - public void setSize(int width, int height) { - mWidth = width; - mHeight = height; - GLES20.glViewport(0, 0, mWidth, mHeight); - checkError(); - Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex); - Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1); - if (getTargetTexture() == null) { - mScreenWidth = width; - mScreenHeight = height; - Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0); - Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1); - } - } - - @Override - public void clearBuffer() { - GLES20.glClearColor(0f, 0f, 0f, 1f); - checkError(); - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); - checkError(); - } - - @Override - public void clearBuffer(float[] argb) { - GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]); - checkError(); - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); - checkError(); - } - - @Override - public float getAlpha() { - return mAlphas[mCurrentAlphaIndex]; - } - - @Override - public void setAlpha(float alpha) { - mAlphas[mCurrentAlphaIndex] = alpha; - } - - @Override - public void multiplyAlpha(float alpha) { - setAlpha(getAlpha() * alpha); - } - - @Override - public void translate(float x, float y, float z) { - Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z); - } - - // This is a faster version of translate(x, y, z) because - // (1) we knows z = 0, (2) we inline the Matrix.translateM call, - // (3) we unroll the loop - @Override - public void translate(float x, float y) { - int index = mCurrentMatrixIndex; - float[] m = mMatrices; - m[index + 12] += m[index + 0] * x + m[index + 4] * y; - m[index + 13] += m[index + 1] * x + m[index + 5] * y; - m[index + 14] += m[index + 2] * x + m[index + 6] * y; - m[index + 15] += m[index + 3] * x + m[index + 7] * y; - } - - @Override - public void scale(float sx, float sy, float sz) { - Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz); - } - - @Override - public void rotate(float angle, float x, float y, float z) { - if (angle == 0f) { - return; - } - float[] temp = mTempMatrix; - Matrix.setRotateM(temp, 0, angle, x, y, z); - float[] matrix = mMatrices; - int index = mCurrentMatrixIndex; - Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0); - System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE); - } - - @Override - public void multiplyMatrix(float[] matrix, int offset) { - float[] temp = mTempMatrix; - float[] currentMatrix = mMatrices; - int index = mCurrentMatrixIndex; - Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset); - System.arraycopy(temp, 0, currentMatrix, index, 16); - } - - @Override - public void save() { - save(SAVE_FLAG_ALL); - } - - @Override - public void save(int saveFlags) { - boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA; - if (saveAlpha) { - float currentAlpha = getAlpha(); - mCurrentAlphaIndex++; - if (mAlphas.length <= mCurrentAlphaIndex) { - mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2); - } - mAlphas[mCurrentAlphaIndex] = currentAlpha; - } - boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX; - if (saveMatrix) { - int currentIndex = mCurrentMatrixIndex; - mCurrentMatrixIndex += MATRIX_SIZE; - if (mMatrices.length <= mCurrentMatrixIndex) { - mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2); - } - System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE); - } - boolean saveBlending = (saveFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND; - if (saveBlending) { - mBlendings.add(mBlendings.get(mBlendings.size() - 1)); - } - mSaveFlags.add(saveFlags); - } - - @Override - public void restore() { - int restoreFlags = mSaveFlags.removeLast(); - boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA; - if (restoreAlpha) { - mCurrentAlphaIndex--; - } - boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX; - if (restoreMatrix) { - mCurrentMatrixIndex -= MATRIX_SIZE; - } - boolean restoreBlending = (restoreFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND; - if (restoreBlending) { - setBlending(mBlendings.get(mBlendings.size() - 2)); - mBlendings.remove(mBlendings.size() - 1); - } - } - - @Override - public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) { - draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1, - paint); - mCountDrawLine++; - } - - @Override - public void drawRect(float x, float y, float width, float height, GLPaint paint) { - draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint); - mCountDrawLine++; - } - - private void draw(int type, int offset, int count, float x, float y, float width, float height, - GLPaint paint) { - draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth()); - } - - private void draw(int type, int offset, int count, float x, float y, float width, float height, - int color, float lineWidth) { - prepareDraw(offset, color, lineWidth); - draw(mDrawParameters, type, count, x, y, width, height); - } - - private void prepareDraw(int offset, int color, float lineWidth) { - GLES20.glUseProgram(mDrawProgram); - checkError(); - if (lineWidth > 0) { - GLES20.glLineWidth(lineWidth); - checkError(); - } - float[] colorArray = getColor(color); - boolean blendingEnabled = (colorArray[3] < 1f); - enableBlending(blendingEnabled); - if (blendingEnabled) { - GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]); - checkError(); - } - - GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0); - setPosition(mDrawParameters, offset); - checkError(); - } - - private float[] getColor(int color) { - float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha(); - float red = ((color >>> 16) & 0xFF) / 255f * alpha; - float green = ((color >>> 8) & 0xFF) / 255f * alpha; - float blue = (color & 0xFF) / 255f * alpha; - mTempColor[0] = red; - mTempColor[1] = green; - mTempColor[2] = blue; - mTempColor[3] = alpha; - return mTempColor; - } - - private void enableBlending(boolean enableBlending) { - if (enableBlending) { - GLES20.glEnable(GLES20.GL_BLEND); - checkError(); - } else { - GLES20.glDisable(GLES20.GL_BLEND); - checkError(); - } - } - - private void setPosition(ShaderParameter[] params, int offset) { - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates); - checkError(); - GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX, - GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE); - checkError(); - } - - private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width, - float height) { - setMatrix(params, x, y, width, height); - int positionHandle = params[INDEX_POSITION].handle; - GLES20.glEnableVertexAttribArray(positionHandle); - checkError(); - GLES20.glDrawArrays(type, 0, count); - checkError(); - GLES20.glDisableVertexAttribArray(positionHandle); - checkError(); - } - - private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) { - Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f); - Matrix.scaleM(mTempMatrix, 0, width, height, 1f); - Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0); - GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE); - checkError(); - } - - @Override - public void fillRect(float x, float y, float width, float height, int color) { - draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height, - color, 0f); - mCountFillRect++; - } - - @Override - public void drawTexture(BasicTexture texture, int x, int y, int width, int height) { - if (width <= 0 || height <= 0) { - return; - } - copyTextureCoordinates(texture, mTempSourceRect); - mTempTargetRect.set(x, y, x + width, y + height); - convertCoordinate(mTempSourceRect, mTempTargetRect, texture); - drawTextureRect(texture, mTempSourceRect, mTempTargetRect); - } - - private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) { - int left = 0; - int top = 0; - int right = texture.getWidth(); - int bottom = texture.getHeight(); - if (texture.hasBorder()) { - left = 1; - top = 1; - right -= 1; - bottom -= 1; - } - outRect.set(left, top, right, bottom); - } - - @Override - public void drawTexture(BasicTexture texture, RectF source, RectF target) { - if (target.width() <= 0 || target.height() <= 0) { - return; - } - mTempSourceRect.set(source); - mTempTargetRect.set(target); - - convertCoordinate(mTempSourceRect, mTempTargetRect, texture); - drawTextureRect(texture, mTempSourceRect, mTempTargetRect); - } - - @Override - public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w, - int h) { - if (w <= 0 || h <= 0) { - return; - } - mTempTargetRect.set(x, y, x + w, y + h); - drawTextureRect(texture, textureTransform, mTempTargetRect); - } - - private void drawTextureRect(BasicTexture texture, RectF source, RectF target) { - setTextureMatrix(source); - drawTextureRect(texture, mTempTextureMatrix, target); - } - - private void setTextureMatrix(RectF source) { - mTempTextureMatrix[0] = source.width(); - mTempTextureMatrix[5] = source.height(); - mTempTextureMatrix[12] = source.left; - mTempTextureMatrix[13] = source.top; - } - - // This function changes the source coordinate to the texture coordinates. - // It also clips the source and target coordinates if it is beyond the - // bound of the texture. - private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) { - int width = texture.getWidth(); - int height = texture.getHeight(); - int texWidth = texture.getTextureWidth(); - int texHeight = texture.getTextureHeight(); - // Convert to texture coordinates - source.left /= texWidth; - source.right /= texWidth; - source.top /= texHeight; - source.bottom /= texHeight; - - // Clip if the rendering range is beyond the bound of the texture. - float xBound = (float) width / texWidth; - if (source.right > xBound) { - target.right = target.left + target.width() * (xBound - source.left) / source.width(); - source.right = xBound; - } - float yBound = (float) height / texHeight; - if (source.bottom > yBound) { - target.bottom = target.top + target.height() * (yBound - source.top) / source.height(); - source.bottom = yBound; - } - } - - private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) { - ShaderParameter[] params = prepareTexture(texture); - setPosition(params, OFFSET_FILL_RECT); - GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0); - checkError(); - draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top, - target.width(), target.height()); - mCountTextureRect++; - } - - private ShaderParameter[] prepareTexture(BasicTexture texture) { - ShaderParameter[] params; - int program; - if (texture.getTarget() == GLES20.GL_TEXTURE_2D) { - params = mTextureParameters; - program = mTextureProgram; - } else { - params = mOesTextureParameters; - program = mOesTextureProgram; - } - prepareTexture(texture, program, params); - return params; - } - - private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) { - GLES20.glUseProgram(program); - checkError(); - enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - checkError(); - texture.onBind(this); - GLES20.glBindTexture(texture.getTarget(), texture.getId()); - checkError(); - GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0); - checkError(); - GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha()); - checkError(); - } - - @Override - public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer, - int indexBuffer, int indexCount) { - prepareTexture(texture, mMeshProgram, mMeshParameters); - - GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer); - checkError(); - - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer); - checkError(); - int positionHandle = mMeshParameters[INDEX_POSITION].handle; - GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, - VERTEX_STRIDE, 0); - checkError(); - - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer); - checkError(); - int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle; - GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, - false, VERTEX_STRIDE, 0); - checkError(); - - GLES20.glEnableVertexAttribArray(positionHandle); - checkError(); - GLES20.glEnableVertexAttribArray(texCoordHandle); - checkError(); - - setMatrix(mMeshParameters, x, y, 1, 1); - GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_BYTE, 0); - checkError(); - - GLES20.glDisableVertexAttribArray(positionHandle); - checkError(); - GLES20.glDisableVertexAttribArray(texCoordHandle); - checkError(); - GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); - checkError(); - mCountDrawMesh++; - } - - @Override - public void drawMixed(BasicTexture texture, int toColor, float ratio, int x, int y, int w, int h) { - copyTextureCoordinates(texture, mTempSourceRect); - mTempTargetRect.set(x, y, x + w, y + h); - drawMixed(texture, toColor, ratio, mTempSourceRect, mTempTargetRect); - } - - @Override - public void drawMixed(BasicTexture texture, int toColor, float ratio, RectF source, RectF target) { - if (target.width() <= 0 || target.height() <= 0) { - return; - } - save(SAVE_FLAG_ALPHA); - - float currentAlpha = getAlpha(); - float cappedRatio = Math.min(1f, Math.max(0f, ratio)); - - float textureAlpha = (1f - cappedRatio) * currentAlpha; - setAlpha(textureAlpha); - drawTexture(texture, source, target); - - float colorAlpha = cappedRatio * currentAlpha; - setAlpha(colorAlpha); - fillRect(target.left, target.top, target.width(), target.height(), toColor); - - restore(); - } - - @Override - public boolean unloadTexture(BasicTexture texture) { - boolean unload = texture.isLoaded(); - if (unload) { - synchronized (mUnboundTextures) { - mUnboundTextures.add(texture.getId()); - } - } - return unload; - } - - @Override - public void deleteBuffer(int bufferId) { - synchronized (mUnboundTextures) { - mDeleteBuffers.add(bufferId); - } - } - - @Override - public void deleteRecycledResources() { - synchronized (mUnboundTextures) { - IntArray ids = mUnboundTextures; - if (mUnboundTextures.size() > 0) { - glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0); - ids.clear(); - } - - ids = mDeleteBuffers; - if (ids.size() > 0) { - glDeleteBuffers(null, ids.size(), ids.getInternalArray(), 0); - ids.clear(); - } - } - } - - @Override - public void dumpStatisticsAndClear() { - String line = String.format("MESH:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", mCountDrawMesh, - mCountTextureRect, mCountFillRect, mCountDrawLine); - mCountDrawMesh = 0; - mCountTextureRect = 0; - mCountFillRect = 0; - mCountDrawLine = 0; - Log.d(TAG, line); - } - - @Override - public void endRenderTarget() { - RawTexture oldTexture = mTargetTextures.remove(mTargetTextures.size() - 1); - RawTexture texture = getTargetTexture(); - setRenderTarget(oldTexture, texture); - restore(); // restore matrix and alpha - } - - @Override - public void beginRenderTarget(RawTexture texture) { - save(); // save matrix and alpha and blending - RawTexture oldTexture = getTargetTexture(); - mTargetTextures.add(texture); - setRenderTarget(oldTexture, texture); - } - - private RawTexture getTargetTexture() { - return mTargetTextures.get(mTargetTextures.size() - 1); - } - - private void setRenderTarget(BasicTexture oldTexture, RawTexture texture) { - if (oldTexture == null && texture != null) { - GLES20.glGenFramebuffers(1, mFrameBuffer, 0); - checkError(); - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer[0]); - checkError(); - } else if (oldTexture != null && texture == null) { - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - checkError(); - GLES20.glDeleteFramebuffers(1, mFrameBuffer, 0); - checkError(); - } - - if (texture == null) { - setSize(mScreenWidth, mScreenHeight); - } else { - setSize(texture.getWidth(), texture.getHeight()); - - if (!texture.isLoaded()) { - texture.prepare(this); - } - - GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, - texture.getTarget(), texture.getId(), 0); - checkError(); - - checkFramebufferStatus(); - } - } - - private static void checkFramebufferStatus() { - int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); - if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) { - String msg = ""; - switch (status) { - case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - msg = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; - break; - case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: - msg = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS"; - break; - case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - msg = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; - break; - case GLES20.GL_FRAMEBUFFER_UNSUPPORTED: - msg = "GL_FRAMEBUFFER_UNSUPPORTED"; - break; - } - throw new RuntimeException(msg + ":" + Integer.toHexString(status)); - } - } - - @Override - public void setTextureParameters(BasicTexture texture) { - int target = texture.getTarget(); - GLES20.glBindTexture(target, texture.getId()); - checkError(); - GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); - GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); - GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); - GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); - } - - @Override - public void initializeTextureSize(BasicTexture texture, int format, int type) { - int target = texture.getTarget(); - GLES20.glBindTexture(target, texture.getId()); - checkError(); - int width = texture.getTextureWidth(); - int height = texture.getTextureHeight(); - GLES20.glTexImage2D(target, 0, format, width, height, 0, format, type, null); - } - - @Override - public void initializeTexture(BasicTexture texture, Bitmap bitmap) { - int target = texture.getTarget(); - GLES20.glBindTexture(target, texture.getId()); - checkError(); - GLUtils.texImage2D(target, 0, bitmap, 0); - } - - @Override - public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap, - int format, int type) { - int target = texture.getTarget(); - GLES20.glBindTexture(target, texture.getId()); - checkError(); - GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type); - } - - @Override - public int uploadBuffer(FloatBuffer buf) { - return uploadBuffer(buf, FLOAT_SIZE); - } - - @Override - public int uploadBuffer(ByteBuffer buf) { - return uploadBuffer(buf, 1); - } - - private int uploadBuffer(Buffer buffer, int elementSize) { - glGenBuffers(1, mTempIntArray, 0); - checkError(); - int bufferId = mTempIntArray[0]; - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId); - checkError(); - GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer, - GLES20.GL_STATIC_DRAW); - checkError(); - return bufferId; - } - - @Override - public void setBlending(Blending blending) { - Blending currentBlending = mBlendings.get(mBlendings.size() - 1); - if (currentBlending == blending) { - return; // nothing to change - } - mBlendings.set(mBlendings.size() - 1, blending); - int srcFunc = GLES20.GL_ONE; - int dstFunc; - switch (blending) { - case Additive: - dstFunc = GLES20.GL_ONE; - break; - case Mix: - dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA; - break; - default: - Utils.fail("Unknown blend: " + blending); - dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA; - break; - } - GLES20.glBlendFunc(srcFunc, dstFunc); - checkError(); - } - - @Override - public int generateTexture() { - // Can use anything as a lock. No need to create a new object. - synchronized (mTempIntArray) { - return mNextTextureId++; - } - } - - @Override - public void glGenBuffers(int n, int[] buffers, int offset) { - GLES20.glGenBuffers(n, buffers, offset); - checkError(); - } - - @Override - public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) { - GLES20.glDeleteTextures(n, textures, offset); - checkError(); - } - - @Override - public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) { - GLES20.glDeleteBuffers(n, buffers, offset); - checkError(); - } - - @Override - public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) { - GLES20.glDeleteFramebuffers(n, buffers, offset); - checkError(); - } - - @Override - public void enableStencil() { - GLES20.glEnable(GLES20.GL_STENCIL_TEST); - } - - @Override - public void disableStencil() { - GLES20.glDisable(GLES20.GL_STENCIL_TEST); - } - - @Override - public void clearStencilBuffer() { - GLES20.glClear(GLES20.GL_STENCIL_BUFFER_BIT); - } - - @Override - public void updateStencil(boolean update) { - int passOp = update ? GLES20.GL_REPLACE : GLES20.GL_KEEP; - GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, passOp); - } - - @Override - public void drawOnlyOutsideStencil(boolean onlyOutside) { - int func = onlyOutside ? GLES20.GL_NOTEQUAL : GLES20.GL_ALWAYS; - GLES20.glStencilFunc(func, 1, 1); - } - - private static void checkError() { - int error = GLES20.glGetError(); - if (error != 0) { - Throwable t = new Throwable(); - Log.e(TAG, "GL error: " + error, t); - } - } - - @SuppressWarnings("unused") - private static void printMatrix(String message, float[] m, int offset) { - StringBuilder b = new StringBuilder(message); - for (int i = 0; i < MATRIX_SIZE; i++) { - b.append(' '); - if (i % 4 == 0) { - b.append('\n'); - } - b.append(m[offset + i]); - } - Log.v(TAG, b.toString()); - } - -} diff --git a/src/com/android/gallery3d/ui/GLId.java b/src/com/android/gallery3d/ui/GLId.java deleted file mode 100644 index 04977c337..000000000 --- a/src/com/android/gallery3d/ui/GLId.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2012 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 javax.microedition.khronos.opengles.GL11; -import javax.microedition.khronos.opengles.GL11ExtensionPack; - -// This mimics corresponding GL functions. -public interface GLId { - public int generateTexture(); - - public void glGenBuffers(int n, int[] buffers, int offset); - - public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset); - - public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset); - - public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset); -} diff --git a/src/com/android/gallery3d/ui/GLIdImpl.java b/src/com/android/gallery3d/ui/GLIdImpl.java deleted file mode 100644 index 7a0232bf0..000000000 --- a/src/com/android/gallery3d/ui/GLIdImpl.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2012 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 javax.microedition.khronos.opengles.GL11; -import javax.microedition.khronos.opengles.GL11ExtensionPack; - -/** - * Open GL ES 1.1 implementation for generating and destroying texture IDs and - * buffer IDs - */ -public class GLIdImpl implements GLId { - private static int sNextId = 1; - // Mutex for sNextId - private static Object sLock = new Object(); - - @Override - public int generateTexture() { - synchronized (sLock) { - return sNextId++; - } - } - - @Override - public void glGenBuffers(int n, int[] buffers, int offset) { - synchronized (sLock) { - while (n-- > 0) { - buffers[offset + n] = sNextId++; - } - } - } - - @Override - public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) { - synchronized (sLock) { - gl.glDeleteTextures(n, textures, offset); - } - } - - @Override - public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) { - synchronized (sLock) { - gl.glDeleteBuffers(n, buffers, offset); - } - } - - @Override - public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) { - synchronized (sLock) { - gl11ep.glDeleteFramebuffersOES(n, buffers, offset); - } - } - - -} diff --git a/src/com/android/gallery3d/ui/GLPaint.java b/src/com/android/gallery3d/ui/GLPaint.java deleted file mode 100644 index eb75cc51e..000000000 --- a/src/com/android/gallery3d/ui/GLPaint.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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; - - -public class GLPaint { - private float mLineWidth = 1f; - private int mColor = 0; - - public void setColor(int color) { - mColor = color; - } - - public int getColor() { - return mColor; - } - - public void setLineWidth(float width) { - Utils.assertTrue(width >= 0); - mLineWidth = width; - } - - public float getLineWidth() { - return mLineWidth; - } -} diff --git a/src/com/android/gallery3d/ui/GLRoot.java b/src/com/android/gallery3d/ui/GLRoot.java index e406b6703..33a82eaf7 100644 --- a/src/com/android/gallery3d/ui/GLRoot.java +++ b/src/com/android/gallery3d/ui/GLRoot.java @@ -20,6 +20,7 @@ import android.content.Context; import android.graphics.Matrix; import com.android.gallery3d.anim.CanvasAnimation; +import com.android.gallery3d.glrenderer.GLCanvas; public interface GLRoot { diff --git a/src/com/android/gallery3d/ui/GLRootView.java b/src/com/android/gallery3d/ui/GLRootView.java index 6b76999b6..755e10733 100644 --- a/src/com/android/gallery3d/ui/GLRootView.java +++ b/src/com/android/gallery3d/ui/GLRootView.java @@ -33,6 +33,9 @@ import com.android.gallery3d.R; import com.android.gallery3d.anim.CanvasAnimation; import com.android.gallery3d.common.ApiHelper; import com.android.gallery3d.common.Utils; +import com.android.gallery3d.glrenderer.BasicTexture; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.UploadedTexture; import com.android.gallery3d.util.GalleryUtils; import com.android.gallery3d.util.MotionEventHelper; import com.android.gallery3d.util.Profile; diff --git a/src/com/android/gallery3d/ui/GLView.java b/src/com/android/gallery3d/ui/GLView.java index 664012c5a..83de19fe4 100644 --- a/src/com/android/gallery3d/ui/GLView.java +++ b/src/com/android/gallery3d/ui/GLView.java @@ -23,6 +23,7 @@ import android.view.MotionEvent; import com.android.gallery3d.anim.CanvasAnimation; import com.android.gallery3d.anim.StateTransitionAnimation; import com.android.gallery3d.common.Utils; +import com.android.gallery3d.glrenderer.GLCanvas; import java.util.ArrayList; diff --git a/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java b/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java index f57a312cb..4cf3edb34 100644 --- a/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java +++ b/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java @@ -18,6 +18,7 @@ package com.android.gallery3d.ui; import android.opengl.GLSurfaceView.EGLConfigChooser; import com.android.gallery3d.common.ApiHelper; +import com.android.gallery3d.glrenderer.GLCanvas; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; diff --git a/src/com/android/gallery3d/ui/Log.java b/src/com/android/gallery3d/ui/Log.java index 32adc98eb..5570763bb 100644 --- a/src/com/android/gallery3d/ui/Log.java +++ b/src/com/android/gallery3d/ui/Log.java @@ -16,6 +16,7 @@ package com.android.gallery3d.ui; +// TODO: Delete this public class Log { public static int v(String tag, String msg) { return android.util.Log.v(tag, msg); diff --git a/src/com/android/gallery3d/ui/ManageCacheDrawer.java b/src/com/android/gallery3d/ui/ManageCacheDrawer.java index e989af27d..d210bd1f1 100644 --- a/src/com/android/gallery3d/ui/ManageCacheDrawer.java +++ b/src/com/android/gallery3d/ui/ManageCacheDrawer.java @@ -23,6 +23,9 @@ import com.android.gallery3d.app.AbstractGalleryActivity; import com.android.gallery3d.data.DataSourceType; import com.android.gallery3d.data.MediaSet; import com.android.gallery3d.data.Path; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.ResourceTexture; +import com.android.gallery3d.glrenderer.StringTexture; import com.android.gallery3d.ui.AlbumSetSlidingWindow.AlbumSetEntry; public class ManageCacheDrawer extends AlbumSetSlotRenderer { diff --git a/src/com/android/gallery3d/ui/MultiLineTexture.java b/src/com/android/gallery3d/ui/MultiLineTexture.java deleted file mode 100644 index b0c3c2bbf..000000000 --- a/src/com/android/gallery3d/ui/MultiLineTexture.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 android.graphics.Bitmap; -import android.graphics.Canvas; -import android.text.Layout; -import android.text.StaticLayout; -import android.text.TextPaint; - -// MultiLineTexture is a texture shows the content of a specified String. -// -// To create a MultiLineTexture, use the newInstance() method and specify -// the String, the font size, and the color. -class MultiLineTexture extends CanvasTexture { - private final Layout mLayout; - - private MultiLineTexture(Layout layout) { - super(layout.getWidth(), layout.getHeight()); - mLayout = layout; - } - - public static MultiLineTexture newInstance( - String text, int maxWidth, float textSize, int color, - Layout.Alignment alignment) { - TextPaint paint = StringTexture.getDefaultPaint(textSize, color); - Layout layout = new StaticLayout(text, 0, text.length(), paint, - maxWidth, alignment, 1, 0, true, null, 0); - - return new MultiLineTexture(layout); - } - - @Override - protected void onDraw(Canvas canvas, Bitmap backing) { - mLayout.draw(canvas); - } -} diff --git a/src/com/android/gallery3d/ui/NinePatchChunk.java b/src/com/android/gallery3d/ui/NinePatchChunk.java deleted file mode 100644 index 61bf22c33..000000000 --- a/src/com/android/gallery3d/ui/NinePatchChunk.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 android.graphics.Rect; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -// See "frameworks/base/include/utils/ResourceTypes.h" for the format of -// NinePatch chunk. -class NinePatchChunk { - - public static final int NO_COLOR = 0x00000001; - public static final int TRANSPARENT_COLOR = 0x00000000; - - public Rect mPaddings = new Rect(); - - public int mDivX[]; - public int mDivY[]; - public int mColor[]; - - private static void readIntArray(int[] data, ByteBuffer buffer) { - for (int i = 0, n = data.length; i < n; ++i) { - data[i] = buffer.getInt(); - } - } - - private static void checkDivCount(int length) { - if (length == 0 || (length & 0x01) != 0) { - throw new RuntimeException("invalid nine-patch: " + length); - } - } - - public static NinePatchChunk deserialize(byte[] data) { - ByteBuffer byteBuffer = - ByteBuffer.wrap(data).order(ByteOrder.nativeOrder()); - - byte wasSerialized = byteBuffer.get(); - if (wasSerialized == 0) return null; - - NinePatchChunk chunk = new NinePatchChunk(); - chunk.mDivX = new int[byteBuffer.get()]; - chunk.mDivY = new int[byteBuffer.get()]; - chunk.mColor = new int[byteBuffer.get()]; - - checkDivCount(chunk.mDivX.length); - checkDivCount(chunk.mDivY.length); - - // skip 8 bytes - byteBuffer.getInt(); - byteBuffer.getInt(); - - chunk.mPaddings.left = byteBuffer.getInt(); - chunk.mPaddings.right = byteBuffer.getInt(); - chunk.mPaddings.top = byteBuffer.getInt(); - chunk.mPaddings.bottom = byteBuffer.getInt(); - - // skip 4 bytes - byteBuffer.getInt(); - - readIntArray(chunk.mDivX, byteBuffer); - readIntArray(chunk.mDivY, byteBuffer); - readIntArray(chunk.mColor, byteBuffer); - - return chunk; - } -} \ No newline at end of file diff --git a/src/com/android/gallery3d/ui/NinePatchTexture.java b/src/com/android/gallery3d/ui/NinePatchTexture.java deleted file mode 100644 index f5c614554..000000000 --- a/src/com/android/gallery3d/ui/NinePatchTexture.java +++ /dev/null @@ -1,424 +0,0 @@ -/* - * 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 android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Rect; - -import com.android.gallery3d.common.Utils; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - -// NinePatchTexture is a texture backed by a NinePatch resource. -// -// getPaddings() returns paddings specified in the NinePatch. -// getNinePatchChunk() returns the layout data specified in the NinePatch. -// -public class NinePatchTexture extends ResourceTexture { - @SuppressWarnings("unused") - private static final String TAG = "NinePatchTexture"; - private NinePatchChunk mChunk; - private SmallCache mInstanceCache - = new SmallCache(); - - public NinePatchTexture(Context context, int resId) { - super(context, resId); - } - - @Override - protected Bitmap onGetBitmap() { - if (mBitmap != null) return mBitmap; - - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - Bitmap bitmap = BitmapFactory.decodeResource( - mContext.getResources(), mResId, options); - mBitmap = bitmap; - setSize(bitmap.getWidth(), bitmap.getHeight()); - byte[] chunkData = bitmap.getNinePatchChunk(); - mChunk = chunkData == null - ? null - : NinePatchChunk.deserialize(bitmap.getNinePatchChunk()); - if (mChunk == null) { - throw new RuntimeException("invalid nine-patch image: " + mResId); - } - return bitmap; - } - - public Rect getPaddings() { - // get the paddings from nine patch - if (mChunk == null) onGetBitmap(); - return mChunk.mPaddings; - } - - public NinePatchChunk getNinePatchChunk() { - if (mChunk == null) onGetBitmap(); - return mChunk; - } - - // This is a simple cache for a small number of things. Linear search - // is used because the cache is small. It also tries to remove less used - // item when the cache is full by moving the often-used items to the front. - private static class SmallCache { - private static final int CACHE_SIZE = 16; - private static final int CACHE_SIZE_START_MOVE = CACHE_SIZE / 2; - private int[] mKey = new int[CACHE_SIZE]; - private V[] mValue = (V[]) new Object[CACHE_SIZE]; - private int mCount; // number of items in this cache - - // Puts a value into the cache. If the cache is full, also returns - // a less used item, otherwise returns null. - public V put(int key, V value) { - if (mCount == CACHE_SIZE) { - V old = mValue[CACHE_SIZE - 1]; // remove the last item - mKey[CACHE_SIZE - 1] = key; - mValue[CACHE_SIZE - 1] = value; - return old; - } else { - mKey[mCount] = key; - mValue[mCount] = value; - mCount++; - return null; - } - } - - public V get(int key) { - for (int i = 0; i < mCount; i++) { - if (mKey[i] == key) { - // Move the accessed item one position to the front, so it - // will less likely to be removed when cache is full. Only - // do this if the cache is starting to get full. - if (mCount > CACHE_SIZE_START_MOVE && i > 0) { - int tmpKey = mKey[i]; - mKey[i] = mKey[i - 1]; - mKey[i - 1] = tmpKey; - - V tmpValue = mValue[i]; - mValue[i] = mValue[i - 1]; - mValue[i - 1] = tmpValue; - } - return mValue[i]; - } - } - return null; - } - - public void clear() { - for (int i = 0; i < mCount; i++) { - mValue[i] = null; // make sure it's can be garbage-collected. - } - mCount = 0; - } - - public int size() { - return mCount; - } - - public V valueAt(int i) { - return mValue[i]; - } - } - - private NinePatchInstance findInstance(GLCanvas canvas, int w, int h) { - int key = w; - key = (key << 16) | h; - NinePatchInstance instance = mInstanceCache.get(key); - - if (instance == null) { - instance = new NinePatchInstance(this, w, h); - NinePatchInstance removed = mInstanceCache.put(key, instance); - if (removed != null) { - removed.recycle(canvas); - } - } - - return instance; - } - - @Override - public void draw(GLCanvas canvas, int x, int y, int w, int h) { - if (!isLoaded()) { - mInstanceCache.clear(); - } - - if (w != 0 && h != 0) { - findInstance(canvas, w, h).draw(canvas, this, x, y); - } - } - - @Override - public void recycle() { - super.recycle(); - GLCanvas canvas = mCanvasRef; - if (canvas == null) return; - int n = mInstanceCache.size(); - for (int i = 0; i < n; i++) { - NinePatchInstance instance = mInstanceCache.valueAt(i); - instance.recycle(canvas); - } - mInstanceCache.clear(); - } -} - -// This keeps data for a specialization of NinePatchTexture with the size -// (width, height). We pre-compute the coordinates for efficiency. -class NinePatchInstance { - - @SuppressWarnings("unused") - private static final String TAG = "NinePatchInstance"; - - // We need 16 vertices for a normal nine-patch image (the 4x4 vertices) - private static final int VERTEX_BUFFER_SIZE = 16 * 2; - - // We need 22 indices for a normal nine-patch image, plus 2 for each - // transparent region. Current there are at most 1 transparent region. - private static final int INDEX_BUFFER_SIZE = 22 + 2; - - private FloatBuffer mXyBuffer; - private FloatBuffer mUvBuffer; - private ByteBuffer mIndexBuffer; - - // Names for buffer names: xy, uv, index. - private int mXyBufferName = -1; - private int mUvBufferName; - private int mIndexBufferName; - - private int mIdxCount; - - public NinePatchInstance(NinePatchTexture tex, int width, int height) { - NinePatchChunk chunk = tex.getNinePatchChunk(); - - if (width <= 0 || height <= 0) { - throw new RuntimeException("invalid dimension"); - } - - // The code should be easily extended to handle the general cases by - // allocating more space for buffers. But let's just handle the only - // use case. - if (chunk.mDivX.length != 2 || chunk.mDivY.length != 2) { - throw new RuntimeException("unsupported nine patch"); - } - - float divX[] = new float[4]; - float divY[] = new float[4]; - float divU[] = new float[4]; - float divV[] = new float[4]; - - int nx = stretch(divX, divU, chunk.mDivX, tex.getWidth(), width); - int ny = stretch(divY, divV, chunk.mDivY, tex.getHeight(), height); - - prepareVertexData(divX, divY, divU, divV, nx, ny, chunk.mColor); - } - - /** - * Stretches the texture according to the nine-patch rules. It will - * linearly distribute the strechy parts defined in the nine-patch chunk to - * the target area. - * - *
-     *                      source
-     *          /--------------^---------------\
-     *         u0    u1       u2  u3     u4   u5
-     * div ---> |fffff|ssssssss|fff|ssssss|ffff| ---> u
-     *          |    div0    div1 div2   div3  |
-     *          |     |       /   /      /    /
-     *          |     |      /   /     /    /
-     *          |     |     /   /    /    /
-     *          |fffff|ssss|fff|sss|ffff| ---> x
-     *         x0    x1   x2  x3  x4   x5
-     *          \----------v------------/
-     *                  target
-     *
-     * f: fixed segment
-     * s: stretchy segment
-     * 
- * - * @param div the stretch parts defined in nine-patch chunk - * @param source the length of the texture - * @param target the length on the drawing plan - * @param u output, the positions of these dividers in the texture - * coordinate - * @param x output, the corresponding position of these dividers on the - * drawing plan - * @return the number of these dividers. - */ - private static int stretch( - float x[], float u[], int div[], int source, int target) { - int textureSize = Utils.nextPowerOf2(source); - float textureBound = (float) source / textureSize; - - float stretch = 0; - for (int i = 0, n = div.length; i < n; i += 2) { - stretch += div[i + 1] - div[i]; - } - - float remaining = target - source + stretch; - - float lastX = 0; - float lastU = 0; - - x[0] = 0; - u[0] = 0; - for (int i = 0, n = div.length; i < n; i += 2) { - // Make the stretchy segment a little smaller to prevent sampling - // on neighboring fixed segments. - // fixed segment - x[i + 1] = lastX + (div[i] - lastU) + 0.5f; - u[i + 1] = Math.min((div[i] + 0.5f) / textureSize, textureBound); - - // stretchy segment - float partU = div[i + 1] - div[i]; - float partX = remaining * partU / stretch; - remaining -= partX; - stretch -= partU; - - lastX = x[i + 1] + partX; - lastU = div[i + 1]; - x[i + 2] = lastX - 0.5f; - u[i + 2] = Math.min((lastU - 0.5f)/ textureSize, textureBound); - } - // the last fixed segment - x[div.length + 1] = target; - u[div.length + 1] = textureBound; - - // remove segments with length 0. - int last = 0; - for (int i = 1, n = div.length + 2; i < n; ++i) { - if ((x[i] - x[last]) < 1f) continue; - x[++last] = x[i]; - u[last] = u[i]; - } - return last + 1; - } - - private void prepareVertexData(float x[], float y[], float u[], float v[], - int nx, int ny, int[] color) { - /* - * Given a 3x3 nine-patch image, the vertex order is defined as the - * following graph: - * - * (0) (1) (2) (3) - * | /| /| /| - * | / | / | / | - * (4) (5) (6) (7) - * | \ | \ | \ | - * | \| \| \| - * (8) (9) (A) (B) - * | /| /| /| - * | / | / | / | - * (C) (D) (E) (F) - * - * And we draw the triangle strip in the following index order: - * - * index: 04152637B6A5948C9DAEBF - */ - int pntCount = 0; - float xy[] = new float[VERTEX_BUFFER_SIZE]; - float uv[] = new float[VERTEX_BUFFER_SIZE]; - for (int j = 0; j < ny; ++j) { - for (int i = 0; i < nx; ++i) { - int xIndex = (pntCount++) << 1; - int yIndex = xIndex + 1; - xy[xIndex] = x[i]; - xy[yIndex] = y[j]; - uv[xIndex] = u[i]; - uv[yIndex] = v[j]; - } - } - - int idxCount = 1; - boolean isForward = false; - byte index[] = new byte[INDEX_BUFFER_SIZE]; - for (int row = 0; row < ny - 1; row++) { - --idxCount; - isForward = !isForward; - - int start, end, inc; - if (isForward) { - start = 0; - end = nx; - inc = 1; - } else { - start = nx - 1; - end = -1; - inc = -1; - } - - for (int col = start; col != end; col += inc) { - int k = row * nx + col; - if (col != start) { - int colorIdx = row * (nx - 1) + col; - if (isForward) colorIdx--; - if (color[colorIdx] == NinePatchChunk.TRANSPARENT_COLOR) { - index[idxCount] = index[idxCount - 1]; - ++idxCount; - index[idxCount++] = (byte) k; - } - } - - index[idxCount++] = (byte) k; - index[idxCount++] = (byte) (k + nx); - } - } - - mIdxCount = idxCount; - - int size = (pntCount * 2) * (Float.SIZE / Byte.SIZE); - mXyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer(); - mUvBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer(); - mIndexBuffer = allocateDirectNativeOrderBuffer(mIdxCount); - - mXyBuffer.put(xy, 0, pntCount * 2).position(0); - mUvBuffer.put(uv, 0, pntCount * 2).position(0); - mIndexBuffer.put(index, 0, idxCount).position(0); - } - - private static ByteBuffer allocateDirectNativeOrderBuffer(int size) { - return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); - } - - private void prepareBuffers(GLCanvas canvas) { - mXyBufferName = canvas.uploadBuffer(mXyBuffer); - mUvBufferName = canvas.uploadBuffer(mUvBuffer); - mIndexBufferName = canvas.uploadBuffer(mIndexBuffer); - - // These buffers are never used again. - mXyBuffer = null; - mUvBuffer = null; - mIndexBuffer = null; - } - - public void draw(GLCanvas canvas, NinePatchTexture tex, int x, int y) { - if (mXyBufferName == -1) { - prepareBuffers(canvas); - } - canvas.drawMesh(tex, x, y, mXyBufferName, mUvBufferName, mIndexBufferName, mIdxCount); - } - - public void recycle(GLCanvas canvas) { - if (mXyBuffer == null) { - canvas.deleteBuffer(mXyBufferName); - canvas.deleteBuffer(mUvBufferName); - canvas.deleteBuffer(mIndexBufferName); - mXyBufferName = -1; - } - } -} diff --git a/src/com/android/gallery3d/ui/PhotoFallbackEffect.java b/src/com/android/gallery3d/ui/PhotoFallbackEffect.java index 3ca09ab96..4603285a4 100644 --- a/src/com/android/gallery3d/ui/PhotoFallbackEffect.java +++ b/src/com/android/gallery3d/ui/PhotoFallbackEffect.java @@ -23,6 +23,8 @@ import android.view.animation.Interpolator; import com.android.gallery3d.anim.Animation; import com.android.gallery3d.data.Path; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.RawTexture; import com.android.gallery3d.ui.AlbumSlotRenderer.SlotFilter; import java.util.ArrayList; diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java index 58389e4ae..fd2fffafd 100644 --- a/src/com/android/gallery3d/ui/PhotoView.java +++ b/src/com/android/gallery3d/ui/PhotoView.java @@ -35,6 +35,11 @@ import com.android.gallery3d.common.Utils; import com.android.gallery3d.data.MediaItem; import com.android.gallery3d.data.MediaObject; import com.android.gallery3d.data.Path; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.RawTexture; +import com.android.gallery3d.glrenderer.ResourceTexture; +import com.android.gallery3d.glrenderer.StringTexture; +import com.android.gallery3d.glrenderer.Texture; import com.android.gallery3d.util.GalleryUtils; import com.android.gallery3d.util.RangeArray; diff --git a/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java b/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java index 36e7f4b82..f52aa5ff2 100644 --- a/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java +++ b/src/com/android/gallery3d/ui/PreparePageFadeoutTexture.java @@ -3,6 +3,8 @@ package com.android.gallery3d.ui; import android.os.ConditionVariable; import com.android.gallery3d.app.AbstractGalleryActivity; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.RawTexture; import com.android.gallery3d.ui.GLRoot.OnGLIdleListener; public class PreparePageFadeoutTexture implements OnGLIdleListener { diff --git a/src/com/android/gallery3d/ui/ProgressSpinner.java b/src/com/android/gallery3d/ui/ProgressSpinner.java index 4f9381c76..1b31af278 100644 --- a/src/com/android/gallery3d/ui/ProgressSpinner.java +++ b/src/com/android/gallery3d/ui/ProgressSpinner.java @@ -19,6 +19,8 @@ package com.android.gallery3d.ui; import android.content.Context; import com.android.gallery3d.R; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.ResourceTexture; public class ProgressSpinner { private static float ROTATE_SPEED_OUTER = 1080f / 3500f; diff --git a/src/com/android/gallery3d/ui/RawTexture.java b/src/com/android/gallery3d/ui/RawTexture.java deleted file mode 100644 index 53aef9edc..000000000 --- a/src/com/android/gallery3d/ui/RawTexture.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 android.opengl.GLES20; - -import javax.microedition.khronos.opengles.GL11; - -public class RawTexture extends BasicTexture { - private static final String TAG = "RawTexture"; - - private final boolean mOpaque; - - public RawTexture(int width, int height, boolean opaque) { - mOpaque = opaque; - setSize(width, height); - } - - @Override - public boolean isOpaque() { - return mOpaque; - } - - protected void prepare(GLCanvas canvas) { - GLId glId = GLCanvas.getGLId(); - mId = glId.generateTexture(); - canvas.initializeTextureSize(this, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE); - canvas.setTextureParameters(this); - mState = STATE_LOADED; - setAssociatedCanvas(canvas); - } - - @Override - protected boolean onBind(GLCanvas canvas) { - if (isLoaded()) return true; - Log.w(TAG, "lost the content due to context change"); - return false; - } - - @Override - public void yield() { - // we cannot free the texture because we have no backup. - } - - @Override - protected int getTarget() { - return GL11.GL_TEXTURE_2D; - } -} diff --git a/src/com/android/gallery3d/ui/ResourceTexture.java b/src/com/android/gallery3d/ui/ResourceTexture.java deleted file mode 100644 index 1fa9d7014..000000000 --- a/src/com/android/gallery3d/ui/ResourceTexture.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; - -import com.android.gallery3d.common.Utils; - -// ResourceTexture is a texture whose Bitmap is decoded from a resource. -// By default ResourceTexture is not opaque. -public class ResourceTexture extends UploadedTexture { - - protected final Context mContext; - protected final int mResId; - - public ResourceTexture(Context context, int resId) { - mContext = Utils.checkNotNull(context); - mResId = resId; - setOpaque(false); - } - - @Override - protected Bitmap onGetBitmap() { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - return BitmapFactory.decodeResource( - mContext.getResources(), mResId, options); - } - - @Override - protected void onFreeBitmap(Bitmap bitmap) { - if (!inFinalizer()) { - bitmap.recycle(); - } - } -} diff --git a/src/com/android/gallery3d/ui/ScreenNail.java b/src/com/android/gallery3d/ui/ScreenNail.java index 0a16ab850..965bf0b54 100644 --- a/src/com/android/gallery3d/ui/ScreenNail.java +++ b/src/com/android/gallery3d/ui/ScreenNail.java @@ -17,6 +17,8 @@ package com.android.gallery3d.ui; import android.graphics.RectF; +import com.android.gallery3d.glrenderer.GLCanvas; + public interface ScreenNail { public int getWidth(); public int getHeight(); diff --git a/src/com/android/gallery3d/ui/ScrollBarView.java b/src/com/android/gallery3d/ui/ScrollBarView.java index 82d4800cc..34fbcef7a 100644 --- a/src/com/android/gallery3d/ui/ScrollBarView.java +++ b/src/com/android/gallery3d/ui/ScrollBarView.java @@ -20,6 +20,9 @@ import android.content.Context; import android.graphics.Rect; import android.util.TypedValue; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.NinePatchTexture; + public class ScrollBarView extends GLView { @SuppressWarnings("unused") private static final String TAG = "ScrollBarView"; diff --git a/src/com/android/gallery3d/ui/SlideshowView.java b/src/com/android/gallery3d/ui/SlideshowView.java index 7734eb236..b215c6661 100644 --- a/src/com/android/gallery3d/ui/SlideshowView.java +++ b/src/com/android/gallery3d/ui/SlideshowView.java @@ -21,7 +21,9 @@ import android.graphics.PointF; import com.android.gallery3d.anim.CanvasAnimation; import com.android.gallery3d.anim.FloatAnimation; -import com.android.gallery3d.ui.GLCanvas.Blending; +import com.android.gallery3d.glrenderer.BitmapTexture; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.GLCanvas.Blending; import java.util.Random; diff --git a/src/com/android/gallery3d/ui/SlotView.java b/src/com/android/gallery3d/ui/SlotView.java index 15a583e5c..bd0ffdc15 100644 --- a/src/com/android/gallery3d/ui/SlotView.java +++ b/src/com/android/gallery3d/ui/SlotView.java @@ -25,6 +25,7 @@ import android.view.animation.DecelerateInterpolator; import com.android.gallery3d.anim.Animation; import com.android.gallery3d.app.AbstractGalleryActivity; import com.android.gallery3d.common.Utils; +import com.android.gallery3d.glrenderer.GLCanvas; public class SlotView extends GLView { @SuppressWarnings("unused") diff --git a/src/com/android/gallery3d/ui/StringTexture.java b/src/com/android/gallery3d/ui/StringTexture.java deleted file mode 100644 index 97995c8a5..000000000 --- a/src/com/android/gallery3d/ui/StringTexture.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint.FontMetricsInt; -import android.graphics.Typeface; -import android.text.TextPaint; -import android.text.TextUtils; -import android.util.FloatMath; - -// StringTexture is a texture shows the content of a specified String. -// -// To create a StringTexture, use the newInstance() method and specify -// the String, the font size, and the color. -class StringTexture extends CanvasTexture { - private final String mText; - private final TextPaint mPaint; - private final FontMetricsInt mMetrics; - - private StringTexture(String text, TextPaint paint, - FontMetricsInt metrics, int width, int height) { - super(width, height); - mText = text; - mPaint = paint; - mMetrics = metrics; - } - - public static TextPaint getDefaultPaint(float textSize, int color) { - TextPaint paint = new TextPaint(); - paint.setTextSize(textSize); - paint.setAntiAlias(true); - paint.setColor(color); - paint.setShadowLayer(2f, 0f, 0f, Color.BLACK); - return paint; - } - - public static StringTexture newInstance( - String text, float textSize, int color) { - return newInstance(text, getDefaultPaint(textSize, color)); - } - - public static StringTexture newInstance( - String text, float textSize, int color, - float lengthLimit, boolean isBold) { - TextPaint paint = getDefaultPaint(textSize, color); - if (isBold) { - paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); - } - if (lengthLimit > 0) { - text = TextUtils.ellipsize( - text, paint, lengthLimit, TextUtils.TruncateAt.END).toString(); - } - return newInstance(text, paint); - } - - private static StringTexture newInstance(String text, TextPaint paint) { - FontMetricsInt metrics = paint.getFontMetricsInt(); - int width = (int) FloatMath.ceil(paint.measureText(text)); - int height = metrics.bottom - metrics.top; - // The texture size needs to be at least 1x1. - if (width <= 0) width = 1; - if (height <= 0) height = 1; - return new StringTexture(text, paint, metrics, width, height); - } - - @Override - protected void onDraw(Canvas canvas, Bitmap backing) { - canvas.translate(0, -mMetrics.ascent); - canvas.drawText(mText, 0, 0, mPaint); - } -} diff --git a/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java b/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java index ceed71abe..ef8959c9d 100644 --- a/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java +++ b/src/com/android/gallery3d/ui/SurfaceTextureScreenNail.java @@ -21,6 +21,8 @@ import android.graphics.RectF; import android.graphics.SurfaceTexture; import com.android.gallery3d.common.ApiHelper; +import com.android.gallery3d.glrenderer.ExtTexture; +import com.android.gallery3d.glrenderer.GLCanvas; @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) public abstract class SurfaceTextureScreenNail implements ScreenNail, diff --git a/src/com/android/gallery3d/ui/Texture.java b/src/com/android/gallery3d/ui/Texture.java deleted file mode 100644 index 2c426f994..000000000 --- a/src/com/android/gallery3d/ui/Texture.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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; - -// Texture is a rectangular image which can be drawn on GLCanvas. -// The isOpaque() function gives a hint about whether the texture is opaque, -// so the drawing can be done faster. -// -// This is the current texture hierarchy: -// -// Texture -// -- ColorTexture -// -- FadeInTexture -// -- BasicTexture -// -- UploadedTexture -// -- BitmapTexture -// -- Tile -// -- ResourceTexture -// -- NinePatchTexture -// -- CanvasTexture -// -- StringTexture -// -public interface Texture { - public int getWidth(); - public int getHeight(); - public void draw(GLCanvas canvas, int x, int y); - public void draw(GLCanvas canvas, int x, int y, int w, int h); - public boolean isOpaque(); -} diff --git a/src/com/android/gallery3d/ui/TextureUploader.java b/src/com/android/gallery3d/ui/TextureUploader.java deleted file mode 100644 index 9a8c47fc6..000000000 --- a/src/com/android/gallery3d/ui/TextureUploader.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2012 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.ui.GLRoot.OnGLIdleListener; - -import java.util.ArrayDeque; - -public class TextureUploader implements OnGLIdleListener { - private static final int INIT_CAPACITY = 64; - private static final int QUOTA_PER_FRAME = 1; - - private final ArrayDeque mFgTextures = - new ArrayDeque(INIT_CAPACITY); - private final ArrayDeque mBgTextures = - new ArrayDeque(INIT_CAPACITY); - private final GLRoot mGLRoot; - private volatile boolean mIsQueued = false; - - public TextureUploader(GLRoot root) { - mGLRoot = root; - } - - public synchronized void clear() { - while (!mFgTextures.isEmpty()) { - mFgTextures.pop().setIsUploading(false); - } - while (!mBgTextures.isEmpty()) { - mBgTextures.pop().setIsUploading(false); - } - } - - // caller should hold synchronized on "this" - private void queueSelfIfNeed() { - if (mIsQueued) return; - mIsQueued = true; - mGLRoot.addOnGLIdleListener(this); - } - - public synchronized void addBgTexture(UploadedTexture t) { - if (t.isContentValid()) return; - mBgTextures.addLast(t); - t.setIsUploading(true); - queueSelfIfNeed(); - } - - public synchronized void addFgTexture(UploadedTexture t) { - if (t.isContentValid()) return; - mFgTextures.addLast(t); - t.setIsUploading(true); - queueSelfIfNeed(); - } - - private int upload(GLCanvas canvas, ArrayDeque deque, - int uploadQuota, boolean isBackground) { - while (uploadQuota > 0) { - UploadedTexture t; - synchronized (this) { - if (deque.isEmpty()) break; - t = deque.removeFirst(); - t.setIsUploading(false); - if (t.isContentValid()) continue; - - // this has to be protected by the synchronized block - // to prevent the inner bitmap get recycled - t.updateContent(canvas); - } - - // It will took some more time for a texture to be drawn for - // the first time. - // Thus, when scrolling, if a new column appears on screen, - // it may cause a UI jank even these textures are uploaded. - if (isBackground) t.draw(canvas, 0, 0); - --uploadQuota; - } - return uploadQuota; - } - - @Override - public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) { - int uploadQuota = QUOTA_PER_FRAME; - uploadQuota = upload(canvas, mFgTextures, uploadQuota, false); - if (uploadQuota < QUOTA_PER_FRAME) mGLRoot.requestRender(); - upload(canvas, mBgTextures, uploadQuota, true); - synchronized (this) { - mIsQueued = !mFgTextures.isEmpty() || !mBgTextures.isEmpty(); - return mIsQueued; - } - } -} diff --git a/src/com/android/gallery3d/ui/TileImageView.java b/src/com/android/gallery3d/ui/TileImageView.java index 8f26981fe..955105fff 100644 --- a/src/com/android/gallery3d/ui/TileImageView.java +++ b/src/com/android/gallery3d/ui/TileImageView.java @@ -28,6 +28,8 @@ import com.android.gallery3d.common.LongSparseArray; import com.android.gallery3d.common.Utils; import com.android.gallery3d.data.BitmapPool; import com.android.gallery3d.data.DecodeUtils; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.UploadedTexture; import com.android.gallery3d.util.Future; import com.android.gallery3d.util.GalleryUtils; import com.android.gallery3d.util.ThreadPool; diff --git a/src/com/android/gallery3d/ui/TiledScreenNail.java b/src/com/android/gallery3d/ui/TiledScreenNail.java index 74665f584..ab24f5b6c 100644 --- a/src/com/android/gallery3d/ui/TiledScreenNail.java +++ b/src/com/android/gallery3d/ui/TiledScreenNail.java @@ -22,6 +22,8 @@ import android.graphics.RectF; import com.android.gallery3d.common.Utils; import com.android.gallery3d.data.BitmapPool; import com.android.gallery3d.data.MediaItem; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.TiledTexture; // This is a ScreenNail wraps a Bitmap. There are some extra functions: // diff --git a/src/com/android/gallery3d/ui/TiledTexture.java b/src/com/android/gallery3d/ui/TiledTexture.java deleted file mode 100644 index 02bde9f4f..000000000 --- a/src/com/android/gallery3d/ui/TiledTexture.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (C) 2012 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 android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.PorterDuff.Mode; -import android.graphics.PorterDuffXfermode; -import android.graphics.RectF; -import android.os.SystemClock; - -import com.android.gallery3d.ui.GLRoot.OnGLIdleListener; - -import java.util.ArrayDeque; -import java.util.ArrayList; - -// This class is similar to BitmapTexture, except the bitmap is -// split into tiles. By doing so, we may increase the time required to -// upload the whole bitmap but we reduce the time of uploading each tile -// so it make the animation more smooth and prevents jank. -public class TiledTexture implements Texture { - private static final int CONTENT_SIZE = 254; - private static final int BORDER_SIZE = 1; - private static final int TILE_SIZE = CONTENT_SIZE + 2 * BORDER_SIZE; - private static final int INIT_CAPACITY = 8; - - // We are targeting at 60fps, so we have 16ms for each frame. - // In this 16ms, we use about 4~8 ms to upload tiles. - private static final long UPLOAD_TILE_LIMIT = 4; // ms - - private static Tile sFreeTileHead = null; - private static final Object sFreeTileLock = new Object(); - - private static Bitmap sUploadBitmap; - private static Canvas sCanvas; - private static Paint sBitmapPaint; - private static Paint sPaint; - - private int mUploadIndex = 0; - - private final Tile[] mTiles; // Can be modified in different threads. - // Should be protected by "synchronized." - private final int mWidth; - private final int mHeight; - private final RectF mSrcRect = new RectF(); - private final RectF mDestRect = new RectF(); - - public static class Uploader implements OnGLIdleListener { - private final ArrayDeque mTextures = - new ArrayDeque(INIT_CAPACITY); - - private final GLRoot mGlRoot; - private boolean mIsQueued = false; - - public Uploader(GLRoot glRoot) { - mGlRoot = glRoot; - } - - public synchronized void clear() { - mTextures.clear(); - } - - public synchronized void addTexture(TiledTexture t) { - if (t.isReady()) return; - mTextures.addLast(t); - - if (mIsQueued) return; - mIsQueued = true; - mGlRoot.addOnGLIdleListener(this); - } - - @Override - public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) { - ArrayDeque deque = mTextures; - synchronized (this) { - long now = SystemClock.uptimeMillis(); - long dueTime = now + UPLOAD_TILE_LIMIT; - while (now < dueTime && !deque.isEmpty()) { - TiledTexture t = deque.peekFirst(); - if (t.uploadNextTile(canvas)) { - deque.removeFirst(); - mGlRoot.requestRender(); - } - now = SystemClock.uptimeMillis(); - } - mIsQueued = !mTextures.isEmpty(); - - // return true to keep this listener in the queue - return mIsQueued; - } - } - } - - private static class Tile extends UploadedTexture { - public int offsetX; - public int offsetY; - public Bitmap bitmap; - public Tile nextFreeTile; - public int contentWidth; - public int contentHeight; - - @Override - public void setSize(int width, int height) { - contentWidth = width; - contentHeight = height; - mWidth = width + 2 * BORDER_SIZE; - mHeight = height + 2 * BORDER_SIZE; - mTextureWidth = TILE_SIZE; - mTextureHeight = TILE_SIZE; - } - - @Override - protected Bitmap onGetBitmap() { - int x = BORDER_SIZE - offsetX; - int y = BORDER_SIZE - offsetY; - int r = bitmap.getWidth() + x; - int b = bitmap.getHeight() + y; - sCanvas.drawBitmap(bitmap, x, y, sBitmapPaint); - bitmap = null; - - // draw borders if need - if (x > 0) sCanvas.drawLine(x - 1, 0, x - 1, TILE_SIZE, sPaint); - if (y > 0) sCanvas.drawLine(0, y - 1, TILE_SIZE, y - 1, sPaint); - if (r < CONTENT_SIZE) sCanvas.drawLine(r, 0, r, TILE_SIZE, sPaint); - if (b < CONTENT_SIZE) sCanvas.drawLine(0, b, TILE_SIZE, b, sPaint); - - return sUploadBitmap; - } - - @Override - protected void onFreeBitmap(Bitmap bitmap) { - // do nothing - } - } - - private static void freeTile(Tile tile) { - tile.invalidateContent(); - tile.bitmap = null; - synchronized (sFreeTileLock) { - tile.nextFreeTile = sFreeTileHead; - sFreeTileHead = tile; - } - } - - private static Tile obtainTile() { - synchronized (sFreeTileLock) { - Tile result = sFreeTileHead; - if (result == null) return new Tile(); - sFreeTileHead = result.nextFreeTile; - result.nextFreeTile = null; - return result; - } - } - - private boolean uploadNextTile(GLCanvas canvas) { - if (mUploadIndex == mTiles.length) return true; - - synchronized (mTiles) { - Tile next = mTiles[mUploadIndex++]; - - // Make sure tile has not already been recycled by the time - // this is called (race condition in onGLIdle) - if (next.bitmap != null) { - boolean hasBeenLoad = next.isLoaded(); - next.updateContent(canvas); - - // It will take some time for a texture to be drawn for the first - // time. When scrolling, we need to draw several tiles on the screen - // at the same time. It may cause a UI jank even these textures has - // been uploaded. - if (!hasBeenLoad) next.draw(canvas, 0, 0); - } - } - return mUploadIndex == mTiles.length; - } - - public TiledTexture(Bitmap bitmap) { - mWidth = bitmap.getWidth(); - mHeight = bitmap.getHeight(); - ArrayList list = new ArrayList(); - - for (int x = 0, w = mWidth; x < w; x += CONTENT_SIZE) { - for (int y = 0, h = mHeight; y < h; y += CONTENT_SIZE) { - Tile tile = obtainTile(); - tile.offsetX = x; - tile.offsetY = y; - tile.bitmap = bitmap; - tile.setSize( - Math.min(CONTENT_SIZE, mWidth - x), - Math.min(CONTENT_SIZE, mHeight - y)); - list.add(tile); - } - } - mTiles = list.toArray(new Tile[list.size()]); - } - - public boolean isReady() { - return mUploadIndex == mTiles.length; - } - - // Can be called in UI thread. - public void recycle() { - synchronized (mTiles) { - for (int i = 0, n = mTiles.length; i < n; ++i) { - freeTile(mTiles[i]); - } - } - } - - public static void freeResources() { - sUploadBitmap = null; - sCanvas = null; - sBitmapPaint = null; - sPaint = null; - } - - public static void prepareResources() { - sUploadBitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Config.ARGB_8888); - sCanvas = new Canvas(sUploadBitmap); - sBitmapPaint = new Paint(Paint.FILTER_BITMAP_FLAG); - sBitmapPaint.setXfermode(new PorterDuffXfermode(Mode.SRC)); - sPaint = new Paint(); - sPaint.setXfermode(new PorterDuffXfermode(Mode.SRC)); - sPaint.setColor(Color.TRANSPARENT); - } - - // We want to draw the "source" on the "target". - // This method is to find the "output" rectangle which is - // the corresponding area of the "src". - // (x,y) target - // (x0,y0) source +---------------+ - // +----------+ | | - // | src | | output | - // | +--+ | linear map | +----+ | - // | +--+ | ----------> | | | | - // | | by (scaleX, scaleY) | +----+ | - // +----------+ | | - // Texture +---------------+ - // Canvas - private static void mapRect(RectF output, - RectF src, float x0, float y0, float x, float y, float scaleX, - float scaleY) { - output.set(x + (src.left - x0) * scaleX, - y + (src.top - y0) * scaleY, - x + (src.right - x0) * scaleX, - y + (src.bottom - y0) * scaleY); - } - - // Draws a mixed color of this texture and a specified color onto the - // a rectangle. The used color is: from * (1 - ratio) + to * ratio. - public void drawMixed(GLCanvas canvas, int color, float ratio, - int x, int y, int width, int height) { - RectF src = mSrcRect; - RectF dest = mDestRect; - float scaleX = (float) width / mWidth; - float scaleY = (float) height / mHeight; - synchronized (mTiles) { - for (int i = 0, n = mTiles.length; i < n; ++i) { - Tile t = mTiles[i]; - src.set(0, 0, t.contentWidth, t.contentHeight); - src.offset(t.offsetX, t.offsetY); - mapRect(dest, src, 0, 0, x, y, scaleX, scaleY); - src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY); - canvas.drawMixed(t, color, ratio, mSrcRect, mDestRect); - } - } - } - - // Draws the texture on to the specified rectangle. - @Override - public void draw(GLCanvas canvas, int x, int y, int width, int height) { - RectF src = mSrcRect; - RectF dest = mDestRect; - float scaleX = (float) width / mWidth; - float scaleY = (float) height / mHeight; - synchronized (mTiles) { - for (int i = 0, n = mTiles.length; i < n; ++i) { - Tile t = mTiles[i]; - src.set(0, 0, t.contentWidth, t.contentHeight); - src.offset(t.offsetX, t.offsetY); - mapRect(dest, src, 0, 0, x, y, scaleX, scaleY); - src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY); - canvas.drawTexture(t, mSrcRect, mDestRect); - } - } - } - - // Draws a sub region of this texture on to the specified rectangle. - public void draw(GLCanvas canvas, RectF source, RectF target) { - RectF src = mSrcRect; - RectF dest = mDestRect; - float x0 = source.left; - float y0 = source.top; - float x = target.left; - float y = target.top; - float scaleX = target.width() / source.width(); - float scaleY = target.height() / source.height(); - - synchronized (mTiles) { - for (int i = 0, n = mTiles.length; i < n; ++i) { - Tile t = mTiles[i]; - src.set(0, 0, t.contentWidth, t.contentHeight); - src.offset(t.offsetX, t.offsetY); - if (!src.intersect(source)) continue; - mapRect(dest, src, x0, y0, x, y, scaleX, scaleY); - src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY); - canvas.drawTexture(t, src, dest); - } - } - } - - @Override - public int getWidth() { - return mWidth; - } - - @Override - public int getHeight() { - return mHeight; - } - - @Override - public void draw(GLCanvas canvas, int x, int y) { - draw(canvas, x, y, mWidth, mHeight); - } - - @Override - public boolean isOpaque() { - return false; - } -} diff --git a/src/com/android/gallery3d/ui/UndoBarView.java b/src/com/android/gallery3d/ui/UndoBarView.java index 8c9836deb..42f12ae72 100644 --- a/src/com/android/gallery3d/ui/UndoBarView.java +++ b/src/com/android/gallery3d/ui/UndoBarView.java @@ -21,6 +21,10 @@ import android.view.MotionEvent; import com.android.gallery3d.R; import com.android.gallery3d.common.Utils; +import com.android.gallery3d.glrenderer.GLCanvas; +import com.android.gallery3d.glrenderer.NinePatchTexture; +import com.android.gallery3d.glrenderer.ResourceTexture; +import com.android.gallery3d.glrenderer.StringTexture; import com.android.gallery3d.util.GalleryUtils; public class UndoBarView extends GLView { diff --git a/src/com/android/gallery3d/ui/UploadedTexture.java b/src/com/android/gallery3d/ui/UploadedTexture.java deleted file mode 100644 index 470ee6a98..000000000 --- a/src/com/android/gallery3d/ui/UploadedTexture.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * 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 android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.opengl.GLUtils; - -import com.android.gallery3d.common.Utils; - -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(). -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 sBorderLines = - new HashMap(); - 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() { - 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; - 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(); - - Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight); - - // Upload the bitmap to a new texture. - mId = GLCanvas.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(); - } -} -- cgit v1.2.3