Loading cmds/bootanimation/BootAnimation.cpp +92 −18 Original line number Diff line number Diff line Loading @@ -459,6 +459,8 @@ status_t BootAnimation::readyToRun() { mFlingerSurface = s; mTargetInset = -1; projectSceneToWindow(); // Register a display event receiver mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>(); status_t status = mDisplayEventReceiver->initCheck(); Loading @@ -470,6 +472,16 @@ status_t BootAnimation::readyToRun() { return NO_ERROR; } void BootAnimation::projectSceneToWindow() { glViewport(0, 0, mWidth, mHeight); glScissor(0, 0, mWidth, mHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrthof(0, static_cast<float>(mWidth), 0, static_cast<float>(mHeight), -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void BootAnimation::resizeSurface(int newWidth, int newHeight) { // We assume this function is called on the animation thread. if (newWidth == mWidth && newHeight == mHeight) { Loading @@ -494,8 +506,8 @@ void BootAnimation::resizeSurface(int newWidth, int newHeight) { SLOGE("Can't make the new surface current. Error %d", eglGetError()); return; } glViewport(0, 0, mWidth, mHeight); glScissor(0, 0, mWidth, mHeight); projectSceneToWindow(); mSurface = surface; } Loading Loading @@ -776,6 +788,37 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) { return status; } void BootAnimation::fadeFrame(const int frameLeft, const int frameBottom, const int frameWidth, const int frameHeight, const Animation::Part& part, const int fadedFramesCount) { glEnable(GL_BLEND); glEnableClientState(GL_VERTEX_ARRAY); glDisable(GL_TEXTURE_2D); // avoid creating a hole due to mixing result alpha with GL_REPLACE texture glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); const float alpha = static_cast<float>(fadedFramesCount) / part.framesToFadeCount; glColor4f(part.backgroundColor[0], part.backgroundColor[1], part.backgroundColor[2], alpha); const float frameStartX = static_cast<float>(frameLeft); const float frameStartY = static_cast<float>(frameBottom); const float frameEndX = frameStartX + frameWidth; const float frameEndY = frameStartY + frameHeight; const GLfloat frameRect[] = { frameStartX, frameStartY, frameEndX, frameStartY, frameEndX, frameEndY, frameStartX, frameEndY }; glVertexPointer(2, GL_FLOAT, 0, frameRect); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_BLEND); } void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) { glEnable(GL_BLEND); // Allow us to draw on top of the animation glBindTexture(GL_TEXTURE_2D, font.texture.name); Loading Loading @@ -867,23 +910,34 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { int height = 0; int count = 0; int pause = 0; int framesToFadeCount = 0; char path[ANIM_ENTRY_NAME_MAX]; char color[7] = "000000"; // default to black if unspecified char clockPos1[TEXT_POS_LEN_MAX + 1] = ""; char clockPos2[TEXT_POS_LEN_MAX + 1] = ""; char pathType; int nextReadPos; if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { // SLOGD("> w=%d, h=%d, fps=%d", width, height, fps); animation.width = width; animation.height = height; animation.fps = fps; } else if (sscanf(l, " %c %d %d %" STRTO(ANIM_PATH_MAX) "s #%6s %16s %16s", &pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) { //SLOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s", // pathType, count, pause, path, color, clockPos1, clockPos2); } else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n", &pathType, &count, &pause, path, &nextReadPos) >= 4) { if (pathType == 'f') { sscanf(l + nextReadPos, " %d #%6s %16s %16s", &framesToFadeCount, color, clockPos1, clockPos2); } else { sscanf(l + nextReadPos, " #%6s %16s %16s", color, clockPos1, clockPos2); } // SLOGD("> type=%c, count=%d, pause=%d, path=%s, framesToFadeCount=%d, color=%s, " // "clockPos1=%s, clockPos2=%s", // pathType, count, pause, path, framesToFadeCount, color, clockPos1, clockPos2); Animation::Part part; part.playUntilComplete = pathType == 'c'; part.framesToFadeCount = framesToFadeCount; part.count = count; part.pause = pause; part.path = path; Loading @@ -902,6 +956,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { // SLOGD("> SYSTEM"); Animation::Part part; part.playUntilComplete = false; part.framesToFadeCount = 0; part.count = 1; part.pause = 0; part.audioData = nullptr; Loading Loading @@ -1098,12 +1153,19 @@ bool BootAnimation::movie() { return false; } bool BootAnimation::shouldStopPlayingPart(const Animation::Part& part, const int fadedFramesCount) { // stop playing only if it is time to exit and it's a partial part which has been faded out return exitPending() && !part.playUntilComplete && fadedFramesCount >= part.framesToFadeCount; } bool BootAnimation::playAnimation(const Animation& animation) { const size_t pcount = animation.parts.size(); nsecs_t frameDuration = s2ns(1) / animation.fps; SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime()); int fadedFramesCount = 0; for (size_t i=0 ; i<pcount ; i++) { const Animation::Part& part(animation.parts[i]); const size_t fcount = part.frames.size(); Loading @@ -1117,10 +1179,9 @@ bool BootAnimation::playAnimation(const Animation& animation) { 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) break; // process the part not only while the count allows but also if already fading for (int r=0 ; !part.count || r<part.count || fadedFramesCount > 0 ; r++) { if (shouldStopPlayingPart(part, fadedFramesCount)) break; mCallbacks->playPart(i, part, r); Loading @@ -1130,7 +1191,9 @@ bool BootAnimation::playAnimation(const Animation& animation) { part.backgroundColor[2], 1.0f); for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) { for (size_t j=0 ; j<fcount ; j++) { if (shouldStopPlayingPart(part, fadedFramesCount)) break; processDisplayEvents(); const int animationX = (mWidth - animation.width) / 2; Loading Loading @@ -1169,11 +1232,22 @@ bool BootAnimation::playAnimation(const Animation& animation) { } // specify the y center as ceiling((mHeight - frame.trimHeight) / 2) // which is equivalent to mHeight - (yc + frame.trimHeight) glDrawTexiOES(xc, mHeight - (yc + frame.trimHeight), 0, frame.trimWidth, frame.trimHeight); const int frameDrawY = mHeight - (yc + frame.trimHeight); glDrawTexiOES(xc, frameDrawY, 0, frame.trimWidth, frame.trimHeight); // if the part hasn't been stopped yet then continue fading if necessary if (exitPending() && part.hasFadingPhase()) { fadeFrame(xc, frameDrawY, frame.trimWidth, frame.trimHeight, part, ++fadedFramesCount); if (fadedFramesCount >= part.framesToFadeCount) { fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading } } if (mClockEnabled && mTimeIsAccurate && validClock(part)) { drawClock(animation.clockFont, part.clockPosX, part.clockPosY); } handleViewport(frameDuration); eglSwapBuffers(mDisplay, mSurface); Loading @@ -1198,11 +1272,11 @@ bool BootAnimation::playAnimation(const Animation& animation) { usleep(part.pause * ns2us(frameDuration)); // For infinite parts, we've now played them at least once, so perhaps exit if(exitPending() && !part.count && mCurrentInset >= mTargetInset) break; if (exitPending() && !part.count && mCurrentInset >= mTargetInset && !part.hasFadingPhase()) { break; // exit the infinite non-fading part when it has been played at least once } } } // Free textures created for looping parts now that the animation is done. Loading cmds/bootanimation/BootAnimation.h +12 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <vector> #include <queue> #include <climits> #include <stdint.h> #include <sys/types.h> Loading @@ -45,6 +46,8 @@ class SurfaceControl; class BootAnimation : public Thread, public IBinder::DeathRecipient { public: static constexpr int MAX_FADED_FRAMES_COUNT = std::numeric_limits<int>::max(); struct Texture { GLint w; GLint h; Loading Loading @@ -84,10 +87,15 @@ public: String8 trimData; SortedVector<Frame> frames; bool playUntilComplete; int framesToFadeCount; float backgroundColor[3]; uint8_t* audioData; int audioLength; Animation* animation; bool hasFadingPhase() const { return !playUntilComplete && framesToFadeCount > 0; } }; int fps; int width; Loading Loading @@ -160,6 +168,8 @@ private: bool movie(); void drawText(const char* str, const Font& font, bool bold, int* x, int* y); void drawClock(const Font& font, const int xPos, const int yPos); void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight, const Animation::Part& part, int fadedFramesCount); bool validClock(const Animation::Part& part); Animation* loadAnimation(const String8&); bool playAnimation(const Animation&); Loading @@ -172,7 +182,9 @@ private: EGLConfig getEglConfig(const EGLDisplay&); ui::Size limitSurfaceSize(int width, int height) const; void resizeSurface(int newWidth, int newHeight); void projectSceneToWindow(); bool shouldStopPlayingPart(const Animation::Part& part, int fadedFramesCount); void checkExit(); void handleViewport(nsecs_t timestep); Loading cmds/bootanimation/FORMAT.md +7 −1 Original line number Diff line number Diff line Loading @@ -30,14 +30,20 @@ The first line defines the general parameters of the animation: It is followed by a number of rows of the form: TYPE COUNT PAUSE PATH [#RGBHEX [CLOCK1 [CLOCK2]]] TYPE COUNT PAUSE PATH [FADE [#RGBHEX [CLOCK1 [CLOCK2]]]] * **TYPE:** a single char indicating what type of animation segment this is: + `p` -- this part will play unless interrupted by the end of the boot + `c` -- this part will play to completion, no matter what + `f` -- same as `p` but in addition the specified number of frames is being faded out while continue playing. Only the first interrupted `f` part is faded out, other subsequent `f` parts are skipped * **COUNT:** how many times to play the animation, or 0 to loop forever until boot is complete * **PAUSE:** number of FRAMES to delay after this part ends * **PATH:** directory in which to find the frames for this part (e.g. `part0`) * **FADE:** _(ONLY FOR `f` TYPE)_ number of frames to fade out when interrupted where `0` means _immediately_ which makes `f ... 0` behave like `p` and doesn't count it as a fading part * **RGBHEX:** _(OPTIONAL)_ a background color, specified as `#RRGGBB` * **CLOCK1, CLOCK2:** _(OPTIONAL)_ the coordinates at which to draw the current time (for watches): + If only `CLOCK1` is provided it is the y-coordinate of the clock and the x-coordinate Loading Loading
cmds/bootanimation/BootAnimation.cpp +92 −18 Original line number Diff line number Diff line Loading @@ -459,6 +459,8 @@ status_t BootAnimation::readyToRun() { mFlingerSurface = s; mTargetInset = -1; projectSceneToWindow(); // Register a display event receiver mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>(); status_t status = mDisplayEventReceiver->initCheck(); Loading @@ -470,6 +472,16 @@ status_t BootAnimation::readyToRun() { return NO_ERROR; } void BootAnimation::projectSceneToWindow() { glViewport(0, 0, mWidth, mHeight); glScissor(0, 0, mWidth, mHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrthof(0, static_cast<float>(mWidth), 0, static_cast<float>(mHeight), -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void BootAnimation::resizeSurface(int newWidth, int newHeight) { // We assume this function is called on the animation thread. if (newWidth == mWidth && newHeight == mHeight) { Loading @@ -494,8 +506,8 @@ void BootAnimation::resizeSurface(int newWidth, int newHeight) { SLOGE("Can't make the new surface current. Error %d", eglGetError()); return; } glViewport(0, 0, mWidth, mHeight); glScissor(0, 0, mWidth, mHeight); projectSceneToWindow(); mSurface = surface; } Loading Loading @@ -776,6 +788,37 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) { return status; } void BootAnimation::fadeFrame(const int frameLeft, const int frameBottom, const int frameWidth, const int frameHeight, const Animation::Part& part, const int fadedFramesCount) { glEnable(GL_BLEND); glEnableClientState(GL_VERTEX_ARRAY); glDisable(GL_TEXTURE_2D); // avoid creating a hole due to mixing result alpha with GL_REPLACE texture glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); const float alpha = static_cast<float>(fadedFramesCount) / part.framesToFadeCount; glColor4f(part.backgroundColor[0], part.backgroundColor[1], part.backgroundColor[2], alpha); const float frameStartX = static_cast<float>(frameLeft); const float frameStartY = static_cast<float>(frameBottom); const float frameEndX = frameStartX + frameWidth; const float frameEndY = frameStartY + frameHeight; const GLfloat frameRect[] = { frameStartX, frameStartY, frameEndX, frameStartY, frameEndX, frameEndY, frameStartX, frameEndY }; glVertexPointer(2, GL_FLOAT, 0, frameRect); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_BLEND); } void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) { glEnable(GL_BLEND); // Allow us to draw on top of the animation glBindTexture(GL_TEXTURE_2D, font.texture.name); Loading Loading @@ -867,23 +910,34 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { int height = 0; int count = 0; int pause = 0; int framesToFadeCount = 0; char path[ANIM_ENTRY_NAME_MAX]; char color[7] = "000000"; // default to black if unspecified char clockPos1[TEXT_POS_LEN_MAX + 1] = ""; char clockPos2[TEXT_POS_LEN_MAX + 1] = ""; char pathType; int nextReadPos; if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { // SLOGD("> w=%d, h=%d, fps=%d", width, height, fps); animation.width = width; animation.height = height; animation.fps = fps; } else if (sscanf(l, " %c %d %d %" STRTO(ANIM_PATH_MAX) "s #%6s %16s %16s", &pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) { //SLOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s", // pathType, count, pause, path, color, clockPos1, clockPos2); } else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n", &pathType, &count, &pause, path, &nextReadPos) >= 4) { if (pathType == 'f') { sscanf(l + nextReadPos, " %d #%6s %16s %16s", &framesToFadeCount, color, clockPos1, clockPos2); } else { sscanf(l + nextReadPos, " #%6s %16s %16s", color, clockPos1, clockPos2); } // SLOGD("> type=%c, count=%d, pause=%d, path=%s, framesToFadeCount=%d, color=%s, " // "clockPos1=%s, clockPos2=%s", // pathType, count, pause, path, framesToFadeCount, color, clockPos1, clockPos2); Animation::Part part; part.playUntilComplete = pathType == 'c'; part.framesToFadeCount = framesToFadeCount; part.count = count; part.pause = pause; part.path = path; Loading @@ -902,6 +956,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { // SLOGD("> SYSTEM"); Animation::Part part; part.playUntilComplete = false; part.framesToFadeCount = 0; part.count = 1; part.pause = 0; part.audioData = nullptr; Loading Loading @@ -1098,12 +1153,19 @@ bool BootAnimation::movie() { return false; } bool BootAnimation::shouldStopPlayingPart(const Animation::Part& part, const int fadedFramesCount) { // stop playing only if it is time to exit and it's a partial part which has been faded out return exitPending() && !part.playUntilComplete && fadedFramesCount >= part.framesToFadeCount; } bool BootAnimation::playAnimation(const Animation& animation) { const size_t pcount = animation.parts.size(); nsecs_t frameDuration = s2ns(1) / animation.fps; SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime()); int fadedFramesCount = 0; for (size_t i=0 ; i<pcount ; i++) { const Animation::Part& part(animation.parts[i]); const size_t fcount = part.frames.size(); Loading @@ -1117,10 +1179,9 @@ bool BootAnimation::playAnimation(const Animation& animation) { 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) break; // process the part not only while the count allows but also if already fading for (int r=0 ; !part.count || r<part.count || fadedFramesCount > 0 ; r++) { if (shouldStopPlayingPart(part, fadedFramesCount)) break; mCallbacks->playPart(i, part, r); Loading @@ -1130,7 +1191,9 @@ bool BootAnimation::playAnimation(const Animation& animation) { part.backgroundColor[2], 1.0f); for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) { for (size_t j=0 ; j<fcount ; j++) { if (shouldStopPlayingPart(part, fadedFramesCount)) break; processDisplayEvents(); const int animationX = (mWidth - animation.width) / 2; Loading Loading @@ -1169,11 +1232,22 @@ bool BootAnimation::playAnimation(const Animation& animation) { } // specify the y center as ceiling((mHeight - frame.trimHeight) / 2) // which is equivalent to mHeight - (yc + frame.trimHeight) glDrawTexiOES(xc, mHeight - (yc + frame.trimHeight), 0, frame.trimWidth, frame.trimHeight); const int frameDrawY = mHeight - (yc + frame.trimHeight); glDrawTexiOES(xc, frameDrawY, 0, frame.trimWidth, frame.trimHeight); // if the part hasn't been stopped yet then continue fading if necessary if (exitPending() && part.hasFadingPhase()) { fadeFrame(xc, frameDrawY, frame.trimWidth, frame.trimHeight, part, ++fadedFramesCount); if (fadedFramesCount >= part.framesToFadeCount) { fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading } } if (mClockEnabled && mTimeIsAccurate && validClock(part)) { drawClock(animation.clockFont, part.clockPosX, part.clockPosY); } handleViewport(frameDuration); eglSwapBuffers(mDisplay, mSurface); Loading @@ -1198,11 +1272,11 @@ bool BootAnimation::playAnimation(const Animation& animation) { usleep(part.pause * ns2us(frameDuration)); // For infinite parts, we've now played them at least once, so perhaps exit if(exitPending() && !part.count && mCurrentInset >= mTargetInset) break; if (exitPending() && !part.count && mCurrentInset >= mTargetInset && !part.hasFadingPhase()) { break; // exit the infinite non-fading part when it has been played at least once } } } // Free textures created for looping parts now that the animation is done. Loading
cmds/bootanimation/BootAnimation.h +12 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <vector> #include <queue> #include <climits> #include <stdint.h> #include <sys/types.h> Loading @@ -45,6 +46,8 @@ class SurfaceControl; class BootAnimation : public Thread, public IBinder::DeathRecipient { public: static constexpr int MAX_FADED_FRAMES_COUNT = std::numeric_limits<int>::max(); struct Texture { GLint w; GLint h; Loading Loading @@ -84,10 +87,15 @@ public: String8 trimData; SortedVector<Frame> frames; bool playUntilComplete; int framesToFadeCount; float backgroundColor[3]; uint8_t* audioData; int audioLength; Animation* animation; bool hasFadingPhase() const { return !playUntilComplete && framesToFadeCount > 0; } }; int fps; int width; Loading Loading @@ -160,6 +168,8 @@ private: bool movie(); void drawText(const char* str, const Font& font, bool bold, int* x, int* y); void drawClock(const Font& font, const int xPos, const int yPos); void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight, const Animation::Part& part, int fadedFramesCount); bool validClock(const Animation::Part& part); Animation* loadAnimation(const String8&); bool playAnimation(const Animation&); Loading @@ -172,7 +182,9 @@ private: EGLConfig getEglConfig(const EGLDisplay&); ui::Size limitSurfaceSize(int width, int height) const; void resizeSurface(int newWidth, int newHeight); void projectSceneToWindow(); bool shouldStopPlayingPart(const Animation::Part& part, int fadedFramesCount); void checkExit(); void handleViewport(nsecs_t timestep); Loading
cmds/bootanimation/FORMAT.md +7 −1 Original line number Diff line number Diff line Loading @@ -30,14 +30,20 @@ The first line defines the general parameters of the animation: It is followed by a number of rows of the form: TYPE COUNT PAUSE PATH [#RGBHEX [CLOCK1 [CLOCK2]]] TYPE COUNT PAUSE PATH [FADE [#RGBHEX [CLOCK1 [CLOCK2]]]] * **TYPE:** a single char indicating what type of animation segment this is: + `p` -- this part will play unless interrupted by the end of the boot + `c` -- this part will play to completion, no matter what + `f` -- same as `p` but in addition the specified number of frames is being faded out while continue playing. Only the first interrupted `f` part is faded out, other subsequent `f` parts are skipped * **COUNT:** how many times to play the animation, or 0 to loop forever until boot is complete * **PAUSE:** number of FRAMES to delay after this part ends * **PATH:** directory in which to find the frames for this part (e.g. `part0`) * **FADE:** _(ONLY FOR `f` TYPE)_ number of frames to fade out when interrupted where `0` means _immediately_ which makes `f ... 0` behave like `p` and doesn't count it as a fading part * **RGBHEX:** _(OPTIONAL)_ a background color, specified as `#RRGGBB` * **CLOCK1, CLOCK2:** _(OPTIONAL)_ the coordinates at which to draw the current time (for watches): + If only `CLOCK1` is provided it is the y-coordinate of the clock and the x-coordinate Loading