blob: 3edcff5f5aba7b34a33f4027bb408239e3faa5df [file] [log] [blame]
Yuli Huang6a12ad72011-09-12 22:25:30 +08001/*
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
17package com.android.gallery3d.photoeditor;
18
19import android.graphics.Bitmap;
20import android.opengl.GLES20;
21import android.opengl.GLUtils;
Chih-Chung Changfd914132012-02-11 07:19:47 +080022import android.util.FloatMath;
Yuli Huang6a12ad72011-09-12 22:25:30 +080023
24import java.nio.ByteBuffer;
25import java.nio.ByteOrder;
26import java.nio.FloatBuffer;
27
28/**
29 * Utils for GL renderer.
30 */
31public 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
Yuli Huang7b62f482011-10-20 18:29:05 +0800125 private static float[] getFitVertices(int srcWidth, int srcHeight, int dstWidth,
126 int dstHeight) {
Yuli Huang6a12ad72011-09-12 22:25:30 +0800127 float srcAspectRatio = ((float) srcWidth) / srcHeight;
128 float dstAspectRatio = ((float) dstWidth) / dstHeight;
129 float relativeAspectRatio = dstAspectRatio / srcAspectRatio;
130
Yuli Huang7b62f482011-10-20 18:29:05 +0800131 float[] vertices = new float[8];
Yuli Huang6a12ad72011-09-12 22:25:30 +0800132 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 }
Yuli Huang7b62f482011-10-20 18:29:05 +0800145 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));
Yuli Huang6a12ad72011-09-12 22:25:30 +0800152 }
153
154 public static void setRenderToRotate(RenderContext context, int srcWidth, int srcHeight,
155 int dstWidth, int dstHeight, float degrees) {
Yuli Huang7b62f482011-10-20 18:29:05 +0800156 float radian = -degrees * DEGREE_TO_RADIAN;
Chih-Chung Changfd914132012-02-11 07:19:47 +0800157 float cosTheta = FloatMath.cos(radian);
158 float sinTheta = FloatMath.sin(radian);
Yuli Huang6a12ad72011-09-12 22:25:30 +0800159 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
Yuli Huang7b62f482011-10-20 18:29:05 +0800185 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;
Chih-Chung Changfd914132012-02-11 07:19:47 +0800209 float cosTheta = FloatMath.cos(radian);
210 float sinTheta = FloatMath.sin(radian);
Yuli Huang7b62f482011-10-20 18:29:05 +0800211
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;
Chih-Chung Changfd914132012-02-11 07:19:47 +0800227 float cosTheta = FloatMath.cos(radian);
228 float sinTheta = FloatMath.sin(radian);
Yuli Huang7b62f482011-10-20 18:29:05 +0800229
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
Yuli Huangeb83d832011-10-11 21:23:22 +0800245 public static void renderBackground() {
246 GLES20.glClearColor(0, 0, 0, 1);
247 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
248 }
249
Yuli Huang6a12ad72011-09-12 22:25:30 +0800250 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!
Yuli Huang6a12ad72011-09-12 22:25:30 +0800280 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}