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 createTexture()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 createTexture(Bitmap bitmap)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 saveTexture(int texture, int width, int height)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 clearTexture(int texture)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 getFitVertices(int srcWidth, int srcHeight, int dstWidth, int dstHeight)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 setRenderToFit(RenderContext context, int srcWidth, int srcHeight, int dstWidth, int dstHeight)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 setRenderToRotate(RenderContext context, int srcWidth, int srcHeight, int dstWidth, int dstHeight, float degrees)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 setRenderToFlip(RenderContext context, int srcWidth, int srcHeight, int dstWidth, int dstHeight, float horizontalDegrees, float verticalDegrees)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 renderBackground()244 public static void renderBackground() { 245 GLES20.glClearColor(0, 0, 0, 1); 246 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 247 } 248 renderTexture( RenderContext context, int texture, int viewWidth, int viewHeight)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 createProgram()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 loadShader(int shaderType, String source)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 createVerticesBuffer(float[] vertices)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 checkGlError(String op)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