【第3版emWin教程】第19章 emWin6.x的2D图形库之绘制图形(含二维码和条形码)

2021-06-29 11:29:44 浏览数 (1)

教程不断更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429

第19章 emWin6.x的2D图形库之绘制图形(含二维码和条形码)

本期主要讲解2D图形库的图形绘制,包括绘制多边形,绘制圆,绘制椭圆,绘制弧线,绘制线图,绘制饼图。本章节的例子不用在开发板上面做调试,直接用emWin模拟器即可。

学习本章节,务必保证已经学习了第9章或者第10章以及第11章。本章节提供的模拟器演示代码都是可以在模拟器上面运行的,使用方法是将SWIPELIST_Demo.c文件里面的所有内容删掉并将本章节提供的代码复制到SWIPELIST_Demo.c文件即可运行。

19.1 初学者重要提示

19.2 绘制多边形

19.3 绘制圆

19.4 显示屏绘制的圆为什么不圆

19.4 绘制椭圆

19.5 绘制弧线

19.6 绘制线图

19.7 绘制饼图

19.8 总结

19.1 初学者重要提示

  1. 2D函数的图形绘制就是一些API函数,从应用上来讲,基本没有什么难度,初学者多做练习,调用几次就熟练了。
  2. 2D绘图的所有API函数在emWin手册中都有讲解,下图是中文版手册里面API函数的位置

下图是英文版手册里面API函数的位置:

19.2 绘制多边形

当前emWin支持的多边形函数主要有以下5个:

下面我们通过如下三个实例来讲解这几个API函数的用法。

19.2.1 例子一

这个例子主要涉及到以下三个函数:

  • void GUI_DrawPolygon(const GUI_POINT * pPoint, int NumPoints, int x, int y)

根据用户设置的坐标点(x,y)位置,将点列表pPoint中的NumPoints个坐标点连接,最终绘制出一个闭合的多边形。

  • void GUI_EnlargePolygon(GUI_POINT * pDest, const GUI_POINT * pSrc, int NumPoints, int Len)

将点列表pSrc中的NumPoints个坐标点按指定的像素个数Len全方位扩展,并将最终结果赋值给新的点列表pDest,这个新的点列表就是扩展后的多边形坐标点。

  • void GUI_FillPolygon(const GUI_POINT * pPoint, int NumPoints, int x, int y)

根据用户设置的坐标点(x,y)位置,将点列表pPoint中的NumPoints个坐标点连接,最终绘制出一个填充的多边形。

下面是在模拟器上实际运行的例子:

代码语言:javascript复制
#include "GUI.h"


/* 图形的原始坐标点 */
const GUI_POINT aPoints[] = {
{ 40, 20},
{ 0, 20},
{ 20, 0}
};

/* 用于存储放大后的坐标点 */
GUI_POINT aEnlargedPoints[GUI_COUNTOF(aPoints)];

void Sample(void) {
    int i;

    /* 清屏 */
    GUI_Clear();

    /* 设置绘图模式 */
    GUI_SetDrawMode(GUI_DM_XOR);
    
    /* 绘制多边形 */
    GUI_FillPolygon(aPoints,              /* 指向要显示和填充的多边形 */
                    GUI_COUNTOF(aPoints), /* 点列表中指定的点数量 */
                    140,                  /* 原点的X位置 */
                    110);                 /* 原点的Y位置 */

    for (i = 1; i < 10; i  ) {
        GUI_EnlargePolygon(aEnlargedPoints,      /* 指向目标多边形 */
                           aPoints,              /* 指向源多边形 */
                           GUI_COUNTOF(aPoints), /* 点列表中指定的点数量 */
                           i * 5);               /* 扩展多边形的长度 (像素) */

        /* 绘制放大后的多边形 */
        GUI_FillPolygon(aEnlargedPoints, GUI_COUNTOF(aPoints), 140, 110);
    }
}

/*********************************************************************
*
*       MainTask
*/
void MainTask(void) {

  /* emWin初始化 */
  GUI_Init();

  /* 调用测试函数 */
  Sample();

  while (1)
  {
      GUI_Delay(10);
  }
}

显示效果如下:

19.2.2 例子二

  • void GUI_MagnifyPolygon(GUI_POINT * pDest, const GUI_POINT * pSrc, int NumPoints,

int Mag);

此函数可以按照用户设置的放大系数Mag放大多边形。另外请注意,扩展和放大多边形之间的区别,比如调用函数GUI_EnlargePolygon()(参数Len= 1)是将多边形的所有边扩展1像素,而调用GUI_MagnifyPolygon()(参数Mag= 1)则没有效果。

下面是在模拟器上面实际运行的例子:

代码语言:javascript复制
#include "GUI.h"


/* 图形的原始坐标点 */
const GUI_POINT aPoints[] = {
{ 0, 20},
{ 40, 20},
{ 20, 0}
};

/* 用于存储放大后的坐标点 */
GUI_POINT aMagnifiedPoints[GUI_COUNTOF(aPoints)];

void Sample(void) {
    int Mag, y = 0, Count = 4;

    /* 清屏 */
    GUI_Clear();
    
    /* 设置前景色,即所绘制图形的颜色 */
    GUI_SetColor(GUI_GREEN);

    for (Mag = 1; Mag <= 4; Mag *= 2, Count /= 2) {
        int i, x = 0;

        /* 放大多边形 */
        GUI_MagnifyPolygon(aMagnifiedPoints, aPoints, GUI_COUNTOF(aPoints), Mag);

        /* 绘制填充的多边形 */
        for (i = Count; i > 0; i--, x  = 40 * Mag) {
            GUI_FillPolygon(aMagnifiedPoints, GUI_COUNTOF(aPoints), x, y);
        }

        y  = 20 * Mag;
    }
}


/*********************************************************************
*
*       MainTask
*/
void MainTask(void) {

  /* emWin初始化 */
  GUI_Init();

  /* 调用测试函数 */
  Sample();

  while (1)
  {
      GUI_Delay(10);
  }
}

实际显示效果如下:

19.2.3 例子三

这个多边形的例子是官方提供的,例子所在的位置如下:

显示效果如下:

指出官方例子的所在位置和演示现象是为了方便用户以后做项目来参考。

19.3 绘制圆

  • void GUI_DrawCircle(int x0, int y0, int r);

在当前窗口中的指定位置(x0, y0)绘制半径为r的圆圈。

下面是在模拟器上面实际运行的例子:

代码语言:javascript复制
#include "GUI.h"

void ShowCircles(void) 
{
    int i;

    /* 绘制圆圈 */
    for (i=10; i<50; i  = 3)
    GUI_DrawCircle(120, 60, i);
}

/*********************************************************************
*
*       MainTask
*/
void MainTask(void) {

  /* emWin初始化 */
  GUI_Init();

  /* 调用测试函数 */
  ShowCircles();

  while (1)
  {
      GUI_Delay(10);
  }
}

实际显示效果如下:

19.4 显示屏绘制的圆为什么不圆

这个问题经常有初学者会问,比如这个帖子:http://bbs.armfly.com/read.php?tid=3899 。用户在

显示屏上面绘制圆圈,显示出来的效果是这个样子的:

给人的感觉是圆圈不够圆,实际上是因为显示屏的每个像素点的长度和宽度不是1:1的,从而造成显示出来的效果有点扁。

19.5 绘制椭圆

  • void GUI_DrawEllipse(int x0, int y0, int rx, int ry)

在当前窗口中的指定位置(x0,y0)绘制x轴方向半径为rx,y轴方向半径为ry的椭圆。

  • void GUI_FillEllipse(int x0, int y0, int rx, int ry)

在当前窗口中的指定位置(x0,y0)绘制x轴方向半径为rx,y轴方向半径为ry的填充的椭圆。

下面是在模拟器上面实际运行的例子:

代码语言:javascript复制
#include "GUI.h"


/*********************************************************************
*
*       MainTask
*/
void MainTask(void) {

  /* emWin初始化 */
  GUI_Init();
  
  /* 绘制填充的椭圆 */
  GUI_SetColor(GUI_RED);   
  GUI_FillEllipse(100, 100, 50, 70);

  /* 绘制椭圆 */
  GUI_SetColor(GUI_GREEN);    
  GUI_DrawEllipse(100, 100, 60, 80);

   /* 绘制填充的椭圆 */
  GUI_SetColor(GUI_BLACK);    
  GUI_FillEllipse(100, 100, 10, 50);

  while (1)
  {
      GUI_Delay(10);
  }

}

实际现象效果如下:

19.6 绘制弧线

void GUI_DrawArc(int xCenter, int yCenter, int rx, int ry, int a0, int a1)

在当前窗口中的指定位置(xCenter,yCenter)绘制x轴方向半径为rx,y轴方向半径为ry,起始角度为a0,结束角度为a1的弧线。

注意,当前参数ry未使用,是以rx代替的,也就是说绘制的弧线是圆弧。

下面是在模拟器上面实际运行的例子:

代码语言:javascript复制
#include "GUI.h"
#include "math.h"
#include "stdio.h"


void DrawArcScale(void) 
{
    int x0 = 160;
    int y0 = 160;
    int i;
    char ac[4];
    
    /* 设置背景色为白色并清屏 */
    GUI_SetBkColor(GUI_WHITE);
    GUI_Clear();
    
    /* 设置画笔大小 */
    GUI_SetPenSize( 5 );

    /* 设置文本模式,字体和前景色 */
    GUI_SetTextMode(GUI_TM_TRANS);
    GUI_SetFont(&GUI_FontComic18B_ASCII);
    GUI_SetColor(GUI_BLACK);

    /* 绘制圆弧 */
    GUI_DrawArc( x0,y0,150, 150,-30, 210 );

    /* 在圆弧上面显示刻度和相应刻度的数值 */
    for (i=0; i<= 23; i  ) {
        float a = (-30 i*10)*3.1415926/180;
        int x = -141*cos(a) x0;
        int y = -141*sin(a) y0;

        if (i%2 == 0)
        GUI_SetPenSize( 5 );
        else
        GUI_SetPenSize( 4 );

        GUI_DrawPoint(x,y);
        if (i%2 == 0) {
            x = -123*cos(a) x0;
            y = -130*sin(a) y0;
            sprintf(ac, "%d", 10*i);
            GUI_SetTextAlign(GUI_TA_VCENTER);
            GUI_DispStringHCenterAt(ac,x,y);
        }
    }
}

/*********************************************************************
*
*       MainTask
*/
void MainTask(void) {

   /* 初始化emWin */
   GUI_Init();

   /* 绘制饼图 */
   DrawArcScale();

   while (1)
   {
      GUI_Delay(10);
   }

}

实际显示效果如下:

19.7 绘制曲线

void GUI_DrawGraph(I16 * paY, int NumPoints, int x0, int y0)

根据用户设置的起始坐标(x0,y0),依次将NumPoints个点坐标

(x0 0, y0 *(paY 0)))

(x0 1, y0 *(paY 1)))

(x0 2, y0 *(paY 2)))

(x0 3, y0 *(paY 3)))

…………

(x0 NumPoints-2, y0 *(paY NumPoints-2)))

(x0 NumPoints-1, y0 *(paY NumPoints-1)))

连接起来绘制成曲线。

下面是在模拟器上面实际运行的例子:

代码语言:javascript复制
#include "GUI.h"
#include <stdlib.h>


I16 aY[200];

/*********************************************************************
*
*       MainTask
*/
void MainTask(void) {
  int i;

  /* 初始化emWin */
  GUI_Init();

  /* 获取随机数 */
  for (i = 0; i < GUI_COUNTOF(aY); i  ) 
  {
     aY[i] = rand() % 50;
  }

  /* 绘制波形 */
  GUI_SetColor(GUI_YELLOW);
  GUI_DrawGraph(aY, GUI_COUNTOF(aY), 0, 0);

  while (1)
  {
      GUI_Delay(10);
  }

}

实际显示效果如下:

19.8 绘制饼图

void GUI_DrawPie(int x0, int y0, int r, int a0, int a1, int Type)

绘制圆形扇区。

在当前窗口中的指定位置(x0,y0)绘制以r为半径,起始角度为a0,结束角度为a1的圆形扇区。

下面是在模拟器上面实际运行的例子:

代码语言:javascript复制
#include "GUI.h"


/* 变量 */
int i, a0, a1;
const unsigned aValues[] = { 100, 135, 190, 240, 340, 360};
const GUI_COLOR aColors[] = { GUI_BLUE, GUI_GREEN, GUI_RED,
                              GUI_CYAN, GUI_MAGENTA, GUI_YELLOW };


/*********************************************************************
*
*       MainTask
*/
void MainTask(void) {
  int i;

  /* 初始化emWin */
  GUI_Init();

  /* 绘制饼图 */
  for (i = 0; i < GUI_COUNTOF(aValues); i  ) {
     a0 = (i == 0) ?0 :aValues[i - 1];
     a1 = aValues[i];
     GUI_SetColor(aColors[i]);
     GUI_DrawPie(100, 100, 50, a0, a1, 0);
  }

  while (1)
  {
      GUI_Delay(10);
  }

}

实际显示效果如下:

19.9 绘制二维码

GUI_HMEM GUI_QR_Create(const char * pText,

int PixelSize,

int EccLevel,

int Version);

  • 第1个参数是要显示二维码的字符,注意要是UTF-8编码格式,汉字也支持。
  • 第2个参数是二维码中单位点阵大小。
  • 第3个参数是ECC纠错,支持的参数如下:

GUI_QR_ECLEVEL_L 大约 7% 的错误被纠正。

GUI_QR_ECLEVEL_M 大约15% 的错误被纠正。

GUI_QR_ECLEVEL_Q 大约25% 的错误被纠正。

GUI_QR_ECLEVEL_H 大约30% 的错误被纠正。

  • 第4个参数是二维码一个像素模块需要的大小,范围1-40。如果设置为0的话,会自动计算。

下面是在模拟器上面实际运行的例子:

代码语言:javascript复制
#include "GUI.h"


/*********************************************************************
*
*       MainTask
*/
void MainTask(void)
{
    GUI_HMEM hMem, hMem1, hMem2;
    GUI_Init();

    GUI_SetBkColor(GUI_BLUE);
    GUI_Clear();

    GUI_SetColor(GUI_WHITE);
    GUI_SetFont(&GUI_Font16_1);
    GUI_DispStringAt("http://www.armfly.com/", 2, 5);
    hMem = GUI_QR_Create("http://www.armfly.com/", 7, GUI_QR_ECLEVEL_L, 0);
    GUI_QR_Draw(hMem, 2, 30);

    GUI_DispStringAt("http://www.armbbs.cn/", 190, 5);
    hMem1 = GUI_QR_Create("http://www.armbbs.cn/", 5, GUI_QR_ECLEVEL_L, 0);
    GUI_QR_Draw(hMem1, 190, 30);

    GUI_DispStringAt("https://www.segger.com/", 330, 5);
    hMem2 = GUI_QR_Create("https://www.segger.com/", 3, GUI_QR_ECLEVEL_L, 0);
    GUI_QR_Draw(hMem2, 330, 30);

    while (1)
    {
        GUI_Delay(10);
    }
}

实际显示效果如下:

19.10 绘制条形码

int GUI_BARCODE_Draw( int xPos,

int yPos,

int ModuleSize,

int ySize,

int Type,

const char * sBarcode);

  • 第1个参数是条形码x坐标位置。
  • 第2个参数是条形码y坐标位置。
  • 第3个参数是绘制条形码竖条时,最小的宽度。
  • 第4个参数是条形码高度,高度至少要是第3个参数的3倍。
  • 第5个参数是条形码类型,支持两种类型

GUI_BARCODE_ITF 可以展示偶数个数字。

GUI_BARCODE_128 可以展示所有128个字符。

  • 第6个参数是要展示的内容

下面是在模拟器上面实际运行的例子:

代码语言:javascript复制
#include "GUI.h"



/*********************************************************************
*
*       MainTask
*/
void MainTask(void) {
  int i;

  /* 初始化emWin */
  GUI_Init();

  /* 绘制条形码 */
  GUI_SetColor(GUI_BLACK);
GUI_SetBkColor(GUI_WHITE);
GUI_BARCODE_Draw(0, 0, 3, 60, GUI_BARCODE_ITF, "123456789");
GUI_BARCODE_Draw(0, 0, 2, 60, GUI_BARCODE_128, "armfly");

  while (1)
  {
      GUI_Delay(10);
  }

}

实际显示效果如下:

19.11 总结

关于2D图形库的绘图部分就跟大家讲这么多,还是那句话,多多练习,熟能生巧。

0 人点赞