blob: 612c7c4f2d577f6c273ee9b3d8e1b80286575d5f [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.gallery3d.ui;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.util.IntArray;
import android.graphics.Rect;
import android.graphics.RectF;
import android.opengl.GLU;
import android.opengl.Matrix;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.Stack;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11Ext;
public class GLCanvasImpl 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 final GL11 mGL;
private final float mMatrixValues[] = new float[16];
private final float mTextureMatrixValues[] = new float[16];
// mapPoints needs 10 input and output numbers.
private final float mMapPointsBuffer[] = new float[10];
private final float mTextureColor[] = new float[4];
private int mBoxCoords;
private final GLState mGLState;
private long mAnimationTime;
private float mAlpha;
private final Rect mClipRect = new Rect();
private final Stack<ConfigState> mRestoreStack =
new Stack<ConfigState>();
private ConfigState mRecycledRestoreAction;
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 mHeight;
private boolean mBlendEnabled = true;
// Drawing statistics
int mCountDrawLine;
int mCountFillRect;
int mCountDrawMesh;
int mCountTextureRect;
int mCountTextureOES;
GLCanvasImpl(GL11 gl) {
mGL = gl;
mGLState = new GLState(gl);
initialize();
}
public void setSize(int width, int height) {
Utils.assertTrue(width >= 0 && height >= 0);
mHeight = height;
GL11 gl = mGL;
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL11.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluOrtho2D(gl, 0, width, 0, height);
gl.glMatrixMode(GL11.GL_MODELVIEW);
gl.glLoadIdentity();
float matrix[] = mMatrixValues;
Matrix.setIdentityM(matrix, 0);
Matrix.translateM(matrix, 0, 0, mHeight, 0);
Matrix.scaleM(matrix, 0, 1, -1, 1);
mClipRect.set(0, 0, width, height);
gl.glScissor(0, 0, width, height);
}
public long currentAnimationTimeMillis() {
return mAnimationTime;
}
public void setAlpha(float alpha) {
Utils.assertTrue(alpha >= 0 && alpha <= 1);
mAlpha = alpha;
}
public void multiplyAlpha(float alpha) {
Utils.assertTrue(alpha >= 0 && alpha <= 1);
mAlpha *= alpha;
}
public float getAlpha() {
return mAlpha;
}
private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
}
private void initialize() {
GL11 gl = mGL;
// 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];
gl.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 will be initialized in setSize()
mAlpha = 1.0f;
}
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());
mGLState.setLineSmooth(paint.getAntiAlias());
saveTransform();
translate(x, y, 0);
scale(width, height, 1);
gl.glLoadMatrixf(mMatrixValues, 0);
gl.glDrawArrays(GL11.GL_LINE_LOOP, OFFSET_DRAW_RECT, 4);
restoreTransform();
mCountDrawLine++;
}
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());
mGLState.setLineSmooth(paint.getAntiAlias());
saveTransform();
translate(x1, y1, 0);
scale(x2 - x1, y2 - y1, 1);
gl.glLoadMatrixf(mMatrixValues, 0);
gl.glDrawArrays(GL11.GL_LINE_STRIP, OFFSET_DRAW_LINE, 2);
restoreTransform();
mCountDrawLine++;
}
public void fillRect(float x, float y, float width, float height, int color) {
mGLState.setColorMode(color, mAlpha);
GL11 gl = mGL;
saveTransform();
translate(x, y, 0);
scale(width, height, 1);
gl.glLoadMatrixf(mMatrixValues, 0);
gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4);
restoreTransform();
mCountFillRect++;
}
public void translate(float x, float y, float z) {
Matrix.translateM(mMatrixValues, 0, x, y, z);
}
public void scale(float sx, float sy, float sz) {
Matrix.scaleM(mMatrixValues, 0, sx, sy, sz);
}
public void rotate(float angle, float x, float y, float z) {
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);
}
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, 0);
scale(width, height, 1);
gl.glLoadMatrixf(mMatrixValues, 0);
gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4);
restoreTransform();
mCountTextureRect++;
}
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, 0);
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++;
}
private float[] mapPoints(float matrix[], int x1, int y1, int x2, int y2) {
float[] point = mMapPointsBuffer;
int srcOffset = 6;
point[srcOffset] = x1;
point[srcOffset + 1] = y1;
point[srcOffset + 2] = 0;
point[srcOffset + 3] = 1;
int resultOffset = 0;
Matrix.multiplyMV(point, resultOffset, matrix, 0, point, srcOffset);
point[resultOffset] /= point[resultOffset + 3];
point[resultOffset + 1] /= point[resultOffset + 3];
// map the second point
point[srcOffset] = x2;
point[srcOffset + 1] = y2;
resultOffset = 2;
Matrix.multiplyMV(point, resultOffset, matrix, 0, point, srcOffset);
point[resultOffset] /= point[resultOffset + 3];
point[resultOffset + 1] /= point[resultOffset + 3];
return point;
}
public boolean clipRect(int left, int top, int right, int bottom) {
float point[] = mapPoints(mMatrixValues, left, top, right, bottom);
// mMatrix could be a rotation matrix. In this case, we need to find
// the boundaries after rotation. (only handle 90 * n degrees)
if (point[0] > point[2]) {
left = (int) point[2];
right = (int) point[0];
} else {
left = (int) point[0];
right = (int) point[2];
}
if (point[1] > point[3]) {
top = (int) point[3];
bottom = (int) point[1];
} else {
top = (int) point[1];
bottom = (int) point[3];
}
Rect clip = mClipRect;
boolean intersect = clip.intersect(left, top, right, bottom);
if (!intersect) clip.set(0, 0, 0, 0);
mGL.glScissor(clip.left, clip.top, clip.width(), clip.height());
return intersect;
}
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 = Math.round(points[0]);
y = Math.round(points[1]);
width = Math.round(points[2]) - x;
height = Math.round(points[3]) - y;
if (width > 0 && height > 0) {
((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height);
mCountTextureOES++;
}
}
}
public void drawTexture(
BasicTexture texture, int x, int y, int width, int height) {
drawTexture(texture, x, y, width, height, mAlpha);
}
public void setBlendEnabled(boolean enabled) {
mBlendEnabled = enabled;
}
public 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);
}
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());
}
// 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 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;
}
}
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);
}
public void drawMixed(BasicTexture from, BasicTexture to,
float ratio, int x, int y, int w, int h) {
drawMixed(from, to, ratio, x, y, w, h, mAlpha);
}
private boolean bindTexture(BasicTexture texture) {
if (!texture.onBind(this)) return false;
mGLState.setTexture2DEnabled(true);
mGL.glBindTexture(GL11.GL_TEXTURE_2D, 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 drawMixed(BasicTexture from, int toColor,
float ratio, int x, int y, int width, int height, float alpha) {
if (ratio <= 0) {
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;
//
// The formula we want:
// alpha * ((1 - ratio) * from + ratio * to)
// The formula that GL supports is in the form of:
// combo * (modulate * from) + (1 - combo) * to
//
// So, we have combo = 1 - alpha * ratio
// and modulate = alpha * (1f - ratio) / combo
//
float comboRatio = 1 - alpha * ratio;
// handle the case that (1 - comboRatio) == 0
if (alpha < OPAQUE_ALPHA) {
mGLState.setTextureAlpha(alpha * (1f - ratio) / comboRatio);
} else {
mGLState.setTextureAlpha(1f);
}
// Interpolate the RGB and alpha values between both textures.
mGLState.setTexEnvMode(GL11.GL_COMBINE);
// 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 colorAlpha = (float) (toColor >>> 24) / (0xff * 0xff);
setTextureColor(((toColor >>> 16) & 0xff) * colorAlpha,
((toColor >>> 8) & 0xff) * colorAlpha,
(toColor & 0xff) * colorAlpha, comboRatio);
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);
drawBoundTexture(from, x, y, width, height);
mGLState.setTexEnvMode(GL11.GL_REPLACE);
}
private void drawMixed(BasicTexture from, BasicTexture to,
float ratio, int x, int y, int width, int height, float alpha) {
if (ratio <= 0) {
drawTexture(from, x, y, width, height, alpha);
return;
} else if (ratio >= 1) {
drawTexture(to, x, y, width, height, alpha);
return;
}
// In the current implementation the two textures must have the
// same size.
Utils.assertTrue(from.getWidth() == to.getWidth()
&& from.getHeight() == to.getHeight());
mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
|| !to.isOpaque() || alpha < OPAQUE_ALPHA));
final GL11 gl = mGL;
if (!bindTexture(from)) return;
//
// The formula we want:
// alpha * ((1 - ratio) * from + ratio * to)
// The formula that GL supports is in the form of:
// combo * (modulate * from) + (1 - combo) * to
//
// So, we have combo = 1 - alpha * ratio
// and modulate = alpha * (1f - ratio) / combo
//
float comboRatio = 1 - alpha * ratio;
// handle the case that (1 - comboRatio) == 0
if (alpha < OPAQUE_ALPHA) {
mGLState.setTextureAlpha(alpha * (1f - ratio) / comboRatio);
} else {
mGLState.setTextureAlpha(1f);
}
gl.glActiveTexture(GL11.GL_TEXTURE1);
if (!bindTexture(to)) {
// Disable TEXTURE1.
gl.glDisable(GL11.GL_TEXTURE_2D);
// Switch back to the default texture unit.
gl.glActiveTexture(GL11.GL_TEXTURE0);
return;
}
gl.glEnable(GL11.GL_TEXTURE_2D);
// Interpolate the RGB and alpha values between both textures.
mGLState.setTexEnvMode(GL11.GL_COMBINE);
gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
// Specify the interpolation factor via the alpha component of
// GL_TEXTURE_ENV_COLORs.
// We don't use the RGB color, so just give them 0s.
setTextureColor(0, 0, 0, comboRatio);
gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);
// Wire up the interpolation factor for RGB.
gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);
// Wire up the interpolation factor for alpha.
gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
// Draw the combined texture.
drawBoundTexture(to, x, y, width, height);
// Disable TEXTURE1.
gl.glDisable(GL11.GL_TEXTURE_2D);
// Switch back to the default texture unit.
gl.glActiveTexture(GL11.GL_TEXTURE0);
}
// TODO: the code only work for 2D should get fixed for 3D or removed
private static final int MSKEW_X = 4;
private static final int MSKEW_Y = 1;
private static final int MSCALE_X = 0;
private static final int MSCALE_Y = 5;
private static boolean isMatrixRotatedOrFlipped(float matrix[]) {
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;
}
public BasicTexture copyTexture(int x, int y, int width, int height) {
if (isMatrixRotatedOrFlipped(mMatrixValues)) {
throw new IllegalArgumentException("cannot support rotated matrix");
}
float points[] = mapPoints(mMatrixValues, x, y + height, x + width, y);
x = (int) points[0];
y = (int) points[1];
width = (int) points[2] - x;
height = (int) points[3] - y;
GL11 gl = mGL;
RawTexture texture = RawTexture.newInstance(this);
gl.glBindTexture(GL11.GL_TEXTURE_2D, texture.getId());
texture.setSize(width, height);
int[] cropRect = {0, 0, width, height};
gl.glTexParameteriv(GL11.GL_TEXTURE_2D,
GL11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, 0);
gl.glTexParameteri(GL11.GL_TEXTURE_2D,
GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL11.GL_TEXTURE_2D,
GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL11.GL_TEXTURE_2D,
GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
gl.glTexParameterf(GL11.GL_TEXTURE_2D,
GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
gl.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0,
GL11.GL_RGB, x, y, texture.getTextureWidth(),
texture.getTextureHeight(), 0);
return texture;
}
private static class GLState {
private final GL11 mGL;
private int mTexEnvMode = GL11.GL_REPLACE;
private float mTextureAlpha = 1.0f;
private boolean mTexture2DEnabled = true;
private boolean mBlendEnabled = true;
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.glEnable(GL11.GL_SCISSOR_TEST);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glEnable(GL11.GL_TEXTURE_2D);
gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
// Set the background color
gl.glClearColor(0f, 0f, 0f, 0f);
gl.glClearStencil(0);
gl.glEnable(GL11.GL_BLEND);
gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
// We use 565 or 8888 format, so set the alignment to 2 bytes/pixel.
gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2);
}
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 setLineSmooth(boolean enabled) {
if (mLineSmooth == enabled) return;
mLineSmooth = enabled;
if (enabled) {
mGL.glEnable(GL11.GL_LINE_SMOOTH);
} else {
mGL.glDisable(GL11.GL_LINE_SMOOTH);
}
}
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;
setTexture2DEnabled(false);
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));
}
public void setTexture2DEnabled(boolean enabled) {
if (mTexture2DEnabled == enabled) return;
mTexture2DEnabled = enabled;
if (enabled) {
mGL.glEnable(GL11.GL_TEXTURE_2D);
} else {
mGL.glDisable(GL11.GL_TEXTURE_2D);
}
}
public void setBlendEnabled(boolean enabled) {
if (mBlendEnabled == enabled) return;
mBlendEnabled = enabled;
if (enabled) {
mGL.glEnable(GL11.GL_BLEND);
} else {
mGL.glDisable(GL11.GL_BLEND);
}
}
}
public GL11 getGLInstance() {
return mGL;
}
public void setCurrentAnimationTimeMillis(long time) {
Utils.assertTrue(time >= 0);
mAnimationTime = time;
}
public void clearBuffer() {
mGL.glClear(GL10.GL_COLOR_BUFFER_BIT);
}
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);
}
// unloadTexture and deleteBuffer can be called from the finalizer thread,
// so we synchronized on the mUnboundTextures object.
public boolean unloadTexture(BasicTexture t) {
synchronized (mUnboundTextures) {
if (!t.isLoaded(this)) return false;
mUnboundTextures.add(t.mId);
return true;
}
}
public void deleteBuffer(int bufferId) {
synchronized (mUnboundTextures) {
mDeleteBuffers.add(bufferId);
}
}
public void deleteRecycledResources() {
synchronized (mUnboundTextures) {
IntArray ids = mUnboundTextures;
if (ids.size() > 0) {
mGL.glDeleteTextures(ids.size(), ids.getInternalArray(), 0);
ids.clear();
}
ids = mDeleteBuffers;
if (ids.size() > 0) {
mGL.glDeleteBuffers(ids.size(), ids.getInternalArray(), 0);
ids.clear();
}
}
}
public int save() {
return save(SAVE_FLAG_ALL);
}
public int save(int saveFlags) {
ConfigState config = obtainRestoreConfig();
if ((saveFlags & SAVE_FLAG_ALPHA) != 0) {
config.mAlpha = mAlpha;
} else {
config.mAlpha = -1;
}
if ((saveFlags & SAVE_FLAG_CLIP) != 0) {
config.mRect.set(mClipRect);
} else {
config.mRect.left = Integer.MAX_VALUE;
}
if ((saveFlags & SAVE_FLAG_MATRIX) != 0) {
System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16);
} else {
config.mMatrix[0] = Float.NEGATIVE_INFINITY;
}
mRestoreStack.push(config);
return mRestoreStack.size() - 1;
}
public void restore() {
if (mRestoreStack.isEmpty()) throw new IllegalStateException();
ConfigState config = mRestoreStack.pop();
config.restore(this);
freeRestoreConfig(config);
}
private void freeRestoreConfig(ConfigState action) {
action.mNextFree = mRecycledRestoreAction;
mRecycledRestoreAction = action;
}
private ConfigState obtainRestoreConfig() {
if (mRecycledRestoreAction != null) {
ConfigState result = mRecycledRestoreAction;
mRecycledRestoreAction = result.mNextFree;
return result;
}
return new ConfigState();
}
private static class ConfigState {
float mAlpha;
Rect mRect = new Rect();
float mMatrix[] = new float[16];
ConfigState mNextFree;
public void restore(GLCanvasImpl canvas) {
if (mAlpha >= 0) canvas.setAlpha(mAlpha);
if (mRect.left != Integer.MAX_VALUE) {
Rect rect = mRect;
canvas.mClipRect.set(rect);
canvas.mGL.glScissor(
rect.left, rect.top, rect.width(), rect.height());
}
if (mMatrix[0] != Float.NEGATIVE_INFINITY) {
System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16);
}
}
}
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);
}
}