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