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  __fastcall GetImageEncoderClsid(AnsiString format, PGUID Clsid)
{
    UINT num, size 
=   0 ;
    DllExports::GdipGetImageEncodersSize(
& num,  & size);
    
if  (size  ==   0 return   false ;
    ImageCodecInfo 
* Info  =  (ImageCodecInfo * )DllExports::GdipAlloc(size);
    
if (Info  ==  NULL)  return   false ;
    UINT i 
=   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  __fastcall TGdipBitmap::SaveToStream(Classes::TStream *  Stream)
{
    TGUID Clsid;
    
if  (ImageFormat  !=   ""   &&  GetImageEncoderClsid(ImageFormat,  & Clsid))
    {
        Gdiplus::Bitmap Image(Handle, Palette);
        TStreamAdapter 
* Adapter  =   new  TStreamAdapter(Stream, soReference);
        Adapter
-> _AddRef();
        
try
        {
            
if  (Image.Save((IStream * ) * Adapter,  & Clsid)  !=  Ok)
                
throw   new  Exception( " Save Image fail " );
        }
        __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,这段代码根本就运行不下去。

 

你可能感兴趣的:(Stream,image,Delphi,Constructor,C++Builder,GDI+)