Home | History | Annotate | Download | only in photoeditor
      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