STM32單片機圖片解碼


圖片解碼首先是最簡單的bmp圖片解碼,關於bmp的結構可自行查閱,代碼如下

#ifndef __BMPDECODE_H_
#define __BMPDECODE_H_

#include "ff.h"
#include "lcd.h"
#include "stdlib.h"
#include "usb_type.h"

//重定義區
typedef char            CHAR;                     //數據類型重定義,便於移植
typedef short           SHORT;
//typedef int            LONG;               
//typedef unsigned int   DWORD;
typedef int             BOOL;
typedef u8              BYTE;
typedef unsigned short  WORD;

//#define FALSE 0
//#define TRUE  1 

//BMP圖象數據壓縮的類型
#define BI_RGB       0L          //無壓縮
#define BI_RLE8      1L          //每個像素四個bit
#define BI_RLE4      2L          //每個像素8個bit
#define BI_BITFIELDS 3L          //每個像素的bit由指定的掩碼決定

#define bufferToLong(buffer,t)   (LONG)((((u32)bmpbuffer[t])) + (((u32)bmpbuffer[t+1])<<8) +(((u32)bmpbuffer[t+2])<<16) + (((u32)bmpbuffer[t+3])<<24))
#define bufferToWord(buffer,t)  (WORD)(((u32)(buffer[t])) + (((u32)(buffer[t+1]))<<8))
#define bufferToDword(buffer,t)  (DWORD)((((u32)bmpbuffer[t])) + (((u32)bmpbuffer[t+1])<<8) +(((u32)bmpbuffer[t+2])<<16) + (((u32)bmpbuffer[t+3])<<24))

#define RGB888buffertoRGB565(buffer,t)   ((((u16)buffer[t])>>3) + ((((u16)buffer[t+1])>>2)<<5) + ((((u16)buffer[t+2])>>3)<<11))


//BMP文件頭   14個字節
typedef __packed struct     
{
    WORD  bfType ;     //文件標志.只對'BM',用來識別BMP位圖類型 2
    DWORD bfSize ;       //文件大小,占四個字節     4
    WORD  bfReserved1 ;//保留            2
    WORD  bfReserved2 ;//保留            2
    DWORD bfOffBits ;  //從文件開始到位圖數據(bitmap data)開始之間的的偏移量,這一段中存放的就是文件信息 4
}BITMAPFILEHEADER ;            //位圖文件頭

//位圖信息頭
typedef __packed struct                
{
    DWORD biSize ;           //說明BITMAPINFOHEADER結構所需要的字數。
    LONG  biWidth ;           //說明圖象的寬度,以象素為單位 
    LONG  biHeight ;       //說明圖象的高度,以象素為單位 
    WORD  biPlanes ;       //為目標設備說明位面數,其值將總是被設為1 
    WORD  biBitCount ;       //說明比特數/象素,其值為1、4、8、16、24、或32
    DWORD biCompression ;  //說明圖象數據壓縮的類型。其值可以是下述值之一:
    /*BI_RGB:沒有壓縮;
     *BI_RLE8:每個象素8比特的RLE壓縮編碼,壓縮格式由2字節組成(重復象素計數和顏色索引);  
     *BI_RLE4:每個象素4比特的RLE壓縮編碼,壓縮格式由2字節組成
       *BI_BITFIELDS:每個象素的比特由指定的掩碼決定。*/
    DWORD biSizeImage ;//說明圖象的大小,以字節為單位。當用BI_RGB格式時,可設置為0  
    LONG  biXPelsPerMeter ;//說明水平分辨率,用象素/米表示
    LONG  biYPelsPerMeter ;//說明垂直分辨率,用象素/米表示
    DWORD biClrUsed ;       //說明位圖實際使用的彩色表中的顏色索引數
    DWORD biClrImportant ; //說明對圖象顯示有重要影響的顏色索引的數目,如果是0,表示都重要。 
}BITMAPINFOHEADER; 

//顏色索引表,每個顏色索引占4個字節,16位以及以下才會有這個數據 也就是說 最多65535個數據
//當然,最好不要用16位色的,浪費空間
typedef __packed struct 
{
    BYTE rgbBlue ;    //指定藍色強度
    BYTE rgbGreen ;      //指定綠色強度 
    BYTE rgbRed ;      //指定紅色強度 
    BYTE rgbReserved ;//保留,設置為0 
}RGBQUAD ;

BOOL BmpDecode(u8 *filename,u16 sx,u16 sy,u16 ex,u16 ey);






#endif
u8 bmpbuffer[1024] = {0};       //存儲bmp文件的數組

//解碼這個BMP文件
//設定顯示起始位置以及終止位置                                             
BOOL BmpDecode(u8 *filename,u16 sx,u16 sy,u16 ex,u16 ey)
{
    FIL f_bmp;              //文件系統變量,用於讀取bmp文件
    u16 filereadnum = 0;    //用於記錄文件字節讀取數量
    FRESULT res;            //文件讀取返回信息
    BITMAPFILEHEADER bmpfileHead;  //位圖文件頭
    BITMAPINFOHEADER bmpinfoHead;  //位圖信息頭
    u8 colorByte = 0;               //用於標記顏色位
    u16 index = 0;          //讀取文件信息時候用來定位
    u16 uitemp = 0;         //記錄實際數據一行有多少個點
    u16 xtemp = 0;
    u16 ytemp = 0;          //顯示時輔助計數
    u16 colortemp = 0;      //顏色緩沖
    
    res=f_open(&f_bmp,(const TCHAR*)filename,FA_READ);  //打開文件   
    if(res!=FR_OK) return FALSE;
     res = f_read(&f_bmp,bmpbuffer,1024,(UINT*)&filereadnum); //讀取文件
    if(res!=FR_OK) return FALSE;
    
    //獲取位圖文件頭
    bmpfileHead.bfType =  (WORD)((((u16)bmpbuffer[0])<<8) + bmpbuffer[1+1]);
    bmpfileHead.bfSize = bufferToDword(bmpbuffer,2);
    bmpfileHead.bfReserved1 = bufferToWord(bmpbuffer,6);
    bmpfileHead.bfReserved2 = bufferToWord(bmpbuffer,8);
    bmpfileHead.bfOffBits = bufferToDword(bmpbuffer,10);    //數據段開始地址
    //獲取位圖信息頭
    bmpinfoHead.biSize = bufferToDword(bmpbuffer,14);   
    bmpinfoHead.biWidth = bufferToLong(bmpbuffer,18);   
    bmpinfoHead.biHeight = bufferToLong(bmpbuffer,22);  
    bmpinfoHead.biPlanes = bufferToWord(bmpbuffer,26);   
    bmpinfoHead.biBitCount = bufferToWord(bmpbuffer,28);    //顏色位  
    bmpinfoHead.biCompression = bufferToDword(bmpbuffer,30); 
    bmpinfoHead.biSizeImage = bufferToDword(bmpbuffer,34);
    bmpinfoHead.biXPelsPerMeter = bufferToLong(bmpbuffer,38);
    bmpinfoHead.biYPelsPerMeter = bufferToLong(bmpbuffer,42);
    bmpinfoHead.biClrUsed = bufferToDword(bmpbuffer,46);  
    bmpinfoHead.biClrImportant = bufferToDword(bmpbuffer,50);
    index = bmpfileHead.bfOffBits;
    //所有信息得到,這里應該進行測試了
    
    colorByte =  bmpinfoHead.biBitCount/8;      //顏色位數 16 / 24 / 32 得到2/3/4
    if((bmpinfoHead.biWidth%4) != 0)   //不是4的倍數
    {
        uitemp = (((bmpinfoHead.biWidth/4) + 1) * 4);
    }
    else                        //是4的倍數
    {
        uitemp = bmpinfoHead.biWidth ;
    }
    if(colorByte == 3)  //24位顏色
    {
        while(1)
        {
            if(index <= 1021)
            {
                colortemp = RGB888buffertoRGB565(bmpbuffer,index);
                index = index +3;
                if(sx+xtemp <ex && sy+ytemp < ey)
                LCD_Draw_Point_Buffer(sx+xtemp,sy+ytemp,colortemp);
                xtemp++;
                if(xtemp >= uitemp)
                {
                    xtemp = 0;
                    ytemp++;
                }
            }
            else
            {
                if(index == 1022)
                {
                    if(sx+xtemp <ex && sy+ytemp < ey)
                    LCD_Draw_Point_Buffer(sx+xtemp,sy+ytemp,colortemp);
                    xtemp++;
                    if(xtemp >= uitemp)
                    {
                        xtemp = 0;
                        ytemp++;
                    }
                    index = 1;
                }
                else if(index == 1023)
                {
                    if(sx+xtemp <ex && sy+ytemp < ey)
                    LCD_Draw_Point_Buffer(sx+xtemp,sy+ytemp,colortemp);
                    xtemp++;
                    if(xtemp >= uitemp)
                    {
                        xtemp = 0;
                        ytemp++;
                    }
                    index = 2;
                }
                else index = 0;
                res = f_read(&f_bmp,bmpbuffer,1024,(UINT*)&filereadnum); //讀取文件
                if(res||filereadnum==0)break;        //讀取出錯
            }
        }        
    }
    f_close(&f_bmp);     /* 關閉打開的文件 */ 
    
    LCD_Flush();
    return TRUE;
}

為了防止圖片刷新速度過慢可以在內存中建立一個屏幕緩存,解碼完成后一次性刷入屏幕

JPEG圖片解碼使用網絡上一個tjpgdec的庫,該庫的移植如下

#ifndef __JPGDEC_CALLBACK_H_
#define __JPGDEC_CALLBACK_H_

#include "lcd.h"
#include "ff.h"
#include "tjpgd.h"
#include "stdlib.h"

void load_jpg (FIL *fp,void *work,UINT sz_work);

void load_file (const char *fn);


#endif
// 以下3句宏定義在tjpgd.h中修改
static int MaskL = 0; 
static int MaskR = LCD_X_SIZE - 1; 
static int MaskT = 0; 
static int MaskB = LCD_Y_SIZE - 1;

#define SIZE_OF_FILE_BUFFER        4096

BYTE* jpegbuffer; //圖像文件數據緩沖區

/*********************************************************************************************
  函數名稱:    STM32_Display
  函數功能:    在TFTLCD屏幕上顯示圖片 
  入口參數:    見函數頭
  出口參數:    無
  全局變量:       無
  備注說明:    無
*********************************************************************************************/
void Lcd_Display_Rect( 
                            int left,                            /*圖片左方起始點,即一行的起始點 */ 
                            int right,                           /*圖片右方的結束點,即一行的結束點*/ 
                            int top,                             /* 圖片上方的起始點,即一列的起始點 */ 
                            int bottom,                          /*圖像下方的結束點,即一列的結束點 */ 
                            const uint16_t * RGB_Data_Pointer    /* 待顯示的圖像數據,RGB格式*/ 
                        ) 
{ 
    int yc, xc, xl, xs;
    unsigned short pd;

    if (left > right || top > bottom)  
    { 
        return;             // Check varidity 
    } 
    
    if (left > MaskR || right < MaskL  || top > MaskB || bottom < MaskT)  
    { 
        return;                // Check if in active area  
    }     
    yc = bottom - top + 1 ;            /* Vertical size */
    xc = right - left + 1; xs = 0;    /* Horizontal size and skip */
    
    // 已經計算出了要繪制的x方向pixels和y方向pixels 
    // 上下左右的值已經校正為,為屏幕上的絕對位置,由此可以算出屏幕緩沖區的起始位置 
    do {    /* Send image data */
        xl = xc;
        do {
            pd = *RGB_Data_Pointer++;
            LCD_Draw_Point_Buffer(right - xl,bottom - yc,pd);            //顯示像素
        } while (--xl);
        RGB_Data_Pointer += xs;
    } while (--yc);
} 


BYTE Buff[1024] __attribute__ ((aligned(4)));    //定義全局數組變量,作為輸入的緩沖區,強制4字節對齊
/*********************************************************************************************
  函數名稱:    Jpeg_Data_in_func
  函數功能:    用戶自定義的用於輸入文件數據的功能函數
  入口參數:    見函數頭
  出口參數:    讀取或者刪除的數據量 
  全局變量:       無
  備注說明:    本函數在解碼准備工作中用於讀取文件頭信息
*********************************************************************************************/
UINT Jpeg_Data_in_func ( 
                            JDEC* jd,        /*儲存待解碼的對象信息的結構體 */ 
                            BYTE* buff,        /* 輸入數據緩沖區 (NULL:刪除數據) */ 
                            UINT nd            /*需要從輸入數據流讀出/刪除的數據量*/ 
                      ) 
{ 
    UINT  rb; 
    FIL * dev = (FIL *)jd->device;    /* 待解碼的文件的信息,使用FATFS中的FIL結構類型進行定義 */   
    if (buff)     /*讀取數據有效,開始讀取數據 */ 
    { 
        f_read(dev, buff, nd, &rb); //調用FATFS的f_read函數,用於把jpeg文件的數據讀取出來
        return rb;        /* 返回讀取到的字節數目*/ 
    }  
    else  
    { 
        return (f_lseek(dev, f_tell(dev) + nd) == FR_OK) ? nd : 0;/* 重新定位數據點,相當於刪除之前的n字節數據 */ 
    } 
} 


/*********************************************************************************************
  函數名稱:    STM32_out_func
  函數功能:    用戶自定義的用於輸出RGB位圖數據的功能函數
  入口參數:    見函數頭 
  出口參數:    1:令解碼函數繼續執行 
  全局變量:       無
  備注說明:    無
*********************************************************************************************/
UINT Jpeg_Data_out_func ( 
                         JDEC*   jd,        /*儲存待解碼的對象信息的結構體*/ 
                         void*   bitmap,    /* 指向等待輸出的RGB位圖數據 的指針*/ 
                         JRECT*  rect      /* 等待輸出的矩形圖像的參數 */ 
                        ) 
{ 
    jd = jd;    /* 說明:輸出函數中JDEC結構體沒有用到 */ 

    //顯示一塊區域到LCD上
    Lcd_Display_Rect(rect->left, rect->right, rect->top, rect->bottom, (uint16_t*)bitmap); 
    
    return 1;    /*返回1,使解碼工作繼續執行 */ 
} 


//加載並顯示jpg文件
void load_jpg (
    FIL *fp,        /* 指向打開的文件執政 */
    void *work,        /*指向四字節對其的工作區緩存 */
    UINT sz_work    /*工作區的大小 */
)
{
    JDEC jd;        /* Decoder object (124 bytes) */
    JRESULT rc;
    BYTE scale;


    /* Prepare to decompress the file */
    rc = jd_prepare(&jd, Jpeg_Data_in_func, work, sz_work, fp);
    if (rc == JDR_OK) 
    {
        /* 根據圖片大小選選擇一個剛剛好能夠縮放的圖片比例 */
        for (scale = 0; scale < 3; scale++) 
        {
            if ((jd.width >> scale) <= LCD_X_SIZE && (jd.height >> scale) <= LCD_Y_SIZE) break;
        }
        rc = jd_decomp(&jd, Jpeg_Data_out_func, scale);    /* Start to decompress */
    } else 
    {
        //顯示錯誤,將錯誤信息打印在屏幕上
        printf("jpg error %d\r\n",rc);
    }
}

/*參數:指向文件名 */
void load_file (const char *fn)
{
    FIL fil;            /* Pointer to a file object */

    if (f_open(&fil, fn, FA_READ) == FR_OK) 
    {
        jpegbuffer = NULL;
        jpegbuffer = malloc(SIZE_OF_FILE_BUFFER);
        if(jpegbuffer != NULL)
        {
            LCD_Fill_Buffer(LCD_BLACK);
            load_jpg(&fil, (void *)jpegbuffer, SIZE_OF_FILE_BUFFER );  //打開jpg文件並解碼顯示    
        }
        f_close(&fil);
        if(jpegbuffer != NULL)free(jpegbuffer);
        LCD_Flush();
    }
    else
    {
        printf("open file failed\r\n");
    }
}

該庫的解碼性能還不錯,使用了圖片流的概念來進行解碼操作,剩下的是一個GIF圖片解碼,如下

#ifndef __GIFDECODE_H_
#define __GIFDECODE_H_

#include "ff.h"
#include "lcd.h"
#include "stdlib.h"


#define MAX_NUM_LWZ_BITS     12
#define LCD_MAX_LOG_COLORS  256 

#define GIF_INTRO_TERMINATOR ';'    //0X3B   GIF文件結束符
#define GIF_INTRO_EXTENSION  '!'    //0X21
#define GIF_INTRO_IMAGE      ','    //0X2C

#define GIF_COMMENT         0xFE
#define GIF_APPLICATION     0xFF
#define GIF_PLAINTEXT       0x01
#define GIF_GRAPHICCTL      0xF9

#define PIC_FORMAT_ERR        0x27    //格式錯誤
#define PIC_SIZE_ERR        0x28    //圖片尺寸錯誤
#define PIC_WINDOW_ERR        0x29    //窗口設定錯誤
#define PIC_MEM_ERR            0x11    //內存錯誤

typedef struct
{
    u8    aBuffer[258];                     // Input buffer for data block 
    short aCode  [(1 << MAX_NUM_LWZ_BITS)]; // This array stores the LZW codes for the compressed strings 
    u8    aPrefix[(1 << MAX_NUM_LWZ_BITS)]; // Prefix character of the LZW code.
    u8    aDecompBuffer[3000];              // Decompression buffer. The higher the compression, the more bytes are needed in the buffer.
    u8 *  sp;                               // Pointer into the decompression buffer 
    int   CurBit;
    int   LastBit;
    int   GetDone;
    int   LastByte;
    int   ReturnClear;
    int   CodeSize;
    int   SetCodeSize;
    int   MaxCode;
    int   MaxCodeSize;
    int   ClearCode;
    int   EndCode;
    int   FirstCode;
    int   OldCode;
}LZW_INFO;

//邏輯屏幕描述塊
__packed typedef struct
{
    u16 width;        //GIF寬度
    u16 height;        //GIF高度
    u8 flag;        //標識符  1:3:1:3=全局顏色表標志(1):顏色深度(3):分類標志(1):全局顏色表大小(3)
    u8 bkcindex;    //背景色在全局顏色表中的索引(僅當存在全局顏色表時有效)
    u8 pixratio;    //像素寬高比
}LogicalScreenDescriptor;


//圖像描述塊
__packed typedef struct
{
    u16 xoff;        //x方向偏移
    u16 yoff;        //y方向偏移
    u16 width;        //寬度
    u16 height;        //高度
    u8 flag;        //標識符  1:1:1:2:3=局部顏色表標志(1):交織標志(1):保留(2):局部顏色表大小(3)
}ImageScreenDescriptor;

//圖像描述
__packed typedef struct
{
    LogicalScreenDescriptor gifLSD;    //邏輯屏幕描述塊
    ImageScreenDescriptor gifISD;    //圖像描述快
    u16 colortbl[256];                //當前使用顏色表
    u16 bkpcolortbl[256];            //備份顏色表.當存在局部顏色表時使用
    u16 numcolors;                    //顏色表大小
    u16 delay;                        //延遲時間
    LZW_INFO *lzw;                    //LZW信息
}gif89a;

extern u8 gifdecoding;    //GIF正在解碼標記.

u8 gif_check_head(FIL *file);                                                            //檢測GIF頭
u16 gif_getrgb565(u8 *ctb);                                                                //將RGB888轉為RGB565
u8 gif_readcolortbl(FIL *file,gif89a * gif,u16 num);                                    //讀取顏色表
u8 gif_getinfo(FIL *file,gif89a * gif);                                                    //得到邏輯屏幕描述,圖像尺寸等
void gif_savegctbl(gif89a* gif);                                                        //保存全局顏色表
void gif_recovergctbl(gif89a* gif);                                                        //恢復全局顏色表
void gif_initlzw(gif89a* gif,u8 codesize);                                                //初始化LZW相關參數
u16 gif_getdatablock(FIL *gfile,u8 *buf,u16 maxnum);                                       //讀取一個數據塊
u8 gif_readextension(FIL *gfile,gif89a* gif, int *pTransIndex,u8 *pDisposal);               //讀取擴展部分
int gif_getnextcode(FIL *gfile,gif89a* gif);                                               //從LZW緩存中得到下一個LZW碼,每個碼包含12位
int gif_getnextbyte(FIL *gfile,gif89a* gif);                                            //得到LZW的下一個碼
u8 gif_dispimage(FIL *gfile,gif89a* gif,u16 x0,u16 y0,int Transparency, u8 Disposal);    //顯示圖片
void gif_clear2bkcolor(u16 x,u16 y,gif89a* gif,ImageScreenDescriptor pimge);               //恢復成背景色
u8 gif_drawimage(FIL *gfile,gif89a* gif,u16 x0,u16 y0);                                    //畫GIF圖像的一幀

u8 gif_decode(const u8 *filename,u16 x,u16 y,u16 width,u16 height);//在指定區域解碼一個GIF文件.
void gif_quit(void);                                    //退出當前解碼.











#endif
const u16 _aMaskTbl[16] =
{
    0x0000, 0x0001, 0x0003, 0x0007,
    0x000f, 0x001f, 0x003f, 0x007f,
    0x00ff, 0x01ff, 0x03ff, 0x07ff,
    0x0fff, 0x1fff, 0x3fff, 0x7fff,
};      
const u8 _aInterlaceOffset[]={8,8,4,2};
const u8 _aInterlaceYPos  []={0,4,2,1};
 
u8 gifdecoding=0;//標記GIF正在解碼.

//檢測GIF頭
//返回值:0,是GIF89a/87a;非零,非GIF89a/87a
u8 gif_check_head(FIL *file)
{
    u8 gifversion[6];
    u32 readed;
    u8 res;
    res=f_read(file,gifversion,6,(UINT*)&readed);
    if(res)return 1;       
    if((gifversion[0]!='G')||(gifversion[1]!='I')||(gifversion[2]!='F')||
    (gifversion[3]!='8')||((gifversion[4]!='7')&&(gifversion[4]!='9'))||
    (gifversion[5]!='a'))return 2;
    else return 0;    
}


//將RGB888轉為RGB565
//ctb:RGB888顏色數組首地址.
//返回值:RGB565顏色.
u16 gif_getrgb565(u8 *ctb) 
{
    u16 r,g,b;
    r=(ctb[0]>>3)&0X1F;
    g=(ctb[1]>>2)&0X3F;
    b=(ctb[2]>>3)&0X1F;
    return b+(g<<5)+(r<<11);
}


//讀取顏色表
//file:文件;
//gif:gif信息;
//num:tbl大小.
//返回值:0,OK;其他,失敗;
u8 gif_readcolortbl(FIL *file,gif89a * gif,u16 num)
{
    u8 rgb[3];
    u16 t;
    u8 res;
    u32 readed;
    for(t=0;t<num;t++)
    {
        res=f_read(file,rgb,3,(UINT*)&readed);
        if(res)return 1;//讀錯誤
        gif->colortbl[t]=gif_getrgb565(rgb);
    }
    return 0;
} 


//得到邏輯屏幕描述,圖像尺寸等
//file:文件;
//gif:gif信息;
//返回值:0,OK;其他,失敗;
u8 gif_getinfo(FIL *file,gif89a * gif)
{
    u32 readed;     
    u8 res;   
    res=f_read(file,(u8*)&gif->gifLSD,7,(UINT*)&readed);
    if(res)return 1;
    if(gif->gifLSD.flag&0x80)//存在全局顏色表
    {
        gif->numcolors=2<<(gif->gifLSD.flag&0x07);//得到顏色表大小
        if(gif_readcolortbl(file,gif,gif->numcolors))return 1;//讀錯誤    
    }       
    return 0;
}


//保存全局顏色表     
//gif:gif信息;
void gif_savegctbl(gif89a* gif)
{
    u16 i=0;
    for(i=0;i<256;i++)gif->bkpcolortbl[i]=gif->colortbl[i];//保存全局顏色.
}


//恢復全局顏色表     
//gif:gif信息;
void gif_recovergctbl(gif89a* gif)
{
    u16 i=0;
    for(i=0;i<256;i++)gif->colortbl[i]=gif->bkpcolortbl[i];//恢復全局顏色.
}

//初始化LZW相關參數       
//gif:gif信息;
//codesize:lzw碼長度
void gif_initlzw(gif89a* gif,u8 codesize) 
{
     mymemset((u8 *)gif->lzw, 0, sizeof(LZW_INFO));
    gif->lzw->SetCodeSize  = codesize;
    gif->lzw->CodeSize     = codesize + 1;
    gif->lzw->ClearCode    = (1 << codesize);
    gif->lzw->EndCode      = (1 << codesize) + 1;
    gif->lzw->MaxCode      = (1 << codesize) + 2;
    gif->lzw->MaxCodeSize  = (1 << codesize) << 1;
    gif->lzw->ReturnClear  = 1;
    gif->lzw->LastByte     = 2;
    gif->lzw->sp           = gif->lzw->aDecompBuffer;
}

//讀取一個數據塊
//gfile:gif文件;
//buf:數據緩存區
//maxnum:最大讀寫數據限制
u16 gif_getdatablock(FIL *gfile,u8 *buf,u16 maxnum) 
{
    u8 cnt;
    u32 readed;
    u32 fpos;
    f_read(gfile,&cnt,1,(UINT*)&readed);//得到LZW長度             
    if(cnt) 
    {
        if (buf)//需要讀取 
        {
            if(cnt>maxnum)
            {
                fpos=f_tell(gfile);
                f_lseek(gfile,fpos+cnt);//跳過
                return cnt;//直接不讀
            }
            f_read(gfile,buf,cnt,(UINT*)&readed);//得到LZW長度    
        }else     //直接跳過
        {
            fpos=f_tell(gfile);
            f_lseek(gfile,fpos+cnt);//跳過
        }
    }
    return cnt;
}


//ReadExtension         
//Purpose:
//Reads an extension block. One extension block can consist of several data blocks.
//If an unknown extension block occures, the routine failes.
//返回值:0,成功;
//          其他,失敗
u8 gif_readextension(FIL *gfile,gif89a* gif, int *pTransIndex,u8 *pDisposal)
{
    u8 temp;
    u32 readed;     
    u8 buf[4];  
    f_read(gfile,&temp,1,(UINT*)&readed);//得到長度         
    switch(temp)
    {
        case GIF_PLAINTEXT:
        case GIF_APPLICATION:
        case GIF_COMMENT:
            while(gif_getdatablock(gfile,0,256)>0);            //獲取數據塊
            return 0;
        case GIF_GRAPHICCTL://圖形控制擴展塊
            if(gif_getdatablock(gfile,buf,4)!=4)return 1;    //圖形控制擴展塊的長度必須為4 
              gif->delay=(buf[2]<<8)|buf[1];                    //得到延時 
            *pDisposal=(buf[0]>>2)&0x7;                     //得到處理方法
            if((buf[0]&0x1)!=0)*pTransIndex=buf[3];            //透明色表 
            f_read(gfile,&temp,1,(UINT*)&readed);             //得到LZW長度    
             if(temp!=0)return 1;                            //讀取數據塊結束符錯誤.
            return 0;
    }
    return 1;//錯誤的數據
}

//從LZW緩存中得到下一個LZW碼,每個碼包含12位
//返回值:<0,錯誤.
//         其他,正常.
int gif_getnextcode(FIL *gfile,gif89a* gif) 
{
    int i,j,End;
    long Result;
    if(gif->lzw->ReturnClear)
    {
        //The first code should be a clearcode.
        gif->lzw->ReturnClear=0;
        return gif->lzw->ClearCode;
    }
    End=gif->lzw->CurBit+gif->lzw->CodeSize;
    if(End>=gif->lzw->LastBit)
    {
        int Count;
        if(gif->lzw->GetDone)return-1;//Error 
        gif->lzw->aBuffer[0]=gif->lzw->aBuffer[gif->lzw->LastByte-2];
        gif->lzw->aBuffer[1]=gif->lzw->aBuffer[gif->lzw->LastByte-1];
        if((Count=gif_getdatablock(gfile,&gif->lzw->aBuffer[2],300))==0)gif->lzw->GetDone=1;
        if(Count<0)return -1;//Error 
        gif->lzw->LastByte=2+Count;
        gif->lzw->CurBit=(gif->lzw->CurBit-gif->lzw->LastBit)+16;
        gif->lzw->LastBit=(2+Count)*8;
        End=gif->lzw->CurBit+gif->lzw->CodeSize;
    }
    j=End>>3;
    i=gif->lzw->CurBit>>3;
    if(i==j)Result=(long)gif->lzw->aBuffer[i];
    else if(i+1==j)Result=(long)gif->lzw->aBuffer[i]|((long)gif->lzw->aBuffer[i+1]<<8);
    else Result=(long)gif->lzw->aBuffer[i]|((long)gif->lzw->aBuffer[i+1]<<8)|((long)gif->lzw->aBuffer[i+2]<<16);
    Result=(Result>>(gif->lzw->CurBit&0x7))&_aMaskTbl[gif->lzw->CodeSize];
    gif->lzw->CurBit+=gif->lzw->CodeSize;
    return(int)Result;
}    


//得到LZW的下一個碼
//返回值:<0,錯誤(-1,不成功;-2,讀到結束符了)
//         >=0,OK.(LZW的第一個碼)
int gif_getnextbyte(FIL *gfile,gif89a* gif) 
{
    int i,Code,Incode;
    while((Code=gif_getnextcode(gfile,gif))>=0)
    {
        if(Code==gif->lzw->ClearCode)
        {
            //Corrupt GIFs can make this happen  
            if(gif->lzw->ClearCode>=(1<<MAX_NUM_LWZ_BITS))return -1;//Error 
            //Clear the tables 
            mymemset((u8*)gif->lzw->aCode,0,sizeof(gif->lzw->aCode));
            for(i=0;i<gif->lzw->ClearCode;++i)gif->lzw->aPrefix[i]=i;
            //Calculate the'special codes' independence of the initial code size
            //and initialize the stack pointer 
            gif->lzw->CodeSize=gif->lzw->SetCodeSize+1;
            gif->lzw->MaxCodeSize=gif->lzw->ClearCode<<1;
            gif->lzw->MaxCode=gif->lzw->ClearCode+2;
            gif->lzw->sp=gif->lzw->aDecompBuffer;
            //Read the first code from the stack after clear ingand initializing*/
            do
            {
                gif->lzw->FirstCode=gif_getnextcode(gfile,gif);
            }while(gif->lzw->FirstCode==gif->lzw->ClearCode);
            gif->lzw->OldCode=gif->lzw->FirstCode;
            return gif->lzw->FirstCode;
        }
        if(Code==gif->lzw->EndCode)return -2;//End code
        Incode=Code;
        if(Code>=gif->lzw->MaxCode)
        {
            *(gif->lzw->sp)++=gif->lzw->FirstCode;
            Code=gif->lzw->OldCode;
        }
        while(Code>=gif->lzw->ClearCode)
        {
            *(gif->lzw->sp)++=gif->lzw->aPrefix[Code];
            if(Code==gif->lzw->aCode[Code])return Code;
            if((gif->lzw->sp-gif->lzw->aDecompBuffer)>=sizeof(gif->lzw->aDecompBuffer))return Code;
            Code=gif->lzw->aCode[Code];
        }
        *(gif->lzw->sp)++=gif->lzw->FirstCode=gif->lzw->aPrefix[Code];
        if((Code=gif->lzw->MaxCode)<(1<<MAX_NUM_LWZ_BITS))
        {
            gif->lzw->aCode[Code]=gif->lzw->OldCode;
            gif->lzw->aPrefix[Code]=gif->lzw->FirstCode;
            ++gif->lzw->MaxCode;
            if((gif->lzw->MaxCode>=gif->lzw->MaxCodeSize)&&(gif->lzw->MaxCodeSize<(1<<MAX_NUM_LWZ_BITS)))
            {
                gif->lzw->MaxCodeSize<<=1;
                ++gif->lzw->CodeSize;
            }
        }
        gif->lzw->OldCode=Incode;
        if(gif->lzw->sp>gif->lzw->aDecompBuffer)return *--(gif->lzw->sp);
    }
    return Code;
}


//DispGIFImage         
//Purpose:
//   This routine draws a GIF image from the current pointer which should point to a
//   valid GIF data block. The size of the desired image is given in the image descriptor.
//Return value:
//  0 if succeed
//  1 if not succeed
//Parameters:
//  pDescriptor  - Points to a IMAGE_DESCRIPTOR structure, which contains infos about size, colors and interlacing.
//  x0, y0       - Obvious.
//  Transparency - Color index which should be treated as transparent.
//  Disposal     - Contains the disposal method of the previous image. If Disposal == 2, the transparent pixels
//                 of the image are rendered with the background color.
u8 gif_dispimage(FIL *gfile,gif89a* gif,u16 x0,u16 y0,int Transparency, u8 Disposal) 
{
    u32 readed;       
       u8 lzwlen;
    int Index,OldIndex,XPos,YPos,YCnt,Pass,Interlace,XEnd;
    int Width,Height,Cnt,ColorIndex;
    u16 bkcolor;
    u16 *pTrans;

    Width=gif->gifISD.width;
    Height=gif->gifISD.height;
    XEnd=Width+x0-1;
    bkcolor=gif->colortbl[gif->gifLSD.bkcindex];
    pTrans=(u16*)gif->colortbl;
    f_read(gfile,&lzwlen,1,(UINT*)&readed);//得到LZW長度     
    gif_initlzw(gif,lzwlen);//Initialize the LZW stack with the LZW code size 
    Interlace=gif->gifISD.flag&0x40;//是否交織編碼
    for(YCnt=0,YPos=y0,Pass=0;YCnt<Height;YCnt++)
    {
        Cnt=0;
        OldIndex=-1;
        for(XPos=x0;XPos<=XEnd;XPos++)
        {
            if(gif->lzw->sp>gif->lzw->aDecompBuffer)Index=*--(gif->lzw->sp);
            else Index=gif_getnextbyte(gfile,gif);       
            if(Index==-2)return 0;//Endcode     
            if((Index<0)||(Index>=gif->numcolors))
            {
                //IfIndex out of legal range stop decompressing
                return 1;//Error
            }
            //If current index equals old index increment counter
            if((Index==OldIndex)&&(XPos<=XEnd))Cnt++;
             else
            {
                if(Cnt)
                {
                    if(OldIndex!=Transparency)
                    {                                        
                        LCD_Draw_Hline(XPos-Cnt-1,YPos,Cnt+1,*(pTrans+OldIndex));
                    }else if(Disposal==2)
                    {
                        LCD_Draw_Hline(XPos-Cnt-1,YPos,Cnt+1,bkcolor);
                    }
                    Cnt=0;
                }else
                {
                    if(OldIndex>=0)
                    {
                        if(OldIndex!=Transparency)LCD_Draw_Point(XPos-1,YPos,*(pTrans+OldIndex));
                         else if(Disposal==2)LCD_Draw_Point(XPos-1,YPos,bkcolor); 
                    }
                }
            }
            OldIndex=Index;
        }
        if((OldIndex!=Transparency)||(Disposal==2))
        {
            if(OldIndex!=Transparency)ColorIndex=*(pTrans+OldIndex);
            else ColorIndex=bkcolor;
             if(Cnt)
            {
                LCD_Draw_Hline(XPos-Cnt-1,YPos,Cnt+1,ColorIndex);
            }else LCD_Draw_Point(XEnd,YPos,ColorIndex);     
        }
        //Adjust YPos if image is interlaced 
        if(Interlace)//交織編碼
        {
            YPos+=_aInterlaceOffset[Pass];
            if((YPos-y0)>=Height)
            {
                ++Pass;
                YPos=_aInterlaceYPos[Pass]+y0;
            }
        }else YPos++;        
    }
    return 0;
}      


//恢復成背景色
//x,y:坐標
//gif:gif信息.
//pimge:圖像描述塊信息
void gif_clear2bkcolor(u16 x,u16 y,gif89a* gif,ImageScreenDescriptor pimge)
{
    u16 x0,y0,x1,y1;
    u16 color=gif->colortbl[gif->gifLSD.bkcindex];
    if(pimge.width==0||pimge.height==0)return;//直接不用清除了,原來沒有圖像!!
    if(gif->gifISD.yoff>pimge.yoff)
    {
           x0=x+pimge.xoff;
        y0=y+pimge.yoff;
        x1=x+pimge.xoff+pimge.width-1;;
        y1=y+gif->gifISD.yoff-1;
        if(x0<x1&&y0<y1&&x1<320&&y1<320)LCD_Fill_Rect(x0,y0,x1,y1,color); //設定xy,的范圍不能太大.
    }
    if(gif->gifISD.xoff>pimge.xoff)
    {
           x0=x+pimge.xoff;
        y0=y+pimge.yoff;
        x1=x+gif->gifISD.xoff-1;;
        y1=y+pimge.yoff+pimge.height-1;
        if(x0<x1&&y0<y1&&x1<320&&y1<320)LCD_Fill_Rect(x0,y0,x1,y1,color);
    }
    if((gif->gifISD.yoff+gif->gifISD.height)<(pimge.yoff+pimge.height))
    {
           x0=x+pimge.xoff;
        y0=y+gif->gifISD.yoff+gif->gifISD.height-1;
        x1=x+pimge.xoff+pimge.width-1;;
        y1=y+pimge.yoff+pimge.height-1;
        if(x0<x1&&y0<y1&&x1<320&&y1<320)LCD_Fill_Rect(x0,y0,x1,y1,color);
    }
     if((gif->gifISD.xoff+gif->gifISD.width)<(pimge.xoff+pimge.width))
    {
           x0=x+gif->gifISD.xoff+gif->gifISD.width-1;
        y0=y+pimge.yoff;
        x1=x+pimge.xoff+pimge.width-1;;
        y1=y+pimge.yoff+pimge.height-1;
        if(x0<x1&&y0<y1&&x1<320&&y1<320)LCD_Fill_Rect(x0,y0,x1,y1,color);
    }   
}


//畫GIF圖像的一幀
//gfile:gif文件.
//x0,y0:開始顯示的坐標
u8 gif_drawimage(FIL *gfile,gif89a* gif,u16 x0,u16 y0)
{          
    u32 readed;
    u8 res,temp;    
    u16 numcolors;
    ImageScreenDescriptor previmg;

    u8 Disposal;
    int TransIndex;
    u8 Introducer;
    TransIndex=-1;                  
    do
    {
        res=f_read(gfile,&Introducer,1,(UINT*)&readed);//讀取一個字節
        if(res)return 1;   
        switch(Introducer)
        {         
            case GIF_INTRO_IMAGE://圖像描述
                previmg.xoff=gif->gifISD.xoff;
                 previmg.yoff=gif->gifISD.yoff;
                previmg.width=gif->gifISD.width;
                previmg.height=gif->gifISD.height;

                res=f_read(gfile,(u8*)&gif->gifISD,9,(UINT*)&readed);//讀取一個字節
                if(res)return 1;             
                if(gif->gifISD.flag&0x80)//存在局部顏色表
                {                              
                    gif_savegctbl(gif);//保存全局顏色表
                    numcolors=2<<(gif->gifISD.flag&0X07);//得到局部顏色表大小
                    if(gif_readcolortbl(gfile,gif,numcolors))return 1;//讀錯誤    
                }
                if(Disposal==2)gif_clear2bkcolor(x0,y0,gif,previmg); 
                gif_dispimage(gfile,gif,x0+gif->gifISD.xoff,y0+gif->gifISD.yoff,TransIndex,Disposal);
                 while(1)
                {
                    f_read(gfile,&temp,1,(UINT*)&readed);//讀取一個字節
                    if(temp==0)break;
                    readed=f_tell(gfile);//還存在塊.    
                    if(f_lseek(gfile,readed+temp))break;//繼續向后偏移     
                }
                if(temp!=0)return 1;//Error 
                return 0;
            case GIF_INTRO_TERMINATOR://得到結束符了
                return 2;//代表圖像解碼完成了.
            case GIF_INTRO_EXTENSION:
                //Read image extension*/
                res=gif_readextension(gfile,gif,&TransIndex,&Disposal);//讀取圖像擴展塊消息
                if(res)return 1;
                 break;
            default:
                return 1;
        }
    }while(Introducer!=GIF_INTRO_TERMINATOR);//讀到結束符了
    return 0;
}



//退出當前解碼.
void gif_quit(void)
{
    gifdecoding=0;
}

//解碼一個gif文件
//本例子不能顯示尺寸大於給定尺寸的gif圖片!!!
//filename:帶路徑的gif文件名字
//x,y,width,height:顯示坐標及區域大小.
u8 gif_decode(const u8 *filename,u16 x,u16 y,u16 width,u16 height)
{
    u8 res=0;
    u16 dtime=0;//解碼延時
    gif89a *mygif89a;
    FIL *gfile;
    
    gfile=(FIL*)malloc(sizeof(FIL));
    if(gfile==NULL)res=PIC_MEM_ERR;//申請內存失敗 
    mygif89a=(gif89a*)malloc(sizeof(gif89a));
    if(mygif89a==NULL)res=PIC_MEM_ERR;//申請內存失敗    
    mygif89a->lzw=(LZW_INFO*)malloc(sizeof(LZW_INFO));
    if(mygif89a->lzw==NULL)res=PIC_MEM_ERR;//申請內存失敗 

    if(res==0)//OK
    {
        res=f_open(gfile,(TCHAR *)filename,FA_READ);
        if(res==0)//打開文件ok
        {
            if(gif_check_head(gfile))res=PIC_FORMAT_ERR;
            if(gif_getinfo(gfile,mygif89a))res=PIC_FORMAT_ERR;
            if(mygif89a->gifLSD.width>width||mygif89a->gifLSD.height>height)res=PIC_SIZE_ERR;//尺寸太大.
            else
            {
                x=(width-mygif89a->gifLSD.width)/2+x;
                y=(height-mygif89a->gifLSD.height)/2+y;
            }
            gifdecoding=1;
            while(gifdecoding&&res==0)//解碼循環
            {     
                res=gif_drawimage(gfile,mygif89a,x,y);//顯示一張圖片
                if(mygif89a->gifISD.flag&0x80)gif_recovergctbl(mygif89a);//恢復全局顏色表
                if(mygif89a->delay)dtime=mygif89a->delay;
                else dtime=10;//默認延時
                while(dtime--&&gifdecoding)Delay_Ms(10);//延遲
                if(res==2)
                {
                    res=0;
                    break;
                }
            }
        }
        f_close(gfile);
    }   
    free(gfile);
    free(mygif89a->lzw);
    free(mygif89a); 
    return res;
}

這是UCGUI的庫,性能差一點的單片機解碼起來有點慢,看起來有點卡,哦對了還是用了fatfs文件系統,移植接口如下

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2014        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/

#include "diskio.h"        /* FatFs lower layer API */
#include "sdcard.h"        /* Example: MMC/SDC contorl */

#define MMC        0    /* Example: Map MMC/SD card to drive number 1 */


/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
    BYTE pdrv        /* Physical drive nmuber to identify the drive */
)
{
    DSTATUS stat;
    int result;

    switch (pdrv) 
    {
    case MMC :
        result = SDCARD_Get_Sector_Count();
        if(result == 0)stat = STA_NOINIT;
        else stat = 0;//OK
        return stat;
    }
    return STA_NOINIT;
}



/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
    BYTE pdrv                /* Physical drive nmuber to identify the drive */
)
{
    DSTATUS stat;
    int result;

    switch (pdrv) 
    {
    case MMC :
        result = SDCARD_Init();
        if(result == 0)stat = 0;
        else //STM32 SPI的bug,在sd卡操作失敗的時候如果不執行下面的語句,可能導致SPI讀寫異常
        {
            SPI2_Set_Speed(SPI_SPEED_256);
            SPI2_Write_Read_Byte(0xff);//提供額外的8個時鍾
            SPI2_Set_Speed(SPI_SPEED_4);
            stat = STA_NOINIT;//error
        }
        return stat;
    }
    return STA_NOINIT;
}



/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
    BYTE pdrv,        /* Physical drive nmuber to identify the drive */
    BYTE *buff,        /* Data buffer to store read data */
    DWORD sector,    /* Sector address in LBA */
    UINT count        /* Number of sectors to read */
)
{
    DRESULT res;
    int result;

    switch (pdrv) 
    {

    case MMC :
        result = SDCARD_Read_Sector(buff, sector, count);
        if(result == 0)res = RES_OK;
        else res = RES_ERROR;
        return res;

    }

    return RES_PARERR;
}



/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

#if _USE_WRITE
DRESULT disk_write (
    BYTE pdrv,            /* Physical drive nmuber to identify the drive */
    const BYTE *buff,    /* Data to be written */
    DWORD sector,        /* Sector address in LBA */
    UINT count            /* Number of sectors to write */
)
{
    DRESULT res;
    int result;

    switch (pdrv) 
    {
    case MMC :
        result = SDCARD_Write_Sector((u8*)buff, sector, count);
        if(result == 0)res = RES_OK;
        else res = RES_ERROR;
        return res;

    }

    return RES_PARERR;
}
#endif


/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

#if _USE_IOCTL
DRESULT disk_ioctl (
    BYTE pdrv,        /* Physical drive nmuber (0..) */
    BYTE cmd,        /* Control code */
    void *buff        /* Buffer to send/receive control data */
)
{
    DRESULT res;

    switch (pdrv) 
    {
    case MMC :
        switch(cmd)
        {
            case CTRL_SYNC:
                SD_CS=0;
                if(SDCARD_Wait_Ready()==0)res = RES_OK; 
                else res = RES_ERROR;      
                SD_CS=1;
                break;     
            case GET_SECTOR_SIZE:
                *(WORD*)buff = 512;
                res = RES_OK;
                break;     
            case GET_BLOCK_SIZE:
                *(WORD*)buff = 8;
                res = RES_OK;
                break;     
            case GET_SECTOR_COUNT:
                *(DWORD*)buff = SDCARD_Get_Sector_Count();
                res = RES_OK;
                break;
            default:
                res = RES_PARERR;
                break;
        }
        return res;
    }

    return RES_PARERR;
}
#endif

到這里基本就完成了,還需要一個控制界面能控制上一幅圖下一幅圖的我們留到后面一章再說,工程代碼的下載地址為

http://download.csdn.net/detail/dengrengong/8542869

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM