summaryrefslogtreecommitdiff
path: root/cmds/bootanimation/BootAnimation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/bootanimation/BootAnimation.cpp')
-rw-r--r--cmds/bootanimation/BootAnimation.cpp222
1 files changed, 189 insertions, 33 deletions
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 44b432e69fbc..25b264ecff10 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -68,15 +68,11 @@ static const int ANIM_ENTRY_NAME_MAX = 256;
// ---------------------------------------------------------------------------
-BootAnimation::BootAnimation() : Thread(false), mZip(NULL)
-{
+BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true) {
mSession = new SurfaceComposerClient();
}
BootAnimation::~BootAnimation() {
- if (mZip != NULL) {
- delete mZip;
- }
}
void BootAnimation::onFirstRef() {
@@ -289,19 +285,15 @@ status_t BootAnimation::readyToRun() {
bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
- ZipFileRO* zipFile = NULL;
- if ((encryptedAnimation &&
- (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
- ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||
-
- ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&
- ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||
-
- ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
- ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
- mZip = zipFile;
+ if (encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) {
+ mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE;
+ }
+ else if (access(OEM_BOOTANIMATION_FILE, R_OK) == 0) {
+ mZipFileName = OEM_BOOTANIMATION_FILE;
+ }
+ else if (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) {
+ mZipFileName = SYSTEM_BOOTANIMATION_FILE;
}
-
return NO_ERROR;
}
@@ -310,7 +302,7 @@ bool BootAnimation::threadLoop()
bool r;
// We have no bootanimation file, so we use the stock android logo
// animation.
- if (mZip == NULL) {
+ if (mZipFileName.isEmpty()) {
r = android();
} else {
r = movie();
@@ -431,16 +423,17 @@ static bool parseColor(const char str[7], float color[3]) {
return true;
}
-bool BootAnimation::readFile(const char* name, String8& outString)
+
+static bool readFile(ZipFileRO* zip, const char* name, String8& outString)
{
- ZipEntryRO entry = mZip->findEntryByName(name);
+ ZipEntryRO entry = zip->findEntryByName(name);
ALOGE_IF(!entry, "couldn't find %s", name);
if (!entry) {
return false;
}
- FileMap* entryMap = mZip->createEntryFileMap(entry);
- mZip->releaseEntry(entry);
+ FileMap* entryMap = zip->createEntryFileMap(entry);
+ zip->releaseEntry(entry);
ALOGE_IF(!entryMap, "entryMap is null");
if (!entryMap) {
return false;
@@ -451,18 +444,81 @@ bool BootAnimation::readFile(const char* name, String8& outString)
return true;
}
-bool BootAnimation::movie()
+// The time glyphs are stored in a single image of height 64 pixels. Each digit is 40 pixels wide,
+// and the colon character is half that at 20 pixels. The glyph order is '0123456789:'.
+// We render 24 hour time.
+void BootAnimation::drawTime(const Texture& clockTex, const int yPos) {
+ static constexpr char TIME_FORMAT[] = "%H:%M";
+ static constexpr int TIME_LENGTH = sizeof(TIME_FORMAT);
+
+ static constexpr int DIGIT_HEIGHT = 64;
+ static constexpr int DIGIT_WIDTH = 40;
+ static constexpr int COLON_WIDTH = DIGIT_WIDTH / 2;
+ static constexpr int TIME_WIDTH = (DIGIT_WIDTH * 4) + COLON_WIDTH;
+
+ if (clockTex.h < DIGIT_HEIGHT || clockTex.w < (10 * DIGIT_WIDTH + COLON_WIDTH)) {
+ ALOGE("Clock texture is too small; abandoning boot animation clock");
+ mClockEnabled = false;
+ return;
+ }
+
+ time_t rawtime;
+ time(&rawtime);
+ struct tm* timeInfo = localtime(&rawtime);
+
+ char timeBuff[TIME_LENGTH];
+ size_t length = strftime(timeBuff, TIME_LENGTH, TIME_FORMAT, timeInfo);
+
+ if (length != TIME_LENGTH - 1) {
+ ALOGE("Couldn't format time; abandoning boot animation clock");
+ mClockEnabled = false;
+ return;
+ }
+
+ glEnable(GL_BLEND); // Allow us to draw on top of the animation
+ glBindTexture(GL_TEXTURE_2D, clockTex.name);
+
+ int xPos = (mWidth - TIME_WIDTH) / 2;
+ int cropRect[4] = { 0, DIGIT_HEIGHT, DIGIT_WIDTH, -DIGIT_HEIGHT };
+
+ for (int i = 0; i < TIME_LENGTH - 1; i++) {
+ char c = timeBuff[i];
+ int width = DIGIT_WIDTH;
+ int pos = c - '0'; // Position in the character list
+ if (pos < 0 || pos > 10) {
+ continue;
+ }
+ if (c == ':') {
+ width = COLON_WIDTH;
+ }
+
+ // Crop the texture to only the pixels in the current glyph
+ int left = pos * DIGIT_WIDTH;
+ cropRect[0] = left;
+ cropRect[2] = width;
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect);
+
+ glDrawTexiOES(xPos, yPos, 0, width, DIGIT_HEIGHT);
+
+ xPos += width;
+ }
+
+ glDisable(GL_BLEND); // Return to the animation's default behaviour
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+bool BootAnimation::parseAnimationDesc(Animation& animation)
{
String8 desString;
- if (!readFile("desc.txt", desString)) {
+ if (!readFile(animation.zip, "desc.txt", desString)) {
return false;
}
char const* s = desString.string();
// Create and initialize an AudioPlayer if we have an audio_conf.txt file
String8 audioConf;
- if (readFile("audio_conf.txt", audioConf)) {
+ if (readFile(animation.zip, "audio_conf.txt", audioConf)) {
mAudioPlayer = new AudioPlayer;
if (!mAudioPlayer->init(audioConf.string())) {
ALOGE("mAudioPlayer.init failed");
@@ -470,15 +526,18 @@ bool BootAnimation::movie()
}
}
- Animation animation;
-
// Parse the description file
for (;;) {
const char* endl = strstr(s, "\n");
if (endl == NULL) break;
String8 line(s, endl - s);
const char* l = line.string();
- int fps, width, height, count, pause;
+ int fps = 0;
+ int width = 0;
+ int height = 0;
+ int count = 0;
+ int pause = 0;
+ int clockPosY = -1;
char path[ANIM_ENTRY_NAME_MAX];
char color[7] = "000000"; // default to black if unspecified
@@ -488,15 +547,17 @@ bool BootAnimation::movie()
animation.width = width;
animation.height = height;
animation.fps = fps;
- }
- else if (sscanf(l, " %c %d %d %s #%6s", &pathType, &count, &pause, path, color) >= 4) {
- // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s", pathType, count, pause, path, color);
+ } else if (sscanf(l, " %c %d %d %s #%6s %d",
+ &pathType, &count, &pause, path, color, &clockPosY) >= 4) {
+ // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPosY=%d", pathType, count, pause, path, color, clockPosY);
Animation::Part part;
part.playUntilComplete = pathType == 'c';
part.count = count;
part.pause = pause;
part.path = path;
+ part.clockPosY = clockPosY;
part.audioFile = NULL;
+ part.animation = NULL;
if (!parseColor(color, part.backgroundColor)) {
ALOGE("> invalid color '#%s'", color);
part.backgroundColor[0] = 0.0f;
@@ -505,13 +566,29 @@ bool BootAnimation::movie()
}
animation.parts.add(part);
}
-
+ else if (strcmp(l, "$SYSTEM") == 0) {
+ // ALOGD("> SYSTEM");
+ Animation::Part part;
+ part.playUntilComplete = false;
+ part.count = 1;
+ part.pause = 0;
+ part.audioFile = NULL;
+ part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
+ if (part.animation != NULL)
+ animation.parts.add(part);
+ }
s = ++endl;
}
+ return true;
+}
+
+bool BootAnimation::preloadZip(Animation& animation)
+{
// read all the data structures
const size_t pcount = animation.parts.size();
void *cookie = NULL;
+ ZipFileRO* mZip = animation.zip;
if (!mZip->startIteration(&cookie)) {
return false;
}
@@ -557,6 +634,18 @@ bool BootAnimation::movie()
mZip->endIteration(cookie);
+ return true;
+}
+
+bool BootAnimation::movie()
+{
+
+ Animation* animation = loadAnimation(mZipFileName);
+ if (animation == NULL)
+ return false;
+
+ // Blend required to draw time on top of animation frames.
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
@@ -570,6 +659,25 @@ bool BootAnimation::movie()
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ bool clockTextureInitialized = false;
+ if (mClockEnabled) {
+ clockTextureInitialized = (initTexture(&mClock, mAssets, "images/clock64.png") == NO_ERROR);
+ mClockEnabled = clockTextureInitialized;
+ }
+
+ playAnimation(*animation);
+ releaseAnimation(animation);
+
+ if (clockTextureInitialized) {
+ glDeleteTextures(1, &mClock.name);
+ }
+
+ return false;
+}
+
+bool BootAnimation::playAnimation(const Animation& animation)
+{
+ const size_t pcount = animation.parts.size();
const int xc = (mWidth - animation.width) / 2;
const int yc = ((mHeight - animation.height) / 2);
nsecs_t frameDuration = s2ns(1) / animation.fps;
@@ -582,6 +690,14 @@ bool BootAnimation::movie()
const size_t fcount = part.frames.size();
glBindTexture(GL_TEXTURE_2D, 0);
+ // Handle animation package
+ if (part.animation != NULL) {
+ playAnimation(*part.animation);
+ if (exitPending())
+ break;
+ continue; //to next part
+ }
+
for (int r=0 ; !part.count || r<part.count ; r++) {
// Exit any non playuntil complete parts immediately
if(exitPending() && !part.playUntilComplete)
@@ -630,6 +746,10 @@ bool BootAnimation::movie()
// which is equivalent to mHeight - (yc + animation.height)
glDrawTexiOES(xc, mHeight - (yc + animation.height),
0, animation.width, animation.height);
+ if (mClockEnabled && part.clockPosY >= 0) {
+ drawTime(mClock, part.clockPosY);
+ }
+
eglSwapBuffers(mDisplay, mSurface);
nsecs_t now = systemTime();
@@ -665,10 +785,46 @@ bool BootAnimation::movie()
}
}
}
+ return true;
+}
- return false;
+void BootAnimation::releaseAnimation(Animation* animation) const
+{
+ for (Vector<Animation::Part>::iterator it = animation->parts.begin(),
+ e = animation->parts.end(); it != e; ++it) {
+ if (it->animation)
+ releaseAnimation(it->animation);
+ }
+ if (animation->zip)
+ delete animation->zip;
+ delete animation;
}
+BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
+{
+ if (mLoadedFiles.indexOf(fn) >= 0) {
+ ALOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
+ fn.string());
+ return NULL;
+ }
+ ZipFileRO *zip = ZipFileRO::open(fn);
+ if (zip == NULL) {
+ ALOGE("Failed to open animation zip \"%s\": %s",
+ fn.string(), strerror(errno));
+ return NULL;
+ }
+
+ Animation *animation = new Animation;
+ animation->fileName = fn;
+ animation->zip = zip;
+ mLoadedFiles.add(animation->fileName);
+
+ parseAnimationDesc(*animation);
+ preloadZip(*animation);
+
+ mLoadedFiles.remove(fn);
+ return animation;
+}
// ---------------------------------------------------------------------------
}