ASCII字符和中文字符的显示

news2024/9/19 12:29:45

目录

前言

ASCII字符的点阵显示

获取点阵

描点

main

中文字符的点阵显示

指定编码格式

汉字区位码

汉字点阵显示实验

打开汉字库文件

编写显示汉字的函数

使用 lcd_put_chinese 函数


前言

        板子为韦东山老师的imx6ull板,要在LCD上实现字符的显示,要先实现我之前的博客Framebuffer应用编程里的描点函数 lcd_put_pixel ,描点函数是屏幕显示的基础,实现了该函数才能进行本篇博客的操作,本文重点讲解字符的显示原理,实际场景中会调用函数就行了


ASCII字符的点阵显示

        要在 LCD 中显示一个 ASCII 字符,即英文字母这些字符,首先是要找到字符对应的点阵。在 Linux 内核源码中有这个文件:lib\fonts\font_8x16.c,里面以数组形式保存各个字符的点阵,比如:

数组里的数字是如何表示点阵的?以字符 A 为例:

 

        上图左侧有 16 行数值,每行 1 个字节。每一个节对应右侧一行中 8 个像素:像素从右边数起,bit0 对应第 0 个像素,bit1 对应第 1 个像素,……,bit7 对应第 7 个像素。某位的值为 1 时,表示对应的像素要被点亮;值为 0 时表示对应的像素要熄灭。

        所以要显示某个字符时,根据它的 ASCII 码在 fontdata_8x16 数组中找到它的点阵,然后取出这 16 个字节(这里要强调一下16个字节,在后面从数组里取字符的时候就能理解)去描画 16 行像素。
        比如字符 A 的 ASCII 值是 0x41,那么从 fontdata_8x16[0x41*16开始取其点阵数据。
        问:为什么要x16?
        答:每一个字符在数组里占的位置都是16个字节,数组的第一个16字节对应ASCII码为0x00的字符,那么我们要找0x41,就要用编码值乘每个字符占的字节数,这样得到的就是字符'A'在数组中的位置。

        核心函数是 void lcd_put_ascii(int x, int y, unsigned char c),它在 LCD 的(x,y)位置处显示字符 c,代码如下图所示:

获取点阵

 对于字符 c,char c,它的点阵获取方法如下:

unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];

描点

        根据“字符 A 的点阵图”,我们分析核心代码 void lcd_put_ascii(int x, int y, unsigned char c) 是如何利用点阵在 LCD 上显示一 个英文字母。

        因为有十六行,所以首先要有一个循环 16 次的大循环,然后每一行里有 8位,那么在每一个大循环里也需要一个循环 8 次的小循环。小循环里的判断单行的描点情况,如果是 1,就填充白色,如果是 0 就填充黑色,如此一来,就可以显示出黑色底,白色轮廓的英文字母。

for (i = 0; i < 16; i++)
{
    byte = dots[i];
    for (b = 7; b >= 0; b--)
    {
        if (byte & (1<<b))
        {
            /* show */
            /* 之前实现的描点函数 */
            lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 白 */
        }
        else
        {
            /* hide */
            lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */
        }
    }
}

main

        main 函数中首先要打开 LCD 设备,获取 Framebuffer 参数,实现 lcd_put_pixel(描点函数) ;然后调用 lcd_put_ascii(绘制ASCII字符函数) 即可绘制字符,代码如下:

int main(int argc, char **argv)
{
	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);

	lcd_put_ascii(var.xres/2, var.yres/2, 'A'); /*在屏幕中间显示 8*16 的字母 A*/
	
	munmap(fbmem , screen_size);
	close(fd_fb);
	
	return 0;	
}

        源码就是将描点函数和显示字符函数结合起来,但是字符点阵数组需要自己定义,在lib\fonts\font_8x16.c这个文件中找到数组,由于该数组被定义成静态变量,所以自己粘贴过来就好了。

交叉编译后在板子上运行该程序,如果实验成功就能在屏幕中心显示字符 'A' 。


中文字符的点阵显示

指定编码格式

        使用点阵字库时,中文字符的显示原理跟 ASCII 字符是一样的。要注意的地方在于中文的编码:在 C 源文件中它的编码方式是 GB2312(ANSI) 还是 UTF-8(UNICODE)?关于字符编码的知识看这篇博客字符的编码方式,编译出的可执行程序,其中的汉字编码方式是 GB2312 还是 UTF-8?

问:为什么要关心C源文件的编码方式还要关心可执行程序的编码方式?

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

        如果不指定“-finput-charset”,GCC 就会默认 C 程序的编码方式为 UTF-8,即使你是以 ANSI 格式保存,也会被当作 UTF-8 来对待。 

        对于编译出来的可执行程序,可以指定它里面的字符是以什么方式编码,可以使用以下的选项编译器:

        如果不指定“-fexec-charset”,GCC 就会默认编译出的可执行程序中字符的编码方式为 UTF-8。 

        如果“-finput-charset”与“-fexec-charset”不一样,编译器会进行格式转换。

汉字区位码

        我们从网上搜到 HZK16 这个文件,它是常用汉字的 16*16 点阵字库。HZK16里每个汉字使用 32 字节来描述(可以类比ASCII字符的点阵数组

        跟 ASCII 字库一样,每个字节中每一位用来表示一个像素,位值等于 1 时表示对应像素被点亮,位值等于 0 时表示对应像素被熄灭。 
        HZK16 中是以 GB2312 编码值来查找点阵的,以“中”字为例,它的编码值是“0xd6 0xd0”,其中的 0xd6 表示“区码”,表示在哪一个区:第“0xd6-0xa1”区(减去0xa1);其中的 0xd0 表示“位码”,表示它是这个区里的哪一个字符:第“0xd0 -0xa1”个。每一个区有 94 个汉字。区位码从 0xa1 而不是从 0 开始,是为了兼容 ASCII 码。
        所以,我们要显示的“中”字,它的 GB2312 编码是 d6d0,它是 HZK16 里第“(0xd6-0xa1)*94+(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_SHARE
                               D, fd_hzk16, 0);
if (hzkmem == (unsigned char *)-1)
{
    printf("can't mmap for hzk16\n");
    return -1;
}

\bullet open函数打开当前目录的字库文件:HZK16。
\bullet fstat获得文件的状态信息,里面含有文件长度,这在后面的 mmap 中用到。
        fstat 用于获取与文件描述符 fd 关联的文件的状态信息,并将这些信息存储在 buf 指向的struct stat 结构体中。这个结构体包括文件类型、权限、文件大小、设备ID、inode号、硬链接数量等信息。
\bullet 使用 mmap 映射文件,以后就可以像访问内存一样读取文件内容;mmap 的返回结果保存在 hzkmem 中,它将作为字库的基地址

编写显示汉字的函数

        核心函数是 void lcd_put_chinese(int x, int y, unsigned char*str),它在 LCD 的(x,y)位置处显示汉字字符 str,str[0]中保存区码、str[1]中保存位码。代码如下图所示:

代码分解如下:
\bullet 第 4734 行确定该汉字属于哪个区;第 4735 行确实它是该区中哪一个汉字。
\bullet 第 4736 行确实它的字库地址:每个区中有 94 个汉字,每个汉字在字库中占据 32 字节。
\bullet 看之前的汉字点阵排布的示意图,总共有十六行,因此需要一个循环 16 次的大循环(第 4740         行)。
\bullet 考虑到一行有两个字节,在大循环中加入一个 2 次的循环用于区分是哪个字节(第 4741 行)。
\bullet 最后使用第 3 个循环来处理一个字节中的 8 位(第 4744 行)。对于每一位,它等于 1 时对应的像     素被设置为白色,它等于 0 时对应的像素被设置为黑色。需要注意的是根据 x、y、i、j、b 来计     算像素坐标。

使用 lcd_put_chinese 函数

unsigned char str[] = "中";
……
printf("chinese code: %02x %02x\n", str[0], str[1]);
lcd_put_chinese(var.xres/2 + 8, var.yres/2, str);

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

实验成功可以在屏幕看见白色的“中”字。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2141002.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

idea集成和使用Git指南

前言 Git是一个分布式的版本控制工具&#xff0c;可以管理我们开发过程中的源代码文件&#xff0c;而idea是Java的集成开发环境&#xff0c;在idea中配置Git&#xff0c;可以提高我们的团队开发效率。因此在idea中集成Git并使用Git管理我们的源代码是必要的 第一步&#xff1a;…

多线程的高手——海王(浅谈线程概念)

听到大海的声音了吗 让我想想炉石里面能被成为海王的卡牌 我个人感觉 就是拿着三叉戟的甲壳元素 可是这牌被删了我心痛 背景知识 还是地址空间那点破事&#xff01; OS进行内存管理&#xff0c;不是以字节为单位的&#xff0c;而是以内存块为单位的&#xff01; 默认是4k…

Halo 开发者指南——项目运行、构建

准备工作 环境要求 OpenJDK 17 LTSNode.js 20 LTSpnpm 9IntelliJ IDEAGitDocker&#xff08;可选&#xff09; 名词解释 工作目录 指 Halo 所依赖的工作目录&#xff0c;在 Halo 运行的时候会在系统当前用户目录下产生一个 halo-next 的文件夹&#xff0c;绝对路径为 ~/ha…

学习图解算法 使用C语言

图解算法 使用C语言 也就是通过C语言实现各种算法 链接&#xff1a;百度云盘 提取码&#xff1a;1001

【CMake】使用CMake在Visual Stdudio构建一个最简单的项目

一、准备工作 首先&#xff0c;确保在 V i s u a l S t u d i o Visual\ Studio Visual Studio上安装了 C C C桌面开发&#xff0c;如果没有安装&#xff0c;打开 V i s u a l S t u d i o I n s t a l l e r Visual\ Studio\ Installer Visual Studio Installer就可以修改…

【JAVA干货店】带你玩转数组与递归

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 递归利用递归求斐波那契数列数组入门 递归 自己调用自己 StackOverflowError:栈溢出错误,出现的原…

滑动窗口(3)_最大连续1的数组个数III

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 滑动窗口(3)_最大连续1的数组个数III 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; …

CTFHub技能树-信息泄露-HG泄漏

目录 漏洞产生原因 解题过程 当开发人员使用 Mercurial 进行版本控制&#xff0c;对站点自动部署。如果配置不当,可能会将.hg 文件夹直接部署到线上环境。这就引起了 hg 泄露漏洞。 漏洞产生原因 Mercurial(hg)是一种分布式版本控制系统&#xff0c;它与Git类似也可以用于管…

【Java】线程状态:线程生命周期的六个阶段

欢迎浏览高耳机的博客 希望我们彼此都有更好的收获 感谢三连支持&#xff01; 在Java中&#xff0c;线程可以处于多种状态&#xff0c;这些状态描述了线程的生命周期。了解这些状态及其转换条件对于编写高效且无错误的多线程应用程序至关重要。本文将总结Java线程的几种状态&am…

半导体制造技术中的沉积和驱入(Deposition and drive-in)过程

来源&#xff1a;半导体制造技术导论——萧宏 沉积和驱入过程 图5.34 硼掺杂工艺高温扩散炉系统示意图 图5.35 扩散掺杂工艺流程 图5.36 扩散工艺在超浅结深&#xff08;USJ&#xff09;上的应用

C++设计模式——Prototype Pattern原型模式

一&#xff0c;原型模式的定义 原型模式是一种创建型设计模式&#xff0c;它允许通过克隆已有对象来创建新对象&#xff0c;从而无需调用显式的实例化过程。 原型模式的设计&#xff0c;使得它可以创建一个与原型对象相同或类似的新对象&#xff0c;同时又可以减少对象实例化…

项目实战应用Redis分布式锁

Redis分布式锁 一、前言二、Redis分布式锁过期处理三、Redis分布式实现3.1 基于Jedis 的API实现分布式锁3.1.1 基础命令3.1.2 基于Jedis API的分布式锁3.1.3 基于Lua脚本实现分布式锁 四、Redisson的使用五、Redision锁 核心源码分析六、总结 一、前言 对于项目中使用Redis分布…

jdk相关介绍

JDK&#xff0c;全称Java Development Kit&#xff0c;是Java语言开发的基础工具包。它包含了Java运行时环境&#xff08;JRE&#xff09;以及用于开发Java应用程序的各种工具和库。JDK为Java程序员提供了编译、调试和运行Java应用程序所需的全部环境。 JDK的主要组成部分包括&…

OpenCV_图像像素读写操作

本文详细介绍了如何在C项目中使用OpenCV进行图像像素的读写操作&#xff0c;包括使用头文件声明Pixel类&#xff0c;通过遍历和指针方式处理灰度图和彩色图&#xff0c;以及在主函数中调用这些操作。 数组遍历的方式进行图像像素读写 void QuickDemo::pixelVisit_Demo(Mat&am…

【最新华为OD机试E卷-支持在线评测】增强的strstr(100分)多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 💻 ACM金牌🏅️团队 | 大厂实习经历 | 多年算法竞赛经历 ✨ 本系列打算持续跟新华为OD-E/D卷的多语言AC题解 🧩 大部分包含 Python / C / Javascript / Java / Cpp 多语言代码 👏 感谢大家的订阅➕ 和 喜欢�…

替换 Oracle ,江河信息用 TDengine 解决高基数查询写入问题

在数字经济快速发展的背景下&#xff0c;智慧水利作为重要的基础设施之一&#xff0c;正逐步成为提升水资源管理效率、优化生态环境的重要力量。江西省水投江河信息技术有限公司&#xff08;以下简称“江河信息”&#xff09;作为高新技术国有企业&#xff0c;坚定致力于打造数…

监控binlog日志监控表变化进行消息通知

前言 由于我们做项目的时候有项目任务管理工具&#xff0c;目前用的最多的是禅道&#xff0c;而我们用的是redmine&#xff0c;而redmine是使用ruby写的&#xff0c;刚好我们这边没有会用ruby的人&#xff0c;所以就有了这样一个小工具&#xff0c;用于监控binlog日志通过钉钉…

机器人相关知识的本身和价值

简要将人类简史分为 农业工业信息智能 四个时代。 在信息时代&#xff0c;知识本身就可以等同于价值。 常识看&#xff0c;学历可以变现&#xff0c;高品质文凭能极大概率获得工资远远高于平均值的工作机会。 在智能时代&#xff0c;知识本身毫无价值&#xff0c;知识的应…

UML 类图(提供 Java 实现)

文章目录 UML 类图概述及作用类图表示法类&#xff08;接口&#xff09;的表示类与类之间关系的表示关联关系&#xff08;Association&#xff09;单向关联&#xff08;Unidirectional Association&#xff09;双向关联&#xff08;Bidirectional Association&#xff09;自关联…

大学生看过来,必备4款写论文AI写作网站先稿后付

在当今学术研究和写作领域&#xff0c;AI论文写作工具的出现极大地提高了写作效率和质量。这些工具不仅能够帮助研究人员快速生成论文草稿&#xff0c;还能进行内容优化、查重和排版等操作。其中&#xff0c;千笔-aipasspaper是一个备受推荐的平台&#xff0c;它结合了先稿后付…