android设备上使用bmp开机logo
在android系统中更换内核开机logo的方法已经提过,一般我们想定制自己的logo直接改内核开机图片就好. 但是假如放入内核的是超高清或者高分辨率图片,那么会导致内核的大小变得比较大,而内核的大小是有限制的,一般是8Mb,也有些嵌入式平台甚至是4M,所以内核的空间是珍贵的,如果图片这种数据在内核里占用了较多空间,就显得不太合理了.另外,linux内核中默认使用的是logo_linux_clut224.ppm这张图片,颜色深度只有224,低于8位的256,在大屏幕上会看到比较明显的失真.
解决方案:把图片数据(原始数据),放在boot镜像中,等内核启动时直接从这里读取数据并写入到framebuffer中.考虑到扩展性,可以在内核显示logo的时候加一个判断,优先检查根文件系统中是否存在logo的数据文件,如果有则显示boot里面的logo,如果没有就继续显示内核原有的logo.
基本实现过程:找到fb的驱动程序,内核中的路径一般是kernel/drivers/video/xxx/xxxfb.c,不同的平台这个文件的路径可能有些出入,但是基本是在kernel/drivers/video/下面.下面对这个文件进行修改,加入一个新的写fb的函数并调用它.
首先,在文件中增加一个函数fb_draw_bmp_logo():
第一步是包含必要的头文件进来,这里加进了三个头文件,其中syscalls.h是系统调用,我们要用它里面的接口进行文件操作;decompress/inflate.h这个头文件有解压缩用到的gunzip函数,没错,为了进一步节省空间,我们会对放在boot镜像中的图片数据进行文件级别的压缩,这并不会降低图片的质量;vmalloc.h是申请内存空间时用到,这里之所以不用kmalloc是考虑到图片的数据有可能会超过4M,超过了kmalloc的上限.
//leung modify
#include<linux/syscalls.h>
#include<linux/decompress/inflate.h>
#include <linux/vmalloc.h>
然后开始写函数的实现,这里的动作是用系统调用打开图片数据文件,获取图片的大小信息,然后根据图片大小申请内存,然后直接把数据解压到fb中,实现显示.
int fb_draw_bmp_logo(struct fb_info *fi){
int fd;
int len;
char *input;
struct stat st;
char logo_path[128];
sprintf(logo_path,"/%dx%d.gz",fi->var.xres,fi->var.yres);
printk("logo dir=%s\n",logo_path);
fd=sys_open(logo_path,O_RDONLY,0);
printk("logo open fd=%d\n",fd);
while(fd < 0){
return -1;
}
sys_newfstat(fd,&st);
len=st.st_size;
printk("%p %x\n",fi->screen_base,len);
input=vmalloc(len);
if(!input){
printk(KERN_ERR "Failed to vmalloc input.\n");
return -1;
}
sys_read(fd,input,len);
gunzip(input, len, NULL, NULL, fi->screen_base, NULL, NULL);
printk("logo open succeed!\n");
vfree(input);
sys_close(fd);
return 0;
}
//leung modify end
函数编写完毕之后,就找到原本的显示函数调用的地方,在那里调用自己的显示函数,比如说我找到了xx_fb_register(),在它里面找到了原本的显示函数fb_show_logo()的最终调用处;于是我作出如下修改:
ret = -1;
ret = fb_draw_bmp_logo(fb_inf->fb[fb_inf->num_fb-2]);
if(ret < 0) {
//旧的调用函数
}
然后因为我们的logo数据是放在了boot镜像中,也就是必须要等待根文件系统起来之后,我们的数据文件才能被读取,所以我们需要把fb的驱动加载优先级降低,把它延后到根文件系统那一级,可以直接在文件的末尾,找到驱动的初始化函数,修改为rootfs_initcall(),比如说我这里的修改如下:
//subsys_initcall_sync(xx_fb_init);
rootfs_initcall(xx_fb_init);
值得注意的是,在有些平台中,你需要针对驱动加载顺序做的修改可能不只是上面提到的一点,因为fb延后到rootfs这一级别,而内核中可能还有其他的模块在rootfs启动之前就调用到了fb,这样就会导致空指针错误,内核会挂起,机器完全黑屏,嘿嘿。假如测试的时候发现机器出现这种情况,只能接上串口看看内核的打印信息了,然后就是追着堆栈错误信息跟踪代码,能力所限,对这方面还没有成熟的经验。但不用太担心,这需要一点内核调试的经验,然而并不是什么不可解的问题.只是需要一点时间来试验.
具体的加载顺序,可以查看头文件kernel/include/linux/init.h
logo的制作
代码中是直接把图片原始数据解压出来,然后写入到帧缓冲区中实现显示. 但是普通的图片,不管是jpg,bmp,png格式,都是经过编码的,这些数据不是直接能用的,因此还需要先行对图片进行处理,这个工作可以借助专业的开源工具ffmpeg.
获取ffmpeg
这里使用ffmpeg是希望把普通格式的图片转化成原始的raw文件,觉得委屈ffmpeg了,哈哈...如果在gentoo系统上,直接emerge一个就好了,而在ubuntu上试了好几个源,发现版本比gentoo上拿到的最低版本还低,囧...所以ubuntu党还是直接把源码克隆下来好了
$ git clone git://source/ffmpeg.org//ffmpeg.git
$ sudo apt-get install yasm
安装yasm是因为汇编需要用到汇编器,貌似一般ubuntu系统也没有自带。ffmpeg的仓库克隆下来之后,就按惯例进行配置,编译,编译安装,去到ffmpeg的目录,执行:
$ ./configure
$ make
$ make install
执行configure之后会生成config.h文件,查看一下#define CONFIG_FFPLAY,这个值如果是0,那么还需要安装缺少的SDL库
$ sudo apt-get install libsdl1.2-dev
重新执行配置,编译,编译安装几个步骤,一般问题不大...假设ffmpeg搞定了,下一步就可以使用它来处理图片了,这里列出我使用的命令:
$ ./ffmpeg -i test.jpg -f image2 -pix_fmt rgb565 test.raw
ffmpeg的参数实在是太多了,这里的-i表示输入文件,-f表示force format,image2表示输出的图片是image2包装格式,pix,bmp fmt表示可用的像素格式,这里用的是rgb565格式,是16位的图片,没有用到32位的图片是因为32位的图片数据量更大,而且16位图片与32位图片在视觉上基本无法看出差别.....关于ffmpeg的其他用法,有兴趣的可以自己ffmpeg --help研究研究
当然了,为了更方便使用这个工具,利用脚本是一个很好的方式,我的脚本如下,功能是查找跟脚本在同一目录下的文件,把其中的bmp,jpg,png格式的文件转换成raw文件,并进行压缩,并自动拷贝到android工程的out目录.
#!/bin/bash
set -e
cd ${0%/*}
for name in *.{bmp,jpg,png}; do
gzname=${name%.*}
if [ ! -f "${name}" ]; then
continue
fi
./ffmpeg -i ${name} -f image2 -pix_fmt rgb565 ${gzname}.raw
gzip -9 ${gzname}.raw
mv ${gzname}.raw.gz $OUT/root/${gzname}.gz
echo "$name --> $OUT/root/$gzname.gz"
done
cd -
感觉写得太长了,在浏览器上估计会很难看吧- -
Author:leung
08 Oct 2013
← Home comments powered by Disqus