用两种RGB颜色对矩形进行线性渐变填充时,需要合适的算法计算各点的颜色,这样才不会出现中间过渡色,或者出现渐变填充不完整。而在增加从任意角度进行渐变后,情况似乎变得更加复杂。
又比如这样:
这些都是色彩计算错误引起的。现在来看看正确的渐变图,其中颜色和渐变角度和上面的一样:
首先说明渐变角度,在本例中是以垂直向上为0度,顺时针增加到360度。
如45度偏转渐变如下:
下边贴出渐变色计算过程代码,例子是在VC2010下做的,颜色的表示借用了windows的COLORREF:
/**
* 计算线性渐变色
* @param crBegin 前景色
* @param crEnd 渐变色
* @param degree 渐变角度,以垂直向上为0度,顺时针增加到360度
* @param width 矩形区域的宽度
* @param height 矩形区域的高度
* @param x 横坐标,在矩形区域中起点为0,向右递增
* @param y 纵坐标,在矩形区域中起点为0,向下递增
* @return 计算后的线性渐变色
*/
COLORREF CalcLinearGradientColor(COLORREF crBegin, COLORREF crEnd, int degree, int width, int height, int x, int y)
{
/// 渐变高宽值
float fwdith = (float)width;
float fheight = (float)height;
/// 坐标值
int posx = x;
int posy = y;
int gradientDegree = 0; ///< 渐变角度,以垂直向上为0度,顺时针增加到360度
/// 对不同角度使用了对称变换和平移变换。颜色出现分割是角度不对;颜色渐变方向错误是坐标不对
if( degree>=0 && degree<90 )
gradientDegree = degree;
else if( degree>90 && degree<=180 )
gradientDegree = degree-90;
else if( degree>180 && degree<=270 )
gradientDegree = degree-180;
else // if( degree>270 && degree<=360 )
gradientDegree = degree-270;
/// 当前坐标值加1;若坐标值=0,则在边界上会出现纯色
//posx += 1;
//posy += 1;
/// 角度转为弧度
float radian = gradientDegree*YY_PI/180;
/// 根据渐变角度重新计算高宽,以便能容纳下整个图像。
float newWdith = fwdith*(cos(radian)) + fheight*(sin(radian));
float newHeight = fwdith*(sin(radian)) + fheight*(cos(radian));
/// 将当前坐标点转换为新的矩形下的坐标点;此例只需纵坐标。注意当角度偏转为反斜线时,须互换高宽
float newY = 0.0;
if(degree>=0&°ree<90)
newY = posy*cos(radian) + (fwdith-posx)*sin(radian);
else if(degree>=90&°ree<=180) {
float temp = newWdith;
newWdith = newHeight;
newHeight = temp;
newY = newHeight-(posx*cos(radian)+posy*sin(radian));
} else if(degree>180&°ree<=270)
newY = (fheight-posy)*cos(radian) + posx*sin(radian);
else { //if( degree>270 && degree<=360 )
float temp = newWdith;
newWdith = newHeight;
newHeight = temp;
newY = posx*cos(radian) + posy*sin(radian);
}
/// 计算渐变后的颜色
float fbegin=0.0; float fend = 0.0; // 起止颜色,须以float参与运算
float step = 0.0; // 渐变步长,以各种颜色分量进行计算
/// 计算渐变色
unsigned char rRed, rGreen, rBlue;
/// 红
fbegin = (float)GetRValue(crBegin); fend = (float)GetRValue(crEnd);
step = (fend-fbegin)/newHeight;
rRed = (int)( fbegin+step*newY );
/// 绿
fbegin = (float)GetGValue(crBegin); fend = (float)GetGValue(crEnd);
step = (fend-fbegin)/newHeight;
rGreen = (int)( fbegin+step*newY );
/// 蓝
fbegin = (float)GetBValue(crBegin); fend = (float)GetBValue(crEnd);
step = (fend-fbegin)/newHeight;
rBlue = (int)( fbegin+step*newY );
/ 透明度
//fbegin = (float)bAlpha; fend = (float)eAlpha;
//step = (fend-fbegin)/newHeight;
//rAlpha = (int)( fbegin+step*newY );
return RGB(rRed, rGreen, rBlue);
}
下面描述一下此例中线性渐变算法的过程以及数学依据,不感兴趣的可直接跳到末尾下载代码:
- 在渐变方向上做一个能包容当前矩形的新的矩形,新矩形与渐变线是垂直的。
- 根据当前矩形区域的宽和高以及渐变角度计算新矩形的宽和高
- 把当前矩形中的当前点的坐标映射到新矩形中,此例中只用到了Y坐标
- 从渐变线方向开始,从上往下进行垂直渐变
这样,不管从什么角度进行渐变,实际上都是好像是从上往下进行的渐变,如此一来,就容易理解了。
需注意的是渐变角度要分为4个区域进行讨论,分别如下:
图示说明:
- w和h表示当前矩形的宽和高
- a表示渐变角度
- 带箭头的黑线表示了渐变角度和方向
- nw和nh表示新矩形的宽和高
- 黑点是用作分析的示例点,虚线表示它在当前矩形中的横纵坐标,红色线表示它在新矩形中的横纵坐
- 标记出的各个角是角度相同的角
另外特别需要注意的一点是各个渐变角度在实际计算时都将其范围缩减到0~90度之间。
当渐变角度在0~90度之间时,如下图所示:
2、当渐变角度在90~180度之间时,如下图所示:
3、当渐变角度在180~270度之间时,如下图所示:
4、当渐变角度在270~360度之间时,如下图所示: