目录
一、准备工作
二、复制文件
三、添加C文件到Keil中
四、修改接口
五、调用
六、优化
七、效果
一、准备工作
下载Freetype源码 ----- 下载FreeType
以移植到Keil 的STM32工程为例
移植前的软件环境:
1,实现了内存分配函数
2,实现了文件系统
3,使用了TR-Theard操作系统
4,实现了GUI界面,并支持点阵字体的显示
5,实现了Unicode,GBK,UTF8字符编码的相互转换
二、复制文件
解压源码压缩包得到如下文件
复制以下文件到单片机工程下
如图
三、添加C文件到Keil中
设置包含路径
添加以下文件到工程中
修改include\freetype\config\ftoption.h
注释掉
//#define FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
1
在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_HFT_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_Hstatic 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);elseuni[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++;elsefont->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;