烟台开发区建设业联合会网站,宁波网站制作费用,个人作品展示网站,短网址生成器手机版freetype学习总结 目录 freetype学习总结1. LCD显示字符问题引入2. freetype概念2.1 嵌入式设备使用FreeType的方法步骤2.2 嵌入式设备使用FreeType的注意事项 3. freetype官方C示例3.1 example1.c源码 4. 嵌入式设备上使用FreeType的简单示例4.1 简单示例代码4.2 代码分析 5. …freetype学习总结 目录 freetype学习总结1. LCD显示字符问题引入2. freetype概念2.1 嵌入式设备使用FreeType的方法步骤2.2 嵌入式设备使用FreeType的注意事项 3. freetype官方C示例3.1 example1.c源码 4. 嵌入式设备上使用FreeType的简单示例4.1 简单示例代码4.2 代码分析 5. 交叉编译freetype5.1 确定头文件、库文件在工具链中的目录5.2 交叉编译、安装libpng5.3 交叉编译、安装freetype 6. 使用freetype在LCD显示矢量字体6.1 在LCD上显示一个矢量字体6.1.1 使用wchar_t获得字符的UNICODE值6.1.2 使用freetype得到位图6.1.3 在屏幕上显示位图6.1.4 交叉编译代码6.1.5 测试 6.2 在LCD上令矢量字体旋转某个角度6.2.1 代码分析6.2.2 交叉编译代码6.2.3 测试 6.3 使用freetype显示一行文字6.3.1 使用freetype显示一行文字的方法6.3.1.1笛卡尔坐标系6.3.1.2 每个字符的大小可能不同6.3.1.3 在指定位置显示一行文字6.3.1.4 freetype的几个重要数据结构 6.3.2 使用freetype显示一行文字源码6.3.3 代码分析6.3.3.1 计算一行文字的外框6.3.3.2 调整原点并绘制 6.3.4 交叉编译代码6.3.5 测试 基于韦东山IMX6ULL开发板和配套资料中LCD屏幕学习 freetype库资料包开源的字体引擎库 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所以我们应该先编译安装 zlib再编译安装 libpng最后编译安装 freetype。 但是有些工具链里有 zlib, 那就不用编译安装 zlib。 freetype官方文档FreeType Documentation 1. LCD显示字符问题引入
在使用字库点阵的方式在LCD上显示英文字母、汉字时大小固定如果放大缩小则会模糊甚至有锯齿出现为了解决这个问题引用矢量字体。
矢量字体形成分三步
确定关键点使用数学曲线贝塞尔曲线连接头键点填充闭合区线内部空间
以字母“A”为例它的的关键点如图中的黄色所示 再用数学曲线(比如贝塞尔曲线)将关键点都连接起来得到一系列的封闭的曲线如图所示 最后把封闭空间填满颜色就显示出一个A字母如图所示 如果需要放大或者缩小字体关键点的相对位置是不变的只要数学曲线平滑字体就不会变形。
在嵌入式设备上移植开源的字体渲染引擎freetype调用对应的API接口提供字体文件就可以让freetype库帮我们取出关键点、实现闭合曲线填充颜色达到显示矢量字体的目的。
2. freetype概念
FreeType是一个开源的字体渲染引擎它能够加载和渲染多种格式的字体文件如TrueType(.ttf)、OpenType(.otf)等。FreeType提供了一组API来处理字体数据包括字符的加载、渲染以及获取字符的各种信息例如字形边界框、位图等。它被广泛应用于各种需要显示文本的应用程序中尤其是在那些需要高质量文本渲染的地方。
2.1 嵌入式设备使用FreeType的方法步骤
安装 FreeType 库 下载FreeType源码。配置并编译FreeType以适应你的目标平台。将编译好的库文件和头文件部署到你的开发环境中。 编写代码 初始化FreeType库。加载字体文件。设置字体大小。渲染指定的字符或字符串。处理渲染后的位图数据。清理资源。 集成到图形系统 根据你的嵌入式系统的图形库如SDL,OpenGL ES,或者自定义的绘图函数将渲染后的位图绘制到屏幕上。
2.2 嵌入式设备使用FreeType的注意事项
内存限制嵌入式设备可能有严格的内存限制因此要确保 FreeType 的配置和使用不会占用过多内存。性能优化根据嵌入式设备的性能特性对 FreeType 的使用进行优化比如选择合适的字体大小和渲染选项。字体文件大小考虑存储空间限制选择合适大小的字体文件。跨平台兼容性确保 FreeType 的编译设置与你的目标硬件平台兼容。
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. 嵌入式设备上使用FreeType的简单示例
4.1 简单示例代码
在嵌入式设备上使用FreeType来加载字体并渲染字符 “A”然后假设你有一个自定义的绘图函数draw_pixel来在屏幕上绘制点。
代码
#include ft2build.h
#include FT_FREETYPE_H
#include stdio.h
#include stdlib.h// 假设这是你的绘图函数
void draw_pixel(int x, int y, unsigned char color) {// 实现具体的绘图逻辑// 这里只是一个示例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库
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);
}设置字体大小
FT_Set_Pixel_Sizes(face, 0, 24); // 设置为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;绘制位图
// 假设 draw_pixel(x, y, color) 是绘图函数
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; // 假设起始位置是 (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所以我们应该先编译安装 zlib再编译安装 libpng最后编译安装 freetype。 但是有些工具链里有 zlib, 那就不用编译安装 zlib。文章开头资料包中包含需要的库。
基于IMX6ULL开发板验证使用对于的工具链对freetype交叉编译。
5.1 确定头文件、库文件在工具链中的目录
先设置交叉编译工具链
export ARCHarm
export CROSS_COMPILEarm-buildroot-linux-gnueabihf-
export PATH$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/binIMX6ULL开发板为例它的工具链是arm-buildroot-linuxgnueabihf-gcc可以执行以下命令
echo main(){} | arm-buildroot-linux-gnueabihf-gcc -E -v -可以确定头文件的系统目录为
/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include库文件的系统目录为
/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/lib5.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库的压缩文件上传到Ubuntu freetype依赖于libpng所以需要先编译、安装libpng。命令如下
tar xJf libpng-1.6.37.tar.xz
cd libpng-1.6.37/
./configure --hostarm-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/lib5.3 交叉编译、安装freetype
命令如下
tar xJf freetype-2.10.2.tar.xz
cd freetype-2.10.2
./configure --hostarm-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/lib6. 使用freetype在LCD显示矢量字体
freetype使用统一的接口来访问多种字体格式文件从而实现矢量字体显示。关键点(glyph)存在字体文件中Windows使用的字体文件在c:\Windows\Fonts目录下扩展名为TTF的都是矢量字库本次使用实验使用的是新宋字体simsun.ttc可以在资料包中下载。
.ttf 和 .ttc 文件都是字体文件格式但它们之间存在一些关键的区别
.ttf (TrueType Font) TrueType 字体是一种常见的计算机字体类型由苹果公司和微软共同开发。这种格式的字体文件通常包含一个单独的字体样式。例如你可能会有一个文件用于常规样式另一个文件用于粗体样式等。.ttf 文件可以跨平台使用在 Windows、macOS 以及许多其他操作系统上都可以被支持。 .ttc (TrueType Collection) TrueType Collection 是一种特殊的字体文件格式它允许将多个 TrueType 字体打包到一个单一的文件中。在 TTC 文件内你可以找到多个字体变体如常规、斜体、粗体等这有助于减少文件大小并提高加载效率因为相关联的字体数据会被共享。使用 TTC 可以节省磁盘空间并且在某些情况下可以加快字体加载速度因为它减少了需要读取的文件数量。
.ttf 文件是单个字体样式的标准容器而 .ttc 文件则是一个更高效的格式能够在一个文件中封装多款相关的字体样式。对于用户而言安装 .ttc 文件就像安装普通的 .ttf文件一样简单但是背后的数据结构更为紧凑和优化。
freetype对.ttf和.ttc字体格式都支持 以simsun.ttc为例该字体文件的格如下头部含有charmaps可以使用某种编码值去charmaps中找到它对应的关键点。下图中的“A、B、中、国、韦”等只是glyph的示意图表示关键点。 Charmaps表示字符映射表字体文件可能支持哪一些编码GB2312、UNICODE、BIG5或其他。如果字体文件支持该编码使用编码值通过charmap就可以找到对应的glyph一般而言都支持UNICODE码。
一个文字的显示过程可以概括如下参考4.2 代码分析
给定一个字符可以确定它的编码值(ASCII、UNICODE、GB2312)设置字体大小根据编码值从文件头部中通过charmap找到对应的关键点(glyph)它会根据字体大小调整关键点把关键点转换为位图点阵在LCD上显示出来
6.1 在LCD上显示一个矢量字体
在LCD上显示一个矢量字体的源码
#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;/*********************************************************************** 函数名称 lcd_put_pixel* 功能描述 在LCD指定位置上输出指定颜色描点* 输入参数 x坐标y坐标颜色* 输出参数 无* 返 回 值 会* 修改日期 版本号 修改人 修改内容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 创建***********************************************************************/
void lcd_put_pixel(int x, int y, unsigned int color)
{unsigned char *pen_8 fbmemy*line_widthx*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(cant surport %dbpp\n, var.bits_per_pixel);break;}}
}/*********************************************************************** 函数名称 draw_bitmap* 功能描述 根据bitmap位图在LCD指定位置显示汉字* 输入参数 x坐标y坐标位图指针* 输出参数 无* 返 回 值 无* 修改日期 版本号 修改人 修改内容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 创建***********************************************************************/
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(cant open /dev/fb0\n);return -1;}if (ioctl(fd_fb, FBIOGET_VSCREENINFO, var)){printf(cant 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(cant mmap\n);return -1;}/* 清屏: 全部设为黑色 */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);/* 确定座标:*///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值
要显示一个字符首先要确定它的编码值。常用的是UNICODE编码在程序里使用这样的语句定义字符串时str中保存的要么是GB2312编码值要么是UTF-8格式的编码值即使编译时使用“-fexec-charsetUTF-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, strs 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编译、测试命令如下 每个wchar_t占据4字节可执行程序里wchar_t中保存的就是字符的UNICODE值。 注意注意如果test_wchar.c是以ANSI(GB2312)格式保存那么需要使用以下命令来编译 gcc -finput-charsetGB2312 -fexec-charsetUTF-8 -o test_wchar test_wchar.c6.1.2 使用freetype得到位图
使用 freetype 得到一个字符的位图需要 4 个步骤 初始化freetype库 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 里。 设置字体大小 165 FT_Set_Pixel_Sizes(face, font_size, 0);根据编码值得到位图 使用 FT_Load_Char 函数就可以实现这 3 个功能 根据编码值获得 glyph_index FT_Get_Char_Index根据 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 );执行 FT_Load_Char 之后字符的位图被存在 slot-bitmap 里即 face-glyph-bitmap。
6.1.3 在屏幕上显示位图
位图里的数据格式是怎样的参考 3.1 example1.c 的代码可以得到下图 在屏幕上显示出这些位图
183 draw_bitmap( slot-bitmap,
184 var.xres/2,
185 var.yres/2);draw_bitmap 函数代码如下由于位图中每一个像素用一个字节来表示在0x00RRGGBB 的颜色格式中它只能表示蓝色所以在 LCD 上显示出来的文字是蓝色的 6.1.4 交叉编译代码
IMX6ULL使用如下命令编译
arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font freetype_show_font.c -lfreetype如果提示以下错误
freetype_show_font.c:12:10: fatal error: ft2build.h: No such file or directory#include ft2build.h^~~~~~~~~~~~
compilation terminated.之前编译出 freetype 后得到的 ft2build.h 是位于 freetype2 目录里把整个 freetype2 目录复制进了工具链里。
但是包括头文件时用的是“ #include ft2build.h”要么改成
#include freetype2/ft2build.h要么把工具链里 incldue/freetype2/*.h 复制到上一级目录使用这种方法跟 freetype 文档保持一致。执行以下命令
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/* ./再次执行以下命令
arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font freetype_show_font.c -lfreetype6.1.5 测试
将编译好的 freetype_show_font 文件与 simsun.ttc 字体文件拷贝至开发板这 2 个文件放在同一个目录下然后执行以下命令
./freetype_show_font ./simsun.ttc或者
./freetype_show_font ./simsun.ttc 3006.2 在LCD上令矢量字体旋转某个角度
在LCD上令矢量字体旋转某个角度源码
#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;/*********************************************************************** 函数名称 lcd_put_pixel* 功能描述 在LCD指定位置上输出指定颜色描点* 输入参数 x坐标y坐标颜色* 输出参数 无* 返 回 值 会* 修改日期 版本号 修改人 修改内容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 创建***********************************************************************/
void lcd_put_pixel(int x, int y, unsigned int color)
{unsigned char *pen_8 fbmemy*line_widthx*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(cant surport %dbpp\n, var.bits_per_pixel);break;}}
}/*********************************************************************** 函数名称 draw_bitmap* 功能描述 根据bitmap位图在LCD指定位置显示汉字* 输入参数 x坐标y坐标位图指针* 输出参数 无* 返 回 值 无* 修改日期 版本号 修改人 修改内容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 创建***********************************************************************/
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(cant open /dev/fb0\n);return -1;}if (ioctl(fd_fb, FBIOGET_VSCREENINFO, var)){printf(cant 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(cant mmap\n);return -1;}/* 清屏: 全部设为黑色 */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);/* 确定座标:*/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上令矢量字体旋转某个角度源码“和“在LCD上显示一个矢量字体的源码”对比 6.2.1 代码分析 6.2.2 交叉编译代码
IMX6ULL使用如下命令编译
arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font_angle freetype_show_font_angle.c -lfreetype -lm6.2.3 测试
将编译好的 freetype_show_font_angle文件与 simsun.ttc 字体文件拷贝至开发板这 2 个文件放在同一个目录下然后执行以下命令
./freetype_show_font_angle ./simsun.ttc 90 2006.3 使用freetype显示一行文字
6.3.1 使用freetype显示一行文字的方法
在 LCD 上指定一个左上角坐标(x, y)把一行文字显示出来。下图中文字的外框用虚线表示外框的左上角坐标就是(x, y)。 6.3.1.1笛卡尔坐标系
在 LCD 的坐标系中原点在屏幕的左上角。对于笛卡尔坐标系原点在左下角。 freetype 使用笛卡尔坐标系在显示时需要转换为 LCD 坐标系。
从下图可知 X 方向坐标值是一样的。
在 Y 方向坐标值需要换算假设 LCD 的高度是 V。
在 LCD 坐标系中坐标是(x, y)那么它在笛卡尔坐标系中的坐标值为(x, V-y)。
反过来也是一样的在笛卡尔坐标系中坐标是(x, y)那么它在 LCD 坐标系中坐标值为(x, V-y)。 6.3.1.2 每个字符的大小可能不同
在使用 FT_Set_Pixel_Sizes 函数设置字体大小时这只是“期望值”。比如“百问网 www.100ask.net”如果把“ .”显示得跟其他汉字一样大不好看。
所以在显示一行文字时后面文字的位置会受到前面文字的影响。
freetype 字体的尺寸(freetype Metrics)参考 4.2 代码分析中官方freetype教程中“Managing Glyphs”教程 在显示一行文字时这些文字会基于同一个基线来绘制位图baseline。
在 baseline 上每一个字符都有它的原点(origin)比如上图中 baseline左边的黑色圆点就是字母“ g”的原点。当前 origin 加上 advance 就可以得到下一个字符的 origin比如上图中 baseline 右边的黑色圆点。在显示一行中多个文件字时后一个文字的原点依赖于前一个文字的原点及 advance。
字符的位图是有可能越过 baseline 的比如上图中字母“ g”在 baseline下方还有图像。
上图中红色方框内就是字母“g”所点据的位图它的四个角落不一定与原点重合。
上图中那些xMin、xMax、yMin、yMax如何获得可以使用FT_Glyph_Get_CBox函数获得一个字体的这些参数将会保存在一个FT_BBox结构体中以后想计算一行文字的外框时要用到这些信息 6.3.1.3 在指定位置显示一行文字
要显示一行文字时每一个字符都有自己外框 xMin、 xMax、 yMin、 yMax。把这些字符的 xMin、 yMin 中的最小值取出来把这些字符的 xMax、 yMax 中的最大值取出来就可以确定这行文字的外框了。
如下图在指定位置(x, y)显示一行文字 先指定第 1 个字符的原点 pen 坐标为(0, 0)计算出它的外框再计算右边字符的原点也计算出它的外框把所有字符都处理完后就可以得到一行文字的整体外框假设外框左上角坐标为(x’, y’)想在(x, y)处显示这行文字调整一下 pen 坐标即可。 pen 为(0, 0)时对应左上角(x’, y’)那么左上角为(x, y)时就可以算出pen 为(x-x’, y-y’)
6.3.1.4 freetype的几个重要数据结构 参考4.2代码分析中官方freetype文档 FT_Library:
对应 freetype 库使用 freetype 之前要先调用以下代码
FT_Library library; /* 对应 freetype 库 */
error FT_Init_FreeType( library ); /* 初始化 freetype 库 */FT_Face
它对应一个矢量字体文件在源码中使用 FT_New_Face 函数打开字体文件后就可以得到一个 face。
为什么称之为 face
估计是文字都是写在二维平面上的吧正对着人脸不用管原因了总之认为它对应一个字体文件就可以。
代码如下
error FT_New_Face(library, font_file, 0, face ); /* 加载字体文件 */FT_GlyphSlot
插槽用来保存字符的处理结果比如转换后的 glyph、位图如下图 一个 face 中有很多字符生成一个字符的点阵位图时位图保存在哪里保存在插槽中 face-glyph。
生成第 1 个字符位图时它保存在 face-glyph 中生成第 2 个字符位图时也会保存在 face-glyph 中会覆盖第 1 个字符的位图。
代码如下
FT_GlyphSlot slot face-glyph; /* 插槽: 字体的处理结果保存在这里 */FT_Glyph
字体文件中保存有字符的原始关键点信息使用 freetype 的函数可以放大、缩小、旋转这些新的关键点保存在插槽中(注意位图也是保存在插槽中)。
新的关键点使用 FT_Glyph 来表示可以使用这样的代码从 slot 中获得glyph
error FT_Get_Glyph(slot , glyph);FT_BBox
FT_BBox 结构体定义如下它表示一个字符的外框即新 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 fbmemy*line_widthx*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(cant surport %dbpp\n, var.bits_per_pixel);break;}}
}/*********************************************************************** 函数名称 draw_bitmap* 功能描述 根据bitmap位图在LCD指定位置显示汉字* 输入参数 x坐标y坐标位图指针* 输出参数 无* 返 回 值 无* 修改日期 版本号 修改人 修改内容* -----------------------------------------------* 2020/05/12 V1.0 zh(angenao) 创建***********************************************************************/
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;/* 指定原点为(0, 0) */pen.x 0;pen.y 0;/* 计算每个字符的bounding box *//* 先translate, 再load char, 就可以得到它的外框了 */for (i 0; i wcslen(wstr); i){/* 转换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;/* 计算下一个字符的原点: 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坐标转换为笛卡尔坐标 */int x lcd_x;int y var.yres - lcd_y;/* 计算外框 */compute_string_bbox(face, wstr, bbox);/* 反推原点 */pen.x (x - bbox.xMin) * 64; /* 单位: 1/64像素 */pen.y (y - bbox.yMax) * 64; /* 单位: 1/64像素 *//* 处理每个字符 */for (i 0; i wcslen(wstr); i){/* 转换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坐标 */draw_bitmap( slot-bitmap,slot-bitmap_left,var.yres - slot-bitmap_top);/* 计算下一个字符的原点: 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百问网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(cant open /dev/fb0\n);return -1;}if (ioctl(fd_fb, FBIOGET_VSCREENINFO, var)){printf(cant get var\n);return -1;}if (ioctl(fd_fb, FBIOGET_FSCREENINFO, fix)){printf(cant 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(cant mmap\n);return -1;}/* 清屏: 全部设为黑色 */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 计算一行文字的外框
一行文字中后一个字符的原点前一个字符的原点advance。所以要计算一行文字的外框需要按照排列顺序处理其中的每一个字符。代码如下
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 /* 指定原点为(0, 0) */
117 pen.x 0;
118 pen.y 0;
119
120 /* 计算每个字符的 bounding box */
121 /* 先 translate, 再 load char, 就可以得到它的外框了 */
122 for (i 0; i wcslen(wstr); i)
123 {
124 /* 转换 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 /* 计算下一个字符的原点: 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 调整原点并绘制
代码如下
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 坐标转换为笛卡尔坐标 */
179 int x lcd_x;
180 int y var.yres - lcd_y;
181
182 /* 计算外框 */
183 compute_string_bbox(face, wstr, bbox);
184
185 /* 反推原点 */
186 pen.x (x - bbox.xMin) * 64; /* 单位: 1/64 像素 */
187 pen.y (y - bbox.yMax) * 64; /* 单位: 1/64 像素 */
188
189 /* 处理每个字符 */
190 for (i 0; i wcslen(wstr); i)
191 {
192 /* 转换 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 坐标 */
204 draw_bitmap( slot-bitmap,
205 slot-bitmap_left,
206 var.yres - slot-bitmap_top);
207
208 /* 计算下一个字符的原点: 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 -lfreetype6.3.5 测试
将编译好的 show_line 文件与 simsun.ttc 字体文件拷贝至开发板这 2个文件放在同一个目录下然后执行以下命令(其中的 3 个数字分别表示 LCD 的X 坐标、 Y 坐标、字体大小)
./freetype_show_font_angle ./simsun.ttc 90 200基于freetyp的基础使用可以进行更复杂的LCD操作。