GDI+ for VCL基础 -- 画笔

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

        GDI+ 提供了很多绘图方法,如直线、曲线、圆弧、矩形、椭圆、扇形、多边形以及路径线条等,这些图形都需要使用GDI+ 画笔对象。

        GDI+ for VCL提供了画笔类TGpPen,TGpPen提供了2个构造方法,一个以TARGB颜色(BCB中为TGpColor类)建立画笔,另一个则以GDI+ 画刷类(TGpBrush)对象建立画笔,实际上,GDI+画笔都是建立在GDI+画刷对象基础上的,以颜色建立画笔, 其实就是以实色刷类TGpSoLidBrush对象建立画笔,所以下面建立的2种画笔是完全等价的,不过第一种方法更简单、方便些而已:

  pen : =  TGpPen.Create(kcRed);       //  以颜色建立画笔
  brush : =  TGpSoildBrush.Create(kcRed);
  pen :
=  TGpPen.Create(brush);        //  以实色画刷建立画笔

通过TGpPen.PenType可以得到画笔的类型TPenType,实际也就是建立画笔的画刷类型,其定义如下:

TPenType = (ptSolidColor, ptHatchFill, ptTextureFill, ptPathGradient, ptLinearGradient);

进一步,通过TGpPen.Brush属性,可以得到画笔内部的画刷(GDI+ for VCL的各种画刷我已经在前面几篇文章中作了介绍)。

        下面将TGpPen同VCL的画笔类TPen进行比较,相较TGpPen而言,TPen对象更简单,只相当于以实色画刷对象建立的TGpPen对象。二者(指TPen对象和TGpPen的实色笔对象)都可以设置画笔颜色、宽度和线条式样(TPen多一个psClear,可以做清除;而TGpPen多一个dsCustom,可以自定义线条式样);不同的有:TPen有个Mode属性,可以用来设置笔的作图方式,而TGpPen没有类似属性和方法,所以某些地方不太方便,如鼠标拖曳时的橡皮筋线条,TGpPen对象就没法直接画出这种效果,好在在VCL中使用GDI+的同时,也可使用TPen对象,弥补了这个缺陷;TGpPen可以设置线的对齐方式和复合钢笔(可同时绘制多条平行线)和线帽,还可进行画笔的几何变换,而TPen无这些功能,加之前面所说的TGpPen可以各种GDI+画刷建立对象,所以GDI+的TGpPen比VCL的TPen的功能丰富多了。

        首先,看看TGpPen的对齐方式设置。我们知道,无论是TGpPen还是TPen对象,都可设置笔的宽度,默认情况下,线条以基线(宽度为1的线条)为中心,两边均分,而TGpPen还可以改变对齐方式为全部线条宽度都置于基线内侧,下面的代码演示这种2种结果:

var
  g: TGpGraphics;
  pen: TGpPen;
begin
  g :
=  TGpGraphics.Create(Canvas.Handle);
  g.FillRectangle(Brushs[ARGBFromTColor(Color)], GpRect(ClientRect));
  pen :
=  TGpPen.Create($FF00FF00);
  pen.Width :
=   10 ;
//   pen.Alignment := paInset;
  g.DrawRectangle(Pen,  10 10 100 100 );  //  画线宽为5的矩形
  pen.Color : =  KcRed;
  Pen.Width :
=   1 ;
  g.DrawRectangle(Pen, 
10 10 100 100 );  //  画线宽为1的基线矩形
  pen.Free;
  g.Free;
end;

图一为默认对齐方式,即pen.Alignment := paCenter,红线为基线,线条以基线为中心均分;图二则是pen.Alignment := paInset对齐方式,线条居于基线之内侧。

            

这里附带说一下,GDI+画图形,无论是直线、矩形还是椭圆,和TCanvas的画图形有些不一样,实际画的图形长度总是比给出的长度多1线,而且线条位置还受TGpGraphics的线条像素的偏移模式有关,偏移模式定义如下:

  TPixelOffsetMode  =  (
    pmDefault,            
//  默认
    pmHighSpeed,      //  高速度、低质量
    pmHighQuality,      //  高质量、低速度
    pmNone,               //  没有任何像素偏移
    pmHalf                  //  像素在水平和垂直距离上均偏移 -.5 个单位,以进行高速锯齿消除
  );

比如上面矩形如果设置g.PixelOffsetMode := pmHalf;// 或者pmHighQuality时,在屏幕上,整个图形位置会向左上方移动一线(-0.5个像素四舍五入)。

        再看看复合线条的设置,下面是.net类库关于复合线条的说明:

        复合直线由平行直线和具有不同宽度的空白区域交替组成。数组中的值指定复合直线中每个组件的起始点位置,该位置与钢笔的宽度有关。数组中的第一个值指定第一个组件(直线)的起始位置,相当于钢笔宽的一小部分。数组中的第二个值指定下一个组件(空白)的起始位置,相当于钢笔宽的一小部分。数组中的最后一个值指定最后一个组件的结束位置。

        假设要用钢笔绘制两条平行直线,第一条直线的宽度是钢笔宽度的 20 %,将两条直线隔开的空白区域的宽度是钢笔宽度的 50 %,第二条直线的宽度是钢笔宽度的 30 %。先创建 Pen 和实数数组。通过将包含 0.0、0.2、0.7 和 1.0 值的数组传递给此属性来设置复合数组。

        如果 PenAlignment 属性设置为 Inset ,则不要设置此属性。

        下面的代码演示了上面的举例,设置彼得宽度为20,先在左边画一段直线,然后按上面例子设置复合线条数组后,在右边画一段双线的线段,第一条线占线宽的20%,为4,中间间隔50%的线宽等于10,第二条线则占剩下的30%,为6,效果见图三:

var
  g: TGpGraphics;
  pen: TGpPen;
begin
  g :
=  TGpGraphics.Create(Canvas.Handle);
  pen :
=  TGpPen.Create(kcRed);
  Pen.Width :
=   20 ;
  g.DrawLine(pen, 
10 150 110 150 );
  pen.SetCompoundArray([
0.0 0.2 0.7 1.0 ]);
  g.TranslateTransform(
110 0 );
  g.DrawLine(pen, 
10 150 110 150 );
  pen.Free;
  g.Free;
end;

        TGpPen的线条式样是由DashStyle属性决定的,前面已经说了,DashStyle与VCL TPen.Style除最后一个不同外,其它定义相同,不过,TGpPen可以设置线条式样两端的形状,GDI+定义了3种形状,即方形、圆角形和三角尖形,下面的例子设置为圆帽,画出各种线条式样的直线:

var
  g: TGpGraphics;
  pen: TGpPen;
  I: TDashStyle;
begin
  g :
=  TGpGraphics.Create(Canvas.Handle);
  g.FillRectangle(Brushs[ARGBFromTColor(Color)], GpRect(ClientRect));
  pen :
=  TGpPen.Create(kcGreen);
  pen.Width :
=   6.0 ;
  pen.DashCap :
=  dcRound;
  g.SmoothingMode :
=  smAntiAlias;
  
for  I : =  Low(TDashStyle) to High(TDashStyle)  do
  begin
    
if  I  =  dsCustom then
      pen.SetDashPattern([
4 2 1 3 ]);
    pen.DashStyle :
=  I;
    g.TranslateTransform(
0 20 );
    g.DrawLine(Pen, 
0 0 300 0 );
  end;
  pen.Free;
  g.Free;
end;

效果图见图四,最后一条为自定义线条式样,自定义线条数组设置为(4,2,1,3),各个值乘以笔的宽度(例子中为6),分别为第一个线段长24,间隔12,第二线段长6,间隔18,整个直线为这种格式的线段循环。

        TGpPen还可以设置线条联接式样,也就是由两条端点相交或重叠的线条联接点的联接式样,定义如下:

  TLineJoin  =  (
    ljMiter        
=   0 //  斜联接。这将产生一个锐角或切除角
    ljBevel         =   1 //  成斜角的联接。这将产生一个斜角。
    ljRound         =   2 //  圆形联接。这将在两条线之间产生平滑的圆弧。
    ljMiterClipped  =   3    //  斜联接。这将产生一个锐角或斜角,
  );

下面的代码展示了这几种联结式样:

procedure PaintLine(g: TGpGraphics);
var
  path: TGpGraphicsPath;
  pen: TGpPen;
  I: TLineJoin;
begin
  path :
=  TGpGraphicsPath.Create;
  path.AddRectangle(
10 10 100 100 );
  pen :
=  TGpPen.Create(kcBlue,  6 );
  
for  I : =  Low(TLineJoin) to High(TLineJoin)  do
  begin
    pen.LineJoin :
=  I;
    g.DrawPath(pen, path);
    g.TranslateTransform(
110 0 );
  end;
  pen.Free;
  path.Free;
end;

效果图如下,说明一下,因疏忽,Delphi的Gdiplus.pas第843行,原LineJoinRound应改为ljRound:

        TGpPen最具特色的就是可以定义线条两端的形状(线帽TLineCap),注意,这里的线帽形状和上面线条式样的形状TDashCap是两个不同的概念,前者是所要绘制的整个线条两端的形状;后者则是指线条式样两端的形状,一个线条是由无数个线条式样组成的。下面的C++代码绘制了2条不同线帽的直线,见图五:

void  __fastcall TForm1::PaintLines(TGpGraphics  * g)
{
    TGpPen 
* pen  =   new  TGpPen(kcBlue,  10 );
    g
-> SmoothingMode  =  smAntiAlias;
    
//  绘制开始方形线帽,结束菱形线帽的实线直线
    pen -> StartCap  =  lcSquareAnchor;
    pen
-> EndCap  =  lcDiamondAnchor;
    g
-> DrawLine(pen,  20 20 300 20 );
    
//  绘制开始圆头线帽,结束箭头线帽的虚线直线
    pen -> DashStyle  =  dsDash;
    pen
-> StartCap  =  lcRoundAnchor;
    pen
-> EndCap  =  lcArrowAnchor;
    g
-> TranslateTransform( 0 40 );
    g
-> DrawLine(pen,  20 20 300 20 );
    delete pen;
}

   

        GDI+还可以自定义画笔的线帽,下面移植MSDN上的一个自定义线帽例子作为本文的结束:

procedure TForm1.FormPaint(Sender: TObject);
const
  points: array[
0 .. 2 ] of TGpPoint  =
      ( (X:
100 ; Y: 100 ), (X: 200 ; Y: 50 ), (X: 250 ; Y: 300 ) );
var
  g: TGpGraphics;
  capPen, customCapPen: TGpPen;
  HookCap: TGpCustomLineCap;
  path: TGpGraphicsPath;
  StartCap, Endcap: TLineCap;
begin
  g :
=  TGpGraphics.Create(Canvas.Handle);
  
//  用窗体颜色填充窗体背景
  g.FillRectangle(Brushs[ARGBFromTColor(Color)], GpRect(ClientRect));
  
//  建立一个路径
  path : =  TGpGraphicsPath.Create;
  path.AddLine(GpPoint(
0 0 ), GpPoint( 0 5 ));
  path.AddLine(GpPoint(
0 5 ), GpPoint( 5 1 ));
  path.AddLine(GpPoint(
5 1 ), GpPoint( 3 1 ));
  
//  建立自定义线帽
  HookCap : =  TGpCustomLineCap.Create(nil, path);
  
//  设置用于构成自定义线帽的起始线帽和结束线帽
  HookCap.SetStrokeCaps(lcRound, lcRound);
  
//  建立宽度为5的黑色画笔
  customCapPen : =  TGpPen.Create(kcBlack,  5 );
  
//  设置黑色画笔自的起始线帽和结束线帽为自定义线帽
  customCapPen.SetCustomStartCap(HookCap);
  customCapPen.SetCustomEndCap(HookCap);
  
//  建立宽度为10的红色画笔
  capPen : =  TGpPen.Create(kcRed,  10 );
  
//  获取用于构成自定义线帽的起始线帽和结束线帽
  HookCap.GetStrokeCaps(StartCap, EndCap);
  
//  设置红色画笔的起始线帽和结束线帽(本例实际为圆头帽: lcRound)
  capPen.StartCap : =  StartCap;
  capPen.EndCap :
=  EndCap;
  
//  绘制图案
  g.SmoothingMode : =  smAntiAlias;
  g.DrawLines(capPen, points);
  g.DrawLines(customCapPen, points);

  HookCap.Free;
  path.Free;
  capPen.Free;
  customCapPen.Free;
  g.Free;
end;

效果图如下:

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值