Android系統的開機畫面顯示過程分析(11)

  BootAnimation類的成員函數movie的實現比較長,咱們分段來閱讀:
  
  
  
  
  1. bool BootAnimation::movie()   
  2. {   
  3.     ZipFileRO& zip(mZip);   
  4.    
  5.     size_t numEntries = zip.getNumEntries();   
  6.     ZipEntryRO desc = zip.findEntryByName("desc.txt");   
  7.     FileMap* descMap = zip.createEntryFileMap(desc);   
  8.     LOGE_IF(!descMap, "descMap is null");   
  9.     if (!descMap) {   
  10.         return false;   
  11.     }   
  12.    
  13.     String8 desString((char const*)descMap->getDataPtr(),   
  14.             descMap->getDataLength());   
  15.     char const* s = desString.string();   
  16.    
  17.     Animation animation;   
  18.    
  19.     // Parse the description file   
  20.     for (;;) {   
  21.         const char* endl = strstr(s, "\n");   
  22.         if (!endl) break;   
  23.         String8 line(s, endl - s);   
  24.         const char* l = line.string();   
  25.         int fps, width, height, count, pause;   
  26.         char path[256];   
  27.         if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {   
  28.             //LOGD("> w=%d, h=%d, fps=%d", fps, width, height);   
  29.             animation.width = width;   
  30.             animation.height = height;   
  31.             animation.fps = fps;   
  32.         }   
  33.         if (sscanf(l, "p %d %d %s", &count, &pause, path) == 3) {   
  34.             //LOGD("> count=%d, pause=%d, path=%s"count, pause, path);   
  35.             Animation::Part part;   
  36.             part.count = count;   
  37.             part.pause = pause;   
  38.             part.path = path;   
  39.             animation.parts.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 the data structures   
  2. const size_t pcount = animation.parts.size();   
  3. for (size_t i=0 ; i<numEntries ; i++) {   
  4.     char name[256];   
  5.     ZipEntryRO entry = zip.findEntryByIndex(i);   
  6.     if (zip.getEntryFileName(entry, name, 256) == 0) {   
  7.         const String8 entryName(name);   
  8.         const String8 path(entryName.getPathDir());   
  9.         const String8 leaf(entryName.getPathLeaf());   
  10.         if (leaf.size() > 0) {   
  11.             for (int j=0 ; j<pcount ; j++) {   
  12.                 if (path == animation.parts[j].path) {   
  13.                     int method;   
  14.                     // supports only stored png files   
  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.                                 frame.name = leaf;   
  21.                                 frame.map = map;   
  22.                                 Animation::Part& part(animation.parts.editItemAt(j));   
  23.                                 part.frames.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 xc = (mWidth - animation.width) / 2;   
  19. const int yc = ((mHeight - animation.height) / 2);   
  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 i=0 ; i<pcount && !exitPending() ; i++) {   
  2.         const Animation::Part& part(animation.parts[i]);   
  3.         const size_t fcount = part.frames.size();   
  4.         glBindTexture(GL_TEXTURE_2D, 0);   
  5.    
  6.         for (int r=0 ; !part.count || r<part.count ; r++) {   
  7.             for (int j=0 ; j<fcount && !exitPending(); j++) {   
  8.                 const Animation::Frame& frame(part.frames[j]);   
  9.    
  10.                 if (r > 0) {   
  11.                     glBindTexture(GL_TEXTURE_2D, frame.tid);   
  12.                 } else {   
  13.                     if (part.count != 1) {   
  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.                     Region::const_iterator head(clearReg.begin());   
  26.                     Region::const_iterator tail(clearReg.end());   
  27.                     glEnable(GL_SCISSOR_TEST);   
  28.                     while (head != tail) {   
  29.                         const Rect& r(*head++);   
  30.                         glScissor(r.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 this part   
  50.         if (part.count != 1) {   
  51.             for (int j=0 ; j<fcount ; j++) {   
  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圖片都建立過一個紋理對象。如今既然這個片段的顯示過程已經結束了,所以,就須要釋放前面爲它所建立的紋理對象。
        至此,第三個開機畫面的顯示過程就分析完成了。
相關文章
相關標籤/搜索