2010-02-27 2 views
5

Эй, у меня странная проблема с рисованием текста в openGL, загруженном библиотекой Freetype 2. Вот скриншот того, что я вижу.Проблема с Freetype и OpenGL

example http://img203.imageshack.us/img203/3316/freetypeweird.png

Вот мои биты коды для загрузки и рендеринга моего текста.

class Font 
{ 
    Font(const String& filename) 
    { 
     if (FT_New_Face(Font::ftLibrary, "arial.ttf", 0, &mFace)) { 
      cout << "UH OH!" << endl; 
     } 

     FT_Set_Char_Size(mFace, 16 * 64, 16 * 64, 72, 72); 
    } 

    Glyph* GetGlyph(const unsigned char ch) 
    { 
     if(FT_Load_Char(mFace, ch, FT_LOAD_RENDER)) 
      cout << "OUCH" << endl; 

     FT_Glyph glyph; 

     if(FT_Get_Glyph(mFace->glyph, &glyph)) 
      cout << "OUCH" << endl; 

     FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph; 

     Glyph* thisGlyph = new Glyph; 
     thisGlyph->buffer = bitmap_glyph->bitmap.buffer; 
     thisGlyph->width = bitmap_glyph->bitmap.width; 
     thisGlyph->height = bitmap_glyph->bitmap.rows; 

     return thisGlyph; 
    } 
}; 

Соответствующая информация глиф (ширина, высота, буфер) хранится в следующей структуры

struct Glyph { 
    GLubyte* buffer; 
    Uint width; 
    Uint height; 
}; 

И, наконец, чтобы сделать это, у меня есть класс с именем RenderFont.

class RenderFont 
{ 
    RenderFont(Font* font) 
    { 
     mTextureIds = new GLuint[128]; 

     mFirstDisplayListId=glGenLists(128); 
     glGenTextures(128, mTextureIds); 

     for(unsigned char i=0;i<128;i++) 
     { 
     MakeDisplayList(font, i); 
     } 
    } 

    void MakeDisplayList(Font* font, unsigned char ch) 
    { 
     Glyph* glyph = font->GetGlyph(ch); 

     glBindTexture(GL_TEXTURE_2D, mTextureIds[ch]); 
     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 
     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 

     glTexImage2D(GL_TEXTURE_2D, 
        0, 
        GL_RGBA, 
        glyph->width, 
        glyph->height, 
        0, 
        GL_ALPHA, 
        GL_UNSIGNED_BYTE, 
        glyph->buffer); 

     glNewList(mFirstDisplayListId+ch,GL_COMPILE); 
     glBindTexture(GL_TEXTURE_2D, mTextureIds[ch]); 

     glBegin(GL_QUADS); 
     glTexCoord2d(0,1); glVertex2f(0,glyph->height); 
     glTexCoord2d(0,0); glVertex2f(0,0); 
     glTexCoord2d(1,0); glVertex2f(glyph->width,0); 
     glTexCoord2d(1,1); glVertex2f(glyph->width,glyph->height); 
     glEnd(); 

     glTranslatef(16, 0, 0); 

     glEndList(); 
    } 

    void Draw(const String& text, Uint size, const TransformComponent* transform, const Color32* color) 
    { 
     glEnable(GL_TEXTURE_2D); 
     glEnable(GL_BLEND); 
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 

     glTranslatef(100, 250, 0.0f); 

     glListBase(mFirstDisplayListId); 

     glCallLists(text.length(), GL_UNSIGNED_BYTE, text.c_str()); 

     glDisable(GL_TEXTURE_2D); 
     glDisable(GL_BLEND); 

     glLoadIdentity(); 
    } 

private: 
    GLuint mFirstDisplayListId; 
    GLuint* mTextureIds; 
}; 

Может ли кто-нибудь увидеть что-нибудь странное здесь, что приведет к искажению текста? Это странно, потому что, если я изменю размер шрифта или DPI, то некоторые из букв, которые отображаются правильно, искажаются, а другие буквы, которые были искажены до того, отображаются правильно.

ответ

7

Я не знаком с FreeType, но от картины, это выглядит как ширина символов не связана напрямую с размером буферов (то есть. Glyph-> буфер не указывают на массив glyph-> width * glyth-> height bytes).

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

+0

В этом была проблема. Я скопировал все в новый буфер, который был установленным размером, заполняющим его пустыми пикселями, и все работало правильно. Благодаря! – Morgan

1

Вы уверены, что FT_Glyph на самом деле является растровым глифом? Убедитесь, что вы используете FT_Glyph_To_Bitmap.

В качестве альтернативы, так как вы, кажется, не нужно хранить FT_Glyph сек вокруг после этого, вы можете просто сделать:

int error = FT_Load_Char(face, ch, FT_LOAD_RENDER); 
if(error) 
    return error; 

FT_GlyphSlot slot = face->glyph; 
FT_Bitmap bitmap = slot->bitmap; 

// do stuff with this FT_Bitmap 

См here для документации по FT_Bitmap. Обратите внимание, что при следующем вызове FT_Load_Char данные в bitmap больше не будут действительны.

У вас также есть ряд проблем с управлением памятью.

  1. Вы используете new Glyph выделить ваши глифы, но никогда не называйте delete. Поскольку вам просто нужно временно сделать глиф для создания текстуры и список отображения, вы должны использовать std::auto_ptr<Glyph>.

  2. Вы никогда не звоните FT_Glyph_Done, поэтому все те FT_Glyph, которые вы выделили, так и не были освобождены.

+0

Спасибо за отзыв о управлении памятью. Я удалил весь этот код только для тестирования, чтобы убедиться, что мои данные случайно не удалены случайно, когда этого не должно быть. – Morgan

-1

"16" должен быть размером шрифта в точках. Итак, при переводе вы должны использовать глиф-> ширину вместо «16». Более того, для некоторых писем может потребоваться кернинг для самых красивых текстов. Например, «AV» может выглядеть как «A V» без использования кернинга.

2

Использование:

glPixelStorei(GL_PACK_ALIGNMENT, 1);<br> 
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 

Это раздражает щеколда из меня тоже, но вы должны сказать OpenGL использовать интервал вы даете его, а не обычные 32-разрядные границы он ожидает. Шаг изображения изменяется, но OpenGL не знает, чтобы использовать меньшие выравнивания упаковки без этих вызовов перед созданием текстуры.

Я делаю это так:

// Convert the glyph to a bitmap. 
FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, NULL, true); 
FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph; 

// This reference will make accessing the bitmap easier 
FT_Bitmap& bitmap = bitmap_glyph->bitmap; 

int _Left = abs(bitmap_glyph->left); 
int _Top = abs(bitmap_glyph->top); 
int _Height = abs(bitmap.rows); 
int _Width = _Left+abs(bitmap.width); 

// If it's not a glyph or spacing, go to the next one 
if ((_Width == 0 || _Height == 0) && !isspace(i)) 
    return; 

advances[i] = max(_Width, face->glyph->advance.x >> 6); 

vector<unsigned char> Data(_Height*_Width*2, 0); 
for (int32 h=0; h < abs(bitmap.rows); ++h) 
    for (int32 w=0; w < abs(bitmap.width); ++w) 
    { 
     int32 luminance = bitmap.buffer[h*bitmap.pitch + w]; 
     Data[(h*_Width + w + _Left)*2 + 0] = 255; 
     Data[(h*_Width + w + _Left)*2 + 1] = luminance; 
    } 

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

The Address & данных [0] теперь содержит формат GL_LUMINANCE_ALPHA external, с типом и размером GL_UNSIGNED_CHAR_Width*_Height. Это должно сделать всех, кто делает жизнь этого материала легче.

Смежные вопросы