Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2010 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.gallery3d.ui; |
| 18 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 19 | import android.graphics.Rect; |
| 20 | import android.graphics.RectF; |
| 21 | import android.opengl.GLU; |
| 22 | import android.opengl.Matrix; |
| 23 | |
Owen Lin | 73a04ff | 2012-03-14 17:27:24 +0800 | [diff] [blame] | 24 | import com.android.gallery3d.common.Utils; |
| 25 | import com.android.gallery3d.util.IntArray; |
| 26 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 27 | import java.nio.ByteBuffer; |
| 28 | import java.nio.ByteOrder; |
| 29 | import java.nio.FloatBuffer; |
Chih-Chung Chang | be55f1e | 2012-02-18 06:17:18 +0800 | [diff] [blame] | 30 | import java.util.ArrayList; |
Owen Lin | 73a04ff | 2012-03-14 17:27:24 +0800 | [diff] [blame] | 31 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 32 | import javax.microedition.khronos.opengles.GL10; |
| 33 | import javax.microedition.khronos.opengles.GL11; |
| 34 | import javax.microedition.khronos.opengles.GL11Ext; |
| 35 | |
| 36 | public class GLCanvasImpl implements GLCanvas { |
| 37 | @SuppressWarnings("unused") |
| 38 | private static final String TAG = "GLCanvasImp"; |
| 39 | |
| 40 | private static final float OPAQUE_ALPHA = 0.95f; |
| 41 | |
| 42 | private static final int OFFSET_FILL_RECT = 0; |
| 43 | private static final int OFFSET_DRAW_LINE = 4; |
| 44 | private static final int OFFSET_DRAW_RECT = 6; |
| 45 | private static final float[] BOX_COORDINATES = { |
| 46 | 0, 0, 1, 0, 0, 1, 1, 1, // used for filling a rectangle |
| 47 | 0, 0, 1, 1, // used for drawing a line |
| 48 | 0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle |
| 49 | |
| 50 | private final GL11 mGL; |
| 51 | |
| 52 | private final float mMatrixValues[] = new float[16]; |
| 53 | private final float mTextureMatrixValues[] = new float[16]; |
| 54 | |
Chih-Chung Chang | 3f43ecb | 2012-02-16 07:27:03 +0800 | [diff] [blame] | 55 | // The results of mapPoints are stored in this buffer, and the order is |
| 56 | // x1, y1, x2, y2. |
| 57 | private final float mMapPointsBuffer[] = new float[4]; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 58 | |
| 59 | private final float mTextureColor[] = new float[4]; |
| 60 | |
| 61 | private int mBoxCoords; |
| 62 | |
| 63 | private final GLState mGLState; |
| 64 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 65 | private float mAlpha; |
Chih-Chung Chang | be55f1e | 2012-02-18 06:17:18 +0800 | [diff] [blame] | 66 | private final ArrayList<ConfigState> mRestoreStack = |
| 67 | new ArrayList<ConfigState>(); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 68 | private ConfigState mRecycledRestoreAction; |
| 69 | |
| 70 | private final RectF mDrawTextureSourceRect = new RectF(); |
| 71 | private final RectF mDrawTextureTargetRect = new RectF(); |
| 72 | private final float[] mTempMatrix = new float[32]; |
| 73 | private final IntArray mUnboundTextures = new IntArray(); |
| 74 | private final IntArray mDeleteBuffers = new IntArray(); |
| 75 | private int mHeight; |
| 76 | private boolean mBlendEnabled = true; |
| 77 | |
| 78 | // Drawing statistics |
| 79 | int mCountDrawLine; |
| 80 | int mCountFillRect; |
| 81 | int mCountDrawMesh; |
| 82 | int mCountTextureRect; |
| 83 | int mCountTextureOES; |
| 84 | |
| 85 | GLCanvasImpl(GL11 gl) { |
| 86 | mGL = gl; |
| 87 | mGLState = new GLState(gl); |
| 88 | initialize(); |
| 89 | } |
| 90 | |
| 91 | public void setSize(int width, int height) { |
| 92 | Utils.assertTrue(width >= 0 && height >= 0); |
| 93 | mHeight = height; |
| 94 | |
| 95 | GL11 gl = mGL; |
| 96 | gl.glViewport(0, 0, width, height); |
| 97 | gl.glMatrixMode(GL11.GL_PROJECTION); |
| 98 | gl.glLoadIdentity(); |
| 99 | GLU.gluOrtho2D(gl, 0, width, 0, height); |
| 100 | |
| 101 | gl.glMatrixMode(GL11.GL_MODELVIEW); |
| 102 | gl.glLoadIdentity(); |
| 103 | float matrix[] = mMatrixValues; |
| 104 | |
| 105 | Matrix.setIdentityM(matrix, 0); |
| 106 | Matrix.translateM(matrix, 0, 0, mHeight, 0); |
| 107 | Matrix.scaleM(matrix, 0, 1, -1, 1); |
| 108 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 109 | gl.glScissor(0, 0, width, height); |
| 110 | } |
| 111 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 112 | public void setAlpha(float alpha) { |
| 113 | Utils.assertTrue(alpha >= 0 && alpha <= 1); |
| 114 | mAlpha = alpha; |
| 115 | } |
| 116 | |
Chih-Chung Chang | 980724b | 2012-02-22 08:00:31 +0800 | [diff] [blame] | 117 | public float getAlpha() { |
| 118 | return mAlpha; |
| 119 | } |
| 120 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 121 | public void multiplyAlpha(float alpha) { |
| 122 | Utils.assertTrue(alpha >= 0 && alpha <= 1); |
| 123 | mAlpha *= alpha; |
| 124 | } |
| 125 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 126 | private static ByteBuffer allocateDirectNativeOrderBuffer(int size) { |
| 127 | return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); |
| 128 | } |
| 129 | |
| 130 | private void initialize() { |
| 131 | GL11 gl = mGL; |
| 132 | |
| 133 | // First create an nio buffer, then create a VBO from it. |
| 134 | int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE; |
| 135 | FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer(); |
| 136 | xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0); |
| 137 | |
| 138 | int[] name = new int[1]; |
| 139 | gl.glGenBuffers(1, name, 0); |
| 140 | mBoxCoords = name[0]; |
| 141 | |
| 142 | gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords); |
| 143 | gl.glBufferData(GL11.GL_ARRAY_BUFFER, |
| 144 | xyBuffer.capacity() * (Float.SIZE / Byte.SIZE), |
| 145 | xyBuffer, GL11.GL_STATIC_DRAW); |
| 146 | |
| 147 | gl.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); |
| 148 | gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); |
| 149 | |
| 150 | // Enable the texture coordinate array for Texture 1 |
| 151 | gl.glClientActiveTexture(GL11.GL_TEXTURE1); |
| 152 | gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); |
| 153 | gl.glClientActiveTexture(GL11.GL_TEXTURE0); |
| 154 | gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); |
| 155 | |
| 156 | // mMatrixValues will be initialized in setSize() |
| 157 | mAlpha = 1.0f; |
| 158 | } |
| 159 | |
| 160 | public void drawRect(float x, float y, float width, float height, GLPaint paint) { |
| 161 | GL11 gl = mGL; |
| 162 | |
| 163 | mGLState.setColorMode(paint.getColor(), mAlpha); |
| 164 | mGLState.setLineWidth(paint.getLineWidth()); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 165 | |
| 166 | saveTransform(); |
Chih-Chung Chang | 3f43ecb | 2012-02-16 07:27:03 +0800 | [diff] [blame] | 167 | translate(x, y); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 168 | scale(width, height, 1); |
| 169 | |
| 170 | gl.glLoadMatrixf(mMatrixValues, 0); |
| 171 | gl.glDrawArrays(GL11.GL_LINE_LOOP, OFFSET_DRAW_RECT, 4); |
| 172 | |
| 173 | restoreTransform(); |
| 174 | mCountDrawLine++; |
| 175 | } |
| 176 | |
| 177 | public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) { |
| 178 | GL11 gl = mGL; |
| 179 | |
| 180 | mGLState.setColorMode(paint.getColor(), mAlpha); |
| 181 | mGLState.setLineWidth(paint.getLineWidth()); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 182 | |
| 183 | saveTransform(); |
Chih-Chung Chang | 3f43ecb | 2012-02-16 07:27:03 +0800 | [diff] [blame] | 184 | translate(x1, y1); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 185 | scale(x2 - x1, y2 - y1, 1); |
| 186 | |
| 187 | gl.glLoadMatrixf(mMatrixValues, 0); |
| 188 | gl.glDrawArrays(GL11.GL_LINE_STRIP, OFFSET_DRAW_LINE, 2); |
| 189 | |
| 190 | restoreTransform(); |
| 191 | mCountDrawLine++; |
| 192 | } |
| 193 | |
| 194 | public void fillRect(float x, float y, float width, float height, int color) { |
| 195 | mGLState.setColorMode(color, mAlpha); |
| 196 | GL11 gl = mGL; |
| 197 | |
| 198 | saveTransform(); |
Chih-Chung Chang | 3f43ecb | 2012-02-16 07:27:03 +0800 | [diff] [blame] | 199 | translate(x, y); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 200 | scale(width, height, 1); |
| 201 | |
| 202 | gl.glLoadMatrixf(mMatrixValues, 0); |
| 203 | gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4); |
| 204 | |
| 205 | restoreTransform(); |
| 206 | mCountFillRect++; |
| 207 | } |
| 208 | |
| 209 | public void translate(float x, float y, float z) { |
| 210 | Matrix.translateM(mMatrixValues, 0, x, y, z); |
| 211 | } |
| 212 | |
Chih-Chung Chang | 3f43ecb | 2012-02-16 07:27:03 +0800 | [diff] [blame] | 213 | // This is a faster version of translate(x, y, z) because |
| 214 | // (1) we knows z = 0, (2) we inline the Matrix.translateM call, |
| 215 | // (3) we unroll the loop |
| 216 | public void translate(float x, float y) { |
| 217 | float[] m = mMatrixValues; |
| 218 | m[12] += m[0] * x + m[4] * y; |
| 219 | m[13] += m[1] * x + m[5] * y; |
| 220 | m[14] += m[2] * x + m[6] * y; |
| 221 | m[15] += m[3] * x + m[7] * y; |
| 222 | } |
| 223 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 224 | public void scale(float sx, float sy, float sz) { |
| 225 | Matrix.scaleM(mMatrixValues, 0, sx, sy, sz); |
| 226 | } |
| 227 | |
| 228 | public void rotate(float angle, float x, float y, float z) { |
Chih-Chung Chang | be55f1e | 2012-02-18 06:17:18 +0800 | [diff] [blame] | 229 | if (angle == 0) return; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 230 | float[] temp = mTempMatrix; |
| 231 | Matrix.setRotateM(temp, 0, angle, x, y, z); |
| 232 | Matrix.multiplyMM(temp, 16, mMatrixValues, 0, temp, 0); |
| 233 | System.arraycopy(temp, 16, mMatrixValues, 0, 16); |
| 234 | } |
| 235 | |
| 236 | public void multiplyMatrix(float matrix[], int offset) { |
| 237 | float[] temp = mTempMatrix; |
Chih-Chung Chang | be07485 | 2011-10-12 17:10:33 +0800 | [diff] [blame] | 238 | Matrix.multiplyMM(temp, 0, mMatrixValues, 0, matrix, offset); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 239 | System.arraycopy(temp, 0, mMatrixValues, 0, 16); |
| 240 | } |
| 241 | |
| 242 | private void textureRect(float x, float y, float width, float height) { |
| 243 | GL11 gl = mGL; |
| 244 | |
| 245 | saveTransform(); |
Chih-Chung Chang | 3f43ecb | 2012-02-16 07:27:03 +0800 | [diff] [blame] | 246 | translate(x, y); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 247 | scale(width, height, 1); |
| 248 | |
| 249 | gl.glLoadMatrixf(mMatrixValues, 0); |
| 250 | gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4); |
| 251 | |
| 252 | restoreTransform(); |
| 253 | mCountTextureRect++; |
| 254 | } |
| 255 | |
| 256 | public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer, |
| 257 | int uvBuffer, int indexBuffer, int indexCount) { |
| 258 | float alpha = mAlpha; |
| 259 | if (!bindTexture(tex)) return; |
| 260 | |
| 261 | mGLState.setBlendEnabled(mBlendEnabled |
| 262 | && (!tex.isOpaque() || alpha < OPAQUE_ALPHA)); |
| 263 | mGLState.setTextureAlpha(alpha); |
| 264 | |
| 265 | // Reset the texture matrix. We will set our own texture coordinates |
| 266 | // below. |
| 267 | setTextureCoords(0, 0, 1, 1); |
| 268 | |
| 269 | saveTransform(); |
Chih-Chung Chang | 3f43ecb | 2012-02-16 07:27:03 +0800 | [diff] [blame] | 270 | translate(x, y); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 271 | |
| 272 | mGL.glLoadMatrixf(mMatrixValues, 0); |
| 273 | |
| 274 | mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, xyBuffer); |
| 275 | mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); |
| 276 | |
| 277 | mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, uvBuffer); |
| 278 | mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); |
| 279 | |
| 280 | mGL.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, indexBuffer); |
| 281 | mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP, |
| 282 | indexCount, GL11.GL_UNSIGNED_BYTE, 0); |
| 283 | |
| 284 | mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords); |
| 285 | mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); |
| 286 | mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0); |
| 287 | |
| 288 | restoreTransform(); |
| 289 | mCountDrawMesh++; |
| 290 | } |
| 291 | |
Chih-Chung Chang | 3f43ecb | 2012-02-16 07:27:03 +0800 | [diff] [blame] | 292 | // Transforms two points by the given matrix m. The result |
| 293 | // {x1', y1', x2', y2'} are stored in mMapPointsBuffer and also returned. |
| 294 | private float[] mapPoints(float m[], int x1, int y1, int x2, int y2) { |
| 295 | float[] r = mMapPointsBuffer; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 296 | |
Chih-Chung Chang | 3f43ecb | 2012-02-16 07:27:03 +0800 | [diff] [blame] | 297 | // Multiply m and (x1 y1 0 1) to produce (x3 y3 z3 w3). z3 is unused. |
| 298 | float x3 = m[0] * x1 + m[4] * y1 + m[12]; |
| 299 | float y3 = m[1] * x1 + m[5] * y1 + m[13]; |
| 300 | float w3 = m[3] * x1 + m[7] * y1 + m[15]; |
| 301 | r[0] = x3 / w3; |
| 302 | r[1] = y3 / w3; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 303 | |
Chih-Chung Chang | 3f43ecb | 2012-02-16 07:27:03 +0800 | [diff] [blame] | 304 | // Same for x2 y2. |
| 305 | float x4 = m[0] * x2 + m[4] * y2 + m[12]; |
| 306 | float y4 = m[1] * x2 + m[5] * y2 + m[13]; |
| 307 | float w4 = m[3] * x2 + m[7] * y2 + m[15]; |
| 308 | r[2] = x4 / w4; |
| 309 | r[3] = y4 / w4; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 310 | |
Chih-Chung Chang | 3f43ecb | 2012-02-16 07:27:03 +0800 | [diff] [blame] | 311 | return r; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 312 | } |
| 313 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 314 | private void drawBoundTexture( |
| 315 | BasicTexture texture, int x, int y, int width, int height) { |
| 316 | // Test whether it has been rotated or flipped, if so, glDrawTexiOES |
| 317 | // won't work |
| 318 | if (isMatrixRotatedOrFlipped(mMatrixValues)) { |
Chih-Chung Chang | e3312ff | 2011-09-22 14:55:32 +0800 | [diff] [blame] | 319 | if (texture.hasBorder()) { |
| 320 | setTextureCoords( |
| 321 | 1.0f / texture.getTextureWidth(), |
| 322 | 1.0f / texture.getTextureHeight(), |
| 323 | (texture.getWidth() - 1.0f) / texture.getTextureWidth(), |
| 324 | (texture.getHeight() - 1.0f) / texture.getTextureHeight()); |
| 325 | } else { |
| 326 | setTextureCoords(0, 0, |
| 327 | (float) texture.getWidth() / texture.getTextureWidth(), |
| 328 | (float) texture.getHeight() / texture.getTextureHeight()); |
| 329 | } |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 330 | textureRect(x, y, width, height); |
| 331 | } else { |
| 332 | // draw the rect from bottom-left to top-right |
| 333 | float points[] = mapPoints( |
| 334 | mMatrixValues, x, y + height, x + width, y); |
Chih-Chung Chang | be55f1e | 2012-02-18 06:17:18 +0800 | [diff] [blame] | 335 | x = (int) (points[0] + 0.5f); |
| 336 | y = (int) (points[1] + 0.5f); |
| 337 | width = (int) (points[2] + 0.5f) - x; |
| 338 | height = (int) (points[3] + 0.5f) - y; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 339 | if (width > 0 && height > 0) { |
| 340 | ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height); |
| 341 | mCountTextureOES++; |
| 342 | } |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | public void drawTexture( |
| 347 | BasicTexture texture, int x, int y, int width, int height) { |
| 348 | drawTexture(texture, x, y, width, height, mAlpha); |
| 349 | } |
| 350 | |
Chih-Chung Chang | 980724b | 2012-02-22 08:00:31 +0800 | [diff] [blame] | 351 | private void drawTexture(BasicTexture texture, |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 352 | int x, int y, int width, int height, float alpha) { |
| 353 | if (width <= 0 || height <= 0) return; |
| 354 | |
| 355 | mGLState.setBlendEnabled(mBlendEnabled |
| 356 | && (!texture.isOpaque() || alpha < OPAQUE_ALPHA)); |
| 357 | if (!bindTexture(texture)) return; |
| 358 | mGLState.setTextureAlpha(alpha); |
| 359 | drawBoundTexture(texture, x, y, width, height); |
| 360 | } |
| 361 | |
| 362 | public void drawTexture(BasicTexture texture, RectF source, RectF target) { |
| 363 | if (target.width() <= 0 || target.height() <= 0) return; |
| 364 | |
| 365 | // Copy the input to avoid changing it. |
| 366 | mDrawTextureSourceRect.set(source); |
| 367 | mDrawTextureTargetRect.set(target); |
| 368 | source = mDrawTextureSourceRect; |
| 369 | target = mDrawTextureTargetRect; |
| 370 | |
| 371 | mGLState.setBlendEnabled(mBlendEnabled |
| 372 | && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA)); |
| 373 | if (!bindTexture(texture)) return; |
| 374 | convertCoordinate(source, target, texture); |
| 375 | setTextureCoords(source); |
| 376 | mGLState.setTextureAlpha(mAlpha); |
| 377 | textureRect(target.left, target.top, target.width(), target.height()); |
| 378 | } |
| 379 | |
| 380 | // This function changes the source coordinate to the texture coordinates. |
| 381 | // It also clips the source and target coordinates if it is beyond the |
| 382 | // bound of the texture. |
| 383 | private void convertCoordinate(RectF source, RectF target, |
| 384 | BasicTexture texture) { |
| 385 | |
| 386 | int width = texture.getWidth(); |
| 387 | int height = texture.getHeight(); |
| 388 | int texWidth = texture.getTextureWidth(); |
| 389 | int texHeight = texture.getTextureHeight(); |
| 390 | // Convert to texture coordinates |
| 391 | source.left /= texWidth; |
| 392 | source.right /= texWidth; |
| 393 | source.top /= texHeight; |
| 394 | source.bottom /= texHeight; |
| 395 | |
| 396 | // Clip if the rendering range is beyond the bound of the texture. |
| 397 | float xBound = (float) width / texWidth; |
| 398 | if (source.right > xBound) { |
| 399 | target.right = target.left + target.width() * |
| 400 | (xBound - source.left) / source.width(); |
| 401 | source.right = xBound; |
| 402 | } |
| 403 | float yBound = (float) height / texHeight; |
| 404 | if (source.bottom > yBound) { |
| 405 | target.bottom = target.top + target.height() * |
| 406 | (yBound - source.top) / source.height(); |
| 407 | source.bottom = yBound; |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | public void drawMixed(BasicTexture from, |
| 412 | int toColor, float ratio, int x, int y, int w, int h) { |
| 413 | drawMixed(from, toColor, ratio, x, y, w, h, mAlpha); |
| 414 | } |
| 415 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 416 | private boolean bindTexture(BasicTexture texture) { |
| 417 | if (!texture.onBind(this)) return false; |
| 418 | mGLState.setTexture2DEnabled(true); |
| 419 | mGL.glBindTexture(GL11.GL_TEXTURE_2D, texture.getId()); |
| 420 | return true; |
| 421 | } |
| 422 | |
| 423 | private void setTextureColor(float r, float g, float b, float alpha) { |
| 424 | float[] color = mTextureColor; |
| 425 | color[0] = r; |
| 426 | color[1] = g; |
| 427 | color[2] = b; |
| 428 | color[3] = alpha; |
| 429 | } |
| 430 | |
| 431 | private void drawMixed(BasicTexture from, int toColor, |
| 432 | float ratio, int x, int y, int width, int height, float alpha) { |
| 433 | |
| 434 | if (ratio <= 0) { |
| 435 | drawTexture(from, x, y, width, height, alpha); |
| 436 | return; |
| 437 | } else if (ratio >= 1) { |
| 438 | fillRect(x, y, width, height, toColor); |
| 439 | return; |
| 440 | } |
| 441 | |
| 442 | mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque() |
| 443 | || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA)); |
| 444 | |
| 445 | final GL11 gl = mGL; |
| 446 | if (!bindTexture(from)) return; |
| 447 | |
| 448 | // |
| 449 | // The formula we want: |
| 450 | // alpha * ((1 - ratio) * from + ratio * to) |
| 451 | // The formula that GL supports is in the form of: |
| 452 | // combo * (modulate * from) + (1 - combo) * to |
| 453 | // |
| 454 | // So, we have combo = 1 - alpha * ratio |
| 455 | // and modulate = alpha * (1f - ratio) / combo |
| 456 | // |
| 457 | float comboRatio = 1 - alpha * ratio; |
| 458 | |
| 459 | // handle the case that (1 - comboRatio) == 0 |
| 460 | if (alpha < OPAQUE_ALPHA) { |
| 461 | mGLState.setTextureAlpha(alpha * (1f - ratio) / comboRatio); |
| 462 | } else { |
| 463 | mGLState.setTextureAlpha(1f); |
| 464 | } |
| 465 | |
| 466 | // Interpolate the RGB and alpha values between both textures. |
| 467 | mGLState.setTexEnvMode(GL11.GL_COMBINE); |
| 468 | // Specify the interpolation factor via the alpha component of |
| 469 | // GL_TEXTURE_ENV_COLORs. |
| 470 | // RGB component are get from toColor and will used as SRC1 |
| 471 | float colorAlpha = (float) (toColor >>> 24) / (0xff * 0xff); |
| 472 | setTextureColor(((toColor >>> 16) & 0xff) * colorAlpha, |
| 473 | ((toColor >>> 8) & 0xff) * colorAlpha, |
| 474 | (toColor & 0xff) * colorAlpha, comboRatio); |
| 475 | gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0); |
| 476 | |
| 477 | gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE); |
| 478 | gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE); |
| 479 | gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_RGB, GL11.GL_CONSTANT); |
| 480 | gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_RGB, GL11.GL_SRC_COLOR); |
| 481 | gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_ALPHA, GL11.GL_CONSTANT); |
| 482 | gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_ALPHA, GL11.GL_SRC_ALPHA); |
| 483 | |
| 484 | // Wire up the interpolation factor for RGB. |
| 485 | gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT); |
| 486 | gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA); |
| 487 | |
| 488 | // Wire up the interpolation factor for alpha. |
| 489 | gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT); |
| 490 | gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA); |
| 491 | |
| 492 | drawBoundTexture(from, x, y, width, height); |
| 493 | mGLState.setTexEnvMode(GL11.GL_REPLACE); |
| 494 | } |
| 495 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 496 | // TODO: the code only work for 2D should get fixed for 3D or removed |
| 497 | private static final int MSKEW_X = 4; |
| 498 | private static final int MSKEW_Y = 1; |
| 499 | private static final int MSCALE_X = 0; |
| 500 | private static final int MSCALE_Y = 5; |
| 501 | |
| 502 | private static boolean isMatrixRotatedOrFlipped(float matrix[]) { |
| 503 | final float eps = 1e-5f; |
| 504 | return Math.abs(matrix[MSKEW_X]) > eps |
| 505 | || Math.abs(matrix[MSKEW_Y]) > eps |
| 506 | || matrix[MSCALE_X] < -eps |
| 507 | || matrix[MSCALE_Y] > eps; |
| 508 | } |
| 509 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 510 | private static class GLState { |
| 511 | |
| 512 | private final GL11 mGL; |
| 513 | |
| 514 | private int mTexEnvMode = GL11.GL_REPLACE; |
| 515 | private float mTextureAlpha = 1.0f; |
| 516 | private boolean mTexture2DEnabled = true; |
| 517 | private boolean mBlendEnabled = true; |
| 518 | private float mLineWidth = 1.0f; |
| 519 | private boolean mLineSmooth = false; |
| 520 | |
| 521 | public GLState(GL11 gl) { |
| 522 | mGL = gl; |
| 523 | |
| 524 | // Disable unused state |
| 525 | gl.glDisable(GL11.GL_LIGHTING); |
| 526 | |
| 527 | // Enable used features |
| 528 | gl.glEnable(GL11.GL_DITHER); |
| 529 | gl.glEnable(GL11.GL_SCISSOR_TEST); |
| 530 | |
| 531 | gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); |
| 532 | gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); |
| 533 | gl.glEnable(GL11.GL_TEXTURE_2D); |
| 534 | |
| 535 | gl.glTexEnvf(GL11.GL_TEXTURE_ENV, |
| 536 | GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE); |
| 537 | |
| 538 | // Set the background color |
| 539 | gl.glClearColor(0f, 0f, 0f, 0f); |
| 540 | gl.glClearStencil(0); |
| 541 | |
| 542 | gl.glEnable(GL11.GL_BLEND); |
| 543 | gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); |
| 544 | |
| 545 | // We use 565 or 8888 format, so set the alignment to 2 bytes/pixel. |
| 546 | gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2); |
| 547 | } |
| 548 | |
| 549 | public void setTexEnvMode(int mode) { |
| 550 | if (mTexEnvMode == mode) return; |
| 551 | mTexEnvMode = mode; |
| 552 | mGL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, mode); |
| 553 | } |
| 554 | |
| 555 | public void setLineWidth(float width) { |
| 556 | if (mLineWidth == width) return; |
| 557 | mLineWidth = width; |
| 558 | mGL.glLineWidth(width); |
| 559 | } |
| 560 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 561 | public void setTextureAlpha(float alpha) { |
| 562 | if (mTextureAlpha == alpha) return; |
| 563 | mTextureAlpha = alpha; |
| 564 | if (alpha >= OPAQUE_ALPHA) { |
| 565 | // The alpha is need for those texture without alpha channel |
| 566 | mGL.glColor4f(1, 1, 1, 1); |
| 567 | setTexEnvMode(GL11.GL_REPLACE); |
| 568 | } else { |
| 569 | mGL.glColor4f(alpha, alpha, alpha, alpha); |
| 570 | setTexEnvMode(GL11.GL_MODULATE); |
| 571 | } |
| 572 | } |
| 573 | |
| 574 | public void setColorMode(int color, float alpha) { |
| 575 | setBlendEnabled(!Utils.isOpaque(color) || alpha < OPAQUE_ALPHA); |
| 576 | |
| 577 | // Set mTextureAlpha to an invalid value, so that it will reset |
| 578 | // again in setTextureAlpha(float) later. |
| 579 | mTextureAlpha = -1.0f; |
| 580 | |
| 581 | setTexture2DEnabled(false); |
| 582 | |
| 583 | float prealpha = (color >>> 24) * alpha * 65535f / 255f / 255f; |
| 584 | mGL.glColor4x( |
| 585 | Math.round(((color >> 16) & 0xFF) * prealpha), |
| 586 | Math.round(((color >> 8) & 0xFF) * prealpha), |
| 587 | Math.round((color & 0xFF) * prealpha), |
| 588 | Math.round(255 * prealpha)); |
| 589 | } |
| 590 | |
| 591 | public void setTexture2DEnabled(boolean enabled) { |
| 592 | if (mTexture2DEnabled == enabled) return; |
| 593 | mTexture2DEnabled = enabled; |
| 594 | if (enabled) { |
| 595 | mGL.glEnable(GL11.GL_TEXTURE_2D); |
| 596 | } else { |
| 597 | mGL.glDisable(GL11.GL_TEXTURE_2D); |
| 598 | } |
| 599 | } |
| 600 | |
| 601 | public void setBlendEnabled(boolean enabled) { |
| 602 | if (mBlendEnabled == enabled) return; |
| 603 | mBlendEnabled = enabled; |
| 604 | if (enabled) { |
| 605 | mGL.glEnable(GL11.GL_BLEND); |
| 606 | } else { |
| 607 | mGL.glDisable(GL11.GL_BLEND); |
| 608 | } |
| 609 | } |
| 610 | } |
| 611 | |
| 612 | public GL11 getGLInstance() { |
| 613 | return mGL; |
| 614 | } |
| 615 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 616 | public void clearBuffer() { |
| 617 | mGL.glClear(GL10.GL_COLOR_BUFFER_BIT); |
| 618 | } |
| 619 | |
| 620 | private void setTextureCoords(RectF source) { |
| 621 | setTextureCoords(source.left, source.top, source.right, source.bottom); |
| 622 | } |
| 623 | |
| 624 | private void setTextureCoords(float left, float top, |
| 625 | float right, float bottom) { |
| 626 | mGL.glMatrixMode(GL11.GL_TEXTURE); |
| 627 | mTextureMatrixValues[0] = right - left; |
| 628 | mTextureMatrixValues[5] = bottom - top; |
| 629 | mTextureMatrixValues[10] = 1; |
| 630 | mTextureMatrixValues[12] = left; |
| 631 | mTextureMatrixValues[13] = top; |
| 632 | mTextureMatrixValues[15] = 1; |
| 633 | mGL.glLoadMatrixf(mTextureMatrixValues, 0); |
| 634 | mGL.glMatrixMode(GL11.GL_MODELVIEW); |
| 635 | } |
| 636 | |
| 637 | // unloadTexture and deleteBuffer can be called from the finalizer thread, |
| 638 | // so we synchronized on the mUnboundTextures object. |
| 639 | public boolean unloadTexture(BasicTexture t) { |
| 640 | synchronized (mUnboundTextures) { |
| 641 | if (!t.isLoaded(this)) return false; |
| 642 | mUnboundTextures.add(t.mId); |
| 643 | return true; |
| 644 | } |
| 645 | } |
| 646 | |
| 647 | public void deleteBuffer(int bufferId) { |
| 648 | synchronized (mUnboundTextures) { |
| 649 | mDeleteBuffers.add(bufferId); |
| 650 | } |
| 651 | } |
| 652 | |
| 653 | public void deleteRecycledResources() { |
| 654 | synchronized (mUnboundTextures) { |
| 655 | IntArray ids = mUnboundTextures; |
| 656 | if (ids.size() > 0) { |
| 657 | mGL.glDeleteTextures(ids.size(), ids.getInternalArray(), 0); |
| 658 | ids.clear(); |
| 659 | } |
| 660 | |
| 661 | ids = mDeleteBuffers; |
| 662 | if (ids.size() > 0) { |
| 663 | mGL.glDeleteBuffers(ids.size(), ids.getInternalArray(), 0); |
| 664 | ids.clear(); |
| 665 | } |
| 666 | } |
| 667 | } |
| 668 | |
Chih-Chung Chang | be55f1e | 2012-02-18 06:17:18 +0800 | [diff] [blame] | 669 | public void save() { |
| 670 | save(SAVE_FLAG_ALL); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 671 | } |
| 672 | |
Chih-Chung Chang | be55f1e | 2012-02-18 06:17:18 +0800 | [diff] [blame] | 673 | public void save(int saveFlags) { |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 674 | ConfigState config = obtainRestoreConfig(); |
| 675 | |
| 676 | if ((saveFlags & SAVE_FLAG_ALPHA) != 0) { |
| 677 | config.mAlpha = mAlpha; |
| 678 | } else { |
| 679 | config.mAlpha = -1; |
| 680 | } |
| 681 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 682 | |
| 683 | if ((saveFlags & SAVE_FLAG_MATRIX) != 0) { |
| 684 | System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16); |
| 685 | } else { |
| 686 | config.mMatrix[0] = Float.NEGATIVE_INFINITY; |
| 687 | } |
| 688 | |
Chih-Chung Chang | be55f1e | 2012-02-18 06:17:18 +0800 | [diff] [blame] | 689 | mRestoreStack.add(config); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 690 | } |
| 691 | |
| 692 | public void restore() { |
| 693 | if (mRestoreStack.isEmpty()) throw new IllegalStateException(); |
Chih-Chung Chang | be55f1e | 2012-02-18 06:17:18 +0800 | [diff] [blame] | 694 | ConfigState config = mRestoreStack.remove(mRestoreStack.size() - 1); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 695 | config.restore(this); |
| 696 | freeRestoreConfig(config); |
| 697 | } |
| 698 | |
| 699 | private void freeRestoreConfig(ConfigState action) { |
| 700 | action.mNextFree = mRecycledRestoreAction; |
| 701 | mRecycledRestoreAction = action; |
| 702 | } |
| 703 | |
| 704 | private ConfigState obtainRestoreConfig() { |
| 705 | if (mRecycledRestoreAction != null) { |
| 706 | ConfigState result = mRecycledRestoreAction; |
| 707 | mRecycledRestoreAction = result.mNextFree; |
| 708 | return result; |
| 709 | } |
| 710 | return new ConfigState(); |
| 711 | } |
| 712 | |
| 713 | private static class ConfigState { |
| 714 | float mAlpha; |
| 715 | Rect mRect = new Rect(); |
| 716 | float mMatrix[] = new float[16]; |
| 717 | ConfigState mNextFree; |
| 718 | |
| 719 | public void restore(GLCanvasImpl canvas) { |
| 720 | if (mAlpha >= 0) canvas.setAlpha(mAlpha); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 721 | if (mMatrix[0] != Float.NEGATIVE_INFINITY) { |
| 722 | System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16); |
| 723 | } |
| 724 | } |
| 725 | } |
| 726 | |
| 727 | public void dumpStatisticsAndClear() { |
| 728 | String line = String.format( |
| 729 | "MESH:%d, TEX_OES:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", |
| 730 | mCountDrawMesh, mCountTextureRect, mCountTextureOES, |
| 731 | mCountFillRect, mCountDrawLine); |
| 732 | mCountDrawMesh = 0; |
| 733 | mCountTextureRect = 0; |
| 734 | mCountTextureOES = 0; |
| 735 | mCountFillRect = 0; |
| 736 | mCountDrawLine = 0; |
| 737 | Log.d(TAG, line); |
| 738 | } |
| 739 | |
| 740 | private void saveTransform() { |
| 741 | System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16); |
| 742 | } |
| 743 | |
| 744 | private void restoreTransform() { |
| 745 | System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16); |
| 746 | } |
| 747 | } |