到現在為止我們只創建了一個窗口,其他什麼都沒干,這次我們將在窗口裡顯示圖片,這是視頻子系統最常用的功能,顯示圖片。SDL視頻子系統只能加載bmp格式的位圖。調用函數是SDL_Surface *SDL_LoadBMP(const char *file);這個函數的參數是c語言的字符串,返回值是一個繪圖表面。在SDL中有兩種繪圖表面,第一種是使用SDL_SetVideoMode函數生成的顯示表面或窗口),顯示表面是唯一的,只能使用SDL_SetVideoMod生成,顯示表面可以直接顯示在屏幕上;除了顯示表面,加載圖片生成的表面,加載字體生成的表面,這些表面不能直接顯示在屏幕上,這是這兩種表面的差別。
在開始本例前要准備一張bmp圖片,然後我們可以自己寫一個加載bmp圖片的函數:
/*--------------------------------------------------------------------
函數名: loadImage
參 數: char *filename 圖像文件的名字
返回值: SDL_Surface * 成功返回指向圖像表面的指針,否則返回NULL
功 能: 載入圖像
備 注:
----------------------------------------------------------------------*/
SDL_Surface *loadImage(char *aFilename)
{
SDL_Surface* loadedImage = NULL;
SDL_Surface* optimizedImage = NULL;
//載入圖像
loadedImage = SDL_LoadBMP( aFilename);
if( NULL != loadedImage )//如果加載成功,loadedImage不為空
{
//創建優化圖像
optimizedImage = SDL_DisplayFormat( loadedImage );
//釋放loadedImage
SDL_FreeSurface( loadedImage );
}
return optimizedImage;
}
在函數裡首先載入bmp圖片,然後判斷是否加載成功,如果加載成功,則要對生成的表面進行優化,為什麼要進行優化?因為加載圖片的生成的表面的格式可能和顯示表面的格式不一樣,以後顯示這張圖片的時候每次都要把圖片的表面格式轉換成顯示表面的格式然後在顯示,當然這個工作是系統進行的,但這會降低系統的效率,所以我們在這要將圖片的表面按照顯示表面的格式進行轉換,然後將轉換後表面指針返回。
SDL_DisplayFormat函數的作用就是將loadedImage所指的表面按照顯示表面的格式生成一個副本,所以在生成副本以後我們會把loadedImage所指的原來的表面釋放,然後將新生成的表面指針optimizedImage返回。
本例中我們將顯示一副鐘表作為背景圖,鐘表大小580*580,圖片名字為clock.bmp。首先在vs2008裡創建一個空的控制台應用,然後設置工程屬性,這個在第一個安裝教程裡有詳細說明。例子的運行效果:
工程源代碼為:
/* 功能:演示加載位圖 作者:csl 日期:2012-5-5 */ #include <stdio.h> #include <stdlib.h> #include "sdl\SDL.h" //定義窗口的寬度、高度、位深 #define WIDTH 800 #define HEIGH 600 #define BPP 32 //定義表面指針 SDL_Surface * gpScreen = NULL;//顯示窗口的指針 SDL_Surface * gpClock = NULL;//bmp圖片表面的指針 SDL_Surface *loadImage(char *aFilename);//加載bmp圖片 int main(int arc,char* agv[]) { if((SDL_Init(SDL_INIT_EVERYTHING)==-1)) //初始化SDL子系統 { printf("Unable to init SDL: %s\n", SDL_GetError()); exit(-1); } atexit(SDL_Quit);// 注冊SDL_Quit,當退出時調用,使得退出時程序自動清理 //創建窗口 gpScreen = SDL_SetVideoMode(WIDTH,HEIGH, BPP, SDL_HWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF ); if(!gpScreen) { printf("Unable to create window: %s\n", SDL_GetError()); exit(1); } //加載圖片 gpClock = loadImage("clock.bmp"); //位塊傳輸 SDL_BlitSurface(gpClock,NULL,gpScreen,NULL); //顯示圖片 SDL_Flip(gpScreen); SDL_Delay(30000); //暫停3秒 //退出程序前必須釋放表面指針 SDL_FreeSurface(gpScreen); SDL_FreeSurface(gpClock); system("pause"); return 0; } /*-------------------------------------------------------------------- 函數名: loadImage 參 數: char *filename 圖像文件的名字 返回值: SDL_Surface * 返回指向圖像表面的指針 功 能: 載入圖像 備 注: ----------------------------------------------------------------------*/ SDL_Surface *loadImage(char *aFilename) { SDL_Surface* loadedImage = NULL; SDL_Surface* optimizedImage = NULL; //載入圖像 loadedImage = SDL_LoadBMP( aFilename); if( NULL != loadedImage )//If the image loaded { //創建優化圖像 optimizedImage = SDL_DisplayFormat( loadedImage ); //釋放loadImage SDL_FreeSurface( loadedImage ); } return optimizedImage; }
在主函數中第40行我們調用自己定義的loadImage函數加載了時鐘的bmp圖片,加載成功後我們要將該表面“貼到”顯示表面上,然後調用刷新函數刷新就可以顯示圖片了。所以顯示一個圖片需要三步: 1.加載圖片;2.將圖片表面貼到顯示表面上;3 刷新屏幕。
其中,第1步我們已經做完了,第2步如何將圖片表面貼到顯示表面上呢?首先要了解如何在屏幕上顯示東西,在屏幕上顯示的任何東西都存儲在一種稱之為幀緩存中,幀緩存類似一個二維數組,是一塊內存區域,存儲了屏幕上每一個像素顯示數據,顯示圖像的時候,就會按幀緩存數據在屏幕上顯示出圖像來,而顯示表面就是幀緩存,所以其他表面要顯示的話,必須把自己的顯示數據傳輸到顯示表面,覆蓋相應的顯示表面的數據,則顯示的時候就會顯示出來,這就類似把一副畫圖片表面)貼到白板顯示表面)上。在SDL中這稱為Blit(bit block transfer,位塊傳輸),可以把源表面的數據傳輸到目標表面上,覆蓋相應的數據,這種傳輸很快,並且會自動把源表面格式轉換成目標表格的格式。在SDL完成這個功能的函數:
int SDL_BlitSurface
(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);
參數:src 源表面
srcrect 源表面需要顯示的地方,用一個矩形表示,SDL_Rect是一種結構體描述的是一個矩形)
dst 目標表面
dstrect 在目標表面顯示的位置,也是用一個矩形表示
用法,調用這個函數的時候需要把源表面的指針傳給第一個參數,在SDL中任意一個圖片都是用一個矩形SDL_Rect)表示,你需要顯示源表面的那一塊就用矩形來指出,需要把這個矩形的指針傳給第srcrect,如果為NULL則顯示源表面的全部內容;將目標表面的指針傳給第3個參數dst,如果要指定在目標表面上的顯示位置,同樣需要用一個矩形來表示這個位置,如果為NULL,則從目標表面左上角開始顯示源表面。這樣就理解了第43行的意思,把鐘表全部顯示到顯示表面上,從目標表面的左上角開始顯示。
位塊傳輸完成以後呢,還不能在屏幕上看到,因為你要告訴顯卡刷新屏幕,這就要調用SDL_Flip函數,函數的參數是顯示表面的指針。刷新屏幕後就可以看到圖片了。
顯示任意一副圖片都要做這三步,最後記著,退出程序時要釋放相應的表面,所以第50,51行是釋放顯示表面和圖片表面。
在這個例子中有一個新的數據類型SDL_Rect,這是SDL中的結構體
typedef struct{如果你希望將clock顯示到窗口的中間,則可以這樣寫
SDL_Rect dstRect;
dstRect.x = (WIDTH-gpClock->w)/2;
dstRect.y = (HEIGH-gpClock->h)/2;
dstRect.w = gpClock->w;
dstRect.h = gpClock->h;
SDL_BlitSurface(gpClock,NULL,gpScreen,&dstRect);//記著一定是dstRect的地址
用這段代碼代替第43行則可以在窗口中間顯示鐘表,注意SDL_Rect dstRect;這句要放到函數的開始部分,因為C語言不允許在中間定義變量。這個例子的源代碼可以點這兒下載。注意本例是在vs2008下調試通過。
使用SDL_LoadBmp不能加載其他格式圖片的函數,如何加載其他格式圖片呢?
SDL有一個擴展庫SDL_image,這個擴展庫支持BMP, PPM, XPM, PCX, GIF, JPEG, PNG和TGA格式的圖片,這個擴展庫在安裝教程裡已經介紹過如何安裝了,如果安裝了這個擴展庫,那麼在你的源文件裡引入它的頭文件:#include "SDL_image.h",然後在工程設置裡添加靜態庫的引用SDL_image.lib。然後我們修改一下loadImage函數就可以了。
在loadImage函數裡我們用IMG_Load函數代替SDLLoadBmp函數就可以加載其他格式圖像了。
/*-------------------------------------------------------------------- 函數名: loadImage 參 數: char *filename 圖像文件的名字 返回值: SDL_Surface * 返回指向圖像表面的指針 功 能: 載入圖像 備 注: ----------------------------------------------------------------------*/ SDL_Surface *loadImage(char *aFilename) { SDL_Surface* loadedImage = NULL; SDL_Surface* optimizedImage = NULL; //載入圖像 loadedImage = IMG_Load( aFilename); if( NULL != loadedImage )//If the image loaded { //創建優化圖像 optimizedImage = SDL_DisplayFormat( loadedImage ); //釋放loadImage SDL_FreeSurface( loadedImage ); } return optimizedImage; }
提示,無論是IMG_Load函數還是SDL_LoadBmp函數,加載的圖像文件一般和源文件在一個目錄,文件名字可以使用絕對路徑或相對路徑,相對路徑是相對於源文件所在目錄而言的,比如要加載和源文件同目錄下的圖片clock.bmp,可以這樣加載IMG_Load("clock.bmp");,如果把圖像文件都放到源文件目錄的子目錄image下,則可以IMG_Load("image\\clock.bmp");記住一定是兩個\\,\是轉義字符。必須用兩個\表示一個\。如果是絕對目錄,比如說加載d:\bmp下的clock.bmp文件,則可以這樣調用IMG_Load("d:\\bmp\\clock.bmp");。
本文出自 “學習之樂” 博客,請務必保留此出處http://chengshaolei.blog.51cto.com/1994169/1291967