【第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百科上面的介紹:
- https://en.wikipedia.org/wiki/Portable_Network_Graphics 講解非常詳細。
- 如果覺得英文版讀起來比較吃力些,可以看wiki中文版,只是資料沒有英文的詳細:https://zh.wikipedia.org/wiki/PNG 。
- 更多PNG圖片的知識可以google或者百度進行了解。
推薦初學者瞭解一下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)
實驗目的:
- 學習emWin的PNG圖片顯示。
- 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圖片顯示(裸機)
實驗目的:
- 學習emWin的PNG圖片顯示。
- 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圖片在實際專案中用的比較少。對於初學者來說,會使用即可,以後用到了再深入研究。