3.數碼相框-通過freetype庫實現矢量顯示


本章主要內容如下:

  • 1)矢量字體原理
  • 2)使用freetype庫實現矢量字體顯示

 


 

1. 矢量字體原理

   將漢字的筆划邊緣用直線段描述成封閉的曲線,並將線段各端點的坐標經壓縮存儲,如下圖所示:

       

 

                                       

由於每個漢字的筆划不一樣,從而每個漢字數據長度也不同,所以只能采用索引的方法。因而每種矢量字庫都是由兩部分組成,一部分是漢字的索引信息,一部分是漢字的字形(glyph)數據.

當顯示文字時,便提取出各端點,並通過貝塞爾曲線來連接各個坐標,最后填充封閉空間.

接下來便使用freetype庫制作矢量字體

2. freetype-2.4.10庫

freetype庫是一個開源的字體引擎,支持多種字符集編碼(utf-8等).

freetype庫下載: https://sourceforge.net/projects/freetype/files/freetype2/2.4.10/

freetyoe英文參考文檔下載:https://sourceforge.net/projects/freetype/files/freetype-docs/2.4.10/

 FreeType 中文使用參考:

http://wenku.baidu.com/view/2d24be10cc7931b765ce155b.html

https://wenku.baidu.com/view/e7149f6748d7c1c708a14574.html

 

2.1如何來使用freetype

1)包含頭文件:

#include <ft2build.h>
#include FT_FREETYPE_H

 

2) 初始化庫:

使用FT_Init_FreeType()函數初始化一個FT_Library類型的變量,例如:

FT_LIBRARY library;                         //庫的句柄

error = FT_Init_FreeType( &library );   
if ( error )
{
//初始化失敗
}

... ...

 

3)加載face對象:

通過FT_NEW_Face()打開一個字體文件,然后提取該文件的一個FT_Face類型的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 );   

 

4)設置字體大小(參考freetype-2.4.10/docs/reference/ft2-base_interface.html):

方法1:

FT_Set_Char_Size( FT_Face     face,

                  FT_F26Dot6  char_width,  //字符寬度,單位為1/64點

                  FT_F26Dot6  char_height, //字符高度,單位為1/64點

                  FT_UInt     horz_resolution, //水平分辨率

                  FT_UInt     vert_resolution ); //垂直分辨率

字符寬度和高度以1/64點為單位表示。點是物理上的距離,一個點代表1/72英寸(2.54cm)

分辨率以dpi(dots per inch)為單位表示,表示一個英寸有多少個像素

例如:

error = FT_Set_Char_Size( face, 50 * 64, 0,100, 0 );    //0表示與另一個尺寸值相等。 

得出:

字符物理大小為: 50*64* (1/64) * (1/72)英寸

字符的像素為: 50*64* (1/64) * (1/72)*100

 

方法2:

FT_Set_Pixel_Sizes(   FT_Face  face,
                      FT_UInt  pixel_width,     //像素寬度
                      FT_UInt  pixel_height );  //像素高低

例如: 

error = FT_Set_Pixel_Sizes( face, 0,16);      //把字符像素設置為16*16像素, 0表示與另一個尺寸值相等。

 

5)設置字體位置,以及旋轉度數(不設置的話表示原點位於0,0):

error = FT_Set_Transform(

face, /* 目標face對象 */

&matrix, /* 指向2x2矩陣的指針,寫0表示不旋轉,使用正矩形 */

&delta ); /*字體坐標位置(用的笛卡爾坐標),以1/64像素為單位表示,寫0表示原點是(0,0) */

由於我們LCD的坐標原點是位於左上方

笛卡爾坐標:表示坐標原點位於左下方(與LCD的y軸相反)

所以轉換之前填寫坐標時,需要轉換一下y軸值(總高度-y)

轉換成功后還需要轉換回來(總高度-y)

 

比如,旋轉25,並在(300,200)處顯示:

FT_Vector     pen;                    /*   */
FT_Matrix     matrix;                 /* transformation matrix */


angle         = ( 25.0 / 360 ) * 3.14159 * 2;      /* use 25 degrees  */

/*將該文字坐標轉為笛卡爾坐標*/
  pen.x = 300 * 64;                                         
  pen.y = ( target_height - 200 ) * 64;        // target_height: LCD總高度


//設置 矩形參數
  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 );

FT_Set_Transform( face, &matrix, &pen );

 

 

6)加載字形圖像

a.獲取編碼的索引

通過FT_Get_Char_Inde()函數將字符編碼轉換為一個字形(glyph)索引   (Freetype默認是utf-16編碼類型)

例如:

glyph_index = FT_Get_Char_Index( face, charcode );        

若glyph_index為NULL,表示沒找到字形(glyph)索引

如果使用其它字符編碼,則通過FT_Select_Charmap()來獲取,例如獲取big5編碼:

 

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

//FT_ENCODING_BIG5枚舉定義在FT_FREETYPE_H中
//FT_ENCODING_GB2312 :GB2312編碼
//該函數頭文件位於:FT_FREETYPE_H (freetype/freetype.h).

 

b.通過索引,從face中加載字形

獲得字形索引后,接下來便根據字形索引,來將字形圖像存儲到字形槽(glyph slot)中.

字形槽:每次只能存儲一個字形圖像,每個face對象都有一個字形槽,位於face->glyph

通過FT_Load_Glyph()來加載一個字形圖像到字形槽:

error = FT_Load_Glyph(

face, /* face對象的句柄 */

glyph_index, /* 字形索引 */

load_flags ); /* 裝載標志,一般填FT_LOAD_DEFAULT*/

並更新face->glyph下的其它成員,比如:

    FT_Int            bitmap_left;            //該字形圖像的最左邊的X值
    FT_Int            bitmap_top;            //該字形圖像的最上邊的Y值

 

c.轉為位圖

通過FT_Render_Glyph()函數,將字形槽的字形圖像轉為位圖,並存到 face->glyph->bitmap->buffer[]里

error = FT_Render_Glyph( face->glyph, /* 字形槽 */

render_mode ); /* 渲染模式 */

render_mode標志可以設為以下幾種:

FT_RENDER_MODE_NORMAL:表示生成位圖每個像素是RGB888的

FT_RENDER_MODE_MONO :表示生成位圖每個像素是1位的(黑白圖)

 

並更新face->glyph->bitmap下的其它成員,比如:

int             rows;         //該位圖總高度,有多少行
int             width;        //該位圖總寬度,有多少列像素點
int             pitch:        //指一行的數據跨度(字節數),比如對於24位(3字節)的24*30漢字,則pitch=24*3

char            pixel_mode    //像素模式,1 指單色的,8 表示反走樣灰度值

unsigned char*  buffer        //glyph 的點陣位圖內存綬沖區

 

 

d.也可以直接使用FT_Load_Char()代替FT_Get_Char_Index()FT_Get_Load_Glyph()FT_Render_Glyph().

例如: 

error = FT_Load_Char( face, charcode, FT_LOAD_RENDER );

其中FT_LOAD_RENDER:表示直接將圖像轉為位圖,所以不需要使用FT_Render_Glyph()函數 

該函數默認生成的位圖是默認生成的FT_RENDER_MODE_NORMAL類型,RGB888的

若想生成FT_RENDER_MODE_MONO(黑白圖)類型,操作如下:

error = FT_Load_Char( face, charcode, FT_LOAD_RENDER | FT_LOAD_MONOCHROME );

生成出來的位圖像素,每8個像素點便表示 face->glyph->bitmap->buffer[]里的一個字節.

 

2.2參考example1.c例程

example1.c位於freetype-doc-2.4.10.tar.bz2\freetype-2.4.10\docs\tutorial下

 

3.在PC虛擬機里編譯例程:example1.c

3.1安裝freetype到/usr/local/里(拿給PC用)

tar -xjf freetype-2.4.10.tar.bz2

mv freetype-2.4.10   freetype-2.4.10_pc

cd freetype-2.4.10_pc/

./configure                                 //配置

make                                        //編譯

sudo make install                           //直接將庫安裝到根目錄/usr/local/里,所以需要加sudo

由於example1.c的打印范圍是640*480,而我們secureCRT沒有那么大,所以修改example1.c.

將:

#define WIDTH   640
#define HEIGHT  480

改為:

#define WIDTH   80
#define HEIGHT  80

 

然后將119行處的文字顯示坐標:

  pen.x = 300 * 64;
  pen.y = ( target_height - 200 ) * 64;

改為:

  pen.x = 0 * 64;                                //在坐標(0,40)處顯示
  pen.y = ( target_height - 40 ) * 64;

 

3.2 編譯運行

gcc -o example1 example1.c

編譯出錯:

 

通過ls,發現又有這個文件:

 

 

所以通過-I,直接指定頭文件目錄:

gcc -o example1 example1.c  -I /usr/local/include/freetype2/

編譯再次出錯:

 

發現這些出錯的都是函數,其中FT開頭的是freetype庫的函數,cos等都是數學庫的函數,

freetype庫的文件名是 libfreetype.so

數學庫的文件名是libm.so

 

所以編譯時,加上-l,指定庫文件:

gcc -o example1 example1.c  -I /usr/local/include/freetype2/   -lfreetype  -lm

 

3.3 運行example1

將C:\Windows\Fonts下的simsun.ttc(宋體)字體文件拷到虛擬機里,輸入./example1   simsun.ttc  agf,發現是斜的:

 

這是因為example1.c里通過FT_Set_Transform()設置了字體旋轉

3.4 繼續修改example1.c

關閉字體旋轉,將

FT_Set_Transform( face, &matrix, &pen );

改為

FT_Set_Transform( face, 0, &pen );

 

修改字體大小,將

error = FT_Set_Char_Size( face, 50 * 64, 0, 100, 0 );

改為:

error = FT_Set_Pixel_Sizes( face, 24, 0 );             //24*24像素

 

編譯運行:

 

 

3.5 顯示漢字

如果用char存儲漢字英文等,則還需要判斷數據類型,而wchar_t剛好可以放一個unicode字符。

注意:wchar_t在windows占2byte,在linux4bytes.

寬字符:wchar_t

頭文件: #include<wchar.h>

通過wcslen()判斷wchar_t數組大小

修改example1.c

...
#include<wchar.h>    //添加此行

...
int main( int argc,char**  argv )
{
  ... ...
  wchar_t  *chinese_str=L"韋東山g";    //添加此行

  ... ...
for ( n = 0; n <wcslen(chinese_str); n++ )  //修改此行
{
FT_Set_Transform( face, 0, &pen );     //字體轉換

 

    /* load glyph image into the slot (erase previous one) */
error = FT_Load_Char( face, chinese_str[n], FT_LOAD_RENDER );    //修改此行
... ...

} 
  return 0;
}            

通過另存為文件,來看看文件本身是什么編碼格式

如下圖所示,看到是ANSI編碼, 對於中文PC,ANSI編碼對應的是GBK編碼:

 

linux默認是utf-8編碼,所以編譯時,需要指定字符集:

gcc -o example1 example1.c  -I /usr/local/include/freetype2/   -lfreetype  -lm -finput-charset=GBK  -fexec-charset=utf-8
// -finput-charset:告訴編譯器,文件里的字符是GBK格式
//-fexec-charset:告訴編譯器,需要先將里面的內容轉換為utf-8格式后,再來編譯

運行代碼:

 

添加坐標打印信息:

 

 

發現,我們打印坐標是在(40,0),為什么文字坐標還會超過原點?,參考以下圖所示:

advance: 位於face->glyph-> advance,用來存放每個文字之間的間隔信息,每當加載一個新的圖像時,系統便會更新該數據.

 

3.6 獲取位圖文字的信息

當我們每次將新的字形圖像(face->glyph)轉為位圖后,而存放的前一個字形圖像就會被刪除.

當有時候,有可能需要提取字形圖像的坐標,該怎么做?

1)首先添加頭文件:

#include FT_GLYPH_H

 

2)通過FT_Get_Glyph()將一個字形圖像(face->glyph)存到FT_Glyph類型的變量里,例如:

FT_Glyph  glyph;    /* a handle to the glyph image */
...

  error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NORMAL );
//通過字符編碼,獲取字形圖像存到face->glyph里,並轉為位圖存到face->glyph->bitmap->buffer[]里

  if ( error ) { ... }

  error = FT_Get_Glyph( face->glyph, &glyph );         //將字形圖像(face->glyph)存到glyph里
  if ( error ) { ... }

 

3) 通過FT_Glyph_Get_CBox()獲取文字的xMin, xMax, yMin, yMax坐標信息

參考: /freetype-2.4.10/docs/reference/ft2-index.html

FT_Glyph_Get_CBox( FT_Glyph  glyph,                 //該值通過FT_Get_Glyph()來獲取
                     FT_UInt   bbox_mode,        //模式,填入FT_GLYPH_BBOX_TRUNCATE即可
                     FT_BBox  *acbox );        //用來存放獲取到的xMin, xMax, yMin, yMax信息

其中FT_GLYPH_BBOX_TRUNCATE表示:獲取的坐標信息是像素坐標,而不是點坐標

 

修改example1.c,使它能打印每個漢字的坐標信息:

#include FT_GLYPH_H      //添加此行
... ...

int main( int     argc, char**  argv )
{
  FT_Glyph  glyph;
  FT_BBox   acbox;
... ...

for ( n = 0; n < wcslen(chinese_str); n++ )
{ 
 ... ...

error = FT_Load_Char( face,chinese_str[n], FT_LOAD_RENDER );
  if ( error )
      continue;                 /* ignore errors */

error = FT_Get_Glyph( face->glyph, &glyph );                   //添加此行
FT_Glyph_Get_CBox(  glyph,FT_GLYPH_BBOX_TRUNCATE,&acbox );     //添加此行
printf("0x%x:xMin=%ld,xMax=%ld,yMin=%ld,yMax=%ld\n",chinese_str[n],acbox.xMin,acbox.xMax,acbox.yMin,acbox.yMax);    //添加此行

... ...

編譯運行:

 

表示韋字(97e6)的笛卡爾坐標 :  X坐標在0~23,y坐標在37~60,是個24*24字體.

由於笛卡爾坐標的原點坐標位於左下方.

所以對應韋字(97e6)的LCD坐標: X坐標在0~23 ,y坐標為20~43

 

4.在LCD上顯示矢量文字

安裝freetype到交叉編譯目錄里(供arm-linux-gcc編譯)

4.1首先查看,需要安裝到哪個lib和include目錄

1)通過$PATH找到arm-linu-gcc交叉編譯位於:

/work/tools/arm-linux-gcc-4.3.2/usr/local/arm/4.3.2/bin

然后進入.../arm/4.3.2/目錄,通過find查找stdio.h文件,找到:

 

所以編譯出來的頭文件應該放入:

/work/tools/arm-linux-gcc-4.3.2/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include

 

2)通過find查找lib,找到:

 

由於ARM9屬於ARMv4T架構,所以編譯出來的庫文件應該放入:

/work/tools/arm-linux-gcc-4.3.2/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib

 

4.2安裝

參考:freetype-2.4.10/docs/INSTALL.CROSS

tar -xjf freetype-2.4.10.tar.bz2   
mv freetype-2.4.10 freetype-2.4.10_arm
cd freetype-2.4.10_arm
mkdir   tmp                                    //創建安裝的臨時目錄,后面會拷貝到交叉編譯目錄里
./configure --host=arm-linux  --prefix=$PWD/tmp  //配置交叉編譯,安裝前綴
make
make install
 
cd tmp/

cp  ./include/*  /work/tools/arm-linux-gcc-4.3.2/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include/ -rfd 
//將include下的頭文件拷貝到交叉編譯里去 cp lib/* /work/tools/arm-linux-gcc-4.3.2/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib/ -rfd
//將lib下的庫文件拷貝到交叉編譯里去 cp lib/ * /work/nfs_root/3.4_fs_mini_mdev/lib/ -rfd
//將lib下的庫文件拷貝到nfs文件系統去

為什么不拷貝頭文件? 因為編譯好了freetype程序后,頭文件會被gcc展開存到可執行文件里,所以運行時,只會用到庫文件.

 

4.3寫代碼(參考上章代碼和example1.c)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/fb.h>
#include <math.h>
#include<wchar.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H

unsigned char *fbmem;
unsigned char *hzkmem;

struct fb_var_screeninfo fb_var;
struct fb_fix_screeninfo fb_fix;

unsigned int screensize;
#define FONTDATAMAX 4096

static const unsigned char fontdata_8x16[FONTDATAMAX] = {
//ASCII碼點陣太長,省略...
};
/*rgb565*/ void pixel_show(int x,int y, unsigned int color) { unsigned int red,green,blue; switch(fb_var.bits_per_pixel) //rgb 像素 { case 32: { unsigned int *addr=(unsigned int *)fbmem+(fb_var.xres*y+x); *addr=color; break; } case 24: { unsigned int *addr=(unsigned int *)fbmem+(fb_var.xres*y+x); *addr=color; break; } case 16: //將RGB888 轉為RGB565 { unsigned short *addr=(unsigned short *)fbmem+(fb_var.xres*y+x); red = (color >> 16) & 0xff; green = (color >> 8) & 0xff; blue = (color >> 0) & 0xff; color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); *addr = color; break; } case 8: { unsigned char *addr=(unsigned char *)fbmem+(fb_var.xres*y+x); *addr = (unsigned char)color; break; } default: { printf("can't surport %dbpp \n",fb_var.bits_per_pixel); break; } } } /*顯示ascii碼*/ void lcd_put_char(int x,int y, unsigned char s) { unsigned char *index=(unsigned char *)&fontdata_8x16[s*16]; unsigned char i,j; for(i=0;i<16;i++) //8*16 for(j=0;j<8;j++) { //從高位到低 if(index[i]&(1<<(7-j))) // pixel_show(x+j,y+i, 0xffffff); //白色 else // pixel_show(x+j,y+i, 0x0); //黑色 } } /*顯示GBK碼*/ void lcd_put_chinese(int x,int y, unsigned char *s) { unsigned char i,j,k; //將編碼轉為區碼 unsigned int index=(s[0]-0xA1)*94+(s[1]-0xA1); //轉為點陣碼(每個漢字32字節) unsigned char *dots=hzkmem+index*32; for(i=0;i<16;i++) //16*16 for(k=0;k<2;k++) for(j=0;j<8;j++) { if((dots[i*2+k]>>(7-j))&0X01) // pixel_show(x+8*k+j,y+i, 0xffffff); //白色 else // pixel_show(x+8*k+j,y+i, 0x0); //黑色 } } void lcd_put(int x,int y, unsigned char *s) { while(*s) { if(*s<0xA1) //ASCII碼8*16 { printf("ASCII %x \r\n",*s ); lcd_put_char(x,y,*s); s+=1; x+=8; } else //GB2313 16*16 { printf("GBK %x %x\r\n",*s, *(s+1)); lcd_put_chinese(x,y,s); s+=2; x+=16; } } } 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; //x:當前X位置, bitmap->width:該字寬度 FT_Int y_max = y + bitmap->rows; for ( i = x, p = 0; i < x_max; i++, p++ ) //i:lcd的x軸 { for ( j = y, q = 0; j < y_max; j++, q++ ) //j:lcd的y軸 { if ( i < 0 || j < 0 || i >= fb_var.xres || j >= fb_var.yres ) continue; pixel_show( i, j, bitmap->buffer[q * bitmap->width + p]); } } } void lcd_vector_show(char *argv,wchar_t *str) { FT_Library library; FT_Face face; FT_GlyphSlot slot; FT_Vector pen; /* untransformed origin */ unsigned char error; unsigned char n,font_size;
error
= FT_Init_FreeType( &library ); /* initialize library */ if(error) { printf("FT_Init_FreeType ERROR\n"); return ; } error = FT_New_Face( library, argv, 0, &face ); /* create face object */ if(error) { printf("FT_New_Face ERROR\n"); return ; } slot = face->glyph; /*顯示坐標(從LCD中間顯示) *x=fb_var.xres /2 *y=fb_var.yres-fb_var.yres/2-16 (減16,是因為笛卡爾坐標以左下方開始計算坐標值的) */ pen.x = fb_var.xres /2* 64; pen.y = ( fb_var.yres/2-16) * 64; for ( n = 0; n < wcslen(str); n++ ) { font_size=(n%6)*4+20; // 20*20 24*24 28*28 32*32 36*36 40*40 error = FT_Set_Pixel_Sizes( face, 0,font_size); /* set character size */ FT_Set_Transform( face, 0, &pen ); error = FT_Load_Char( face,str[n], FT_LOAD_RENDER ); if ( error ) { printf("FT_Load_Char ERROR\n"); continue; } draw_bitmap( &slot->bitmap, slot->bitmap_left, fb_var.yres- slot->bitmap_top ); pen.x += slot->advance.x; pen.y += slot->advance.y; } FT_Done_Face( face ); FT_Done_FreeType( library ); } int main(int argc,char **argv) { int fd_fb,fd_hzk; struct stat hzk_start; //HZK16文件信息 unsigned char s[]="abc 中國chinese"; wchar_t *chinese_str=L"韋東山g h "; if ( argc != 2 ) { printf ("usage: %s font_file \n", argv[0] ); return 0; } fd_hzk=open("HZK16",O_RDONLY); if(fd_hzk<0) { printf("can't open HZK16 \n"); return 0; } if(fstat(fd_hzk,&hzk_start)<0) //獲取HZK16文件信息 { printf("can't get fstart \n"); return 0; } hzkmem =(unsigned char *)mmap(NULL,hzk_start.st_size, PROT_READ,MAP_SHARED, fd_hzk, 0); //映射HZK16文件 if(!hzkmem) { printf("can't map HZK16 \n"); return 0; } fd_fb=open("/dev/fb0", O_RDWR); if(fd_fb<0) { printf("can't open /dev/fb0 \n"); return 0; } if(ioctl(fd_fb,FBIOGET_VSCREENINFO,&fb_var)<0) { printf("can't get var \n"); return 0; } if(ioctl(fd_fb,FBIOGET_FSCREENINFO,&fb_fix)<0) { printf("can't get fix \n"); return 0; } screensize=fb_var.xres*fb_var.yres*(fb_var.bits_per_pixel/8); //顯存大小 fbmem =(unsigned char *)mmap(NULL,screensize, PROT_READ|PROT_WRITE,MAP_SHARED, fd_fb, 0); //映射fb0 if(!fbmem) { printf("can't map /dev/fb0 \n"); return 0; } memset(fbmem, 0, screensize); //清屏黑色 /*顯示數據*/ lcd_put(0,fb_var.yres/2,s); /*顯示矢量文字*/ lcd_vector_show(argv[1], chinese_str); munmap(hzkmem,hzk_start.st_size); munmap(fbmem,screensize); return 0; }

4.4編譯程序

編譯報錯: 56:38: error: freetype/config/ftheader.h: No such file or directory

通過find找到ftheader.h的位置是位於:../include/freetype2/freetype/config/ftheader.h

輸入:

cd ./arm-none-linux-gnueabi/libc/usr/include/freetype2
mv freetype/ ../freetype                    //將freetype2下的freetype移到include目錄下

 

編譯:

arm-linux-gcc -o show_font show_font.c  -lfreetype  -lm -finput-charset=GBK  -fexec-charset=GBK

 

運行: 

 (發現,顯示16*16字體時,會亂碼, 新宋字體simsun不支持16點陣大小的字體)

 

下章學習:4.數碼相框-freetype多行顯示,居中顯示

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM