blob: 3232eedcf51e9fbee48ab541be12d51fd45fbf2c [file] [log] [blame]
Romain Guy8f0095c2011-05-02 17:24:22 -07001/*
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;
Romain Guyd0d07052011-06-24 18:51:28 -070022import android.content.res.Resources;
23import android.graphics.Bitmap;
24import android.graphics.BitmapFactory;
Romain Guy8f0095c2011-05-02 17:24:22 -070025import android.graphics.SurfaceTexture;
Romain Guyd0d07052011-06-24 18:51:28 -070026import android.opengl.GLUtils;
Romain Guy8f0095c2011-05-02 17:24:22 -070027import android.os.Bundle;
28import android.util.Log;
29import android.view.Gravity;
30import android.view.TextureView;
31import android.view.View;
Romain Guyd0d07052011-06-24 18:51:28 -070032import android.view.ViewGroup;
Romain Guy8f0095c2011-05-02 17:24:22 -070033import android.widget.FrameLayout;
34
35import javax.microedition.khronos.egl.EGL10;
Romain Guy8f0095c2011-05-02 17:24:22 -070036import javax.microedition.khronos.egl.EGLConfig;
37import javax.microedition.khronos.egl.EGLContext;
38import javax.microedition.khronos.egl.EGLDisplay;
39import javax.microedition.khronos.egl.EGLSurface;
40import javax.microedition.khronos.opengles.GL;
Romain Guyec19b4a2011-07-07 21:05:04 -070041import java.io.BufferedOutputStream;
42import java.io.FileNotFoundException;
43import java.io.FileOutputStream;
44import java.io.IOException;
Romain Guyd0d07052011-06-24 18:51:28 -070045import java.nio.ByteBuffer;
46import java.nio.ByteOrder;
47import java.nio.FloatBuffer;
48
49import static android.opengl.GLES20.*;
50
Romain Guy8f0095c2011-05-02 17:24:22 -070051@SuppressWarnings({"UnusedDeclaration"})
52public class GLTextureViewActivity extends Activity implements TextureView.SurfaceTextureListener {
53 private RenderThread mRenderThread;
54 private TextureView mTextureView;
55
56 @Override
57 protected void onCreate(Bundle savedInstanceState) {
58 super.onCreate(savedInstanceState);
59
60 mTextureView = new TextureView(this);
61 mTextureView.setSurfaceTextureListener(this);
Romain Guyec19b4a2011-07-07 21:05:04 -070062 mTextureView.setOnClickListener(new View.OnClickListener() {
63 @Override
64 public void onClick(View v) {
Romain Guycfacbea2011-07-07 21:33:41 -070065 Bitmap b = mTextureView.getBitmap(800, 800);
Romain Guyec19b4a2011-07-07 21:05:04 -070066 BufferedOutputStream out = null;
67 try {
68 out = new BufferedOutputStream(new FileOutputStream("/sdcard/out.png"));
69 b.compress(Bitmap.CompressFormat.PNG, 100, out);
70 } catch (FileNotFoundException e) {
71 e.printStackTrace();
72 } finally {
73 if (out != null) try {
74 out.close();
75 } catch (IOException e) {
76 e.printStackTrace();
77 }
78 }
79 }
80 });
Romain Guy8f0095c2011-05-02 17:24:22 -070081
Romain Guyd0d07052011-06-24 18:51:28 -070082 setContentView(mTextureView, new FrameLayout.LayoutParams(
83 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
84 Gravity.CENTER));
Romain Guy8f0095c2011-05-02 17:24:22 -070085 }
86
87 @Override
Romain Guy451ce442011-06-10 15:40:36 -070088 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Romain Guyd0d07052011-06-24 18:51:28 -070089 mRenderThread = new RenderThread(getResources(), surface);
Romain Guy8f0095c2011-05-02 17:24:22 -070090 mRenderThread.start();
91
92 mTextureView.setCameraDistance(5000);
93
94 ObjectAnimator animator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
95 animator.setRepeatMode(ObjectAnimator.REVERSE);
96 animator.setRepeatCount(ObjectAnimator.INFINITE);
97 animator.setDuration(4000);
98 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
99 @Override
100 public void onAnimationUpdate(ValueAnimator animation) {
Romain Guyec19b4a2011-07-07 21:05:04 -0700101 mTextureView.invalidate();
Romain Guy8f0095c2011-05-02 17:24:22 -0700102 }
103 });
104 animator.start();
105 }
106
107 @Override
108 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
109 }
110
Romain Guy451ce442011-06-10 15:40:36 -0700111 @Override
Grace Kloba402f0552011-08-09 18:47:17 -0700112 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Romain Guy451ce442011-06-10 15:40:36 -0700113 mRenderThread.finish();
114 try {
115 mRenderThread.join();
116 } catch (InterruptedException e) {
117 Log.e(RenderThread.LOG_TAG, "Could not wait for render thread");
118 }
Grace Kloba402f0552011-08-09 18:47:17 -0700119 return true;
Romain Guy451ce442011-06-10 15:40:36 -0700120 }
121
Grace Klobacf559372011-06-22 23:05:40 -0700122 @Override
123 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
124 }
125
Romain Guy8f0095c2011-05-02 17:24:22 -0700126 private static class RenderThread extends Thread {
127 private static final String LOG_TAG = "GLTextureView";
128
129 static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
Romain Guy8f0095c2011-05-02 17:24:22 -0700130 static final int EGL_OPENGL_ES2_BIT = 4;
131
132 private volatile boolean mFinished;
133
Romain Guyd0d07052011-06-24 18:51:28 -0700134 private final Resources mResources;
135 private final SurfaceTexture mSurface;
Romain Guy8f0095c2011-05-02 17:24:22 -0700136
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
Romain Guyd0d07052011-06-24 18:51:28 -0700144 RenderThread(Resources resources, SurfaceTexture surface) {
145 mResources = resources;
Romain Guy8f0095c2011-05-02 17:24:22 -0700146 mSurface = surface;
147 }
148
Romain Guyd0d07052011-06-24 18:51:28 -0700149 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
Romain Guy8f0095c2011-05-02 17:24:22 -0700177 @Override
178 public void run() {
179 initGL();
Romain Guyd0d07052011-06-24 18:51:28 -0700180
181 FloatBuffer triangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length
182 * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
183 triangleVertices.put(mTriangleVerticesData).position(0);
Romain Guy8f0095c2011-05-02 17:24:22 -0700184
Romain Guyd0d07052011-06-24 18:51:28 -0700185 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
Romain Guy407ec782011-08-24 17:06:58 -0700209 glUniform1i(uniformTexture, texture);
Romain Guyd0d07052011-06-24 18:51:28 -0700210 checkGlError();
211
Romain Guy8f0095c2011-05-02 17:24:22 -0700212 while (!mFinished) {
213 checkCurrent();
214
Romain Guy451ce442011-06-10 15:40:36 -0700215 Log.d(LOG_TAG, "Rendering frame");
216
Romain Guyd0d07052011-06-24 18:51:28 -0700217 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
Romain Guy451ce442011-06-10 15:40:36 -0700218 checkGlError();
Romain Guy8f0095c2011-05-02 17:24:22 -0700219
Romain Guyd0d07052011-06-24 18:51:28 -0700220 glClear(GL_COLOR_BUFFER_BIT);
Romain Guy451ce442011-06-10 15:40:36 -0700221 checkGlError();
Romain Guy8f0095c2011-05-02 17:24:22 -0700222
Romain Guyd0d07052011-06-24 18:51:28 -0700223 // 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
Romain Guy8f0095c2011-05-02 17:24:22 -0700234 if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
235 throw new RuntimeException("Cannot swap buffers");
236 }
Romain Guy451ce442011-06-10 15:40:36 -0700237 checkEglError();
238
Romain Guy8f0095c2011-05-02 17:24:22 -0700239 try {
240 Thread.sleep(20);
241 } catch (InterruptedException e) {
242 // Ignore
243 }
Romain Guy8f0095c2011-05-02 17:24:22 -0700244 }
245
246 finishGL();
247 }
248
Romain Guyd0d07052011-06-24 18:51:28 -0700249 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
Romain Guy451ce442011-06-10 15:40:36 -0700328 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() {
Romain Guyd0d07052011-06-24 18:51:28 -0700336 int error = glGetError();
337 if (error != GL_NO_ERROR) {
Romain Guy451ce442011-06-10 15:40:36 -0700338 Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
339 }
340 }
341
Romain Guy8f0095c2011-05-02 17:24:22 -0700342 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 "
Romain Guy407ec782011-08-24 17:06:58 -0700352 + GLUtils.getEGLErrorString(mEgl.eglGetError()));
Romain Guy8f0095c2011-05-02 17:24:22 -0700353 }
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 "
Romain Guy407ec782011-08-24 17:06:58 -0700363 + GLUtils.getEGLErrorString(mEgl.eglGetError()));
Romain Guy8f0095c2011-05-02 17:24:22 -0700364 }
365
366 int[] version = new int[2];
367 if (!mEgl.eglInitialize(mEglDisplay, version)) {
368 throw new RuntimeException("eglInitialize failed " +
Romain Guy407ec782011-08-24 17:06:58 -0700369 GLUtils.getEGLErrorString(mEgl.eglGetError()));
Romain Guy8f0095c2011-05-02 17:24:22 -0700370 }
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 "
Romain Guy407ec782011-08-24 17:06:58 -0700388 + GLUtils.getEGLErrorString(error));
Romain Guy8f0095c2011-05-02 17:24:22 -0700389 }
390
391 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
392 throw new RuntimeException("eglMakeCurrent failed "
Romain Guy407ec782011-08-24 17:06:58 -0700393 + GLUtils.getEGLErrorString(mEgl.eglGetError()));
Romain Guy8f0095c2011-05-02 17:24:22 -0700394 }
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 " +
Romain Guy407ec782011-08-24 17:06:58 -0700411 GLUtils.getEGLErrorString(mEgl.eglGetError()));
Romain Guy8f0095c2011-05-02 17:24:22 -0700412 } 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
Romain Guy8f0095c2011-05-02 17:24:22 -0700431 void finish() {
432 mFinished = true;
433 }
434 }
435}