本帖最后由 allyzc 于 2015-9-30 14:36 编辑
本方法从 “基于汉字字模的Stellaris+GUI控件驱动设计.pdf“ 修改。
建立hanzi16x16.c文件:
一、在hanzi16x16.c定义汉字字模数据结构:
typedef struct{
const unsigned char data[32];//16*16的汉字占用32个字节
const unsigned char code[3];//汉字内码索引,包含\0,占用3个字节
}HanziTypeDef_16x16;//16*16汉字字模数据结构
二、使用PCtoLCD2002制作汉字字模:
设置软件取模格式:高位在前
汉字取模,如果汉字多可以使用如下文本的方式。
三、在hanzi16x16.c文件定义保存字模的数组,直接把数据复制过来就好,不用做任何修改,非常方便。
四、制作所有英文ansi字模数组,注意格式如下
使用上面所说的文本方式,点击生成英文点阵字库
在hanzi16x16.c文件定义保存所有英文ansi字模的数组,把点阵数据制过来就好
如图中红框注释部分编译不通过,要把“ ”里面编译器不认识的删除掉
五、在grlib.h中的tFont结构体添加一个指向汉字的指针
typedef struct
{
//
//! The format of the font. Can be one of FONT_FMT_UNCOMPRESSED or
//! FONT_FMT_PIXEL_RLE.
//
uint8_t ui8Format;
//
//! The maximum width of a character; this is the width of the widest
//! character in the font, though any individual character may be narrower
//! than this width.
//
uint8_t ui8MaxWidth;//这里我们用来保存汉字的宽
//
//! The height of the character cell; this may be taller than the font data
//! for the characters (to provide inter-line spacing).
//
uint8_t ui8Height;//这里我们用来保存汉字的高
//
//! The offset between the top of the character cell and the baseline of
//! the glyph. The baseline is the bottom row of a capital letter, below
//! which only the descenders of the lower case letters occur.
//
uint8_t ui8Baseline;
//
//! The offset within pui8Data to the data for each character in the font.
//
uint16_t pui16Offset[96];//这个我们用第一个元素保存有多少个汉字
//
//! A pointer to the data for the font.
//
const uint8_t *pui8Data;//这里我们可以保存英文点阵的指针
//A pointer to the data for the Chinese font.
const uint8_t *CHFontdata;//这是我们添加的,添加在这里,添加一个指向汉字的指针
}
tFont;
六、设置一个判断标识符来区分当前字符是字母还是汉字, 因此在 grlib.h 中添加汉
字标识符:
// Indicates that the font data is stored in an uncompressed format.
#define FONT_FMT_UNCOMPRESSED 0x00 //
//!Indicates that the font data is stored using a pixel-based RLE format.
#define FONT_FMT_PIXEL_RLE 0x01
//Indicates that the font data is stored in Chinese format.
#define FONT_CH_STYLE 0x02//添加在这里
七、在grlib.h 中字体声明
extern const tFont g_sFontCH16;
#define g_psFontCH16 (const tFont *)&g_sFontCH16
八、定义16*16的字体
//***********16x16 的汉字字体结构体***********//
const tFont g_sFontCH16=
{
FONT_CH_STYLE,//这里使用我们上面添加的汉字标识符
16,//宽16
16,//高16
16,//这个不知道用来干嘛,我们用不到,随便填
{(sizeof(Hanzi_16x16)/sizeof(HanziTypeDef_16x16))},//记录有多少个汉字,保存在这个数组的第一个元素
Ansi_8x16,//英文点阵指针
Hanzi_16x16[0].data,//汉字点阵指针,用Hanzi_16x16[0]表示指向第一个汉字
};
九、在 Stellaris GUI 中控件的字符显示最终是调用函数 GrStringDraw 完成的,
其在 StellarisWare\grlib\String.c 中进行了具体的实现。 因此, 在 GrStringDraw
中添加实现汉字显示的具体代码:if(pContext->psFont->ui8Format == FONT_CH_STYLE )
{
char *pString = (char *)pcString;//取得要显示字符串的指针
while ( *pString )//判断字符串是否结束
{
if ( *pString < 128 )//如果小128,则为字母
{
LCD_Char_En(pContext, pString, i32X, i32Y);//显示一个字母
pString += 1;//指针指向下一个
i32X += (pContext->psFont->ui8MaxWidth/2);//这里显示的位置向右移动一个字母的宽度
}
else
{
LCD_Char_Cn(pContext, pString, i32X, i32Y);//大于128则表示是汉字
pString += 2;//为什么加2呢,因为汉字的保存就是保存汉字的内码,占用2个字节
i32X += pContext->psFont->ui8MaxWidth;//这里显示的位置向右移动一个汉字的宽度
}
}
return;
}
十 、因为上面调用了函数LCD_Char_En(pContext, pString, i32X, i32Y);//在LCD上显示一个字母
所以在string.c中添加它的实现代码
void LCD_Char_En(const tContext *pContext, char *acsii, uint16_t x, uint16_t y)
{
uint8_t temp, t;
uint8_t xpos;//记录横向的字节标识
uint8_t ypos;//记录纵向的字节标识
//一行中有多少个字节,8*16的英文,就是宽度为8,一个字节8位,刚好一个字节
//更直观应该是(pContext->psFont->ui8MaxWidth)/2/8,
uint8_t hByteSize = (pContext->psFont->ui8MaxWidth/16);
//获取一个英文字模纵向多少有行,8*16的英文,就是16行
uint8_t vSize = pContext->psFont->ui8Height;
//这里获取这次字母点阵第一个数据的地址,pui8Data就是上面保存英文点阵的指针
//pui8Data[(*acsii)*hByteSize*vSize]就是我们所需,为什么是(*acsii)*hByteSize*vSize
//在做英文字模时就是按acsii顺序排列的,如A即在65位置,
//如8*16英文,即65*横向所需字节1*纵向的行数16,之间的8*16个字节都是这个字母的数据
acsii = (char *)&pContext->psFont->pui8Data[(*acsii)*hByteSize*vSize];
for ( ypos = 0 ; ypos < vSize; ypos++ )
{
for (xpos=0; xpos<hByteSize; xpos++) {
temp = *acsii;//取得具体字节数据
for(t=0; t<8; t++)
{
if(temp & 0x80)
{
GrPixelDraw(pContext, x+(xpos*8)+t, y+ypos);//这个就是画点了
}
temp <<= 1;
}
acsii++;//指向下一个字节
}
} }
11、因为上面调用了函数LCD_Char_Cn(pContext, pString, i32X, i32Y);//在LCD上显示一个汉字
所以在string.c中添加它的实现代码,和上面的差不多
void LCD_Char_Cn(const tContext *pContext, char *hanzi, uint16_t x, uint16_t y)
{
uint8_t temp, t;
uint8_t xpos;
uint8_t ypos;
//下面的表示一个汉字字模所占的字节数,16*16的点阵
//就是纵向行数16*横向字节数2+汉字内码和\0即3=35个字节
//也就是上面定义的HanziTypeDef_16x16结构体的大小35
uint8_t hzASize = pContext->psFont->ui8Height*pContext->psFont->ui8MaxWidth/8+3;
//横向所需字节,如16*16的汉字,就是横向占用2个字节
uint8_t hByteSize = (pContext->psFont->ui8MaxWidth/8);
uint16_t hanziId = getHanziId(pContext, hanzi);//这里多了一个获取汉字的位置
if ( hanziId == 0xffff )
{
return;//如果是0xffff,表示取不到汉字的位置,即没有这个汉字的字模,直接反回
}
//这里获取这次汉字字模第一个数据的地址,如16*16的汉字
// 第5个汉字的字模第一个数据的地址 就是5*35的位置
hanzi = (char *)&pContext->psFont->CHFontdata[hanziId*hzASize];
//上面的上英文的一样
for ( ypos = 0 ; ypos < (pContext->psFont->ui8Height); ypos++ )
{
for (xpos=0; xpos<hByteSize; xpos++)
{
temp = *hanzi;
for(t=0; t<8; t++)
{
if(temp & 0x80)
{
GrPixelDraw(pContext, x+(xpos*8)+t, y+ypos);
}
temp <<= 1;
}
hanzi++;
}
}
}
12、获取汉字位置uint16_t getHanziId(const tContext *pContext, char *hanzi)
所以在string.c中添加它的实现代码
uint16_t getHanziId(const tContext *pContext, char *hanzi)
{
uint16_t i = 0;
//表示一个汉字字模所占的字节数如16*16汉字即16*2+3=35了
uint8_t hzASize = pContext->psFont->ui8Height*pContext->psFont->ui8MaxWidth/8+3;
//这里i从1开始,如果汉字内码2个都相同,即找到了汉字所在的位置
//为什么是-3,-2呢,比如第一个汉字35-3,35-2就是汉字内码的第一和第二个字节
for (i = 1; i <= pContext->psFont->pui16Offset[0]; ++i)
{
if ((hanzi[0] == pContext->psFont->CHFontdata[hzASize*i-3]) &&
(hanzi[1] == pContext->psFont->CHFontdata[hzASize*i-2]))
{
return i-1;//因为上面i从1开始,返回i-1,即数组下标是从0开始的
}
}
return 0xffff;//找不到所需的汉字
}
附上效果图
GrContextFontSet(&g_sContext, g_psFontCH16);
GrStringDraw(&g_sContext, "检测到SD卡,正在读取", -1, 132, 89, 0);
附上PDF版:
grlib中英文混合显示方法,汉字取模非常方便.pdf
(735.28 KB)
源代码:pan.baidu.com/s/1c0whlOc
|