freetypeLCD显示
freetypeLCD显示
安装交叉编译环境
参考文档 : 代码中的docs/INSTALL.CROSS
#解压
$ mkdir freetype-2.4.10_arm
$ tar xjf freetype-2.4.10.tar.bz2 -C ./freetype-2.4.10_arm
#查看说明 docs/INSTALL.CROSS
cd docs/
vi INSTALL.CROSS
配置
./configure --host=arm-linux --prefix=$PWD/tmp //配置交叉编译,安装前缀
make
make install
--host 运行位置
--prefix 安装前缀
头文件和库的位置
查看下交叉编译工具的位置
book@100ask:docs$ echo $PATH
/home/book/bin:/home/book/.local/bin:/opt/slickedit-pro2017/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/arm/4.3.2/bin/:/opt/smartgit/bin/:/snap/bin
book@100ask:docs$
# /usr/local/arm/4.3.2/bin/
看下编译工具的头文件是怎么放的,应该是要放到
book@100ask:4.3.2$ pwd
/usr/local/arm/4.3.2
book@100ask:4.3.2$ ls
arm-none-linux-gnueabi bin lib libexec share
book@100ask:4.3.2$ find -name include
./arm-none-linux-gnueabi/include
./arm-none-linux-gnueabi/libc/usr/include
./lib/gcc/arm-none-linux-gnueabi/4.3.2/install-tools/include
./lib/gcc/arm-none-linux-gnueabi/4.3.2/include
book@100ask:4.3.2$ find -name stdio.h
./arm-none-linux-gnueabi/include/c++/4.3.2/tr1/stdio.h
./arm-none-linux-gnueabi/libc/usr/include/bits/stdio.h
./arm-none-linux-gnueabi/libc/usr/include/stdio.h
所以头文件应该是在这个位置
/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include
查看下库的位置
book@100ask:4.3.2$ find -name lib
./arm-none-linux-gnueabi/lib
./arm-none-linux-gnueabi/libc/usr/lib
./arm-none-linux-gnueabi/libc/armv4t/usr/lib
./arm-none-linux-gnueabi/libc/armv4t/lib
./arm-none-linux-gnueabi/libc/lib
./arm-none-linux-gnueabi/libc/thumb2/usr/lib
./arm-none-linux-gnueabi/libc/thumb2/lib
./lib
库文件是在这个位置
/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib
编译安装
我们可以在配置的时候指定prefix
,也可以在make install
时指定安装位置
book@100ask:freetype-2.4.10_arm$ cd freetype-2.4.10/
book@100ask:freetype-2.4.10$ mkdir tmp
book@100ask:freetype-2.4.10$ ./configure --host=arm-linux
book@100ask:freetype-2.4.10$make
book@100ask:freetype-2.4.10$make DESTDIR=$PWD/tmp install
book@100ask:tmp$ ls
usr
最终的目录结构在tmp下这样的
bin
include
-freetype2
-....h
-ft2build.h
lib
-libfreetype.a
-libfreetype.la
-libfreetype.so
-libfreetype.so.6
-libfreetype.so.6.9.0
-pkgconfig
share
复制到PC编译工具链
# -d 保持链接属性
sudo cp ./include/* /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include -rfd
sudo cp ./lib/* /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib -rfd
复制到文件系统
这里只需要复制库,不需要头文件,头文件是在编译阶段被读取的,运行阶段只需要库就可以了
cp ../../../demo_tar/freetype-2.4.10_arm/freetype-2.4.10/tmp/usr/local/lib/*.so /usr/lib/ -rfd
运行测试
./example simsun.ttc 123
LCD显示
编码转换问题
编译的时候提示
cc1: error: failure to convert GBK to UTF-8
查看下具体的帮助
**-finput-charset=***charset*
Set the input character set, used for translation from the character set of the input file to the source character set used by GCC . If the locale does not specify, or GCC cannot get this information from the locale, the default is UTF-8 . This can be overridden by either the locale or this command line option. Currently the command line option takes precedence if there's a conflict. charset can be any encoding supported by the system's "iconv"
library routine.
也就是说我们尝试使用命令来转换这个文件,
iconv -f GBK -t UTF-8 show_font.c
#没有-o选项 将转换后的utf-8格式打印到控制台
这就会提示哪里不能转换,我们依次修改
/* 128 0x80 'iconv: 未知 50380 处的非法输入序列
接着修改makefile就可以顺利编译了
arm-linux-gcc -finput-charset=GBK -fexec-charset=GBK -o show show_font.c -I /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include/freetype2 -lfreetype -lm
简单显示
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 >= 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]);
}
}
}
void ShowByFreetype(int argc,char **argv,int x ,int y )
{
FT_Library library;
FT_Face face;
FT_GlyphSlot slot;
FT_Matrix matrix; /* transformation matrix */
FT_Vector pen; /* untransformed origin */
FT_Error error;
FT_Glyph glyph;
FT_BBox bbox;
int target_height;
int n, num_chars;
wchar_t * chinese_str=L"韦中aghp";
if (argc!=2) {
printf("Usage : %s <font_file>\n",argv[0]);
return ;
}
error = FT_Init_FreeType( &library ); /* initialize library */
error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
error = FT_Set_Pixel_Sizes(face,24,0);
pen.x=x*64;
pen.y=y*64;
target_height=var.yres;
slot = face->glyph;
for ( n = 0; n < wcslen(chinese_str); n++ )
{
/* set transformation */
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 );
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;
}
FT_Done_Face ( face );
FT_Done_FreeType( library );
}
int main(int argc, char **argv)
{
....
printf("chinese code: %02x %02x\n", str[0], str[1]);
lcd_put_chinese(var.xres/2 + 8, var.yres/2, str);
// 上面 var.xres/2 + 8 var.yres/2 是中文字的原点坐标 位于左上角
/* 确定座标:
* lcd_x = var.xres/2 + 8 + 16 这里加16是因为 我们lcd之前用点阵的原点是左上角,现在笛卡尔坐标是左下角,差了16
* lcd_y = var.yres/2 + 16
* 笛卡尔座标系:
* x = lcd_x = var.xres/2 + 8 + 16
* y = var.yres - lcd_y = var.yres/2 - 16
*/
ShowByFreetype(argc,argv,var.xres/2 + 8 + 16,var.yres/2 - 16);
}
角度旋转
这里我们用第三个参数指定角度,使用strtoul
来转换角度
if (argc!=3) {
printf("Usage : %s <font_file> angle \n",argv[0]);
return ;
}
angle = (1.0*strtoul(argv[2],NULL,0) / 360 ) * 3.14159 * 2; /* use 25 degrees */
/* 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 );
FT_Set_Transform( face, &matrix, &pen );
测试如下
./show simsun.ttc 45
./show simsun.ttc 180
这里会发现有遮挡,我们换个底色看一下
解决方法可以见下一个文档05-freetype字形解析
,刷新新字的时候如果颜色是0的时候要跳过去,不刷新
换行显示
所谓换行,也就是计算y的坐标
x坐标我们可以用上一个字符的advanxe的x计算,y的坐标我们需要计算上一行字符advance.y的最大值来确定
也就是通过FT_Glyph_Get_CBox
来统计最小的yMin和最大的yMax,下一行的y就在最小的min-24即可,
// 将槽转换为glyph
FT_Get_Glyph( face->glyph, &glyph );
//从glyph 提取边界信息
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );
if (line_box_ymin > bbox.yMin)
line_box_ymin = bbox.yMin;
if (line_box_ymax < bbox.yMax)
line_box_ymax = bbox.yMax;
老师的代码是这么计算的line_box_ymax - line_box_ymin + 24
也就是减去了整个边框的大小
/* 确定座标:
* lcd_x = 0
* lcd_y = line_box_ymax - line_box_ymin + 24
* 笛卡尔座标系:
* x = lcd_x = 0
* y = var.yres - lcd_y = var.yres - (line_box_ymax - line_box_ymin + 24)
*/
pen.x = 0 * 64;
pen.y = (var.yres - (line_box_ymax - line_box_ymin + 24)) * 64;
但是实际上从图上看出来,只有ymin才是在原点的下方的,所以可以这样
pen.x = 0 * 64;
pen.y = (line_box_ymin - 24) * 64;
居中显示
参考网址 https://www.freetype.org/freetype2/docs/tutorial/step2.html
中文文档 第11页:https://wenku.baidu.com/view/060a0b44f12d2af90342e63a.html?from=search
所谓居中显示,就是先计算字符长度,然后计算出原点的位置
也就是先得到这一行字的所有外观数据,存起来,再去一次性去渲染显示它,流程简述如下
- 通过要显示的str 填充具体的 编码,外观(矢量信息),位置信息(基于(0,0)),统一用
glyphs[]
管理 - 遍历所有的外观,计算最大最小的框架信息
这里得到的虽然是每个边框的信息,但是统计他们的最大最小也就是整体边框的最大最小点了 - 计算居中的位置
pen
- 绘图
- 调整外观数据到具体的显示的位置
FT_Glyph_Transform(glyphs[n].image, 0, &pen);
- 转换成位图
FT_Glyph_To_Bitmap
- 显示位图
draw_bitmap
- 调整外观数据到具体的显示的位置
- 内存销毁等
FT_Done_Glyph
注意
这里需要调整两次位置信息
- 第一次是在保存到全局数组的外观时,产生行数据是基于pen在(0,0)的位置
FT_Glyph_Transform
- 第二次是实际转换位图前,将实际的
pen
的位置与之前的位置计算偏移
完整代码
#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 <string.h>
#include <math.h>
#include <wchar.h>
#include <ft2build.h>
#include <stdlib.h>
#include FT_FREETYPE_H
#include FT_GLYPH_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;
/* 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;
}
}
}
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 >= 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]);
}
}
}
typedef struct TGlyph_ {
FT_UInt index; /* glyph index 字符编码*/
FT_Vector pos; /* glyph origin on the baseline 以(0,0)为原点的偏移点*/
FT_Glyph image; /* glyph image 矢量外观/*/
} TGlyph, *PGlyph;
TGlyph g_glyphs[100];
int Get_Glyphs_Frm_Wstr(FT_Face face, wchar_t * wstr, TGlyph glyphs[])
{
int n;
TGlyph * glyph = glyphs;
FT_GlyphSlot slot = face->glyph;
int pen_x = 0;
int pen_y = 0;
int error;
//get encode
for (n = 0; n < wcslen(wstr); n++)
{
glyph->index = FT_Get_Char_Index( face, wstr[n]);
/* store current pen position */
glyph->pos.x = pen_x;
glyph->pos.y = pen_y;
/* load时是把glyph放入插槽face->glyph */
error = FT_Load_Glyph(face, glyph->index, FT_LOAD_DEFAULT);
FT_Get_Glyph(face->glyph, &glyph->image );
/* translate the glyph image now */
/* 这使得glyph->image里含有位置信息 */
// by layty 这里加入了pen 的位置信息
FT_Glyph_Transform(glyph->image, 0, &glyph->pos );
pen_x += slot->advance.x; /* 1/64 point */
/* increment number of glyphs */
glyph++;
}
return n;
}
void calc_all_bbox(TGlyph glyphs[], int num_glyphs, FT_BBox *abbox )
{
FT_BBox bbox;
int n;
bbox.xMin = bbox.yMin = 32000;
bbox.xMax = bbox.yMax = -32000;
for ( n = 0; n < num_glyphs; n++ )
{
FT_BBox glyph_bbox;
FT_Glyph_Get_CBox(glyphs[n].image, 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;
}
*abbox = bbox;
}
void Draw_Glyphs(TGlyph glyphs[], int num_glyphs, FT_Vector pen)
{
int n;
int error;
for (n = 0; n < num_glyphs; n++)
{
/**/
FT_Glyph_Transform(glyphs[n].image, 0, &pen);
/* convert glyph image to bitmap (destroy the glyph copy!) */
error = FT_Glyph_To_Bitmap(&glyphs[n].image, FT_RENDER_MODE_NORMAL, 0, /* no additional translation */
1 ); /* destroy copy in "image" */
if ( !error )
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)glyphs[n].image;
draw_bitmap(&bit->bitmap, bit->left, var.yres - bit->top);
FT_Done_Glyph(glyphs[n].image );
}
}
}
void ShowByFreetype(int argc,char **argv)
{
FT_Library library;
FT_Face face;
FT_Glyph glyph;
FT_GlyphSlot slot;
FT_Vector pen; /* untransformed origin */
FT_Error error;
int n;
FT_BBox bbox;
int line_box_width;
int line_box_height;
wchar_t * str_dis[]={
L"韦中aghp",
L"你1 www.baidu.com",
L"你2 www.baidu.com",
L"你3 www.baidu.com",
L"你4 www.baidu.com",
};
// str[0][...]
// str[1][...]
wchar_t * pt=( wchar_t *)&str_dis[0];
int line;
if (argc!=2) {
printf("Usage : %s <font_file> \n",argv[0]);
return ;
}
error = FT_Init_FreeType( &library ); /* initialize library */
error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
error = FT_Set_Pixel_Sizes(face,24,0);
printf("size of str=%d",sizeof(str_dis)/sizeof(wchar_t *));
for (line=0;line<sizeof(str_dis)/sizeof(wchar_t *);line++) {
n = Get_Glyphs_Frm_Wstr(face, str_dis[line], g_glyphs);
calc_all_bbox(g_glyphs, n, &bbox);
line_box_width = bbox.xMax - bbox.xMin;
line_box_height = bbox.yMax - bbox.yMin;
pen.x = (var.xres - line_box_width)/2 * 64;
pen.y = ((var.yres - line_box_height)/2-line*24) * 64;
Draw_Glyphs(g_glyphs, n, pen);
;
}
FT_Done_Face ( face );
FT_Done_FreeType( library );
}
int main(int argc, char **argv)
{
unsigned char str[] = "中";
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;
}
/* 清屏: 全部设为黑色 */
memset(fbmem, 0, screen_size);
/*
0 ---------------------->var.xres
|
|
| lcd
|
|
var.yres
^
|
|
| freetype
|
|
0--------------------->var.xres
*/
/*
we set pen to first line , is to (0,24) unit is 1/64 pixel
*/
ShowByFreetype(argc, argv);
return 0;
}