陆续写了十几篇关于《GDI+在Delphi程序的应用》的文章后,应几个小友来信要求,将我所使用的GDI+for VCL,包括Delphi和C++Builder版发布在了csdn的资源下载区。
GDI+ C语言版本同时GDI+ for VCL版本2010.7.10修改版下载地址:http://download.youkuaiyun.com/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;
class functionNewInstance: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: string readGetGdipErrorString;
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;
class Color
{
private :
union
{
ARGBFARGB;
struct
{
BYTEFBlue;
BYTEFGreen;
BYTEFRed;
BYTEFAlpha;
};
};
static TIdentMapEntryKnownColors[];
void MakeARGB(BYTEa,BYTEr,BYTEg,BYTEb);
void MakeARGB(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 );
static ColorFromTColor(BYTEalpha,Graphics::TColorcolor);
static ColorFromTColor(Graphics::TColorcolor);
static ColorFromArgb(ARGBargb);
static ColorFromArgb(BYTEalpha,ARGBargb);
static ColorFromArgb(BYTEr,BYTEg,BYTEb);
static ColorFromArgb(BYTEa,BYTEr,BYTEg,BYTEb);
static ColorFromName(AnsiStringName,BYTEAlpha = 255 );
static ColorFromCOLORREF(BYTEalpha,COLORREFrgb);
static ColorFromCOLORREF(COLORREFrgb);
bool IsEmpty();
Color & operator = (Colorc);
Color & operator = (ARGBc);
bool operator == (Color & c);
bool operator != (Color & c);
static ARGBStringToARGB(AnsiStringName,BYTEAlpha = 255 );
static AnsiStringARGBToString(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( const S: string ;Alpha:BYTE = 255 ):TARGB;
procedureGetARGBValues(Proc:TGetStrProc);
functionARGBToIdent(Argb:Longint;varIdent: string ):Boolean;
functionIdentToARGB( const Ident: 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( const s: 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 ]of string =
( ' 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 ) = $ 808080 thenbc: = 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))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( const s: string );
begin
CbColor.Items.Add(s);
end;
end.
C++ Builder代码:
#ifndefmainH
#define mainH
// ---------------------------------------------------------------------------
#include < Classes.hpp >
#include < Controls.hpp >
#include < StdCtrls.hpp >
#include < Forms.hpp >
#include < Buttons.hpp >
// ---------------------------------------------------------------------------
class TMainForm: public TForm
{
__published: // IDE-managedComponents
TBitBtn * BitBtn1;
TComboBox * CbColor;
void __fastcallCbColorDrawItem(TWinControl * Control, int Index,TRect & Rect,
TOwnerDrawStateState);
void __fastcallCbColorChange(TObject * Sender);
void __fastcallFormPaint(TObject * Sender);
private : // Userdeclarations
void __fastcallGetKnownColorStr( const Strings);
public : // Userdeclarations
__fastcallTMainForm(TComponent * Owner);
};
// ---------------------------------------------------------------------------
extern PACKAGETMainForm * MainForm;
// ---------------------------------------------------------------------------
#endif
// ---------------------------------------------------------------------------
#include < vcl.h >
#pragma hdrstop
#include " main.h "
#include " Gdiplus.hpp "
// ---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource"*.dfm"
TMainForm * MainForm;
// ---------------------------------------------------------------------------
__fastcallTMainForm::TMainForm(TComponent * Owner)
:TForm(Owner)
{
GetARGBValues(GetKnownColorStr);
CbColor -> ItemIndex = 0 ;
}
// ---------------------------------------------------------------------------
void __fastcallTMainForm::GetKnownColorStr( const Strings)
{
CbColor -> Items -> Add(s);
}
// ---------------------------------------------------------------------------
void __fastcallTMainForm::CbColorDrawItem(TWinControl * Control, int Index,
TRect & Rect,TOwnerDrawStateState)
{
TGpGraphics * g = new TGpGraphics(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)
{
const static StringQualityStr[ 5 ] =
{ " Default " , " HighSpeed " , " HighQuality " , " GammaCorrected " , " AssumeLinear " };
const static ByteAlphas[ 4 ] = { 255 , 128 , 64 , 32 };
// TGpGraphics*g=newTGpGraphics(Handle,false);
TGpGraphics * g = new TGpGraphics(Canvas -> Handle);
// 建立与本窗口字体关联的Gdiplus字体对象,以下3句都可建立,
// 但是第三句显示有点不一样,可能没包括字符集的信息
TGpFont * font = new TGpFont(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 ( 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
{
deletefont;
deleteg;
}
}
// ---------------------------------------------------------------------------
运行结果:
通过这个例子,可以进一步了解前面介绍的颜色转换函数的应用、Pens和Brushs的应用;同时也增加对GDI+颜色类型TARGB不同于TColor的感性认识,即对Alpha的了解以及不同的Alphi值在不同的合成品质下的差异;还可掌握TCanvas与GDI+混合使用自绘TComboBox选项的技巧。