summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gallerycommon/src/com/android/gallery3d/common/ApiHelper.java3
-rw-r--r--src/com/android/gallery3d/ui/GLCanvas.java41
-rw-r--r--src/com/android/gallery3d/ui/GLCanvasImpl.java51
-rw-r--r--src/com/android/gallery3d/ui/GLES20Canvas.java1068
-rw-r--r--src/com/android/gallery3d/ui/GLRootView.java4
-rw-r--r--src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java31
-rw-r--r--src/com/android/gallery3d/ui/NinePatchTexture.java27
-rw-r--r--src/com/android/gallery3d/ui/RawTexture.java8
-rw-r--r--src/com/android/gallery3d/util/IntArray.java5
-rw-r--r--tests/src/com/android/gallery3d/ui/GLCanvasStub.java14
-rw-r--r--tests/src/com/android/gallery3d/ui/GLCanvasTest.java24
-rw-r--r--tests/src/com/android/gallery3d/ui/TextureTest.java11
12 files changed, 1218 insertions, 69 deletions
diff --git a/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java b/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java
index e0432749e..56adcb1e9 100644
--- a/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java
+++ b/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java
@@ -176,6 +176,9 @@ public class ApiHelper {
public static final boolean HAS_OBJECT_ANIMATION =
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
+ public static final boolean HAS_GLES20_REQUIRED =
+ Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
+
public static int getIntFieldIfExists(Class<?> klass, String fieldName,
Class<?> obj, int defaultVal) {
try {
diff --git a/src/com/android/gallery3d/ui/GLCanvas.java b/src/com/android/gallery3d/ui/GLCanvas.java
index d1f5ba2c3..1dbee5dd9 100644
--- a/src/com/android/gallery3d/ui/GLCanvas.java
+++ b/src/com/android/gallery3d/ui/GLCanvas.java
@@ -19,6 +19,10 @@ package com.android.gallery3d.ui;
import android.graphics.Bitmap;
import android.graphics.RectF;
+import com.android.gallery3d.common.ApiHelper;
+
+import javax.microedition.khronos.opengles.GL11;
+
//
// GLCanvas gives a convenient interface to draw using OpenGL.
//
@@ -30,12 +34,31 @@ public abstract class GLCanvas {
Additive, Mix,
}
- private static GLId sGLId = new GLIdImpl();
+ private static GLCanvas sInstance = instantiateCanvas();
+ private static GLId sGLId = instantiateGLId();
public static GLId getGLId() {
return sGLId;
}
+ public static GLCanvas getInstance() {
+ return sInstance;
+ }
+
+ private static GLId instantiateGLId() {
+ return ApiHelper.HAS_GLES20_REQUIRED ? (GLES20Canvas) sInstance : new GLIdImpl();
+ }
+
+ private static GLCanvas instantiateCanvas() {
+ return ApiHelper.HAS_GLES20_REQUIRED ? new GLES20Canvas() : new GLCanvasImpl();
+ }
+
+ public static int getEGLContextClientVersion() {
+ return ApiHelper.HAS_GLES20_REQUIRED ? 2 : 1;
+ }
+
+ public abstract void initialize(GL11 gl);
+
// Tells GLCanvas the size of the underlying GL surface. This should be
// called before first drawing and when the size of GL surface is changed.
// This is called by GLRoot and should not be called by the clients
@@ -188,10 +211,18 @@ public abstract class GLCanvas {
/**
* Generates buffers and uploads the buffer data.
*
- * @param buffers An array of buffers to upload
- * @return The buffer IDs that were generated.
+ * @param buffer The buffer to upload
+ * @return The buffer ID that was generated.
+ */
+ public abstract int uploadBuffer(java.nio.FloatBuffer buffer);
+
+ /**
+ * Generates buffers and uploads the element array buffer data.
+ *
+ * @param buffer The buffer to upload
+ * @return The buffer ID that was generated.
*/
- public abstract int[] uploadBuffers(java.nio.Buffer[] buffers);
+ public abstract int uploadBuffer(java.nio.ByteBuffer buffer);
/**
* Sets the blending algorithm if a texture is not opaque.
@@ -217,7 +248,7 @@ public abstract class GLCanvas {
/**
* Start/stop updating the stencil buffer.
- *
+ *
* @param update True if the stencil should be updated, false otherwise.
*/
public abstract void updateStencil(boolean update);
diff --git a/src/com/android/gallery3d/ui/GLCanvasImpl.java b/src/com/android/gallery3d/ui/GLCanvasImpl.java
index ad49ec16a..54c231c3b 100644
--- a/src/com/android/gallery3d/ui/GLCanvasImpl.java
+++ b/src/com/android/gallery3d/ui/GLCanvasImpl.java
@@ -50,7 +50,7 @@ public class GLCanvasImpl extends GLCanvas {
0, 0, 1, 1, // used for drawing a line
0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle
- private final GL11 mGL;
+ private GL11 mGL;
private final float mMatrixValues[] = new float[16];
private final float mTextureMatrixValues[] = new float[16];
@@ -63,7 +63,7 @@ public class GLCanvasImpl extends GLCanvas {
private int mBoxCoords;
- private final GLState mGLState;
+ private GLState mGLState;
private final ArrayList<RawTexture> mTargetStack = new ArrayList<RawTexture>();
private float mAlpha;
@@ -91,10 +91,7 @@ public class GLCanvasImpl extends GLCanvas {
int mCountTextureRect;
int mCountTextureOES;
- GLCanvasImpl(GL11 gl) {
- mGL = gl;
- mGLState = new GLState(gl);
- initialize();
+ GLCanvasImpl() {
}
@Override
@@ -146,9 +143,10 @@ public class GLCanvasImpl extends GLCanvas {
return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
}
- private void initialize() {
- GL11 gl = mGL;
-
+ @Override
+ public void initialize(GL11 gl) {
+ mGL = gl;
+ mGLState = new GLState(gl);
// First create an nio buffer, then create a VBO from it.
int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE;
FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
@@ -977,27 +975,24 @@ public class GLCanvasImpl extends GLCanvas {
}
@Override
- public int[] uploadBuffers(Buffer[] buffers) {
- int[] bufferIds = new int[buffers.length];
- GLId glId = getGLId();
- glId.glGenBuffers(bufferIds.length, bufferIds, 0);
+ public int uploadBuffer(FloatBuffer buf) {
+ return uploadBuffer(buf, Float.SIZE / Byte.SIZE);
+ }
- for (int i = 0; i < bufferIds.length; i++) {
- Buffer buf = buffers[i];
- int elementSize = 0;
- if (buf instanceof FloatBuffer) {
- elementSize = Float.SIZE / Byte.SIZE;
- } else if (buf instanceof ByteBuffer) {
- elementSize = 1;
- } else {
- Utils.fail("Unknown element size for %s", buf.getClass().getSimpleName());
- }
- mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, bufferIds[i]);
- mGL.glBufferData(GL11.GL_ARRAY_BUFFER, buf.capacity() * elementSize, buf,
- GL11.GL_STATIC_DRAW);
- }
+ @Override
+ public int uploadBuffer(ByteBuffer buf) {
+ return uploadBuffer(buf, 1);
+ }
- return bufferIds;
+ private int uploadBuffer(Buffer buf, int elementSize) {
+ int[] bufferIds = new int[1];
+ GLId glId = getGLId();
+ glId.glGenBuffers(bufferIds.length, bufferIds, 0);
+ int bufferId = bufferIds[0];
+ mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, bufferId);
+ mGL.glBufferData(GL11.GL_ARRAY_BUFFER, buf.capacity() * elementSize, buf,
+ GL11.GL_STATIC_DRAW);
+ return bufferId;
}
@Override
diff --git a/src/com/android/gallery3d/ui/GLES20Canvas.java b/src/com/android/gallery3d/ui/GLES20Canvas.java
new file mode 100644
index 000000000..b720a773b
--- /dev/null
+++ b/src/com/android/gallery3d/ui/GLES20Canvas.java
@@ -0,0 +1,1068 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.gallery3d.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.RectF;
+import android.opengl.GLES20;
+import android.opengl.GLUtils;
+import android.opengl.Matrix;
+import android.util.Log;
+
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.util.IntArray;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11ExtensionPack;
+
+public class GLES20Canvas extends GLCanvas implements GLId {
+ // ************** Constants **********************
+ private static final String TAG = GLES20Canvas.class.getSimpleName();
+ private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE;
+ private static final float OPAQUE_ALPHA = 0.95f;
+
+ private static final int COORDS_PER_VERTEX = 2;
+ private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE;
+
+ private static final int COUNT_FILL_VERTEX = 4;
+ private static final int COUNT_LINE_VERTEX = 2;
+ private static final int COUNT_RECT_VERTEX = 4;
+ private static final int OFFSET_FILL_RECT = 0;
+ private static final int OFFSET_DRAW_LINE = OFFSET_FILL_RECT + COUNT_FILL_VERTEX;
+ private static final int OFFSET_DRAW_RECT = OFFSET_DRAW_LINE + COUNT_LINE_VERTEX;
+
+ private static final float[] BOX_COORDINATES = {
+ 0, 0, // Fill rectangle
+ 1, 0,
+ 0, 1,
+ 1, 1,
+ 0, 0, // Draw line
+ 1, 1,
+ 0, 0, // Draw rectangle outline
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ };
+
+ private static final String POSITION_ATTRIBUTE = "aPosition";
+ private static final String COLOR_UNIFORM = "uColor";
+ private static final String MATRIX_UNIFORM = "uMatrix";
+ private static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix";
+ private static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler";
+ private static final String ALPHA_UNIFORM = "uAlpha";
+ private static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate";
+
+ private static final String DRAW_VERTEX_SHADER = ""
+ + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
+ + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
+ + "void main() {\n"
+ + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
+ + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
+ + "}\n";
+
+ private static final String DRAW_FRAGMENT_SHADER = ""
+ + "precision mediump float;\n"
+ + "uniform vec4 " + COLOR_UNIFORM + ";\n"
+ + "void main() {\n"
+ + " gl_FragColor = " + COLOR_UNIFORM + ";\n"
+ + "}\n";
+
+ private static final String TEXTURE_VERTEX_SHADER = ""
+ + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
+ + "uniform mat4 " + TEXTURE_MATRIX_UNIFORM + ";\n"
+ + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
+ + "varying vec2 vTextureCoord;\n"
+ + "void main() {\n"
+ + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
+ + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
+ + " vTextureCoord = (" + TEXTURE_MATRIX_UNIFORM + " * pos).xy;\n"
+ + "}\n";
+
+ private static final String MESH_VERTEX_SHADER = ""
+ + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
+ + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
+ + "attribute vec2 " + TEXTURE_COORD_ATTRIBUTE + ";\n"
+ + "varying vec2 vTextureCoord;\n"
+ + "void main() {\n"
+ + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
+ + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
+ + " vTextureCoord = " + TEXTURE_COORD_ATTRIBUTE + ";\n"
+ + "}\n";
+
+ private static final String TEXTURE_FRAGMENT_SHADER = ""
+ + "precision mediump float;\n"
+ + "varying vec2 vTextureCoord;\n"
+ + "uniform float " + ALPHA_UNIFORM + ";\n"
+ + "uniform sampler2D " + TEXTURE_SAMPLER_UNIFORM + ";\n"
+ + "void main() {\n"
+ + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
+ + " gl_FragColor.a *= " + ALPHA_UNIFORM + ";\n"
+ + "}\n";
+
+ private static final String OES_TEXTURE_FRAGMENT_SHADER = ""
+ + "#extension GL_OES_EGL_image_external : require\n"
+ + "precision mediump float;\n"
+ + "varying vec2 vTextureCoord;\n"
+ + "uniform float " + ALPHA_UNIFORM + ";\n"
+ + "uniform samplerExternalOES " + TEXTURE_SAMPLER_UNIFORM + ";\n"
+ + "void main() {\n"
+ + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
+ + " gl_FragColor.a *= " + ALPHA_UNIFORM + ";\n"
+ + "}\n";
+
+ private static final int INITIAL_RESTORE_STATE_SIZE = 8;
+ private static final int MATRIX_SIZE = 16;
+
+ // Keep track of restore state
+ private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE];
+ private float[] mAlphas = new float[INITIAL_RESTORE_STATE_SIZE];
+ private IntArray mSaveFlags = new IntArray();
+ private ArrayList<Blending> mBlendings = new ArrayList<Blending>();
+
+ private int mCurrentAlphaIndex = 0;
+ private int mCurrentMatrixIndex = 0;
+
+ // Viewport size
+ private int mWidth;
+ private int mHeight;
+
+ // Projection matrix
+ private float[] mProjectionMatrix = new float[MATRIX_SIZE];
+
+ // Screen size for when we aren't bound to a texture
+ private int mScreenWidth;
+ private int mScreenHeight;
+
+ // GL programs
+ private int mDrawProgram;
+ private int mTextureProgram;
+ private int mOesTextureProgram;
+ private int mMeshProgram;
+
+ // GL buffer containing BOX_COORDINATES
+ private int mBoxCoordinates;
+
+ // Handle indices -- common
+ private static final int INDEX_POSITION = 0;
+ private static final int INDEX_MATRIX = 1;
+
+ // Handle indices -- draw
+ private static final int INDEX_COLOR = 2;
+
+ // Handle indices -- texture
+ private static final int INDEX_TEXTURE_MATRIX = 2;
+ private static final int INDEX_TEXTURE_SAMPLER = 3;
+ private static final int INDEX_ALPHA = 4;
+
+ // Handle indices -- mesh
+ private static final int INDEX_TEXTURE_COORD = 2;
+
+ private abstract static class ShaderParameter {
+ public int handle;
+ protected final String mName;
+
+ public ShaderParameter(String name) {
+ mName = name;
+ }
+
+ public abstract void loadHandle(int program);
+ }
+
+ private static class UniformShaderParameter extends ShaderParameter {
+ public UniformShaderParameter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void loadHandle(int program) {
+ handle = GLES20.glGetUniformLocation(program, mName);
+ checkError();
+ }
+ }
+
+ private static class AttributeShaderParameter extends ShaderParameter {
+ public AttributeShaderParameter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void loadHandle(int program) {
+ handle = GLES20.glGetAttribLocation(program, mName);
+ checkError();
+ }
+ }
+
+ ShaderParameter[] mDrawParameters = {
+ new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
+ new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
+ new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR
+ };
+ ShaderParameter[] mTextureParameters = {
+ new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
+ new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
+ new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
+ new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
+ new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
+ };
+ ShaderParameter[] mOesTextureParameters = {
+ new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
+ new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
+ new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
+ new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
+ new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
+ };
+ ShaderParameter[] mMeshParameters = {
+ new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
+ new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
+ new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD
+ new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
+ new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
+ };
+
+ private final IntArray mUnboundTextures = new IntArray();
+ private final IntArray mDeleteBuffers = new IntArray();
+
+ // Keep track of statistics for debugging
+ private int mCountDrawMesh = 0;
+ private int mCountTextureRect = 0;
+ private int mCountFillRect = 0;
+ private int mCountDrawLine = 0;
+
+ private int mNextTextureId = 1;
+
+ // Buffer for framebuffer IDs -- we keep track so we can switch the attached
+ // texture.
+ private int[] mFrameBuffer = new int[1];
+
+ // Bound textures.
+ private ArrayList<RawTexture> mTargetTextures = new ArrayList<RawTexture>();
+
+ // Temporary variables used within calculations
+ private final float[] mTempMatrix = new float[32];
+ private final float[] mTempColor = new float[4];
+ private final RectF mTempSourceRect = new RectF();
+ private final RectF mTempTargetRect = new RectF();
+ private final float[] mTempTextureMatrix = new float[MATRIX_SIZE];
+ private final int[] mTempIntArray = new int[1];
+
+ public GLES20Canvas() {
+ Matrix.setIdentityM(mTempTextureMatrix, 0);
+ Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
+ mAlphas[mCurrentAlphaIndex] = 1f;
+ mTargetTextures.add(null);
+ }
+
+ @Override
+ public void initialize(GL11 gl) {
+ FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES);
+ mBoxCoordinates = uploadBuffer(boxBuffer);
+
+ int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER);
+ int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER);
+ int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER);
+ int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER);
+ int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER);
+ int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
+ OES_TEXTURE_FRAGMENT_SHADER);
+
+ mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters);
+ mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader,
+ mTextureParameters);
+ mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader,
+ mOesTextureParameters);
+ mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters);
+
+ mBlendings.clear();
+ mBlendings.add(null);
+ setBlending(Blending.Mix);
+ }
+
+ private static FloatBuffer createBuffer(float[] values) {
+ // First create an nio buffer, then create a VBO from it.
+ int size = values.length * FLOAT_SIZE;
+ FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder())
+ .asFloatBuffer();
+ buffer.put(values, 0, values.length).position(0);
+ return buffer;
+ }
+
+ private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) {
+ int program = GLES20.glCreateProgram();
+ checkError();
+ if (program == 0) {
+ throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError());
+ }
+ GLES20.glAttachShader(program, vertexShader);
+ checkError();
+ GLES20.glAttachShader(program, fragmentShader);
+ checkError();
+ GLES20.glLinkProgram(program);
+ checkError();
+ int[] mLinkStatus = mTempIntArray;
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0);
+ if (mLinkStatus[0] != GLES20.GL_TRUE) {
+ Log.e(TAG, "Could not link program: ");
+ Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+ GLES20.glDeleteProgram(program);
+ program = 0;
+ }
+ for (int i = 0; i < params.length; i++) {
+ params[i].loadHandle(program);
+ }
+ return program;
+ }
+
+ private static int loadShader(int type, String shaderCode) {
+ // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
+ // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
+ int shader = GLES20.glCreateShader(type);
+
+ // add the source code to the shader and compile it
+ GLES20.glShaderSource(shader, shaderCode);
+ checkError();
+ GLES20.glCompileShader(shader);
+ checkError();
+
+ return shader;
+ }
+
+ @Override
+ public void setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ GLES20.glViewport(0, 0, mWidth, mHeight);
+ checkError();
+ Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
+ Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1);
+ if (getTargetTexture() == null) {
+ mScreenWidth = width;
+ mScreenHeight = height;
+ Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0);
+ Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1);
+ }
+ }
+
+ @Override
+ public void clearBuffer() {
+ GLES20.glClearColor(0f, 0f, 0f, 1f);
+ checkError();
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ checkError();
+ }
+
+ @Override
+ public void clearBuffer(float[] argb) {
+ GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]);
+ checkError();
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ checkError();
+ }
+
+ @Override
+ public float getAlpha() {
+ return mAlphas[mCurrentAlphaIndex];
+ }
+
+ @Override
+ public void setAlpha(float alpha) {
+ mAlphas[mCurrentAlphaIndex] = alpha;
+ }
+
+ @Override
+ public void multiplyAlpha(float alpha) {
+ setAlpha(getAlpha() * alpha);
+ }
+
+ @Override
+ public void translate(float x, float y, float z) {
+ Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z);
+ }
+
+ // This is a faster version of translate(x, y, z) because
+ // (1) we knows z = 0, (2) we inline the Matrix.translateM call,
+ // (3) we unroll the loop
+ @Override
+ public void translate(float x, float y) {
+ int index = mCurrentMatrixIndex;
+ float[] m = mMatrices;
+ m[index + 12] += m[index + 0] * x + m[index + 4] * y;
+ m[index + 13] += m[index + 1] * x + m[index + 5] * y;
+ m[index + 14] += m[index + 2] * x + m[index + 6] * y;
+ m[index + 15] += m[index + 3] * x + m[index + 7] * y;
+ }
+
+ @Override
+ public void scale(float sx, float sy, float sz) {
+ Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz);
+ }
+
+ @Override
+ public void rotate(float angle, float x, float y, float z) {
+ if (angle == 0f) {
+ return;
+ }
+ float[] temp = mTempMatrix;
+ Matrix.setRotateM(temp, 0, angle, x, y, z);
+ float[] matrix = mMatrices;
+ int index = mCurrentMatrixIndex;
+ Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0);
+ System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE);
+ }
+
+ @Override
+ public void multiplyMatrix(float[] matrix, int offset) {
+ float[] temp = mTempMatrix;
+ float[] currentMatrix = mMatrices;
+ int index = mCurrentMatrixIndex;
+ Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset);
+ System.arraycopy(temp, 0, currentMatrix, index, 16);
+ }
+
+ @Override
+ public void save() {
+ save(SAVE_FLAG_ALL);
+ }
+
+ @Override
+ public void save(int saveFlags) {
+ boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
+ if (saveAlpha) {
+ float currentAlpha = getAlpha();
+ mCurrentAlphaIndex++;
+ if (mAlphas.length <= mCurrentAlphaIndex) {
+ mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2);
+ }
+ mAlphas[mCurrentAlphaIndex] = currentAlpha;
+ }
+ boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
+ if (saveMatrix) {
+ int currentIndex = mCurrentMatrixIndex;
+ mCurrentMatrixIndex += MATRIX_SIZE;
+ if (mMatrices.length <= mCurrentMatrixIndex) {
+ mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2);
+ }
+ System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE);
+ }
+ boolean saveBlending = (saveFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND;
+ if (saveBlending) {
+ mBlendings.add(mBlendings.get(mBlendings.size() - 1));
+ }
+ mSaveFlags.add(saveFlags);
+ }
+
+ @Override
+ public void restore() {
+ int restoreFlags = mSaveFlags.removeLast();
+ boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
+ if (restoreAlpha) {
+ mCurrentAlphaIndex--;
+ }
+ boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
+ if (restoreMatrix) {
+ mCurrentMatrixIndex -= MATRIX_SIZE;
+ }
+ boolean restoreBlending = (restoreFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND;
+ if (restoreBlending) {
+ setBlending(mBlendings.get(mBlendings.size() - 2));
+ mBlendings.remove(mBlendings.size() - 1);
+ }
+ }
+
+ @Override
+ public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
+ draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1,
+ paint);
+ mCountDrawLine++;
+ }
+
+ @Override
+ public void drawRect(float x, float y, float width, float height, GLPaint paint) {
+ draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint);
+ mCountDrawLine++;
+ }
+
+ private void draw(int type, int offset, int count, float x, float y, float width, float height,
+ GLPaint paint) {
+ draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth());
+ }
+
+ private void draw(int type, int offset, int count, float x, float y, float width, float height,
+ int color, float lineWidth) {
+ prepareDraw(offset, color, lineWidth);
+ draw(mDrawParameters, type, count, x, y, width, height);
+ }
+
+ private void prepareDraw(int offset, int color, float lineWidth) {
+ GLES20.glUseProgram(mDrawProgram);
+ checkError();
+ if (lineWidth > 0) {
+ GLES20.glLineWidth(lineWidth);
+ checkError();
+ }
+ float[] colorArray = getColor(color);
+ boolean blendingEnabled = (colorArray[3] < 1f);
+ enableBlending(blendingEnabled);
+ if (blendingEnabled) {
+ GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]);
+ checkError();
+ }
+
+ GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0);
+ setPosition(mDrawParameters, offset);
+ checkError();
+ }
+
+ private float[] getColor(int color) {
+ float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha();
+ float red = ((color >>> 16) & 0xFF) / 255f * alpha;
+ float green = ((color >>> 8) & 0xFF) / 255f * alpha;
+ float blue = (color & 0xFF) / 255f * alpha;
+ mTempColor[0] = red;
+ mTempColor[1] = green;
+ mTempColor[2] = blue;
+ mTempColor[3] = alpha;
+ return mTempColor;
+ }
+
+ private void enableBlending(boolean enableBlending) {
+ if (enableBlending) {
+ GLES20.glEnable(GLES20.GL_BLEND);
+ checkError();
+ } else {
+ GLES20.glDisable(GLES20.GL_BLEND);
+ checkError();
+ }
+ }
+
+ private void setPosition(ShaderParameter[] params, int offset) {
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates);
+ checkError();
+ GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX,
+ GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE);
+ checkError();
+ }
+
+ private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width,
+ float height) {
+ setMatrix(params, x, y, width, height);
+ int positionHandle = params[INDEX_POSITION].handle;
+ GLES20.glEnableVertexAttribArray(positionHandle);
+ checkError();
+ GLES20.glDrawArrays(type, 0, count);
+ checkError();
+ GLES20.glDisableVertexAttribArray(positionHandle);
+ checkError();
+ }
+
+ private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) {
+ Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
+ Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
+ Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0);
+ GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE);
+ checkError();
+ }
+
+ @Override
+ public void fillRect(float x, float y, float width, float height, int color) {
+ draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height,
+ color, 0f);
+ mCountFillRect++;
+ }
+
+ @Override
+ public void drawTexture(BasicTexture texture, int x, int y, int width, int height) {
+ if (width <= 0 || height <= 0) {
+ return;
+ }
+ copyTextureCoordinates(texture, mTempSourceRect);
+ mTempTargetRect.set(x, y, x + width, y + height);
+ convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
+ drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
+ }
+
+ private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) {
+ int left = 0;
+ int top = 0;
+ int right = texture.getWidth();
+ int bottom = texture.getHeight();
+ if (texture.hasBorder()) {
+ left = 1;
+ top = 1;
+ right -= 1;
+ bottom -= 1;
+ }
+ outRect.set(left, top, right, bottom);
+ }
+
+ @Override
+ public void drawTexture(BasicTexture texture, RectF source, RectF target) {
+ if (target.width() <= 0 || target.height() <= 0) {
+ return;
+ }
+ mTempSourceRect.set(source);
+ mTempTargetRect.set(target);
+
+ convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
+ drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
+ }
+
+ @Override
+ public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w,
+ int h) {
+ if (w <= 0 || h <= 0) {
+ return;
+ }
+ mTempTargetRect.set(x, y, x + w, y + h);
+ drawTextureRect(texture, textureTransform, mTempTargetRect);
+ }
+
+ private void drawTextureRect(BasicTexture texture, RectF source, RectF target) {
+ setTextureMatrix(source);
+ drawTextureRect(texture, mTempTextureMatrix, target);
+ }
+
+ private void setTextureMatrix(RectF source) {
+ mTempTextureMatrix[0] = source.width();
+ mTempTextureMatrix[5] = source.height();
+ mTempTextureMatrix[12] = source.left;
+ mTempTextureMatrix[13] = source.top;
+ }
+
+ // This function changes the source coordinate to the texture coordinates.
+ // It also clips the source and target coordinates if it is beyond the
+ // bound of the texture.
+ private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) {
+ int width = texture.getWidth();
+ int height = texture.getHeight();
+ int texWidth = texture.getTextureWidth();
+ int texHeight = texture.getTextureHeight();
+ // Convert to texture coordinates
+ source.left /= texWidth;
+ source.right /= texWidth;
+ source.top /= texHeight;
+ source.bottom /= texHeight;
+
+ // Clip if the rendering range is beyond the bound of the texture.
+ float xBound = (float) width / texWidth;
+ if (source.right > xBound) {
+ target.right = target.left + target.width() * (xBound - source.left) / source.width();
+ source.right = xBound;
+ }
+ float yBound = (float) height / texHeight;
+ if (source.bottom > yBound) {
+ target.bottom = target.top + target.height() * (yBound - source.top) / source.height();
+ source.bottom = yBound;
+ }
+ }
+
+ private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) {
+ ShaderParameter[] params = prepareTexture(texture);
+ setPosition(params, OFFSET_FILL_RECT);
+ GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0);
+ checkError();
+ draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top,
+ target.width(), target.height());
+ mCountTextureRect++;
+ }
+
+ private ShaderParameter[] prepareTexture(BasicTexture texture) {
+ ShaderParameter[] params;
+ int program;
+ if (texture.getTarget() == GLES20.GL_TEXTURE_2D) {
+ params = mTextureParameters;
+ program = mTextureProgram;
+ } else {
+ params = mOesTextureParameters;
+ program = mOesTextureProgram;
+ }
+ prepareTexture(texture, program, params);
+ return params;
+ }
+
+ private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) {
+ GLES20.glUseProgram(program);
+ checkError();
+ enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA);
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ checkError();
+ texture.onBind(this);
+ GLES20.glBindTexture(texture.getTarget(), texture.getId());
+ checkError();
+ GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0);
+ checkError();
+ GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha());
+ checkError();
+ }
+
+ @Override
+ public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer,
+ int indexBuffer, int indexCount) {
+ prepareTexture(texture, mMeshProgram, mMeshParameters);
+
+ GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
+ checkError();
+
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer);
+ checkError();
+ int positionHandle = mMeshParameters[INDEX_POSITION].handle;
+ GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
+ VERTEX_STRIDE, 0);
+ checkError();
+
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer);
+ checkError();
+ int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle;
+ GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
+ false, VERTEX_STRIDE, 0);
+ checkError();
+
+ GLES20.glEnableVertexAttribArray(positionHandle);
+ checkError();
+ GLES20.glEnableVertexAttribArray(texCoordHandle);
+ checkError();
+
+ setMatrix(mMeshParameters, x, y, 1, 1);
+ GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_BYTE, 0);
+ checkError();
+
+ GLES20.glDisableVertexAttribArray(positionHandle);
+ checkError();
+ GLES20.glDisableVertexAttribArray(texCoordHandle);
+ checkError();
+ GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
+ checkError();
+ mCountDrawMesh++;
+ }
+
+ @Override
+ public void drawMixed(BasicTexture texture, int toColor, float ratio, int x, int y, int w, int h) {
+ copyTextureCoordinates(texture, mTempSourceRect);
+ mTempTargetRect.set(x, y, x + w, y + h);
+ drawMixed(texture, toColor, ratio, mTempSourceRect, mTempTargetRect);
+ }
+
+ @Override
+ public void drawMixed(BasicTexture texture, int toColor, float ratio, RectF source, RectF target) {
+ if (target.width() <= 0 || target.height() <= 0) {
+ return;
+ }
+ save(SAVE_FLAG_ALPHA);
+
+ float currentAlpha = getAlpha();
+ float cappedRatio = Math.min(1f, Math.max(0f, ratio));
+
+ float textureAlpha = (1f - cappedRatio) * currentAlpha;
+ setAlpha(textureAlpha);
+ drawTexture(texture, source, target);
+
+ float colorAlpha = cappedRatio * currentAlpha;
+ setAlpha(colorAlpha);
+ fillRect(target.left, target.top, target.width(), target.height(), toColor);
+
+ restore();
+ }
+
+ @Override
+ public boolean unloadTexture(BasicTexture texture) {
+ boolean unload = texture.isLoaded();
+ if (unload) {
+ synchronized (mUnboundTextures) {
+ mUnboundTextures.add(texture.getId());
+ }
+ }
+ return unload;
+ }
+
+ @Override
+ public void deleteBuffer(int bufferId) {
+ synchronized (mUnboundTextures) {
+ mDeleteBuffers.add(bufferId);
+ }
+ }
+
+ @Override
+ public void deleteRecycledResources() {
+ synchronized (mUnboundTextures) {
+ IntArray ids = mUnboundTextures;
+ if (mUnboundTextures.size() > 0) {
+ glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0);
+ ids.clear();
+ }
+
+ ids = mDeleteBuffers;
+ if (ids.size() > 0) {
+ glDeleteBuffers(null, ids.size(), ids.getInternalArray(), 0);
+ ids.clear();
+ }
+ }
+ }
+
+ @Override
+ public void dumpStatisticsAndClear() {
+ String line = String.format("MESH:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", mCountDrawMesh,
+ mCountTextureRect, mCountFillRect, mCountDrawLine);
+ mCountDrawMesh = 0;
+ mCountTextureRect = 0;
+ mCountFillRect = 0;
+ mCountDrawLine = 0;
+ Log.d(TAG, line);
+ }
+
+ @Override
+ public void endRenderTarget() {
+ RawTexture oldTexture = mTargetTextures.remove(mTargetTextures.size() - 1);
+ RawTexture texture = getTargetTexture();
+ setRenderTarget(oldTexture, texture);
+ restore(); // restore matrix and alpha
+ }
+
+ @Override
+ public void beginRenderTarget(RawTexture texture) {
+ save(); // save matrix and alpha and blending
+ RawTexture oldTexture = getTargetTexture();
+ mTargetTextures.add(texture);
+ setRenderTarget(oldTexture, texture);
+ }
+
+ private RawTexture getTargetTexture() {
+ return mTargetTextures.get(mTargetTextures.size() - 1);
+ }
+
+ private void setRenderTarget(BasicTexture oldTexture, RawTexture texture) {
+ if (oldTexture == null && texture != null) {
+ GLES20.glGenFramebuffers(1, mFrameBuffer, 0);
+ checkError();
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer[0]);
+ checkError();
+ } else if (oldTexture != null && texture == null) {
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
+ checkError();
+ GLES20.glDeleteFramebuffers(1, mFrameBuffer, 0);
+ checkError();
+ }
+
+ if (texture == null) {
+ setSize(mScreenWidth, mScreenHeight);
+ } else {
+ setSize(texture.getWidth(), texture.getHeight());
+
+ if (!texture.isLoaded()) {
+ texture.prepare(this);
+ }
+
+ GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
+ texture.getTarget(), texture.getId(), 0);
+ checkError();
+
+ checkFramebufferStatus();
+ }
+ }
+
+ private static void checkFramebufferStatus() {
+ int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
+ if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
+ String msg = "";
+ switch (status) {
+ case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ msg = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
+ break;
+ case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+ msg = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
+ break;
+ case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ msg = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+ break;
+ case GLES20.GL_FRAMEBUFFER_UNSUPPORTED:
+ msg = "GL_FRAMEBUFFER_UNSUPPORTED";
+ break;
+ }
+ throw new RuntimeException(msg + ":" + Integer.toHexString(status));
+ }
+ }
+
+ @Override
+ public void setTextureParameters(BasicTexture texture) {
+ int target = texture.getTarget();
+ GLES20.glBindTexture(target, texture.getId());
+ checkError();
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ }
+
+ @Override
+ public void initializeTextureSize(BasicTexture texture, int format, int type) {
+ int target = texture.getTarget();
+ GLES20.glBindTexture(target, texture.getId());
+ checkError();
+ int width = texture.getTextureWidth();
+ int height = texture.getTextureHeight();
+ GLES20.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
+ }
+
+ @Override
+ public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
+ int target = texture.getTarget();
+ GLES20.glBindTexture(target, texture.getId());
+ checkError();
+ GLUtils.texImage2D(target, 0, bitmap, 0);
+ }
+
+ @Override
+ public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
+ int format, int type) {
+ int target = texture.getTarget();
+ GLES20.glBindTexture(target, texture.getId());
+ checkError();
+ GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
+ }
+
+ @Override
+ public int uploadBuffer(FloatBuffer buf) {
+ return uploadBuffer(buf, FLOAT_SIZE);
+ }
+
+ @Override
+ public int uploadBuffer(ByteBuffer buf) {
+ return uploadBuffer(buf, 1);
+ }
+
+ private int uploadBuffer(Buffer buffer, int elementSize) {
+ glGenBuffers(1, mTempIntArray, 0);
+ checkError();
+ int bufferId = mTempIntArray[0];
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId);
+ checkError();
+ GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer,
+ GLES20.GL_STATIC_DRAW);
+ checkError();
+ return bufferId;
+ }
+
+ @Override
+ public void setBlending(Blending blending) {
+ Blending currentBlending = mBlendings.get(mBlendings.size() - 1);
+ if (currentBlending == blending) {
+ return; // nothing to change
+ }
+ mBlendings.set(mBlendings.size() - 1, blending);
+ int srcFunc = GLES20.GL_ONE;
+ int dstFunc;
+ switch (blending) {
+ case Additive:
+ dstFunc = GLES20.GL_ONE;
+ break;
+ case Mix:
+ dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ default:
+ Utils.fail("Unknown blend: " + blending);
+ dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ }
+ GLES20.glBlendFunc(srcFunc, dstFunc);
+ checkError();
+ }
+
+ @Override
+ public int generateTexture() {
+ // Can use anything as a lock. No need to create a new object.
+ synchronized (mTempIntArray) {
+ return mNextTextureId++;
+ }
+ }
+
+ @Override
+ public void glGenBuffers(int n, int[] buffers, int offset) {
+ GLES20.glGenBuffers(n, buffers, offset);
+ checkError();
+ }
+
+ @Override
+ public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
+ GLES20.glDeleteTextures(n, textures, offset);
+ checkError();
+ }
+
+ @Override
+ public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
+ GLES20.glDeleteBuffers(n, buffers, offset);
+ checkError();
+ }
+
+ @Override
+ public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
+ GLES20.glDeleteFramebuffers(n, buffers, offset);
+ checkError();
+ }
+
+ @Override
+ public void enableStencil() {
+ GLES20.glEnable(GLES20.GL_STENCIL_TEST);
+ }
+
+ @Override
+ public void disableStencil() {
+ GLES20.glDisable(GLES20.GL_STENCIL_TEST);
+ }
+
+ @Override
+ public void clearStencilBuffer() {
+ GLES20.glClear(GLES20.GL_STENCIL_BUFFER_BIT);
+ }
+
+ @Override
+ public void updateStencil(boolean update) {
+ int passOp = update ? GLES20.GL_REPLACE : GLES20.GL_KEEP;
+ GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, passOp);
+ }
+
+ @Override
+ public void drawOnlyOutsideStencil(boolean onlyOutside) {
+ int func = onlyOutside ? GLES20.GL_NOTEQUAL : GLES20.GL_ALWAYS;
+ GLES20.glStencilFunc(func, 1, 1);
+ }
+
+ private static void checkError() {
+ int error = GLES20.glGetError();
+ if (error != 0) {
+ Throwable t = new Throwable();
+ Log.e(TAG, "GL error: " + error, t);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static void printMatrix(String message, float[] m, int offset) {
+ StringBuilder b = new StringBuilder(message);
+ for (int i = 0; i < MATRIX_SIZE; i++) {
+ b.append(' ');
+ if (i % 4 == 0) {
+ b.append('\n');
+ }
+ b.append(m[offset + i]);
+ }
+ Log.v(TAG, b.toString());
+ }
+
+}
diff --git a/src/com/android/gallery3d/ui/GLRootView.java b/src/com/android/gallery3d/ui/GLRootView.java
index ea457f7fa..e2268fa4e 100644
--- a/src/com/android/gallery3d/ui/GLRootView.java
+++ b/src/com/android/gallery3d/ui/GLRootView.java
@@ -117,6 +117,7 @@ public class GLRootView extends GLSurfaceView
super(context, attrs);
mFlags |= FLAG_INITIALIZED;
setBackgroundDrawable(null);
+ setEGLContextClientVersion(GLCanvas.getEGLContextClientVersion());
setEGLConfigChooser(mEglConfigChooser);
setRenderer(this);
if (ApiHelper.USE_888_PIXEL_FORMAT) {
@@ -283,7 +284,8 @@ public class GLRootView extends GLSurfaceView
mRenderLock.lock();
try {
mGL = gl;
- mCanvas = new GLCanvasImpl(gl);
+ mCanvas = GLCanvas.getInstance();
+ mCanvas.initialize(gl);
BasicTexture.invalidateAllTextures();
} finally {
mRenderLock.unlock();
diff --git a/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java b/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java
index deeb3b76d..f57a312cb 100644
--- a/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java
+++ b/src/com/android/gallery3d/ui/GalleryEGLConfigChooser.java
@@ -49,12 +49,35 @@ class GalleryEGLConfigChooser implements EGLConfigChooser {
EGL10.EGL_NONE
};
+ private final int mConfig2Spec565[] = new int[] {
+ EGL10.EGL_RED_SIZE, 5,
+ EGL10.EGL_GREEN_SIZE, 6,
+ EGL10.EGL_BLUE_SIZE, 5,
+ EGL10.EGL_ALPHA_SIZE, 0,
+ EGL10.EGL_RENDERABLE_TYPE, 4, /* EGL_OPENGL_ES2_BIT */
+ EGL10.EGL_NONE
+ };
+
+ private final int mConfig2Spec888[] = new int[] {
+ EGL10.EGL_RED_SIZE, 8,
+ EGL10.EGL_GREEN_SIZE, 8,
+ EGL10.EGL_BLUE_SIZE, 8,
+ EGL10.EGL_ALPHA_SIZE, 0,
+ EGL10.EGL_RENDERABLE_TYPE, 4, /* EGL_OPENGL_ES2_BIT */
+ EGL10.EGL_NONE
+ };
+
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] numConfig = new int[1];
- int mConfigSpec[] = ApiHelper.USE_888_PIXEL_FORMAT
- ? mConfigSpec888 : mConfigSpec565;
- if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, numConfig)) {
+
+ int configSpec[];
+ if (GLCanvas.getEGLContextClientVersion() == 2) {
+ configSpec = ApiHelper.USE_888_PIXEL_FORMAT ? mConfig2Spec888 : mConfig2Spec565;
+ } else {
+ configSpec = ApiHelper.USE_888_PIXEL_FORMAT ? mConfigSpec888 : mConfigSpec565;
+ }
+ if (!egl.eglChooseConfig(display, configSpec, null, 0, numConfig)) {
throw new RuntimeException("eglChooseConfig failed");
}
@@ -64,7 +87,7 @@ class GalleryEGLConfigChooser implements EGLConfigChooser {
EGLConfig[] configs = new EGLConfig[numConfig[0]];
if (!egl.eglChooseConfig(display,
- mConfigSpec, configs, configs.length, numConfig)) {
+ configSpec, configs, configs.length, numConfig)) {
throw new RuntimeException();
}
diff --git a/src/com/android/gallery3d/ui/NinePatchTexture.java b/src/com/android/gallery3d/ui/NinePatchTexture.java
index b60504c76..f5c614554 100644
--- a/src/com/android/gallery3d/ui/NinePatchTexture.java
+++ b/src/com/android/gallery3d/ui/NinePatchTexture.java
@@ -23,7 +23,6 @@ import android.graphics.Rect;
import com.android.gallery3d.common.Utils;
-import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
@@ -198,7 +197,9 @@ class NinePatchInstance {
private ByteBuffer mIndexBuffer;
// Names for buffer names: xy, uv, index.
- private int[] mBufferNames;
+ private int mXyBufferName = -1;
+ private int mUvBufferName;
+ private int mIndexBufferName;
private int mIdxCount;
@@ -395,10 +396,9 @@ class NinePatchInstance {
}
private void prepareBuffers(GLCanvas canvas) {
- Buffer[] buffers = {
- mXyBuffer, mUvBuffer, mIndexBuffer
- };
- mBufferNames = canvas.uploadBuffers(buffers);
+ mXyBufferName = canvas.uploadBuffer(mXyBuffer);
+ mUvBufferName = canvas.uploadBuffer(mUvBuffer);
+ mIndexBufferName = canvas.uploadBuffer(mIndexBuffer);
// These buffers are never used again.
mXyBuffer = null;
@@ -407,19 +407,18 @@ class NinePatchInstance {
}
public void draw(GLCanvas canvas, NinePatchTexture tex, int x, int y) {
- if (mBufferNames == null) {
+ if (mXyBufferName == -1) {
prepareBuffers(canvas);
}
- canvas.drawMesh(tex, x, y, mBufferNames[0], mBufferNames[1],
- mBufferNames[2], mIdxCount);
+ canvas.drawMesh(tex, x, y, mXyBufferName, mUvBufferName, mIndexBufferName, mIdxCount);
}
public void recycle(GLCanvas canvas) {
- if (mBufferNames != null) {
- canvas.deleteBuffer(mBufferNames[0]);
- canvas.deleteBuffer(mBufferNames[1]);
- canvas.deleteBuffer(mBufferNames[2]);
- mBufferNames = null;
+ if (mXyBuffer == null) {
+ canvas.deleteBuffer(mXyBufferName);
+ canvas.deleteBuffer(mUvBufferName);
+ canvas.deleteBuffer(mIndexBufferName);
+ mXyBufferName = -1;
}
}
}
diff --git a/src/com/android/gallery3d/ui/RawTexture.java b/src/com/android/gallery3d/ui/RawTexture.java
index e67848f41..53aef9edc 100644
--- a/src/com/android/gallery3d/ui/RawTexture.java
+++ b/src/com/android/gallery3d/ui/RawTexture.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.ui;
+import android.opengl.GLES20;
+
import javax.microedition.khronos.opengles.GL11;
public class RawTexture extends BasicTexture {
@@ -26,8 +28,6 @@ public class RawTexture extends BasicTexture {
public RawTexture(int width, int height, boolean opaque) {
mOpaque = opaque;
setSize(width, height);
- GLId glId = GLCanvas.getGLId();
- mId = glId.generateTexture();
}
@Override
@@ -36,8 +36,10 @@ public class RawTexture extends BasicTexture {
}
protected void prepare(GLCanvas canvas) {
- canvas.setTextureParameters(this);
+ GLId glId = GLCanvas.getGLId();
+ mId = glId.generateTexture();
canvas.initializeTextureSize(this, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
+ canvas.setTextureParameters(this);
mState = STATE_LOADED;
setAssociatedCanvas(canvas);
}
diff --git a/src/com/android/gallery3d/util/IntArray.java b/src/com/android/gallery3d/util/IntArray.java
index 082089a65..2c4dc2c83 100644
--- a/src/com/android/gallery3d/util/IntArray.java
+++ b/src/com/android/gallery3d/util/IntArray.java
@@ -31,6 +31,11 @@ public class IntArray {
mData[mSize++] = value;
}
+ public int removeLast() {
+ mSize--;
+ return mData[mSize];
+ }
+
public int size() {
return mSize;
}
diff --git a/tests/src/com/android/gallery3d/ui/GLCanvasStub.java b/tests/src/com/android/gallery3d/ui/GLCanvasStub.java
index afa34acf3..e9932239b 100644
--- a/tests/src/com/android/gallery3d/ui/GLCanvasStub.java
+++ b/tests/src/com/android/gallery3d/ui/GLCanvasStub.java
@@ -19,7 +19,8 @@ package com.android.gallery3d.ui;
import android.graphics.Bitmap;
import android.graphics.RectF;
-import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL11;
@@ -132,8 +133,12 @@ public class GLCanvasStub extends GLCanvas {
int format, int type) {
}
@Override
- public int[] uploadBuffers(Buffer[] buffers) {
- return null;
+ public int uploadBuffer(ByteBuffer buffer) {
+ return 0;
+ }
+ @Override
+ public int uploadBuffer(FloatBuffer buffer) {
+ return 0;
}
@Override
public void setBlending(Blending blending) {
@@ -153,4 +158,7 @@ public class GLCanvasStub extends GLCanvas {
@Override
public void drawOnlyOutsideStencil(boolean onlyOutside) {
}
+ @Override
+ public void initialize(GL11 gl) {
+ }
}
diff --git a/tests/src/com/android/gallery3d/ui/GLCanvasTest.java b/tests/src/com/android/gallery3d/ui/GLCanvasTest.java
index 8df7a3dfb..ddbddad17 100644
--- a/tests/src/com/android/gallery3d/ui/GLCanvasTest.java
+++ b/tests/src/com/android/gallery3d/ui/GLCanvasTest.java
@@ -39,7 +39,8 @@ public class GLCanvasTest extends TestCase {
@SmallTest
public void testSetSize() {
GL11 glStub = new GLStub();
- GLCanvas canvas = new GLCanvasImpl(glStub);
+ GLCanvas canvas = new GLCanvasImpl();
+ canvas.initialize(glStub);
canvas.setSize(100, 200);
canvas.setSize(1000, 100);
try {
@@ -57,7 +58,8 @@ public class GLCanvasTest extends TestCase {
private static class ClearBufferTest extends GLMock {
void run() {
- GLCanvas canvas = new GLCanvasImpl(this);
+ GLCanvas canvas = new GLCanvasImpl();
+ canvas.initialize(this);
assertEquals(0, mGLClearCalled);
canvas.clearBuffer();
assertEquals(GL10.GL_COLOR_BUFFER_BIT, mGLClearMask);
@@ -79,7 +81,8 @@ public class GLCanvasTest extends TestCase {
0x7F010101, 0xFEFEFDFC, 0x017F8081, 0x027F8081, 0x2ADE4C4D
};
- GLCanvas canvas = new GLCanvasImpl(this);
+ GLCanvas canvas = new GLCanvasImpl();
+ canvas.initialize(this);
canvas.setSize(400, 300);
// Test one color to make sure blend function is set.
assertEquals(0, mGLColorCalled);
@@ -107,7 +110,8 @@ public class GLCanvasTest extends TestCase {
@SmallTest
public void testSetGetMultiplyAlpha() {
GL11 glStub = new GLStub();
- GLCanvas canvas = new GLCanvasImpl(glStub);
+ GLCanvas canvas = new GLCanvasImpl();
+ canvas.initialize(glStub);
canvas.setAlpha(1f);
assertEquals(1f, canvas.getAlpha());
@@ -146,7 +150,8 @@ public class GLCanvasTest extends TestCase {
private static class AlphaTest extends GLMock {
void run() {
- GLCanvas canvas = new GLCanvasImpl(this);
+ GLCanvas canvas = new GLCanvasImpl();
+ canvas.initialize(this);
canvas.setSize(400, 300);
assertEquals(0, mGLColorCalled);
@@ -188,7 +193,8 @@ public class GLCanvasTest extends TestCase {
}
void run() {
- GLCanvas canvas = new GLCanvasImpl(this);
+ GLCanvas canvas = new GLCanvasImpl();
+ canvas.initialize(this);
canvas.setSize(400, 300);
canvas.drawLine(2, 7, 1, 8, newColorPaint(0) /* color */);
assertTrue(mGLVertexArrayEnabled);
@@ -232,7 +238,8 @@ public class GLCanvasTest extends TestCase {
}
void run() {
- GLCanvas canvas = new GLCanvasImpl(this);
+ GLCanvas canvas = new GLCanvasImpl();
+ canvas.initialize(this);
canvas.setSize(400, 300);
canvas.fillRect(2, 7, 1, 8, 0 /* color */);
assertTrue(mGLVertexArrayEnabled);
@@ -294,7 +301,8 @@ public class GLCanvasTest extends TestCase {
}
void run() {
- GLCanvas canvas = new GLCanvasImpl(this);
+ GLCanvas canvas = new GLCanvasImpl();
+ canvas.initialize(this);
canvas.setSize(40, 50);
int color = 0;
diff --git a/tests/src/com/android/gallery3d/ui/TextureTest.java b/tests/src/com/android/gallery3d/ui/TextureTest.java
index 36446b389..361bf7b84 100644
--- a/tests/src/com/android/gallery3d/ui/TextureTest.java
+++ b/tests/src/com/android/gallery3d/ui/TextureTest.java
@@ -48,6 +48,7 @@ public class TextureTest extends TestCase {
return GL11.GL_TEXTURE_2D;
}
+ @Override
public boolean isOpaque() {
mOpaqueCalled++;
return true;
@@ -61,7 +62,8 @@ public class TextureTest extends TestCase {
@SmallTest
public void testBasicTexture() {
GL11 glStub = new GLStub();
- GLCanvas canvas = new GLCanvasImpl(glStub);
+ GLCanvas canvas = new GLCanvasImpl();
+ canvas.initialize(glStub);
MyBasicTexture texture = new MyBasicTexture(canvas, 47);
assertEquals(47, texture.getId());
@@ -81,7 +83,8 @@ public class TextureTest extends TestCase {
assertTrue(texture.isLoaded());
// For a different GL, it's not loaded.
- GLCanvas canvas2 = new GLCanvasImpl(new GLStub());
+ GLCanvas canvas2 = new GLCanvasImpl();
+ canvas2.initialize(glStub);
assertFalse(texture.isLoaded());
assertEquals(0, texture.mOnBindCalled);
@@ -135,7 +138,8 @@ public class TextureTest extends TestCase {
@SmallTest
public void testUploadedTexture() {
GL11 glStub = new GLStub();
- GLCanvas canvas = new GLCanvasImpl(glStub);
+ GLCanvas canvas = new GLCanvasImpl();
+ canvas.initialize(glStub);
MyUploadedTexture texture = new MyUploadedTexture();
// draw it and the bitmap should be fetched.
@@ -181,6 +185,7 @@ public class TextureTest extends TestCase {
return GL11.GL_TEXTURE_2D;
}
+ @Override
public boolean isOpaque() {
return true;
}