一、开发环境介绍
开发板:友善之臂Tiny4412
LCD型号: S702 、分辨率: 800*480
Linux内核版本: Linux 3.5
交叉编译器: arm-linux-gcc 4.5.1
二、GIF文件格式简单介绍
生活中常用图片格式有BMP、PNG、JPG、GIF等。BMP图片的显示很简单,可以直接从图片文件里读取RGB数据进行显示.。PNG格式图片显示,直接调用libpng库里的接口函数解码显示;JPG格式图片也一样,调用libjpeg库的接口函数完成解码即可得到原始RGB数据完成显示;如果要在LCD屏上显示GIF图片,那么也是调用giflib库的接口函数完成解码显示。
在解码jpeg图片和png图片的时候我们不需要对jpeg和png文件格式有了解就可以解码了(了解jpeg和png当然更好),但是在使用giflib解码gif的时候,我们必须要对gif文件有很简单的了解。
gif文件中可以存放一帧或者多帧图像数据,并且可以存放图像控制信息,因此可以存储动画图片,gif文件由文件头开头,文件尾结尾,中间是一些连续的数据块(block)。这些数据块又分为图像数据块和扩展数据块(extension),图像数据块可以理解成存放一帧的图像数据。扩展数据块存放的是一些辅助信息,比如指示怎样显示图像数据等等。
gif文件中的图像基于调色板的,因此一张gif文件中的图像最多只能有255中颜色,因此gif文件只能存储比较简单的图像。gif文件中有两种调色板 ——全局调色板和图像局部调色板。当一帧图像有局部调色板时,则以局部调色板来解码该帧图像,如果该帧图像没有局部调色板则用全局调色板来解码该图像。
更详细的信息可以查阅giflib的文档中的gif89.txt文件,或者在网络搜索相关的信息。
三、移植giflib库到嵌入式Linux平台
giflib库下载地址: http://www.linuxfromscratch.org/blfs/view/svn/general/giflib.html
编译过程:
[wbyq@wbyq work]$ tar xvf /mnt/hgfs/linux-share-dir/giflib-5.2.1.tar.gz [wbyq@wbyq work]$ cd giflib-5.2.1/ [wbyq@wbyq giflib-5.2.1]$ make CC=arm-linux-gcc [wbyq@wbyq giflib-5.2.1]$ make PREFIX=$PWD/_install install [wbyq@wbyq giflib-5.2.1]$ tree _install/ _install/ ├── bin │ ├── gif2rgb │ ├── gifbuild │ ├── gifclrmp │ ├── giffix │ ├── giftext │ └── giftool ├── include │ └── gif_lib.h ├── lib │ ├── libgif.a │ ├── libgif.so -> libgif.so.7 │ ├── libgif.so.7 -> libgif.so.7.2.0 │ └── libgif.so.7.2.0 └── share └── man └── man1 ├── gif2rgb.1 ├── gifbg.1 ├── gifbuild.1 ├── gifclrmp.1 ├── gifcolor.1 ├── gifecho.1 ├── giffix.1 ├── gifhisto.1 ├── gifinto.1 ├── giflib.1 ├── giftext.1 ├── giftool.1 └── gifwedge.1 6 directories, 24 files [wbyq@wbyq giflib-5.2.1]$
编译完成之后,将头文件和库文件拷贝一份到交叉编译器的路径下,方便程序编译时直接可以找到头文件和库文件;当前,也可以在程序编译的时候在编译器后面指定gif使用的头文件和库文件也可以。再将动态库文件拷贝一份到目标开发板的lib目录下,方便程序在开发板上运行时能找到对应的库。
[wbyq@wbyq _install]$ pwd /home/wbyq/work/giflib-5.2.1/_install [wbyq@wbyq _install]$ ls bin include lib share [wbyq@wbyq _install]$ sudo cp include/* /home/wbyq/work/arm-linux-gcc/opt/FriendlyARM/toolschain/4.5.1/arm-none-linux-gnueabi/sys-root/usr/include/ -rf [wbyq@wbyq _install]$ sudo cp lib/* /home/wbyq/work/arm-linux-gcc/opt/FriendlyARM/toolschain/4.5.1/arm-none-linux-gnueabi/sys-root/usr/lib/ -rf [wbyq@wbyq _install]$ cp lib/* /home/wbyq/work/rootfs/lib/
四、示例代码
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/fb.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdlib.h> #include <gif_lib.h> struct fb_var_screeninfo var; //可变参数 struct fb_fix_screeninfo fix; //固定参数 unsigned char *fb_mem=NULL; //LCD屏的首地址 /* 函数功能: 画点 */ void LCD_WritePoint(int x,int y,int color) { unsigned int *lcd=(unsigned int *)(fb_mem+y*var.xres*var.bits_per_pixel/8+x*var.bits_per_pixel/8); *lcd=color; //颜色赋值 } //帧缓冲显示 void FrameBufferDraw(int x,int y,int image_w,int image_h,unsigned char *rgbBuf) { int w,h; unsigned char r,g,b; unsigned int c; /*将图像数据显示在LCD屏幕上*/ unsigned char *rgb_p=rgbBuf; for(h=0;h<image_h;h++) { for(w=0;w<image_w;w++) { b=*rgb_p++; g=*rgb_p++; r=*rgb_p++; c=r<<16|g<<8|b<<0; LCD_WritePoint(w+x,h+y,c); /*绘制像素点到LCD屏*/ } } } //颜色转换 void GifBufferToRgb888(ColorMapObject *ColorMap, unsigned char *inRgb,GifRowType *Buffer, int w, int h) { GifColorType *ColorMapEntry = NULL; GifRowType GifRow = NULL; unsigned char *rgbBuf = inRgb; int idxH = 0; int idxW = 0; for (idxH = 0; idxH < h; idxH++) { GifRow = Buffer[idxH]; rgbBuf = inRgb + idxH * w * 3; for(idxW = 0; idxW < w; idxW++) { ColorMapEntry = &ColorMap->Colors[GifRow[idxW]]; *rgbBuf++ = ColorMapEntry->Blue; *rgbBuf++ = ColorMapEntry->Green; *rgbBuf++ = ColorMapEntry->Red; } } } //显示GIF图像 int LCD_DisplayGIF(int x,int y,unsigned char *file) { int error=0; int size; int i; GifRowType *Buffer; GifFileType *fp; /*1. 打开图片文件*/ fp=DGifOpenFileName(file,&error); if(fp==NULL)return -1; printf("GIF图片尺寸:%dx%d\n",fp->SWidth,fp->SHeight); /*2. 内存空间申请、初始化*/ Buffer=(GifRowType*)malloc(fp->SHeight*sizeof(GifRowType)); /*一行字节大小*/ size = fp->SWidth*sizeof(GifPixelType); Buffer[0]=(GifRowType)malloc(size); /*将其颜色设置为BackGround*/ for(i=0;i<fp->SWidth;i++) { Buffer[0][i]=fp->SBackGroundColor; } /*分配其他行,并将它们的颜色也设置为背景 */ for(i=1;i<fp->SHeight;i++) { Buffer[i]=(GifRowType)malloc(size); memcpy(Buffer[i],Buffer[0],size); } /*3. 显示图片*/ ColorMapObject *colorMap=NULL; GifByteType *extension=NULL; GifRecordType gRecordType=UNDEFINED_RECORD_TYPE; int InterlacedOffset[]={0,4,2,1}; // The way Interlaced image should int InterlacedJumps[]={8,8,4,2}; // be read - offsets and jumps... unsigned char rgbBuf[800 * 480]={0}; int extCode = 0; int row = 0; int col = 0; int width = 0; int height = 0; int iW = 0; int iH = 0; do { if(DGifGetRecordType(fp,&gRecordType)==GIF_ERROR)break; switch(gRecordType) { case IMAGE_DESC_RECORD_TYPE: if(DGifGetImageDesc(fp)==GIF_ERROR)break; row=fp->Image.Top; col=fp->Image.Left; width=fp->Image.Width; height=fp->Image.Height; if(fp->Image.Interlace) { for(iH=0;iH<4;iH++) { for(iW=row+InterlacedOffset[iH];iW<row+height;iW+=InterlacedJumps[iH]) { DGifGetLine(fp,&Buffer[iW][col],width); } } } else { for(iH=0;iH<height;iH++) { DGifGetLine(fp,&Buffer[row++][col],width); } } colorMap=(fp->Image.ColorMap?fp->Image.ColorMap:fp->SColorMap); if(colorMap==NULL) { break; } GifBufferToRgb888(colorMap,rgbBuf,Buffer,fp->SWidth,fp->SHeight); //将图像显示在LCD屏上 FrameBufferDraw(x,y,fp->SWidth,fp->SHeight,rgbBuf); //帧间隔时间 usleep(1000*50); break; case EXTENSION_RECORD_TYPE: /* 跳过文件中的所有扩展块*/ if(DGifGetExtension(fp,&extCode,&extension)==GIF_ERROR)break; while(extension!=NULL) { if(DGifGetExtensionNext(fp, &extension) == GIF_ERROR)break; } break; case TERMINATE_RECORD_TYPE: break; default: break; } }while(gRecordType!=TERMINATE_RECORD_TYPE); /*4. 释放空间*/ for(i =0;i<fp->SHeight;i++) { free(Buffer[i]); } free(Buffer); DGifCloseFile(fp,&error); return 0; } int main(int argc,char **argv) { if(argc!=2) { printf("./app <GIF图片文件>\n"); return 0; } int fd=open("/dev/fb0",O_RDWR); if(fd<0) { perror("设备文件打开失败"); return 0; } /*1. 获取LCD屏的可变形参*/ ioctl(fd,FBIOGET_VSCREENINFO,&var); printf("分辨率:%d*%d\n",var.xres,var.yres); printf("像素点位数:%d\n",var.bits_per_pixel); /*2. 获取LCD屏的固定形参*/ ioctl(fd,FBIOGET_FSCREENINFO,&fix); printf("映射的长度:%d\n",fix.smem_len); printf("一行的字节数:%d\n",fix.line_length); /*3. 映射LCD缓冲区地址到进程空间*/ fb_mem=mmap(NULL,fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if(fb_mem==NULL) { perror("空间映射失败!\n"); return 0; } /*4. 控制显示屏*/ memset(fb_mem,0xFFFFFF,fix.smem_len); //将屏幕清屏为白色 while(1) { printf("GIF图片显示状态:%d\n",LCD_DisplayGIF(100,100,argv[1])); } munmap(fb_mem,fix.smem_len); close(fd); return 0; }
五、编译Makefile代码
all: arm-linux-gcc lcd_app.c -o app -lgif cp app /home/wbyq/project rm app -f
六、运行效果图