NeHe Tutorials Народный учебник по OpenGL
Урок 43. OpenGL

FreeType Fonts in OpenGL

 

Итак, этот небольшой урок покажет вам, как использовать с OpenGL библиотеку шрифтов FreeType. Используя библиотеку FreeType мы можем создавать текст со сглаживанием краев, который выглядит намного лучше, чем текст сделанный с использованием растровых шрифтов - также нам будет легче поворачивать его и работать с функциями выбора объектов.

 

Мотивация

 

Сегодня мы будем печатать текст, используя одновременно и растровые шрифты WGL и шрифты сделанные с помощью FreeType (оба Arial Black Italic).

 

Основная проблема состоит в том, что используемые растровые шрифты в OpenGL по определению бинарные изображения. Смысл этого в том, что растровые изображения в OpenGL имеют только один бит на один пиксель. Если вы будете увеличивать текст, используя WGL, результат будет похож на этот:

 

 

Поскольку растры бинарные, в них нет оттенков серого цвета, они показывают только текст.

 

К счастью очень легко сделать прилично смотрящиеся шрифты, используя GNU FreeType library. Кстати FreeType использовали в Blizzard для вывода шрифтов в их играх, так что вы знаете всю прелесть этой библиотеки.

 

Вот текст, который я создал при помощи FreeType Library.

 

 

Вы можете видеть, что здесь есть оттенки серого цвета на углах текста; это типичный признак шрифта со сглаживанием, оттенки серого цвета делают текст гладким в независимости от расстояния.

 

Создание программы.

 

Первый шаг, который мы должны сделать, это получить копию GNUFreeType library. Идите по адресу  http://gnuwin32.sourceforge.net/packages/freetype.htm и загрузите бинарные и файлы для разработчиков. Когда вы установите ее, прочитайте лицензионное соглашение, где написано, что если вы используете FreeType в вашей программе, вы должны отметить разработчиков, где-нибудь в вашей документации.

 

Сейчас нам нужен MSVC, чтобы использовать FreeType. Итак, создайте новый проект как показано в первом уроке, но когда нажмете Project->Setting->Link будьте, уверены, что вы добавили libfreetype.lib в Object Modules /libraries вместе c opengl32.lib, glu32.lib и glaux.lib (если нужно).

 

Следующее что нам нужно это добавить директорию FreeType library в Tools->Options->Directories. Под "Show Directories For" выберите "Include Files", затем два раза кликните на пустой линии вверху листа каталогов, после вашего щелчка по кнопке "..." появится окно, в котором вы можете выбрать директорию. В этом случае добавьте C:\PROGRAM FILES\GNUWIN32\INCLUDE\FREETYPE2 и C:\PROGRAM FILES\GNUWIN32\INCLUDE. В список используемых директорий. Теперь под "Show Directories For" выберите "Library Files", и добавьте C:\PROGRAM FILES\GNUWIN32\LIB.

 

В этой точке мы должны быть готовы к компиляции программ используя FreeType, но они не захотят запускать пока не получат доступа к freetype-6.dll. Эта библиотека лежим в каталоге GNUWIN32\BIN, и если вы запихнете ее, куда-нибудь где все ваши программы смогут видеть её (Program Files\Microsoft Visual Studio\VC98\Bin хороший вариант), вы сможете запускать программы использующие FreeType.  Но помните то, что если вы будете распространять программу, которая использует FreeType, вы должны будете также распространять копии этой DLL с ней.

 

Наконец то, теперь мы можем начать писать код. Я решил работать с уроком 13, поэтому скачайте пример к этому уроку, если у вас нет его. Скопируйте lesson13.cpp в директорию вашего проекта и добавьте файл в проект.

 

Сейчас добавьте и создайте два новых файла:"freetype.cpp" и "freetype.h". Мы поместим все наши функции, которые специфичны для FreeType в эти файлы, и тогда немного изменим lesson13.cpp , чтобы показать написанные функции. Когда мы закончим, у нас будет созданное очень простое приложение OpenGL FreeType library, которое теоретически может быть использовано в любом OpenGL проекте.

 

Начнём с freetype.h.

 

Обычно сначала мы подключаем заголовочные файлы FreeType и OpenGL. Также мы подключим некоторые части Standart Template Library, включая STLовские классы обработчики прерываний, которые сделают отладку приложения проще.

 

#ifndef FREE_NEHE_H

#define FREE_NEHE_H

 

// FreeType заголовочные файлы

#include <ft2build.h>

#include <freetype/freetype.h>

#include <freetype/ftglyph.h>

#include <freetype/ftoutln.h>

#include <freetype/fttrigon.h>

 

// OpenGL заголовочные файлы

#include <windows.h>                    // (GL'у это нужно)

#include <GL/gl.h>

#include <GL/glu.h>

 

// Некоторые заголовки STL

#include <vector>

#include <string>

 

// Использование STL библиотеки исключений увеличивает шансы

// на то, что другой человек будет корректно отлавливать посылаемые нами исключения.

#include <stdexcept>

 

// MSVC будет выплевывать все сорта бесполезных предупреждений, если

// вы создаете векторы строк, эта pragma отключает их

#pragma warning(disable: 4786)

 

Мы поместим информацию нужную каждому шрифту в структуру (это облегчит управление несколькими шрифтами). Как мы видели в уроке 13, при создании шрифта с WGL генерировался набор последовательных списков отображения. Это изящно, потому что мы можем вызывать glCallLists чтобы напечать текст, при помощи лишь одной команды. Когда мы создаем шрифт, мы делаем аналогично, что обозначает то, что list_base поле будет хранить первые 128 списков отображения. Поскольку нам надо использовать текстуры для прорисовки текста, нам также нужно хранилище для 128 связанных текстур. Последний кусок информации это высота в пикселях шрифта, который будет создан (это сделает возможным обработку символов перевода строк в нашей функции печати).

 

//Помещая все в пространство имен, мы можем, не беспокоится о конфликте с чужим

// кодом такой распространенной функцией как print

namespace freetype {

 

// В этом пространстве, даем себе возможность написать только "vector" вместо "std::vector"

using std::vector;

 

// тоже самое для строки.

using std::string;

 

// Здесь мы храним всю информацию о FreeType шрифте, который мы хотим создать

struct font_data {

  float h;                    // Высота

  GLuint * textures;          // Идентификатор

  GLuint list_base;           // Содержит указатель на список отображения

 

  // Функция инициализации создаст шрифт с

  // высотой h из файла fname

  void init(const char * fname, unsigned int h);

 

  // Освобождаем ресурсы связанные со шрифтом

  void clean();

};

 

 

Последнее что нам нужно это прототип функции печати

 

// Главная функция библиотеки -  она будет печатать

// текст в окне по координатам X,Y используя Font ft_font.

// Текущая матрица вида модели также будет применена к тексту

 

void print(const font_data &ft_font, float x, float y, const char *fmt, ...);

 

}                        // Закрываем пространство имен

 

#endif

 

 

А вот и конец заголовочного файла! Время редактировать freetype.cpp.

 

// Включаем заголовочные файлы

#include "freetype.h"

 

namespace freetype {

 

Мы используем текстуры, чтобы отобразить каждый символ в нашем шрифте. OpenGL текстуры должны иметь размеры, которые являются степенью двойки, так что мы должны преобразовать наши растры шрифтов сделанные FreeType в размер такого типа. Для этого нам нужна следующая функция:

 

// Эта функция возвращает число в степени два, большее, чем число a

inline int next_p2 (int a )

{

  int rval=1;

  // rval<<=1 это лучше чем rval*=2;

  while(rval<a) rval<<=1;

  return rval;

}

 

Следующее что нам нужно сделать это функцию make_dlist, это действительно самая главная функция. Она использует FT_Face, который является объектом, используемым FreeType для сохранения информации о шрифте, и создания списка отображения, который отвечает за каждый символ.

 

// Создает список отображения на базе данного символа

void make_dlist ( FT_Face face, char ch, GLuint list_base, GLuint * tex_base ) {

 

  // Первая вещь, которую нам надо сделать, это вывести наш символ

  // в растр. Это делается набором команд FreeType

 

  // Загрузить глифы для каждого символа.

 

  if(FT_Load_Glyph( face, FT_Get_Char_Index( face, ch ), FT_LOAD_DEFAULT ))

    throw std::runtime_error("FT_Load_Glyph failed");

 

  // Поместить глиф в объект.

  FT_Glyph glyph;

  if(FT_Get_Glyph( face->glyph, &glyph ))

    throw std::runtime_error("FT_Get_Glyph failed");

 

  // Конвертировать глиф в растр.

  FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 );

  FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;

 

  // С помощью этой ссылки, получаем легкий доступ до растра.

  FT_Bitmap& bitmap=bitmap_glyph->bitmap;

 

Примечание:

"Символы - объекты, назначенные по стандарту Unicode, которые представляют самые маленькие семантические модули языка. Глифы - определенные формы, которыми символы могут быть представлены. Один символ может прописываться как несколько глифов: нижний регистр "a", капительная буква "a" и специальный символ "a" - это три отдельных глифа. Один глиф может также представлять многие символы, как в случае "ffi" связи, который соответствует последовательности трех символов: f, f и i. Для любого символа имеется заданный по умолчанию глиф и позиционированные данные."

 

Теперь, когда у нас есть растр созданный с помощью FreeType, нам нужно его наложить на текстуру OpenGL. Важно помнить, что пока OpenGL использует термин "растр" это подразумевает двоичные рисунки, в FreeType растры сохраняют 8 битов информации на пиксель, так что FreeType'ские растры смогу сохранять оттенки серого, которые нам нужны для сглаживания.

 

  // Используем нашу вспомогательную функцию для вычисления ширины и высоты

  // текстуры для нашего растра.

  int width = next_p2( bitmap.width );

  int height = next_p2( bitmap.rows );

 

  // Выделим память для данных текстуры.

  GLubyte* expanded_data = new GLubyte[ 2 * width * height];

 

  // Поместим данные в расширенный растр.

  // Отмечу, что использован двухканальный растр (Один для

  // канала яркости и один для альфа), но мы будем назначать

  // обоим каналам одно и тоже значение, которое мы

  // получим из растра FreeType.

  // Мы используем оператор ?: для того чтобы поместить 0 в зону вне растра FreeType.

  for(int j=0; j <height;j++) {

    for(int i=0; i < width; i++){

      expanded_data[2*(i+j*width)]= expanded_data[2*(i+j*width)+1] =

        (i>=bitmap.width || j>=bitmap.rows) ?

        0 : bitmap.buffer[i + bitmap.width*j];

    }

  }

 

Когда заполнение закончится, мы сможем создать текстуру OpenGL. Мы включим альфа канал, таким образом, черные куски растра будут прозрачными, а остальные слегка прозрачные (что должно сделать вывод шрифта правильным на любом фоне).

 

  // Теперь мы только устанавливаем параметры

  glBindTexture( GL_TEXTURE_2D, tex_base[ch]);

  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

 

  // Здесь мы создаем текстуру

  // Помните, что используем GL_LUMINANCE_ALPHA, чтобы было два альфа канала данных

 

  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,

    GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data );

 

  // После создания текстуры, мы больше не нуждаемся в промежуточных данных.

  delete [] expanded_data;

 

Мы используем наложение текстуры на четырехугольники для прорисовки текста. Это обозначает то, что будет легко поворачивать и увеличивать/приближать текст, и мы также создадим шрифты с текущим цветом OpenGL (ни один из которых не будет использоваться в растрах).

 

  // Создать список отображения

  glNewList(list_base+ch,GL_COMPILE);

 

  glBindTexture(GL_TEXTURE_2D,tex_base[ch]);

 

  // Вначале мы сдвинем символ вправо на расстояние между ним и символам до него.

  glTranslatef(bitmap_glyph->left,0,0);

 

  // Сдвинем вниз в том случае, если растр уходит вниз строки.

  // Это истинно только для символов, таких как 'g' или 'y'.

  glPushMatrix();

  glTranslatef(0,bitmap_glyph->top-bitmap.rows,0);

 

  // Вычислим какая часть нашей текстуры будет заполнена пустым пространством.

  // Мы рисуем только ту часть текстуры, в которой находится символ, и сохраняем

  // информацию в переменных x и y, затем, когда мы рисуем четырехугольник,

  // мы будем только ссылаться на ту часть текстуры, в которой непосредственно

  // содержится символ.

  float   x=(float)bitmap.width / (float)width,

  y=(float)bitmap.rows / (float)height;

 

  // Рисуем текстурированный четырехугольник.

  glBegin(GL_QUADS);

  glTexCoord2d(0,0); glVertex2f(0,bitmap.rows);

  glTexCoord2d(0,y); glVertex2f(0,0);

  glTexCoord2d(x,y); glVertex2f(bitmap.width,0);

  glTexCoord2d(x,0); glVertex2f(bitmap.width,bitmap.rows);

  glEnd();

  glPopMatrix();

  glTranslatef(face->glyph->advance.x >> 6 ,0,0);

 

  // Увеличиваем позицию растра, как если бы это был растровый шрифт.

  // (Необходимо только, если вы хотите вычислить длину текста)

  // glBitmap(0,0,0,0,face->glyph->advance.x >> 6,0,NULL);

 

  // Завершим создание списка отображения

  glEndList();

}

 

Следующая функция, которую мы создадим, будет использовать make_dlist, для создания наборов списков отображения, соответствующих данному файлу шрифта и высоте пикселя.

 

FreeType использует шрифты TrueType, так что неплохо бы найти какой файл шрифта TrueType. Шрифты TrueType очень распространены, так что вы можете найти множество сайтов, где вы можете загрузить себе разные шрифты TrueType. Windows 98 используется такой тип для почти всех шрифтов, так что если вы сможете найти старый компьютер, использующий эту ОС, вы можете получить все стандартные шрифты формата TrueType в каталоге windows/fonts.

 

void font_data::init(const char * fname, unsigned int h) {

  // Выделим память для идентификаторов текстуры.

  textures = new GLuint[128];

 

  this->h=h;

 

  // Инициализация библиотеки FreeType.

  FT_Library library;

  if (FT_Init_FreeType( &library ))

    throw std::runtime_error("FT_Init_FreeType failed");

 

  // Объект для хранения шрифта.

  FT_Face face;

 

  // Загрузим шрифт из файла. Если файла шрифта не существует или шрифт битый,

  // то программа может умереть.

  if (FT_New_Face( library, fname, 0, &face ))

    throw std::runtime_error("FT_New_Face failed (there is probably a problem

                              with your font file)");

 

  // По некоторым причинам FreeType измеряет размер шрифта в терминах 1/64 пикселя.

  // Таким образом, для того чтобы сделать шрифт выстой h пикселей, мы запрашиваем размер h*64.

  // (h << 6 тоже самое что и h*64)

  FT_Set_Char_Size( face, h << 6, h << 6, 96, 96);

 

  // Здесь попросим OpenGL, чтобы он выделил память для

  // всех текстур и списков отображения, которые нам нужны. 

  list_base=glGenLists(128);

  glGenTextures( 128, textures );

 

  // Создаем списки отображения шрифтов.

  for(unsigned char i=0;i<128;i++)

    make_dlist(face,i,list_base,textures);

 

  // Уничтожим шрифт.

  FT_Done_Face(face);

 

  // Не нужна и библиотека.

  FT_Done_FreeType(library);

}

 

Теперь нам нужна функция для удаления текстур и списков отображения связанных со шрифтом.

 

void font_data::clean() {

  glDeleteLists(list_base,128);

  glDeleteTextures(128,textures);

  delete [] textures;

}

 

Ещё у нас осталось две функции, которые нам надо сделать, перед тем как мы сделаем функцию отображения текста.

 

В OpenGL есть две очень удобных функции, glGet возвращает размеры окна, и glPush/PopAttrib используется для сохранения состояния режимов OpenGL. Если вы незнакомы с этими функциями, вам лучше посмотреть их описание.

 

// Простая функция, в которой сохраняется матрица проекции,

// затем делаем мировые координаты идентичными с координатами окна.

inline void pushScreenCoordinateMatrix() {

  glPushAttrib(GL_TRANSFORM_BIT);

  GLint   viewport[4];

  glGetIntegerv(GL_VIEWPORT, viewport);

  glMatrixMode(GL_PROJECTION);

  glPushMatrix();

  glLoadIdentity();

  gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3]);

  glPopAttrib();

}

 

// Восстановить координаты матрицы проекции.

inline void pop_projection_matrix() {

  glPushAttrib(GL_TRANSFORM_BIT);

  glMatrixMode(GL_PROJECTION);

  glPopMatrix();

  glPopAttrib();

}

 

Наша функция печати очень похожа на такую же из Урока 13, но есть несколько важных различий. Мы разрешаем флаги, которые отражают тот факт, что, мы используем двухканальные текстуры, а не растр. Нам также нужно выполнить немного дополнительной работы для обработки символов перевода на новую строку. Поскольку мы такие добрые самаритяне, мы также заботимся о сохранении и восстановлении внутренних стеков OpenGL.

 

// Модифицируем функцию glPrint.

void print(const font_data &ft_font, float x, float y, const char *fmt, ...)  {

       

  // Мы хотим систему координат, в которой расстояние измеряется в пикселях.

  pushScreenCoordinateMatrix();                                   

       

  GLuint font=ft_font.list_base;

  // Сделаем высоту немного больше, что бы оставить место между линиями.

  float h=ft_font.h/.63f;                                                

  char  text[256];            // Сохраним нашу строку

  va_list  ap;                // Указатель на лист аргументов

 

  if (fmt == NULL)            // Если это не текст

    *text=0;                  // Тогда ничего не делать

  else {

    va_start(ap, fmt);        // Разбор строки на переменные

    vsprintf(text, fmt, ap);  // И конвертировать символы в числа

    va_end(ap);               // Результат сохранить в текст

  }

 

  // Разделим текст на строки.

  const char *start_line=text;

  vector<string> lines;

  for(const char *c=text;*c;c++) {

    if(*c=='\n') {

      string line;

      for(const char *n=start_line;n<c;n++) line.append(1,*n);

      lines.push_back(line);

      start_line=c+1;

    }

  }

  if(start_line) {

    string line;

    for(const char *n=start_line;n<c;n++) line.append(1,*n);

    lines.push_back(line);

  }

 

  glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT  | GL_ENABLE_BIT | GL_TRANSFORM_BIT);

  glMatrixMode(GL_MODELVIEW);

  glDisable(GL_LIGHTING);

  glEnable(GL_TEXTURE_2D);

  glDisable(GL_DEPTH_TEST);

  glEnable(GL_BLEND);

  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);     

 

  glListBase(font);

 

Поскольку мы используем текстурированные четырехугольники, любые преобразования, которые мы применяем к матрице вида модели до того как вызвать glCallLists будут влиять и на текст. Это значит, что есть возможность крутить или масштабировать наш текст (другое преимущество использования WGL растров). Самый хороший способ использовать это преимущество было бы оставить текущую матрицу вида в покое, что допускает любые преобразования, перед тем как функция print выведет текст. Но поскольку мы используем матрицу вида модели, чтобы установить позицию текста, это не сработает. Лучшим для нас было бы сохранить копию матрицу, и применить ее между glTranslate и glCallLists. Это просто сделать, но поскольку мы рисуем текст используя специальную матрицу проекции, то эффект от воздействия матрицы вида модели будет отличаться от того, что мы ожидаем увидеть в масштабе пикселей. Мы бы могли обойти эту проблему, не сбрасывая матрицу проекции в функции печати. Это вероятно хорошая идея в некоторых случаях, но если вы попробуете это, то удостоверьтесь, что вы масштабируете шрифты правильным размером (например, размер 32x32, соответствует размеру 0.01x0.01).

 

  float modelview_matrix[16];    

  glGetFloatv(GL_MODELVIEW_MATRIX, modelview_matrix);

 

  // На каждой строчке мы сбрасываем матрицу вида модели

  // Поэтому строки будут начинаться с правильной позиции.

  // Отмечу, что сброс надо делать до сдвига вниз на h, поскольку затем каждый

  // символ рисуется и это модифицирует текущую матрицу, поэтому следующий

  // символ будет нарисован прямо после него.

  for(int i=0;i<lines.size();i++) {

    glPushMatrix();

    glLoadIdentity();

    glTranslatef(x,y-h*i,0);

    glMultMatrixf(modelview_matrix);

 

    // Уберите комментарии у следующего оператора и трех строк после вызова glCallLists,

    // если хотите знать длину строки, но не забудьте убрать комментарий

    // у glBitmap в функции make_dist.

    // glRasterPos2f(0,0);

    glCallLists(lines[i].length(), GL_UNSIGNED_BYTE, lines[i].c_str());

    // float rpos[4];

    // glGetFloatv(GL_CURRENT_RASTER_POSITION ,rpos);

    // float len=x-rpos[0]; (Надеюсь, что нет вращения)

 

    glPopMatrix();

  }

 

  glPopAttrib();         

 

  pop_projection_matrix();

}

 

}                        // Закроем пространство имени

 

Библиотека закончена. Откройте lesson13.cpp и мы сделаем некоторые незначительные изменения, чтобы показать все функции, которые мы только что написали.

 

Добавьте FreeType.h за другими заголовочными файлами:

 

#include "freetype.h" // Заголовочный файл нашей маленькой библиотеки.

 

И пока мы здесь, давайте создадим глобальную переменную font_data.

 

// Сохраним всю информацию о нашем шрифте.

freetype::font_data our_font;

 

Сейчас нам нужно заняться созданием и удалением нашего шрифта. Так что добавьте следующее в конец InitGL.

 

our_font.init("Test.ttf", 16); // Создать шрифт FreeType

 

Добавьте это в начало KillGL Window, чтобы уничтожить шрифт, когда мы закончим.

 

our_font.clean();

 

Теперь мы должны изменить нашу функцию DrawGLScene так чтобы она вызывала функцию печати. По идее это также просто, как и добавить простую строку “hello world” в конце функции, но я хочу сделать чуточку побольше, поскольку я бы хотел показать масштабирование и вращение.

 

int DrawGLScene(GLvoid)                    // Здесь мы рисуем

{

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Очистка экрана и буфера глубины

  glLoadIdentity();                  // Сброс матрицы вида модели

  glTranslatef(0.0f,0.0f,-1.0f);     // Сдвиг на одну единицу в экран

 

  // Синий текст

  glColor3ub(0,0,0xff);

 

  // Позиция текста WGL на экране

  glRasterPos2f(-0.40f, 0.35f);

  glPrint("Active WGL Bitmap Text With NeHe - %7.2f", cnt1);  // Вывод текста

 

  // Выводим тот же текст, но с вращением и масштабированием.

 

  // Красный текст

  glColor3ub(0xff,0,0);

 

  glPushMatrix();

  glLoadIdentity();

  glRotatef(cnt1,0,0,1);

  glScalef(1,.8+.3*cos(cnt1/5),1);

  glTranslatef(-180,0,0);

  freetype::print(our_font, 320, 200, "Active FreeType Text - %7.2f", cnt1);

  glPopMatrix();

 

  // Уберите комментарий для вывода текста в несколько строк.

  // freetype::print(our_font, 320, 200, "Here\nthere\nbe\n\nnewlines\n.", cnt1);

 

  cnt1+=0.051f;                   // Увеличим первый счетчик

  cnt2+=0.005f;                   // Увеличим второй счетчик

  return TRUE;                    // Все OK

}

 

Последняя вещь, которую осталось сделать это добавить обработку исключений для надежности. Идите в WinMain и добавьте в начале ее try{..}.

 

  MSG  msg;                    // Структура сообщения

  BOOL  done=FALSE;            // Переменная цикла

 

  try {                        // Использовать обработку сообщений

 

 

Затем измените конец функции, чтобы иметь catch{}.

 

  // Завершение

  KillGLWindow();                    // убить окно

 

  // Захват исключений

  } catch (std::exception &e) {

    MessageBox(NULL,e.what(),"CAUGHT AN EXCEPTION",MB_OK | MB_ICONINFORMATION);

  }

 

  return (msg.wParam);                  // Выход из программы

}

 

Теперь, если когда-нибудь, что-то случается, появляется исключение, и мы получим небольшое сообщение гласящее, что собственно случилось. Заметьте, что обработка исключений может замедлить код, так что когда вы будете выпускать окончательную версию, желательно было бы выключить exception handling в Project->Settings->C/C++, в категории “C++ Language”.

 

Вот и все! Скомпилируйте программу и вы должны видеть красивый FreeType сгенерированный текст, движущийся вокруг оригинального растрового текста из Урока 13.

 

Общие замечания

 

Есть ряд усовершенствований, который вы можете добавить в библиотеку. С одной стороны непосредственное использование данных шрифта выглядит неуклюже, так что вы можете захотеть создать стандартный кэш шрифтов, чтобы скрыть управление шрифтовыми ресурсами от пользователя. Вы также можете наподобие OpenGL и создать стек шрифтов, который позволил вам ссылаться на шрифт при вызове функции печати. (Эти вещи я всегда делаю в своем коде, но не привожу этого в уроке для простоты). Вы также можете захотеть сделать версию функции print, которая выравнивает текст по центру, тогда вам, вероятно, нужно использовать нижеследующие методы.

 

Сейчас у меня есть текст, вращающийся вокруг центра. Однако, чтобы получить такой эффект для любого текста, вам нужно точно знать длину текста – это довольно сложно. Один способ получения длины текста – это засунуть команды glBitmap в список отображения в порядке  изменения растровой позиции как в матрице вида модели (я оставил нужную линию в коде, но она закомментирована). Тогда мы должны установить позицию x,y перед использованием glCallLists, и использовать glGet чтобы найти её после отрисовки текста – разница даст вам длину текста в пикселях.

 

Вы должны знать, что FreeType шрифты используют гораздо больше памяти, чем WGL's растровые шрифты (это одно из преимуществ бинарных изображений, они используют мало памяти). Если по каким-нибудь причинам вам нужно минимизировать использование памяти, наверное лучше придерживаться кода из урока 13.

 

Другое интересное преимущество использования текстурированных четырехугольников то, что четырехугольники, по сравнению с растрами, работают лучше с функциями выбора ОpenGL (см Урок 32). Это делает жизнь гораздо проще, если вы хотите создать текст, который отвечает за наведение мыши или кликанье. (Использование WGL шрифтов здесь возможно, но опять же есть хитрый момент в использовании растровых координат, чтобы вычислить длину текста в пикселях).

 

И в завершении, я даю вам некоторые ссылки на библиотеки шрифтов к OpenGL. В зависимости от ваших целей и компилятора вы можете захотеть использовать одну из них вместо этой (на самом деле их очень много, я только включил то, с чем я немного поработал).

 

GLTT - эта старая библиотека которая вроде бы уже заброшена, но она получила позитивные отзывы. Основана на FreeType1. Я думаю, что вам надо найти копию старых исходников FreeType1 чтобы скомпилировать в MSVC6. Загрузка доступна отсюда  http://gltt.sourceforge.net/index.html.

 

OGLFT неплохая библиотека шрифтов, основанная на FreeType, потребуется немного усилий, чтобы скомпилировать ее под MSVC. Основная платформа там Linux...  http://oglft.sourceforge.net.

 

FTGL ещё третья библиотека, основанная на FreeType, она была разработана под OS X. http://homepages.paradise.net.nz/henryj/code/#FTGL.

 

FNT библиотека, основанная не на FreeType, являющаяся частью PLIB. Имеет неплохой интерфейс, использует собственный формат, отлично компилируется под MSVC6. http://plib.sourceforge.net/fnt.

 

© Sven Olsen

PMG  27 июля 2004 (c)  СhipSet