FreeType2教程

第一步 -- 簡易的字形裝載

介紹

這是“FreeType2 教程”的第一部分。它將教會你如何:

* 初始化庫
* 通過創建一個新的 face 對象來打開一個字體文件
* 以點或者象素的形式選擇一個字符大小
* 裝載一個字形(glyph)圖像,並把它轉換爲位圖
* 渲染一個簡單的字符串
* 容易地渲染一個旋轉的字符串

1.頭文件

下面的內容是編譯一個使用了FreeType2庫的應用程序所需要的指令。請謹慎閱讀,自最近一次版本更新後我們已經更改了少許東西。

1.FreeType2 include 目錄

你必須把FreeType2頭文件的目錄添加到編譯包含(include)目錄中。

注意,現在在Unix系統,你可以運行freetype-config腳本加上--cflags選項來獲得正確的編譯標記。這個腳本也可以用來檢查安裝在你係統中的庫的版本,以及需要的庫和連接標記。

2. 包含名爲ft2build.h的文件

Ft2build.h包含了接下來要#include的公共FreeType2頭文件的宏聲明。

3. 包含主要的FreeType2 API頭文件

你要使用FT_FREETYPE_H宏來完成這個工作,就像下面這樣:

#include <ft2build.h>
#include FT_FREETYPE_H

FT_FREETYPE_H是在ftheader.h中定義的一個特別的宏。Ftheader.h包含了一些安裝所特定的宏,這些宏指名了FreeType2 API的其他公共頭文件。

你可以閱讀“FreeType 2 API參考”的這個部分來獲得頭文件的完整列表。

#include語句中宏的用法是服從ANSI的。這有幾個原因:

* 這可以避免一些令人痛苦的與FreeType 1.x公共頭文件的衝突。

* 宏名字不受限於DOS的8.3文件命名限制。象FT_MULTIPLE_MASTERS_H或FT_SFNT_NAMES_H這樣的名字比真實的文件名ftmm.h和fsnames.h更具可讀性並且更容易理解。

* 它允許特別的安裝技巧,我們不在這裏討論它。

注意:從FreeType 2.1.6開始,舊式的頭文件包含模式將不會再被支持。這意味着現在如果你做了象下面那樣的事情,你將得到一個錯誤:

#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
. . .

2. 初始化庫

簡單地創建一個FT_Library類型的變量,例如library,然後象下面那樣調用函數FT_Init_FreeType:

#include <ft2build.h>
#include FT_FREETYPE_H

FT_LIBRARY library;

. . .

Error = FT_Init_FreeType ( &library );
If ( error )
{
. . . 當初始化庫時發生了一個錯誤 . . .
}

這個函數負責下面的事情:

* 它創建一個FreeType 2庫的新實例,並且設置句柄library爲它。

* 它裝載庫中FreeType所知道的每一個模塊。除了別的以外,你新建的library對象可以優雅地處理TrueType, Type 1, CID-keyed 和OpenType/CFF字體。

就像你所看到的,這個函數返回一個錯誤代碼,如同FreeType API的大部分其他函數一樣。值爲0的錯誤代碼始終意味着操作成功了,否則,返回值指示錯誤,library設爲NULL。

3.裝載一個字體face

a.從一個字體文件裝載

應用程序通過調用FT_New_Face創建一個新的face對象。一個face對象描述了一個特定的字樣和風格。例如,’Times New Roman Regular’和’Times New Roman Italic’對應兩個不同的face。

FT_Library library; /* 庫的句柄 */
FT_Face face; /* face對象的句柄 */

error = FT_Init_FreeType( &library );
if ( error ) { ... }

error = FT_New_Face( library,
"/usr/share/fonts/truetype/arial.ttf",
0,
&face );
if ( error == FT_Err_Unknown_File_Format )
{
... 可以打開和讀這個文件,但不支持它的字體格式
}
else if ( error )
{
... 其它的錯誤碼意味着這個字體文件不能打開和讀,或者簡單的說它損壞了...
}

就如你所想到的,FT_NEW_Face打開一個字體文件,然後設法從中提取一個face。它的參數爲:

Library
一個FreeType庫實例的句柄,face對象從中建立

Filepathname
字體文件路徑名(一個標準的C字符串)

Face_index
某些字體格式允許把幾個字體face嵌入到同一個文件中。
這個索引指示你想裝載的face。
如果這個值太大,函數將會返回一個錯誤。Index 0總是正確的。

Face
一個指向新建的face對象的指針。
當失敗時其值被置爲NULL。

要知道一個字體文件包含多少個face,只要簡單地裝載它的第一個face(把face_index設置爲0),face->num_faces的值就指示出了有多少個face嵌入在該字體文件中。

b.從內存裝載

如果你已經把字體文件裝載到內存,你可以簡單地使用FT_New_Memory_Face爲它新建一個face對象,如下所示:

FT_Library library; /* 庫的句柄 */
FT_Face face; /* face對象的句柄 */


error = FT_Init_FreeType( &library );
if ( error ) { ... }

error = FT_New_Memory_Face( library,
buffer, /* 緩存的第一個字節 */
size, /* 緩存的大小(以字節表示) */
0, /* face索引 */
&face );
if ( error ) { ... }

如你所看到的,FT_New_Memory_Face簡單地用字體文件緩存的指針和它的大小(以字節計算)代替文件路徑。除此之外,它與FT_New_Face的語義一致。

c.從其他來源裝載(壓縮文件,網絡,等)

使用文件路徑或者預裝載文件到內存是簡單的,但還不足夠。FreeType 2可以支持通過你自己實現的I/O程序來裝載文件。

這是通過FT_Open_Face函數來完成的。FT_Open_Face可以實現使用一個自定義的輸入流,選擇一個特定的驅動器來打開,乃至當創建該對象時傳遞外部參數給字體驅動器。我們建議你查閱“FreeType 2參考手冊”,學習如何使用它。

4.訪問face內容

一個face對象包含該face的全部全局描述信息。通常的,這些數據可以通過分別查詢句柄來直接訪問,例如face->num_glyphs。

FT_FaceRec結構描述包含了可用字段的完整列表。我們在這裏詳細描述其中的某些:

Num_glyphs
這個值給出了該字體face中可用的字形(glyphs)數目。簡單來說,一個字形就是一個字符圖像。但它不一定符合一個字符代碼。

Flags
一 個32位整數,包含一些用來描述face特性的位標記。例如,標記FT_FACE_FLAG_SCALABLE用來指示該face的字體格式是可伸縮並且 該字形圖像可以渲染到任何字符象素尺寸。要了解face標記的更多信息,請閱讀“FreeType 2 API 參考”。

Units_per_EM
這個字段只對可伸縮格式有效,在其他格式它將會置爲0。它指示了EM所覆蓋的字體單位的個數。

Num_fixed_size
這個字段給出了當前face中嵌入的位圖的個數。簡單來說,一個strike就是某一特定字符象素尺寸下的一系列字形圖像。例如,一個字體face可以包含象素尺寸爲10、12和14的strike。要注意的是即使是可伸縮的字體格式野可以包含嵌入的位圖!

Fixed_sizes
一個指向FT_Bitmap_Size成員組成的數組的指針。每一個FT_Bitmap_Size指示face中的每一個strike的水平和垂直字符象素尺寸。
注意,通常來說,這不是位圖strike的單元尺寸。

5.設置當前象素尺寸

對於特定face中與字符大小相關的信息,FreeType 2使用size對象來構造。例如,當字符大小爲12點時,使用一個size對象以1/64象素爲單位保存某些規格(如ascender或者文字高度)的值。

當FT_New_Face或它的親戚被調用,它會自動在face中新建一個size對象,並返回。該size對象可以通過face->size直接訪問。

注意:一個face對象可以同時處理一個或多個size對象,但只有很少程序員需要用到這個功能,因而,我們決定簡化該API,(例如,每個face對象只擁有一個size對象)但是這個特性我們仍然通過附加的函數提供。

當一個新的face對象建立時,對於可伸縮字體格式,size對象默認值爲字符大小水平和垂直均爲10象素。對於定長字體格式,這個大小是未定義的,這就是你必須在裝載一個字形前設置該值的原因。

使用FT_Set_Char_Size完成該功能。這裏有一個例子,它在一個300x300dpi設備上把字符大小設置爲16pt。

error = FT_Set_Char_Size(
face, /* face對象的句柄 */
0, /* 以1/64點爲單位的字符寬度 */
16*64, /* 以1/64點爲單位的字符高度 */
300, /* 設備水平分辨率 */
300 ); /* 設備垂直分辨率 */

注意:

* 字符寬度和高度以1/64點爲單位表示。一個點是一個1/72英寸的物理距離。通常,這不等於一個象素。

* 設備的水平和垂直分辨率以每英寸點數(dpi)爲單位表示。顯示設備(如顯示器)的常規值爲72dpi或96dpi。這個分辨率是用來從字符點數計算字符象素大小的。

* 字符寬度爲0意味着“與字符高度相同”,字符高度爲0意味着“與字符寬度相同”。對於其他情況則意味着指定不一樣的字符寬度和高度。

* 水平或垂直分辨率爲0時表示使用默認值72dpi。

* 第一個參數是face對象的句柄,不是size對象的。

這個函數計算對應字符寬度、高度和設備分辨率的字符象素大小。然而,如果你想自己指定象素大小,你可以簡單地調用FT_Set_Pixel_Sizes,就像這樣:

error = FT_Set_Pixel_Sizes(
face, /* face對象句柄 */
0, /* 象素寬度 */
16 ); /* 象素高度 */

這個例子把字符象素設置爲16x16象素。如前所說的,尺寸中的任一個爲0意味着“與另一個尺寸值相等”。

注意這兩個函數都返回錯誤碼。通常,錯誤會發生在嘗試對定長字體格式(如FNT或PCF)設置不在face->fixed_size數組中的象素尺寸值。

6.裝載一個字形圖像

a.把一個字符碼轉換爲一個字形索引

通常,一個應用程序想通過字符碼來裝載它的字形圖像。字符碼是一個特定編碼中代表該字符的數值。例如,字符碼64代表了ASCII編碼中的’A’。

一 個face對象包含一個或多個字符表(charmap),字符表是用來轉換字符碼到字形索引的。例如,很多TrueType字體包含兩個字符表,一個用來 轉換Unicode字符碼到字形索引,另一個用來轉換Apple Roman編碼到字形索引。這樣的字體既可以用在Windows(使用Unicode)和Macintosh(使用Apple Roman)。同時要注意,一個特定的字符表可能沒有覆蓋完字體裏面的全部字形。

當新建一個face對象時,它默認選擇 Unicode字符表。如果字體沒包含Unicode字符表,FreeType會嘗試在字形名的基礎上模擬一個。注意,如果字形名是不標準的那麼模擬的字 符表有可能遺漏某些字形。對於某些字體,包括符號字體和舊的亞洲手寫字體,Unicode模擬是不可能的。

我們將在稍後敘述如何尋找face中特定的字符表。現在我們假設face包含至少一個Unicode字符表,並且在調用FT_New_Face時已經被選中。我們使用FT_Get_Char_Index把一個Unicode字符碼轉換爲字形索引,如下所示:

glyph_index = FT_Get_Char_Index( face, charcode );

這個函數會在face裏被選中的字符表中查找與給出的字符碼對應的字形索引。如果沒有字符表被選中,這個函數簡單的返回字符碼。

注意,這個函數是FreeType中罕有的不返回錯誤碼的函數中的一個。然而,當一個特定的字符碼在face中沒有字形圖像,函數返回0。按照約定,它對應一個特殊的字形圖像――缺失字形,通常會顯示一個框或一個空格。

b.從face中裝載一個字形

一 旦你獲得了字形索引,你便可以裝載對應的字形圖像。在不同的字體中字形圖像存儲爲不同的格式。對於固定尺寸字體格式,如FNT或者PCF,每一個圖像都是 一個位圖。對於可伸縮字體格式,如TrueType或者Type1,使用名爲輪廓(outlines)的矢量形狀來描述每一個字形。一些字體格式可能有更 特殊的途徑來表示字形(如MetaFont――但這個格式不被支持)。幸運的,FreeType2有足夠的靈活性,可以通過一個簡單的API支持任何類型 的字形格式。

字形圖像存儲在一個特別的對象――字形槽(glyph slot)中。就如其名所暗示的,一個字形槽只是一個簡單的容器,它一次只能容納一個字形圖像,可以是位圖,可以是輪廓,或者其他。每一個face對象都 有一個字形槽對象,可以通過face->glyph來訪問。它的字段在FT_GlyphSlotRec結構的文檔中解釋了。

通過調用FT_Load_Glyph來裝載一個字形圖像到字形槽中,如下:

error = FT_Load_Glyph(
face, /* face對象的句柄 */
glyph_index, /* 字形索引 */
load_flags ); /* 裝載標誌,參考下面 */

load_flags的值是位標誌集合,是用來指示某些特殊操作的。其默認值是FT_LOAD_DEFAULT即0。

這個函數會設法從face中裝載對應的字形圖像:

* 如果找到一個對應該字形和象素尺寸的位圖,那麼它將會被裝載到字形槽中。嵌入的位圖總是比原生的圖像格式優先裝載。因爲我們假定對一個字形,它有更高質量的版本。這可以用FT_LOAD_NO_BITMAP標誌來改變。

* 否則,將裝載一個該字形的原生圖像,把它伸縮到當前的象素尺寸,並且對應如TrueType和Type1這些格式,也會完成hinted操作。

字段face->glyph->format描述了字形槽中存儲的字形圖像的格式。如果它的值不是FT_GLYPH_FORMAT_BITMAP,你可以通過FT_Render_Glyph把它直接轉換爲一個位圖。如下:

error = FT_Render_Glyph( face->glyph, /* 字形槽 */
render_mode ); /* 渲染模式 */

render_mode參數是一個位標誌集合,用來指示如何渲染字形圖像。把它設爲FT_RENDER_MODE_NORMAL渲染出一個高質量的抗鋸齒(256級灰度)位圖。這是默認情況,如果你想生成黑白位圖,可以使用FT_RENDER_MODE_MONO標誌。

一旦你生成了一個字形圖像的位圖,你可以通過glyph->bitmap(一個簡單的位圖描述符)直接訪問,同時用glyph->bitmap_left和glyph->bitmap_top來指定起始位置。

要注意,bitmap_left是從字形位圖當前筆位置到最左邊界的水平距離,而bitmap_top是從筆位置(位於基線)到最高邊界得垂直距離。他麼是正數,指示一個向上的距離。

下一部分將給出字形槽內容的更多細節,以及如何訪問特定的字形信息(包括度量)。

c.使用其他字符表

如 前面所說的,當一個新face對象創建時,它會尋找一個Unicode字符表並且選擇它。當前被選中的字符表可以通過face->charmap訪 問。當沒有字符表被選中時,該字段爲NULL。這種情況在你從一個不含Unicode字符表的字體文件(這種文件現在非常罕見)創建一個新的 FT_Face對象時發生。

有兩種途徑可以在FreeType 2中選擇不同的字符表。最輕鬆的途徑是你所需的編碼已經有對應的枚舉定義在FT_FREETYPE_H中,例如FT_ENCODING_BIG5。在這種情況下,你可以簡單地調用FT_Select_CharMap,如下:

error = FT_Select_CharMap(
face, /* 目標face對象 */
FT_ENCODING_BIG5 ); /* 編碼 */

另 一種途徑是手動爲face解析字符表。這通過face對象的字段num_charmaps和charmaps(注意這是複數)來訪問。如你想到的,前者是 face中的字符表的數目,後者是一個嵌入在face中的指向字符表的指針表(a table of pointers to the charmaps)。

每一個字符表有一些可見的字段,用來更精確地描述它,主要用到的字段是charmap->platform_id和charmap->encoding_id。這兩者定義了一個值組合,以更普
通的形式用來描述該字符表。

每一個值組合對應一個特定的編碼。例如組合(3,1)對應Unicode。組合列表定義在TrueType規範中,但你也可以使用文件FT_TRUETYPE_IDS_H來處理它們,該文件定義了幾個有用的常數。

要選擇一個具體的編碼,你需要在規範中找到一個對應的值組合,然後在字符表列表中尋找它。別忘記,由於歷史的原因,某些編碼會對應幾個值組合。這裏是一些代碼:

FT_CharMap found = 0;
FT_CharMap charmap;
int n;


for ( n = 0; n < face->num_charmaps; n++ )
{
charmap = face->charmaps[n];
if ( charmap->platform_id == my_platform_id &&
charmap->encoding_id == my_encoding_id )
{
found = charmap;
break;
}
}

if ( !found ) { ... }

/* 現在,選擇face對象的字符表*/
error = FT_Set_CharMap( face, found );
if ( error ) { ... }

一旦某個字符表被選中,無論通過FT_Select_CharMap還是通過FT_Set_CharMap,它都會在後面的FT_Get_Char_Index調用使用。

d.字形變換

當字形圖像被裝載時,可以對該字形圖像進行仿射變換。當然,這隻適用於可伸縮(矢量)字體格式。

簡單地調用FT_Set_Transform來完成這個工作,如下:

error = FT_Set_Transform(
face, /* 目標face對象 */
&matrix, /* 指向2x2矩陣的指針 */
&delta ); /* 指向2維矢量的指針 */

這個函數將對指定的face對象設置變換。它的第二個參數是一個指向FT_Matrix結
構的指針。該結構描述了一個2x2仿射矩陣。第三個參數是一個指向FT_Vector結構的指針。該結構描述了一個簡單的二維矢量。該矢量用來在2x2變換後對字形圖像平移。

注意,矩陣指針可以設置爲NULL,在這種情況下將進行恆等變換。矩陣的係數是16.16形式的固定浮點單位。

矢量指針也可以設置爲NULL,在這種情況下將使用(0, 0)的delta。矢量座標以一個象素的1/64爲單位表示(即通常所說的26.6固定浮點格式)。

注意:變換將適用於使用FT_Load_Glyph裝載的全部字形,並且完全獨立於任何hinting處理。這意味着你對一個12象素的字形進行2倍放大變換不會得到與24象素字形相同的結果(除非你禁止hints)。

如果你需要使用非正交變換和最佳hints,你首先必須把你的變換分解爲一個伸縮部分和一個旋轉/剪切部分。使用伸縮部分來計算一個新的字符象素大小,然後使用旋轉/剪切部分來調用FT_Set_Transform。這在本教程的後面部分有詳細解釋。

同時要注意,對一個字形位圖進行非同一性變換將產生錯誤。

7. 簡單的文字渲染

現在我們將給出一個非常簡單的例子程序,該例子程序渲染一個8位Latin-1文本字符串,並且假定face包含一個Unicode字符表。

該程序的思想是建立一個循環,在該循環的每一次迭代中裝載一個字形圖像,把它轉換爲一個抗鋸齒位圖,把它繪製到目標表面(surface)上,然後增加當前筆的位置。

a.基本代碼

下面的代碼完成我們上面提到的簡單文本渲染和其他功能。

FT_GlyphSlot slot = face->glyph; /* 一個小捷徑 */
int pen_x, pen_y, n;


... initialize library ...
... create face object ...
... set character size ...

pen_x = 300;
pen_y = 200;

for ( n = 0; n < num_chars; n++ )
{
FT_UInt glyph_index;


/* 從字符碼檢索字形索引 */
glyph_index = FT_Get_Char_Index( face, text[n] );

/* 裝載字形圖像到字形槽(將會抹掉先前的字形圖像) */
error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
if ( error )
continue; /* 忽略錯誤 */

/* 轉換爲一個抗鋸齒位圖 */
error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
if ( error )
continue;

/* 現在,繪製到我們的目標表面(surface) */
my_draw_bitmap( &slot->bitmap,
pen_x + slot->bitmap_left,
pen_y - slot->bitmap_top );

/* 增加筆位置 */
pen_x += slot->advance.x >> 6;
pen_y += slot->advance.y >> 6; /* 現在還是沒用的 */
}

這個代碼需要一些解釋:

* 我們定義了一個名爲slot的句柄,它指向face對象的字形槽。(FT_GlyphSlot類型是一個指針)。這是爲了便於避免每次都使用face->glyph->XXX。

* 我們以slot->advance增加筆位置,slot->advance符合字形的步進寬度(也就是通常所說的走格(escapement))。步進矢量以象素的1/64爲單位表示,並且在每一次迭代中刪減爲整數象素。

* 函數my_draw_bitmap不是FreeType的一部分,但必須由應用程序提供以用來繪製位圖到目標表面。在這個例子中,該函數以一個FT_Bitmap描述符的指針和它的左上角位置爲參數。

* Slot->bitmap_top的值是正數,指字形圖像頂點與pen_y的垂直距離。我們假定my_draw_bitmap採用的座標使用一樣的約定(增加Y值對應向下的掃描線)。我們用pen_y減它,而不是加它。

b.精練的代碼

下面的代碼是上面例子程序的精練版本。它使用了FreeType 2中我們還沒有介紹的特性和函數,我們將在下面解釋:

FT_GlyphSlot slot = face->glyph; /* 一個小捷徑 */
FT_UInt glyph_index;
int pen_x, pen_y, n;


... initialize library ...
... create face object ...
... set character size ...

pen_x = 300;
pen_y = 200;

for ( n = 0; n < num_chars; n++ )
{
/* 裝載字形圖像到字形槽(將會抹掉先前的字形圖像) */
error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );
if ( error )
continue; /* 忽略錯誤 */

/* 現在,繪製到我們的目標表面(surface) */
my_draw_bitmap( &slot->bitmap,
pen_x + slot->bitmap_left,
pen_y - slot->bitmap_top );

/* 增加筆位置 */
pen_x += slot->advance.x >> 6;
}

我們簡化了代碼的長度,但它完成相同的工作:

* 我們使用函數FT_Loac_Char代替FT_Load_Glyph。如你大概想到的,它相當於先調用GT_Get_Char_Index然後調用FT_Get_Load_Glyph。

* 我們不使用FT_LOAD_DEFAULT作爲裝載模式,使用FT_LOAD_RENDER。它指示了字形圖像必須立即轉換爲一個抗鋸齒位圖。這是一個捷徑,可以取消明顯的調用FT_Render_Glyph,但功能是相同的。
注意,你也可以指定通過附加FT_LOAD_MONOCHROME裝載標誌來獲得一個單色位圖。

c.更高級的渲染

現在,讓我們來嘗試渲染變換文字(例如通過一個環)。我們可以用FT_Set_Transform來完成。這裏是示例代碼:

FT_GlyphSlot slot;
FT_Matrix matrix; /* 變換矩陣 */
FT_UInt glyph_index;
FT_Vector pen; /* 非變換原點 */
int n;


... initialize library ...
... create face object ...
... set character size ...

slot = face->glyph; /* 一個小捷徑 */

/* 準備矩陣 */
matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );

/* 26.6 笛卡兒空間座標中筆的位置,以(300, 200)爲起始 */
pen.x = 300 * 64;
pen.y = ( my_target_height - 200 ) * 64;

for ( n = 0; n < num_chars; n++ )
{
/* 設置變換 */
FT_Set_Transform( face, &matrix, &pen );

/* 裝載字形圖像到字形槽(將會抹掉先前的字形圖像) */
error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );
if ( error )
continue; /* 忽略錯誤 */

/* 現在,繪製到我們的目標表面(變換位置) */
my_draw_bitmap( &slot->bitmap,
slot->bitmap_left,
my_target_height - slot->bitmap_top );

/* 增加筆位置 */
pen.x += slot->advance.x;
pen.y += slot->advance.y;
}

一些說明:

* 現在我們使用一個FT_Vector類型的矢量來存儲筆位置,其座標以象素的1/64爲單位表示,並且倍增。該位置表示在笛卡兒空間。

* 不同於系統典型的對位圖使用的座標系(其最高的掃描線是座標0),FreeType中,字形圖像的裝載、變換和描述總是採用笛卡兒座標系(這意味着增加Y對應向上的掃描線)。因此當我們定義筆位置和計算位圖左上角時必須在兩個系統之間轉換。

* 我們對每一個字形設置變換來指示旋轉矩陣以及使用一個delta來移動轉換後的圖像到當前筆位置(在笛卡兒空間,不是位圖空間)。結 果,bitmap_left和bitmap_top的值對應目標空間象素中的位圖原點。因此,我們在調用my_draw_bitmap時不在它們的值上加 pen.x或pen.y。

* 步進寬度總會在變換後返回,這就是它可以直接加到當前筆位置的原因。注意,這次它不會四捨五入。

一個例子完整的源代碼可以在這裏找到。

要很注意,雖然這個例子比前面的更復雜,但是變換效果是完全一致的。因此它可以作爲一個替換(但更強大)。

然而該例子有少許缺點,我們將在本教程的下一部分中解釋和解決。

結論

在這個部分,你已經學習了FreeType2的基礎以及渲染旋轉文字的充分知識。

下一部分將深入瞭解FreeType 2 API更詳細的資料,它可以讓你直接訪問字形度量標準和字形圖像,還能讓你學習到如何處理縮放、hinting、自居調整,等等。

發佈了60 篇原創文章 · 獲贊 12 · 訪問量 39萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章