Layered Window 透明窗体的实现总结

此片文章是以前写的, 刚刚新开了博客, 就发出来跟大家分享下。

这篇文章主要讲得是vc中各种分层、透明、不规则窗口的实现, 基本囊括GDI、GDI+能使用的所有方法。

本文讲述了三种方法,其中第一种方法有两种不同效果,第三种方法有两种不同的实现方式。文中有方法使用了GDi+,关于GDI+的使

用请自行查询资料,本文不进行细述。

  

方法一:窗体整体透明,支持子控件透明,支持OnPaint重绘。

这个方法比较简单,使用win32 Api 中SetLayeredWindowAttributes

函数即可,关于该函数可查询MSDN,用这种方法有两种效果:

效果1:窗体整体透明,子控件也透明,可以实现半透明效果

//第一步要修改窗体属性,WS_EX_LAYERED支持透明

LONG lWindowStyle = ::GetWindowLong(hwnd, GWL_EXSTYLE) |

WS_EX_LAYERED;

//设置Alpha不透明度

BYTE byteAlpha = 150;

//注意最后一个参数为LWA_ALPHA,第二个参数颜色掩码(透明

//色无用)

SetLayeredWindowAttributes(m_hwnd, 0/*any*/, byteAlpha,

----------------------- Page 2-----------------------

LWA_ALPHA )

效果2:窗体整体透明,子控件不透明,实现不规则窗体,区域透明。

首先需要一张背景位图,需要透明的地方用单一颜色填充,然后将其

贴在背景上,代码如下

第一步跟效果一中一样需修改窗体属性

::SetWindowLong(hwnd, GWL_EXSTYLE, lWindowStyle);

//将红色设为透明色, 注意透明区域鼠标并不能穿透RGB(255, 0, 0)

//为透明色

//注意最后一个参数为LWA_COLORKEY,第三个参数透明度无用

::SetLayeredWindowAttributes(hwnd, RGB(255, 0, 0), 111/*any*/,

LWA_COLORKEY);

需要注意的是效果1和效果2可以结合起来使用,最后一个参数改成

LWA_COLORKEY | LWA_ALPHA即可。使用

SetLayeredWindowAttributes函数实现不规则形状简单易行,但是通常

会有锯齿很难处理。

方法二:根据位图进行区域裁剪 ,关键函数CombineRgn和SetWindowRgn。

该方法跟方法一一样,需要将背景位图需要透明的

地方填充为单一颜色,该方法的原理是遍历位图中的每个像素,将需

要透明的像素过滤,将其他不需要透明的像素所在区域用

CombineRgn函数连接起来形成一个区域,然后用SetWindowRgn将贴

好背景图的窗体放进这个区域。此方法好处是可以实现镂空,即鼠标

穿透透明区域。缺点是遍历每个像素对于大的位图算法时间复杂度高,

效率很低。代码如下:

void CMeterHeadDlg::SetupRegion(CDC & pDC, HBITMAP cBitmap,

COLORREF TransColor)

{

CDC memDC;

HBITMAP pOldMemBmp=NULL;

BITMAP bit;

CRect rect;

GetWindowRect(rect);

CRgn wndRgn;

//创建于传入dc兼容的临时dc

memDC.CreateCompatibleDC(pDC);

//取得位图参数,要用其长和宽¨ª

::GetObjectA(m_hBkBitmap, sizeof(bit), &bit);
//将位图选入临时dc

pOldMemBmp= memDC.SelectBitmap(m_hBkBitmap);

//创建总的窗体区域

wndRgn.CreateRectRgn(0,0,rect.Width(),rect.Height());

for(int y=0;y<rect.Height()+1;y++)

{

CRgn rgnTemp; //保存临时区域

int iX = 0;

do

{

//等于透明色跳过找到下一个非透明色

if (memDC.GetPixel(iX, y) == TransColor)

{

rgnTemp.CreateRectRgn(iX,y,iX+1,y+1);

//合并region,注意ComebineRgn最后一个参数为“异或”

wndRgn.CombineRgn(wndRgn, rgnTemp, RGN_XOR);

//删除临时region

rgnTemp.DeleteObject();

}

iX++;

}while(iX <rect.Width()+1);

iX = 0;

}

if(pOldMemBmp)

memDC.SelectBitmap(pOldMemBmp);

SetWindowRgn(wndRgn,TRUE);

SetForegroundWindow(m_hWnd);

DeleteDC(memDC);

}

方法三:使用透明png贴图,并实现透明区域的透明。

此方法的优点是可以实现不规则形状贴图,鼠标能穿透透明区,并且边缘无锯齿。

该方法根据实现方式可分为两种方法

1、使用CImage(ATL和MFC中都有该类,直接用win32 api没有CImage,会麻烦点可能要用CreateFIle函数加载)绘制。

为什么我们正常的的使用CImage加载png透明区总是有白色背景呢?查了很多资料才

发现这其实是微软GDI+的设计问题,PNG 图片是ARGB,使用GDI+

载入图片的时候,GDI+会默认已经进行了预剩运算(PARGB),即

每象素的实际值是已经和ALPHA值按比例相乘的结果,实际上它根

本就没有做预乘, 在使用透明图片的象素ALPHA通道的时候,

CImage 内部正是调用的AlphaBlend,没有预乘的图当作预乘的图片

处理的结果就是这相当于一张和纯白背景进行了预剩, 所以图象总

是出现白色背景。所以我们只需要对症下药,载入图片前与处理下即

可:

if (Image.GetBPP() == 32) //确认32位包含alpha通道

for (i = 0; i < Image.GetWidth(); i++)

{

for (j = 0; j < Image.GetHeight(); j++)

{

byte *pByte =(byte*)Image.GetPixelAddress(i, j);

pByte[0] = pByte[0] * pByte[3] / 255;

pByte[1] = pByte[1] * pByte[3] / 255;

pByte[2] = pByte[2] * pByte[3] / 255;

}

}

}

最后调用CImage中Draw方法就可。

2、使用GDI+贴图,利用UpdateLayeredWindow

函数实现png透明区域透明。该函数请查询MSDN。这种方法不支持子

控件透明, 不支持OnPaint重绘代码如下:

首先在OnInitDialog中修改窗体属性

ModifyStyleEx(0, WS_EX_LAYERED | WS_OVERLAPPED);

下面为贴图函数,注意由于不支持OnPaint,所以需重绘是手动调用

贴图函数,贴图中使用到GDI+

//在OnInitDialog中初始化m_pBkImage ,m_pBkImage为

//Gdiplus::Image指针,Image::FromFile是从外面文件夹中导入png文

//件,若果是从本地资源文件中导入,需使用其他方法。

m_pBkImage = Image::FromFile(g_strResPath+_T("main_.png"));

下面为贴图函数

// 初始化时该函数放在OnInitDialog中调用,后面需要刷新时,手动

//调用,该方法贴的背景图不能响应WM_PAINT消息,也不能够在

//OnPaint函数中调用该绘图方法。

void CMainPanel::DrawAlphaPng()

{

CRect rcClient;

GetClientRect(&rcClient);

CClientDC dc(m_hWnd);

CDC memDc;

memDc.CreateCompatibleDC(dc.m_hDC);

CBitmap bmp;

bmp.CreateCompatibleBitmap(dc.m_hDC, rcClient.Width(),

rcClient.Height());

memDc.SelectBitmap(bmp);

//用GDI+显示图片

Graphics graph(memDc.m_hDC);

graph.DrawImage(m_pBkImage, 0,0 ,rcClient.Width(),

rcClient.Height());

BLENDFUNCTION _Blend;

_Blend.BlendOp = 0;

_Blend.BlendFlags = 0;

_Blend.AlphaFormat = 1;

_Blend.SourceConstantAlpha = 255;

SIZE sz = {rcClient.Width(), rcClient.Height()};

//::UpdateLayeredWindow(m_hWnd, hDC, &ptWinPos,&sizeWindow,

//hdcMemory, &ptSrc, 0, &stBlend, ULW_ALPHA);

UpdateLayeredWindow(m_hWnd, dc, &CPoint(0, 0), &sz, memDc,

&CPoint(0, 0), 0, &_Blend, ULW_ALPHA);

bmp.DeleteObject();

graph.ReleaseHDC(memDc.m_hDC);

ReleaseDC(dc.m_hDC);

}

需要注意的是使用方法三中第二种方法虽然不会出现锯齿,但是

会导致界面上的子空间全部透明,这样我们在界面上添加的控件都没

用。我试过在界面上用Create方法创建及对控件重绘,但是没用。解

决这个问题的方法是结合方法一中的效果二。需要两个窗口A (背景

窗口),B(用于放置控件)。用方法三第二种方式实现窗口A,使用

方法一种第二种效果创建B,MFC中可以在OnCtlColor函数中将背景

颜色设为单一颜色,ATL中则没有OnCtlColor,最简单的做法是在

OnEraseBKGND消息函数中返回TRUE(背景会变成白色),然后将背

景颜色设为透明色(这时B窗体会全透明,但是其上控件不会透明),

将A上所需要的控件放在B上相应位置,B窗口覆盖在A上面与其重合

(由于B透明所以B上控件看着像放在A上)。在移动B窗口时同时移

动A窗口。这样就能达到我们想要的效果。

除了这三种方法之外,使用GDI中TransparentBlt函数也可以实现

透明,该函数可以将一张有背景的贴图消除背景贴在窗体上。关于该

函数使用就不再介绍了,可以查询MSDN

以上所述三种方法本人都尝试过,都是可行的。如有疑问请向我

咨询。

时间: 2024-12-19 03:58:41

Layered Window 透明窗体的实现总结的相关文章

纯win32实现PNG图片透明窗体

#include <windows.h> #include <gdiplus.h> /*  GDI+ startup token */ ULONG_PTR gdiplusStartupToken; /*  Declare Windows procedure  */ LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); // UpdateLayeredWindow Defination typedef BOOL(

使用Layered Window遇到的一些问题及解决方法

1. 使用Layered Window需要设置 WS_EX_LAYERED 属性 2.  Layered Window不能作为Child Window 3. 它也不能包含子窗口,为什么呢,因为它收不到WM_PAINT消息,它上边的子窗口显示不了 4. 使用DrawText或者Graphics::DrawString,他们画出的字是透明的, 这个问题我不知道原因,但是解决方法很简单: 先将文字画到一个内存位图上,然后使用Bitblt的方式将此内存位图拷贝到目标dc即可 5.  SetWindowR

Qt Widget 利用 Qt4.5 实现酷炫透明窗体

本文讲述的是Qt Widget 利用 Qt4.5 实现酷炫透明窗体,QWidget类中的每一个窗口部件都是矩形,并且它们按Z轴顺序排列的.一个窗口部件可以被它的父窗口部件或者它前面的窗口部件盖住一部分. 先来看内容吧. Qt4.2引入了QWidget::setWindowOpacity函数, 可以为窗体设置透明度, 从0.0到1.0之间, 值越小越透明. 经过设置的窗体可以整体呈现透明的效果. 但这种设置比较粗糙, 只能设一个整体的效果,大概只有比如像拖动的时候能用一下,大多数时候都不太实用.在

C# 制作透明窗体

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; using ControlExs; namespace

透明窗体透明窗体 控件正常显示

//TransparentFrm透明窗体透明窗体 控件正常显示 {以图片的形状作为窗体形态}{使窗体透明 透明的只剩下个控件} // 调用 setFormTransParent(Form1); procedure setFormTransParent(Form:TForm); begin Form.BorderStyle:=bsNone; Form.TransparentColor:=True; Form.TransparentColorValue:=20; Form.Color:=Form.T

gdi+ 高速绘制透明窗体

gdi+ 高速绘制透明窗体: 方法一: 1.用Iamge对象载入png资源, 2.调用drawimage函数讲图片绘制出了 3.UpdateLayeredWindow对窗体进行布局 方法二: 1.用Bitmap对象载入资源 2.通过CDC选中,再用bitblt拷贝或者AlphaBlend融合到目标CDC上. 3.UpdateLayeredWindow对目标CDC上的hdc进行布局,达到融合背景的效果. 方法一是比較常规的方法,可是drawimage函数的效率太低,假设要实现实时更新的话就会有问题

DSAPI 透明窗体+WIN7磨砂+窗体投影组合

你可以使用DSAPI和DS控件库组合多种特效,以下是透明窗体+WIN7磨砂+窗体投影组合效果 设计界面 编写代码 Private 透明 As New DSAPI.控件.Form窗体.透明窗体助手 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load DSAPI.Win7特性.设置窗体为磨砂透明效果_指定形状(Me, New Region(New Rectangle(0, 0, Me.Width,

Binders 与 Window Tokens(窗体令牌)

原文地址:http://www.androiddesignpatterns.com/2013/07/binders-window-tokens.html 安卓的一项核心设计思想是希望能提供一个不须要依赖中央检验部门来检验程序请求的开放平台.为此,Android使用了程序沙盒和Linux的进程分离来防止程序以无法控制和不安全的方式訪问系统内部或者其它程序.这样的架构是开发人员与使用者共同选择的:既不须要额外的保护来防止恶意程序,同一时候系统要自己主动的处理好全部事情. 在非常长一段时间我对这样的安

PNG透明窗体全攻略(控件不透明)

http://blog.csdn.net/riklin/article/details/4417247 看好了,这是XP系统,未装.net.我的Photoshop学的不太好,把玻璃片弄的太透了些,如果你们有好的美术,再加上这种技术,肯定会如鱼得水.下面就来详细说说它的制作过程吧:     第 一步:在VC6中使用GDI+:你得从网上弄个GDI+ for XP的库,大约500K.如果找不到的话,找我QQ要吧,我会把这个窗口的源程序一起发给你的.把它解压后,将所有文件还包括子目录中的文件复制到你的项