为什么要分析字符串的显示过程?

  学习uCGUI主要是学习如何使用的,为何要深究到源码的层次呢?

  就分析字符串显示过程的原因来说,是因为移植汉字字库的需要。uCGUI并么有合适的汉字字库,而且完整的汉字字库非常庞大,消耗单片机的Flash资源。如果想要移植一个合适的字库,分析字符串显示的过程以及uCGUI字库数据结构,还是很有必要的。

GUI_DispString()函数源码                                          

 void GUI_DispString(const char GUI_UNI_PTR *s) {
int xAdjust, yAdjust, xOrg;
int FontSizeY;
if (!s)
return;
GUI_LOCK();
FontSizeY = GUI_GetFontDistY(); //获取字体的高度
xOrg = GUI_Context.DispPosX; //获取当前显示的x坐标
/* Adjust vertical position */
yAdjust = GUI_GetYAdjust();
GUI_Context.DispPosY -= yAdjust; //根据Y方向上的对齐方式对y进行调整
for (; *s; s++) {
GUI_RECT r;
int LineNumChars = GUI__GetLineNumChars(s, 0x7fff); //当前一行要显示几个字符
int xLineSize = GUI__GetLineDistX(s, LineNumChars); //当前一行在x方向上的像素数
/* Check if x-position needs to be changed due to h-alignment */
switch (GUI_Context.TextAlign & GUI_TA_HORIZONTAL) {
case GUI_TA_CENTER: xAdjust = xLineSize / ; break;
case GUI_TA_RIGHT: xAdjust = xLineSize; break;
default: xAdjust = ;
}
/* 计算出每一行显示内容的矩形区域 */
r.x0 = GUI_Context.DispPosX -= xAdjust; //根据水平方向的对齐方式对x坐标进行调整
r.x1 = r.x0 + xLineSize - ;
r.y0 = GUI_Context.DispPosY;
r.y1 = r.y0 + FontSizeY - ; GUI__DispLine(s, LineNumChars, &r); //以计算好的矩形区域显示当前行字符
GUI_Context.DispPosY = r.y0;
s += GUI_UC__NumChars2NumBytes(s, LineNumChars); //从第一个字符开始,地址加1,可以遍历整行字符串
if ((*s == '\n') || (*s == '\r')) {
switch (GUI_Context.TextAlign & GUI_TA_HORIZONTAL) {
case GUI_TA_CENTER:
case GUI_TA_RIGHT:
GUI_Context.DispPosX = xOrg;
break;
default:
GUI_Context.DispPosX = GUI_Context.LBorder;
break;
}
if (*s == '\n')
GUI_Context.DispPosY += FontSizeY;
} else {
GUI_Context.DispPosX = r.x0 + xLineSize;
}
if (*s == ) /* end of string (last line) reached ? */
break;
}
GUI_Context.DispPosY += yAdjust; //
GUI_Context.TextAlign &= ~GUI_TA_HORIZONTAL; //
GUI_UNLOCK();
}

字符串显示过程概括                                                

<1> 获取选择字体的高度、宽度

<2> 从所传字符串参数中,依次读出一行的字符数,最终得到一行显示所对应的矩形区域

<3> 将一行所得到的详细信息传给行显示函数,行显示函数会从字库中找到匹配的字依次将一行的字符进行显示,完成一行的显示

<4> 如果不是只有一行,重新计算下一行显示的坐标,重复<1、2、3>的工作,直到将字符串显示完毕。

============================================================================================

                   重要细节分析

============================================================================================

 1、GUI运行的全局变量                                             

  GUI_Context是GUI保存运行环境的全局变量,它的类型GUI_CONTEXT在GUI.h中被定义。

struct GUI_CONTEXT {
/* Variables in LCD module */
LCD_COLORINDEX_UNION LCD;
LCD_RECT ClipRect;
U8 DrawMode;
U8 SelLayer;
U8 TextStyle;
/* Variables in GL module */
GUI_RECT* pClipRect_HL; /* High level clip rectangle ... Speed optimization so drawing routines can optimize */
U8 PenSize;
U8 PenShape;
U8 LineStyle;
U8 FillStyle;
/* Variables in GUICHAR module */
const GUI_FONT GUI_UNI_PTR * pAFont; //指向当前选择的字体
#if GUI_SUPPORT_UNICODE
const GUI_UC_ENC_APILIST * pUC_API; /* Unicode encoding API */
#endif
I16P LBorder;
I16P DispPosX, DispPosY;
I16P DrawPosX, DrawPosY;
I16P TextMode, TextAlign; //对齐方式
GUI_COLOR Color, BkColor; /* Required only when changing devices and for speed opt (caching) */
/* Variables in WM module */
#if GUI_WINSUPPORT
const GUI_RECT* WM__pUserClipRect;
GUI_HWIN hAWin;  //当前激活的窗口 
int xOff, yOff;
#endif
/* Variables in MEMDEV module (with memory devices only) */
#if GUI_SUPPORT_DEVICES
const tLCDDEV_APIList* pDeviceAPI; /* function pointers only */
GUI_HMEM hDevData;
GUI_RECT ClipRectPrev;
#endif
/* Variables in Anitaliasing module */
#if GUI_SUPPORT_AA
const tLCD_HL_APIList* pLCD_HL; /* Required to reroute drawing (HLine & Pixel) to the AA module */
U8 AA_Factor;
U8 AA_HiResEnable;
#endif
};

2、GUI_FONT的定义                                                 

一种字库想要被uCGUI所调用,需要将其定义成GUI_GONT类型的一个常量,当我们需要自己制作字库的时候,就需要这样做。GUI_FONT类型的定义在GUIType.h文件中。

struct GUI_FONT {
GUI_DISPCHAR* pfDispChar; //显示一个属于当前字库字符的函数
GUI_GETCHARDISTX* pfGetCharDistX; //获取字库中某字符的宽度
GUI_GETFONTINFO* pfGetFontInfo; //获取字库信息
GUI_ISINFONT* pfIsInFont; //查询字库中是否存在此字符
const tGUI_ENC_APIList* pafEncode; //
U8 YSize; //高度
U8 YDist; //对应的像素点
U8 XMag; //X方向上的放大系数
U8 YMag; //Y方向上的放大系数
union { //此共用体主要是提供字库数据的访问地址,
//对于不同的字库,其从字库中查找字模的方法,是不一样的
//这里主要分成三种情况,对应三种类型的指针
const void GUI_UNI_PTR * pFontData;
const GUI_FONT_MONO GUI_UNI_PTR * pMono;
const GUI_FONT_PROP GUI_UNI_PTR *
pProp;
} p;
U8 Baseline; //
U8 LHeight; /* height of a small lower case character (a,x) */ //小写高度
U8 CHeight; /* height of a small upper case character (A,X) */ //大写高度
};

(1)单一分区的字库,例如GUI_Font6x8,使用的共用体指针是第二个

/* MONO字库代表的是单一分区 */
typedef struct {
const unsigned char GUI_UNI_PTR * pData; //字库的起始地址
const U8 GUI_UNI_PTR * pTransData; //
const GUI_FONT_TRANSINFO GUI_UNI_PTR * pTrans; //
U16P FirstChar; //第一个字符的索引
U16P LastChar; //最后一个字符的索引
U8 XSize; //宽度
U8 XDist; //宽度对应的像素点
U8 BytesPerLine; //每行需要几个字节
} GUI_FONT_MONO;

这类字库的特点:

① 字符不多,对应的字模也很少,不会很浪费Flash

② 之所以是一个分区,那是因为所有的字符的索引码都是连续的,所以根据字符索引寻找字符的字模时,从字库的基地址开始找起就可以了。

(2)多个分区的字库,比如汉字的字库,使用的共用体指针是第三个

typedef struct GUI_FONT_PROP {
U16P First; /* first character */
U16P Last; /* last character */
const GUI_CHARINFO GUI_UNI_PTR * paCharInfo; /* address of first character */
const struct GUI_FONT_PROP GUI_UNI_PTR * pNext; /* pointer to next */
} GUI_FONT_PROP;

相较(1)结构体中的内容,GUI_FONT_PROP显得少了几个。由于字库是分区管理,每一个分区有自己特有的参数,通过paCharInfo指针来指向这个分区的特点,也会给出这个分区的第一个字符字模的地址。

typedef struct {
U8 XSize; //字符的宽度
U8 XDist; //显示所对应的像素点
U8 BytesPerLine; //每行占据的字节数
const unsigned char GUI_UNI_PTR * pData; //指向字模的数据区
} GUI_CHARINFO;

  可能有人会说:不使用GUI,将汉字字库下载到SD卡中,然后使用下边的函数GetGBKCode_from_sd()也可以获得字模,而且这种字库所有的分区都是连续排放的。为什么要与上一种情况单独分开呢?

/*******************************************************************************
* Function Name : GetGBKCode_from_sd
* Description : 从SD卡字库中读取自摸数据到指定的缓冲区
* Input : pBuffer---数据保存地址
* c--汉字字符低字节码
* Output : None
* Return : 0(success) -1(fail)
* Attention : None
*******************************************************************************/
int GetGBKCode_from_sd(unsigned char* pBuffer,const unsigned char * c)
{
unsigned char High8bit,Low8bit;
unsigned int pos;
High8bit=*c; /* 取高8位数据 */
Low8bit=*(c+); /* 取低8位数据 */ pos = ((High8bit-0xa0-)*+Low8bit-0xa0-)**; f_mount(, &myfs[]);
myres = f_open(&myfsrc , "0:/HZLIB.bin", FA_OPEN_EXISTING | FA_READ); if ( myres == FR_OK )
{
f_lseek (&myfsrc, pos);      //指针偏移
myres = f_read( &myfsrc, pBuffer, , &mybr ); //16*16大小的汉字 其字模 占用16*2个字节
f_close(&myfsrc); return ;
}
else
return -;
}

虽然字库是连续存放的,但是根据汉字机内码从这样的字库中找到对应的字模存放的位置,这样的算法是不通用的。不通用的意思是,uCGUI支持多种外语,可它不可能为每一种外语都写这样的算法,那样的代码不具包容性。
   正确的做法是考虑到多种外语的共同点,字符索引都可以看做是分区排列的,而且在每个分区中都是连续的。每个分区中查找字模的算法肯定是一样的,而在创建字库的时候,将字模分区管理。

这种做法最为重要的意义在于:可以裁剪字库,自然节省了Flash的消耗。16*16点阵的汉字字库,它的大小是200多kB,对于小容量Flash的单片机来说难以承受。有人说可以外置SD卡,可是实际的项目中怎么可能将字库放在SD卡中,怎么可能就为了字库而添加SD卡这样的硬件资源,而且SD卡的读速度跟内部Flash是不可比的。

为了减少ROM的消耗,裁剪字库是最为有效的方法。裁剪字库的基础就是将字库进行分区管理,一个分区甚至可以只有一个汉字。将字库进行了分割,但是所有的分区有都属于同一个字库,这就需要链表将它们连接起来。于是乎,就产生了第三种共用体指针。

3、属于字库的特有的函数                                           

struct GUI_FONT {
GUI_DISPCHAR* pfDispChar; //显示一个属于当前字库字符的函数
GUI_GETCHARDISTX* pfGetCharDistX; //获取字库中某字符的宽度
GUI_GETFONTINFO* pfGetFontInfo; //获取字库信息
GUI_ISINFONT* pfIsInFont; //查询字库中是否存在此字符
......
};

不同的字库不仅有属于自己的数据库,而且还有自己特有的函数,这也是由于其特别的因素所决定的。

4、构建自己需要的字库时需要的工作                                  

英文ASCII的字库,uCGUI提供的已经非常完备了,通常无需多虑。而uCGUI所欠缺的是属于我们中国的汉字字库。

庞大的字库使我们只有选择“const GUI_FONT_PROP GUI_UNI_PTR * pProp;”这种办法才是光明之道。

我们需要将自己需要显示的汉字字模分区建立GUI_FONT_PROP类型的结构体,然后使用指针pProp将它们连接成单项链表构成一个完整的字库。这样字库的数据区就做好了。

还需要为字库创建诸多查询功能的管理函数,例如16*16点阵的汉字字库可以选用

#define GUI_FONTTYPE_PROP_SJIS  \
GUIPROP_DispChar, \
GUIPROP_GetCharDistX, \
GUIPROP_GetFontInfo, \
GUIPROP_IsInFont, \
&GUI_ENC_APIList_SJIS

显然上边的这些函数都是基于链表的,有什么样的数据结构就有什么样的算法的一种体现。

对于只有一个分区的MONO英文字库,是不需要用链表的,只需要在数组中根据偏移量查找就行了。

uCGUI字符串显示过程分析和uCGUI字库的组建的更多相关文章

  1. 四轴飞行器1.6 emwin与ucgui的移植,汉字外挂字库移植和DEMO效果对比

    飞控的遥控器打算自己做,这样全局都能掌握,可以通过遥控器对飞控的参数和飞行模式进行修改,而买遥控器是做不到这样的哈..以后做图传的时候,屏幕还可以实时现实摄像头拍回来的画面,挺好的哈.. 做遥控我们选 ...

  2. C# 在网页中将Base64编码的字符串显示成图片

    在写一个接口,返回的json里面有图片,是Base64编码的字符串. 测试接口的时候,发现原来在html显示,是直接可以将Base64编码的字符串显示成图片的. 格式如下: <img src=d ...

  3. 解决CsvWriter:中文乱码、末尾行多一行空格(/r)、非第一列空字符串""显示null问题

    一:主要内容 解决CsvWriter存csv,csv文件打开后中文乱码问题 解决CsvWriter存csv,csv文件最后一行总是多一行空行的问题 解决CsvWriter存csv,csv文件不是第一列 ...

  4. C# asp.net XML格式的字符串显示不全

    前台显示XML字符串显示不全 后台XML字符串使用<xmp></xmp>将XML格式字符串括起来

  5. 改变对象的字符串显示__str__repr

    改变对象的字符串显示 # l=list('hello') # # print(l) # file=open('test.txt','w') # print(file) class Foo: def _ ...

  6. MSSQL sql numeric转字符串显示不补0

    由于工作中需要把numeric转字符串显示,但是有一个问题会自动补0. DECLARE @f NUMERIC(18,4)=1.1200, @str VARCHAR(50) SELECT CAST(@f ...

  7. emWin(ucGui)数值显示例程 -【worldsing笔记】

    本例程下载:2.emWin5.26(ucGui)VS2008数字显示.zip   在emWin显示文本字符还是容易,我们也可以使用字符串和标准 C 库的函数来显示数值.然而,有时候这会是件困难的事.通 ...

  8. 关于C#调用C++ 的DLL传送字符串显示乱码的解决

    最近在做一个程序,想把某些功能用C++写成DLL供C#调用,但是在写好DLL用C#传递字符串参数时,在DLL中显示传送过来的字符串是乱码,DLL里的代码根本无法用这些字符串进行其它的处理.为此,花了一 ...

  9. js截取字符串显示引号两种方法

    //截取字符串多余显示引号 var cutStrForNum = function (str, num) { var len = 0; for (var i = 0; i < str.lengt ...

随机推荐

  1. C# 之 OpenFileDialog的使用

    一.打开文件对话框(OpenFileDialog) 1. OpenFileDialog控件有以下基本属性 [1]InitialDirectory:对话框的初始目录 [2]Filter:要在对话框中显示 ...

  2. Linux学习之路:shell变量(一)

    一.变量的显示与设置 1.变量的显示运用echo命令 +$符号: 上图例子显示的是系统变量,咱们可以自己设置变量 2.设置变量运用“=”符号 设置了变量NIU 值为“niunai” 变量设置规则: ( ...

  3. 深入理解计算机系统第二版习题解答CSAPP 2.17

    假设w=4,我们能给每个可能的十六进制数字赋予一个数值,假设用一个无符号或者补码表示.完成下表: x 无符号(B2U(x)) 补码(B2T(x)) 十六进制 二进制 0xE 1110 14 -2 0x ...

  4. [转载]GDB十分钟教程

    转自:http://blog.csdn.net/liigo/article/details/582231/ GDB十分钟教程 作者: liigo原文链接: http://blog.csdn.net/l ...

  5. RGBa颜色 css3的Alpha通道支持

    CSS3中,RGBa 为颜色声明添加Alpha通道. RGB值被指定使用3个8位无符号整数(0 – 255)并分别代表红色.蓝色.和绿色.增加的一个alpha通道并不是一个颜色通道——它只是用来指定除 ...

  6. 注册Model类

    根据username查找是否存在相同的用户名的方法 自动填充功能填充注册时间字段 如果两次输入的密码一直则写入数据库的方法 userModel.class.php <?php /**** 燕十八 ...

  7. 2015年校园招聘12家IT公司面试体验

    背景 2015年注定是一个不平凡的年头,作为一个应届毕业生,我也算是经历了工作上的大起大落.下面我先简单讲述一下自己的遭遇,然后根据自己亲身的面试经历,从一个学生的角度去谈谈自己对面试过的公司的一些看 ...

  8. Html5时钟的实现

    最近准备把自己的博客装修一下,首先,先为自己设计一个时钟吧,希望博客园能够尽快发放给我使用js的权限! 自从看见了苹果设计的那款因为侵权而赔钱了时钟,我就决定我的时钟一定是要参考这个来设计了! 不得不 ...

  9. CefSharp开源库的使用(一)

    关于CEF: 嵌入式Chromium框架(简称CEF) 是一个由Marshall Greenblatt在2008建立的开源项目,它主要目的是开发一个基于Google Chromium的Webbrows ...

  10. 第27条:使用“class-continuation分类”隐藏实现细节

    Objective-C动态消息系统(参见第11条)的工作方式决定了其不可能实现真正的私有方法或私有实例变量. 匿名分类的特点: 与普通的分类不同,它必须定义在其所接续的那个类的实现文件里. 唯一能声明 ...