GDI+ for VCL基础 -- GDI+ 与 VCL
陆续写了十几篇关于《GDI+在Delphi程序的应用》的文章后,应几个小友来信要求,将我所使用的GDI+for VCL,包括Delphi和C++Builder版发布在了****的资源下载区。
GDI+ C语言版本同时GDI+ for VCL版本2010.7.10修改版下载地址:http://download.****.net/source/2737743
其中的Delphi版与目前网上流通的版本不完全兼容;而C++Builder本来自带有C++版的Gdiplus,但由于与VCL有些冲突,使用起来较麻烦,所以本人参照Delphi版完全重新写了一个供C++Builder使用的VCL版(开源的),本BLOG中的有关GDI+的Delphi例子很容易就移植到C++Builder。
下面就GDI+ for VCL的一些特点作简单介绍:
TGdiplusBase=class(TObject)
private
FNative:GpNative;
protected
constructorCreateClone(SrcNative:GpNative;clonefunc:TCloneAPI=nil);
propertyNative:GpNativereadFNativewriteFNative;
public
constructorCreate;
classfunctionNewInstance:TObject;override;
procedureFreeInstance;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:
private
FGdipError:TStatus;
functionGetGdipErrorString:string;
public
constructorCreateStatus(Status:TStatus);
propertyGdipError:TStatusreadFGdipError;
propertyGdipErrorString:stringreadGetGdipErrorString;
end;
除各类的析构方法外,其它类方法都使用了异常检查,这使得GDI+ for VCL代码同原C++代码和目前流通的GDI+ for Delphi比,更加方便和健壮,通过使用EGdiplusException.GdipError或者EGdiplusException.GdipErrorString,可以得到GDI+最后一次异常代码或信息。
GDI+ for VCL重新定义了绝大多数数据类型,如将C++风格的常量和枚举类型改为了VCL风格的枚举和集合类型,重构某些数据结构,以提供对VCL数据类型的支持或转换。以Color类为例,改写后的TGpColor:
#defineKnownColorCount141
static const ARGB kcAntiqueWhite = 0xfffaebd7;
classColor
{
private:
union
{
ARGBFARGB;
struct
{
BYTEFBlue;
BYTEFGreen;
BYTEFRed;
BYTEFAlpha;
};
};
staticTIdentMapEntryKnownColors[];
voidMakeARGB(BYTEa,BYTEr,BYTEg,BYTEb);
voidMakeARGB(BYTEa,Graphics::TColorcolor);
COLORREFGetCOLORREF();
AnsiStringGetKnownName(void);
public:
Color();
Color(Color&color);
Color(ARGBargb);
Color(BYTEalpha,ARGBargb);
Color(BYTEr,BYTEg,BYTEb);
Color(BYTEa,BYTEr,BYTEg,BYTEb);
Color(BYTEalpha,Graphics::TColorcolor);
Color(Graphics::TColorcolor);
Color(AnsiStringName,BYTEAlpha=255);
staticColorFromTColor(BYTEalpha,Graphics::TColorcolor);
staticColorFromTColor(Graphics::TColorcolor);
staticColorFromArgb(ARGBargb);
staticColorFromArgb(BYTEalpha,ARGBargb);
staticColorFromArgb(BYTEr,BYTEg,BYTEb);
staticColorFromArgb(BYTEa,BYTEr,BYTEg,BYTEb);
staticColorFromName(AnsiStringName,BYTEAlpha=255);
staticColorFromCOLORREF(BYTEalpha,COLORREFrgb);
staticColorFromCOLORREF(COLORREFrgb);
boolIsEmpty();
Color&operator=(Colorc);
Color&operator=(ARGBc);
booloperator==(Color&c);
booloperator!=(Color&c);
staticARGBStringToARGB(AnsiStringName,BYTEAlpha=255);
staticAnsiStringARGBToString(ARGBargb);
__propertyARGBArgb={read=FARGB};
__propertyBYTEAlpha={read=FAlpha};
__propertyBYTEA={read=FAlpha};
__propertyBYTERed={read=FRed};
__propertyBYTER={read=FRed};
__propertyBYTEGreen={read=FGreen};
__propertyBYTEG={read=FGreen};
__propertyBYTEBlue={read=FBlue};
__propertyBYTEB={read=FBlue};
__propertyCOLORREFRgb={read=GetCOLORREF};
__propertyAnsiStringName={read=GetKnownName};
};
typedefColorTGpColor,*PGpColor;
typedefARGBTARGB,*PARGB;
不仅提供了对VCL的TColor类型的支持(定义为TGpColor类型的参数可直接传递TColor类型),也提供了对GDI+标准颜色的支持与转换(按标准颜色名称得到标准颜色或者按标准颜色取得名称)。在Delphi中,对应TGpColor的地方一律采用TARGB类型,同时提供了与TGpColor函数成员类似的转换方法:
functionStringToARGB(constS:string;Alpha:BYTE=255):TARGB;
procedureGetARGBValues(Proc:TGetStrProc);
functionARGBToIdent(Argb:Longint;varIdent:string):Boolean;
functionIdentToARGB(constIdent:string;varArgb:Longint):Boolean;
functionARGB(r,g,b:BYTE):TARGB;overload;
functionARGB(a,r,g,b:BYTE):TARGB;overload;
functionARGB(a:Byte;Argb:TARGB):TARGB;overload;
functionARGBToCOLORREF(Argb:TARGB):Longint;
functionARGBToColor(Argb:TARGB):Graphics.TColor;
functionARGBFromCOLORREF(Rgb:Longint):TARGB;overload;
functionARGBFromCOLORREF(Alpha:Byte;Rgb:Longint):TARGB;overload;
functionARGBFromTColor(Color:Graphics.TColor):TARGB;overload;
functionARGBFromTColor(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代码:
interface
uses
Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,
Dialogs,StdCtrls,Buttons,ExtCtrls;
type
TMainForm=class(TForm)
BitBtn1:TBitBtn;
CbColor:TComboBox;
procedureFormPaint(Sender:TObject);
procedureCbColorDrawItem(Control:TWinControl;Index:Integer;
Rect:TRect;State:TOwnerDrawState);
procedureFormCreate(Sender:TObject);
procedureCbColorChange(Sender:TObject);
private
{Privatedeclarations}
procedureGetKnownColorStr(consts:string);
public
{Publicdeclarations}
end;
var
MainForm:TMainForm;
implementation
usesGdiplus;
{$R*.dfm}
procedureTMainForm.CbColorChange(Sender:TObject);
begin
Invalidate;
end;
procedureTMainForm.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;
procedureTMainForm.FormCreate(Sender:TObject);
begin
GetARGBValues(GetKnownColorStr);
CbColor.ItemIndex:=0;
end;
procedureTMainForm.FormPaint(Sender:TObject);
const
QualityStr:array[0..4]ofstring=
('Default','HighSpeed','HighQuality','GammaCorrected','AssumeLinear');
Alphas:array[0..3]ofByte=(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(kcand$808080)=$808080thenbc:=kcBlack
elsebc:=kcAliceBlue;
//以下使用内建的Pens和Brushs作图,也可分别使用TGpPen和TGpBrush建立
g.DrawLine(Pens.Brown,120,30,659,30);
g.FillRectangle(Brushs[bc],120,38,540,200);
//显示纵标题
fori:=0to4do
g.DrawString(QualityStr[i],font,Brushs.Black,4.0,i*40+48);
//显示横标题
fori:=0to3do
g.DrawString('Alpha:'+IntToStr(Alphas[i]),font,Brushs.Black,130.0+i*140,8);
g.DrawString('选择显示颜色',font,Brushs.Black,4.0,260.0);
//根据所选颜色和Alpha,用不同的合成品质画色块
fori:=0to3do
begin
forj:=Integer(Low(TCompositingQuality))toInteger(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;
procedureTMainForm.GetKnownColorStr(consts:string);
begin
CbColor.Items.Add(s);
end;
end.
C++ Builder代码:
#ifndefmainH
#definemainH
//---------------------------------------------------------------------------
#include<Classes.hpp>
#include<Controls.hpp>
#include<StdCtrls.hpp>
#include<Forms.hpp>
#include<Buttons.hpp>
//---------------------------------------------------------------------------
classTMainForm:publicTForm
{
__published://IDE-managedComponents
TBitBtn*BitBtn1;
TComboBox*CbColor;
void__fastcallCbColorDrawItem(TWinControl*Control,intIndex,TRect&Rect,
TOwnerDrawStateState);
void__fastcallCbColorChange(TObject*Sender);
void__fastcallFormPaint(TObject*Sender);
private://Userdeclarations
void__fastcallGetKnownColorStr(constStrings);
public://Userdeclarations
__fastcallTMainForm(TComponent*Owner);
};
//---------------------------------------------------------------------------
externPACKAGETMainForm*MainForm;
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#include<vcl.h>
#pragmahdrstop
#include"main.h"
#include"Gdiplus.hpp"
//---------------------------------------------------------------------------
#pragmapackage(smart_init)
#pragmaresource"*.dfm"
TMainForm*MainForm;
//---------------------------------------------------------------------------
__fastcallTMainForm::TMainForm(TComponent*Owner)
:TForm(Owner)
{
GetARGBValues(GetKnownColorStr);
CbColor->ItemIndex=0;
}
//---------------------------------------------------------------------------
void__fastcallTMainForm::GetKnownColorStr(constStrings)
{
CbColor->Items->Add(s);
}
//---------------------------------------------------------------------------
void__fastcallTMainForm::CbColorDrawItem(TWinControl*Control,intIndex,
TRect&Rect,TOwnerDrawStateState)
{
TGpGraphics*g=newTGpGraphics(CbColor->Canvas->Handle);
try
{
CbColor->Canvas->FillRect(Rect);
TGpRectr(Rect.Left,Rect.Top,CbColor->ItemHeight,CbColor->ItemHeight-4);
r.Offset(2,2);
TGpColorc=TGpColor::StringToARGB(CbColor->Items->Strings[Index]);
TGpBrush*b=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
{
deleteg;
}
}
//---------------------------------------------------------------------------
void__fastcallTMainForm::CbColorChange(TObject*Sender)
{
Invalidate();
}
//---------------------------------------------------------------------------
void__fastcallTMainForm::FormPaint(TObject*Sender)
{
conststaticStringQualityStr[5]=
{"Default","HighSpeed","HighQuality","GammaCorrected","AssumeLinear"};
conststaticByteAlphas[4]={255,128,64,32};
//TGpGraphics*g=newTGpGraphics(Handle,false);
TGpGraphics*g=newTGpGraphics(Canvas->Handle);
//建立与本窗口字体关联的Gdiplus字体对象,以下3句都可建立,
//但是第三句显示有点不一样,可能没包括字符集的信息
TGpFont*font=newTGpFont(Canvas->Handle);
//TGpFont*font=newTGpFont(Canvas->Handle,Font->Handle);
//TGpFont*font=newTGpFont(Font->Name,Font->Size,Font->Style);
try
{
TARGBkc=TGpColor::StringToARGB(CbColor->Items->Strings[CbColor->ItemIndex]);
TARGBbc=(kc&0x808080)==0x808080?kcBlack:kcAliceBlue;
g->DrawLine(Pens.Brown,120,30,659,30);
g->FillRectangle(Brushs[bc],120,38,540,200);
for(inti=0;i<5;i++)
g->DrawString(QualityStr[i],font,Brushs.Black,4.0,i*40+48);
for(inti=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(inti=0;i<4;i++)
{
for(intj=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
{
deletefont;
deleteg;
}
}
//---------------------------------------------------------------------------
运行结果:
通过这个例子,可以进一步了解前面介绍的颜色转换函数的应用、Pens和Brushs的应用;同时也增加对GDI+颜色类型TARGB不同于TColor的感性认识,即对Alpha的了解以及不同的Alphi值在不同的合成品质下的差异;还可掌握TCanvas与GDI+混合使用自绘TComboBox选项的技巧。
2011.6.26:
TGdiplusBase.CreatClone是一个构造方法 由于疏忽,在一些该方法调用语句中,未使用类类型作限定,导致克隆对象错误,应予以修正,以TGpMatrix.Clone为例:
function TGpMatrix.Clone: TGpMatrix;
begin
Result := CreateClone(Native, @GdipCloneMatrix);
end;
改为
Result := TGpMatrix.CreateClone(Native, @GdipCloneMatrix);
已修改过的主单元文件Gdiplus.pas的下载地址:http://download.****.net/source/3395894