wordpress 復(fù)制網(wǎng)站競(jìng)價(jià)推廣網(wǎng)絡(luò)推廣運(yùn)營(yíng)
freetype學(xué)習(xí)總結(jié)
目錄
- freetype學(xué)習(xí)總結(jié)
- 1. LCD顯示字符問(wèn)題引入
- 2. freetype概念
- 2.1 嵌入式設(shè)備使用FreeType的方法步驟
- 2.2 嵌入式設(shè)備使用FreeType的注意事項(xiàng)
- 3. freetype官方C示例
- 3.1 example1.c源碼
- 4. 嵌入式設(shè)備上使用FreeType的簡(jiǎn)單示例
- 4.1 簡(jiǎn)單示例代碼
- 4.2 代碼分析
- 5. 交叉編譯freetype
- 5.1 確定頭文件、庫(kù)文件在工具鏈中的目錄
- 5.2 交叉編譯、安裝libpng
- 5.3 交叉編譯、安裝freetype
- 6. 使用freetype在LCD顯示矢量字體
- 6.1 在LCD上顯示一個(gè)矢量字體
- 6.1.1 使用wchar_t獲得字符的UNICODE值
- 6.1.2 使用freetype得到位圖
- 6.1.3 在屏幕上顯示位圖
- 6.1.4 交叉編譯代碼
- 6.1.5 測(cè)試
- 6.2 在LCD上令矢量字體旋轉(zhuǎn)某個(gè)角度
- 6.2.1 代碼分析
- 6.2.2 交叉編譯代碼
- 6.2.3 測(cè)試
- 6.3 使用freetype顯示一行文字
- 6.3.1 使用freetype顯示一行文字的方法
- 6.3.1.1笛卡爾坐標(biāo)系
- 6.3.1.2 每個(gè)字符的大小可能不同
- 6.3.1.3 在指定位置顯示一行文字
- 6.3.1.4 freetype的幾個(gè)重要數(shù)據(jù)結(jié)構(gòu)
- 6.3.2 使用freetype顯示一行文字源碼
- 6.3.3 代碼分析
- 6.3.3.1 計(jì)算一行文字的外框
- 6.3.3.2 調(diào)整原點(diǎn)并繪制
- 6.3.4 交叉編譯代碼
- 6.3.5 測(cè)試
基于韋東山IMX6ULL開發(fā)板和配套資料中LCD屏幕學(xué)習(xí)
freetype庫(kù)資料包:開源的字體引擎庫(kù) freetype 和字體文件 simsun.ttc,資料包括:
? 1、freetype-2.10.2.tar.xz
? 2、freetype-doc-2.10.2.tar.xz
? 3、libpng-1.6.37.tar.xz
? 4、zlib-1.2.11.tar.gz
? 5、simsun.ttc文件,freetype可以直接使用
freetype 依賴于 libpng, libpng 又依賴于 zlib,所以我們應(yīng)該:先編譯安裝 zlib,再編譯安裝 libpng,最后編譯安裝 freetype。 但是,有些工具鏈里有 zlib, 那就不用編譯安裝 zlib。
freetype官方文檔:FreeType Documentation
1. LCD顯示字符問(wèn)題引入
在使用字庫(kù)點(diǎn)陣的方式在LCD上顯示英文字母、漢字時(shí),大小固定,如果放大縮小則會(huì)模糊甚至有鋸齒出現(xiàn),為了解決這個(gè)問(wèn)題,引用矢量字體。
矢量字體形成分三步:
- 確定關(guān)鍵點(diǎn)
- 使用數(shù)學(xué)曲線(貝塞爾曲線)連接頭鍵點(diǎn)
- 填充閉合區(qū)線內(nèi)部空間
以字母“A”為例,它的的關(guān)鍵點(diǎn)如圖中的黃色所示:
再用數(shù)學(xué)曲線(比如貝塞爾曲線)將關(guān)鍵點(diǎn)都連接起來(lái),得到一系列的封閉的曲線,如圖所示:
最后把封閉空間填滿顏色,就顯示出一個(gè)A字母,如圖所示:
如果需要放大或者縮小字體,關(guān)鍵點(diǎn)的相對(duì)位置是不變的,只要數(shù)學(xué)曲線平滑,字體就不會(huì)變形。
在嵌入式設(shè)備上移植開源的字體渲染引擎freetype,調(diào)用對(duì)應(yīng)的API接口,提供字體文件,就可以讓freetype庫(kù)幫我們?nèi)〕鲫P(guān)鍵點(diǎn)、實(shí)現(xiàn)閉合曲線,填充顏色,達(dá)到顯示矢量字體的目的。
2. freetype概念
FreeType是一個(gè)開源的字體渲染引擎,它能夠加載和渲染多種格式的字體文件,如TrueType(.ttf)、OpenType(.otf)等。FreeType提供了一組API來(lái)處理字體數(shù)據(jù),包括字符的加載、渲染以及獲取字符的各種信息(例如字形邊界框、位圖等)。它被廣泛應(yīng)用于各種需要顯示文本的應(yīng)用程序中,尤其是在那些需要高質(zhì)量文本渲染的地方。
2.1 嵌入式設(shè)備使用FreeType的方法步驟
- 安裝 FreeType 庫(kù):
- 下載FreeType源碼。
- 配置并編譯FreeType以適應(yīng)你的目標(biāo)平臺(tái)。
- 將編譯好的庫(kù)文件和頭文件部署到你的開發(fā)環(huán)境中。
- 編寫代碼:
- 初始化FreeType庫(kù)。
- 加載字體文件。
- 設(shè)置字體大小。
- 渲染指定的字符或字符串。
- 處理渲染后的位圖數(shù)據(jù)。
- 清理資源。
- 集成到圖形系統(tǒng):
- 根據(jù)你的嵌入式系統(tǒng)的圖形庫(kù)(如SDL,OpenGL ES,或者自定義的繪圖函數(shù)),將渲染后的位圖繪制到屏幕上。
2.2 嵌入式設(shè)備使用FreeType的注意事項(xiàng)
- 內(nèi)存限制:嵌入式設(shè)備可能有嚴(yán)格的內(nèi)存限制,因此要確保 FreeType 的配置和使用不會(huì)占用過(guò)多內(nèi)存。
- 性能優(yōu)化:根據(jù)嵌入式設(shè)備的性能特性,對(duì) FreeType 的使用進(jìn)行優(yōu)化,比如選擇合適的字體大小和渲染選項(xiàng)。
- 字體文件大小:考慮存儲(chǔ)空間限制,選擇合適大小的字體文件。
- 跨平臺(tái)兼容性:確保 FreeType 的編譯設(shè)置與你的目標(biāo)硬件平臺(tái)兼容。
3. freetype官方C示例
3.1 example1.c源碼
/* example1.c */
/* */
/* This small program shows how to print a rotated string with the */
/* FreeType 2 library. */#include <stdio.h>
#include <string.h>
#include <math.h>#include <ft2build.h>
#include FT_FREETYPE_H#define WIDTH 640
#define HEIGHT 480/* origin is the upper left corner */
unsigned char image[HEIGHT][WIDTH];/* Replace this function with something useful. */void
draw_bitmap( FT_Bitmap* bitmap,FT_Int x,FT_Int y)
{FT_Int i, j, p, q;FT_Int x_max = x + bitmap->width;FT_Int y_max = y + bitmap->rows;/* for simplicity, we assume that `bitmap->pixel_mode' *//* is `FT_PIXEL_MODE_GRAY' (i.e., not a bitmap font) */for ( i = x, p = 0; i < x_max; i++, p++ ){for ( j = y, q = 0; j < y_max; j++, q++ ){if ( i < 0 || j < 0 ||i >= WIDTH || j >= HEIGHT )continue;image[j][i] |= bitmap->buffer[q * bitmap->width + p];}}
}void
show_image( void )
{int i, j;for ( i = 0; i < HEIGHT; i++ ){for ( j = 0; j < WIDTH; j++ )putchar( image[i][j] == 0 ? ' ': image[i][j] < 128 ? '+': '*' );putchar( '\n' );}
}int
main( int argc,char** argv )
{FT_Library library;FT_Face face;FT_GlyphSlot slot;FT_Matrix matrix; /* transformation matrix */FT_Vector pen; /* untransformed origin */FT_Error error;char* filename;char* text;double angle;int target_height;int n, num_chars;if ( argc != 3 ){fprintf ( stderr, "usage: %s font sample-text\n", argv[0] );exit( 1 );}filename = argv[1]; /* first argument */text = argv[2]; /* second argument */num_chars = strlen( text );angle = ( 25.0 / 360 ) * 3.14159 * 2; /* use 25 degrees */target_height = HEIGHT;error = FT_Init_FreeType( &library ); /* initialize library *//* error handling omitted */error = FT_New_Face( library, filename, 0, &face );/* create face object *//* error handling omitted *//* use 50pt at 100dpi */error = FT_Set_Char_Size( face, 50 * 64, 0,100, 0 ); /* set character size *//* error handling omitted *//* cmap selection omitted; *//* for simplicity we assume that the font contains a Unicode cmap */slot = face->glyph;/* set up matrix */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 );/* the pen position in 26.6 cartesian space coordinates; *//* start at (300,200) relative to the upper left corner */pen.x = 300 * 64;pen.y = ( target_height - 200 ) * 64;for ( n = 0; n < num_chars; n++ ){/* set transformation */FT_Set_Transform( face, &matrix, &pen );/* load glyph image into the slot (erase previous one) */error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );if ( error )continue; /* ignore errors *//* now, draw to our target surface (convert position) */draw_bitmap( &slot->bitmap,slot->bitmap_left,target_height - slot->bitmap_top );/* increment pen position */pen.x += slot->advance.x;pen.y += slot->advance.y;}show_image();FT_Done_Face ( face );FT_Done_FreeType( library );return 0;
}/* EOF */
4. 嵌入式設(shè)備上使用FreeType的簡(jiǎn)單示例
4.1 簡(jiǎn)單示例代碼
在嵌入式設(shè)備上使用FreeType來(lái)加載字體并渲染字符 “A”,然后假設(shè)你有一個(gè)自定義的繪圖函數(shù)draw_pixel
來(lái)在屏幕上繪制點(diǎn)。
代碼:
#include <ft2build.h>
#include FT_FREETYPE_H
#include <stdio.h>
#include <stdlib.h>// 假設(shè)這是你的繪圖函數(shù)
void draw_pixel(int x, int y, unsigned char color) {// 實(shí)現(xiàn)具體的繪圖邏輯// 這里只是一個(gè)示例printf("Draw pixel at (%d, %d) with color %u\n", x, y, color);
}int main() {FT_Library ft;if (FT_Init_FreeType(&ft)) {fprintf(stderr, "Could not init FreeType Library\n");return 1;}FT_Face face;if (FT_New_Face(ft, "/path/to/your/fontfile.ttf", 0, &face)) {fprintf(stderr, "Could not open font\n");return 1;}FT_Set_Pixel_Sizes(face, 0, 24);if (FT_Load_Char(face, 'A', FT_LOAD_RENDER)) {fprintf(stderr, "Could not load character 'A'\n");return 1;}FT_Bitmap bitmap = face->glyph->bitmap;int width = bitmap.width;int height = bitmap.rows;unsigned char* buffer = bitmap.buffer;for (int y = 0; y < height; ++y) {for (int x = 0; x < width; ++x) {if (buffer[y * width + x] > 0) {int screen_x = 100 + x;int screen_y = 100 + y;draw_pixel(screen_x, screen_y, 255);}}}FT_Done_Face(face);FT_Done_FreeType(ft);return 0;
}
4.2 代碼分析
代碼分析參考官方freetype教程:
- 包含必要的頭文件:
#include <ft2build.h>
#include FT_FREETYPE_H
#include <stdio.h>
#include <stdlib.h>
- 初始化FreeType庫(kù):
FT_Library ft;
if (FT_Init_FreeType(&ft)) {fprintf(stderr, "Could not init FreeType Library\n");exit(1);
}
- 加載字體文件:
FT_Face face;
if (FT_New_Face(ft, "/path/to/your/fontfile.ttf", 0, &face)) {fprintf(stderr, "Could not open font\n");exit(1);
}
- 設(shè)置字體大小:
FT_Set_Pixel_Sizes(face, 0, 24); // 設(shè)置為24像素高
- 加載置頂字符:
if (FT_Load_Char(face, 'A', FT_LOAD_RENDER)) {fprintf(stderr, "Could not load character 'A'\n");exit(1);
}
- 獲取位圖信息:
FT_Bitmap bitmap = face->glyph->bitmap;
int width = bitmap.width;
int height = bitmap.rows;
unsigned char* buffer = bitmap.buffer;
- 繪制位圖:
// 假設(shè) draw_pixel(x, y, color) 是繪圖函數(shù)
for (int y = 0; y < height; ++y) {for (int x = 0; x < width; ++x) {if (buffer[y * width + x] > 0) { // 如果像素不透明int screen_x = 100 + x; // 假設(shè)起始位置是 (100, 100)int screen_y = 100 + y;draw_pixel(screen_x, screen_y, 255); // 使用白色繪制}}
}
- 清理資源:
FT_Done_Face(face);
FT_Done_FreeType(ft);
5. 交叉編譯freetype
freetype 依賴于 libpng, libpng 又依賴于 zlib,所以我們應(yīng)該:先編譯安裝 zlib,再編譯安裝 libpng,最后編譯安裝 freetype。 但是,有些工具鏈里有 zlib, 那就不用編譯安裝 zlib。文章開頭資料包中包含需要的庫(kù)。
基于IMX6ULL開發(fā)板驗(yàn)證,使用對(duì)于的工具鏈對(duì)freetype交叉編譯。
5.1 確定頭文件、庫(kù)文件在工具鏈中的目錄
先設(shè)置交叉編譯工具鏈:
export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
IMX6ULL開發(fā)板為例,它的工具鏈?zhǔn)莂rm-buildroot-linuxgnueabihf-gcc,可以執(zhí)行以下命令:
echo 'main(){}' | arm-buildroot-linux-gnueabihf-gcc -E -v -
可以確定頭文件的系統(tǒng)目錄為:
/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include
庫(kù)文件的系統(tǒng)目錄為:
/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/lib
5.2 交叉編譯、安裝libpng
先把freetype-2.10.2.tar.xz 、freetype-doc-2.10.2.tar.xz 、libpng-1.6.37.tar.xz 、zlib-1.2.11.tar.gz庫(kù)的壓縮文件上傳到Ubuntu:
freetype依賴于libpng,所以需要先編譯、安裝libpng。命令如下:
tar xJf libpng-1.6.37.tar.xz
cd libpng-1.6.37/
./configure --host=arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp
make
make install
cd tmp
cp include/* -rf /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include
cp lib/* -rfd /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/lib
5.3 交叉編譯、安裝freetype
命令如下:
tar xJf freetype-2.10.2.tar.xz
cd freetype-2.10.2
./configure --host=arm-buildroot-linux-gnueabihf -prefix=$PWD/tmp
make
make install
cd tmp
cp include/freetype2/* -rfd /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include
cp lib/* -rfd /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/lib
6. 使用freetype在LCD顯示矢量字體
freetype使用統(tǒng)一的接口來(lái)訪問(wèn)多種字體格式文件,從而實(shí)現(xiàn)矢量字體顯示。關(guān)鍵點(diǎn)(glyph)存在字體文件中,Windows使用的字體文件在c:\Windows\Fonts目錄下,擴(kuò)展名為TTF的都是矢量字庫(kù),本次使用實(shí)驗(yàn)使用的是新宋字體simsun.ttc,可以在資料包中下載。
.ttf 和 .ttc 文件都是字體文件格式,但它們之間存在一些關(guān)鍵的區(qū)別:
- .ttf (TrueType Font)
- TrueType 字體是一種常見的計(jì)算機(jī)字體類型,由蘋果公司和微軟共同開發(fā)。
- 這種格式的字體文件通常包含一個(gè)單獨(dú)的字體樣式。例如,你可能會(huì)有一個(gè)文件用于常規(guī)樣式,另一個(gè)文件用于粗體樣式等。
- .ttf 文件可以跨平臺(tái)使用,在 Windows、macOS 以及許多其他操作系統(tǒng)上都可以被支持。
- .ttc (TrueType Collection)
- TrueType Collection 是一種特殊的字體文件格式,它允許將多個(gè) TrueType 字體打包到一個(gè)單一的文件中。
- 在 TTC 文件內(nèi),你可以找到多個(gè)字體變體(如常規(guī)、斜體、粗體等),這有助于減少文件大小并提高加載效率,因?yàn)橄嚓P(guān)聯(lián)的字體數(shù)據(jù)會(huì)被共享。
- 使用 TTC 可以節(jié)省磁盤空間,并且在某些情況下可以加快字體加載速度,因?yàn)樗鼫p少了需要讀取的文件數(shù)量。
.ttf 文件是單個(gè)字體樣式的標(biāo)準(zhǔn)容器,而 .ttc 文件則是一個(gè)更高效的格式,能夠在一個(gè)文件中封裝多款相關(guān)的字體樣式。對(duì)于用戶而言,安裝 .ttc 文件就像安裝普通的 .ttf文件一樣簡(jiǎn)單,但是背后的數(shù)據(jù)結(jié)構(gòu)更為緊湊和優(yōu)化。
freetype對(duì).ttf和.ttc字體格式都支持:
以simsun.ttc為例,該字體文件的格如下:頭部含有charmaps,可以使用某種編碼值去charmaps中找到它對(duì)應(yīng)的關(guān)鍵點(diǎn)。下圖中的“A、B、中、國(guó)、韋”等只是glyph的示意圖,表示關(guān)鍵點(diǎn)。
Charmaps表示字符映射表,字體文件可能支持哪一些編碼,GB2312、UNICODE、BIG5或其他。如果字體文件支持該編碼,使用編碼值通過(guò)charmap就可以找到對(duì)應(yīng)的glyph,一般而言都支持UNICODE碼。
一個(gè)文字的顯示過(guò)程可以概括如下(參考4.2 代碼分析):
- 給定一個(gè)字符可以確定它的編碼值(ASCII、UNICODE、GB2312)
- 設(shè)置字體大小
- 根據(jù)編碼值,從文件頭部中通過(guò)charmap找到對(duì)應(yīng)的關(guān)鍵點(diǎn)(glyph),它會(huì)根據(jù)字體大小調(diào)整關(guān)鍵點(diǎn)
- 把關(guān)鍵點(diǎn)轉(zhuǎn)換為位圖點(diǎn)陣
- 在LCD上顯示出來(lái)
6.1 在LCD上顯示一個(gè)矢量字體
在LCD上顯示一個(gè)矢量字體的源碼:
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <wchar.h>
#include <sys/ioctl.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_Hint fd_fb;
struct fb_var_screeninfo var; /* Current var */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;/*********************************************************************** 函數(shù)名稱: lcd_put_pixel* 功能描述: 在LCD指定位置上輸出指定顏色(描點(diǎn))* 輸入?yún)?shù): x坐標(biāo),y坐標(biāo),顏色* 輸出參數(shù): 無(wú)* 返 回 值: 會(huì)* 修改日期 版本號(hào) 修改人 修改內(nèi)容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 創(chuàng)建***********************************************************************/
void lcd_put_pixel(int x, int y, unsigned int color)
{unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;unsigned short *pen_16; unsigned int *pen_32; unsigned int red, green, blue; pen_16 = (unsigned short *)pen_8;pen_32 = (unsigned int *)pen_8;switch (var.bits_per_pixel){case 8:{*pen_8 = color;break;}case 16:{/* 565 */red = (color >> 16) & 0xff;green = (color >> 8) & 0xff;blue = (color >> 0) & 0xff;color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);*pen_16 = color;break;}case 32:{*pen_32 = color;break;}default:{printf("can't surport %dbpp\n", var.bits_per_pixel);break;}}
}/*********************************************************************** 函數(shù)名稱: draw_bitmap* 功能描述: 根據(jù)bitmap位圖,在LCD指定位置顯示漢字* 輸入?yún)?shù): x坐標(biāo),y坐標(biāo),位圖指針* 輸出參數(shù): 無(wú)* 返 回 值: 無(wú)* 修改日期 版本號(hào) 修改人 修改內(nèi)容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 創(chuàng)建***********************************************************************/
void
draw_bitmap( FT_Bitmap* bitmap,FT_Int x,FT_Int y)
{FT_Int i, j, p, q;FT_Int x_max = x + bitmap->width;FT_Int y_max = y + bitmap->rows;//printf("x = %d, y = %d\n", x, y);for ( j = y, q = 0; j < y_max; j++, q++ ){for ( i = x, p = 0; i < x_max; i++, p++ ){if ( i < 0 || j < 0 ||i >= var.xres || j >= var.yres )continue;//image[j][i] |= bitmap->buffer[q * bitmap->width + p];lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);}}
}int main(int argc, char **argv)
{wchar_t *chinese_str = L"繁";FT_Library library;FT_Face face;int error;FT_Vector pen;FT_GlyphSlot slot;int font_size = 24;if (argc < 2){printf("Usage : %s <font_file> [font_size]\n", argv[0]);return -1;}if (argc == 3)font_size = strtoul(argv[2], NULL, 0);fd_fb = open("/dev/fb0", O_RDWR);if (fd_fb < 0){printf("can't open /dev/fb0\n");return -1;}if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)){printf("can't get var\n");return -1;}line_width = var.xres * var.bits_per_pixel / 8;pixel_width = var.bits_per_pixel / 8;screen_size = var.xres * var.yres * var.bits_per_pixel / 8;fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);if (fbmem == (unsigned char *)-1){printf("can't mmap\n");return -1;}/* 清屏: 全部設(shè)為黑色 */memset(fbmem, 0, screen_size);/* 顯示矢量字體 */error = FT_Init_FreeType( &library ); /* initialize library *//* error handling omitted */error = FT_New_Face( library, argv[1], 0, &face ); /* create face object *//* error handling omitted */ slot = face->glyph;FT_Set_Pixel_Sizes(face, font_size, 0);/* 確定座標(biāo):*///pen.x = 0;//pen.y = 0;/* set transformation *///FT_Set_Transform( face, 0, &pen);/* load glyph image into the slot (erase previous one) */error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER );if (error){printf("FT_Load_Char error\n");return -1;}draw_bitmap( &slot->bitmap,var.xres/2,var.yres/2);return 0;
}
6.1.1 使用wchar_t獲得字符的UNICODE值
要顯示一個(gè)字符,首先要確定它的編碼值。常用的是UNICODE編碼,在程序里使用這樣的語(yǔ)句定義字符串時(shí),str中保存的要么是GB2312編碼值,要么是UTF-8格式的編碼值,即使編譯時(shí)使用“-fexec-charset=UTF-8”,str中保存的也不是直接能使用的UNICODE值:
char *str = “中”;
如果想在代碼中能直接使用UNICODE值,需要使用wchar_t,寬字符,示例代碼如下:
01 #include <stdio.h>
02 #include <string.h>
03 #include <wchar.h>
04
05 int main( int argc, char** argv)
06 {
07 wchar_t *chinese_str = L"中 gif";
08 unsigned int *p = (wchar_t *)chinese_str;
09 int i;
10
11 printf("sizeof(wchar_t) = %d, str's Uniocde: \n", (int)sizeof(wchar_t));
12 for (i = 0; i < wcslen(chinese_str); i++)
13 {
14 printf("0x%x ", p[i]);
15 }
16 printf("\n");
17
18 return 0;
19 }
UTF-8格式保存test_wchar.c,編譯、測(cè)試命令如下:
每個(gè)wchar_t占據(jù)4字節(jié),可執(zhí)行程序里wchar_t中保存的就是字符的UNICODE值。
注意:注意:如果test_wchar.c是以ANSI(GB2312)格式保存,那么需要使用以下命令來(lái)編譯:
gcc -finput-charset=GB2312 -fexec-charset=UTF-8 -o test_wchar test_wchar.c
6.1.2 使用freetype得到位圖
使用 freetype 得到一個(gè)字符的位圖,需要 4 個(gè)步驟:
-
初始化freetype庫(kù)
158 error = FT_Init_FreeType( &library ); /* initialize library */
-
加載字體文件,保存在&face中
161 error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */ 162 /* error handling omitted */ 163 slot = face->glyph;
第 163 行是從 face 中獲得 FT_GlyphSlot,后面的代碼中文字的位圖就是保存在 FT_GlyphSlot 里。
-
設(shè)置字體大小
165 FT_Set_Pixel_Sizes(face, font_size, 0);
-
根據(jù)編碼值得到位圖
使用 FT_Load_Char 函數(shù),就可以實(shí)現(xiàn)這 3 個(gè)功能:
- 根據(jù)編碼值獲得 glyph_index: FT_Get_Char_Index
- 根據(jù) glyph_idex 取出 glyph: FT_Load_Glyph
- 渲染出位圖: FT_Render_Glyph
175 /* load glyph image into the slot (erase previous one) */ 176 error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER );
執(zhí)行 FT_Load_Char 之后,字符的位圖被存在 slot->bitmap 里,即 face->glyph->bitmap。
6.1.3 在屏幕上顯示位圖
位圖里的數(shù)據(jù)格式是怎樣的?參考 3.1 example1.c 的代碼,可以得到下圖:
在屏幕上顯示出這些位圖:
183 draw_bitmap( &slot->bitmap,
184 var.xres/2,
185 var.yres/2);
draw_bitmap 函數(shù)代碼如下,由于位圖中每一個(gè)像素用一個(gè)字節(jié)來(lái)表示,在0x00RRGGBB 的顏色格式中它只能表示藍(lán)色,所以在 LCD 上顯示出來(lái)的文字是藍(lán)色的:
6.1.4 交叉編譯代碼
IMX6ULL使用如下命令編譯:
arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font freetype_show_font.c -lfreetype
如果提示以下錯(cuò)誤:
freetype_show_font.c:12:10: fatal error: ft2build.h: No such file or directory#include <ft2build.h>^~~~~~~~~~~~
compilation terminated.
之前編譯出 freetype 后,得到的 ft2build.h 是位于 freetype2 目錄里,把整個(gè) freetype2 目錄復(fù)制進(jìn)了工具鏈里。
但是包括頭文件時(shí),用的是“ #include <ft2build.h>”,要么改成:
#include <freetype2/ft2build.h>
要么把工具鏈里 incldue/freetype2/*.h 復(fù)制到上一級(jí)目錄,使用這種方法:跟 freetype 文檔保持一致。執(zhí)行以下命令:
cd /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include
mv freetype2/* ./
再次執(zhí)行以下命令:
arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font freetype_show_font.c -lfreetype
6.1.5 測(cè)試
將編譯好的 freetype_show_font 文件與 simsun.ttc 字體文件拷貝至開發(fā)板,這 2 個(gè)文件放在同一個(gè)目錄下,然后執(zhí)行以下命令:
./freetype_show_font ./simsun.ttc
或者
./freetype_show_font ./simsun.ttc 300
6.2 在LCD上令矢量字體旋轉(zhuǎn)某個(gè)角度
在LCD上令矢量字體旋轉(zhuǎn)某個(gè)角度源碼:
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <wchar.h>
#include <sys/ioctl.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_Hint fd_fb;
struct fb_var_screeninfo var; /* Current var */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;/*********************************************************************** 函數(shù)名稱: lcd_put_pixel* 功能描述: 在LCD指定位置上輸出指定顏色(描點(diǎn))* 輸入?yún)?shù): x坐標(biāo),y坐標(biāo),顏色* 輸出參數(shù): 無(wú)* 返 回 值: 會(huì)* 修改日期 版本號(hào) 修改人 修改內(nèi)容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 創(chuàng)建***********************************************************************/
void lcd_put_pixel(int x, int y, unsigned int color)
{unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;unsigned short *pen_16; unsigned int *pen_32; unsigned int red, green, blue; pen_16 = (unsigned short *)pen_8;pen_32 = (unsigned int *)pen_8;switch (var.bits_per_pixel){case 8:{*pen_8 = color;break;}case 16:{/* 565 */red = (color >> 16) & 0xff;green = (color >> 8) & 0xff;blue = (color >> 0) & 0xff;color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);*pen_16 = color;break;}case 32:{*pen_32 = color;break;}default:{printf("can't surport %dbpp\n", var.bits_per_pixel);break;}}
}/*********************************************************************** 函數(shù)名稱: draw_bitmap* 功能描述: 根據(jù)bitmap位圖,在LCD指定位置顯示漢字* 輸入?yún)?shù): x坐標(biāo),y坐標(biāo),位圖指針* 輸出參數(shù): 無(wú)* 返 回 值: 無(wú)* 修改日期 版本號(hào) 修改人 修改內(nèi)容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 創(chuàng)建***********************************************************************/
void
draw_bitmap( FT_Bitmap* bitmap,FT_Int x,FT_Int y)
{FT_Int i, j, p, q;FT_Int x_max = x + bitmap->width;FT_Int y_max = y + bitmap->rows;//printf("x = %d, y = %d\n", x, y);for ( j = y, q = 0; j < y_max; j++, q++ ){for ( i = x, p = 0; i < x_max; i++, p++ ){if ( i < 0 || j < 0 ||i >= var.xres || j >= var.yres )continue;//image[j][i] |= bitmap->buffer[q * bitmap->width + p];lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);}}
}int main(int argc, char **argv)
{wchar_t *chinese_str = L"繁";FT_Library library;FT_Face face;int error;FT_Vector pen;FT_GlyphSlot slot;int font_size = 24;FT_Matrix matrix; /* transformation matrix */double angle;if (argc < 3){printf("Usage : %s <font_file> <angle> [font_size]\n", argv[0]);return -1;}angle = ( 1.0* strtoul(argv[2], NULL, 0) / 360 ) * 3.14159 * 2; /* use 25 degrees */if (argc == 4)font_size = strtoul(argv[3], NULL, 0);fd_fb = open("/dev/fb0", O_RDWR);if (fd_fb < 0){printf("can't open /dev/fb0\n");return -1;}if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)){printf("can't get var\n");return -1;}line_width = var.xres * var.bits_per_pixel / 8;pixel_width = var.bits_per_pixel / 8;screen_size = var.xres * var.yres * var.bits_per_pixel / 8;fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);if (fbmem == (unsigned char *)-1){printf("can't mmap\n");return -1;}/* 清屏: 全部設(shè)為黑色 */memset(fbmem, 0, screen_size);/* 顯示矢量字體 */error = FT_Init_FreeType( &library ); /* initialize library *//* error handling omitted */error = FT_New_Face( library, argv[1], 0, &face ); /* create face object *//* error handling omitted */ slot = face->glyph;FT_Set_Pixel_Sizes(face, font_size, 0);/* 確定座標(biāo):*/pen.x = 0;pen.y = 0;/* set up matrix */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 );/* set transformation */FT_Set_Transform( face, &matrix, &pen);/* load glyph image into the slot (erase previous one) */error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER );if (error){printf("FT_Load_Char error\n");return -1;}draw_bitmap( &slot->bitmap,var.xres/2,var.yres/2);return 0;
}
“在LCD上令矢量字體旋轉(zhuǎn)某個(gè)角度源碼“和“在LCD上顯示一個(gè)矢量字體的源碼”對(duì)比:
6.2.1 代碼分析
6.2.2 交叉編譯代碼
IMX6ULL使用如下命令編譯:
arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font_angle freetype_show_font_angle.c -lfreetype -lm
6.2.3 測(cè)試
將編譯好的 freetype_show_font_angle文件與 simsun.ttc 字體文件拷貝至開發(fā)板,這 2 個(gè)文件放在同一個(gè)目錄下,然后執(zhí)行以下命令:
./freetype_show_font_angle ./simsun.ttc 90 200
6.3 使用freetype顯示一行文字
6.3.1 使用freetype顯示一行文字的方法
在 LCD 上指定一個(gè)左上角坐標(biāo)(x, y),把一行文字顯示出來(lái)。下圖中,文字的外框用虛線表示,外框的左上角坐標(biāo)就是(x, y)。
6.3.1.1笛卡爾坐標(biāo)系
在 LCD 的坐標(biāo)系中,原點(diǎn)在屏幕的左上角。對(duì)于笛卡爾坐標(biāo)系,原點(diǎn)在左下角。 freetype 使用笛卡爾坐標(biāo)系,在顯示時(shí)需要轉(zhuǎn)換為 LCD 坐標(biāo)系。
從下圖可知, X 方向坐標(biāo)值是一樣的。
在 Y 方向坐標(biāo)值需要換算,假設(shè) LCD 的高度是 V。
在 LCD 坐標(biāo)系中坐標(biāo)是(x, y),那么它在笛卡爾坐標(biāo)系中的坐標(biāo)值為(x, V-y)。
反過(guò)來(lái)也是一樣的,在笛卡爾坐標(biāo)系中坐標(biāo)是(x, y),那么它在 LCD 坐標(biāo)系中坐標(biāo)值為(x, V-y)。
6.3.1.2 每個(gè)字符的大小可能不同
在使用 FT_Set_Pixel_Sizes 函數(shù)設(shè)置字體大小時(shí),這只是“期望值”。比如“百問(wèn)網(wǎng) www.100ask.net”,如果把“ .”顯示得跟其他漢字一樣大,不好看。
所以在顯示一行文字時(shí),后面文字的位置會(huì)受到前面文字的影響。
freetype 字體的尺寸(freetype Metrics),參考 4.2 代碼分析中官方freetype教程中“Managing Glyphs”教程:
在顯示一行文字時(shí),這些文字會(huì)基于同一個(gè)基線來(lái)繪制位圖:baseline。
在 baseline 上,每一個(gè)字符都有它的原點(diǎn)(origin),比如上圖中 baseline左邊的黑色圓點(diǎn)就是字母“ g”的原點(diǎn)。當(dāng)前 origin 加上 advance 就可以得到下一個(gè)字符的 origin,比如上圖中 baseline 右邊的黑色圓點(diǎn)。在顯示一行中多個(gè)文件字時(shí),后一個(gè)文字的原點(diǎn)依賴于前一個(gè)文字的原點(diǎn)及 advance。
字符的位圖是有可能越過(guò) baseline 的,比如上圖中字母“ g”在 baseline下方還有圖像。
上圖中紅色方框內(nèi)就是字母“g”所點(diǎn)據(jù)的位圖,它的四個(gè)角落不一定與原點(diǎn)重合。
上圖中那些xMin、xMax、yMin、yMax如何獲得?可以使用FT_Glyph_Get_CBox函數(shù)獲得一個(gè)字體的這些參數(shù),將會(huì)保存在一個(gè)FT_BBox結(jié)構(gòu)體中,以后想計(jì)算一行文字的外框時(shí)要用到這些信息:
6.3.1.3 在指定位置顯示一行文字
要顯示一行文字時(shí),每一個(gè)字符都有自己外框: xMin、 xMax、 yMin、 yMax。把這些字符的 xMin、 yMin 中的最小值取出來(lái),把這些字符的 xMax、 yMax 中的最大值取出來(lái),就可以確定這行文字的外框了。
如下圖,在指定位置(x, y)顯示一行文字:
- 先指定第 1 個(gè)字符的原點(diǎn) pen 坐標(biāo)為(0, 0),計(jì)算出它的外框
- 再計(jì)算右邊字符的原點(diǎn),也計(jì)算出它的外框,把所有字符都處理完后就可以得到一行文字的整體外框:假設(shè)外框左上角坐標(biāo)為(x’, y’)
- 想在(x, y)處顯示這行文字,調(diào)整一下 pen 坐標(biāo)即可。 pen 為(0, 0)時(shí)對(duì)應(yīng)左上角(x’, y’);那么左上角為(x, y)時(shí)就可以算出pen 為(x-x’, y-y’)
6.3.1.4 freetype的幾個(gè)重要數(shù)據(jù)結(jié)構(gòu)
參考4.2代碼分析中官方freetype文檔
FT_Library:
對(duì)應(yīng) freetype 庫(kù),使用 freetype 之前要先調(diào)用以下代碼:
FT_Library library; /* 對(duì)應(yīng) freetype 庫(kù) */
error = FT_Init_FreeType( &library ); /* 初始化 freetype 庫(kù) */
FT_Face:
它對(duì)應(yīng)一個(gè)矢量字體文件,在源碼中使用 FT_New_Face 函數(shù)打開字體文件后,就可以得到一個(gè) face。
為什么稱之為 face?
估計(jì)是文字都是寫在二維平面上的吧,正對(duì)著人臉?不用管原因了,總之認(rèn)為它對(duì)應(yīng)一個(gè)字體文件就可以。
代碼如下:
error = FT_New_Face(library, font_file, 0, &face ); /* 加載字體文件 */
FT_GlyphSlot:
插槽?用來(lái)保存字符的處理結(jié)果:比如轉(zhuǎn)換后的 glyph、位圖,如下圖:
一個(gè) face 中有很多字符,生成一個(gè)字符的點(diǎn)陣位圖時(shí),位圖保存在哪里?保存在插槽中: face->glyph。
生成第 1 個(gè)字符位圖時(shí),它保存在 face->glyph 中;生成第 2 個(gè)字符位圖時(shí),也會(huì)保存在 face->glyph 中,會(huì)覆蓋第 1 個(gè)字符的位圖。
代碼如下:
FT_GlyphSlot slot = face->glyph; /* 插槽: 字體的處理結(jié)果保存在這里 */
FT_Glyph:
字體文件中保存有字符的原始關(guān)鍵點(diǎn)信息,使用 freetype 的函數(shù)可以放大、縮小、旋轉(zhuǎn),這些新的關(guān)鍵點(diǎn)保存在插槽中(注意:位圖也是保存在插槽中)。
新的關(guān)鍵點(diǎn)使用 FT_Glyph 來(lái)表示,可以使用這樣的代碼從 slot 中獲得glyph:
error = FT_Get_Glyph(slot , &glyph);
FT_BBox:
FT_BBox 結(jié)構(gòu)體定義如下,它表示一個(gè)字符的外框,即新 glyph 的外框:
可以使用以下代碼從 glyph 中獲得這些信息:
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );
示例代碼:
6.3.2 使用freetype顯示一行文字源碼
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <wchar.h>
#include <sys/ioctl.h>#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_Hint fd_fb;
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;/* color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;unsigned short *pen_16; unsigned int *pen_32; unsigned int red, green, blue; pen_16 = (unsigned short *)pen_8;pen_32 = (unsigned int *)pen_8;switch (var.bits_per_pixel){case 8:{*pen_8 = color;break;}case 16:{/* 565 */red = (color >> 16) & 0xff;green = (color >> 8) & 0xff;blue = (color >> 0) & 0xff;color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);*pen_16 = color;break;}case 32:{*pen_32 = color;break;}default:{printf("can't surport %dbpp\n", var.bits_per_pixel);break;}}
}/*********************************************************************** 函數(shù)名稱: draw_bitmap* 功能描述: 根據(jù)bitmap位圖,在LCD指定位置顯示漢字* 輸入?yún)?shù): x坐標(biāo),y坐標(biāo),位圖指針* 輸出參數(shù): 無(wú)* 返 回 值: 無(wú)* 修改日期 版本號(hào) 修改人 修改內(nèi)容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 創(chuàng)建***********************************************************************/
void
draw_bitmap( FT_Bitmap* bitmap,FT_Int x,FT_Int y)
{FT_Int i, j, p, q;FT_Int x_max = x + bitmap->width;FT_Int y_max = y + bitmap->rows;//printf("x = %d, y = %d\n", x, y);for ( j = y, q = 0; j < y_max; j++, q++ ){for ( i = x, p = 0; i < x_max; i++, p++ ){if ( i < 0 || j < 0 ||i >= var.xres || j >= var.yres )continue;//image[j][i] |= bitmap->buffer[q * bitmap->width + p];lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);}}
}int compute_string_bbox(FT_Face face, wchar_t *wstr, FT_BBox *abbox)
{int i;int error;FT_BBox bbox;FT_BBox glyph_bbox;FT_Vector pen;FT_Glyph glyph;FT_GlyphSlot slot = face->glyph;/* 初始化 */bbox.xMin = bbox.yMin = 32000;bbox.xMax = bbox.yMax = -32000;/* 指定原點(diǎn)為(0, 0) */pen.x = 0;pen.y = 0;/* 計(jì)算每個(gè)字符的bounding box *//* 先translate, 再load char, 就可以得到它的外框了 */for (i = 0; i < wcslen(wstr); i++){/* 轉(zhuǎn)換:transformation */FT_Set_Transform(face, 0, &pen);/* 加載位圖: load glyph image into the slot (erase previous one) */error = FT_Load_Char(face, wstr[i], FT_LOAD_RENDER);if (error){printf("FT_Load_Char error\n");return -1;}/* 取出glyph */error = FT_Get_Glyph(face->glyph, &glyph);if (error){printf("FT_Get_Glyph error!\n");return -1;}/* 從glyph得到外框: bbox */FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox);/* 更新外框 */if ( glyph_bbox.xMin < bbox.xMin )bbox.xMin = glyph_bbox.xMin;if ( glyph_bbox.yMin < bbox.yMin )bbox.yMin = glyph_bbox.yMin;if ( glyph_bbox.xMax > bbox.xMax )bbox.xMax = glyph_bbox.xMax;if ( glyph_bbox.yMax > bbox.yMax )bbox.yMax = glyph_bbox.yMax;/* 計(jì)算下一個(gè)字符的原點(diǎn): increment pen position */pen.x += slot->advance.x;pen.y += slot->advance.y;}/* return string bbox */*abbox = bbox;
}int display_string(FT_Face face, wchar_t *wstr, int lcd_x, int lcd_y)
{int i;int error;FT_BBox bbox;FT_Vector pen;FT_Glyph glyph;FT_GlyphSlot slot = face->glyph;/* 把LCD坐標(biāo)轉(zhuǎn)換為笛卡爾坐標(biāo) */int x = lcd_x;int y = var.yres - lcd_y;/* 計(jì)算外框 */compute_string_bbox(face, wstr, &bbox);/* 反推原點(diǎn) */pen.x = (x - bbox.xMin) * 64; /* 單位: 1/64像素 */pen.y = (y - bbox.yMax) * 64; /* 單位: 1/64像素 *//* 處理每個(gè)字符 */for (i = 0; i < wcslen(wstr); i++){/* 轉(zhuǎn)換:transformation */FT_Set_Transform(face, 0, &pen);/* 加載位圖: load glyph image into the slot (erase previous one) */error = FT_Load_Char(face, wstr[i], FT_LOAD_RENDER);if (error){printf("FT_Load_Char error\n");return -1;}/* 在LCD上繪制: 使用LCD坐標(biāo) */draw_bitmap( &slot->bitmap,slot->bitmap_left,var.yres - slot->bitmap_top);/* 計(jì)算下一個(gè)字符的原點(diǎn): increment pen position */pen.x += slot->advance.x;pen.y += slot->advance.y;}return 0;
}int main(int argc, char **argv)
{wchar_t *wstr = L"百問(wèn)網(wǎng)www.100ask.net";FT_Library library;FT_Face face;int error;FT_BBox bbox;int font_size = 24;int lcd_x, lcd_y;if (argc < 4){printf("Usage : %s <font_file> <lcd_x> <lcd_y> [font_size]\n", argv[0]);return -1;}lcd_x = strtoul(argv[2], NULL, 0); lcd_y = strtoul(argv[3], NULL, 0); if (argc == 5)font_size = strtoul(argv[4], NULL, 0); fd_fb = open("/dev/fb0", O_RDWR);if (fd_fb < 0){printf("can't open /dev/fb0\n");return -1;}if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)){printf("can't get var\n");return -1;}if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix)){printf("can't get fix\n");return -1;}line_width = var.xres * var.bits_per_pixel / 8;pixel_width = var.bits_per_pixel / 8;screen_size = var.xres * var.yres * var.bits_per_pixel / 8;fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);if (fbmem == (unsigned char *)-1){printf("can't mmap\n");return -1;}/* 清屏: 全部設(shè)為黑色 */memset(fbmem, 0, screen_size);error = FT_Init_FreeType( &library ); /* initialize library */error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */FT_Set_Pixel_Sizes(face, font_size, 0);display_string(face, wstr, lcd_x, lcd_y);return 0;
}
6.3.3 代碼分析
6.3.3.1 計(jì)算一行文字的外框
一行文字中:后一個(gè)字符的原點(diǎn)=前一個(gè)字符的原點(diǎn)+advance。所以要計(jì)算一行文字的外框,需要按照排列順序處理其中的每一個(gè)字符。代碼如下:
102 int compute_string_bbox(FT_Face face, wchar_t *wstr, FT_BBox *abbox)
103 {
104 int i;
105 int error;
106 FT_BBox bbox;
107 FT_BBox glyph_bbox;
108 FT_Vector pen;
109 FT_Glyph glyph;
110 FT_GlyphSlot slot = face->glyph;
111
112 /* 初始化 */
113 bbox.xMin = bbox.yMin = 32000;
114 bbox.xMax = bbox.yMax = -32000;
115
116 /* 指定原點(diǎn)為(0, 0) */
117 pen.x = 0;
118 pen.y = 0;
119
120 /* 計(jì)算每個(gè)字符的 bounding box */
121 /* 先 translate, 再 load char, 就可以得到它的外框了 */
122 for (i = 0; i < wcslen(wstr); i++)
123 {
124 /* 轉(zhuǎn)換: transformation */
125 FT_Set_Transform(face, 0, &pen);
126
127 /* 加載位圖: load glyph image into the slot (erase previous one) */
128 error = FT_Load_Char(face, wstr[i], FT_LOAD_RENDER);
129 if (error)
130 {
131 printf("FT_Load_Char error\n");
132 return -1;
133 }
134
135 /* 取出 glyph */
136 error = FT_Get_Glyph(face->glyph, &glyph);
137 if (error)
138 {
139 printf("FT_Get_Glyph error!\n");
140 return -1;
141 }
142
143 /* 從 glyph 得到外框: bbox */
144 FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox);
145
146 /* 更新外框 */
147 if ( glyph_bbox.xMin < bbox.xMin )
148 bbox.xMin = glyph_bbox.xMin;
149
150 if ( glyph_bbox.yMin < bbox.yMin )
151 bbox.yMin = glyph_bbox.yMin;
152
153 if ( glyph_bbox.xMax > bbox.xMax )
154 bbox.xMax = glyph_bbox.xMax;
155
156 if ( glyph_bbox.yMax > bbox.yMax )
157 bbox.yMax = glyph_bbox.yMax;
158
159 /* 計(jì)算下一個(gè)字符的原點(diǎn): increment pen position */
160 pen.x += slot->advance.x;
161 pen.y += slot->advance.y;
162 }
163
164 /* return string bbox */
165 *abbox = bbox;
166 }
6.3.3.2 調(diào)整原點(diǎn)并繪制
代碼如下:
169 int display_string(FT_Face face, wchar_t *wstr, int lcd_x, int lcd_y)
170 {
171 int i;
172 int error;
173 FT_BBox bbox;
174 FT_Vector pen;
175 FT_Glyph glyph;
176 FT_GlyphSlot slot = face->glyph;
177
178 /* 把 LCD 坐標(biāo)轉(zhuǎn)換為笛卡爾坐標(biāo) */
179 int x = lcd_x;
180 int y = var.yres - lcd_y;
181
182 /* 計(jì)算外框 */
183 compute_string_bbox(face, wstr, &bbox);
184
185 /* 反推原點(diǎn) */
186 pen.x = (x - bbox.xMin) * 64; /* 單位: 1/64 像素 */
187 pen.y = (y - bbox.yMax) * 64; /* 單位: 1/64 像素 */
188
189 /* 處理每個(gè)字符 */
190 for (i = 0; i < wcslen(wstr); i++)
191 {
192 /* 轉(zhuǎn)換: transformation */
193 FT_Set_Transform(face, 0, &pen);
194
195 /* 加載位圖: load glyph image into the slot (erase previous one) */
196 error = FT_Load_Char(face, wstr[i], FT_LOAD_RENDER);
197 if (error)
198 {
199 printf("FT_Load_Char error\n");
200 return -1;
201 }
202
203 /* 在 LCD 上繪制: 使用 LCD 坐標(biāo) */
204 draw_bitmap( &slot->bitmap,
205 slot->bitmap_left,
206 var.yres - slot->bitmap_top);
207
208 /* 計(jì)算下一個(gè)字符的原點(diǎn): increment pen position */
209 pen.x += slot->advance.x;
210 pen.y += slot->advance.y;
211 }
212
213 return 0;
214 }
6.3.4 交叉編譯代碼
IMX6ULL使用如下命令編譯:
arm-buildroot-linux-gnueabihf-gcc -o show_line show_line.c -lfreetype
6.3.5 測(cè)試
將編譯好的 show_line 文件與 simsun.ttc 字體文件拷貝至開發(fā)板,這 2個(gè)文件放在同一個(gè)目錄下,然后執(zhí)行以下命令(其中的 3 個(gè)數(shù)字分別表示 LCD 的X 坐標(biāo)、 Y 坐標(biāo)、字體大小):
./freetype_show_font_angle ./simsun.ttc 90 200
基于freetyp的基礎(chǔ)使用,可以進(jìn)行更復(fù)雜的LCD操作。