Extract GLCanvas interface and start testing.

Change-Id: Iba41eacbe477de7adbbddcdf556bb57d979261f5
diff --git a/new3d/src/com/android/gallery3d/anim/AlphaAnimation.java b/new3d/src/com/android/gallery3d/anim/AlphaAnimation.java
index d806885..c55d7c2 100644
--- a/new3d/src/com/android/gallery3d/anim/AlphaAnimation.java
+++ b/new3d/src/com/android/gallery3d/anim/AlphaAnimation.java
@@ -23,7 +23,7 @@
 
     @Override
     public int getCanvasSaveFlags() {
-        return GLCanvas.ALPHA_SAVE_FLAG;
+        return GLCanvas.SAVE_FLAG_ALPHA;
     }
 
     @Override
diff --git a/new3d/src/com/android/gallery3d/anim/ScaleAnimation.java b/new3d/src/com/android/gallery3d/anim/ScaleAnimation.java
index f5dca50..f065cf7 100644
--- a/new3d/src/com/android/gallery3d/anim/ScaleAnimation.java
+++ b/new3d/src/com/android/gallery3d/anim/ScaleAnimation.java
@@ -51,7 +51,7 @@
 
     @Override
     public int getCanvasSaveFlags() {
-        return GLCanvas.MATRIX_SAVE_FLAG;
+        return GLCanvas.SAVE_FLAG_MATRIX;
     }
 
     @Override
diff --git a/new3d/src/com/android/gallery3d/ui/DisplayItemPanel.java b/new3d/src/com/android/gallery3d/ui/DisplayItemPanel.java
index ba6ec37..8b9a4bd 100644
--- a/new3d/src/com/android/gallery3d/ui/DisplayItemPanel.java
+++ b/new3d/src/com/android/gallery3d/ui/DisplayItemPanel.java
@@ -19,8 +19,8 @@
     // The state of the items that changed position
     private static final int STATE_MOVED = 3;
 
-    private static final long NO_ANIMATION = -1;
-    private static final long START_ANIMATION = 0;
+    private static final long START_ANIMATION = -1;
+    private static final long NO_ANIMATION = -2;
 
     private ArrayList<DisplayItem> mItems = new ArrayList<DisplayItem>();
 
@@ -114,7 +114,7 @@
     }
 
     private void renderItem(GLCanvas canvas, DisplayItem item) {
-        canvas.save(GLCanvas.ALPHA_SAVE_FLAG | GLCanvas.MATRIX_SAVE_FLAG);
+        canvas.save(GLCanvas.SAVE_FLAG_ALPHA | GLCanvas.SAVE_FLAG_MATRIX);
         item.mCurrent.apply(canvas);
         item.render(canvas);
         canvas.restore();
@@ -122,7 +122,7 @@
 
     private void renderItem(
             GLCanvas canvas, DisplayItem item, float interpolate) {
-        canvas.save(GLCanvas.ALPHA_SAVE_FLAG | GLCanvas.MATRIX_SAVE_FLAG);
+        canvas.save(GLCanvas.SAVE_FLAG_ALPHA | GLCanvas.SAVE_FLAG_MATRIX);
         switch (item.mState) {
             case STATE_MOVED:
                 item.updateCurrentPosition(interpolate);
diff --git a/new3d/src/com/android/gallery3d/ui/GLCanvas.java b/new3d/src/com/android/gallery3d/ui/GLCanvas.java
index 770cac4..5560098 100644
--- a/new3d/src/com/android/gallery3d/ui/GLCanvas.java
+++ b/new3d/src/com/android/gallery3d/ui/GLCanvas.java
@@ -2,773 +2,109 @@
 
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.opengl.GLU;
-import android.opengl.Matrix;
+import android.view.animation.Transformation;
 
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
 import java.util.Collection;
-import java.util.Stack;
 
-import javax.microedition.khronos.opengles.GL10;
 import javax.microedition.khronos.opengles.GL11;
-import javax.microedition.khronos.opengles.GL11Ext;
 
-public class GLCanvas {
+//
+// 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 interface GLCanvas {
+    // 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 GLRootView and should not be called by the clients
+    // who only want to draw on the GLCanvas.
+    public void setSize(int width, int height);
 
-    public static final int ALL_SAVE_FLAG = 0xFFFFFFFF;
-    public static final int CLIP_SAVE_FLAG = 0x01;
-    public static final int ALPHA_SAVE_FLAG = 0x02;
-    public static final int MATRIX_SAVE_FLAG = 0x04;
+    // Clear the drawing buffers. This should only be used by GLRootView.
+    public void clearBuffer();
 
-    // We need 16 vertices for a normal nine-patch image (the 4x4 vertices)
-    private static final int VERTEX_BUFFER_SIZE = 16 * 2;
+    // This is the time value used to calculate the animation in the current
+    // frame. The "set" function should only called by GLRootView, and the
+    // "time" parameter must be nonnegative.
+    public void setCurrentAnimationTimeMillis(long time);
+    public long currentAnimationTimeMillis();
 
-    // We need 22 indices for a normal nine-patch image
-    private static final int INDEX_BUFFER_SIZE = 22;
+    // Sets the current drawing color used for drawLine and fillRect.
+    public void setColor(int color);
 
-    private static final float OPAQUE_ALPHA = 0.95f;
+    // Sets and gets the current alpha
+    public void setAlpha(float alpha);
+    public float getAlpha();
 
-    private final GL11 mGL;
+    // (current alpha) = (current alpha) * alpha
+    public void multiplyAlpha(float alpha);
 
-    private final float mMatrixValues[] = new float[16];
+    public void translate(float x, float y, float z);
+    public void scale(float sx, float sy, float sz);
+    public void rotate(float angle, float x, float y, float z);
 
-    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];
+    // Modifies the current clip with the specified rectangle.
+    // (current clip) = (current clip) intersect (specified rectangle).
+    // Returns true if the result clip is non-empty.
+    public boolean clipRect(int x, int y, int width, int height);
 
-    private final int mNinePatchX[] = new int[4];
-    private final int mNinePatchY[] = new int[4];
-    private final float mNinePatchU[] = new float[4];
-    private final float mNinePatchV[] = new float[4];
-    private final float mTextureColor[] = new float[4];
+    // Pushes the configuration state (matrix, alpha, and clip) onto
+    // a private stack.
+    public int save();
 
-    private FloatBuffer mXyPointer;
-    private FloatBuffer mUvPointer;
-    private ByteBuffer mIndexPointer;
+    // Same as save(), but only save those specified in saveFlags.
+    public int save(int saveFlags);
 
-    private final GLState mGLState;
+    public static final int SAVE_FLAG_ALL = 0xFFFFFFFF;
+    public static final int SAVE_FLAG_CLIP = 0x01;
+    public static final int SAVE_FLAG_ALPHA = 0x02;
+    public static final int SAVE_FLAG_MATRIX = 0x04;
 
-    private long mAnimationTime;
+    // 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 void restore();
 
-    private float mAlpha;
-    private final Rect mClipRect = new Rect();
-    private final Stack<ConfigState> mRestoreStack =
-            new Stack<ConfigState>();
-    private ConfigState mRecycledRestoreAction;
+    // Draws a line using current drawing color from (x1, y1) to (x2, y2).
+    // (Both end points are included).
+    public void drawLine(int x1, int y1, int x2, int y2);
 
-    GLCanvas(GL11 gl) {
-        mGL = gl;
-        mGLState = new GLState(gl);
-        initialize();
-    }
+    // Fills the specified rectange with the current drawing color or
+    // the color of texture.
+    public void fillRect(int x, int y, int width, int height);
+    public void fillRect(Rect r);
 
-    public void setSize(int width, int height) {
-        GL11 gl = mGL;
-        gl.glViewport(0, 0, width, height);
-        gl.glMatrixMode(GL11.GL_PROJECTION);
-        gl.glLoadIdentity();
-        GLU.gluOrtho2D(gl, 0, width, 0, height);
+    // Sets the texture coordinates for fillRect calls.
+    public void setTextureCoords(RectF source);
 
-        gl.glMatrixMode(GL11.GL_MODELVIEW);
-        gl.glLoadIdentity();
-
-        // The positive direction in Y coordinate in OpenGL is from bottom to
-        // top, which is different from the coordinate system in Java. So, we
-        // flip it here.
-        float matrix[] = mMatrixValues;
-        Matrix.setIdentityM(matrix, 0);
-        Matrix.translateM(matrix, 0, 0, height, 0);
-        Matrix.scaleM(matrix, 0, 1, -1, 1);
-
-        mClipRect.set(0, 0, width, height);
-        gl.glScissor(0, 0, width, height);
-    }
-
-    public long currentAnimationTimeMillis() {
-        return mAnimationTime;
-    }
-
-    public void setAlpha(float alpha) {
-        mAlpha = alpha;
-    }
-
-    public void multiplyAlpha(float alpha) {
-        mAlpha *= alpha;
-    }
-
-    public float getAlpha() {
-        return mAlpha;
-    }
-
-    private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
-        return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
-    }
-
-    private void initialize() {
-        int size = VERTEX_BUFFER_SIZE * Float.SIZE / Byte.SIZE;
-        mXyPointer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
-        mUvPointer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
-        mIndexPointer = allocateDirectNativeOrderBuffer(INDEX_BUFFER_SIZE);
-
-        GL11 gl = mGL;
-
-        gl.glVertexPointer(2, GL11.GL_FLOAT, 0, mXyPointer);
-        gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, mUvPointer);
-
-        // Enable the texture coordinate array for Texture 1
-        gl.glClientActiveTexture(GL11.GL_TEXTURE1);
-        gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, mUvPointer);
-        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
-
-        // mMatrixValues will be initialized in setSize()
-        mAlpha = 1.0f;
-    }
-
-    private static void putRectangle(float x, float y,
-            float width, float height, float[] buffer, FloatBuffer 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.put(buffer, 0, 8).position(0);
-    }
-
-    public void setColor(int color) {
-        float alpha = mAlpha;
-        mGLState.setBlendEnabled(!Util.isOpaque(color) || alpha < OPAQUE_ALPHA);
-        mGLState.setFragmentColor(color, alpha);
-    }
-
-    public void drawLine(int x1, int y1, int x2, int y2) {
-        GL11 gl = mGL;
-        gl.glLoadMatrixf(mMatrixValues, 0);
-        float buffer[] = mXyBuffer;
-        buffer[0] = x1;
-        buffer[1] = y1;
-        buffer[2] = x2;
-        buffer[3] = y2;
-        mXyPointer.put(buffer, 0, 4).position(0);
-        gl.glDrawArrays(GL11.GL_LINE_STRIP, 0, 2);
-    }
-
-    public void fillRect(Rect r) {
-        fillRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
-    }
-
-    public void translate(float x, float y, float z) {
-        Matrix.translateM(mMatrixValues, 0, x, y, z);
-    }
-
-    public void scale(float sx, float sy, float sz) {
-        Matrix.scaleM(mMatrixValues, 0, sx, sy, sz);
-    }
-
-    public void rotate(float angle, float x, float y, float z) {
-        Matrix.rotateM(mMatrixValues, 0, angle, x, y, z);
-    }
-
-    public void fillRect(int x, int y, int width, int height) {
-        GL11 gl = mGL;
-        gl.glLoadMatrixf(mMatrixValues, 0);
-        putRectangle(x, y, width, height, mXyBuffer, mXyPointer);
-        gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, 4);
-    }
-
-    public void drawNinePatch(
-            NinePatchTexture tex, int x, int y, int width, int height) {
-        float alpha = mAlpha;
-        NinePatchChunk chunk = tex.getNinePatchChunk();
-
-        mGLState.setTexture2DEnabled(true);
-
-        // 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");
-        }
-        tex.bind(this);
-        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);
-        mGLState.setBlendEnabled(!tex.isOpaque() || alpha < OPAQUE_ALPHA);
-
-        mGLState.setTextureAlpha(alpha);
-
-        GL11 gl = mGL;
-        gl.glLoadMatrixf(mMatrixValues, 0);
-        gl.glTranslatef(x, y, 0);
-        drawMesh(divX, divY, divU, divV, nx, ny);
-    }
-
-    /**
-     * 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.put(uv, 0, pntCount << 1).position(0);
-        mXyPointer.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(float matrix[], int x1, int y1, int x2, int y2) {
-        float[] point = mXyBuffer;
-        int srcOffset = 6;
-        point[srcOffset] = x1;
-        point[srcOffset + 1] = y1;
-        point[srcOffset + 2] = 0;
-        point[srcOffset + 3] = 1;
-
-        int resultOffset = 0;
-        Matrix.multiplyMV(point, resultOffset, matrix, 0, point, srcOffset);
-        point[resultOffset] /= point[resultOffset + 3];
-        point[resultOffset + 1] /= point[resultOffset + 3];
-
-        // map the second point
-        point[srcOffset] = x2;
-        point[srcOffset + 1] = y2;
-        resultOffset = 2;
-        Matrix.multiplyMV(point, resultOffset, matrix, 0, point, srcOffset);
-        point[resultOffset] /= point[resultOffset + 3];
-        point[resultOffset + 1] /= point[resultOffset + 3];
-
-        return point;
-    }
-
-
-    public boolean clipRect(int left, int top, int right, int bottom) {
-        float point[] = mapPoints(mMatrixValues, left, top, right, bottom);
-
-        // 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]) {
-            left = (int) point[2];
-            right = (int) point[0];
-        } else {
-            left = (int) point[0];
-            right = (int) point[2];
-        }
-        if (point[1] > point[3]) {
-            top = (int) point[3];
-            bottom = (int) point[1];
-        } else {
-            top = (int) point[1];
-            bottom = (int) point[3];
-        }
-        Rect clip = mClipRect;
-
-        boolean intersect = clip.intersect(left, top, right, bottom);
-        if (!intersect) clip.set(0, 0, 0, 0);
-        mGL.glScissor(clip.left, clip.top, clip.width(), clip.height());
-        return intersect;
-    }
-
-    public void drawColor(int x, int y, int width, int height, int color) {
-        float alpha = mAlpha;
-        mGLState.setBlendEnabled(!Util.isOpaque(color) || alpha < OPAQUE_ALPHA);
-        mGLState.setFragmentColor(color, alpha);
-        fillRect(x, y, width, height);
-    }
-
-    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)) {
-            putRectangle(0, 0,
-                    (texture.mWidth - 0.5f) / texture.mTextureWidth,
-                    (texture.mHeight - 0.5f) / texture.mTextureHeight,
-                    mUvBuffer, mUvPointer);
-            fillRect(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];
-            y = (int) points[1];
-            width = (int) points[2] - x;
-            height = (int) points[3] - y;
-            if (width > 0 && height > 0) {
-                ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height);
-            }
-        }
-
-    }
-
+    // Draws a texture to the specified rectangle.
     public void drawTexture(
-            BasicTexture texture, int x, int y, int width, int height) {
-        drawTexture(texture, x, y, width, height, mAlpha);
-    }
+            BasicTexture texture, int x, int y, int width, int height);
+    public void drawNinePatch(
+            NinePatchTexture tex, int x, int y, int width, int height);
 
+    // Draws a texture to the specified rectangle. The "alpha" parameter
+    // overrides the current drawing alpha value.
     public void drawTexture(BasicTexture texture,
-            int x, int y, int width, int height, float alpha) {
-        if (width <= 0 || height <= 0) return ;
+            int x, int y, int width, int height, float alpha);
 
-        mGLState.setBlendEnabled(!texture.isOpaque() || alpha < OPAQUE_ALPHA);
-        mGLState.setTexture2DEnabled(true);
-        texture.bind(this);
-        mGLState.setTextureAlpha(alpha);
-        drawBoundTexture(texture, x, y, width, height);
-    }
+    // Draw two textures to the specified rectange. The actual texture used is
+    // from * (1 - ratio) + to * ratio
+    public void drawMixed(BasicTexture from, BasicTexture to,
+            float ratio, int x, int y, int w, int h);
 
     public void drawMixed(BasicTexture from, BasicTexture to,
-            float ratio, int x, int y, int w, int h) {
-        drawMixed(from, to, ratio, x, y, w, h, mAlpha);
-    }
+            float ratio, int x, int y, int width, int height, float alpha);
 
-    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;
-    }
-
-    public void handleLowMemory() {
-    }
-
-    public void drawMixed(BasicTexture from, BasicTexture to,
-            float ratio, int x, int y, int width, int height, float alpha) {
-        if (alpha < OPAQUE_ALPHA) {
-            throw new RuntimeException("Cannot support alpha value");
-        }
-        mGLState.setBlendEnabled(!from.isOpaque() || !to.isOpaque());
-        mGLState.setTextureAlpha(1);
-        mGLState.setTexture2DEnabled(true);
-
-        final GL11 gl = mGL;
-        from.bind(this);
-
-        gl.glActiveTexture(GL11.GL_TEXTURE1);
-        to.bind(this);
-        gl.glEnable(GL11.GL_TEXTURE_2D);
-
-        // Interpolate the RGB and alpha values between both textures.
-        mGLState.setTexEnvMode(GL11.GL_COMBINE);
-        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);
-
-        // Specify the interpolation factor via the alpha component of
-        // GL_TEXTURE_ENV_COLORes.
-        setTextureColor(ratio, ratio, ratio, ratio);
-        gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);
-
-        // 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);
-
-        // Draw the combined texture.
-        drawBoundTexture(to, x, y, width, height);
-
-        // Disable TEXTURE1.
-        gl.glDisable(GL11.GL_TEXTURE_2D);
-        // Switch back to the default texture unit.
-        gl.glActiveTexture(GL11.GL_TEXTURE0);
-    }
-
-    // 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[]) {
-        return matrix[MSKEW_X] != 0 || matrix[MSKEW_Y] != 0
-                || matrix[MSCALE_X] < 0 || matrix[MSCALE_Y] > 0;
-    }
-
+    // Copies the specified rectangle to the RawTexture.
     public void copyTexture2D(
-            RawTexture texture, int x, int y, int width, int height) {
+            RawTexture texture, int x, int y, int width, int height);
 
-        if (isMatrixRotatedOrFlipped(mMatrixValues)) {
-            throw new IllegalArgumentException("cannot support rotated matrix");
-        }
-        float points[] = mapPoints(mMatrixValues, x, y + height, x + width, y);
-        x = (int) points[0];
-        y = (int) points[1];
-        width = (int) points[2] - x;
-        height = (int) points[3] - y;
+    // TODO: Remove this or document it.
+    public void releaseTextures(Collection<? extends BasicTexture> c);
 
-        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);
-    }
-
-    private static class GLState {
-
-        private final GL11 mGL;
-
-        private int mTexEnvMode = GL11.GL_REPLACE;
-        private float mTextureAlpha = 1.0f;
-        private boolean mTexture2DEnabled = true;
-        private boolean mBlendEnabled = true;
-
-        public GLState(GL11 gl) {
-            mGL = gl;
-
-            // Disable unused state
-            gl.glDisable(GL11.GL_LIGHTING);
-
-            // Enable used features
-            gl.glEnable(GL11.GL_DITHER);
-
-            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);
-
-            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);
-        }
-
-        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 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 setFragmentColor(int color, float alpha) {
-            // Set mTextureAlpha to an invalid value, so that it will reset
-            // again in setTextureAlpha(float) later.
-            mTextureAlpha = -1.0f;
-
-            setTexture2DEnabled(false);
-            int prealpha = (int) ((color >>> 24) * alpha + 0.5);
-            mGL.glColor4x(
-                    ((color >> 16) & 0xFF) * prealpha,
-                    ((color >> 8) & 0xFF) * prealpha,
-                    (color & 0xFF) * prealpha, prealpha << 8);
-        }
-
-        public void setTexture2DEnabled(boolean enabled) {
-            if (mTexture2DEnabled == enabled) return;
-            mTexture2DEnabled = enabled;
-            if (enabled) {
-                mGL.glEnable(GL11.GL_TEXTURE_2D);
-            } else {
-                mGL.glDisable(GL11.GL_TEXTURE_2D);
-            }
-        }
-
-        public void setBlendEnabled(boolean enabled) {
-            if (mBlendEnabled == enabled) return;
-            mBlendEnabled = enabled;
-            if (enabled) {
-                mGL.glEnable(GL11.GL_BLEND);
-            } else {
-                mGL.glDisable(GL11.GL_BLEND);
-            }
-        }
-    }
-
-    public GL11 getGLInstance() {
-        return mGL;
-    }
-
-    protected void setCurrentTimeMillis(long time) {
-        mAnimationTime = time;
-    }
-
-    public void clearBuffer() {
-        mGL.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_STENCIL_BUFFER_BIT);
-    }
-
-    public void setTextureCoords(RectF source) {
-        float buffer[] = mUvBuffer;
-        buffer[0] = source.left;
-        buffer[1] = source.top;
-        buffer[2] = source.right;
-        buffer[3] = source.top;
-        buffer[4] = source.left;
-        buffer[5] = source.bottom;
-        buffer[6] = source.right;
-        buffer[7] = source.bottom;
-        mUvPointer.put(buffer, 0, 8).position(0);
-    }
-
-    public void releaseTextures(Collection<? extends BasicTexture> c) {
-        IntArray array = new IntArray();
-        for (BasicTexture t : c) {
-            if (t.isLoaded(this)) array.add(t.mId);
-        }
-        if (array.size() > 0) {
-            mGL.glDeleteTextures(array.size(), array.toArray(null), 0);
-        }
-    }
-
-    public int save() {
-        return save(ALL_SAVE_FLAG);
-    }
-
-    public int save(int saveFlags) {
-        ConfigState config = obtainRestoreConfig();
-
-        if ((saveFlags & ALPHA_SAVE_FLAG) != 0) {
-            config.mAlpha = mAlpha;
-        } else {
-            config.mAlpha = -1;
-        }
-
-        if ((saveFlags & CLIP_SAVE_FLAG) != 0) {
-            config.mRect.set(mClipRect);
-        } else {
-            config.mRect.left = Integer.MAX_VALUE;
-        }
-
-        if ((saveFlags & MATRIX_SAVE_FLAG) != 0) {
-            System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16);
-        } else {
-            config.mMatrix[0] = Float.NEGATIVE_INFINITY;
-        }
-
-        mRestoreStack.push(config);
-        return mRestoreStack.size() - 1;
-    }
-
-    public void restore() {
-        if (mRestoreStack.isEmpty()) throw new IllegalStateException();
-        ConfigState config = mRestoreStack.pop();
-        config.restore(this);
-        freeRestoreConfig(config);
-    }
-
-    public void restoreToCount(int saveCount) {
-        while (mRestoreStack.size() > saveCount) {
-            restore();
-        }
-    }
-
-    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;
-        Rect mRect = new Rect();
-        float mMatrix[] = new float[16];
-        ConfigState mNextFree;
-
-        public void restore(GLCanvas canvas) {
-            if (mAlpha >= 0) canvas.setAlpha(mAlpha);
-            if (mRect.left != Integer.MAX_VALUE) {
-                Rect rect = mRect;
-                canvas.mClipRect.set(rect);
-                canvas.mGL.glScissor(
-                        rect.left, rect.top, rect.width(), rect.height());
-            }
-            if (mMatrix[0] != Float.NEGATIVE_INFINITY) {
-                System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16);
-            }
-        }
-    }
+    // Gets the underlying GL instance. This is used when
+    public GL11 getGLInstance();
 }
diff --git a/new3d/src/com/android/gallery3d/ui/GLCanvasImp.java b/new3d/src/com/android/gallery3d/ui/GLCanvasImp.java
new file mode 100644
index 0000000..33796f1
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/GLCanvasImp.java
@@ -0,0 +1,770 @@
+package com.android.gallery3d.ui;
+
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.opengl.GLU;
+import android.opengl.Matrix;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.util.Collection;
+import java.util.Stack;
+
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11Ext;
+
+public class GLCanvasImp implements GLCanvas {
+
+    // 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 float OPAQUE_ALPHA = 0.95f;
+
+    private final GL11 mGL;
+
+    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 final int mNinePatchX[] = new int[4];
+    private final int mNinePatchY[] = new int[4];
+    private final float mNinePatchU[] = new float[4];
+    private final float mNinePatchV[] = new float[4];
+    private final float mTextureColor[] = new float[4];
+
+    private FloatBuffer mXyPointer;
+    private FloatBuffer mUvPointer;
+    private ByteBuffer mIndexPointer;
+
+    private final GLState mGLState;
+
+    private long mAnimationTime;
+
+    private float mAlpha;
+    private final Rect mClipRect = new Rect();
+    private final Stack<ConfigState> mRestoreStack =
+            new Stack<ConfigState>();
+    private ConfigState mRecycledRestoreAction;
+
+    GLCanvasImp(GL11 gl) {
+        mGL = gl;
+        mGLState = new GLState(gl);
+        initialize();
+    }
+
+    public void setSize(int width, int height) {
+        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();
+
+        // The positive direction in Y coordinate in OpenGL is from bottom to
+        // top, which is different from the coordinate system in Java. So, we
+        // flip it here.
+        float matrix[] = mMatrixValues;
+        Matrix.setIdentityM(matrix, 0);
+        Matrix.translateM(matrix, 0, 0, height, 0);
+        Matrix.scaleM(matrix, 0, 1, -1, 1);
+
+        mClipRect.set(0, 0, width, height);
+        gl.glScissor(0, 0, width, height);
+    }
+
+    public long currentAnimationTimeMillis() {
+        return mAnimationTime;
+    }
+
+    public void setAlpha(float alpha) {
+        mAlpha = alpha;
+    }
+
+    public void multiplyAlpha(float alpha) {
+        mAlpha *= alpha;
+    }
+
+    public float getAlpha() {
+        return mAlpha;
+    }
+
+    private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
+        return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
+    }
+
+    private void initialize() {
+        int size = VERTEX_BUFFER_SIZE * Float.SIZE / Byte.SIZE;
+        mXyPointer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
+        mUvPointer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
+        mIndexPointer = allocateDirectNativeOrderBuffer(INDEX_BUFFER_SIZE);
+
+        GL11 gl = mGL;
+
+        gl.glVertexPointer(2, GL11.GL_FLOAT, 0, mXyPointer);
+        gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, mUvPointer);
+
+        // Enable the texture coordinate array for Texture 1
+        gl.glClientActiveTexture(GL11.GL_TEXTURE1);
+        gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, mUvPointer);
+        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
+
+        // mMatrixValues will be initialized in setSize()
+        mAlpha = 1.0f;
+    }
+
+    private static void putRectangle(float x, float y,
+            float width, float height, float[] buffer, FloatBuffer 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.put(buffer, 0, 8).position(0);
+    }
+
+    public void setColor(int color) {
+        float alpha = mAlpha;
+        mGLState.setBlendEnabled(!Util.isOpaque(color) || alpha < OPAQUE_ALPHA);
+        mGLState.setFragmentColor(color, alpha);
+    }
+
+    public void drawLine(int x1, int y1, int x2, int y2) {
+        GL11 gl = mGL;
+        gl.glLoadMatrixf(mMatrixValues, 0);
+        float buffer[] = mXyBuffer;
+        buffer[0] = x1;
+        buffer[1] = y1;
+        buffer[2] = x2;
+        buffer[3] = y2;
+        mXyPointer.put(buffer, 0, 4).position(0);
+        gl.glDrawArrays(GL11.GL_LINE_STRIP, 0, 2);
+    }
+
+    public void fillRect(Rect r) {
+        fillRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
+    }
+
+    public void translate(float x, float y, float z) {
+        Matrix.translateM(mMatrixValues, 0, x, y, z);
+    }
+
+    public void scale(float sx, float sy, float sz) {
+        Matrix.scaleM(mMatrixValues, 0, sx, sy, sz);
+    }
+
+    public void rotate(float angle, float x, float y, float z) {
+        Matrix.rotateM(mMatrixValues, 0, angle, x, y, z);
+    }
+
+    public void fillRect(int x, int y, int width, int height) {
+        GL11 gl = mGL;
+        gl.glLoadMatrixf(mMatrixValues, 0);
+        putRectangle(x, y, width, height, mXyBuffer, mXyPointer);
+        gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, 4);
+    }
+
+    public void drawNinePatch(
+            NinePatchTexture tex, int x, int y, int width, int height) {
+        float alpha = mAlpha;
+        NinePatchChunk chunk = tex.getNinePatchChunk();
+
+        mGLState.setTexture2DEnabled(true);
+
+        // 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");
+        }
+        tex.bind(this);
+        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);
+        mGLState.setBlendEnabled(!tex.isOpaque() || alpha < OPAQUE_ALPHA);
+
+        mGLState.setTextureAlpha(alpha);
+
+        GL11 gl = mGL;
+        gl.glLoadMatrixf(mMatrixValues, 0);
+        gl.glTranslatef(x, y, 0);
+        drawMesh(divX, divY, divU, divV, nx, ny);
+    }
+
+    /**
+     * 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.put(uv, 0, pntCount << 1).position(0);
+        mXyPointer.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(float matrix[], int x1, int y1, int x2, int y2) {
+        float[] point = mXyBuffer;
+        int srcOffset = 6;
+        point[srcOffset] = x1;
+        point[srcOffset + 1] = y1;
+        point[srcOffset + 2] = 0;
+        point[srcOffset + 3] = 1;
+
+        int resultOffset = 0;
+        Matrix.multiplyMV(point, resultOffset, matrix, 0, point, srcOffset);
+        point[resultOffset] /= point[resultOffset + 3];
+        point[resultOffset + 1] /= point[resultOffset + 3];
+
+        // map the second point
+        point[srcOffset] = x2;
+        point[srcOffset + 1] = y2;
+        resultOffset = 2;
+        Matrix.multiplyMV(point, resultOffset, matrix, 0, point, srcOffset);
+        point[resultOffset] /= point[resultOffset + 3];
+        point[resultOffset + 1] /= point[resultOffset + 3];
+
+        return point;
+    }
+
+
+    public boolean clipRect(int left, int top, int right, int bottom) {
+        float point[] = mapPoints(mMatrixValues, left, top, right, bottom);
+
+        // 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]) {
+            left = (int) point[2];
+            right = (int) point[0];
+        } else {
+            left = (int) point[0];
+            right = (int) point[2];
+        }
+        if (point[1] > point[3]) {
+            top = (int) point[3];
+            bottom = (int) point[1];
+        } else {
+            top = (int) point[1];
+            bottom = (int) point[3];
+        }
+        Rect clip = mClipRect;
+
+        boolean intersect = clip.intersect(left, top, right, bottom);
+        if (!intersect) clip.set(0, 0, 0, 0);
+        mGL.glScissor(clip.left, clip.top, clip.width(), clip.height());
+        return intersect;
+    }
+
+    public void drawColor(int x, int y, int width, int height, int color) {
+        float alpha = mAlpha;
+        mGLState.setBlendEnabled(!Util.isOpaque(color) || alpha < OPAQUE_ALPHA);
+        mGLState.setFragmentColor(color, alpha);
+        fillRect(x, y, width, height);
+    }
+
+    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)) {
+            putRectangle(0, 0,
+                    (texture.mWidth - 0.5f) / texture.mTextureWidth,
+                    (texture.mHeight - 0.5f) / texture.mTextureHeight,
+                    mUvBuffer, mUvPointer);
+            fillRect(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];
+            y = (int) points[1];
+            width = (int) points[2] - x;
+            height = (int) points[3] - y;
+            if (width > 0 && height > 0) {
+                ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height);
+            }
+        }
+
+    }
+
+    public void drawTexture(
+            BasicTexture texture, int x, int y, int width, int height) {
+        drawTexture(texture, x, y, width, height, mAlpha);
+    }
+
+    public void drawTexture(BasicTexture texture,
+            int x, int y, int width, int height, float alpha) {
+        if (width <= 0 || height <= 0) return ;
+
+        mGLState.setBlendEnabled(!texture.isOpaque() || alpha < OPAQUE_ALPHA);
+        mGLState.setTexture2DEnabled(true);
+        texture.bind(this);
+        mGLState.setTextureAlpha(alpha);
+        drawBoundTexture(texture, x, y, width, height);
+    }
+
+    public void drawMixed(BasicTexture from, BasicTexture to,
+            float ratio, int x, int y, int w, int h) {
+        drawMixed(from, to, ratio, x, y, w, h, mAlpha);
+    }
+
+    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;
+    }
+
+    public void handleLowMemory() {
+    }
+
+    public void drawMixed(BasicTexture from, BasicTexture to,
+            float ratio, int x, int y, int width, int height, float alpha) {
+        if (alpha < OPAQUE_ALPHA) {
+            throw new RuntimeException("Cannot support alpha value");
+        }
+        mGLState.setBlendEnabled(!from.isOpaque() || !to.isOpaque());
+        mGLState.setTextureAlpha(1);
+        mGLState.setTexture2DEnabled(true);
+
+        final GL11 gl = mGL;
+        from.bind(this);
+
+        gl.glActiveTexture(GL11.GL_TEXTURE1);
+        to.bind(this);
+        gl.glEnable(GL11.GL_TEXTURE_2D);
+
+        // Interpolate the RGB and alpha values between both textures.
+        mGLState.setTexEnvMode(GL11.GL_COMBINE);
+        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);
+
+        // Specify the interpolation factor via the alpha component of
+        // GL_TEXTURE_ENV_COLORes.
+        setTextureColor(ratio, ratio, ratio, ratio);
+        gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);
+
+        // 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);
+
+        // Draw the combined texture.
+        drawBoundTexture(to, x, y, width, height);
+
+        // Disable TEXTURE1.
+        gl.glDisable(GL11.GL_TEXTURE_2D);
+        // Switch back to the default texture unit.
+        gl.glActiveTexture(GL11.GL_TEXTURE0);
+    }
+
+    // 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[]) {
+        return matrix[MSKEW_X] != 0 || matrix[MSKEW_Y] != 0
+                || matrix[MSCALE_X] < 0 || matrix[MSCALE_Y] > 0;
+    }
+
+    public void copyTexture2D(
+            RawTexture texture, int x, int y, int width, int height) {
+
+        if (isMatrixRotatedOrFlipped(mMatrixValues)) {
+            throw new IllegalArgumentException("cannot support rotated matrix");
+        }
+        float points[] = mapPoints(mMatrixValues, 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);
+    }
+
+    private static class GLState {
+
+        private final GL11 mGL;
+
+        private int mTexEnvMode = GL11.GL_REPLACE;
+        private float mTextureAlpha = 1.0f;
+        private boolean mTexture2DEnabled = true;
+        private boolean mBlendEnabled = true;
+
+        public GLState(GL11 gl) {
+            mGL = gl;
+
+            // Disable unused state
+            gl.glDisable(GL11.GL_LIGHTING);
+
+            // Enable used features
+            gl.glEnable(GL11.GL_DITHER);
+
+            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);
+
+            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);
+        }
+
+        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 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 setFragmentColor(int color, float alpha) {
+            // Set mTextureAlpha to an invalid value, so that it will reset
+            // again in setTextureAlpha(float) later.
+            mTextureAlpha = -1.0f;
+
+            setTexture2DEnabled(false);
+            int prealpha = (int) ((color >>> 24) * alpha + 0.5);
+            mGL.glColor4x(
+                    ((color >> 16) & 0xFF) * prealpha,
+                    ((color >> 8) & 0xFF) * prealpha,
+                    (color & 0xFF) * prealpha, prealpha << 8);
+        }
+
+        public void setTexture2DEnabled(boolean enabled) {
+            if (mTexture2DEnabled == enabled) return;
+            mTexture2DEnabled = enabled;
+            if (enabled) {
+                mGL.glEnable(GL11.GL_TEXTURE_2D);
+            } else {
+                mGL.glDisable(GL11.GL_TEXTURE_2D);
+            }
+        }
+
+        public void setBlendEnabled(boolean enabled) {
+            if (mBlendEnabled == enabled) return;
+            mBlendEnabled = enabled;
+            if (enabled) {
+                mGL.glEnable(GL11.GL_BLEND);
+            } else {
+                mGL.glDisable(GL11.GL_BLEND);
+            }
+        }
+    }
+
+    public GL11 getGLInstance() {
+        return mGL;
+    }
+
+    public void setCurrentAnimationTimeMillis(long time) {
+        if (time < 0) throw new IllegalArgumentException("time must be nonnegative");
+        mAnimationTime = time;
+    }
+
+    public void clearBuffer() {
+        mGL.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_STENCIL_BUFFER_BIT);
+    }
+
+    public void setTextureCoords(RectF source) {
+        float buffer[] = mUvBuffer;
+        buffer[0] = source.left;
+        buffer[1] = source.top;
+        buffer[2] = source.right;
+        buffer[3] = source.top;
+        buffer[4] = source.left;
+        buffer[5] = source.bottom;
+        buffer[6] = source.right;
+        buffer[7] = source.bottom;
+        mUvPointer.put(buffer, 0, 8).position(0);
+    }
+
+    public void releaseTextures(Collection<? extends BasicTexture> c) {
+        IntArray array = new IntArray();
+        for (BasicTexture t : c) {
+            if (t.isLoaded(this)) array.add(t.mId);
+        }
+        if (array.size() > 0) {
+            mGL.glDeleteTextures(array.size(), array.toArray(null), 0);
+        }
+    }
+
+    public int save() {
+        return save(SAVE_FLAG_ALL);
+    }
+
+    public int save(int saveFlags) {
+        ConfigState config = obtainRestoreConfig();
+
+        if ((saveFlags & SAVE_FLAG_ALPHA) != 0) {
+            config.mAlpha = mAlpha;
+        } else {
+            config.mAlpha = -1;
+        }
+
+        if ((saveFlags & SAVE_FLAG_CLIP) != 0) {
+            config.mRect.set(mClipRect);
+        } else {
+            config.mRect.left = Integer.MAX_VALUE;
+        }
+
+        if ((saveFlags & SAVE_FLAG_MATRIX) != 0) {
+            System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16);
+        } else {
+            config.mMatrix[0] = Float.NEGATIVE_INFINITY;
+        }
+
+        mRestoreStack.push(config);
+        return mRestoreStack.size() - 1;
+    }
+
+    public void restore() {
+        if (mRestoreStack.isEmpty()) throw new IllegalStateException();
+        ConfigState config = mRestoreStack.pop();
+        config.restore(this);
+        freeRestoreConfig(config);
+    }
+
+    public void restoreToCount(int saveCount) {
+        while (mRestoreStack.size() > saveCount) {
+            restore();
+        }
+    }
+
+    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;
+        Rect mRect = new Rect();
+        float mMatrix[] = new float[16];
+        ConfigState mNextFree;
+
+        public void restore(GLCanvasImp canvas) {
+            if (mAlpha >= 0) canvas.setAlpha(mAlpha);
+            if (mRect.left != Integer.MAX_VALUE) {
+                Rect rect = mRect;
+                canvas.mClipRect.set(rect);
+                canvas.mGL.glScissor(
+                        rect.left, rect.top, rect.width(), rect.height());
+            }
+            if (mMatrix[0] != Float.NEGATIVE_INFINITY) {
+                System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16);
+            }
+        }
+    }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/GLListView.java b/new3d/src/com/android/gallery3d/ui/GLListView.java
index b2042c5..edc1627 100644
--- a/new3d/src/com/android/gallery3d/ui/GLListView.java
+++ b/new3d/src/com/android/gallery3d/ui/GLListView.java
@@ -132,7 +132,7 @@
 
     @Override
     protected void render(GLCanvas canvas) {
-        canvas.save(GLCanvas.CLIP_SAVE_FLAG);
+        canvas.save(GLCanvas.SAVE_FLAG_CLIP);
         canvas.clipRect(0, 0, getWidth(), getHeight());
         if (mHighlightIndex != INDEX_NONE) {
             GLView view = mModel.getView(mHighlightIndex);
diff --git a/new3d/src/com/android/gallery3d/ui/GLRootView.java b/new3d/src/com/android/gallery3d/ui/GLRootView.java
index 659a4be..81cc256 100644
--- a/new3d/src/com/android/gallery3d/ui/GLRootView.java
+++ b/new3d/src/com/android/gallery3d/ui/GLRootView.java
@@ -167,7 +167,7 @@
             Log.i(TAG, "GLObject has changed from " + mGL + " to " + gl);
         }
         mGL = gl;
-        mCanvas = new GLCanvas(gl);
+        mCanvas = new GLCanvasImp(gl);
         if (!ENABLE_FPS_TEST) {
             setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
         } else {
@@ -207,7 +207,7 @@
 
         if ((mFlags & FLAG_NEED_LAYOUT) != 0) layoutContentPane();
         mCanvas.clearBuffer();
-        mCanvas.setCurrentTimeMillis(SystemClock.uptimeMillis());
+        mCanvas.setCurrentAnimationTimeMillis(SystemClock.uptimeMillis());
         if (mContentView != null) {
            mContentView.render(mCanvas);
         }
diff --git a/new3d/src/com/android/gallery3d/ui/Pathbar.java b/new3d/src/com/android/gallery3d/ui/Pathbar.java
index 06cf4e0..b92f1da 100644
--- a/new3d/src/com/android/gallery3d/ui/Pathbar.java
+++ b/new3d/src/com/android/gallery3d/ui/Pathbar.java
@@ -251,7 +251,7 @@
             int degrees = mProgressStep * 360 / PROGRESS_STEP_COUNT;
             int pivotX = offsetX + icon.getWidth() / 2;
             int pivotY = offsetY + icon.getHeight() / 2;
-            canvas.save(GLCanvas.MATRIX_SAVE_FLAG);
+            canvas.save(GLCanvas.SAVE_FLAG_MATRIX);
             canvas.translate(pivotX, pivotY, 0);
             canvas.rotate(degrees, 0, 0, 1);
             canvas.translate(-pivotX, -pivotY, 0);
diff --git a/new3d/src/com/android/gallery3d/ui/ScrollerHelper.java b/new3d/src/com/android/gallery3d/ui/ScrollerHelper.java
index 916a637..a14de7d 100644
--- a/new3d/src/com/android/gallery3d/ui/ScrollerHelper.java
+++ b/new3d/src/com/android/gallery3d/ui/ScrollerHelper.java
@@ -6,7 +6,7 @@
 import android.view.animation.Interpolator;
 
 public class ScrollerHelper {
-    private static final long ANIMATION_START = 0;
+    private static final long START_ANIMATION = -1;
 
     private final float mDeceleration;
 
@@ -38,7 +38,7 @@
      */
     public boolean computeScrollOffset(long currentTimeMillis) {
         if (mFinished) return false;
-        if (mStartTime == ANIMATION_START) mStartTime = currentTimeMillis;
+        if (mStartTime == START_ANIMATION) mStartTime = currentTimeMillis;
 
         int timePassed = (int)(currentTimeMillis - mStartTime);
         if (timePassed < mDuration) {
@@ -70,7 +70,7 @@
         mVelocity = Math.abs(velocity);
         mDirection = velocity >= 0 ? 1 : -1;
         mDuration = (int) (1000 * mVelocity / mDeceleration);
-        mStartTime = ANIMATION_START;
+        mStartTime = START_ANIMATION;
         mStart = start;
         mMin = min;
         mMax = max;
diff --git a/new3d/tests/src/com/android/gallery3d/ui/GLCanvasTest.java b/new3d/tests/src/com/android/gallery3d/ui/GLCanvasTest.java
new file mode 100644
index 0000000..597ca7b
--- /dev/null
+++ b/new3d/tests/src/com/android/gallery3d/ui/GLCanvasTest.java
@@ -0,0 +1,41 @@
+package com.android.gallery3d.ui;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.util.Arrays;
+import javax.microedition.khronos.opengles.GL11;
+import junit.framework.TestCase;
+
+@SmallTest
+public class GLCanvasTest extends TestCase {
+    private static final String TAG = "GLCanvasTest";
+
+    GL11 mGlStub;
+    GLCanvas mCanvas;
+
+    public void setUp() {
+        mGlStub = new GLStub();
+        mCanvas = new GLCanvasImp(mGlStub);
+    }
+
+    public void testGetGLInstance() {
+        assertSame(mGlStub, mCanvas.getGLInstance());
+    }
+
+    public void testAnimationTime() {
+        long[] testData = {0, 1, 2, 1000, 10000, Long.MAX_VALUE};
+
+        for (long v : testData) {
+            mCanvas.setCurrentAnimationTimeMillis(v);
+            assertEquals(v, mCanvas.currentAnimationTimeMillis());
+        }
+
+        try {
+            mCanvas.setCurrentAnimationTimeMillis(-1);
+            fail();
+        } catch (IllegalArgumentException ex) {
+            // expected.
+        }
+    }
+}
diff --git a/new3d/tests/src/com/android/gallery3d/ui/GLStub.java b/new3d/tests/src/com/android/gallery3d/ui/GLStub.java
new file mode 100644
index 0000000..3b4d7d6
--- /dev/null
+++ b/new3d/tests/src/com/android/gallery3d/ui/GLStub.java
@@ -0,0 +1,1476 @@
+package com.android.gallery3d.ui;
+
+import android.util.Log;
+
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL10Ext;
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11Ext;
+import javax.microedition.khronos.opengles.GL11ExtensionPack;
+
+public class GLStub implements GL, GL10, GL10Ext, GL11, GL11Ext {
+    private static final String TAG = "GLStub";
+
+    public void glActiveTexture(
+        int texture
+    ){}
+
+    public void glAlphaFunc(
+        int func,
+        float ref
+    ){}
+
+    public void glAlphaFuncx(
+        int func,
+        int ref
+    ){}
+
+    public void glBindTexture(
+        int target,
+        int texture
+    ){}
+
+    public void glBlendFunc(
+        int sfactor,
+        int dfactor
+    ){}
+
+    public void glClear(
+        int mask
+    ){}
+
+    public void glClearColor(
+        float red,
+        float green,
+        float blue,
+        float alpha
+    ){}
+
+    public void glClearColorx(
+        int red,
+        int green,
+        int blue,
+        int alpha
+    ){}
+
+    public void glClearDepthf(
+        float depth
+    ){}
+
+    public void glClearDepthx(
+        int depth
+    ){}
+
+    public void glClearStencil(
+        int s
+    ){}
+
+    public void glClientActiveTexture(
+        int texture
+    ){}
+
+    public void glColor4f(
+        float red,
+        float green,
+        float blue,
+        float alpha
+    ){}
+
+    public void glColor4x(
+        int red,
+        int green,
+        int blue,
+        int alpha
+    ){}
+
+    public void glColorMask(
+        boolean red,
+        boolean green,
+        boolean blue,
+        boolean alpha
+    ){}
+
+    public void glColorPointer(
+        int size,
+        int type,
+        int stride,
+        java.nio.Buffer pointer
+    ){}
+
+    public void glCompressedTexImage2D(
+        int target,
+        int level,
+        int internalformat,
+        int width,
+        int height,
+        int border,
+        int imageSize,
+        java.nio.Buffer data
+    ){}
+
+    public void glCompressedTexSubImage2D(
+        int target,
+        int level,
+        int xoffset,
+        int yoffset,
+        int width,
+        int height,
+        int format,
+        int imageSize,
+        java.nio.Buffer data
+    ){}
+
+    public void glCopyTexImage2D(
+        int target,
+        int level,
+        int internalformat,
+        int x,
+        int y,
+        int width,
+        int height,
+        int border
+    ){}
+
+    public void glCopyTexSubImage2D(
+        int target,
+        int level,
+        int xoffset,
+        int yoffset,
+        int x,
+        int y,
+        int width,
+        int height
+    ){}
+
+    public void glCullFace(
+        int mode
+    ){}
+
+    public void glDeleteTextures(
+        int n,
+        int[] textures,
+        int offset
+    ){}
+
+    public void glDeleteTextures(
+        int n,
+        java.nio.IntBuffer textures
+    ){}
+
+    public void glDepthFunc(
+        int func
+    ){}
+
+    public void glDepthMask(
+        boolean flag
+    ){}
+
+    public void glDepthRangef(
+        float zNear,
+        float zFar
+    ){}
+
+    public void glDepthRangex(
+        int zNear,
+        int zFar
+    ){}
+
+    public void glDisable(
+        int cap
+    ){}
+
+    public void glDisableClientState(
+        int array
+    ){}
+
+    public void glDrawArrays(
+        int mode,
+        int first,
+        int count
+    ){}
+
+    public void glDrawElements(
+        int mode,
+        int count,
+        int type,
+        java.nio.Buffer indices
+    ){}
+
+    public void glEnable(
+        int cap
+    ){}
+
+    public void glEnableClientState(
+        int array
+    ){}
+
+    public void glFinish(
+    ){}
+
+    public void glFlush(
+    ){}
+
+    public void glFogf(
+        int pname,
+        float param
+    ){}
+
+    public void glFogfv(
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glFogfv(
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glFogx(
+        int pname,
+        int param
+    ){}
+
+    public void glFogxv(
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glFogxv(
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glFrontFace(
+        int mode
+    ){}
+
+    public void glFrustumf(
+        float left,
+        float right,
+        float bottom,
+        float top,
+        float zNear,
+        float zFar
+    ){}
+
+    public void glFrustumx(
+        int left,
+        int right,
+        int bottom,
+        int top,
+        int zNear,
+        int zFar
+    ){}
+
+    public void glGenTextures(
+        int n,
+        int[] textures,
+        int offset
+    ){}
+
+    public void glGenTextures(
+        int n,
+        java.nio.IntBuffer textures
+    ){}
+
+    public int glGetError(
+    ){ throw new UnsupportedOperationException(); }
+
+    public void glGetIntegerv(
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetIntegerv(
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public String glGetString(
+        int name
+    ){ throw new UnsupportedOperationException(); }
+
+    public void glHint(
+        int target,
+        int mode
+    ){}
+
+    public void glLightModelf(
+        int pname,
+        float param
+    ){}
+
+    public void glLightModelfv(
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glLightModelfv(
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glLightModelx(
+        int pname,
+        int param
+    ){}
+
+    public void glLightModelxv(
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glLightModelxv(
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glLightf(
+        int light,
+        int pname,
+        float param
+    ){}
+
+    public void glLightfv(
+        int light,
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glLightfv(
+        int light,
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glLightx(
+        int light,
+        int pname,
+        int param
+    ){}
+
+    public void glLightxv(
+        int light,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glLightxv(
+        int light,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glLineWidth(
+        float width
+    ){}
+
+    public void glLineWidthx(
+        int width
+    ){}
+
+    public void glLoadIdentity(
+    ){}
+
+    public void glLoadMatrixf(
+        float[] m,
+        int offset
+    ){}
+
+    public void glLoadMatrixf(
+        java.nio.FloatBuffer m
+    ){}
+
+    public void glLoadMatrixx(
+        int[] m,
+        int offset
+    ){}
+
+    public void glLoadMatrixx(
+        java.nio.IntBuffer m
+    ){}
+
+    public void glLogicOp(
+        int opcode
+    ){}
+
+    public void glMaterialf(
+        int face,
+        int pname,
+        float param
+    ){}
+
+    public void glMaterialfv(
+        int face,
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glMaterialfv(
+        int face,
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glMaterialx(
+        int face,
+        int pname,
+        int param
+    ){}
+
+    public void glMaterialxv(
+        int face,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glMaterialxv(
+        int face,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glMatrixMode(
+        int mode
+    ){}
+
+    public void glMultMatrixf(
+        float[] m,
+        int offset
+    ){}
+
+    public void glMultMatrixf(
+        java.nio.FloatBuffer m
+    ){}
+
+    public void glMultMatrixx(
+        int[] m,
+        int offset
+    ){}
+
+    public void glMultMatrixx(
+        java.nio.IntBuffer m
+    ){}
+
+    public void glMultiTexCoord4f(
+        int target,
+        float s,
+        float t,
+        float r,
+        float q
+    ){}
+
+    public void glMultiTexCoord4x(
+        int target,
+        int s,
+        int t,
+        int r,
+        int q
+    ){}
+
+    public void glNormal3f(
+        float nx,
+        float ny,
+        float nz
+    ){}
+
+    public void glNormal3x(
+        int nx,
+        int ny,
+        int nz
+    ){}
+
+    public void glNormalPointer(
+        int type,
+        int stride,
+        java.nio.Buffer pointer
+    ){}
+
+    public void glOrthof(
+        float left,
+        float right,
+        float bottom,
+        float top,
+        float zNear,
+        float zFar
+    ){}
+
+    public void glOrthox(
+        int left,
+        int right,
+        int bottom,
+        int top,
+        int zNear,
+        int zFar
+    ){}
+
+    public void glPixelStorei(
+        int pname,
+        int param
+    ){}
+
+    public void glPointSize(
+        float size
+    ){}
+
+    public void glPointSizex(
+        int size
+    ){}
+
+    public void glPolygonOffset(
+        float factor,
+        float units
+    ){}
+
+    public void glPolygonOffsetx(
+        int factor,
+        int units
+    ){}
+
+    public void glPopMatrix(
+    ){}
+
+    public void glPushMatrix(
+    ){}
+
+    public void glReadPixels(
+        int x,
+        int y,
+        int width,
+        int height,
+        int format,
+        int type,
+        java.nio.Buffer pixels
+    ){}
+
+    public void glRotatef(
+        float angle,
+        float x,
+        float y,
+        float z
+    ){}
+
+    public void glRotatex(
+        int angle,
+        int x,
+        int y,
+        int z
+    ){}
+
+    public void glSampleCoverage(
+        float value,
+        boolean invert
+    ){}
+
+    public void glSampleCoveragex(
+        int value,
+        boolean invert
+    ){}
+
+    public void glScalef(
+        float x,
+        float y,
+        float z
+    ){}
+
+    public void glScalex(
+        int x,
+        int y,
+        int z
+    ){}
+
+    public void glScissor(
+        int x,
+        int y,
+        int width,
+        int height
+    ){}
+
+    public void glShadeModel(
+        int mode
+    ){}
+
+    public void glStencilFunc(
+        int func,
+        int ref,
+        int mask
+    ){}
+
+    public void glStencilMask(
+        int mask
+    ){}
+
+    public void glStencilOp(
+        int fail,
+        int zfail,
+        int zpass
+    ){}
+
+    public void glTexCoordPointer(
+        int size,
+        int type,
+        int stride,
+        java.nio.Buffer pointer
+    ){}
+
+    public void glTexEnvf(
+        int target,
+        int pname,
+        float param
+    ){}
+
+    public void glTexEnvfv(
+        int target,
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glTexEnvfv(
+        int target,
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glTexEnvx(
+        int target,
+        int pname,
+        int param
+    ){}
+
+    public void glTexEnvxv(
+        int target,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glTexEnvxv(
+        int target,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glTexImage2D(
+        int target,
+        int level,
+        int internalformat,
+        int width,
+        int height,
+        int border,
+        int format,
+        int type,
+        java.nio.Buffer pixels
+    ){}
+
+    public void glTexParameterf(
+        int target,
+        int pname,
+        float param
+    ){}
+
+    public void glTexParameterx(
+        int target,
+        int pname,
+        int param
+    ){}
+
+    public void glTexSubImage2D(
+        int target,
+        int level,
+        int xoffset,
+        int yoffset,
+        int width,
+        int height,
+        int format,
+        int type,
+        java.nio.Buffer pixels
+    ){}
+
+    public void glTranslatef(
+        float x,
+        float y,
+        float z
+    ){}
+
+    public void glTranslatex(
+        int x,
+        int y,
+        int z
+    ){}
+
+    public void glVertexPointer(
+        int size,
+        int type,
+        int stride,
+        java.nio.Buffer pointer
+    ){}
+
+    public void glViewport(
+        int x,
+        int y,
+        int width,
+        int height
+    ){}
+
+    public int glQueryMatrixxOES(
+        int[] mantissa,
+        int mantissaOffset,
+        int[] exponent,
+        int exponentOffset
+    ){ throw new UnsupportedOperationException(); }
+
+    public int glQueryMatrixxOES(
+        java.nio.IntBuffer mantissa,
+        java.nio.IntBuffer exponent
+    ){ throw new UnsupportedOperationException(); }
+
+    public void glGetPointerv(int pname, java.nio.Buffer[] params){}
+    public void glBindBuffer(
+        int target,
+        int buffer
+    ){}
+
+    public void glBufferData(
+        int target,
+        int size,
+        java.nio.Buffer data,
+        int usage
+    ){}
+
+    public void glBufferSubData(
+        int target,
+        int offset,
+        int size,
+        java.nio.Buffer data
+    ){}
+
+    public void glClipPlanef(
+        int plane,
+        float[] equation,
+        int offset
+    ){}
+
+    public void glClipPlanef(
+        int plane,
+        java.nio.FloatBuffer equation
+    ){}
+
+    public void glClipPlanex(
+        int plane,
+        int[] equation,
+        int offset
+    ){}
+
+    public void glClipPlanex(
+        int plane,
+        java.nio.IntBuffer equation
+    ){}
+
+    public void glColor4ub(
+        byte red,
+        byte green,
+        byte blue,
+        byte alpha
+    ){}
+
+    public void glColorPointer(
+        int size,
+        int type,
+        int stride,
+        int offset
+    ){}
+
+    public void glDeleteBuffers(
+        int n,
+        int[] buffers,
+        int offset
+    ){}
+
+    public void glDeleteBuffers(
+        int n,
+        java.nio.IntBuffer buffers
+    ){}
+
+    public void glDrawElements(
+        int mode,
+        int count,
+        int type,
+        int offset
+    ){}
+
+    public void glGenBuffers(
+        int n,
+        int[] buffers,
+        int offset
+    ){}
+
+    public void glGenBuffers(
+        int n,
+        java.nio.IntBuffer buffers
+    ){}
+
+    public void glGetBooleanv(
+        int pname,
+        boolean[] params,
+        int offset
+    ){}
+
+    public void glGetBooleanv(
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glGetBufferParameteriv(
+        int target,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetBufferParameteriv(
+        int target,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glGetClipPlanef(
+        int pname,
+        float[] eqn,
+        int offset
+    ){}
+
+    public void glGetClipPlanef(
+        int pname,
+        java.nio.FloatBuffer eqn
+    ){}
+
+    public void glGetClipPlanex(
+        int pname,
+        int[] eqn,
+        int offset
+    ){}
+
+    public void glGetClipPlanex(
+        int pname,
+        java.nio.IntBuffer eqn
+    ){}
+
+    public void glGetFixedv(
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetFixedv(
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glGetFloatv(
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glGetFloatv(
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glGetLightfv(
+        int light,
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glGetLightfv(
+        int light,
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glGetLightxv(
+        int light,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetLightxv(
+        int light,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glGetMaterialfv(
+        int face,
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glGetMaterialfv(
+        int face,
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glGetMaterialxv(
+        int face,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetMaterialxv(
+        int face,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glGetTexEnviv(
+        int env,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetTexEnviv(
+        int env,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glGetTexEnvxv(
+        int env,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetTexEnvxv(
+        int env,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glGetTexParameterfv(
+        int target,
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glGetTexParameterfv(
+        int target,
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glGetTexParameteriv(
+        int target,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetTexParameteriv(
+        int target,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glGetTexParameterxv(
+        int target,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetTexParameterxv(
+        int target,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public boolean glIsBuffer(
+        int buffer
+    ){ throw new UnsupportedOperationException(); }
+
+    public boolean glIsEnabled(
+        int cap
+    ){ throw new UnsupportedOperationException(); }
+
+    public boolean glIsTexture(
+        int texture
+    ){ throw new UnsupportedOperationException(); }
+
+    public void glNormalPointer(
+        int type,
+        int stride,
+        int offset
+    ){}
+
+    public void glPointParameterf(
+        int pname,
+        float param
+    ){}
+
+    public void glPointParameterfv(
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glPointParameterfv(
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glPointParameterx(
+        int pname,
+        int param
+    ){}
+
+    public void glPointParameterxv(
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glPointParameterxv(
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glPointSizePointerOES(
+        int type,
+        int stride,
+        java.nio.Buffer pointer
+    ){}
+
+    public void glTexCoordPointer(
+        int size,
+        int type,
+        int stride,
+        int offset
+    ){}
+
+    public void glTexEnvi(
+        int target,
+        int pname,
+        int param
+    ){}
+
+    public void glTexEnviv(
+        int target,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glTexEnviv(
+        int target,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glTexParameterfv(
+        int target,
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glTexParameterfv(
+        int target,
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glTexParameteri(
+        int target,
+        int pname,
+        int param
+    ){}
+
+    public void glTexParameteriv(
+        int target,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glTexParameteriv(
+        int target,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glTexParameterxv(
+        int target,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glTexParameterxv(
+        int target,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glVertexPointer(
+        int size,
+        int type,
+        int stride,
+        int offset
+    ){}
+
+    public void glCurrentPaletteMatrixOES(
+        int matrixpaletteindex
+    ){}
+
+    public void glDrawTexfOES(
+        float x,
+        float y,
+        float z,
+        float width,
+        float height
+    ){}
+
+    public void glDrawTexfvOES(
+        float[] coords,
+        int offset
+    ){}
+
+    public void glDrawTexfvOES(
+        java.nio.FloatBuffer coords
+    ){}
+
+    public void glDrawTexiOES(
+        int x,
+        int y,
+        int z,
+        int width,
+        int height
+    ){}
+
+    public void glDrawTexivOES(
+        int[] coords,
+        int offset
+    ){}
+
+    public void glDrawTexivOES(
+        java.nio.IntBuffer coords
+    ){}
+
+    public void glDrawTexsOES(
+        short x,
+        short y,
+        short z,
+        short width,
+        short height
+    ){}
+
+    public void glDrawTexsvOES(
+        short[] coords,
+        int offset
+    ){}
+
+    public void glDrawTexsvOES(
+        java.nio.ShortBuffer coords
+    ){}
+
+    public void glDrawTexxOES(
+        int x,
+        int y,
+        int z,
+        int width,
+        int height
+    ){}
+
+    public void glDrawTexxvOES(
+        int[] coords,
+        int offset
+    ){}
+
+    public void glDrawTexxvOES(
+        java.nio.IntBuffer coords
+    ){}
+
+    public void glLoadPaletteFromModelViewMatrixOES(
+    ){}
+
+    public void glMatrixIndexPointerOES(
+        int size,
+        int type,
+        int stride,
+        java.nio.Buffer pointer
+    ){}
+
+    public void glMatrixIndexPointerOES(
+        int size,
+        int type,
+        int stride,
+        int offset
+    ){}
+
+    public void glWeightPointerOES(
+        int size,
+        int type,
+        int stride,
+        java.nio.Buffer pointer
+    ){}
+
+    public void glWeightPointerOES(
+        int size,
+        int type,
+        int stride,
+        int offset
+    ){}
+
+    public void glBindFramebufferOES(
+        int target,
+        int framebuffer
+    ){}
+
+    public void glBindRenderbufferOES(
+        int target,
+        int renderbuffer
+    ){}
+
+    public void glBlendEquation(
+        int mode
+    ){}
+
+    public void glBlendEquationSeparate(
+        int modeRGB,
+        int modeAlpha
+    ){}
+
+    public void glBlendFuncSeparate(
+        int srcRGB,
+        int dstRGB,
+        int srcAlpha,
+        int dstAlpha
+    ){}
+
+    public int glCheckFramebufferStatusOES(
+        int target
+    ){ throw new UnsupportedOperationException(); }
+
+    public void glDeleteFramebuffersOES(
+        int n,
+        int[] framebuffers,
+        int offset
+    ){}
+
+    public void glDeleteFramebuffersOES(
+        int n,
+        java.nio.IntBuffer framebuffers
+    ){}
+
+    public void glDeleteRenderbuffersOES(
+        int n,
+        int[] renderbuffers,
+        int offset
+    ){}
+
+    public void glDeleteRenderbuffersOES(
+        int n,
+        java.nio.IntBuffer renderbuffers
+    ){}
+
+    public void glFramebufferRenderbufferOES(
+        int target,
+        int attachment,
+        int renderbuffertarget,
+        int renderbuffer
+    ){}
+
+    public void glFramebufferTexture2DOES(
+        int target,
+        int attachment,
+        int textarget,
+        int texture,
+        int level
+    ){}
+
+    public void glGenerateMipmapOES(
+        int target
+    ){}
+
+    public void glGenFramebuffersOES(
+        int n,
+        int[] framebuffers,
+        int offset
+    ){}
+
+    public void glGenFramebuffersOES(
+        int n,
+        java.nio.IntBuffer framebuffers
+    ){}
+
+    public void glGenRenderbuffersOES(
+        int n,
+        int[] renderbuffers,
+        int offset
+    ){}
+
+    public void glGenRenderbuffersOES(
+        int n,
+        java.nio.IntBuffer renderbuffers
+    ){}
+
+    public void glGetFramebufferAttachmentParameterivOES(
+        int target,
+        int attachment,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetFramebufferAttachmentParameterivOES(
+        int target,
+        int attachment,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glGetRenderbufferParameterivOES(
+        int target,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetRenderbufferParameterivOES(
+        int target,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glGetTexGenfv(
+        int coord,
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glGetTexGenfv(
+        int coord,
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glGetTexGeniv(
+        int coord,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetTexGeniv(
+        int coord,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glGetTexGenxv(
+        int coord,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glGetTexGenxv(
+        int coord,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public boolean glIsFramebufferOES(
+        int framebuffer
+    ){ throw new UnsupportedOperationException(); }
+
+    public boolean glIsRenderbufferOES(
+        int renderbuffer
+    ){ throw new UnsupportedOperationException(); }
+
+    public void glRenderbufferStorageOES(
+        int target,
+        int internalformat,
+        int width,
+        int height
+    ){}
+
+    public void glTexGenf(
+        int coord,
+        int pname,
+        float param
+    ){}
+
+    public void glTexGenfv(
+        int coord,
+        int pname,
+        float[] params,
+        int offset
+    ){}
+
+    public void glTexGenfv(
+        int coord,
+        int pname,
+        java.nio.FloatBuffer params
+    ){}
+
+    public void glTexGeni(
+        int coord,
+        int pname,
+        int param
+    ){}
+
+    public void glTexGeniv(
+        int coord,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glTexGeniv(
+        int coord,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+
+    public void glTexGenx(
+        int coord,
+        int pname,
+        int param
+    ){}
+
+    public void glTexGenxv(
+        int coord,
+        int pname,
+        int[] params,
+        int offset
+    ){}
+
+    public void glTexGenxv(
+        int coord,
+        int pname,
+        java.nio.IntBuffer params
+    ){}
+}