Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21819ef3 authored by Yegor Malyshev's avatar Yegor Malyshev Committed by Gerrit Code Review
Browse files

Merge "Support animation parts fading out"

parents 14a7d344 eafcb997
Loading
Loading
Loading
Loading
+92 −18
Original line number Diff line number Diff line
@@ -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();
@@ -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) {
@@ -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;
}
@@ -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);
@@ -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;
@@ -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;
@@ -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();
@@ -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);

@@ -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;
@@ -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);
@@ -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.
+12 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@

#include <vector>
#include <queue>
#include <climits>

#include <stdint.h>
#include <sys/types.h>
@@ -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;
@@ -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;
@@ -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&);
@@ -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);
+7 −1
Original line number Diff line number Diff line
@@ -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