1 /* 2 * Copyright (C) 2012 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 package com.android.gallery3d.glrenderer; 17 18 import android.graphics.Bitmap; 19 import android.graphics.RectF; 20 import android.opengl.GLES20; 21 import android.opengl.GLUtils; 22 import android.opengl.Matrix; 23 import android.util.Log; 24 25 import java.nio.Buffer; 26 import java.nio.ByteBuffer; 27 import java.nio.ByteOrder; 28 import java.nio.FloatBuffer; 29 import java.util.Arrays; 30 31 import javax.microedition.khronos.opengles.GL11; 32 33 public class GLES20Canvas implements GLCanvas { 34 // ************** Constants ********************** 35 private static final String TAG = GLES20Canvas.class.getSimpleName(); 36 private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE; 37 38 private static final int COORDS_PER_VERTEX = 2; 39 private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE; 40 41 private static final int COUNT_FILL_VERTEX = 4; 42 private static final int OFFSET_FILL_RECT = 0; 43 44 private static final int GL_TARGET = GL11.GL_TEXTURE_2D; 45 46 private static final float[] BOX_COORDINATES = { 47 0, 0, // Fill rectangle 48 1, 0, 49 0, 1, 50 1, 1, 51 0, 0, // Draw line 52 1, 1, 53 0, 0, // Draw rectangle outline 54 0, 1, 55 1, 1, 56 1, 0, 57 }; 58 59 private static final String POSITION_ATTRIBUTE = "aPosition"; 60 private static final String MATRIX_UNIFORM = "uMatrix"; 61 private static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix"; 62 private static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler"; 63 private static final String ALPHA_UNIFORM = "uAlpha"; 64 65 private static final String TEXTURE_VERTEX_SHADER = "" 66 + "uniform mat4 " + MATRIX_UNIFORM + ";\n" 67 + "uniform mat4 " + TEXTURE_MATRIX_UNIFORM + ";\n" 68 + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n" 69 + "varying vec2 vTextureCoord;\n" 70 + "void main() {\n" 71 + " vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n" 72 + " gl_Position = " + MATRIX_UNIFORM + " * pos;\n" 73 + " vTextureCoord = (" + TEXTURE_MATRIX_UNIFORM + " * pos).xy;\n" 74 + "}\n"; 75 76 private static final String TEXTURE_FRAGMENT_SHADER = "" 77 + "precision mediump float;\n" 78 + "varying vec2 vTextureCoord;\n" 79 + "uniform float " + ALPHA_UNIFORM + ";\n" 80 + "uniform sampler2D " + TEXTURE_SAMPLER_UNIFORM + ";\n" 81 + "void main() {\n" 82 + " gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n" 83 + " gl_FragColor *= " + ALPHA_UNIFORM + ";\n" 84 + "}\n"; 85 86 private static final int INITIAL_RESTORE_STATE_SIZE = 8; 87 private static final int MATRIX_SIZE = 16; 88 89 // Keep track of restore state 90 private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE]; 91 private IntArray mSaveFlags = new IntArray(); 92 93 private int mCurrentMatrixIndex = 0; 94 95 // Viewport size 96 private int mWidth; 97 private int mHeight; 98 99 // Projection matrix 100 private float[] mProjectionMatrix = new float[MATRIX_SIZE]; 101 102 // GL programs 103 private int mTextureProgram; 104 105 // GL buffer containing BOX_COORDINATES 106 private int mBoxCoordinates; 107 108 // Handle indices -- common 109 private static final int INDEX_POSITION = 0; 110 private static final int INDEX_MATRIX = 1; 111 112 // Handle indices -- texture 113 private static final int INDEX_TEXTURE_MATRIX = 2; 114 private static final int INDEX_TEXTURE_SAMPLER = 3; 115 private static final int INDEX_ALPHA = 4; 116 117 private abstract static class ShaderParameter { 118 public int handle; 119 protected final String mName; 120 ShaderParameter(String name)121 public ShaderParameter(String name) { 122 mName = name; 123 } 124 loadHandle(int program)125 public abstract void loadHandle(int program); 126 } 127 128 private static class UniformShaderParameter extends ShaderParameter { UniformShaderParameter(String name)129 public UniformShaderParameter(String name) { 130 super(name); 131 } 132 133 @Override loadHandle(int program)134 public void loadHandle(int program) { 135 handle = GLES20.glGetUniformLocation(program, mName); 136 checkError(); 137 } 138 } 139 140 private static class AttributeShaderParameter extends ShaderParameter { AttributeShaderParameter(String name)141 public AttributeShaderParameter(String name) { 142 super(name); 143 } 144 145 @Override loadHandle(int program)146 public void loadHandle(int program) { 147 handle = GLES20.glGetAttribLocation(program, mName); 148 checkError(); 149 } 150 } 151 152 private ShaderParameter[] mTextureParameters = { 153 new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION 154 new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX 155 new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX 156 new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER 157 new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA 158 }; 159 160 private final IntArray mUnboundTextures = new IntArray(); 161 162 // Temporary variables used within calculations 163 private final float[] mTempMatrix = new float[32]; 164 private final RectF mTempSourceRect = new RectF(); 165 private final RectF mTempTargetRect = new RectF(); 166 private final float[] mTempTextureMatrix = new float[MATRIX_SIZE]; 167 private final int[] mTempIntArray = new int[1]; 168 169 private static final GLId mGLId = new GLES20IdImpl(); 170 GLES20Canvas()171 public GLES20Canvas() { 172 Matrix.setIdentityM(mTempTextureMatrix, 0); 173 Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex); 174 175 FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES); 176 mBoxCoordinates = uploadBuffer(boxBuffer); 177 178 int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER); 179 int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER); 180 181 mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader, 182 mTextureParameters); 183 GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA); 184 checkError(); 185 } 186 createBuffer(float[] values)187 private static FloatBuffer createBuffer(float[] values) { 188 // First create an nio buffer, then create a VBO from it. 189 int size = values.length * FLOAT_SIZE; 190 FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()) 191 .asFloatBuffer(); 192 buffer.put(values, 0, values.length).position(0); 193 return buffer; 194 } 195 assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params)196 private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) { 197 int program = GLES20.glCreateProgram(); 198 checkError(); 199 if (program == 0) { 200 throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError()); 201 } 202 GLES20.glAttachShader(program, vertexShader); 203 checkError(); 204 GLES20.glAttachShader(program, fragmentShader); 205 checkError(); 206 GLES20.glLinkProgram(program); 207 checkError(); 208 int[] mLinkStatus = mTempIntArray; 209 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0); 210 if (mLinkStatus[0] != GLES20.GL_TRUE) { 211 Log.e(TAG, "Could not link program: "); 212 Log.e(TAG, GLES20.glGetProgramInfoLog(program)); 213 GLES20.glDeleteProgram(program); 214 program = 0; 215 } 216 for (int i = 0; i < params.length; i++) { 217 params[i].loadHandle(program); 218 } 219 return program; 220 } 221 loadShader(int type, String shaderCode)222 private static int loadShader(int type, String shaderCode) { 223 // create a vertex shader type (GLES20.GL_VERTEX_SHADER) 224 // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) 225 int shader = GLES20.glCreateShader(type); 226 227 // add the source code to the shader and compile it 228 GLES20.glShaderSource(shader, shaderCode); 229 checkError(); 230 GLES20.glCompileShader(shader); 231 checkError(); 232 233 return shader; 234 } 235 236 @Override setSize(int width, int height)237 public void setSize(int width, int height) { 238 mWidth = width; 239 mHeight = height; 240 GLES20.glViewport(0, 0, mWidth, mHeight); 241 checkError(); 242 Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex); 243 Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1); 244 Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0); 245 Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1); 246 } 247 248 @Override clearBuffer()249 public void clearBuffer() { 250 GLES20.glClearColor(0f, 0f, 0f, 1f); 251 checkError(); 252 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 253 checkError(); 254 } 255 256 // This is a faster version of translate(x, y, z) because 257 // (1) we knows z = 0, (2) we inline the Matrix.translateM call, 258 // (3) we unroll the loop 259 @Override translate(float x, float y)260 public void translate(float x, float y) { 261 int index = mCurrentMatrixIndex; 262 float[] m = mMatrices; 263 m[index + 12] += m[index + 0] * x + m[index + 4] * y; 264 m[index + 13] += m[index + 1] * x + m[index + 5] * y; 265 m[index + 14] += m[index + 2] * x + m[index + 6] * y; 266 m[index + 15] += m[index + 3] * x + m[index + 7] * y; 267 } 268 269 @Override rotate(float angle, float x, float y, float z)270 public void rotate(float angle, float x, float y, float z) { 271 if (angle == 0f) { 272 return; 273 } 274 float[] temp = mTempMatrix; 275 Matrix.setRotateM(temp, 0, angle, x, y, z); 276 float[] matrix = mMatrices; 277 int index = mCurrentMatrixIndex; 278 Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0); 279 System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE); 280 } 281 282 @Override save(int saveFlags)283 public void save(int saveFlags) { 284 boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX; 285 if (saveMatrix) { 286 int currentIndex = mCurrentMatrixIndex; 287 mCurrentMatrixIndex += MATRIX_SIZE; 288 if (mMatrices.length <= mCurrentMatrixIndex) { 289 mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2); 290 } 291 System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE); 292 } 293 mSaveFlags.add(saveFlags); 294 } 295 296 @Override restore()297 public void restore() { 298 int restoreFlags = mSaveFlags.removeLast(); 299 boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX; 300 if (restoreMatrix) { 301 mCurrentMatrixIndex -= MATRIX_SIZE; 302 } 303 } 304 setPosition(ShaderParameter[] params, int offset)305 private void setPosition(ShaderParameter[] params, int offset) { 306 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates); 307 checkError(); 308 GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX, 309 GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE); 310 checkError(); 311 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); 312 checkError(); 313 } 314 draw(ShaderParameter[] params, int type, int count, float x, float y, float width, float height)315 private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width, 316 float height) { 317 setMatrix(params, x, y, width, height); 318 int positionHandle = params[INDEX_POSITION].handle; 319 GLES20.glEnableVertexAttribArray(positionHandle); 320 checkError(); 321 GLES20.glDrawArrays(type, 0, count); 322 checkError(); 323 GLES20.glDisableVertexAttribArray(positionHandle); 324 checkError(); 325 } 326 setMatrix(ShaderParameter[] params, float x, float y, float width, float height)327 private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) { 328 Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f); 329 Matrix.scaleM(mTempMatrix, 0, width, height, 1f); 330 Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0); 331 GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE); 332 checkError(); 333 } 334 335 @Override drawTexture(BasicTexture texture, int x, int y, int width, int height)336 public void drawTexture(BasicTexture texture, int x, int y, int width, int height) { 337 if (width <= 0 || height <= 0) { 338 return; 339 } 340 copyTextureCoordinates(texture, mTempSourceRect); 341 mTempTargetRect.set(x, y, x + width, y + height); 342 convertCoordinate(mTempSourceRect, mTempTargetRect, texture); 343 drawTextureRect(texture, mTempSourceRect, mTempTargetRect); 344 } 345 copyTextureCoordinates(BasicTexture texture, RectF outRect)346 private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) { 347 outRect.set(0, 0, texture.getWidth(), texture.getHeight()); 348 } 349 350 @Override drawTexture(BasicTexture texture, RectF source, RectF target)351 public void drawTexture(BasicTexture texture, RectF source, RectF target) { 352 if (target.width() <= 0 || target.height() <= 0) { 353 return; 354 } 355 mTempSourceRect.set(source); 356 mTempTargetRect.set(target); 357 358 convertCoordinate(mTempSourceRect, mTempTargetRect, texture); 359 drawTextureRect(texture, mTempSourceRect, mTempTargetRect); 360 } 361 drawTextureRect(BasicTexture texture, RectF source, RectF target)362 private void drawTextureRect(BasicTexture texture, RectF source, RectF target) { 363 setTextureMatrix(source); 364 drawTextureRect(texture, mTempTextureMatrix, target); 365 } 366 setTextureMatrix(RectF source)367 private void setTextureMatrix(RectF source) { 368 mTempTextureMatrix[0] = source.width(); 369 mTempTextureMatrix[5] = source.height(); 370 mTempTextureMatrix[12] = source.left; 371 mTempTextureMatrix[13] = source.top; 372 } 373 374 // This function changes the source coordinate to the texture coordinates. 375 // It also clips the source and target coordinates if it is beyond the 376 // bound of the texture. convertCoordinate(RectF source, RectF target, BasicTexture texture)377 private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) { 378 int width = texture.getWidth(); 379 int height = texture.getHeight(); 380 int texWidth = texture.getTextureWidth(); 381 int texHeight = texture.getTextureHeight(); 382 // Convert to texture coordinates 383 source.left /= texWidth; 384 source.right /= texWidth; 385 source.top /= texHeight; 386 source.bottom /= texHeight; 387 388 // Clip if the rendering range is beyond the bound of the texture. 389 float xBound = (float) width / texWidth; 390 if (source.right > xBound) { 391 target.right = target.left + target.width() * (xBound - source.left) / source.width(); 392 source.right = xBound; 393 } 394 float yBound = (float) height / texHeight; 395 if (source.bottom > yBound) { 396 target.bottom = target.top + target.height() * (yBound - source.top) / source.height(); 397 source.bottom = yBound; 398 } 399 } 400 drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target)401 private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) { 402 ShaderParameter[] params = prepareTexture(texture); 403 setPosition(params, OFFSET_FILL_RECT); 404 GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0); 405 checkError(); 406 draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top, 407 target.width(), target.height()); 408 } 409 prepareTexture(BasicTexture texture)410 private ShaderParameter[] prepareTexture(BasicTexture texture) { 411 ShaderParameter[] params; 412 int program; 413 params = mTextureParameters; 414 program = mTextureProgram; 415 prepareTexture(texture, program, params); 416 return params; 417 } 418 prepareTexture(BasicTexture texture, int program, ShaderParameter[] params)419 private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) { 420 deleteRecycledResources(); 421 GLES20.glUseProgram(program); 422 checkError(); 423 GLES20.glDisable(GLES20.GL_BLEND); 424 checkError(); 425 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 426 checkError(); 427 texture.onBind(this); 428 GLES20.glBindTexture(GL_TARGET, texture.getId()); 429 checkError(); 430 GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0); 431 checkError(); 432 GLES20.glUniform1f(params[INDEX_ALPHA].handle, 1); 433 checkError(); 434 } 435 436 @Override unloadTexture(BasicTexture texture)437 public boolean unloadTexture(BasicTexture texture) { 438 boolean unload = texture.isLoaded(); 439 if (unload) { 440 synchronized (mUnboundTextures) { 441 mUnboundTextures.add(texture.getId()); 442 } 443 } 444 return unload; 445 } 446 447 @Override deleteRecycledResources()448 public void deleteRecycledResources() { 449 synchronized (mUnboundTextures) { 450 IntArray ids = mUnboundTextures; 451 if (mUnboundTextures.size() > 0) { 452 mGLId.glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0); 453 ids.clear(); 454 } 455 } 456 } 457 458 @Override setTextureParameters(BasicTexture texture)459 public void setTextureParameters(BasicTexture texture) { 460 GLES20.glBindTexture(GL_TARGET, texture.getId()); 461 checkError(); 462 GLES20.glTexParameteri(GL_TARGET, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); 463 GLES20.glTexParameteri(GL_TARGET, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); 464 GLES20.glTexParameterf(GL_TARGET, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); 465 GLES20.glTexParameterf(GL_TARGET, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); 466 } 467 468 @Override initializeTextureSize(BasicTexture texture, int format, int type)469 public void initializeTextureSize(BasicTexture texture, int format, int type) { 470 GLES20.glBindTexture(GL_TARGET, texture.getId()); 471 checkError(); 472 int width = texture.getTextureWidth(); 473 int height = texture.getTextureHeight(); 474 GLES20.glTexImage2D(GL_TARGET, 0, format, width, height, 0, format, type, null); 475 } 476 477 @Override initializeTexture(BasicTexture texture, Bitmap bitmap)478 public void initializeTexture(BasicTexture texture, Bitmap bitmap) { 479 GLES20.glBindTexture(GL_TARGET, texture.getId()); 480 checkError(); 481 GLUtils.texImage2D(GL_TARGET, 0, bitmap, 0); 482 } 483 484 @Override texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap, int format, int type)485 public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap, 486 int format, int type) { 487 GLES20.glBindTexture(GL_TARGET, texture.getId()); 488 checkError(); 489 GLUtils.texSubImage2D(GL_TARGET, 0, xOffset, yOffset, bitmap, format, type); 490 } 491 492 @Override uploadBuffer(FloatBuffer buf)493 public int uploadBuffer(FloatBuffer buf) { 494 return uploadBuffer(buf, FLOAT_SIZE); 495 } 496 uploadBuffer(Buffer buffer, int elementSize)497 private int uploadBuffer(Buffer buffer, int elementSize) { 498 mGLId.glGenBuffers(1, mTempIntArray, 0); 499 checkError(); 500 int bufferId = mTempIntArray[0]; 501 GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId); 502 checkError(); 503 GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer, 504 GLES20.GL_STATIC_DRAW); 505 checkError(); 506 return bufferId; 507 } 508 checkError()509 public static void checkError() { 510 int error = GLES20.glGetError(); 511 if (error != 0) { 512 Throwable t = new Throwable(); 513 Log.e(TAG, "GL error: " + error, t); 514 } 515 } 516 517 @Override getGLId()518 public GLId getGLId() { 519 return mGLId; 520 } 521 } 522