GDI+ for VCL基础 -- 画刷之PathGradientBrush

时间:2021-01-02 19:19:10

          本文为GDI+ for VCL基础系列文章之一,主要供GDI+初学者入门参考,例子使用GDI+版本下载地址和说明见《GDI+ for VCL基础 -- GDI+ 与 VCL》。如有错误或者建议请来信:maozefa@hotmail.com

        GDI+由二维矢量图形、图像和版面等三部分组成,其中的二维矢量图形的图元,包括点、线条、曲线和图形等的绘制工具就是画笔和画刷,而画笔的特征又是由画刷决定的(在以后的关于画笔的文章中介绍),因此,熟练地掌握GDI+的各种画刷特性和使用方法是绘制GDI+图形的前提条件。

        GDI+提供了SolidBrush(实色刷)、HatchBrush(阴影刷)、TextureBrush(纹理刷)、LinearGradientBrush(渐变刷)和PathGradientBrush(路径刷)等五种画刷,在GDI+ for VCL中,各种画刷在原C++类类名基础上加了TGp前缀,均派生于TGpBrush,其中的TGpSolidBrush和TGpHatchBrush相当于VCL中传统的GDI的画刷TBrush。 

        说明:如果你是使用本人写的GDI+ for VCL版本,请注意其TGpPathGradientBrush类有个错误,请参照本文后面的“重要更正”内容进行必要修改,否则,下面的例子会出现运行时刻错误 

GDI+ for VCL的TGpPathGradientBrush 类用渐变色来填充形状。一个TGpPathGradientBrush对象有一个轨迹边界和一个中心点。可为轨迹的中心指定一种颜色,为轨迹的边界指定另一种颜色。还可为轨迹边界上的多个点分别指定颜色。 下面的Delphi代码和C++Builder代码分别用路径刷填充椭圆和三角形: // Delphi 2007
var
  path: TGpGraphicsPath;
  brush: TGpPathGradientBrush;
begin
  path :
= TGpGraphicsPath.Create;
  path.AddEllipse(
0014070);
  brush :
= TGpPathGradientBrush.Create(path);
  brush.CenterColor :
= kcBlue;
  brush.SetSurroundColors([$FF00FFFF]);
  
// 假定TGpGraphics对象g已经建立
  g.FillPath(brush, path);
  
// 设置中心点
  brush.CenterPointI := GpPoint(10050);
  g.TranslateTransform(
1500);
  g.FillPath(brush, path);
  brush.Free;
  path.Free;
end;

// C++ Builder 2007
    TGpGraphicsPath *path = new TGpGraphicsPath();
    path
->AddLine(70014070);
    path
->AddLine(14070070);
    TGpPathGradientBrush 
*brush = new TGpPathGradientBrush(path);
    brush
->CenterColor = kcBlue;
    TGpColor colors(
0xFF00FFFF);
    brush
->SetSurroundColors(&colors, 1);
      
// 假定TGpGraphics对象g已经建立
    g->FillPath(brush, path);
    
// 设置中心点
    brush->CenterPointI = TGpPoint(10060);
    g
->TranslateTransform(1500);
    g
->FillPath(brush, path);
    delete path;
    delete brush;
   例子中,用GDI+的路径类TGpGraphicsPath建立图形(关于路径将用另外的文章讨论),然后,以路径为参数建立路径渐变刷并填充路径图形,图形中心的颜色设置为蓝色,边界的颜色设置为浅绿色。图形的中心点可以改变,例子中通过CenterPointI属性改变缺省中心点。效果图分别见图一和图二,图的左边是用缺省中心点填充,右边则是改变中心点后填充的。GDI+ for VCL基础 -- 画刷之PathGradientBrush 也可直接用多个点来直接建立路径渐变刷,下面C++代码用点数组建立了一个路径渐变刷,并填充一个矩形,效果见图三。可见,路径渐变刷并非只是路径专用的。     TGpPoint ps[] = {TGpPoint(00), TGpPoint(1400),
                     TGpPoint(
14070), TGpPoint(070)
                    };
    TGpPathGradientBrush 
*brush = new TGpPathGradientBrush(ps, 4);
    brush
->CenterColor = kcBlue;
    TGpColor colors 
= (0xFF00FFFF);
    brush
->SetSurroundColors(&colors, 1);
    g
->FillRectangle(brush, 0014070);
    
// 设置中心点
    brush->CenterPointI = TGpPoint(3015);
    g
->TranslateTransform(1500);
    g
->FillRectangle(brush, 0014070);
    delete brush;

以上的例子中都设置了中心点颜色和边界轨迹颜色,如果不设置,其缺省的颜色分别为黑色和白色,见图四。

GDI+ for VCL基础 -- 画刷之PathGradientBrush            GDI+ for VCL基础 -- 画刷之PathGradientBrush

上面的例子中,TGpPathGradientBrush的SetSurroundColors方法都只设置了一种颜色,所以颜色比较单调,SetSurroundColors方法可以设置多种颜色,只要颜色的个数大于0且不大于路径渐变刷中图形点的个数(点的个数可以从TGpPathGradientBrush.PointCount属性取得),原则上,路径渐变刷的颜色个数是与图形点的个数相对应的,如果画刷颜色个数小于图形点的的个数,缺少的颜色用颜色数组中最后一种颜色替代。下面的代码是一个经典的GDI+路径渐变刷填充例子,由10个点组成一个五角星路径,然后用10种边界颜色和中心色为红色的路径渐变刷填充该路径(效果见图五),图的左边是正常填充,图的右边是采用伽玛修正后的填充:   

    TGpPoint ps[] = {
                        TGpPoint(
750), TGpPoint(10050),
                        TGpPoint(
15050), TGpPoint(11275),
                        TGpPoint(
150150), TGpPoint(75100),
                        TGpPoint(
0150), TGpPoint(3775),
                        TGpPoint(
050), TGpPoint(5050)
                    };
    TGpGraphicsPath 
*path = new TGpGraphicsPath();
    path
->AddLines(ps, 10);
    TGpPathGradientBrush 
*brush = new TGpPathGradientBrush(path);
    brush
->CenterColor = kcRed;
    TGpColor colors[] 
=
    {
        kcBlack, kcLime,
        kcBlue,  kcWhite,
        kcBlack, kcLime,
        kcBlue,  kcWhite,
        kcBlack, kcLime
    };

    brush
->SetSurroundColors(colors, 10);
    g
->FillPath(brush, path);
    
// 伽玛修正
    brush->GammaCorrection = true;
    g
->TranslateTransform(1600);
    g
->FillPath(brush, path);
    delete path;
    delete brush;

      GDI+ for VCL基础 -- 画刷之PathGradientBrush

        还有一个非常经典的GDI+例子,有一段贝塞尔曲线和几段直线组成一个复杂的图形,通过6种颜色的路径渐变刷填充后非常漂亮。不知什么原因,我写的代码始终不能达到那种效果,后来用原C++ GDI+和C#都试了,还是不行,好像路径渐变在贝塞尔曲线处无法按图形点展开填充,如果把代码中的ptBezier改为ptLine,就可以展开了,不过就很丑陋了。我尝试用一种颜色填充,图案也还华美。而且,我查看网上的.net例子(http://chs.gotdotnet.com/quickstart/winforms/doc/WinFormsGDIPlus.aspx),效果图好像也是单色填充的,与代码不符。下面把我写的代码贴在这里,如果有谁知道怎样修改代码可以达到效果请告知。

    TGpPoint ps[] = {
                        TGpPoint(
40140), TGpPoint(275200),
                        TGpPoint(
105225), TGpPoint(190300),
                        TGpPoint(
50350), TGpPoint(20180),
                    };
    TPathPointTypes ts[] 
=
    {
        TPathPointTypes(),
        TPathPointTypes() 
<< ptBezier,
        TPathPointTypes() 
<< ptBezier,
        TPathPointTypes() 
<< ptBezier,
        TPathPointTypes() 
<< ptLine,
        TPathPointTypes() 
<< ptLine
    };
    TGpGraphicsPath 
*path = new TGpGraphicsPath(ps, ts, 6);
    TGpPathGradientBrush 
*brush = new TGpPathGradientBrush(path);
    TGpColor colors[] 
=
    {
        kcGreen, kcYellow, kcRed, kcBlue, kcOrange, kcWhite
    };
    brush
->SetSurroundColors(colors, 6);
    
// 用六种颜色填充图案
    g->FillPath(brush, path);
    brush
->SetSurroundColors(colors, 1);
    g
->TranslateTransform(1700);
    
// 用一种颜色填充图案
    g->FillPath(brush, path);
    delete path;
    delete brush;

效果图见图六,左边为6种颜色未能展开的填充,中间是用一种颜色进行的填充效果,而右边则是改曲线为直线并修改2、3点位置后的6色填充。  

GDI+ for VCL基础 -- 画刷之PathGradientBrush

TGpPathGradientBrush和TGpLinearGradientBrush一样,提供了很多自定义渐变的手段,下面的代码演示了TGpPathGradientBrush的渐变过渡填充功能:

var
  path: TGpGraphicsPath;
  brush: TGpPathGradientBrush;
begin
  path :
= TGpGraphicsPath.Create;
  path.AddEllipse(
0014070);
  brush :
= TGpPathGradientBrush.Create(path);
  brush.GammaCorrection :
= True;            // 伽玛灰度修正
  brush.CenterColor := kcAqua;                 // 中心色
  brush.SetSurroundColors([kcBlue]);        // 边界色
  g.FillPath(brush, path);                            // 正常填充
  brush.FocusScales := GpPoint(0.30.8);// 置渐变过渡的焦点
  g.TranslateTransform(1500);
  g.FillPath(brush, path);                            
// 带渐变过渡的填充
  brush.Free;
  path.Free;

效果图见图七,图中第一个图形为正常填充,第二个图形为渐变过渡填充,所谓渐变过渡,就是扩展中心点到边界的渐变过渡范围,例子中的过渡焦点为(0.3, 0.8),也就是水平方向按图形的0.3的比例,垂直方向按图形的0.8的比例扩大中心点颜色的填充范围。图七种的第三、四个分别为钟形曲线的渐变过渡0.5(SetBlendBellShape(0.5);)和中心色向周围色的线性渐变过渡(SetBlendTriangularShape(0.5);)的效果,代码就不写了,它们和其它一些方法的功能及参数要求和TGpLinearGradientBrush的介绍一样,请参见《GDI+ for VCL基础 -- 画刷之LinearGradientBrush》一文。

GDI+ for VCL基础 -- 画刷之PathGradientBrush       

TGpPathGradientBrush也可使用SetInterpolationColors方法自定义多色渐变,如果说TGpLinearGradientBrush的功能是双色线性渐变填充,利用其SetInterpolationColors自定义多色渐变容易理解的话,那么TGpPathGradientBrush本身就是可以定义多色渐变的画刷,再使用SetInterpolationColors方法自定义多色渐变,似乎有些难以理解。其实,用SetSurroundColors方法设置的颜色是针对画刷中定义的点而言的,也就是说各种颜色将分别以路径画刷中定义的各个点到路径中心点这条直线上扩散开的(即使设置的颜色少于点的个数,缺少的颜色是用最后颜色替代的),而SetInterpolationColors方法自定义的多色渐变中的各个颜色,则是从路径画刷边界到中心点的位置(距离比例)环绕中心点渐变过渡的,前者是径向的渐变扩散填充,后者则是纬向的渐变过渡填充,以从边界路径点到中心点渐变为填充方向的角度看,才是真正意义上的多色填充。

最后,用一段自定义路径多色渐变例子作为本文结束:

const
  ps: array[
0..2] of TGpPoint = ((X:100; Y:0), (X:200; Y:200), (X:0; Y:200));
var
  brush: TGpPathGradientBrush;
begin
  brush :
= TGpPathGradientBrush.Create(ps);
  
// 自定义多色渐变
  brush.SetInterpolationColors([kcGreen, kcAqua, kcBlue], [00.31]);
  g.FillRectangle(brush, 
00200200);
  brush.Free;
end;

例子用SetInterpolationColors方法定义了一个3色渐变,其功能及参数说明参见《GDI+ for VCL基础 -- 画刷之LinearGradientBrush》一文,位子数组中的值是从画刷边界到中心的距离比率。注意,本例子同样也只是用3个点直接定义了路径渐变刷,没有使用路径操作,前面有使用路径填充三角形的例子,路径刷只是随已经确定了图形的形状的路径填充,而本例由于没有路径来界定,只好作为矩形填充,由于画刷的3个点构成了三角形,所以矩形范围内只有这个三角形是可见的。

        GDI+ for VCL基础 -- 画刷之PathGradientBrush

        关于GDI+的画刷就介绍完了,由于是基础性的介绍文章,有很多特性没作深入的讨论,有关画刷的一些高级操作也没有介绍,可能的话,将在以后的文章中予以补充。

         重要更正(2007.12.15):因疏忽,GDI+ for VCL 的TGpPathGradientBrush有个错误,请予以修改。

        Delhi的Gdiplus.pas第4769行开始的TGpPathGradientBrush.SetSurroundColors方法改为: 

procedure TGpPathGradientBrush.SetSurroundColors(colors: array of TARGB);
begin
  RV.rINT :
= GetPointCount;
  
if (Length(colors) > RV.rINT) or (RV.rINT <= 0) then
    CheckStatus(InvalidParameter);
  RV.rINT :
= Length(colors);
  CheckStatus(GdipSetPathGradientSurroundColorsWithCount(Native, @colors, RV.rINT));
end;

       BCB的GdipPath.hpp第547行开始的TGpPathGradientBrush.SetSurroundColors方法改为: 

inline
int __fastcall TGpPathGradientBrush::SetSurroundColors(const TGpColor *colors, const int colors_Size)
{
    Result.rINT 
= GetPointCount();
    
if (colors_Size > Result.rINT || Result.rINT <= 0)
        CheckStatus(InvalidParameter);
    Result.rINT 
= colors_Size;
    CheckStatus(GdipSetPathGradientSurroundColorsWithCount(Native, (TARGB
*)colors, &Result.rINT));
    
return Result.rINT;
}