研究CCanvas类如何绘制透明的图形对象
内容简介
简介
在MetaTrader5中绘图很简单,你只需要知道一些细节就行。一个细节就是终端屏幕是如何设计的。跟准确的说,我们对图形在屏幕上的输出方式感兴趣。例如,图表能够在前景或是背景上显示。在屏幕上输出的颜色取决于显示的图表。某些图表对象可能在重叠或者交叉区域产生颜色变化。
在用CCanvas类直接绘图前,让我们分析下和颜色而处理相关的定义。例如,让我们搞清楚Alpha通道的意义。
在我看来,实现透明化是最重要的技术,能够让图像看起来生动。例如,通过使用平滑的色彩过渡和阴影,透明化能够用于让图像看起来更加吸引人。暗影增加了图形对象的维度,并在视觉上柔化物体边缘。
1. 透明(Alpha通道)
我们生活在一个三维世界里,并以三维视角观察我们周围的任何物体。我们习惯于看和感受到立体感。在三维世界我们可以理解哪个对象离我们更近。
一些对象可能是半透明的。例如,将一个干净的盛满半透明个液体的玻璃杯放在蓝色背景下。透过杯中的液体能看到蓝色的背景。背景的可见度取决于液体的透明度。
图. 1 空间维度的常识
透明在这个例子中不是虚拟和虚幻的。在这个例子中透明是显而易见的。
当图像在计算机显示器上显示是完全另一回事了 — 像素矩阵是二维的。例如,图像是通过高度和宽度的矩阵来显示的,但没有第三维深度要素。然而将一个像素放在另一个像素上面,就好比将黄色背景的像素放在半透明玻璃杯像素下面那样,是不可能做到的。屏幕上的任何三维图像和真实物体皆是错觉,都是通过使用色彩和阴影实现的。
让我们来看一个可以分割为两个图层的图像的例子:底层是一个而蓝色背景,顶层是不透明的液体。这就是在屏幕上看上去的样子:
图 2. 不透明玻璃杯
在最终的结果图上玻璃杯完全是不透明的。为了增加(转变为)透明效果,我们需要将图像上的所有颜色用ARGB色彩表示。
2. ARGB颜色表示
我并没有忘记玻璃杯的透明。这个问题将在第二部分详细讨论。
ARGB色彩用四字节uint型表示,分别代表的值为:alpha通道,红,绿,蓝。即,为了给RGB格式增加透明效果,增加一个代表透明度的额外的字节,alpha通道值。
图 3. ARG
alpha通道值从0(前景像素的颜色不改变背景颜色的显示)到255(背景像素的颜色完全替代前景像素的颜色)透明度的百分比如下计算:
换句话说,alpha通道的值越小透明度越高。如果我们知道目标透明度,alpha值能够如下计算出来:
函数ColorToARGB(颜色,alpha)用于将色彩变换为ARGB格式。
3. 绘制对象的方法
为了更好的了解颜色是被如何处理的。让我们来研究下两种图表下图形对象的设置方案:背景图和前景图。
3.1. 背景图
这个选项可以通过右键图表,然后选择下拉菜单中的“Properties...”并去到“Common”标签页中。
图 4. 背景图
终端中由4个图层组成的图表窗口。你可以在两端的图层(背景和前景)上绘图:
图. 5. 图表窗口的样式
在前景和背景中,根据创建时间一个绘制对象和另一个相互重叠。
也就是说,最早创建的对象处在最底层即“背景”,最后创建的对象处在最上层即“前景”。后出现的对象在上层。
图. 6. 对象的位置取决于创建时间
如果不重绘和其他图形重叠的区域,不是所有的对象都能完全重叠的。
下表总结了图形对象的特点,并解释了何为对象的重叠区域重绘。
ID | 对象 | 描述 | 同底层对象的重叠 |
---|---|---|---|
OBJ_VLINE | 垂直线 | 不重绘 | |
OBJ_HLINE | 水平线 | 不重绘 | |
OBJ_TREND | 趋势线 | 不重绘 | |
OBJ_TRENDBYANGLE | 带角的趋势线 | 不重绘 | |
OBJ_CYCLES | 周期线 | 不重绘 | |
OBJ_ARROWED_LINE | 箭头线 | 不重绘 | |
OBJ_CHANNEL | 等距离通道 | 不重绘 | |
OBJ_STDDEVCHANNEL | 标准差通道 | 不重绘 | |
OBJ_REGRESSION | 线性回归通道 | 不重绘 | |
OBJ_PITCHFORK | 安德鲁分叉线 | 不重绘 | |
OBJ_GANNLINE | 江恩线 | 不重绘 | |
OBJ_GANNFAN | 江恩扇形 | 不重绘 | |
OBJ_GANNGRID | 江恩网格 | 不重绘 | |
OBJ_FIBO | 斐波纳契回撤 | 不重绘 | |
OBJ_FIBOTIMES | 斐波纳契时间区间 | 不重绘 | |
OBJ_FIBOFAN | 斐波纳契扇形 | 不重绘 | |
OBJ_FIBOARC | 斐波纳契弧线 | 不重绘 | |
OBJ_FIBOCHANNEL | 斐波那契通道 | 不重绘 | |
OBJ_EXPANSION | 斐波那契扩展 | 不重绘 | |
OBJ_ELLIOTWAVE5 | 艾略特波浪 - 5 | 不重绘 | |
OBJ_ELLIOTWAVE3 | 艾略特波浪 - 3 | 不重绘 | |
OBJ_RECTANGLE | 矩形 | 如果不填充无需重绘, 若填充则重绘 | |
OBJ_TRIANGLE | Triangle | 如果不填充无需重绘, 若填充则重绘 | |
OBJ_ELLIPSE | 椭圆形 | 如果不填充无需重绘, 若填充则重绘 | |
OBJ_ARROW_THUMB_UP | 拇指向上 | 不重绘 | |
OBJ_ARROW_THUMB_DOWN | 拇指向下 | 不重绘 | |
OBJ_ARROW_UP | 向上的箭头 | 不重绘 | |
OBJ_ARROW_DOWN | Array Down | 不重绘 | |
OBJ_ARROW_STOP | 停止 | 不重绘 | |
OBJ_ARROW_CHECK | 检查标志 | 不重绘 | |
OBJ_ARROW_LEFT_PRICE | 左侧价格标签 | 不重绘 | |
OBJ_ARROW_RIGHT_PRICE | 右侧价格标签 | 不重绘 | |
OBJ_ARROW_BUY | 买入标记 | 不重绘 | |
OBJ_ARROW_SELL | Sell mark | 不重绘 | |
OBJ_ARROW | 箭头对象 | 不重绘 | |
OBJ_TEXT | 文本对象 | 不重绘 | |
OBJ_LABEL | 文本标签对象 | 不重绘 | |
OBJ_BUTTON | 按钮对象 | 不重绘 | |
OBJ_CHART | 图表对象 | 不重绘 | |
OBJ_BITMAP | 位图对象 | 不重绘 | |
OBJ_BITMAP_LABEL | 位图标签对象 | 不重绘 | |
OBJ_EDIT | 标记对象 | 不重绘 | |
OBJ_EVENT | 对应经济数据日历事件的事件对象 | 不重绘 | |
OBJ_RECTANGLE_LABEL | 用于创建和设计自定义图形对象的矩形标签对象 | 不重绘 |
表1 图形对象的重叠与透明
让我们看看类型为OBJ_RECTANGLE (矩形)的三个对象,并讨论对象重叠区域重绘的算法(文件xor.mq5)。
脚本(文件xor.mq5)设置白色背景颜色 (0xFFFFFF),并绘制填充蓝色 (0x0000FF)的矩形No1和No2及红色 (0xFF0000) 矩形。
图. 7. 重绘。背景图
我们获得两个重叠区域,颜色发生了变化:
- 1号区域– 最终颜色(0x000000) 完全透明,因此在此我们看到没有变化的背景和图像。
- 2号区域 – 最终颜色(0x00FF00)。
当图形对象如矩形重叠时,用算法Bitwise OR对其进行重绘。
图. 6 下面显示一个此两区域重绘的例子:
文字表示 | 整形表示 | 二进制表示 | 注意 |
---|---|---|---|
C’0,0,255’ | 0x0000FF | 0000 0000 0000 0000 1111 1111 | 蓝色 |
XOR | |||
C’0,0,225’ | 0x0000FF | 0000 0000 0000 0000 1111 1111 | 蓝色 |
= | |||
C’0,0,0’ | 0x000000 | 0000 0000 0000 0000 0000 0000 | 透明Transparent |
XOR | |||
C’255,255,255’ | 0xFFFFFF | 1111 1111 1111 1111 1111 1111 | 白色(背景) |
= | |||
C’255,255,255’ | 0xFFFFFF | 1111 1111 1111 1111 1111 1111 | 白色 |
表 2. Blue + Blue + White 的 比特位OR
文字表示 | 整形表示 | 二进制表示 | 注意 |
---|---|---|---|
C’0,0,255’ | 0x0000FF | 0000 0000 0000 0000 1111 1111 | 蓝色 |
XOR | |||
C’255,0,0’ | 0xFF0000 | 1111 1111 0000 0000 0000 0000 | 红色 |
= | |||
С’255,0,255’ | 0xFF00FF | 1111 1111 0000 0000 1111 1111 | |
XOR | |||
C’255,255,255’ | 0xFFFFFF | 1111 1111 1111 1111 1111 1111 | 白色(背景) |
= | |||
С’0,255,0’ | 0x00FF00 | 0000 0000 1111 1111 0000 0000 |
表 3. Bitwise OR for Blue + Red + White
3.2. 前景图
当“图表的前景”参数为on,图表窗口的图层的组织有别与背景图表:
图. 8. 图表窗口方案。顶层图
当“图表的前景”参数为on,两个绘制图层“前景”和“背景”图层汇聚成一个公共图层。这个图层在K线图和网格线下面。
3.3. 重绘“顶层图”
如图. 7所示,重叠对象的重绘算法(文件 xor.mq5)。
脚本(文件xor.mq5)设置白色背景颜色 (0xFFFFFF),并绘制填充蓝色 (0x0000FF)的矩形No1和No2及红色 (0xFF0000) 矩形。
Fig. 9. 重绘。前景图
如果我们比较一下图7和图8就会发现重叠区域的重绘结果是一样的。
4. 混合色
如上所述,屏幕上的透明效果是幻觉。颜色操作。为了模拟图2,我们只需要知道如何在屏幕上将一个颜色显示为透明。需要计算出此像素的最终颜色。
假设要在白色背景(“Black On White”色彩模板的图表背景)上绘制alpha通道为128的红色。在ARGB格式下红色为0x80FF0000。要计算最终颜色,我们要计算每一个通道(红,绿,蓝)的颜色。
这里是一个计算带alpha通道的效果色的公式,归一化后:
此处:
- result 是色彩通道强度值。如果值比255大,返回255.
- background 是背景颜色通道值。
- foreground 是重叠图像的色彩通道值。
- alpha 是归一化后的alpha值。
根据公式1.3计算效果色:
Alpha 通道 | alpha通道,归一化 | R | G | B | 注意 |
---|---|---|---|---|---|
255 | 255 | 255 | 白色 | ||
128 | 0,5 | 255 | 0 | 0 | alpha为128的红色 |
255*(1-0.5)+255*0.5=255 | 255*(1-0.5)+0*0.5=127 | 255*(1-0.5)+0*0.5=127 |
表 4. 用公式1.3的计算结果
屏幕上的最终颜色如下:
图. 10. 效果色
4.1. 颜色处理方法ENUM_COLOR_FORMAT
当创建画布时你能指定三种处理颜色的方法之一 (ENUM_COLOR_FORMAT):
ID | 描述 |
---|---|
COLOR_FORMAT_XRGB_NOALPHA | Alpha 元素被忽略了 |
COLOR_FORMAT_ARGB_RAW | 色彩组件不是由终端处理的(它们应该由用户指定) |
COLOR_FORMAT_ARGB_NORMALIZE | 色彩组建由终端处理 |
表 5. 用于创建画布的色彩处理方法
COLOR_FORMAT_ARGB_NORMALIZE考虑了RGB组件的正确重叠方式,能提供一种更加漂亮的图形。当使用alpha通道的色彩时,通过公式1.3计算结果色。
COLOR_FORMAT_ARGB_RAW 不控制RGB色彩组件的重叠, 因此相比COLOR_FORMAT_ARGB_NORMALIZE,COLOR_FORMAT_ARGB_RAW是较快的处理方法。
这里是计算带alpha通道的颜色效果色的公式,使用COLOR_FORMAT_ARGB_RAW方法:
此处:
- result 是色彩通道强度值。如果值比255大,返回255.
- background 是背景颜色通道值。
- foreground 是重叠图像的色彩通道值。
- alpha 是归一化后的alpha值。
5. 透明效果
现在我们可以着手实现透明效果了。
让我们绘制一些填充矩形(脚本 "xor.mq5")。为了揭示色彩处理方法的不同,在图表的顶层创建三个互不重叠的水平画布。
第一个用COLOR_FORMAT_XRGB_NOALPHA处理,第二个用COLOR_FORMAT_ARGB_RAW ,第三个用COLOR_FORMAT_ARGB_NORMALIZE。然后我们逐渐将透明从255(不透明)改到0(完全透明)。调用脚本 "Illusion.mq5"。
这个短片显示脚本 "Illusion.mq5" 如何运作:
图. 11. 脚本illusion.mq5的运作方式
5.1. 创建“Illusion.mq5”脚本
新增或修改的代码突出显示。
空白的脚本模板:
//+------------------------------------------------------------------+ //| Illusion.mq5 | //| Copyright © 2015, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2015, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.0" //+------------------------------------------------------------------+ //| 脚本程序start函数 | //+------------------------------------------------------------------+ void OnStart() { //--- }
添加脚本描述,脚本开始时的输入参数,以及包含用于绘图的CCanvas类。
#property version "1.0" #property description "The illusion of transparency" //--- 加载脚本时显示输入参数窗口 #property script_show_inputs #include <Canvas\Canvas.mqh>
本脚本需要一些参数:图表高度和宽度,画布的高度和宽度,以及绘制画布坐标时的辅助变量:
#include <Canvas\Canvas.mqh> //+------------------------------------------------------------------+ //| inputs | //+------------------------------------------------------------------+ input color colr=clrRed; input color clr_Circle=clrBlue; //--- 图表高度和宽度变量。 int ChartWidth=-1; int ChartHeight=-1; //--- uchar alpha=0; //控制色彩透明度的alpha通道值 int can_width,can_height; //画布的高度和宽度变量。 int can_x1,can_y1,can_x2,can_y2,can_y3,can_x3; //坐标我们使用标准函数接收图表的宽度和高度:
//+------------------------------------------------------------------+ //| 脚本程序start函数 | //+------------------------------------------------------------------+ void OnStart() { //--- } //+------------------------------------------------------------------+ //| Chart property width | //+------------------------------------------------------------------+ int ChartWidthInPixels(const long chart_ID=0) { //--- 定义获取属性的变量 long result=-1; //--- 重置报错信息 ResetLastError(); //--- 接收属性变量 if(!ChartGetInteger(chart_ID,CHART_WIDTH_IN_PIXELS,0,result)) { //--- 在智能交易日志中显示报错信息 Print(__FUNCTION__+", Error Code = ",GetLastError()); } //--- 返回图表属性值 return((int)result); } //+------------------------------------------------------------------+ //| Chart property height | //+------------------------------------------------------------------+ int ChartHeightInPixelsGet(const long chart_ID=0,const int sub_window=0) { //--- 定义获取属性的变量 long result=-1; //--- 重置报错信息 ResetLastError(); //--- 接收属性变量 if(!ChartGetInteger(chart_ID,CHART_HEIGHT_IN_PIXELS,sub_window,result)) { //--- 在智能交易日志中显示报错信息 Print(__FUNCTION__+", Error Code = ",GetLastError()); } //--- 返回图表属性值 return((int)result); }
直接进入OnStart()。
为了说明图12 显示的图表画布图层,以及画布坐标辅助变量:
图. 12. 图表坐标
让我们找到图表的高和宽并计算画布坐标的辅助变量。
void OnStart() { //--- 图表高度和宽度 ChartWidth=ChartWidthInPixels(); ChartHeight=ChartHeightInPixelsGet()-50; //--- can_width=ChartWidth/3; can_height=ChartHeight; can_x1=0; can_y1=0; can_x2=can_width; can_y2=0; can_x3=can_width*2; can_y3=0; }
已经计算了画布的宽和高以及坐标辅助变量,我们可以开始绘图了。
接下来让我们将OnStart()函数的void类型改为int型,并且在第一个画布上绘制一个填充矩形,一个画布色彩处理方法的名称文本和一个填充圆形:
int OnStart() { //--- 图表高度和宽度 ChartWidth=ChartWidthInPixels(); ChartHeight=ChartHeightInPixelsGet()-50; //--- can_width=ChartWidth/3; can_height=ChartHeight; can_x1=0; can_y1=0; can_x2=can_width; can_y2=0; can_x3=can_width*2; can_y3=0; //--- 创建画布COLOR_FORMAT_XRGB_NOALPHA CCanvas canvas_XRGB_NOALPHA,canvas_ARGB_RAW,canvas_XARGB_NORMALIZE; if(!canvas_XRGB_NOALPHA.CreateBitmapLabel("canvas_XRGB_NOALPHA",can_x1,can_y1,can_width-1,can_height,COLOR_FORMAT_XRGB_NOALPHA)) { Print("Error creating canvas: ",GetLastError()); return(-1); } canvas_XRGB_NOALPHA.Erase(ColorToARGB(colr,alpha)); canvas_XRGB_NOALPHA.TextOut((can_width)/2,can_height/2,"canvas_XRGB_NOALPHA",ColorToARGB(clrBlue,255),TA_CENTER|TA_VCENTER); canvas_XRGB_NOALPHA.FillCircle((can_width)/2,can_height/2+50,25,ColorToARGB(clr_Circle,255)); canvas_XRGB_NOALPHA.Update(); return(0); }
更多详细的内容在文后代码部分。
canvas_XRGB_NOALPHA.CreateBitmapLabel("canvas_XRGB_NOALPHA",can_x1,can_y1,can_width-1,can_height,COLOR_FORMAT_XRGB_NOALPHA)
canvas_XRGB_NOALPHA.CreateBitmapLabel - 创建一个图表对象的图形资源。
第一个画布的颜色处理方法为COLOR_FORMAT_XRGB_NOALPHA - alpha元素被忽略。
canvas_XRGB_NOALPHA.Erase(ColorToARGB(colr,alpha));
用带alpha透明元素的ARGB颜色填充整个画布。
画布的填充将忽略alpha通道,因为这里使用COLOR_FORMAT_XRGB_NOALPHA方法来处理颜色。
canvas_XRGB_NOALPHA.TextOut((can_width)/2,can_height/2,"canvas_XRGB_NOALPHA",ColorToARGB(clrBlue,255),TA_CENTER|TA_VCENTER);
文本输出 - 画布的图像处理类型。文本的颜色为ARGB格式,alpha通道值为255,也就是说文本的颜色完全不透明。
文本水平(TA_CENTER) 以及并相对于矩形的中心垂直 (TA_VCENTER) 显示。
canvas_XRGB_NOALPHA.FillCircle((can_width)/2,can_height/2+50,25,ColorToARGB(clr_Circle,255));
绘制实心圆。我们将用填充画布的颜色 (canvas_XRGB_NOALPHA.Erase(ColorToARGB(colr, alpha));)绘制它。
用于显示绘制在画布上的一个形状(或者区域/点)完全覆盖底层图像。也就是说,这里没有重绘画布,因为最近一次绘图完全是覆盖底层区域。
canvas_XRGB_NOALPHA.Update();
为了显示屏幕上的所有已绘制对象我们要刷新屏幕。
另两个画布的绘制也类似: 显示模式为COLOR_FORMAT_ARGB_RAW以及COLOR_FORMAT_ARGB_NORMALIZE的画布:
canvas_XRGB_NOALPHA.Update(); //--- 创建画布 COLOR_FORMAT_ARGB_RAW if(!canvas_ARGB_RAW.CreateBitmapLabel("canvas_ARGB_RAW",can_x2,can_y2,can_width-1,can_height,COLOR_FORMAT_ARGB_RAW)) { Print("Error creating canvas: ",GetLastError()); return(-1); } canvas_ARGB_RAW.Erase(ColorToARGB(colr,alpha)); //clrNONE,0)); canvas_ARGB_RAW.TextOut((can_width)/2,can_height/2,"canvas_ARGB_RAW",ColorToARGB(clrBlue,255),TA_CENTER|TA_VCENTER); canvas_ARGB_RAW.FillCircle((can_width)/2,can_height/2+50,25,ColorToARGB(clr_Circle,255)); canvas_ARGB_RAW.Update(); //--- 创建画布 COLOR_FORMAT_ARGB_NORMALIZE if(!canvas_XARGB_NORMALIZE.CreateBitmapLabel("canvas_XARGB_NORMALIZE",can_x3,can_y3,can_width-1,can_height,COLOR_FORMAT_ARGB_NORMALIZE)) { Print("Error creating canvas: ",GetLastError()); return(-1); } canvas_XARGB_NORMALIZE.Erase(ColorToARGB(colr,alpha)); canvas_XARGB_NORMALIZE.TextOut((can_width)/2,can_height/2,"canvas_XARGB_NORMALIZE",ColorToARGB(clrBlue,255),TA_CENTER|TA_VCENTER); canvas_XARGB_NORMALIZE.FillCircle((can_width)/2,can_height/2+50,25,ColorToARGB(clr_Circle,255)); canvas_XARGB_NORMALIZE.Update(); return(0); }
画布及图形对象被绘制。
现在让我们添加循环来改变整张画布的透明度:
canvas_XARGB_NORMALIZE.FillCircle((can_width)/2,can_height/2+50,25,ColorToARGB(clr_Circle,255)); canvas_XARGB_NORMALIZE.Update(); //--- 透明度从255到0 uchar transparent; for(transparent=255;transparent>0;transparent--) { canvas_XRGB_NOALPHA.TransparentLevelSet(transparent); canvas_XRGB_NOALPHA.Update(); canvas_ARGB_RAW.TransparentLevelSet(transparent); canvas_ARGB_RAW.Update(); canvas_XARGB_NORMALIZE.TransparentLevelSet(transparent); canvas_XARGB_NORMALIZE.Update(); Sleep(50); } canvas_XRGB_NOALPHA.TransparentLevelSet(transparent); canvas_XRGB_NOALPHA.Update(); canvas_ARGB_RAW.TransparentLevelSet(transparent); canvas_ARGB_RAW.Update(); canvas_XARGB_NORMALIZE.TransparentLevelSet(transparent); canvas_XARGB_NORMALIZE.Update(); Sleep(6000); return(0); }
使用这行代码来改变所有画布的透明度:
.TransparentLevelSet(transparent)
在绘制的最后我们要进行清理,例如移除图形资源。
因为我们已经创建了图表对象的图形资源 (用方法 CreateBitmapLabel),让我们使用Destroy() 方法来移除它们,同时删除图表对象(Bitmap Label):
canvas_XARGB_NORMALIZE.Update(); Sleep(6000); //--- 完成 canvas_XRGB_NOALPHA.Destroy(); canvas_ARGB_RAW.Destroy(); canvas_XARGB_NORMALIZE.Destroy(); return(0); }
平滑的改变图像透明度的脚本能够正常运行。
如果你先在白色背景下运行本脚本,然后在到黑色背景下运行,那么你能够更好的发现COLOR_FORMAT_ARGB_RAW 和 COLOR_FORMAT_ARGB_NORMALIZE 模式之间的区别。
总结
本文涵盖了处理颜色的基本概念。学到了如何在图表窗口中绘制图形。本文还讨论了标准类库中CCanvas类的基本方法以及用ARGB格式表示颜色的透明度。
这仅仅是基础,在MetaTrader 5终端中还存在着大量的创建各种图形效果的可能。本文讨论透明度:其实部分透明能够让图形对象的边缘看起来更具吸引力。因为显示器的二维属性,图表的透明效果其实是像素处理后的幻觉。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/1341