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.photoeditor; 18 19 import android.graphics.Bitmap; 20 import android.opengl.GLES20; 21 import android.opengl.GLUtils; 22 import android.util.FloatMath; 23 24 import java.nio.ByteBuffer; 25 import java.nio.ByteOrder; 26 import java.nio.FloatBuffer; 27 28 /** 29 * Utils for GL renderer. 30 */ 31 public class RendererUtils { 32 33 public static class RenderContext { 34 private int shaderProgram; 35 private int texSamplerHandle; 36 private int texCoordHandle; 37 private int posCoordHandle; 38 private FloatBuffer texVertices; 39 private FloatBuffer posVertices; 40 } 41 42 private static final float[] TEX_VERTICES = { 43 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f 44 }; 45 46 private static final float[] POS_VERTICES = { 47 -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f 48 }; 49 50 private static final String VERTEX_SHADER = 51 "attribute vec4 a_position;\n" + 52 "attribute vec2 a_texcoord;\n" + 53 "varying vec2 v_texcoord;\n" + 54 "void main() {\n" + 55 " gl_Position = a_position;\n" + 56 " v_texcoord = a_texcoord;\n" + 57 "}\n"; 58 59 private static final String FRAGMENT_SHADER = 60 "precision mediump float;\n" + 61 "uniform sampler2D tex_sampler;\n" + 62 "varying vec2 v_texcoord;\n" + 63 "void main() {\n" + 64 " gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" + 65 "}\n"; 66 67 private static final int FLOAT_SIZE_BYTES = 4; 68 private static final float DEGREE_TO_RADIAN = (float) Math.PI / 180.0f; 69 createTexture()70 public static int createTexture() { 71 int[] textures = new int[1]; 72 GLES20.glGenTextures(textures.length, textures, 0); 73 checkGlError("glGenTextures"); 74 return textures[0]; 75 } 76 createTexture(Bitmap bitmap)77 public static int createTexture(Bitmap bitmap) { 78 int texture = createTexture(); 79 80 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture); 81 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); 82 GLES20.glTexParameteri( 83 GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); 84 GLES20.glTexParameteri( 85 GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); 86 GLES20.glTexParameteri( 87 GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); 88 GLES20.glTexParameteri( 89 GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); 90 checkGlError("texImage2D"); 91 92 return texture; 93 } 94 saveTexture(int texture, int width, int height)95 public static Bitmap saveTexture(int texture, int width, int height) { 96 int[] frame = new int[1]; 97 GLES20.glGenFramebuffers(1, frame, 0); 98 checkGlError("glGenFramebuffers"); 99 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frame[0]); 100 checkGlError("glBindFramebuffer"); 101 GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, 102 GLES20.GL_TEXTURE_2D, texture, 0); 103 checkGlError("glFramebufferTexture2D"); 104 105 ByteBuffer buffer = ByteBuffer.allocate(width * height * 4); 106 GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer); 107 checkGlError("glReadPixels"); 108 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 109 bitmap.copyPixelsFromBuffer(buffer); 110 111 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); 112 checkGlError("glBindFramebuffer"); 113 GLES20.glDeleteFramebuffers(1, frame, 0); 114 checkGlError("glDeleteFramebuffer"); 115 return bitmap; 116 } 117 clearTexture(int texture)118 public static void clearTexture(int texture) { 119 int[] textures = new int[1]; 120 textures[0] = texture; 121 GLES20.glDeleteTextures(textures.length, textures, 0); 122 checkGlError("glDeleteTextures"); 123 } 124 getFitVertices(int srcWidth, int srcHeight, int dstWidth, int dstHeight)125 private static float[] getFitVertices(int srcWidth, int srcHeight, int dstWidth, 126 int dstHeight) { 127 float srcAspectRatio = ((float) srcWidth) / srcHeight; 128 float dstAspectRatio = ((float) dstWidth) / dstHeight; 129 float relativeAspectRatio = dstAspectRatio / srcAspectRatio; 130 131 float[] vertices = new float[8]; 132 System.arraycopy(POS_VERTICES, 0, vertices, 0, vertices.length); 133 if (relativeAspectRatio > 1.0f) { 134 // Screen is wider than the camera, scale down X 135 vertices[0] /= relativeAspectRatio; 136 vertices[2] /= relativeAspectRatio; 137 vertices[4] /= relativeAspectRatio; 138 vertices[6] /= relativeAspectRatio; 139 } else { 140 vertices[1] *= relativeAspectRatio; 141 vertices[3] *= relativeAspectRatio; 142 vertices[5] *= relativeAspectRatio; 143 vertices[7] *= relativeAspectRatio; 144 } 145 return vertices; 146 } 147 setRenderToFit(RenderContext context, int srcWidth, int srcHeight, int dstWidth, int dstHeight)148 public static void setRenderToFit(RenderContext context, int srcWidth, int srcHeight, 149 int dstWidth, int dstHeight) { 150 context.posVertices = createVerticesBuffer( 151 getFitVertices(srcWidth, srcHeight, dstWidth, dstHeight)); 152 } 153 setRenderToRotate(RenderContext context, int srcWidth, int srcHeight, int dstWidth, int dstHeight, float degrees)154 public static void setRenderToRotate(RenderContext context, int srcWidth, int srcHeight, 155 int dstWidth, int dstHeight, float degrees) { 156 float radian = -degrees * DEGREE_TO_RADIAN; 157 float cosTheta = FloatMath.cos(radian); 158 float sinTheta = FloatMath.sin(radian); 159 float cosWidth = cosTheta * srcWidth; 160 float sinWidth = sinTheta * srcWidth; 161 float cosHeight = cosTheta * srcHeight; 162 float sinHeight = sinTheta * srcHeight; 163 164 float[] vertices = new float[8]; 165 vertices[0] = -cosWidth + sinHeight; 166 vertices[1] = -sinWidth - cosHeight; 167 vertices[2] = cosWidth + sinHeight; 168 vertices[3] = sinWidth - cosHeight; 169 vertices[4] = -vertices[2]; 170 vertices[5] = -vertices[3]; 171 vertices[6] = -vertices[0]; 172 vertices[7] = -vertices[1]; 173 174 float maxWidth = Math.max(Math.abs(vertices[0]), Math.abs(vertices[2])); 175 float maxHeight = Math.max(Math.abs(vertices[1]), Math.abs(vertices[3])); 176 float scale = Math.min(dstWidth / maxWidth, dstHeight / maxHeight); 177 178 for (int i = 0; i < 8; i += 2) { 179 vertices[i] *= scale / dstWidth; 180 vertices[i + 1] *= scale / dstHeight; 181 } 182 context.posVertices = createVerticesBuffer(vertices); 183 } 184 setRenderToFlip(RenderContext context, int srcWidth, int srcHeight, int dstWidth, int dstHeight, float horizontalDegrees, float verticalDegrees)185 public static void setRenderToFlip(RenderContext context, int srcWidth, int srcHeight, 186 int dstWidth, int dstHeight, float horizontalDegrees, float verticalDegrees) { 187 // Calculate the base flip coordinates. 188 float[] base = getFitVertices(srcWidth, srcHeight, dstWidth, dstHeight); 189 int horizontalRounds = (int) horizontalDegrees / 180; 190 if (horizontalRounds % 2 != 0) { 191 base[0] = -base[0]; 192 base[4] = base[0]; 193 base[2] = -base[2]; 194 base[6] = base[2]; 195 } 196 int verticalRounds = (int) verticalDegrees / 180; 197 if (verticalRounds % 2 != 0) { 198 base[1] = -base[1]; 199 base[3] = base[1]; 200 base[5] = -base[5]; 201 base[7] = base[5]; 202 } 203 204 float length = 5; 205 float[] vertices = new float[8]; 206 System.arraycopy(base, 0, vertices, 0, vertices.length); 207 if (horizontalDegrees % 180f != 0) { 208 float radian = (horizontalDegrees - horizontalRounds * 180) * DEGREE_TO_RADIAN; 209 float cosTheta = FloatMath.cos(radian); 210 float sinTheta = FloatMath.sin(radian); 211 212 float scale = length / (length + sinTheta * base[0]); 213 vertices[0] = cosTheta * base[0] * scale; 214 vertices[1] = base[1] * scale; 215 vertices[4] = vertices[0]; 216 vertices[5] = base[5] * scale; 217 218 scale = length / (length + sinTheta * base[2]); 219 vertices[2] = cosTheta * base[2] * scale; 220 vertices[3] = base[3] * scale; 221 vertices[6] = vertices[2]; 222 vertices[7] = base[7] * scale; 223 } 224 225 if (verticalDegrees % 180f != 0) { 226 float radian = (verticalDegrees - verticalRounds * 180) * DEGREE_TO_RADIAN; 227 float cosTheta = FloatMath.cos(radian); 228 float sinTheta = FloatMath.sin(radian); 229 230 float scale = length / (length + sinTheta * base[1]); 231 vertices[0] = base[0] * scale; 232 vertices[1] = cosTheta * base[1] * scale; 233 vertices[2] = base[2] * scale; 234 vertices[3] = vertices[1]; 235 236 scale = length / (length + sinTheta * base[5]); 237 vertices[4] = base[4] * scale; 238 vertices[5] = cosTheta * base[5] * scale; 239 vertices[6] = base[6] * scale; 240 vertices[7] = vertices[5]; 241 } 242 context.posVertices = createVerticesBuffer(vertices); 243 } 244 renderBackground()245 public static void renderBackground() { 246 GLES20.glClearColor(0, 0, 0, 1); 247 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 248 } 249 renderTexture( RenderContext context, int texture, int viewWidth, int viewHeight)250 public static void renderTexture( 251 RenderContext context, int texture, int viewWidth, int viewHeight) { 252 // Use our shader program 253 GLES20.glUseProgram(context.shaderProgram); 254 checkGlError("glUseProgram"); 255 256 // Set viewport 257 GLES20.glViewport(0, 0, viewWidth, viewHeight); 258 checkGlError("glViewport"); 259 260 // Disable blending 261 GLES20.glDisable(GLES20.GL_BLEND); 262 263 // Set the vertex attributes 264 GLES20.glVertexAttribPointer( 265 context.texCoordHandle, 2, GLES20.GL_FLOAT, false, 0, context.texVertices); 266 GLES20.glEnableVertexAttribArray(context.texCoordHandle); 267 GLES20.glVertexAttribPointer( 268 context.posCoordHandle, 2, GLES20.GL_FLOAT, false, 0, context.posVertices); 269 GLES20.glEnableVertexAttribArray(context.posCoordHandle); 270 checkGlError("vertex attribute setup"); 271 272 // Set the input texture 273 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 274 checkGlError("glActiveTexture"); 275 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture); 276 checkGlError("glBindTexture"); 277 GLES20.glUniform1i(context.texSamplerHandle, 0); 278 279 // Draw! 280 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); 281 } 282 createProgram()283 public static RenderContext createProgram() { 284 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); 285 if (vertexShader == 0) { 286 return null; 287 } 288 int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); 289 if (pixelShader == 0) { 290 return null; 291 } 292 293 int program = GLES20.glCreateProgram(); 294 if (program != 0) { 295 GLES20.glAttachShader(program, vertexShader); 296 checkGlError("glAttachShader"); 297 GLES20.glAttachShader(program, pixelShader); 298 checkGlError("glAttachShader"); 299 GLES20.glLinkProgram(program); 300 int[] linkStatus = new int[1]; 301 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); 302 if (linkStatus[0] != GLES20.GL_TRUE) { 303 String info = GLES20.glGetProgramInfoLog(program); 304 GLES20.glDeleteProgram(program); 305 program = 0; 306 throw new RuntimeException("Could not link program: " + info); 307 } 308 } 309 310 // Bind attributes and uniforms 311 RenderContext context = new RenderContext(); 312 context.texSamplerHandle = GLES20.glGetUniformLocation(program, "tex_sampler"); 313 context.texCoordHandle = GLES20.glGetAttribLocation(program, "a_texcoord"); 314 context.posCoordHandle = GLES20.glGetAttribLocation(program, "a_position"); 315 context.texVertices = createVerticesBuffer(TEX_VERTICES); 316 context.posVertices = createVerticesBuffer(POS_VERTICES); 317 318 context.shaderProgram = program; 319 return context; 320 } 321 loadShader(int shaderType, String source)322 private static int loadShader(int shaderType, String source) { 323 int shader = GLES20.glCreateShader(shaderType); 324 if (shader != 0) { 325 GLES20.glShaderSource(shader, source); 326 GLES20.glCompileShader(shader); 327 int[] compiled = new int[1]; 328 GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); 329 if (compiled[0] == 0) { 330 String info = GLES20.glGetShaderInfoLog(shader); 331 GLES20.glDeleteShader(shader); 332 shader = 0; 333 throw new RuntimeException("Could not compile shader " + shaderType + ":" + info); 334 } 335 } 336 return shader; 337 } 338 createVerticesBuffer(float[] vertices)339 private static FloatBuffer createVerticesBuffer(float[] vertices) { 340 if (vertices.length != 8) { 341 throw new RuntimeException("Number of vertices should be four."); 342 } 343 344 FloatBuffer buffer = ByteBuffer.allocateDirect( 345 vertices.length * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer(); 346 buffer.put(vertices).position(0); 347 return buffer; 348 } 349 checkGlError(String op)350 private static void checkGlError(String op) { 351 int error; 352 while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 353 throw new RuntimeException(op + ": glError " + error); 354 } 355 } 356 } 357