From 837007c09f723dd96cd8e99476de242c633759c2 Mon Sep 17 00:00:00 2001 From: George Mount Date: Fri, 16 Nov 2012 15:44:26 -0800 Subject: Add GLES20 canvas implementation. Change-Id: I5680909f31dc097599d0e063aa1f6daba834d3e2 --- .../com/android/gallery3d/common/ApiHelper.java | 3 + src/com/android/gallery3d/ui/GLCanvas.java | 41 +- src/com/android/gallery3d/ui/GLCanvasImpl.java | 51 +- src/com/android/gallery3d/ui/GLES20Canvas.java | 1068 ++++++++++++++++++++ src/com/android/gallery3d/ui/GLRootView.java | 4 +- .../gallery3d/ui/GalleryEGLConfigChooser.java | 31 +- src/com/android/gallery3d/ui/NinePatchTexture.java | 27 +- src/com/android/gallery3d/ui/RawTexture.java | 8 +- src/com/android/gallery3d/util/IntArray.java | 5 + .../src/com/android/gallery3d/ui/GLCanvasStub.java | 14 +- .../src/com/android/gallery3d/ui/GLCanvasTest.java | 24 +- .../src/com/android/gallery3d/ui/TextureTest.java | 11 +- 12 files changed, 1218 insertions(+), 69 deletions(-) create mode 100644 src/com/android/gallery3d/ui/GLES20Canvas.java 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 mTargetStack = new ArrayList(); 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 mBlendings = new ArrayList(); + + private int mCurrentAlphaIndex = 0; + private int mCurrentMatrixIndex = 0; + + // Viewport size + private int mWidth; + private int mHeight; + + // Projection matrix + private float[] mProjectionMatrix = new float[MATRIX_SIZE]; + + // Screen size for when we aren't bound to a texture + private int mScreenWidth; + private int mScreenHeight; + + // GL programs + private int mDrawProgram; + private int mTextureProgram; + private int mOesTextureProgram; + private int mMeshProgram; + + // GL buffer containing BOX_COORDINATES + private int mBoxCoordinates; + + // Handle indices -- common + private static final int INDEX_POSITION = 0; + private static final int INDEX_MATRIX = 1; + + // Handle indices -- draw + private static final int INDEX_COLOR = 2; + + // Handle indices -- texture + private static final int INDEX_TEXTURE_MATRIX = 2; + private static final int INDEX_TEXTURE_SAMPLER = 3; + private static final int INDEX_ALPHA = 4; + + // Handle indices -- mesh + private static final int INDEX_TEXTURE_COORD = 2; + + private abstract static class ShaderParameter { + public int handle; + protected final String mName; + + public ShaderParameter(String name) { + mName = name; + } + + public abstract void loadHandle(int program); + } + + private static class UniformShaderParameter extends ShaderParameter { + public UniformShaderParameter(String name) { + super(name); + } + + @Override + public void loadHandle(int program) { + handle = GLES20.glGetUniformLocation(program, mName); + checkError(); + } + } + + private static class AttributeShaderParameter extends ShaderParameter { + public AttributeShaderParameter(String name) { + super(name); + } + + @Override + public void loadHandle(int program) { + handle = GLES20.glGetAttribLocation(program, mName); + checkError(); + } + } + + ShaderParameter[] mDrawParameters = { + new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION + new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX + new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR + }; + ShaderParameter[] mTextureParameters = { + new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION + new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX + new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX + new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER + new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA + }; + ShaderParameter[] mOesTextureParameters = { + new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION + new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX + new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX + new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER + new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA + }; + ShaderParameter[] mMeshParameters = { + new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION + new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX + new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD + new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER + new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA + }; + + private final IntArray mUnboundTextures = new IntArray(); + private final IntArray mDeleteBuffers = new IntArray(); + + // Keep track of statistics for debugging + private int mCountDrawMesh = 0; + private int mCountTextureRect = 0; + private int mCountFillRect = 0; + private int mCountDrawLine = 0; + + private int mNextTextureId = 1; + + // Buffer for framebuffer IDs -- we keep track so we can switch the attached + // texture. + private int[] mFrameBuffer = new int[1]; + + // Bound textures. + private ArrayList mTargetTextures = new ArrayList(); + + // Temporary variables used within calculations + private final float[] mTempMatrix = new float[32]; + private final float[] mTempColor = new float[4]; + private final RectF mTempSourceRect = new RectF(); + private final RectF mTempTargetRect = new RectF(); + private final float[] mTempTextureMatrix = new float[MATRIX_SIZE]; + private final int[] mTempIntArray = new int[1]; + + public GLES20Canvas() { + Matrix.setIdentityM(mTempTextureMatrix, 0); + Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex); + mAlphas[mCurrentAlphaIndex] = 1f; + mTargetTextures.add(null); + } + + @Override + public void initialize(GL11 gl) { + FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES); + mBoxCoordinates = uploadBuffer(boxBuffer); + + int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER); + int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER); + int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER); + int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER); + int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER); + int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, + OES_TEXTURE_FRAGMENT_SHADER); + + mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters); + mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader, + mTextureParameters); + mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader, + mOesTextureParameters); + mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters); + + mBlendings.clear(); + mBlendings.add(null); + setBlending(Blending.Mix); + } + + private static FloatBuffer createBuffer(float[] values) { + // First create an nio buffer, then create a VBO from it. + int size = values.length * FLOAT_SIZE; + FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()) + .asFloatBuffer(); + buffer.put(values, 0, values.length).position(0); + return buffer; + } + + private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) { + int program = GLES20.glCreateProgram(); + checkError(); + if (program == 0) { + throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError()); + } + GLES20.glAttachShader(program, vertexShader); + checkError(); + GLES20.glAttachShader(program, fragmentShader); + checkError(); + GLES20.glLinkProgram(program); + checkError(); + int[] mLinkStatus = mTempIntArray; + GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0); + if (mLinkStatus[0] != GLES20.GL_TRUE) { + Log.e(TAG, "Could not link program: "); + Log.e(TAG, GLES20.glGetProgramInfoLog(program)); + GLES20.glDeleteProgram(program); + program = 0; + } + for (int i = 0; i < params.length; i++) { + params[i].loadHandle(program); + } + return program; + } + + private static int loadShader(int type, String shaderCode) { + // create a vertex shader type (GLES20.GL_VERTEX_SHADER) + // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) + int shader = GLES20.glCreateShader(type); + + // add the source code to the shader and compile it + GLES20.glShaderSource(shader, shaderCode); + checkError(); + GLES20.glCompileShader(shader); + checkError(); + + return shader; + } + + @Override + public void setSize(int width, int height) { + mWidth = width; + mHeight = height; + GLES20.glViewport(0, 0, mWidth, mHeight); + checkError(); + Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex); + Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1); + if (getTargetTexture() == null) { + mScreenWidth = width; + mScreenHeight = height; + Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0); + Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1); + } + } + + @Override + public void clearBuffer() { + GLES20.glClearColor(0f, 0f, 0f, 1f); + checkError(); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + checkError(); + } + + @Override + public void clearBuffer(float[] argb) { + GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]); + checkError(); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + checkError(); + } + + @Override + public float getAlpha() { + return mAlphas[mCurrentAlphaIndex]; + } + + @Override + public void setAlpha(float alpha) { + mAlphas[mCurrentAlphaIndex] = alpha; + } + + @Override + public void multiplyAlpha(float alpha) { + setAlpha(getAlpha() * alpha); + } + + @Override + public void translate(float x, float y, float z) { + Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z); + } + + // This is a faster version of translate(x, y, z) because + // (1) we knows z = 0, (2) we inline the Matrix.translateM call, + // (3) we unroll the loop + @Override + public void translate(float x, float y) { + int index = mCurrentMatrixIndex; + float[] m = mMatrices; + m[index + 12] += m[index + 0] * x + m[index + 4] * y; + m[index + 13] += m[index + 1] * x + m[index + 5] * y; + m[index + 14] += m[index + 2] * x + m[index + 6] * y; + m[index + 15] += m[index + 3] * x + m[index + 7] * y; + } + + @Override + public void scale(float sx, float sy, float sz) { + Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz); + } + + @Override + public void rotate(float angle, float x, float y, float z) { + if (angle == 0f) { + return; + } + float[] temp = mTempMatrix; + Matrix.setRotateM(temp, 0, angle, x, y, z); + float[] matrix = mMatrices; + int index = mCurrentMatrixIndex; + Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0); + System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE); + } + + @Override + public void multiplyMatrix(float[] matrix, int offset) { + float[] temp = mTempMatrix; + float[] currentMatrix = mMatrices; + int index = mCurrentMatrixIndex; + Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset); + System.arraycopy(temp, 0, currentMatrix, index, 16); + } + + @Override + public void save() { + save(SAVE_FLAG_ALL); + } + + @Override + public void save(int saveFlags) { + boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA; + if (saveAlpha) { + float currentAlpha = getAlpha(); + mCurrentAlphaIndex++; + if (mAlphas.length <= mCurrentAlphaIndex) { + mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2); + } + mAlphas[mCurrentAlphaIndex] = currentAlpha; + } + boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX; + if (saveMatrix) { + int currentIndex = mCurrentMatrixIndex; + mCurrentMatrixIndex += MATRIX_SIZE; + if (mMatrices.length <= mCurrentMatrixIndex) { + mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2); + } + System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE); + } + boolean saveBlending = (saveFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND; + if (saveBlending) { + mBlendings.add(mBlendings.get(mBlendings.size() - 1)); + } + mSaveFlags.add(saveFlags); + } + + @Override + public void restore() { + int restoreFlags = mSaveFlags.removeLast(); + boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA; + if (restoreAlpha) { + mCurrentAlphaIndex--; + } + boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX; + if (restoreMatrix) { + mCurrentMatrixIndex -= MATRIX_SIZE; + } + boolean restoreBlending = (restoreFlags & SAVE_FLAG_BLEND) == SAVE_FLAG_BLEND; + if (restoreBlending) { + setBlending(mBlendings.get(mBlendings.size() - 2)); + mBlendings.remove(mBlendings.size() - 1); + } + } + + @Override + public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) { + draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1, + paint); + mCountDrawLine++; + } + + @Override + public void drawRect(float x, float y, float width, float height, GLPaint paint) { + draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint); + mCountDrawLine++; + } + + private void draw(int type, int offset, int count, float x, float y, float width, float height, + GLPaint paint) { + draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth()); + } + + private void draw(int type, int offset, int count, float x, float y, float width, float height, + int color, float lineWidth) { + prepareDraw(offset, color, lineWidth); + draw(mDrawParameters, type, count, x, y, width, height); + } + + private void prepareDraw(int offset, int color, float lineWidth) { + GLES20.glUseProgram(mDrawProgram); + checkError(); + if (lineWidth > 0) { + GLES20.glLineWidth(lineWidth); + checkError(); + } + float[] colorArray = getColor(color); + boolean blendingEnabled = (colorArray[3] < 1f); + enableBlending(blendingEnabled); + if (blendingEnabled) { + GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]); + checkError(); + } + + GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0); + setPosition(mDrawParameters, offset); + checkError(); + } + + private float[] getColor(int color) { + float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha(); + float red = ((color >>> 16) & 0xFF) / 255f * alpha; + float green = ((color >>> 8) & 0xFF) / 255f * alpha; + float blue = (color & 0xFF) / 255f * alpha; + mTempColor[0] = red; + mTempColor[1] = green; + mTempColor[2] = blue; + mTempColor[3] = alpha; + return mTempColor; + } + + private void enableBlending(boolean enableBlending) { + if (enableBlending) { + GLES20.glEnable(GLES20.GL_BLEND); + checkError(); + } else { + GLES20.glDisable(GLES20.GL_BLEND); + checkError(); + } + } + + private void setPosition(ShaderParameter[] params, int offset) { + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates); + checkError(); + GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX, + GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE); + checkError(); + } + + private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width, + float height) { + setMatrix(params, x, y, width, height); + int positionHandle = params[INDEX_POSITION].handle; + GLES20.glEnableVertexAttribArray(positionHandle); + checkError(); + GLES20.glDrawArrays(type, 0, count); + checkError(); + GLES20.glDisableVertexAttribArray(positionHandle); + checkError(); + } + + private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) { + Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f); + Matrix.scaleM(mTempMatrix, 0, width, height, 1f); + Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0); + GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE); + checkError(); + } + + @Override + public void fillRect(float x, float y, float width, float height, int color) { + draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height, + color, 0f); + mCountFillRect++; + } + + @Override + public void drawTexture(BasicTexture texture, int x, int y, int width, int height) { + if (width <= 0 || height <= 0) { + return; + } + copyTextureCoordinates(texture, mTempSourceRect); + mTempTargetRect.set(x, y, x + width, y + height); + convertCoordinate(mTempSourceRect, mTempTargetRect, texture); + drawTextureRect(texture, mTempSourceRect, mTempTargetRect); + } + + private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) { + int left = 0; + int top = 0; + int right = texture.getWidth(); + int bottom = texture.getHeight(); + if (texture.hasBorder()) { + left = 1; + top = 1; + right -= 1; + bottom -= 1; + } + outRect.set(left, top, right, bottom); + } + + @Override + public void drawTexture(BasicTexture texture, RectF source, RectF target) { + if (target.width() <= 0 || target.height() <= 0) { + return; + } + mTempSourceRect.set(source); + mTempTargetRect.set(target); + + convertCoordinate(mTempSourceRect, mTempTargetRect, texture); + drawTextureRect(texture, mTempSourceRect, mTempTargetRect); + } + + @Override + public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w, + int h) { + if (w <= 0 || h <= 0) { + return; + } + mTempTargetRect.set(x, y, x + w, y + h); + drawTextureRect(texture, textureTransform, mTempTargetRect); + } + + private void drawTextureRect(BasicTexture texture, RectF source, RectF target) { + setTextureMatrix(source); + drawTextureRect(texture, mTempTextureMatrix, target); + } + + private void setTextureMatrix(RectF source) { + mTempTextureMatrix[0] = source.width(); + mTempTextureMatrix[5] = source.height(); + mTempTextureMatrix[12] = source.left; + mTempTextureMatrix[13] = source.top; + } + + // This function changes the source coordinate to the texture coordinates. + // It also clips the source and target coordinates if it is beyond the + // bound of the texture. + private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) { + int width = texture.getWidth(); + int height = texture.getHeight(); + int texWidth = texture.getTextureWidth(); + int texHeight = texture.getTextureHeight(); + // Convert to texture coordinates + source.left /= texWidth; + source.right /= texWidth; + source.top /= texHeight; + source.bottom /= texHeight; + + // Clip if the rendering range is beyond the bound of the texture. + float xBound = (float) width / texWidth; + if (source.right > xBound) { + target.right = target.left + target.width() * (xBound - source.left) / source.width(); + source.right = xBound; + } + float yBound = (float) height / texHeight; + if (source.bottom > yBound) { + target.bottom = target.top + target.height() * (yBound - source.top) / source.height(); + source.bottom = yBound; + } + } + + private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) { + ShaderParameter[] params = prepareTexture(texture); + setPosition(params, OFFSET_FILL_RECT); + GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0); + checkError(); + draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top, + target.width(), target.height()); + mCountTextureRect++; + } + + private ShaderParameter[] prepareTexture(BasicTexture texture) { + ShaderParameter[] params; + int program; + if (texture.getTarget() == GLES20.GL_TEXTURE_2D) { + params = mTextureParameters; + program = mTextureProgram; + } else { + params = mOesTextureParameters; + program = mOesTextureProgram; + } + prepareTexture(texture, program, params); + return params; + } + + private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) { + GLES20.glUseProgram(program); + checkError(); + enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + checkError(); + texture.onBind(this); + GLES20.glBindTexture(texture.getTarget(), texture.getId()); + checkError(); + GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0); + checkError(); + GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha()); + checkError(); + } + + @Override + public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer, + int indexBuffer, int indexCount) { + prepareTexture(texture, mMeshProgram, mMeshParameters); + + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer); + checkError(); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer); + checkError(); + int positionHandle = mMeshParameters[INDEX_POSITION].handle; + GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, + VERTEX_STRIDE, 0); + checkError(); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer); + checkError(); + int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle; + GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, + false, VERTEX_STRIDE, 0); + checkError(); + + GLES20.glEnableVertexAttribArray(positionHandle); + checkError(); + GLES20.glEnableVertexAttribArray(texCoordHandle); + checkError(); + + setMatrix(mMeshParameters, x, y, 1, 1); + GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_BYTE, 0); + checkError(); + + GLES20.glDisableVertexAttribArray(positionHandle); + checkError(); + GLES20.glDisableVertexAttribArray(texCoordHandle); + checkError(); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); + checkError(); + mCountDrawMesh++; + } + + @Override + public void drawMixed(BasicTexture texture, int toColor, float ratio, int x, int y, int w, int h) { + copyTextureCoordinates(texture, mTempSourceRect); + mTempTargetRect.set(x, y, x + w, y + h); + drawMixed(texture, toColor, ratio, mTempSourceRect, mTempTargetRect); + } + + @Override + public void drawMixed(BasicTexture texture, int toColor, float ratio, RectF source, RectF target) { + if (target.width() <= 0 || target.height() <= 0) { + return; + } + save(SAVE_FLAG_ALPHA); + + float currentAlpha = getAlpha(); + float cappedRatio = Math.min(1f, Math.max(0f, ratio)); + + float textureAlpha = (1f - cappedRatio) * currentAlpha; + setAlpha(textureAlpha); + drawTexture(texture, source, target); + + float colorAlpha = cappedRatio * currentAlpha; + setAlpha(colorAlpha); + fillRect(target.left, target.top, target.width(), target.height(), toColor); + + restore(); + } + + @Override + public boolean unloadTexture(BasicTexture texture) { + boolean unload = texture.isLoaded(); + if (unload) { + synchronized (mUnboundTextures) { + mUnboundTextures.add(texture.getId()); + } + } + return unload; + } + + @Override + public void deleteBuffer(int bufferId) { + synchronized (mUnboundTextures) { + mDeleteBuffers.add(bufferId); + } + } + + @Override + public void deleteRecycledResources() { + synchronized (mUnboundTextures) { + IntArray ids = mUnboundTextures; + if (mUnboundTextures.size() > 0) { + glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0); + ids.clear(); + } + + ids = mDeleteBuffers; + if (ids.size() > 0) { + glDeleteBuffers(null, ids.size(), ids.getInternalArray(), 0); + ids.clear(); + } + } + } + + @Override + public void dumpStatisticsAndClear() { + String line = String.format("MESH:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", mCountDrawMesh, + mCountTextureRect, mCountFillRect, mCountDrawLine); + mCountDrawMesh = 0; + mCountTextureRect = 0; + mCountFillRect = 0; + mCountDrawLine = 0; + Log.d(TAG, line); + } + + @Override + public void endRenderTarget() { + RawTexture oldTexture = mTargetTextures.remove(mTargetTextures.size() - 1); + RawTexture texture = getTargetTexture(); + setRenderTarget(oldTexture, texture); + restore(); // restore matrix and alpha + } + + @Override + public void beginRenderTarget(RawTexture texture) { + save(); // save matrix and alpha and blending + RawTexture oldTexture = getTargetTexture(); + mTargetTextures.add(texture); + setRenderTarget(oldTexture, texture); + } + + private RawTexture getTargetTexture() { + return mTargetTextures.get(mTargetTextures.size() - 1); + } + + private void setRenderTarget(BasicTexture oldTexture, RawTexture texture) { + if (oldTexture == null && texture != null) { + GLES20.glGenFramebuffers(1, mFrameBuffer, 0); + checkError(); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer[0]); + checkError(); + } else if (oldTexture != null && texture == null) { + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + checkError(); + GLES20.glDeleteFramebuffers(1, mFrameBuffer, 0); + checkError(); + } + + if (texture == null) { + setSize(mScreenWidth, mScreenHeight); + } else { + setSize(texture.getWidth(), texture.getHeight()); + + if (!texture.isLoaded()) { + texture.prepare(this); + } + + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, + texture.getTarget(), texture.getId(), 0); + checkError(); + + checkFramebufferStatus(); + } + } + + private static void checkFramebufferStatus() { + int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); + if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) { + String msg = ""; + switch (status) { + case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + msg = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; + break; + case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + msg = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS"; + break; + case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + msg = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + break; + case GLES20.GL_FRAMEBUFFER_UNSUPPORTED: + msg = "GL_FRAMEBUFFER_UNSUPPORTED"; + break; + } + throw new RuntimeException(msg + ":" + Integer.toHexString(status)); + } + } + + @Override + public void setTextureParameters(BasicTexture texture) { + int target = texture.getTarget(); + GLES20.glBindTexture(target, texture.getId()); + checkError(); + GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + } + + @Override + public void initializeTextureSize(BasicTexture texture, int format, int type) { + int target = texture.getTarget(); + GLES20.glBindTexture(target, texture.getId()); + checkError(); + int width = texture.getTextureWidth(); + int height = texture.getTextureHeight(); + GLES20.glTexImage2D(target, 0, format, width, height, 0, format, type, null); + } + + @Override + public void initializeTexture(BasicTexture texture, Bitmap bitmap) { + int target = texture.getTarget(); + GLES20.glBindTexture(target, texture.getId()); + checkError(); + GLUtils.texImage2D(target, 0, bitmap, 0); + } + + @Override + public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap, + int format, int type) { + int target = texture.getTarget(); + GLES20.glBindTexture(target, texture.getId()); + checkError(); + GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type); + } + + @Override + public int uploadBuffer(FloatBuffer buf) { + return uploadBuffer(buf, FLOAT_SIZE); + } + + @Override + public int uploadBuffer(ByteBuffer buf) { + return uploadBuffer(buf, 1); + } + + private int uploadBuffer(Buffer buffer, int elementSize) { + glGenBuffers(1, mTempIntArray, 0); + checkError(); + int bufferId = mTempIntArray[0]; + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId); + checkError(); + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer, + GLES20.GL_STATIC_DRAW); + checkError(); + return bufferId; + } + + @Override + public void setBlending(Blending blending) { + Blending currentBlending = mBlendings.get(mBlendings.size() - 1); + if (currentBlending == blending) { + return; // nothing to change + } + mBlendings.set(mBlendings.size() - 1, blending); + int srcFunc = GLES20.GL_ONE; + int dstFunc; + switch (blending) { + case Additive: + dstFunc = GLES20.GL_ONE; + break; + case Mix: + dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA; + break; + default: + Utils.fail("Unknown blend: " + blending); + dstFunc = GLES20.GL_ONE_MINUS_SRC_ALPHA; + break; + } + GLES20.glBlendFunc(srcFunc, dstFunc); + checkError(); + } + + @Override + public int generateTexture() { + // Can use anything as a lock. No need to create a new object. + synchronized (mTempIntArray) { + return mNextTextureId++; + } + } + + @Override + public void glGenBuffers(int n, int[] buffers, int offset) { + GLES20.glGenBuffers(n, buffers, offset); + checkError(); + } + + @Override + public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) { + GLES20.glDeleteTextures(n, textures, offset); + checkError(); + } + + @Override + public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) { + GLES20.glDeleteBuffers(n, buffers, offset); + checkError(); + } + + @Override + public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) { + GLES20.glDeleteFramebuffers(n, buffers, offset); + checkError(); + } + + @Override + public void enableStencil() { + GLES20.glEnable(GLES20.GL_STENCIL_TEST); + } + + @Override + public void disableStencil() { + GLES20.glDisable(GLES20.GL_STENCIL_TEST); + } + + @Override + public void clearStencilBuffer() { + GLES20.glClear(GLES20.GL_STENCIL_BUFFER_BIT); + } + + @Override + public void updateStencil(boolean update) { + int passOp = update ? GLES20.GL_REPLACE : GLES20.GL_KEEP; + GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, passOp); + } + + @Override + public void drawOnlyOutsideStencil(boolean onlyOutside) { + int func = onlyOutside ? GLES20.GL_NOTEQUAL : GLES20.GL_ALWAYS; + GLES20.glStencilFunc(func, 1, 1); + } + + private static void checkError() { + int error = GLES20.glGetError(); + if (error != 0) { + Throwable t = new Throwable(); + Log.e(TAG, "GL error: " + error, t); + } + } + + @SuppressWarnings("unused") + private static void printMatrix(String message, float[] m, int offset) { + StringBuilder b = new StringBuilder(message); + for (int i = 0; i < MATRIX_SIZE; i++) { + b.append(' '); + if (i % 4 == 0) { + b.append('\n'); + } + b.append(m[offset + i]); + } + Log.v(TAG, b.toString()); + } + +} diff --git a/src/com/android/gallery3d/ui/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; } -- cgit v1.2.3