天天看點

嵌入式Linux下LCD應用程式設計: 調用giflib庫解碼顯示GIF動态圖

一、開發環境介紹

開發闆:友善之臂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
嵌入式Linux下LCD應用程式設計: 調用giflib庫解碼顯示GIF動态圖
編譯過程:  

[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      

六、運作效果圖

嵌入式Linux下LCD應用程式設計: 調用giflib庫解碼顯示GIF動态圖
嵌入式Linux下LCD應用程式設計: 調用giflib庫解碼顯示GIF動态圖

繼續閱讀