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