Проблема с Freetype и OpenGL - PullRequest
       45

Проблема с Freetype и OpenGL

5 голосов
/ 28 февраля 2010

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

пример 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, то некоторые буквы, которые отображаются правильно, искажаются, а другие буквы, которые были искажены ранее, затем отображаются правильно.

Ответы [ 4 ]

8 голосов
/ 28 февраля 2010

Я не знаком с FreeType, но из рисунка видно, что ширина символов не имеет прямого отношения к размеру буферов (т. Е. Glyph-> buffer не указывает на массив glyph- > ширина * глиф-> высота байт).

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

2 голосов
/ 24 января 2011

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

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 (Белый) в функцию инициализации String, а затем просто использовать значения в FreeType для моих альфа-значений, но этот способ кажется более описательным, и скорость не является проблемой при моем использовании.

Адрес и данные [0] теперь содержат формат GL_LUMINANCE_ALPHA external с типом GL_UNSIGNED_CHAR и размером _Width*_Height. Это должно облегчить жизнь тем, кто занимается этим делом.

1 голос
/ 28 февраля 2010

Вы уверены, что 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

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

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

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

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

0 голосов
/ 08 апреля 2010

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

...