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 onCreate(Bundle savedInstanceState)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 onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)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 onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)112 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 113 } 114 115 @Override onSurfaceTextureDestroyed(SurfaceTexture surface)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 onSurfaceTextureUpdated(SurfaceTexture surface)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 RenderThread(Resources resources, SurfaceTexture surface)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 run()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, 0); 214 checkGlError(); 215 216 // drawQuad 217 triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); 218 glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false, 219 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices); 220 checkGlError(); 221 222 triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); 223 glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false, 224 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices); 225 checkGlError(); 226 227 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 228 checkGlError(); 229 230 while (!mFinished) { 231 checkCurrent(); 232 233 glClear(GL_COLOR_BUFFER_BIT); 234 checkGlError(); 235 236 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 237 checkGlError(); 238 239 if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { 240 throw new RuntimeException("Cannot swap buffers"); 241 } 242 checkEglError(); 243 244 try { 245 Thread.sleep(2000); 246 } catch (InterruptedException e) { 247 // Ignore 248 } 249 } 250 251 finishGL(); 252 } 253 loadTexture(int resource)254 private int loadTexture(int resource) { 255 int[] textures = new int[1]; 256 257 glActiveTexture(GL_TEXTURE0); 258 glGenTextures(1, textures, 0); 259 checkGlError(); 260 261 int texture = textures[0]; 262 glBindTexture(GL_TEXTURE_2D, texture); 263 checkGlError(); 264 265 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 266 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 267 268 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 269 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 270 271 Bitmap bitmap = BitmapFactory.decodeResource(mResources, resource); 272 273 GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0); 274 checkGlError(); 275 276 bitmap.recycle(); 277 278 return texture; 279 } 280 buildProgram(String vertex, String fragment)281 private int buildProgram(String vertex, String fragment) { 282 int vertexShader = buildShader(vertex, GL_VERTEX_SHADER); 283 if (vertexShader == 0) return 0; 284 285 int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER); 286 if (fragmentShader == 0) return 0; 287 288 int program = glCreateProgram(); 289 glAttachShader(program, vertexShader); 290 checkGlError(); 291 292 glAttachShader(program, fragmentShader); 293 checkGlError(); 294 295 glLinkProgram(program); 296 checkGlError(); 297 298 int[] status = new int[1]; 299 glGetProgramiv(program, GL_LINK_STATUS, status, 0); 300 if (status[0] != GL_TRUE) { 301 String error = glGetProgramInfoLog(program); 302 Log.d(LOG_TAG, "Error while linking program:\n" + error); 303 glDeleteShader(vertexShader); 304 glDeleteShader(fragmentShader); 305 glDeleteProgram(program); 306 return 0; 307 } 308 309 return program; 310 } 311 buildShader(String source, int type)312 private int buildShader(String source, int type) { 313 int shader = glCreateShader(type); 314 315 glShaderSource(shader, source); 316 checkGlError(); 317 318 glCompileShader(shader); 319 checkGlError(); 320 321 int[] status = new int[1]; 322 glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0); 323 if (status[0] != GL_TRUE) { 324 String error = glGetShaderInfoLog(shader); 325 Log.d(LOG_TAG, "Error while compiling shader:\n" + error); 326 glDeleteShader(shader); 327 return 0; 328 } 329 330 return shader; 331 } 332 checkEglError()333 private void checkEglError() { 334 int error = mEgl.eglGetError(); 335 if (error != EGL10.EGL_SUCCESS) { 336 Log.w(LOG_TAG, "EGL error = 0x" + Integer.toHexString(error)); 337 } 338 } 339 checkGlError()340 private void checkGlError() { 341 int error = glGetError(); 342 if (error != GL_NO_ERROR) { 343 Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error)); 344 } 345 } 346 finishGL()347 private void finishGL() { 348 mEgl.eglDestroyContext(mEglDisplay, mEglContext); 349 mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 350 } 351 checkCurrent()352 private void checkCurrent() { 353 if (!mEglContext.equals(mEgl.eglGetCurrentContext()) || 354 !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) { 355 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 356 throw new RuntimeException("eglMakeCurrent failed " 357 + GLUtils.getEGLErrorString(mEgl.eglGetError())); 358 } 359 } 360 } 361 initGL()362 private void initGL() { 363 mEgl = (EGL10) EGLContext.getEGL(); 364 365 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 366 if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { 367 throw new RuntimeException("eglGetDisplay failed " 368 + GLUtils.getEGLErrorString(mEgl.eglGetError())); 369 } 370 371 int[] version = new int[2]; 372 if (!mEgl.eglInitialize(mEglDisplay, version)) { 373 throw new RuntimeException("eglInitialize failed " + 374 GLUtils.getEGLErrorString(mEgl.eglGetError())); 375 } 376 377 mEglConfig = chooseEglConfig(); 378 if (mEglConfig == null) { 379 throw new RuntimeException("eglConfig not initialized"); 380 } 381 382 mEglContext = createContext(mEgl, mEglDisplay, mEglConfig); 383 384 mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null); 385 386 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 387 int error = mEgl.eglGetError(); 388 if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { 389 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); 390 return; 391 } 392 throw new RuntimeException("createWindowSurface failed " 393 + GLUtils.getEGLErrorString(error)); 394 } 395 396 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 397 throw new RuntimeException("eglMakeCurrent failed " 398 + GLUtils.getEGLErrorString(mEgl.eglGetError())); 399 } 400 401 mGL = mEglContext.getGL(); 402 } 403 404 createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig)405 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { 406 int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; 407 return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); 408 } 409 chooseEglConfig()410 private EGLConfig chooseEglConfig() { 411 int[] configsCount = new int[1]; 412 EGLConfig[] configs = new EGLConfig[1]; 413 int[] configSpec = getConfig(); 414 if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) { 415 throw new IllegalArgumentException("eglChooseConfig failed " + 416 GLUtils.getEGLErrorString(mEgl.eglGetError())); 417 } else if (configsCount[0] > 0) { 418 return configs[0]; 419 } 420 return null; 421 } 422 getConfig()423 private int[] getConfig() { 424 return new int[] { 425 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 426 EGL10.EGL_RED_SIZE, 8, 427 EGL10.EGL_GREEN_SIZE, 8, 428 EGL10.EGL_BLUE_SIZE, 8, 429 EGL10.EGL_ALPHA_SIZE, 8, 430 EGL10.EGL_DEPTH_SIZE, 0, 431 EGL10.EGL_STENCIL_SIZE, 0, 432 EGL10.EGL_NONE 433 }; 434 } 435 finish()436 void finish() { 437 mFinished = true; 438 } 439 } 440 } 441