Freetype 和 OpenGL 的问题

Posted

技术标签:

【中文标题】Freetype 和 OpenGL 的问题【英文标题】:Issue with Freetype and OpenGL 【发布时间】:2011-01-21 21:17:40 【问题描述】:

嘿,我在使用 Freetype 2 库加载的 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,那么一些正确显示的字母会变成乱码,而之前出现乱码的其他字母会正确显示。

【问题讨论】:

【参考方案1】:

我对 FreeType 不熟悉,但是从图片上看,字符的宽度似乎与缓冲区的大小没有直接关系(即 glyph->buffer 不指向 glyph- 的数组>width*glyth->height 字节)。

作为一种猜测,我会说所有字符在内存中都有一个宽度(与它们在屏幕上使用的大小相反),可能是所有字形宽度中最大的一个,但你用每个字符的宽度而不是正确的宽度。因此,只有使用全宽的字形是正确的。

【讨论】:

这就是问题所在。我将所有内容复制到一个新缓冲区中,该缓冲区是一个设置大小的缓冲区,用空像素填充它,并且一切正常。谢谢!【参考方案2】:

用途:

glPixelStorei(GL_PACK_ALIGNMENT, 1);
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 中的值作为我的 alpha 值,但这种方式似乎更具描述性,而且速度在我的使用中不是问题。

地址 &Data[0] 现在包含GL_LUMINANCE_ALPHA external 格式,类型为GL_UNSIGNED_CHAR,大小为_Width*_Height。这应该会让从事这些工作的人的生活更轻松。

【讨论】:

【参考方案3】:

您确定FT_Glyph 实际上是位图字形吗?请务必先使用FT_Glyph_To_Bitmap

或者,由于您之后似乎不需要存储FT_Glyphs,您可以这样做:

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 的文档,请参阅here。请注意,下次调用FT_Load_Char时,bitmap中的数据将不再有效。

您在内存管理方面也存在许多问题。

    你使用new Glyph来分配你的字形,但永远不要调用delete.因为你只需要临时制作一个字形来生成纹理和显示列表,你应该使用std::auto_ptr&lt;Glyph&gt;

    你永远不会调用FT_Glyph_Done,所以你分配的所有FT_Glyphs永远不会被释放。

【讨论】:

感谢有关内存管理的提示。我删除了所有这些代码只是为了测试目的,以确保我的数据不会在不应该被意外删除时被意外删除。【参考方案4】:

“16”应该是字体的大小,以磅为单位。所以,在翻译时你应该使用 glyph->width 而不是“16”。此外,某些字母可能需要调整字距以获得最佳文本。例如,“AV”可能看起来像“AV”,但没有使用字距调整。

【讨论】:

以上是关于Freetype 和 OpenGL 的问题的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL FreeType2 位图不渲染

orthoProject freetype opengl

QT+OpenGL(04)—freetype库的编译

使用 openGL ES 2.0 结合其他线条绘制功能的字体渲染(Freetype)不起作用

FreeType 使用FT_MEM_ALLOC/FT_FREE内存操作的正确写法

渲染 text-freetype 空白屏幕