1. 程式人生 > 其它 >【第3版emWin教程】第23章 emWin6.x的PNG圖片顯示

【第3版emWin教程】第23章 emWin6.x的PNG圖片顯示

教程不斷更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429

第23章 emWin6.x的PNG圖片顯示

本期主要講emWin支援的PNG圖片顯示,官方支援的主要有兩種顯示方法,一種方法是直接從外部儲存器讀取資料並顯示,這種方法的好處就是不需要大的RAM,每次讀取一些資料顯示一次,缺點就是顯示速度稍慢。另一種是從外部儲存器讀取整個圖片到RAM(比如內部SRAM,外部SRAM或者外部SDRAM),然後再顯示圖片,這種方法的顯示速度要稍快些。一般情況下,PNG圖片主要是用來做圖示使用,圖示都比較小,所以兩種方法區別不大,後者稍快些。

23.1 初學者重要提示

23.2 PNG圖片基礎知識

23.3 PNG圖片的API函式及其顯示方法

23.4 實驗例程說明(RTOS)

23.5 實驗例程說明(裸機)

23.6 總結

23.1 初學者重要提示

1、 emWin庫中是不包含PNG的,需要使用者自己新增PNG庫,對於初學者來說,這點要特別注意。

2、 PNG圖片顯示的所有API函式在emWin手冊中都有講解,下圖是中文版手冊裡面API函式的位置

下圖是英文版手冊裡面API函式的位置:

3、 本章教程使用的外部儲存器是SD卡,實際專案中使用任何其它型別的儲存器都可以的,支不支援檔案系統都沒有關係的,使用方法與本章教程一樣,使用者要做的就是把圖片從外部儲存器讀出即可。

23.2 PNG圖片基礎知識

關於PNG圖片格式方面的知識,推薦大家看wiki百科上面的介紹:

推薦初學者瞭解一下PNG圖片的格式,如果沒有了解也是沒有任何關係的,直接呼叫emWin的API函式就可以顯示PNG圖片了。

----------------------------------------------------------------------------------------------------------

下面這點小知識還是要知道的:

行動式網路圖形(Portable Network Graphics,PNG)是一種無失真壓縮的點陣圖圖形格式,支援索引、灰度、RGB三種顏色方案以及Alpha通道等特性。PNG的開發目標是改善並替換GIF作為適合網路傳輸的格式而且不需專利許可,所以被廣泛應用於網際網路及其它方面上。PNG另一個非正式的名稱來源為遞迴縮寫:“PNG is Not GIF”。

PNG圖片的特性:

  • 支援256色調色盤技術以產生小體積檔案。
  • 最高支援48位真彩色影象以及16位灰度影象。
  • 支援Alpha通道的透明/半透明特性。
  • 支援影象亮度的Gamma校準資訊。
  • 支援儲存附加文字資訊,以保留影象名稱、作者、版權、創作時間、註釋等資訊。
  • 使用無失真壓縮。
  • 漸近顯示和流式讀寫,適合在網路傳輸中快速顯示預覽效果後再展示全貌。
  • 使用CRC防止檔案出錯。
  • 最新的PNG標準允許在一個檔案記憶體儲多幅影象。

emWin對PNG的支援來自Glenn Randers-Pehrson、Guy Eric Schalnat和Andreas Dilger的libpng庫,該庫可在www.libpng.org下免費獲得。emWin對該庫的使用符合GUI\PNG\png.h中的版權通知,通知中允許使用該庫,而沒有任何限制。

23.3 PNG圖片的API函式及其顯示方法

當前emWin支援的API函式有如下6個:

從上面的表格中可以看出,emWin支援PNG檔案顯示主要有兩種型別的函式,一類是以Ex結尾的函式,這種函式顯示PNG圖片是一邊從外部儲存器載入資料一邊顯示,顯示速度相對較慢,適用於記憶體較小的場合。另一類是不以Ex結尾的函式,這種函式直接從指定的地址讀取資料進行顯示(注意,這裡的地址需是匯流排式地址,比如外部SDRAM,外部SRAM,內部Flash和內部SRAM都可以),顯示速度相對稍快。

本章教程會對這兩種方式都進行說明:

  • int GUI_PNG_Draw(const void * pFileData, int FileSize, int x0, int y0);

此函式直接從地址pFileData,讀取PNG圖片的資料,將圖片顯示到使用者設定的位置(x0, y0)。

  • int GUI_PNG_GetXSizeEx(GUI_GET_DATA_FUNC * pfGetData, void * p);

此函式通過其回撥函式pfGetData讀取PNG圖片的資料,從而實現邊讀取圖片資料邊顯示的功能,將圖片顯示到使用者設定的位置(x0, y0)。

另外還有一個知識點需要初學者瞭解,emWin解碼一張PNG圖片需要多少RAM?這主要有兩部分組成,PNG解碼本身需要大約21KB的RAM,外加圖片的長度和寬度對RAM需求的影響,具體公式如下:

大約RAM要求= (xSize + 1)* ySize* 4 + 21Kbytes。 xSize代表長度,ySize代表寬度。

23.3.1 PNG庫的移植方法

emWin的庫中是不含有PNG庫的,需要使用者自行新增,新增也比較簡單,只需使用者把原始碼檔案新增到工程裡面就可以使用了。

PNG庫的下載地址:www.segger.com/link/emwin_png.zip 。下載軟體包後,解壓出來的是如下四個版本(如果官方升級了,移植方法是一樣的):

  • 第1步:開啟V616資料夾,將PNG資料夾及其裡面的原始碼檔案全部複製到emWin工程的emWin資料夾裡面(其它任意資料夾都是可以的,不限制)。

  • 第2步:以MDK為例,將PNG的原始碼檔案新增到MDK工程裡面(IAR同樣的設定),下面是部分原始碼檔案的截圖。

  • 第3步:以MDK為例,新增PNG標頭檔案的路徑(IAR類似的設定),新增完畢後別忘了點選OK。

  • 第4步:新增完畢後,驗證是否已經新增成功,可以進行一次全編譯,全編譯後看到有幾十個警告,這個是正常的,而使用IAR時警告很少。

至此,PNG的庫就新增成功了。剩下就可以呼叫PNG的API函數了。

23.3.2 繪製已經載入到儲存器的PNG圖片

繪製載入到儲存器的PNG圖片主要是通過函式GUI_PNG_Draw來實現,下面我們分2步來說明如何將SD卡中的PNG圖片顯示到LCD上面。

  • 第1步:將PNG圖片複製到SD卡的根目錄下,然後通過emWin的動態記憶體管理函式申請動態記憶體並將PNG檔案載入進來, 這裡我們用的是外部SDRAM做emWin的動態記憶體。
char *_acBuffer;
    GUI_HMEM hMem;

    /* 開啟檔案 */        
    result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
    if (result != FR_OK)
    {
        return;
    }
     
    /* 申請一塊記憶體空間 並且將其清零 */
    hMem = GUI_ALLOC_AllocZero(file.obj.objsize);
    
    /* 將申請到記憶體的控制代碼轉換成指標型別 */
    _acBuffer = GUI_ALLOC_h2p(hMem);

    /* 讀取檔案到動態記憶體 */
    result = f_read(&file, _acBuffer, file.obj.objsize, &bw);
    if (result != FR_OK)
    {
        return;
    }
  • 第2步:將載入到emWin動態記憶體的PNG圖片直接顯示即可,然後結合第1步,完整的程式碼如下:
/*
*********************************************************************************************************
*    函 數 名: _ShowPNG2
*    功能說明: 顯示PNG圖片,使用函式GUI_PNG_Draw
*    形    參: sFilename  要讀取的檔名
*                     x  要顯示的x軸座標位置
*                     y  要顯示的y軸座標位置
*    返 回 值: 無
*********************************************************************************************************
*/
void _ShowPNG2(const char *sFilename, int x, int y) 
{
    char *_acBuffer;
    GUI_HMEM hMem;
    

    /* 開啟檔案 */        
    result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
    if (result != FR_OK)
    {
        return;
    }
     
    /* 申請一塊記憶體空間 並且將其清零 */
    hMem = GUI_ALLOC_AllocZero(file.obj.objsize);
    
    /* 將申請到記憶體的控制代碼轉換成指標型別 */
    _acBuffer = GUI_ALLOC_h2p(hMem);

    /* 讀取檔案到動態記憶體 */
    result = f_read(&file, _acBuffer, file.obj.objsize, &bw);
    if (result != FR_OK)
    {
        return;
    }
    
    /* 顯示PNG圖片 */
    GUI_PNG_Draw(_acBuffer, file.obj.objsize, x, y);

    /* 釋放動態記憶體hMem */
    GUI_ALLOC_Free(hMem);
    
    /* 關閉檔案 */
    f_close(&file);
}

通過上面兩步就完成了PNG圖片的動態顯示,使用者要顯示哪個圖片,呼叫函式_ShowPNG2()即可,比如要顯示1.png圖片,可以呼叫_ShowPNG2("1.png"),這種方式顯示PNG圖片相對稍快些。實際顯示效果參看本章節配套的實驗例程說明。

23.3.3 繪製無需載入到儲存器的PNG圖片

繪製無需載入到儲存器的PNG圖片主要是通過函式GUI_PNG_DrawEx來實現,這種方式的優點是需要的記憶體小,但是顯示速度稍慢。下面我們分2步來說明如何將SD卡中的PNG圖片顯示到LCD上面。

  • 第1步:將PNG圖片複製到SD卡的根目錄下,然後直接呼叫函式GUI_GIF_DrawSubEx就可以顯示。(注意,這裡的_GetData函式是與前面BMP,JPEG和GIF圖片的_GetData是不同的)
/*
*********************************************************************************************************
*    函 數 名: _GetData
*    功能說明: 被函式GUI_PNG_DrawEx呼叫
*    形    參: p             FIL型別資料
*             NumBytesReq   請求讀取的位元組數
*             ppData        資料指標
*             Off           如果Off = 1,那麼將重新從其實位置讀取                 
*    返 回 值: 返回讀取的位元組數
*********************************************************************************************************
*/
static int _GetData(void * p, const U8 ** ppData, unsigned NumBytesReq, U32 Off) 
{
    static int FileAddress = 0;
    FIL *file;
    UINT    NumBytesRead;
    U8     * pData;

    pData  = (U8 *)*ppData;
    file = (FIL *)p;
    
    //
    // 設定資料讀取位置
    //
    if(Off == 1) FileAddress = 0;
    else FileAddress = Off;
    result =f_lseek(file, FileAddress);
    
    //
    // 讀取資料到快取
    //
    result = f_read(file, pData, NumBytesReq, &NumBytesRead);

    //
    // 返回讀取大小
    //
    return NumBytesRead;

}

/*
*********************************************************************************************************
*    函 數 名: _ShowPNG1
*    功能說明: 顯示PNG圖片,使用函式GUI_PNG_DrawEx
*    形    參: sFilename 要顯示的圖片名字
*                     x  要顯示的x軸座標位置
*                     y  要顯示的y軸座標位置
*    返 回 值: 無
*********************************************************************************************************
*/
static void _ShowPNG1(const char * sFilename, int x, int y) 
{    

    /* 開啟檔案 */        
    result = f_open(&file, sFilename, FA_OPEN_EXISTING | FA_READ | FA_OPEN_ALWAYS);
    if (result != FR_OK)
    {
        return;
    }
    
    /* 顯示PNG圖片 */
    GUI_PNG_DrawEx(_GetData, &file, x, y);

    /* 關閉檔案 */
    f_close(&file);
}
  • 第2步:使用者要顯示指定的檔案1.png,呼叫函式_ShowPNG1("1.png")即可顯示。

通過上面2步就完成了PNG圖片的動態顯示,這種方式顯示PNG圖片速度稍慢,實際顯示效果參看本章節配套的實驗例程說明。

23.3.4 將PNG格式的圖片轉換成C檔案

使用這種方法可以方便的將較小的PNG格式圖片存到內部Flash。將PNG圖片轉換成C檔案需要用到Bin2C.exe小軟體。比如,我們將1.png圖片(此圖片在本章教程配套例子的的Doc資料夾裡面)轉換成C檔案,生成的程式碼如下:

static const unsigned char _ac1[12721UL + 1] = {
  0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x08, 0x06, 0x00, 0x00, 0x00, 0xE2, 0x98, 0x77, 0x38, 0x00, 0x00, 0x20, 0x00, 0x49, 0x44, 0x41,
  0x54, 0x78, 0x9C, 0xDD, 0xBD, 0x79, 0x94, 0x5C, 0xD7, 0x7D, 0xDF, 0xF9, 0xB9, 0xF7, 0x2D, 0xB5, 0x74, 0xF5, 0xDE, 0x68, 0x00, 0x8D, 0x46, 0x03, 0xC4, 0x4A, 0x82, 0x20, 0x08, 0x82, 0x20, 0x09, 0xAE, 0xA2, 0x25, 0x93, 0x12, 0x29, 0x5B, 0x92,
  0x65, 0x91, 0x11, 0x6D, 0x29, 0x56, 0x9C, 0x4C, 0x1
/* 其餘資料省略 */
}

使用者顯示時呼叫函式GUI_PNG_Draw(_ac1, sizeof(_ac1), 0, 0)即可。

23.4 實驗例程說明(RTOS)

配套例子:

V7-524_emWin6.x實驗_PNG圖片顯示(RTOS)

實驗目的:

  1. 學習emWin的PNG圖片顯示。
  2. emWin功能的實現在MainTask.c檔案裡面。

實驗內容:

1、K1按鍵按下,串列埠或者RTT列印任務執行情況(串列埠波特率115200,資料位8,奇偶校驗位無,停止位1)。

2、(1) 凡是用到printf函式的全部通過函式App_Printf實現。

(2) App_Printf函式做了訊號量的互斥操作,解決資源共享問題。

3、預設上電是通過串列埠列印資訊,如果使用RTT列印資訊:

MDK AC5,MDK AC6或IAR通過使能bsp.h檔案中的巨集定義為1即可

#define Enable_RTTViewer 1

4、各個任務實現的功能如下:

App Task Start 任務 :啟動任務,這裡用作BSP驅動包處理。

App Task MspPro任務 :訊息處理,這裡用作LED閃爍。

App Task UserIF 任務 :按鍵訊息處理。

App Task COM 任務 :暫未使用。

App Task GUI 任務 :GUI任務。

μCOS-III任務除錯資訊(按K1按鍵,串列埠列印):

RTT 列印資訊方式:

程式設計:

任務棧大小分配:

μCOS-III任務棧大小在app_cfg.h檔案中配置:

#define APP_CFG_TASK_START_STK_SIZE 512u

#define APP_CFG_TASK_MsgPro_STK_SIZE 2048u

#define APP_CFG_TASK_COM_STK_SIZE 512u

#define APP_CFG_TASK_USER_IF_STK_SIZE 512u

#define APP_CFG_TASK_GUI_STK_SIZE 2048u

任務棧大小的單位是4位元組,那麼每個任務的棧大小如下:

App Task Start 任務 :2048位元組。

App Task MspPro任務 :8192位元組。

App Task UserIF 任務 :2048位元組。

App Task COM 任務 :2048位元組。

App Task GUI 任務 :8192位元組。

系統棧大小分配:

μCOS-III的系統棧大小在os_cfg_app.h檔案中配置:

#define OS_CFG_ISR_STK_SIZE 512u

系統棧大小的單位是4位元組,那麼這裡就是配置系統棧大小為2KB

emWin動態記憶體配置:

GUIConf.c檔案中的配置如下:

#define EX_SRAM   1/*1 used extern sram, 0 used internal sram */

#if EX_SRAM
#define GUI_NUMBYTES  (1024*1024*24)
#else
#define GUI_NUMBYTES  (100*1024)
#endif

通過巨集定義來配置使用內部SRAM還是外部的SDRAM做為emWin的動態記憶體,當配置:

#define EX_SRAM 1 表示使用外部SDRAM作為emWin動態記憶體,大小24MB。

#define EX_SRAM 0 表示使用內部SRAM作為emWin動態記憶體,大小100KB。

預設情況下,本教程配套的所有emWin例子都是用外部SDRAM作為emWin動態記憶體。

emWin介面顯示效果:

800*480解析度介面效果。

23.5 實驗例程說明(裸機)

配套例子:

V7-523_emWin6.x實驗_PNG圖片顯示(裸機)

實驗目的:

  1. 學習emWin的PNG圖片顯示。
  2. emWin功能的實現在MainTask.c檔案裡面。

emWin介面顯示效果:

800*480解析度介面效果。

emWin動態記憶體配置:

GUIConf.c檔案中的配置如下:

#define EX_SRAM   1/*1 used extern sram, 0 used internal sram */

#if EX_SRAM
#define GUI_NUMBYTES  (1024*1024*24)
#else
#define GUI_NUMBYTES  (100*1024)
#endif

通過巨集定義來配置使用內部SRAM還是外部的SDRAM做為emWin的動態記憶體,當配置:

#define EX_SRAM 1 表示使用外部SDRAM作為emWin動態記憶體,大小24MB。

#define EX_SRAM 0 表示使用內部SRAM作為emWin動態記憶體,大小100KB。

預設情況下,本教程配套的所有emWin例子都是用外部SDRAM作為emWin動態記憶體。

23.6 總結

本章節主要為大家講解了PNG圖片的繪製,PNG圖片在實際專案中用的比較少。對於初學者來說,會使用即可,以後用到了再深入研究。