Window 窗口属性及嵌入窗口到D3D渲染窗口顶层

Window窗口有很多属性,可以通过设定window的style和ex style中知道。


这些属性有时候在实现某些效果的时候,是非常非常重要(以前都没有怎么详细了解,只是在用到对应的API时看一下,根本没有详细深入)。


这里想记录一下最近工作上遇到的问题,是关于窗口的层次关系或者可以说是彼此之间的包含关系,和窗口控制的经验


首先,需要知道parent和owner之间的区别:

1、如果一个窗口有WS_CHILD属性,那么它一定有一个父窗口,即GetParent()返回的一定是其父窗口;

2、如果一个窗口有WS_CHILD属性,那么它一定不能够是popup/overlap(这两个属性在CreateWindow里面出入,或者使用GetWindowLong/SetWindowlong,GWL_STYLE进行设定)窗口;

3、top-level窗口是指,在桌面下一层的窗口,它只能是popup/overlap窗口;

4、popup/overlap窗口,是top-level窗口,通常popup窗口是我们看到的普通的dialog窗口,而overlap窗口是我们用MFC直接创建的那些single document的窗口;

5、owner窗口只能是top-level窗口,owned窗口也是top-level窗口;

6、GetParent()函数返回的是对应窗口的parent,或者是owner窗口

7、GetWindow(hWnd, GW_OWNER)返回的只能够是owner窗口,否则就是NULL;

8、要获取真正的parent窗口,逻辑应该这样:UINT style = GetWindowLong(hWnd, GWL_STYLE);  return style & WS_CHILD ? GetParent(hWnd) : GetWindow(hWnd, GW_OWNER);


由于以下这篇文章写得实在太好了,我是参考下面得出上面的结论的:


http://www.laho.gov.cn/cjs_new/print.jsp?oldID=34


这个blog也有对窗口属性的详细讲解,我也参考了一些呢:

http://www.cppblog.com/Clouderman/default.html?page=2


然后就是MSDN上的东西也是需要看的:


http://msdn.microsoft.com/en-us/library/ms632599%28v=VS.85%29.aspx


msdn貌似才是最权威的,但上面的那个文章中提及的东西和其中的实验,非常值得一看,看完上面,再看下面,会比较实际,呵呵


如何将一个窗口挂在一个D3D渲染的窗口前面,目前有三种选择:

1、创建owner/owned窗口

由于owned窗口一定在owned窗口前面,所以这个方法是行得通的。window的机制,能够确保:

1) owner窗口渲染完之后,再渲染owned窗口;

2) owner窗口最小化时,owned窗口也一起最小化;

3) owner窗口恢复的时候,owned窗口也一起恢复;

如果你在spy++中查看owner和owned窗口的层次,你会发现他们是在同一层的,owner/owned窗口关系没有parent/child窗口关系强!(详细的看上面的链接,里面有更清楚的描述)。

我这里想说的是,owner/owned窗口,有一个限制条件,就是owner/owned窗口都必须是top-level窗口。这就出问题了:如果我的D3D窗口是某个窗口的子窗口,怎么办呢?那么这时候,就会很容易出现问题了,由于渲染的D3D窗口本身是一个child窗口,十分容易收到父窗口的影响,而你创建的这个owned窗口,是受渲染窗口的父窗口控制的,由于你通常都是拥有渲染窗口的HANDLE,有时候owner窗口做出的操作并不是你想要的!这时候就麻烦了,需要通过检测各种各样的消息避免一些你不想出现的情况。

PS:创建owner/owned窗口的SDK方法是:CreateWindow("mywinclass", "title", WS_POPUP, x, y, w, h, hOwnerWnd, NULL, hInst, NULL); 其中WS_POPUP和hOwnerWnd这里必须要填,WS_POPUP可以是WS_OVERLAPPED属性(因为这样可以创建出一个top-level窗口)

2、parent/child关系的窗口

其实可以将owner/owned看作是一种弱的parent/child关系。所以上面描述的有点,parent/child都有。但parent不需要一定是top-level,所以你的child肯定可以绑定在parent上,而且一定是显示在parent窗口前面,这正是我们想要的。但parent在invalid的时候,会发送repaint消息到child中,由于是D3D窗口(一般在30帧渲染一次,导致窗口invalid),所以一定会导致child窗口不停收到repaint的消息,而出现闪烁的问题。

ms已经想到有这个情况了,所以提供了一个WS_CLIPCHILEDREN的属性,用来设定parent的窗口,这样,parent在invalid的时候,就不会不停地发消息过去了。

所以这里也需要注意了,这个child窗口的渲染,必须你自己来管理,如果你依赖parent窗口invalid的时候刷新的话,那么不好意思,肯定有BUG了

3、top-most窗口

这个不用多说了吧,在SetWindowPos的时候可以设定的,不同通常用在全屏是,其他情况应该很少用到

要将 Qt 视频渲染到透明窗口上,可以使用 Direct3D实现。以下是大致步骤: 1. 创建一个透明窗口(设置窗口样式为 WS_EX_LAYERED 并调用 SetLayeredWindowAttributes 函数设置透明度)。 2. 创建一个 Direct3D 设备,并在其中创建一个纹理。 3. 将 Qt 视频渲染到这个纹理上(可以使用 QAbstractVideoSurface 类)。 4. 使用 Direct3D 将该纹理渲染到透明窗口上。 具体实现可以参考以下代码: ```cpp // 创建透明窗口 HWND hwnd = CreateWindowEx( WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST, L"myWindowClass", L"My Transparent Window", WS_POPUP, 0, 0, 800, 600, NULL, NULL, hInstance, NULL); // 设置窗口透明度 SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA); // 创建 Direct3D 设备和纹理 IDirect3D9* d3d = Direct3DCreate9(D3D_SDK_VERSION); IDirect3DDevice9* device = NULL; D3DPRESENT_PARAMETERS parameters = {0}; parameters.Windowed = TRUE; parameters.SwapEffect = D3DSWAPEFFECT_DISCARD; parameters.BackBufferFormat = D3DFMT_UNKNOWN; d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &parameters, &device); IDirect3DTexture9* texture = NULL; device->CreateTexture(width, height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture, NULL); // 将 Qt 视频渲染到纹理上 MyVideoSurface* surface = new MyVideoSurface(texture); QMediaPlayer* player = new QMediaPlayer; player->setVideoOutput(surface); player->setMedia(QUrl::fromLocalFile("myvideo.mp4")); player->play(); // 渲染纹理到透明窗口上 device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0); device->BeginScene(); LPDIRECT3DSURFACE9 backbuffer = NULL; device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer); device->StretchRect(texture, NULL, backbuffer, NULL, D3DTEXF_NONE); device->EndScene(); device->Present(NULL, NULL, NULL, NULL); ``` 其中,MyVideoSurface 类是继承自 QAbstractVideoSurface 的自定义类,用于将视频渲染到 Direct3D 纹理上。具体实现可以参考 Qt 官方文档。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值