/* * Create By : 李绍良[lsl](zyzx)
* Create Time : 2015-03-16
* 转载请注明来源:http://www.cppblog.com/zyzx/category/21065.html
*/
为什么我们可以在屏幕上看到文字呢?
我们知道屏幕显示的是RGB三原色,软件就是将一幅位图(RGB8888等格式)交给显卡,再呈现到用户面前。也就是说UI里面划分的控件也好、文字也罢,任何可显化部分都使用BMP位图来表达。所谓的控件也不过是工程师们赋予某个含有特定变化逻辑的位图区域。
也就是说文字的表现形式是一幅位图,那么计算机如何辨识哪个是"A",哪个是"B"呢?伟大的计算机说了:一切都是数,也就是0或1。相应的各种编码标准应运而生,比如ascii、GB2312、GBK等等。也就是说一个数值,表示一个文字,显示的时候却是使用这个数值对应的图片。
那么软件所使用文字就有二个部分了:1.所有的字符串;2.字符串中每个字符对应的图片。字符串资源的做法就各领风骚了,有人硬编码;MFC使用exe的资源段;打包做到二进制文件等等。不管如何处理,最终都需要把一个字符串比如"Hello World!"交给绘图模块。绘图模块则需要把这个字符串拆分成单个字符,按照各个语言规则选取对应的图片,一个一个的贴到屏幕上(其实是后台缓存)。后面这个步骤一般由操作系统完成,那么是如何做到的呢?答案就是字库,以及字库的显示规则。
字库一般分为栅格字库和矢量字库,它们的目标都是根据一个编码值取到对应图片。栅格字库相对来说简单、高效,网络上有很多关于这个的文章,可以找找相关的资料。
这里为方便,就直接取出"linux-3.18.3/lib/fonts/font_10x18.c"这个文件作为我们字库数据。其他类型的字库和这个类似,不过是将字模和规则存放于二进制文件中而已。
如上图,右边的注释框中,有个明显的由"1"组成的图案。其实也就是由一个个比特位组成的二值图片。
fontdata_10x18[FONTDATAMAX]这些数据组成的规则也很简单:
1. 二值图片高为18,宽为16(有效宽度为10),每一行有2个字节,一个字模共36字节
2. 某ascii值的字模首地址为:fontdata_10x18[id * 36]
3. 这个是等宽字体
那么要显示"1"其实就是就是需要将比特位1的地方着色绘制屏幕。
如下函数即是将此二值数据,绘制到缓存中。
int_t CWinGraph::DrawFont(LUI_CANVAS *ptr, int_t zoom, char id, int_t x, int_t y, int_t &cx, int_t &cy,uint32 color, bool border, uint32 color_border)
{
//下段代码并不严谨
//实际的情况则需要看根据字库的设计情况,按如下的思路即可
int_t w = 10, h = 18;
unsigned char *pFont = &fontdata_10x18[id * 36];
unsigned char *pData = ptr->pData + y * ptr->pitch + x * ptr->bpp;
for(int j = 0; j < h; j++)
{
unsigned char *pd = pData;
for(int i = 0; i < w; i++)
{
unsigned char *pf = pFont + j * 2 + (i >> 3);
if(*pf & (0x1 << (7 - i & 0x7)))
{
memcpy(pd, &color, ptr->bpp);
}
else if(border)
{
//简单描边算法
int xi = 0, yj = 0;
if(0 < j)
{
xi = i; yj = j - 1;
}
else if(j < (h - 1))
{
xi = i; yj = j + 1;
}
else if(0 < i)
{
xi = i - 1; yj = j;
}
else if (i < (cx - 1))
{
xi = i + 1; yj = j;
}
pf = pFont + yj * 2 + (xi >> 3);
if(*pf & (0x1 << (7 - xi & 0x7)))
{
memcpy(pd, &color_border, ptr->bpp);
}
}
pd += ptr->bpp;
}
pData += ptr->pitch;
}
cx = w;
cy = h;
return 0;
}
好了至于字符串,那就很简单了,一个一个调用这个函数就可以了。
如下图即是测试绘制的字符串: