simplegui自定义字库——UTF8版

2023-05-16

写作背景

日前,笔者在学习simplegui,制作了一套适用于simplegui的自定义字库——UTF8版。

在和群友交流的时候感觉自己讲不清楚,经过分析是自己还没能深入理解相关知识!!!

为了方便交流和提高自己,编写本文。

预备知识

simplegui简介

simplegui是一个面向单色显示屏的开源GUI接口库。作者是Polarix。目前主要在gitee上维护。源码地址:https://gitee.com/Polarix/simplegui。

作者的设计初衷是制作轻量级的仅仅用在单色屏的GUI。毕竟现在主流的GUI,比如emWin、Qt等都太过庞大。在单片机作为主控的小型项目中,有很多不能支持emWin这种GUI需要的RAM和ROM,比如笔者经常使用M0内核单片机,RAM16K,ROM120K都不能支持,更别说STM32F030系列中仅有8K的RAM,64K的ROM。

字库说明

关于字库和字符编码就不详细介绍了。可以参考simplegui作者Polarix的文档https://gitee.com/Polarix/simplegui/blob/Develope/Documents/How to create font data.md
或者群友的博客https://blog.csdn.net/weixin_43614541/article/details/104581473
同时上述两个文档也都介绍了如何在simplegui中自定义字库。

理论内容看两位的文档即可,关于UTF8的实际测试。范例如下:

//文档编码格式必须是UTF8,同时代码中的汉字不乱码

#include "stdio.h"

char c_chinese_buf[] = "123456请输入密码";

int main(int argc, char const *argv[])
{

	for (int i = 0; i < sizeof(c_chinese_buf); i++)
	{
		printf("%02x ", c_chinese_buf[i]);
	}

	return 0;
}
/**
得到结果如下:
e8 af b7 请
e8 be 93 输
e5 85 a5 入
e5 af 86 密
e7 a0 81 码
00 
*/

如果你的系统是64位,可能得到的结果是 0xffffffe8、af、0xffffffb7 这样的结果。自动忽略前置 f 即可。
如此就可以清晰的看到一个汉字(此处仅包含简单常用汉字)是由三个字节组成。

simplegui字库简介

simplegui采用结构的方式调用字库。

typedef struct
{
    SGUI_INT							iHalfWidth;
    SGUI_INT							iFullWidth;
    SGUI_INT							iHeight;
	SGUI_FN_IF_GET_CHAR_INDEX			fnGetIndex;
	SGUI_FN_IF_GET_DATA                 fnGetData;
	SGUI_FN_IF_STEP_NEXT                fnStepNext;
	SGUI_FN_IF_IS_FULL_WIDTH            fnIsFullWidth;
}SGUI_FONT_RES;

重点是后面四个函数的用法。我用我的语言描述下个人理解:

名称描述
iHalfWidth半字宽度,一般用来显示 ASCII 字符
iFullWidth全字宽度,一般用来做汉字
iHeight字高度
fnGetIndex获取字符索引,实际是根据指针获取以半字宽为单位的字库中的地址索引
fnGetData获取字库数据,根据上一个函数获取的地址索引而计算的地址作为传入参数
fnStepNext下一个字符偏移,实际是获取本字符的编码,返回下个字符的地址
fnIsFullWidth判断是否全字符宽度,就是fnGetData中的那个计算方式

可能比较难理解,下面有举例说明,此处不再赘述。

simplegui自定义字库

此处也参看上文引用的两篇文章即可。如果基础不够可以百度学习下。
我的学习经验告诉我,不是不会做字库,是不懂字符编码。

simplegui自定义UTF8字库

我的定义如下:

const SGUI_FONT_RES SGUI_DEFAULT_FONT_chinese =
	{
		/*SGUI_INT                      iHalfWidth*/
		8,
		/*SGUI_INT                      iFullWidth*/
		16,
		/*SGUI_INT                      iHeight*/
		16,
		/*SGUI_FN_IF_GET_CHAR_INDEX     fnGetIndex*/
		SGUI_Resource_GetCharIndex_chinese,
		/*SGUI_FN_IF_GET_DATA           fnGetData*/
		SGUI_Resource_GetFontData_chinese,
		/*SGUI_FN_IF_STEP_NEXT          fnStepNext*/
		SGUI_Resource_StepNext_chinese,
		/*SGUI_FN_IF_IS_FULL_WIDTH      fnIsFullWidth*/
		SGUI_Resource_IsFullWidth_chinese};

后面详细介绍每个移植的细节,同时解释上文中关于这个结构体的描述。
我们不按照结构体定义的顺序介绍,而是simplegui底层库的调用顺序,准确的说是SGUI_Text_DrawText()函数的调用顺序

  1. SGUI_Resource_StepNext_chinese()
SGUI_CSZSTR SGUI_Resource_StepNext_chinese(SGUI_CSZSTR cszSrc,
										   SGUI_UINT32 *puiCode)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	const SGUI_CHAR *pcNextChar;

	/*----------------------------------*/
	/* Initialize						*/
	/*----------------------------------*/
	pcNextChar = cszSrc;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/

	if (NULL != pcNextChar)
	{
		//字符是ASCII
		if (*pcNextChar < 0x80)
		{
			*puiCode = *pcNextChar; //ASCII字符则直接赋值
			pcNextChar++;			//ASCII字符则直接指向下一个字符的地址
		}
		//字符是汉字
		else
		{
			//UTF8的汉字编码是三个byte表示,做一个组合表示一个汉字
			*puiCode = ((*(pcNextChar + 0)) << 16) +
					   ((*(pcNextChar + 1)) << 8) +
					   ((*(pcNextChar + 2)) << 0);

			pcNextChar += 3; //此处是指向的下一个汉字的地址而不是索引值
		}
	}

	return pcNextChar;
}
  • 第一个传入参数是字符指针,比如 “请输入密码” 中的 “请” 所在的地址。
  • 第二个参数是传出参数指针,这个可以自由定义,此处我把他定义为每个汉字特有的编码的组合
    比如我们之前测试得到的结果:e8 af b7 请,我就使用 0x00e8afb7 表示请。
    ASCII 码直接表示即可。比如 “123456” 中的 ‘1’ 直接返回 0x31 即可。
  • 函数返回值是下一个字符的地址,ASCII直接+1 即可,但是汉字字符必须+3,
    因为UTF8表示的常用汉字用三个字节表示
    注意:根据 SGUI_Text_DrawText()源码,此处的返回值会在下一个循环中,作为第一个传入参数使用。
  1. SGUI_Resource_IsFullWidth_chinese()
SGUI_BOOL SGUI_Resource_IsFullWidth_chinese(SGUI_UINT32 uiCode)
{
	//字符是ASCII
	if (uiCode < 0x80)
	{
		return SGUI_FALSE;
	}
	//字符是汉字
	else
	{
		return SGUI_TRUE;
	}
}
  • 传入参数就是SGUI_Resource_StepNext_chinese()的传出参数。如果字符则半宽,如果汉字则全部宽度。
  1. SGUI_Resource_GetCharIndex_chinese()
SGUI_INT SGUI_Resource_GetCharIndex_chinese(SGUI_UINT32 uiCode)
{
	/*----------------------------------*/
	/* Variable Declaration				*/
	/*----------------------------------*/
	SGUI_INT iIndex;

	//为了计算方便,要求字库必须是先数字后汉字,同时下面连两个数组按顺序包含全部字符
	//半字宽的目录
	SGUI_CBYTE c_chinese_font_index_buf_for_halfwidth[] = "0123456789";
	//汉字目录
	SGUI_CBYTE c_chinese_font_index_buf[] = "请输入密码老婆我爱你";
	//汉字索引
	SGUI_CBYTE *pc_chinese_index = NULL;

	/*----------------------------------*/
	/* Initialize						*/
	/*----------------------------------*/
	// Initialize variable.
	iIndex = SGUI_INVALID_INDEX;

	/*----------------------------------*/
	/* Process							*/
	/*----------------------------------*/

	//ASCII码
	if (uiCode < 128)
	{
		pc_chinese_index = c_chinese_font_index_buf_for_halfwidth;
		//去除汉字,和判定结束
		while ((*pc_chinese_index < 0x80) &&
			   (*pc_chinese_index != 0x00))
		{
			if (*pc_chinese_index++ == uiCode)
			{
				iIndex = pc_chinese_index -
						 c_chinese_font_index_buf_for_halfwidth - 1;
				break; //ASCII直接返回地址即可
			}
		}
	}
	//汉字
	else
	{
		pc_chinese_index = c_chinese_font_index_buf;
		//去除ascii
		while (*pc_chinese_index < 0x80)
		{
			pc_chinese_index++;
		}

		//检查数组结束
		while (*pc_chinese_index > 0x7F)
		{
			if ((*pc_chinese_index++ == ((uiCode >> 16) & 0xff)) &&
				(*pc_chinese_index++ == ((uiCode >> 8) & 0xff)) &&
				(*pc_chinese_index++ == ((uiCode >> 0) & 0xff)))
			{
				//获取了在纯汉字中的地址偏移
				iIndex = pc_chinese_index - c_chinese_font_index_buf - 3;
				//获取了在纯汉字中的索引,本文件是UTF8编码,汉字数组占3个byte
				iIndex /= 3;
				//汉字占用两个宽度
				iIndex *= 2;
				//然后加上前面的ASCII的个数
				iIndex += sizeof(c_chinese_font_index_buf_for_halfwidth) - 1;
				break;
			}
		}
	}

	return iIndex; //得到的指针偏移要除以每个汉字所占byte的长度
}
  • 传入参数是SGUI_Resource_StepNext_chinese()的传出参数
  • 返回值是字符在字库中的位置索引,这个位置以半字宽度为一个单位。比如“12345请输入密码”,
    此处“1”字位置索引是0,“2”字位置索引是1,“5”字位置索引是4,
    注意“请”字的索引是5,但是“输”字的索引是7,因为“请”字是汉字,占用两个半字宽度。
    同理:“入”字索引是9。
  • 本函数的代码解释:
    理解 SGUI_Resource_StepNext_chinese()函数的传出参数,对本函数编程的理解有重大意义。
    首先定义两个数组,分别包含半字宽的字符和汉字宽度的字符。同时要求字库内容必须先ASCII后汉字
    ASCII的代码比较简单就不再赘述。
    汉字的解析代码是正确比对 表示汉字的三个字节 和 字节索引数组中汉字的三个字节。
    如果找到了就除以3,计算第几个汉字,然后乘以2计算第几个半字宽度索引,再加上前边的ASCII字符即可。
  1. SGUI_Resource_GetFontData_chinese()
SGUI_SIZE SGUI_Resource_GetFontData_chinese(SGUI_SIZE sStartAddr,
											SGUI_BYTE *pDataBuffer,
											SGUI_SIZE sReadSize)
{
	SGUI_SIZE sReadCount;
	//TODO:考虑此处根据地址自动计算是否乘以2,以便实现字符和汉字统一数组的操作
	/*貌似是库的问题,此处要乘以2*/
	const SGUI_BYTE *pSrc = SGUI_FONT_chinese + sStartAddr;
	SGUI_BYTE *pDest = pDataBuffer;

	if (NULL != pDataBuffer)
	{
		for (sReadCount = 0; sReadCount < sReadSize; sReadCount++)
		{
			*pDest++ = *pSrc++;
		}
	}
	return sReadCount;
}
  • 这个比较简单,只是简单的复制,此处的第一个参数就是根据SGUI_Resource_StepNext_chinese()返回值计算的。

simplegui自定义字库——UTF8使用范例

void TestTxt_TestDrawTextChiness(SGUI_SCR_DEV *pstDeviceInterface)
{
    SGUI_RECT stDisplayArea;
    SGUI_POINT stPos; //控制位置偏移

    stDisplayArea.iX = 0;
    stDisplayArea.iY = 0;
    stDisplayArea.iWidth = 180;
    stDisplayArea.iHeight = 16;
    stPos.iX = 10;
    stPos.iY = 0;

    SGUI_Text_DrawText(pstDeviceInterface,
                       "123请4输5入1密2码345",
                       &SGUI_DEFAULT_FONT_chinese,
                       &stDisplayArea,
                       &stPos,
                       SGUI_DRAW_NORMAL);
    pstDeviceInterface->fnSyncBuffer();
}

这样就实现了汉字ASCII混合使用了。例如菜单中,“1.温度校准”这种使用方式。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

simplegui自定义字库——UTF8版 的相关文章

随机推荐

  • Parseval’s theorem帕萨瓦尔定理及其证明过程

    Parseval 定理是信号视频分析 xff0c 相关推导过程汇总最常用的定理之一 xff0c 我们较为常用的表述是 xff0c 信号在时域和频域上的功率相等 xff0c 现在找到一个较为详细的帕萨瓦尔定理的原始版本及其证明过程 xff0c
  • 外包or外派岗,可以去?

    大家好 xff0c 今天说说外包 外派岗位那些事 上一回 说到 xff0c Sheldon争取到大厂工作的机会 xff0c 其实是世界500强外资银行H xff0c 委托某上市人力外包公司C招的外派岗位 外包 外派的本质 在H银行的经理口中
  • 仓库 “http://ppa.launchpad.net/v-launchpad-jochen-sprickerhof-de/pcl/ubuntu focal Release” 没有 Release

    sudo add apt repository ppa v launchpad jochen sprickerhof de pcl报错仓库 http ppa launchpad net v launchpad jochen spricker
  • VNCViewer 灰屏白屏 没有按钮没有桌面 非root,实验室环境

    背景 实验室有很多的账户 xff0c 我不是root用户 我想开一个vnc远程 我依次执行下面的代码 vncserver cmd打印的结果会包括有一个你对应的序号 xff0c 也就是你是第几个申请使用vncserver的 例如我是10 接着
  • 20200628-01 树莓派4B 交叉编译 Qt5.14.2 基于 ubuntu18.04 & ubuntu20.0

    修改日志 20210813 增加部分内容 1 rpi update 本地更新的方式 2 tests pkg config fail 错误的处理 3 增加 GL 驱动开启的说明 前言 执行流程包含树莓派上的操作 xff0c 以及 PC 机上的
  • 基于K-近邻算法的手写字符识别

    收集数据集 包括trainingDigits和testDigits文件夹 文件夹下是字符图像的二进制文本文件 大小为32 32 如下图所示 1 将二进制图像文本文件转换为一行向量 首先介绍listdir 它能返回文件夹路径下的所有子文件 i
  • vncviewer客户端连接服务器失败,报connect:connect refused(10061)

    这个问题百度了一下 xff0c 发现百度说的这个拒绝连接千遍一律都是 xff1a 1 服务器上的vnc没起来 xff0c service vncserver status 查看运行状态 xff0c 如果没起来就重新启动service vnc
  • virtualbox ubuntu桥接网卡不能上网,重启后不能上网问题

    设置完桥接网卡后 sudo vim etc network interfaces ip根据宿主机ip设置 编辑虚拟机网络配置文件 内容如下 interfaces 5 file used by ifup 8 and ifdown 8 auto
  • STM32 模拟IIC驱动MS5607气压传感器调试记录

    标题 STM32 模拟IIC驱动MS5607调试记录 因项目需求 xff0c 需使用MS5607的作为检测气压 xff0c 再此写下自己遇见的问题以及调试记录 硬件原理图如下 xff1a IIC接口总线使用了气压传感器 xff0c SHT3
  • 单片机学习 2-蜂鸣器

    蜂鸣器 蜂鸣器介绍 压电式蜂鸣器 xff08 无源蜂鸣器 xff09 发出声音必须提供一定频率的脉冲信号 1 5 5000HZ 频率周期固定 xff0c 高电平占用周期内时间不同 xff0c 音调不同 xff1b 高电平占用比不变 xff0
  • Linux配置网桥

    一 创建网桥配置文件 cp etc sysconfig network scripts ifcfg eth0 etc sysconfig network scripts ifcfg br0 二 配置网卡 设置网络为静态IP vi etc s
  • “野火FreeRTOS教程”第9章知识点总结-空闲任务与阻塞延时

    前言 在第7章我们知道在创建任务并启动任务调度器后 xff0c 当前任务控制块是指向第一个任务 xff08 即pxCurrentTCB 61 amp Task1TCB xff09 xff0c 然后会产生SVC中断 xff0c 将pxCurr
  • XShell+Xmanager实现在XShell中显示远程服务器的图形界面

    最近开始学习机器学习的知识 xff0c 迫切的需要用到Linux环境 xff0c 但是因为虚拟机用着电脑会变卡 xff0c 而且自己有台式 43 笔记本 xff0c 所以自己想要创建一个随时随地多设备可以用的实验环境 因此想到了搭建一个Li
  • MFC CStringArray使用方法

    xfeff xfeff MFC CStringArray使用方法 2014 07 04 10 03 34 转载 标签 xff1a vc 分类 xff1a Visual C CArray arrroads CStringArray road
  • 致我们终将逝去的2014

    一眨眼 xff0c 2014年的最后一张日历即将撕去 xff0c 迎来的是面貌全新的2015 回首2014 xff0c 回首这一年所经历的一切 xff0c 感觉那么近又那么远 下面将从几个方便总结自己的2014 xff1a 一 专业方面 x
  • 计算机基础知识之-线程&进程

    线程 amp 进程 死锁 多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力 然而 xff0c 并发执行也带来了新的问题 死锁 死锁是指两个或两个以上的进程 xff08 线程 xff09 在运行过程中因争夺资源而造成的一种僵局
  • linux下mysql访问速度缓慢并且ssh连接缓慢

    最近发现ssh连接的时候却很慢 xff0c ping的速度非常好 xff0c 让人误以为是ssh连接不上 分析结果 xff0c 主要原因为 xff1a DNS的解析IP导致 xff0c 可分别使用以下几种分析处理方式 1 linux ssh
  • vmware安装centos8之网络配置

    1 下载vmware 2 新建虚拟机 3 网络配置 3 1 查看自己主机ip 网关等信息 xff0c 例如个人主机信息如下 3 2 设置vmware网络连接 vmware gt 我的计算机 gt 设置 gt 网络适配器 gt 网络连接 gt
  • Ubuntu连上wifi后,能内网通讯,但访问不了外网的问题解决办法

    最近用Jetson Nano遇到了这样一个问题 系统时间一直定格在了2月7日的晚上 每次重启 xff0c 就会回到那个时间 经过一番检测 xff0c 发现是因为连不上外网 xff0c 导致不能同步时间 然后按照常规方法 xff0c 检测DN
  • simplegui自定义字库——UTF8版

    写作背景 日前 xff0c 笔者在学习simplegui xff0c 制作了一套适用于simplegui的自定义字库 UTF8版 在和群友交流的时候感觉自己讲不清楚 xff0c 经过分析是自己还没能深入理解相关知识 xff01 xff01