1 package com.cooliris.media;
3 import java.lang.ref.ReferenceQueue;
4 import java.lang.ref.WeakReference;
5 import java.util.ArrayList;
7 import javax.microedition.khronos.egl.EGLConfig;
8 import javax.microedition.khronos.opengles.GL10;
9 import javax.microedition.khronos.opengles.GL11;
10 import javax.microedition.khronos.opengles.GL11Ext;
12 import android.content.Context;
13 import android.graphics.Bitmap;
14 import android.graphics.Canvas;
15 import android.graphics.Rect;
16 import android.hardware.Sensor;
17 import android.hardware.SensorEvent;
18 import android.hardware.SensorEventListener;
19 import android.hardware.SensorManager;
20 import android.opengl.GLU;
21 import android.opengl.GLUtils;
22 import android.opengl.GLSurfaceView;
23 import android.os.Process;
24 import android.os.SystemClock;
25 import android.util.Log;
26 import android.util.SparseArray;
27 import android.view.KeyEvent;
28 import android.view.MotionEvent;
29 import android.view.SurfaceHolder;
31 public final class RenderView extends GLSurfaceView implements GLSurfaceView.Renderer, SensorEventListener {
32 private static final String TAG = "RenderView";
33 private static final int NUM_TEXTURE_LOAD_THREADS = 4;
34 private static final int MAX_LOADING_COUNT = 128;
36 private static final int EVENT_NONE = 0;
37 // private static final int EVENT_TOUCH = 1;
38 private static final int EVENT_KEY = 2;
39 private static final int EVENT_FOCUS = 3;
41 private SensorManager mSensorManager;
43 private GL11 mGL = null;
44 private int mViewWidth = 0;
45 private int mViewHeight = 0;
47 private RootLayer mRootLayer = null;
48 private boolean mListsDirty = false;
49 private static final Lists sLists = new Lists();
51 private Layer mTouchEventTarget = null;
53 private int mCurrentEventType = EVENT_NONE;
54 private KeyEvent mCurrentKeyEvent = null;
55 private boolean mCurrentKeyEventResult = false;
56 private volatile boolean mPendingSensorEvent = false;
58 private int mLoadingCount = 0;
59 private static final Deque<Texture> sLoadInputQueue = new Deque<Texture>();
60 private static final Deque<Texture> sLoadInputQueueCached = new Deque<Texture>();
61 private static final Deque<Texture> sLoadInputQueueVideo = new Deque<Texture>();
62 private static final Deque<Texture> sLoadOutputQueue = new Deque<Texture>();
63 private static TextureLoadThread sCachedTextureLoadThread = null;
64 private static TextureLoadThread sVideoTextureLoadThread = null;
65 private static final TextureLoadThread[] sTextureLoadThreads = new TextureLoadThread[NUM_TEXTURE_LOAD_THREADS];
67 private final Deque<MotionEvent> mTouchEventQueue = new Deque<MotionEvent>();
68 private final DirectLinkedList<TextureReference> mActiveTextureList = new DirectLinkedList<TextureReference>();
69 @SuppressWarnings("unchecked")
70 private final ReferenceQueue mUnreferencedTextureQueue = new ReferenceQueue();
72 // Frame time in milliseconds and delta since last frame in seconds. Uses SystemClock.getUptimeMillis().
73 private long mFrameTime = 0;
74 private float mFrameInterval = 0.0f;
77 private long mLoadingExpensiveTexturesStartTime = 0;
78 private final SparseArray<ResourceTexture> sCacheScaled = new SparseArray<ResourceTexture>();
79 private final SparseArray<ResourceTexture> sCacheUnscaled = new SparseArray<ResourceTexture>();
81 private boolean mFirstDraw;
82 // The cached texture that is bound to Texture Unit 0.
83 // We need to reset this to null whenever the active texture unit changes.
84 private Texture mBoundTexture;
86 // Weak reference to a texture that stores the associated texture ID.
87 private static final class TextureReference extends WeakReference<Texture> {
88 @SuppressWarnings("unchecked")
89 public TextureReference(Texture texture, GL11 gl, ReferenceQueue referenceQueue, int textureId) {
90 super(texture, referenceQueue);
91 this.textureId = textureId;
95 public final int textureId;
97 public final DirectLinkedList.Entry<TextureReference> activeListEntry = new DirectLinkedList.Entry<TextureReference>(this);
100 public static final class Lists {
101 public final ArrayList<Layer> updateList = new ArrayList<Layer>();
102 public final ArrayList<Layer> opaqueList = new ArrayList<Layer>();
103 public final ArrayList<Layer> blendedList = new ArrayList<Layer>();
104 public final ArrayList<Layer> hitTestList = new ArrayList<Layer>();
105 public final ArrayList<Layer> systemList = new ArrayList<Layer>();
116 public RenderView(final Context context) {
118 setBackgroundDrawable(null);
120 setEGLConfigChooser(true);
122 mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
123 if (sCachedTextureLoadThread == null) {
124 for (int i = 0; i != NUM_TEXTURE_LOAD_THREADS; ++i) {
125 TextureLoadThread thread = new TextureLoadThread();
127 sCachedTextureLoadThread = thread;
130 sVideoTextureLoadThread = thread;
132 sTextureLoadThreads[i] = thread;
138 public void setRootLayer(RootLayer layer) {
139 if (mRootLayer != layer) {
143 mRootLayer.setSize(mViewWidth, mViewHeight);
148 public ResourceTexture getResource(int resourceId) {
149 return getResourceInternal(resourceId, true);
152 public ResourceTexture getResource(int resourceId, boolean scaled) {
153 return getResourceInternal(resourceId, scaled);
156 private ResourceTexture getResourceInternal(int resourceId, boolean scaled) {
157 final SparseArray<ResourceTexture> cache = (scaled) ? sCacheScaled : sCacheUnscaled;
158 ResourceTexture texture = cache.get(resourceId);
159 if (texture == null && resourceId != 0) {
160 texture = new ResourceTexture(resourceId, scaled);
161 cache.put(resourceId, texture);
166 public void clearCache() {
167 clearTextureArray(sCacheScaled);
168 clearTextureArray(sCacheUnscaled);
171 private void clearTextureArray(SparseArray<ResourceTexture> array) {
173 * final int size = array.size(); for (int i = 0; i < size; ++i) { ResourceTexture texture = array.get(array.keyAt(i)); if
174 * (texture != null) { texture.clear(); } }
181 public long getFrameTime() {
185 public float getFrameInterval() {
186 return mFrameInterval;
189 public void prime(Texture texture, boolean highPriority) {
190 if (texture != null && texture.mState == Texture.STATE_UNLOADED && (highPriority || mLoadingCount < MAX_LOADING_COUNT)) {
191 queueLoad(texture, highPriority);
195 public void loadTexture(Texture texture) {
196 if (texture != null) {
197 switch (texture.mState) {
198 case Texture.STATE_UNLOADED:
199 case Texture.STATE_QUEUED:
200 int[] textureId = new int[1];
201 texture.mState = Texture.STATE_LOADING;
202 loadTextureAsync(texture);
203 uploadTexture(texture, textureId);
209 private void loadTextureAsync(Texture texture) {
211 Bitmap bitmap = texture.load(this);
212 if (bitmap != null) {
213 bitmap = Utils.resizeBitmap(bitmap, 1024);
214 int width = bitmap.getWidth();
215 int height = bitmap.getHeight();
216 texture.mWidth = width;
217 texture.mHeight = height;
218 // Create a padded bitmap if the natural size is not a power of 2.
219 if (!Shared.isPowerOf2(width) || !Shared.isPowerOf2(height)) {
220 int paddedWidth = Shared.nextPowerOf2(width);
221 int paddedHeight = Shared.nextPowerOf2(height);
222 Bitmap.Config config = bitmap.getConfig();
224 config = Bitmap.Config.RGB_565;
225 if (width * height >= 512 * 512)
226 config = Bitmap.Config.RGB_565;
227 Bitmap padded = Bitmap.createBitmap(paddedWidth, paddedHeight, config);
228 Canvas canvas = new Canvas(padded);
229 canvas.drawBitmap(bitmap, 0, 0, null);
232 // Store normalized width and height for use in texture coordinates.
233 texture.mNormalizedWidth = (float) width / (float) paddedWidth;
234 texture.mNormalizedHeight = (float) height / (float) paddedHeight;
236 texture.mNormalizedWidth = 1.0f;
237 texture.mNormalizedHeight = 1.0f;
240 texture.mBitmap = bitmap;
241 } catch (Exception e) {
242 texture.mBitmap = null;
243 } catch (OutOfMemoryError eMem) {
244 Log.i(TAG, "Bitmap power of 2 creation fail, outofmemory");
249 public boolean bind(Texture texture) {
250 if (texture != null) {
251 if (texture == mBoundTexture)
253 switch (texture.mState) {
254 case Texture.STATE_UNLOADED:
255 if (texture.getClass().equals(ResourceTexture.class)) {
256 loadTexture(texture);
259 if (mLoadingCount < MAX_LOADING_COUNT) {
260 queueLoad(texture, false);
263 case Texture.STATE_LOADED:
264 mGL.glBindTexture(GL11.GL_TEXTURE_2D, texture.mId);
265 mBoundTexture = texture;
274 public void setAlpha(float alpha) {
276 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
277 gl.glColor4f(alpha, alpha, alpha, alpha);
281 public float getAlpha() {
285 public void setColor(float red, float green, float blue, float alpha) {
287 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
288 gl.glColor4f(red, green, blue, alpha);
292 public void resetColor() {
294 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
295 gl.glColor4f(1, 1, 1, 1);
298 public boolean isLoadingExpensiveTextures() {
299 return mLoadingExpensiveTexturesStartTime != 0;
302 public long elapsedLoadingExpensiveTextures() {
303 long startTime = mLoadingExpensiveTexturesStartTime;
304 if (startTime != 0) {
305 return SystemClock.uptimeMillis() - startTime;
311 private void queueLoad(final Texture texture, boolean highPriority) {
312 // Allow the texture to defer queuing.
313 if (!texture.shouldQueue()) {
317 // Change the texture state to loading.
318 texture.mState = Texture.STATE_LOADING;
320 // Push the texture onto the load input queue.
321 Deque<Texture> inputQueue = (texture.isUncachedVideo()) ? sLoadInputQueueVideo : (texture.isCached()) ? sLoadInputQueueCached : sLoadInputQueue;;
322 synchronized (inputQueue) {
324 inputQueue.addFirst(texture);
326 inputQueue.addLast(texture);
333 public void draw2D(Texture texture, float x, float y) {
335 final float width = texture.getWidth();
336 final float height = texture.getHeight();
337 ((GL11Ext) mGL).glDrawTexfOES(x, mViewHeight - y - height, 0f, width, height);
341 public void draw2D(Texture texture, float x, float y, float width, float height) {
343 ((GL11Ext) mGL).glDrawTexfOES(x, mViewHeight - y - height, 0f, width, height);
347 public void draw2D(Texture texture, int x, int y, int width, int height) {
349 ((GL11Ext) mGL).glDrawTexiOES(x, (int) (mViewHeight - y - height), 0, width, height);
353 public void draw2D(float x, float y, float z, float width, float height) {
354 ((GL11Ext) mGL).glDrawTexfOES(x, mViewHeight - y - height, z, width, height);
357 public boolean bindMixed(Texture from, Texture to, float ratio) {
358 // Bind "from" and "to" to TEXTURE0 and TEXTURE1, respectively.
362 gl.glActiveTexture(GL11.GL_TEXTURE1);
363 mBoundTexture = null;
370 gl.glEnable(GL11.GL_TEXTURE_2D);
372 // Interpolate the RGB and alpha values between both textures.
373 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_COMBINE);
374 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
375 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
377 // Specify the interpolation factor via the alpha component of GL_TEXTURE_ENV_COLOR.
378 final float[] color = { 1f, 1f, 1f, ratio };
379 gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, color, 0);
381 // Wire up the interpolation factor for RGB.
382 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
383 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);
385 // Wire up the interpolation factor for alpha.
386 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
387 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
391 public void unbindMixed() {
394 gl.glDisable(GL11.GL_TEXTURE_2D);
396 // Switch back to the default texture unit.
397 gl.glActiveTexture(GL11.GL_TEXTURE0);
398 mBoundTexture = null;
401 public void drawMixed2D(Texture from, Texture to, float ratio, float x, float y, float z, float width, float height) {
404 // Bind "from" and "to" to TEXTURE0 and TEXTURE1, respectively.
406 gl.glActiveTexture(GL11.GL_TEXTURE1);
407 mBoundTexture = null;
410 gl.glEnable(GL11.GL_TEXTURE_2D);
412 // Interpolate the RGB and alpha values between both textures.
413 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_COMBINE);
414 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
415 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
417 // Specify the interpolation factor via the alpha component of GL_TEXTURE_ENV_COLOR.
418 final float[] color = { 1f, 1f, 1f, ratio };
419 gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, color, 0);
421 // Wire up the interpolation factor for RGB.
422 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
423 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);
425 // Wire up the interpolation factor for alpha.
426 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
427 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
429 // Draw the combined texture.
430 ((GL11Ext) gl).glDrawTexfOES(x, mViewHeight - y - height, z, width, height);
433 gl.glDisable(GL11.GL_TEXTURE_2D);
436 // Switch back to the default texture unit.
437 gl.glActiveTexture(GL11.GL_TEXTURE0);
438 mBoundTexture = null;
442 public void processAllTextures() {
443 processTextures(true);
446 final static int[] textureId = new int[1];
448 /** Uploads at most one texture to GL. */
449 private void processTextures(boolean processAll) {
450 // Destroy any textures that are no longer referenced.
452 TextureReference textureReference;
453 while ((textureReference = (TextureReference) mUnreferencedTextureQueue.poll()) != null) {
454 textureId[0] = textureReference.textureId;
455 GL11 glOld = textureReference.gl;
457 gl.glDeleteTextures(1, textureId, 0);
459 mActiveTextureList.remove(textureReference.activeListEntry);
461 Deque<Texture> outputQueue = sLoadOutputQueue;
464 // Upload loaded textures to the GPU one frame at a time.
465 synchronized (outputQueue) {
466 texture = outputQueue.pollFirst();
468 if (texture != null) {
469 // Extract the bitmap from the texture.
470 uploadTexture(texture, textureId);
472 // Decrement the loading count.
477 } while (processAll);
480 private void uploadTexture(Texture texture, int[] textureId) {
481 Bitmap bitmap = texture.mBitmap;
483 int glError = GL11.GL_NO_ERROR;
484 if (bitmap != null) {
485 final int width = texture.mWidth;
486 final int height = texture.mHeight;
488 // Define a vertically flipped crop rectangle for OES_draw_texture.
489 int[] cropRect = { 0, height, width, -height };
491 // Upload the bitmap to a new texture.
492 gl.glGenTextures(1, textureId, 0);
493 gl.glBindTexture(GL11.GL_TEXTURE_2D, textureId[0]);
494 gl.glTexParameteriv(GL11.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, 0);
495 gl.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
496 gl.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
497 gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
498 gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
499 GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, bitmap, 0);
500 glError = gl.glGetError();
503 if (glError == GL11.GL_OUT_OF_MEMORY) {
506 if (glError != GL11.GL_NO_ERROR) {
507 // There was an error, we need to retry this texture at some later time
508 Log.i(TAG, "Texture creation fail, glError " + glError);
510 texture.mBitmap = null;
511 texture.mState = Texture.STATE_UNLOADED;
513 // Update texture state.
514 texture.mBitmap = null;
515 texture.mId = textureId[0];
516 texture.mState = Texture.STATE_LOADED;
518 // Add to the active list.
519 final TextureReference textureRef = new TextureReference(texture, gl, mUnreferencedTextureQueue, textureId[0]);
520 mActiveTextureList.add(textureRef.activeListEntry);
524 texture.mState = Texture.STATE_ERROR;
530 public void onResume() {
532 Sensor sensorAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
533 Sensor sensorOrientation = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
534 if (sensorAccelerometer != null) {
535 mSensorManager.registerListener(this, sensorAccelerometer, SensorManager.SENSOR_DELAY_UI);
537 if (sensorOrientation != null) {
538 mSensorManager.registerListener(this, sensorOrientation, SensorManager.SENSOR_DELAY_UI);
543 public void onPause() {
545 Log.i(TAG, "OnPause RenderView " + this);
546 mSensorManager.unregisterListener(this);
549 /** Renders a frame of the UI. */
551 public void onDrawFrame(GL10 gl1) {
552 GL11 gl = (GL11) gl1;
554 Log.i(TAG, "First Draw");
557 // setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
558 // Rebuild the display lists if the render tree has changed.
563 boolean wasLoadingExpensiveTextures = isLoadingExpensiveTextures();
564 boolean loadingExpensiveTextures = false;
565 int numTextureThreads = sTextureLoadThreads.length;
566 for (int i = 2; i < numTextureThreads; ++i) {
567 if (sTextureLoadThreads[i].mIsLoading) {
568 loadingExpensiveTextures = true;
572 if (loadingExpensiveTextures != wasLoadingExpensiveTextures) {
573 mLoadingExpensiveTexturesStartTime = loadingExpensiveTextures ? SystemClock.uptimeMillis() : 0;
576 // Upload new textures.
577 processTextures(false);
579 // Update the current time and frame time interval.
580 long now = SystemClock.uptimeMillis();
581 final float dt = 0.001f * Math.min(50, now - mFrameTime);
585 // Dispatch the current touch event.
586 processCurrentEvent();
588 // Run the update pass.
589 final Lists lists = sLists;
590 synchronized (lists) {
591 final ArrayList<Layer> updateList = lists.updateList;
592 boolean isDirty = false;
593 for (int i = 0, size = updateList.size(); i != size; ++i) {
594 boolean retVal = updateList.get(i).update(this, mFrameInterval);
601 // Clear the depth buffer.
602 gl.glClear(GL11.GL_DEPTH_BUFFER_BIT);
603 gl.glEnable(GL11.GL_SCISSOR_TEST);
604 gl.glScissor(0, 0, getWidth(), getHeight());
606 // Run the opaque pass.
607 gl.glDisable(GL11.GL_BLEND);
608 final ArrayList<Layer> opaqueList = lists.opaqueList;
609 for (int i = opaqueList.size() - 1; i >= 0; --i) {
610 final Layer layer = opaqueList.get(i);
611 if (!layer.mHidden) {
612 layer.renderOpaque(this, gl);
616 // Run the blended pass.
617 gl.glEnable(GL11.GL_BLEND);
618 final ArrayList<Layer> blendedList = lists.blendedList;
619 for (int i = 0, size = blendedList.size(); i != size; ++i) {
620 final Layer layer = blendedList.get(i);
621 if (!layer.mHidden) {
622 layer.renderBlended(this, gl);
625 gl.glDisable(GL11.GL_BLEND);
629 private void processCurrentEvent() {
630 final int type = mCurrentEventType;
641 synchronized (this) {
642 mCurrentEventType = EVENT_NONE;
647 private void processTouchEvent() {
648 MotionEvent event = null;
649 int numEvents = mTouchEventQueue.size();
652 // We look at the touch event queue and process one event at a time
653 synchronized (mTouchEventQueue) {
654 event = mTouchEventQueue.pollFirst();
659 // Detect the hit layer.
660 final int action = event.getAction();
662 if (action == MotionEvent.ACTION_DOWN) {
663 target = hitTest(event.getX(), event.getY());
664 mTouchEventTarget = target;
666 target = mTouchEventTarget;
669 // Dispatch event to the hit layer.
670 if (target != null) {
671 target.onTouchEvent(event);
674 // Clear the hit layer.
675 if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
676 mTouchEventTarget = null;
680 } while (event != null && i < numEvents);
681 synchronized (this) {
686 private void processKeyEvent() {
688 final KeyEvent event = mCurrentKeyEvent;
689 boolean result = false;
690 mCurrentKeyEvent = null;
692 // Dispatch the event to the root layer.
693 if (mRootLayer != null) {
694 if (event.getAction() == KeyEvent.ACTION_DOWN) {
695 result = mRootLayer.onKeyDown(event.getKeyCode(), event);
697 result = mRootLayer.onKeyUp(event.getKeyCode(), event);
700 mCurrentKeyEventResult = result;
703 private void processFocusEvent() {
704 // Get event information.
705 if (mRootLayer != null) {
710 private Layer hitTest(float x, float y) {
711 final ArrayList<Layer> hitTestList = sLists.hitTestList;
712 for (int i = hitTestList.size() - 1; i >= 0; --i) {
713 final Layer layer = hitTestList.get(i);
714 if (layer != null && !layer.mHidden) {
715 final float layerX = layer.mX;
716 final float layerY = layer.mY;
717 if (x >= layerX && y >= layerY && x < layerX + layer.mWidth && y < layerY + layer.mHeight
718 && layer.containsPoint(x, y)) {
726 private void updateLists() {
727 if (mRootLayer != null) {
728 synchronized (sLists) {
730 mRootLayer.generate(this, sLists);
735 /** Called when the OpenGL surface is recreated without destroying the context. */
736 public void onSurfaceChanged(GL10 gl1, int width, int height) {
737 GL11 gl = (GL11) gl1;
740 mViewHeight = height;
741 if (mRootLayer != null) {
742 mRootLayer.setSize(width, height);
745 // Set the viewport and projection matrix.
746 final float zNear = 0.1f;
747 final float zFar = 100.0f;
748 gl.glViewport(0, 0, width, height);
749 gl.glMatrixMode(GL11.GL_PROJECTION);
751 GLU.gluPerspective(gl, 45.0f, (float) width / height, zNear, zFar);
752 if (mRootLayer != null) {
753 mRootLayer.onSurfaceChanged(this, width, height);
755 gl.glMatrixMode(GL11.GL_MODELVIEW);
758 public void setFov(float fov) {
760 gl.glMatrixMode(GL11.GL_PROJECTION);
762 final float zNear = 0.1f;
763 final float zFar = 100.0f;
764 GLU.gluPerspective(gl, fov, (float) getWidth() / getHeight(), zNear, zFar);
765 gl.glMatrixMode(GL11.GL_MODELVIEW);
768 /** Called when the context is created, possibly after automatic destruction. */
769 public void onSurfaceCreated(GL10 gl1, EGLConfig config) {
770 // Clear the resource texture cache.
773 GL11 gl = (GL11) gl1;
777 // The GL Object has changed.
778 Log.i(TAG, "GLObject has changed from " + mGL + " to " + gl);
781 setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
782 // Increase the priority of the render thread.
783 Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
785 // Disable unused state.
786 gl.glEnable(GL11.GL_DITHER);
787 gl.glDisable(GL11.GL_LIGHTING);
790 // gl.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
793 gl.glEnable(GL11.GL_TEXTURE_2D);
794 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
796 // Set up state for multitexture operations. Since multitexture is currently used
797 // only for layered crossfades the needed state can be factored out into one-time
798 // initialization. This section may need to be folded into drawMixed2D() if multitexture
799 // is used for other effects.
801 // Enable Vertex Arrays
802 gl.glEnableClientState(GL11.GL_VERTEX_ARRAY);
803 gl.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
804 gl.glClientActiveTexture(GL11.GL_TEXTURE1);
805 gl.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
806 gl.glClientActiveTexture(GL11.GL_TEXTURE0);
808 // Enable depth test.
809 gl.glEnable(GL11.GL_DEPTH_TEST);
810 gl.glDepthFunc(GL11.GL_LEQUAL);
812 // Set the blend function for premultiplied alpha.
813 gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
815 // Set the background color.
816 gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
817 gl.glClear(GL11.GL_COLOR_BUFFER_BIT);
819 // Start reloading textures if the context was automatically destroyed.
820 if (!mActiveTextureList.isEmpty()) {
821 DirectLinkedList.Entry<TextureReference> iter = mActiveTextureList.getHead();
822 while (iter != null) {
823 final Texture texture = iter.value.get();
824 if (texture != null) {
825 texture.mState = Texture.STATE_UNLOADED;
830 mActiveTextureList.clear();
831 if (mRootLayer != null) {
832 mRootLayer.onSurfaceCreated(this, gl);
834 synchronized (sLists) {
835 ArrayList<Layer> systemList = sLists.systemList;
836 for (int i = systemList.size() - 1; i >= 0; --i) {
837 systemList.get(i).onSurfaceCreated(this, gl);
842 /** Indicates that the accuracy of a sensor value has changed. */
843 public void onAccuracyChanged(Sensor sensor, int accuracy) {
846 /** Indicates that a sensor value has changed. */
847 public void onSensorChanged(SensorEvent event) {
848 final int type = event.sensor.getType();
849 if ((!mPendingSensorEvent && type == Sensor.TYPE_ACCELEROMETER) || type == Sensor.TYPE_ORIENTATION) {
850 final SensorEvent e = event;
851 if (mRootLayer != null)
852 mRootLayer.onSensorChanged(RenderView.this, e);
853 if (type == Sensor.TYPE_ORIENTATION) {
860 public boolean onTouchEvent(MotionEvent event) {
861 // Ignore events received before the surface is created to avoid
862 // deadlocking with GLSurfaceView's needToWait().
866 // Wait for the render thread to process this event.
867 if (mTouchEventQueue.size() > 8 && event.getAction() == MotionEvent.ACTION_MOVE)
869 synchronized (mTouchEventQueue) {
870 MotionEvent eventCopy = MotionEvent.obtain(event);
871 mTouchEventQueue.addLast(eventCopy);
878 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
879 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
881 // Ignore events received before the surface is created to avoid
882 // deadlocking with GLSurfaceView's needToWait().
884 * if (mGL == null) { return; }
886 * // Wait for the render thread to process this event. try { synchronized (this) { mCurrentFocusEventGain = gainFocus;
887 * mCurrentFocusEventDirection = direction; mCurrentEventType = EVENT_FOCUS; do { wait(); } while (mCurrentEventType !=
888 * EVENT_NONE); } } catch (InterruptedException e) { // Stop waiting for the render thread if interrupted. }
894 public boolean onKeyDown(int keyCode, KeyEvent event) {
895 // Ignore events received before the surface is created to avoid
896 // deadlocking with GLSurfaceView's needToWait().
901 // Wait for the render thread to process this event.
903 synchronized (this) {
904 mCurrentKeyEvent = event;
905 mCurrentEventType = EVENT_KEY;
907 long timeout = SystemClock.uptimeMillis() + 50;
910 } while (mCurrentEventType != EVENT_NONE && SystemClock.uptimeMillis() < timeout);
912 } catch (InterruptedException e) {
913 // Stop waiting for the render thread if interrupted.
916 // Key events are handled on the main thread.
917 boolean retVal = false;
918 if (!mCurrentKeyEventResult) {
919 retVal = super.onKeyDown(keyCode, event);
928 public void surfaceDestroyed(SurfaceHolder holder) {
929 super.surfaceDestroyed(holder);
933 protected void onAttachedToWindow() {
934 super.onAttachedToWindow();
938 protected void onDetachedFromWindow() {
939 super.onDetachedFromWindow();
942 private final class TextureLoadThread extends Thread {
943 public boolean mIsLoading;
945 public TextureLoadThread() {
946 super("TextureLoad");
950 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
951 Deque<Texture> inputQueue = (sVideoTextureLoadThread == this) ? sLoadInputQueueVideo : ((sCachedTextureLoadThread == this) ? sLoadInputQueueCached : sLoadInputQueue);
952 Deque<Texture> outputQueue = sLoadOutputQueue;
955 // Pop the next texture from the input queue.
956 Texture texture = null;
957 synchronized (inputQueue) {
958 while ((texture = inputQueue.pollFirst()) == null) {
962 if (sCachedTextureLoadThread != this)
964 // Load the texture bitmap.
968 // Push the texture onto the output queue.
969 synchronized (outputQueue) {
970 outputQueue.addLast(texture);
973 } catch (InterruptedException e) {
974 // Terminate the thread.
978 private void load(Texture texture) {
979 // Generate the texture bitmap.
980 RenderView view = RenderView.this;
981 view.loadTextureAsync(texture);
982 view.requestRender();
986 public void shutdown() {
988 synchronized (sLists) {
993 public void handleLowMemory() {
994 Log.i(TAG, "Handling low memory condition");
995 if (mRootLayer != null) {
996 mRootLayer.handleLowMemory();
1000 public Lists getLists() {