GLTextureViewActivity.java revision ec19b4a764d512091a780fc93ced567dfbf80914
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 17package com.android.test.hwui; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.ObjectAnimator; 22import android.animation.ValueAnimator; 23import android.app.Activity; 24import android.content.res.Resources; 25import android.graphics.Bitmap; 26import android.graphics.BitmapFactory; 27import android.graphics.SurfaceTexture; 28import android.opengl.GLUtils; 29import android.os.Bundle; 30import android.util.Log; 31import android.view.Gravity; 32import android.view.TextureView; 33import android.view.View; 34import android.view.ViewGroup; 35import android.widget.FrameLayout; 36 37import javax.microedition.khronos.egl.EGL10; 38import javax.microedition.khronos.egl.EGL11; 39import javax.microedition.khronos.egl.EGLConfig; 40import javax.microedition.khronos.egl.EGLContext; 41import javax.microedition.khronos.egl.EGLDisplay; 42import javax.microedition.khronos.egl.EGLSurface; 43import javax.microedition.khronos.opengles.GL; 44 45import java.io.BufferedOutputStream; 46import java.io.FileNotFoundException; 47import java.io.FileOutputStream; 48import java.io.IOException; 49import java.nio.ByteBuffer; 50import java.nio.ByteOrder; 51import java.nio.FloatBuffer; 52 53import static android.opengl.GLES20.*; 54 55@SuppressWarnings({"UnusedDeclaration"}) 56public class GLTextureViewActivity extends Activity implements TextureView.SurfaceTextureListener { 57 private RenderThread mRenderThread; 58 private TextureView mTextureView; 59 60 @Override 61 protected void onCreate(Bundle savedInstanceState) { 62 super.onCreate(savedInstanceState); 63 64 mTextureView = new TextureView(this); 65 mTextureView.setSurfaceTextureListener(this); 66 mTextureView.setOnClickListener(new View.OnClickListener() { 67 @Override 68 public void onClick(View v) { 69 Bitmap b = mTextureView.getBitmap(600, 350); 70 BufferedOutputStream out = null; 71 try { 72 out = new BufferedOutputStream(new FileOutputStream("/sdcard/out.png")); 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 void 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 } 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, 0.f, 0.f, 175 1.0f, -1.0f, 0, 1.f, 0.f, 176 -1.0f, 1.0f, 0, 0.f, 1.f, 177 1.0f, 1.0f, 0, 1.f, 1.f, 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(texture, 0); 213 checkGlError(); 214 215 while (!mFinished) { 216 checkCurrent(); 217 218 Log.d(LOG_TAG, "Rendering frame"); 219 220 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 221 checkGlError(); 222 223 glClear(GL_COLOR_BUFFER_BIT); 224 checkGlError(); 225 226 // drawQuad 227 triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); 228 glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false, 229 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices); 230 231 triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); 232 glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false, 233 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices); 234 235 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 236 237 if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { 238 throw new RuntimeException("Cannot swap buffers"); 239 } 240 checkEglError(); 241 242 try { 243 Thread.sleep(20); 244 } catch (InterruptedException e) { 245 // Ignore 246 } 247 } 248 249 finishGL(); 250 } 251 252 private int loadTexture(int resource) { 253 int[] textures = new int[1]; 254 255 glActiveTexture(GL_TEXTURE0); 256 glGenTextures(1, textures, 0); 257 checkGlError(); 258 259 int texture = textures[0]; 260 glBindTexture(GL_TEXTURE_2D, texture); 261 checkGlError(); 262 263 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 264 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 265 266 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 267 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 268 269 Bitmap bitmap = BitmapFactory.decodeResource(mResources, resource); 270 271 GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0); 272 checkGlError(); 273 274 bitmap.recycle(); 275 276 return texture; 277 } 278 279 private int buildProgram(String vertex, String fragment) { 280 int vertexShader = buildShader(vertex, GL_VERTEX_SHADER); 281 if (vertexShader == 0) return 0; 282 283 int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER); 284 if (fragmentShader == 0) return 0; 285 286 int program = glCreateProgram(); 287 glAttachShader(program, vertexShader); 288 checkGlError(); 289 290 glAttachShader(program, fragmentShader); 291 checkGlError(); 292 293 glLinkProgram(program); 294 checkGlError(); 295 296 int[] status = new int[1]; 297 glGetProgramiv(program, GL_LINK_STATUS, status, 0); 298 if (status[0] != GL_TRUE) { 299 String error = glGetProgramInfoLog(program); 300 Log.d(LOG_TAG, "Error while linking program:\n" + error); 301 glDeleteShader(vertexShader); 302 glDeleteShader(fragmentShader); 303 glDeleteProgram(program); 304 return 0; 305 } 306 307 return program; 308 } 309 310 private int buildShader(String source, int type) { 311 int shader = glCreateShader(type); 312 313 glShaderSource(shader, source); 314 checkGlError(); 315 316 glCompileShader(shader); 317 checkGlError(); 318 319 int[] status = new int[1]; 320 glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0); 321 if (status[0] != GL_TRUE) { 322 String error = glGetShaderInfoLog(shader); 323 Log.d(LOG_TAG, "Error while compiling shader:\n" + error); 324 glDeleteShader(shader); 325 return 0; 326 } 327 328 return shader; 329 } 330 331 private void checkEglError() { 332 int error = mEgl.eglGetError(); 333 if (error != EGL10.EGL_SUCCESS) { 334 Log.w(LOG_TAG, "EGL error = 0x" + Integer.toHexString(error)); 335 } 336 } 337 338 private void checkGlError() { 339 int error = glGetError(); 340 if (error != GL_NO_ERROR) { 341 Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error)); 342 } 343 } 344 345 private void finishGL() { 346 mEgl.eglDestroyContext(mEglDisplay, mEglContext); 347 mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 348 } 349 350 private void checkCurrent() { 351 if (!mEglContext.equals(mEgl.eglGetCurrentContext()) || 352 !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) { 353 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 354 throw new RuntimeException("eglMakeCurrent failed " 355 + getEGLErrorString(mEgl.eglGetError())); 356 } 357 } 358 } 359 360 private void initGL() { 361 mEgl = (EGL10) EGLContext.getEGL(); 362 363 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 364 if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { 365 throw new RuntimeException("eglGetDisplay failed " 366 + getEGLErrorString(mEgl.eglGetError())); 367 } 368 369 int[] version = new int[2]; 370 if (!mEgl.eglInitialize(mEglDisplay, version)) { 371 throw new RuntimeException("eglInitialize failed " + 372 getEGLErrorString(mEgl.eglGetError())); 373 } 374 375 mEglConfig = chooseEglConfig(); 376 if (mEglConfig == null) { 377 throw new RuntimeException("eglConfig not initialized"); 378 } 379 380 mEglContext = createContext(mEgl, mEglDisplay, mEglConfig); 381 382 mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null); 383 384 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 385 int error = mEgl.eglGetError(); 386 if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { 387 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); 388 return; 389 } 390 throw new RuntimeException("createWindowSurface failed " 391 + getEGLErrorString(error)); 392 } 393 394 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 395 throw new RuntimeException("eglMakeCurrent failed " 396 + getEGLErrorString(mEgl.eglGetError())); 397 } 398 399 mGL = mEglContext.getGL(); 400 } 401 402 403 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { 404 int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; 405 return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); 406 } 407 408 private EGLConfig chooseEglConfig() { 409 int[] configsCount = new int[1]; 410 EGLConfig[] configs = new EGLConfig[1]; 411 int[] configSpec = getConfig(); 412 if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) { 413 throw new IllegalArgumentException("eglChooseConfig failed " + 414 getEGLErrorString(mEgl.eglGetError())); 415 } else if (configsCount[0] > 0) { 416 return configs[0]; 417 } 418 return null; 419 } 420 421 private int[] getConfig() { 422 return new int[] { 423 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 424 EGL10.EGL_RED_SIZE, 8, 425 EGL10.EGL_GREEN_SIZE, 8, 426 EGL10.EGL_BLUE_SIZE, 8, 427 EGL10.EGL_ALPHA_SIZE, 8, 428 EGL10.EGL_DEPTH_SIZE, 0, 429 EGL10.EGL_STENCIL_SIZE, 0, 430 EGL10.EGL_NONE 431 }; 432 } 433 434 static String getEGLErrorString(int error) { 435 switch (error) { 436 case EGL10.EGL_SUCCESS: 437 return "EGL_SUCCESS"; 438 case EGL10.EGL_NOT_INITIALIZED: 439 return "EGL_NOT_INITIALIZED"; 440 case EGL10.EGL_BAD_ACCESS: 441 return "EGL_BAD_ACCESS"; 442 case EGL10.EGL_BAD_ALLOC: 443 return "EGL_BAD_ALLOC"; 444 case EGL10.EGL_BAD_ATTRIBUTE: 445 return "EGL_BAD_ATTRIBUTE"; 446 case EGL10.EGL_BAD_CONFIG: 447 return "EGL_BAD_CONFIG"; 448 case EGL10.EGL_BAD_CONTEXT: 449 return "EGL_BAD_CONTEXT"; 450 case EGL10.EGL_BAD_CURRENT_SURFACE: 451 return "EGL_BAD_CURRENT_SURFACE"; 452 case EGL10.EGL_BAD_DISPLAY: 453 return "EGL_BAD_DISPLAY"; 454 case EGL10.EGL_BAD_MATCH: 455 return "EGL_BAD_MATCH"; 456 case EGL10.EGL_BAD_NATIVE_PIXMAP: 457 return "EGL_BAD_NATIVE_PIXMAP"; 458 case EGL10.EGL_BAD_NATIVE_WINDOW: 459 return "EGL_BAD_NATIVE_WINDOW"; 460 case EGL10.EGL_BAD_PARAMETER: 461 return "EGL_BAD_PARAMETER"; 462 case EGL10.EGL_BAD_SURFACE: 463 return "EGL_BAD_SURFACE"; 464 case EGL11.EGL_CONTEXT_LOST: 465 return "EGL_CONTEXT_LOST"; 466 default: 467 return "0x" + Integer.toHexString(error); 468 } 469 } 470 471 void finish() { 472 mFinished = true; 473 } 474 } 475} 476