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.os.Environment; 29import android.util.Log; 30import android.view.Gravity; 31import android.view.TextureView; 32import android.view.View; 33import android.view.ViewGroup; 34import android.widget.FrameLayout; 35 36import javax.microedition.khronos.egl.EGL10; 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.File; 44import java.io.FileNotFoundException; 45import java.io.FileOutputStream; 46import java.io.IOException; 47import java.nio.ByteBuffer; 48import java.nio.ByteOrder; 49import java.nio.FloatBuffer; 50 51import static android.opengl.GLES20.*; 52 53@SuppressWarnings({"UnusedDeclaration"}) 54public 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