OSDN Git Service

Merge "[DO NOT MERGE] Throw exception if slot has invalid offset" into lmp-mr1-dev...
[android-x86/frameworks-base.git] / cmds / bootanimation / BootAnimation.cpp
1 /*
2  * Copyright (C) 2007 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 #define LOG_NDEBUG 0
18 #define LOG_TAG "BootAnimation"
19
20 #include <stdint.h>
21 #include <sys/types.h>
22 #include <math.h>
23 #include <fcntl.h>
24 #include <utils/misc.h>
25 #include <signal.h>
26 #include <time.h>
27
28 #include <cutils/properties.h>
29
30 #include <androidfw/AssetManager.h>
31 #include <binder/IPCThreadState.h>
32 #include <utils/Atomic.h>
33 #include <utils/Errors.h>
34 #include <utils/Log.h>
35
36 #include <ui/PixelFormat.h>
37 #include <ui/Rect.h>
38 #include <ui/Region.h>
39 #include <ui/DisplayInfo.h>
40
41 #include <gui/ISurfaceComposer.h>
42 #include <gui/Surface.h>
43 #include <gui/SurfaceComposerClient.h>
44
45 // TODO: Fix Skia.
46 #pragma GCC diagnostic push
47 #pragma GCC diagnostic ignored "-Wunused-parameter"
48 #include <SkBitmap.h>
49 #include <SkStream.h>
50 #include <SkImageDecoder.h>
51 #pragma GCC diagnostic pop
52
53 #include <GLES/gl.h>
54 #include <GLES/glext.h>
55 #include <EGL/eglext.h>
56
57 #include "BootAnimation.h"
58 #include "AudioPlayer.h"
59
60 #define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
61 #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
62 #define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
63 #define EXIT_PROP_NAME "service.bootanim.exit"
64
65 namespace android {
66
67 static const int ANIM_ENTRY_NAME_MAX = 256;
68
69 // ---------------------------------------------------------------------------
70
71 BootAnimation::BootAnimation() : Thread(false), mZip(NULL)
72 {
73     mSession = new SurfaceComposerClient();
74 }
75
76 BootAnimation::~BootAnimation() {
77     if (mZip != NULL) {
78         delete mZip;
79     }
80 }
81
82 void BootAnimation::onFirstRef() {
83     status_t err = mSession->linkToComposerDeath(this);
84     ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
85     if (err == NO_ERROR) {
86         run("BootAnimation", PRIORITY_DISPLAY);
87     }
88 }
89
90 sp<SurfaceComposerClient> BootAnimation::session() const {
91     return mSession;
92 }
93
94
95 void BootAnimation::binderDied(const wp<IBinder>&)
96 {
97     // woah, surfaceflinger died!
98     ALOGD("SurfaceFlinger died, exiting...");
99
100     // calling requestExit() is not enough here because the Surface code
101     // might be blocked on a condition variable that will never be updated.
102     kill( getpid(), SIGKILL );
103     requestExit();
104     if (mAudioPlayer != NULL) {
105         mAudioPlayer->requestExit();
106     }
107 }
108
109 status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
110         const char* name) {
111     Asset* asset = assets.open(name, Asset::ACCESS_BUFFER);
112     if (asset == NULL)
113         return NO_INIT;
114     SkBitmap bitmap;
115     SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(),
116             &bitmap, kUnknown_SkColorType, SkImageDecoder::kDecodePixels_Mode);
117     asset->close();
118     delete asset;
119
120     // ensure we can call getPixels(). No need to call unlock, since the
121     // bitmap will go out of scope when we return from this method.
122     bitmap.lockPixels();
123
124     const int w = bitmap.width();
125     const int h = bitmap.height();
126     const void* p = bitmap.getPixels();
127
128     GLint crop[4] = { 0, h, w, -h };
129     texture->w = w;
130     texture->h = h;
131
132     glGenTextures(1, &texture->name);
133     glBindTexture(GL_TEXTURE_2D, texture->name);
134
135     switch (bitmap.colorType()) {
136         case kAlpha_8_SkColorType:
137             glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA,
138                     GL_UNSIGNED_BYTE, p);
139             break;
140         case kARGB_4444_SkColorType:
141             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
142                     GL_UNSIGNED_SHORT_4_4_4_4, p);
143             break;
144         case kN32_SkColorType:
145             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
146                     GL_UNSIGNED_BYTE, p);
147             break;
148         case kRGB_565_SkColorType:
149             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB,
150                     GL_UNSIGNED_SHORT_5_6_5, p);
151             break;
152         default:
153             break;
154     }
155
156     glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
157     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
158     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
159     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
160     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
161     return NO_ERROR;
162 }
163
164 status_t BootAnimation::initTexture(const Animation::Frame& frame)
165 {
166     //StopWatch watch("blah");
167
168     SkBitmap bitmap;
169     SkMemoryStream  stream(frame.map->getDataPtr(), frame.map->getDataLength());
170     SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
171     if (codec != NULL) {
172         codec->setDitherImage(false);
173         codec->decode(&stream, &bitmap,
174                 kN32_SkColorType,
175                 SkImageDecoder::kDecodePixels_Mode);
176         delete codec;
177     }
178
179     // FileMap memory is never released until application exit.
180     // Release it now as the texture is already loaded and the memory used for
181     // the packed resource can be released.
182     delete frame.map;
183
184     // ensure we can call getPixels(). No need to call unlock, since the
185     // bitmap will go out of scope when we return from this method.
186     bitmap.lockPixels();
187
188     const int w = bitmap.width();
189     const int h = bitmap.height();
190     const void* p = bitmap.getPixels();
191
192     GLint crop[4] = { 0, h, w, -h };
193     int tw = 1 << (31 - __builtin_clz(w));
194     int th = 1 << (31 - __builtin_clz(h));
195     if (tw < w) tw <<= 1;
196     if (th < h) th <<= 1;
197
198     switch (bitmap.colorType()) {
199         case kN32_SkColorType:
200             if (tw != w || th != h) {
201                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
202                         GL_UNSIGNED_BYTE, 0);
203                 glTexSubImage2D(GL_TEXTURE_2D, 0,
204                         0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p);
205             } else {
206                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
207                         GL_UNSIGNED_BYTE, p);
208             }
209             break;
210
211         case kRGB_565_SkColorType:
212             if (tw != w || th != h) {
213                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
214                         GL_UNSIGNED_SHORT_5_6_5, 0);
215                 glTexSubImage2D(GL_TEXTURE_2D, 0,
216                         0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p);
217             } else {
218                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
219                         GL_UNSIGNED_SHORT_5_6_5, p);
220             }
221             break;
222         default:
223             break;
224     }
225
226     glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
227
228     return NO_ERROR;
229 }
230
231 status_t BootAnimation::readyToRun() {
232     mAssets.addDefaultAssets();
233
234     sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
235             ISurfaceComposer::eDisplayIdMain));
236     DisplayInfo dinfo;
237     status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
238     if (status)
239         return -1;
240
241     // create the native surface
242     sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
243             dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
244
245     SurfaceComposerClient::openGlobalTransaction();
246     control->setLayer(0x40000000);
247     SurfaceComposerClient::closeGlobalTransaction();
248
249     sp<Surface> s = control->getSurface();
250
251     // initialize opengl and egl
252     const EGLint attribs[] = {
253             EGL_RED_SIZE,   8,
254             EGL_GREEN_SIZE, 8,
255             EGL_BLUE_SIZE,  8,
256             EGL_DEPTH_SIZE, 0,
257             EGL_NONE
258     };
259     EGLint w, h;
260     EGLint numConfigs;
261     EGLConfig config;
262     EGLSurface surface;
263     EGLContext context;
264
265     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
266
267     eglInitialize(display, 0, 0);
268     eglChooseConfig(display, attribs, &config, 1, &numConfigs);
269     surface = eglCreateWindowSurface(display, config, s.get(), NULL);
270     context = eglCreateContext(display, config, NULL, NULL);
271     eglQuerySurface(display, surface, EGL_WIDTH, &w);
272     eglQuerySurface(display, surface, EGL_HEIGHT, &h);
273
274     if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
275         return NO_INIT;
276
277     mDisplay = display;
278     mContext = context;
279     mSurface = surface;
280     mWidth = w;
281     mHeight = h;
282     mFlingerSurfaceControl = control;
283     mFlingerSurface = s;
284
285     // If the device has encryption turned on or is in process
286     // of being encrypted we show the encrypted boot animation.
287     char decrypt[PROPERTY_VALUE_MAX];
288     property_get("vold.decrypt", decrypt, "");
289
290     bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
291
292     ZipFileRO* zipFile = NULL;
293     if ((encryptedAnimation &&
294             (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
295             ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||
296
297             ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&
298             ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||
299
300             ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
301             ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
302         mZip = zipFile;
303     }
304
305     return NO_ERROR;
306 }
307
308 bool BootAnimation::threadLoop()
309 {
310     bool r;
311     // We have no bootanimation file, so we use the stock android logo
312     // animation.
313     if (mZip == NULL) {
314         r = android();
315     } else {
316         r = movie();
317     }
318
319     eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
320     eglDestroyContext(mDisplay, mContext);
321     eglDestroySurface(mDisplay, mSurface);
322     mFlingerSurface.clear();
323     mFlingerSurfaceControl.clear();
324     eglTerminate(mDisplay);
325     IPCThreadState::self()->stopProcess();
326     return r;
327 }
328
329 bool BootAnimation::android()
330 {
331     initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
332     initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
333
334     // clear screen
335     glShadeModel(GL_FLAT);
336     glDisable(GL_DITHER);
337     glDisable(GL_SCISSOR_TEST);
338     glClearColor(0,0,0,1);
339     glClear(GL_COLOR_BUFFER_BIT);
340     eglSwapBuffers(mDisplay, mSurface);
341
342     glEnable(GL_TEXTURE_2D);
343     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
344
345     const GLint xc = (mWidth  - mAndroid[0].w) / 2;
346     const GLint yc = (mHeight - mAndroid[0].h) / 2;
347     const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);
348
349     glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
350             updateRect.height());
351
352     // Blend state
353     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
354     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
355
356     const nsecs_t startTime = systemTime();
357     do {
358         nsecs_t now = systemTime();
359         double time = now - startTime;
360         float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;
361         GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;
362         GLint x = xc - offset;
363
364         glDisable(GL_SCISSOR_TEST);
365         glClear(GL_COLOR_BUFFER_BIT);
366
367         glEnable(GL_SCISSOR_TEST);
368         glDisable(GL_BLEND);
369         glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
370         glDrawTexiOES(x,                 yc, 0, mAndroid[1].w, mAndroid[1].h);
371         glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);
372
373         glEnable(GL_BLEND);
374         glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
375         glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);
376
377         EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
378         if (res == EGL_FALSE)
379             break;
380
381         // 12fps: don't animate too fast to preserve CPU
382         const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
383         if (sleepTime > 0)
384             usleep(sleepTime);
385
386         checkExit();
387     } while (!exitPending());
388
389     glDeleteTextures(1, &mAndroid[0].name);
390     glDeleteTextures(1, &mAndroid[1].name);
391     return false;
392 }
393
394
395 void BootAnimation::checkExit() {
396     // Allow surface flinger to gracefully request shutdown
397     char value[PROPERTY_VALUE_MAX];
398     property_get(EXIT_PROP_NAME, value, "0");
399     int exitnow = atoi(value);
400     if (exitnow) {
401         requestExit();
402         if (mAudioPlayer != NULL) {
403             mAudioPlayer->requestExit();
404         }
405     }
406 }
407
408 // Parse a color represented as an HTML-style 'RRGGBB' string: each pair of
409 // characters in str is a hex number in [0, 255], which are converted to
410 // floating point values in the range [0.0, 1.0] and placed in the
411 // corresponding elements of color.
412 //
413 // If the input string isn't valid, parseColor returns false and color is
414 // left unchanged.
415 static bool parseColor(const char str[7], float color[3]) {
416     float tmpColor[3];
417     for (int i = 0; i < 3; i++) {
418         int val = 0;
419         for (int j = 0; j < 2; j++) {
420             val *= 16;
421             char c = str[2*i + j];
422             if      (c >= '0' && c <= '9') val += c - '0';
423             else if (c >= 'A' && c <= 'F') val += (c - 'A') + 10;
424             else if (c >= 'a' && c <= 'f') val += (c - 'a') + 10;
425             else                           return false;
426         }
427         tmpColor[i] = static_cast<float>(val) / 255.0f;
428     }
429     memcpy(color, tmpColor, sizeof(tmpColor));
430     return true;
431 }
432
433 bool BootAnimation::readFile(const char* name, String8& outString)
434 {
435     ZipEntryRO entry = mZip->findEntryByName(name);
436     ALOGE_IF(!entry, "couldn't find %s", name);
437     if (!entry) {
438         return false;
439     }
440
441     FileMap* entryMap = mZip->createEntryFileMap(entry);
442     mZip->releaseEntry(entry);
443     ALOGE_IF(!entryMap, "entryMap is null");
444     if (!entryMap) {
445         return false;
446     }
447
448     outString.setTo((char const*)entryMap->getDataPtr(), entryMap->getDataLength());
449     delete entryMap;
450     return true;
451 }
452
453 bool BootAnimation::movie()
454 {
455     String8 desString;
456
457     if (!readFile("desc.txt", desString)) {
458         return false;
459     }
460     char const* s = desString.string();
461
462     // Create and initialize an AudioPlayer if we have an audio_conf.txt file
463     String8 audioConf;
464     if (readFile("audio_conf.txt", audioConf)) {
465         mAudioPlayer = new AudioPlayer;
466         if (!mAudioPlayer->init(audioConf.string())) {
467             ALOGE("mAudioPlayer.init failed");
468             mAudioPlayer = NULL;
469         }
470     }
471
472     Animation animation;
473
474     // Parse the description file
475     for (;;) {
476         const char* endl = strstr(s, "\n");
477         if (endl == NULL) break;
478         String8 line(s, endl - s);
479         const char* l = line.string();
480         int fps, width, height, count, pause;
481         char path[ANIM_ENTRY_NAME_MAX];
482         char color[7] = "000000"; // default to black if unspecified
483
484         char pathType;
485         if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
486             // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps);
487             animation.width = width;
488             animation.height = height;
489             animation.fps = fps;
490         }
491         else if (sscanf(l, " %c %d %d %s #%6s", &pathType, &count, &pause, path, color) >= 4) {
492             // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s", pathType, count, pause, path, color);
493             Animation::Part part;
494             part.playUntilComplete = pathType == 'c';
495             part.count = count;
496             part.pause = pause;
497             part.path = path;
498             part.audioFile = NULL;
499             if (!parseColor(color, part.backgroundColor)) {
500                 ALOGE("> invalid color '#%s'", color);
501                 part.backgroundColor[0] = 0.0f;
502                 part.backgroundColor[1] = 0.0f;
503                 part.backgroundColor[2] = 0.0f;
504             }
505             animation.parts.add(part);
506         }
507
508         s = ++endl;
509     }
510
511     // read all the data structures
512     const size_t pcount = animation.parts.size();
513     void *cookie = NULL;
514     if (!mZip->startIteration(&cookie)) {
515         return false;
516     }
517
518     ZipEntryRO entry;
519     char name[ANIM_ENTRY_NAME_MAX];
520     while ((entry = mZip->nextEntry(cookie)) != NULL) {
521         const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
522         if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
523             ALOGE("Error fetching entry file name");
524             continue;
525         }
526
527         const String8 entryName(name);
528         const String8 path(entryName.getPathDir());
529         const String8 leaf(entryName.getPathLeaf());
530         if (leaf.size() > 0) {
531             for (size_t j=0 ; j<pcount ; j++) {
532                 if (path == animation.parts[j].path) {
533                     uint16_t method;
534                     // supports only stored png files
535                     if (mZip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {
536                         if (method == ZipFileRO::kCompressStored) {
537                             FileMap* map = mZip->createEntryFileMap(entry);
538                             if (map) {
539                                 Animation::Part& part(animation.parts.editItemAt(j));
540                                 if (leaf == "audio.wav") {
541                                     // a part may have at most one audio file
542                                     part.audioFile = map;
543                                 } else {
544                                     Animation::Frame frame;
545                                     frame.name = leaf;
546                                     frame.map = map;
547                                     part.frames.add(frame);
548                                 }
549                             }
550                         }
551                     }
552                 }
553             }
554         }
555     }
556
557     mZip->endIteration(cookie);
558
559     // clear screen
560     glShadeModel(GL_FLAT);
561     glDisable(GL_DITHER);
562     glDisable(GL_SCISSOR_TEST);
563     glDisable(GL_BLEND);
564     glClearColor(0,0,0,1);
565     glClear(GL_COLOR_BUFFER_BIT);
566
567     eglSwapBuffers(mDisplay, mSurface);
568
569     glBindTexture(GL_TEXTURE_2D, 0);
570     glEnable(GL_TEXTURE_2D);
571     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
572     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
573     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
574     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
575     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
576
577     const int xc = (mWidth - animation.width) / 2;
578     const int yc = ((mHeight - animation.height) / 2);
579     nsecs_t frameDuration = s2ns(1) / animation.fps;
580
581     Region clearReg(Rect(mWidth, mHeight));
582     clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height));
583
584     for (size_t i=0 ; i<pcount ; i++) {
585         const Animation::Part& part(animation.parts[i]);
586         const size_t fcount = part.frames.size();
587         glBindTexture(GL_TEXTURE_2D, 0);
588
589         for (int r=0 ; !part.count || r<part.count ; r++) {
590             // Exit any non playuntil complete parts immediately
591             if(exitPending() && !part.playUntilComplete)
592                 break;
593
594             // only play audio file the first time we animate the part
595             if (r == 0 && mAudioPlayer != NULL && part.audioFile) {
596                 mAudioPlayer->playFile(part.audioFile);
597             }
598
599             glClearColor(
600                     part.backgroundColor[0],
601                     part.backgroundColor[1],
602                     part.backgroundColor[2],
603                     1.0f);
604
605             for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
606                 const Animation::Frame& frame(part.frames[j]);
607                 nsecs_t lastFrame = systemTime();
608
609                 if (r > 0) {
610                     glBindTexture(GL_TEXTURE_2D, frame.tid);
611                 } else {
612                     if (part.count != 1) {
613                         glGenTextures(1, &frame.tid);
614                         glBindTexture(GL_TEXTURE_2D, frame.tid);
615                         glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
616                         glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
617                     }
618                     initTexture(frame);
619                 }
620
621                 if (!clearReg.isEmpty()) {
622                     Region::const_iterator head(clearReg.begin());
623                     Region::const_iterator tail(clearReg.end());
624                     glEnable(GL_SCISSOR_TEST);
625                     while (head != tail) {
626                         const Rect& r2(*head++);
627                         glScissor(r2.left, mHeight - r2.bottom,
628                                 r2.width(), r2.height());
629                         glClear(GL_COLOR_BUFFER_BIT);
630                     }
631                     glDisable(GL_SCISSOR_TEST);
632                 }
633                 // specify the y center as ceiling((mHeight - animation.height) / 2)
634                 // which is equivalent to mHeight - (yc + animation.height)
635                 glDrawTexiOES(xc, mHeight - (yc + animation.height),
636                               0, animation.width, animation.height);
637                 eglSwapBuffers(mDisplay, mSurface);
638
639                 nsecs_t now = systemTime();
640                 nsecs_t delay = frameDuration - (now - lastFrame);
641                 //ALOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));
642                 lastFrame = now;
643
644                 if (delay > 0) {
645                     struct timespec spec;
646                     spec.tv_sec  = (now + delay) / 1000000000;
647                     spec.tv_nsec = (now + delay) % 1000000000;
648                     int err;
649                     do {
650                         err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
651                     } while (err<0 && errno == EINTR);
652                 }
653
654                 checkExit();
655             }
656
657             usleep(part.pause * ns2us(frameDuration));
658
659             // For infinite parts, we've now played them at least once, so perhaps exit
660             if(exitPending() && !part.count)
661                 break;
662         }
663
664         // free the textures for this part
665         if (part.count != 1) {
666             for (size_t j=0 ; j<fcount ; j++) {
667                 const Animation::Frame& frame(part.frames[j]);
668                 glDeleteTextures(1, &frame.tid);
669             }
670         }
671     }
672
673     return false;
674 }
675
676 // ---------------------------------------------------------------------------
677
678 }
679 ; // namespace android