按照网上找到的资料,在ucgui中添加自定义字体文件的方法如下:
1.打开字体生成器,添加需要用到的汉字,选择自动添加ASCII码表,并生成对应的代码,将内容复制到工程中自定义新建的C文件中(eg:myfont.c)
2.该字体的结构体在文件末尾处:
GUI_FONT GUI_FontHZ16x16 = {
GUI_FONTTYPE_PROP_SJIS,
16,
16,
1,
1,
&GUI_FontHZ_16x16_Prop1
};
在GUI.h中添加extern GUI_FONT GUI_FontHZ16x16,GUI_FontHZ16x16x2;即可使用。
现在要显示一个字符串,如果全为汉字字符,显示是没有问题的。
如果全为英文字符,eg:“abc”,实际的显示效果为a b c 。注意:字符后面有空格,这并不是我想要的。
分析:生成的点阵信息中,英文字符的宽度为一个字节,中文为两个字节,这是符合常理的。但为什么显示的时候,英文后面会多一个空格呢?ucGUI底层是怎么获取字符信息以及显示的?底层处理函数中有没有区分中英字符?
分析底层可知,ucGUI字体类型分为等宽字体(Mono)、均衡字体(PROP)、漫画字体(Comic fonts)和数字字体(Digits)。其中,等宽字体就是所有字符高度和宽度相同。均衡字体是指字体中每个字符都可以有自己的宽度和高度。
追踪字体信息中GUI_FONTTYPE_PROP_SJIS得到:
/* PROP: Proportional fonts SJIS */
DECLARE_FONT(PROP);
#define GUI_FONTTYPE_PROP_SJIS \
GUIPROP_DispChar, \
GUIPROP_GetCharDistX, \
GUIPROP_GetFontInfo, \
GUIPROP_IsInFont, \
&GUI_ENC_APIList_SJIS
上面就是几个函数指针,具体函数实现是在GUICharP.c中,其中GUIPROP_DispChar就是分析的关键,它就是均衡字体字符处理函数。
void GUIPROP_DispChar(U16P c) {
int BytesPerLine;
GUI_DRAWMODE DrawMode = GUI_Context.TextMode;
const GUI_FONT_PROP GUI_UNI_PTR * pProp = GUIPROP_FindChar(GUI_Context.pAFont->p.pProp, c);
if (pProp) {
GUI_DRAWMODE OldDrawMode;
const GUI_CHARINFO GUI_UNI_PTR * pCharInfo = pProp->paCharInfo+(c-pProp->First);
BytesPerLine = pCharInfo->BytesPerLine;
OldDrawMode = LCD_SetDrawMode(DrawMode);
LCD_DrawBitmap( GUI_Context.DispPosX, GUI_Context.DispPosY,
pCharInfo->XSize,
GUI_Context.pAFont->YSize,
GUI_Context.pAFont->XMag,
GUI_Context.pAFont->YMag,
1, /* Bits per Pixel */
BytesPerLine,
pCharInfo->pData,
&LCD_BKCOLORINDEX
);
/* Fill empty pixel lines */
if (GUI_Context.pAFont->YDist > GUI_Context.pAFont->YSize) {
int YMag = GUI_Context.pAFont->YMag;
int YDist = GUI_Context.pAFont->YDist * YMag;
int YSize = GUI_Context.pAFont->YSize * YMag;
if (DrawMode != LCD_DRAWMODE_TRANS) {
LCD_COLOR OldColor = GUI_GetColor();
GUI_SetColor(GUI_GetBkColor());
LCD_FillRect(GUI_Context.DispPosX,
GUI_Context.DispPosY + YSize,
GUI_Context.DispPosX + pCharInfo->XSize,
GUI_Context.DispPosY + YDist);
GUI_SetColor(OldColor);
}
}
LCD_SetDrawMode(OldDrawMode); /* Restore draw mode */
GUI_Context.DispPosX += pCharInfo->XDist * GUI_Context.pAFont->XMag;
}
}
函数中主要做的事:
1.通过GUIPROP_FindChar(GUI_Context.pAFont->p.pProp, c); 得到字符信息。
2.通过LCD_DrawBitmap(...)绘制背景色和前景色。
3.最后一句就是计算下一个字符显示的X坐标。
回到之前的问题:为什么英文字符后面会有一个空格?
分析:是不是跟显示一个字符后推进的X轴距离有关?
要了解这个就得分析函数中用到的几个结构体中包含的信息及其代表的具体含义。
typedef struct {
/* 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;
const GUI_UC_ENC_APILIST * pUC_API; /* Unicode encoding API */
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
} GUI_CONTEXT; //GUI文本结构体
typedef struct {
U8 XSize; //X轴大小
U8 XDist; //X轴距离
U8 BytesPerLine; //一个字符每行所占字节数
const unsigned char GUI_UNI_PTR * pData; //字符点阵数据
} GUI_CHARINFO; //字符信息结构体
typedef struct GUI_FONT_PROP {
U16P First; /* 第一个字符 */
U16P Last; /* 第二个字符 */
const GUI_CHARINFO GUI_UNI_PTR * paCharInfo; /* 第一个字符的字符信息地址*/
const struct GUI_FONT_PROP GUI_UNI_PTR * pNext; /* 下一个字符的自付信息地址*/
} GUI_FONT_PROP; //均衡字体结构体
struct GUI_FONT { //字体结构体
GUI_DISPCHAR* pfDispChar;
GUI_GETCHARDISTX* pfGetCharDistX;
GUI_GETFONTINFO* pfGetFontInfo;
GUI_ISINFONT* pfIsInFont;
const tGUI_ENC_APIList* pafEncode;
U8 YSize; //Y轴大小
U8 YDist; //Y轴距离
char XMag; //X轴放大倍数
char 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.英文字符本身X轴占有两个字节的距离造成错误
2.下一个字符显示的X坐标计算有错
分析完结构体后,上面两个原因说白了就跟字符信息结构体中XSize、XDist有关。
那么再回头看软件生成的字符信息:
/* char: 1 code:0x0031 */
unsigned char acFontHZ_0031[16] = {
________,
________,
________,
___X____,
_XXX____,
___X____,
___X____,
___X____,
___X____,
___X____,
___X____,
___X____,
___X____,
_XXXXX__,
________,
________,
};
/* char: 你 code:0xC4E3 */
unsigned char acFontHZ_C4E3[32] = {
____X___,X_______,
____X___,X_______,
____X___,X_______,
___X___X,XXXXXXX_,
___X___X,______X_,
__XX__X_,_____X__,
__XX_X__,__X_____,
_X_X____,__X_____,
X__X___X,__X_X___,
___X___X,__X__X__,
___X__X_,__X__X__,
___X__X_,__X___X_,
___X_X__,__X___X_,
___X____,__X_____,
___X____,X_X_____,
___X____,_X______,
};
GUI_CHARINFO GUI_FontHZ_16x16_CharInfo[2] = {
//前面三个数字分别为XSize、XDist、BytesPerLine
{ 8, 16, 1, (unsigned char *)&acFontHZ_0031 }, /*0: 1*/
{ 16, 16, 2, (unsigned char *)&acFontHZ_C4E3 }, /*1: 你*/
};
GUI_FONT_PROP GUI_FontHZ_16x16_Prop2 = {
0xC4E3, /*start :你*/
0xC4E3, /*end :你, len=1*/
&GUI_FontHZ_16x16_CharInfo[ 1 ],
(void*)0
};
GUI_FONT_PROP GUI_FontHZ_16x16_Prop1 = {
0x0031, /*start :1*/
0x0031, /*end :1, len=1*/
&GUI_FontHZ_16x16_CharInfo[ 0 ],
&GUI_FontHZ_16x16_Prop2
};
GUI_FONT GUI_FontHZ16x16 = {
GUI_FONTTYPE_PROP_SJIS,
16, //YSize
16, //YDist
1, //XMag
1, //YMag
&GUI_FontHZ_16x16_Prop1
};
从上面蓝色高亮的内容看,很显然英文字符信息中XDist=16是错误的,这个就导致计算一个字符显示的初始坐标向后多推进了一个字节。将其改为8,问题就解决了。
总结:说白了,Bug出在字体生成器软件。至于为什么花了一个星期才解决这问题,有两点原因导致分析问题的思路不清晰:
1.上面分析问题的流程是基于16x16大小的字体。这种情况下,软件得到YSize=YDist,相当于少了一个Bug。最开始实验时,设置的字体大小是24x20,这种情况下,YSize>YDist。现象是字符只有上半部分或下半部分显示。
2.一开始出现问题时,并没有考虑到是字体生成器软件的问题,而是一直纠结与ucGUI底层如何区分汉字字符和英文字符。事实上,在自制小字体文件中,每一个字符所包含的信息都是独立的,然后用链表的形式将所有字符信息链接起来,ucGUI底层只要通过字符内码索引到相应的字符信息就可以正常显示内容,不用特意去区分中英字符。