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

陆续写了十几篇关于《GDI+在Delphi程序的应用》的文章后,应几个小友来信要求,将我所使用的GDI+ for VCL,包括Delphi和C++Builder版发布在了csdn的资源下载区,下载地址:http://download.csdn.net/source/297704

其中的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,这是本人写代码时的一点疏忽,不过对写代码没任何影响。

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.

运行结果:

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

后记1(2007.12.1):GDI+ for C++Builder代码原本在BCB6下写的,安装BCB2007后,做了些调整修改,BCB2007下测试正常,但没有重新到BCB6环境测试,导致BCB6下出现些数据类型兼容性问题,对此,我首先表示抱歉!请小作修改(可能仅仅是加上个类型强制转换)即可,如果你下载了本文前面推荐地址的文档,请将GdipRegion.hpp的202行的:CheckStatus(GdipGetRegionHRgn(Native, g->Native, &Result.rPVOID));改为CheckStatus(GdipGetRegionHRgn(Native, g->Native, &(HRGN)Result.rPVOID));即可。如果你下载的是以前的下载地址http://download.csdn.net/source/280521,那么,除修改上面的内容外,还请对GdipColor.h文件作如下修改:

1、在class Color类声明前面添加:
struct TIdentMap : public TIdentMapEntry
{
TIdentMap(ARGB argb, char *name){ Value = (int)argb; Name = name; }
};
2、Color类的static TIdentMapEntry KnownColors[];改为static TIdentMap KnownColors[];Color类外面的初始化说明:
TIdentMapEntry Color::KnownColors[] =
{
kcAliceBlue, "kcAliceBlue",
kcAntiqueWhite, "kcAntiqueWhite",
(略)
};
改为
TIdentMap Color::KnownColors[] =
{
TIdentMap(kcAliceBlue, "kcAliceBlue"),
TIdentMap(kcAntiqueWhite, "kcAntiqueWhite"),
(略,后面的所有项都如此修改)
};
3、方法Color::ARGBToString体内的TVarRec v = argb;改为TVarRec v = (int)argb;

后记2(2007.12.13):为满足一些朋友们的要求,从今天起,将推出GDI+ for VCL基础系列文章,主要供GDI+初学者入门参考,例子采用本文介绍的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;
}
另外,Delphi的Gdiplus.pas第1577行和BCB的GdipGraphics.hpp第50行的imeHighQualityBicubic改为imHighQualityBicubic;Delphi的Gdiplus.pas第843行的LineJoinRound改为ljRound;Delphi和BCB的TImageLockMode类型的前缀不一致,Delphi为im,而BCB的却为lm,这个改不改无所谓(我把BCB改为im前缀Gdipheaders.hpp的426行-431行)。

更新(2008.9.3):在Delphi2009环境下,由于TObject增加了一个Equals方法,与TGpMatrix和TGpRegion的方法Equals重名,为避免产生编译警告,可在这2个类的Equals方法说明的后面加上:

{$IF RTLVersion >= 20}reintroduce; overload;{$IFEND}

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/maozefa/archive/2007/11/12/1880484.aspx

根据提供的引用,可以了解到GDI+是一种图形设备接口,它是微软公司开发的一种图形设备接口,用于在Windows操作系统上呈现二维图形。GDI+支持半透明窗体和PNG图片,可以实现漂亮、个性化的窗体效果。而Delphi是一种基于Pascal语言的集成开发环境,可以用于开发Windows应用程序。因此,可以使用Delphi和GDI+技术来实现半透明窗体和PNG图片的效果。 关于如何使用Delphi和GDI+技术实现半透明窗体和PNG图片的效果,可以参考以下步骤: 1.在Delphi中创建一个新的VCL Forms应用程序。 2.在Form的OnCreate事件中添加以下代码,以启用窗体的半透明效果: ```delphi AlphaBlend := True; AlphaBlendValue := 200; // 设置透明度 ``` 3.在Form的OnPaint事件中添加以下代码,以绘制PNG图片: ```delphi var png: TPngImage; begin png := TPngImage.Create; try png.LoadFromFile('image.png'); Canvas.Draw(0, 0, png); finally png.Free; end; end; ``` 4.在Form的OnMouseDown事件中添加以下代码,以实现右键菜单: ```delphi if Button = mbRight then begin PopupMenu1.Popup(Mouse.CursorPos.X, Mouse.CursorPos.Y); end; ``` 5.在Form的OnCreate事件中添加以下代码,以将窗体置顶: ```delphi FormStyle := fsStayOnTop; ``` 6.在Form的OnDestroy事件中添加以下代码,以释放GDI+资源: ```delphi GdiplusShutdown(gdiplusToken); ``` 关于引用中的问题,可能是由于保存的文件路径不正确或没有写入文件的权限导致的。可以检查文件路径是否正确,并确保程序有写入文件的权限。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值