GLTextureViewActivity.java revision 402f05530352f34d5320c2d23be43c274d97c4e2
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 boolean 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 return true; 121 } 122 123 @Override 124 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 125 } 126 127 private static class RenderThread extends Thread { 128 private static final String LOG_TAG = "GLTextureView"; 129 130 static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 131 static final int EGL_OPENGL_ES2_BIT = 4; 132 133 private volatile boolean mFinished; 134 135 private final Resources mResources; 136 private final SurfaceTexture mSurface; 137 138 private EGL10 mEgl; 139 private EGLDisplay mEglDisplay; 140 private EGLConfig mEglConfig; 141 private EGLContext mEglContext; 142 private EGLSurface mEglSurface; 143 private GL mGL; 144 145 RenderThread(Resources resources, SurfaceTexture surface) { 146 mResources = resources; 147 mSurface = surface; 148 } 149 150 private static final String sSimpleVS = 151 "attribute vec4 position;\n" + 152 "attribute vec2 texCoords;\n" + 153 "varying vec2 outTexCoords;\n" + 154 "\nvoid main(void) {\n" + 155 " outTexCoords = texCoords;\n" + 156 " gl_Position = position;\n" + 157 "}\n\n"; 158 private static final String sSimpleFS = 159 "precision mediump float;\n\n" + 160 "varying vec2 outTexCoords;\n" + 161 "uniform sampler2D texture;\n" + 162 "\nvoid main(void) {\n" + 163 " gl_FragColor = texture2D(texture, outTexCoords);\n" + 164 "}\n\n"; 165 166 private static final int FLOAT_SIZE_BYTES = 4; 167 private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES; 168 private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0; 169 private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3; 170 private final float[] mTriangleVerticesData = { 171 // X, Y, Z, U, V 172 -1.0f, -1.0f, 0, 0.f, 0.f, 173 1.0f, -1.0f, 0, 1.f, 0.f, 174 -1.0f, 1.0f, 0, 0.f, 1.f, 175 1.0f, 1.0f, 0, 1.f, 1.f, 176 }; 177 178 @Override 179 public void run() { 180 initGL(); 181 182 FloatBuffer triangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length 183 * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer(); 184 triangleVertices.put(mTriangleVerticesData).position(0); 185 186 int texture = loadTexture(R.drawable.large_photo); 187 int program = buildProgram(sSimpleVS, sSimpleFS); 188 189 int attribPosition = glGetAttribLocation(program, "position"); 190 checkGlError(); 191 192 int attribTexCoords = glGetAttribLocation(program, "texCoords"); 193 checkGlError(); 194 195 int uniformTexture = glGetUniformLocation(program, "texture"); 196 checkGlError(); 197 198 glBindTexture(GL_TEXTURE_2D, texture); 199 checkGlError(); 200 201 glUseProgram(program); 202 checkGlError(); 203 204 glEnableVertexAttribArray(attribPosition); 205 checkGlError(); 206 207 glEnableVertexAttribArray(attribTexCoords); 208 checkGlError(); 209 210 glUniform1i(texture, 0); 211 checkGlError(); 212 213 while (!mFinished) { 214 checkCurrent(); 215 216 Log.d(LOG_TAG, "Rendering frame"); 217 218 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 219 checkGlError(); 220 221 glClear(GL_COLOR_BUFFER_BIT); 222 checkGlError(); 223 224 // drawQuad 225 triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); 226 glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false, 227 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices); 228 229 triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); 230 glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false, 231 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices); 232 233 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 234 235 if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { 236 throw new RuntimeException("Cannot swap buffers"); 237 } 238 checkEglError(); 239 240 try { 241 Thread.sleep(20); 242 } catch (InterruptedException e) { 243 // Ignore 244 } 245 } 246 247 finishGL(); 248 } 249 250 private int loadTexture(int resource) { 251 int[] textures = new int[1]; 252 253 glActiveTexture(GL_TEXTURE0); 254 glGenTextures(1, textures, 0); 255 checkGlError(); 256 257 int texture = textures[0]; 258 glBindTexture(GL_TEXTURE_2D, texture); 259 checkGlError(); 260 261 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 262 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 263 264 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 265 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 266 267 Bitmap bitmap = BitmapFactory.decodeResource(mResources, resource); 268 269 GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0); 270 checkGlError(); 271 272 bitmap.recycle(); 273 274 return texture; 275 } 276 277 private int buildProgram(String vertex, String fragment) { 278 int vertexShader = buildShader(vertex, GL_VERTEX_SHADER); 279 if (vertexShader == 0) return 0; 280 281 int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER); 282 if (fragmentShader == 0) return 0; 283 284 int program = glCreateProgram(); 285 glAttachShader(program, vertexShader); 286 checkGlError(); 287 288 glAttachShader(program, fragmentShader); 289 checkGlError(); 290 291 glLinkProgram(program); 292 checkGlError(); 293 294 int[] status = new int[1]; 295 glGetProgramiv(program, GL_LINK_STATUS, status, 0); 296 if (status[0] != GL_TRUE) { 297 String error = glGetProgramInfoLog(program); 298 Log.d(LOG_TAG, "Error while linking program:\n" + error); 299 glDeleteShader(vertexShader); 300 glDeleteShader(fragmentShader); 301 glDeleteProgram(program); 302 return 0; 303 } 304 305 return program; 306 } 307 308 private int buildShader(String source, int type) { 309 int shader = glCreateShader(type); 310 311 glShaderSource(shader, source); 312 checkGlError(); 313 314 glCompileShader(shader); 315 checkGlError(); 316 317 int[] status = new int[1]; 318 glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0); 319 if (status[0] != GL_TRUE) { 320 String error = glGetShaderInfoLog(shader); 321 Log.d(LOG_TAG, "Error while compiling shader:\n" + error); 322 glDeleteShader(shader); 323 return 0; 324 } 325 326 return shader; 327 } 328 329 private void checkEglError() { 330 int error = mEgl.eglGetError(); 331 if (error != EGL10.EGL_SUCCESS) { 332 Log.w(LOG_TAG, "EGL error = 0x" + Integer.toHexString(error)); 333 } 334 } 335 336 private void checkGlError() { 337 int error = glGetError(); 338 if (error != GL_NO_ERROR) { 339 Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error)); 340 } 341 } 342 343 private void finishGL() { 344 mEgl.eglDestroyContext(mEglDisplay, mEglContext); 345 mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 346 } 347 348 private void checkCurrent() { 349 if (!mEglContext.equals(mEgl.eglGetCurrentContext()) || 350 !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) { 351 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 352 throw new RuntimeException("eglMakeCurrent failed " 353 + getEGLErrorString(mEgl.eglGetError())); 354 } 355 } 356 } 357 358 private void initGL() { 359 mEgl = (EGL10) EGLContext.getEGL(); 360 361 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 362 if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { 363 throw new RuntimeException("eglGetDisplay failed " 364 + getEGLErrorString(mEgl.eglGetError())); 365 } 366 367 int[] version = new int[2]; 368 if (!mEgl.eglInitialize(mEglDisplay, version)) { 369 throw new RuntimeException("eglInitialize failed " + 370 getEGLErrorString(mEgl.eglGetError())); 371 } 372 373 mEglConfig = chooseEglConfig(); 374 if (mEglConfig == null) { 375 throw new RuntimeException("eglConfig not initialized"); 376 } 377 378 mEglContext = createContext(mEgl, mEglDisplay, mEglConfig); 379 380 mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null); 381 382 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 383 int error = mEgl.eglGetError(); 384 if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { 385 Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); 386 return; 387 } 388 throw new RuntimeException("createWindowSurface failed " 389 + getEGLErrorString(error)); 390 } 391 392 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 393 throw new RuntimeException("eglMakeCurrent failed " 394 + getEGLErrorString(mEgl.eglGetError())); 395 } 396 397 mGL = mEglContext.getGL(); 398 } 399 400 401 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { 402 int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; 403 return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); 404 } 405 406 private EGLConfig chooseEglConfig() { 407 int[] configsCount = new int[1]; 408 EGLConfig[] configs = new EGLConfig[1]; 409 int[] configSpec = getConfig(); 410 if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) { 411 throw new IllegalArgumentException("eglChooseConfig failed " + 412 getEGLErrorString(mEgl.eglGetError())); 413 } else if (configsCount[0] > 0) { 414 return configs[0]; 415 } 416 return null; 417 } 418 419 private int[] getConfig() { 420 return new int[] { 421 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 422 EGL10.EGL_RED_SIZE, 8, 423 EGL10.EGL_GREEN_SIZE, 8, 424 EGL10.EGL_BLUE_SIZE, 8, 425 EGL10.EGL_ALPHA_SIZE, 8, 426 EGL10.EGL_DEPTH_SIZE, 0, 427 EGL10.EGL_STENCIL_SIZE, 0, 428 EGL10.EGL_NONE 429 }; 430 } 431 432 static String getEGLErrorString(int error) { 433 switch (error) { 434 case EGL10.EGL_SUCCESS: 435 return "EGL_SUCCESS"; 436 case EGL10.EGL_NOT_INITIALIZED: 437 return "EGL_NOT_INITIALIZED"; 438 case EGL10.EGL_BAD_ACCESS: 439 return "EGL_BAD_ACCESS"; 440 case EGL10.EGL_BAD_ALLOC: 441 return "EGL_BAD_ALLOC"; 442 case EGL10.EGL_BAD_ATTRIBUTE: 443 return "EGL_BAD_ATTRIBUTE"; 444 case EGL10.EGL_BAD_CONFIG: 445 return "EGL_BAD_CONFIG"; 446 case EGL10.EGL_BAD_CONTEXT: 447 return "EGL_BAD_CONTEXT"; 448 case EGL10.EGL_BAD_CURRENT_SURFACE: 449 return "EGL_BAD_CURRENT_SURFACE"; 450 case EGL10.EGL_BAD_DISPLAY: 451 return "EGL_BAD_DISPLAY"; 452 case EGL10.EGL_BAD_MATCH: 453 return "EGL_BAD_MATCH"; 454 case EGL10.EGL_BAD_NATIVE_PIXMAP: 455 return "EGL_BAD_NATIVE_PIXMAP"; 456 case EGL10.EGL_BAD_NATIVE_WINDOW: 457 return "EGL_BAD_NATIVE_WINDOW"; 458 case EGL10.EGL_BAD_PARAMETER: 459 return "EGL_BAD_PARAMETER"; 460 case EGL10.EGL_BAD_SURFACE: 461 return "EGL_BAD_SURFACE"; 462 case EGL11.EGL_CONTEXT_LOST: 463 return "EGL_CONTEXT_LOST"; 464 default: 465 return "0x" + Integer.toHexString(error); 466 } 467 } 468 469 void finish() { 470 mFinished = true; 471 } 472 } 473} 474