FreeType用起来比较麻烦,这里写了一份简单的示例代码,仅供参考。

实现了FT库生成字符位图,并上传到GL纹理。

实现了字符位图缓存功能,多个字符图像保存在同一个纹理中。

实现了简单的字体管理框架。

实现了简单的加粗和倾斜效果。

实现了反锯齿开关,并且兼容加粗倾斜效果。

代码如下:

 // OpenGL library
#include <gl/glut.h> // Std misc
#include <map>
#include <vector> // FreeType library
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_BITMAP_H
#include FT_OUTLINE_H #ifdef CreateFont
#undef CreateFont
#endif typedef unsigned char byte; class CFontManager
{
public:
CFontManager();
~CFontManager(); bool initialize(void);
void release(void);
int createFont(const char *filename, int face, int tall, bool bold, bool italic, bool antialias);
bool getCharInfo(int font_index, int code, int *wide, int *tall, int *horiBearingX, int *horiBearingY, int *horiAdvance, GLuint *texture, float coords[]);
int getFontTall(int font_index); private:
struct glyphMetrics
{
int width;
int height;
int horiBearingX;
int horiBearingY;
int horiAdvance;
//int vertBearingX;
//int vertBearingY;
//int vertAdvance;
}; class CFont
{
public:
CFont();
~CFont(); bool create(FT_Library library, const char *filename, FT_Long face_index, int tall, bool bold, bool italic, bool antialias);
void release(void);
bool getCharInfo(int code, glyphMetrics *metrics, GLuint *texture, float coords[]);
int getFontTall(void); private:
bool loadChar(int code, glyphMetrics *metrics); class CChar
{
public:
void setInfo(glyphMetrics *metrics);
void getInfo(glyphMetrics *metrics, GLuint *texture, float coords[]); public:
int m_code;
GLuint m_texture;
float m_coords[]; // left top right bottom private:
glyphMetrics m_metrics;
}; class CPage
{
public:
CPage();
~CPage(); bool append(int wide, int tall, byte *rgba, float coords[]);
GLuint getTexture(void); private:
GLuint m_texture;
int m_wide;
int m_tall;
int m_posx;
int m_posy;
int m_maxCharTall;
}; typedef std::map<int, CChar *> TCharMap; FT_Library m_library;
FT_Face m_face;
bool m_antialias;
bool m_bold;
int m_tall;
int m_rgbaSize;
GLubyte *m_rgba;
TCharMap m_chars;
std::vector<CPage *> m_pages;
}; FT_Library m_library;
std::vector<CFont *> m_fonts;
}; //------------------------------------------------------------
// CFont
//------------------------------------------------------------
CFontManager::CFont::CFont()
{
m_face = NULL;
m_rgba = NULL;
m_antialias = false;
m_bold = false;
m_tall = ;
} CFontManager::CFont::~CFont()
{
release();
} bool CFontManager::CFont::create(FT_Library library, const char *filename, FT_Long face_index, int tall, bool bold, bool italic, bool antialias)
{
FT_Error err; if (tall > )
{
// Bigger than a page size
return false;
} if ((err = FT_New_Face(library, filename, face_index, &m_face)) != FT_Err_Ok)
{
printf("FT_New_Face() Error %d\n", err);
return false;
} if ((err = FT_Set_Pixel_Sizes(m_face, , tall)) != FT_Err_Ok)
{
printf("FT_Set_Pixel_Sizes() Error %d\n", err);
return false;
} m_rgbaSize = (tall * ) * tall * ; m_rgba = new GLubyte[m_rgbaSize]; if (m_rgba == NULL)
{
return false;
} m_library = library;
m_antialias = antialias;
m_bold = bold;
m_tall = tall; if (italic)
{
FT_Matrix m;
m.xx = 0x10000L;
m.xy = 0.5f * 0x10000L;
m.yx = ;
m.yy = 0x10000L;
FT_Set_Transform(m_face, &m, NULL);
} return true;
} void CFontManager::CFont::release(void)
{
FT_Error err; if (m_face)
{
if ((err = FT_Done_Face(m_face)) != FT_Err_Ok)
{
printf("FT_Done_Face() Error %d\n", err);
}
m_face = NULL;
} if (m_rgba)
{
delete[] m_rgba;
m_rgba = NULL;
} for (TCharMap::iterator it = m_chars.begin(); it != m_chars.end(); it++)
{
delete it->second;
it->second = NULL;
} m_chars.clear(); for (size_t i = ; i < m_pages.size(); i++)
{
delete m_pages[i];
m_pages[i] = NULL;
} m_pages.clear();
} bool CFontManager::CFont::getCharInfo(int code, glyphMetrics *metrics, GLuint *texture, float coords[])
{
// fast find it
TCharMap::iterator it = m_chars.find(code); if (it != m_chars.end())
{
it->second->getInfo(metrics, texture, coords);
return true;
} glyphMetrics gm; if (loadChar(code, &gm) == false)
{
return false;
} CChar *ch = new CChar(); ch->m_code = code;
ch->setInfo(&gm); for (size_t i = ; i < m_pages.size(); i++)
{
CPage *page = m_pages[i]; if (page->append(gm.width, gm.height, m_rgba, ch->m_coords))
{
ch->m_texture = page->getTexture();
ch->getInfo(metrics, texture, coords);
m_chars.insert(TCharMap::value_type(code, ch));
return true;
}
} CPage *page = new CPage(); if (page->append(gm.width, gm.height, m_rgba, ch->m_coords))
{
ch->m_texture = page->getTexture();
ch->getInfo(metrics, texture, coords);
m_chars.insert(TCharMap::value_type(code, ch));
m_pages.push_back(page);
return true;
} delete ch;
delete page; return false;
} int CFontManager::CFont::getFontTall(void)
{
return m_tall;
} // bitmap.width 位图宽度
// bitmap.rows 位图行数(高度)
// bitmap.pitch 位图一行占用的字节数 //MONO模式每1个像素仅用1bit保存,只有黑和白。
//1个byte可以保存8个像素,1个int可以保存8*4个像素。
void ConvertMONOToRGBA(FT_Bitmap *source, GLubyte *rgba)
{
GLubyte *s = source->buffer;
GLubyte *t = rgba; for (GLuint y = source->rows; y > ; y--)
{
GLubyte *ss = s;
GLubyte *tt = t; for (GLuint x = source->width >> ; x > ; x--)
{
GLuint val = *ss; for (GLuint i = ; i > ; i--)
{
tt[] = tt[] = tt[] = tt[] = ( val & (<<(i-)) ) ? 0xFF : 0x00;
tt += ;
} ss += ;
} GLuint rem = source->width & ; if (rem > )
{
GLuint val = *ss; for (GLuint x = rem; x > ; x--)
{
tt[] = tt[] = tt[] = tt[] = ( val & 0x80 ) ? 0xFF : 0x00;
tt += ;
val <<= ;
}
} s += source->pitch;
t += source->width * ; //pitch
}
} //GRAY模式1个像素用1个字节保存。
void ConvertGRAYToRGBA(FT_Bitmap *source, GLubyte *rgba)
{
for (GLuint y = ; y < source->rows; y++)
{
for (GLuint x = ; x < source->width; x++)
{
GLubyte *s = &source->buffer[(y * source->pitch) + x];
GLubyte *t = &rgba[((y * source->pitch) + x) * ]; t[] = t[] = t[] = 0xFF;
t[] = *s;
}
}
} bool CFontManager::CFont::loadChar(int code, glyphMetrics *metrics)
{
FT_Error err; FT_UInt glyph_index = FT_Get_Char_Index(m_face, (FT_ULong)code); if (glyph_index > )
{
if ((err = FT_Load_Glyph(m_face, glyph_index, FT_LOAD_DEFAULT)) == FT_Err_Ok)
{
FT_GlyphSlot glyph = m_face->glyph; FT_Render_Mode render_mode = m_antialias ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO; if (m_antialias && m_bold)
{
if ((err = FT_Outline_EmboldenXY(&glyph->outline, , )) != FT_Err_Ok)
{
printf("FT_Outline_EmboldenXY() Error %d\n", err);
}
} if ((err = FT_Render_Glyph(glyph, render_mode)) == FT_Err_Ok)
{
FT_Bitmap *bitmap = &glyph->bitmap; switch (bitmap->pixel_mode)
{
case FT_PIXEL_MODE_MONO:
{
if (!m_antialias && m_bold)
{
if ((err = FT_Bitmap_Embolden(m_library, bitmap, , )) != FT_Err_Ok)
{
printf("FT_Bitmap_Embolden() Error %d\n", err);
}
}
ConvertMONOToRGBA(bitmap, m_rgba);
break;
}
case FT_PIXEL_MODE_GRAY:
{
ConvertGRAYToRGBA(bitmap, m_rgba);
break;
}
default:
{
memset(m_rgba, 0xFF, m_rgbaSize);
break;
}
} metrics->width = bitmap->width;
metrics->height = bitmap->rows;
metrics->horiBearingX = glyph->bitmap_left;
metrics->horiBearingY = glyph->bitmap_top;
metrics->horiAdvance = glyph->advance.x >> ; return true;
}
else
{
printf("FT_Render_Glyph() Error %d\n", err);
}
}
else
{
printf("FT_Load_Glyph() Error %d\n", err);
}
} memset(metrics, , sizeof(glyphMetrics)); return false;
} //------------------------------------------------------------
// CChar
//------------------------------------------------------------
void CFontManager::CFont::CChar::setInfo(glyphMetrics *metrics)
{
memcpy(&m_metrics, metrics, sizeof(glyphMetrics));
} void CFontManager::CFont::CChar::getInfo(glyphMetrics *metrics, GLuint *texture, float coords[])
{
memcpy(metrics, &m_metrics, sizeof(glyphMetrics)); *texture = m_texture;
memcpy(coords, m_coords, sizeof(float)*);
} //------------------------------------------------------------
// CPage
//------------------------------------------------------------
CFontManager::CFont::CPage::CPage()
{
m_wide = m_tall = ;
m_posx = m_posy = ; // In a line, for a max height character
m_maxCharTall = ; glGenTextures(, &m_texture); // Using your API here
glBindTexture(GL_TEXTURE_2D, m_texture);
glTexImage2D(GL_TEXTURE_2D, , GL_RGBA, m_wide, m_tall, , GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} CFontManager::CFont::CPage::~CPage()
{
// free the texture
} bool CFontManager::CFont::CPage::append(int wide, int tall, byte *rgba, float coords[])
{
if (m_posy + tall > m_tall)
{
// not enough line space in this page
return false;
} // If this line is full ...
if (m_posx + wide > m_wide)
{
int newLineY = m_posy + m_maxCharTall; if (newLineY + tall > m_tall)
{
// No more space for new line in this page, should allocate a new one
return false;
} // Begin in new line
m_posx = ;
m_posy = newLineY;
// Reset
m_maxCharTall = ;
} glBindTexture(GL_TEXTURE_2D, m_texture);
glTexSubImage2D(GL_TEXTURE_2D, , m_posx, m_posy, wide, tall, GL_RGBA, GL_UNSIGNED_BYTE, rgba); coords[] = m_posx / (float)m_wide; // left
coords[] = m_posy / (float)m_tall; // top
coords[] = (m_posx + wide) / (float)m_wide; // right
coords[] = (m_posy + tall) / (float)m_tall; // bottom m_posx += wide; if (tall > m_maxCharTall)
{
m_maxCharTall = tall;
} return true;
} GLuint CFontManager::CFont::CPage::getTexture(void)
{
return m_texture;
} //------------------------------------------------------------
// CFontManager
//------------------------------------------------------------
CFontManager::CFontManager()
{
m_library = NULL;
} CFontManager::~CFontManager()
{
release();
} bool CFontManager::initialize(void)
{
FT_Error err; if ((err = FT_Init_FreeType(&m_library)) != FT_Err_Ok)
{
printf("FT_Init_FreeType() Error %d\n", err);
return false;
} return true;
} void CFontManager::release(void)
{
FT_Error err; for (size_t i = ; i < m_fonts.size(); i++)
{
delete m_fonts[i];
m_fonts[i] = NULL;
} m_fonts.clear(); if ((err = FT_Done_FreeType(m_library)) != FT_Err_Ok)
{
printf("FT_Done_FreeType() Error %d\n");
}
} int CFontManager::createFont(const char *filename, int face, int tall, bool bold, bool italic, bool antialias)
{
CFont *font = new CFont(); if (font->create(m_library, filename, face, tall, bold, italic, antialias) != true)
{
delete font;
return ;
} m_fonts.push_back(font); return m_fonts.size();
} #define CONVERT_FONT_INDEX(x) (((x) < 1 || (x) > (int)m_fonts.size()) ? -1 : (x) - 1) bool CFontManager::getCharInfo(int font_index, int code, int *wide, int *tall, int *horiBearingX, int *horiBearingY, int *horiAdvance, GLuint *texture, float coords[])
{
int i = CONVERT_FONT_INDEX(font_index); if (i == -)
{
return false;
} CFont *font = m_fonts[i]; glyphMetrics metrics; if (font->getCharInfo(code, &metrics, texture, coords) == false)
{
return false;
} *wide = metrics.width;
*tall = metrics.height;
*horiBearingX = metrics.horiBearingX;
*horiBearingY = metrics.horiBearingY;
*horiAdvance = metrics.horiAdvance; return true;
} int CFontManager::getFontTall(int font_index)
{
int i = CONVERT_FONT_INDEX(font_index); if (i == -)
{
return false;
} CFont *font = m_fonts[i]; return font->getFontTall();
} CFontManager g_FontManager; int char_font; void init(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); g_FontManager.initialize(); char_font = g_FontManager.createFont("FZDH_GBK.TTF", , , false, false, true); if (char_font == )
{
printf("createFont failed\n");
}
} wchar_t ciphertext[] =
{
L"第一我做的是人气,不是他妈的信仰不是他妈的信任\n"
L"我想信任来着,一群狼心的人就知道玩游戏。我投入的你们给我一分钱回报了么。\n"
L"不懂少在那里嫌弃我,白花花的银子砸出来的东西免费给你们玩你们还这bb那bb\n"
L"不管有没有人骂我我只在乎有没有人看我 懂么?\n"
L"第二我你们尽管骂我\n"
L"第三我的事情是我的自由\n"
L"第四我一直很讨厌csol\n"
L"第五世界上最失败的一次行动就是创建百度贴吧\n"
L"第六 一群二逼骂个毛线管我屁事\n"
L"http://tieba.baidu.com/p/3144980358"
}; //#define DRAW_PAGE void draw_string(int x, int y, int font, wchar_t *string)
{
if (!font)
return; int tall = g_FontManager.getFontTall(font); int dx = x;
int dy = y; GLuint sglt = ; while (*string)
{
if (*string == L'\n')
{
string++;
dx = x;
dy += tall + ; //row spacing
continue;
} int cw, ct, bx, by, av;
GLuint glt;
float crd[]; if (!g_FontManager.getCharInfo(font, *string, &cw, &ct, &bx, &by, &av, &glt, crd))
{
string++;
continue;
} //大多数情况下多个字符都在同一个纹理中,避免频繁绑定纹理,可以提高效率
if (glt != sglt)
{
glBindTexture(GL_TEXTURE_2D, glt);
sglt = glt;
} int px = dx + bx;
int py = dy - by; glBegin(GL_QUADS);
glTexCoord2f(crd[], crd[]);
glVertex3f(px, py, 0.0f);
glTexCoord2f(crd[], crd[]);
glVertex3f(px + cw, py, 0.0f);
glTexCoord2f(crd[], crd[]);
glVertex3f(px + cw, py + ct, 0.0f);
glTexCoord2f(crd[], crd[]);
glVertex3f(px, py + ct, 0.0f);
glEnd(); dx += av; string++;
}
} void draw_page_texture(int x, int y, GLuint glt)
{
if (!glt)
{
glDisable(GL_TEXTURE_2D);
glColor4f(0.2, 0.2, 0.2, 1.0);
}
else
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, glt);
glColor4f(1.0, 1.0, 1.0, 1.0);
} int w = ;
int t = ; glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0);
glVertex3f(x, y, 0.0f);
glTexCoord2f(1.0, 0.0);
glVertex3f(x + w, y, 0.0f);
glTexCoord2f(1.0, 1.0);
glVertex3f(x + w, y + t, 0.0f);
glTexCoord2f(0.0, 1.0);
glVertex3f(x, y + t, 0.0f);
glEnd();
} void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glEnable(GL_TEXTURE_2D);
glColor4f(1.0, 1.0, 1.0, 1.0);
draw_string(, , char_font, ciphertext); draw_page_texture(, , ); //background
draw_page_texture(, , ); //page1 texture draw_page_texture(, , ); //background
draw_page_texture(, , ); //page2 texture glutSwapBuffers();
glutPostRedisplay();
} void reshape(int width, int height)
{
glViewport(, , width, height); glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(, width, height, );
glMatrixMode(GL_MODELVIEW);
} void keyboard(unsigned char key, int x, int y)
{
} void main(int argc, char **argv)
{
glutInitWindowPosition(, );
glutInitWindowSize(, );
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
glutCreateWindow("FreeType OpenGL");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop();
}

例子中用到的GLUT和FreeType库请自行配置好。

运行效果:

【OpenGL】使用FreeType库加载字体并在GL中绘制文字的更多相关文章

  1. Android OpenGL库加载过程源码分析

    Android系统采用OpenGL绘制3D图形,使用skia来绘制二维图形:OpenGL源码位于: frameworks/native/opengl frameworks/base/opengl 本文 ...

  2. IIS无法加载字体文件(*.woff,*.svg)的解决办法

    在编写前端代码的过程中经常会遇到使用特定的字体(*.woff,*.svg),此时在加载字体时请求会被返回 Failed to load resource: the server responded w ...

  3. 安卓奇葩问题之.so库加载不了

    真是哔了狗了. 今天突然遇到一个问题:之前用第三方的密码控件,给了一个.so库文件.然后我就放在了/jniLibs/armeabi目录下. 运行,一切都很OK. 然后重点来了.N天之后的今天,突然打包 ...

  4. androidStudio中如何加载字体资源?

    在android中字体的格式总是不能尽善尽美的显示出来 ,  于是要求我们使用一些有美感的字体,加载的方式(就像HTML的字体一样),我们需要通过加载字体的方式来使用android中不曾提供的字体; ...

  5. LIB库加载方法-引用百度百科

    LIB库加载方法,有三种,如下: 1.LIB文件直接加入到工程文件列表中 在VC中打开File View一页,选中工程名,单击鼠标右键,然后选中\"Add Files to Project\ ...

  6. CSS远程加载字体

    CSS 远程加载字体的方法,做网站CSS的都知道,用户浏览网站时,网页上的字体是加载本地的.换言之,如果网站使用了用户电脑所没有安装的字体,那显示字体就会被默认字体所代替了,自然效果就大受影响了. 上 ...

  7. linux动态库加载RPATH, RUNPATH

    摘自http://gotowqj.iteye.com/blog/1926771 linux动态库加载RPATH, RUNPATH 链接动态库 如何程序在连接时使用了共享库,就必须在运行的时候能够找到共 ...

  8. linux动态库加载的秘密

    摘自http://gotowqj.iteye.com/blog/1926734 摘自http://www.360doc.com/content/14/0313/13/12747488_36024641 ...

  9. webpack4加载字体

    webpack加载字体,刚开始下载完字体后就用css去引用它,结果死活没显示我要的字体,后来https://www.aliyun.com/jiaocheng/654750.html这篇文章说要把下载的 ...

随机推荐

  1. [2016北京集训测试赛5]小Q与内存-[线段树的神秘操作]

    Description Solution 哇真的异常服气..线段树都可以搞合并和拆分的啊orzorz.神的世界我不懂 Code #include<iostream> #include< ...

  2. CF 1083 A. The Fair Nut and the Best Path

    A. The Fair Nut and the Best Path https://codeforces.com/contest/1083/problem/A 题意: 在一棵树内找一条路径,使得从起点 ...

  3. 强化学习读书笔记 - 12 - 资格痕迹(Eligibility Traces)

    强化学习读书笔记 - 12 - 资格痕迹(Eligibility Traces) 学习笔记: Reinforcement Learning: An Introduction, Richard S. S ...

  4. TensorFlow深度学习实战---图像数据处理

    图像的亮度.对比度等属性对图像的影响非常大,这些因素都会影响最后的识别结构.当然,复杂的预处理过程可能会导致训练效率的下降(利用TensorFlow中多线程处理输入数据的解决方案). 同一不同的原始数 ...

  5. Unity中几个特殊路径在各个平台的访问方式

    1.文件路径Resources:Unity在发布成移动端项目后,其他文件路径都将不存在,但是如果有一些必要的资源,可以放在Resources文件夹下,因为这个文件夹下的所有资源是由Unity内部进行调 ...

  6. python购物车优化

    一.需求分析 拥有用户接口和商家接口 用户能够进行消费记录查询,充值,购物等功能,消费记录存储于数据库 商家可以进行商品的增删改等操作 二.程序流程图 程序大致流程图如下: 三.代码实现 本程序分成两 ...

  7. day02——作业讲解

    # 设定⼀个理想数字⽐如:66,让⽤户输⼊数字,如果⽐66⼤,则显示猜测# 的结果⼤了:如果⽐66⼩,则显示猜测的结果⼩了;只有等于66,显示猜测结果# 正确,然后退出循环 #升级版# 可以帮我们生成 ...

  8. Material Safety Data Sheet,MSDS - 化学品安全说明书

    化学品安全说明书(Material Safety Data Sheet)MSDS,国际上称作化学品安全信息卡,是化学品生产商和经销商按法律要求必须提供的化学品理化特性(如PH值,闪点,易燃度,反应活性 ...

  9. spark-local-运行异常-Could not locate executable null\bin\winutils.exe in the Hadoop binaries

    windows下-local模式-运行spark: 1.下载winutils的windows版本 GitHub上,有人提供了winutils的windows的版本,项目地址是:https://gith ...

  10. Coloring a Tree(耐心翻译+思维)

    Description You are given a rooted tree with n vertices. The vertices are numbered from 1 to n, the ...