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 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 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 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 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 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 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 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 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 245 public static void renderBackground() { 246 GLES20.glClearColor(0, 0, 0, 1); 247 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 248 } 249 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 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 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 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 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