第四章 載入並顯示PNG圖片

吳吉慶發表於2014-12-29

在第三章中,我們講解了如何在SDL程式中載入並顯示BMP影像。雖然SDL直接支援BMP圖片的載入,但BMP格式的圖片佔用較多的硬碟空間。如果遊戲的圖片資源都使用BMP格式,會增大我們小遊戲的體積。

那麼我們可以使用別的圖片格式嗎?SDL_image幫我們提供瞭解決方案。使用SDL_image,可以載入JPG,PNG,GIF,TIF等多種圖片格式。我們將使用流行的PNG格式。

一、安裝SDL_image

這一節其實主要為在windows下使用MinGW的使用者寫的,因為Linux上安裝SDL_image的開發包是非常簡單的事。

下面主要講解怎麼在MinGw中安裝SDL_image,使用Linux的使用者可以直接跳到下一節。

SDL_image 1.2的主頁在這裡:http://www.libsdl.org/projects/SDL_image/release-1.2.html,我們要從這裡下載 SDL_image的開發包。咦?怎麼只有VC的開發包,沒有MinGW的開發包呢?

沒有關係,就下載VC的開發包吧:http://www.libsdl.org/projects/SDL_image/release/SDL_image-devel-1.2.12-VC.zip

一個開發庫如果以動態連結庫的形式釋出,包含三個組成部分:標頭檔案、動態庫、匯入庫。下面我們一一搞定。

  • 解壓壓縮包,把include目錄中的SDL_image.h拷貝到MinGW的include/SDL目錄中,標頭檔案搞定。
  • 開啟開發包中的lib目錄,發現有SDL_image.dll和jpeg、png等圖片格式的解碼庫,把這些動態庫全部放入MinGW中的bin目錄中,動態庫搞定。
  • lib目錄中還有一個SDL_image.lib,這是VC格式的匯入庫,MinGW能識別嗎?把它放入MinGW的lib目錄中,我們試試吧。

修改makefile,在連結選項後增加 -lSDL_image,表示生成執行程式時,連結SDL_image庫。

game: main.c
    gcc -o game main.c `sdl-config --cflags --libs` -lSDL_image

make一下,發現成功了。原來MinGW可以識別VC的lib格式的匯入庫,真是寬容,真是偉大!

其實,如果沒有VC的匯入庫,MinGW還是有辦法自己生成匯入庫的。比如我們有標頭檔案和SDL_image.dll,分兩步生成SDL_image.dll的匯入庫。

  1. pexports (從網上搜尋下載)匯出SDL_image.dll中的函式定義: pexports SDL_image.dll > SDL_image.def。生成的SDL_image.def是個文字檔案,可以用文字編輯器檢視。
  2. 用mingw自帶的dlltool生成匯入庫: dlltool -d SDL_image.def -l libSDL_image.dll.a,會生成libSDL_image.dll.a,這就是我們需要的東西,dll.a的字尾名告訴MinGW這是對應dll的匯入庫。把它放入MinGW的lib目錄就可以了。

SDL_image安裝好後,還有一件事,就是SDL_image的文件,文件頁面在這裡: http://www.libsdl.org/projects/SDL_image/docs/SDL_image.html。可以下載下來看,也可以線上看。

二、裝載和顯示PNG圖片

這裡我們以下面圖片(檔名: 200x125-skill.png)為例:

200x125-skill.png

如何裝載 200x125-skill.png,最簡單的方式是使用SDL_imageIMG_Load函式,原型如下:

SDL_Surface *IMG_Load(const char *file)

給定該函式一個圖片路徑,得到一個包含影像資料的SDL surface。得到surface後,後續的處理就和上面一樣了。程式碼片段如下:

/* 裝載PNG圖片,得到一個SDL頁面 */
SDL_Surface *temp = IMG_Load("200x125-skill.png");
/* 轉換SDL頁面,得到一個畫素格式和screen相同的頁面 */
SDL_Surface *skill_surface = SDL_DisplayFormat(temp);
SDL_FreeSurface(temp);

/* 在screen的中間選定一個矩形區域來顯示影像 */
SDL_Rect dest_rect = {(screen->w - 200)/2, (screen->h - 125)/2, 200, 125};
SDL_BlitSurface(skill_surface, NULL, screen, &dest_rect);
SDL_Flip(screen);

注意,別忘了在程式開頭#include <SDL_image.h>make然後執行一下程式,看看效果如何。

三、設定螢幕背景色

我們使用的圖片背景色是黑色,剛好screen預設的頁面顏色也是黑色,如果screen是其它的背景色,顯示效果就沒有那麼好了。

現在我們用SDL_FillRect函式把螢幕顯示區域填充成別的顏色。

int SDL_FillRect(SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color);
  • dst表示要填充顏色的頁面, 我們要填充的是screen頁面。
  • dstrect指向要填充的矩形區域,我們要填充整個screen頁面,可以傳遞NULLdstrect
  • color指定要填充的顏色,我們用十六進位制來表示這個值,這次我們給它的值是:0xff0099000x之後每兩位數表示一個位元組,4個位元組一次表示Alpha值(透明度)、紅色分量R、綠色分量G和藍色分量B。這裡我們將紅色分量和藍色分量置為0,而綠色分量是0x99。因此0xff009900表示一個暗綠色。

綜上,SDL_FillRect(screen, NULL, 0xff009900);將把螢幕填充成暗綠色。 改寫原來的程式碼片段,增加SDL_FillRect語句:

/* Fill screen with some color */
SDL_FillRect(screen, NULL, 0xff009900);

/* Load PNG and Display */
SDL_Surface *temp = IMG_Load("200x125-skill.png");
SDL_SetColorKey(temp, SDL_SRCCOLORKEY|SDL_RLEACCEL, 0x00000000);
SDL_Surface *skill_surface = SDL_DisplayFormat(temp);
SDL_FreeSurface(temp);

SDL_Rect dest_rect = {(screen->w - 200)/2, (screen->h - 125)/2, 200, 125};
SDL_BlitSurface(skill_surface, NULL, screen, &dest_rect);
SDL_Flip(screen);

make然後執行程式,效果如圖:

影像的黑色背景很突兀

現在影像的黑色背景給人的感覺很不好。

四、去除圖片背景色

在顯示一個頁面時,怎麼把它的背景色去掉呢?使用SDL_SetColorKey來設定頁面的透明色。

int SDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key);

surface是要設定關鍵色的頁面,我們這裡要過濾背景的頁面是skill_surface

flag的值通常是 SDL_SRCCOLORKEY | SDL_RLEACCEL, 其中 SDL_SRCCOLORKEY 表示為頁面設定的關鍵色在BlitSurface時將被過濾掉。SDL_RLEACCEL表示用 行程編碼 (?) 提高BlitSurface的效率。

key是要設定的關鍵色,即在BlitSurface時要過濾掉的透明色。是32位整數,包含4個位元組。可以表示為0xAARRGGBB。其中AA是表示alpha通道的位元組,RR, GG, BB分別表示紅綠藍三個分量。因為我們圖片的背景色是純黑,直接把0傳遞給key即可。

注意:當你知道圖片的背景色(即你知道RGB的分量各是多少)時,你並不能直接用這個值來設定關鍵色。因為你圖片的畫素格式轉換成screen的畫素格式時,背景色RGB的值極可能改變,這樣,背景色仍然過濾不掉。這時候,我們可以把RGB的值傳遞給SDL_MapRGB函式,從而得到一個轉換後的Uint32的關鍵色表示。如果用黑色做圖片的背景色,則可以省略這一步,因為不管畫素格式怎麼變,RGB分量的值始終是0。

綜上,我們可以呼叫SDL_SetColorKey(skill_surface, SDL_SRCCOLORKEY|SDL_RLEACCEL, 0)來將skill_surface的黑色背景色設為透明色。

載入並顯示圖片的程式碼變為:

/* Load PNG */
SDL_Surface *temp = IMG_Load("200x125-skill.png");
/* set transparent color before converting surface with SDL_DisplayFormat */
/* in order to take advantage of hardware acceleration*/
SDL_SetColorKey(temp, SDL_SRCCOLORKEY|SDL_RLEACCEL, 0x00000000);
SDL_Surface *skill_surface = SDL_DisplayFormat(temp);
SDL_FreeSurface(temp);

SDL_Rect dest_rect = {(screen->w - 200)/2, (screen->h - 125)/2, 200, 125};
SDL_BlitSurface(skill_surface, NULL, screen, &dest_rect);
SDL_Flip(screen);

重新make,執行./game,效果如圖:

濾掉背景色的影像顯示

五、小結

第4章的原始碼和資源可以從這裡檢視、下載: https://github.com/jollywing/make-linux-rpg/tree/master/chap04

這一章中,我們學到了哪些新東西呢?

  1. 在MinGW下安裝SDL_image開發庫。
  2. 給定dll檔案,如何在MinGW下生成匯出庫。(注意,這個方法可能要視dll中函式的呼叫方式做適當調整)
  3. SDL_imageIMG_Load的函式載入PNG, JPG, GIF等格式的圖片。當然,用IMG_Load一樣可以載入BMP圖片。
  4. SDL_FillRect填充螢幕背景。
  5. SDL_SetColorKey設定頁面的關鍵色,這樣當該頁面Blit到其它頁面上時,關鍵色將被過濾掉。

下一章,我們將學習更激動人心的內容:動起來的畫面


如果你喜歡我的文章,可以點 這裡 給我打賞,五分一毛也是對我的認同。

相關文章