首先说在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; }
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; }
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; }
通过上面对代码和格式的分析,我们就可以根据自己产品的需要,选择合适的logo显示方式了。