单色画刷产生的颜色是单一的,比如一个红色画刷只能输出红色,而渐变画刷则不同,它可以输出多种颜色,这些颜色通常是连续的,从一个颜色平滑过渡到另一个颜色,看起来有渐变的效果。渐变画刷又分为两种,一种是ID2D1LinearGradientBrush(线性渐变画刷),另一种是ID2D1RadialGradientBrush(放射渐变画刷),下图显示了这两种画刷的效果。
这种画刷的颜色从一个位置线性渐变到另外一个位置,所以在创建这种画刷之前需要确定以下两个属性
在Direct2D中,结构体D2D1_GRADIENT_STOP用来表示一个位置及其颜色,其定义如下:
struct D2D1_GRADIENT_STOP { FLOAT position; D2D1_COLOR_F color; };
其中第一个成员position表示位置,这个值得范围必须是[0.0, 1.0]。第二个成员color表示颜色。我们用两个这样的结构体变量就可以表示起始/终止位置及颜色了。
在创建画刷时,需要指定画刷的会制范围,如果待填充图形的范围大于画刷的会制范围,我们需要告诉D2D如何处填充超出范围的部分。这需要通过颜色的扩展方式来指定,颜色扩展的方式用D2D1_EXTEND_MODE这个枚举类型来表示,它的定义如下:
typedef enum { D2D1_EXTEND_MODE_CLAMP = 0, D2D1_EXTEND_MODE_WRAP = 1, D2D1_EXTEND_MODE_MIRROR = 2 } D2D1_EXTEND_MODE;
如果大家了解纹理的话,对这几种方式一定不陌生,他们的含义如下:
这三种扩展方式的效果图如下,这里,矩形的宽度是画刷绘制范围的2倍,也就是说,颜色的扩展是从矩形的中间开始的。
D2D1_EXTEND_MODE_CLAMP
D2D1_EXTEND_MODE_WRAP
D2D1_EXTEND_MODE_MIRROR
下面开始具体的创建步骤,首先定义两个梯度点并指定其位置及颜色,这里第一个点位置是0,颜色是黄色,第二个点位置是1,颜色是红色。
D2D1_GRADIENT_STOP gradientStops[2] ; gradientStops[0].color = D2D1::ColorF(D2D1::ColorF::Yellow) ; gradientStops[0].position = 0.f ; gradientStops[1].color = D2D1::ColorF(D2D1::ColorF::Red) ; gradientStops[1].position = 1.f ;
其次,创建梯度点集合,这个集合将作为参数用来创建梯度色画刷,我们使用函数CreateGradientStopCollection来完成这个任务,它的定义如下:
virtual HRESULT CreateGradientStopCollection( [in] const D2D1_GRADIENT_STOP *gradientStops, UINT gradientStopsCount, D2D1_GAMMA colorInterpolationGamma, D2D1_EXTEND_MODE extendMode, [out] ID2D1GradientStopCollection **gradientStopCollection ) = 0;
参数说明:
typedef enum { D2D1_GAMMA_2_2 = 0, D2D1_GAMMA_1_0 = 1 } D2D1_GAMMA;
这两种方式的效果图如下,这里我们使用D2D1_GAMMA_2_2。
extendMode,指定颜色超出绘制范围时如何扩展。这里我们使用D2D1_EXTEND_MODE_CLAMP,也就是按照边缘色扩展。
gradientStopCollection,这个参数用来接收创建后的梯度点集合。
创建梯度点集合的代码如下,所有参数前面均已说明。
// Create gradient stops collection ID2D1GradientStopCollection* pGradientStops = NULL ; hr = pRenderTarget->CreateGradientStopCollection( gradientStops, 2, D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, &pGradientStops ) ; if (FAILED(hr)) { MessageBox(NULL, "Create gradient stops collection failed!", "Error", 0); }
最后,创建线性梯度色画刷。我们使用函数CreateLinearGradientBrush,看看它的定义:
virtual HRESULT CreateLinearGradientBrush( [in] const D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES *linearGradientBrushProperties, [in, optional] const D2D1_BRUSH_PROPERTIES *brushProperties, [in] ID2D1GradientStopCollection *gradientStopCollection, [out] ID2D1LinearGradientBrush **linearGradientBrush ) = 0;
参数说明:
具体代码如下,画刷的起始和结束位置分别对应矩形的左上角点和右下角点。
// Create a linear gradient brush to fill in the rectangle hr = pRenderTarget->CreateLinearGradientBrush( D2D1::LinearGradientBrushProperties( D2D1::Point2F(roundRect.rect.left, roundRect.rect.top), D2D1::Point2F(roundRect.rect.right, roundRect.rect.bottom)), pGradientStops, &pLinearGradientBrush ) ; if (FAILED(hr)) { MessageBox(hWnd, "Create linear gradient brush failed!", "Error", 0) ; return ; }
效果图
这种画刷的颜色从一个中心点向周围扩散,呈放射状,这可能也是其名字的由来吧。创建这种画刷的步骤和上面一样,先创建梯度点及其集合,然后使用函数CreateRadialGradientBrush创建画刷,关于如何创建梯度点及其集合,前面已经说了,所以这里只看创建画刷的步骤。函数CreateRadialGradientBrush的定义如下:
HRESULT CreateRadialGradientBrush( const D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES &radialGradientBrushProperties, [in] ID2D1GradientStopCollection *gradientStopCollection, [out] ID2D1RadialGradientBrush **radialGradientBrush );
参数说明:
struct D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES { D2D1_POINT_2F center; D2D1_POINT_2F gradientOriginOffset; FLOAT radiusX; FLOAT radiusY; };
代码如下:
创建椭圆
D2D1_ELLIPSE g_Ellipse = D2D1::Ellipse(D2D1::Point2F(300, 300), 200, 150);
创建画刷,这里我们指定起始颜色为黄色,终止颜色为蓝色,填充图形为椭圆,颜色从椭圆的中心向周围发散。
// Create a linear gradient brush to fill in the rectangle hr = pRenderTarget->CreateRadialGradientBrush( D2D1::RadialGradientBrushProperties( g_Ellipse.point, D2D1::Point2F(0, 0), g_Ellipse.radiusX, g_Ellipse.radiusY), pGradientStops, &pRadialGradientBrush ) ; if (FAILED(hr)) { MessageBox(hWnd, "Create linear gradient brush failed!", "Error", 0) ; return ; }
效果图
使用渐变画刷配合椭圆及圆角矩形可以创建出非常漂亮的Button控件,如果你喜欢自定义控件,不妨试一试。
== Happy Coding ==