freetypeLCD显示
title: freetypeLCD显示
date: 2019/03/03 13:34:02
toc: true
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;
}
代码仓库
freetypeLCD显示的更多相关文章
- win10 环境 gitbash 显示中文乱码问题处理
gitbash 是 windows 环境下非常好用的命令行终端,可以模拟一下linux下的命令如ls / mkdir 等等,如果使用过程中遇到中文显示不完整或乱码的情况,多半是因为编码问题导致的,修改 ...
- 关于textview显示特殊符号居中的问题
话说这是2017年的第一篇博客,也是一篇技术博客.先从简单的一篇解决问题开始吧,千里之行,始于足下! ------------------------------------------------- ...
- IE的F12开发人员工具不显示问题
按下F12之后,开发人员工具在桌面上看不到,但是任务栏里有显示.将鼠标放在任务栏的开发人员工具上,出现一片透明的区域,选中之后却出不来.将鼠标移动到开发人员工具的缩略图上,右键-最大化,工具就全屏出现 ...
- input[tyle="file"]样式修改及上传文件名显示
默认的上传样式我们总觉得不太好看,根据需求总想改成和上下结构统一的风格…… 实现方法和思路: 1.在input元素外加a超链接标签 2.给a标签设置按钮样式 3.设置input[type='file' ...
- css实现单行,多行文本溢出显示省略号……
1.单行文本溢出显示省略号我们可以直接用text-overflow: ellipsis 实现方法: <style> .div_text{width: 300px; padding:10px ...
- MVC Core 网站开发(Ninesky) 2.1、栏目的前台显示(补充)
在2.1.栏目的前台显示中因右键没有添加视图把微软给鄙视了一下,后来有仔细研究了一下发现应该鄙视自己,其实这个功能是有的,是自己没搞清楚乱吐糟. 其实只要在NuGet中安装两个包(Microsoft. ...
- MVC Core 网站开发(Ninesky) 2.1、栏目的前台显示
上次创建了栏目模型,这次主要做栏目的前台显示.涉及到数据存储层.业务逻辑层和Web层.用到了迁移,更新数据库和注入的一些内容. 一.添加数据存储层 1.添加Ninesky.DataLibrary(与上 ...
- 让kindeditor显示高亮代码
kindeditor4.x代码高亮功能默认使用的是prettify插件,prettify是Google提供的一款源代码语法高亮着色器,它提供一种简单的形式来着色HTML页面上的程序代码,实现方式如下: ...
- ASP.NET Core应用针对静态文件请求的处理[5]: DefaultFilesMiddleware中间件如何显示默认页面
DefaultFilesMiddleware中间件的目的在于将目标目录下的默认文件作为响应内容.我们知道,如果直接请求的就是这个默认文件,那么前面介绍的StaticFileMiddleware中间件会 ...
随机推荐
- git错误--ssh: Could not resolve hostname ssh.github.com: Name or service not known--解决方式
错误如下: git push origin ssh: Could not resolve hostname ssh.github.com: Name or service not known fata ...
- DVWA-XSS学习笔记
DVWA-XSS XSS概念:由于web应用程序对用户的输入过滤不严,通过html注入篡改网页,插入恶意脚本,从而在用户浏览网页时,控制用户浏览器的一种攻击. XSS类型: 反射型XSS:只是简单地把 ...
- WIn10系统软件默认安装c盘后消失看不见问题
一.win10系统下c盘,program 文件下 软件一般为32 或者 64位,但是现在win10系统有些C盘会显示program x86 向这种情况的话我们的软件默认安装在这个盘的话可能会造成很多 ...
- 金蝶K3外购入库单单价取数规则调整
涉及界面: 问题:财务抱怨外购入库单价格取错,单价多除了一次税率 例如,采购单里面注明了价格是不含税15.3256 结果在外购入库单里面,又自做主张除以税率17%,把采购成本搞成了13.0988, 咨 ...
- java 根据ip获取地区信息(淘宝和新浪)
package com.test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStr ...
- 突击战 (uva 11729)贪心
思路:就是把J大的放在前面.为什么这样贪心呢? 看看这个图 #include<iostream> #include<algorithm> #include<vector& ...
- django开发新手教程(原创)
为了帮助新手简单高效解决django开发的问题,从而写了这么一篇,随便转载! 本人用的是windows10操作系统 #联网安装 ==指定版本号 我在自定义的www文件夹安装C:\Users\ ...
- JS 设计模式四 -- 模块模式
概念 模块模式的思路 就是 就是单例模式添加私有属性和私有方法,减少全局变量的使用. 简单的代码结构: var singleMode = (function(){ // 创建私有变量 var priv ...
- vue keepalive 动态设置缓存
场景:A首页.B列表页.C详情页B---->C 缓存‘列表1’详情的数据A---->C 读取‘列表1’详情的数据B---->C (希望清除‘列表1’的缓存,变成缓存‘列表2’详情的数 ...
- 使用python抓取数据之菜鸟爬虫1
''' Created on 2018-5-27 @author: yaoshuangqi ''' #本代码获取百度乐彩网站上的信息,只获取最近100期的双色球 import urllib.reque ...