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 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 
     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 
    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 
    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 
    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 
    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 
    245     public static void renderBackground() {
    246         GLES20.glClearColor(0, 0, 0, 1);
    247         GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    248     }
    249 
    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 
    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 }
    357