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