Merge "Support animation parts fading out" am: 21819ef3b6 am: 648a4c2ea4 am: 858986161e
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1466262 Change-Id: I12887abcae65f27d5a8c8b19acbbf63ea4833ff0
This commit is contained in:
@@ -459,6 +459,8 @@ status_t BootAnimation::readyToRun() {
|
|||||||
mFlingerSurface = s;
|
mFlingerSurface = s;
|
||||||
mTargetInset = -1;
|
mTargetInset = -1;
|
||||||
|
|
||||||
|
projectSceneToWindow();
|
||||||
|
|
||||||
// Register a display event receiver
|
// Register a display event receiver
|
||||||
mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>();
|
mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>();
|
||||||
status_t status = mDisplayEventReceiver->initCheck();
|
status_t status = mDisplayEventReceiver->initCheck();
|
||||||
@@ -470,6 +472,16 @@ status_t BootAnimation::readyToRun() {
|
|||||||
return NO_ERROR;
|
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) {
|
void BootAnimation::resizeSurface(int newWidth, int newHeight) {
|
||||||
// We assume this function is called on the animation thread.
|
// We assume this function is called on the animation thread.
|
||||||
if (newWidth == mWidth && newHeight == mHeight) {
|
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());
|
SLOGE("Can't make the new surface current. Error %d", eglGetError());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
glViewport(0, 0, mWidth, mHeight);
|
|
||||||
glScissor(0, 0, mWidth, mHeight);
|
projectSceneToWindow();
|
||||||
|
|
||||||
mSurface = surface;
|
mSurface = surface;
|
||||||
}
|
}
|
||||||
@@ -776,6 +788,37 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) {
|
|||||||
return status;
|
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) {
|
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
|
glEnable(GL_BLEND); // Allow us to draw on top of the animation
|
||||||
glBindTexture(GL_TEXTURE_2D, font.texture.name);
|
glBindTexture(GL_TEXTURE_2D, font.texture.name);
|
||||||
@@ -867,23 +910,34 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) {
|
|||||||
int height = 0;
|
int height = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int pause = 0;
|
int pause = 0;
|
||||||
|
int framesToFadeCount = 0;
|
||||||
char path[ANIM_ENTRY_NAME_MAX];
|
char path[ANIM_ENTRY_NAME_MAX];
|
||||||
char color[7] = "000000"; // default to black if unspecified
|
char color[7] = "000000"; // default to black if unspecified
|
||||||
char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
|
char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
|
||||||
char clockPos2[TEXT_POS_LEN_MAX + 1] = "";
|
char clockPos2[TEXT_POS_LEN_MAX + 1] = "";
|
||||||
|
|
||||||
char pathType;
|
char pathType;
|
||||||
|
|
||||||
|
int nextReadPos;
|
||||||
|
|
||||||
if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
|
if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
|
||||||
// SLOGD("> w=%d, h=%d, fps=%d", width, height, fps);
|
// SLOGD("> w=%d, h=%d, fps=%d", width, height, fps);
|
||||||
animation.width = width;
|
animation.width = width;
|
||||||
animation.height = height;
|
animation.height = height;
|
||||||
animation.fps = fps;
|
animation.fps = fps;
|
||||||
} else if (sscanf(l, " %c %d %d %" STRTO(ANIM_PATH_MAX) "s #%6s %16s %16s",
|
} else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n",
|
||||||
&pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) {
|
&pathType, &count, &pause, path, &nextReadPos) >= 4) {
|
||||||
//SLOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s",
|
if (pathType == 'f') {
|
||||||
// pathType, count, pause, path, color, clockPos1, clockPos2);
|
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;
|
Animation::Part part;
|
||||||
part.playUntilComplete = pathType == 'c';
|
part.playUntilComplete = pathType == 'c';
|
||||||
|
part.framesToFadeCount = framesToFadeCount;
|
||||||
part.count = count;
|
part.count = count;
|
||||||
part.pause = pause;
|
part.pause = pause;
|
||||||
part.path = path;
|
part.path = path;
|
||||||
@@ -902,6 +956,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) {
|
|||||||
// SLOGD("> SYSTEM");
|
// SLOGD("> SYSTEM");
|
||||||
Animation::Part part;
|
Animation::Part part;
|
||||||
part.playUntilComplete = false;
|
part.playUntilComplete = false;
|
||||||
|
part.framesToFadeCount = 0;
|
||||||
part.count = 1;
|
part.count = 1;
|
||||||
part.pause = 0;
|
part.pause = 0;
|
||||||
part.audioData = nullptr;
|
part.audioData = nullptr;
|
||||||
@@ -1098,12 +1153,19 @@ bool BootAnimation::movie() {
|
|||||||
return false;
|
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) {
|
bool BootAnimation::playAnimation(const Animation& animation) {
|
||||||
const size_t pcount = animation.parts.size();
|
const size_t pcount = animation.parts.size();
|
||||||
nsecs_t frameDuration = s2ns(1) / animation.fps;
|
nsecs_t frameDuration = s2ns(1) / animation.fps;
|
||||||
|
|
||||||
SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
|
SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
|
||||||
elapsedRealtime());
|
elapsedRealtime());
|
||||||
|
|
||||||
|
int fadedFramesCount = 0;
|
||||||
for (size_t i=0 ; i<pcount ; i++) {
|
for (size_t i=0 ; i<pcount ; i++) {
|
||||||
const Animation::Part& part(animation.parts[i]);
|
const Animation::Part& part(animation.parts[i]);
|
||||||
const size_t fcount = part.frames.size();
|
const size_t fcount = part.frames.size();
|
||||||
@@ -1117,10 +1179,9 @@ bool BootAnimation::playAnimation(const Animation& animation) {
|
|||||||
continue; //to next part
|
continue; //to next part
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int r=0 ; !part.count || r<part.count ; r++) {
|
// process the part not only while the count allows but also if already fading
|
||||||
// Exit any non playuntil complete parts immediately
|
for (int r=0 ; !part.count || r<part.count || fadedFramesCount > 0 ; r++) {
|
||||||
if(exitPending() && !part.playUntilComplete)
|
if (shouldStopPlayingPart(part, fadedFramesCount)) break;
|
||||||
break;
|
|
||||||
|
|
||||||
mCallbacks->playPart(i, part, r);
|
mCallbacks->playPart(i, part, r);
|
||||||
|
|
||||||
@@ -1130,7 +1191,9 @@ bool BootAnimation::playAnimation(const Animation& animation) {
|
|||||||
part.backgroundColor[2],
|
part.backgroundColor[2],
|
||||||
1.0f);
|
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();
|
processDisplayEvents();
|
||||||
|
|
||||||
const int animationX = (mWidth - animation.width) / 2;
|
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)
|
// specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
|
||||||
// which is equivalent to mHeight - (yc + frame.trimHeight)
|
// which is equivalent to mHeight - (yc + frame.trimHeight)
|
||||||
glDrawTexiOES(xc, mHeight - (yc + frame.trimHeight),
|
const int frameDrawY = mHeight - (yc + frame.trimHeight);
|
||||||
0, frame.trimWidth, 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)) {
|
if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
|
||||||
drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
|
drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleViewport(frameDuration);
|
handleViewport(frameDuration);
|
||||||
|
|
||||||
eglSwapBuffers(mDisplay, mSurface);
|
eglSwapBuffers(mDisplay, mSurface);
|
||||||
@@ -1198,11 +1272,11 @@ bool BootAnimation::playAnimation(const Animation& animation) {
|
|||||||
|
|
||||||
usleep(part.pause * ns2us(frameDuration));
|
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 &&
|
||||||
if(exitPending() && !part.count && mCurrentInset >= mTargetInset)
|
!part.hasFadingPhase()) {
|
||||||
break;
|
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.
|
// Free textures created for looping parts now that the animation is done.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@@ -45,6 +46,8 @@ class SurfaceControl;
|
|||||||
class BootAnimation : public Thread, public IBinder::DeathRecipient
|
class BootAnimation : public Thread, public IBinder::DeathRecipient
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static constexpr int MAX_FADED_FRAMES_COUNT = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
struct Texture {
|
struct Texture {
|
||||||
GLint w;
|
GLint w;
|
||||||
GLint h;
|
GLint h;
|
||||||
@@ -84,10 +87,15 @@ public:
|
|||||||
String8 trimData;
|
String8 trimData;
|
||||||
SortedVector<Frame> frames;
|
SortedVector<Frame> frames;
|
||||||
bool playUntilComplete;
|
bool playUntilComplete;
|
||||||
|
int framesToFadeCount;
|
||||||
float backgroundColor[3];
|
float backgroundColor[3];
|
||||||
uint8_t* audioData;
|
uint8_t* audioData;
|
||||||
int audioLength;
|
int audioLength;
|
||||||
Animation* animation;
|
Animation* animation;
|
||||||
|
|
||||||
|
bool hasFadingPhase() const {
|
||||||
|
return !playUntilComplete && framesToFadeCount > 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
int fps;
|
int fps;
|
||||||
int width;
|
int width;
|
||||||
@@ -160,6 +168,8 @@ private:
|
|||||||
bool movie();
|
bool movie();
|
||||||
void drawText(const char* str, const Font& font, bool bold, int* x, int* y);
|
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 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);
|
bool validClock(const Animation::Part& part);
|
||||||
Animation* loadAnimation(const String8&);
|
Animation* loadAnimation(const String8&);
|
||||||
bool playAnimation(const Animation&);
|
bool playAnimation(const Animation&);
|
||||||
@@ -172,7 +182,9 @@ private:
|
|||||||
EGLConfig getEglConfig(const EGLDisplay&);
|
EGLConfig getEglConfig(const EGLDisplay&);
|
||||||
ui::Size limitSurfaceSize(int width, int height) const;
|
ui::Size limitSurfaceSize(int width, int height) const;
|
||||||
void resizeSurface(int newWidth, int newHeight);
|
void resizeSurface(int newWidth, int newHeight);
|
||||||
|
void projectSceneToWindow();
|
||||||
|
|
||||||
|
bool shouldStopPlayingPart(const Animation::Part& part, int fadedFramesCount);
|
||||||
void checkExit();
|
void checkExit();
|
||||||
|
|
||||||
void handleViewport(nsecs_t timestep);
|
void handleViewport(nsecs_t timestep);
|
||||||
|
|||||||
@@ -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:
|
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:
|
* **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
|
+ `p` -- this part will play unless interrupted by the end of the boot
|
||||||
+ `c` -- this part will play to completion, no matter what
|
+ `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
|
* **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
|
* **PAUSE:** number of FRAMES to delay after this part ends
|
||||||
* **PATH:** directory in which to find the frames for this part (e.g. `part0`)
|
* **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`
|
* **RGBHEX:** _(OPTIONAL)_ a background color, specified as `#RRGGBB`
|
||||||
* **CLOCK1, CLOCK2:** _(OPTIONAL)_ the coordinates at which to draw the current time (for watches):
|
* **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
|
+ If only `CLOCK1` is provided it is the y-coordinate of the clock and the x-coordinate
|
||||||
|
|||||||
Reference in New Issue
Block a user