浅谈:屏幕与窗口

来源:互联网 发布:stussy淘宝正品店 编辑:程序博客网 时间:2024/06/14 06:21

本文将从客观、讨论、假设和具有一定可行性的角度来阐述屏幕和窗口的关系:

小B:P哥,你能给我说说屏幕和窗口的关系么。
P哥:可以啊!我先问你一个很直观的问题。

小B:问吧!
P哥:你知道屏幕是由什么基础单位组成么?

小B:一个个的像素咯,以矩阵的形式分布
P哥:对,那你知道每一个像素点色彩的表现么?

小B:三原色是 红(R)、绿(G)、蓝(B),像素点的色彩表现应该是以这三种颜色的单色或者混合色表现出来的
P哥:嗯没错,我再问你一个问题

P哥:你觉得屏幕和图片有什么共同点呢?
小B:都是由一个个的像素组成

P哥:那不同点呢?
小B:屏幕是一种现实设备,而图片则是由一堆的像素数据组成

P哥:对,图片保存了一个个像素的色值,然后根据色值映射到屏幕上。

P哥:在这里,我简单的介绍下32位图片和24位图片,32位图一个像素占用4个字节,它在内存中以B(蓝)、G(绿)、R(红)、A(Alpha)四通道的形式排列,每一个

字节是一个通道的数值,范围在0 ~ 255之间。


小B:哥,不是只有三原色么,那个Alpha是什么?
P哥:Alpha是透明通道,在客观的角度来讲是指控制像素的透明度,你平时看到的png、bmp格式的图片就是32位图片

小B:原来如此,那24位图应该就是R、G、B三通道咯,是jpg这些图片吧
P哥:对

小B:个,我有一个疑问,自然界中明明只有RGB三原色,那个Alpha是怎么控制图片的透明度的?
P哥:这里的问题就涉及到一个技术:Alpha混合

小B:什么是Alpha混合呢?
P哥:首先,有些东西你是要知道的,我们平时看到的桌面背景图片肯定是24位图。其次,32位图的每一个像素都是经过跟它相对的像素合成后再映射到屏幕设备的。

P哥:在上面的基础上我假设往桌面贴一张png的图片,这张图片的上半部分是透明的,下半部分时半透明的。topColor(0,0,0,0)是黑色全透明,bottomColor(255,255,255,100)是白色半透明,背景图是一张纯红色的图(255,0,0)


P哥:这里以红色通道为例,Alpha混合的算法是:
screenR = bkR * (1 - Alpha/255)+ dstR * (Alpha/255)
bkR:背景对应的某一个像素的红色色值, dstR:目标图片上对应的某一个像素的红色色值, screenR:最终合成显示到屏幕的红色色值
算法优化:screenR = bkR +  Alpha *(dstR - bkR)/ 255

过程:
我要从屏幕坐标(100,100)位置开始贴。
首先,我要去目标图(0,0)坐标的像素色值(0,0,0,0),
接着,取出桌面上(100,100)位置的像素色值(255,0,0),// 值得注意得是:这里我指的是桌面,并非单纯的背景图
最后,我把两个色值用Alpha混合算法合成一个新的像素色值 — 红色(255,0,0),然后把这个色值映射到屏幕(100,100)位置上...省略上半部分合成过程。
现在到了下半部分了,从图片上取出的像素色值是(255,255,255,100),算法:G通道 = 0 * (1-100/255)+ 255 *(100/255) = 100
合成的色值为(255,100,100),最后映射到屏幕点上。

这里的几个步骤就是图片经过Alpha混合映射到屏幕的过程。由此可见,Alpha通道并不是真正意义上的透明通道,只是在底色和图片色值合成中,充当这一个分量

Key。


小B:我懂了,就是说32位图片像素是根据底色动态合成一个新的色值再映射到屏幕上的。
P哥:图片就谈这么多,现在说说窗口对象

P哥:如果我没猜错的话,窗口在创建的时候,内核会为其开辟一块帧缓冲区(图片缓冲区),这块缓冲区锁占的内存大小在 Min(窗口高 * 宽 * 4字节)~ Max(屏幕高 * 

宽)之间

小B:你怎么知道会开辟一块缓冲区呢?
P哥:使用缓冲技术可以让那些静态窗口(非动态,如视频),在屏幕移动时CPU消费更低。

小B:哦,那我猜这块内存应该是Min咯。

P哥:我倒认为是Max。因为窗口大小发生变化Min需要重新向内存池申请一块新的内存并进行内存复制,这样的消费很大,而Max则不需要,再者桌面的程序不多,一个占

用1M多的内存并没有太大的影响


小B:那按你这么说,我想桌面也算一个大的窗口吧,是不是也应该会有缓冲区呢?
P哥:理论上应该有,因为窗口在位移后,那么他曾经所在的位置就要被填充,所以需要一块desktop缓冲区

小B:那哥,我在想普通的窗口跟普通置顶窗口发生重叠的裁剪问题只需要在像素着色到屏幕之前判断是否在置顶窗口范围就行了,
但普通窗口跟不规则置顶窗口的裁剪怎么弄呢?

P哥:关于你这个问题我提出了一个解决办法:
首先,要申请一块(屏幕高 * 宽 * 4字节)的标志表flagsTable,将其全部置0,同时为每一个窗口增加一个isTopmost标志,
非置顶窗口,该标志设为0,置顶则为1。当我们要把窗口绘制到屏幕是,假设要将窗口(0,0)位置的像素绘制到屏幕(100,100)的坐标上,

我们先给标志表赋值:
flagsTable[100][100] = isTopmost  |  flagsTable[100][100]

整个过程会把置顶窗口范围的标志表的值置为1,那么当我为窗口的每一个像素着色到屏幕之前,判断一下对应的标志位:
if (isTopmost ≥ flagsTbale[x][y]) 若为真,进行着色

同理,对于不规则窗口,可以先判断像素的Alpha值是否为0,0就不着色,当然我们可以约定全透明的像素值等于0来提高判断的效率。


P哥:这里,我说下我对透明不规则窗体实现的看法:
我认为设置了透明的窗体实际只是将像素的Alpha置为0,而当鼠标落在该窗体的透明像素上的时候,通过判断Alpha的值
来确定是否向该窗体投放鼠标消息。当然,实现这功能也需要一个标志来区分普通窗体和这种非普通透明窗体,而这个标志也许就是窗口的扩展属性:分层
在此,可能会有疑问:为什么要区分呢,因为windows窗体创建时可以指定透明画布NULL_BRUSH,但这个画布鼠标是不能穿透的。因此需要一个标志

P哥:由此,当我们的鼠标进入透明不规则窗体的矩形范围是,就可以判断该位置的窗口像素值Alpha是否为0,是则不投递鼠标消息到该窗口。

小B:我还有一个疑问,就是子控件有独立开辟的缓冲区么?
P哥:我猜没有,因为当你设置控件透明度的时候,你会发现整个窗体都发生透明度改变,由此我猜子控件是直接绘制到窗口缓冲区的,
不然完全可以实现子控件单独透明。再者,因为顶级窗体可以自由平移需要缓冲区来减少CPU的消费,子控件就不需要了。

P哥:在这我再谈谈那个标志裁剪法,这是我根据判断窗口的Z序(近屏幕和远屏幕窗口)来对普通窗口裁剪的,
虽然我对3D并不是很熟悉,但理论上只要把表的值该为近屏幕面像素的Z值,进行判断就可以实现裁剪、消隐,
但效率和可行性我都以客观的态度来看待。

作者:Tan承诺
日期:2017/9/15




原创粉丝点击