數碼相冊——使用FreeType在LCD上顯示單個字符
- 硬件平臺:韋東山嵌入式Linxu開發板(S3C2440.v3)
- 軟件平臺:運行於VMware Workstation 12 Player下UbuntuLTS16.04_x64 系統
- 參考資料:《嵌入式Linux應用開發手冊》、《嵌入式Linux應用開發手冊第2版》、【FreeType教程】、【FreeType2使用總結】
- 開發環境:Linux 3.4.2內核、arm-linux-gcc 4.3.2工具鏈
目錄
一、代碼文件
代碼在官方示例examplel.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 <wchar.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#define WIDTH 80
#define HEIGHT 80
/* 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 ( 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++ )
{
printf("%02d", 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;
wchar_t *chinese_str = L"韋gif"; //寬字符,每個字用4個字節表示
unsigned int *p = (wchar_t *)chinese_str;
int i;
FT_BBox bbox;
FT_Glyph glyph;
/* 打印"韋gif"的Uniocde值 */
printf("Uniocde: \n");
for (i = 0; i < wcslen(chinese_str); i++)
{
printf("0x%x ", p[i]);
}
printf("\n");
if ( argc != 2 )
{
fprintf ( stderr, "usage: %s font\n", argv[0] );
exit( 1 );
}
filename = argv[1]; /* first argument */
angle = ( 0.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, argv[1], 0, &face ); /* create face object */
/* error handling omitted */
FT_Set_Pixel_Sizes(face, 24, 0);
/* error handling omitted */
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 (0,40) relative to the upper left corner */
pen.x = 0 * 64;
pen.y = ( target_height - 40 ) * 64;
for ( n = 0; n < wcslen(chinese_str); n++ )
{
/* set transformation */
FT_Set_Transform( face, &matrix, &pen );
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Char( face, chinese_str[n], FT_LOAD_RENDER );
if (error)
continue; /* ignore errors */
error = FT_Get_Glyph( face->glyph, &glyph ); //從插槽中取出face->glyph,存到glyph中
if (error)
{
printf("FT_Get_Glyph error!\n");
return -1;
}
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox ); //得到xMin,xMax等字符大小參數信息
/* now, draw to our target surface (convert position) */
draw_bitmap( &slot->bitmap,
slot->bitmap_left,
target_height - slot->bitmap_top );
printf("Unicode: 0x%x\n", chinese_str[n]);
printf("origin.x/64 = %d, origin.y/64 = %d\n", pen.x/64, pen.y/64);
printf("xMin = %d, xMax = %d, yMin = %d, yMax = %d\n", bbox.xMin, bbox.xMax, bbox.yMin, bbox.yMax);
printf("slot->advance.x/64 = %d, slot->advance.y/64 = %d\n", slot->advance.x/64, slot->advance.y/64);
/* 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 */
二、在開發板根文件系統的控制檯進行驗證
1、前期準備
1.1 在虛擬機中解壓、配置、安裝
解壓命令:tar xjf freetype-2.4.10.tar.bz2
配置:./configure --host=arm-linux
,指定編譯工具鏈
安裝:make
、make DESTDIR=$PWD/tmp install
安裝到當前目錄下的tmp
目錄下
1.2 移動頭文件和庫文件到交叉編譯工具鏈對應文件夾中
安裝好的FreeType需要把它的/include和/lib
目錄下的所有文件,分別移動到/4.3.2/arm-none-linux-gnueabi/libc/usr/include
和/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib
先進入到/tmp/usr/local
目錄下
命令如下:
-
移動
到/lib
目錄文件,執行sudo cp * /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib -d -rf
-
移動
/include
目錄文件,執行sudo cp * /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include -rf
1.3 移動庫文件的.so
文件到開發板根文件系統的/lib
目錄下
先進入到/tmp/usr/local
目錄下
命令如下:
- 移動到
/lib
目錄文件,執行cp *so* /work/nfs_root/fs_mini_mdev_new/lib -d
2、編譯並運行
執行arm-linux-gcc -finput-charset=GBK -o example1 example1.c -lfreetype -lm
命令
-lfreetype
:指定頭文件-lm
:指定數學庫-finput-charset=GBK
:指定輸出文件的編碼格式爲國標碼
2.1 編譯出現問題並解決
2.1.1 無法找到頭文件
- 問題:
可事實上頭文件目錄在/freetype2/freetype/config
,而尋找的是/freetype/config
目錄下
- 解決方法:
最簡便的方法:在/include
目錄下執行sudo mv freetype2/freetype .
,把/freetype2
目錄下的freetype
文件夾移動到當前目錄下include
2.1.2 未定義標識符
- 問題:
- 解決方法:
執行arm-linux-gcc -o example1 example1.c -lfreetype
2.1.3 數學三角運算符未定義
- 問題:
- 解決方法:
執行arm-linux-gcc -o example1 example1.c -lfreetype -lm
2.1.4 指定輸入的字符集
執行arm-linux-gcc -finput-charset=GBK -o example1 example1.c -lfreetype -lm
2.2 在開發板上運行
2.2.1 把example
可執行文件上傳到開發板上
執行cp examplel /work/nfs_root/fs_mini_mdev_new
2.2.2 在開發板上執行
執行./examplel ./simsun.ttc
- 結果:在控制檯輸出了以下圖示
三、在LCD顯示單個文字
1、代碼文件
是在【1.3 數碼相冊—英文和漢字的點陣顯示】的Show_font.c
和這篇博文的examplel.c
基礎上修改,把examplel.c
的關鍵部分集成到了Show_font.c
中
#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 <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#define FONTDATAMAX 4096
static const unsigned char fontdata_8x16[FONTDATAMAX] = {
/* 方便顯示,這裏省略了字庫編碼,可以到內核的font_8x16.c文件中找*/
};
/* 文件操作 */
static int fd_fb;
static unsigned char *fbmem;
/* hzk16字庫操作 */
static int fd_hzk16;
static struct stat hzk_stat;
static unsigned char *hzkmem;
/* lcd參數 */
static int screen_size;
static unsigned int line_width;
static unsigned int pixel_width;
static struct fb_var_screeninfo var;
static struct fb_fix_screeninfo fix;
/* lcd描色 color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{
unsigned char *pen_8;
unsigned short *pen_16;
unsigned int *pen_32;
unsigned int red, green, blue;
/* 該座標在內存中對應像素的位置 */
pen_8 = fbmem+y*line_width+x*pixel_width;
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:
/* RGB:565 */
red = ((color >> 16) & 0xffff) >> 3;
green = ((color >> 8) & 0xffff) >> 2;
blue = ((color >> 0) & 0xffff) >> 3;
*pen_16 = (red << 11) | (green << 5) | blue;
break;
case 32:
*pen_32 = color;
break;
default:
printf("can not surport &dbpp\n", var.bits_per_pixel);
break;
}
}
/* lcd顯示ASCII碼 */
void lcd_put_ascii(int x, int y, unsigned char c)
{
unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
unsigned char byte;
int i, b;
/* 每行進行每位檢查,爲1則亮,否則滅 */
for (i = 0; i < 16; i++) {
byte = dots[i];
for (b = 7; b >= 0; b--) {
if (byte & (1<<b))
lcd_put_pixel(x+7-b, y+i, 0xffffff); //亮
else
lcd_put_pixel(x+7-b, y+i, 0x000000); //滅
}
}
}
/* lcd顯示中文 */
void lcd_put_chinese(int x, int y, unsigned char *str)
{
unsigned int area = str[0] - 0xA1;
unsigned int where = str[1] - 0xA1;
unsigned char *dots = hzkmem + (area * 94 + where) * 32;
int i, j, b;
unsigned char byte;
/* 由於漢字需要16*16個點陣,即一行需要2個字節的數據
* 所以在提取字節時需要:i*2跳到對應行,j跳到對應的字節
* 在描點時需要x+7-b跳到該行該字節的每個位,j*8跳到對應的字節
*/
for (i = 0; i < 16; i++) {
for (j = 0; j < 2; j++) {
byte = dots[i*2 + j];
for (b = 7; b >= 0; b--) {
if (byte & 1 << b)
lcd_put_pixel(x+7-b+j*8, y+i, 0xffffff); //亮
else
lcd_put_pixel(x+7-b+j*8, y+i, 0x000000); //滅
}
}
}
}
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 (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;
//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)
{
unsigned char str[] = "中";
wchar_t *chinese_str = L"繁";
FT_Library library;
FT_Face face;
FT_Vector pen;
FT_GlyphSlot slot;
FT_Matrix matrix;
double angle;
int error;
/* 提示信息 */
if (argc != 2) {
printf("Usage : %s <font_file> <angle>\n", argv[0]);
return -1;
}
/* 打開設備:支持讀寫 */
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0) {
printf("can not open /dev/fb0 , err code :%d\n", fd_fb);
return -1;
}
/* 獲得可變信息 */
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) {
printf("can not get var\n");
return -1;
}
/* 獲得固定信息 */
if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix)) {
printf("can not get var\n");
return -1;
}
/* 直接映射到內存的Framebuffer */
screen_size = var.xres * var.yres * var.bits_per_pixel / 8; // 屏幕總像素所佔的字節數
line_width = var.xres * var.bits_per_pixel / 8; // 每行像素所佔的字節數
pixel_width = 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 not mmap\n");
return -1;
}
/* 打開漢字庫 */
fd_hzk16 = open("HZK16", O_RDONLY);
if (fd_hzk16 < 0) {
printf("can not open /dev/fb0 , err code :%d\n", fd_hzk16);
return -1;
}
/* 計算字庫文件大小 */
if (fstat(fd_hzk16, &hzk_stat)) {
printf("can not get fstat\n");
return -1;
}
/* 漢字庫直接映射到內存 */
hzkmem = (unsigned char *)mmap(NULL, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
if (hzkmem == (unsigned char *)-1) {
printf("can not mmap for hzk16err code :%d\n", fd_hzk16);
return -1;
}
/* 清屏 */
memset(fbmem, 0, screen_size);
/* 屏幕中間顯示字母 */
lcd_put_ascii(var.xres/2, var.yres/2, 'A');
/* 屏幕中間顯示中文 */
printf("chinese code : %02x %02x\n", str[0], str[1]); //打印編碼
lcd_put_chinese(var.xres/2 + 8, var.yres/2, str);
/* 顯示矢量字 */
/* 初始化庫 */
error = FT_Init_FreeType(&library);
/* 裝載字體文件 */
error = FT_New_Face(library, argv[1], 0, &face);
slot = face->glyph;
/* 設置大小:24*24 */
FT_Set_Pixel_Sizes(face, 24, 0);
/* 確定座標:
* lcd_x = var.xres/2 + 8 + 16
* lcd_y = var.yres/2 + 16
* 笛卡爾座標系:
* x = lcd_x = var.xres/2 + 8 + 16
* y = var.yres - lcd_y = var.yres/2 - 16
*/
pen.x = (var.xres/2 + 8 + 16) * 64;
pen.y = (var.yres/2 - 16) * 64;
/* 設置旋轉角度 */
angle = (1.0 * strtoul(argv[2], NULL, 0) / 360) * 3.14159 * 2; /* use 25 degrees */
/* 設置矩陣 */
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);
/* 根據給定的文字信息,加載文字 */
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, slot->bitmap_left, var.yres - slot->bitmap_top);
return 0;
}
2、編譯與運行
- 編譯:
執行arm-linux-gcc -finput-charset=GBK -fexec-charset=GBK -o show_font Show_font.c -lfreetype -lm
命令
-finput-charset=GBK
:指定輸入文件的編碼格式爲國標碼
-fexec-charset=GBK
:指定輸出文件的編碼格式爲國標碼
-lfreetype
:指定頭文件
-lm
:指定數學庫
- 運行
把show_font
可執行文件傳到開發板的根文件系統中
執行./show_font ./simsun.ttc 0
,讓“繁”字旋轉0度顯示
執行./show_font ./simsun.ttc 90
,讓“繁”字旋轉90度顯示
執行./show_font ./simsun.ttc 180
,讓“繁”字旋轉180度顯示