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