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