BootAnimation类的成员函数movie的实现比较长,我们分段来阅读:

1. bool BootAnimation::movie()   
2. {   
3.     ZipFileRO& zip(mZip);   
4.    
5.     size_t numEntries = zip.getNumEntries();   
6. desc = zip.findEntryByName("desc.txt");   
7. desc);   
8. "descMap is null");   
9.     if (!descMap) {   
10. return false;   
11.     }   
12.    
13. char
14.             descMap->getDataLength());   
15. char
16.    
17.     Animation animation;   
18.    
19.     // Parse the description file   
20. for
21. char* endl = strstr(s, "\n");   
22.         if (!endl) break;   
23.         String8 line(s, endl - s);   
24. char* l = line.string();   
25. int fps, width, height, count, pause;   
26. char
27. "%d %d %d", &width, &height, &fps) == 3) {   
28. "> w=%d, h=%d, fps=%d", fps, width, height);   
29.             animation.width = width;   
30.             animation.height = height;   
31.             animation.fps = fps;   
32.         }   
33. "p %d %d %s", &count, &pause, path) == 3) {   
34. "> count=%d, pause=%d, path=%s", count, pause, path);   
35.             Animation::Part part;   
36. count = count;   
37.             part.pause = pause;   
38.             part.path = path;   
39. add(part);   
40.         }   
41.         s = ++endl;   
42.     }

 

     从前面BootAnimation类的成员函数readyToRun的实现可以知道,如果目标设备上存在压缩文件/data/local/bootanimation.zip,那么BootAnimation类的成员变量mZip就会指向它,否则的话,就会指向目标设备上的压缩文件/system/media/bootanimation.zip。无论BootAnimation类的成员变量mZip指向的是哪一个压缩文件,这个压缩文件都必须包含有一个名称为“desc.txt”的文件,用来描述用户自定义的开机动画是如何显示的。

 

文件desc.txt的内容格式如下面的例子所示:

1. 600 480 24   
2. p   1   0   part1   
3. p   0   10  part2

        第一行的三个数字分别表示开机动画在屏幕中的显示宽度、高度以及帧速(fps)。剩余的每一行都用来描述一个动画片断,这些行必须要以字符“p”来开头,后面紧跟着两个数字以及一个文件目录路径名称。第一个数字表示一个片断的循环显示次数,如果它的值等于0,那么就表示无限循环地显示该动画片断。第二个数字表示每一个片断在两次循环显示之间的时间间隔。这个时间间隔是以一个帧的时间为单位的。文件目录下面保存的是一系列png文件,这些png文件会被依次显示在屏幕中。

 

       以上面这个desct.txt文件的内容为例,它描述了一个大小为600 x 480的开机动画,动画的显示速度为24帧每秒。这个开机动画包含有两个片断part1和part2。片断part1只显示一次,它对应的png图片保存在目录part1中。片断part2无限循环地显示,其中,每两次循环显示的时间间隔为10 x (1 / 24)秒,它对应的png图片保存在目录part2中。

       上面的for循环语句分析完成desc.txt文件的内容后,就得到了开机动画的显示大小、速度以及片断信息。这些信息都保存在Animation对象animation中,其中,每一个动画片断都使用一个Animation::Part对象来描述,并且保存在Animation对象animation的成员变量parts所描述的一个片断列表中。

       接下来,BootAnimation类的成员函数movie再断续将每一个片断所对应的png图片读取出来,如下所示:

1. // read all
2. const size_t pcount = animation.parts.size();   
3. for
4. char name[256];   
5.     ZipEntryRO entry = zip.findEntryByIndex(i);   
6. name, 256) == 0) {   
7. name);   
8.         const String8 path(entryName.getPathDir());   
9.         const String8 leaf(entryName.getPathLeaf());   
10. size() > 0) {   
11. for (int
12.                 if (path == animation.parts[j].path) {   
13. int
14. only
15.                     if (zip.getEntryInfo(entry, &method, 0, 0, 0, 0, 0)) {   
16.                         if (method == ZipFileRO::kCompressStored) {   
17.                             FileMap* map = zip.createEntryFileMap(entry);   
18.                             if (map) {   
19.                                 Animation::Frame frame;   
20. name
21.                                 frame.map = map;   
22.                                 Animation::Part& part(animation.parts.editItemAt(j));   
23. add(frame);   
24.                             }   
25.                         }   
26.                     }   
27.                 }   
28.             }   
29.         }   
30.     }   
31. }

 

      每一个png图片都表示一个动画帧,使用一个Animation::Frame对象来描述,并且保存在对应的Animation::Part对象的成员变量frames所描述的一个帧列表中。

 

        获得了开机动画的所有信息之后,接下来BootAnimation类的成员函数movie就准备开始显示开机动画了,如下所示:

1. // clear screen   
2. glShadeModel(GL_FLAT);   
3. glDisable(GL_DITHER);   
4. glDisable(GL_SCISSOR_TEST);   
5. glDisable(GL_BLEND);   
6. glClear(GL_COLOR_BUFFER_BIT);   
7.    
8. eglSwapBuffers(mDisplay, mSurface);   
9.    
10. glBindTexture(GL_TEXTURE_2D, 0);   
11. glEnable(GL_TEXTURE_2D);   
12. glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);   
13. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   
14. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);   
15. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);   
16. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);   
17.    
18. const int
19. const int
20. nsecs_t lastFrame = systemTime();   
21. nsecs_t frameDuration = s2ns(1) / animation.fps;   
22.    
23. Region clearReg(Rect(mWidth, mHeight));   
24. clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height));

       前面的一系列gl函数首先用来清理屏幕,接下来的一系列gl函数用来设置OpenGL的纹理显示方式。

 

       变量xc和yc的值用来描述开机动画的显示位置,即需要在屏幕中间显示开机动画,另外一个变量frameDuration的值用来描述每一帧的显示时间,它是以纳秒为单位的。

       Region对象clearReg用来描述屏幕中除了开机动画之外的其它区域,它是用整个屏幕区域减去开机动画所点据的区域来得到的。

       准备好开机动画的显示参数之后,最后就可以执行显示开机动画的操作了,如下所示:

1. for (int
2.         const Animation::Part& part(animation.parts[i]);   
3. size();   
4.         glBindTexture(GL_TEXTURE_2D, 0);   
5.    
6. for (int r=0 ; !part.count || r<part.count
7. for (int
8.                 const Animation::Frame& frame(part.frames[j]);   
9.    
10.                 if (r > 0) {   
11.                     glBindTexture(GL_TEXTURE_2D, frame.tid);   
12. else
13. count
14.                         glGenTextures(1, &frame.tid);   
15.                         glBindTexture(GL_TEXTURE_2D, frame.tid);   
16.                         glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);   
17.                         glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);   
18.                     }   
19.                     initTexture(   
20.                             frame.map->getDataPtr(),   
21.                             frame.map->getDataLength());   
22.                 }   
23.    
24.                 if (!clearReg.isEmpty()) {   
25. begin());   
26. end());   
27.                     glEnable(GL_SCISSOR_TEST);   
28.                     while (head != tail) {   
29.                         const Rect& r(*head++);   
30. left, mHeight - r.bottom,   
31.                                 r.width(), r.height());   
32.                         glClear(GL_COLOR_BUFFER_BIT);   
33.                     }   
34.                     glDisable(GL_SCISSOR_TEST);   
35.                 }   
36.                 glDrawTexiOES(xc, yc, 0, animation.width, animation.height);   
37.                 eglSwapBuffers(mDisplay, mSurface);   
38.    
39.                 nsecs_t now = systemTime();   
40.                 nsecs_t delay = frameDuration - (now - lastFrame);   
41.                 lastFrame = now;   
42.                 long wait = ns2us(frameDuration);   
43.                 if (wait > 0)   
44.                     usleep(wait);   
45.             }   
46.             usleep(part.pause * ns2us(frameDuration));   
47.         }   
48.    
49. free the textures for
50. count
51. for (int
52.                 const Animation::Frame& frame(part.frames[j]);   
53.                 glDeleteTextures(1, &frame.tid);   
54.             }   
55.         }   
56.     }   
57.    
58. return false;   
59. }

        第一层for循环用来显示每一个动画片断,第二层的for循环用来循环显示每一个动画片断,第三层的for循环用来显示每一个动画片断所对应的png图片。这些png图片以纹理的方式来显示在屏幕中。

 

        注意,如果一个动画片断的循环显示次数不等于1,那么就说明这个动画片断中的png图片需要重复地显示在屏幕中。由于每一个png图片都需要转换为一个纹理对象之后才能显示在屏幕中,因此,为了避免重复地为同一个png图片创建纹理对象,第三层的for循环在第一次显示一个png图片的时候,会调用函数glGenTextures来为这个png图片创建一个纹理对象,并且将这个纹理对象的名称保存在对应的Animation::Frame对象的成员变量tid中,这样,下次再显示相同的图片时,就可以使用前面已经创建好了的纹理对象,即调用函数glBindTexture来指定当前要操作的纹理对象。

        如果Region对象clearReg所包含的区域不为空,那么在调用函数glDrawTexiOES和eglSwapBuffers来显示每一个png图片之前,首先要将它所包含的区域裁剪掉,避免开机动画可以显示在指定的位置以及大小中。

        每当显示完成一个png图片之后,都要将变量frameDuration的值从纳秒转换为毫秒。如果转换后的值大小于,那么就需要调用函数usleep函数来让线程睡眠一下,以保证每一个png图片,即每一帧动画都按照预先指定好的速度来显示。注意,函数usleep指定的睡眠时间只能精确到毫秒,因此,如果预先指定的帧显示时间小于1毫秒,那么BootAnimation类的成员函数movie是无法精确地控制地每一帧的显示时间的。

        还有另外一个地方需要注意的是,每当循环显示完成一个片断时,需要调用usleep函数来使得线程睡眠part.pause * ns2us(frameDuration)毫秒,以便可以按照预先设定的节奏来显示开机动画。

        最后一个if语句判断一个动画片断是否是循环显示的,即循环次数不等于1。如果是的话,那么就说明前面为它所对应的每一个png图片都创建过一个纹理对象。现在既然这个片断的显示过程已经结束了,因此,就需要释放前面为它所创建的纹理对象。

        至此,第三个开机画面的显示过程就分析完成了。