【OpenGL】使用FreeType库加载字体并在GL中绘制文字
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中绘制文字的更多相关文章
- Android OpenGL库加载过程源码分析
Android系统采用OpenGL绘制3D图形,使用skia来绘制二维图形:OpenGL源码位于: frameworks/native/opengl frameworks/base/opengl 本文 ...
- IIS无法加载字体文件(*.woff,*.svg)的解决办法
在编写前端代码的过程中经常会遇到使用特定的字体(*.woff,*.svg),此时在加载字体时请求会被返回 Failed to load resource: the server responded w ...
- 安卓奇葩问题之.so库加载不了
真是哔了狗了. 今天突然遇到一个问题:之前用第三方的密码控件,给了一个.so库文件.然后我就放在了/jniLibs/armeabi目录下. 运行,一切都很OK. 然后重点来了.N天之后的今天,突然打包 ...
- androidStudio中如何加载字体资源?
在android中字体的格式总是不能尽善尽美的显示出来 , 于是要求我们使用一些有美感的字体,加载的方式(就像HTML的字体一样),我们需要通过加载字体的方式来使用android中不曾提供的字体; ...
- LIB库加载方法-引用百度百科
LIB库加载方法,有三种,如下: 1.LIB文件直接加入到工程文件列表中 在VC中打开File View一页,选中工程名,单击鼠标右键,然后选中\"Add Files to Project\ ...
- CSS远程加载字体
CSS 远程加载字体的方法,做网站CSS的都知道,用户浏览网站时,网页上的字体是加载本地的.换言之,如果网站使用了用户电脑所没有安装的字体,那显示字体就会被默认字体所代替了,自然效果就大受影响了. 上 ...
- linux动态库加载RPATH, RUNPATH
摘自http://gotowqj.iteye.com/blog/1926771 linux动态库加载RPATH, RUNPATH 链接动态库 如何程序在连接时使用了共享库,就必须在运行的时候能够找到共 ...
- linux动态库加载的秘密
摘自http://gotowqj.iteye.com/blog/1926734 摘自http://www.360doc.com/content/14/0313/13/12747488_36024641 ...
- webpack4加载字体
webpack加载字体,刚开始下载完字体后就用css去引用它,结果死活没显示我要的字体,后来https://www.aliyun.com/jiaocheng/654750.html这篇文章说要把下载的 ...
随机推荐
- CF 1037 D. Valid BFS?
D. Valid BFS? http://codeforces.com/contest/1037/problem/D 题意: 给一个序列,一棵树,判断能否bfs这棵树,得到这个序列. 分析: 将每个点 ...
- Drupal7 配置多站点及为每个站点设置语言
默认情况 在Drupal7的安装目录下存在sites目录 sites目录结构如下: --all --default --example.sites.php --README.txt 1. 添加新域名, ...
- Maven学习(三)-----Maven本地资源库
Maven本地资源库 Maven的本地资源库是用来存储所有项目的依赖关系(插件jar和其他文件,这些文件被Maven下载)到本地文件夹.很简单,当你建立一个Maven项目,所有相关文件将被存储在你的M ...
- 在eclipse中通过git添加Maven 多重项目时会遇到的问题
最近,项目换到了使用git作版本控制.于是就开始了,拉代码,测试的时候了. 再过程中遇到两个问题: 1.下载下来的不是项目,只是文档,转换为Maven项目之后 pom.xml报错(org.codeha ...
- java 中的字符串
创建String对象 String s1="xxx"://创建一个字符串对象“xxx”,名为s1; String s2=new String();//创建一个空字符串对象,名为S2 ...
- Openwrt之移动硬盘ext3/ext4格式化工具
在给openwrt挂载移动硬盘的时候,最好是ext3/ext4方式,但在windows下苦于无法找到合适的工具进行格式化. 踅摸了半天,终于找到了它:MiniTool Partion Wizard ...
- Egret入门(二)--windows下环境搭建
准备材料 安装Node.js TypeScript编辑器 HTTP服务器(可选) Chorme(可选) Egret 安装Node.js 打开www.nodejs.org 下载安装(全部next,全默认 ...
- 数据库sql优化总结之2-百万级数据库优化方案+案例分析
项目背景 有三张百万级数据表 知识点表(ex_subject_point)9,316条数据 试题表(ex_question_junior)2,159,519条数据 有45个字段 知识点试题关系表(ex ...
- 如何使用Win+R快捷键打开自定义程序
鉴于大家对于提高效率这块有争议,更改了下标题. 大家平时一定都使用过Win+R运行快捷键, 在运行里可以快捷的打开一些系统软件,比如说输入mstsc是打开远程连接,输入explorer是打开文件管理器 ...
- url乱码问题
//url乱码,有时候要解码2次才能成功 String url=URLDecoder.decode(URLDecoder.decode(returnUrl, "UTF-8"),&q ...