首页  编辑  

窗体的Alpha通道透明支持

Tags: /超级猛料/VCL/Form,窗体/窗体特效/   Date Created:

窗体的Alpha通道透明色支持

参考: http://www.delphibbs.com/delphibbs/dispq.asp?lid=2190768

Windows 2000后,为了支持类似MAC界面的Alpha通道混合效果,提供了GDI+,提供了很多的界面功能函数,可以实现很好的界面效果。例如可以使用UpdateLayeredWindow来实现窗体的颜色透明。但是一旦 Form 被定义为利用 LayeredWindow ,窗口的绘图不再响应沿用多年的 WM_Paint 消息。

UpdateLayeredWindow(hwnd:HWND; // 窗口句柄

                   hdcDst:HDC; // 目标 DC

                   ptDst:pPoint; // 目标的 TopLeft

                   Size:pSize; // 显示 Size

                   hdcSrc:HDC; // 源 DC

                   ptSrc:pPoint; // 源 DC 的 TopLeft

                   crKey:COLORREF; // 透明颜色值

                   Blend:pBlendFunction; // Alpha 混合函数

                   dwFlags:DWord // 一组标志位常量

                   );

这个函数不但可以设置 Alpha Blend ,还可以完成类似本例中的异形窗口显示。标

志常量有以下几个:

 ULW_COLORKEY = $00000001; // 透明颜色设置是需要的

 ULW_ALPHA = $00000002; // Alpha 混合是需要的

 ULW_OPAQUE = $00000004; // 保持不透明

不管设置哪些常量,首先与 SetLayeredWindowAttributes 函数一样,

需要对窗口设置一个新的扩展风格标志:WS_EX_LAYERED 。

设置窗口的扩展标志,只对 Windows2000 以上操作系统有效,设置方法与设置普

通窗口风格的方法一样:

SetWindowLong(Handle, GWL_EXSTYLE,

             GetWindowLong(Handle,GWL_EXSTYLE)

             or WS_EX_LAYERED // 增加这个标志

            );

注意:如果设置了Alpha 混合标志,必须提供 BlendFunction 的值。

 PBlendFunction = ^TBlendFunction;

 TBlendFunction = packed record

   BlendOp: BYTE; // 取值可以为 AC_SRC_OVER

   BlendFlags: BYTE; // 必须是 0

   SourceConstantAlpha: BYTE; // 取值为希望得到的 Alpha 的值

   AlphaFormat: BYTE;

 end;

下面的代码演示如何实现窗体上面某个颜色为全透明,并可以穿透鼠标,效果图如下:

代码如下(Delphi 6):

procedure TForm1 . FormCreate ( Sender : TObject );

begin

 Color := clWhite ;

 TransparentColor := True ;

 TransparentColorValue := clWhite ;

end ;

这个比较简单,但是实际上还是用的上面的函数,察看Forms.pas单元就知道了。当然也可以使用图片来做效果:即设计一张合适的图片,然后使用上面的方法即可作出一个不规则的图片窗体出来,非常简单!!

function UpdateLayeredWindow ( hWnd : HWND ;

 hdcDst : HDC ; Dst : PPoint ; const size : PSize ;

 hdcSrc : HDC ; Src : PPoint ;

 crKey : COLORREF ;

 pblend : PBlendFunction ;

 dwFlags : DWORD ): BOOL ; stdcall ; external 'user32.dll' ;

procedure ColorUpdateLayeredWindow ( Wnd : HWND ; BMP : TBitmap ; TransColor : TColor );

var

 R : TRect ;

 S : TSize ;

 P : TPoint ;

begin

 GetWindowRect ( Wnd , R );

 P := Point ( 0 , 0 );

 S . cx := Bmp . Width ;

 S . cY := Bmp . Height ;

 SetWindowLong ( Wnd , GWL_EXSTYLE , GetWindowLong ( Wnd , GWL_EXSTYLE ) or WS_EX_LAYERED );

 UpdateLayeredWindow ( Wnd , 0 , @ R . TopLeft , @ S , Bmp . Canvas . Handle , @ P , TransColor , 0 , ULW_COLORKEY );

end ;

procedure AlphaUpdateLayeredWindow ( Wnd : HWND ; Bmp : TBitmap ; Alpha : Byte );

var

 P : TPoint ;

 R : TRect ;

 S : TSize ;

 BF : _BLENDFUNCTION ;

begin

 GetWindowRect ( Wnd , R );

 P := Point ( 0 , 0 );

 S . cx := Bmp . Width ;

 S . cY := Bmp . Height ;

 bf . BlendOp := AC_SRC_OVER ;

 bf . BlendFlags := 0 ;

 bf . SourceConstantAlpha := Alpha ;

 bf . AlphaFormat := AC_SRC_ALPHA ;

 SetWindowLong ( wnd , GWL_EXSTYLE , GetWindowLong ( wnd , GWL_EXSTYLE ) or WS_EX_LAYERED );

 UpdateLayeredWindow ( wnd , 0 , @ R . TopLeft , @ S , Bmp . Canvas . Handle , @ P , 0 , @ BF , ULW_ALPHA );

end ;

procedure TForm1 . FormCreate ( Sender : TObject );

var

 BMP : TBitmap ;

 GB : TGPBitmap ;

 h : HBITMAP ;

begin

 Bmp := TBitmap . Create ;

 Bmp . LoadFromFile ( 'F:\password.bmp' );

//  GB := TGPBitmap.Create('F:\ 水晶图标 \1.png');

//  GB.GetHBITMAP(0, h);

//  Bmp.Handle := h;

//  AlphaUpdateLayeredWindow(Handle, Bmp, 200);

 ColorUpdateLayeredWindow ( Handle , Bmp , Bmp . TransparentColor );

 Bmp . Free ;

//  GB . Free ;  

end ;

使用上面得ColorLayeredWindow函数,就可以使BMP作为一个通透的窗口出来,上面的方式不支持Alpha混合,因此对PNG之类的支持不好。使用AlphaLayeredWindow可以支持Alpha通道。如果要支持PNG图片,可以使用GDI+,也可以使用TPNGObject来读取PNG图片,下面代码使用GDI+,需要使用GDIPAPI和GDIPObj两个单元,这两个单元Google一把到处可以找到。

使用上面的方法,窗体上面的控件都需要自己绘制,否则不可见了,但是实际上是在的,可以响应事件等。

可惜上面的方法不能使窗体的某些部分半透明,设置AlphaBlend和AlphaBlendValue又会使所有的窗体部分半透明,达不到要求的效果,而且那种透明效果不太好,不能实现Alpha通道效果。那么如何实现下面的效果呢?

要实现这个效果,不知道XDeskWeather是如何实现的,但是使用SGlass可以做到窗体透明效果。

procedure TForm1.FormCreate(Sender: TObject);

var

  b : TBitmap;

  h : HBITMAP;

  gb: TGPBitmap;

begin

 BorderStyle := bsNone;

 Image1.Picture.LoadFromFile('F:\[2508]水晶图\[16]警告类\1.png');

 with TStainedGlass.Create(Self) do

 begin

   AltTransparency := 100;

   //BackStyle := bsCentered;

   BackStyle := bsMosaic;

   DelayTime := 1;

   b := TBitmap.Create;

//    gb := tgpbitmap.Create('F:\[2508]水晶图\[16]警告类\1.png');

//    gb.GetHBITMAP(0, h);

//    b.Handle := h;

   //Glyph := b;

 end;

end;

需要GDIPlus和SGlass,PNGImage的支持,你也可以使用前面的GDI+的方法,这样不需要PNGImage。附件中有SGlass单元。

附:XDeskWeather使用了GDI+,采用了Raize控件和PNGImage,采用PNGImage里面的TPNGObject来支持PNG图片。PNGImage非常好用,和JPEG库类似,自己使用一下就知道了。XDeskWeather的透明效果是不是GDI+来实现的,问过作者,没有反应。  :-(

据说XDesk采取两个窗体来实现的,一个透明作为背景,另外一个是普通的窗体。

---------------------------------------

2005,6月10号又翻出来XDeskWeather看了一下,找到它的资源文件,于是又决心研究一下,发现点击它的About对话框的背景的时候,其设置对话框中的控件会失去焦点,这样很明显是两个不同的窗体才会出现的情况,因此决定按两个窗口的方式来实现一下,结果发现原来如此简单~~~~~~: 两个窗口,一个背景,一个前台,控制同步移动即可!

下面是我做的效果:

参考附件代码!

结论:

使用Layer窗口,可以使用WM_PRINTCLIENT消息。最重要牢记一点:所有的窗口的更新都必须以图片方式提供给UpdateLayeredWindow函数!例如,你可以在你的窗口Active的时候,Move的时候,或者鼠标移动到你的控件的时候,在后台绘制一个图片,包括你的界面操作变化等,都需要更新图片,最后使用UpdateLayeredWindow函数更新这个位图即可!

AlphaTrans.rar (93.9KB)
SGlass.pas (11.3KB)
img_13644.bmp (583.0KB)
img_20298.bmp (654.8KB)
img_3813.bmp (337.1KB)
img_7867.bmp (335.8KB)
img_8940.bmp (280.6KB)