android開機logo分析和最佳化

來源:互聯網
上載者:User

    通常為了提高使用者體驗,在系統啟動階段lcd初始化完畢後,會快速顯示第一屏畫面,避免黑屏時間過長而導致使用者從感官上認為系統運行速度慢,所有電子產品都會這樣設計。而這個畫面經常被用來顯示產品logo。在android中,有兩處可以做logo的列印。第一處是在kernel中,顯示裝置初始化完畢後,會將一個ppm圖片複製到fb中;第二處是在android boot階段,將一個rle格式的圖片解壓縮到fb中。

    首先說在kernel中做的logo顯示。kernel顯示的logo使用ppm格式圖片。ppm是一種簡單的linux圖片格式,僅包含格式、映像寬高、bit數等資訊和映像資料。映像資料的儲存格式可以用ASCII碼,也可用二進位。下面列舉ppm格式中比較簡單的一種(24位彩色、二進位儲存的映像):  

檔案頭部分——
P6\n
width height\n   
255\n   

像素部分——
rgbrgb...   
其中P6表示ppm的這種格式;\n表示分行符號;width和height表示映像的寬高,用空格隔開;255表示每個顏色分量的最大值;rgb資料從上到下,從左至右排放。

    由ppm映像的格式可以看出,這是一種未經壓縮的映像格式。使用它做logo的優點是節省掉瞭解碼的時間,可以使畫面較快顯示,缺點是在一些會使鏡像體積過於龐大。如果沒有極端的需求,其實我們可以採取一些折中的方式來解決這些問題,比如,在編譯階段將映像壓縮後再打包到鏡像中,然後運行階段再解壓映像並顯示。壓縮演算法不是很複雜的時候,顯示畫面的速度應該還是可以接受的。比如,將圖片先轉化成raw資料格式,再壓縮成gz包,打包到root中,當fb初始化完成之後,直接將gz壓縮包解壓到顯示緩衝區中(使用gunzip介面)。解壓介面如下:

/*fileName: logo image file nameframeBuffer: display buffer*/int draw_logo(char* fileName, void* frameBuffer){int fd;char *input;struct stat st;fd =sys_open(fileName,O_RDONLY, 0);if (fd < 0){return -1;}sys_newfstat(fd, &st);len = st.st_size;input = vmalloc(st.st_size);if (!input) {return -1;}sys_read(fd, input, st.st_size);gunzip(input, st.st_size, NULL, NULL, frameBuffer, NULL, NULL);vfree(input);sys_close(fd);load_565rle_imagereturn 0;} 

    下面探討android的logo顯示。android的啟動始於system/core/init/init.c。啟動流程有很多文章寫的很好,不贅述了,這裡只說說logo顯示函數是如何被調用到的:

    main(位於system/core/init/init.c)--> console_init_action(位於system/core/init/init.c)-->load_565rle_image(位於system/core/init/logo.c)。

    load_565rle_image用於列印rle格式映像到fb中。先說說rle資料格式。rle是一種無損資料壓縮格式,壓縮時會計算出連續的資料內容和連續長度,用個簡單的例子說明:AAABBBCCC可以壓縮為3A3B3C,這樣原本長度為9Bytes的資料被壓縮到了6Bytes。使用這種壓縮方式,優點是可以既節約磁碟空間,又不使映像受損;缺點是如果遇到了非連續的資料區段,反而會增加資料大小。例如:ABCABCABC,壓縮後變成1A1B1C1A1B1C1A1B1C,這樣原本9Bytes的資料變成了18Bytes。瞭解了rle壓縮格式,再來看load_565rle_image的代碼就比較容易了:

/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */int load_565rle_image(char *fn){    struct FB fb;    struct stat s;    unsigned short *data, *bits, *ptr;    unsigned count, max;    int fd;    if (vt_set_mode(1))        return -1;    fd = open(fn, O_RDONLY);    //開啟rle影像檔    if (fd < 0) {        ERROR("cannot open '%s'\n", fn);        goto fail_restore_text;    }    if (fstat(fd, &s) < 0) {        goto fail_close_file;    }    data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0);  //將映像資料對應到記憶體,data指向該記憶體的首地址    if (data == MAP_FAILED)        goto fail_close_file;    if (fb_open(&fb))  //開啟fb裝置        goto fail_unmap_data;    max = fb_width(&fb) * fb_height(&fb);    ptr = data;    count = s.st_size;    bits = fb.bits;    while (count > 3) {        unsigned n = ptr[0];        if (n > max)            break;        android_memset16(bits, ptr[1], n << 1);  //將rle資料解碼到顯示緩衝區        bits += n;        max -= n;        ptr += 2;        count -= 4;    }    munmap(data, s.st_size);    fb_update(&fb);    fb_close(&fb);    close(fd);    unlink(fn);    return 0;fail_unmap_data:    munmap(data, s.st_size);fail_close_file:    close(fd);fail_restore_text:    vt_set_mode(0);    return -1;}

    load_565rle_image函數看起來沒有什麼問題,但是實際上是有隱患的。當kernel啟動後,fb的rgb設定為565時,顯示沒有問題;但是如果預設設定為其他格式時,就會顯示花屏。因此,修改fb_open函數,重新使用ioctl介面設定fb會比較保險。將fb_open修改如下:

static int fb_open(struct FB *fb){    fb->fd = open("/dev/graphics/fb0", O_RDWR);    if (fb->fd < 0)        return -1;    if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0)        goto fail;    if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0)        goto fail;    fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE,                    MAP_SHARED, fb->fd, 0);        /*      add by Lynn, reset fb format */        fb->vi.bits_per_pixel = 16;        fb->vi.yres_virtual=fb->vi.yres*2;        fb->vi.red.offset     = 11;        fb->vi.red.length     = 5;        fb->vi.green.offset   = 5;        fb->vi.green.length   = 6;        fb->vi.blue.offset    = 0;        fb->vi.blue.length    = 5;        fb->vi.transp.offset  = 0;        fb->vi.transp.length  = 0;        fb->vi.nonstd =4;        fb->vi.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;        if(ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi) < 0)        {                ERROR("Put screen info failed !!!");                goto fail;        }        /*      end add */    if (fb->bits == MAP_FAILED)        goto fail;    return 0;fail:    close(fb->fd);    return -1;}

    如上修改就可以保證無論kernel對fb設定如何,android中的logo都能正常顯示了。

    

    通過上面對代碼和格式的分析,我們就可以根據自己產品的需要,選擇合適的logo顯示方式了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

Tags Index: