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. windows phone 手机解锁失败问题

    1.使用 VS 2015  自带的  Windows Phone Developer Registration (8.1) 工具, 解锁手机. 总是提示 日期和时间错误. 解决办法,  有2个 1.打 ...

  2. dotnet core 开发中遇到的问题

    1.发布的时候把视图cshtml文件也编译为dll了,如何控制不编译视图? 编辑功能文件(xx.csproj),加入一个选项: <PropertyGroup> <TargetFram ...

  3. 在tomcat5中发布项目时,用IP地址+端口不能访问项目,而用localhost加端口时可以访问成功

    最近在开发项目中,遇到的一个问题是: 在 tomcat中发布一个web项目,但是发布成功后,只能用http://localhost:8080/fm访问项目,不能用 http://127.0.0.1:8 ...

  4. java 多路分发

    1.概念 一个函数处理多种类型,其实和多态差不多. 但是要处理两种或者多种类型的数据时,就需要判断每种类型以及每种类型所对应的处理.(PS:我只是在走别人的老路,网上一搜这种概念,博客一大堆,我不知道 ...

  5. JMeter怎样测试WebSocket

    一.安装WebSocket取样器 1.从JMeter插件管理器官网下载: https://jmeter-plugins.org/ 把这6个jar包放到C:\JMeter\apache-jmeter-3 ...

  6. Django——POST请求及Action触发事件

    添加网页login,将类型置为post,并添加action page,也就是之前写好的页面 添加page网页的views函数,要求获取post指令,如果username及password均正确则跳转到 ...

  7. 05-Docker架构详解

    Docker 的核心组件包括: Docker 客户端 - Client Docker 服务器 - Docker daemon Docker 镜像 - Image Registry Docker 容器 ...

  8. Linux 安装Redis<单机版>(使用Mac远程访问)

    阅读本文需要先阅读安装Redis<准备> redis依赖 yum install gcc-c++ 解压 cd redis压缩包所在目录 tar -xvf redis-4.0.10.tar. ...

  9. 设置PNG图片DPI 信息,保存为PDF(使用Magick),与OpenCV转换

    目录 任务描述 解决方案 Magick++ Talk is cheap, show me the code. 与 Opencv 配合 相关链接 任务描述 我有这样一个需求,读取一张格式为PNG 或者 ...

  10. nginx 根据get参数重定向(根据电视访问的mac地址传递的值,来重定向访问别的url地址,这样就可以进行单台的测试环境。。)

    背景是这样的: 公司要做所有客户端的迁移到别的云平台,但又担心会有问题,所以考虑分批次迁移过去,这样就需要迁移部分用户,因为客户端刷但都是统一但rom包,不能轻易发生改动,所以决定用重定向方式将部分客 ...