您的位置:首页 > 其它

以BMP图片为蓝本创建不规则窗口

2009-11-18 10:51 405 查看
Windows可以创建不规则的窗口,例如QQ宠物,例如下图。



很定会有人有做一个类似程序的想法,例如我。但是,如何去做呢?先抛开其他的一切,从最简单的说起:如何创建一个不规则的窗口呢?

就是通过这个语句:

SetWindowRgn(hWnd, hRgn, true);

第一个参数是需要设置成不规则窗口的窗口的句柄,第二个参数就是不规则窗口的样子,第三个窗口是控制是否重新绘制。

很显然,最重要的就是第二个参数。不规则窗口到底“不规则”到什么程度,就靠他了。

以我的理解,RGN,就是由曲线包围的区域。

HRGN hRgn = CreatePolygonRgn(point, index*2, ALTERNATE);

用上面的语句可以创建一个RGN句柄。其中第一参数是一个指向POINT结构体的指针(其实是POINT结构体数组),第二个参数是数组大小,第三个参数是填充模式。(RGN是一个区域,内部是需要系统填充的,但是创建的时候只需要提供包围这个区域的曲线上的点就行了,可以说就是曲线上的像素点)

 

那么,问题就来了。如何获得需要的区域的包围曲线呢……我选择了用BMP图片为蓝本创建RGN。于是,现在就是如何扫描BMP图片上的像素点了。

具体获得像素点数据方法什么的可以看程序,现在我只说明思想。如何获得RGN包围曲线。

为了方便的获得包围曲线,这张BMP图片必须有一个透明色:即,这个颜色是不会被包含进RGN曲线区域的。

我的做法是将图片左上角第一个像素的颜色作为透明色(当然,这张BMP图片也是以同样的思想制作出来的)

首先,在获得了BMP的像素数据以后,从图片第一行像素的左侧开始向右侧扫描。当扫描到第一不是透明色的地方时,记录这个点。这里就是RGN曲线区域的一个边缘。然后就是进入第二行,然后重复,直到左侧完全扫描完毕。这个时候,就在从图片第一行右侧开始向左侧扫描,然后重复,直到外轮廓完全的扫描完全。

然后将左侧的点以从上到下的顺序排列,右侧的点以从下到上顺序排列,将这些点依次放入同一个数组中,然后就可以使用创建RGN句柄的语句创建出一个不规则的区域了。

现在,我们就有了一个外轮廓了。对于简单的图片来说,这样就已经够了。RGN已经有了。但是,对于最开始的那一张图。明显还有问题:那个不规则窗口中间是镂空的。而且,对于图片凹进去的部分明显没有办法处理。

于是,我们开始第二步:修改刚刚创建出来的RGN。

所谓的镂空,其实就是挖洞。在已经存在的区域内把不想要的地方剔除。

如何在已经存在的RGN区域内挖洞呢?

CombineRgn(hRgn, hRgn, TempRgn, RGN_XOR);

有上面的这个语句。这个就是合并RGN的语句。很好理解。第一个参数是结果,第二,第三个参数是需要合并的两个RGN,而第四个参数就是最重要的合并方法了。这里使用的是RGN_XOR,即异或,如果第三个参数中的区域在第二个参数中的区域内,则将第三个参数内的区域从第二个参数中去掉。这就是挖洞了。

那么,需要挖掉什么东西呢?答案应该很容易想出来——内部的透明区域。

这里,我们还使用逐行扫描的方法:当前行第一个透明像素到接下来的第一个非透明像素中间就是需要挖掉的透明区域。于是挖掉,然后重复,直到这一行的透明区域都被挖掉,然后换下一行。

做完以后。一个RGN区域就有了。而且是具有镂空效果的。

 

这里在稍微说一点,就是这个RGN的使用问题。

SetWindowRgn(hWnd, hRgn, true);

在使用了这个语句以后,这个hRgn就无法使用了。你无法再次使用这个hRgn创建第二个不规则窗口。所以在使用hRgn之前,需要首先复制一个出来。复制的方法其实很简单。

HRGN hRgnTemp = CreateRectRgn(0, 0, 0, 0);

CombineRgn(hRgnTemp,hRgn,NULL,RGN_COPY);

创建创建一个空的RGN,然后使用合并RGN将需要使用的hRgn通过RGN_COPY拷贝的方法赋给这个临时RGN,然后就用这个临时RGN创建不规则窗口吧。

 

我写了一个方法,专门用来使用一个BMP图片创建RGN的。这个方法是不基于MFC的,不需要MFC就可使用。

//-----------------------------------------------------------------------------------------------------
// 名称: createRgn()
// 参数: HBITMAP _hBitMap: 一个BITMAP的句柄
// 返回: HRGN句柄
// 描述: 根据一张BMP图片句柄创建一个Rgn句柄,用于设置窗口外观。
//-----------------------------------------------------------------------------------------------------
HRGN WinImage::createRgn(HBITMAP _hBitMap)
{
BITMAP * bitMap;        // BITMAP对象,用于储存BITMAP信息
BYTE * BMP;             // BMP数组,储存BITMAP的RGB信息数组
POINT * leftPoint;      // 左POINT数组,储存获得的点信息
POINT * rightPoint;     // 右POINT数组,储存获得的点信息
POINT * point;          // POINT数组,储存获得的点信息
int index = 0;          // POINT数组索引
int bmpIndex = 0;       // BMP数组索引
COLORREF color;			// 透明颜色值
bitMap = new BITMAP();
GetObject(_hBitMap, sizeof(BITMAP),bitMap);
int bytesPix = bitMap->bmBitsPixel/8;
int count = bitMap->bmHeight*bitMap->bmWidth*bytesPix;
BMP = new BYTE[count];
leftPoint = new POINT[bitMap->bmHeight];
rightPoint = new POINT[bitMap->bmHeight];
GetBitmapBits(_hBitMap,count, BMP);
// 设置左上角第一个像素颜色为默认透明色
color = RGB(BMP[2],BMP[1],BMP[0]);
// 左边轮廓扫描
for (int row=0; row<bitMap->bmHeight; row +=1)
{
for (int col=0; col<bitMap->bmWidth*bytesPix; col+=4)
{
bmpIndex = row*bitMap->bmWidth*bytesPix + col;
if(BMP[bmpIndex] != GetBValue(color) || BMP[bmpIndex+1] != GetGValue(color) || BMP[bmpIndex+2] != GetRValue(color))
{
leftPoint[index].x = (col/4)%bitMap->bmWidth;
leftPoint[index].y = row;
index++;
break;
}
}
}
// 右边轮廓扫描
index = 0;
for (int row=0; row<bitMap->bmHeight; row+=1)
{
for (int col=bitMap->bmWidth*bytesPix-1; col>=0; col-=4)
{
bmpIndex = row*bitMap->bmWidth*bytesPix + col;
if(BMP[bmpIndex-3] != GetBValue(color) || BMP[bmpIndex-2] != GetGValue(color) || BMP[bmpIndex-1] != GetRValue(color))
{
rightPoint[index].x = (col/4)%bitMap->bmWidth+1;
rightPoint[index].y = row;
index++;
break;
}
}
}
point = new POINT[index*2];
for(int i=0; i<index; i++)
{
point[i] = leftPoint[i];
point[i+index] = rightPoint[index-i-1];
}
HRGN hRgn = CreatePolygonRgn(point, index*2, ALTERNATE);

int start = 0;
int length = 0;
bool isStart = false;

4000
for (int row=0; row<index; row +=1)
{
for (int col=leftPoint[row].x; col<rightPoint[row].x; col+=1)
{
bmpIndex = leftPoint[row].y*bitMap->bmWidth*bytesPix + col*bytesPix;
if(BMP[bmpIndex] == GetBValue(color) && BMP[bmpIndex+1] == GetGValue(color) && BMP[bmpIndex+2] == GetRValue(color))
{
if(!isStart)
{
isStart = true;
start = col;
}
length++;
}
else
{
if(isStart)
{
HRGN TempRgn = CreateRectRgn(
start%bitMap->bmWidth,
leftPoint[row].y,
(start+length)%bitMap->bmWidth,
leftPoint[row].y+1);
CombineRgn(hRgn, hRgn, TempRgn, RGN_XOR);
DeleteObject(TempRgn);
start = start+length;
length = 0;
isStart = false;
}
}
}
}
delete point;
delete leftPoint;
delete rightPoint;
delete BMP;
delete bitMap;
return hRgn;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  delete mfc byte windows null qq