• 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 
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