目前正在做linux上的opengl开发,是做视频图像处理的,需要叠加一下文字动画和特效。用opengl的做法是先生成文字贴图然后用opengl渲染在视频帧上就可以了,所以关键是要获得文字的图片,网上查了一下可以用freetype来做文字处理,所以就下载安装了freetype2 (2.8), 地址github:https://github.com/code4game/freetype2,我下的是release版的:http://download.savannah.gnu.org/releases/freetype/freetype-2.8.tar.gz
freetype2依赖的开发包是zlib, bzip2, libpng, harfbuzz,所以要先安装这些库,我用的是centos,所以直接搜yum search zlib, 然后....。
解压后cd到解压目录下,编译的时候需要注意一下,包含harfbuzz时编译不过,所以这个库可以不下,编译过程如下:
./configure --without-harfbuzz
make
make install (先获取root权限)
编译完后会在/usr/include生成头文件路径freetype2, 这个路径会有点问题,等下再讲。生成的包文件在目录/usr/local/lib下,同时这个目录下还有一个文件夹pkgconfig,工程编译的时候需要连接到库,因为libfreetype.a还需要连接其他库,这些连接关系都在pkgconfig/freetype2.pc文件里面,所以系统用pkg-config包来管理编译链接问题(不懂pkg-config的自行搜索)。我的工程是gyp工程,所以在.gyp下面用pkg-config引入freetype2库:
'conditions': [
['OS=="linux"', {
'link_settings':{
'libraries':[
'
],
},
}],
],
由于freetype2是freetype的一个版本,刚才说的头文件包含路径/usr/include/freetype2会有点问题,问题是freetype2/freetype/config/ftheader.h文件中头文件包含都是这样的
'include_dirs':[
'/usr/include/freetype2',
],
这样工程就可以顺利编译链接到freetype2。好了freetype2安装编译就讲到这。下面讲freetype2的使用。
我也是搜网上的教程学习的,有一个教程说的比较好,地址:http://blog.sina.com.cn/s/blog_7b87d8b70100wjh3.html
我的代码基本是按照他的来写的,不过他的文章仅仅讲解了freetype2的编程使用,没有涉及文字转换过程。你需要准备的工作是,先找一个规范的.ttf矢量字体文件,freetype2需要一个字体文件来生成face。我先按照流程讲,等下会把全部代码贴出来。(1)首先先初始化库:
FT_Library library;
FT_Face pFTFace;
FT_Error error = FT_Init_FreeType(&library);
if (error) {
printf("error = %d\n", error);
return strim;
}
(2)创建字体的face:
error = FT_New_Face(library, "/home/.../zujiejian.ttf", 0, &pFTFace);
if (error == FT_Err_Unknown_File_Format ) {
printf("ERROR: fonts not support format!\n");
return;
}
(3)设置字形大小,ttf是矢量图,一般是可以改变字形大小的:
FT_Set_Char_Size(pFTFace, 16 << 6 , 16 << 6 , 300, 300);
(4)获取字符索引。因为FT_Get_Char_Index()接受的是unicode编码格式,而程序字符串的编码方式是UFT-8,这种编码方式可以使得所有文字字符编码的唯一性又可以节省内存空间,其实就是一种unicode的实现方式,只是纯unicode不会压缩编码,如果每个字符都用4个字节存,那么将会耗费很多存储空间,有篇文章介绍UFT-8和unicode可以看看http://blog.csdn.net/ztm521/article/details/44084805。所以需要先转码,这个需要自己编接口,linux上好像有相应接口,但我按照别人说的那样做好像不对,可能是我不知道其中的一些细节吧,然后我就自己写了一个:
typedef struct FTCode_ {
FT_ULong code;
char utf8str[8];
}FTCode;
inline unsigned int numOfUnicodebyte(char c, char* code)
{
unsigned int n = 0;
while(c&0x80) {
c = c<<1;
n++;
};
*code = c>>n;
return n;
}
void UTF8ToUnicode(const char* str, FTCode** codes, unsigned int* num)
{
if (!str) {
return;
}
unsigned int len = strlen(str);
FTCode* ftcodes = (FTCode*)malloc(sizeof(FTCode)*len);
unsigned int ftcount = 0;
unsigned int offset = 0;
unsigned int nbyte = 0;
char c = '\0';
while(str[offset] != '\0') {
nbyte = numOfUnicodebyte(str[offset], &c);
if (nbyte > 0) {
FT_ULong code = c;
ftcodes[ftcount].utf8str[0] = str[offset];
for (unsigned int i = 1; i < nbyte; i++) {
code = (code<<6)|(str[offset+i]&0x3F);
ftcodes[ftcount].utf8str[i] = str[offset+i];
}
ftcodes[ftcount].utf8str[nbyte] = '\0';
ftcodes[ftcount].code = code;
offset += nbyte;
}
else {
ftcodes[ftcount].code = str[offset];
ftcodes[ftcount].utf8str[0] = str[offset];
ftcodes[ftcount].utf8str[1] = '\0';
offset++;
}
ftcount++;
}
*codes = ftcodes;
*num = ftcount;
}
注意FTCode* codes使用完后需要释放掉free(codes);
(5)获取字形信息:
error = FT_Load_Glyph(pFTFace, code, FT_LOAD_DEFAULT);
error = FT_Get_Glyph(pFTFace->glyph, &glyph);
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &glyp_bbox);
(6)获取256级灰度位图
error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, &pen, 0);
上面的步骤是我总结出来的,具体可以去这个网址看:http://blog.sina.com.cn/s/blog_7b87d8b70100wjh3.html。后面就需要把一个个字的bitmap考到自己的buffer然后生成贴图使用了。
下面把全部代码贴出来:
typedef struct FTStrImage {
unsigned int width;
unsigned int height;
unsigned char* imageBuffer;
}FTStrImage;
typedef struct TGlyph_
{
FT_UInt index;
FT_Vector pos;
FT_BBox drawBox;
FT_Glyph glyph;
} TGlyph, *PGlyph;
typedef struct {
unsigned char* image;
unsigned int width;
unsigned int height;
unsigned int strLen;
}FTString;
#define MAX_GLYPHS 1024
FTStrImage NewFTString(const char* str)
{
FTStrImage strim = {0};
if (!str) {
printf("ERROR: string is null!\n");
return strim;
}
FT_Library library;
FT_Face pFTFace;
FT_Error error = FT_Init_FreeType(&library);
if (error) {
printf("error = %d\n", error);
return strim;
}
error = FT_New_Face(library, "/home/.../zujiejian.ttf", 0, &pFTFace);
if (error == FT_Err_Unknown_File_Format ) {
printf("ERROR: fonts not support format!\n");
return strim;
}
else if (error) {
printf("ERROR: fonts file cannot open!\n");
return strim;
}
FT_Set_Char_Size(pFTFace, 16 << 6 , 16 << 6 , 300, 300);
FT_GlyphSlot slot = pFTFace->glyph;
FT_UInt glyph_index = 0;
FT_Bool use_kerning = FT_HAS_KERNING(pFTFace);
FT_UInt previous = 0;
int pen_x = 0;
int pen_y = 0;
TGlyph glyphs[MAX_GLYPHS];
FT_UInt num_glyphs = 0;
FTCode* codes = 0;
unsigned int codeNum = 0;
UTF8ToUnicode(str, &codes, &codeNum);
if (!codes || codeNum == 0) {
printf("ERROR: get charater code failed!\n");
return strim;
}
FT_BBox bbox;
bbox.xMin = bbox.yMin = 32000;
bbox.xMax = bbox.yMax = -32000;
PGlyph glyph = glyphs;
for ( unsigned int n = 0; n < codeNum; n++)
{
glyph->index = FT_Get_Char_Index(pFTFace, codes[n].code);
if ( use_kerning && previous && glyph->index)
{
FT_Vector delta;
FT_Get_Kerning(pFTFace, previous, glyph->index,
FT_KERNING_DEFAULT, &delta );
pen_x += delta.x >> 6;
}
glyph->pos.x = pen_x;
glyph->pos.y = pen_y;
error = FT_Load_Glyph(pFTFace, glyph->index, FT_LOAD_DEFAULT);
if ( error ) continue;
error = FT_Get_Glyph(pFTFace->glyph, &glyph->glyph);
if ( error ) continue;
pen_x += slot->advance.x >> 6;
previous = glyph->index;
FT_BBox glyph_bbox;
FT_Glyph_Get_CBox(glyph->glyph, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox);
glyph_bbox.xMin += glyph->pos.x;
glyph_bbox.xMax += glyph->pos.x;
glyph_bbox.yMin += glyph->pos.y;
glyph_bbox.yMax += glyph->pos.y;
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;
}
//printf("box(%d): [%d %d %d %d]\n", n, glyph_bbox.xMin, glyph_bbox.yMin, glyph_bbox.xMax, glyph_bbox.yMax);
glyph->drawBox = glyph_bbox;
glyph++;
}
num_glyphs = glyph - glyphs;
if ( bbox.xMin > bbox.xMax )
{
bbox.xMin = 0;
bbox.yMin = 0;
bbox.xMax = 0;
bbox.yMax = 0;
}
//printf("BOX(%d): [%d %d %d %d]\n", num_glyphs, bbox.xMin, bbox.yMin, bbox.xMax - bbox.xMin, bbox.yMax - bbox.yMin);
unsigned int width = bbox.xMax+1;
unsigned int height = bbox.yMax-bbox.yMin+1;
unsigned char* imageBuffer = (unsigned char*)calloc(width*height,4);
unsigned int start_x = 0;
unsigned int start_y = 0;
//printf("width = %d height = %d \n", width, height);
for ( unsigned int n = 0; n < codeNum; n++) {
PGlyph pplyph = glyphs+n;
FT_Vector pen = glyph->pos;
error = FT_Glyph_To_Bitmap(&pplyph->glyph, FT_RENDER_MODE_NORMAL, &pen, 0);
if ( !error )
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)pplyph->glyph;
start_x = pplyph->drawBox.xMin;
start_y = height - (pplyph->drawBox.yMin-bbox.yMin+bit->bitmap.rows);
for (unsigned int y = 0; y < bit->bitmap.rows; y++) {
for (unsigned int x = 0; x < bit->bitmap.width; x++) {
unsigned int iy = start_y + y;
unsigned int ix = start_x + x;
unsigned int index = (iy*width + ix)*4;
imageBuffer[index] = 0;
imageBuffer[index+1] = 0;
imageBuffer[index+2] = 255;
imageBuffer[index+3] = bit->bitmap.buffer[y * bit->bitmap.width + x];
}
}
FT_Done_Glyph(pplyph->glyph);
}
}
//free codes
free(codes);
//free face
FT_Done_Face(pFTFace);
pFTFace = NULL;
//free FreeType Lib
FT_Done_FreeType(library);
library = NULL;
strim.width = width;
strim.height = height;
strim.imageBuffer = imageBuffer;//release after
return strim;
}
效果: