现在的位置: 首页 > 综合 > 正文

android开机logo分析和优化

2013年09月20日 ⁄ 综合 ⁄ 共 3555字 ⁄ 字号 评论关闭

    通常为了提高用户体验,在系统启动阶段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 name
	frameBuffer: 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_image

	return 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显示方式了。

抱歉!评论已关闭.