1. /*!
    2. * \file: Exp1.cpp
    3. * \date: 2020/05/29 15:04
    4. * \author: wangqi
    5. * Contact: wangq@southzn.com
    6. *
    7. * \brief:
    8. *
    9. * TODO: long description
    10. *
    11. * \note: 实现了FT库生成字符位图,并上传到GL纹理。
    12. 实现了字符位图缓存功能,多个字符图像保存在同一个纹理中。
    13. 实现了简单的字体管理框架。
    14. 实现了简单的加粗和倾斜效果。
    15. 实现了反锯齿开关,并且兼容加粗倾斜效果。
    16. */
    17. // OpenGL library
    18. #include <gl/glut.h>
    19. // Std misc
    20. #include <map>
    21. #include <vector>
    22. // FreeType library
    23. #include <ft2build.h>
    24. #include FT_FREETYPE_H
    25. #include FT_BITMAP_H
    26. #include FT_OUTLINE_H
    27. #ifdef CreateFont
    28. #undef CreateFont
    29. #endif
    30. typedef unsigned char byte;
    31. class CFontManager //字体管理器:管理了一个字体库
    32. {
    33. public:
    34. CFontManager();
    35. ~CFontManager();
    36. // 初始化
    37. bool initialize(void);
    38. // 释放
    39. void release(void);
    40. /*!
    41. * \brief: createFont 创建字体
    42. *
    43. * \param: filename 字体文件的路径
    44. * \param: face
    45. * \param: tall 高度
    46. * \param: bold 加粗
    47. * \param: italic 是否斜体
    48. * \param: antialias 是否抗锯齿
    49. * \returns: int
    50. * \author: wangqi
    51. * \date: 2020/06/02 14:16
    52. *
    53. * TODO:
    54. *
    55. */
    56. int createFont(const char *filename, int face, int tall, bool bold, bool italic, bool antialias);
    57. // 得到字符信息
    58. bool getCharInfo(int font_index, int code, int *wide, int *tall, int *horiBearingX, int *horiBearingY, int *horiAdvance, GLuint *texture, float coords[]);
    59. // 得到字体的高度
    60. int getFontTall(int font_index);
    61. private:
    62. struct glyphMetrics //字形度量
    63. {
    64. int width; //宽度
    65. int height; //长度
    66. int horiBearingX;
    67. int horiBearingY;
    68. int horiAdvance;
    69. //int vertBearingX;
    70. //int vertBearingY;
    71. //int vertAdvance;
    72. };
    73. class CFont //字体
    74. {
    75. public:
    76. CFont();
    77. ~CFont();
    78. /*!
    79. * \brief: create 创建字体
    80. *
    81. * \param: library FT库。需要提前FT_Init_FreeType
    82. * \param: filename 字体文件的路径
    83. * \param: face_index Face下标
    84. * \param: tall 高度
    85. * \param: bold 是否加粗
    86. * \param: italic 是否斜体
    87. * \param: antialias 是否抗锯齿
    88. * \returns: bool
    89. * \author: wangqi
    90. * \date: 2020/06/02 14:13
    91. *
    92. * TODO:
    93. *
    94. */
    95. bool create(FT_Library library, const char *filename, FT_Long face_index,
    96. int tall, bool bold, bool italic, bool antialias);
    97. // 释放
    98. void release(void);
    99. // 得到字符串信息
    100. bool getCharInfo(int code, glyphMetrics *metrics, GLuint *texture, float coords[]);
    101. // 得到字符高度
    102. int getFontTall(void);
    103. private:
    104. // 加载字符
    105. bool loadChar(int code, glyphMetrics *metrics);
    106. class CChar //渲染之后的字符
    107. {
    108. public:
    109. // 设置信息
    110. void setInfo(glyphMetrics *metrics);
    111. // 得到信息
    112. void getInfo(glyphMetrics *metrics, GLuint *texture, float coords[]);
    113. public:
    114. int m_code; //编码
    115. GLuint m_texture; //纹理
    116. float m_coords[4]; //坐标:left top right bottom
    117. private:
    118. glyphMetrics m_metrics;
    119. };
    120. class CPage //页面:纹理=1:1。一个纹理中管理着多个字符
    121. {
    122. public:
    123. CPage();
    124. ~CPage();
    125. /*!
    126. * \brief: append 加当前字体加入这个纹理
    127. *
    128. * \param: wide 宽度
    129. * \param: tall 高度
    130. * \param: rgba 字体的内容
    131. * \param: coords 字体在当前纹理中的坐标
    132. * \returns: bool
    133. * \author: wangqi
    134. * \date: 2020/06/02 14:36
    135. *
    136. * TODO:
    137. *
    138. */
    139. bool append(int wide, int tall, byte *rgba, float coords[]);
    140. GLuint getTexture(void); //得到纹理
    141. private:
    142. GLuint m_texture; //纹理
    143. int m_wide; //宽度
    144. int m_tall; //高度
    145. int m_posx;
    146. int m_posy;
    147. int m_maxCharTall;
    148. };
    149. typedef std::map<int, CChar *> TCharMap;
    150. //键值对:找到字符的位置
    151. //key: 字符
    152. //value: 字符的信息
    153. FT_Library m_library;
    154. FT_Face m_face;
    155. bool m_antialias;
    156. bool m_bold;
    157. int m_tall;
    158. int m_rgbaSize;
    159. GLubyte *m_rgba;
    160. TCharMap m_chars;
    161. std::vector<CPage *> m_pages;
    162. };
    163. FT_Library m_library;
    164. std::vector<CFont *> m_fonts; //一个字体库有多个字体
    165. };
    166. //------------------------------------------------------------
    167. // CFont
    168. //------------------------------------------------------------
    169. CFontManager::CFont::CFont()
    170. {
    171. m_face = NULL;
    172. m_rgba = NULL;
    173. m_antialias = false;
    174. m_bold = false;
    175. m_tall = 0;
    176. }
    177. CFontManager::CFont::~CFont()
    178. {
    179. release();
    180. }
    181. bool CFontManager::CFont::create(FT_Library library, const char *filename, FT_Long face_index,
    182. int tall, bool bold, bool italic, bool antialias)
    183. {
    184. FT_Error err;
    185. if(tall > 256)
    186. {
    187. // Bigger than a page size
    188. return false;
    189. }
    190. // 得到字符信息
    191. if((err = FT_New_Face(library, filename, face_index, &m_face)) != FT_Err_Ok)
    192. {
    193. printf("FT_New_Face() Error %d\n", err);
    194. return false;
    195. }
    196. // 设置像素
    197. if((err = FT_Set_Pixel_Sizes(m_face, 0, tall)) != FT_Err_Ok)
    198. {
    199. printf("FT_Set_Pixel_Sizes() Error %d\n", err);
    200. return false;
    201. }
    202. //m_rgbaSize = (tall * 2) * tall * 4; //源代码是*2,但是觉得博主写错了
    203. m_rgbaSize = (tall) * tall * 4; //高*宽*4。但位图的高宽相同,故可以高*高*4;4代表RGBA四个通道
    204. m_rgba = new GLubyte[m_rgbaSize];
    205. if(m_rgba == NULL)
    206. {
    207. return false;
    208. }
    209. m_library = library;
    210. m_antialias = antialias;
    211. m_bold = bold;
    212. m_tall = tall;
    213. if(italic) //是否抗锯齿
    214. {
    215. FT_Matrix m;
    216. m.xx = 0x10000L;
    217. m.xy = 0.5f * 0x10000L;
    218. m.yx = 0;
    219. m.yy = 0x10000L;
    220. FT_Set_Transform(m_face, &m, NULL);
    221. }
    222. return true;
    223. }
    224. void CFontManager::CFont::release(void)
    225. {
    226. FT_Error err;
    227. if(m_face)
    228. {
    229. if((err = FT_Done_Face(m_face)) != FT_Err_Ok)
    230. {
    231. printf("FT_Done_Face() Error %d\n", err);
    232. }
    233. m_face = NULL;
    234. }
    235. if(m_rgba)
    236. {
    237. delete[] m_rgba;
    238. m_rgba = NULL;
    239. }
    240. for(TCharMap::iterator it = m_chars.begin(); it != m_chars.end(); it++)
    241. {
    242. delete it->second;
    243. it->second = NULL;
    244. }
    245. m_chars.clear();
    246. for(size_t i = 0; i < m_pages.size(); i++)
    247. {
    248. delete m_pages[i];
    249. m_pages[i] = NULL;
    250. }
    251. m_pages.clear();
    252. }
    253. bool CFontManager::CFont::getCharInfo(int code, glyphMetrics *metrics, GLuint *texture, float coords[])
    254. {
    255. // 记忆化搜索
    256. TCharMap::iterator it = m_chars.find(code); //根据code获取CChar
    257. if(it != m_chars.end()) //找到
    258. {
    259. // 得到绘制信息
    260. it->second->getInfo(metrics, texture, coords); //字符、纹理、 坐标
    261. return true;
    262. }
    263. // 没有找到
    264. glyphMetrics gm;
    265. if(loadChar(code, &gm) == false) //加载字符
    266. {
    267. return false;
    268. }
    269. CChar *ch = new CChar();
    270. ch->m_code = code;
    271. ch->setInfo(&gm);
    272. // 追加到一张纸中
    273. for(size_t i = 0; i < m_pages.size(); i++)
    274. {
    275. CPage *page = m_pages[i];
    276. if(page->append(gm.width, gm.height, m_rgba, ch->m_coords))
    277. {
    278. // 这张纸还放的下
    279. ch->m_texture = page->getTexture();
    280. ch->getInfo(metrics, texture, coords);
    281. m_chars.insert(TCharMap::value_type(code, ch));
    282. return true;
    283. }
    284. }
    285. // 之前的纸都放不下了,新建一张纸
    286. CPage *page = new CPage();
    287. if(page->append(gm.width, gm.height, m_rgba, ch->m_coords))
    288. {
    289. ch->m_texture = page->getTexture();
    290. ch->getInfo(metrics, texture, coords);
    291. m_chars.insert(TCharMap::value_type(code, ch));
    292. m_pages.push_back(page);
    293. return true;
    294. }
    295. delete ch;
    296. delete page;
    297. return false;
    298. }
    299. int CFontManager::CFont::getFontTall(void)
    300. {
    301. return m_tall;
    302. }
    303. // bitmap.width 位图宽度
    304. // bitmap.rows 位图行数(高度)
    305. // bitmap.pitch 位图一行占用的字节数
    306. //MONO模式每1个像素仅用1bit保存,只有黑和白。
    307. //1个byte可以保存8个像素,1个int可以保存8*4个像素。
    308. void ConvertMONOToRGBA(FT_Bitmap *source, GLubyte *rgba)
    309. {
    310. GLubyte *s = source->buffer;
    311. GLubyte *t = rgba;
    312. for(GLuint y = source->rows; y > 0; y--)
    313. {
    314. GLubyte *ss = s;
    315. GLubyte *tt = t;
    316. for(GLuint x = source->width >> 3; x > 0; x--)
    317. {
    318. GLuint val = *ss;
    319. for(GLuint i = 8; i > 0; i--)
    320. {
    321. tt[0] = tt[1] = tt[2] = tt[3] = (val & (1 << (i - 1))) ? 0xFF : 0x00;
    322. tt += 4;
    323. }
    324. ss += 1;
    325. }
    326. GLuint rem = source->width & 7;
    327. if(rem > 0)
    328. {
    329. GLuint val = *ss;
    330. for(GLuint x = rem; x > 0; x--)
    331. {
    332. tt[0] = tt[1] = tt[2] = tt[3] = (val & 0x80) ? 0xFF : 0x00;
    333. tt += 4;
    334. val <<= 1;
    335. }
    336. }
    337. s += source->pitch;
    338. t += source->width * 4; //pitch
    339. }
    340. }
    341. //GRAY模式1个像素用1个字节保存。
    342. void ConvertGRAYToRGBA(FT_Bitmap *source, GLubyte *rgba)
    343. {
    344. for(GLuint y = 0; y < source->rows; y++)
    345. {
    346. for(GLuint x = 0; x < source->width; x++)
    347. {
    348. GLubyte *s = &source->buffer[(y * source->pitch) + x];
    349. GLubyte *t = &rgba[((y * source->pitch) + x) * 4];
    350. t[0] = t[1] = t[2] = 0xFF;
    351. t[3] = *s;
    352. }
    353. }
    354. }
    355. bool CFontManager::CFont::loadChar(int code, glyphMetrics *metrics)
    356. {
    357. FT_Error err;
    358. FT_UInt glyph_index = FT_Get_Char_Index(m_face, (FT_ULong)code); //得到下标
    359. if(glyph_index > 0)
    360. {
    361. //将一个字形图像装到字形槽中
    362. if((err = FT_Load_Glyph(m_face, glyph_index, FT_LOAD_DEFAULT)) == FT_Err_Ok)
    363. {
    364. FT_GlyphSlot glyph = m_face->glyph; //获得图形信息
    365. // 模式:抗锯齿为256级灰度;FT_RENDER_MODE_MONO黑白
    366. FT_Render_Mode render_mode = m_antialias ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO;
    367. if(m_antialias && m_bold) //加粗
    368. {
    369. if((err = FT_Outline_EmboldenXY(&glyph->outline, 60, 60)) != FT_Err_Ok)
    370. {
    371. printf("FT_Outline_EmboldenXY() Error %d\n", err);
    372. }
    373. }
    374. // 得到图形信息
    375. if((err = FT_Render_Glyph(glyph, render_mode)) == FT_Err_Ok)
    376. {
    377. FT_Bitmap *bitmap = &glyph->bitmap; //得到位图
    378. switch(bitmap->pixel_mode) //像素模式
    379. {
    380. case FT_PIXEL_MODE_MONO: //黑白
    381. {
    382. if(!m_antialias && m_bold)
    383. {
    384. // 加粗
    385. if((err = FT_Bitmap_Embolden(m_library, bitmap, 60, 0)) != FT_Err_Ok)
    386. {
    387. printf("FT_Bitmap_Embolden() Error %d\n", err);
    388. }
    389. }
    390. // 将黑白转成RGBA
    391. ConvertMONOToRGBA(bitmap, m_rgba);
    392. break;
    393. }
    394. case FT_PIXEL_MODE_GRAY: //灰度
    395. {
    396. ConvertGRAYToRGBA(bitmap, m_rgba); //灰度转RGBA
    397. break;
    398. }
    399. default:
    400. {
    401. memset(m_rgba, 0xFF, m_rgbaSize);
    402. break;
    403. }
    404. }
    405. metrics->width = bitmap->width;
    406. metrics->height = bitmap->rows;
    407. metrics->horiBearingX = glyph->bitmap_left;
    408. metrics->horiBearingY = glyph->bitmap_top;
    409. metrics->horiAdvance = glyph->advance.x >> 6;
    410. return true;
    411. }
    412. else
    413. {
    414. printf("FT_Render_Glyph() Error %d\n", err);
    415. }
    416. }
    417. else
    418. {
    419. printf("FT_Load_Glyph() Error %d\n", err);
    420. }
    421. }
    422. memset(metrics, 0, sizeof(glyphMetrics));
    423. return false;
    424. }
    425. //------------------------------------------------------------
    426. // CChar
    427. //------------------------------------------------------------
    428. void CFontManager::CFont::CChar::setInfo(glyphMetrics *metrics)
    429. {
    430. memcpy(&m_metrics, metrics, sizeof(glyphMetrics));
    431. }
    432. void CFontManager::CFont::CChar::getInfo(glyphMetrics *metrics, GLuint *texture, float coords[])
    433. {
    434. memcpy(metrics, &m_metrics, sizeof(glyphMetrics));
    435. *texture = m_texture;
    436. memcpy(coords, m_coords, sizeof(float) * 4);
    437. }
    438. //------------------------------------------------------------
    439. // CPage 开辟一页的纹理,来存放多个字符
    440. //------------------------------------------------------------
    441. CFontManager::CFont::CPage::CPage() //一页对应一个纹理
    442. {
    443. m_wide = m_tall = 256; //高
    444. m_posx = m_posy = 0;
    445. // In a line, for a max height character
    446. m_maxCharTall = 0;
    447. glGenTextures(1, &m_texture); // Using your API here
    448. glBindTexture(GL_TEXTURE_2D, m_texture);
    449. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_wide, m_tall, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    450. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    451. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    452. }
    453. CFontManager::CFont::CPage::~CPage()
    454. {
    455. // free the texture
    456. }
    457. bool CFontManager::CFont::CPage::append(int wide, int tall, byte *rgba, float coords[])
    458. {
    459. if(m_posy + tall > m_tall)
    460. {
    461. // not enough line space in this page
    462. return false;
    463. }
    464. // If this line is full ...
    465. if(m_posx + wide > m_wide)
    466. {
    467. int newLineY = m_posy + m_maxCharTall;
    468. if(newLineY + tall > m_tall)
    469. {
    470. // No more space for new line in this page, should allocate a new one
    471. return false;
    472. }
    473. // Begin in new line
    474. m_posx = 0;
    475. m_posy = newLineY;
    476. // Reset
    477. m_maxCharTall = 0;
    478. }
    479. glBindTexture(GL_TEXTURE_2D, m_texture);
    480. glTexSubImage2D(GL_TEXTURE_2D, 0, m_posx, m_posy, wide, tall, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
    481. // 该字符的位置
    482. coords[0] = m_posx / (float)m_wide; // left
    483. coords[1] = m_posy / (float)m_tall; // top
    484. coords[2] = (m_posx + wide) / (float)m_wide; // right
    485. coords[3] = (m_posy + tall) / (float)m_tall; // bottom
    486. m_posx += wide;
    487. if(tall > m_maxCharTall)
    488. {
    489. m_maxCharTall = tall;
    490. }
    491. return true;
    492. }
    493. GLuint CFontManager::CFont::CPage::getTexture(void)
    494. {
    495. return m_texture;
    496. }
    497. //------------------------------------------------------------
    498. // CFontManager
    499. //------------------------------------------------------------
    500. CFontManager::CFontManager()
    501. {
    502. m_library = NULL;
    503. }
    504. CFontManager::~CFontManager()
    505. {
    506. release();
    507. }
    508. bool CFontManager::initialize(void)
    509. {
    510. FT_Error err;
    511. if((err = FT_Init_FreeType(&m_library)) != FT_Err_Ok) //初始化一个库
    512. {
    513. printf("FT_Init_FreeType() Error %d\n", err);
    514. return false;
    515. }
    516. return true;
    517. }
    518. void CFontManager::release(void)
    519. {
    520. FT_Error err;
    521. for(size_t i = 0; i < m_fonts.size(); i++)
    522. {
    523. delete m_fonts[i];
    524. m_fonts[i] = NULL;
    525. }
    526. m_fonts.clear();
    527. if((err = FT_Done_FreeType(m_library)) != FT_Err_Ok)
    528. {
    529. printf("FT_Done_FreeType() Error %d\n");
    530. }
    531. }
    532. int CFontManager::createFont(const char *filename, int face, int tall, bool bold,
    533. bool italic, bool antialias)
    534. {
    535. // 新建字体
    536. CFont *font = new CFont();
    537. if(font->create(m_library, filename, face, tall, bold, italic, antialias) != true)
    538. {
    539. delete font; //创建失败
    540. return 0;
    541. }
    542. // 放入字体数组,进行管理
    543. m_fonts.push_back(font);
    544. return m_fonts.size();
    545. }
    546. #define CONVERT_FONT_INDEX(x) (((x) < 1 || (x) > (int)m_fonts.size()) ? -1 : (x) - 1)
    547. bool CFontManager::getCharInfo(int font_index, int code, int *wide, int *tall, int *horiBearingX, int *horiBearingY, int *horiAdvance, GLuint *texture, float coords[])
    548. {
    549. int i = CONVERT_FONT_INDEX(font_index); //转换字符的下标
    550. if(i == -1)
    551. {
    552. return false;
    553. }
    554. CFont *font = m_fonts[i]; //获得字体
    555. glyphMetrics metrics; //绘制信息
    556. if(font->getCharInfo(code, &metrics, texture, coords) == false) //获得绘制信息
    557. {
    558. return false;
    559. }
    560. // 解析绘制信息
    561. *wide = metrics.width;
    562. *tall = metrics.height;
    563. *horiBearingX = metrics.horiBearingX;
    564. *horiBearingY = metrics.horiBearingY;
    565. *horiAdvance = metrics.horiAdvance;
    566. return true;
    567. }
    568. int CFontManager::getFontTall(int font_index)
    569. {
    570. int i = CONVERT_FONT_INDEX(font_index);
    571. if(i == -1)
    572. {
    573. return false;
    574. }
    575. CFont *font = m_fonts[i];
    576. return font->getFontTall();
    577. }
    578. CFontManager g_FontManager;
    579. int char_font;
    580. void init(void)
    581. {
    582. // 清除颜色
    583. glClearColor(0.0, 0.0, 0.0, 0.0);
    584. // 混合模式
    585. glEnable(GL_BLEND);
    586. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    587. // 初始化一个库
    588. g_FontManager.initialize();
    589. // 创建字体,返回字体集的个数
    590. char_font = g_FontManager.createFont("C:\\WINDOWS\\Fonts\\simhei.ttf",
    591. 0,
    592. 32, //字体高度
    593. false,
    594. false,
    595. true);
    596. if(char_font == 0)//创建失败
    597. {
    598. printf("createFont failed\n");
    599. }
    600. }
    601. wchar_t ciphertext[] =
    602. {
    603. L"你好吗?\n"
    604. L"温馨提示:\n"
    605. L"不要忘记明天上午 7:30-12:00 在公司的体检哦~\n"
    606. L"体检前一天不要暴饮暴食,不要喝酒,不要进食太甜太咸的食物\n"
    607. L"以免影响体检结果\n"
    608. L"晚上10点后一般要求禁食。\n"
    609. L"体检当天早晨千万不要吃早餐!\n"
    610. L"不要吃早餐!\n"
    611. L"不要吃早餐!\n"
    612. L"绘制位置。忘记了,可费解为无法\n"
    613. };
    614. //#define DRAW_PAGE
    615. /*!
    616. * \brief: draw_string
    617. *
    618. * \param: x 绘制位置
    619. * \param: y
    620. * \param: font 字体
    621. * \param: string 字符串
    622. * \returns: void
    623. * \author: wangqi
    624. * \date: 2020/06/02 14:18
    625. *
    626. * TODO:
    627. *
    628. */
    629. void draw_string(int x, int y, int font_index, wchar_t *string)
    630. {
    631. if(!font_index) //字体
    632. {
    633. return;
    634. }
    635. int tall = g_FontManager.getFontTall(font_index); //得到字符高:初始化字体时设置的
    636. // 绘制的初始位置
    637. int dx = x;
    638. int dy = y;
    639. GLuint sglt = 0; //纹理编号,第几张纸
    640. while(*string) // 遍历字符串
    641. {
    642. if(*string == L'\n') //回车
    643. {
    644. string++;
    645. dx = x; //绘制的初始位置x
    646. dy += tall + 2; //行间距
    647. continue;
    648. }
    649. int cw, ct; //宽度、高度
    650. int bx, by, av;
    651. GLuint glt; //该字符所在的纸张
    652. float crd[4]; //该字符的坐标
    653. //得到字符信息
    654. if(!g_FontManager.getCharInfo(font_index, *string, &cw, &ct, &bx, &by, &av, &glt, crd))
    655. {
    656. string++;
    657. continue;
    658. }
    659. //大多数情况下多个字符都在同一个纹理中,避免频繁绑定纹理,可以提高效率
    660. if(glt != sglt) //和原来的不一样,则绑定当前的纹理
    661. {
    662. glBindTexture(GL_TEXTURE_2D, glt); //绑定纹理
    663. sglt = glt;
    664. }
    665. int px = dx + bx;
    666. int py = dy - by;
    667. // 开始绘制四边形
    668. glBegin(GL_QUADS);
    669. // 第一个点
    670. glTexCoord2f(crd[0], crd[1]); //指定该字符在纹理中的坐标,阈值为[0, 1]
    671. glVertex3f(px, py, 0.0f); //对应画布中的位置
    672. // 第二个点
    673. glTexCoord2f(crd[2], crd[1]);
    674. glVertex3f(px + cw, py, 0.0f);
    675. // 第三个点
    676. glTexCoord2f(crd[2], crd[3]);
    677. glVertex3f(px + cw, py + ct, 0.0f);
    678. // 第四个点
    679. glTexCoord2f(crd[0], crd[3]);
    680. glVertex3f(px, py + ct, 0.0f);
    681. // 结束绘制
    682. glEnd();
    683. dx += av;
    684. string++;
    685. }
    686. }
    687. void draw_page_texture(int x, int y, GLuint glt)
    688. {
    689. if(!glt)
    690. {
    691. glDisable(GL_TEXTURE_2D); //关闭
    692. glColor4f(0.2, 0.2, 0.2, 1.0);
    693. }
    694. else
    695. {
    696. glEnable(GL_TEXTURE_2D); //开启
    697. glBindTexture(GL_TEXTURE_2D, glt); //绑定纹理
    698. glColor4f(1.0, 1.0, 1.0, 1.0);
    699. }
    700. int w = 256;
    701. int t = 256;
    702. glBegin(GL_QUADS);
    703. glTexCoord2f(0.0, 0.0);
    704. glVertex3f(x, y, 0.0f);
    705. glTexCoord2f(1.0, 0.0);
    706. glVertex3f(x + w, y, 0.0f);
    707. glTexCoord2f(1.0, 1.0);
    708. glVertex3f(x + w, y + t, 0.0f);
    709. glTexCoord2f(0.0, 1.0);
    710. glVertex3f(x, y + t, 0.0f);
    711. glEnd();
    712. }
    713. void display(void)
    714. {
    715. // 清除缓存
    716. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    717. // 开启纹理
    718. glEnable(GL_TEXTURE_2D);
    719. // 设置画笔颜色,即字体颜色
    720. glColor4f(0.0, 0.0, 1.0, 1.0);
    721. // 绘制字符串
    722. draw_string(10, 30, char_font, ciphertext);
    723. // 绘制整个纹理
    724. draw_page_texture(10, 350, 0); //绘制一个背景
    725. draw_page_texture(10, 350, 1); //绘制第一张纸(即第一个纹理)
    726. draw_page_texture(276, 350, 0); //绘制一个背景
    727. draw_page_texture(276, 350, 2); //绘制第二张纸(即第二个纹理)
    728. glutSwapBuffers();
    729. glutPostRedisplay();
    730. }
    731. void reshape(int width, int height)
    732. {
    733. glViewport(0, 0, width, height);
    734. glMatrixMode(GL_PROJECTION);
    735. glLoadIdentity();
    736. gluOrtho2D(0, width, height, 0);
    737. glMatrixMode(GL_MODELVIEW);
    738. }
    739. void keyboard(unsigned char key, int x, int y)
    740. {
    741. }
    742. void main(int argc, char **argv)
    743. {
    744. glutInitWindowPosition(200, 200);
    745. glutInitWindowSize(1200, 680);
    746. glutInit(&argc, argv);
    747. glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
    748. glutCreateWindow("FreeType OpenGL");
    749. init();
    750. glutDisplayFunc(display);
    751. glutReshapeFunc(reshape);
    752. glutKeyboardFunc(keyboard);
    753. glutMainLoop();
    754. }