GDI+ 在Delphi、C++Builder程序的应用 -- IStream

GDI+的Image及派生类中涉及到IStream流,在Delphi和C++Builder中广泛使用的TStream不能直接作为参数进行传递,VCL提供了一个TStreamAdapter类,用于把VCL流TStream转换为IStream。TStreamAdapter的构造过程原型如下:

constructor Create(Stream: TStream; Ownership: TStreamOwnership = soReference);

其中的TStreamOwnership枚举类型有2个值:soReference和,如果是soOwned,TStream对象由TStreamAdapter释放,否则由用户自己处理。请看下面的用流建立Image的Delphi代码:

var
Stream:TMemoryStream;
Adapter:TStreamAdapter;
Image:TGpImage;
begin
Stream:
= TMemoryStream.Create;
Stream.LoadFromFile(
' Test.jpg ' );
Adapter:
= TStreamAdapter.Create(Stream, soOwned);
Image:
= TGpImage.Create(Adapter);
try
.....
finally
Image.Free;
end;
end;

代码中,首先建立一个内存流,并从磁盘中装入一个图像文件,然后用TStreamAdapter将这个流封装起来,转换为IStream,供建立TGpImage使用。注意,我在建立TStreamAdapter对象时使用了soOwned,这样,由Adapter负责释放Stream,否则,必须在Image.Free后面Stream.Free。但是TStreamAdapter对象Adapter为什么没有释放代码呢?因为TStreamAdapter是个接口实现类,作为IStream传递给TGpImage.Create后,由系统自动跟踪释放,如果我们将它Free,会引起错误,如果非要显式释放它,必须用var I: IStream; I := Adapter; I := nil;的方法释放,而且必须在Image.Free之前,因为在Image.Free的同时,Adapter也随着IStream一起被释放掉了。

C++Builder与Delphi享有同一个VCL,也可按照Delphi的方法使用TStream,但是,由于C++Builder使用的GDI+代码包含在Gdiplus名字空间中,所有函数均采用inline方式,不是真正的函数调用方式,按照Delphi的方式,用TStreamAdapter将TStream转换为IStream传递给Image的构造函数,将会出现运行时刻错误,因为如果Image的构造函数是真正的函数,在传递接口参数时,会对接口对象作隐式的引用调用,而inline函数一般只是直接将函数体内容插入调用函数的地方,没有真正地传递参数的过程,所以,我们必须显式的做一个引用调用。请看下面保存Image到流的C++代码片断:

bool __fastcallGetImageEncoderClsid(AnsiStringformat,PGUIDClsid)
{
UINTnum,size
= 0 ;
DllExports::GdipGetImageEncodersSize(
& num, & size);
if (size == 0 ) return false ;
ImageCodecInfo
* Info = (ImageCodecInfo * )DllExports::GdipAlloc(size);
if (Info == NULL) return false ;
UINTi
= 0 ;
try
{
DllExports::GdipGetImageEncoders(num,size,Info);
for (;i < num && CompareText(Info[i].MimeType,format) != 0 ;i ++ );
if (i < num)
memcpy(Clsid,
& Info[i].Clsid, sizeof (TGUID));
}
__finally
{
DllExports::GdipFree(Info);
}
return i < num ? true : false ;
}

void __fastcallTGdipBitmap::SaveToStream(Classes::TStream * Stream)
{
TGUIDClsid;
if (ImageFormat != "" && GetImageEncoderClsid(ImageFormat, & Clsid))
{
Gdiplus::BitmapImage(Handle,Palette);
TStreamAdapter
* Adapter = new TStreamAdapter(Stream,soReference);
Adapter
-> _AddRef();
try
{
if (Image.Save((IStream * ) * Adapter, & Clsid) != Ok)
throw new Exception( " SaveImagefail " );
}
__finally
{
Adapter
-> _Release();
}
}
else
TBitmap::SaveToStream(Stream);
}

代码中,显式的调用了TStreamAdapter的_AddRef()和_Release()函数,如果将这个调用省略,运行时会出现错误。

由于本人是业余爱好者,水平和功力有限,错误在所难免,欢迎来信指导:[email protected]

补充(2008.9.7):从本文举例中可以看到,在C++Builder的GDI+中 使用TStreamAdapter,必须显式的调用其_AddRef()和_Release()函数,否则会出错,而Delphi中使用TStreamAdapter,似乎不必显式调用_AddRef和_Release,程序就工作的很好,但是如果多次使用TStreamAdapter的同一个对象,同样也会出错,也需要显式调用_AddRef和_Release。而Delphi的TStreamAdapter的_AddRef和_Release不是public方法,只能转换为IStream后调用。请看下面的代码片断:

var
tmp: TGpBitmap;
Adapter: TStreamAdapter;
Stream: TStream;
Clsid: TGUID;
begin
Stream := TMemoryStream.Create;
Adapter := TStreamAdapter.Create(Stream, soOwned);
GetEncoderClsid('image/bmp', Clsid);
IStream(Adapter)._AddRef;
Image.Save(Adapter, Clsid);
tmp := TGpBitmap.Create(Adapter);
try
......

......
finally
tmp.Free;
IStream(Adapter)._Release;
end;

代码中的Image是函数中的一个TGpImage类型参数,代码先把Image保存到内存流,然后用这个流建立一个临时TGpBitmap对象,便于进行操作,操作完毕,释放临时对象。为了适应GDI+的IStream接口,代码中用TStreamAdapter进行了转换,如果不显式地调用_AddRef和_Release,这段代码根本就运行不下去。

你可能感兴趣的:(C++,c,C#,Delphi,C++Builder)