【OpenGL】使用FreeType库加载字体并在GL中绘制文字

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

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

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

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

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

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

代码如下:

  1 // OpenGL library
  2 #include <gl/glut.h>
  3 
  4 // Std misc
  5 #include <map>
  6 #include <vector>
  7 
  8 // FreeType library
  9 #include <ft2build.h>
 10 #include FT_FREETYPE_H
 11 #include FT_BITMAP_H
 12 #include FT_OUTLINE_H
 13 
 14 
 15 #ifdef CreateFont
 16 #undef CreateFont
 17 #endif
 18 
 19 typedef unsigned char byte;
 20 
 21 class CFontManager
 22 {
 23 public:
 24     CFontManager();
 25     ~CFontManager();
 26 
 27     bool initialize(void);
 28     void release(void);
 29     int createFont(const char *filename, int face, int tall, bool bold, bool italic, bool antialias);
 30     bool getCharInfo(int font_index, int code, int *wide, int *tall, int *horiBearingX, int *horiBearingY, int *horiAdvance, GLuint *texture, float coords[]);
 31     int getFontTall(int font_index);
 32 
 33 private:
 34     struct glyphMetrics
 35     {
 36         int    width;
 37         int    height;
 38         int    horiBearingX;
 39         int    horiBearingY;
 40         int    horiAdvance;
 41         //int    vertBearingX;
 42         //int    vertBearingY;
 43         //int    vertAdvance;
 44     };
 45 
 46     class CFont
 47     {
 48     public:
 49         CFont();
 50         ~CFont();
 51 
 52         bool create(FT_Library library, const char *filename, FT_Long face_index, int tall, bool bold, bool italic, bool antialias);
 53         void release(void);
 54         bool getCharInfo(int code, glyphMetrics *metrics, GLuint *texture, float coords[]);
 55         int getFontTall(void);
 56 
 57     private:
 58         bool loadChar(int code, glyphMetrics *metrics);
 59 
 60         class CChar
 61         {
 62         public:
 63             void setInfo(glyphMetrics *metrics);
 64             void getInfo(glyphMetrics *metrics, GLuint *texture, float coords[]);
 65 
 66         public:
 67             int                m_code;
 68             GLuint            m_texture;
 69             float            m_coords[4];    // left top right bottom
 70 
 71         private:
 72             glyphMetrics    m_metrics;
 73         };
 74 
 75         class CPage
 76         {
 77         public:
 78             CPage();
 79             ~CPage();
 80 
 81             bool append(int wide, int tall, byte *rgba, float coords[]);
 82             GLuint getTexture(void);
 83 
 84         private:
 85             GLuint    m_texture;
 86             int        m_wide;
 87             int        m_tall;
 88             int        m_posx;
 89             int        m_posy;
 90             int        m_maxCharTall;
 91         };
 92 
 93         typedef std::map<int, CChar *> TCharMap;
 94 
 95         FT_Library                m_library;
 96         FT_Face                    m_face;
 97         bool                    m_antialias;
 98         bool                    m_bold;
 99         int                        m_tall;
100         int                        m_rgbaSize;
101         GLubyte                    *m_rgba;
102         TCharMap                m_chars;
103         std::vector<CPage *>    m_pages;
104     };
105 
106     FT_Library                m_library;
107     std::vector<CFont *>    m_fonts;
108 };
109 
110 
111 
112 //------------------------------------------------------------
113 // CFont
114 //------------------------------------------------------------
115 CFontManager::CFont::CFont()
116 {
117     m_face = NULL;
118     m_rgba = NULL;
119     m_antialias = false;
120     m_bold = false;
121     m_tall = 0;
122 }
123 
124 CFontManager::CFont::~CFont()
125 {
126     release();
127 }
128 
129 bool CFontManager::CFont::create(FT_Library library, const char *filename, FT_Long face_index, int tall, bool bold, bool italic, bool antialias)
130 {
131     FT_Error err;
132 
133     if (tall > 256)
134     {
135         // Bigger than a page size
136         return false;
137     }
138 
139     if ((err = FT_New_Face(library, filename, face_index, &m_face)) != FT_Err_Ok)
140     {
141         printf("FT_New_Face() Error %d\n", err);
142         return false;
143     }
144 
145     if ((err = FT_Set_Pixel_Sizes(m_face, 0, tall)) != FT_Err_Ok)
146     {
147         printf("FT_Set_Pixel_Sizes() Error %d\n", err);
148         return false;
149     }
150 
151     m_rgbaSize = (tall * 2) * tall * 4;
152 
153     m_rgba = new GLubyte[m_rgbaSize];
154 
155     if (m_rgba == NULL)
156     {
157         return false;
158     }
159 
160     m_library = library;
161     m_antialias = antialias;
162     m_bold = bold;
163     m_tall = tall;
164 
165     if (italic)
166     {
167         FT_Matrix m;
168         m.xx = 0x10000L;
169         m.xy = 0.5f * 0x10000L;
170         m.yx = 0;
171         m.yy = 0x10000L;
172         FT_Set_Transform(m_face, &m, NULL);
173     }
174 
175     return true;
176 }
177 
178 void CFontManager::CFont::release(void)
179 {
180     FT_Error err;
181 
182     if (m_face)
183     {
184         if ((err = FT_Done_Face(m_face)) != FT_Err_Ok)
185         {
186             printf("FT_Done_Face() Error %d\n", err);
187         }
188         m_face = NULL;
189     }
190 
191     if (m_rgba)
192     {
193         delete[] m_rgba;
194         m_rgba = NULL;
195     }
196 
197     for (TCharMap::iterator it = m_chars.begin(); it != m_chars.end(); it++)
198     {
199         delete it->second;
200         it->second = NULL;
201     }
202 
203     m_chars.clear();
204 
205     for (size_t i = 0; i < m_pages.size(); i++)
206     {
207         delete m_pages[i];
208         m_pages[i] = NULL;
209     }
210 
211     m_pages.clear();
212 }
213 
214 bool CFontManager::CFont::getCharInfo(int code, glyphMetrics *metrics, GLuint *texture, float coords[])
215 {
216     // fast find it
217     TCharMap::iterator it = m_chars.find(code);
218 
219     if (it != m_chars.end())
220     {
221         it->second->getInfo(metrics, texture, coords);
222         return true;
223     }
224 
225     glyphMetrics gm;
226 
227     if (loadChar(code, &gm) == false)
228     {
229         return false;
230     }
231 
232     CChar *ch = new CChar();
233 
234     ch->m_code = code;
235     ch->setInfo(&gm);
236 
237     for (size_t i = 0; i < m_pages.size(); i++)
238     {
239         CPage *page = m_pages[i];
240 
241         if (page->append(gm.width, gm.height, m_rgba, ch->m_coords))
242         {
243             ch->m_texture = page->getTexture();
244             ch->getInfo(metrics, texture, coords);
245             m_chars.insert(TCharMap::value_type(code, ch));
246             return true;
247         }
248     }
249 
250     CPage *page = new CPage();
251 
252     if (page->append(gm.width, gm.height, m_rgba, ch->m_coords))
253     {
254         ch->m_texture = page->getTexture();
255         ch->getInfo(metrics, texture, coords);
256         m_chars.insert(TCharMap::value_type(code, ch));
257         m_pages.push_back(page);
258         return true;
259     }
260 
261     delete ch;
262     delete page;
263 
264     return false;
265 }
266 
267 int CFontManager::CFont::getFontTall(void)
268 {
269     return m_tall;
270 }
271 
272 // bitmap.width  位图宽度
273 // bitmap.rows   位图行数(高度)
274 // bitmap.pitch  位图一行占用的字节数
275 
276 //MONO模式每1个像素仅用1bit保存,只有黑和白。
277 //1个byte可以保存8个像素,1个int可以保存8*4个像素。
278 void ConvertMONOToRGBA(FT_Bitmap *source, GLubyte *rgba)
279 {
280     GLubyte *s = source->buffer;
281     GLubyte *t = rgba;
282 
283     for (GLuint y = source->rows; y > 0; y--)
284     {
285         GLubyte *ss = s;
286         GLubyte *tt = t;
287 
288         for (GLuint x = source->width >> 3; x > 0; x--)
289         {
290             GLuint val = *ss;
291 
292             for (GLuint i = 8; i > 0; i--)
293             {
294                 tt[0] = tt[1] = tt[2] = tt[3] = ( val & (1<<(i-1)) ) ? 0xFF : 0x00;
295                 tt += 4;
296             }
297 
298             ss += 1;
299         }
300 
301         GLuint rem = source->width & 7;
302 
303         if (rem > 0)
304         {
305             GLuint val = *ss;
306 
307             for (GLuint x = rem; x > 0; x--)
308             {
309                 tt[0] = tt[1] = tt[2] = tt[3] = ( val & 0x80 ) ? 0xFF : 0x00;
310                 tt += 4;
311                 val <<= 1;
312             }
313         }
314 
315         s += source->pitch;
316         t += source->width * 4;    //pitch
317     }
318 }
319 
320 //GRAY模式1个像素用1个字节保存。
321 void ConvertGRAYToRGBA(FT_Bitmap *source, GLubyte *rgba)
322 {
323     for (GLuint y = 0; y < source->rows; y++)
324     {
325         for (GLuint x = 0; x < source->width; x++)
326         {
327             GLubyte *s = &source->buffer[(y * source->pitch) + x];
328             GLubyte *t = &rgba[((y * source->pitch) + x) * 4];
329 
330             t[0] = t[1] = t[2] = 0xFF;
331             t[3] = *s;
332         }
333     }
334 }
335 
336 bool CFontManager::CFont::loadChar(int code, glyphMetrics *metrics)
337 {
338     FT_Error err;
339 
340     FT_UInt glyph_index = FT_Get_Char_Index(m_face, (FT_ULong)code);
341 
342     if (glyph_index > 0)
343     {
344         if ((err = FT_Load_Glyph(m_face, glyph_index, FT_LOAD_DEFAULT)) == FT_Err_Ok)
345         {
346             FT_GlyphSlot glyph = m_face->glyph;
347 
348             FT_Render_Mode render_mode = m_antialias ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO;
349 
350             if (m_antialias && m_bold)
351             {
352                 if ((err = FT_Outline_EmboldenXY(&glyph->outline, 60, 60)) != FT_Err_Ok)
353                 {
354                     printf("FT_Outline_EmboldenXY() Error %d\n", err);
355                 }
356             }
357 
358             if ((err = FT_Render_Glyph(glyph, render_mode)) == FT_Err_Ok)
359             {
360                 FT_Bitmap *bitmap = &glyph->bitmap;
361 
362                 switch (bitmap->pixel_mode)
363                 {
364                 case FT_PIXEL_MODE_MONO:
365                     {
366                         if (!m_antialias && m_bold)
367                         {
368                             if ((err = FT_Bitmap_Embolden(m_library, bitmap, 60, 0)) != FT_Err_Ok)
369                             {
370                                 printf("FT_Bitmap_Embolden() Error %d\n", err);
371                             }
372                         }
373                         ConvertMONOToRGBA(bitmap, m_rgba);
374                         break;
375                     }
376                 case FT_PIXEL_MODE_GRAY:
377                     {
378                         ConvertGRAYToRGBA(bitmap, m_rgba);
379                         break;
380                     }
381                 default:
382                     {
383                         memset(m_rgba, 0xFF, m_rgbaSize);
384                         break;
385                     }
386                 }
387 
388                 metrics->width = bitmap->width;
389                 metrics->height = bitmap->rows;
390                 metrics->horiBearingX = glyph->bitmap_left;
391                 metrics->horiBearingY = glyph->bitmap_top;
392                 metrics->horiAdvance = glyph->advance.x >> 6;
393 
394                 return true;
395             }
396             else
397             {
398                 printf("FT_Render_Glyph() Error %d\n", err);
399             }
400         }
401         else
402         {
403             printf("FT_Load_Glyph() Error %d\n", err);
404         }
405     }
406 
407     memset(metrics, 0, sizeof(glyphMetrics));
408 
409     return false;
410 }
411 
412 //------------------------------------------------------------
413 // CChar
414 //------------------------------------------------------------
415 void CFontManager::CFont::CChar::setInfo(glyphMetrics *metrics)
416 {
417     memcpy(&m_metrics, metrics, sizeof(glyphMetrics));
418 }
419 
420 void CFontManager::CFont::CChar::getInfo(glyphMetrics *metrics, GLuint *texture, float coords[])
421 {
422     memcpy(metrics, &m_metrics, sizeof(glyphMetrics));
423 
424     *texture = m_texture;
425     memcpy(coords, m_coords, sizeof(float)*4);
426 }
427 
428 //------------------------------------------------------------
429 // CPage
430 //------------------------------------------------------------
431 CFontManager::CFont::CPage::CPage()
432 {
433     m_wide = m_tall = 256;
434     m_posx = m_posy = 0;
435 
436     // In a line, for a max height character
437     m_maxCharTall = 0;
438 
439     glGenTextures(1, &m_texture);    // Using your API here
440     glBindTexture(GL_TEXTURE_2D, m_texture);
441     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_wide, m_tall, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
442     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
443     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
444 }
445 
446 CFontManager::CFont::CPage::~CPage()
447 {
448     // free the texture
449 }
450 
451 bool CFontManager::CFont::CPage::append(int wide, int tall, byte *rgba, float coords[])
452 {
453     if (m_posy + tall > m_tall)
454     {
455         // not enough line space in this page
456         return false;
457     }
458 
459     // If this line is full ...
460     if (m_posx + wide > m_wide)
461     {
462         int newLineY = m_posy + m_maxCharTall;
463 
464         if (newLineY + tall > m_tall)
465         {
466             // No more space for new line in this page, should allocate a new one
467             return false;
468         }
469 
470         // Begin in new line
471         m_posx = 0;
472         m_posy = newLineY;
473         // Reset
474         m_maxCharTall = 0;
475     }
476 
477     glBindTexture(GL_TEXTURE_2D, m_texture);
478     glTexSubImage2D(GL_TEXTURE_2D, 0, m_posx, m_posy, wide, tall, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
479 
480     coords[0] = m_posx / (float)m_wide;                // left
481     coords[1] = m_posy / (float)m_tall;                // top
482     coords[2] = (m_posx + wide) / (float)m_wide;    // right
483     coords[3] = (m_posy + tall) / (float)m_tall;    // bottom
484 
485     m_posx += wide;
486 
487     if (tall > m_maxCharTall)
488     {
489         m_maxCharTall = tall;
490     }
491 
492     return true;
493 }
494 
495 GLuint CFontManager::CFont::CPage::getTexture(void)
496 {
497     return m_texture;
498 }
499 
500 //------------------------------------------------------------
501 // CFontManager
502 //------------------------------------------------------------
503 CFontManager::CFontManager()
504 {
505     m_library = NULL;
506 }
507 
508 CFontManager::~CFontManager()
509 {
510     release();
511 }
512 
513 bool CFontManager::initialize(void)
514 {
515     FT_Error err;
516 
517     if ((err = FT_Init_FreeType(&m_library)) != FT_Err_Ok)
518     {
519         printf("FT_Init_FreeType() Error %d\n", err);
520         return false;
521     }
522 
523     return true;
524 }
525 
526 void CFontManager::release(void)
527 {
528     FT_Error err;
529 
530     for (size_t i = 0; i < m_fonts.size(); i++)
531     {
532         delete m_fonts[i];
533         m_fonts[i] = NULL;
534     }
535 
536     m_fonts.clear();
537 
538     if ((err = FT_Done_FreeType(m_library)) != FT_Err_Ok)
539     {
540         printf("FT_Done_FreeType() Error %d\n");
541     }
542 }
543 
544 int CFontManager::createFont(const char *filename, int face, int tall, bool bold, bool italic, bool antialias)
545 {
546     CFont *font = new CFont();
547 
548     if (font->create(m_library, filename, face, tall, bold, italic, antialias) != true)
549     {
550         delete font;
551         return 0;
552     }
553 
554     m_fonts.push_back(font);
555 
556     return m_fonts.size();
557 }
558 
559 #define CONVERT_FONT_INDEX(x) (((x) < 1 || (x) > (int)m_fonts.size()) ? -1 : (x) - 1)
560 
561 bool CFontManager::getCharInfo(int font_index, int code, int *wide, int *tall, int *horiBearingX, int *horiBearingY, int *horiAdvance, GLuint *texture, float coords[])
562 {
563     int i = CONVERT_FONT_INDEX(font_index);
564 
565     if (i == -1)
566     {
567         return false;
568     }
569 
570     CFont *font = m_fonts[i];
571 
572     glyphMetrics metrics;
573 
574     if (font->getCharInfo(code, &metrics, texture, coords) == false)
575     {
576         return false;
577     }
578 
579     *wide = metrics.width;
580     *tall = metrics.height;
581     *horiBearingX = metrics.horiBearingX;
582     *horiBearingY = metrics.horiBearingY;
583     *horiAdvance = metrics.horiAdvance;
584 
585     return true;
586 }
587 
588 int CFontManager::getFontTall(int font_index)
589 {
590     int i = CONVERT_FONT_INDEX(font_index);
591 
592     if (i == -1)
593     {
594         return false;
595     }
596 
597     CFont *font = m_fonts[i];
598 
599     return font->getFontTall();
600 }
601 
602 CFontManager g_FontManager;
603 
604 
605 int char_font;
606 
607 
608 void init(void)
609 {
610     glClearColor(0.0, 0.0, 0.0, 0.0);
611 
612     glEnable(GL_BLEND);
613     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
614 
615     g_FontManager.initialize();
616 
617     char_font = g_FontManager.createFont("FZDH_GBK.TTF", 0, 32, false, false, true);
618 
619     if (char_font == 0)
620     {
621         printf("createFont failed\n");
622     }
623 }
624 
625 wchar_t ciphertext[] =
626 {
627     L"第一我做的是人气,不是他妈的信仰不是他妈的信任\n"
628     L"我想信任来着,一群狼心的人就知道玩游戏。我投入的你们给我一分钱回报了么。\n"
629     L"不懂少在那里嫌弃我,白花花的银子砸出来的东西免费给你们玩你们还这bb那bb\n"
630     L"不管有没有人骂我我只在乎有没有人看我 懂么?\n"
631     L"第二我你们尽管骂我\n"
632     L"第三我的事情是我的自由\n"
633     L"第四我一直很讨厌csol\n"
634     L"第五世界上最失败的一次行动就是创建百度贴吧\n"
635     L"第六 一群二逼骂个毛线管我屁事\n"
636     L"http://tieba.baidu.com/p/3144980358"
637 };
638 
639 //#define DRAW_PAGE
640 
641 void draw_string(int x, int y, int font, wchar_t *string)
642 {
643     if (!font)
644         return;
645 
646     int tall = g_FontManager.getFontTall(font);
647 
648     int dx = x;
649     int dy = y;
650 
651     GLuint sglt = 0;
652 
653     while (*string)
654     {
655         if (*string == L'\n')
656         {
657             string++;
658             dx = x;
659             dy += tall + 2;    //row spacing
660             continue;
661         }
662 
663         int cw, ct, bx, by, av;
664         GLuint glt;
665         float crd[4];
666 
667         if (!g_FontManager.getCharInfo(font, *string, &cw, &ct, &bx, &by, &av, &glt, crd))
668         {
669             string++;
670             continue;
671         }
672 
673         //大多数情况下多个字符都在同一个纹理中,避免频繁绑定纹理,可以提高效率
674         if (glt != sglt)
675         {
676             glBindTexture(GL_TEXTURE_2D, glt);
677             sglt = glt;
678         }
679 
680         int px = dx + bx;
681         int py = dy - by;
682 
683         glBegin(GL_QUADS);
684             glTexCoord2f(crd[0], crd[1]);
685             glVertex3f(px, py, 0.0f);
686             glTexCoord2f(crd[2], crd[1]);
687             glVertex3f(px + cw, py, 0.0f);
688             glTexCoord2f(crd[2], crd[3]);
689             glVertex3f(px + cw, py + ct, 0.0f);
690             glTexCoord2f(crd[0], crd[3]);
691             glVertex3f(px, py + ct, 0.0f);
692         glEnd();
693 
694         dx += av;
695 
696         string++;
697     }
698 }
699 
700 void draw_page_texture(int x, int y, GLuint glt)
701 {
702     if (!glt)
703     {
704         glDisable(GL_TEXTURE_2D);
705         glColor4f(0.2, 0.2, 0.2, 1.0);
706     }
707     else
708     {
709         glEnable(GL_TEXTURE_2D);
710         glBindTexture(GL_TEXTURE_2D, glt);
711         glColor4f(1.0, 1.0, 1.0, 1.0);
712     }
713 
714     int w = 256;
715     int t = 256;
716 
717     glBegin(GL_QUADS);
718         glTexCoord2f(0.0, 0.0);
719         glVertex3f(x, y, 0.0f);
720         glTexCoord2f(1.0, 0.0);
721         glVertex3f(x + w, y, 0.0f);
722         glTexCoord2f(1.0, 1.0);
723         glVertex3f(x + w, y + t, 0.0f);
724         glTexCoord2f(0.0, 1.0);
725         glVertex3f(x, y + t, 0.0f);
726     glEnd();
727 }
728 
729 void display(void)
730 {
731     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
732 
733     glEnable(GL_TEXTURE_2D);
734     glColor4f(1.0, 1.0, 1.0, 1.0);
735     draw_string(10, 30, char_font, ciphertext);
736 
737     draw_page_texture(10, 350, 0);    //background
738     draw_page_texture(10, 350, 1);    //page1 texture
739 
740     draw_page_texture(276, 350, 0);    //background
741     draw_page_texture(276, 350, 2);    //page2 texture
742 
743     glutSwapBuffers();
744     glutPostRedisplay();
745 }
746 
747 void reshape(int width, int height)
748 {
749     glViewport(0, 0, width, height);
750 
751     glMatrixMode(GL_PROJECTION);
752     glLoadIdentity();
753     gluOrtho2D(0, width, height, 0);
754     glMatrixMode(GL_MODELVIEW);
755 }
756 
757 void keyboard(unsigned char key, int x, int y)
758 {
759 }
760 
761 void main(int argc, char **argv)
762 {
763     glutInitWindowPosition(200, 200);
764     glutInitWindowSize(1200, 680);
765     glutInit(&argc, argv);
766     glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
767     glutCreateWindow("FreeType OpenGL");
768     init();
769     glutDisplayFunc(display);
770     glutReshapeFunc(reshape);
771     glutKeyboardFunc(keyboard);
772     glutMainLoop();
773 }

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

 

运行效果:

posted @ 2017-07-31 00:25  Akatsuki-  阅读(8176)  评论(0编辑  收藏  举报