fuzidage
专注嵌入式、linux驱动 、arm裸机研究

导航

 

ASCII

ascii是“American Standard Code for Information Interchange”的缩写, 美国信息交换标准代码。

电脑毕竟是西方人发明的,他们常用字母就 26 个,区分大小写、加上标点符号也没超过 127 个,每个字符用一个字节来表示就足够了。一个字节的 7 位就可以表示 128 个数值,在 ASCII 码中最高位永远是 0。

linux-4.18.16/lib/fonts这个目录下就有对应文件。在这里我挑选font_8x16.c

 

 

ANSI

ASNI 是 ASCII 的扩展,向下包含 ASCII。对于 ASCII 字符仍以一个字节来表示.

对于非 ASCII 字符则使用 2 字节来表示。并没有固定的 ASNI 编码.

比如在中国大陆地区, ANSI 的默认编码是 GB2312;

在港澳台地区默认编码是 BIG5。以数值“ 0xd0d6”为例,对于 GB2312 编码它表示“中”;对于 BIG5 编码它表示“ 笢”。

用ANSI编码字符'aa中'的16进制数据

 

UNICODE

在 ANSI 标准中,很多种文字都有自己的编码标准,汉字简体字有 GB2312、繁体字有 BIG5,这难免同一个数值对应不同字符。比如数值“ 0xd0d6”,对于GB2312 编码它表示“中”;对于 BIG5 编码它表示“ 笢”。这造成了使用 ANSI 编码保存的文件,不适合跨地区交流。

UNICODE 编码就是解决这类问题:对于地球上任意一个字符,都给它一个唯一的数值。

  1. ASCII 编码中使用一个字节来表示一个字符,只用到其中的 7 位,最高位恒为 0;
  2. ANSI 编码中,对于 ASCII 字符仍使用一个字节来表示(BIT7 是 0),对于非ASCII 字符一般使用 2 个字节来表示,非 ASCII 字符的数值 BIT7 都是 1

UTF-16 LE

每个 UNICODE 值用 3 字节来表示有点浪费,那只用 2 字节呢?它可以表示2^16=65536 个字符,全世界常用的字符都可以表示了。Little endian 表示小字节序,数值中权重低的字节放在前面,比如字符“ A 中”在 TXT 文件中的数值如下,其中的“ A”使用“0x41 0x00”两字节表示;“中”使用“ 0x2d 0x4e”两字节表示。文件开头的“ 0xff 0xfe”表示“UTF-16 LE”。

 

UTF-16 BE

Big endian 表示大字节序,数值中权重低的字节放在后面,比如字符“ ab中”在 TXT 文件中的数值如下,其中的“ A”使用“ 0x00 0x41”两字节表示;“中”使用“ 0x4e 0x2d”两字节表示。文件开头的“ 0xfe 0xff”表示“UTF-16 BE”

 

UTF8

UTF8 是一种变长的编码方法,有 2 种 UTF8格式的文件:带有头部、不带头部。

 

对于 ASCII 字符用UTF-16有空间浪费、而且文件中有某个字节丢失,这会使得后面所有字符都因为错位而无法显示。UTF8则不会有这样的问题。0x41表示大写字母'A',只用了一个字节。上图中的 3 个字节“ 0xe4 0xb8 0xad”表示的数值是 0x4e2d,对应“中”的 UNICODE 码.

 

上图中, 0xe4 的二进制是“ 11100100”,高位有 3 个 1,表示从当前字节起有 3 字节参与表示 UNICODE;

0xb8 的二进制是“10111000”,高位有 1 个 1,表示从当前字节起有 1 字节参与表示 UNICODE;

0xad 的二进制是“10101101”,高位有 1 个 1,表示从当前字节起有 1 字节参与表示 UNICODE;

除去高位的“ 1110”、“ 10”、“ 10”后,剩下的二进制数组合起来得到“ 01001110001101”,它就是 0x4e2d,即“中”的 UNICODE 值。

使用 UTF8 编码时,即使 TXT 文件中丢失了某些数据,也只会影响到当前字符的显示,后面的字符不受影响。

中文字库移植

-finput-charset -fexec-charset编译选项

我们编写 C 程序时,可以使用 ANSI 编码,或是 UTF-8 编码;在编译程序时,可以使用以下的选项告诉编译器用什么方式编码:

-finput-charset=GB2312

-finput-charset=UTF-8

如果不指定“ -finput-charset”, GCC 就会默认 C 程序的编码方式为 UTF8。

1.用ANSI格式编写编码,vim浏览显示会乱码,用notepad++采用ANSI编码格式浏览.

 

由于编译器默认用utf8编码,所以看到最终打印是乱码的。可以看到“中”的ANSI码是d6 d0。

2.用utf8编写代码

 

 

最终打印是OK的。可以看到“中”的utf8码是e4 b8 ad.

GB2312 转为 UTF-8

 

从上面的输出信息可以看出来, GB2312 的"0xd6 0xd0"可以转换为 UTF-8的“ 0xe4 0xb8 0xad”。而如果把原本就是 UTF-8 格式的 test_charset_utf8.c当作 GB2312 格式,会引起错误

UTF-8 转为 GB2312

 

从 上 面 的 输 出 信 息 可 以 看 出 来 , 如 果 把 原 本 就 是 GB2312 格 式 test_charset_ansi.c 当成UTF-8 格式,会引起错误。而 UTF-8 格式的“中”编码值为“ 0xe4 0xb8 0xad”,可以转换为 GB2312 的“0xd6 0xd0”

HZK16中文字库

HZK16字库里的16×16汉字一共需要256个点来显示,也就是说需要32个字节才能达到显示一个普通汉字的目的。符合GB2312标准。

一个GB2312汉字是由两个字节编码的,范围为A1A1~FEFE。A1-A9为符号区,B0到F7为汉字区。每一个区有94个字符。

HZK16 中是以 GB2312 编码值来查找点阵的,以“中”字为例,它的编码值是“ 0xd6 0xd0”,其中的 0xd6 表示“区码”,表示在哪一个区;其中的 0xd0 表示“位码”,表示它是这个区里的哪一个字符。每一个区有 94 个汉字。区位码从 0xa1 而不是从 0 开始,是为了兼容 ASCII码。

要显示“中”字, 它的 GB2312 编码是 d6d0,它是 HZK16 里第“ (0xd6-0xa1)*94+(0xd0-0xa1)”个字符。(0xd6-0xa1)表示是哪个区,(0xd0-0xa1)表示是哪个位。

如何获取和显示汉字”中“?

fd_hzk16 = open("HZK16", O_RDONLY);
if (fd_hzk16 < 0){
        printf("can't open HZK16\n");
        return -1;
}
if(fstat(fd_hzk16, &hzk_stat)){
        printf("can't get fstat\n");
        return -1;
}
hzkmem = (unsigned char *)mmap(NULL , hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
if (hzkmem == (unsigned char *)-1){
        printf("can't mmap for hzk16\n");
        return -1;
}

下载HZK16字库,读取并且mmap字库。

 

 

 

 该函数在 LCD 的(x,y)位置处显示汉字字符 str, str[0]中保存区码、 str[1]中保存位码。

第 4734 行确定该汉字属于哪个区;第 4735 行确实它是该区中哪一个汉字。

第 4736 行确实它的字库地址(第多少个字节):每个区中有 94 个汉字,每个汉字在字库中占据 32 字节。

根据下图来理解字库中每个像素点是如何显示的。

 

总共有十六行,因此需要一个循环 16次的大循环(第 4740 行)。

考虑到一行有两个字节, 在大循环中加入一个 2 次的循环用于区分是哪个字节(第 4741 行)。

最后使用第 3 个循环来处理一个字节中的 8 位(第 4744 行)。对于每一位,它等于 1 时对应的像素被设置为白色,它等于 0 时对应的像素被设置为黑色。需要注意的是根据 x、 y、 i、 j、 b 来计算像素坐标。

测试:

 

注意:使用上述命令时 show_chinese.c 的编码格式必须是 ANSI(GB2312),因为HZK16字库是按照GB2312编码的,否则编译时需要指定“ -fexec-charset=GB2312”。

freetype移植

FreeType库是一个完全免费(开源)的、高质量的且可移植的字体引擎。

Freetype 是开源的字体引擎库, 它提供统一的接口来访问多种字体格式文件,从而实现矢量字体显示。我们只需要移植这个字体引擎,调用对应的 API 接口,提供字体文件,就可以让 freetype 库帮我们取出关键点、实现闭合曲线, 填充

颜色, 达到显示矢量字体的目的。

这里仅移植freetype库,freetype的使用不做具体展开。可以从 https://www.freetype.org/ 可 以 下 载 到 “ freetype-doc-2.10.2.tar.xz”。

什么是矢量字体

使用点阵字库显示英文字母、汉字时, 大小固定, 如果放大缩小则会模糊甚

至有锯齿出现,为了解决这个问题,引用矢量字体。

第1步 确定关键点,

第2步 使用数学曲线( 贝塞尔曲线) 连接头键点,

第3步 填充闭合区线内部空间。

什么是关键点?以字母“ A”为例

 

再用数学曲线(比如贝塞尔曲线)将关键点都连接起来, 得到一系列的封闭的曲线

 

 最后把封闭空间填满颜色,就显示出一个 A 字母

 

 如果需要放大或者缩小字体,关键点的相对位置是不变的, 只要数学曲线平滑,字体就不会变形。

下载freetype: https://freetype.org/download.html

https://download.savannah.gnu.org/releases/freetype/

freetype 依赖于 libpng, libpng 又依赖于 zlib,所以我们应该:先编译安装 zlib,再编译安装 libpng,最后编译安装 freetype。 但是,有些工具链里有 zlib, 那就不用编译安装 zlib.

下载安装libpng: https://www.linuxfromscratch.org/blfs/view/svn/general/libpng.html

下载安装zlib: https://www.zlib.net/fossils/

1.根据自己的工具链设置环境:

export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin

echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v -

头文件的系统目录为:

 /media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include

 /media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include-fixed

 /media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/include

 /media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include

库文件的系统目录为:

COMPILER_PATH=/media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../libexec/gcc/arm-buildroot-linux-gnueabihf/7.5.0/:/media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../libexec/gcc/:/media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/bin/

LIBRARY_PATH=/media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/:/media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/:/media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/lib/:/media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/lib/:/media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/lib/

2.编译zlib

编译zlib库时,./configure不允许传入–host参数;不支持的话需要export CC设置为你的arm工具链

export CC=arm-buildroot-linux-gnueabihf-gcc
./configure --prefix=$PWD/tmp
make;make install
cd tmp/lib;ls

将lib和头文件拷贝到工具链目录(或者不拷贝,到时候编译用-L, -I指定即可,运行时指定LIBRARY_PATH)

cp include/* -rf /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include
cp lib/* -rfd /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/lib

3.编译libpng

./configure --host= arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp
make
make install
cd tmp/lib;ls

 将lib和头文件拷贝到工具链目录

4.编译freetype

./configure --host= arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp
Make
Make  install

注意:如果你的工具链路径不是 /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot,那么make时会出现类似如下错误。

 

修改自己工具链下的$(TOOLCHAIN)/arm-buildroot-linux-gnueabihf/sysroot/usr/lib目录下编辑libfreetype.la, 替换dependency_libs和libdir的路径。

来自 < http://bbs.100ask.net/question/15908>

 

libfreetype库如下:

 

将lib和头文件拷贝到工具链目录。

5.测试

gcc freetype_show_font.c -I /media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include/freetype2/ -L /media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/lib -lfreetype
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <wchar.h>
#include <sys/ioctl.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H

int fd_fb;
struct fb_var_screeninfo var;        /* Current var */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;

/**********************************************************************
 * 函数名称: lcd_put_pixel
 * 功能描述: 在LCD指定位置上输出指定颜色(描点)
 * 输入参数: x坐标,y坐标,颜色
 * 输出参数: 无
 * 返 回 值: 会
 ***********************************************************************/ 
void lcd_put_pixel(int x, int y, unsigned int color)
{
    unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;
    unsigned short *pen_16;        
    unsigned int *pen_32;        
    unsigned int red, green, blue;        

    pen_16 = (unsigned short *)pen_8;
    pen_32 = (unsigned int *)pen_8;

    switch (var.bits_per_pixel)
    {
        case 8:
        {
            *pen_8 = color;
            break;
        }
        case 16:
        {
            /* 565 */
            red   = (color >> 16) & 0xff;
            green = (color >> 8) & 0xff;
            blue  = (color >> 0) & 0xff;
            color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
            *pen_16 = color;
            break;
        }
        case 32:
        {
            *pen_32 = color;
            break;
        }
        default:
        {
            printf("can't surport %dbpp\n", var.bits_per_pixel);
            break;
        }
    }
}

/**********************************************************************
 * 函数名称: draw_bitmap
 * 功能描述: 根据bitmap位图,在LCD指定位置显示汉字
 * 输入参数: x坐标,y坐标,位图指针
 * 输出参数: 无
 * 返 回 值: 无
 ***********************************************************************/ 
void
draw_bitmap( FT_Bitmap*  bitmap,
             FT_Int      x,
             FT_Int      y)
{
    FT_Int  i, j, p, q;
    FT_Int  x_max = x + bitmap->width;
    FT_Int  y_max = y + bitmap->rows;

    //printf("x = %d, y = %d\n", x, y);

    for ( j = y, q = 0; j < y_max; j++, q++ )
    {
        for ( i = x, p = 0; i < x_max; i++, p++ )
        {
            if ( i < 0      || j < 0       ||
                i >= var.xres || j >= var.yres )
            continue;

    //image[j][i] |= bitmap->buffer[q * bitmap->width + p];
    lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);
    }
    }
}

int main(int argc, char **argv)
{
    wchar_t *chinese_str = L"";
    FT_Library          library;
    FT_Face           face;
    int error;
    FT_Vector     pen;
    FT_GlyphSlot  slot;
    int font_size = 24;

    if (argc < 2)
    {
        printf("Usage : %s <font_file> [font_size]\n", argv[0]);
        return -1;
    }

    if (argc == 3)
        font_size = strtoul(argv[2], NULL, 0);
        
    fd_fb = open("/dev/fb0", O_RDWR);
    if (fd_fb < 0)
    {
        printf("can't open /dev/fb0\n");
        return -1;
    }

    if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
    {
        printf("can't get var\n");
        return -1;
    }

    line_width  = var.xres * var.bits_per_pixel / 8;
    pixel_width = var.bits_per_pixel / 8;
    screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
    fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
    if (fbmem == (unsigned char *)-1)
    {
        printf("can't mmap\n");
        return -1;
    }

    /* 清屏: 全部设为黑色 */
    memset(fbmem, 0, screen_size);

    /* 显示矢量字体 */
    error = FT_Init_FreeType( &library );                           /* initialize library */
    /* error handling omitted */
    
    error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
    /* error handling omitted */        
    slot = face->glyph;

    FT_Set_Pixel_Sizes(face, font_size, 0);

    /* 确定座标:
     */
    //pen.x = 0;
    //pen.y = 0;

    /* set transformation */
    //FT_Set_Transform( face, 0, &pen);

    /* load glyph image into the slot (erase previous one) */
    error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER );
    if (error)
    {
        printf("FT_Load_Char error\n");
        return -1;
    }
    
    draw_bitmap( &slot->bitmap,
                 var.xres/2,
                 var.yres/2);

    return 0;        
}
View Code
posted on 2022-10-26 14:33  fuzidage  阅读(369)  评论(0编辑  收藏  举报