GDI+ for VCL基础 -- GDI+ 与 VCL

        陆续写了十几篇关于《GDI+Delphi程序的应用》的文章后,应几个小友来信要求,将我所使用的GDI+ for VCL,包括DelphiC++Builder版发布在了csdn的资源下载区。

    GDI+ C语言版本同时GDI+ for VCL版本2010.7.10修改版下载地址:http://download.csdn.net/source/2737743(因疏忽,导致个别克隆方法错误,修改过的主单元文件Gdiplus.pas的下载地址:http://download.csdn.net/source/3395894,可覆盖原Gdiplus.pas文件)。

        其中的Delphi版与目前网上流通的版本不完全兼容;而C++Builder本来自带有C++版的Gdiplus,但由于与VCL有些冲突,使用起来较麻烦,所以本人参照Delphi版完全重新写了一个供C++Builder使用的VCL版(开源的),本BLOG中的有关GDI+的Delphi例子很容易就移植到C++Builder。

        下面就GDI+ for VCL的一些特点作简单介绍:

        GDI+是伴随Windows XP系统出现的增强性图形设备接口子系统,除了一整套API外,还提供了几十个C++类和大量的数据类型,同传统的Win32 GDI相比,GDI+不仅优化和扩展了GDI,而且使得使用C/C++开发Windows程序图形界面更容易操作。但是,对于Delphi和C++Builder所共用的VCL来说,由于VCL通过TCanvas、TBitmap和TImage等及其相关的类,把传统的GDI封装的几乎无可挑剔,类似C++的GDI+类,在易操作上就没什么优势可言了。但是,要想把GDI+封装成完全的VCL风格也存在几个问题:
        1、正是由于VCL对GDI良好的封装,VCL所有与界面和图形有关的类对GDI存在严重的依赖性,即使GDI+封装成完全的VCL风格,也无法“插足”已有的界面和图形类,充其量只能起到“敲敲边鼓”的辅助作用,所以把GDI+封装成完全的VCL风格意义不大,除非GDI+为主,完全重构原有的界面和图形类;
        2、现有大量的C++、.NET有关GDI+的代码移植到Delphi(C++Builser)时,结构修改量太大,而且对于已经熟悉C++或者.NET GDI+的人来说,重新掌握新的VCL风格无疑是痛苦的;
        3、GDI+的坐标计量类型有整数和实数2套,如果按VCL风格写,势必要舍弃一套,保留整数型当然是最好的,但这2套类型又都不那么完整,整数型更是个半吊子系统。
        鉴于以上原因,无论是目前网上流通的GDI+ for Delphi版本,还是本人改写的GDI+ for VCL,都基本采用了原C++类风格,甚至于.net的GDI+版本也与.net其它类风格不一样,更多的保留了原C++风格(就我个人的看法,不知什么原因,GDI+ for C++版本无论是架构设计,还是代码水平,其实都很差,同C++的STL没法比)。
        当然,同网上流通的GDI+ for Delphi版本比,我改写的GDI+ for VCL版本并没有完全照搬原C++代码,兼顾了部分VCL和.NET风格,比如加入了VCL异常、布尔类型和绝大部分枚举和集合类型都采用了VCL风格、增加了.NET风格的Pens和Brushs等全局变量(C++Builder)或者全局函数(Delphi)。
        GDI+ for VCL的所有类(不包括TPens和TBrushs)都派生于TGdiplusBase:
  TCloneAPI  =  function(Native: GpNative; var clone: GpNative): TStatus; stdcall;

  TGdiplusBase 
=   class (TObject)
  
private
    FNative: GpNative;
  
protected
    constructor CreateClone(SrcNative: GpNative; clonefunc: TCloneAPI 
=  nil);
    property Native: GpNative read FNative write FNative;
  
public
    constructor Create;
    
class  function NewInstance: TObject;  override ;
    procedure FreeInstance; 
override ;
  end;

        原C++的GdiplusBase只是重载了new和delete操作符,分别以GDI+的GdipAlloc和GdipFree替换了原系统默认的内存分配和释放方法,而TGdiplusBase也相应的重载了TObject的NewInstance方法和FreeInstance方法(其实在不重载也能正常运行);

        在TGdiplusBase中有个保护的GpNative(指针)类型的成员Native(Delphi中说明为属性,C++builder直接说明为数据成员),供所用派生类使用(原C++类将这个成员分散说明在各个类中),这个数据成员就是Gdiplus.dll内部使用的类指针,GDI+类都是通过对应的内部类指针对Dll Exports函数的调用实现的(假如你讨厌GDI+的类,你完全可以抛开它们而直接使用指针操作原始的Dll Exports函数);

        至于TGdiplusBase构造方法CreateClone则是我为了简化派生类的Clone方法所提供的基类保护方法。

        GDI+ for VCL定义了一个异常类EGdiplusException:

  EGdiplusException  =   class (Exception)
  
private
    FGdipError: TStatus;
    function GetGdipErrorString: 
string ;
  
public
    constructor CreateStatus(Status: TStatus);
    property GdipError: TStatus read FGdipError;
    property GdipErrorString: 
string  read GetGdipErrorString;
  end;

        除各类的析构方法外,其它类方法都使用了异常检查,这使得GDI+ for VCL代码同原C++代码和目前流通的GDI+ for Delphi比,更加方便和健壮,通过使用EGdiplusException.GdipError或者EGdiplusException.GdipErrorString,可以得到GDI+最后一次异常代码或信息。

        GDI+ for VCL重新定义了绝大多数数据类型,如将C++风格的常量和枚举类型改为了VCL风格的枚举和集合类型,重构某些数据结构,以提供对VCL数据类型的支持或转换。以Color类为例,改写后的TGpColor:

// Known Color

#define  KnownColorCount  141
static const ARGB kcAliceBlue   = 0xfff0f8ff;
static const ARGB kcAntiqueWhite  = 0xfffaebd7;
(略)

class  Color
{
private :

    union
    {
        ARGB FARGB;
        
struct
        {
            BYTE FBlue;
            BYTE FGreen;
            BYTE FRed;
            BYTE FAlpha;
        };
    };
    
static  TIdentMapEntry KnownColors[];

    
void  MakeARGB(BYTE a, BYTE r, BYTE g, BYTE b);
    
void  MakeARGB(BYTE a, Graphics::TColor color);
    COLORREF GetCOLORREF();
    AnsiString GetKnownName(
void );

public :

    Color();
    Color(Color 
& color);
    Color(ARGB argb);
    Color(BYTE alpha, ARGB argb);
    Color(BYTE r, BYTE g, BYTE b);
    Color(BYTE a, BYTE r, BYTE g, BYTE b);
    Color(BYTE alpha, Graphics::TColor color);
    Color(Graphics::TColor color);
    Color(AnsiString Name, BYTE Alpha 
=   255 );

    
static  Color FromTColor(BYTE alpha, Graphics::TColor color);
    
static  Color FromTColor(Graphics::TColor color);
    
static  Color FromArgb(ARGB argb);
    
static  Color FromArgb(BYTE alpha, ARGB argb);
    
static  Color FromArgb(BYTE r, BYTE g, BYTE b);
    
static  Color FromArgb(BYTE a, BYTE r, BYTE g, BYTE b);
    
static  Color FromName(AnsiString Name, BYTE Alpha  =   255 );
    
static  Color FromCOLORREF(BYTE alpha, COLORREF rgb);
    
static  Color FromCOLORREF(COLORREF rgb);

    
bool  IsEmpty();
    Color
&   operator   =  (Color c);
    Color
&   operator   =  (ARGB c);
    
bool   operator   ==  (Color &  c);
    
bool   operator   !=  (Color &  c);

    
static  ARGB StringToARGB(AnsiString Name, BYTE Alpha  =   255 );
    
static  AnsiString ARGBToString(ARGB argb);

    __property ARGB Argb 
=  { read  =  FARGB };
    __property BYTE Alpha 
=  { read  =  FAlpha };
    __property BYTE A 
=  { read  =  FAlpha };
    __property BYTE Red 
=  { read  =  FRed };
    __property BYTE R 
=  { read  =  FRed };
    __property BYTE Green 
=  { read  =  FGreen };
    __property BYTE G 
=  { read  =  FGreen };
    __property BYTE Blue 
=  { read  =  FBlue };
    __property BYTE B 
=  { read  =  FBlue };
    __property COLORREF Rgb 
=  { read  =  GetCOLORREF };
    __property AnsiString Name 
=  { read  =  GetKnownName };

};

typedef Color                         TGpColor, 
* PGpColor;
typedef ARGB    TARGB, 
* PARGB;

不仅提供了对VCL的TColor类型的支持(定义为TGpColor类型的参数可直接传递TColor类型),也提供了对GDI+标准颜色的支持与转换(按标准颜色名称得到标准颜色或者按标准颜色取得名称)。在Delphi中,对应TGpColor的地方一律采用TARGB类型,同时提供了与TGpColor函数成员类似的转换方法:

function ARGBToString(Argb: TARGB):  string ;
function StringToARGB(
const  S:  string ; Alpha: BYTE  =   255 ): TARGB;
procedure GetARGBValues(Proc: TGetStrProc);
function ARGBToIdent(Argb: Longint; var Ident: 
string ): Boolean;
function IdentToARGB(
const  Ident:  string ; var Argb: Longint): Boolean;

function ARGB(r, g, b: BYTE): TARGB; overload;
function ARGB(a, r, g, b: BYTE): TARGB; overload;
function ARGB(a: Byte; Argb: TARGB): TARGB; overload;

function ARGBToCOLORREF(Argb: TARGB): Longint;
function ARGBToColor(Argb: TARGB): Graphics.TColor;
function ARGBFromCOLORREF(Rgb: Longint): TARGB; overload;
function ARGBFromCOLORREF(Alpha: Byte; Rgb: Longint): TARGB; overload;
function ARGBFromTColor(Color: Graphics.TColor): TARGB; overload;
function ARGBFromTColor(Alpha: Byte; Color: Graphics.TColor): TARGB; overload;

        GDI+ for VCL还增加了.NET风格的Pens和Brushs等全局变量(C++Builder)或者全局函数(Delphi),不仅提供了141种标准颜色的画笔和画刷,也可使用自定义颜色调用Pens和Brushs的缺省数组(Delphi)或者重载的操作符(C++Builder)形成新的画笔和画刷,大大简化了一般的GDI+编程代码,C++Builder的定义为:

static TGpPens Pens;
static TGpBrushs Brushs;

而Delphi则定义为:

function Pens: TPens;
function Brushs: TBrushs;

        你可能注意到上面的类型说明中Delphi和C++Builder类的名称不一样,前者为TGpPens和TGpBrushs,而后者直接写为TPens和TBrushs,这是本人写代码时的一点疏忽,不过对写代码没任何影响。

        有关GDI+与VCL的话题就说到这里,下面用Delphi和C++Builder以一个简单相同的例子程序作为本文的结尾。

        Delphi代码:

unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, ExtCtrls;

type
  TMainForm 
=   class (TForm)
    BitBtn1: TBitBtn;
    CbColor: TComboBox;
    procedure FormPaint(Sender: TObject);
    procedure CbColorDrawItem(Control: TWinControl; Index: Integer;
      Rect: TRect; State: TOwnerDrawState);
    procedure FormCreate(Sender: TObject);
    procedure CbColorChange(Sender: TObject);
  
private
    { Private declarations }
    procedure GetKnownColorStr(
const  s:  string );
  
public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

uses Gdiplus;

{$R 
* .dfm}

procedure TMainForm.CbColorChange(Sender: TObject);
begin
  Invalidate;
end;

procedure TMainForm.CbColorDrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
var
  g: TGpGraphics;
  r: TGpRect;
begin
  g :
=  TGpGraphics.Create(CbColor.Canvas.Handle);
  
try
    CbColor.Canvas.FillRect(Windows.TRect(Rect));
    r :
=  GpRect(Rect.Left, Rect.Top, CbColor.ItemHeight, CbColor.ItemHeight  -   4 );
    OffSet(r, 
2 2 );
    g.FillRectangle(Brushs[StringToARGB(CbColor.Items[Index])], r);
    g.DrawRectangle(Pens.Black, r);
    CbColor.Canvas.TextOut(r.X 
+  r.Width  +   5 , r.Y, CbColor.Items[Index]);
  
finally
    g.Free;
  end;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  GetARGBValues(GetKnownColorStr);
  CbColor.ItemIndex :
=   0 ;
end;

procedure TMainForm.FormPaint(Sender: TObject);
const
  QualityStr: array[
0 .. 4 ] of  string   =
    (
' Default ' ' HighSpeed ' ' HighQuality ' ' GammaCorrected ' ' AssumeLinear ' );
  Alphas: array[
0 .. 3 ] of Byte  =  ( 255 128 64 32 );
var
  g : TGpGraphics;
  font: TGpFont;
  kc, bc: TARGB;
  i, j: Integer;
begin
  
//  建立与窗口关联的Graphics对象,使用Handle建立在D7中效果很好,可2007不停闪烁
//   g := TGpGraphics.Create(Handle, False);
  g : =  TGpGraphics.Create(Canvas.Handle);
  
//  建立与本窗口字体关联的Gdiplus字体对象,以下3句都可建立,
  
//  但是第三句显示有点不一样,可能没包括字符集的信息
  font : =  TGpFont.Create(Canvas.Handle);
//   font := TGpFont.Create(Canvas.Handle, Self.Font.Handle);
//   font := TGpFont.Create(Self.Font.Name, Self.Font.Size, Self.Font.Style);
  kc : =  StringToARGB(CbColor.Items[CbColor.ItemIndex]);
  
if  (kc and $ 808080 =  $ 808080  then bc : =  kcBlack
  
else  bc : =  kcAliceBlue;
  
//  以下使用内建的Pens和Brushs作图,也可分别使用TGpPen和TGpBrush建立
  g.DrawLine(Pens.Brown,  120 30 659 30 );
  g.FillRectangle(Brushs[bc], 
120 38 540 200 );
  
//  显示纵标题
   for  i : =   0  to  4   do
    g.DrawString(QualityStr[i], font, Brushs.Black, 
4.0 , i  *   40   +   48 );
  
//  显示横标题
   for  i : =   0  to  3   do
    g.DrawString(
' Alpha:  '   +  IntToStr(Alphas[i]), font, Brushs.Black,  130.0   +  i  *   140 8 );
  g.DrawString(
' 选择显示颜色 ' , font, Brushs.Black,  4.0 260.0 );
  
//  根据所选颜色和Alpha,用不同的合成品质画色块
   for  i : =   0  to  3   do
  begin
    
for  j : =  Integer(Low(TCompositingQuality)) to Integer(High(TCompositingQuality))  do
    begin
      g.CompositingQuality :
=  TCompositingQuality(j);
      g.DrawLine(Pens[ARGB(Alphas[i], kc), 
20 ],
                 
130   +  i  *   140 , j  *   40   +   58 230   +  i  *   140 , j  *   40   +   58 );
    end;
  end;
  font.Free;
  g.Free;
end;

procedure TMainForm.GetKnownColorStr(
const  s:  string );
begin
  CbColor.Items.Add(s);
end;

end.

        C++ Builder代码:

// ---------------------------------------------------------------------------

#ifndef mainH
#define  mainH
// ---------------------------------------------------------------------------
#include  < Classes.hpp >
#include 
< Controls.hpp >
#include 
< StdCtrls.hpp >
#include 
< Forms.hpp >
#include 
< Buttons.hpp >
// ---------------------------------------------------------------------------
class  TMainForm :  public  TForm
{
__published:    
//  IDE-managed Components
    TBitBtn  * BitBtn1;
    TComboBox 
* CbColor;
    
void  __fastcall CbColorDrawItem(TWinControl  * Control,  int  Index, TRect  & Rect,
          TOwnerDrawState State);
    
void  __fastcall CbColorChange(TObject  * Sender);
    
void  __fastcall FormPaint(TObject  * Sender);
private :     //  User declarations
     void  __fastcall GetKnownColorStr( const  String s);
public :         //  User declarations
    __fastcall TMainForm(TComponent *  Owner);
};
// ---------------------------------------------------------------------------
extern  PACKAGE TMainForm  * MainForm;
// ---------------------------------------------------------------------------
#endif


// ---------------------------------------------------------------------------

#include 
< vcl.h >
#pragma  hdrstop

#include 
" main.h "
#include 
" Gdiplus.hpp "
// ---------------------------------------------------------------------------
#pragma  package(smart_init)
#pragma  resource "*.dfm"
TMainForm 
* MainForm;
// ---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent *  Owner)
    : TForm(Owner)
{
    GetARGBValues(GetKnownColorStr);
    CbColor
-> ItemIndex  =   0 ;
}
// ---------------------------------------------------------------------------
void  __fastcall TMainForm::GetKnownColorStr( const  String s)
{
    CbColor
-> Items -> Add(s);
}
// ---------------------------------------------------------------------------
void  __fastcall TMainForm::CbColorDrawItem(TWinControl  * Control,  int  Index,
      TRect 
& Rect, TOwnerDrawState State)
{
    TGpGraphics 
* =   new  TGpGraphics(CbColor -> Canvas -> Handle);
    
try
    {
        CbColor
-> Canvas -> FillRect(Rect);
        TGpRect r(Rect.Left, Rect.Top, CbColor
-> ItemHeight, CbColor -> ItemHeight  -   4 );
        r.Offset(
2 2 );
        TGpColor c 
=  TGpColor::StringToARGB(CbColor -> Items -> Strings[Index]);
        TGpBrush 
* =  Brushs[c];
        g
-> FillRectangle(b, r);
        g
-> DrawRectangle(Pens.Black, r);
        CbColor
-> Canvas -> TextOutA(r.X  +  r.Width  +   5 , r.Y, CbColor -> Items -> Strings[Index]);
    }
    __finally
    {
        delete g;
    }
}
// ---------------------------------------------------------------------------
void  __fastcall TMainForm::CbColorChange(TObject  * Sender)
{
    Invalidate();    
}
// ---------------------------------------------------------------------------
void  __fastcall TMainForm::FormPaint(TObject  * Sender)
{
    
const   static  String QualityStr[ 5 =
        {
" Default " " HighSpeed " " HighQuality " " GammaCorrected " " AssumeLinear " };
    
const   static  Byte Alphas[ 4 =  { 255 128 64 32 };

//     TGpGraphics *g = new TGpGraphics(Handle, false);
    TGpGraphics  * =   new  TGpGraphics(Canvas -> Handle);
  
//  建立与本窗口字体关联的Gdiplus字体对象,以下3句都可建立,
  
//  但是第三句显示有点不一样,可能没包括字符集的信息
    TGpFont  * font  =   new  TGpFont(Canvas -> Handle);
//     TGpFont *font = new TGpFont(Canvas->Handle, Font->Handle);
//     TGpFont *font = new TGpFont(Font->Name, Font->Size, Font->Style);
     try
    {
        TARGB kc 
=  TGpColor::StringToARGB(CbColor -> Items -> Strings[CbColor -> ItemIndex]);
        TARGB bc 
=  (kc  &   0x808080 ==   0x808080 ?  kcBlack : kcAliceBlue;
        g
-> DrawLine(Pens.Brown,  120 30 659 30 );
        g
-> FillRectangle(Brushs[bc],  120 38 540 200 );
        
for  ( int  i  =   0 ; i  <   5 ; i  ++ )
            g
-> DrawString(QualityStr[i], font, Brushs.Black,  4.0 , i  *   40   +   48 );
        
for  ( int  i  =   0 ; i  <   4 ; i  ++ )
            g
-> DrawString( " Alpha:  "   +  IntToStr(Alphas[i]),
                          font, Brushs.Black, 
130.0   +  i  *   140 8.0 );
        g
-> DrawString( " 选择显示颜色 " , font, Brushs.Black,  4.0 260.0 );
        
for  ( int  i  =   0 ; i  <   4 ; i  ++ )
        {
            
for  ( int  j  =   0 ; j  <   5 ; j  ++ )
            {
                g
-> CompositingQuality  =  (TCompositingQuality)j;

                g
-> DrawLine(Pens(TGpColor(Alphas[i], kc),  20 ),
                            
130   +  i  *   140 , j  *   40   +   58 230   +  i  *   140 , j  *   40   +   58 );
           }
        }
    }
    __finally
    {
        delete font;
        delete g;
    }
}
// ---------------------------------------------------------------------------

运行结果:

GDI+ for VCL基础 -- GDI+ 与 VCL_第1张图片

        通过这个例子,可以进一步了解前面介绍的颜色转换函数的应用、Pens和Brushs的应用;同时也增加对GDI+颜色类型TARGB不同于TColor的感性认识,即对Alpha的了解以及不同的Alphi值在不同的合成品质下的差异;还可掌握TCanvas与GDI+混合使用自绘TComboBox选项的技巧。

 

你可能感兴趣的:(C++,String,Integer,Delphi,C++Builder,GDI+)