# 研究 CCanvas 类。抗锯齿和阴影

15 九月 2016, 10:49
0
1 256

### 1. 坐标和画布

PixelSetAA double -
LineAA int -
PolylineAA int -
PolygonAA int -
TriangleAA int -
CircleAA int double

### 2. 抗锯齿算法

PixelSetAA PixelSetAA
LineAA PixelSetAA
PolylineAA LineAA -> PixelSetAA
PolygonAA LineAA -> PixelSetAA
TriangleAA LineAA -> PixelSetAA
CircleAA PixelSetAA

```//+------------------------------------------------------------------+
//| 绘制抗锯齿像素                                                    |
//+------------------------------------------------------------------+
void CCanvas::PixelSetAA(const double x,const double y,const uint clr)
{

```

```void CCanvas::PixelSetAA(const double x,const double y,const uint clr)
{
static double rr[4];
static int    xx[4];
static int    yy[4];

```

```static int    yy[4];
//--- 初步计算
int    ix=(int)MathRound(x);
int    iy=(int)MathRound(y);

```

```void OnStart()
{
Print("MathRound(3.2)=",DoubleToString(MathRound(3.2),8),"; (int)MathRound(3.2)=",IntegerToString((int)MathRound(3.2)));
Print("MathRound(3.5)=",DoubleToString(MathRound(3.5),8),"; (int)MathRound(3.5)=",IntegerToString((int)MathRound(3.5)));
Print("MathRound(3.8)=",DoubleToString(MathRound(3.8),8),"; (int)MathRound(3.8)=",IntegerToString((int)MathRound(3.8)));
}
//+------------------------------------------------------------------+

```

```MathRound(3.8)=4.00000000; (int)MathRound(3.8)=4
MathRound(3.5)=4.00000000; (int)MathRound(3.5)=4
MathRound(3.2)=3.00000000; (int)MathRound(3.2)=3

```

```int    iy=(int)MathRound(y);
double rrr=0;
double k;
double dx=x-ix;
double dy=y-iy;

```

```double dy=y-iy;
uchar  a,r,g,b;
uint   c;
//--- 对于抗锯齿没必要
if(dx==0.0 && dy==0.0)
{
PixelSet(ix,iy,clr);
return;
}

```

```PixelSet(ix,iy,clr);
return;
}
//--- 准备像素数组
xx[0]=xx[2]=ix;
yy[0]=yy[1]=iy;
if(dx<0.0)
xx[1]=xx[3]=ix-1;
if(dx==0.0)
xx[1]=xx[3]=ix;
if(dx>0.0)
xx[1]=xx[3]=ix+1;
if(dy<0.0)
yy[2]=yy[2]=iy-1;
if(dy==0.0)
yy[2]=yy[2]=iy;
if(dy>0.0)
yy[2]=yy[2]=iy+1;

```

```yy[2]=yy[2]=iy+1;
//--- 计算半径及其平方和
for(int i=0;i<4;i++)
{
dx=xx[i]-x;
dy=yy[i]-y;
rr[i]=1/(dx*dx+dy*dy);
rrr+=rr[i];
}

```

```rrr+=rr[i];
}
//--- 绘制像素
for(int i=0;i<4;i++)
{
k=rr[i]/rrr;
c=PixelGet(xx[i],yy[i]);
a=(uchar)(k*GETRGBA(clr)+(1-k)*GETRGBA(c));
r=(uchar)(k*GETRGBR(clr)+(1-k)*GETRGBR(c));
g=(uchar)(k*GETRGBG(clr)+(1-k)*GETRGBG(c));
b=(uchar)(k*GETRGBB(clr)+(1-k)*GETRGBB(c));
PixelSet(xx[i],yy[i],ARGB(a,r,g,b));
}

```

### 3. 对象阴影

3.1. 阴影类型

"光环" 阴影也许可以设置光环宽度。"外部对角线" 阴影也许可以设置阴影透视的角度。这两种类型的阴影均可选择设置颜色。

3.2. 获取正态分布

```//+------------------------------------------------------------------+
//| 获取积分权重数组                                                  |
//+------------------------------------------------------------------+
bool GetQuadratureWeights(const double mu0,const int n,double &w[])
{
CAlglib alglib;            // 类 CAlglib 的静态成员
double      alp[];         // α 系数数组
double      bet[];         // β 系数数组
ArrayResize(alp,n);
ArrayResize(bet,n);
ArrayInitialize(alp,1.0);  // 初始化 α 数组数值
ArrayInitialize(bet,1.0);  // 初始化 β 数组数值

double      out_x[];
int         inf=0;
//| 错误代码:                                                        |
//|                 * -3    内部特征问题求解器尚未                    |
//|                         融合                                     |
//|                 * -2    Beta[i]<=0                               |
//|                 * -1    传递的 N 不正确                           |
//|                 *  1    OK                                       |
alglib.GQGenerateRec(alp,bet,mu0,n,inf,out_x,w);
if(inf!=1)
{
Print("调用 CGaussQ::GQGenerateRec 错误");
return(false);
}
return(true);
}

```

3.3. 资源

### 4. 高斯模糊算法的示例

```#property script_show_inputs
#include <Canvas\Canvas.mqh>
#include <Math\Alglib\alglib.mqh>

```

```//--- 输入
input uint  radius=4;               // 模糊半径
input color clrShadow=clrBlack;     // 阴影颜色
input uchar ShadowTransparence=160; // 透明度
input int   ShadowShift=3;          // 阴影偏移
input color clrDraw=clrBlue;        // 阴影颜色
input uchar DrawwTransparence=255;  // 透明度
//---

```

```//--- 创建画布
CCanvas CanvasDraw;
ChartHeight,COLOR_FORMAT_ARGB_NORMALIZE))
{
Print("创建画布错误: ",GetLastError());
return;
}
if(!CanvasDraw.CreateBitmapLabel("DrawLayer",0,0,ChartWidth
,ChartHeight,COLOR_FORMAT_ARGB_NORMALIZE))
{
Print("创建画布错误: ",GetLastError());
return;
}

```

```//--- 在画布上绘图

CanvasDraw.Erase(ColorToARGB(clrNONE,0));
CanvasDraw.Update();

```

```//+------------------------------------------------------------------+
//| 从图形资源里读取数据                                              |
//+------------------------------------------------------------------+
ResetLastError();
{
Print("从图形资源里读取数据错误 ",GetLastError());
Print("尝试第二次");
//--- 尝试第二次: 现在图片宽度和长度已知
ResetLastError();
{
Print("从图形资源里读取数据错误 ",GetLastError());
return;
}
}

```

```//+------------------------------------------------------------------+
//| 图片分解为组件 r, g, b                                            |
//+------------------------------------------------------------------+
...
return;

for(int i=0;i<size;i++)
{
clr_temp=res_data[i];
a_data[i]=GETRGBA(clr_temp);
r_data[i]=GETRGBR(clr_temp);
g_data[i]=GETRGBG(clr_temp);
b_data[i]=GETRGBB(clr_temp);
}

```

```//+------------------------------------------------------------------+
//| 水平模糊 (轴 X)                                                   |
//+------------------------------------------------------------------+
uint XY;             // 数组中的像素坐标
double   a_temp=0.0,r_temp=0.0,g_temp=0.0,b_temp=0.0;
int      coef=0;
for(uint Y=0;Y<res_height;Y++)                  // 圈定图像宽度
{
{
XY=Y*res_width+X;
a_temp=0.0; r_temp=0.0; g_temp=0.0; b_temp=0.0;
coef=0;
for(int i=-1*j;i<j+1;i=i+1)
{
a_temp+=a_data[XY+i]*weights[coef];
r_temp+=r_data[XY+i]*weights[coef];
g_temp+=g_data[XY+i]*weights[coef];
b_temp+=b_data[XY+i]*weights[coef];
coef++;
}
a_data[XY]=(uchar)MathRound(a_temp);
r_data[XY]=(uchar)MathRound(r_temp);
g_data[XY]=(uchar)MathRound(g_temp);
b_data[XY]=(uchar)MathRound(b_temp);
}
//--- 删除左侧工件
{
XY=Y*res_width+x;
}
//--- 删除右侧工件
{
XY=Y*res_width+x;
}
}

```

```for(uint Y=0;Y<res_height;Y++)                  // 圈定图像宽度
{
{
...
}
}

```

```for(uint X=radius;X<res_width-radius;X++)    // 圈定图像高度
{
XY=Y*res_width+X;
a_temp=0.0; r_temp=0.0; g_temp=0.0; b_temp=0.0;
coef=0;
for(int i=-1*j;i<j+1;i=i+1)
{
a_temp+=a_data[XY+i]*weights[coef];
r_temp+=r_data[XY+i]*weights[coef];
g_temp+=g_data[XY+i]*weights[coef];
b_temp+=b_data[XY+i]*weights[coef];
coef++;
}
a_data[XY]=(uchar)MathRound(a_temp);
r_data[XY]=(uchar)MathRound(r_temp);
g_data[XY]=(uchar)MathRound(g_temp);
b_data[XY]=(uchar)MathRound(b_temp);
}

```

```//--- 删除左侧工件
{
XY=Y*res_width+x;
}
//--- 删除右侧工件
{
XY=Y*res_width+x;
}

```

```//---
for(int i=0;i<size;i++)
{
clr_temp=ARGB(a1_data[i],r1_data[i],g1_data[i],b1_data[i]);
res_data[i]=clr_temp;
}
for(uint X=0;X<res_width;X++)
{
{
XY=Y*res_width+X;
}
}
CanvasDraw.Update();
Sleep(21000);

```

### 5. 用于绘制阴影的类

LineVertical 绘制带阴影的垂直线
LineHorizontal 绘制带阴影的水平线
Line 随意绘制带阴影的线条
Polyline 绘制带阴影的折线
Polygon 绘制带阴影的多边形
Rectangle 绘制带阴影的长方形
Circle 绘制带阴影的圆形
FillRectangle 绘制带阴影的实心长方形
FillTriangle 绘制带阴影的实心三角形
FillPolygon 绘制带阴影的实心多边形
FillCircle 绘制带阴影的实心圆形
FillEllipse 绘制带阴影的实心椭圆型
Fill 填充阴影区域
TextOut 显示带阴影文本

CGauss 的一般思路是创建两块画布。底层画布将执行绘制阴影的功能, 上层画布将作为工作层用来绘制图形说明。两块画布的尺寸等于图表的大小。其中底层画布在创建时, 按照阴影位移大小在水平和垂直方向平移 — 这种计算阴影绘制坐标的方式变得十分容易。

### 结论

blur.mq5 (5.93 KB)
pixelsetaa.mq5 (3.99 KB)
gauss.mqh (24.33 KB)