FreeType移植到 STM32 单片机以支持矢量字体

一、准备工作

下载Freetype源码 ----- 下载FreeType
以移植到Keil 的STM32工程为例

移植前的软件环境:
1,实现了内存分配函数
2,实现了文件系统
3,使用了TR-Theard操作系统
4,实现了GUI界面,并支持点阵字体的显示
5,实现了Unicode,GBK,UTF8字符编码的相互转换

二、复制文件

解压源码压缩包得到如下文件
解压的文件
复制以下文件到单片机工程下
要复制的文件
如图
复制到工程中

三、添加C文件到Keil中

设置包含路径
包含路径
添加以下文件到工程中
添加的C文件
修改include\freetype\config\ftoption.h
注释掉

//#define FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES

在include\ft2build.h文件中添加如下代码

#ifndef FT2BUILD_H_
#define FT2BUILD_H_

#ifndef FT2_BUILD_LIBRARY
#define FT2_BUILD_LIBRARY
#endif


#include <freetype/config/ftheader.h>

#endif /* FT2BUILD_H_ */

现在应该能编译通过了,如果不能通过则根据提示修改

四、修改接口

在单片机环境下一般都是自己实现内存分配函数和文件操作函数,所以需要修改内存和文件操作的函数接口
在工程中新建以下文件:
新建的文件
其中ftdebug.c提供调试输出路径,如不需要调试则可以编写以下代码

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_INTERNAL_DEBUG_H










  FT_BASE_DEF( void )
  ft_debug_init( void )
  {
    /* nothing */
  }


  FT_BASE_DEF( FT_Int )
  FT_Trace_Get_Count( void )
  {
    return 0;
  }


  FT_BASE_DEF( const char * )
  FT_Trace_Get_Name( FT_Int  idx )
  {
    FT_UNUSED( idx );

    return NULL;
  }


  FT_BASE_DEF( void )
  FT_Trace_Disable( void )
  {
    /* nothing */
  }


  /* documentation is in ftdebug.h */

  FT_BASE_DEF( void )
  FT_Trace_Enable( void )
  {
    /* nothing */
  }

ftfile.c和ftfile.h文件根据具体的文件系统修改,在本例中使用了fats文件系统管理SD卡中的文件和使用自定义的文件系统管理FLASH,在这里封装统一的操作接口

在ftfile.h中添加如下代码:

#ifndef FT_FILE_H__
#define FT_FILE_H__





typedef struct
{
	u32 addr;
	u32 ptr;
	u32 size;
}ft_flash_file;



typedef struct
{
	FILINFO file_info;
	FIL file;
	ft_flash_file flash;
	
}ft_file_struct;


//打开文件
ft_file_struct *ft_if_fopen (const char *filename,const char *mode);

//关闭文件
int ft_if_fclose (ft_file_struct *file);

//读取文件
//缓冲指针,缓冲元素大小,元素个数
unsigned int ft_if_fread (void *ptr,unsigned int size,unsigned int nmemb, ft_file_struct *file);

//文件偏移
int ft_if_fseek (ft_file_struct *file,long int offset,int whence);

//返回相对与文件开头的偏移
long int ft_if_ftell (ft_file_struct *file);











#endif



在ftfile.c中添加如下代码:

#include "stm32f4xx.h"
#include "ff.h"
#include "flash_manager.h"
#include "ftfile.h"
#include "mymem.h"
#include <stdio.h>


#include "freetype/config/ftstdlib.h"


//int fclose(FILE * /*stream*/)

//FILE *fopen(const char * __restrict /*filename*/,
//                           const char * __restrict /*mode*/)


//size_t fread(void * __restrict /*ptr*/,
//                    size_t /*size*/, size_t /*nmemb*/, FILE * __restrict /*stream*/)

//int fseek(FILE * /*stream*/, long int /*offset*/, int /*whence*/)


//long int ftell(FILE * /*stream*/)


//打开文件
ft_file_struct *ft_if_fopen (const char *filename,const char *mode)
{
	ft_file_struct *file=mycalloc (sizeof (ft_file_struct));
	u8 ret=0;
	
	if (filename[0]=='0'&&filename[1]==':')
	{
		ret=f_stat (filename,&file->file_info);
		if (ret==FR_OK)
		{
			f_open(&file->file,filename,FA_READ);
		}
		else
		{
			myfree(file);
			file=0;
		}
	}
	else
	{
		if (file->flash.addr= FLASH_FindFile((char *)filename,&file->flash.size),file->flash.addr==0)
		{
			myfree(file);
			file=0;
		}
	}
	return file;
}


//关闭文件
int ft_if_fclose (ft_file_struct *file)
{
	if (file)
	{
		if (file->flash.size==0)
			f_close(&file->file);
		return 0;
	}
	return -1;
}


//读取文件
//缓冲指针,缓冲元素大小,元素个数
unsigned int ft_if_fread (void *ptr,unsigned int size,unsigned int nmemb, ft_file_struct *file)
{
	if (file)
	{
		if (file->flash.size==0)
		{
			UINT rb=0;
			f_read (&file->file,ptr,size*nmemb,&rb);
			return rb;
		}
		else
		{
			u32 read_size=size*nmemb;
			if (read_size>file->flash.size-file->flash.ptr)
				read_size=file->flash.size-file->flash.ptr;
			FLASH_ReadData(ptr,file->flash.ptr+file->flash.addr,size*nmemb);
			file->flash.ptr+=read_size;
			return read_size;
		}
	}
	return 0;
}


//文件偏移
int ft_if_fseek (ft_file_struct *file,long int offset,int whence)
{
	if (file)
	{
		if (file->flash.size==0)
		{
			//文件开头
			if (SEEK_SET==whence)
			{
				if (f_lseek(&file->file,offset)==FR_OK)
					return 0;
			}
			else if (SEEK_END==whence)
			{
				//这时offset为负数
				offset=file->file_info.fsize+offset;
				if (f_lseek(&file->file,offset)==FR_OK)
					return 0;
			}
		}
		else
		{
			//文件开头
			if (SEEK_SET==whence)
			{
				if (file->flash.size>offset)
				{
					file->flash.ptr=offset;
					return 0;
				}
			}
			else if (SEEK_END==whence)
			{
				if (file->flash.size+offset>0)
				{
					file->flash.ptr=file->flash.size+offset;
					return 0;
				}
			}
		}
	}
	return -1;
}


//返回相对与文件开头的偏移
long int ft_if_ftell (ft_file_struct *file)
{
	if (file)
	{
		if (file->flash.size==0)
		{
			return f_tell(&file->file);
		}
		else
		{
			return file->flash.ptr;
		}
	}
	return -1;
}





如果没有自己实现malloc函数,则需要把堆空间设置大一点,

修改include\freetype\config\ftstdlib.h 文件,如下:

  
//..........省略无关代码..........

/**************************************************************************
   *
   *                          file handling
   *
   */


#include "stm32f4xx.h"
#include <stdio.h>
#include "ff.h"
#include "ftfile.h"

//#define FT_FILE     FILE
//#define ft_fclose   fclose
//#define ft_fopen    fopen
//#define ft_fread    fread
//#define ft_fseek    fseek
//#define ft_ftell    ftell
//#define ft_sprintf  sprintf

#define FT_FILE     ft_file_struct
#define ft_fclose   ft_if_fclose
#define ft_fopen    ft_if_fopen
#define ft_fread    ft_if_fread
#define ft_fseek    ft_if_fseek
#define ft_ftell    ft_if_ftell
#define ft_sprintf  sprintf

//..........省略无关代码..........

  /**************************************************************************
   *
   *                       memory allocation
   *
   */
	 
//这里把接口设置为自己实现的内存分配函数
#include "mymem.h"

#define ft_scalloc   mycalloc
#define ft_sfree     myfree
#define ft_smalloc   mymalloc
#define ft_srealloc  myrealloc



接下来应该可以编译通过了,如不能通过则根据提示修改

五、调用

在GUI界面初始化函数中添加如下代码:


#include "ft2build.h"
#include FT_FREETYPE_H



static FT_Library g_ft_library=0;
static FT_Face g_ft_face=0;




void WIN_FontInit (void)
{
	FT_Error ft_error=FT_Init_FreeType (&g_ft_library);
	if (ft_error)
	{
		ft_error=0;
	}
	else
	{
		ft_error=FT_New_Face (g_ft_library,"simfang.ttf",0,&g_ft_face);
		//ft_error=FT_New_Face (g_ft_library,"0:/SYS/simfang.ttf",0,&g_ft_face);
		if (ft_error)
		{
			ft_error=0;
		}
	}
}


修改获取字符点阵数据的函数:
由于FreeType使用的是Unicode编码,而Keil编译的文件是GBK编码,需要把GBK转为Unicode编码

//根据字体类型获取中文字模数据
int WIN_GetWordData(u8 type,unsigned char *buff, int word, int buff_size)
{
	u32 addr=WIN_FindWordAddr(type);
	
  	unsigned long foffset=0; 
	u8 qh=word>>8;u8 ql=word&0xff;
	u8 gbk[3]={0};
	gbk[0]=word>>8;
	gbk[1]=word&0xff;
	u8 uni[3]={0};
	
	//转换为Unicode编码
	if (word>0x80)
		gbk2uni_str(gbk,uni);
	else
		uni[1]=word;

	
	if (g_ft_face)
	{
		FT_Set_Pixel_Sizes(g_ft_face, type, type);
		FT_Load_Char (g_ft_face,(uni[0]<<8)|uni[1],FT_LOAD_RENDER|FT_LOAD_MONOCHROME);

		FT_GlyphSlot slot = g_ft_face->glyph;
		
		int w_byte=slot->bitmap.pitch;
		u8 *buf=slot->bitmap.buffer;
		mymemcpy (buff,buf,buff_size-5);
		buff[buff_size-5]=slot->bitmap.width;
		buff[buff_size-4]=slot->bitmap.rows;
		buff[buff_size-3]=slot->bitmap.pitch;
		buff[buff_size-2]=slot->bitmap_left;
		buff[buff_size-1]=slot->bitmap_top;
		
		return slot->bitmap.width;
	}
	else
	{
		buff[buff_size-5]=0;
		buff[buff_size-4]=0;
		buff[buff_size-3]=0;
		buff[buff_size-2]=0;
		buff[buff_size-1]=0;
		return type;
	}
	
}


修改绘制字符函数:

//-------------------------绘制字体回调函数--------------------------------------




//通用的绘制英文字符
static u32 WIN_DrawCharAtCommon (char c,int x,int y)
{
	u8 type=WIN_GetWinStruct()->font.TxtType;
	u8 wid=type/2;
	u8 hit=type;
	u8 off_left=0;
	u8 off_up=0;
	u8 w_byte=type/2/8+1;
	if (type/2%8) w_byte++;
	u8 h_byte=type;
	u32 all_byte= w_byte*h_byte+5;
	u32 ret=0;
	u8 *buff=0;
	
	//获取字模
	if (g_font==0) g_font=WIN_CreatFontBuff(40);
	buff=WIN_GetFontData(g_font,type,c,&all_byte);
	
	off_up=(type-buff[all_byte-1]);
	off_left=buff[all_byte-2];
	w_byte=buff[all_byte-3];
	hit=buff[all_byte-4];
	wid=buff[all_byte-5];
	
	//显示字模
	for (int j=0;j<hit+0;j++)
	{
		for (int i=0;i<wid+0;i++)
		{
			WIN_DrawPointSafe (x+i+off_left,y+j+off_up,(buff[j*w_byte+i/8]<<(i%8))&0x80);
		}	
	}
	ret=WIN_GetFontWidth()/2;
	
	return ret;
}



//通用的绘制中文字符
static u32 WIN_DrawWordAtCommon (char *c,int x,int y)
{
	u8 type=WIN_GetWinStruct()->font.TxtType;
	u8 wid=type;
	u8 hit=type;
	u8 off_left=0;
	u8 off_up=0;
	u8 w_byte=type/8+1;
	if (type%8) w_byte++;
	u8 h_byte=type;
	u32 all_byte= w_byte*h_byte+5;
	u32 ret=0;
	u8 *buff=0;
	
	//获取字模
	if (g_font==0) g_font=WIN_CreatFontBuff(40);
	buff=WIN_GetFontData(g_font,type,(c[0]<<8)|c[1],&all_byte);
	
	
	
	off_up=(type-buff[all_byte-1]);
	off_left=buff[all_byte-2];
	w_byte=buff[all_byte-3];
	hit=buff[all_byte-4];
	wid=buff[all_byte-5];

	//显示字模
	for (int j=0;j<hit+0;j++)
	{
		for (int i=0;i<wid+0;i++)
		{
			WIN_DrawPointSafe (x+i+off_left,y+j+off_up,(buff[j*w_byte+i/8]<<(i%8))&0x80);
		}	
	}
	ret=WIN_GetFontWidth();

	return ret;
}




六、优化

FreeType会占用较大的栈空间,如果栈设置太小容易造成栈溢出
本例设置20KB的栈空间可以保证正常运行

	ALIGN(RT_ALIGN_SIZE)
	static rt_uint8_t rt_gui_tack[20*1024]={0};
	static struct rt_thread rt_gui_thread={0};
	rt_thread_init (&rt_gui_thread,"gui_task",gui_task,0,rt_gui_tack,sizeof (rt_gui_tack),20,20);
	rt_thread_startup(&rt_gui_thread);

由于每次显示字符都要重新栅格化会消耗大量时间,本例实现了字形缓冲池来缓冲使用过的字形,提高重绘速度:


//-------------------------字形缓冲区--------------------------------------



typedef struct
{
	u8 type;
	int word;
	u32 data_size;
	u8 *data;
} _font_data;



typedef struct
{
	u32 buff_size;			//总大小
	u32 buff_used;			//使用了多少
	u32 buff_tail;			//尾部
	_font_data *font_data;
}WIN_FontBuffStruct;



//创建一个指定大小的字形缓冲区
WIN_FontBuffStruct *WIN_CreatFontBuff (u32 size)
{
	WIN_FontBuffStruct *ret=mycalloc( sizeof (WIN_FontBuffStruct));
	if (ret==0) return ret;
	
	ret->font_data=mycalloc (sizeof(_font_data)*size);
	if (ret->font_data==0)
	{
		myfree(ret);
		ret=0;
		return ret;
	}
	ret->buff_size=size;
	return ret;
}


//删除一个字形缓冲区
void WIN_DeleteFontBuff (WIN_FontBuffStruct *font)
{
	if (font)
	{
		for (int i=0;i<font->buff_used;i++)
		{
			_font_data *font_data=&font->font_data[i];
			if (font_data->data) myfree(font_data->data);
		}
		myfree(font);
	}
}


//添加字形数据
int WIN_AddFontData (WIN_FontBuffStruct *font,u8 type,int word,u8 *buff,u32 buff_size)
{
	if (font)
	{
		_font_data *font_data=0;
		if (font->buff_used<font->buff_size)
		{
			font_data=&font->font_data[font->buff_used];
			font->buff_used++;
		}
		else
		{
			//缓冲区已满
			font_data=&font->font_data[font->buff_tail];	
			
			myfree(font_data->data);
			
			if (font->buff_tail<font->buff_size-1)
				font->buff_tail++;
			else
				font->buff_tail=0;
		}
		font_data->type=type;
		font_data->word=word;
		font_data->data=buff;
		font_data->data_size=buff_size;
	}
	return 0;
}



//搜索缓冲区中的字形数据
u8 *WIN_SearchFontData (WIN_FontBuffStruct *font,u8 type,int word,u32 *buff_size)
{
	u8 *ret=0;
	if (font)
	{
		for (int i=0;i<font->buff_used;i++)
		{
			_font_data *font_data=&font->font_data[i];
			if (font_data->type==type)
			{
				if (font_data->word==word)
				{
					ret=font_data->data;
					if (buff_size) *buff_size=font_data->data_size;
				}
			}
		}
	}
	return ret;
}


//获取字形数据,缓冲区有就提取缓冲区的数据
//缓冲区中没有就调用获取字形函数
u8 *WIN_GetFontData (WIN_FontBuffStruct *font,u8 type,int word,u32 *buff_size)
{
	u8 *buff=0;
	buff=WIN_SearchFontData(font,type,word,buff_size);
	if (buff==0)
	{
		buff=mymalloc(*buff_size);
		WIN_GetWordData (type,buff,word,*buff_size);
		WIN_AddFontData (font,type,word,buff,*buff_size);
	}
	return buff;
}



static WIN_FontBuffStruct *g_font;


七、效果

部分界面截图
截图1
截图2
对比使用点阵字体的反应速度确实偏慢,但使用了缓冲池之后速度快了很多

  • 9
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
这份文档提供了FreeType 2函数库设计与实现的细节。本文档的目标是让开发人员更好的理解FreeType 2是如何组织的,并让他们扩充、定制和调试它。 首先,我们先了解这个库的目的,也就是说,为什么会写这个库: * 它让客户应用程序方便的访问字体文件,无论字体文件存储在哪里,并且与字体格式无关。 * 方便的提取全局字体数据,这些数据在平常的字体格式中普遍存在。(例如:全局度量标准,字符编码/字符映射表,等等) * 方便的提取某个字符的字形数据(度量标准,图像,名字,其他任何东西) * 访问字体格式特定的功能(例如,SFNT表,多重控制,OpenType轮廓表) Freetype 2的设计也受如下要求很大的影响: * 高可移植性。这个库必须可以运行在任何环境中。这个要求引入了一些非常激烈的选择,这些是FreeType2的低级系统界面的一部分。 * 可扩展性。新特性应该可以在极少改动库基础代码的前提下添加。这个要求引入了非常简单的设计:几乎所有操作都是以模块的形式提供的。 * 可定制。它应该能够很容易建立一个只包含某个特定项目所需的特性的版本。当你需要集成它到一个嵌入式图形库的字体服务器中时,这是非常重要的。 * 简洁高效。这个库的主要目标是只有很少cpu和内存资源的嵌入式系统。 这份文档的其他部分分为几个部分。首先,一些章节介绍了库的基本设计以及Freetype 2内部对象/数据的管理。 接下来的章节专注于库的定制和与这个话题相关的系统特定的界面,如何写你自己的模块和如何按需裁减库初始化和编译。
### 回答1: freetype2是一个开源的字体渲染库,主要用于将矢量字体转换为位图字体以供显示设备使用。单片机是一种集成了处理器、存储器和各种输入输出接口的微型计算机,用于控制和驱动各种电子设备。 使用freetype2库可以在单片机上实现字体的显示功能。具体步骤如下: 1. 首先,我们需要将所需的矢量字体文件导入到单片机的存储器中。这些字体文件可以从互联网上下载或者使用字体编辑软件自行设计。 2. 在单片机程序中,我们需要调用freetype2库的相关函数来解析字体文件,并将其转换为位图字体。这些函数包括字体初始化、加载字体文件、设置字体大小等。 3. 一旦字体文件被解析并转换为位图字体,我们可以通过单片机的显示接口来将字体渲染到屏幕上。这涉及到底层硬件的操作,例如设置屏幕分辨率、颜色深度等。 4. 在将字体显示到屏幕上之前,还可以通过freetype2库提供的函数设置字体的样式、颜色、对齐方式等。这使得我们可以在单片机上展示特定的字体效果。 总之,通过使用freetype2库,结合单片机的硬件接口和显示功能,我们可以实现在单片机上显示各种字体的功能。这对于某些需要显示文字信息的应用场景,如显示器、小型屏幕终端等,具有很大的实用价值。 ### 回答2: freetype是一款用于处理字体的开源软件库,它可以将字体文件中的字符转换为可显示的图像。单片机是一种集成了微处理器、存储器和各种输入输出接口的芯片,它常用于控制电子设备。 如果想在单片机上显示字体,可以借助freetype2库来完成。首先,需要将字体文件转换为freetype2支持的格式,比如.ttf文件。然后,在单片机上编写程序,使用freetype2库来解析字体文件。通过指定要显示的字体样式、大小和位置等参数,可以将需要显示的字体转换为图像数据。最后,将图像数据发送给单片机的显示屏或其他输出设备,就可以在单片机上显示字体了。 在单片机上显示字体可能涉及到一些硬件驱动的问题,比如驱动液晶显示屏或其他显示设备。具体的操作步骤和代码实现会依赖于所使用的单片机和显示设备。可以参考freetype2库官方的文档和示例代码,或者搜索相关教程和案例进行学习和实践。 总之,使用freetype2库可以在单片机上显示字体,通过将字体文件转换为图像数据,并通过适当的硬件驱动将图像数据发送到显示设备上。这样,单片机就可以实现字体的显示功能了。 ### 回答3: freetype2 是一个开源的字体渲染库,它可以在计算机上进行字体的渲染和排版。单片机是指一种集成了处理器、存储器和输入输出接口等功能的微型计算机,常用于嵌入式系统中。那么如何在单片机上使用freetype2来显示字呢? 首先,我们需要在计算机上安装freetype2库,并使用它来渲染和生成我们需要显示的字体。渲染好的字体可以保存为位图文件或者字库文件。 然后,将生成的字库文件或位图文件转换成单片机可以识别和处理的格式。可以使用专门的转换工具将字库文件转换成单片机所需的字模数组或字模数据表,并将其储存在单片机的存储器中。 接下来,在单片机程序中,我们可以通过调用相应字模的索引或地址,来显示特定的字形。我们可以使用单片机的液晶显示模块或其他合适的显示设备来显示字体,通过控制像素点的亮暗来绘制字形。 在显示字体时,我们需要根据字模的大小和位置,在显示设备上逐个点绘制字体的像素。可以采用行扫描或列扫描的方法来控制像素点的亮暗,逐行或逐列地设置像素状态,绘制完整的字形。 需要注意的是,在单片机上显示字体可能会受到处理能力和资源限制的影响。单片机的存储器容量有限,对字模的存储和处理可能会受到限制。同时,单片机的处理速度相对较慢,可能需要耗费较多的时间来完成字体的渲染和显示。 综上所述,使用freetype2库在单片机上显示字体需要将渲染好的字库文件转换成单片机可以处理的格式,并通过程序控制显示设备逐个像素地绘制字体。同时要考虑到单片机的资源和处理能力的限制,尽量优化显示效果和速度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值