EGE专栏:EGE专栏
下一篇:EGE基础入门篇(七):组合图形
一、EGE提供的基本图形
EGE提供了如下图所示的一些基本图形,包括 线框图形 和 填充图形 。
在图中,列出了图形所对应的函数,其中函数又分为普通绘图函数和高级绘图函数。
- 高级绘图函数可以绘制出更高质量的图形,可以进行图形反走样,减少图形边缘的锯齿感,并且图形使用的是 ARGB颜色,可以设置透明度。
- 普通绘图函数会忽略颜色透明度,只能使用RGB颜色,没有抗锯齿功能,绘制图的图形边缘有明显的锯齿。
在带来更美观的图形的同时,透明度和反走样也会带来的更大的计算开销。同样大小的图形,高级绘图函数所需的时间可能会是普通绘图函数的1~3倍。一般的绘图耗时较小,影响不大,不会造成帧率的降低。如果是极其复杂的界面绘图,则可能会有些影响。
通常情况下,还是选择高级绘图函数来进行绘制,以此得到高质量的图形。现在的图形界面,基本都进行了反走样处理。
二、 基本图形的绘制
1. 图形样式设置
绘制图形前需要先设置好前景色、填充色、线宽和线型等样式相关的设置,因为这些设置只对之后的图形绘制生效。如果先绘制图形,再设置样式,那么已经绘制出的图形是不会因此而改变样式的。
1.1 设置颜色
可以分别通过 setbkcolor_f()、setcolor() 和 setfillcolor() 来设置背景色、前景色和填充色。
颜色设置对后续所有的绘图都有效,因此不需要频繁设置。 在绘图之前,如果不确定之前的颜色,此时便可以进行颜色设置。设置后就已经明确当前的颜色设置,如果后面连续的绘图都是同样的颜色,则可以不用再设置。
setbkcolor(WHITE); //设置背景色
setcolor(EGERGB(0x30, 0x80, 0xFF); //设置前景色
setfillcolor(EGEARGB(0x80, 0xB0, 0xB0, 0x20)); //设置填充颜色
示例程序
#include <graphics.h>
int main()
{
initgraph(600, 600, INIT_RENDERMANUAL);
setbkcolor(EGERGB(0xFF, 0xFF, 0xFF)); //设置背景色为纯白
ege_enable_aa(true); //开启抗锯齿
//填充椭圆
setfillcolor(EGEACOLOR(0xA0, BLUE));
ege_fillellipse(200, 100, 200, 200);
//填充矩形
setfillcolor(EGEARGB(0xFF, 0xFF, 0x20, 0xA0));
ege_fillrect(100, 200, 400, 200);
//直线
setcolor(EGEACOLOR(0xFF, RED));
ege_line(50, 50, 500, 500);
//多边形顶点数和顶点坐标
int numPoints = 3;
ege_point polyPoints[3] = { {560, 460}, {560, 100}, {100, 460} };
//填充多边形
setfillcolor(EGEARGB(0x50, 0x00, 0x20, 0xFF));
ege_fillpoly(numPoints, polyPoints);
getch();
closegraph();
return 0;
}
1.2 设置线宽
调用 setlinewidth() 可以设置线条宽度。(EGE19.01及之前的版本需要在setlinewidth()之后调用一次setcolor()才会生效)
函数声明
void setlinewidth(float width, PIMAGE pimg = NULL);// 设置当前线宽
示例程序
#include <graphics.h>
int main()
{
initgraph(600, 600, INIT_RENDERMANUAL);
setbkcolor(EGERGB(0xFF, 0xFF, 0xFF)); //设置背景色为纯白
ege_enable_aa(true); //开启抗锯齿
setcolor(EGEACOLOR(0xFF, BLACK)); //设置线条颜色
//绘制线宽为1的直线
setlinewidth(1);
ege_line(20, 20, 300, 200);
//绘制线宽为2的直线
setlinewidth(2);
ege_line(200, 300, 400, 40);
//绘制线宽为3的直线
setlinewidth(3);
ege_line(40, 400, 500, 400);
//绘制线宽为4的直线
setlinewidth(4);
ege_line(300, 300, 400, 300);
getch();
closegraph();
return 0;
}
2. 普通绘图函数与高级绘图函数
普通绘图函数与高级绘图函数的区别就是,普通绘图函数没有抗锯齿功能,并且忽略颜色透明度。这样绘制出来的图形都是不透明的,并且图形边缘有明显的锯齿感,图形很不美观。
高级绘图函数 在绘图时会根据ARGB颜色中的透明度与背景颜色按比例混合,可以有透明效果。并且可以开启抗锯齿,边缘变得平滑,绘图效果较好。
2.1 普通绘图函数的使用
详细内容请阅读:
EGE基础:基础绘图篇
EGE基础:光栅操作篇
普通绘图函数在使用前,需要设置好相应的填充颜色,线条颜色等等。即使不设置,也可以画出图形,只是图形颜色可能不符合。此外,还可以设置线宽等,还有光栅操作码等等设置。
普通绘图函数使用流程:
2.1.1 普通绘图函数示例:色光三原色图
色光三原色是红绿蓝三种色光,其余色光都是这三种色光按不同的比例线性叠加而成。
发光的物体所发出的光的颜色, 是将所有颜色光进行叠加,称为 加色模式 。而屏幕也是发光物体,所以屏幕每一个像素的颜色,是红绿蓝三个分量的简单线性叠加。
如:
#
F
F
0000
+
#
00
F
F
00
=
#
F
F
F
F
00
\text{如:}\mathrm{\#FF0000+\#00FF00=\#FFFF00}
如:#FF0000+#00FF00=#FFFF00 如果以原来的设置来进行绘图,那么会以覆盖的方式进行,是得不到混合的效果的,所以应该先用 setwritemode() 把绘图时像素的位操作改为位或,而不是直接赋值,这样就能使颜色叠加。
通过计算三个色圆中心位置,调用 fillellipse() 绘图即可。
完整代码如下:
#include <graphics.h>
#include <cmath>
double angleToRadian(double angle)
{
return angle * PI / 180.0;
}
int main() {
//定义窗口大小,以及窗口中心
int winWidth = 640, winHeight = 640;
int xCenter = winWidth / 2, yCenter = winHeight / 2;
initgraph(winWidth, winHeight, INIT_RENDERMANUAL);
setbkcolor(EGERGB(0x00, 0x00, 0x00));
setwritemode(R2_MERGEPEN); //光栅操作码:使用位或的方式计算目标像素
//定义红绿蓝三原色
color_t colors[3] = {
EGERGB(0xFF, 0x00, 0x00), //红
EGERGB(0x00, 0xFF, 0x00), //绿
EGERGB(0x00, 0x00, 0xff), //蓝
};
float circleRadius = 180;
float rotationRadius = circleRadius * sqrt(3) / 3;
float startAngle = -90;
for (int i = 0; i < 3; i++) {
float angle = startAngle - i * 360.0 / 3; //计算圆心角度, 因为y轴向下,逆时针是角度减小
float radian = angleToRadian(angle); //计算弧度
//计算圆中心坐标: x1 = x0 + r * cos(radian), y1 = y0 + r * sin(radian)
float cx = xCenter + rotationRadius * cos(radian);
float cy = yCenter + rotationRadius * sin(radian);
//设置边框和填充颜色
setcolor(colors[i]);
setfillcolor(colors[i]);
fillellipse(cx, cy, circleRadius, circleRadius);
}
getch();
closegraph();
return 0;
}
2.1.2 普通绘图函数示例:色料三原色图
色料三原色一般用于颜料,印刷品等,也叫印刷三原色(CMY),分别是青(Cyan)、品红(Magenta) 和 黄(Yellow) 三种颜色。
颜料本身不发出颜色光,所呈现的颜色是光源中的颜色光被颜料吸收后所剩余的部分,是减色模式。反光物体表现出来的颜色,与照射光的颜色和所吸收的光的颜色有关。
如黄颜料是颜料吸收了白光中的蓝色光,剩余的红光和绿光被反射出来,看到的就是黄色。所以如果色料三种颜色的颜料混合,那么三种色光都会被吸收,显现出来的就是黑色。
所以色料三原色的绘图是将相应的色光除去,因此只要先对应颜色值取反,再和底色位与即可。
通过将色光三原色示例代码中的光栅操作码改为R2_MASKNOTPEN,背景色改为纯白,再调整一下角度,即可得到色料三原色代码。
#include <graphics.h>
#include <cmath>
double angleToRadian(double angle)
{
return angle * PI / 180.0;
}
int main() {
//定义窗口大小,以及窗口中心
int winWidth = 640, winHeight = 640;
int xCenter = winWidth / 2, yCenter = winHeight / 2;
initgraph(winWidth, winHeight, INIT_RENDERMANUAL);
setbkcolor(EGERGB(0xFF, 0xFF, 0xFF));
//光栅操作码:使用先取反再位与的方式计算目标像素
setwritemode(R2_MASKNOTPEN);
//定义红绿蓝三原色
color_t colors[3] = {
EGERGB(0xFF, 0x00, 0x00), //红
EGERGB(0x00, 0xFF, 0x00), //绿
EGERGB(0x00, 0x00, 0xff), //蓝
};
float circleRadius = 180;
float rotationRadius = circleRadius * sqrt(3) / 3;
float startAngle = 90;
for (int i = 0; i < 3; i++) {
float angle = startAngle - i * 360.0 / 3; //计算圆心角度, 因为y轴向下,逆时针是角度减小
float radian = angleToRadian(angle); //计算弧度
//计算圆中心坐标: x1 = x0 + r * cos(radian), y1 = y0 + r * sin(radian)
float cx = xCenter + rotationRadius * cos(radian);
float cy = yCenter + rotationRadius * sin(radian);
//设置边框和填充颜色
setcolor(colors[i]);
setfillcolor(colors[i]);
fillellipse(cx, cy, circleRadius, circleRadius);
}
getch();
closegraph();
return 0;
}
2.1.3 普通绘图函数示例:Poly Code图标
图标由同一个平行四边形以60°为旋转角旋转6次而成。我们可以以中心为原点建立坐标系,然后计算出每个平行四边形的顶点坐标。
运行结果如图:
#include <graphics.h>
#include <cmath>
//角度转弧度
double angleToRadian(double angle)
{
return angle * PI / 180.0;
}
//自定义一个Point类,方便运算
typedef struct Point
{
float x, y;
Point(float x = 0.0f, float y = 0.0f) :x(x), y(y) {}
Point operator+ (const Point& point) { return Point(x + point.x, y + point.y); }
Point operator- (const Point& point) { return Point(x - point.x, y - point.y); }
Point operator* (const float factor) { return Point(x * factor, y * factor); }
Point operator/ (const float div) { return Point(x / div, y / div); }
};
int main() {
//定义窗口大小,以及窗口中心
int winWidth = 640, winHeight = 640;
int xCenter = winWidth / 2, yCenter = winHeight / 2;
initgraph(winWidth, winHeight, INIT_RENDERMANUAL);
setbkcolor(EGERGB(0xFF, 0xFF, 0xFF));
//设置边框线条不绘制
setlinestyle(NULL_LINE);
//暗色圆底
float circleRadius = 180;
setfillcolor(EGERGB(70, 60, 60));
fillellipse(xCenter, yCenter, circleRadius, circleRadius);
double lenOfSide = circleRadius / 2.0; //六边形边长取底圆半径的一半
float rotationAngle = 60;
double offsetAngle = rotationAngle / 2;
int numPoints = 4;
int pointsCoord[4 * 2];
color_t colors[6] = {
HSVtoRGB(0, 0.84, 0.92), HSVtoRGB(35, 0.87, 0.96), HSVtoRGB(55, 1.0, 1.0),
HSVtoRGB(100, 0.62, 0.74), HSVtoRGB(210, 0.65, 0.75), HSVtoRGB(300, 0.48, 0.62),
};
Point p[5];
//计算第一次需要利用的到的历史坐标点
p[0] = Point(lenOfSide * cos(angleToRadian(-rotationAngle - offsetAngle)), lenOfSide * sin(angleToRadian(-rotationAngle - offsetAngle)));
p[4] = Point(lenOfSide * cos(angleToRadian(-rotationAngle + offsetAngle)), lenOfSide * sin(angleToRadian(-rotationAngle + offsetAngle)));
p[3] = p[0] + (p[4] - p[0]) * 4.0f / 3.0f;
for (int i = 0; i < 6; i++) {
double angle = i * rotationAngle;
//直接取上次计算好的坐标。(利用到上次的P0,P3,P4)
Point lastP4 = p[4];
p[4] = lastP4 - p[0];
p[0] = lastP4;
p[1] = p[3];
//利用现有点计算P2, P3新坐标
p[2] = p[1] + (p[4] - p[0]) * 4.0 / 3;
p[3] = p[2] + (p[0] - p[1]);
//转换到窗口坐标系
for (int i = 0; i < numPoints; i++) {
pointsCoord[2 * i] = roundf(xCenter + p[i].x);
pointsCoord[2 * i + 1] = roundf(yCenter - p[i].y);
}
//设置颜色
setfillcolor(colors[i]);
fillpoly(numPoints, pointsCoord);
}
//恢复线条绘制
setlinestyle(SOLID_LINE);
getch();
closegraph();
return 0;
}
2.2 高级绘图函数的使用
详细内容请阅读: EGE基础:高级绘图篇
高级绘图函数是一系列命名带有 ege_ 前缀的绘图函数。
普通绘图函数的使用很简单,传入正确的参数调用即可。而高级绘图函数有抗锯齿的功能,这个是默认不开启的,需要进行设置。
高级函数使用流程
流程如下图所示,相比普通绘图函数仅仅多了一个抗锯齿的设置,并且抗锯齿设置并不是必需的,可以不设置,这时就如果普通绘图函数一样的使用方法。在创建窗口后设置一次即可将窗口的抗锯齿功能打开,不需要对窗口多次调用ege_enable_aa()。
但是有一个需要注意, 由于高级绘图函数使用的是ARGB颜色,所以当颜色的透明度为0时将看不到绘制出的图形。 使用前确保颜色的透明度不为0。
由于EGE19.01及之前的版本,将RGB颜色定义为透明度为0的ARGB颜色,所以直接设置为RGB颜色时,高级绘图函数绘制出的图形是看不到的,需要使用 EGEACOLOR() 为其设置透明度。或者使用 EGEARGB() 直接定义ARGB颜色。
setfillcolor(EGEACOLOR(0xFF, BLUE)); //设置ARGB填充颜色,利用EGEACOLOR设置RGB颜色透明度
setcolor(EGEARGB(0x80, 0xFF, 0x80, 0xA0)); //设置ARGB前景色
2.2.1 高级绘图函数使用示例:简单图形
以下为高级绘图函数的使用示例,可以看到,除了多了一个抗锯齿的设置 ege_enable_aa() 外,其它使用和普通绘图函数没有区别。
#include <graphics.h>
int main()
{
initgraph(500, 500, INIT_RENDERMANUAL);
setbkcolor(EGERGB(0xFF, 0xFF, 0xFF)); //设置背景色为纯白
//步骤一:开启抗锯齿(可不设置,默认为不开启)
ege_enable_aa(true);
//步骤二:绘制前设置好相应的ARGB颜色
//步骤三:调用高级绘图函数
//黄色三角形
setfillcolor(EGEARGB(0xFF, 0xFF, 0xFF, 0x00));
int numPoints = 3;
ege_point polyPoints[3] = { {100, 100}, {100, 400}, {400, 400} };
ege_fillpoly(numPoints, polyPoints);
//蓝色正方形左上角(100, 200),边长200
setfillcolor(EGEARGB(0xFF, 0x00, 0x80, 0xFF));
ege_fillrect(100, 200, 200, 200);
//品红色的半透明圆
setfillcolor(EGEARGB(0x80, 0xFF, 0x00, 0xFF));
ege_fillellipse(200, 100, 200, 200);
getch();
closegraph();
return 0;
}
2.2.2 高级绘图函数使用示例:Poly Code图标
我们将用普通绘图函数绘制的Poly Code图标改为用高级绘图函数绘制。
除了开启抗锯齿外,还需要将RGB颜色改为对应的ARGB颜色,透明度为255。
运行结果如图:
通过和普通绘图函数绘制出的Poly Code图标对比,可以看到高级绘图函数绘制的图形质量更高。
#include <graphics.h>
#include <cmath>
//角度转弧度
double angleToRadian(double angle)
{
return angle * PI / 180.0;
}
//自定义一个Point类,方便运算
typedef struct Point
{
float x, y;
Point(float x = 0.0f, float y = 0.0f) :x(x), y(y) {}
Point operator+ (const Point& point) { return Point(x + point.x, y + point.y); }
Point operator- (const Point& point) { return Point(x - point.x, y - point.y); }
Point operator* (const float factor) { return Point(x * factor, y * factor); }
Point operator/ (const float div) { return Point(x / div, y / div); }
};
int main() {
//定义窗口大小,以及窗口中心
int winWidth = 640, winHeight = 640;
int xCenter = winWidth / 2, yCenter = winHeight / 2;
initgraph(winWidth, winHeight, INIT_RENDERMANUAL);
ege_enable_aa(true);
setbkcolor(EGERGB(0xFF, 0xFF, 0xFF));
//设置边框线条不绘制
setlinestyle(NULL_LINE);
//暗色圆底
float circleRadius = 180;
setfillcolor(EGEARGB(255, 70, 60, 60));
ege_fillellipse(xCenter - circleRadius, yCenter - circleRadius, 2 * circleRadius, 2 * circleRadius);
double lenOfSide = circleRadius / 2.0; //六边形边长取底圆半径的一半
float rotationAngle = 60;
double offsetAngle = rotationAngle / 2;
int numPoints = 4;
ege_point pointsCoord[4];
color_t colors[6] = {
HSVtoRGB(0, 0.84, 0.92), HSVtoRGB(35, 0.87, 0.96), HSVtoRGB(55, 1.0, 1.0),
HSVtoRGB(100, 0.62, 0.74), HSVtoRGB(210, 0.65, 0.75), HSVtoRGB(300, 0.48, 0.62),
};
Point p[5];
//计算第一次需要利用的到的历史坐标点
p[0] = Point(lenOfSide * cos(angleToRadian(-rotationAngle - offsetAngle)), lenOfSide * sin(angleToRadian(-rotationAngle - offsetAngle)));
p[4] = Point(lenOfSide * cos(angleToRadian(-rotationAngle + offsetAngle)), lenOfSide * sin(angleToRadian(-rotationAngle + offsetAngle)));
p[3] = p[0] + (p[4] - p[0]) * 4.0f / 3.0f;
for (int i = 0; i < 6; i++) {
double angle = -i * rotationAngle;
//直接取上次计算好的坐标。(利用到上次的P0,P3,P4)
Point lastP4 = p[4];
p[4] = lastP4 - p[0];
p[0] = lastP4;
p[1] = p[3];
//利用现有点计算P2, P3新坐标
p[2] = p[1] + (p[4] - p[0]) * 4.0 / 3;
p[3] = p[2] + (p[0] - p[1]);
//转换成多边形的实际坐标
for (int i = 0; i < numPoints; i++) {
pointsCoord[i].x = xCenter + p[i].x;
pointsCoord[i].y = yCenter - p[i].y;
}
//设置颜色,绘制多边形
setfillcolor(EGEACOLOR(255, colors[i]));
ege_fillpoly(numPoints, pointsCoord);
}
getch();
closegraph();
return 0;
}
3. 抗锯齿绘图示例
设置启用抗锯齿后,高级绘图函数绘制图形时会进行抗锯齿处理,减弱图形边缘的锯齿感,得到更高质量的图形。
太极图
可以看到,高级绘图函数绘制出的图形的边缘看上去是十分光滑的,没有普通绘图函数那种严重的锯齿感。
#include <graphics.h>
#include <cmath>
//以(cx, cy)为中心的圆环,外圆半径为outerRad, 颜色为outerColor,内圆半径为innerRad, 颜色为innerColor
void annulus(float cx, float cy, float outerRad, color_t outerColor, float innerRad, color_t innerColor);
//以(cx, cy)为中心绘制半径为radius的角度为angle的太极图
void taichi(float cx, float cy, float radius, float angle);
int main() {
initgraph(640, 640, INIT_RENDERMANUAL);
setbkcolor(WHITE);
//开启抗锯齿,使圆更平滑
ege_enable_aa(true);
float angle = 0; //旋转角度
//绘制太极图
taichi(320, 320, 200, angle);
getch();
closegraph();
return 0;
}
//圆环
void annulus(float cx, float cy, float outerRad, color_t outerColor, float innerRad, color_t innerColor)
{
//绘制外圆
setfillcolor(outerColor);
ege_fillellipse(cx - outerRad, cy - outerRad, 2 * outerRad, 2 * outerRad);
//绘制内圆
setfillcolor(innerColor);
ege_fillellipse(cx - innerRad, cy - innerRad, 2 * innerRad, 2 * innerRad);
}
void taichi(float cx, float cy, float radius, float angle)
{
float left = cx - radius, top = cy - radius;
float width = 2 * radius, height = 2 * radius;
color_t colWhite = EGEACOLOR(0xFF, WHITE), colBlack = EGEACOLOR(0xFF, BLACK);
//白半圆
setfillcolor(colWhite);
ege_fillpie(left, top, width, height, angle + 90, 180);
//黑半圆
setfillcolor(colBlack);
ege_fillpie(left, top, width, height, angle - 90, 180);
//鱼眼中心偏移位置
float radian = (angle + 90) * PI / 180;
float dx = radius / 2 * cos(radian), dy = radius / 2 * sin(radian);
//黑鱼头部
annulus(cx + dx, cy + dy, radius / 2, colBlack, radius / 6, colWhite);
//白鱼头部
annulus(cx - dx, cy - dy, radius / 2, colWhite, radius / 6, colBlack);
//太极黑边框
setlinewidth(2);
setcolor(colBlack);
ege_ellipse(left, top, 2 * radius, 2 * radius);
}
4. 填充图形的边框
4.1 填充图形是否有边框
普通绘图函数 :除了 bar() 之外,其余填充图形的都带有边框。
高级绘图函数 :填充图形都无边框。
普通绘图函数 | 高级绘图函数 |
---|---|
带边框 | 不带边框 |
4.2 普通绘图函数边框设置
边框线宽可以通过 setlinewidth() 设置。边框颜色可以通过 setcolor() 设置。
(ege20.08之前的版本中,setlinewidth设置之后需要再调用一次setcolor才有效)
普通绘图函数的填充图形默认有边框,所有不仅要设置填充颜色,还需要设置前景色。
setfillcolor(); //设置填充颜色
setcolor(); //设置边框颜色
fillellipse(); //绘制带边框的填充圆
4.2.1 普通填充图形取消边框绘制
如果想取消普通图形的边框绘制,可以先使用 setlinestyle() 设置线型为 NULL_LINE ,再使用普通绘图函数绘制填充图形,这样填充图形的线条就不会被绘制出来。
但是这个设置会使得之后的所有线条都不会被绘制,所以可以再将线型设置为 SOLID_LINE,以恢复线框绘制。
setlinestyle() 是对所有线条绘制生效,如直线等都会受影响,所以设置为 NULL_LINE 后记得恢复为 SOLID_LINE 。
//设置不绘制线条,然后绘制填充图形
setllinestyle(NULL_LINE);
fillellipse(320, 320, 100, 100);
//恢复线条绘制
setllinestyle(SOLID_LINE);
4.2.2 高级绘图函数添加边框
高级绘图函数的填充图形是不包含边框的,所以绘制时只需要设置填充颜色即可。
如果需要边框,可以通过图形组合的方式来进行绘图,先画填充图形,再在同样的位置上绘制一个同样形状和大小的线框图形。
比如要绘制一个带边框的填充圆,可以按照如下顺序绘图:
- 先设置填充圆的绘图参数,并绘制填充圆
//设置填充颜色
setfillcolor(EGEACOLOR(0xFF, BLUE));
//绘制填充圆
ege_fillellipse(20, 20, 100, 100);
- 再设置线条相关参数,并绘制圆线框
//设置线条宽度和线条颜色
setlinewidth();
setcolor(EGEACOLOR(0xFF, BLACK));
//绘制圆线框
ege_ellipse(20, 20, 100, 100);
EGE专栏:EGE专栏
下一篇:EGE基础入门篇(七):组合图形