• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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