单片机 GUI 设计(二)- 显示透明字体
单片机 GUI 设计(二)- 显示透明字体

单片机 GUI 设计(二)- 显示透明字体

基于 GD32F303ZET6 苹果派开发板

简介

在本章中将介绍如何显示透明字体,即实现 “text” 或 “label” 的功能。

LCDShowChar 函数

LCD 驱动中提供了绘制字符函数,用于显示一个 ASCII 码,LCDShowChar 函数的形参如下所示。

void LCDShowChar(
  u16 x,    //字符横坐标
  u16 y,    //字符纵坐标
  u8 code,  //字符编码,即 ASCII 码 
  u8 size,  //字体大小
  u8 mode)  //显示模式

x 和 y 即为字符的坐标原点,要注意,屏幕的坐标原点在左上角,为统一管理,字符的原点我们也定义在字符的左上角。

code 即为字符编码。LCD 驱动中存储了 6×12、8×16 和 12×24 ASCII 码点阵数据,位于 “LCD/Font.h”,用于显示英文字符。如果用户想要显示中文,那么就需要将 code 形参拓展到 16 位甚至是 32 位,并提供中文字符的点阵数据。使用 LCDShowChar 函数显示字符时,code 参数直接填入字符对应的 ASCII 码即可。

size 为字体大小,可输入 12、16 或 24,分别对应 6×12、8×16 和 12×24 字体。

mode 为显示模式,输入 0 可显示带背景颜色的字符。LCDShowChar 显示的字符颜色由 g_iLCDPointColor 控制,背景颜色由 g_iLCDBackColor 控制。输入 1 将不绘制背景色,可显示透明效果,为本章中讨论的重点。

带透明效果和不带透明效果显示的字符比较

要想显示带背景的字符,可参考如下所示代码。

  //带背景的字符
  g_iLCDPointColor = BLACK;
  g_iLCDBackColor = WHITE;
  LCDShowChar(400, 240, 'A', 24, 0);

显示效果如下所示。

带背景的字符

可以看到字符 ‘A’ 显示在白色方框中。可我们在 PC 端或移动端做界面设计时,很少遇到带有背景色的文字。如果我们想让字体完美的融入背景中,怎么办?可以将 LCDShowChar 函数的 mode 形参设为 1,即可显示透明效果,如下所示。

不带背景的字符

字符重叠

前边的实验中可以看到,只要将 LCDShowChar 函数最后一个参数,即 mode 参数设为 1,即可将字符融入背景中,但这又会引入新的问题,如下所示。假设字符 ‘A’ 显示后,由于某种原因,需要刷新此区域,改为显示 ‘B’ 字符,我们也同样调用 LCDShowChar 函数显示。

  //字符重叠
  g_iLCDPointColor = BLACK;
  g_iLCDBackColor = WHITE;
  LCDShowChar(400, 240, 'A', 24, 1);
  LCDShowChar(400, 240, 'B', 24, 1);

正常来说应该显示的是字符 ‘B’,可是实际显示的图案如下所示。仔细看的话可以发现,这时字符 ‘A’ 和 字符 ‘B’ 重叠后呈现的效果。显示字符 ‘B’ 时,字符 ‘A’ 被当成了背景,又因为字符 ‘A’ 与字符 ‘B’ 颜色相同,就会造成字符重叠的现象。如果是纯色区域,为避免字符重叠,字符 ‘B’ 可以用带背景的方式显示,但如果背景是图片,这就比较麻烦了。

字符重叠

为了正常显示后续的字体,需要在单片机里设置一个背景缓冲区,绘制第一个字符之前首先将背景图案保存到缓冲区中,每次显示字体之前,首先还原背景图案,再绘制字符,这样就可以避免字符重叠。

  static u16 s_arrBackground[12 * 24];
  u16 x, y, i;

  //保存背景
  i = 0;
  for(x = 0; x < 12; x++)
  {
    for(y = 0; y < 24; y++)
    {
      s_arrBackground[i++] = LCDReadPoint(x + 400, y + 240);
    }
  }
  
  //绘制字符 'A'
  g_iLCDPointColor = BLACK;
  LCDShowChar(400, 240, 'A', 24, 1);
  
  //显示字符 'B' 之前先还原背景
  i = 0;
  for(x = 0; x < 12; x++)
  {
    for(y = 0; y < 24; y++)
    {
      LCDFastDrawPoint(x + 400, y + 240, s_arrBackground[i++]);
    }
  }
  
  //显示字符 'B' 
  LCDShowChar(400, 240, 'B', 24, 1);

封装代码

将上述代码封装,即可实现一个简单的 “text” 或 “label” 控件。头文件如下所示

//Text 控件
typedef struct
{
  u16  x, y, width, height; //原点、宽度、高度
  u8   size;                //字体大小,可以是 12、16、24
  u16* background;          //控件背景,由动态内存自动创建
}StructText;

void TextInit(StructText* widget);             //初始化 Text 控件
void TextShow(StructText* widget, char* text); //更新 Text 显示

源文件如下所示。源文件中使用到了 malloc 函数申请动态内存,背景缓冲区一般比较大,所以要将启动文件中的堆区设置的大一些,具体为 “ARM/System/startup_gd32f30x_hd.s” 文件,第 27 行的 “Heap_Size” 即为堆区大小。

//包含头文件
#include "Text.h"
#include "stdio.h"
#include "LCD.h"

//初始化 Text 控件
void TextInit(StructText* widget)
{
  u16 x, y, i;

  //为背景图片申请动态内存
  widget->background = (u16*)malloc(widget->width * widget->height * sizeof(u16));

  //保存背景
  i = 0;
  for(x = widget->x; x < widget->x + widget->width; x++)
  {
    for(y = widget->y; y < widget->y + widget->height; y++)
    {
      widget->background[i++] = LCDReadPoint(x, y);
    }
  }
}

//更新 Text 显示
void TextShow(StructText* widget, char* text)
{
  u16 x, y, i;

  //重绘背景
  i = 0;
  for(x = widget->x; x < widget->x + widget->width; x++)
  {
    for(y = widget->y; y < widget->y + widget->height; y++)
    {
      LCDFastDrawPoint(x, y, widget->background[i++]);
    }
  }

  //绘制字符
  x = widget->x;
  y = widget->y;
  while(0 != *text)
  {
    LCDShowChar(x, y, *text, widget->size, 1);
    text++;
    x = x + widget->size / 2;
  }
}

测试代码如下。

void main(void)
{
  static StructText s_structTextWidget;

  //初始化 Text 控件
  s_structTextWidget.x = 400;
  s_structTextWidget.y = 240;
  s_structTextWidget.width = 12 * 10;
  s_structTextWidget.size = 24;
  s_structTextWidget.height = s_structTextWidget.size;
  TextInit(&s_structTextWidget);

  while(1)
  {
    //显示字符串 "ABCD"
    TextShow(&s_structTextWidget, "ABCD");
    LEDFlicker(0);
    DelayNms(500);
    
    //显示字符串 "EFGH"
    TextShow(&s_structTextWidget, "EFGH");
    LEDFlicker(0);
    DelayNms(500);
  }
}

将代码烧入苹果派开发板后,就可以看到字符串 “ABCD” 和 “EFGH” 交替显示,没有重叠现象。

上述代码只实现了最基础的 Text 控件功能,还有好多地方需要完善,例如支持多行显示、支持左对齐、右对齐和显示等等。

实验结果

实验结果如下所示。

源码

本章节中的源码请参考《单片机 GUI 设计(零)- 大纲