6.1 显示资料概述
点阵及灰度显示相关资料
6.1 概述
显示资料包含点阵、灰度、二维码显示参考代码。另外高通为了方便开发人员更加便捷的开发 GUI 文字图像界面,特此开发出划时代的 GUI-LCD 产品,有更加详细的开发资料,高通免费技术支持,可配合 GT-HMI 及高通灰度字库产品打造炫丽、复杂的文字图形交互界面。
GT-HMI 下载:高通字库-开发工具
点阵字库显示效果
GT-GUI-LCD 相关资料:GT-GUI-LCD 液晶模组
灰度字库显示效果
以下各节提供了从简易显示到完整字符串渲染的示例函数,以及宽度计算、横竖转换和字节逆序等实用工具,帮助开发者快速集成字库芯片的显示功能。
6.2 排置说明
使用字库显示字符前,用户需先确认其排置方式。可通过型号尾缀进行区分: 尾缀为 W 或无尾缀则代表横置横排,尾缀为 Y 代表竖置横排,尾缀为 Z 代表竖置竖排。
横置横排:比如宽度是16bit位,高度是16bit位的字符。那么“横置”指的就是两个字节水平放置,即宽度。“横排”指的是每个字节从做到右横向放两个字节,满两个字节换行继续放,一共有16排,即高度。如下图所示。
描述
竖置横排:比如宽度是16bit位,高度是16bit位的字符。那么“竖置”指的就是每个字节都是竖直放置,放16个字节,即宽度。“横排”指的从左到右放16个字节,满16个字节换行继续放,一共有2排,即高度。如下图所示。
描述
竖置竖排:比如宽度是24bit位,高度是24bit位的字符。那么“竖置”指的就是每个字节都是竖直放置,放24个字节,即宽度。“竖排”指的是从上到下放3个字节,满3个字节换列继续放,一共有24列,即高度。如下图所示。
描述
6.3 WY 简易显示函数
WY_DisplayChar 是一个简化的显示函数,适用于快速原型开发和调试。该函数将字模数据直接写入帧缓冲区,适合分辨率较低或对性能要求不高的场景。
//点阵数据显示 - 数据高位在前 (字库文字的显示格式)
/**
* @brief 字符字体数据排置方式
*/
#define FONT_DATA_ARRAY_TYPE_W (0)
#define FONT_DATA_ARRAY_TYPE_Y (1)
/**
* @brief 字符 点阵数据显示
* @param pBits 字符数据的缓存
* @param x_y 坐标
* @param width 字符宽度
* @param high 字符高度
* @param ft_color 字体颜色
* @param bg_color 背景颜色
* @param _type 排置方式
*/
void fontDisplay_DZ(unsigned char * pBits, unsigned short x, unsigned short y, unsigned short width, unsigned short high,
unsigned short ft_color, unsigned short bg_color, unsigned char _type)
{
unsigned int i, j, k, cnt = 0;
unsigned char temp;
unsigned char multiple = 0;
unsigned short disp_x, disp_y = 0;
//竖置横排显示Y
if (FONT_DATA_ARRAY_TYPE_Y == _type) {
multiple = ((high + 7) >> 3);
for (i = 0; i < multiple; i++) {
for (j = 0; j < width; j++) {
temp = pBits[cnt++];
for (k = 0; k < 8; k++) {
disp_x = (x + j);
disp_y = (y + k + (i * 8));
if (((temp >> k) & 0x01) == 0) //bg
{
/* 显示一个像素点 */
LCD_Fast_DrawPoint(disp_x, disp_y, bg_color); //0xFFFF = WHITE
}
else //font
{
/* 显示一个像素点 */
LCD_Fast_DrawPoint(disp_x, disp_y, ft_color); //0x0000 = BLACK
}
}
}
}
}
//横置横排显示W
else if (FONT_DATA_ARRAY_TYPE_W == _type) {
multiple = ((width + 7) >> 3);
for (i = 0; i < high; i++) {
for (j = 0; j < multiple; j++) {
temp = pBits[cnt++];
for (k = 0; k < 8; k++) {
disp_x = (x + k + (j * 8));
disp_y = (y + i);
if (((temp << k) & 0x80) == 0) //bg
{
/* 显示一个像素点 */
LCD_Fast_DrawPoint(disp_x, disp_y, bg_color);
}
else //font
{
/* 显示一个像素点 */
LCD_Fast_DrawPoint(disp_x, disp_y, ft_color);
}
}
}
}
}
}
/**
* @brief 测试验证 lcd 在任意坐标画点函数
*/
void test_lcd_draw_point(void)
{
for (unsigned char x = 0; x < 12; x++) {
for (unsigned char y = 0; y < 24; y++) {
/* 显示一个像素点 */
LCD_Fast_DrawPoint(x, y, 0xFFFF);
}
}
}
6.4 灰度矢量文字底色混合显示函数
GrayScale_DisplayChar 提供带 Alpha 混合的灰度矢量文字渲染功能。该函数将前景字模数据与背景图像数据按 Alpha 值进行混合,适合需要在 LCD 屏幕上渲染高质量文字的场合。
#define DISMODE 0 // 1 刷RAM显示 0 打点显示
//描点函数
#if DISMODE
void LCD_WriteRAM(unsigned short RGB_Code)
{
LCD->LCD_RAM = RGB_Code;//写十六位GRAM
}
#else //!DISMODE
static void Gui_DrawPoint(unsigned int x, unsigned int y, unsigned int color)
{
//显示一个点
LCD_Fast_DrawPoint(x, y, color);//用户根据自身定义更换
}
#endif //!DISMODE
#define GT_COLOR_GET_R(COLOR) (((COLOR) >> 11) & 0x1f)
#define GT_COLOR_GET_G(COLOR) (((COLOR) >> 5) & 0x3f)
#define GT_COLOR_GET_B(COLOR) (((COLOR)) & 0x1f)
#define GT_UDIV255(x) (((x) * 0x8081U) >> 0x17)
/**
* @brief 16位灰度颜色混合
* @param bg_color 背景色
* @param ft_color 字体颜色
* @param gray_scale 灰度值
* @return unsigned short
*/
static unsigned short gt_color_mix(unsigned short bg_color, unsigned short ft_color, unsigned char gray_scale)
{
unsigned short mix_color = 0, temp = 0;
//R
temp = GT_UDIV255(GT_COLOR_GET_R(ft_color) * gray_scale + GT_COLOR_GET_R(bg_color) * (0xff - gray_scale));
mix_color = (temp & 0x1f) << 11;
//G
temp = GT_UDIV255(GT_COLOR_GET_G(ft_color) * gray_scale + GT_COLOR_GET_G(bg_color) * (0xff - gray_scale));
mix_color = mix_color | ((temp & 0x3f) << 5);
//B
temp = GT_UDIV255(GT_COLOR_GET_B(ft_color) * gray_scale + GT_COLOR_GET_B(bg_color) * (0xff - gray_scale));
mix_color = mix_color | (temp & 0x1f);
return mix_color;
}
/**
* @brief 灰度矢量文字 底色混合 显示函数
* @param pdata 灰度文字数据
* @param x_y_w_h x,y 显示起始坐标; w 宽度, h 高度
* @param grade 灰度阶级[1阶/2阶/3阶/4阶]
* @param bg_color 背景颜色
* @param ft_color 字体颜色
*/
void display_gray_vector_font(unsigned char * pdata, unsigned short x, unsigned short y, unsigned short w, unsigned short h,
unsigned char grade, unsigned short bg_color, unsigned short ft_color)
{
unsigned short w_byte = (w + 7) >> 3;
unsigned short x_s = x, idx = 0, i = 0, j = 0;
unsigned short mix_color = 0;
unsigned char gray_data = 0, gray_scale = 0;
w = w_byte << 3;
#if DISMODE
LCD_Set_Window(x, y, w, h); //设置窗口
LCD_WriteRAM_Prepare(); //开始写入GRAM
#endif //!DISMODE
if (2 == grade || 4 == grade) {
for (i = 0; i < h * w_byte * grade; i++) {
gray_data = pdata[idx++];//取一个字节灰度数据
for (j = 0; j < (8 / grade); j++) {
gray_scale = (gray_data >> (8 - grade)) * (0xFF / ((1 << grade) - 1)); //获取该点灰阶值
gray_data <<= grade;
if (gray_scale == 0) { //获取该点颜色值
mix_color = bg_color;
}
else if (0xff == gray_scale) {
mix_color = ft_color;
}
else {
mix_color = gt_color_mix(bg_color, ft_color, gray_scale);
}
#if DISMODE //显示
//写RAM的方式16位数据
LCD_WriteRAM(mix_color);
#else //!DISMODE
if (x < x_s + w) {
Gui_DrawPoint(x, y, mix_color); //打点方式
x++;
}
else {
x = x_s;
y++;
Gui_DrawPoint(x, y, mix_color); //打点方式
x++;
}
#endif //!DISMODE
}
}
}
else if (3 == grade) {
for (i = 0; i < h * w_byte; i++) {
gray_data = pdata[idx];//取一个字节灰度数据
for (j = 0; j < 8; j++) {
if (2 == j) {//计算灰阶值
gray_data = (pdata[idx] & 0x03) << 1;
idx++;
gray_scale = (gray_data | (pdata[idx] >> 7)) * (0xFF / ((1 << 3) - 1)); //(前字节后两位 | 后字节前1位) * 每份比例
gray_data = pdata[idx] << 1;
}
else if (5 == j) {
gray_data = (pdata[idx] & 0x01) << 2;
idx++;
gray_scale = (gray_data | (pdata[idx] >> 6)) * (0xFF / ((1 << 3) - 1));//(前字节后1位 | 后字节前2位) * 每份比例
gray_data = pdata[idx] << 2;
}
else {
gray_scale = (gray_data >> 5) * (0xFF / ((1 << 3) - 1));
gray_data <<= 3;
}
if (gray_scale == 0) { //根据灰阶值获取该点颜色值
mix_color = bg_color;
}
else if (0xfc == gray_scale) { //0xff/7*7 = 0xfc
mix_color = ft_color;
}
else {
mix_color = gt_color_mix(bg_color, ft_color, gray_scale);
}
#if DISMODE //显示
//写RAM的方式16位数据
LCD_WriteRAM(mix_color);
#else //!DISMODE
if (x < x_s + w) {
Gui_DrawPoint(x, y, mix_color); //打点方式
x++;
}
else {
x = x_s;
y++;
Gui_DrawPoint(x, y, mix_color); //打点方式
x++;
}
#endif //!DISMODE
}
idx++;
}
}
else {
for (i = 0; i < h * w_byte; i++) {
gray_data = pdata[idx++];//取一个字节灰度数据
for (j = 0; j < 8; j++) {
if (gray_data & 0x80) {
mix_color = ft_color;
}
else {
mix_color = bg_color;
}
gray_data <<= 1;
#if DISMODE //显示
//写RAM的方式16位数据
LCD_WriteRAM(mix_color);
#else //!DISMODE
if (x < x_s + w) {
Gui_DrawPoint(x, y, mix_color); //打点方式
x++;
}
else {
x = x_s;
y++;
Gui_DrawPoint(x, y, mix_color); //打点方式
x++;
}
#endif //!DISMODE
}
}
}
}
6.5 字符串显示例子
LCD_ShowString 是一个完整的字符串显示例程,支持换行符处理。该函数遍历字符串中的每个字符,通过 UTF8 解码获取 Unicode 编码,从字库芯片读取字模数据,再调用简易显示函数绘制到屏幕上。
unsigned char jtwb[128] = "收款退款交易查询终端管理"; //每个中文字符实际由两个字节组成, 对应GBK等编码
unsigned char pBits[512];
/*
功能:字符串显示
text:字符串
(x,y):显示的起始坐标
*/
void show_char(unsigned char * text, unsigned int x, unsigned int y)
{
while (*text != '\0')//判断是否是结尾
{
if (*text < 0x80) { //ASCII 码 *text是ASCII码
ASCII_GetData(*text, ASCII_8X16_A, pBits); //示例, 按实际函数名进行修改
DisZK_DZ_W(x, y, 8, 16, BLACK, WHITE, pBits, 1); //显示8X16点ASCII函数
x = x + 8;
}
else { //汉字编码*text是汉字编码的高位,*(text+1)是汉字编码的低位
gt_16_GetData(*text, *(text + 1), pBits); //示例, 按实际函数名进行修改
DisZK_DZ_W(x, y, 16, 16, BLACK, WHITE, pBits, 1); //显示汉字
x = x + 16;
text++;
}
text++;
}
}
void main()
{
unsigned char Data_buf[512];
show_char(jtwb, 0, 0);
#if 0 //汉字字符串
unsigned short arr[3] = { 0xb0a1, 0xb0a2, 0xb0a3 }; //对应中文编码 , "啊": 0xb0a1 ...
for (i = 0; i < sizeof(arr), i++) {
gt_24_data(arr[i] >> 16, arr[i] & 0xff, Data_buf);
DisZK_DZ_W(j, 0, 24, 24, BLACK, WHITE, Data_buf, 1); //显示函数
j += 24;
}
#endif
}
char * itoa(int num, char * str, int radix)
{
char index[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//索引表
unsigned unum;//存放要转换的整数的绝对值,转换的整数可能是负数
int i = 0, j, k;//i用来指示设置字符串相应位,转换之后i其实就是字符串的长度;转换后顺序是逆序的,有正负的情况,k用来指示调整顺序的开始位置;j用来指示调整顺序时的交换。
//获取要转换的整数的绝对值
if (radix == 10 && num < 0)//要转换成十进制数并且是负数
{
unum = (unsigned)-num;//将num的绝对值赋给unum
str[i++] = '-';//在字符串最前面设置为'-'号,并且索引加1
}
else unum = (unsigned)num;//若是num为正,直接赋值给unum
//转换部分,注意转换后是逆序的
do {
str[i++] = index[unum % (unsigned)radix];//取unum的最后一位,并设置为str对应位,指示索引加1
unum /= radix;//unum去掉最后一位
} while (unum);//直至unum为0退出循环
str[i] = '\0';//在字符串最后添加'\0'字符,c语言字符串以'\0'结束。
//将顺序调整过来
if (str[0] == '-') k = 1;//如果是负数,符号不用调整,从符号后面开始调整
else k = 0;//不是负数,全部都要调整
char temp;//临时变量,交换两个值时用到
for (j = k;j <= (i - 1) / 2;j++)//头尾一一对称交换,i其实就是字符串的长度,索引最大值比长度少1
{
temp = str[j];//头部赋值给临时变量
str[j] = str[i - 1 + k - j];//尾部赋值给头部
str[i - 1 + k - j] = temp;//将临时变量的值(其实就是之前的头部值)赋给尾部
}
return str;//返回转换后的字符串
}
6.6 一维码调用示例
字库芯片内置一维码(条形码)绘制功能。GT_DrawBarcode 函数通过配置 GT_BARCODE_PARAM 结构体,可快速在屏幕上绘制 CODE128 等格式的条形码。
#include "stdio.h"
#include "string.h"
/**************************************
arr[] : 13个数组
**************************************/
void BAR_codeEAN13(unsigned int x, unsigned int y, unsigned char arr[])
{
unsigned char buff_temp[60];
unsigned long *tmp ;
unsigned char i = 0;
tmp = BAR_CODE_EAN13(arr);
for(i = 0;i < 13;i++)
{
memset(buff_temp,0,sizeof(buff_temp));
r_dat_bat(*tmp, 54, buff_temp);
DisZK_DZ_W(x,y,16,28,BLACK,WHITE,&buff_temp[2],1);
x+=buff_temp[1];
tmp++;
}
}
/**************************************
ascii_arr[] : 字符数组 ASCII
arr_len: 字符数组的长度
Code 39码只接受如下43个有效输入字符:
1. 26个大写字母(A – Z);
2. 十个数字(0 – 9);
3. 连接号(-),句号(.),空格,美圆符号($),斜扛(/),加号(+)以及百分号(%)。其余的输入将被忽略。
**************************************/
void BAR_codeEAN39(unsigned int x, unsigned int y, unsigned char ascii_arr[], unsigned int arr_len) {
unsigned char buff_tmp[50], i;
for(i=0; i<arr_len; i++) {
BAR_CODE39(ascii_arr[i], buff_tmp);
DisZK_DZ_W(x,y,16,22,BLACK,WHITE,&buff_tmp[0],1);
x+=11;
}
}
/**************************************
arr[]: 条形码编码 整数
arr_len: 数组长度
flag: 起始符有3种模式
当flag=1时为Code-128-A;
当flag=2时为Code-128-B;
当flag=3时为Code-128-C;
Code128编码表参考: https://blog.csdn.net/rodulf/article/details/51276820
**************************************/
void BAR_codeEAN128(unsigned int x, unsigned y, unsigned char arr[], unsigned int arr_len, unsigned char flag) {
unsigned char buff_tmp[60];
unsigned long *tmp;
unsigned char i=0;
const unsigned char verify = 3;
tmp = BAR_CODE128(arr, arr_len, flag);
for(i=0; i<arr_len+verify; i++) {
memset(buff_tmp, 0, sizeof(buff_tmp));
r_dat_bat(*tmp, 40, buff_tmp);
DisZK_DZ_W(x, y, 16, 20, BLACK, WHITE, &buff_tmp[0], 1); //显示函数, 根据实际修改, 参考显示函数文件
x+=11; //可修改
tmp++;
}
}
//
#if 0
int main(void) {
unsigned char code[13]={6,9,0,1,6,8,7,5,8,2,4,4,2};
unsigned char code1[13]={6,9,3,8,7,0,9,3,0,0,4,4,9};
unsigned char code2[13]={'A','1','9','3','4','7','$',' ','-','5','8','3','E'};
unsigned char code3[13] = {16,33,45,31,27,26,40};
#if 01
BAR_codeEAN13(0, 0, code);
BAR_codeEAN13(0, 0, code1);
#endif
#if 01
BAR_codeEAN39(0, 150, code2, 13);
#endif
#if 01
BAR_codeEAN128(0, 200, code3, 7, 1);
#endif
return 0;
}
#endif
6.7 获取宽度_W简易
WY_GetStringWidth 是一个简化的字符串宽度计算函数,适用于等宽字体场景。该函数假设每个字符宽度等于字号大小,适合快速估算字符串显示所需的像素宽度。
/**
* @brief 得到灰度文字的宽度 (W横置横排)
*
* @param pdata 文字数据
* @param dot_w 文字宽度 例如:48x48大小的文字 dot_w = 48
* @param dot_h 文字高度 例如:48x48大小的文字 dot_h = 48
* @param gray 取值: 1 / 2 / 4
* @return int 实际文字宽度
*/
int _get_font_width(uint8_t * pdata, uint16_t dot_w, uint16_t dot_h, uint16_t gray)
{
uint16_t byte_w, i;
int j, len;
uint8_t tmp = 0;
byte_w = ((dot_w + 7) >> 3) * gray;
for (j = byte_w - 1; j >= 0; j--) {
tmp = 0;
for (i = 0; i < dot_h; i++) {
tmp |= pdata[i * byte_w + j];
}
if (0 != tmp) {
for (i = 0; i < 8; i++) {
if ((tmp >> i) & 0x01) {
break;
}
}
len = ((j / gray) * 8) + ((j % gray) * 8 + (8 - i)) / gray;
//
if (len >= dot_h) {
continue;
}
return len;
}
}
return dot_w;
}
6.8 获取宽度
GT_GetStringWidth 是完整的字符串宽度计算函数,通过查询每个字符的实际宽度来精确累加。与简易版本不同,该函数使用 GT_GetCharWidth 获取每个字符的真实像素宽度,适用于非等宽字体场景。
/**
@p: 字符点阵数组
@zfwidth: 显示字符的宽度
@zfhigh: 显示字符的高度
@return: 字符的实际最大宽度
*/
unsigned int get_width(unsigned char * p, unsigned int zfwidth, unsigned int zfhigh)
{
unsigned char * q;
unsigned int i, j, tem, tem1, witdh1 = 0, witdh2 = 0;
q = p;
for (i = 0;i < zfwidth / 16;i++) {
tem = 0;
tem1 = 0;
for (j = 0;j < zfhigh;j++) {
tem = (*(q + j * (zfwidth / 8) + i * 2) | (*(q + 1 + j * (zfwidth / 8) + i * 2)) << 8);
tem1 = tem1 | tem;
}
witdh1 = 0;
for (j = 0;j < 16;j++) {
if (((tem1 << j) & 0x8000) == 0x8000) {
witdh1 = j;
}
}
witdh2 += witdh1;
}
return witdh2;
}
//
/**
@p: 字符点阵数组
@zfwidth: 显示字符的宽度
@zfhigh: 显示字符的高度
@return: 字符的实际最大宽度
*/
unsigned int get_width(unsigned char * p, unsigned int zfwidth, unsigned int zfhigh)
{
int high_i = 0, width_i = 0;
unsigned int record = 0;
unsigned int max_width = 0;
for (width_i = (zfwidth * 2) - 1; width_i >= 0; width_i--) {
for (high_i = 0;high_i <= zfhigh;high_i++) {
record = p[(zfwidth * high_i * 2) + width_i];
if (record) {
max_width = width_i;
break;
}
}
if (record) {
// max_width = width_i;
break;
}
}
if (width_i >= 0)
return max_width / 2 + 1;
else
return zfwidth / 3;
}
/**************/
/**
只适用横置横排
@p: 字符点阵数组
@zfwidth: 显示字符的宽度
@zfhigh: 显示字符的高度
@return: 字符的实际最大宽度
*/
unsigned int get_width_1(unsigned char * p, unsigned int zfwidth, unsigned int zfhigh)
{
unsigned char width_Byte = zfwidth / 8;
unsigned char record = 0;
unsigned int max_width = 0, cnt = 0;
int i, j, m;
for (i = width_Byte - 1; i >= 0; i--) {
record = 0;
cnt = i;
for (j = 0; j < zfhigh; j++) {
record |= p[cnt];
cnt += width_Byte;
}
if (record != 0) {
break;
}
}
for (m = 0; m < 8; m++) {
if ((record >> m) & 0x01) {
max_width = (8 - m) + i * 8;
break;
}
}
return max_width;
}
6.9 横竖转换
GT_MatrixTranspose 将点阵字模数据进行横竖方向转换。当需要将横向扫描的字模数据转换为纵向扫描格式时(或反之),可调用此函数完成矩阵转置操作。
#define HZ_MODE00 0//竖置横排(Y)
#define HZ_MODE01 1//竖置竖排(Z)
#define HZ_MODE10 2//横置横排(W)
#define HZ_MODE11 3//横置竖排(X)
#define HZ_MODE4 4//Y-->W
//条件putdata的数据写入X,Y的LCD显示缓冲RAM中
/*
*getdate: 转换后的字形数据
*putdate: 转换签订字形数据
*width: 字宽
*high: 字高
*style: 转换功能的选择, HZ_MODE00 0//竖置横排(Y)
HZ_MODE01 1//竖置竖排(Z)
HZ_MODE10 2//横置横排(W)
HZ_MODE11 3//横置竖排(X)
HZ_MODE4 4//Y-->W
*使用示例:lcdram_map(getdate,putdate,32,32,HZ_MODE4);//将32x32的字形数据由Y排置(竖置横排)转换成W排置(横置横排)保存在getdate数组中
*/
void lcdram_map(u8 * getdate, u8 * putdata, u8 width, u8 high, u8 style)
{
u16 i, j, hbyte, wbyte;
unsigned char i_8, j_8;
wbyte = (width + 7) / 8;
hbyte = (high + 7) / 8;
//--------------------------------------------------------------------------
// Y--> W; Y-->X; Y-->Z;
//--------------------------------------------------------------------------
if (style == HZ_MODE4) //竖置横排 转 横置横排 ( Y-->W )
{
for (i = 0; i < high; i++)
for (j = 0; j < width; j++) {
i_8 = i / 8;
if ((*(putdata + i_8 * width + j) & (0x01 << (i % 8))) > 0)
getdate[wbyte * i + j / 8] |= (0x80 >> (j % 8));
else
getdate[wbyte * i + j / 8] &= (~(0x80 >> (j % 8)));
}
}
//--------------------------------------------------------------------------
// W--> Y; W-->Z;W-->X;
//--------------------------------------------------------------------------
if (style == HZ_MODE00) //竖置横排 (W--> Y)
{
for (i = 0; i < high; i++)
for (j = 0; j < width; j++) {
i_8 = i / 8;
if ((*(putdata + wbyte * i + j / 8) & (0x80 >> (j % 8))) > 0)
getdate[i_8 * width + j] |= (0x01 << (i % 8));
else
getdate[i_8 * width + j] &= (~(0x01 << (i % 8)));
}
}
else if (style == HZ_MODE01) //竖置竖排 (W-->Z)
{
for (i = 0; i < high; i++)
for (j = 0; j < width; j++) {
if ((*(putdata + wbyte * i + j / 8) & (0x80 >> (j % 8))) > 0)
getdate[j * hbyte + i / 8] |= (0x01 << (i % 8));
else
getdate[j * hbyte + i / 8] &= (~(0x01 << (i % 8)));
}
}
else if (style == HZ_MODE11)//横置竖排 (W-->X)
{
for (i = 0; i < high; i++)
for (j = 0; j < width; j++) {
j_8 = j / 8;
if ((*(putdata + wbyte * i + j / 8) & (0x80 >> (j % 8))) > 0)
getdate[j_8 * high + i] |= (0x80 >> (j % 8));
else
getdate[j_8 * high + i] &= (~(0x80 >> (j % 8)));
}
}
else if (style == HZ_MODE10)//横置横排 做镜像(W-->W')
{
for (i = 0; i < high; i++)
for (j = 0; j < width; j++) {
if ((*(putdata + wbyte * i + j / 8) & (0x80 >> (j % 8))) > 0)
*(getdate + wbyte * i + (width - j) / 8) |= (0x80 >> ((width - j) % 8));
else
*(getdate + wbyte * i + (width - j) / 8) &= ~(0x80 >> ((width - j) % 8));
}
}
}
//竖直竖排转横置横排 Z转W
/*
*getdate: 转换后的字形数据
*putdate: 转换签订字形数据
*width: 字宽
*high: 字高
*/
void Z2W(unsigned char * getData, unsigned char * putData, unsigned char width, unsigned char height) {
unsigned char line, lie;
unsigned int i = 0, count = 0, mark = 0; //i控制putData的下标, count控制getData的下标
for (line = 0; line < height; line++) { //控制行
for (lie = 0; lie < width; lie++) { //控制列
getData[count] = (unsigned char)(getData[count] << 0x01); //准备接收下一下
if (putData[i] & (0x01 << (line % 8)))
getData[count] += 1; //先取低位, 存新数组内
else
getData[count] += 0;
i += height >> 3; //下一列
if (lie % 8 == 7) {
count++;
}
}
if (line % 8 == 7) {
i = ++mark;
}
else
i = mark;
}
}
//竖直竖排转竖置横排(Z->Y)
/*
*getdate: 转换后的字形数据
*putdate: 转换签订字形数据
*width: 字宽
*high: 字高
*/
void Z2Y(unsigned char * getData, unsigned char * putData, unsigned char width, unsigned char height) {
unsigned char line = height >> 3;
unsigned int i = 0, count = 0, mark_count = 0, sum = width * height >> 3;
for (i = 0;i < sum;i++) {
getData[count] = putData[i];
count += width;
if (((i + 1) % line) == 0)
count = ++mark_count;
}
}
//竖置横排转竖直竖排 Y转Z
/*
*getdate: 转换后的字形数据
*putdate: 转换签订字形数据
*width: 字宽
*high: 字高
*/
void Y2Z(unsigned char * getData, unsigned char * putData, unsigned char width, unsigned char height) {
unsigned char line = height >> 3;
unsigned int i = 0, count = 0, mark_count = 0, sum = width * height >> 3;
for (i = 0; i < sum; i++) {
getData[count] = putData[i];
count += line;
if (((i + 1) % width) == 0) {
count = ++mark_count;
}
}
}
6.10 字节逆序
GT_ReverseByte 将字节的位序进行反转(bit 7 与 bit 0 交换,bit 6 与 bit 1 交换,以此类推)。当字模数据的位序与显示设备要求不一致时,可调用此函数进行转换。
/*
p:需要逆置的数据地址
len:需要逆置的数据长度
*/
void byte_cov(unsigned char * p, unsigned long len)
{
unsigned long i;
unsigned char tem;
for (i = 0;i < len;i++) {
tem = p[i];
p[i] = ((tem & 0x01) << 7) | ((tem & 0x02) << 5) | ((tem & 0x04) << 3) | ((tem & 0x08) << 1) \
| ((tem & 0x10) >> 1) | ((tem & 0x20) >> 3) | ((tem & 0x40) >> 5) | ((tem & 0x80) >> 7);
}
}
/**
* @brief Y排置字体镜像
* dz_data:字形数据
* len:数据长度
*/
void y_mirror(unsigned char * dz_data, unsigned long len)
{
#if 0
unsigned long i;
unsigned char tem;
for (i = 0; i < len; i++) {
tem = dz_data[i];
dz_data[i] = dz_data[len - i - 1];
dz_data[len - i - 1] = tem;
}
#else
unsigned long i, mid;
unsigned char tem;
mid = len >> 1;
for (i = 0; i < mid; i++) {
tem = dz_data[i];
dz_data[i] = dz_data[mid - 1 - i];
dz_data[mid - 1 - i] = tem;
}
for (i = mid; i < len; i++) {
tem = dz_data[i];
dz_data[i] = dz_data[len - 1 - i];
dz_data[len - 1 - i] = tem;
}
for (i = 0; i < mid; i++) {
tem = dz_data[i];
dz_data[i] = dz_data[mid + i];
dz_data[mid + i] = tem;
}
#endif
}
/**
* @brief Y排置字体镜像
* dz_data:字形数据
* len:数据长度
*/
void y_mirror(unsigned char * dz_data, unsigned long len)
{
unsigned long i, mid;
unsigned char tem;
mid = len >> 1;
for (i = 0; i < mid; i++) {
tem = dz_data[i];
dz_data[i] = dz_data[mid - 1 - i];
dz_data[mid - 1 - i] = tem;
}
for (i = mid; i < len; i++) {
tem = dz_data[i];
dz_data[i] = dz_data[len - 1 - i];
dz_data[len - 1 - i] = tem;
}
for (i = 0; i < mid; i++) {
tem = dz_data[i];
dz_data[i] = dz_data[mid + i];
dz_data[mid + i] = tem;
}
}
6.11 不等宽字符的显示
1.点阵/灰度字库芯片不等宽字符显示处理
点阵/灰度字库 不等宽字符排列格式
以16点阵ASCII不等宽字符为例(不等宽字符类型详见各字库芯片宏定义中的注释),16点阵不等宽字符的信息需要34个字节(BYTE 0 – BYTE33)来表示。
存储格式
由于字符是不等宽的,因此在存储格式中BYTE0~ BYTE1存放文字宽度数据,BYTE2-33存放点阵数据。具体格式见下图:
具体数据格式图
存储结构
不等宽字符的点阵存储宽度是以BYTE(8bit)为单位取整的,根据不同字符宽度会出现相应的空白区。根BYTE0~ BYTE1所存放点阵的实际宽度数据,即x坐标加上该字符的宽度数据,做为下一个字符显示的x轴的起始位置,
例如:ASCII圆角字符B
0-33BYTE的全部数据是: 00 0C 00 00 00 00 00 00 7F 80 7F C0 60 C0 60 C0 60 C0 7F 80 7F C0 60 E0 60 60 60 60 7F C0 7F 80 00 00
其中头两个字节:
BYTE0~ BYTE1: 00 0C 为ASCII圆角字符B的点阵宽度数据,即:12位宽度。字符后面有4位空白区,可以在排版下一个字时考虑到这一点,将下一个字的起始位置前移。即x坐标加上该字符的宽度数据,即为下一个字符的x轴的起始位置,
BYTE2-33: 00 00 00 00 00 00 7F 80 7F C0 60 C0 60 C0 60 C0 7F 80 7F C0 60 E0 60 60 60 60 7F C0 7F 80 00 00 为ASCII圆角字符B的点阵数据。
不等宽示例代码
/*
* 示例:使用 ASCII_12_A 显示 "Ab"。
* ASCII_12_A 是不等宽 12x12 Arial 字体。
*/
unsigned char DZ_Data[512];
int x_position = 10, y_position = 10;
unsigned int a_width = 0;
/* 读取并显示字符 'A'。 */
memset(DZ_Data, 0, sizeof(DZ_Data));
ASCII_GetData(0x41, ASCII_12_A, DZ_Data);
a_width = (DZ_Data[0] << 8) | DZ_Data[1];
LCD_Display_hz(&DZ_Data[2], x_position, y_position, 16, 12, 1);
/* 读取并显示字符 'b',x 坐标使用 'A' 的实际宽度计算。 */
memset(DZ_Data, 0, sizeof(DZ_Data));
ASCII_GetData(0x62, ASCII_12_A, DZ_Data);
LCD_Display_hz(&DZ_Data[2], x_position + a_width, y_position, 16, 12, 1);
2 矢量字库芯片不等宽字符显示处理
如果使用get_font函数则打开“05_屏幕显示资料\获取宽度.c”,直接拷贝获取宽度的代码。如果使用get_font_gray函数,则打开“05_屏幕显示资料\获取灰度字形的宽度”拷贝获取灰度宽度的代码。
代码所在路径截图
具体的代码以及参数含义如下文所示:
/**
* @brief Get the width object
*
* @param p 字符数据数组首地址
* @param zfwidth 获取字符时填写的宽度
* @param zfhigh 获取字符时填写的高度
* @return unsigned int 实际宽度
*/
unsigned int get_width(unsigned char *p,unsigned int zfwidth,unsigned int zfhigh )
{
unsigned char *q;
unsigned int i,j,tem,tem1,witdh1=0,witdh2=0;
q=p;
for (i=0;i<zfwidth/16;i++)
{
tem=0;
tem1=0;
for (j=0;j<zfhigh;j++)
{
tem=(*(q+j*(zfwidth/8)+i*2)|(*(q+1+j*(zfwidth/8)+i*2))<<8);
tem1=tem1|tem;
}
witdh1=0;
for (j=0;j<16;j++)
{
if (((tem1<<j)&0x8000)==0x8000)
{
witdh1=j;
}
}
witdh2+=witdh1;
}
return witdh2;
}
/**
* @brief 获取灰度字库宽度
*
* @param data 带灰度的字符数据数组
* @param dot_w 获取字符时填写的宽度
* @param dot_h 获取字符时填写的高度
* @param gray 取值: 1 / 2 / 4
* @return int 实际宽度
*/
uint16_t get _width_gray(const uint8_t* data, uint16_t dot_w, uint16_t dot_h, uint8_t gray)
{
uint16_t byte_w, i;
int j, len;
uint8_t tmp = 0;
byte_w = ((dot_w + 7) >> 3) * gray;
for(j = byte_w-1;j >= 0; j--) {
tmp = 0;
for(i = 0; i < dot_h; i++) {
tmp |= data[i*byte_w + j];
}
if(0 != tmp) {
for(i = 0; i < 8; i++){
if((tmp >> i)&0x01){
break;
}
}
len = ((j / gray) * 8) + ((j%gray)*8+(8-i))/gray;
if (len >= dot_h) {
continue;
}
return len;
}
}
return dot_w;
}
矢量输出灰度数据时获取宽度示例:
unsigned int *tmp, real_w = 0;
tmp = get_Font_Gray(pBits,(*text) < 0x80?VEC_FT_ASCII_STY:VEC_SONG_STY,*text,size, size); //
real_w = get_width_gray(pBits,tmp[0],size, tmp[1]); //获取灰度的宽度
Gray_Display_hz(pBits,x,y,tmp[0],size,tmp[1]);
x = x + real_w;
矢量输出点阵数据时获取宽度示例:
VECFONT_ST font_st = {.fontCode = 0x41, .type = VEC_FT_ASCII_STY, .size = 16, .thick = 16, .zkBuffer = {0}};
get_font_st(&font_st); //获取矢量字符数据
int width = get_width(font_st.zkBuffer, 16, 16); //获取实际的字符宽度
Display_W(font_st.zkBuffer, 100, 100, width, 16);
char pBits[256] = {0};
get_font(pBits, VEC_FT_ASCII_STY, 0x41, 16, 16, 16); //获取矢量字符数据
int width = get_width(pBits, 16, 16); //获取实际的字符宽度
Display_W(pBits, 100, 100, width, 16);
注:无论点阵/灰度/矢量字库芯片,中文的宽度都是等宽。

