FreeType显示汉字

本文介绍了一个使用FreeType库结合OpenGL来渲染中文字体的示例程序。该程序能够加载并显示TrueType字体文件,如微软雅黑,并通过OpenGL纹理映射实现高质量的文字渲染效果。文章展示了如何初始化FreeType库、加载字体文件、获取字符位图并将其转换为OpenGL纹理的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <windows.h>
#include <stdio.h>
#include <gl/glut.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>

#ifdef _WIN32
#pragma comment(lib , "libfreetype.lib")
#endif

#define MAX_NO_TEXTURES 1
#define CUBE_TEXTURE 0

GLuint texture_id[MAX_NO_TEXTURES];

struct xCharTexture
{
 GLuint  m_texID;
 wchar_t m_chaID;
 int     m_Width;
 int     m_Height;

    int     m_adv_x;
    int     m_adv_y;
    int     m_delta_x;
    int     m_delta_y;
public:
 xCharTexture()
 {
  m_texID  = 0;
  m_chaID  = 0;
  m_Width  = 0;
  m_Height = 0;
 }
}g_TexID[65536];
class xFreeTypeLib
{
    FT_Library m_FT2Lib;
 FT_Face    m_FT_Face;

 int   m_w;
 int   m_h;
public:
 xFreeTypeLib()
 {
  if (FT_Init_FreeType( &m_FT2Lib) )
   exit(0);
 }

 void load(const char* font_file , int _w , int _h)
 {
  //加载一个字体,取默认的Face,一般为Regualer
  if (FT_New_Face( m_FT2Lib, font_file, 0, &m_FT_Face ))
   exit(0);
  FT_Select_Charmap(m_FT_Face, FT_ENCODING_UNICODE);
  m_w = _w ; m_h = _h;
  m_FT_Face->num_fixed_sizes;
  //大小要乘64.这是规定。照做就可以了。
  //FT_Set_Char_Size( m_FT_Face , 0 , m_w << 6, 96, 96);
  FT_Set_Pixel_Sizes(m_FT_Face,m_w, m_h);
 }

 GLuint loadChar(wchar_t ch)
 {
  if(g_TexID[ch].m_texID)
   return g_TexID[ch].m_texID;

  if(FT_Load_Char(m_FT_Face, ch,FT_LOAD_RENDER|FT_LOAD_FORCE_AUTOHINT|
   (true ? FT_LOAD_TARGET_NORMAL : FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO) )   )
  {
   return 0;
  }

        xCharTexture& charTex = g_TexID[ch];
  
  //得到字模
  FT_Glyph glyph;
  if(FT_Get_Glyph( m_FT_Face->glyph, &glyph ))
   return 0;

  //转化成位图
  FT_Render_Glyph( m_FT_Face->glyph,   FT_RENDER_MODE_LCD );//FT_RENDER_MODE_NORMAL  );
  FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 );
  FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;

  //取道位图数据
  FT_Bitmap& bitmap=bitmap_glyph->bitmap;

  //把位图数据拷贝自己定义的数据区里.这样旧可以画到需要的东西上面了。
  int width  =  bitmap.width;
  int height =  bitmap.rows;

  m_FT_Face->size->metrics.y_ppem;
  m_FT_Face->glyph->metrics.horiAdvance;


  charTex.m_Width = width;
  charTex.m_Height = height;
  charTex.m_adv_x = m_FT_Face->glyph->advance.x / 64.0f;
  charTex.m_adv_y = m_FT_Face->size->metrics.y_ppem; //m_FT_Face->glyph->metrics.horiBearingY / 64.0f;
  charTex.m_delta_x = (float)bitmap_glyph->left;
  charTex.m_delta_y = (float)bitmap_glyph->top - height;

  glGenTextures(1,&charTex.m_texID);
        glBindTexture(GL_TEXTURE_2D,charTex.m_texID);
  char* pBuf = new char[width * height * 4];

  bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;

  for(int j=0; j  < height ; j++)
  {
   for(int i=0; i < width; i++)
   {
    unsigned char _vl =  (i>=bitmap.width || j>=bitmap.rows) ? 0 : bitmap.buffer[i + bitmap.width*j];
    pBuf[(4*i + (height - j - 1) * width * 4)  ] = 0xff;
    pBuf[(4*i + (height - j - 1) * width * 4)+1] = 0xff;
    pBuf[(4*i + (height - j - 1) * width * 4)+2] = 0xff;
    pBuf[(4*i + (height - j - 1) * width * 4)+3] = _vl;
   }
  }

  gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pBuf);//creates 2-D mipmaps
  //glTexImage2D( GL_TEXTURE_2D,0,GL_RGBA,width, height,0,GL_RGBA,GL_UNSIGNED_BYTE,pBuf);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
  glTexEnvi(GL_TEXTURE_2D,GL_TEXTURE_ENV_MODE,GL_REPLACE);
  delete[]pBuf;
  return charTex.m_chaID;
 }

}g_FreeTypeLib;

float ratio;

xCharTexture* getTextChar(wchar_t ch)
{
  g_FreeTypeLib.loadChar(ch);
  return &g_TexID[ch];
}

wchar_t g_UnicodeString[]=L"远交华盖欲何求,未敢翻身已碰头。/n/
破帽遮颜过闹市,漏船载酒泛中流。/n/
横眉冷对千夫指,俯首甘为孺子牛。/n/
躲进小楼成一统,管他冬夏与春秋。";


void drawText(wchar_t* _strText,int x , int y, int maxW , int h)
{
 int sx = x;
 int sy = y;
 int maxH = h;
 for(int i = 0 ; i < wcslen(_strText) ; i ++)
 {
  
  if(_strText[i] =='/n')
  {
   sx = x ; sy += maxH + 12;
   continue;
  }
  xCharTexture* pCharTex = getTextChar(_strText[i]);
  glBindTexture(GL_TEXTURE_2D,pCharTex->m_texID);
  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
  //glDisable(GL_TEXTURE_2D);
  int w = pCharTex->m_Width;
  int h = pCharTex->m_Height;

  int ch_x = sx + pCharTex->m_delta_x;
  int ch_y = sy - h - pCharTex->m_delta_y;

  if(maxH < h) maxH = h;
  glBegin ( GL_QUADS );
  {
   glTexCoord2f(0.0f, 1.0f); glVertex3f(ch_x      , ch_y    ,  1.0f);
   glTexCoord2f(1.0f, 1.0f); glVertex3f(ch_x +  w, ch_y    ,  1.0f);
   glTexCoord2f(1.0f, 0.0f); glVertex3f(ch_x +  w, ch_y + h,  1.0f);
   glTexCoord2f(0.0f, 0.0f); glVertex3f(ch_x     , ch_y + h,  1.0f);
  }
  glEnd();
  sx += pCharTex->m_adv_x;
  if(sx > x + maxW)
  {
   sx = x ; sy += maxH + 12;
  }
 }
}

void init(void)
{
   glShadeModel(GL_SMOOTH);       // Enable Smooth Shading
   glClearColor(0.0f, 0.0f, 0.0f, 0.5f);    // Black Background
   glEnable ( GL_COLOR_MATERIAL );
   glColorMaterial ( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );

   g_FreeTypeLib.load("simhei.ttf",24,24);
  
   glDisable ( GL_CULL_FACE );

   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}

void reshape( int w, int h )
{
 // Prevent a divide by zero, when window is too short
 // (you cant make a window of zero width).
 if(h == 0)
  h = 1;

 ratio = 1.0f * w / h;
 // Reset the coordinate system before modifying
 glMatrixMode(GL_PROJECTION);
 glLoadIdentity();
 
 // Set the viewport to be the entire window
    glViewport(0, 0, w, h);
    glOrtho(0,w,h,0,-100,200);
 // Set the clipping volume
 glMatrixMode(GL_MODELVIEW);
 glLoadIdentity();
 gluLookAt(0, 0, 30 ,0 , 0 ,10 , 0.0f , 1.0f , 0.0f);
}

void display( void )
{
   glClearColor(0.0f , 0.0f , 0.6f , 1.0f);
   glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

   glEnable ( GL_TEXTURE_2D );
   drawText(g_UnicodeString,50,50,900,25);
   glutSwapBuffers();
}

void keyboard ( unsigned char key, int x, int y )  // Create Keyboard Function
{
  switch ( key ) {
    case 27:        // When Escape Is Pressed...
      exit ( 0 );   // Exit The Program
      break;        // Ready For Next Case
    default:        // Now Wrap It Up
      break;
  }
}

void arrow_keys ( int a_keys, int x, int y )  // Create Special Function (required for arrow keys)
{
  switch ( a_keys ) {
    case GLUT_KEY_UP:     // When Up Arrow Is Pressed...
      glutFullScreen ( ); // Go Into Full Screen Mode
      break;
    case GLUT_KEY_DOWN:               // When Down Arrow Is Pressed...
      glutReshapeWindow ( 500, 500 ); // Go Into A 500 By 500 Window
      break;
    default:
      break;
  }
}

int main ( int argc, char** argv )   // Create Main Function For Bringing It All Together
{
  glutInit            ( &argc, argv ); // Erm Just Write It =)
  glutInitDisplayMode ( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA ); // Display Mode
  glutInitWindowPosition (0,0);
  glutInitWindowSize  ( 500, 500 ); // If glutFullScreen wasn't called this is the window size
  glutCreateWindow    ( "Chinese - Display" ); // Window Title (argv[0] for current directory as title)
  init ();
  glutDisplayFunc     ( display );  // Matching Earlier Functions To Their Counterparts
  glutReshapeFunc     ( reshape );
  glutKeyboardFunc    ( keyboard );
  glutSpecialFunc     ( arrow_keys );
  glutIdleFunc    ( display );
  glutMainLoop        ( );          // Initialize The Main Loop
  return 1;
}

<think>好的,我现在需要帮助用户解决在C++中使用OpenCV和FreeType显示中文的问题。首先,用户已经提供了三个相关的引用,我需要仔细阅读这些引用来提取有用的信息。 首先,引用[1]提到在Windows下配置FreeType库的步骤,包括下载和解压,编译生成lib文件。这说明用户可能需要指导如何正确安装和配置FreeType库,尤其是处理不同平台下的编译问题。引用[2]提到了Linux下出现头文件缺失的问题,即找不到ft2build.h,这可能是因为没有正确安装FreeType的开发包,需要安装libfreetype6-dev。引用[3]指出OpenCV的putText函数本身不支持中文,必须使用FreeType库来渲染中文,否则会出现豆腐块。 接下来,我需要整理出一个步骤清晰的解决方案。首先,用户需要在他们的系统上安装FreeType库。对于不同的操作系统,步骤会有所不同:Windows用户需要下载源码并编译,而Linux用户则可以通过包管理器安装。然后,配置OpenCV项目,确保包含路径和链接库正确设置。之后,利用FreeType加载中文字体,并将渲染后的文字添加到OpenCV的Mat对象中。需要注意的是,处理中文时需要正确的字符编码,比如UTF-8,同时要确保字体文件路径正确。 用户可能遇到的常见问题包括编译时的头文件缺失(如引用[2]中的问题),这可能是因为没有正确安装开发包或者包含路径没有设置正确。另外,链接错误可能源于库文件路径未正确指定或库名称错误。还有,渲染中文时出现乱码,可能是字体文件路径错误或编码问题。 最后,我需要生成一些相关问题,帮助用户进一步理解和扩展这个功能,比如如何处理不同字体大小、多行显示,或者性能优化的方法。确保回答结构清晰,步骤详细,并覆盖不同操作系统的注意事项。</think>### 如何在C++中使用OpenCV和FreeType显示中文字符 #### 一、环境配置步骤 1. **安装FreeType库** - **Windows**:从[FreeType官网](https://www.freetype.org/)下载源码(如`freetype-2.7.tar.gz`),解压后使用VS编译生成`lib`文件(需区分Debug/Release和x86/x64)[^1]。 - **Linux**:通过包管理器安装开发包: ```bash sudo apt-get install libfreetype6-dev ``` 若编译时仍报错`ft2build.h`缺失,需检查头文件路径是否包含`/usr/include/freetype2`[^2]。 2. **配置OpenCV项目** - **包含路径**:添加FreeType头文件目录(如`freetype/include`)。 - **链接库**:添加编译后的`freetype.lib`(Windows)或`-lfreetype`(Linux)。 - **示例CMake配置**: ```cmake find_package(Freetype REQUIRED) include_directories(${FREETYPE_INCLUDE_DIRS}) target_link_libraries(your_project ${OpenCV_LIBS} ${FREETYPE_LIBRARIES}) ``` --- #### 二、代码实现 1. **创建FreeType渲染对象** ```cpp #include <ft2build.h> #include FT_FREETYPE_H FT_Library ft; if (FT_Init_FreeType(&ft)) { std::cerr << "FreeType初始化失败" << std::endl; return -1; } FT_Face face; if (FT_New_Face(ft, "fonts/simhei.ttf", 0, &face)) { // 使用中文字体文件 std::cerr << "字体加载失败" << std::endl; return -1; } FT_Set_Pixel_Sizes(face, 0, 24); // 设置字号为24 ``` 2. **渲染文字到OpenCV图像** ```cpp cv::Mat img(500, 500, CV_8UC3, cv::Scalar(255, 255, 255)); std::wstring text = L"你好,OpenCV!"; // 使用宽字符支持中文 int x = 50, y = 100; for (wchar_t c : text) { FT_Load_Char(face, c, FT_LOAD_RENDER); FT_Bitmap* bitmap = &face->glyph->bitmap; for (int i = 0; i < bitmap->rows; i++) { for (int j = 0; j < bitmap->width; j++) { if (bitmap->buffer[i * bitmap->width + j] > 0) { img.at<cv::Vec3b>(y + i, x + j + face->glyph->bitmap_left) = cv::Vec3b(0, 0, 0); // 黑色文字 } } } x += face->glyph->advance.x >> 6; // 计算下一个字符位置 } cv::imshow("Result", img); cv::waitKey(0); ``` --- #### 三、关键注意事项 1. **字体文件选择**:必须使用支持中文的字体(如`simhei.ttf`、`msyh.ttf`),路径需为绝对路径或确保可执行文件能访问。 2. **字符编码处理**:需使用`wstring`和宽字符类型(如`wchar_t`)避免乱码,编译时添加`-finput-charset=UTF-8`(Linux)[^3]。 3. **性能优化**:预先渲染常用字符到缓存,避免实时渲染开销。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值