目录
原始方式:取模显示字符
LCD
显示屏是由
width * height
个像素点构成的,显示字符,一个非常容易想到的方法便是对字符取模, 然后在 LCD
屏上打点显示字符;如果大家以前学习过单片机,想必接触过一些显示屏,譬如
oled
、或者其 它一些点阵式的显示屏,其实这些显示屏显示字符的原理都是一样的,如下所示:
我们可以通过一些字符取模软件获取到字符的子模;所谓子模,其实就是一个二维数组,用于表示字符 点阵中,哪些小方块应该要填充颜色、哪些小方块不填充颜色。譬如上图“正”字符点阵,这是一个宽度为 64(
64
个小方块)、高度为
86
(
86
个小方块)的字符点阵,我们会使用一个二维数组来表示这个字符点阵:
unsigned char arr[86][8];
也就是一个
86
行
8
列的
unsigned char
类型数组,数组存储的其实就是字符的位图数据,字符点阵中的 每一个小方块对应一个 bit
位,因为一行一共有
64
个小方块、也就对应
8
个字节(
8 * 8 = 64
);将填充颜 色的方块使用 1
表示、不填充颜色的方块使用
0
来表示,所以一个小方块刚好可以使用一个
bit
位来描述。
我们编写一个简单的程序去测试下,网上有很多的这种字符取模的小软件,大家可以找一下,我们用这 个取模软件,获取几个字符的子模,然后在我们的 LCD 屏上去显示这些字符。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <linux/fb.h>
#define FB_DEV "/dev/fb0" //LCD 设备节点
static int width; //LCD 宽度
static int height; //LCD 高度
static unsigned short *screen_base = NULL;//LCD 显存基地址
static unsigned char ch_char1[86][8] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
{0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
{0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
{0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
{0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
{0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
{0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
{0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
{0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07},
{0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
{0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
{0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
{0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
{0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
{0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
{0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
{0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
{0x00,0xFC,0x07,0xC0,0xFF,0xFF,0xFF,0x01},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0x00,0xFC,0x07,0xC0,0x7F,0x00,0x00,0x00},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"正",0*/
};
static unsigned char ch_char2[86][8] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
{0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
{0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
{0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
{0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
{0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
{0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
{0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
{0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
{0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
{0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
{0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
{0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0x0F},
{0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
{0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
{0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
{0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
{0x00,0x00,0x00,0xFC,0x07,0x00,0x00,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
{0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
{0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
{0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
{0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
{0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
{0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
{0x00,0xFF,0x01,0x00,0x00,0xC0,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00},
{0x00,0x01,0x00,0x00,0xC0,0x00,0x7C,0x00},
{0x00,0x0F,0x00,0x03,0xF8,0x00,0x7F,0x00},
{0x80,0x3F,0xF0,0x03,0xFF,0x81,0xFF,0x00},
{0x80,0xFF,0xFC,0x03,0xFE,0x81,0xFF,0x00},
{0xC0,0x7F,0xFC,0x07,0xFE,0x03,0xFF,0x01},
{0xC0,0x7F,0xF8,0x07,0xFC,0x03,0xFF,0x01},
{0xE0,0x3F,0xF8,0x0F,0xFC,0x07,0xFE,0x01},
{0xE0,0x3F,0xF0,0x0F,0xFC,0x07,0xFE,0x03},
{0xF0,0x1F,0xF0,0x0F,0xF8,0x07,0xFE,0x03},
{0xF0,0x1F,0xF0,0x1F,0xF8,0x0F,0xFC,0x07},
{0xF8,0x0F,0xE0,0x1F,0xF8,0x0F,0xFC,0x07},
{0xF8,0x0F,0xE0,0x1F,0xF0,0x0F,0xF8,0x0F},
{0xFC,0x07,0xE0,0x3F,0xF0,0x1F,0xF8,0x0F},
{0xFC,0x07,0xE0,0x3F,0xF0,0x1F,0xF0,0x1F},
{0xFC,0x03,0xC0,0x0F,0xE0,0x03,0xF0,0x07},
{0xF0,0x03,0xC0,0x00,0x60,0x00,0xE0,0x01},
{0xE0,0x01,0x00,0x00,0x00,0x00,0x20,0x00},
{0x80,0x01,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"点",1*/
};
static unsigned char ch_char3[86][8] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0x80,0x7F,0x00,0x80,0xFF,0x00,0x00,0x00},
{0x80,0x7F,0x00,0xC0,0x7F,0x00,0x00,0x00},
{0x80,0x7F,0x00,0xC0,0x7F,0x00,0x00,0x00},
{0x80,0x7F,0x00,0xC0,0x3F,0x00,0x00,0x00},
{0x80,0x7F,0x00,0xE0,0x3F,0x00,0x00,0x00},
{0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
{0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
{0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
{0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
{0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0x80,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
{0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
{0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
{0x80,0x7F,0xF8,0x07,0x00,0x00,0xFE,0x01},
{0xC0,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0xC0,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0xC0,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0xC0,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0xC0,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0xC0,0x7F,0xF8,0xFF,0xFF,0xFF,0xFF,0x01},
{0xC0,0x3F,0x00,0x00,0xFC,0x07,0x06,0x00},
{0xC0,0x3F,0x80,0x00,0xFC,0x07,0x07,0x00},
{0xC0,0x3F,0x80,0x03,0xFC,0x87,0x0F,0x00},
{0xE0,0x3F,0xC0,0x07,0xFC,0xE7,0x1F,0x00},
{0xE0,0x3F,0xE0,0x1F,0xFC,0xF7,0x3F,0x00},
{0xE0,0x3F,0xF0,0x3F,0xFC,0xE7,0x7F,0x00},
{0xE0,0x1F,0xF0,0x3F,0xFC,0xE7,0xFF,0x00},
{0xF0,0x1F,0xF8,0x1F,0xFC,0xC7,0xFF,0x00},
{0xF0,0x1F,0xFC,0x0F,0xFC,0x87,0xFF,0x01},
{0xF0,0x1F,0xFE,0x0F,0xFC,0x07,0xFF,0x03},
{0xF0,0x1F,0xFF,0x07,0xFC,0x07,0xFF,0x07},
{0xF8,0x8F,0xFF,0x03,0xFC,0x07,0xFE,0x0F},
{0xF8,0x8F,0xFF,0x01,0xFE,0x07,0xFC,0x0F},
{0xFC,0xCF,0xFF,0xFF,0xFF,0x07,0xF8,0x1F},
{0xFC,0xE7,0xFF,0xFE,0xFF,0x07,0xF8,0x0F},
{0xFE,0xC7,0x7F,0xFE,0xFF,0x03,0xF0,0x07},
{0xFE,0x83,0x3F,0xFC,0xFF,0x03,0xE0,0x03},
{0xF8,0x03,0x1F,0xFC,0xFF,0x03,0xE0,0x00},
{0xF0,0x03,0x0E,0xFC,0xFF,0x01,0x40,0x00},
{0xE0,0x01,0x04,0xFC,0xFF,0x00,0x00,0x00},
{0xC0,0x00,0x00,0xFC,0x3F,0x00,0x00,0x00},
{0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"原",2*/
};
static unsigned char ch_char4[86][8] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
{0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
{0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
{0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
{0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
{0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
{0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
{0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00},
{0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x00},
{0x00,0x00,0x00,0x00,0x00,0xFF,0x3F,0x00},
{0x00,0x00,0x00,0x00,0xC0,0xFF,0x1F,0x00},
{0x00,0x00,0x00,0x00,0xF0,0xFF,0x07,0x00},
{0x00,0x00,0x00,0x00,0xF8,0xFF,0x03,0x00},
{0x00,0x00,0x00,0x00,0xFE,0xFF,0x00,0x00},
{0x00,0x00,0x00,0x80,0xFF,0x7F,0x00,0x00},
{0x00,0x00,0x00,0xC0,0xFF,0x1F,0x00,0x00},
{0x00,0x00,0x00,0xF0,0xFF,0x07,0x00,0x00},
{0x00,0x00,0x00,0xF8,0xFF,0x03,0x00,0x00},
{0x00,0x00,0x00,0xF8,0xFF,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xF8,0x0F,0x00,0x00,0x00},
{0x00,0x00,0x00,0xFC,0x0F,0x00,0x00,0x00},
{0x00,0x10,0x00,0xFE,0x0F,0x00,0x00,0x00},
{0x00,0xF0,0xFF,0xFF,0x0F,0x00,0x00,0x00},
{0x00,0xF0,0xFF,0xFF,0x0F,0x00,0x00,0x00},
{0x00,0xE0,0xFF,0xFF,0x0F,0x00,0x00,0x00},
{0x00,0xE0,0xFF,0xFF,0x07,0x00,0x00,0x00},
{0x00,0xE0,0xFF,0xFF,0x07,0x00,0x00,0x00},
{0x00,0xE0,0xFF,0xFF,0x03,0x00,0x00,0x00},
{0x00,0xC0,0xFF,0xFF,0x01,0x00,0x00,0x00},
{0x00,0xC0,0xFF,0xFF,0x00,0x00,0x00,0x00},
{0x00,0xC0,0xFF,0x3F,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"子",3*/
};
#define argb8888_to_rgb565(color) ({ \
unsigned int temp = (color); \
((temp & 0xF80000UL) >> 8) | \
((temp & 0xFC00UL) >> 5) | \
((temp & 0xF8UL) >> 3); \
})
/********************************************************************
* 函数名称: lcd_draw_character
* 功能描述: 在 LCD 屏指定位置处(x, y)画字符, 参数 color 指定字符的颜色
指针 ch 指向字符对应的子模数组、参数 w、h 分别表示字符的宽度和高度
* 输入参数: color
* 返 回 值: 无
********************************************************************/
static void lcd_draw_character(unsigned int x, unsigned int y,
const unsigned char *ch, unsigned int w,
unsigned int h, unsigned int color)
{
unsigned short rgb565_color = argb8888_to_rgb565(color);//得到 RGB565 颜色值
unsigned long temp;
unsigned int end_x, end_y;
int j;
int columns;
/**
计算出二维数组有多少列
参数 w 表示的是字符的宽度,1 个宽度表示的是 1 个 bit 位
并不是一个字节,这里要注意,如果宽度不是 byte 单位的整数倍
通常会补零
**/
columns = w / 8; //1byte=8bit
if (0 != w % 8) columns++;
/* 对参数进行限定 */
if (w < 1 || h < 1) return;
if (x >= width || y >= height) return;
/* 计算出结束坐标位置 */
end_x = x + w - 1;
end_y = y + h - 1;
/* 对结束坐标位置进行限定 */
if (end_x >= width)
end_x = width - 1;
if (end_y >= height)
end_y = height - 1;
/* 计算有效宽度 */
h = end_y - y + 1;
w = end_x - x + 1;
/* 打点 */
temp = y * width + x; //定位到起点
for (y = 0; y < h; y++, temp += width) {
for (x = 0, j = 0; x < w; ) {
if (*(ch + y * columns + j) & (0x1 << (x % 8)))
screen_base[temp + x] = rgb565_color;
x++;
if (0 == x % 8) j++;
}
}
}
int main(void)
{
struct fb_var_screeninfo fb_var = {0};
struct fb_fix_screeninfo fb_fix = {0};
unsigned long screen_size;
int fd;
/* 打开 framebuffer 设备 */
fd = open(FB_DEV, O_RDWR);
if (0 > fd) {
fprintf(stderr, "open error: %s: %s\n", FB_DEV, strerror(errno));
exit(EXIT_FAILURE);
}
/* 获取 framebuffer 设备信息 */
ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);
screen_size = fb_fix.line_length * fb_var.yres;
width = fb_var.xres;
height = fb_var.yres;
/* 内存映射 */
screen_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == (void *)screen_base) {
perror("mmap error");
close(fd);
exit(EXIT_FAILURE);
}
/* LCD 背景刷白 */
memset(screen_base, 0xFF, screen_size);
/* 显示字符 */
int x = width * 0.5 - 128;
int y = height * 0.5 - 43;
lcd_draw_character(x, y, (unsigned char *)ch_char1, 64, 86, 0xFF00FF);
lcd_draw_character(x + 64, y, (unsigned char *)ch_char2, 64, 86, 0xFF00FF);
lcd_draw_character(x + 128, y, (unsigned char *)ch_char3, 64, 86, 0xFF00FF);
lcd_draw_character(x + 192, y, (unsigned char *)ch_char4, 64, 86, 0xFF00FF);
/* 退出程序 */
munmap(screen_base, screen_size);
close(fd);
return 0;
}
程序中定义了
4
个二维数组
ch_char1
、
ch_char2
、
ch_char3
、
ch_char4
,这
4
个二维数组便是
4
个中文字 符“正点原子”的子模,字符宽度为 64
、高度为
86
。这种字符取模软件就不给大家介绍了,网上很多这种 软件、用法方法通常也非常简单,我们在取模的时候,选择从左到右、从上到下这种方式进行取模,上述示 例代码便是按照这种方式进行解析的。
调用自定义函数
lcd_draw_character
在
LCD
上显示字符、并且是居中显示。
操作系统中通常都会有很多的字体文件,譬如
Windows
系统“
C:\Windows\Fonts
”目录下就有很多的字 体文件,如下所示:
字体文件的格式也有很多种,譬如
otf
、
ttf
、
ttc
等,这里就不给大家列举了,有兴趣的读者自己百度一 下;Linux
系统中,字体文件通常会放在
/usr/share/fonts
目录下,有了字体文件之后,我们就不需要再对字符 进行取模了,它们已经编码进了字体文件中,我们只需要解析字体文件、访问字体文件,从字体文件中读取 出字符的位图数据即可!
当然,这些复杂的解析过程并不需要我们自己去实现,有很多开源的字体引擎可以帮助我们来处理这些 复杂的解析过程,譬如 freetype
库,下小节将向大家介绍如何使用
freetype
访问字体文件。
freetype 简介
FreeType
一个完全免费(开源)的软件字体引擎库,设计小巧、高效、高度可定制且可移植,它提供了 统一的接口来访问多种不同格式的字体文件。它提供了一个简单、易于使用且统一的接口来访问字体文件 的内容,从而大大简化了这些任务。
请注意,“
FreeType
”也称为“
FreeType 2
”,以区别于旧的、已弃用的“
FreeType 1
”库,
Freetype 1
库已经不再维护和支持了。
freetype 移植
本小节我们来移植
FreeType
,将
FreeType
移植到我们的开发板根文件系统中;事实上,开发板出厂系 统已经移植好了这些库,但是版本稍微太低了,作为学习者,我们应该要学会自己动手移植,这个是很重要 的工作;从上几个章节的学习内容可知,我们很多的编程工作都是基于别人写好的库、调用库函数来实现应 用程序的功能,因为 Linux
软件生态做得非常好,有很多免费、开源的库是可以使用的,不用自己去捣鼓这 些东西;所以作为一个嵌入式 Linux
软件开发人员,学会移植就显得很重要了。
下载 FreeType 源码
开发板出厂系统中,
FreeType
的版本为
2.6
,这个版本稍微有点低,我们选择移植
2.8
版本的
FreeType
。
进入到
https://download.savannah.gnu.org/releases/freetype/
链接地址,如下所示:
往下翻找到 2.8 版本,选择 freetype-2.8.tar.gz 压缩文件:
点击 freetype-2.8.tar.gz 下载源码,下载完成后我们将会得到 2.8 版本的 FreeType 源码包:
大家要自己动手下载,开发板资料盘中是没有提供这些源码包的,包括前面几个章节介绍的库,譬如 libpng、
libjpeg
以及
tslib
等,都没有提供它们的源码压缩文件。
交叉编译 FreeType 源码
将下载好的
freetype-2.8.tar.gz
压缩文件拷贝到
Ubuntu
系统的用户家目录下,
在
tools
目录下创建一个名为
freetype
的目录,把它作为
FreeType
的安装目录:
执行命令将
freetype-2.8.tar.gz
解压开来:
tar -xzf freetype-2.8.tar.gz
解压成功之后便会得到
FreeType
的源码目录
freetype-2.8
。
进入到
freetype-2.8
目录,老规矩,同样是三部曲:配置、编译、安装。
FreeType
库基于模块化设计,意味着我们可以对其进行裁剪,将不需要的功能模块从配置中移除,减 小库文件的体积;除此之外,FreeType
还支持很多配置选项,如果大家想要对
FreeType
做一些自定义配置 或者对其进行裁剪,可以参考 FreeType
源码目录下
docs/CUSTOMIZE
文档,该文件对此有比较详细的说明, 建议大家看一看,如果有需求的话。docs
目录下还有其它很多的说明文档,也都可以读一读。
这里我们简单地配置一下,打开
include/freetype/config/ftoption.h
文件,如下所示:
vi include/freetype/config/ftoption.h
该文件定义了很多的配置宏,我们可以选择使能或禁用这些配置选项,具体配置哪些功能,大家自己去 研究,每一个配置宏都有详细地解释说明。这里我们打开以下两个配置宏:
#define FT_CONFIG_OPTION_SYSTEM_ZLIB#define FT_CONFIG_OPTION_USE_PNG
大家找到这两个宏,默认情况下,这两个都被注释掉了,所以是没有使能的;把这两个宏的注释去掉, 使能这两个配置宏。
第一个配置宏表示使用系统安装的
zlib
库,因为
FreeType
支持
Gzip
压缩文件,会使用到
zlib
库,
zlib 之前我们移植好了;第二个配置宏表示支持 PNG bitmap
位图,因为
FreeType
可以加载
PNG
格式的彩色位 图字形,需要依赖于 libpng
库,这个库前面我们也是移植好了。
配置好之后,保存、退出
ftoption.h
文件,接着执行如下命令对
FreeType
工程源码进行配置:
./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
库的名称。
配置完成之后接着执行 make 编译,编译完成之后执行 make install 安装即可!
安装目录下的文件
进入到
FreeType
安装目录下,如下所示:
同样有
bin
目录、
include
目录以及
lib
目录,大家可以自己进入到这些目录下,浏览下这些目录下的有 哪些文件,对此有个印象。
如果要使用
FreeType
库,我们需要在应用程序源码中包含
include/freetype2
目录下的
ft2build.h
头文件, 除此之外,还需要包含另一个头文件 FT_FREETYPE_H
,这是一个用宏定义的头文件,其实就是 include/freetype2/freetype/freetype.h 头文件。
所以,在我们的应用程序一般是这样写:
#include <ft2build.h>#include FT_FREETYPE_H
移植到开发板
接下来将编译得到的动态链接库文件拷贝到开发板
Linux
系统
/usr/lib
目录,在拷贝之前,需将
/usr/lib
目 录下原有的 FreeType
库文件删除掉,执行下面这条命令:
rm -rf /usr/lib/libfreetype.*
删除之后,再将我们编译得到的库文件拷贝到开发板
/usr/lib
目录下,也就是
FreeType
安装目录
lib
目 录下的所有库文件,拷贝的时候注意符号链接的问题。拷贝完成之后,如下所示:
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
可以计算出像素 点数,公式如下:
像素点数 = 点数 * dpi / 72
譬如,假设某一显示设备水平方向
dpi
为
300
,已知水平方向的点数为
50
,那么像素点数的计算方式 为:
50 * 300 / 72 = 208
字形的布局
以下两张图清晰地描述了字形布局的情况:分为水平布局和垂直布局,分别使用图
23.4.1
和图
23.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
大小为字形的宽
*
高个字节(字形边界框的宽
*
高个字节),也就是图
23.4.1
中
width*height
个字 节大小,每一个点使用一个字节来表示,当数组中该点对应的数值等于 0
,表示该点不填充颜色;当数值大 于 0
,表示该点需要填充颜色。
字符显示时如何对齐?
平时我们使用文本编辑器编写文字的时候,这些字符都是对齐显示的;譬如在一行文本中,即使包含大 小写的英文字母、标点符号、汉字等这些字符,这一行字符显示在屏幕上时、都是对齐显示的;这里说的对 齐是按照标准规范进行对齐,譬如逗号"
,
"
显示时是靠近下边的、而不是靠近上边显示;双引号“”显示时 是靠近上边的、而不是居中显示;这就是笔者认为的字符显示时的对齐规范,你可以认为每一个字符,它都 有对应的一个显示规范,是靠近上边显示呢、还是靠近下边显示亦或者是靠近中间显示呢等。
那我们如何保证对齐显示呢?其实就是通过图
23.4.1
中的水平基线和垂直基线,如下图所示:
不同字符对应的字形,水平基线到字形轮廓最上边的距离都是不一样的,譬如图中水平基线到“
A
”和 “a
”轮廓最上边的距离明显是不一样的;除此之外,有些字形的轮廓最下边已经在水平基线之下、而有些 字形的轮廓最下边却又在水平基线之上。
分析完水平基线之后,我们再来看看垂直基线,每一个字形的垂直基线到字形轮廓最左边的距离也都是 不一样的,譬如“韩”和“a
”,很明显、它们各自的垂直基线到字形轮廓最左边的距离是不一样的。
水平基线可以作为垂直方向(上下方向)上对齐显示的基准线,而垂直基线可以作为水平方向(左右方 向)上对齐显示的基准线;对于水平布局来说,相邻两条垂直基线的距离就是字间距或者叫步进宽度;从一 个字形的原点加上一个步进宽度就到了下一个字形的原点。
当我们要在屏幕上画字形的时候,首先要定位到字形的左上角位置,从左上角开始,依次从左到右、从 上到下,字形显示的宽度就是字形的宽度 width
、字符显示的高度就是字形的高度
height
。那如何找到左上 角的位置,这个很简单,通过 bearingY
和
bearingX
便可确定。譬如我们将
(100, 100)
这个位置作为原点,那 么,字符显示位置的左上角便是(100+bearingX, 100-bearingY)
。
初始化 FreeType 库
在使用
FreeType
库函数之前,需要对
FreeType
库进行初始化操作,使用
FT_Init_FreeType()
函数完成 初始化操作。在调用该函数之前,我们需要定义一个 FT_Library
类型变量,调用
FT_Init_FreeType()
函数时 将该变量的指针作为参数传递进去;使用示例如下所示:
FT_Library library;FT_Error error;error = FT_Init_FreeType(&library);if (error)fprintf(stderr, "Error: failed to initialize FreeType library object\n");
FT_Init_FreeType
完成以下操作:
⚫
它创建了一个
FreeType
库对象,并将
library
作为库对象的句柄。
⚫
FT_Init_FreeType()
调用成功返回
0
;失败将返回一个非零值错误码。
加载 face 对象
应用程序通过调用
FT_New_Face()
函数创建一个新的
face
对象,其实就是加载字体文件,为啥叫
face (脸),应该是一种抽象的说法!一个 face
对象描述了一个特定的字体样式和风格,譬如
"Times New Roman Regular"和
"Times New Roman Italic"
对应两种不同的
face
。
调用
FT_New_Face()
函数前,我们需要定义一个
FT_Face
类型变量,使用示例如下所示:
FT_Library library; //库对象的句柄FT_Face face; //face 对象的句柄FT_Error error;FT_Init_FreeType(&library);error = FT_New_Face(library, "/usr/share/fonts/font.ttf", 0, &face);if (error) {/* 发生错误、进行相关处理 */}
FT_New_Face()函数原型如下所示:
FT_Error FT_New_Face(FT_Library library, const char *filepathname,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
;失败将返回一个非零值的错误码。
设置字体大小
设置字体的大小有两种方式:
FT_Set_Char_Size()
和
FT_Set_Pixel_Sizes()
。
FT_Set_Pixel_Sizes()
函数
调用
FT_Set_Pixel_Sizes()
函数设置字体的宽度和高度,以像素为单位,使用示例如下所示:
FT_Set_Pixel_Sizes(face, 50, 50);
第一个参数传入
face
句柄;第二个参数和第三个参数分别指示字体的宽度和高度,以像素为单位;需 要注意的是,我们可以将宽度或高度中的任意一个参数设置为 0
,那么意味着设置为
0
的参数将会与另一个 参数保持相等,如下所示:
FT_Set_Pixel_Sizes(face, 50, 0);
上面调用
FT_Set_Pixel_Sizes()
函数时,将字体高度设置为
0
,也就意味着字体高度将自动等于字体宽度 50。
FT_Set_Char_Size()
函数
调用
FT_Set_Char_Size()
函数设置字体大小,示例如下所示,假设在一个
300x300dpi
的设备上把字体大 小设置为 16pt
:
error = FT_Set_Char_Size(face,//face 对象的句柄16*64, // 以 1/64 点为单位的字体宽度16*64, // 以 1/64 点为单位的字体高度300, // 水平方向上每英寸的像素点数300); //垂直方向上每英寸的像素点数
说明:
⚫
字体的宽度和高度并不是以像素为单位,而是以
1/64
点(
point
)为单位表示(也就是
26.6
固定浮 点格式),一个点是一个 1/72
英寸的距离。
⚫
同样也可将宽度或高度其中之一设置为
0
,那么意味着设置为
0
的参数将会与另一个参数保持相 等。
⚫
dpi
参数设置为
0
时,表示使用默认值
72dpi
。
加载字形图像
设置完成之后,接下来就可以加载字符图像了,总共分为
3
步:
a)
、获取字符的字形索引
通过
FT_Get_Char_Inde()
函数将字符编码转换为字形索引(
glyph index
),
Freetype
默认使用
UTF-16
编 码类型,也就是 Unicode
编码方式,采用
2
个字节来表示一个编码值。
示例代码
显示单个
ASCII
字符
显示单个中文字符
显示一行字符(一行字符对齐显示)
显示多行字符(一行字符的高度计算)
字体变形(旋转、斜体
……..
)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <math.h> //数学库函数头文件
#include <wchar.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#define FB_DEV "/dev/fb0" //LCD 设备节点
#define argb8888_to_rgb565(color) ({ \
unsigned int temp = (color); \
((temp & 0xF80000UL) >> 8) | \
((temp & 0xFC00UL) >> 5) | \
((temp & 0xF8UL) >> 3); \
})
static unsigned int width; //LCD 宽度
static unsigned int height; //LCD 高度
static unsigned short *screen_base = NULL;//LCD 显存基地址 RGB565
static unsigned long screen_size;
static int fd = -1;
static FT_Library library;
static FT_Face face;
static int fb_dev_init(void)
{
struct fb_var_screeninfo fb_var = {0};
struct fb_fix_screeninfo fb_fix = {0};
/* 打开 framebuffer 设备 */
fd = open(FB_DEV, O_RDWR);
if (0 > fd) {
fprintf(stderr, "open error: %s: %s\n", FB_DEV, strerror(errno));
return -1;
}
/* 获取 framebuffer 设备信息 */
ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);
screen_size = fb_fix.line_length * fb_var.yres;
width = fb_var.xres;
height = fb_var.yres;
/* 内存映射 */
screen_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == (void *)screen_base) {
perror("mmap error");
close(fd);
return -1;
}
/* LCD 背景刷成黑色 */
memset(screen_base, 0xFF, screen_size);
return 0;
}
static int freetype_init(const char *font, int angle)
{
FT_Error error;
FT_Vector pen;
FT_Matrix matrix;
float rad; //旋转角度
/* FreeType 初始化 */
FT_Init_FreeType(&library);
/* 加载 face 对象 */
error = FT_New_Face(library, font, 0, &face);
if (error) {
fprintf(stderr, "FT_New_Face error: %d\n", error);
exit(EXIT_FAILURE);
}
/* 原点坐标 */
pen.x = 0 * 64;
pen.y = 0 * 64; //原点设置为(0, 0)
/* 2x2 矩阵初始化 */
rad = (1.0 * angle / 180) * M_PI; //(角度转换为弧度)M_PI 是圆周率
#if 0 //非水平方向
matrix.xx = (FT_Fixed)( cos(rad) * 0x10000L);
matrix.xy = (FT_Fixed)(-sin(rad) * 0x10000L);
matrix.yx = (FT_Fixed)( sin(rad) * 0x10000L);
matrix.yy = (FT_Fixed)( cos(rad) * 0x10000L);
#endif
#if 1 //斜体 水平方向显示的
matrix.xx = (FT_Fixed)( cos(rad) * 0x10000L);
matrix.xy = (FT_Fixed)( sin(rad) * 0x10000L);
matrix.yx = (FT_Fixed)( 0 * 0x10000L);
matrix.yy = (FT_Fixed)( 1 * 0x10000L);
#endif
/* 设置 */
FT_Set_Transform(face, &matrix, &pen);
FT_Set_Pixel_Sizes(face, 50, 0); //设置字体大小
return 0;
}
static void lcd_draw_character(int x, int y,
const wchar_t *str, unsigned int color)
{
unsigned short rgb565_color = argb8888_to_rgb565(color);//得到 RGB565 颜色值
FT_GlyphSlot slot = face->glyph;
size_t len = wcslen(str); //计算字符的个数
long int temp;
int n;
int i, j, p, q;
int max_x, max_y, start_y, start_x;
// 循环加载各个字符
for (n = 0; n < len; n++) {
// 加载字形、转换得到位图数据
if (FT_Load_Char(face, str[n], FT_LOAD_RENDER))
continue;
start_y = y - slot->bitmap_top; //计算字形轮廓上边 y 坐标起点位置 注意是减去 bitmap_top
if (0 > start_y) {//如果为负数 如何处理??
q = -start_y;
temp = 0;
j = 0;
}
else { // 正数又该如何处理??
q = 0;
temp = width * start_y;
j = start_y;
}
max_y = start_y + slot->bitmap.rows;//计算字形轮廓下边 y 坐标结束位置
if (max_y > (int)height)
max_y = height;
for (; j < max_y; j++, q++, temp += width) {
start_x = x + slot->bitmap_left; //起点位置要加上左边空余部分长度
if (0 > start_x) {
p = -start_x;
i = 0;
}
else {
p = 0;
i = start_x;
}
max_x = start_x + slot->bitmap.width;
if (max_x > (int)width)
max_x = width;
for (; i < max_x; i++, p++) {
// 如果数据不为 0,则表示需要填充颜色
if (slot->bitmap.buffer[q * slot->bitmap.width + p])
screen_base[temp + i] = rgb565_color;
}
}
//调整到下一个字形的原点
x += slot->advance.x / 64; //26.6 固定浮点格式
y -= slot->advance.y / 64;
}
}
int main(int argc, char *argv[])
{
/* LCD 初始化 */
if (fb_dev_init())
exit(EXIT_FAILURE);
/* freetype 初始化 */
if (freetype_init(argv[1], atoi(argv[2])))
exit(EXIT_FAILURE);
/* 在 LCD 上显示中文 */
int y = height * 0.25;
lcd_draw_character(50, 100, L"路漫漫其修远兮,吾将上下而求索", 0x000000);
lcd_draw_character(50, y+100, L"莫愁前路无知己,天下谁人不识君", 0x9900FF);
lcd_draw_character(50, 2*y+100, L"君不见黄河之水天上来,奔流到海不复回", 0xFF0099);
lcd_draw_character(50, 3*y+100, L"君不见高堂明镜悲白发,朝如青丝暮成雪", 0x9932CC);
/* 退出程序 */
FT_Done_Face(face);
FT_Done_FreeType(library);
munmap(screen_base, screen_size);
close(fd);
exit(EXIT_SUCCESS);
}
编译方法:
${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