fbv是一个简单的图像文件显示程序,可以用来在Framebuffer中显示GIF、JPEG、PNG 和BMP文件图像。
Framebuffer,可看作显示屏像素点的抽象。
如果直接将下载编译后的fbv打开GIF文件,显示的是GIF文件所有帧叠加的一张图像,而非按照一帧一帧的图像显示为动画。
main.c中,定位到show_image(char * filename) ,看到显示图像文件时,都是分别调用
load = fh_gif_load;
load = fh_png_load;
等把图像文件的图像解析到image
unsigned char * image = NULL;
指针中,然后再链接到 struct image i 的 rgb ,
i.rgb = image;
接下来调用了fb_display
fb_display(i.rgb, i.alpha, i.width, i.height, x_pan, y_pan, x_offs, y_offs);
进行显示,然后就退出了。。。出了。。。了
也就是说,在fbv中一个图像文件只有一次显示到framebuffer的机会。
看看fh_gif_load的内容,在gif.c中。fbv解析GIF文件用的是giflib
#include
int fh_gif_load(char *name,unsigned char *buffer, unsigned char ** alpha, int x,int y)
使用giflib解析GIF我参考的是 https://blog.csdn.net/go_to_learn/article/details/8070742 ,跟fbv里的解析略有不同。
整个解析流程就是连续读取GIF文件,处理一遍,像素索引转换为RGB存到 传入的指针 buffer里,透明色则在申请到的一段内存进行保存,并将指针存到alpha地址指针中。
#ifdef FBV_SUPPORT_GIF
if(fh_gif_id(filename))
if(fh_gif_getsize(filename, &x_size, &y_size) == FH_ERROR_OK)
{
load = fh_gif_load;
goto identified;
}
#endif
那么加入自己的处理流程 gif_display(char * filename):
#ifdef FBV_SUPPORT_GIF
if(fh_gif_id(filename))
if(fh_gif_getsize(filename, &x_size, &y_size) == FH_ERROR_OK)
{
// load = fh_gif_load;
// goto identified;
gif_display(filename);
printf("exit\n");
exit(0);
}
#endif
代码参考了上面提到的微博,处理GIF文件的过程:
case IMAGE_DESC_RECORD_TYPE
中先读出一帧的颜色索引值,然后按照ColorMap解析成RGB像素,代码参照DumpScreen2RGBA(),注意微博中的DumpScreen2RGBA()中解析RGB的字节顺序可能是不对的。
接着是解析透明色alpha,参照DumpScreen2RGBA()修改一个:
static void GetAlpha(unsigned char * alpha_buffer , GifRowType * ScreenBuffer , int ScreenWidth, int ScreenHeight,int alphaColorIndex){
int i, j;
GifRowType GifRow;
static GifColorType *ColorMapEntry;
unsigned char *BufferP;
for (i = 0; i < ScreenHeight; i++) {
GifRow = ScreenBuffer[i];
BufferP = alpha_buffer + i * (ScreenWidth);
for (j = 0; j < ScreenWidth; j++) {
*BufferP++ = (alphaColorIndex ==GifRow[j])?0x00: 0xff;
}
}
}
读取完RGB ,alpha 数据后,将数据跟其他参数存到struct image im,再调用fb_display进行显示。
完成显示后继续处理下一帧。
case EXTENSION_RECORD_TYPE
中主要处理的是透明色和延时
if(extcode==0xf9) //look image transparency in graph ctr extension
{
if(extension[1] & 1)
{
//透明色获取
transparency = extension[4];
}else{
transparency = -1;
}
if(extension[0] == 4){
unsigned long usec;
usec = (extension[3] << 8 | extension[2]) * 10*1000;
//延时处理
usleep(usec);
printf("Delay msec %d \n",usec/1000);
}
}
按这么处理,理论上就可以正常显示GIF动画了。。。。吧
为啥?我也只能靠猜,靠着printf打印信息出来判断。。。
还好,print还差个f,想到了:透明色处理可能有问题。
第一帧图像是没问题的。
如果把透明色去掉,GIF动画过程会出现越来越多的奇怪的点;残影则像是贴图错位。
来到fb_display.c,看看
void fb_display(unsigned char *rgbbuff, unsigned char * alpha,
int x_size, int y_size, int x_pan, int y_pan,
int x_offs, int y_offs){
...
/* blit buffer 2 fb */
fbbuff = convertRGB2FB(fh, rgbbuff, x_size * y_size, var.bits_per_pixel, &bp);
...
blit2FB(fh, fbbuff, alpha, x_size, y_size, x_stride, var.yres_virtual, x_pan, y_pan, x_offs, y_offs + var.yoffset, bp);
...
}
看看convertRGB2FB()代码,用来将RGB数据转换为framebuff要传输的像素格式数据。
看看blit2FB()代码,把转换后的像素数据填充到framebuff中,涉及到了透明色alpha的处理,那么就是它了。
没有透明色要处理的时候,把数据直接一行行的填进去:
for(i = 0; i < yc; i++, fbptr += scr_xs * cpp, imptr += pic_xs * cpp)
memcpy(fbptr, imptr, xc * cpp);
有透明色要处理的时候:
unsigned char * alphaptr;
int from, to, x;
alphaptr = alpha + (yp * pic_xs + xp);
for(i = 0; i < yc; i++, fbptr += scr_xs * cpp, imptr += pic_xs * cpp, alphaptr += pic_xs)
{
for(x = 0; x 0x80) from = v;
}
else
{
if(alphaptr[v] < 0x80)
{
to = v;
break;
}
}
}
if(from == -1)
break;
if(to == -1) to = xc;
memcpy(fbptr + (from * cpp), imptr + (from * cpp), (to - from - 1) * cpp);
x += to - from - 1;
}
}
看着有点绕,就是按行处理,每行记录不透明的起始位置 from:
if(alphaptr[v] > 0x80) from = v;
和结束位置to:
if(alphaptr[v] < 0x80)
{
to = v;
break;
},
...
if(to == -1) to = xc;
然后用图像像素数据填充from to之间的framebuffer。
想想就能知道这么处理的问题了,就是每一行有多段透明,非透明存在,造成部分数据少填充。
解决的办法也简单,按像素处理,稍微会慢一点(就代码处理的感觉上。。。):
if(alpha)
{
int w ,h ;
unsigned char * alphaptr;
int pos;
alphaptr = alpha + (yp * pic_xs + xp);
printf("writing fb\n");
for( h = 0 ; h< yc ; h++ ,fbptr += scr_xs * cpp, imptr += pic_xs * cpp,alphaptr+=pic_xs){
for(w = 0 ; w< xc ; w++){
if(alphaptr[w]>0x80){
memcpy(fbptr+w * cpp , imptr+w * cpp,cpp);
}
}
}
}
好了,终于正常显示GIF动画了。