Move some GL codes from the Camera project and add some interface classes.
Change-Id: Idc66812aef128e036cff9ee7ee874717b4a85197
diff --git a/new3d/src/com/android/gallery3d/data/MediaItem.java b/new3d/src/com/android/gallery3d/data/MediaItem.java
new file mode 100644
index 0000000..13f3166
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/data/MediaItem.java
@@ -0,0 +1,22 @@
+package com.android.gallery3d.data;
+
+import android.graphics.Bitmap;
+import android.net.Uri;
+
+public interface MediaItem {
+ public static final int TYPE_FULL_IMAGE = 0;
+ public static final int TYPE_SCREEN_NAIL = 1;
+ public static final int TYPE_THUMBNAIL = 2;
+
+ public interface MediaItemListener {
+ public void onImageReady(MediaItem item, int type, Bitmap bitmap);
+ }
+
+ public Uri getMediaUri();
+
+ public String getTitle();
+
+ public Bitmap getImage(int type);
+
+ public void setListener(MediaItemListener listener);
+}
diff --git a/new3d/src/com/android/gallery3d/data/MediaSet.java b/new3d/src/com/android/gallery3d/data/MediaSet.java
new file mode 100644
index 0000000..4a3df37
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/data/MediaSet.java
@@ -0,0 +1,22 @@
+package com.android.gallery3d.data;
+
+public interface MediaSet {
+
+ public interface MediaSetListener {
+ public void onContentChanged();
+ }
+
+ public int getSubMediaSetCount();
+
+ public MediaSet getSubMediaSet(int index);
+
+ public int getMediaCount();
+
+ public MediaItem getMediaItem(int index);
+
+ public int getTotalMediaItemCount();
+
+ public String getTitle();
+
+ public MediaItem[] getCoverMediaItems();
+}
diff --git a/new3d/src/com/android/gallery3d/ui/BasicTexture.java b/new3d/src/com/android/gallery3d/ui/BasicTexture.java
new file mode 100644
index 0000000..a438bff
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/BasicTexture.java
@@ -0,0 +1,77 @@
+package com.android.gallery3d.ui;
+
+import javax.microedition.khronos.opengles.GL11;
+
+abstract class BasicTexture implements Texture {
+
+ protected static final int UNSPECIFIED = -1;
+
+ public static final int STATE_UNLOADED = 0;
+ public static final int STATE_LOADED = 1;
+ public static final int STATE_ERROR = -1;
+
+ protected GL11 mGL;
+
+ protected int mId;
+ protected int mState;
+
+ protected int mWidth = UNSPECIFIED;
+ protected int mHeight = UNSPECIFIED;
+
+ protected int mTextureWidth;
+ protected int mTextureHeight;
+
+ protected BasicTexture(GL11 gl, int id, int state) {
+ mGL = gl;
+ mId = id;
+ mState = state;
+ }
+
+ protected BasicTexture() {
+ this(null, 0, STATE_UNLOADED);
+ }
+
+ protected void setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ }
+
+ /**
+ * Sets the size of the texture. Due to the limit of OpenGL, the texture
+ * size must be of power of 2, the size of the content may not be the size
+ * of the texture.
+ */
+ protected void setTextureSize(int width, int height) {
+ mTextureWidth = width;
+ mTextureHeight = height;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ public void deleteFromGL() {
+ if (mState == STATE_LOADED) {
+ mGL.glDeleteTextures(1, new int[]{mId}, 0);
+ }
+ mState = STATE_UNLOADED;
+ }
+
+ public void draw(GLRootView root, int x, int y) {
+ root.drawTexture(this, x, y, mWidth, mHeight);
+ }
+
+ public void draw(GLRootView root, int x, int y, int w, int h) {
+ root.drawTexture(this, x, y, w, h);
+ }
+
+ abstract protected boolean bind(GLRootView root, GL11 gl);
+}
diff --git a/new3d/src/com/android/gallery3d/ui/BitmapTexture.java b/new3d/src/com/android/gallery3d/ui/BitmapTexture.java
new file mode 100644
index 0000000..f455a78
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/BitmapTexture.java
@@ -0,0 +1,109 @@
+package com.android.gallery3d.ui;
+
+import android.graphics.Bitmap;
+import android.opengl.GLUtils;
+
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11Ext;
+
+abstract class BitmapTexture extends BasicTexture {
+
+ @SuppressWarnings("unused")
+ private static final String TAG = "Texture";
+
+ protected BitmapTexture() {
+ super(null, 0, STATE_UNLOADED);
+ }
+
+ @Override
+ public int getWidth() {
+ if (mWidth == UNSPECIFIED) getBitmap();
+ return mWidth;
+ }
+
+ @Override
+ public int getHeight() {
+ if (mWidth == UNSPECIFIED) getBitmap();
+ return mHeight;
+ }
+
+ protected abstract Bitmap getBitmap();
+
+ protected abstract void freeBitmap(Bitmap bitmap);
+
+ private void uploadToGL(GL11 gl) throws GLOutOfMemoryException {
+ Bitmap bitmap = getBitmap();
+ int glError = GL11.GL_NO_ERROR;
+ if (bitmap != null) {
+ int[] textureId = new int[1];
+ try {
+ // Define a vertically flipped crop rectangle for
+ // OES_draw_texture.
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+ int[] cropRect = {0, height, width, -height};
+
+ // Upload the bitmap to a new texture.
+ gl.glGenTextures(1, textureId, 0);
+ gl.glBindTexture(GL11.GL_TEXTURE_2D, textureId[0]);
+ gl.glTexParameteriv(GL11.GL_TEXTURE_2D,
+ GL11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, 0);
+ gl.glTexParameteri(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
+ gl.glTexParameteri(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
+ gl.glTexParameterf(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
+ gl.glTexParameterf(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+
+ int widthExt = Util.nextPowerOf2(width);
+ int heightExt = Util.nextPowerOf2(height);
+ int format = GLUtils.getInternalFormat(bitmap);
+ int type = GLUtils.getType(bitmap);
+
+ mTextureWidth = widthExt;
+ mTextureHeight = heightExt;
+ gl.glTexImage2D(GL11.GL_TEXTURE_2D, 0, format,
+ widthExt, heightExt, 0, format, type, null);
+ GLUtils.texSubImage2D(
+ GL11.GL_TEXTURE_2D, 0, 0, 0, bitmap, format, type);
+ } finally {
+ freeBitmap(bitmap);
+ }
+ if (glError == GL11.GL_OUT_OF_MEMORY) {
+ throw new GLOutOfMemoryException();
+ }
+ if (glError != GL11.GL_NO_ERROR) {
+ mId = 0;
+ mState = STATE_UNLOADED;
+ throw new RuntimeException(
+ "Texture upload fail, glError " + glError);
+ } else {
+ // Update texture state.
+ mGL = gl;
+ mId = textureId[0];
+ mState = BitmapTexture.STATE_LOADED;
+ }
+ } else {
+ mState = STATE_ERROR;
+ throw new RuntimeException("Texture load fail, no bitmap");
+ }
+ }
+
+ @Override
+ protected boolean bind(GLRootView root, GL11 gl) {
+ if (mState == BitmapTexture.STATE_UNLOADED || mGL != gl) {
+ mState = BitmapTexture.STATE_UNLOADED;
+ try {
+ uploadToGL(gl);
+ } catch (GLOutOfMemoryException e) {
+ root.handleLowMemory();
+ return false;
+ }
+ } else {
+ gl.glBindTexture(GL11.GL_TEXTURE_2D, getId());
+ }
+ return true;
+ }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/CameraEGLConfigChooser.java b/new3d/src/com/android/gallery3d/ui/CameraEGLConfigChooser.java
new file mode 100644
index 0000000..355a248
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/CameraEGLConfigChooser.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009 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.GLSurfaceView.EGLConfigChooser;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLDisplay;
+
+/*
+ * The code is copied/adapted from
+ * <code>android.opengl.GLSurfaceView.BaseConfigChooser</code>. Here we try to
+ * choose a configuration that support RGBA_8888 format and if possible,
+ * with stencil buffer, but is not required.
+ */
+class CameraEGLConfigChooser implements EGLConfigChooser {
+
+ private static final int COLOR_BITS = 8;
+
+ private int mStencilBits;
+
+ private final int mConfigSpec[] = new int[] {
+ EGL10.EGL_RED_SIZE, COLOR_BITS,
+ EGL10.EGL_GREEN_SIZE, COLOR_BITS,
+ EGL10.EGL_BLUE_SIZE, COLOR_BITS,
+ EGL10.EGL_ALPHA_SIZE, COLOR_BITS,
+ EGL10.EGL_NONE
+ };
+
+ public int getStencilBits() {
+ return mStencilBits;
+ }
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ int[] numConfig = new int[1];
+ if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, numConfig)) {
+ throw new RuntimeException("eglChooseConfig failed");
+ }
+
+ if (numConfig[0] <= 0) {
+ throw new RuntimeException("No configs match configSpec");
+ }
+
+ EGLConfig[] configs = new EGLConfig[numConfig[0]];
+ if (!egl.eglChooseConfig(display,
+ mConfigSpec, configs, configs.length, numConfig)) {
+ throw new RuntimeException();
+ }
+
+ return chooseConfig(egl, display, configs);
+ }
+
+ private EGLConfig chooseConfig(
+ EGL10 egl, EGLDisplay display, EGLConfig configs[]) {
+
+ EGLConfig result = null;
+ int minStencil = Integer.MAX_VALUE;
+ int value[] = new int[1];
+
+ // Because we need only one bit of stencil, try to choose a config that
+ // has stencil support but with smallest number of stencil bits. If
+ // none is found, choose any one.
+ for (int i = 0, n = configs.length; i < n; ++i) {
+ if (egl.eglGetConfigAttrib(
+ display, configs[i], EGL10.EGL_STENCIL_SIZE, value)) {
+ if (value[0] == 0) continue;
+ if (value[0] < minStencil) {
+ minStencil = value[0];
+ result = configs[i];
+ }
+ } else {
+ throw new RuntimeException(
+ "eglGetConfigAttrib error: " + egl.eglGetError());
+ }
+ }
+ if (result == null) result = configs[0];
+ egl.eglGetConfigAttrib(
+ display, result, EGL10.EGL_STENCIL_SIZE, value);
+ mStencilBits = value[0];
+ return result;
+ }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/CanvasTexture.java b/new3d/src/com/android/gallery3d/ui/CanvasTexture.java
new file mode 100644
index 0000000..ab1829b
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/CanvasTexture.java
@@ -0,0 +1,45 @@
+/*
+ * 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.Bitmap.Config;
+
+/** Using a canvas to draw the texture */
+abstract class CanvasTexture extends BitmapTexture {
+ protected Canvas mCanvas;
+
+ public CanvasTexture(int width, int height) {
+ setSize(width, height);
+ }
+
+ @Override
+ protected Bitmap getBitmap() {
+ Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888);
+ mCanvas = new Canvas(bitmap);
+ onDraw(mCanvas, bitmap);
+ return bitmap;
+ }
+
+ @Override
+ protected void freeBitmap(Bitmap bitmap) {
+ bitmap.recycle();
+ }
+
+ abstract protected void onDraw(Canvas canvas, Bitmap backing);
+}
diff --git a/new3d/src/com/android/gallery3d/ui/ColorTexture.java b/new3d/src/com/android/gallery3d/ui/ColorTexture.java
new file mode 100644
index 0000000..a037685
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/ColorTexture.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+class ColorTexture implements Texture {
+
+ private int mColor;
+
+ public ColorTexture(int color) {
+ mColor = color;
+ }
+
+ public void draw(GLRootView root, int x, int y) {
+ }
+
+ public void draw(GLRootView root, int x, int y, int w, int h) {
+ root.drawColor(x, y, w, h, mColor);
+ }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/DisplayItemPanel.java b/new3d/src/com/android/gallery3d/ui/DisplayItemPanel.java
new file mode 100644
index 0000000..1b25fc9
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/DisplayItemPanel.java
@@ -0,0 +1,5 @@
+package com.android.gallery3d.ui;
+
+public class DisplayItemPanel {
+
+}
diff --git a/new3d/src/com/android/gallery3d/ui/GLOutOfMemoryException.java b/new3d/src/com/android/gallery3d/ui/GLOutOfMemoryException.java
new file mode 100644
index 0000000..12d0ec5
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/GLOutOfMemoryException.java
@@ -0,0 +1,20 @@
+/*
+ * 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;
+
+public class GLOutOfMemoryException extends Exception {
+}
diff --git a/new3d/src/com/android/gallery3d/ui/GLRootView.java b/new3d/src/com/android/gallery3d/ui/GLRootView.java
new file mode 100644
index 0000000..9fb4f7e
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/GLRootView.java
@@ -0,0 +1,729 @@
+/*
+ * 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.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.opengl.GLSurfaceView;
+import android.opengl.GLU;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Stack;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11Ext;
+
+// The root component of all <code>GLView</code>s. The rendering is done in GL
+// thread while the event handling is done in the main thread. To synchronize
+// the two threads, the entry points of this package need to synchronize on the
+// <code>GLRootView</code> instance unless it can be proved that the rendering
+// thread won't access the same thing as the method. The entry points include:
+// (1) The public methods of HeadUpDisplay
+// (2) The public methods of CameraHeadUpDisplay
+// (3) The overridden methods in GLRootView.
+public class GLRootView extends GLSurfaceView
+ implements GLSurfaceView.Renderer {
+ private static final String TAG = "GLRootView";
+
+ private final boolean ENABLE_FPS_TEST = false;
+ private int mFrameCount = 0;
+ private long mFrameCountingStart = 0;
+
+ // 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
+ private static final int INDEX_BUFFER_SIZE = 22;
+
+ private static final int FLAG_INITIALIZED = 1;
+ private static final int FLAG_NEED_LAYOUT = 2;
+
+ private static boolean mTexture2DEnabled;
+
+ private static float sPixelDensity = -1f;
+
+ private GL11 mGL;
+ private GLView mContentView;
+ private DisplayMetrics mDisplayMetrics;
+
+ private final ArrayList<Animation> mAnimations = new ArrayList<Animation>();
+
+ private final Stack<Transformation> mFreeTransform =
+ new Stack<Transformation>();
+
+ private final Transformation mTransformation = new Transformation();
+ private final Stack<Transformation> mTransformStack =
+ new Stack<Transformation>();
+
+ private float mLastAlpha = mTransformation.getAlpha();
+
+ private final float mMatrixValues[] = new float[16];
+
+ private final float mUvBuffer[] = new float[VERTEX_BUFFER_SIZE];
+ private final float mXyBuffer[] = new float[VERTEX_BUFFER_SIZE];
+ private final byte mIndexBuffer[] = new byte[INDEX_BUFFER_SIZE];
+
+ private int mNinePatchX[] = new int[4];
+ private int mNinePatchY[] = new int[4];
+ private float mNinePatchU[] = new float[4];
+ private float mNinePatchV[] = new float[4];
+
+ private ByteBuffer mXyPointer;
+ private ByteBuffer mUvPointer;
+ private ByteBuffer mIndexPointer;
+
+ private int mFlags = FLAG_NEED_LAYOUT;
+ private long mAnimationTime;
+
+ private CameraEGLConfigChooser mEglConfigChooser = new CameraEGLConfigChooser();
+
+ public GLRootView(Context context) {
+ this(context, null);
+ }
+
+ public GLRootView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initialize();
+ }
+
+ void registerLaunchedAnimation(Animation animation) {
+ // Register the newly launched animation so that we can set the start
+ // time more precisely. (Usually, it takes much longer for the first
+ // rendering, so we set the animation start time as the time we
+ // complete rendering)
+ mAnimations.add(animation);
+ }
+
+ public long currentAnimationTimeMillis() {
+ return mAnimationTime;
+ }
+
+ public synchronized static float dpToPixel(Context context, float dp) {
+ if (sPixelDensity < 0) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ ((Activity) context).getWindowManager()
+ .getDefaultDisplay().getMetrics(metrics);
+ sPixelDensity = metrics.density;
+ }
+ return sPixelDensity * dp;
+ }
+
+ public static int dpToPixel(Context context, int dp) {
+ return (int)(dpToPixel(context, (float) dp) + .5f);
+ }
+
+ public Transformation obtainTransformation() {
+ if (!mFreeTransform.isEmpty()) {
+ Transformation t = mFreeTransform.pop();
+ t.clear();
+ return t;
+ }
+ return new Transformation();
+ }
+
+ public void freeTransformation(Transformation freeTransformation) {
+ mFreeTransform.push(freeTransformation);
+ }
+
+ public Transformation getTransformation() {
+ return mTransformation;
+ }
+
+ public Transformation pushTransform() {
+ Transformation trans = obtainTransformation();
+ trans.set(mTransformation);
+ mTransformStack.push(trans);
+ return mTransformation;
+ }
+
+ public void popTransform() {
+ Transformation trans = mTransformStack.pop();
+ mTransformation.set(trans);
+ freeTransformation(trans);
+ }
+
+ public CameraEGLConfigChooser getEGLConfigChooser() {
+ return mEglConfigChooser;
+ }
+
+ private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
+ return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
+ }
+
+ private void initialize() {
+ mFlags |= FLAG_INITIALIZED;
+ setEGLConfigChooser(mEglConfigChooser);
+ getHolder().setFormat(PixelFormat.TRANSLUCENT);
+ setZOrderOnTop(true);
+
+ setRenderer(this);
+
+ int size = VERTEX_BUFFER_SIZE * Float.SIZE / Byte.SIZE;
+ mXyPointer = allocateDirectNativeOrderBuffer(size);
+ mUvPointer = allocateDirectNativeOrderBuffer(size);
+ mIndexPointer = allocateDirectNativeOrderBuffer(INDEX_BUFFER_SIZE);
+ }
+
+ public void setContentPane(GLView content) {
+ mContentView = content;
+ content.onAttachToRoot(this);
+
+ // no parent for the content pane
+ content.onAddToParent(null);
+ requestLayoutContentPane();
+ }
+
+ public GLView getContentPane() {
+ return mContentView;
+ }
+
+ void handleLowMemory() {
+ //TODO: delete texture from GL
+ }
+
+ public synchronized void requestLayoutContentPane() {
+ if (mContentView == null || (mFlags & FLAG_NEED_LAYOUT) != 0) return;
+
+ // "View" system will invoke onLayout() for initialization(bug ?), we
+ // have to ignore it since the GLThread is not ready yet.
+ if ((mFlags & FLAG_INITIALIZED) == 0) return;
+
+ mFlags |= FLAG_NEED_LAYOUT;
+ requestRender();
+ }
+
+ private synchronized void layoutContentPane() {
+ mFlags &= ~FLAG_NEED_LAYOUT;
+ int width = getWidth();
+ int height = getHeight();
+ Log.v(TAG, "layout content pane " + width + "x" + height);
+ if (mContentView != null && width != 0 && height != 0) {
+ mContentView.layout(0, 0, width, height);
+ }
+ }
+
+ @Override
+ protected void onLayout(
+ boolean changed, int left, int top, int right, int bottom) {
+ if (changed) requestLayoutContentPane();
+ }
+
+ /**
+ * Called when the context is created, possibly after automatic destruction.
+ */
+ // This is a GLSurfaceView.Renderer callback
+ public void onSurfaceCreated(GL10 gl1, EGLConfig config) {
+ GL11 gl = (GL11) gl1;
+ if (mGL != null) {
+ // The GL Object has changed
+ Log.i(TAG, "GLObject has changed from " + mGL + " to " + gl);
+ }
+ mGL = gl;
+
+ if (!ENABLE_FPS_TEST) {
+ setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
+ } else {
+ setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
+ }
+
+ // Disable unused state
+ gl.glDisable(GL11.GL_LIGHTING);
+
+ // Enable used features
+ gl.glEnable(GL11.GL_BLEND);
+ gl.glEnable(GL11.GL_SCISSOR_TEST);
+ gl.glEnable(GL11.GL_STENCIL_TEST);
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+ gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
+ gl.glEnable(GL11.GL_TEXTURE_2D);
+ mTexture2DEnabled = true;
+
+ 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.glVertexPointer(2, GL11.GL_FLOAT, 0, mXyPointer);
+ gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, mUvPointer);
+
+ }
+
+ /**
+ * Called when the OpenGL surface is recreated without destroying the
+ * context.
+ */
+ // This is a GLSurfaceView.Renderer callback
+ public void onSurfaceChanged(GL10 gl1, int width, int height) {
+ Log.v(TAG, "onSurfaceChanged: " + width + "x" + height
+ + ", gl10: " + gl1.toString());
+ GL11 gl = (GL11) gl1;
+ mGL = gl;
+ gl.glViewport(0, 0, width, height);
+
+ gl.glMatrixMode(GL11.GL_PROJECTION);
+ gl.glLoadIdentity();
+
+ GLU.gluOrtho2D(gl, 0, width, 0, height);
+ Matrix matrix = mTransformation.getMatrix();
+ matrix.reset();
+ matrix.preTranslate(0, getHeight());
+ matrix.preScale(1, -1);
+ }
+
+ private void setAlphaValue(float alpha) {
+ if (mLastAlpha == alpha) return;
+
+ GL11 gl = mGL;
+ mLastAlpha = alpha;
+ if (alpha >= 0.95f) {
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
+ GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
+ } else {
+ gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
+ GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
+ gl.glColor4f(alpha, alpha, alpha, alpha);
+ }
+ }
+
+ public void drawRect(int x, int y, int width, int height) {
+ float matrix[] = mMatrixValues;
+ mTransformation.getMatrix().getValues(matrix);
+ drawRect(x, y, width, height, matrix);
+ }
+
+ private static void putRectangle(float x, float y,
+ float width, float height, float[] buffer, ByteBuffer pointer) {
+ buffer[0] = x;
+ buffer[1] = y;
+ buffer[2] = x + width;
+ buffer[3] = y;
+ buffer[4] = x;
+ buffer[5] = y + height;
+ buffer[6] = x + width;
+ buffer[7] = y + height;
+ pointer.asFloatBuffer().put(buffer, 0, 8).position(0);
+ }
+
+ private void drawRect(
+ int x, int y, int width, int height, float matrix[]) {
+ GL11 gl = mGL;
+ gl.glPushMatrix();
+ gl.glMultMatrixf(toGLMatrix(matrix), 0);
+ putRectangle(x, y, width, height, mXyBuffer, mXyPointer);
+ gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, 4);
+ gl.glPopMatrix();
+ }
+
+ public void drawNinePatch(
+ NinePatchTexture tex, int x, int y, int width, int height) {
+
+ NinePatchChunk chunk = tex.getNinePatchChunk();
+
+ // 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");
+ }
+ if (!tex.bind(this, mGL)) {
+ throw new RuntimeException("cannot bind" + tex.toString());
+ }
+ if (width <= 0 || height <= 0) return ;
+
+ int divX[] = mNinePatchX;
+ int divY[] = mNinePatchY;
+ float divU[] = mNinePatchU;
+ float divV[] = mNinePatchV;
+
+ int nx = stretch(divX, divU, chunk.mDivX, tex.getWidth(), width);
+ int ny = stretch(divY, divV, chunk.mDivY, tex.getHeight(), height);
+
+ setAlphaValue(mTransformation.getAlpha());
+ Matrix matrix = mTransformation.getMatrix();
+ matrix.getValues(mMatrixValues);
+ GL11 gl = mGL;
+ gl.glPushMatrix();
+ gl.glMultMatrixf(toGLMatrix(mMatrixValues), 0);
+ gl.glTranslatef(x, y, 0);
+ drawMesh(divX, divY, divU, divV, nx, ny);
+ gl.glPopMatrix();
+ }
+
+ /**
+ * 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.
+ *
+ * <pre>
+ * 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
+ * </pre>
+ *
+ * @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 int stretch(
+ int x[], float u[], int div[], int source, int target) {
+ int textureSize = Util.nextPowerOf2(source);
+ float textureBound = (source - 0.5f) / textureSize;
+
+ int stretch = 0;
+ for (int i = 0, n = div.length; i < n; i += 2) {
+ stretch += div[i + 1] - div[i];
+ }
+
+ float remaining = target - source + stretch;
+
+ int lastX = 0;
+ int lastU = 0;
+
+ x[0] = 0;
+ u[0] = 0;
+ for (int i = 0, n = div.length; i < n; i += 2) {
+ // fixed segment
+ x[i + 1] = lastX + (div[i] - lastU);
+ u[i + 1] = Math.min((float) div[i] / textureSize, textureBound);
+
+ // stretchy segment
+ float partU = div[i + 1] - div[i];
+ int partX = (int)(remaining * partU / stretch + 0.5f);
+ remaining -= partX;
+ stretch -= partU;
+
+ lastX = x[i + 1] + partX;
+ lastU = div[i + 1];
+ x[i + 2] = lastX;
+ u[i + 2] = Math.min((float) lastU / 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[last] == x[i]) continue;
+ x[++last] = x[i];
+ u[last] = u[i];
+ }
+ return last + 1;
+ }
+
+ private void drawMesh(
+ int x[], int y[], float u[], float v[], int nx, int ny) {
+ /*
+ * 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[] = mXyBuffer;
+ float uv[] = mUvBuffer;
+ 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];
+ }
+ }
+ mUvPointer.asFloatBuffer().put(uv, 0, pntCount << 1).position(0);
+ mXyPointer.asFloatBuffer().put(xy, 0, pntCount << 1).position(0);
+
+ int idxCount = 1;
+ byte index[] = mIndexBuffer;
+ for (int i = 0, bound = nx * (ny - 1); true;) {
+ // normal direction
+ --idxCount;
+ for (int j = 0; j < nx; ++j, ++i) {
+ index[idxCount++] = (byte) i;
+ index[idxCount++] = (byte) (i + nx);
+ }
+ if (i >= bound) break;
+
+ // reverse direction
+ int sum = i + i + nx - 1;
+ --idxCount;
+ for (int j = 0; j < nx; ++j, ++i) {
+ index[idxCount++] = (byte) (sum - i);
+ index[idxCount++] = (byte) (sum - i + nx);
+ }
+ if (i >= bound) break;
+ }
+ mIndexPointer.put(index, 0, idxCount).position(0);
+
+ mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP,
+ idxCount, GL11.GL_UNSIGNED_BYTE, mIndexPointer);
+ }
+
+ private float[] mapPoints(Matrix matrix, int x1, int y1, int x2, int y2) {
+ float[] point = mXyBuffer;
+ point[0] = x1; point[1] = y1; point[2] = x2; point[3] = y2;
+ matrix.mapPoints(point, 0, point, 0, 4);
+ return point;
+ }
+
+ public void clipRect(int x, int y, int width, int height) {
+ float point[] = mapPoints(
+ mTransformation.getMatrix(), x, y + height, x + width, y);
+
+ // mMatrix could be a rotation matrix. In this case, we need to find
+ // the boundaries after rotation. (only handle 90 * n degrees)
+ if (point[0] > point[2]) {
+ x = (int) point[2];
+ width = (int) point[0] - x;
+ } else {
+ x = (int) point[0];
+ width = (int) point[2] - x;
+ }
+ if (point[1] > point[3]) {
+ y = (int) point[3];
+ height = (int) point[1] - y;
+ } else {
+ y = (int) point[1];
+ height = (int) point[3] - y;
+ }
+ mGL.glScissor(x, y, width, height);
+ }
+
+ public void clearClip() {
+ mGL.glScissor(0, 0, getWidth(), getHeight());
+ }
+
+ private static float[] toGLMatrix(float v[]) {
+ v[15] = v[8]; v[13] = v[5]; v[5] = v[4]; v[4] = v[1];
+ v[12] = v[2]; v[1] = v[3]; v[3] = v[6];
+ v[2] = v[6] = v[8] = v[9] = 0;
+ v[10] = 1;
+ return v;
+ }
+
+ public void drawColor(int x, int y, int width, int height, int color) {
+ float alpha = mTransformation.getAlpha();
+ GL11 gl = mGL;
+ if (mTexture2DEnabled) {
+ // Set mLastAlpha to an invalid value, so that it will reset again
+ // in setAlphaValue(float) later.
+ mLastAlpha = -1.0f;
+ gl.glDisable(GL11.GL_TEXTURE_2D);
+ mTexture2DEnabled = false;
+ }
+ alpha /= 256.0f;
+ gl.glColor4f(Color.red(color) * alpha, Color.green(color) * alpha,
+ Color.blue(color) * alpha, Color.alpha(color) * alpha);
+ drawRect(x, y, width, height);
+ }
+
+ public void drawTexture(
+ BasicTexture texture, int x, int y, int width, int height) {
+ drawTexture(texture, x, y, width, height, mTransformation.getAlpha());
+ }
+
+ public void drawTexture(BasicTexture texture,
+ int x, int y, int width, int height, float alpha) {
+
+ if (!mTexture2DEnabled) {
+ mGL.glEnable(GL11.GL_TEXTURE_2D);
+ mTexture2DEnabled = true;
+ }
+
+ if (!texture.bind(this, mGL)) {
+ throw new RuntimeException("cannot bind" + texture.toString());
+ }
+ if (width <= 0 || height <= 0) return ;
+
+ Matrix matrix = mTransformation.getMatrix();
+ matrix.getValues(mMatrixValues);
+
+ // Test whether it has been rotated or flipped, if so, glDrawTexiOES
+ // won't work
+ if (isMatrixRotatedOrFlipped(mMatrixValues)) {
+ putRectangle(0, 0,
+ (texture.mWidth - 0.5f) / texture.mTextureWidth,
+ (texture.mHeight - 0.5f) / texture.mTextureHeight,
+ mUvBuffer, mUvPointer);
+ setAlphaValue(alpha);
+ drawRect(x, y, width, height, mMatrixValues);
+ } else {
+ // draw the rect from bottom-left to top-right
+ float points[] = mapPoints(matrix, x, y + height, x + width, y);
+ x = (int) points[0];
+ y = (int) points[1];
+ width = (int) points[2] - x;
+ height = (int) points[3] - y;
+ if (width > 0 && height > 0) {
+ setAlphaValue(alpha);
+ ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height);
+ }
+ }
+ }
+
+ private static boolean isMatrixRotatedOrFlipped(float matrix[]) {
+ return matrix[Matrix.MSKEW_X] != 0 || matrix[Matrix.MSKEW_Y] != 0
+ || matrix[Matrix.MSCALE_X] < 0 || matrix[Matrix.MSCALE_Y] > 0;
+ }
+
+ public synchronized void onDrawFrame(GL10 gl) {
+ if (ENABLE_FPS_TEST) {
+ long now = System.nanoTime();
+ if (mFrameCountingStart == 0) {
+ mFrameCountingStart = now;
+ } else if ((now - mFrameCountingStart) > 1000000000) {
+ Log.v(TAG, "fps: " + (double) mFrameCount
+ * 1000000000 / (now - mFrameCountingStart));
+ mFrameCountingStart = now;
+ mFrameCount = 0;
+ }
+ ++mFrameCount;
+ }
+
+ if ((mFlags & FLAG_NEED_LAYOUT) != 0) layoutContentPane();
+ clearClip();
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_STENCIL_BUFFER_BIT);
+ gl.glEnable(GL11.GL_BLEND);
+ gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+
+ mAnimationTime = SystemClock.uptimeMillis();
+ if (mContentView != null) {
+ mContentView.render(GLRootView.this, (GL11) gl);
+ }
+ long now = SystemClock.uptimeMillis();
+ for (Animation animation : mAnimations) {
+ animation.setStartTime(now);
+ }
+ mAnimations.clear();
+ }
+
+ @Override
+ public synchronized boolean dispatchTouchEvent(MotionEvent event) {
+ // If this has been detached from root, we don't need to handle event
+ return mContentView != null
+ ? mContentView.dispatchTouchEvent(event)
+ : false;
+ }
+
+ public DisplayMetrics getDisplayMetrics() {
+ if (mDisplayMetrics == null) {
+ mDisplayMetrics = new DisplayMetrics();
+ ((Activity) getContext()).getWindowManager()
+ .getDefaultDisplay().getMetrics(mDisplayMetrics);
+ }
+ return mDisplayMetrics;
+ }
+
+ public void copyTexture2D(
+ RawTexture texture, int x, int y, int width, int height)
+ throws GLOutOfMemoryException {
+ Matrix matrix = mTransformation.getMatrix();
+ matrix.getValues(mMatrixValues);
+
+ if (isMatrixRotatedOrFlipped(mMatrixValues)) {
+ throw new IllegalArgumentException("cannot support rotated matrix");
+ }
+ float points[] = mapPoints(matrix, x, y + height, x + width, y);
+ x = (int) points[0];
+ y = (int) points[1];
+ width = (int) points[2] - x;
+ height = (int) points[3] - y;
+
+ GL11 gl = mGL;
+ int newWidth = Util.nextPowerOf2(width);
+ int newHeight = Util.nextPowerOf2(height);
+ int glError = GL11.GL_NO_ERROR;
+
+ gl.glBindTexture(GL11.GL_TEXTURE_2D, texture.getId());
+
+ int[] cropRect = {0, 0, width, height};
+ gl.glTexParameteriv(GL11.GL_TEXTURE_2D,
+ GL11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, 0);
+ gl.glTexParameteri(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
+ gl.glTexParameteri(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
+ gl.glTexParameterf(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
+ gl.glTexParameterf(GL11.GL_TEXTURE_2D,
+ GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+ gl.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0,
+ GL11.GL_RGBA, x, y, newWidth, newHeight, 0);
+ glError = gl.glGetError();
+
+ if (glError == GL11.GL_OUT_OF_MEMORY) {
+ throw new GLOutOfMemoryException();
+ }
+
+ if (glError != GL11.GL_NO_ERROR) {
+ throw new RuntimeException(
+ "Texture copy fail, glError " + glError);
+ }
+
+ texture.setSize(width, height);
+ texture.setTextureSize(newWidth, newHeight);
+ }
+
+}
diff --git a/new3d/src/com/android/gallery3d/ui/GLView.java b/new3d/src/com/android/gallery3d/ui/GLView.java
new file mode 100644
index 0000000..76fbf59
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/GLView.java
@@ -0,0 +1,385 @@
+/*
+ * 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.Matrix;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import java.util.ArrayList;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class GLView {
+ @SuppressWarnings("unused")
+ private static final String TAG = "GLView";
+
+ public static final int VISIBLE = 0;
+ public static final int INVISIBLE = 1;
+
+ public static final int FLAG_INVISIBLE = 1;
+ public static final int FLAG_SET_MEASURED_SIZE = 2;
+ public static final int FLAG_LAYOUT_REQUESTED = 4;
+
+ protected final Rect mBounds = new Rect();
+ protected final Rect mPaddings = new Rect();
+
+ private GLRootView mRootView;
+ private GLView mParent;
+ private ArrayList<GLView> mComponents;
+ private GLView mMotionTarget;
+
+ private OnTouchListener mOnTouchListener;
+ private Animation mAnimation;
+
+ protected int mViewFlags = 0;
+
+ protected int mMeasuredWidth = 0;
+ protected int mMeasuredHeight = 0;
+
+ private int mLastWidthSpec = -1;
+ private int mLastHeightSpec = -1;
+
+ protected int mScrollY = 0;
+ protected int mScrollX = 0;
+ protected int mScrollHeight = 0;
+ protected int mScrollWidth = 0;
+
+ public void startAnimation(Animation animation) {
+ GLRootView root = getGLRootView();
+ if (root == null) throw new IllegalStateException();
+
+ mAnimation = animation;
+ animation.initialize(getWidth(),
+ getHeight(), mParent.getWidth(), mParent.getHeight());
+ mAnimation.start();
+ root.registerLaunchedAnimation(animation);
+ invalidate();
+ }
+
+ public void setVisibility(int visibility) {
+ if (visibility == getVisibility()) return;
+ if (visibility == VISIBLE) {
+ mViewFlags &= ~FLAG_INVISIBLE;
+ } else {
+ mViewFlags |= FLAG_INVISIBLE;
+ }
+ onVisibilityChanged(visibility);
+ invalidate();
+ }
+
+ public int getVisibility() {
+ return (mViewFlags & FLAG_INVISIBLE) == 0 ? VISIBLE : INVISIBLE;
+ }
+
+ public static interface OnTouchListener {
+ public boolean onTouch(GLView view, MotionEvent event);
+ }
+
+ private boolean setBounds(int left, int top, int right, int bottom) {
+ boolean sizeChanged = (right - left) != (mBounds.right - mBounds.left)
+ || (bottom - top) != (mBounds.bottom - mBounds.top);
+ mBounds.set(left, top, right, bottom);
+ return sizeChanged;
+ }
+
+ protected void onAddToParent(GLView parent) {
+ // TODO: enable the check
+ // if (mParent != null) throw new IllegalStateException();
+ mParent = parent;
+ if (parent != null && parent.mRootView != null) {
+ onAttachToRoot(parent.mRootView);
+ }
+ }
+
+ protected void onRemoveFromParent(GLView parent) {
+ if (parent != null && parent.mMotionTarget == this) {
+ long now = SystemClock.uptimeMillis();
+ dispatchTouchEvent(MotionEvent.obtain(
+ now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0));
+ parent.mMotionTarget = null;
+ }
+ onDetachFromRoot();
+ mParent = null;
+ }
+
+ public void clearComponents() {
+ mComponents = null;
+ }
+
+ public int getComponentCount() {
+ return mComponents == null ? 0 : mComponents.size();
+ }
+
+ public GLView getComponent(int index) {
+ if (mComponents == null) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ return mComponents.get(index);
+ }
+
+ public void addComponent(GLView component) {
+ if (mComponents == null) {
+ mComponents = new ArrayList<GLView>();
+ }
+ mComponents.add(component);
+ component.onAddToParent(this);
+ }
+
+ public boolean removeComponent(GLView component) {
+ if (mComponents == null) return false;
+ if (mComponents.remove(component)) {
+ component.onRemoveFromParent(this);
+ return true;
+ }
+ return false;
+ }
+
+ public Rect bounds() {
+ return mBounds;
+ }
+
+ public int getWidth() {
+ return mBounds.right - mBounds.left;
+ }
+
+ public int getHeight() {
+ return mBounds.bottom - mBounds.top;
+ }
+
+ public GLRootView getGLRootView() {
+ return mRootView;
+ }
+
+ public void setOnTouchListener(OnTouchListener listener) {
+ mOnTouchListener = listener;
+ }
+
+ public void invalidate() {
+ GLRootView root = getGLRootView();
+ if (root != null) root.requestRender();
+ }
+
+ public void requestLayout() {
+ mViewFlags |= FLAG_LAYOUT_REQUESTED;
+ if (mParent != null) {
+ mParent.requestLayout();
+ } else {
+ // Is this a content pane ?
+ GLRootView root = getGLRootView();
+ if (root != null) root.requestLayoutContentPane();
+ }
+ }
+
+ protected void render(GLRootView view, GL11 gl) {
+ renderBackground(view, gl);
+ for (int i = 0, n = getComponentCount(); i < n; ++i) {
+ GLView component = getComponent(i);
+ if (component.getVisibility() != GLView.VISIBLE
+ && component.mAnimation == null) continue;
+ renderChild(view, gl, component);
+ }
+ }
+
+ protected void renderBackground(GLRootView view, GL11 gl) {
+ }
+
+ protected void renderChild(GLRootView root, GL11 gl, GLView component) {
+ int xoffset = component.mBounds.left - mScrollX;
+ int yoffset = component.mBounds.top - mScrollY;
+
+ Transformation transform = root.getTransformation();
+ Matrix matrix = transform.getMatrix();
+ matrix.preTranslate(xoffset, yoffset);
+
+ Animation anim = component.mAnimation;
+ if (anim != null) {
+ long now = root.currentAnimationTimeMillis();
+ Transformation temp = root.obtainTransformation();
+ if (!anim.getTransformation(now, temp)) {
+ component.mAnimation = null;
+ }
+ invalidate();
+ root.pushTransform();
+ transform.compose(temp);
+ root.freeTransformation(temp);
+ }
+ component.render(root, gl);
+ if (anim != null) root.popTransform();
+ matrix.preTranslate(-xoffset, -yoffset);
+ }
+
+ protected boolean onTouch(MotionEvent event) {
+ if (mOnTouchListener != null) {
+ return mOnTouchListener.onTouch(this, event);
+ }
+ return false;
+ }
+
+ private boolean dispatchTouchEvent(MotionEvent event,
+ int x, int y, GLView component, boolean checkBounds) {
+ Rect rect = component.mBounds;
+ int left = rect.left;
+ int top = rect.top;
+ if (!checkBounds || rect.contains(x, y)) {
+ event.offsetLocation(-left, -top);
+ if (component.dispatchTouchEvent(event)) {
+ event.offsetLocation(left, top);
+ return true;
+ }
+ event.offsetLocation(left, top);
+ }
+ return false;
+ }
+
+ protected boolean dispatchTouchEvent(MotionEvent event) {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ int action = event.getAction();
+ if (mMotionTarget != null) {
+ if (action == MotionEvent.ACTION_DOWN) {
+ MotionEvent cancel = MotionEvent.obtain(event);
+ cancel.setAction(MotionEvent.ACTION_CANCEL);
+ mMotionTarget = null;
+ } else {
+ dispatchTouchEvent(event, x, y, mMotionTarget, false);
+ if (action == MotionEvent.ACTION_CANCEL
+ || action == MotionEvent.ACTION_UP) {
+ mMotionTarget = null;
+ }
+ return true;
+ }
+ }
+ if (action == MotionEvent.ACTION_DOWN) {
+ for (int i = 0, n = getComponentCount(); i < n; ++i) {
+ GLView component = getComponent(i);
+ if (component.getVisibility() != GLView.VISIBLE) continue;
+ if (dispatchTouchEvent(event, x, y, component, true)) {
+ mMotionTarget = component;
+ return true;
+ }
+ }
+ }
+ return onTouch(event);
+ }
+
+ public Rect getPaddings() {
+ return mPaddings;
+ }
+
+ public void setPaddings(Rect paddings) {
+ mPaddings.set(paddings);
+ }
+
+ public void setPaddings(int left, int top, int right, int bottom) {
+ mPaddings.set(left, top, right, bottom);
+ }
+
+ public void layout(int left, int top, int right, int bottom) {
+ boolean sizeChanged = setBounds(left, top, right, bottom);
+ if (sizeChanged) {
+ mViewFlags &= ~FLAG_LAYOUT_REQUESTED;
+ onLayout(true, left, top, right, bottom);
+ } else if ((mViewFlags & FLAG_LAYOUT_REQUESTED)!= 0) {
+ mViewFlags &= ~FLAG_LAYOUT_REQUESTED;
+ onLayout(false, left, top, right, bottom);
+ }
+ }
+
+ public void measure(int widthSpec, int heightSpec) {
+ if (widthSpec == mLastWidthSpec && heightSpec == mLastHeightSpec
+ && (mViewFlags & FLAG_LAYOUT_REQUESTED) == 0) {
+ return;
+ }
+
+ mLastWidthSpec = widthSpec;
+ mLastHeightSpec = heightSpec;
+
+ mViewFlags &= ~FLAG_SET_MEASURED_SIZE;
+ onMeasure(widthSpec, heightSpec);
+ if ((mViewFlags & FLAG_SET_MEASURED_SIZE) == 0) {
+ throw new IllegalStateException(getClass().getName()
+ + " should call setMeasuredSize() in onMeasure()");
+ }
+ }
+
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ }
+
+ protected void setMeasuredSize(int width, int height) {
+ mViewFlags |= FLAG_SET_MEASURED_SIZE;
+ mMeasuredWidth = width;
+ mMeasuredHeight = height;
+ }
+
+ public int getMeasuredWidth() {
+ return mMeasuredWidth;
+ }
+
+ public int getMeasuredHeight() {
+ return mMeasuredHeight;
+ }
+
+ protected void onLayout(
+ boolean changeSize, int left, int top, int right, int bottom) {
+ }
+
+ /**
+ * Gets the bounds of the given descendant that relative to this view.
+ */
+ public boolean getBoundsOf(GLView descendant, Rect out) {
+ int xoffset = 0;
+ int yoffset = 0;
+ GLView view = descendant;
+ while (view != this) {
+ if (view == null) return false;
+ Rect bounds = view.mBounds;
+ xoffset += bounds.left;
+ yoffset += bounds.top;
+ view = view.mParent;
+ }
+ out.set(xoffset, yoffset, xoffset + descendant.getWidth(),
+ yoffset + descendant.getHeight());
+ return true;
+ }
+
+ protected void onVisibilityChanged(int visibility) {
+ for (int i = 0, n = getComponentCount(); i < n; ++i) {
+ GLView child = getComponent(i);
+ if (child.getVisibility() == GLView.VISIBLE) {
+ child.onVisibilityChanged(visibility);
+ }
+ }
+ }
+
+ protected void onAttachToRoot(GLRootView root) {
+ mRootView = root;
+ for (int i = 0, n = getComponentCount(); i < n; ++i) {
+ getComponent(i).onAttachToRoot(root);
+ }
+ }
+
+ protected void onDetachFromRoot() {
+ for (int i = 0, n = getComponentCount(); i < n; ++i) {
+ getComponent(i).onDetachFromRoot();
+ }
+ mRootView = null;
+ }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/NinePatchChunk.java b/new3d/src/com/android/gallery3d/ui/NinePatchChunk.java
new file mode 100644
index 0000000..c1cf1e4
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/NinePatchChunk.java
@@ -0,0 +1,66 @@
+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/new3d/src/com/android/gallery3d/ui/NinePatchTexture.java b/new3d/src/com/android/gallery3d/ui/NinePatchTexture.java
new file mode 100644
index 0000000..318b500
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/NinePatchTexture.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.ui;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Rect;
+
+class NinePatchTexture extends ResourceTexture {
+ private NinePatchChunk mChunk;
+
+ public NinePatchTexture(Context context, int resId) {
+ super(context, resId);
+ }
+
+ @Override
+ protected Bitmap getBitmap() {
+ 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());
+ mChunk = 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) getBitmap();
+ return mChunk.mPaddings;
+ }
+
+ public NinePatchChunk getNinePatchChunk() {
+ if (mChunk == null) getBitmap();
+ return mChunk;
+ }
+
+ @Override
+ public void draw(GLRootView root, int x, int y, int w, int h) {
+ root.drawNinePatch(this, x, y, w, h);
+ }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/RawTexture.java b/new3d/src/com/android/gallery3d/ui/RawTexture.java
new file mode 100644
index 0000000..35eb3bf
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/RawTexture.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.ui;
+
+
+import javax.microedition.khronos.opengles.GL11;
+
+class RawTexture extends BasicTexture {
+
+ private RawTexture(GL11 gl, int id) {
+ super(gl, id, STATE_LOADED);
+ }
+
+ public GL11 getBoundGL() {
+ return mGL;
+ }
+
+ public static RawTexture newInstance(GL11 gl) {
+ int[] textureId = new int[1];
+ gl.glGenTextures(1, textureId, 0);
+ int glError = gl.glGetError();
+ if (glError != GL11.GL_NO_ERROR) {
+ throw new RuntimeException("GL_ERROR: " + glError);
+ }
+ return new RawTexture(gl, textureId[0]);
+ }
+
+ @Override
+ protected boolean bind(GLRootView glRootView, GL11 gl) {
+ if (mGL == gl) {
+ gl.glBindTexture(GL11.GL_TEXTURE_2D, getId());
+ return true;
+ }
+ return false;
+ }
+
+ public void drawBack(GLRootView root, int x, int y, int w, int h) {
+ root.drawTexture(this, x, y, w, h, 1f);
+ }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/ResourceTexture.java b/new3d/src/com/android/gallery3d/ui/ResourceTexture.java
new file mode 100644
index 0000000..46a35d7
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/ResourceTexture.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+class ResourceTexture extends BitmapTexture {
+
+ protected final Context mContext;
+ protected final int mResId;
+ protected Bitmap mBitmap;
+
+ public ResourceTexture(Context context, int resId) {
+ mContext = Util.checkNotNull(context);
+ mResId = resId;
+ }
+
+ @Override
+ protected Bitmap getBitmap() {
+ if (mBitmap != null) return mBitmap;
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ mBitmap = BitmapFactory.decodeResource(
+ mContext.getResources(), mResId, options);
+ setSize(mBitmap.getWidth(), mBitmap.getHeight());
+ return mBitmap;
+ }
+
+ @Override
+ protected void freeBitmap(Bitmap bitmap) {
+ Util.Assert(bitmap == mBitmap);
+ bitmap.recycle();
+ mBitmap = null;
+ }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/SlotView.java b/new3d/src/com/android/gallery3d/ui/SlotView.java
new file mode 100644
index 0000000..ce4d73f
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/SlotView.java
@@ -0,0 +1,16 @@
+package com.android.gallery3d.ui;
+
+public class SlotView {
+
+ public static interface Slot {
+ public void putOn(int x, int y, DisplayItemPanel panel);
+ }
+
+ public static interface Model {
+ public int size();
+ public int getSlotHeight();
+ public int getSlotWidth();
+ public Slot getSlot(int index);
+ public void free(Slot slot);
+ }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/StringTexture.java b/new3d/src/com/android/gallery3d/ui/StringTexture.java
new file mode 100644
index 0000000..62d84cf
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/StringTexture.java
@@ -0,0 +1,62 @@
+/*
+ * 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.Paint;
+import android.graphics.Paint.FontMetricsInt;
+
+class StringTexture extends CanvasTexture {
+ private static int DEFAULT_PADDING = 1;
+
+ private final String mText;
+ private final Paint mPaint;
+ private final FontMetricsInt mMetrics;
+
+ public StringTexture(String text, Paint paint,
+ FontMetricsInt metrics, int width, int height) {
+ super(width, height);
+ mText = text;
+ mPaint = paint;
+ mMetrics = metrics;
+ }
+
+
+ public static StringTexture newInstance(String text, Paint paint) {
+ FontMetricsInt metrics = paint.getFontMetricsInt();
+ int width = (int) (.5f + paint.measureText(text)) + DEFAULT_PADDING * 2;
+ int height = metrics.bottom - metrics.top + DEFAULT_PADDING * 2;
+ return new StringTexture(text, paint, metrics, width, height);
+ }
+
+ public static StringTexture newInstance(
+ String text, float textSize, int color) {
+ Paint paint = new Paint();
+ paint.setTextSize(textSize);
+ paint.setAntiAlias(true);
+ paint.setColor(color);
+
+ return newInstance(text, paint);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas, Bitmap backing) {
+ canvas.translate(DEFAULT_PADDING, DEFAULT_PADDING - mMetrics.ascent);
+ canvas.drawText(mText, 0, 0, mPaint);
+ }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/Texture.java b/new3d/src/com/android/gallery3d/ui/Texture.java
new file mode 100644
index 0000000..1515bf0
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/Texture.java
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+interface Texture {
+ public void draw(GLRootView root, int x, int y);
+ public void draw(GLRootView root, int x, int y, int w, int h);
+}
diff --git a/new3d/src/com/android/gallery3d/ui/Util.java b/new3d/src/com/android/gallery3d/ui/Util.java
new file mode 100644
index 0000000..a3f1d55
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/Util.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 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;
+
+/**
+ * Collection of utility functions used in this package.
+ */
+class Util {
+ @SuppressWarnings("unused")
+ private static final String TAG = "Util";
+
+ private Util() {
+ }
+
+ public static void Assert(boolean cond) {
+ if (!cond) {
+ throw new AssertionError();
+ }
+ }
+
+ public static <T> T checkNotNull(T object) {
+ if (object == null) throw new NullPointerException();
+ return object;
+ }
+
+ public static boolean equals(Object a, Object b) {
+ return (a == b) || (a == null ? false : a.equals(b));
+ }
+
+ public static boolean isPowerOf2(int n) {
+ return (n & -n) == n;
+ }
+
+ public static int nextPowerOf2(int n) {
+ n -= 1;
+ n |= n >>> 16;
+ n |= n >>> 8;
+ n |= n >>> 4;
+ n |= n >>> 2;
+ n |= n >>> 1;
+ return n + 1;
+ }
+
+ public static float distance(float x, float y, float sx, float sy) {
+ float dx = x - sx;
+ float dy = y - sy;
+ return (float) Math.sqrt(dx * dx + dy * dy);
+ }
+
+ public static int clamp(int x, int min, int max) {
+ if (x > max) return max;
+ if (x < min) return min;
+ return x;
+ }
+}