freetype安裝與應用編程

        點陣顯示有侷限性,如字形固定,大小固定,爲解決這些問題,本文介紹應用freetype來顯示字體。FreeType庫是一個開源的、可移植的字體引擎,它功能強大,簡單說就是支持字體的花樣顯示。

        pc上安裝freetype庫:
1、首先自行下載freetype壓縮包,我下載的是freetype-2.4.10.tar.bz2
2、解壓:tar xjf freetype-2.4.10.tar.bz2 
3、進入freetype目錄:cd freetype
4、./configure
5、make
6、sudo make install

這就在pc上安裝好了freetype。

        如果編寫好了一個測試freetype的應用程序example.c,則編譯的時候要指定頭文件目錄/usr/local/include/freetype2,鏈接freetype庫和數學庫。同時建議加上-finput-charset=xxx編譯選項以指定輸入文件字符集的編碼方式,和-fexec-charset=xxx選項指定輸出的可執行文件的字符集編碼方式,不指定字符編碼方式的話,可能會出錯。如:

    gcc -finput-charset=GBK -fexec-charset=UTF-8 -o example example.c  -I /usr/local/include/freetype2 -lfreetype -lm

        之所以要指定頭文件目錄/usr/local/include/freetype2,是因爲默認加載頭文件的目錄和頭文件的實際存儲目錄有點出入。如對ftheader.h文件,其:
        默認加載目錄:freetype/config/ftheader.h

        實際存儲目錄: freetype2/freetype/config/ftheader.h 

 

如果要在arm開發板上使用freetype顯示字體,則按如下步驟安裝:

1、./configure的時候要設置 host 選項:

        ./configure --host=arm-linux 

2、make

3、make DESTDIR=$PWD/tmp install //指定安裝路徑./tmp

        爲方便編譯arm開發板上跑的應用程序,可以把安裝路徑下產生的庫文件複製到交叉編譯工具鏈下:
即:sudo cp tmp/usr/local/lib/* /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib -d -rf

      sudo cp tmp/usr/local/include/freetype2/* /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include -rf

到此,如果有一個開發板上的測試程序example.c,就可以這樣編譯了,不用指定頭文件目錄和鏈接庫目錄:
        arm-linux-gcc -finput-charset=GBK -fexec-charset=GBK -o example example.c -lfreetype -lm 

   把安裝目錄下生產的所有*so*文件複製到開板的根目錄的lib目錄下。我的根目錄是網絡文件系統/work/nfs_root/fs_mini_mdev_new,直接拷貝即可:
        cp tmp/usr/local/lib/*so* /work/nfs_root/fs_mini_mdev_new/lib -d

開發板的/lib/ 目錄下有了這些 *so* 文件,freetype的應用程序就可以在開發板上跑了。

 

        下面就是要寫一個example來測試一下freetype了。把freetype使用起來還是很簡單的,調用幾個freetype提供的函數就可以搞事情了。當然要有一些freetype的基礎理論知識,這我就不怎麼詳細說了,讀一下其官方文檔即可。

        顯示一個字符:
步驟:
0.初始化FT_Init_Freetype()
1.給定一個文字‘A’的ASCII碼0x41
2.提供一個字體文件
3.根據編碼值到字體文件加載對應的glyph ( glyph含有關鍵點,相對位置。實際上是根據字符的編碼類型和編碼值找到glyph,
不同的編碼類型的字符,要用不同的charmap來尋找其glyph,一個charmap支持一種編碼,即有多個charmap以支持多種編碼)
可理解爲字體文件含有charmap和字的對應glyph
4.設置字體大小
5.用某些函數把glyph裏的關鍵點縮放爲這個字體大小
6.轉換爲位圖點陣(最終還是點陣)

7.在LCD上顯示出來

下面是一個在開發板LCD上用freetype顯示acsii字符的例子:

example.c:  

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>

#include <ft2build.h>
#include FT_FREETYPE_H


int 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;


//在LCD上顯示
/* color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{
	unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;
	//找到對應的像素在內存中的位置,後面向其(*pen_8)寫顏色值。
	//通過pen_8來操作fbmem,實現LCD顯示
	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;//8位像素的話,顏色值直接賦給這個內存中即可。
			break;
		}
		case 16:
		{
			/* color : 0x00RRGGBB ,unsigned int 32位color的格式*/
			/* 565 */
			red   = (color >> 16) & 0xff;//右移16位後剩下高16位,再&0xff,又剩下低8位。即取出32位color的16-23位
			green = (color >> 8) & 0xff;//取出32color的8-15位
			blue  = (color >> 0) & 0xff;//取出32color的0-7位
			color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);//取出對應的組成565
			*pen_16 = color;
			break;
		}
		case 32:
		{
			*pen_32 = color;//32位像素也直接寫即可。
			break;
		}
		default:
		{
			printf("can't surport %dbpp\n", var.bits_per_pixel);
			break;
		}
	}
}

//freetype聲明的一個函數指針draw_bitmap,需要我們自己實現這個函數,根據bitmap把字形描繪出來
//draw_bitmap()函數:把字體在屏上顯示,具體屏具體實現:
void
draw_bitmap( FT_Bitmap*  bitmap,     //draw_bimap()是把一個字符(一個字符對應的方框所有像素所對應的顏色值,
				     //顏色值是8位,16位等,即unsigned char,char等)寫到LCD的framebuffer。
             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 ( 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>=var.xres || j>=var.yres )
        			continue;
     
      			lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);
      			//顯示順序是一列一列的顯示,把i=x這一列,不同的y的這一列先顯示。
      			//即順序:(i,j,q*bitmap->width +p);  (i,j+1, (q+1)*bitmap->width +p ) //(x,y,color)
      			//bitmap->buffer[]是顏色值,lcd_put_pixel()是操作lcd的應用層緩衝區framebuffer.
 	  		//來直接顯示顏色。
 	  	}
	}
}

int lcd_init(void)
{
	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;
	}
}

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 );
  	}

 	lcd_init();
  
  	/* 清屏: 全部設爲黑色 */
	memset(fbmem, 0, screen_size);
  	
  	filename      = argv[1];                          /* first argument     */
  	text          = argv[2];                          /* second argument    */
  	num_chars     = strlen( text );

  	angle         = ( 0.0 / 360 ) * 3.14159 * 2;      /* use 25 degrees     */
  	//旋轉的角度
  
  
 	//這個相當於像素數
  	target_height = var.yres;

  	error = FT_Init_FreeType( &library );              /* initialize library */
  
  	error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
 
#if 0
 	/* use 50pt at 100dpi */
  		error = FT_Set_Char_Size( face, 50 * 64, 0, 100, 0 ); /* set character size */

	/* pixels = 50 /72 * 100 = 69  */
#else
	FT_Set_Pixel_Sizes(face, 24, 0);
	
#endif
 
  	slot = face->glyph;	//先使slot指向face->glyph,下面也是通過glyph來設置slot

  	/* 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 (0,40) relative to the upper left corner  */
  	pen.x = 0 * 64;
  	pen.y = ( target_height - 40 ) * 64;  //(0,40是LCD座標,轉爲笛卡爾),target_height是以dip爲單位的分辨率。
  
  	//這個pen.x = 0 * 64是char_width值,是FT_Set_Char_Size()函數參數中的50*64一樣,
  	//0,50都是LCD座標,總共80*80個座標(分辨率)。
  	//char_width與座標值的關係:char_width=座標值*64,char_width/64=n point,座標值就代表第 n 
  	//個point,第n個分辨率單元。1分辨率就是1point

  	for ( n = 0; n < num_chars; n++ )	
  	{
   		 /* set transformation */
   
    		FT_Set_Transform( face, &matrix, &pen );//這裏使用到了pen

    		/* load glyph image into the slot (erase previous one) */
    		error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );//就會設置face->glyph,轉換的位圖又保存在哪裏?
    		if ( error )					//位圖點陣應該是保存在face->glyph.bitmap中。 
      			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;
  	}
  
    	FT_Done_Face    ( face );
  	FT_Done_FreeType( library );

  	return 0;
}

    編譯:arm-linux-gcc -finput-charset=GBK -fexec-charset=GBK -o example example.c -lfreetype -lm 

測試:
1、把可執行文件放在網絡文件系統:cp example /work/nfs_root/fs_mini_mdev_new/
2、運行程序時需要提供一個字體文件,我的字體文件是simsun.ttc

3、開發板上運行可執行程序:./example ./simsun.ttc https://blog.csdn.net/qq_22863733顯示漢字:

顯示效果:

 

顯示漢字:

main()中添加的代碼:

wchar_t *chinese_str = L"個人博客";//要顯示的漢字
 
 //wcslen()類似與char*類型作參數的strlen()函數,用來獲取wchar_t*變量的長度(字符個數爲單位,不是字節數(不包含終結符)
 for ( n = 0; n < wcslen(chinese_str); n++ )	
 {
   FT_Set_Transform( face, &matrix, &pen );//這裏使用到了pen
   error = FT_Load_Char( face, chinese_str[n], FT_LOAD_RENDER );//chinese_srt[n]實際上是漢字的編碼,本例是是GBK碼。
	if (error)
	{
		printf("FT_Load_Char error\n");
		return -1;
	}
	
    draw_bitmap( &slot->bitmap,
                 slot->bitmap_left,
                 var.yres - slot->bitmap_top);
    pen.x += slot->advance.x;
    pen.y += slot->advance.y;
}
 pen.x = 0;
 pen.y = pen.y + 24*64;	//在新的一行顯示ascii字符,pen.y 、pen.x 爲笛卡爾座標

編譯、測試,效果:

 

        freetype顯示字體的重點是獲得字符的編碼,然後FT_Load_Char( face, chinese_str[n], FT_LOAD_RENDER );有了加載了glyph的face後,就可以吧字形draw_bitmap出來了。利用freetype實現電子書就是這樣的原理,電子書文件保存了字符的編碼值,編碼可能是ANSI,utf-8等,可以把編碼先轉換爲UIcode碼再FT_Load_Char,或在FT_Load_Char時指定字符編碼方式。這需要一些字符編碼方面的知識,有機會再說吧,下面是我用freetype做的簡單的電子書的效果給大家分享一下。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章