前面教程里,我們只顯示圖片,沒提到如何顯示文字, SDL本身沒有顯示文字功能,它需要用擴展庫SDL_ttf來顯示文字。ttf是True Type Font的縮寫,ttf是Windows下的缺省字體,它有美觀,放大縮小不變形的優點,因此廣泛應用很多場合。
使用ttf庫的第一件事要從Windows的字庫下拷貝出一個字庫出來,最好是中文字體,這樣可以同時支持英文和中文顯示。它一般在c:\windows\fonts 目錄下面。比如simhei.ttf 就是仿黑體的字庫,將這個文件拷貝到你的源文件目錄下。
要使用SDL_ttf庫首先要下載該擴展庫:http://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html
下載完成后按以前方式安裝(SDL安裝教程里有講),然后在你的源文件里加載頭文件"SDL_ttf.h”。要顯示文字需要按以下步驟進行:
一.初始TTF庫
初始化TTF庫要調用TTF_Init(),成功返回-1,不成功返回0。
二.打開一個TTF_Font字體.
使用 TTF_Font *TTF_OpenFont(const char *file, int ptsize);函數打開字體
其中file是指字體文件的路徑,可以為相對路徑或絕對路徑, ptsize是指字號,即字體大小。它是基於720DPI的,有一個簡單辦法來估算字體大小,在Word中 選擇相應的字號即可看出效果來,不成功返回NULL。以下是打開一個黑體代碼,字號20,字體文件跟原文件在同一個目錄下。
//打開simfang.ttf 字庫,設字體為20號 font = TTF_OpenFont("simhei.ttf",20); if(font == NULL) { fprintf(stderr,"font open failure %s\n",SDL_GetError()); exit(-1); }
三.將文字轉換成表面
要想顯示文字,首先要將文字渲染成一副圖像,將文字渲染成一個圖像表面,有三種渲染方式:
Solid 渲染的最快,但效果最差,文字不平滑,是單色文字,不帶邊框。
Shaded 比Solid渲染的慢,但顯示效果好於Solid,帶陰影。
Blend 渲染最慢,但顯示效果最好。
四.把文字SDL_Surface 輸出到屏幕顯示,如果不需它,必須釋放它
文字表面和其他表面一樣,可以傳輸到顯示表面顯示。
五.關閉TTF_Font字體
使用 void TTF_CloseFont(TTF_Font *font) ;關閉字體
六.釋放TTF庫
如果不需要再顯示文字,可以釋放TTF庫,使用 void TTF_Quit() ;
下面以一個例子展示一下如何顯示文字,程序運行效果:

在這個例子里我們要掌握如何顯示西文、中文,以及如何給程序設置標題、圖標。程序主要代碼如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include "font.h" 5 6 int main(int argc,char * argv[]) 7 { 8 char a[] = "Hello World!"; 9 int quit = 0; 10 wchar_t * p ; 11 char tmp[]="世界,你好!"; 12 wchar_t msg[] =L"聖 旨"; 13 14 if(!init("文字","icon.bmp")) 15 { 16 exit(0); 17 } 18 19 //打開simfang.ttf 字庫,設字體為20號 20 font = TTF_OpenFont("simhei.ttf",20); 21 if(font == NULL) 22 { 23 fprintf(stderr,"font open failure %s\n",SDL_GetError()); 24 exit(-1); 25 } 26 27 gpBackground = loadImage("background.jpg"); 28 applySurface(0,0,gpBackground,gpScreen); 29 30 //TTF_SetFontStyle(font,TTF_STYLE_BOLD | TTF_STYLE_ITALIC); 31 32 //顯示西文 33 gpMessage[0] = TTF_RenderText_Solid(font,a,RGB_Black); 34 gpMessage[1] = TTF_RenderText_Shaded(font,a,RGB_Black,RGB_White); 35 gpMessage[2] = TTF_RenderText_Blended(font,a,RGB_Black); 36 applySurface(80,120,gpMessage[0],gpScreen); 37 applySurface(80,150,gpMessage[1],gpScreen); 38 applySurface(80,180,gpMessage[2],gpScreen); 39 SDL_Flip(gpScreen); 40 41 42 //顯示中文 43 p = cstringToUnicode(tmp); 44 gpChinese = TTF_RenderUNICODE_Solid( font, p, RGB_Black); 45 applySurface(340,120,gpChinese,gpScreen); 46 SDL_FreeSurface(gpChinese); 47 gpChinese = TTF_RenderUNICODE_Shaded( font, p, RGB_Black,RGB_White); 48 applySurface(340,150,gpChinese,gpScreen); 49 SDL_FreeSurface(gpChinese); 50 gpChinese = TTF_RenderUNICODE_Blended( font, p, RGB_Black); 51 applySurface(340,180,gpChinese,gpScreen); 52 SDL_FreeSurface(gpChinese); 53 SDL_Flip(gpScreen); 54 free(p); 55 p = NULL; 56 57 TTF_CloseFont(font); 58 59 //重新打開simfang.ttf 字庫,設字體為40 60 font = TTF_OpenFont("simhei.ttf",40); 61 if(font == NULL) 62 { 63 fprintf(stderr,"font open failure %s\n",SDL_GetError()); 64 exit(-1); 65 } 66 gpChinese = TTF_RenderUNICODE_Solid( font, msg, RGB_Yellow); 67 applySurface(260,50,gpChinese,gpScreen); 68 SDL_Flip(gpScreen); 69 SDL_FreeSurface(gpChinese); 70 71 gpChinese = TTF_RenderUNICODE_Solid( font, L"C語言始終被模仿,從未被超越!", RGB_Red); 72 applySurface(100,240,gpChinese,gpScreen); 73 SDL_FreeSurface(gpChinese); 74 75 gpChinese = TTF_RenderUNICODE_Solid( font, L"不懂C語言不要說自己是程序員!", RGB_Red); 76 applySurface(100,300,gpChinese,gpScreen); 77 SDL_FreeSurface(gpChinese); 78 SDL_Flip(gpScreen); 79 80 // 事件處理 81 while(!quit) 82 { 83 if (SDL_PollEvent(&myEvent)) 84 { 85 if (myEvent.type==SDL_QUIT) 86 { 87 quit = 1; 88 } 89 } 90 } 91 return 0; 92 }
其中font.h是自己定義的頭文件,聲明了常用的變量、常量、函數,其代碼如下:
#ifndef FONT_H_2012_05_31 #define FONT_H_2012_05_31 #include "SDL.h" #include "SDL_image.h" #include "SDL_ttf.h" #include <Windows.h> //定義布爾類型 typedef int BOOL; #define TRUE 1 #define FALSE 0 //屏幕分辯率 #define SCREEN_WIDTH 718 #define SCREEN_HEIGHT 419 #define SCREEN_BPP 32 //表面聲明 extern SDL_Surface *gpBackground; //背景表面 extern SDL_Surface *gpScreen; //顯示表面 extern SDL_Surface *gpMessage[3]; //西文文字表面 extern SDL_Surface *gpChinese;//中文文字表面 //事件聲明 extern SDL_Event myEvent; // 字體聲明 extern TTF_Font *font; /*****************************聲明常見顏色*****************************************/ extern const SDL_Color RGB_Black; extern const SDL_Color RGB_Red; extern const SDL_Color RGB_Green; extern const SDL_Color RGB_Blue; extern const SDL_Color RGB_Cyan; extern const SDL_Color RGB_Magenta; extern const SDL_Color RGB_Yellow; extern const SDL_Color RGB_White; extern const SDL_Color RGB_Gray; extern const SDL_Color RGB_Grey; extern const SDL_Color RGB_Maroon ; extern const SDL_Color RGB_Darkgreen; extern const SDL_Color RGB_Navy; extern const SDL_Color RGB_Teal; extern const SDL_Color RGB_Purple; extern const SDL_Color RGB_Olive; extern const SDL_Color RGB_Noname; /*****************************函數聲明*****************************************/ BOOL init(char* aCaption,char * aIcon); SDL_Surface *loadImage( char * filename ); void applySurface( int x, int y, SDL_Surface* source, SDL_Surface* destination ); void cleanup(); char *localeToUTF8(char *src); wchar_t* cstringToUnicode(char * aSrc); #endif
其中引用了windows.h,因為要做C語言字符串和寬字節字符的轉換要用到其中函數。C語言本身沒有BOOl類型,所以用typedef定義了自己的布爾類型,並且聲明了布爾常量TRUE和FALSE。
下面我們從主函數說起,在第10行和12行我們看到了一種新的數據類型wcha_t,其實這是C99新加的一種寬字符類型,C語言char類型用一個字節表示一個ANSI字符,但漢字、日文等文字無法表示,所以引入了wchar_t,wchar_t用兩個字節表示一個字符,所以它可以表示絕大多數字符,無論是那種語言,unicode字符是wchar_t一種實現,在C語言中,unicode字符串一般來說都是wchar_t類型。TTF庫中提供了unicode字符串顯示和UTF8字符串顯示,要顯示中文必須是這兩種串才能顯示。
第14行init函數完成系統初始化,包括初始化SDL、建立主窗口、初始化TTF庫,設置程序標題、圖標。其代碼如下:
/* 函數名:init 函數功能:完成系統初始化工作 函數參數:aCaption程序標題欄顯示名稱,c語言字符串 aIcon 程序圖標,必須為32*32bmp圖片 函數返回值:無 */ BOOL init(char* aCaption,char * aIcon) { //初始化 SDL if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 ) { return FALSE; } //載入程序圖標 SDL_WM_SetIcon(SDL_LoadBMP(aIcon), NULL); //初始化窗口 gpScreen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE||SDL_HWSURFACE ); if( gpScreen == NULL )//檢測是否初始化成功 { return FALSE; } atexit(cleanup); //初始化字體庫 if( TTF_Init() == -1 ) return FALSE; //設置窗口名字和圖標 SDL_WM_SetCaption(localeToUTF8(aCaption), NULL ); return TRUE; }
注意要設置程序圖標,必須在建立主窗口前調用SDL_WM_SetIcon(SDL_LoadBMP(aIcon), NULL);來設置圖標,圖標必須是32*32的bmp圖片,因為TTF庫不是SDL自己的,所以必須調用TTF_Init()單獨初始化;最后設置程序標題,注意標題如果設英文可以直接顯示,但如果像我們這個程序這樣顯示中文,則必須將字符串轉換成UTF8的才能顯示,所以我們調用了自定義的函數localeToUTF8(aCaption)完成將c語言的字符串轉換成UTF8格式的字符串,這個函數定義如下:
/*-------------------------------------------------------------------- 函數名: localeToUTF8 參 數: char *src C語言字符串 返回值: char * UTF8字符串 功 能: 將C語言字符串轉換成UTF8字符串 備 注: ----------------------------------------------------------------------*/ char *localeToUTF8(char *src) { static char *buf = NULL; wchar_t *unicode_buf; int nRetLen; if(buf){ free(buf); buf = NULL; } nRetLen = MultiByteToWideChar(CP_ACP,0,src,-1,NULL,0); unicode_buf = (wchar_t*)malloc((nRetLen+1)*sizeof(wchar_t)); MultiByteToWideChar(CP_ACP,0,src,-1,unicode_buf,nRetLen); nRetLen = WideCharToMultiByte(CP_UTF8,0,unicode_buf,-1,NULL,0,NULL,NULL); buf = (char*)malloc(nRetLen+1); WideCharToMultiByte(CP_UTF8,0,unicode_buf,-1,buf,nRetLen,NULL,NULL); free(unicode_buf); return buf; }
這個函數完成將C語言字符串轉換成UTF8格式的字符串。 這樣我們就可以設置中文程序標題了。
在主函數的第20行我們打開了一個字體庫,並設置字號為20,關於字號的大小你可以打開word看一下字號的大小。接下來,在33行我們調用TTF_RenderText_Solid將文字渲染成一幅圖片,返回圖片表面的指針,這個函數只能顯示西文,不能顯示中文。其原型:
SDL_Surface *TTF_RenderText_Solid(TTF_Font *font, const char *text, SDL_Color fg);
參數:font,打開的字體;text要顯示的字符串,C語言格式的字符串(以\0做結束符);fg是文字的顏色,前面我們介紹過SDL_Color結構,這里不再多說。
除了這種渲染,還有:
SDL_Surface *TTF_RenderText_Shaded(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg);
其中font、text和fg與TTF_RenderText_Solid中的一樣,bg是文字背景色。
SDL_Surface *TTF_RenderText_Blended(TTF_Font *font, const char *text, SDL_Color fg)和TTF_RenderText_Solid參數一樣,關於這三種渲染的差別前面已經提過,運行結果頁顯示了blend效果最好,solid最差,shade可以設置文字背景。
然后我們把這些表面和顯示圖片一樣顯示就可以了,36至40我們調用了自定義函數顯示文字:
void applySurface( int x, int y, SDL_Surface* source, SDL_Surface* destination );
x、y是文字顯示到屏幕上的位置,source是文字表面,destination是目標表面,一般是顯示表面。
顯示西文比較簡單。顯示中文稍微麻煩點,如果你把char tmp[]="世界,你好!";顯示到屏幕,調用TTF_RenderText_Solid只會顯示一堆亂碼,因為這個函數只能顯示西文,如果要顯示中文要調用:
| 顯示UTF8字符串 | SDL_Surface *TTF_RenderUTF8_Solid(TTF_Font *font, const char *text, SDL_Color fg); |
| SDL_Surface *TTF_RenderUTF8_Shaded(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg); | |
| SDL_Surface *TTF_RenderUTF8_Blended(TTF_Font *font, const char *text, SDL_Color fg); | |
| 顯示unicode字符串 | SDL_Surface *TTF_RenderUNICODE_Solid(TTF_Font *font, const Uint16 *text, SDL_Color fg) ; |
| SDL_Surface *TTF_RenderUNICODE_Shaded(TTF_Font *font, const Uint16 *text, SDL_Color fg, SDL_Color bg) ; | |
| SDL_Surface *TTF_RenderUNICODE_Blended(TTF_Font *font, const Uint16 *text, SDL_Color fg) ; |
UTF8編碼請查:http://baike.baidu.com/view/25412.htm;UNICODE編碼請查:http://baike.baidu.com/view/40801.htm
這些函數的參數和前面差不多,唯一不同是要求text必須是UTF8或unicode格式的。那么如何將C語言字符串轉換成UTF8字符串或unicode字符串呢,前面我們使用自定義的函數char *localeToUTF8(char *src)將c字符串轉換為UTF8字符串返回,將c字符串轉換成unicode字符串,我們自定義了函數:
/*-------------------------------------------------------------------- 函數名: cstringToUnicode 參 數: char *src C語言字符串 返回值: wchar_t * Unicode字符串 功 能: 將c語言字符串轉換成unicode字符串 備 注: ----------------------------------------------------------------------*/ wchar_t* cstringToUnicode(char * aSrc) { int size; wchar_t *unicodestr = NULL; if(!aSrc) { return NULL; } size=MultiByteToWideChar(CP_ACP,0,aSrc,-1,NULL,0); unicodestr= malloc((size+1)*sizeof(wchar_t)); MultiByteToWideChar(CP_ACP,0,aSrc,-1,unicodestr,size); return unicodestr; }
這個函數完成將c語言字符串轉換成unicode字符串返回,使用這兩個函數要注意,在函數中我們動態分配了內存來保存UTF8或unicode字符串,然后將其返回,所以我們在調用函數中顯示完后必須釋放這些字符串所占空間。主函數第43行我們將tmp串轉換成unicode串返回給p,然后顯示,在54行釋放了p。
在第57行我們關閉了前面打開的字體,因為下面我們將用大一點字體顯示,如果要顯示中文,其實最簡單的辦法是在程序里直接定義wchar_t類型數組保存unicode字符串,然后可以直接顯示這個字符串,在第12行我們定義了wchar_t msg[] =L"聖 旨";unicode的字符串,注意unicode字符串必須以L開頭,否則就是C語言的字符串了。在第66-69行我們直接將其顯示到屏幕了。你也可以顯示unicode常量串,第71-78行我們分別顯示了兩個unicode常量串。
在這里我們只討論了windows平台下中文顯示問題,如果在linux下會有所不同,具體的你可以查詢linux平台相關資料。如果你想要例子的源代碼,請點擊這兒。代碼中的字體文件太大,刪除了,你可以從windows下的fonts目錄下copy一個中文ttf文件到源文件目錄就可以了。
