第三章 載入並顯示BMP影像
一、向螢幕畫圖:SDL_BlitSurface
怎麼顯示一個影像?顯然,首先要將它從硬碟載入記憶體。載入記憶體之後呢?
上一節中我們建立了一個screen
的螢幕表面,你可以把它理解為對應螢幕顯示區域的一塊視訊記憶體。如果我們把載入記憶體的影像資料複製到screen
的某個位置,在螢幕顯示區域就應該能看到我們的影像。確實是這樣。
那麼,怎麼把影像資料複製到screen所指的資料結構中?這就引出了今天的主角SDL_BlitSurface
函式。函式原型:
int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);
src
是源頁面,也就是我們存放影像資料的頁面。srcrect
是源頁面上要複製的矩形區域。如果要複製整個源頁面,則傳遞NULL
給srcrect
即可。dst
是目標頁面,在我們的例子中,它就是螢幕表面screen
。dstrect
是目標頁面上的矩形區域,源頁面的上選定的區域將顯示在這個矩形區域中。
現在srcrect
確定為NULL
,dst
確定為screen
,未定的是src
頁面和dstrect
,dstrect
要視src
頁面的大小而定。
src
頁面是要裝載影像資料的頁面。SDL自身支援BMP格式圖片的載入,SDL_LoadBMP
會把bmp圖片載入到一個SDL_Surface
頁面。
我們要使用的bmp圖片是和程式在同一目錄的card.bmp
。SDL_Surface *temp = SDL_LoadBMP("card.bmp");
把card.bmp的資料裝載到temp頁面中,現在temp
就是即將BlitSurface
的源頁面,因此,BlitSurface
的src
引數也確定了。
dstrect
是一個指向SDL_Rect
結構的地址。SDL_Rect
有四個屬性:x, y, w, h,分別是矩形左上角的x、y座標以及矩形的寬和高。我們想讓影像顯示在螢幕區域的左上角,可以把x和y設為0,把w和h設為源頁面的寬和高即可。假設源頁面是temp
,我們可以這樣定義目標矩形:SDL_Rect dest_rect = {0, 0, temp->w, temp->h};
,然後將dest_rect
的地址傳遞給dstrect
即可。
再來回顧一下我們要進行的步驟:
- 載入影像到頁面(用
SDL_LoadBMP
, 搞定) - 用
SDL_BlitSurface
函式將影像頁面複製到螢幕表面(四個引數都確定了,搞定)
現在我們可以顯示影像了,程式碼如下:
SDL_Surface *temp = SDL_LoadBMP("card.bmp");
SDL_Rect dest_rect = {0, 0, temp->w, temp->h};
SDL_BlitSurface(temp, NULL, screen, &dest_rect);
make
一下,程式成功生成。執行一下,為什麼沒有影像?
二、雙緩衝和SDL_Flip
你記得我們第二節建立頁面的時候使用了雙緩衝的標誌嗎?SDL_DOUBLEBUF
。 雙緩衝是在視訊記憶體中建立了兩塊區域,一塊用於螢幕顯示,另一塊是離屏頁面,用於在後臺作圖。當後臺影像繪製好後,呼叫SDL_Flip
對換離屏頁面和顯示頁面,這樣原來在離屏頁面上繪製的內容就顯示出來了。
雙緩衝可以避免畫面閃爍,想想看,為什麼?當遊戲的每一幀有大量繪圖工作時,都要在後臺完成所有繪製,再一次性顯示到螢幕表面。如果直接在螢幕表面繪製,就會看到各個影像的繪製有先有後,給人的感覺就是畫面閃爍。
剛才我們只是把影像畫到了screen
的離屏頁面,難怪螢幕沒有顯示。趕緊呼叫SDL_Flip
翻轉screen
吧。現在程式變成這樣:
SDL_Surface *temp = SDL_LoadBMP("card.bmp");
SDL_Rect dest_rect = {0, 0, temp->w, temp->h};
SDL_BlitSurface(card_surface, NULL, screen, &dest_rect);
/* DO NOT FORGET! */
SDL_Flip(screen);
程式執行結果如圖:
三、優化效能,影像載入後轉換畫素格式
對上面的程式,我們要做一點優化。當bmp影像裝載入頁面後,其畫素格式和螢幕頁面的畫素格式並不相同,在BlitSurface
時需要進行轉換。多次BlitSurface
就要多次轉換,這樣很低效。
高效的做法是載入後就把頁面轉換成和螢幕頁面相同的畫素格式,這樣以後再BlitSurface
時就不用再轉換了。可以用SDL_DisplayFormat
來完成這一步。
SDL_Surface *SDL_DisplayFormat(SDL_Surface *surface);
該函式把一個頁面的資料轉換畫素格式後複製到新的頁面,並返回新的頁面。原來的程式碼片段變成這樣。
SDL_Surface *temp = SDL_LoadBMP("card.bmp");
/* 轉換生成新的頁面,畫素格式和screen一致 */
SDL_Surface *card_surface = SDL_DisplayFormat(temp);
/* temp已經沒用了,把它釋放掉,回收記憶體 */
SDL_FreeSurface(temp);
SDL_Rect dest_rect = {0, 0, card_surface->w, card_surface->h};
SDL_BlitSurface(card_surface, NULL, screen, &dest_rect);
/* DO NOT FORGET! */
SDL_Flip(screen);
四、完整的程式碼一覽
完整的程式程式碼如下:
/* usage: gcc -o game main.c `sdl-config --cflags --libs` */
#include <stdio.h>
#include <SDL.h>
int main(int argc, char *argv[])
{
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0) {
printf("Unable to initialize SDL: %s\n", SDL_GetError());
exit(1);
}
atexit(SDL_Quit);
SDL_Surface *screen = SDL_SetVideoMode(320, 480, 0, SDL_HWSURFACE|SDL_DOUBLEBUF);
if (screen == NULL) {
printf("Unable to set video mode: %s\n", SDL_GetError());
exit(1);
}
SDL_WM_SetCaption("Hello, Linux Game!", NULL);
SDL_Surface *temp = SDL_LoadBMP("card.bmp");
if(temp == NULL) {
printf("Load bmp image failed!\n");
exit(1);
}
SDL_Surface *card_surface = SDL_DisplayFormat(temp);
SDL_FreeSurface(temp);
SDL_Rect dest_rect = {0, 0, card_surface->w, card_surface->h};
SDL_BlitSurface(card_surface, NULL, screen, &dest_rect);
// DO NOT FORGET!
SDL_Flip(screen);
SDL_FreeSurface(card_surface);
/* To pause the program */
while(1){
SDL_Delay(100);
}
return 0;
}
注意,程式最後用了一個while
死迴圈防止程式一閃就退出,在命令列你可以用C-c結束程式。之後我們會介紹更優雅的退出程式的方式。
while
迴圈中的SDL_Delay(100)
是讓程式阻塞100毫秒,這樣可以避免空迴圈把CPU耗盡。
完整的程式、makefile以及圖片資源點這裡檢視:https://github.com/jollywing/make-linux-rpg/tree/master/chap03。
五、小結
到現在為止,我們學過的SDL函式包括:
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)
,初始化視訊和音訊子系統,需要在使用其它SDL函式前呼叫。SDL_Quit()
,關閉開啟的SDL子系統,在程式退出前呼叫。SDL_SetVideoMode(320, 480, 0, SDL_HWSURFACE|SDL_DOUBLEBUF)
,在視訊記憶體內建立一個寬320畫素,高480畫素,顏色深度和當前顯示一致,具有雙緩衝的顯示頁面。成功返回頁面地址,失敗返回NULL
。SDL_LoadBMP(const char *picpath);
裝載一個BMP圖片,生成一個裝載影像資料的頁面。SDL_DisplayFormat(SDL_Surface *surf)
對surf
中資料轉換畫素格式後存到一個新的頁面,並返回新頁面。SDL_FreeSurface(SDL_Surface *surf)
釋放頁面,回收記憶體。SDL_BlitSurface(SDL_Surface *src, SDL_Rect *src_rect, SDL_Surface *dest, SDL_Rect *dest_rect);
,本節的核心函式。把src
頁面中src_rect
框中的影像顯示到dest
頁面中的dest_rect
區域。- SDL_Flip(SDL_Surface *surf),翻轉帶有雙緩衝的頁面。當繪製完成要更新螢幕顯示時,一定別忘了呼叫這個函式。
SDL_Delay(size_t n)
,等待n毫秒。等待時間不佔用CPU。
相關文章
- 數字影像處理(極簡) 第三章 BMP檔案的讀取與顯示(docx)
- 自定義bmp影像縮放及在lcd螢幕任意位置顯示
- 使用IPicture介面讀取和顯示BMP,GIF,JPG,ICO,EMF,WMF影像 (轉)
- AXIOS從伺服器載入圖片並顯示iOS伺服器
- 第四章 載入並顯示PNG圖片
- OpenCV入門 2_1中使用【總結】的程式碼載入影像、顯示影像報錯OpenCV
- 讓emacs完美顯示BMP檔案的辦法Mac
- BMP圖片的複製#顯示到螢幕
- Swift - 非同步載入各網站的favicon圖示,並在單元格中顯示Swift非同步網站
- LCD螢幕顯示PNG影像
- Android 9 Activity的載入和顯示Android
- 編譯、彙編、連結、載入、顯示編譯
- 預載入顯示圖片的藝術
- ListView分頁顯示 上拉載入更多View
- .net頁面載入顯示word檔案
- 載入網路圖片所顯示的轉圈效果及載入成功前與失敗後所顯示的圖示
- OpenCV_python全屏顯示影像OpenCVPython
- Angular中懶載入一個模組並動態建立顯示該模組下宣告的元件Angular元件
- discuz微社群 始終顯示“正在載入中”
- python——同時顯示多張影像Python
- Xamarin iOS教程之檢視顯示影像iOS
- WSL2 Xlaunch 轉發顯示影像
- Git差異並列顯示Git
- Java從檔案讀入資料並列印到顯示器Java
- html/css 滾動到元素位置,顯示載入動畫HTMLCSS動畫
- 如何在請求資料時,顯示載入動畫動畫
- 直播平臺開發,載入網頁、html檔案顯示載入進度網頁HTML
- python PIL 開啟\顯示\儲存影像Python
- 在Linux控制檯下顯示JPEG影像Linux
- 影像延遲載入 && 列表圖順序載入
- CSS兩列div並排顯示CSS
- Android程式解壓縮zip檔案,並載入顯示解壓後的檔案內容Android
- FPGA影像採集與顯示專案(一)帶LOGO的VGA顯示模組FPGAGo
- Python武器庫 - 科研中常用的python影像操作 - 影像顯示Python
- 第三章 BootLoader載入程式boot
- 報表載入大資料時顯示進度條大資料
- 新增的影像批次調整背景或顯示方法
- 點亮點陣以及點陣的影像顯示