| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.gallery3d.glrenderer; |
| |
| import android.graphics.Bitmap; |
| import android.graphics.Rect; |
| import android.graphics.RectF; |
| import android.opengl.GLU; |
| import android.opengl.GLUtils; |
| import android.opengl.Matrix; |
| import android.util.Log; |
| |
| import com.android.gallery3d.common.ApiHelper; |
| import com.android.gallery3d.common.Utils; |
| import com.android.gallery3d.util.IntArray; |
| |
| import java.nio.Buffer; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.nio.FloatBuffer; |
| import java.util.ArrayList; |
| |
| import javax.microedition.khronos.opengles.GL10; |
| import javax.microedition.khronos.opengles.GL11; |
| import javax.microedition.khronos.opengles.GL11Ext; |
| import javax.microedition.khronos.opengles.GL11ExtensionPack; |
| |
| public class GLES11Canvas implements GLCanvas { |
| @SuppressWarnings("unused") |
| private static final String TAG = "GLCanvasImp"; |
| |
| private static final float OPAQUE_ALPHA = 0.95f; |
| |
| private static final int OFFSET_FILL_RECT = 0; |
| private static final int OFFSET_DRAW_LINE = 4; |
| private static final int OFFSET_DRAW_RECT = 6; |
| private static final float[] BOX_COORDINATES = { |
| 0, 0, 1, 0, 0, 1, 1, 1, // used for filling a rectangle |
| 0, 0, 1, 1, // used for drawing a line |
| 0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle |
| |
| private GL11 mGL; |
| |
| private final float mMatrixValues[] = new float[16]; |
| private final float mTextureMatrixValues[] = new float[16]; |
| |
| // The results of mapPoints are stored in this buffer, and the order is |
| // x1, y1, x2, y2. |
| private final float mMapPointsBuffer[] = new float[4]; |
| |
| private final float mTextureColor[] = new float[4]; |
| |
| private int mBoxCoords; |
| |
| private GLState mGLState; |
| private final ArrayList<RawTexture> mTargetStack = new ArrayList<RawTexture>(); |
| |
| private float mAlpha; |
| private final ArrayList<ConfigState> mRestoreStack = new ArrayList<ConfigState>(); |
| private ConfigState mRecycledRestoreAction; |
| |
| private final RectF mDrawTextureSourceRect = new RectF(); |
| private final RectF mDrawTextureTargetRect = new RectF(); |
| private final float[] mTempMatrix = new float[32]; |
| private final IntArray mUnboundTextures = new IntArray(); |
| private final IntArray mDeleteBuffers = new IntArray(); |
| private int mScreenWidth; |
| private int mScreenHeight; |
| private boolean mBlendEnabled = true; |
| private int mFrameBuffer[] = new int[1]; |
| private static float[] sCropRect = new float[4]; |
| |
| private RawTexture mTargetTexture; |
| |
| // Drawing statistics |
| int mCountDrawLine; |
| int mCountFillRect; |
| int mCountDrawMesh; |
| int mCountTextureRect; |
| int mCountTextureOES; |
| |
| private static GLId mGLId = new GLES11IdImpl(); |
| |
| public GLES11Canvas(GL11 gl) { |
| mGL = gl; |
| mGLState = new GLState(gl); |
| // First create an nio buffer, then create a VBO from it. |
| int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE; |
| FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer(); |
| xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0); |
| |
| int[] name = new int[1]; |
| mGLId.glGenBuffers(1, name, 0); |
| mBoxCoords = name[0]; |
| |
| gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords); |
| gl.glBufferData(GL11.GL_ARRAY_BUFFER, xyBuffer.capacity() * (Float.SIZE / Byte.SIZE), |
| xyBuffer, GL11.GL_STATIC_DRAW); |
| |
| gl.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); |
| gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); |
| |
| // Enable the texture coordinate array for Texture 1 |
| gl.glClientActiveTexture(GL11.GL_TEXTURE1); |
| gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); |
| gl.glClientActiveTexture(GL11.GL_TEXTURE0); |
| gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); |
| |
| // mMatrixValues and mAlpha will be initialized in setSize() |
| } |
| |
| @Override |
| public void setSize(int width, int height) { |
| Utils.assertTrue(width >= 0 && height >= 0); |
| |
| if (mTargetTexture == null) { |
| mScreenWidth = width; |
| mScreenHeight = height; |
| } |
| mAlpha = 1.0f; |
| |
| GL11 gl = mGL; |
| gl.glViewport(0, 0, width, height); |
| gl.glMatrixMode(GL11.GL_PROJECTION); |
| gl.glLoadIdentity(); |
| GLU.gluOrtho2D(gl, 0, width, 0, height); |
| |
| gl.glMatrixMode(GL11.GL_MODELVIEW); |
| gl.glLoadIdentity(); |
| |
| float matrix[] = mMatrixValues; |
| Matrix.setIdentityM(matrix, 0); |
| // to match the graphic coordinate system in android, we flip it vertically. |
| if (mTargetTexture == null) { |
| Matrix.translateM(matrix, 0, 0, height, 0); |
| Matrix.scaleM(matrix, 0, 1, -1, 1); |
| } |
| } |
| |
| @Override |
| public void setAlpha(float alpha) { |
| Utils.assertTrue(alpha >= 0 && alpha <= 1); |
| mAlpha = alpha; |
| } |
| |
| @Override |
| public float getAlpha() { |
| return mAlpha; |
| } |
| |
| @Override |
| public void multiplyAlpha(float alpha) { |
| Utils.assertTrue(alpha >= 0 && alpha <= 1); |
| mAlpha *= alpha; |
| } |
| |
| private static ByteBuffer allocateDirectNativeOrderBuffer(int size) { |
| return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); |
| } |
| |
| @Override |
| public void drawRect(float x, float y, float width, float height, GLPaint paint) { |
| GL11 gl = mGL; |
| |
| mGLState.setColorMode(paint.getColor(), mAlpha); |
| mGLState.setLineWidth(paint.getLineWidth()); |
| |
| saveTransform(); |
| translate(x, y); |
| scale(width, height, 1); |
| |
| gl.glLoadMatrixf(mMatrixValues, 0); |
| gl.glDrawArrays(GL11.GL_LINE_LOOP, OFFSET_DRAW_RECT, 4); |
| |
| restoreTransform(); |
| mCountDrawLine++; |
| } |
| |
| @Override |
| public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) { |
| GL11 gl = mGL; |
| |
| mGLState.setColorMode(paint.getColor(), mAlpha); |
| mGLState.setLineWidth(paint.getLineWidth()); |
| |
| saveTransform(); |
| translate(x1, y1); |
| scale(x2 - x1, y2 - y1, 1); |
| |
| gl.glLoadMatrixf(mMatrixValues, 0); |
| gl.glDrawArrays(GL11.GL_LINE_STRIP, OFFSET_DRAW_LINE, 2); |
| |
| restoreTransform(); |
| mCountDrawLine++; |
| } |
| |
| @Override |
| public void fillRect(float x, float y, float width, float height, int color) { |
| mGLState.setColorMode(color, mAlpha); |
| GL11 gl = mGL; |
| |
| saveTransform(); |
| translate(x, y); |
| scale(width, height, 1); |
| |
| gl.glLoadMatrixf(mMatrixValues, 0); |
| gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4); |
| |
| restoreTransform(); |
| mCountFillRect++; |
| } |
| |
| @Override |
| public void translate(float x, float y, float z) { |
| Matrix.translateM(mMatrixValues, 0, x, y, z); |
| } |
| |
| // This is a faster version of translate(x, y, z) because |
| // (1) we knows z = 0, (2) we inline the Matrix.translateM call, |
| // (3) we unroll the loop |
| @Override |
| public void translate(float x, float y) { |
| float[] m = mMatrixValues; |
| m[12] += m[0] * x + m[4] * y; |
| m[13] += m[1] * x + m[5] * y; |
| m[14] += m[2] * x + m[6] * y; |
| m[15] += m[3] * x + m[7] * y; |
| } |
| |
| @Override |
| public void scale(float sx, float sy, float sz) { |
| Matrix.scaleM(mMatrixValues, 0, sx, sy, sz); |
| } |
| |
| @Override |
| public void rotate(float angle, float x, float y, float z) { |
| if (angle == 0) return; |
| float[] temp = mTempMatrix; |
| Matrix.setRotateM(temp, 0, angle, x, y, z); |
| Matrix.multiplyMM(temp, 16, mMatrixValues, 0, temp, 0); |
| System.arraycopy(temp, 16, mMatrixValues, 0, 16); |
| } |
| |
| @Override |
| public void multiplyMatrix(float matrix[], int offset) { |
| float[] temp = mTempMatrix; |
| Matrix.multiplyMM(temp, 0, mMatrixValues, 0, matrix, offset); |
| System.arraycopy(temp, 0, mMatrixValues, 0, 16); |
| } |
| |
| private void textureRect(float x, float y, float width, float height) { |
| GL11 gl = mGL; |
| |
| saveTransform(); |
| translate(x, y); |
| scale(width, height, 1); |
| |
| gl.glLoadMatrixf(mMatrixValues, 0); |
| gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4); |
| |
| restoreTransform(); |
| mCountTextureRect++; |
| } |
| |
| @Override |
| public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer, |
| int uvBuffer, int indexBuffer, int indexCount) { |
| float alpha = mAlpha; |
| if (!bindTexture(tex)) return; |
| |
| mGLState.setBlendEnabled(mBlendEnabled |
| && (!tex.isOpaque() || alpha < OPAQUE_ALPHA)); |
| mGLState.setTextureAlpha(alpha); |
| |
| // Reset the texture matrix. We will set our own texture coordinates |
| // below. |
| setTextureCoords(0, 0, 1, 1); |
| |
| saveTransform(); |
| translate(x, y); |
| |
| mGL.glLoadMatrixf(mMatrixValues, 0); |
| |
| mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, xyBuffer); |
| mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); |
| |
| mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, uvBuffer); |
| mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); |
| |
| mGL.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
| mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP, |
| indexCount, GL11.GL_UNSIGNED_BYTE, 0); |
| |
| mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords); |
| mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); |
| mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); |
| |
| restoreTransform(); |
| mCountDrawMesh++; |
| } |
| |
| // Transforms two points by the given matrix m. The result |
| // {x1', y1', x2', y2'} are stored in mMapPointsBuffer and also returned. |
| private float[] mapPoints(float m[], int x1, int y1, int x2, int y2) { |
| float[] r = mMapPointsBuffer; |
| |
| // Multiply m and (x1 y1 0 1) to produce (x3 y3 z3 w3). z3 is unused. |
| float x3 = m[0] * x1 + m[4] * y1 + m[12]; |
| float y3 = m[1] * x1 + m[5] * y1 + m[13]; |
| float w3 = m[3] * x1 + m[7] * y1 + m[15]; |
| r[0] = x3 / w3; |
| r[1] = y3 / w3; |
| |
| // Same for x2 y2. |
| float x4 = m[0] * x2 + m[4] * y2 + m[12]; |
| float y4 = m[1] * x2 + m[5] * y2 + m[13]; |
| float w4 = m[3] * x2 + m[7] * y2 + m[15]; |
| r[2] = x4 / w4; |
| r[3] = y4 / w4; |
| |
| return r; |
| } |
| |
| private void drawBoundTexture( |
| BasicTexture texture, int x, int y, int width, int height) { |
| // Test whether it has been rotated or flipped, if so, glDrawTexiOES |
| // won't work |
| if (isMatrixRotatedOrFlipped(mMatrixValues)) { |
| if (texture.hasBorder()) { |
| setTextureCoords( |
| 1.0f / texture.getTextureWidth(), |
| 1.0f / texture.getTextureHeight(), |
| (texture.getWidth() - 1.0f) / texture.getTextureWidth(), |
| (texture.getHeight() - 1.0f) / texture.getTextureHeight()); |
| } else { |
| setTextureCoords(0, 0, |
| (float) texture.getWidth() / texture.getTextureWidth(), |
| (float) texture.getHeight() / texture.getTextureHeight()); |
| } |
| textureRect(x, y, width, height); |
| } else { |
| // draw the rect from bottom-left to top-right |
| float points[] = mapPoints( |
| mMatrixValues, x, y + height, x + width, y); |
| x = (int) (points[0] + 0.5f); |
| y = (int) (points[1] + 0.5f); |
| width = (int) (points[2] + 0.5f) - x; |
| height = (int) (points[3] + 0.5f) - y; |
| if (width > 0 && height > 0) { |
| ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height); |
| mCountTextureOES++; |
| } |
| } |
| } |
| |
| @Override |
| public void drawTexture( |
| BasicTexture texture, int x, int y, int width, int height) { |
| drawTexture(texture, x, y, width, height, mAlpha); |
| } |
| |
| private void drawTexture(BasicTexture texture, |
| int x, int y, int width, int height, float alpha) { |
| if (width <= 0 || height <= 0) return; |
| |
| mGLState.setBlendEnabled(mBlendEnabled |
| && (!texture.isOpaque() || alpha < OPAQUE_ALPHA)); |
| if (!bindTexture(texture)) return; |
| mGLState.setTextureAlpha(alpha); |
| drawBoundTexture(texture, x, y, width, height); |
| } |
| |
| @Override |
| public void drawTexture(BasicTexture texture, RectF source, RectF target) { |
| if (target.width() <= 0 || target.height() <= 0) return; |
| |
| // Copy the input to avoid changing it. |
| mDrawTextureSourceRect.set(source); |
| mDrawTextureTargetRect.set(target); |
| source = mDrawTextureSourceRect; |
| target = mDrawTextureTargetRect; |
| |
| mGLState.setBlendEnabled(mBlendEnabled |
| && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA)); |
| if (!bindTexture(texture)) return; |
| convertCoordinate(source, target, texture); |
| setTextureCoords(source); |
| mGLState.setTextureAlpha(mAlpha); |
| textureRect(target.left, target.top, target.width(), target.height()); |
| } |
| |
| @Override |
| public void drawTexture(BasicTexture texture, float[] mTextureTransform, |
| int x, int y, int w, int h) { |
| mGLState.setBlendEnabled(mBlendEnabled |
| && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA)); |
| if (!bindTexture(texture)) return; |
| setTextureCoords(mTextureTransform); |
| mGLState.setTextureAlpha(mAlpha); |
| textureRect(x, y, w, h); |
| } |
| |
| // This function changes the source coordinate to the texture coordinates. |
| // It also clips the source and target coordinates if it is beyond the |
| // bound of the texture. |
| private static void convertCoordinate(RectF source, RectF target, |
| BasicTexture texture) { |
| |
| int width = texture.getWidth(); |
| int height = texture.getHeight(); |
| int texWidth = texture.getTextureWidth(); |
| int texHeight = texture.getTextureHeight(); |
| // Convert to texture coordinates |
| source.left /= texWidth; |
| source.right /= texWidth; |
| source.top /= texHeight; |
| source.bottom /= texHeight; |
| |
| // Clip if the rendering range is beyond the bound of the texture. |
| float xBound = (float) width / texWidth; |
| if (source.right > xBound) { |
| target.right = target.left + target.width() * |
| (xBound - source.left) / source.width(); |
| source.right = xBound; |
| } |
| float yBound = (float) height / texHeight; |
| if (source.bottom > yBound) { |
| target.bottom = target.top + target.height() * |
| (yBound - source.top) / source.height(); |
| source.bottom = yBound; |
| } |
| } |
| |
| @Override |
| public void drawMixed(BasicTexture from, |
| int toColor, float ratio, int x, int y, int w, int h) { |
| drawMixed(from, toColor, ratio, x, y, w, h, mAlpha); |
| } |
| |
| private boolean bindTexture(BasicTexture texture) { |
| if (!texture.onBind(this)) return false; |
| int target = texture.getTarget(); |
| mGLState.setTextureTarget(target); |
| mGL.glBindTexture(target, texture.getId()); |
| return true; |
| } |
| |
| private void setTextureColor(float r, float g, float b, float alpha) { |
| float[] color = mTextureColor; |
| color[0] = r; |
| color[1] = g; |
| color[2] = b; |
| color[3] = alpha; |
| } |
| |
| private void setMixedColor(int toColor, float ratio, float alpha) { |
| // |
| // The formula we want: |
| // alpha * ((1 - ratio) * from + ratio * to) |
| // |
| // The formula that GL supports is in the form of: |
| // combo * from + (1 - combo) * to * scale |
| // |
| // So, we have combo = alpha * (1 - ratio) |
| // and scale = alpha * ratio / (1 - combo) |
| // |
| float combo = alpha * (1 - ratio); |
| float scale = alpha * ratio / (1 - combo); |
| |
| // Specify the interpolation factor via the alpha component of |
| // GL_TEXTURE_ENV_COLORs. |
| // RGB component are get from toColor and will used as SRC1 |
| float colorScale = scale * (toColor >>> 24) / (0xff * 0xff); |
| setTextureColor(((toColor >>> 16) & 0xff) * colorScale, |
| ((toColor >>> 8) & 0xff) * colorScale, |
| (toColor & 0xff) * colorScale, combo); |
| GL11 gl = mGL; |
| gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0); |
| |
| gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE); |
| gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE); |
| gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_RGB, GL11.GL_CONSTANT); |
| gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_RGB, GL11.GL_SRC_COLOR); |
| gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_ALPHA, GL11.GL_CONSTANT); |
| gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_ALPHA, GL11.GL_SRC_ALPHA); |
| |
| // Wire up the interpolation factor for RGB. |
| gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT); |
| gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA); |
| |
| // Wire up the interpolation factor for alpha. |
| gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT); |
| gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA); |
| |
| } |
| |
| @Override |
| public void drawMixed(BasicTexture from, int toColor, float ratio, |
| RectF source, RectF target) { |
| if (target.width() <= 0 || target.height() <= 0) return; |
| |
| if (ratio <= 0.01f) { |
| drawTexture(from, source, target); |
| return; |
| } else if (ratio >= 1) { |
| fillRect(target.left, target.top, target.width(), target.height(), toColor); |
| return; |
| } |
| |
| float alpha = mAlpha; |
| |
| // Copy the input to avoid changing it. |
| mDrawTextureSourceRect.set(source); |
| mDrawTextureTargetRect.set(target); |
| source = mDrawTextureSourceRect; |
| target = mDrawTextureTargetRect; |
| |
| mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque() |
| || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA)); |
| |
| if (!bindTexture(from)) return; |
| |
| // Interpolate the RGB and alpha values between both textures. |
| mGLState.setTexEnvMode(GL11.GL_COMBINE); |
| setMixedColor(toColor, ratio, alpha); |
| convertCoordinate(source, target, from); |
| setTextureCoords(source); |
| textureRect(target.left, target.top, target.width(), target.height()); |
| mGLState.setTexEnvMode(GL11.GL_REPLACE); |
| } |
| |
| private void drawMixed(BasicTexture from, int toColor, |
| float ratio, int x, int y, int width, int height, float alpha) { |
| // change from 0 to 0.01f to prevent getting divided by zero below |
| if (ratio <= 0.01f) { |
| drawTexture(from, x, y, width, height, alpha); |
| return; |
| } else if (ratio >= 1) { |
| fillRect(x, y, width, height, toColor); |
| return; |
| } |
| |
| mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque() |
| || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA)); |
| |
| final GL11 gl = mGL; |
| if (!bindTexture(from)) return; |
| |
| // Interpolate the RGB and alpha values between both textures. |
| mGLState.setTexEnvMode(GL11.GL_COMBINE); |
| setMixedColor(toColor, ratio, alpha); |
| |
| drawBoundTexture(from, x, y, width, height); |
| mGLState.setTexEnvMode(GL11.GL_REPLACE); |
| } |
| |
| // TODO: the code only work for 2D should get fixed for 3D or removed |
| private static final int MSKEW_X = 4; |
| private static final int MSKEW_Y = 1; |
| private static final int MSCALE_X = 0; |
| private static final int MSCALE_Y = 5; |
| |
| private static boolean isMatrixRotatedOrFlipped(float matrix[]) { |
| final float eps = 1e-5f; |
| return Math.abs(matrix[MSKEW_X]) > eps |
| || Math.abs(matrix[MSKEW_Y]) > eps |
| || matrix[MSCALE_X] < -eps |
| || matrix[MSCALE_Y] > eps; |
| } |
| |
| private static class GLState { |
| |
| private final GL11 mGL; |
| |
| private int mTexEnvMode = GL11.GL_REPLACE; |
| private float mTextureAlpha = 1.0f; |
| private int mTextureTarget = GL11.GL_TEXTURE_2D; |
| private boolean mBlendEnabled = true; |
| private float mLineWidth = 1.0f; |
| private boolean mLineSmooth = false; |
| |
| public GLState(GL11 gl) { |
| mGL = gl; |
| |
| // Disable unused state |
| gl.glDisable(GL11.GL_LIGHTING); |
| |
| // Enable used features |
| gl.glEnable(GL11.GL_DITHER); |
| |
| gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); |
| gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); |
| gl.glEnable(GL11.GL_TEXTURE_2D); |
| |
| gl.glTexEnvf(GL11.GL_TEXTURE_ENV, |
| GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE); |
| |
| // Set the background color |
| gl.glClearColor(0f, 0f, 0f, 0f); |
| |
| gl.glEnable(GL11.GL_BLEND); |
| gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); |
| |
| // We use 565 or 8888 format, so set the alignment to 2 bytes/pixel. |
| if (ApiHelper.USE_888_PIXEL_FORMAT) { |
| gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2); |
| } else { |
| gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); |
| } |
| } |
| |
| public void setTexEnvMode(int mode) { |
| if (mTexEnvMode == mode) return; |
| mTexEnvMode = mode; |
| mGL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, mode); |
| } |
| |
| public void setLineWidth(float width) { |
| if (mLineWidth == width) return; |
| mLineWidth = width; |
| mGL.glLineWidth(width); |
| } |
| |
| public void setTextureAlpha(float alpha) { |
| if (mTextureAlpha == alpha) return; |
| mTextureAlpha = alpha; |
| if (alpha >= OPAQUE_ALPHA) { |
| // The alpha is need for those texture without alpha channel |
| mGL.glColor4f(1, 1, 1, 1); |
| setTexEnvMode(GL11.GL_REPLACE); |
| } else { |
| mGL.glColor4f(alpha, alpha, alpha, alpha); |
| setTexEnvMode(GL11.GL_MODULATE); |
| } |
| } |
| |
| public void setColorMode(int color, float alpha) { |
| setBlendEnabled(!Utils.isOpaque(color) || alpha < OPAQUE_ALPHA); |
| |
| // Set mTextureAlpha to an invalid value, so that it will reset |
| // again in setTextureAlpha(float) later. |
| mTextureAlpha = -1.0f; |
| |
| setTextureTarget(0); |
| |
| float prealpha = (color >>> 24) * alpha * 65535f / 255f / 255f; |
| mGL.glColor4x( |
| Math.round(((color >> 16) & 0xFF) * prealpha), |
| Math.round(((color >> 8) & 0xFF) * prealpha), |
| Math.round((color & 0xFF) * prealpha), |
| Math.round(255 * prealpha)); |
| } |
| |
| // target is a value like GL_TEXTURE_2D. If target = 0, texturing is disabled. |
| public void setTextureTarget(int target) { |
| if (mTextureTarget == target) return; |
| if (mTextureTarget != 0) { |
| mGL.glDisable(mTextureTarget); |
| } |
| mTextureTarget = target; |
| if (mTextureTarget != 0) { |
| mGL.glEnable(mTextureTarget); |
| } |
| } |
| |
| public void setBlendEnabled(boolean enabled) { |
| if (mBlendEnabled == enabled) return; |
| mBlendEnabled = enabled; |
| if (enabled) { |
| mGL.glEnable(GL11.GL_BLEND); |
| } else { |
| mGL.glDisable(GL11.GL_BLEND); |
| } |
| } |
| } |
| |
| @Override |
| public void clearBuffer(float[] argb) { |
| if(argb != null && argb.length == 4) { |
| mGL.glClearColor(argb[1], argb[2], argb[3], argb[0]); |
| } else { |
| mGL.glClearColor(0, 0, 0, 1); |
| } |
| mGL.glClear(GL10.GL_COLOR_BUFFER_BIT); |
| } |
| |
| @Override |
| public void clearBuffer() { |
| clearBuffer(null); |
| } |
| |
| private void setTextureCoords(RectF source) { |
| setTextureCoords(source.left, source.top, source.right, source.bottom); |
| } |
| |
| private void setTextureCoords(float left, float top, |
| float right, float bottom) { |
| mGL.glMatrixMode(GL11.GL_TEXTURE); |
| mTextureMatrixValues[0] = right - left; |
| mTextureMatrixValues[5] = bottom - top; |
| mTextureMatrixValues[10] = 1; |
| mTextureMatrixValues[12] = left; |
| mTextureMatrixValues[13] = top; |
| mTextureMatrixValues[15] = 1; |
| mGL.glLoadMatrixf(mTextureMatrixValues, 0); |
| mGL.glMatrixMode(GL11.GL_MODELVIEW); |
| } |
| |
| private void setTextureCoords(float[] mTextureTransform) { |
| mGL.glMatrixMode(GL11.GL_TEXTURE); |
| mGL.glLoadMatrixf(mTextureTransform, 0); |
| mGL.glMatrixMode(GL11.GL_MODELVIEW); |
| } |
| |
| // unloadTexture and deleteBuffer can be called from the finalizer thread, |
| // so we synchronized on the mUnboundTextures object. |
| @Override |
| public boolean unloadTexture(BasicTexture t) { |
| synchronized (mUnboundTextures) { |
| if (!t.isLoaded()) return false; |
| mUnboundTextures.add(t.mId); |
| return true; |
| } |
| } |
| |
| @Override |
| public void deleteBuffer(int bufferId) { |
| synchronized (mUnboundTextures) { |
| mDeleteBuffers.add(bufferId); |
| } |
| } |
| |
| @Override |
| public void deleteRecycledResources() { |
| synchronized (mUnboundTextures) { |
| IntArray ids = mUnboundTextures; |
| if (ids.size() > 0) { |
| mGLId.glDeleteTextures(mGL, ids.size(), ids.getInternalArray(), 0); |
| ids.clear(); |
| } |
| |
| ids = mDeleteBuffers; |
| if (ids.size() > 0) { |
| mGLId.glDeleteBuffers(mGL, ids.size(), ids.getInternalArray(), 0); |
| ids.clear(); |
| } |
| } |
| } |
| |
| @Override |
| public void save() { |
| save(SAVE_FLAG_ALL); |
| } |
| |
| @Override |
| public void save(int saveFlags) { |
| ConfigState config = obtainRestoreConfig(); |
| |
| if ((saveFlags & SAVE_FLAG_ALPHA) != 0) { |
| config.mAlpha = mAlpha; |
| } else { |
| config.mAlpha = -1; |
| } |
| |
| if ((saveFlags & SAVE_FLAG_MATRIX) != 0) { |
| System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16); |
| } else { |
| config.mMatrix[0] = Float.NEGATIVE_INFINITY; |
| } |
| |
| mRestoreStack.add(config); |
| } |
| |
| @Override |
| public void restore() { |
| if (mRestoreStack.isEmpty()) throw new IllegalStateException(); |
| ConfigState config = mRestoreStack.remove(mRestoreStack.size() - 1); |
| config.restore(this); |
| freeRestoreConfig(config); |
| } |
| |
| private void freeRestoreConfig(ConfigState action) { |
| action.mNextFree = mRecycledRestoreAction; |
| mRecycledRestoreAction = action; |
| } |
| |
| private ConfigState obtainRestoreConfig() { |
| if (mRecycledRestoreAction != null) { |
| ConfigState result = mRecycledRestoreAction; |
| mRecycledRestoreAction = result.mNextFree; |
| return result; |
| } |
| return new ConfigState(); |
| } |
| |
| private static class ConfigState { |
| float mAlpha; |
| float mMatrix[] = new float[16]; |
| ConfigState mNextFree; |
| |
| public void restore(GLES11Canvas canvas) { |
| if (mAlpha >= 0) canvas.setAlpha(mAlpha); |
| if (mMatrix[0] != Float.NEGATIVE_INFINITY) { |
| System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16); |
| } |
| } |
| } |
| |
| @Override |
| public void dumpStatisticsAndClear() { |
| String line = String.format( |
| "MESH:%d, TEX_OES:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", |
| mCountDrawMesh, mCountTextureRect, mCountTextureOES, |
| mCountFillRect, mCountDrawLine); |
| mCountDrawMesh = 0; |
| mCountTextureRect = 0; |
| mCountTextureOES = 0; |
| mCountFillRect = 0; |
| mCountDrawLine = 0; |
| Log.d(TAG, line); |
| } |
| |
| private void saveTransform() { |
| System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16); |
| } |
| |
| private void restoreTransform() { |
| System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16); |
| } |
| |
| private void setRenderTarget(RawTexture texture) { |
| GL11ExtensionPack gl11ep = (GL11ExtensionPack) mGL; |
| |
| if (mTargetTexture == null && texture != null) { |
| mGLId.glGenBuffers(1, mFrameBuffer, 0); |
| gl11ep.glBindFramebufferOES( |
| GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFrameBuffer[0]); |
| } |
| if (mTargetTexture != null && texture == null) { |
| gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0); |
| gl11ep.glDeleteFramebuffersOES(1, mFrameBuffer, 0); |
| } |
| |
| mTargetTexture = texture; |
| if (texture == null) { |
| setSize(mScreenWidth, mScreenHeight); |
| } else { |
| setSize(texture.getWidth(), texture.getHeight()); |
| |
| if (!texture.isLoaded()) texture.prepare(this); |
| |
| gl11ep.glFramebufferTexture2DOES( |
| GL11ExtensionPack.GL_FRAMEBUFFER_OES, |
| GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES, |
| GL11.GL_TEXTURE_2D, texture.getId(), 0); |
| |
| checkFramebufferStatus(gl11ep); |
| } |
| } |
| |
| @Override |
| public void endRenderTarget() { |
| RawTexture texture = mTargetStack.remove(mTargetStack.size() - 1); |
| setRenderTarget(texture); |
| restore(); // restore matrix and alpha |
| } |
| |
| @Override |
| public void beginRenderTarget(RawTexture texture) { |
| save(); // save matrix and alpha |
| mTargetStack.add(mTargetTexture); |
| setRenderTarget(texture); |
| } |
| |
| private static void checkFramebufferStatus(GL11ExtensionPack gl11ep) { |
| int status = gl11ep.glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES); |
| if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES) { |
| String msg = ""; |
| switch (status) { |
| case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES: |
| msg = "FRAMEBUFFER_FORMATS"; |
| break; |
| case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES: |
| msg = "FRAMEBUFFER_ATTACHMENT"; |
| break; |
| case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES: |
| msg = "FRAMEBUFFER_MISSING_ATTACHMENT"; |
| break; |
| case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES: |
| msg = "FRAMEBUFFER_DRAW_BUFFER"; |
| break; |
| case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES: |
| msg = "FRAMEBUFFER_READ_BUFFER"; |
| break; |
| case GL11ExtensionPack.GL_FRAMEBUFFER_UNSUPPORTED_OES: |
| msg = "FRAMEBUFFER_UNSUPPORTED"; |
| break; |
| case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES: |
| msg = "FRAMEBUFFER_INCOMPLETE_DIMENSIONS"; |
| break; |
| } |
| throw new RuntimeException(msg + ":" + Integer.toHexString(status)); |
| } |
| } |
| |
| @Override |
| public void setTextureParameters(BasicTexture texture) { |
| int width = texture.getWidth(); |
| int height = texture.getHeight(); |
| // Define a vertically flipped crop rectangle for OES_draw_texture. |
| // The four values in sCropRect are: left, bottom, width, and |
| // height. Negative value of width or height means flip. |
| sCropRect[0] = 0; |
| sCropRect[1] = height; |
| sCropRect[2] = width; |
| sCropRect[3] = -height; |
| |
| // Set texture parameters. |
| int target = texture.getTarget(); |
| mGL.glBindTexture(target, texture.getId()); |
| mGL.glTexParameterfv(target, GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0); |
| mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE); |
| mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE); |
| mGL.glTexParameterf(target, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); |
| mGL.glTexParameterf(target, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); |
| } |
| |
| @Override |
| public void initializeTextureSize(BasicTexture texture, int format, int type) { |
| int target = texture.getTarget(); |
| mGL.glBindTexture(target, texture.getId()); |
| int width = texture.getTextureWidth(); |
| int height = texture.getTextureHeight(); |
| mGL.glTexImage2D(target, 0, format, width, height, 0, format, type, null); |
| } |
| |
| @Override |
| public void initializeTexture(BasicTexture texture, Bitmap bitmap) { |
| int target = texture.getTarget(); |
| mGL.glBindTexture(target, texture.getId()); |
| GLUtils.texImage2D(target, 0, bitmap, 0); |
| } |
| |
| @Override |
| public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap, |
| int format, int type) { |
| int target = texture.getTarget(); |
| mGL.glBindTexture(target, texture.getId()); |
| GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type); |
| } |
| |
| @Override |
| public int uploadBuffer(FloatBuffer buf) { |
| return uploadBuffer(buf, Float.SIZE / Byte.SIZE); |
| } |
| |
| @Override |
| public int uploadBuffer(ByteBuffer buf) { |
| return uploadBuffer(buf, 1); |
| } |
| |
| private int uploadBuffer(Buffer buf, int elementSize) { |
| int[] bufferIds = new int[1]; |
| mGLId.glGenBuffers(bufferIds.length, bufferIds, 0); |
| int bufferId = bufferIds[0]; |
| mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, bufferId); |
| mGL.glBufferData(GL11.GL_ARRAY_BUFFER, buf.capacity() * elementSize, buf, |
| GL11.GL_STATIC_DRAW); |
| return bufferId; |
| } |
| |
| @Override |
| public void recoverFromLightCycle() { |
| // This is only required for GLES20 |
| } |
| |
| @Override |
| public void getBounds(Rect bounds, int x, int y, int width, int height) { |
| // This is only required for GLES20 |
| } |
| |
| @Override |
| public GLId getGLId() { |
| return mGLId; |
| } |
| } |