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>
",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
", 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 
",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

mark

这里会发现有遮挡,我们换个底色看一下

mark

解决方法可以见下一个文档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;

mark

居中显示

参考网址 https://www.freetype.org/freetype2/docs/tutorial/step2.html

中文文档 第11页:https://wenku.baidu.com/view/060a0b44f12d2af90342e63a.html?from=search

所谓居中显示,就是先计算字符长度,然后计算出原点的位置

mark

也就是先得到这一行字的所有外观数据,存起来,再去一次性去渲染显示它,流程简述如下

  1. 通过要显示的str 填充具体的 编码,外观(矢量信息),位置信息(基于(0,0)),统一用glyphs[]管理
  2. 遍历所有的外观,计算最大最小的框架信息
    这里得到的虽然是每个边框的信息,但是统计他们的最大最小也就是整体边框的最大最小点了
  3. 计算居中的位置pen
  4. 绘图
    1. 调整外观数据到具体的显示的位置
      FT_Glyph_Transform(glyphs[n].image, 0, &pen);
    2. 转换成位图
      FT_Glyph_To_Bitmap
    3. 显示位图
      draw_bitmap
  5. 内存销毁等
    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
", 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>  
",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
");
		return -1;
	}

	if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
	{
		printf("can't get var
");
		return -1;
	}

	if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
	{
		printf("can't get fix
");
		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
");
		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;	
}

代码仓库

移植到arm上的显示 到串口

LCD 显示

LCD换行显示

LCD居中显示

原文地址:https://www.cnblogs.com/zongzi10010/p/10492469.html