搜索
bottom↓
回复: 0

《I.MX6U嵌入式Linux C应用编程指南》第二十四章 在LCD上显示字符

[复制链接]

出0入234汤圆

发表于 2021-8-26 15:57:15 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2021-8-26 15:57 编辑

1)实验平台:正点原子i.MX6ULL Linux阿尔法开发板
2)  章节摘自【正点原子】I.MX6U嵌入式Linux C应用编程指南
3)购买链接:https://item.taobao.com/item.htm?&id=603672744434
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子Linux技术交流群:1027879335
1.png

2.jpg


3.png


第二十四章 在LCD上显示字符

前面几个章节向大家介绍了如何在LCD屏上显示图像,本章我们就来学习下,如何在LCD屏上显示字符,譬如数字、字母以及中文字符等,相信很多读者都已经迫不及待的想要了解了。OK,废话不多说直接开干!
本章将会讨论如下主题。
使用原始的方式:自己取模显示字符
使用freetype访问字体文件;
freetype简介;
freetype移植;
freetype的使用介绍。
1.1原始方式:取模显示字符
       LCD显示屏是由width * height个像素点构成的,显示字符,一个非常容易想到的方法便是对字符取模,然后在LCD屏上打点显示字符;如果大家以前学习过单片机,想必接触过一些显示屏,譬如oled、或者其它一些点阵式的显示屏,其实这些显示屏显示字符的原理都是一样的,如下所示:
第二十四章 在LCD上显示字符351.png

图 24.1.1 字符点阵图

       我们可以通过一些字符取模软件获取到字符的子模;所谓子模,其实就是一个二维数组,用于表示字符点阵中,哪些小方块应该要填充颜色、哪些小方块不填充颜色。譬如上图“正”字符点阵,这是一个宽度为64(64个小方块)、高度为86(86个小方块)的字符点阵,我们会使用一个二维数组来表示这个字符点阵:
  1. unsigned char arr[86][8];
复制代码

       也就是一个86行8列的unsigned char类型数组,数组存储的其实就是字符的位图数据,字符点阵中的每一个小方块对应一个bit位,因为一行一共有64个小方块、也就对应8个字节(8 * 8 = 64);将填充颜色的方块使用1表示、不填充颜色的方块使用0来表示,所以一个小方块刚好可以使用一个bit位来描述。
       以上给大家简单地介绍了字符点阵的问题,相信绝大部分读者都知道这些基础的东西,其实本不太想讲这些内容,但是考虑到可能有些读者确实就真的没接触这些,所以还是简单地提一下。
       我们编写一个简单的程序去测试下,网上有很多的这种字符取模的小软件,大家可以找一下,我们用这个取模软件,获取几个字符的子模,然后在我们的LCD屏上去显示这些字符。
编写应用程序
       本例程源码对应的路径为:开发板光盘->11、Linux C应用编程例程源码->24_freetype->show_char.c。
       示例代码 24.1.1 以取模打点方式在LCD上显示字符
  1. /***************************************************************
  2. Copyright © ALIENTEK Co., Ltd. 1998-2021. All rights reserved.
  3. 文件名 : show_character.c
  4. 作者 : 邓涛
  5. 版本 : V1.0
  6. 描述 : 使用取模软件获取字符的子模,在LCD上显示字符
  7. 其他 : 无
  8. 论坛 : <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  9. 日志 : 初版 V1.0 2021/7/09 邓涛创建
  10. ***************************************************************/

  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #include <fcntl.h>
  16. #include <unistd.h>
  17. #include <sys/ioctl.h>
  18. #include <string.h>
  19. #include <errno.h>
  20. #include <string.h>
  21. #include <sys/mman.h>
  22. #include <linux/fb.h>

  23. #define FB_DEV      "/dev/fb0"      //LCD设备节点

  24. static int width;                       //LCD宽度
  25. static int height;                      //LCD高度
  26. static unsigned short *screen_base = NULL;//LCD显存基地址

  27. static unsigned char ch_char1[86][8] = {
  28. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  29. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  30. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  31. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  32. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  33. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  34. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  35. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  36. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  37. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  38. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  39. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  40. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  41. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  42. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  43. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  44. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  45. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  46. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  47. {0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
  48. {0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
  49. {0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
  50. {0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
  51. {0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
  52. {0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
  53. {0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
  54. {0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
  55. {0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
  56. {0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
  57. {0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
  58. {0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
  59. {0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
  60. {0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
  61. {0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
  62. {0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
  63. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  64. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  65. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  66. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  67. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  68. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  69. {0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
  70. {0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
  71. {0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
  72. {0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
  73. {0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
  74. {0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
  75. {0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
  76. {0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
  77. {0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
  78. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  79. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  80. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  81. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  82. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  83. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  84. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  85. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  86. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  87. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  88. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  89. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  90. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  91. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  92. {0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
  93. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  94. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  95. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  96. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  97. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  98. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  99. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  100. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  101. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  102. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  103. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  104. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  105. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  106. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  107. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  108. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  109. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  110. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  111. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  112. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  113. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"正",0*/
  114. };

  115. static unsigned char ch_char2[86][8] = {
  116. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  117. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  118. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  119. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  120. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  121. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  122. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  123. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  124. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  125. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  126. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  127. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  128. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  129. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  130. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  131. {0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
  132. {0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
  133. {0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
  134. {0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
  135. {0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
  136. {0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
  137. {0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
  138. {0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
  139. {0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
  140. {0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
  141. {0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
  142. {0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
  143. {0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
  144. {0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
  145. {0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
  146. {0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
  147. {0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
  148. {0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
  149. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  150. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  151. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  152. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  153. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  154. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  155. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  156. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  157. {0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
  158. {0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
  159. {0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
  160. {0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
  161. {0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
  162. {0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
  163. {0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
  164. {0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
  165. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  166. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  167. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  168. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  169. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  170. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  171. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  172. {0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
  173. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  174. {0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00},
  175. {0x00,0x01,0x00,0x00,0xC0,0x00,0x7C,0x00},
  176. {0x00,0x0F,0x00,0x03,0xF8,0x00,0x7F,0x00},
  177. {0x80,0x3F,0xF0,0x03,0xFF,0x81,0xFF,0x00},
  178. {0x80,0xFF,0xFC,0x03,0xFE,0x81,0xFF,0x00},
  179. {0xC0,0x7F,0xFC,0x07,0xFE,0x03,0xFF,0x01},
  180. {0xC0,0x7F,0xF8,0x07,0xFC,0x03,0xFF,0x01},
  181. {0xE0,0x3F,0xF8,0x0F,0xFC,0x07,0xFE,0x01},
  182. {0xE0,0x3F,0xF0,0x0F,0xFC,0x07,0xFE,0x03},
  183. {0xF0,0x1F,0xF0,0x0F,0xF8,0x07,0xFE,0x03},
  184. {0xF0,0x1F,0xF0,0x1F,0xF8,0x0F,0xFC,0x07},
  185. {0xF8,0x0F,0xE0,0x1F,0xF8,0x0F,0xFC,0x07},
  186. {0xF8,0x0F,0xE0,0x1F,0xF0,0x0F,0xF8,0x0F},
  187. {0xFC,0x07,0xE0,0x3F,0xF0,0x1F,0xF8,0x0F},
  188. {0xFC,0x07,0xE0,0x3F,0xF0,0x1F,0xF0,0x1F},
  189. {0xFC,0x03,0xC0,0x0F,0xE0,0x03,0xF0,0x07},
  190. {0xF0,0x03,0xC0,0x00,0x60,0x00,0xE0,0x01},
  191. {0xE0,0x01,0x00,0x00,0x00,0x00,0x20,0x00},
  192. {0x80,0x01,0x00,0x00,0x00,0x00,0x00,0x00},
  193. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  194. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  195. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  196. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  197. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  198. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  199. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  200. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  201. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"点",1*/
  202. };

  203. static unsigned char ch_char3[86][8] = {
  204. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  205. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  206. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  207. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  208. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  209. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  210. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  211. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  212. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  213. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  214. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  215. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  216. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  217. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  218. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  219. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  220. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  221. {0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  222. {0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  223. {0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  224. {0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  225. {0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  226. {0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  227. {0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  228. {0x80,0x7F,0x00,0x80,0xFF,0x00,0x00,0x00},
  229. {0x80,0x7F,0x00,0xC0,0x7F,0x00,0x00,0x00},
  230. {0x80,0x7F,0x00,0xC0,0x7F,0x00,0x00,0x00},
  231. {0x80,0x7F,0x00,0xC0,0x3F,0x00,0x00,0x00},
  232. {0x80,0x7F,0x00,0xE0,0x3F,0x00,0x00,0x00},
  233. {0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  234. {0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  235. {0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  236. {0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  237. {0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  238. {0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  239. {0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
  240. {0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
  241. {0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
  242. {0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
  243. {0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  244. {0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  245. {0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  246. {0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  247. {0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  248. {0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  249. {0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
  250. {0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
  251. {0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
  252. {0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
  253. {0xC0,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  254. {0xC0,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  255. {0xC0,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  256. {0xC0,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  257. {0xC0,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  258. {0xC0,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
  259. {0xC0,0x3F,0x00,0x00,0xFC,0x07,0x06,0x00},
  260. {0xC0,0x3F,0x80,0x00,0xFC,0x07,0x07,0x00},
  261. {0xC0,0x3F,0x80,0x03,0xFC,0x87,0x0F,0x00},
  262. {0xE0,0x3F,0xC0,0x07,0xFC,0xE7,0x1F,0x00},
  263. {0xE0,0x3F,0xE0,0x1F,0xFC,0xF7,0x3F,0x00},
  264. {0xE0,0x3F,0xF0,0x3F,0xFC,0xE7,0x7F,0x00},
  265. {0xE0,0x1F,0xF0,0x3F,0xFC,0xE7,0xFF,0x00},
  266. {0xF0,0x1F,0xF8,0x1F,0xFC,0xC7,0xFF,0x00},
  267. {0xF0,0x1F,0xFC,0x0F,0xFC,0x87,0xFF,0x01},
  268. {0xF0,0x1F,0xFE,0x0F,0xFC,0x07,0xFF,0x03},
  269. {0xF0,0x1F,0xFF,0x07,0xFC,0x07,0xFF,0x07},
  270. {0xF8,0x8F,0xFF,0x03,0xFC,0x07,0xFE,0x0F},
  271. {0xF8,0x8F,0xFF,0x01,0xFE,0x07,0xFC,0x0F},
  272. {0xFC,0xCF,0xFF,0xFF,0xFF,0x07,0xF8,0x1F},
  273. {0xFC,0xE7,0xFF,0xFE,0xFF,0x07,0xF8,0x0F},
  274. {0xFE,0xC7,0x7F,0xFE,0xFF,0x03,0xF0,0x07},
  275. {0xFE,0x83,0x3F,0xFC,0xFF,0x03,0xE0,0x03},
  276. {0xF8,0x03,0x1F,0xFC,0xFF,0x03,0xE0,0x00},
  277. {0xF0,0x03,0x0E,0xFC,0xFF,0x01,0x40,0x00},
  278. {0xE0,0x01,0x04,0xFC,0xFF,0x00,0x00,0x00},
  279. {0xC0,0x00,0x00,0xFC,0x3F,0x00,0x00,0x00},
  280. {0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  281. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  282. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  283. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  284. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  285. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  286. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  287. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  288. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  289. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"原",2*/
  290. };

  291. static unsigned char ch_char4[86][8] = {
  292. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  293. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  294. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  295. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  296. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  297. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  298. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  299. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  300. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  301. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  302. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  303. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  304. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  305. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  306. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  307. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  308. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  309. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  310. {0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
  311. {0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
  312. {0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
  313. {0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
  314. {0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
  315. {0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
  316. {0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
  317. {0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
  318. {0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x00},
  319. {0x00,0x00,0x00,0x00,0x00,0xFF,0x3F,0x00},
  320. {0x00,0x00,0x00,0x00,0xC0,0xFF,0x1F,0x00},
  321. {0x00,0x00,0x00,0x00,0xF0,0xFF,0x07,0x00},
  322. {0x00,0x00,0x00,0x00,0xF8,0xFF,0x03,0x00},
  323. {0x00,0x00,0x00,0x00,0xFE,0xFF,0x00,0x00},
  324. {0x00,0x00,0x00,0x80,0xFF,0x7F,0x00,0x00},
  325. {0x00,0x00,0x00,0xC0,0xFF,0x1F,0x00,0x00},
  326. {0x00,0x00,0x00,0xF0,0xFF,0x07,0x00,0x00},
  327. {0x00,0x00,0x00,0xF8,0xFF,0x03,0x00,0x00},
  328. {0x00,0x00,0x00,0xF8,0xFF,0x00,0x00,0x00},
  329. {0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x00},
  330. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  331. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  332. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  333. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  334. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  335. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  336. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  337. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  338. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  339. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  340. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  341. {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
  342. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  343. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  344. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  345. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  346. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  347. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  348. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  349. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  350. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  351. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  352. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  353. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  354. {0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
  355. {0x00,0x00,0x00,0xF8,0x0F,0x00,0x00,0x00},
  356. {0x00,0x00,0x00,0xFC,0x0F,0x00,0x00,0x00},
  357. {0x00,0x10,0x00,0xFE,0x0F,0x00,0x00,0x00},
  358. {0x00,0xF0,0xFF,0xFF,0x0F,0x00,0x00,0x00},
  359. {0x00,0xF0,0xFF,0xFF,0x0F,0x00,0x00,0x00},
  360. {0x00,0xE0,0xFF,0xFF,0x0F,0x00,0x00,0x00},
  361. {0x00,0xE0,0xFF,0xFF,0x07,0x00,0x00,0x00},
  362. {0x00,0xE0,0xFF,0xFF,0x07,0x00,0x00,0x00},
  363. {0x00,0xE0,0xFF,0xFF,0x03,0x00,0x00,0x00},
  364. {0x00,0xC0,0xFF,0xFF,0x01,0x00,0x00,0x00},
  365. {0x00,0xC0,0xFF,0xFF,0x00,0x00,0x00,0x00},
  366. {0x00,0xC0,0xFF,0x3F,0x00,0x00,0x00,0x00},
  367. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  368. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  369. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  370. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  371. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  372. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  373. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  374. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  375. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  376. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  377. {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"子",3*/
  378. };

  379. #define argb8888_to_rgb565(color)   ({ \
  380.             unsigned int temp = (color); \
  381.             ((temp & 0xF80000UL) >> 8) | \
  382.             ((temp & 0xFC00UL) >> 5) | \
  383.             ((temp & 0xF8UL) >> 3); \
  384.             })

  385. /********************************************************************
  386. * 函数名称: lcd_draw_character
  387. * 功能描述: 在LCD屏指定位置处(x, y)画字符, 参数color指定字符的颜色
  388.               指针ch指向字符对应的子模数组、参数w、h分别表示字符的宽度和高度
  389. * 输入参数: color
  390. * 返 回 值: 无
  391. ********************************************************************/
  392. static void lcd_draw_character(unsigned int x, unsigned int y,
  393.             const unsigned char *ch, unsigned int w,
  394.             unsigned int h, unsigned int color)
  395. {
  396.     unsigned short rgb565_color = argb8888_to_rgb565(color);//得到RGB565颜色值
  397.     unsigned long temp;
  398.     unsigned int end_x, end_y;
  399.     int j;
  400.     int columns;

  401.     /**
  402.     计算出二维数组有多少列
  403.     参数w表示的是字符的宽度,1个宽度表示的是1个bit位
  404.     并不是一个字节,这里要注意,如果宽度不是byte单位的整数倍
  405.     通常会补零
  406.     **/
  407.     columns = w / 8;    //1byte=8bit
  408.     if (0 != w % 8) columns++;

  409.     /* 对参数进行限定 */
  410.     if (w < 1 || h < 1) return;
  411.     if (x >= width || y >= height) return;

  412.     /* 计算出结束坐标位置 */
  413.     end_x = x + w - 1;
  414.     end_y = y + h - 1;

  415.     /* 对结束坐标位置进行限定 */
  416.     if (end_x >= width)
  417.         end_x = width - 1;
  418.     if (end_y >= height)
  419.         end_y = height - 1;

  420.     /* 计算有效宽度 */
  421.     h = end_y - y + 1;
  422.     w = end_x - x + 1;

  423.     /* 打点 */
  424.     temp = y * width + x; //定位到起点
  425.     for (y = 0; y < h; y++, temp += width) {

  426.         for (x = 0, j = 0; x < w; ) {

  427.             if (*(ch + y * columns + j) & (0x1 << (x % 8)))
  428.                 screen_base[temp + x] = rgb565_color;
  429.             x++;
  430.             if (0 == x % 8) j++;
  431.         }
  432.     }
  433. }

  434. int main(void)
  435. {
  436.     struct fb_var_screeninfo fb_var = {0};
  437.     struct fb_fix_screeninfo fb_fix = {0};
  438.     unsigned long screen_size;
  439.     int fd;

  440.     /* 打开framebuffer设备 */
  441.     fd = open(FB_DEV, O_RDWR);
  442.     if (0 > fd) {
  443.         fprintf(stderr, "open error: %s: %s\n", FB_DEV, strerror(errno));
  444.         exit(EXIT_FAILURE);
  445.     }

  446.     /* 获取framebuffer设备信息 */
  447.     ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
  448.     ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);

  449.     screen_size = fb_fix.line_length * fb_var.yres;
  450.     width = fb_var.xres;
  451.     height = fb_var.yres;

  452.     /* 内存映射 */
  453.     screen_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  454.     if (MAP_FAILED == (void *)screen_base) {
  455.         perror("mmap error");
  456.         close(fd);
  457.         exit(EXIT_FAILURE);
  458.     }

  459.     /* LCD背景刷白 */
  460.     memset(screen_base, 0xFF, screen_size);

  461.     /* 显示字符 */
  462.     int x = width * 0.5 - 128;
  463.     int y = height * 0.5 - 43;
  464.     lcd_draw_character(x, y, (unsigned char *)ch_char1, 64, 86, 0xFF00FF);
  465.     lcd_draw_character(x + 64, y, (unsigned char *)ch_char2, 64, 86, 0xFF00FF);
  466.     lcd_draw_character(x + 128, y, (unsigned char *)ch_char3, 64, 86, 0xFF00FF);
  467.     lcd_draw_character(x + 192, y, (unsigned char *)ch_char4, 64, 86, 0xFF00FF);

  468.     /* 退出程序 */
  469.     munmap(screen_base, screen_size);
  470.     close(fd);
  471.     return 0;
  472. }
复制代码

       程序中定义了4个二维数组ch_char1、ch_char2、ch_char3、ch_char4,这4个二维数组便是4个中文字符“正点原子”的子模,字符宽度为64、高度为86。这种字符取模软件就不给大家介绍了,网上很多这种软件、用法方法通常也非常简单,我们在取模的时候,选择从左到右、从上到下这种方式进行取模,上述示例代码便是按照这种方式进行解析的。
       调用自定义函数lcd_draw_character在LCD上显示字符、并且是居中显示。
       lcd_draw_character函数中会子模数组进行解析,代码也没什么难度,就不讲解了。
       编译上述示例代码,将编译得到的可执行文件拷贝到开发板Linux系统的用户家目录下,执行该测试程序:
第二十四章 在LCD上显示字符20154.png

图 24.1.2 执行测试程序

此时开发板LCD屏上会显示“正点原子”四个中文字符,如下图所示:
第二十四章 在LCD上显示字符20248.png

图 24.1.3 LCD屏显示中文字符

       使用这种方式还是非常简单地,自己取模、自己写函数打点显示,但是很low!好歹咋们玩的也是操作系统,实际的应用项目一般肯定不会这么干,除非你的程序就是显示那么几个固定的字符。那既然如此,为什么还要给大家介绍这种方式呢?一来是让大家理解字符显示的原理;二来,一些实际的应用项目中可能确实就是这么做的,不同的项目它的需求不同,不能一概而论!
       操作系统中通常都会有很多的字体文件,譬如Windows系统“C:\Windows\Fonts”目录下就有很多的字体文件,如下所示:
第二十四章 在LCD上显示字符20547.png

图 24.1.4 Windows系统中的字体文件

       字体文件的格式也有很多种,譬如otf、ttf、ttc等,这里就不给大家列举了,有兴趣的读者自己百度一下;Linux系统中,字体文件通常会放在/usr/share/fonts目录下,有了字体文件之后,我们就不需要再对字符进行取模了,它们已经编码进了字体文件中,我们只需要解析字体文件、访问字体文件,从字体文件中读取出字符的位图数据即可!
       当然,这些复杂的解析过程并不需要我们自己去实现,有很多开源的字体引擎可以帮助我们来处理这些复杂的解析过程,譬如freetype库,下小节将向大家介绍如何使用freetype访问字体文件。
1.2freetype简介
       FreeType一个完全免费(开源)的软件字体引擎库,设计小巧、高效、高度可定制且可移植,它提供了统一的接口来访问多种不同格式的字体文件。它提供了一个简单、易于使用且统一的接口来访问字体文件的内容,从而大大简化了这些任务。
       请注意,“FreeType”也称为“FreeType 2”,以区别于旧的、已弃用的“FreeType 1”库,Freetype 1库已经不再维护和支持了。
1.3freetype移植
       本小节我们来移植FreeType,将FreeType移植到我们的开发板根文件系统中;事实上,开发板出厂系统已经移植好了这些库,但是版本稍微太低了,作为学习者,我们应该要学会自己动手移植,这个是很重要的工作;从上几个章节的学习内容可知,我们很多的编程工作都是基于别人写好的库、调用库函数来实现应用程序的功能,因为Linux软件生态做得非常好,有很多免费、开源的库是可以使用的,不用自己去捣鼓这些东西;所以作为一个嵌入式Linux软件开发人员,学会移植就显得很重要了。
1.3.1下载FreeType源码
       开发板出厂系统中,FreeType的版本为2.6,这个版本稍微有点低,我们选择移植2.8版本的FreeType。
       进入到https://download.savannah.gnu.org/releases/freetype/链接地址,如下所示:
第二十四章 在LCD上显示字符21529.png

图 24.3.1 FreeType源码下载地址

       往下翻找到2.8版本,选择freetype-2.8.tar.gz压缩文件:
第二十四章 在LCD上显示字符21636.png

图 24.3.2 找到2.8版本

       点击freetype-2.8.tar.gz下载源码,下载完成后我们将会得到2.8版本的FreeType源码包:
第二十四章 在LCD上显示字符21754.png

图 24.3.3 freetype-2.8.tar.gz

       大家要自己动手下载,开发板资料盘中是没有提供这些源码包的,包括前面几个章节介绍的库,譬如libpng、libjpeg以及tslib等,都没有提供它们的源码压缩文件。
1.3.2交叉编译FreeType源码
       将下载好的freetype-2.8.tar.gz压缩文件拷贝到Ubuntu系统的用户家目录下,如下所示:
第二十四章 在LCD上显示字符21979.png

图 24.3.4 将源码包拷贝到Ubuntu系统

       在tools目录下创建一个名为freetype的目录,把它作为FreeType的安装目录:
第二十四章 在LCD上显示字符22095.png

图 24.3.5 创建FreeType的安装目录

        执行命令将freetype-2.8.tar.gz解压开来:
  1. tar -xzf freetype-2.8.tar.gz
复制代码
第二十四章 在LCD上显示字符22224.png

图 24.3.6 将freetype-2.8.tar.gz压缩文件解压

       解压成功之后便会得到FreeType的源码目录freetype-2.8。
       进入到freetype-2.8目录,老规矩,同样是三部曲:配置、编译、安装。
       首先对交叉编译工具的环境进行初始化,前面章节内容已经提过很多次了,使用交叉编译器之前,必须要对其环境进行初始化(如果当前终端已经初始化过了,则无需再次进行初始化)。
       FreeType库基于模块化设计,意味着我们可以对其进行裁剪,将不需要的功能模块从配置中移除,减小库文件的体积;除此之外,FreeType还支持很多配置选项,如果大家想要对FreeType做一些自定义配置或者对其进行裁剪,可以参考FreeType源码目录下docs/CUSTOMIZE文档,该文件对此有比较详细的说明,建议大家看一看,如果有需求的话。docs目录下还有其它很多的说明文档,也都可以读一读。
       这里我们简单地配置一下,打开include/freetype/config/ftoption.h文件,如下所示:
  1. vi include/freetype/config/ftoption.h
复制代码
第二十四章 在LCD上显示字符22762.png

图 24.3.7 ftoption.h配置文件

       该文件定义了很多的配置宏,我们可以选择使能或禁用这些配置选项,具体配置哪些功能,大家自己去研究,每一个配置宏都有详细地解释说明。这里我们打开以下两个配置宏:
  1. #define  FT_CONFIG_OPTION_SYSTEM_ZLIB
  2. #define  FT_CONFIG_OPTION_USE_PNG
复制代码

       大家找到这两个宏,默认情况下,这两个都被注释掉了,所以是没有使能的;把这两个宏的注释去掉,使能这两个配置宏。
       第一个配置宏表示使用系统安装的zlib库,因为FreeType支持Gzip压缩文件,会使用到zlib库,zlib之前我们移植好了;第二个配置宏表示支持PNG bitmap位图,因为FreeType可以加载PNG格式的彩色位图字形,需要依赖于libpng库,这个库前面我们也是移植好了。
       配置好之后,保存、退出ftoption.h文件,接着执行如下命令对FreeType工程源码进行配置:
  1. ./configure --prefix=/home/dt/tools/freetype/ --host=arm-poky-linux-gnueabi --with-zlib=yes --with-bzip2=no --with-png=yes --with-harfbuzz=no ZLIB_CFLAGS="-I/home/dt/tools/zlib/include -L/home/dt/tools/zlib/lib" ZLIB_LIBS=-lz LIBPNG_CFLAGS="-I/home/dt/tools/png/include -L/home/dt/tools/png/lib" LIBPNG_LIBS=-lpng
复制代码

        这个配置命令很长,简单地提一下,具体的细节大家可以执行"./configure --help"查看配置帮助信息。
        --prefix选项指定FreeType库的安装目录;--host选项设置为交叉编译器名称的前缀,这两个选项前面几个章节内容都已经给大家详细地解释过。
       --with-zlib=yes表示使用zlib;
       --with-bzip2=no表示不使用bzip2库;
       --with-png=yes表示使用libpng库;
       --with-harfbuzz=no表示不使用harfbuzz库。
       ZLIB_CFLAGS选项用于指定zlib的头文件路径和库文件路径,根据实际安装路径填写;
       ZLIB_LIBS选项指定链接的zlib库的名称;
       LIBPNG_CFLAGS选项用于指定libpng的头文件路径和库文件路径,根据实际安装路径填写;
       LIBPNG_LIBS选项用于指定链接的libpng库的名称。
第二十四章 在LCD上显示字符23946.png

第二十四章 在LCD上显示字符23948.png

图 24.3.8 配置FreeType工程

       配置完成之后接着执行make编译,编译完成之后执行make install安装即可!
1.3.3安装目录下的文件
       进入到FreeType安装目录下,如下所示:
第二十四章 在LCD上显示字符24090.png

图 24.3.9 FreeType安装目录下的文件

       同样有bin目录、include目录以及lib目录,大家可以自己进入到这些目录下,浏览下这些目录下的有哪些文件,对此有个印象。
       如果要使用FreeType库,我们需要在应用程序源码中包含include/freetype2目录下的ft2build.h头文件,除此之外,还需要包含另一个头文件FT_FREETYPE_H,这是一个用宏定义的头文件,其实就是include/freetype2/freetype/freetype.h头文件。
        所以,在我们的应用程序一般是这样写:
  1. #include <ft2build.h>
  2. #include FT_FREETYPE_H
复制代码

1.3.4移植到开发板
       接下来将编译得到的动态链接库文件拷贝到开发板Linux系统/usr/lib目录,在拷贝之前,需将/usr/lib目录下原有的FreeType库文件删除掉,执行下面这条命令:
  1. rm -rf /usr/lib/libfreetype.*
复制代码

       删除之后,再将我们编译得到的库文件拷贝到开发板/usr/lib目录下,也就是FreeType安装目录lib目录下的所有库文件,拷贝的时候注意符号链接的问题。拷贝完成之后,如下所示:
第二十四章 在LCD上显示字符24657.png

图 24.3.10 开发板/usr/lib目录下的FreeType库文件

1.4freetype库的使用
       整个移植工作完成之后,接着简单地介绍下FreeType库的使用,FreeType库支持的功能很多、提供给用户的库函数也很多,所以笔者肯定不会给大家细聊!以介绍性为主。
       FreeType官方也提供了详细地使用帮助文档,以下便是这些文档的链接地址:
       https://www.freetype.org/freetype2/docs/tutorial/step1.html
       https://www.freetype.org/freetype2/docs/tutorial/step2.html
       https://www.freetype.org/freetype2/docs/reference/index.html
       以下这个链接是一份中文参考文档,大家可以看一下,笔者也不知道是哪位作者编写的,写的非常详细!
       https://www.doc88.com/p-7178359224563.html?r=1
       在正式介绍FreeType库使用之前,需要先了解几个涉及到的概念:
字形(glyph)
       字符图像就叫做字形,一个字符能够有多种不同的字形,可以理解为字形就是字符的一种书写风格,譬如宋体的汉字“国”与微软雅黑的汉字“国”,它们的字形是不同的,也就是它们书写风格是不同;宋体的“国”与微软雅黑的“国”就是两种不同的字形。
字形索引
       在字体文件中,通过字形索引找到对应的字形,而字形索引是由字符编码转换而来的,譬如ASCII编码、GB2312编码、BIG5编码、GBK编码以及国际标准字符集使用的Unicode编码等。对于字符编码,如果还有不了解读者,建议自行查阅相关的书籍。
像素点(pixel)、点(point)以及dpi
       像素点大家都知道,譬如LCD分辨率为800*480,那就表示LCD水平方向有800个像素点、垂直方向有480个像素点,所以此LCD一共有800*480个像素点。
       像素点这个概念大家都很熟悉了,也就不再多说;我们再来看下“点”的概念,点(point)是一种简单地物理单位,在数字印刷中,一个点(point)等于1/72英寸(1英寸等于25.4毫米)。
除此之外,还有一个dpi的概念,dpi(dots per inch)表示每英寸的像素点数,譬如300*400dpi表示在水平方向,每英寸有300个像素点、在垂直方向上每英寸有400个像素点。通过点数和dpi可以计算出像素点数,公式如下:
  1. 像素点数 = 点数 * dpi / 72
复制代码

      譬如,假设某一显示设备水平方向dpi为300,已知水平方向的点数为50,那么像素点数的计算方式为:
50 * 300 / 72 = 208
所以可以算出像素点数为208,因为后面会用到这些概念,所以先给大家简单地说明一下。
字形的布局
       以下两张图清晰地描述了字形布局的情况:分为水平布局和垂直布局,分别使用图 24.4.1和图 24.4.2来描述布局情况,以下这两张图都是从官方的文档中截取过来的。
水平方向书写文字使用水平布局方式,绝大部分情况下我们一般都是在水平方向上书写文字;垂直方向书写文字使用垂直布局方式,对于汉字来说,垂直方向书写也是比较常见的,很有代表性的就是对联、还有很多古书文字的写法,也都是采用这种垂直书写。
第二十四章 在LCD上显示字符26431.png

图 24.4.1 水平布局

第二十四章 在LCD上显示字符26490.png

图 24.4.2 垂直布局

基准线、原点
       从图中可以看到,不管是水平布局还是垂直布局,图中都可以找到一个origin原点,经过原点的水平线(X轴)和垂直线(Y轴)称为基准线,笔者将其称为水平基线和垂直基线。
       对于水平布局,垂直基线在字形的左边,垂直基线简单地放置在字形上,通过图中所标注的度量数据确定与基线的位置关系。
对于垂直布局,水平基线在字形的上方,字形在垂直基线上居中放置,同样也是通过图中所标注的度量数据确定与基线的位置关系。
       原点、基准线可以用于定位字形,水平布局和垂直布局使用不同的约束来放置字形。
字形的宽度和高度
       每一个字形都有自己的宽度和高度,图中使用width(宽)和height(高)来表示,width描述了字形轮廓的最左边到最右边的距离;而height描述了字形轮廓的最上边到最下边的距离。同一种书写风格,不同字符所对应的字形,它们的宽高是不一定相等的,譬如大写A和小写a,宽度和高度明显是不同的;但有些字符的字形宽度和高度是相同的,这个与具体的字符有关!
bearingX和bearingY
       bearingX表示从垂直基线到字形轮廓最左边的距离。对于水平布局来说,字形在垂直基线的右侧,所以bearingX是一个正数;而对于垂直布局来说,字形在垂直基线上居中放置,所以字形轮廓的最左边通常是在垂直基线的左侧,所以bearingX是一个负数。
       bearingY则表示从水平基线到字形轮廓最上边的距离。对于垂直布局来说,bearingY是一个正数,字形处于水平基线的下方;而对于水平布局来说,如果字形轮廓的最上边在水平基线的上方,则bearingY是一个正数、相反则是一个负数。
xMin/xMax、yMin/yMax
       xMin表示字形轮廓最左边的位置,xMax则表示字形轮廓最右边的位置;yMin表示字形轮廓最下边的位置,yMax则表示字形轮廓最上边的位置,通过这4个位置可以构成一个字形的边界框(bounding box,bbox),当然这是一个假象的框子,它尽可能紧密的装入字形。
advance
       advance则表示步进宽度,相邻两个原点位置的距离(字间距)。如果是水平布局,则表示相邻的两个原点在水平方向上的距离(advanceX),也就是相邻两条垂直基线之间的距离;同理,如果是垂直布局,则表示相邻的两个原点在垂直方向上的距离(advanceY),也就是相邻两条水平基线之间的距离。
       以上所提到的这些参数都很重要,大家一定要理解这些参数所表示的意义,绘制字符时,需要以这些参数作为参考值进行对齐显示。
       使用FreeType访问字体文件,可以从字体文件中获取到字形的位图数据,位图数据存储在一个buffer中,buffer大小为字形的宽*高个字节(字形边界框的宽*高个字节),也就是图 24.4.1中width*height个字节大小,每一个点使用一个字节来表示,当数组中该点对应的数值等于0,表示该点不填充颜色;当数值大于0,表示该点需要填充颜色。
字符显示时如何对齐?
       平时我们使用文本编辑器编写文字的时候,这些字符都是对齐显示的;譬如在一行文本中,即使包含大小写的英文字母、标点符号、汉字等这些字符,这一行字符显示在屏幕上时、都是对齐显示的;这里说的对齐是按照标准规范进行对齐,譬如逗号","显示时是靠近下边的、而不是靠近上边显示;双引号“”显示时是靠近上边的、而不是居中显示;这就是笔者认为的字符显示时的对齐规范,你可以认为每一个字符,它都有对应的一个显示规范,是靠近上边显示呢、还是靠近下边显示亦或者是靠近中间显示呢等。
       那我们如何保证对齐显示呢?其实就是通过图 24.4.1中的水平基线和垂直基线,如下图所示:
第二十四章 在LCD上显示字符28112.png

图 24.4.3 通过水平基线和垂直基线保证对齐显示

       不同字符对应的字形,水平基线到字形轮廓最上边的距离都是不一样的,譬如图中水平基线到“A”和“a”轮廓最上边的距离明显是不一样的;除此之外,有些字形的轮廓最下边已经在水平基线之下、而有些字形的轮廓最下边却又在水平基线之上。
       分析完水平基线之后,我们再来看看垂直基线,每一个字形的垂直基线到字形轮廓最左边的距离也都是不一样的,譬如“韩”和“a”,很明显、它们各自的垂直基线到字形轮廓最左边的距离是不一样的。
       水平基线可以作为垂直方向(上下方向)上对齐显示的基准线,而垂直基线可以作为水平方向(左右方向)上对齐显示的基准线;对于水平布局来说,相邻两条垂直基线的距离就是字间距或者叫步进宽度;从一个字形的原点加上一个步进宽度就到了下一个字形的原点。
       当我们要在屏幕上画字形的时候,首先要定位到字形的左上角位置,从左上角开始,依次从左到右、从上到下,字形显示的宽度就是字形的宽度width、字符显示的高度就是字形的高度height。那如何找到左上角的位置,这个很简单,通过bearingY和bearingX便可确定。譬如我们将(100, 100)这个位置作为原点,那么,字符显示位置的左上角便是(100+bearingX, 100-bearingY)。
       以上就给大家介绍关于字符对齐的问题,大家一定要理解这些内容,如果你理解不了上面的内容,后面的示例代码你可能就看不懂。
1.4.1初始化FreeType库
       在使用FreeType库函数之前,需要对FreeType库进行初始化操作,使用FT_Init_FreeType()函数完成初始化操作。在调用该函数之前,我们需要定义一个FT_Library类型变量,调用FT_Init_FreeType()函数时将该变量的指针作为参数传递进去;使用示例如下所示:
  1. FT_Library library;
  2. FT_Error error;

  3. error = FT_Init_FreeType(&library);
  4. if (error)
  5.         fprintf(stderr, "Error: failed to initialize FreeType library object\n");
复制代码

FT_Init_FreeType完成以下操作:
它创建了一个FreeType库对象,并将library作为库对象的句柄。
FT_Init_FreeType()调用成功返回0;失败将返回一个非零值错误码。
1.4.2加载face对象
       应用程序通过调用FT_New_Face()函数创建一个新的face对象,其实就是加载字体文件,为啥叫face(脸),应该是一种抽象的说法!一个face对象描述了一个特定的字体样式和风格,譬如"Times New Roman Regular"和"Times New Roman Italic"对应两种不同的face。
       调用FT_New_Face()函数前,我们需要定义一个FT_Face类型变量,使用示例如下所示:
  1. FT_Library library;                //库对象的句柄
  2. FT_Face face;                        //face对象的句柄
  3. FT_Error error;

  4. FT_Init_FreeType(&library);

  5. error = FT_New_Face(library, "/usr/share/fonts/font.ttf", 0, &face);
  6. if (error) {
  7.         /* 发生错误、进行相关处理 */
  8. }
复制代码

FT_New_Face()函数原型如下所示:
  1. FT_Error FT_New_Face(FT_Library library, const char *filepathname,
  2.                 FT_Long face_index, FT_Face *aface);
复制代码

       函数参数以及返回值说明如下:
       library:一个FreeType库对象的句柄,face对象从中建立;
       filepathname:字库文件路径名(一个标准的C字符串);
       face_index:某些字体格式允许把几个字体face嵌入到同一个文件中,这个索引指示了你想加载的face,其实就是一个下标,         如果这个值太大,函数将会返回一个错误,通常把它设置为0即可!想要知道一个字体文件中包含了多少个face,只要简单地加载它的第一个face(把face_index设置为0),函数调用成功返回后,face->num_faces的值就指示出了有多少个face嵌入在该字体文件中。
       aface:一个指向新建face对象的指针,当失败时其值被设置为NULL。
       返回值:调用成功返回0;失败将返回一个非零值的错误码。
1.4.3设置字体大小
       设置字体的大小有两种方式:FT_Set_Char_Size()和FT_Set_Pixel_Sizes()。
FT_Set_Pixel_Sizes()函数
       调用FT_Set_Pixel_Sizes()函数设置字体的宽度和高度,以像素为单位,使用示例如下所示:
  1. FT_Set_Pixel_Sizes(face, 50, 50);
复制代码

       第一个参数传入face句柄;第二个参数和第三个参数分别指示字体的宽度和高度,以像素为单位;需要注意的是,我们可以将宽度或高度中的任意一个参数设置为0,那么意味着设置为0的参数将会与另一个参数保持相等,如下所示:
  1. FT_Set_Pixel_Sizes(face, 50, 0);
复制代码

       上面调用FT_Set_Pixel_Sizes()函数时,将字体高度设置为0,也就意味着字体高度将自动等于字体宽度50。
FT_Set_Char_Size()函数
       调用FT_Set_Char_Size()函数设置字体大小,示例如下所示,假设在一个300x300dpi的设备上把字体大小设置为16pt:
  1. error = FT_Set_Char_Size(
  2.         face,        //face对象的句柄
  3.         16*64,        //以1/64点为单位的字体宽度
  4.         16*64,        //以1/64点为单位的字体高度
  5.         300,        //水平方向上每英寸的像素点数
  6.         300);        //垂直方向上每英寸的像素点数
复制代码

       说明:
       字体的宽度和高度并不是以像素为单位,而是以1/64点(point)为单位表示(也就是26.6固定浮点格式),一个点是一个1/72英寸的距离。
       同样也可将宽度或高度其中之一设置为0,那么意味着设置为0的参数将会与另一个参数保持相等。
       dpi参数设置为0时,表示使用默认值72dpi。
1.4.4加载字形图像
       设置完成之后,接下来就可以加载字符图像了,总共分为3步:
       a)、获取字符的字形索引
      通过FT_Get_Char_Inde()函数将字符编码转换为字形索引(glyph index),Freetype默认使用UTF-16编码类型,也就是Unicode编码方式,采用2个字节来表示一个编码值。
XXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXX
1.5示例代码
       显示单个ASCII字符
       显示单个中文字符
       显示一行字符(一行字符对齐显示)
       显示多行字符(一行字符的高度计算)
       字体变形(旋转、斜体……..)
       示例代码 24.5.1 FreeType使用示例代码
  1. /***************************************************************
  2. Copyright © ALIENTEK Co., Ltd. 1998-2021. All rights reserved.
  3. 文件名 : freetype_test.c
  4. 作者 : 邓涛
  5. 版本 : V1.0
  6. 描述 : FreeType测试示例代码
  7. 其他 : 无
  8. 论坛 : <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  9. 日志 : 初版 V1.0 2021/7/14 邓涛创建
  10. ***************************************************************/

  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #include <fcntl.h>
  16. #include <unistd.h>
  17. #include <sys/ioctl.h>
  18. #include <string.h>
  19. #include <errno.h>
  20. #include <sys/mman.h>
  21. #include <linux/fb.h>
  22. #include <math.h>       //数学库函数头文件

  23. #include <wchar.h>
  24. #include <ft2build.h>
  25. #include FT_FREETYPE_H

  26. #define FB_DEV      "/dev/fb0"      //LCD设备节点

  27. #define argb8888_to_rgb565(color)   ({ \
  28.             unsigned int temp = (color); \
  29.             ((temp & 0xF80000UL) >> 8) | \
  30.             ((temp & 0xFC00UL) >> 5) | \
  31.             ((temp & 0xF8UL) >> 3); \
  32.             })

  33. static unsigned int width;                       //LCD宽度
  34. static unsigned int height;                      //LCD高度
  35. static unsigned short *screen_base = NULL;//LCD显存基地址 RGB565
  36. static unsigned long screen_size;
  37. static int fd = -1;

  38. static FT_Library library;
  39. static FT_Face face;

  40. static int fb_dev_init(void)
  41. {
  42.     struct fb_var_screeninfo fb_var = {0};
  43.     struct fb_fix_screeninfo fb_fix = {0};

  44.     /* 打开framebuffer设备 */
  45.     fd = open(FB_DEV, O_RDWR);
  46.     if (0 > fd) {
  47.         fprintf(stderr, "open error: %s: %s\n", FB_DEV, strerror(errno));
  48.         return -1;
  49.     }

  50.     /* 获取framebuffer设备信息 */
  51.     ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
  52.     ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);

  53.     screen_size = fb_fix.line_length * fb_var.yres;
  54.     width = fb_var.xres;
  55.     height = fb_var.yres;

  56.     /* 内存映射 */
  57.     screen_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  58.     if (MAP_FAILED == (void *)screen_base) {
  59.         perror("mmap error");
  60.         close(fd);
  61.         return -1;
  62.     }

  63.     /* LCD背景刷成黑色 */
  64.     memset(screen_base, 0xFF, screen_size);
  65.     return 0;
  66. }

  67. static int freetype_init(const char *font, int angle)
  68. {
  69.     FT_Error error;
  70.     FT_Vector pen;
  71.     FT_Matrix matrix;
  72.     float rad;      //旋转角度

  73.     /* FreeType初始化 */
  74.     FT_Init_FreeType(&library);

  75.     /* 加载face对象 */
  76.     error = FT_New_Face(library, font, 0, &face);
  77.     if (error) {
  78.         fprintf(stderr, "FT_New_Face error: %d\n", error);
  79.         exit(EXIT_FAILURE);
  80.     }

  81.     /* 原点坐标 */
  82.     pen.x = 0 * 64;
  83.     pen.y = 0 * 64;     //原点设置为(0, 0)

  84.     /* 2x2矩阵初始化 */
  85.     rad = (1.0 * angle / 180) * M_PI;   //(角度转换为弧度)M_PI是圆周率
  86. #if 0       //非水平方向
  87.     matrix.xx = (FT_Fixed)( cos(rad) * 0x10000L);
  88.     matrix.xy = (FT_Fixed)(-sin(rad) * 0x10000L);
  89.     matrix.yx = (FT_Fixed)( sin(rad) * 0x10000L);
  90.     matrix.yy = (FT_Fixed)( cos(rad) * 0x10000L);
  91. #endif

  92. #if 1       //斜体  水平方向显示的
  93.     matrix.xx = (FT_Fixed)( cos(rad) * 0x10000L);
  94.     matrix.xy = (FT_Fixed)( sin(rad) * 0x10000L);
  95.     matrix.yx = (FT_Fixed)( 0 * 0x10000L);
  96.     matrix.yy = (FT_Fixed)( 1 * 0x10000L);
  97. #endif

  98.     /* 设置 */
  99.     FT_Set_Transform(face, &matrix, &pen);
  100.     FT_Set_Pixel_Sizes(face, 50, 0);    //设置字体大小

  101.     return 0;
  102. }

  103. static void lcd_draw_character(int x, int y,
  104.             const wchar_t *str, unsigned int color)
  105. {
  106.     unsigned short rgb565_color = argb8888_to_rgb565(color);//得到RGB565颜色值
  107.     FT_GlyphSlot slot = face->glyph;
  108.     size_t len = wcslen(str);   //计算字符的个数
  109.     long int temp;
  110.     int n;
  111.     int i, j, p, q;
  112.     int max_x, max_y, start_y, start_x;

  113.     // 循环加载各个字符
  114.     for (n = 0; n < len; n++) {

  115.         // 加载字形、转换得到位图数据
  116.         if (FT_Load_Char(face, str[n], FT_LOAD_RENDER))
  117.             continue;

  118.         start_y = y - slot->bitmap_top; //计算字形轮廓上边y坐标起点位置 注意是减去bitmap_top
  119.         if (0 > start_y) {//如果为负数 如何处理??
  120.             q = -start_y;
  121.             temp = 0;
  122.             j = 0;
  123.         }
  124.         else {      // 正数又该如何处理??
  125.             q = 0;
  126.             temp = width * start_y;
  127.             j = start_y;
  128.         }

  129.         max_y = start_y + slot->bitmap.rows;//计算字形轮廓下边y坐标结束位置
  130.         if (max_y > (int)height)
  131.             max_y = height;

  132.         for (; j < max_y; j++, q++, temp += width) {

  133.             start_x = x + slot->bitmap_left;  //起点位置要加上左边空余部分长度
  134.             if (0 > start_x) {
  135.                 p = -start_x;
  136.                 i = 0;
  137.             }
  138.             else {
  139.                 p = 0;
  140.                 i = start_x;
  141.             }

  142.             max_x = start_x + slot->bitmap.width;
  143.             if (max_x > (int)width)
  144.                 max_x = width;

  145.             for (; i < max_x; i++, p++) {

  146.                 // 如果数据不为0,则表示需要填充颜色
  147.                 if (slot->bitmap.buffer[q * slot->bitmap.width + p])
  148.                     screen_base[temp + i] = rgb565_color;
  149.             }
  150.         }

  151.         //调整到下一个字形的原点
  152.         x += slot->advance.x / 64;  //26.6固定浮点格式
  153.         y -= slot->advance.y / 64;
  154.     }
  155. }

  156. int main(int argc, char *argv[])
  157. {
  158.    

  159.     /* LCD初始化 */
  160.     if (fb_dev_init())
  161.         exit(EXIT_FAILURE);

  162.     /* freetype初始化 */
  163.     if (freetype_init(argv[1], atoi(argv[2])))
  164.         exit(EXIT_FAILURE);

  165.     /* 在LCD上显示中文 */
  166.     int y = height * 0.25;
  167.     lcd_draw_character(50, 100, L"路漫漫其修远兮,吾将上下而求索", 0x000000);
  168.     lcd_draw_character(50, y+100, L"莫愁前路无知己,天下谁人不识君", 0x9900FF);
  169.     lcd_draw_character(50, 2*y+100, L"君不见黄河之水天上来,奔流到海不复回", 0xFF0099);
  170.     lcd_draw_character(50, 3*y+100, L"君不见高堂明镜悲白发,朝如青丝暮成雪", 0x9932CC);

  171.     /* 退出程序 */
  172.     FT_Done_Face(face);
  173.     FT_Done_FreeType(library);
  174.     munmap(screen_base, screen_size);
  175.     close(fd);
  176.     exit(EXIT_SUCCESS);
  177. }
复制代码

       编译方法:
  1. ${CC} -o testApp testApp.c -I/home/dt/tools/freetype/include/freetype2 -L/home/dt/tools/freetype/lib -lfreetype -L/home/dt/tools/zlib/lib -lz -L/home/dt/tools/png/lib -lpng -lm
复制代码

效果图:
第二十四章 在LCD上显示字符36882.png


第二十四章 在LCD上显示字符36884.png

第二十四章 在LCD上显示字符36886.png

第二十四章 在LCD上显示字符36888.png

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 08:12

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表