Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2011 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.test.hwui;
     18 
     19 import android.animation.ObjectAnimator;
     20 import android.animation.ValueAnimator;
     21 import android.app.Activity;
     22 import android.content.res.Resources;
     23 import android.graphics.Bitmap;
     24 import android.graphics.BitmapFactory;
     25 import android.graphics.Matrix;
     26 import android.graphics.SurfaceTexture;
     27 import android.opengl.GLUtils;
     28 import android.os.Bundle;
     29 import android.os.Environment;
     30 import android.util.Log;
     31 import android.view.Gravity;
     32 import android.view.TextureView;
     33 import android.view.View;
     34 import android.view.ViewGroup;
     35 import android.widget.FrameLayout;
     36 
     37 import javax.microedition.khronos.egl.EGL10;
     38 import javax.microedition.khronos.egl.EGLConfig;
     39 import javax.microedition.khronos.egl.EGLContext;
     40 import javax.microedition.khronos.egl.EGLDisplay;
     41 import javax.microedition.khronos.egl.EGLSurface;
     42 import javax.microedition.khronos.opengles.GL;
     43 import java.io.BufferedOutputStream;
     44 import java.io.File;
     45 import java.io.FileNotFoundException;
     46 import java.io.FileOutputStream;
     47 import java.io.IOException;
     48 import java.nio.ByteBuffer;
     49 import java.nio.ByteOrder;
     50 import java.nio.FloatBuffer;
     51 
     52 import static android.opengl.GLES20.*;
     53 
     54 @SuppressWarnings({"UnusedDeclaration"})
     55 public class GLTextureViewActivity extends Activity implements TextureView.SurfaceTextureListener {
     56     private RenderThread mRenderThread;
     57     private TextureView mTextureView;
     58 
     59     @Override
     60     protected void onCreate(Bundle savedInstanceState) {
     61         super.onCreate(savedInstanceState);
     62 
     63         mTextureView = new TextureView(this);
     64         mTextureView.setSurfaceTextureListener(this);
     65         mTextureView.setOnClickListener(new View.OnClickListener() {
     66             @Override
     67             public void onClick(View v) {
     68                 Bitmap b = mTextureView.getBitmap(800, 800);
     69                 BufferedOutputStream out = null;
     70                 try {
     71                     File dump = new File(Environment.getExternalStorageDirectory(), "out.png");
     72                     out = new BufferedOutputStream(new FileOutputStream(dump));
     73                     b.compress(Bitmap.CompressFormat.PNG, 100, out);
     74                 } catch (FileNotFoundException e) {
     75                     e.printStackTrace();
     76                 } finally {
     77                     if (out != null) try {
     78                         out.close();
     79                     } catch (IOException e) {
     80                         e.printStackTrace();
     81                     }
     82                 }
     83             }
     84         });
     85 
     86         setContentView(mTextureView, new FrameLayout.LayoutParams(
     87                 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
     88                 Gravity.CENTER));
     89     }
     90 
     91     @Override
     92     public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
     93         mRenderThread = new RenderThread(getResources(), surface);
     94         mRenderThread.start();
     95 
     96         mTextureView.setCameraDistance(5000);
     97 
     98         ObjectAnimator animator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
     99         animator.setRepeatMode(ObjectAnimator.REVERSE);
    100         animator.setRepeatCount(ObjectAnimator.INFINITE);
    101         animator.setDuration(4000);
    102         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    103             @Override
    104             public void onAnimationUpdate(ValueAnimator animation) {
    105                 mTextureView.invalidate();
    106             }
    107         });
    108         animator.start();
    109     }
    110 
    111     @Override
    112     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    113     }
    114 
    115     @Override
    116     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    117         mRenderThread.finish();
    118         try {
    119             mRenderThread.join();
    120         } catch (InterruptedException e) {
    121             Log.e(RenderThread.LOG_TAG, "Could not wait for render thread");
    122         }
    123         return true;
    124     }
    125 
    126     @Override
    127     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    128     }
    129 
    130     private static class RenderThread extends Thread {
    131         private static final String LOG_TAG = "GLTextureView";
    132 
    133         static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
    134         static final int EGL_OPENGL_ES2_BIT = 4;
    135 
    136         private volatile boolean mFinished;
    137 
    138         private final Resources mResources;
    139         private final SurfaceTexture mSurface;
    140 
    141         private EGL10 mEgl;
    142         private EGLDisplay mEglDisplay;
    143         private EGLConfig mEglConfig;
    144         private EGLContext mEglContext;
    145         private EGLSurface mEglSurface;
    146         private GL mGL;
    147 
    148         RenderThread(Resources resources, SurfaceTexture surface) {
    149             mResources = resources;
    150             mSurface = surface;
    151         }
    152 
    153         private static final String sSimpleVS =
    154                 "attribute vec4 position;\n" +
    155                 "attribute vec2 texCoords;\n" +
    156                 "varying vec2 outTexCoords;\n" +
    157                 "\nvoid main(void) {\n" +
    158                 "    outTexCoords = texCoords;\n" +
    159                 "    gl_Position = position;\n" +
    160                 "}\n\n";
    161         private static final String sSimpleFS =
    162                 "precision mediump float;\n\n" +
    163                 "varying vec2 outTexCoords;\n" +
    164                 "uniform sampler2D texture;\n" +
    165                 "\nvoid main(void) {\n" +
    166                 "    gl_FragColor = texture2D(texture, outTexCoords);\n" +
    167                 "}\n\n";
    168 
    169         private static final int FLOAT_SIZE_BYTES = 4;
    170         private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
    171         private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
    172         private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
    173         private final float[] mTriangleVerticesData = {
    174                 // X, Y, Z, U, V
    175                 -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
    176                  1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
    177                 -1.0f,  1.0f, 0.0f, 0.0f, 1.0f,
    178                  1.0f,  1.0f, 0.0f, 1.0f, 1.0f,
    179         };
    180 
    181         @Override
    182         public void run() {
    183             initGL();
    184 
    185             FloatBuffer triangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length
    186                     * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
    187             triangleVertices.put(mTriangleVerticesData).position(0);
    188 
    189             int texture = loadTexture(R.drawable.large_photo);
    190             int program = buildProgram(sSimpleVS, sSimpleFS);
    191 
    192             int attribPosition = glGetAttribLocation(program, "position");
    193             checkGlError();
    194 
    195             int attribTexCoords = glGetAttribLocation(program, "texCoords");
    196             checkGlError();
    197 
    198             int uniformTexture = glGetUniformLocation(program, "texture");
    199             checkGlError();
    200 
    201             glBindTexture(GL_TEXTURE_2D, texture);
    202             checkGlError();
    203 
    204             glUseProgram(program);
    205             checkGlError();
    206 
    207             glEnableVertexAttribArray(attribPosition);
    208             checkGlError();
    209 
    210             glEnableVertexAttribArray(attribTexCoords);
    211             checkGlError();
    212 
    213             glUniform1i(uniformTexture, texture);
    214             checkGlError();
    215 
    216             while (!mFinished) {
    217                 checkCurrent();
    218 
    219                 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    220                 checkGlError();
    221 
    222                 glClear(GL_COLOR_BUFFER_BIT);
    223                 checkGlError();
    224 
    225                 // drawQuad
    226                 triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
    227                 glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
    228                         TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
    229 
    230                 triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
    231                 glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
    232                         TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
    233 
    234                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    235 
    236                 if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
    237                     throw new RuntimeException("Cannot swap buffers");
    238                 }
    239                 checkEglError();
    240 
    241                 try {
    242                     Thread.sleep(2000);
    243                 } catch (InterruptedException e) {
    244                     // Ignore
    245                 }
    246             }
    247 
    248             finishGL();
    249         }
    250 
    251         private int loadTexture(int resource) {
    252             int[] textures = new int[1];
    253 
    254             glActiveTexture(GL_TEXTURE0);
    255             glGenTextures(1, textures, 0);
    256             checkGlError();
    257 
    258             int texture = textures[0];
    259             glBindTexture(GL_TEXTURE_2D, texture);
    260             checkGlError();
    261 
    262             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    263             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    264 
    265             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    266             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    267 
    268             Bitmap bitmap = BitmapFactory.decodeResource(mResources, resource);
    269 
    270             GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
    271             checkGlError();
    272 
    273             bitmap.recycle();
    274 
    275             return texture;
    276         }
    277 
    278         private int buildProgram(String vertex, String fragment) {
    279             int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
    280             if (vertexShader == 0) return 0;
    281 
    282             int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
    283             if (fragmentShader == 0) return 0;
    284 
    285             int program = glCreateProgram();
    286             glAttachShader(program, vertexShader);
    287             checkGlError();
    288 
    289             glAttachShader(program, fragmentShader);
    290             checkGlError();
    291 
    292             glLinkProgram(program);
    293             checkGlError();
    294 
    295             int[] status = new int[1];
    296             glGetProgramiv(program, GL_LINK_STATUS, status, 0);
    297             if (status[0] != GL_TRUE) {
    298                 String error = glGetProgramInfoLog(program);
    299                 Log.d(LOG_TAG, "Error while linking program:\n" + error);
    300                 glDeleteShader(vertexShader);
    301                 glDeleteShader(fragmentShader);
    302                 glDeleteProgram(program);
    303                 return 0;
    304             }
    305 
    306             return program;
    307         }
    308 
    309         private int buildShader(String source, int type) {
    310             int shader = glCreateShader(type);
    311 
    312             glShaderSource(shader, source);
    313             checkGlError();
    314 
    315             glCompileShader(shader);
    316             checkGlError();
    317 
    318             int[] status = new int[1];
    319             glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
    320             if (status[0] != GL_TRUE) {
    321                 String error = glGetShaderInfoLog(shader);
    322                 Log.d(LOG_TAG, "Error while compiling shader:\n" + error);
    323                 glDeleteShader(shader);
    324                 return 0;
    325             }
    326 
    327             return shader;
    328         }
    329 
    330         private void checkEglError() {
    331             int error = mEgl.eglGetError();
    332             if (error != EGL10.EGL_SUCCESS) {
    333                 Log.w(LOG_TAG, "EGL error = 0x" + Integer.toHexString(error));
    334             }
    335         }
    336 
    337         private void checkGlError() {
    338             int error = glGetError();
    339             if (error != GL_NO_ERROR) {
    340                 Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
    341             }
    342         }
    343 
    344         private void finishGL() {
    345             mEgl.eglDestroyContext(mEglDisplay, mEglContext);
    346             mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
    347         }
    348 
    349         private void checkCurrent() {
    350             if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
    351                     !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
    352                 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
    353                     throw new RuntimeException("eglMakeCurrent failed "
    354                             + GLUtils.getEGLErrorString(mEgl.eglGetError()));
    355                 }
    356             }
    357         }
    358 
    359         private void initGL() {
    360             mEgl = (EGL10) EGLContext.getEGL();
    361 
    362             mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
    363             if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
    364                 throw new RuntimeException("eglGetDisplay failed "
    365                         + GLUtils.getEGLErrorString(mEgl.eglGetError()));
    366             }
    367 
    368             int[] version = new int[2];
    369             if (!mEgl.eglInitialize(mEglDisplay, version)) {
    370                 throw new RuntimeException("eglInitialize failed " +
    371                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
    372             }
    373 
    374             mEglConfig = chooseEglConfig();
    375             if (mEglConfig == null) {
    376                 throw new RuntimeException("eglConfig not initialized");
    377             }
    378 
    379             mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
    380 
    381             mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null);
    382 
    383             if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
    384                 int error = mEgl.eglGetError();
    385                 if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
    386                     Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
    387                     return;
    388                 }
    389                 throw new RuntimeException("createWindowSurface failed "
    390                         + GLUtils.getEGLErrorString(error));
    391             }
    392 
    393             if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
    394                 throw new RuntimeException("eglMakeCurrent failed "
    395                         + GLUtils.getEGLErrorString(mEgl.eglGetError()));
    396             }
    397 
    398             mGL = mEglContext.getGL();
    399         }
    400 
    401 
    402         EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
    403             int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
    404             return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
    405         }
    406 
    407         private EGLConfig chooseEglConfig() {
    408             int[] configsCount = new int[1];
    409             EGLConfig[] configs = new EGLConfig[1];
    410             int[] configSpec = getConfig();
    411             if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
    412                 throw new IllegalArgumentException("eglChooseConfig failed " +
    413                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
    414             } else if (configsCount[0] > 0) {
    415                 return configs[0];
    416             }
    417             return null;
    418         }
    419 
    420         private int[] getConfig() {
    421             return new int[] {
    422                     EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
    423                     EGL10.EGL_RED_SIZE, 8,
    424                     EGL10.EGL_GREEN_SIZE, 8,
    425                     EGL10.EGL_BLUE_SIZE, 8,
    426                     EGL10.EGL_ALPHA_SIZE, 8,
    427                     EGL10.EGL_DEPTH_SIZE, 0,
    428                     EGL10.EGL_STENCIL_SIZE, 0,
    429                     EGL10.EGL_NONE
    430             };
    431         }
    432 
    433         void finish() {
    434             mFinished = true;
    435         }
    436     }
    437 }
    438