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 65namespace android { 66 67static const int ANIM_ENTRY_NAME_MAX = 256; 68 69// --------------------------------------------------------------------------- 70 71BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true) { 72 mSession = new SurfaceComposerClient(); 73} 74 75BootAnimation::~BootAnimation() { 76} 77 78void BootAnimation::onFirstRef() { 79 status_t err = mSession->linkToComposerDeath(this); 80 ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err)); 81 if (err == NO_ERROR) { 82 run("BootAnimation", PRIORITY_DISPLAY); 83 } 84} 85 86sp<SurfaceComposerClient> BootAnimation::session() const { 87 return mSession; 88} 89 90 91void BootAnimation::binderDied(const wp<IBinder>&) 92{ 93 // woah, surfaceflinger died! 94 ALOGD("SurfaceFlinger died, exiting..."); 95 96 // calling requestExit() is not enough here because the Surface code 97 // might be blocked on a condition variable that will never be updated. 98 kill( getpid(), SIGKILL ); 99 requestExit(); 100 if (mAudioPlayer != NULL) { 101 mAudioPlayer->requestExit(); 102 } 103} 104 105status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, 106 const char* name) { 107 Asset* asset = assets.open(name, Asset::ACCESS_BUFFER); 108 if (asset == NULL) 109 return NO_INIT; 110 SkBitmap bitmap; 111 SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), 112 &bitmap, kUnknown_SkColorType, SkImageDecoder::kDecodePixels_Mode); 113 asset->close(); 114 delete asset; 115 116 // ensure we can call getPixels(). No need to call unlock, since the 117 // bitmap will go out of scope when we return from this method. 118 bitmap.lockPixels(); 119 120 const int w = bitmap.width(); 121 const int h = bitmap.height(); 122 const void* p = bitmap.getPixels(); 123 124 GLint crop[4] = { 0, h, w, -h }; 125 texture->w = w; 126 texture->h = h; 127 128 glGenTextures(1, &texture->name); 129 glBindTexture(GL_TEXTURE_2D, texture->name); 130 131 switch (bitmap.colorType()) { 132 case kAlpha_8_SkColorType: 133 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, 134 GL_UNSIGNED_BYTE, p); 135 break; 136 case kARGB_4444_SkColorType: 137 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, 138 GL_UNSIGNED_SHORT_4_4_4_4, p); 139 break; 140 case kN32_SkColorType: 141 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, 142 GL_UNSIGNED_BYTE, p); 143 break; 144 case kRGB_565_SkColorType: 145 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, 146 GL_UNSIGNED_SHORT_5_6_5, p); 147 break; 148 default: 149 break; 150 } 151 152 glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); 153 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 154 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 155 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 156 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 157 return NO_ERROR; 158} 159 160status_t BootAnimation::initTexture(const Animation::Frame& frame) 161{ 162 //StopWatch watch("blah"); 163 164 SkBitmap bitmap; 165 SkMemoryStream stream(frame.map->getDataPtr(), frame.map->getDataLength()); 166 SkImageDecoder* codec = SkImageDecoder::Factory(&stream); 167 if (codec != NULL) { 168 codec->setDitherImage(false); 169 codec->decode(&stream, &bitmap, 170 kN32_SkColorType, 171 SkImageDecoder::kDecodePixels_Mode); 172 delete codec; 173 } 174 175 // FileMap memory is never released until application exit. 176 // Release it now as the texture is already loaded and the memory used for 177 // the packed resource can be released. 178 delete frame.map; 179 180 // ensure we can call getPixels(). No need to call unlock, since the 181 // bitmap will go out of scope when we return from this method. 182 bitmap.lockPixels(); 183 184 const int w = bitmap.width(); 185 const int h = bitmap.height(); 186 const void* p = bitmap.getPixels(); 187 188 GLint crop[4] = { 0, h, w, -h }; 189 int tw = 1 << (31 - __builtin_clz(w)); 190 int th = 1 << (31 - __builtin_clz(h)); 191 if (tw < w) tw <<= 1; 192 if (th < h) th <<= 1; 193 194 switch (bitmap.colorType()) { 195 case kN32_SkColorType: 196 if (tw != w || th != h) { 197 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, 198 GL_UNSIGNED_BYTE, 0); 199 glTexSubImage2D(GL_TEXTURE_2D, 0, 200 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p); 201 } else { 202 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, 203 GL_UNSIGNED_BYTE, p); 204 } 205 break; 206 207 case kRGB_565_SkColorType: 208 if (tw != w || th != h) { 209 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, 210 GL_UNSIGNED_SHORT_5_6_5, 0); 211 glTexSubImage2D(GL_TEXTURE_2D, 0, 212 0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); 213 } else { 214 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, 215 GL_UNSIGNED_SHORT_5_6_5, p); 216 } 217 break; 218 default: 219 break; 220 } 221 222 glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); 223 224 return NO_ERROR; 225} 226 227status_t BootAnimation::readyToRun() { 228 mAssets.addDefaultAssets(); 229 230 sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( 231 ISurfaceComposer::eDisplayIdMain)); 232 DisplayInfo dinfo; 233 status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo); 234 if (status) 235 return -1; 236 237 // create the native surface 238 sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"), 239 dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565); 240 241 SurfaceComposerClient::openGlobalTransaction(); 242 control->setLayer(0x40000000); 243 SurfaceComposerClient::closeGlobalTransaction(); 244 245 sp<Surface> s = control->getSurface(); 246 247 // initialize opengl and egl 248 const EGLint attribs[] = { 249 EGL_RED_SIZE, 8, 250 EGL_GREEN_SIZE, 8, 251 EGL_BLUE_SIZE, 8, 252 EGL_DEPTH_SIZE, 0, 253 EGL_NONE 254 }; 255 EGLint w, h; 256 EGLint numConfigs; 257 EGLConfig config; 258 EGLSurface surface; 259 EGLContext context; 260 261 EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); 262 263 eglInitialize(display, 0, 0); 264 eglChooseConfig(display, attribs, &config, 1, &numConfigs); 265 surface = eglCreateWindowSurface(display, config, s.get(), NULL); 266 context = eglCreateContext(display, config, NULL, NULL); 267 eglQuerySurface(display, surface, EGL_WIDTH, &w); 268 eglQuerySurface(display, surface, EGL_HEIGHT, &h); 269 270 if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) 271 return NO_INIT; 272 273 mDisplay = display; 274 mContext = context; 275 mSurface = surface; 276 mWidth = w; 277 mHeight = h; 278 mFlingerSurfaceControl = control; 279 mFlingerSurface = s; 280 281 // If the device has encryption turned on or is in process 282 // of being encrypted we show the encrypted boot animation. 283 char decrypt[PROPERTY_VALUE_MAX]; 284 property_get("vold.decrypt", decrypt, ""); 285 286 bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt); 287 288 if (encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) { 289 mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE; 290 } 291 else if (access(OEM_BOOTANIMATION_FILE, R_OK) == 0) { 292 mZipFileName = OEM_BOOTANIMATION_FILE; 293 } 294 else if (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) { 295 mZipFileName = SYSTEM_BOOTANIMATION_FILE; 296 } 297 return NO_ERROR; 298} 299 300bool BootAnimation::threadLoop() 301{ 302 bool r; 303 // We have no bootanimation file, so we use the stock android logo 304 // animation. 305 if (mZipFileName.isEmpty()) { 306 r = android(); 307 } else { 308 r = movie(); 309 } 310 311 eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 312 eglDestroyContext(mDisplay, mContext); 313 eglDestroySurface(mDisplay, mSurface); 314 mFlingerSurface.clear(); 315 mFlingerSurfaceControl.clear(); 316 eglTerminate(mDisplay); 317 IPCThreadState::self()->stopProcess(); 318 return r; 319} 320 321bool BootAnimation::android() 322{ 323 initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png"); 324 initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png"); 325 326 // clear screen 327 glShadeModel(GL_FLAT); 328 glDisable(GL_DITHER); 329 glDisable(GL_SCISSOR_TEST); 330 glClearColor(0,0,0,1); 331 glClear(GL_COLOR_BUFFER_BIT); 332 eglSwapBuffers(mDisplay, mSurface); 333 334 glEnable(GL_TEXTURE_2D); 335 glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 336 337 const GLint xc = (mWidth - mAndroid[0].w) / 2; 338 const GLint yc = (mHeight - mAndroid[0].h) / 2; 339 const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h); 340 341 glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), 342 updateRect.height()); 343 344 // Blend state 345 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 346 glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 347 348 const nsecs_t startTime = systemTime(); 349 do { 350 nsecs_t now = systemTime(); 351 double time = now - startTime; 352 float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w; 353 GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w; 354 GLint x = xc - offset; 355 356 glDisable(GL_SCISSOR_TEST); 357 glClear(GL_COLOR_BUFFER_BIT); 358 359 glEnable(GL_SCISSOR_TEST); 360 glDisable(GL_BLEND); 361 glBindTexture(GL_TEXTURE_2D, mAndroid[1].name); 362 glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h); 363 glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h); 364 365 glEnable(GL_BLEND); 366 glBindTexture(GL_TEXTURE_2D, mAndroid[0].name); 367 glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h); 368 369 EGLBoolean res = eglSwapBuffers(mDisplay, mSurface); 370 if (res == EGL_FALSE) 371 break; 372 373 // 12fps: don't animate too fast to preserve CPU 374 const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now); 375 if (sleepTime > 0) 376 usleep(sleepTime); 377 378 checkExit(); 379 } while (!exitPending()); 380 381 glDeleteTextures(1, &mAndroid[0].name); 382 glDeleteTextures(1, &mAndroid[1].name); 383 return false; 384} 385 386 387void BootAnimation::checkExit() { 388 // Allow surface flinger to gracefully request shutdown 389 char value[PROPERTY_VALUE_MAX]; 390 property_get(EXIT_PROP_NAME, value, "0"); 391 int exitnow = atoi(value); 392 if (exitnow) { 393 requestExit(); 394 if (mAudioPlayer != NULL) { 395 mAudioPlayer->requestExit(); 396 } 397 } 398} 399 400// Parse a color represented as an HTML-style 'RRGGBB' string: each pair of 401// characters in str is a hex number in [0, 255], which are converted to 402// floating point values in the range [0.0, 1.0] and placed in the 403// corresponding elements of color. 404// 405// If the input string isn't valid, parseColor returns false and color is 406// left unchanged. 407static bool parseColor(const char str[7], float color[3]) { 408 float tmpColor[3]; 409 for (int i = 0; i < 3; i++) { 410 int val = 0; 411 for (int j = 0; j < 2; j++) { 412 val *= 16; 413 char c = str[2*i + j]; 414 if (c >= '0' && c <= '9') val += c - '0'; 415 else if (c >= 'A' && c <= 'F') val += (c - 'A') + 10; 416 else if (c >= 'a' && c <= 'f') val += (c - 'a') + 10; 417 else return false; 418 } 419 tmpColor[i] = static_cast<float>(val) / 255.0f; 420 } 421 memcpy(color, tmpColor, sizeof(tmpColor)); 422 return true; 423} 424 425 426static bool readFile(ZipFileRO* zip, const char* name, String8& outString) 427{ 428 ZipEntryRO entry = zip->findEntryByName(name); 429 ALOGE_IF(!entry, "couldn't find %s", name); 430 if (!entry) { 431 return false; 432 } 433 434 FileMap* entryMap = zip->createEntryFileMap(entry); 435 zip->releaseEntry(entry); 436 ALOGE_IF(!entryMap, "entryMap is null"); 437 if (!entryMap) { 438 return false; 439 } 440 441 outString.setTo((char const*)entryMap->getDataPtr(), entryMap->getDataLength()); 442 delete entryMap; 443 return true; 444} 445 446// The time glyphs are stored in a single image of height 64 pixels. Each digit is 40 pixels wide, 447// and the colon character is half that at 20 pixels. The glyph order is '0123456789:'. 448// We render 24 hour time. 449void BootAnimation::drawTime(const Texture& clockTex, const int yPos) { 450 static constexpr char TIME_FORMAT[] = "%H:%M"; 451 static constexpr int TIME_LENGTH = sizeof(TIME_FORMAT); 452 453 static constexpr int DIGIT_HEIGHT = 64; 454 static constexpr int DIGIT_WIDTH = 40; 455 static constexpr int COLON_WIDTH = DIGIT_WIDTH / 2; 456 static constexpr int TIME_WIDTH = (DIGIT_WIDTH * 4) + COLON_WIDTH; 457 458 if (clockTex.h < DIGIT_HEIGHT || clockTex.w < (10 * DIGIT_WIDTH + COLON_WIDTH)) { 459 ALOGE("Clock texture is too small; abandoning boot animation clock"); 460 mClockEnabled = false; 461 return; 462 } 463 464 time_t rawtime; 465 time(&rawtime); 466 struct tm* timeInfo = localtime(&rawtime); 467 468 char timeBuff[TIME_LENGTH]; 469 size_t length = strftime(timeBuff, TIME_LENGTH, TIME_FORMAT, timeInfo); 470 471 if (length != TIME_LENGTH - 1) { 472 ALOGE("Couldn't format time; abandoning boot animation clock"); 473 mClockEnabled = false; 474 return; 475 } 476 477 glEnable(GL_BLEND); // Allow us to draw on top of the animation 478 glBindTexture(GL_TEXTURE_2D, clockTex.name); 479 480 int xPos = (mWidth - TIME_WIDTH) / 2; 481 int cropRect[4] = { 0, DIGIT_HEIGHT, DIGIT_WIDTH, -DIGIT_HEIGHT }; 482 483 for (int i = 0; i < TIME_LENGTH - 1; i++) { 484 char c = timeBuff[i]; 485 int width = DIGIT_WIDTH; 486 int pos = c - '0'; // Position in the character list 487 if (pos < 0 || pos > 10) { 488 continue; 489 } 490 if (c == ':') { 491 width = COLON_WIDTH; 492 } 493 494 // Crop the texture to only the pixels in the current glyph 495 int left = pos * DIGIT_WIDTH; 496 cropRect[0] = left; 497 cropRect[2] = width; 498 glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect); 499 500 glDrawTexiOES(xPos, yPos, 0, width, DIGIT_HEIGHT); 501 502 xPos += width; 503 } 504 505 glDisable(GL_BLEND); // Return to the animation's default behaviour 506 glBindTexture(GL_TEXTURE_2D, 0); 507} 508 509bool BootAnimation::parseAnimationDesc(Animation& animation) 510{ 511 String8 desString; 512 513 if (!readFile(animation.zip, "desc.txt", desString)) { 514 return false; 515 } 516 char const* s = desString.string(); 517 518 // Create and initialize an AudioPlayer if we have an audio_conf.txt file 519 String8 audioConf; 520 if (readFile(animation.zip, "audio_conf.txt", audioConf)) { 521 mAudioPlayer = new AudioPlayer; 522 if (!mAudioPlayer->init(audioConf.string())) { 523 ALOGE("mAudioPlayer.init failed"); 524 mAudioPlayer = NULL; 525 } 526 } 527 528 // Parse the description file 529 for (;;) { 530 const char* endl = strstr(s, "\n"); 531 if (endl == NULL) break; 532 String8 line(s, endl - s); 533 const char* l = line.string(); 534 int fps = 0; 535 int width = 0; 536 int height = 0; 537 int count = 0; 538 int pause = 0; 539 int clockPosY = -1; 540 char path[ANIM_ENTRY_NAME_MAX]; 541 char color[7] = "000000"; // default to black if unspecified 542 543 char pathType; 544 if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { 545 // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps); 546 animation.width = width; 547 animation.height = height; 548 animation.fps = fps; 549 } else if (sscanf(l, " %c %d %d %s #%6s %d", 550 &pathType, &count, &pause, path, color, &clockPosY) >= 4) { 551 // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPosY=%d", pathType, count, pause, path, color, clockPosY); 552 Animation::Part part; 553 part.playUntilComplete = pathType == 'c'; 554 part.count = count; 555 part.pause = pause; 556 part.path = path; 557 part.clockPosY = clockPosY; 558 part.audioFile = NULL; 559 part.animation = NULL; 560 if (!parseColor(color, part.backgroundColor)) { 561 ALOGE("> invalid color '#%s'", color); 562 part.backgroundColor[0] = 0.0f; 563 part.backgroundColor[1] = 0.0f; 564 part.backgroundColor[2] = 0.0f; 565 } 566 animation.parts.add(part); 567 } 568 else if (strcmp(l, "$SYSTEM") == 0) { 569 // ALOGD("> SYSTEM"); 570 Animation::Part part; 571 part.playUntilComplete = false; 572 part.count = 1; 573 part.pause = 0; 574 part.audioFile = NULL; 575 part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE)); 576 if (part.animation != NULL) 577 animation.parts.add(part); 578 } 579 s = ++endl; 580 } 581 582 return true; 583} 584 585bool BootAnimation::preloadZip(Animation& animation) 586{ 587 // read all the data structures 588 const size_t pcount = animation.parts.size(); 589 void *cookie = NULL; 590 ZipFileRO* mZip = animation.zip; 591 if (!mZip->startIteration(&cookie)) { 592 return false; 593 } 594 595 ZipEntryRO entry; 596 char name[ANIM_ENTRY_NAME_MAX]; 597 while ((entry = mZip->nextEntry(cookie)) != NULL) { 598 const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX); 599 if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) { 600 ALOGE("Error fetching entry file name"); 601 continue; 602 } 603 604 const String8 entryName(name); 605 const String8 path(entryName.getPathDir()); 606 const String8 leaf(entryName.getPathLeaf()); 607 if (leaf.size() > 0) { 608 for (size_t j=0 ; j<pcount ; j++) { 609 if (path == animation.parts[j].path) { 610 uint16_t method; 611 // supports only stored png files 612 if (mZip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) { 613 if (method == ZipFileRO::kCompressStored) { 614 FileMap* map = mZip->createEntryFileMap(entry); 615 if (map) { 616 Animation::Part& part(animation.parts.editItemAt(j)); 617 if (leaf == "audio.wav") { 618 // a part may have at most one audio file 619 part.audioFile = map; 620 } else { 621 Animation::Frame frame; 622 frame.name = leaf; 623 frame.map = map; 624 part.frames.add(frame); 625 } 626 } 627 } 628 } 629 } 630 } 631 } 632 } 633 634 mZip->endIteration(cookie); 635 636 return true; 637} 638 639bool BootAnimation::movie() 640{ 641 642 Animation* animation = loadAnimation(mZipFileName); 643 if (animation == NULL) 644 return false; 645 646 // Blend required to draw time on top of animation frames. 647 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 648 glShadeModel(GL_FLAT); 649 glDisable(GL_DITHER); 650 glDisable(GL_SCISSOR_TEST); 651 glDisable(GL_BLEND); 652 653 glBindTexture(GL_TEXTURE_2D, 0); 654 glEnable(GL_TEXTURE_2D); 655 glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 656 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 657 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 658 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 659 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 660 661 bool clockTextureInitialized = false; 662 if (mClockEnabled) { 663 clockTextureInitialized = (initTexture(&mClock, mAssets, "images/clock64.png") == NO_ERROR); 664 mClockEnabled = clockTextureInitialized; 665 } 666 667 playAnimation(*animation); 668 releaseAnimation(animation); 669 670 if (clockTextureInitialized) { 671 glDeleteTextures(1, &mClock.name); 672 } 673 674 return false; 675} 676 677bool BootAnimation::playAnimation(const Animation& animation) 678{ 679 const size_t pcount = animation.parts.size(); 680 const int xc = (mWidth - animation.width) / 2; 681 const int yc = ((mHeight - animation.height) / 2); 682 nsecs_t frameDuration = s2ns(1) / animation.fps; 683 684 Region clearReg(Rect(mWidth, mHeight)); 685 clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height)); 686 687 for (size_t i=0 ; i<pcount ; i++) { 688 const Animation::Part& part(animation.parts[i]); 689 const size_t fcount = part.frames.size(); 690 glBindTexture(GL_TEXTURE_2D, 0); 691 692 // Handle animation package 693 if (part.animation != NULL) { 694 playAnimation(*part.animation); 695 if (exitPending()) 696 break; 697 continue; //to next part 698 } 699 700 for (int r=0 ; !part.count || r<part.count ; r++) { 701 // Exit any non playuntil complete parts immediately 702 if(exitPending() && !part.playUntilComplete) 703 break; 704 705 // only play audio file the first time we animate the part 706 if (r == 0 && mAudioPlayer != NULL && part.audioFile) { 707 mAudioPlayer->playFile(part.audioFile); 708 } 709 710 glClearColor( 711 part.backgroundColor[0], 712 part.backgroundColor[1], 713 part.backgroundColor[2], 714 1.0f); 715 716 for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) { 717 const Animation::Frame& frame(part.frames[j]); 718 nsecs_t lastFrame = systemTime(); 719 720 if (r > 0) { 721 glBindTexture(GL_TEXTURE_2D, frame.tid); 722 } else { 723 if (part.count != 1) { 724 glGenTextures(1, &frame.tid); 725 glBindTexture(GL_TEXTURE_2D, frame.tid); 726 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 727 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 728 } 729 initTexture(frame); 730 } 731 732 if (!clearReg.isEmpty()) { 733 Region::const_iterator head(clearReg.begin()); 734 Region::const_iterator tail(clearReg.end()); 735 glEnable(GL_SCISSOR_TEST); 736 while (head != tail) { 737 const Rect& r2(*head++); 738 glScissor(r2.left, mHeight - r2.bottom, 739 r2.width(), r2.height()); 740 glClear(GL_COLOR_BUFFER_BIT); 741 } 742 glDisable(GL_SCISSOR_TEST); 743 } 744 // specify the y center as ceiling((mHeight - animation.height) / 2) 745 // which is equivalent to mHeight - (yc + animation.height) 746 glDrawTexiOES(xc, mHeight - (yc + animation.height), 747 0, animation.width, animation.height); 748 if (mClockEnabled && part.clockPosY >= 0) { 749 drawTime(mClock, part.clockPosY); 750 } 751 752 eglSwapBuffers(mDisplay, mSurface); 753 754 nsecs_t now = systemTime(); 755 nsecs_t delay = frameDuration - (now - lastFrame); 756 //ALOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay)); 757 lastFrame = now; 758 759 if (delay > 0) { 760 struct timespec spec; 761 spec.tv_sec = (now + delay) / 1000000000; 762 spec.tv_nsec = (now + delay) % 1000000000; 763 int err; 764 do { 765 err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL); 766 } while (err<0 && errno == EINTR); 767 } 768 769 checkExit(); 770 } 771 772 usleep(part.pause * ns2us(frameDuration)); 773 774 // For infinite parts, we've now played them at least once, so perhaps exit 775 if(exitPending() && !part.count) 776 break; 777 } 778 779 // free the textures for this part 780 if (part.count != 1) { 781 for (size_t j=0 ; j<fcount ; j++) { 782 const Animation::Frame& frame(part.frames[j]); 783 glDeleteTextures(1, &frame.tid); 784 } 785 } 786 } 787 return true; 788} 789 790void BootAnimation::releaseAnimation(Animation* animation) const 791{ 792 for (Vector<Animation::Part>::iterator it = animation->parts.begin(), 793 e = animation->parts.end(); it != e; ++it) { 794 if (it->animation) 795 releaseAnimation(it->animation); 796 } 797 if (animation->zip) 798 delete animation->zip; 799 delete animation; 800} 801 802BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) 803{ 804 if (mLoadedFiles.indexOf(fn) >= 0) { 805 ALOGE("File \"%s\" is already loaded. Cyclic ref is not allowed", 806 fn.string()); 807 return NULL; 808 } 809 ZipFileRO *zip = ZipFileRO::open(fn); 810 if (zip == NULL) { 811 ALOGE("Failed to open animation zip \"%s\": %s", 812 fn.string(), strerror(errno)); 813 return NULL; 814 } 815 816 Animation *animation = new Animation; 817 animation->fileName = fn; 818 animation->zip = zip; 819 mLoadedFiles.add(animation->fileName); 820 821 parseAnimationDesc(*animation); 822 preloadZip(*animation); 823 824 mLoadedFiles.remove(fn); 825 return animation; 826} 827// --------------------------------------------------------------------------- 828 829} 830; // namespace android 831