OSDN Git Service

Prevent locking of files when the sd card is ejected New Cache Dirty system which...
[android-x86/packages-apps-Gallery2.git] / src / com / cooliris / media / RenderView.java
1 package com.cooliris.media;
2
3 import java.lang.ref.ReferenceQueue;
4 import java.lang.ref.WeakReference;
5 import java.util.ArrayList;
6
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;
11
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;
30
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;
35
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;
40
41     private SensorManager mSensorManager;
42
43     private GL11 mGL = null;
44     private int mViewWidth = 0;
45     private int mViewHeight = 0;
46
47     private RootLayer mRootLayer = null;
48     private boolean mListsDirty = false;
49     private static final Lists sLists = new Lists();
50
51     private Layer mTouchEventTarget = null;
52
53     private int mCurrentEventType = EVENT_NONE;
54     private KeyEvent mCurrentKeyEvent = null;
55     private boolean mCurrentKeyEventResult = false;
56     private volatile boolean mPendingSensorEvent = false;
57
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];
66
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();
71
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;
75     private float mAlpha;
76
77     private long mLoadingExpensiveTexturesStartTime = 0;
78     private final SparseArray<ResourceTexture> sCacheScaled = new SparseArray<ResourceTexture>();
79     private final SparseArray<ResourceTexture> sCacheUnscaled = new SparseArray<ResourceTexture>();
80
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;
85
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;
92             this.gl = gl;
93         }
94
95         public final int textureId;
96         public final GL11 gl;
97         public final DirectLinkedList.Entry<TextureReference> activeListEntry = new DirectLinkedList.Entry<TextureReference>(this);
98     }
99
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>();
106
107         void clear() {
108             updateList.clear();
109             opaqueList.clear();
110             blendedList.clear();
111             hitTestList.clear();
112             systemList.clear();
113         }
114     }
115
116     public RenderView(final Context context) {
117         super(context);
118         setBackgroundDrawable(null);
119         setFocusable(true);
120         setEGLConfigChooser(true);
121         setRenderer(this);
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();
126                 if (i == 0) {
127                     sCachedTextureLoadThread = thread;
128                 }
129                 if (i == 1) {
130                     sVideoTextureLoadThread = thread;
131                 }
132                 sTextureLoadThreads[i] = thread;
133                 thread.start();
134             }
135         }
136     }
137
138     public void setRootLayer(RootLayer layer) {
139         if (mRootLayer != layer) {
140             mRootLayer = layer;
141             mListsDirty = true;
142             if (layer != null) {
143                 mRootLayer.setSize(mViewWidth, mViewHeight);
144             }
145         }
146     }
147
148     public ResourceTexture getResource(int resourceId) {
149         return getResourceInternal(resourceId, true);
150     }
151
152     public ResourceTexture getResource(int resourceId, boolean scaled) {
153         return getResourceInternal(resourceId, scaled);
154     }
155
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);
162         }
163         return texture;
164     }
165
166     public void clearCache() {
167         clearTextureArray(sCacheScaled);
168         clearTextureArray(sCacheUnscaled);
169     }
170
171     private void clearTextureArray(SparseArray<ResourceTexture> array) {
172         /*
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(); } }
175          */
176         array.clear();
177     }
178
179     /** Render API */
180
181     public long getFrameTime() {
182         return mFrameTime;
183     }
184
185     public float getFrameInterval() {
186         return mFrameInterval;
187     }
188
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);
192         }
193     }
194
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);
204                 break;
205             }
206         }
207     }
208
209     private void loadTextureAsync(Texture texture) {
210         try {
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();
223                     if (config == null)
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);
230                     bitmap.recycle();
231                     bitmap = padded;
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;
235                 } else {
236                     texture.mNormalizedWidth = 1.0f;
237                     texture.mNormalizedHeight = 1.0f;
238                 }
239             }
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");
245             handleLowMemory();
246         }
247     }
248
249     public boolean bind(Texture texture) {
250         if (texture != null) {
251             if (texture == mBoundTexture)
252                 return true;
253             switch (texture.mState) {
254             case Texture.STATE_UNLOADED:
255                 if (texture.getClass().equals(ResourceTexture.class)) {
256                     loadTexture(texture);
257                     return false;
258                 }
259                 if (mLoadingCount < MAX_LOADING_COUNT) {
260                     queueLoad(texture, false);
261                 }
262                 break;
263             case Texture.STATE_LOADED:
264                 mGL.glBindTexture(GL11.GL_TEXTURE_2D, texture.mId);
265                 mBoundTexture = texture;
266                 return true;
267             default:
268                 break;
269             }
270         }
271         return false;
272     }
273
274     public void setAlpha(float alpha) {
275         GL11 gl = mGL;
276         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
277         gl.glColor4f(alpha, alpha, alpha, alpha);
278         mAlpha = alpha;
279     }
280
281     public float getAlpha() {
282         return mAlpha;
283     }
284
285     public void setColor(float red, float green, float blue, float alpha) {
286         GL11 gl = mGL;
287         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
288         gl.glColor4f(red, green, blue, alpha);
289         mAlpha = alpha;
290     }
291
292     public void resetColor() {
293         GL11 gl = mGL;
294         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
295         gl.glColor4f(1, 1, 1, 1);
296     }
297
298     public boolean isLoadingExpensiveTextures() {
299         return mLoadingExpensiveTexturesStartTime != 0;
300     }
301
302     public long elapsedLoadingExpensiveTextures() {
303         long startTime = mLoadingExpensiveTexturesStartTime;
304         if (startTime != 0) {
305             return SystemClock.uptimeMillis() - startTime;
306         } else {
307             return -1;
308         }
309     }
310
311     private void queueLoad(final Texture texture, boolean highPriority) {
312         // Allow the texture to defer queuing.
313         if (!texture.shouldQueue()) {
314             return;
315         }
316
317         // Change the texture state to loading.
318         texture.mState = Texture.STATE_LOADING;
319
320         // Push the texture onto the load input queue.
321         Deque<Texture> inputQueue = (texture.isUncachedVideo()) ? sLoadInputQueueVideo : (texture.isCached()) ? sLoadInputQueueCached : sLoadInputQueue;;
322         synchronized (inputQueue) {
323             if (highPriority) {
324                 inputQueue.addFirst(texture);
325             } else {
326                 inputQueue.addLast(texture);
327             }
328             inputQueue.notify();
329         }
330         ++mLoadingCount;
331     }
332
333     public void draw2D(Texture texture, float x, float y) {
334         if (bind(texture)) {
335             final float width = texture.getWidth();
336             final float height = texture.getHeight();
337             ((GL11Ext) mGL).glDrawTexfOES(x, mViewHeight - y - height, 0f, width, height);
338         }
339     }
340
341     public void draw2D(Texture texture, float x, float y, float width, float height) {
342         if (bind(texture)) {
343             ((GL11Ext) mGL).glDrawTexfOES(x, mViewHeight - y - height, 0f, width, height);
344         }
345     }
346
347     public void draw2D(Texture texture, int x, int y, int width, int height) {
348         if (bind(texture)) {
349             ((GL11Ext) mGL).glDrawTexiOES(x, (int) (mViewHeight - y - height), 0, width, height);
350         }
351     }
352
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);
355     }
356
357     public boolean bindMixed(Texture from, Texture to, float ratio) {
358         // Bind "from" and "to" to TEXTURE0 and TEXTURE1, respectively.
359         final GL11 gl = mGL;
360         boolean bind = true;
361         bind &= bind(from);
362         gl.glActiveTexture(GL11.GL_TEXTURE1);
363         mBoundTexture = null;
364         bind &= bind(to);
365         if (!bind) {
366             return false;
367         }
368
369         // Enable TEXTURE1.
370         gl.glEnable(GL11.GL_TEXTURE_2D);
371
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);
376
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);
380
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);
384
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);
388         return true;
389     }
390
391     public void unbindMixed() {
392         // Disable TEXTURE1.
393         final GL11 gl = mGL;
394         gl.glDisable(GL11.GL_TEXTURE_2D);
395
396         // Switch back to the default texture unit.
397         gl.glActiveTexture(GL11.GL_TEXTURE0);
398         mBoundTexture = null;
399     }
400
401     public void drawMixed2D(Texture from, Texture to, float ratio, float x, float y, float z, float width, float height) {
402         final GL11 gl = mGL;
403
404         // Bind "from" and "to" to TEXTURE0 and TEXTURE1, respectively.
405         if (bind(from)) {
406             gl.glActiveTexture(GL11.GL_TEXTURE1);
407             mBoundTexture = null;
408             if (bind(to)) {
409                 // Enable TEXTURE1.
410                 gl.glEnable(GL11.GL_TEXTURE_2D);
411
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);
416
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);
420
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);
424
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);
428
429                 // Draw the combined texture.
430                 ((GL11Ext) gl).glDrawTexfOES(x, mViewHeight - y - height, z, width, height);
431
432                 // Disable TEXTURE1.
433                 gl.glDisable(GL11.GL_TEXTURE_2D);
434             }
435
436             // Switch back to the default texture unit.
437             gl.glActiveTexture(GL11.GL_TEXTURE0);
438             mBoundTexture = null;
439         }
440     }
441
442     public void processAllTextures() {
443         processTextures(true);
444     }
445
446     final static int[] textureId = new int[1];
447
448     /** Uploads at most one texture to GL. */
449     private void processTextures(boolean processAll) {
450         // Destroy any textures that are no longer referenced.
451         GL11 gl = mGL;
452         TextureReference textureReference;
453         while ((textureReference = (TextureReference) mUnreferencedTextureQueue.poll()) != null) {
454             textureId[0] = textureReference.textureId;
455             GL11 glOld = textureReference.gl;
456             if (glOld == gl) {
457                 gl.glDeleteTextures(1, textureId, 0);
458             }
459             mActiveTextureList.remove(textureReference.activeListEntry);
460         }
461         Deque<Texture> outputQueue = sLoadOutputQueue;
462         Texture texture;
463         do {
464             // Upload loaded textures to the GPU one frame at a time.
465             synchronized (outputQueue) {
466                 texture = outputQueue.pollFirst();
467             }
468             if (texture != null) {
469                 // Extract the bitmap from the texture.
470                 uploadTexture(texture, textureId);
471
472                 // Decrement the loading count.
473                 --mLoadingCount;
474             } else {
475                 break;
476             }
477         } while (processAll);
478     }
479
480     private void uploadTexture(Texture texture, int[] textureId) {
481         Bitmap bitmap = texture.mBitmap;
482         GL11 gl = mGL;
483         int glError = GL11.GL_NO_ERROR;
484         if (bitmap != null) {
485             final int width = texture.mWidth;
486             final int height = texture.mHeight;
487
488             // Define a vertically flipped crop rectangle for OES_draw_texture.
489             int[] cropRect = { 0, height, width, -height };
490
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();
501             
502             bitmap.recycle();
503             if (glError == GL11.GL_OUT_OF_MEMORY) {
504                 handleLowMemory();
505             }
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);
509                 texture.mId = 0;
510                 texture.mBitmap = null;
511                 texture.mState = Texture.STATE_UNLOADED;
512             } else {
513                 // Update texture state.
514                 texture.mBitmap = null;
515                 texture.mId = textureId[0];
516                 texture.mState = Texture.STATE_LOADED;
517
518                 // Add to the active list.
519                 final TextureReference textureRef = new TextureReference(texture, gl, mUnreferencedTextureQueue, textureId[0]);
520                 mActiveTextureList.add(textureRef.activeListEntry);
521                 requestRender();
522             }
523         } else {
524             texture.mState = Texture.STATE_ERROR;
525         }
526
527     }
528
529     @Override
530     public void onResume() {
531         super.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);
536         }
537         if (sensorOrientation != null) {
538             mSensorManager.registerListener(this, sensorOrientation, SensorManager.SENSOR_DELAY_UI);
539         }
540     }
541
542     @Override
543     public void onPause() {
544         super.onPause();
545         Log.i(TAG, "OnPause RenderView " + this);
546         mSensorManager.unregisterListener(this);
547     }
548
549     /** Renders a frame of the UI. */
550     // @Override
551     public void onDrawFrame(GL10 gl1) {
552         GL11 gl = (GL11) gl1;
553         if (!mFirstDraw) {
554             Log.i(TAG, "First Draw");
555         }
556         mFirstDraw = true;
557         // setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
558         // Rebuild the display lists if the render tree has changed.
559         if (mListsDirty) {
560             updateLists();
561         }
562
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;
569                 break;
570             }
571         }
572         if (loadingExpensiveTextures != wasLoadingExpensiveTextures) {
573             mLoadingExpensiveTexturesStartTime = loadingExpensiveTextures ? SystemClock.uptimeMillis() : 0;
574         }
575
576         // Upload new textures.
577         processTextures(false);
578
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);
582         mFrameInterval = dt;
583         mFrameTime = now;
584
585         // Dispatch the current touch event.
586         processCurrentEvent();
587         processTouchEvent();
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);
595                 isDirty |= retVal;
596             }
597             if (isDirty) {
598                 requestRender();
599             }
600         
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());
605
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);
613                 }
614             }
615             
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);
623                 }
624             }
625             gl.glDisable(GL11.GL_BLEND);
626         }
627     }
628
629     private void processCurrentEvent() {
630         final int type = mCurrentEventType;
631         switch (type) {
632         case EVENT_KEY:
633             processKeyEvent();
634             break;
635         case EVENT_FOCUS:
636             processFocusEvent();
637             break;
638         default:
639             break;
640         }
641         synchronized (this) {
642             mCurrentEventType = EVENT_NONE;
643             this.notify();
644         }
645     }
646
647     private void processTouchEvent() {
648         MotionEvent event = null;
649         int numEvents = mTouchEventQueue.size();
650         int i = 0;
651         do {
652             // We look at the touch event queue and process one event at a time
653             synchronized (mTouchEventQueue) {
654                 event = mTouchEventQueue.pollFirst();
655             }
656             if (event == null)
657                 return;
658
659             // Detect the hit layer.
660             final int action = event.getAction();
661             Layer target;
662             if (action == MotionEvent.ACTION_DOWN) {
663                 target = hitTest(event.getX(), event.getY());
664                 mTouchEventTarget = target;
665             } else {
666                 target = mTouchEventTarget;
667             }
668
669             // Dispatch event to the hit layer.
670             if (target != null) {
671                 target.onTouchEvent(event);
672             }
673
674             // Clear the hit layer.
675             if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
676                 mTouchEventTarget = null;
677             }
678             event.recycle();
679             ++i;
680         } while (event != null && i < numEvents);
681         synchronized (this) {
682             this.notify();
683         }
684     }
685
686     private void processKeyEvent() {
687         // Get the event.
688         final KeyEvent event = mCurrentKeyEvent;
689         boolean result = false;
690         mCurrentKeyEvent = null;
691
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);
696             } else {
697                 result = mRootLayer.onKeyUp(event.getKeyCode(), event);
698             }
699         }
700         mCurrentKeyEventResult = result;
701     }
702
703     private void processFocusEvent() {
704         // Get event information.
705         if (mRootLayer != null) {
706
707         }
708     }
709
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)) {
719                     return layer;
720                 }
721             }
722         }
723         return null;
724     }
725
726     private void updateLists() {
727         if (mRootLayer != null) {
728             synchronized (sLists) {
729                 sLists.clear();
730                 mRootLayer.generate(this, sLists);
731             }
732         }
733     }
734
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;
738         mFirstDraw = false;
739         mViewWidth = width;
740         mViewHeight = height;
741         if (mRootLayer != null) {
742             mRootLayer.setSize(width, height);
743         }
744
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);
750         gl.glLoadIdentity();
751         GLU.gluPerspective(gl, 45.0f, (float) width / height, zNear, zFar);
752         if (mRootLayer != null) {
753             mRootLayer.onSurfaceChanged(this, width, height);
754         }
755         gl.glMatrixMode(GL11.GL_MODELVIEW);
756     }
757
758     public void setFov(float fov) {
759         GL11 gl = mGL;
760         gl.glMatrixMode(GL11.GL_PROJECTION);
761         gl.glLoadIdentity();
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);
766     }
767
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.
771         clearCache();
772
773         GL11 gl = (GL11) gl1;
774         if (mGL == null) {
775             mGL = gl;
776         } else {
777             // The GL Object has changed.
778             Log.i(TAG, "GLObject has changed from " + mGL + " to " + gl);
779             mGL = gl;
780         }
781         setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
782         // Increase the priority of the render thread.
783         Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
784
785         // Disable unused state.
786         gl.glEnable(GL11.GL_DITHER);
787         gl.glDisable(GL11.GL_LIGHTING);
788
789         // Set global state.
790         // gl.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
791
792         // Enable textures.
793         gl.glEnable(GL11.GL_TEXTURE_2D);
794         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
795
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.
800
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);
807
808         // Enable depth test.
809         gl.glEnable(GL11.GL_DEPTH_TEST);
810         gl.glDepthFunc(GL11.GL_LEQUAL);
811
812         // Set the blend function for premultiplied alpha.
813         gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
814
815         // Set the background color.
816         gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
817         gl.glClear(GL11.GL_COLOR_BUFFER_BIT);
818
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;
826                 }
827                 iter = iter.next;
828             }
829         }
830         mActiveTextureList.clear();
831         if (mRootLayer != null) {
832             mRootLayer.onSurfaceCreated(this, gl);
833         }
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);
838             }
839         }
840     }
841
842     /** Indicates that the accuracy of a sensor value has changed. */
843     public void onAccuracyChanged(Sensor sensor, int accuracy) {
844     }
845
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) {
854                 requestRender();
855             }
856         }
857     }
858
859     @Override
860     public boolean onTouchEvent(MotionEvent event) {
861         // Ignore events received before the surface is created to avoid
862         // deadlocking with GLSurfaceView's needToWait().
863         if (mGL == null) {
864             return false;
865         }
866         // Wait for the render thread to process this event.
867         if (mTouchEventQueue.size() > 8 && event.getAction() == MotionEvent.ACTION_MOVE)
868                 return true;
869         synchronized (mTouchEventQueue) {
870             MotionEvent eventCopy = MotionEvent.obtain(event);
871             mTouchEventQueue.addLast(eventCopy);
872             requestRender();
873         }
874         return true;
875     }
876
877     @Override
878     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
879         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
880
881         // Ignore events received before the surface is created to avoid
882         // deadlocking with GLSurfaceView's needToWait().
883         /*
884          * if (mGL == null) { return; }
885          * 
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. }
889          */
890         requestRender();
891     }
892
893     @Override
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().
897         if (mGL == null) {
898             return false;
899         }
900
901         // Wait for the render thread to process this event.
902         try {
903             synchronized (this) {
904                 mCurrentKeyEvent = event;
905                 mCurrentEventType = EVENT_KEY;
906                 requestRender();
907                 long timeout = SystemClock.uptimeMillis() + 50;
908                 do {
909                     wait(50);
910                 } while (mCurrentEventType != EVENT_NONE && SystemClock.uptimeMillis() < timeout);
911             }
912         } catch (InterruptedException e) {
913             // Stop waiting for the render thread if interrupted.
914         }
915
916         // Key events are handled on the main thread.
917         boolean retVal = false;
918         if (!mCurrentKeyEventResult) {
919             retVal = super.onKeyDown(keyCode, event);
920         } else {
921             retVal = true;
922         }
923         requestRender();
924         return retVal;
925     }
926
927     @Override
928     public void surfaceDestroyed(SurfaceHolder holder) {
929         super.surfaceDestroyed(holder);
930     }
931
932     @Override
933     protected void onAttachedToWindow() {
934         super.onAttachedToWindow();
935     }
936
937     @Override
938     protected void onDetachedFromWindow() {
939         super.onDetachedFromWindow();
940     }
941
942     private final class TextureLoadThread extends Thread {
943         public boolean mIsLoading;
944
945         public TextureLoadThread() {
946             super("TextureLoad");
947         }
948
949         public void run() {
950             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
951             Deque<Texture> inputQueue = (sVideoTextureLoadThread == this) ? sLoadInputQueueVideo : ((sCachedTextureLoadThread == this) ? sLoadInputQueueCached : sLoadInputQueue);
952             Deque<Texture> outputQueue = sLoadOutputQueue;
953             try {
954                 for (;;) {
955                     // Pop the next texture from the input queue.
956                     Texture texture = null;
957                     synchronized (inputQueue) {
958                         while ((texture = inputQueue.pollFirst()) == null) {
959                             inputQueue.wait();
960                         }
961                     }
962                     if (sCachedTextureLoadThread != this)
963                         mIsLoading = true;
964                     // Load the texture bitmap.
965                     load(texture);
966                     mIsLoading = false;
967
968                     // Push the texture onto the output queue.
969                     synchronized (outputQueue) {
970                         outputQueue.addLast(texture);
971                     }
972                 }
973             } catch (InterruptedException e) {
974                 // Terminate the thread.
975             }
976         }
977
978         private void load(Texture texture) {
979             // Generate the texture bitmap.
980             RenderView view = RenderView.this;
981             view.loadTextureAsync(texture);
982             view.requestRender();
983         }
984     }
985
986     public void shutdown() {
987         mRootLayer = null;
988         synchronized (sLists) {
989             sLists.clear();
990         }
991     }
992
993     public void handleLowMemory() {
994         Log.i(TAG, "Handling low memory condition");
995         if (mRootLayer != null) {
996             mRootLayer.handleLowMemory();
997         }
998     }
999
1000     public Lists getLists() {
1001         return sLists;
1002     }
1003 }