当前位置:C++技术网 > 精选软件 > GDI渐变:3 实现水平彩色渐变和垂直彩色渐变(单色渐变双色渐变和多色渐变)的代码

GDI渐变:3 实现水平彩色渐变和垂直彩色渐变(单色渐变双色渐变和多色渐变)的代码

更新时间:2016-11-06 14:02:52浏览次数:1+次

    最近研究的GDI实现水平彩色渐变和垂直彩色渐变,到目前已经都实现好了。下面来分享一下。

    先看看渐变效果:

1.单色渐变

黑色RGB(0,0,0)到蓝色RGB(0,0,255)

【黑色RGB(0,0,0)到蓝色RGB(0,0,255)】

半蓝RGB(0,0,100)到蓝色RGB(0,0,255)

【半蓝RGB(0,0,100)到蓝色RGB(0,0,255)】

蓝RGB(0,0,255)到蓝RGB(0,0,50)

【蓝RGB(0,0,255)到蓝RGB(0,0,50)】

2.双色渐变

蓝RGB(0,0,255)到绿RGB(0,255,50)

【蓝RGB(0,0,255)到绿RGB(0,255,50)】

RGB(0,100,100)到绿RGB(0,255,255)

【RGB(0,100,100)到绿RGB(0,255,255)】

3.三色渐变

RGB(255,100,50)到绿RGB(0,240,100)

【RGB(255,100,50)到绿RGB(0,240,100)】


    可以看到,从单色渐变、双色渐变和三色渐变,想怎么变就怎么变,而且渐变的范围可以是0-255,还可以是之间的任何一个范围,还可以255-0的顺序。双色和三色渐变时,各个颜色分量变化组合起来,可以形成更加绚丽的效果。

    当然,垂直渐变也可以和上面的水平渐变一样的效果,只是方向不一样而已。拥有了这些渐变,已经非常绚丽了哦。

    而渐变函数,只有一个。函数声明如下:


void GradientLinear(CDC* pDC,CRect rt,COLORREF clr1,COLORREF clr2,bool hori);
     这是基于MFC写的代码,所以前两个参数类型都是MFC里的。如果你想用纯Win32,其实也是很好转换的。然后clr1和clr2是渐变的起始颜色和结束颜色,两个颜色值随意。调整不同的颜色值混合出来的渐变效果,都是非常好看的。最后一个参数是用来选择渐变方向的,true为水平渐变,也就是上面图给出来的渐变方向,false则为垂直渐变。


    而单色渐变、双色渐变和三色渐变则全部在clr1和clr2里控制。RGB的值在图底下给出来了。如果要单色渐变,那么只变化一个颜色分量,如果双色渐变则变化两个颜色分量。变化表示起始颜色和终止颜色的这个分量值不一样,不变化的值的起始和终止值是一样的,不需要指定为0。

    下面是函数使用示例代码:


CDC *pDC = GetDC();//获取客户区CDC
CRect rtClient;
GetClientRect(&rtClient);//获取客户区矩形
GradientLinear(pDC,rtClient,RGB(255,100,50),RGB(0,240,100),true);
     这个代码就是在任何一个窗口消息或者控件事件响应函数里使用就行了。绘制的矩形可以是任意矩形,不局限于窗口矩形。你也可以指定为客户区其中一部分的区域,效果如下:


    在窗口客户区任何位置绘制渐变

     那么最为激动人心的就是这个函数的代码实现了。哈哈哈,马上呈上:


void GradientLinear(CDC* pDC,CRect rt,COLORREF clr1,COLORREF clr2,bool hori)
{
    //提前获取好矩形的宽高
    int width = rt.Width();
    int height = rt.Height();
    //提取两个颜色的红绿蓝分量
    int r1 = GetRValue(clr1);
    int r2 = GetRValue(clr2);
    int g1 = GetGValue(clr1);
    int g2 = GetGValue(clr2);
    int b1 = GetBValue(clr1);
    int b2 = GetBValue(clr2);
    //计算两个颜色之间的三个分量的差值,用于计算渐变的变化比例
    int rSpan = r2 - r1;
    int gSpan = g2 - g1;
    int bSpan = b2 - b1;
    //进行水平和垂直来选择遍历的像素总数
    int len = hori?width:height;
    //使用内存DC来加速绘制,防止闪烁
    CDC dcMem;
    dcMem.CreateCompatibleDC(pDC);
    CBitmap bmp;
    bmp.CreateCompatibleBitmap(pDC,rt.Width(),rt.Height());
    dcMem.SelectObject(&bmp);
    //开始内存绘制
    for (int i=0;i<len;i++)
    {
        //颜色跟随着线条移动而更换颜色
        COLORREF clr = RGB(r1+(i*rSpan)/len,g1+(i*gSpan)/len,b1+(i*bSpan)/len);
        //因为水平和垂直渐变相邻线条会紧密拼接,不会留空,画笔粗细只需要1像素即可。
        CPen pen(PS_SOLID,1,clr);
        dcMem.SelectObject(&pen);
        if(hori)
        {
            //水平渐变,线条从上往下画,渐变色从左到右变化
            dcMem.MoveTo(i,0);
            dcMem.LineTo(i,height);
        }
        else
        {
            //垂直渐变,线条从左往右画,渐变色从上到下变化
            dcMem.MoveTo(0,i);
            dcMem.LineTo(width,i);
        }
    }
    //将内存的数据一次性贴到设备DC中
    pDC->BitBlt(rt.left,rt.top,rt.Width(),rt.Height(),&dcMem,0,0,SRCCOPY);
    bmp.DeleteObject();
    dcMem.DeleteDC();
}
     这里不仅有实现代码,而且有详细的注释,可以帮助你迅速理解实现原理。实现的原理就是,画很多的直线,每一条直线使用不同的颜色,按照一个顺序,每一条直线的颜色在渐变,最后拼在一起就是渐变颜色了。


    为什么不用设置像素来实现呢?画线的效率自然比设置像素高很多很多。为了保证渐变的细腻,使用了单像素的递增。单像素递增体现在循环的递增,而不在于画笔的宽度。画笔的宽度的影响在于斜向渐变产生空隙问题。水平和垂直渐变用不着,因为不会产生空隙。

    如果有不清楚的地方,或者代码优化改进,请留言。