OpenGL—Android 开机动画源码分析一

时间:2024-02-29 19:00:02

.1 Android开机动画实现方式目前实现Android开机动画的方式主要是逐帧动画和OpenGL动画。

?逐帧动画

逐帧动画是一种常见的动画形式(Frame By Frame),其原理是在“连续的关键帧”中分解动画动作,也就是在时间轴的每帧上逐帧绘制不同的内容,使其连续播放而成动画。 因为逐帧动画的帧序列内容不一样,不但给制作增加了负担而且最终输出的文件量也很大,但它的优势也很明显:逐帧动画具有非常大的灵活性,几乎可以表现任何想表现的内容,而它类似与电影的播放模式,很适合于表演细腻的动画。
逐帧动画是网上广泛流传的一种实现方法。实现原理是将一系列图片打包成bootanimation.zip放入/system/media/目录,系统将图片一帧一帧循环播放形成一个动画效果。理论上讲这种方法应该是可以实现一切动画需求的,但是实践后你会发现当bootanimation.zip大于5M的时候,动画将有明显卡顿,文件越大动画越不流畅。所以细心的同学会发现手机上的开机动画大多都是只有中间一小部分在变化,而四周全是黑色,这样做是为了使得可以采用100*50(甚至更小)分辨率的图片,这样100帧也才几M的大小。

?OpenGL动画

OpenGL(英语:Open Graphics Library)是个定义了一个跨编程语言、跨平台的应用程序接口(API)的规范,它用于生成二维、三维图像。这个接口由近三百五十个不同的函数调用组成,用来从简单的图形比特绘制复杂的三维景象。

比较两种方式我们不难发现,电视平台上和手机有所不同,特别是开机广告,1920*1080分辨率的图片一张就几百KB,由于大小的限制导致动画帧数很少,所以电视平台采用“逐帧动画”方法无法做出复杂而流畅的动画,本文将主要讨论OpenGL的实现方式。

1.2 原生开机动画的源码分析

在制作自己的开机动画之前,我们先分析一下Android原生的开机动画源码。Android系统的开机动画源码位于framework/base/cmds/bootanimation(表1)。

Bootanimation程序目录:android-x.x/framework/base/cmds/bootanimation/

Android.mk

Android编译定义

bootanimation_main.cpp

入口文件

BootAnimation.h

BootAnimation类的声明

BootAnimation.cpp

BootAnimation类的定义和实现


先来看一下bootanimation_main.cpp,这个文件定义了main函数,代码如下:

[bootanimation_main.cpp]

int main(int argc, char** argv)
{
……
sp proc(ProcessState::self());
ProcessState::self()->startThreadPool();

// create the boot animation object
sp boot = new BootAnimation();

IPCThreadState::self()->joinThreadPool();
……
}


Main函数的代码很简单,首先启动Process的线程池,然后创建一个BootAnimation对象,最后将BootAnimation对象加入到刚才的线程池中, startThreadPool()和joinThreadPool()的用法可以参考android的binder机制。

下面就一起来看一下BootAnimation类:

[BootAnimation.h]

class BootAnimation : public Thread, public IBinder::DeathRecipient
{
……
private:
virtual bool threadLoop();
virtual status_t readyToRun();
virtual void onFirstRef();
virtual void binderDied(const wp& who);
……
status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
status_t initTexture(void* buffer, size_t len);
bool android();
bool movie();

void checkExit();
……
}

 

BootAnimation类继承了Thread类和IBinder::DeathRecipient类,几个override函数的作用如下:
?onFirstRef()属于其父类RefBase,该函数在强引用sp新增引用计数時调用,就是当 有sp包装的类初始化的时候调用。
?binderDied(),当对象死掉时或者其他情况导致该Binder发生结束了,就会回调binderDied()方法;
?readyToRun()定义Thread执行前的初始化工作;
?threadLoop()是每个线程类都要实现的,在这里定义thread的执行内容,这个函数如果返回true,则函数会不停地执行threadloop中的内容,如果这个函数返回false,则threadloop中的内容仅仅执行一次线程就会退出;

[BootAnimation::onFirstRef()]
onFirstRef函数中执行了run BootAnimation线程。

void BootAnimation::onFirstRef() 
{
status_t err = mSession->linkToComposerDeath(this);
ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
if (err == NO_ERROR) {
run("BootAnimation", PRIORITY_DISPLAY);
}
}

void BootAnimation::binderDied(const wp& who)
{
kill( getpid(), SIGKILL );
requestExit();
}


[BootAnimation::readyToRun()]

status_t BootAnimation::readyToRun() {
......
// create the native surface
......
// initialize opengl and egl
......
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
......
surface = eglCreateWindowSurface(display, config, s.get(), NULL);
......
mDisplay = display;
......
mSurface = surface;
......
}

 

readyToRun函数主要是为了得到EGLDisplay mDisplay和EGLDisplay mSurface对象,这两个对象后面再介绍,最后函数还会查询是否存在bootanimation.zip并初始化mAndroidAnimation的值。

#define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip"
#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
......
bool BootAnimation::threadLoop()
{
bool r;
if (mAndroidAnimation) {
r = android();
} else {
r = movie();
}
......
}


[BootAnimation::threadLoop()]
BootAnimation::readyToRun中会检查是否存在bootanimation.zip,如果存在bootanimation.zip这里就执行movie(),否则执行android()。movie()为逐帧动画的实现,android()为opengl动画的实现,本文只考虑无bootanimation.zip的情况。

[BootAnimation::android()]

bool BootAnimation::android()
{
initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
......
do {
......
glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);

EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
......
checkExit();
} while (!exitPending());
......
}

 

终于到了绘图的步骤了,BootAnimation::android函数包含了整个opengl绘图的过程,因为android使用的是标准opengl es api,所以opengl的初始化和绘图过程这里就不详细介绍了,

这里需要注意的是eglSwapBuffers(mDisplay, mSurface),这个方法将mSruface投递到屏幕,这里用到了两个EGLDisplay对象,是由于使用了双缓冲机制,配合sleep实现稳定的刷新率。

最后当检测到系统初始化完成时退出程序。