[实例]鼠标穿透窗口 & 窗口渐变透明 By 小鸟喳喳叫

理论讲解请点击这里查看 ==》 传送门

注:本教程需要原料 Photoshop(我用的是CS6)

第一步:创建一个窗口(实现鼠标穿透)

WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style            = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra        = 0;
    wcex.cbWndExtra        = 0;
    wcex.hInstance        = hInstance;
    wcex.hIcon            = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINTRANSPARENT));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName    = 0;
    wcex.lpszClassName    = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

RegisterClassEx(&wcex);

HWND hWnd = CreateWindowEx(WS_EX_LAYERED|WS_EX_TRANSPARENT,szWindowClass, szTitle,WS_POPUP,\*通过WS_EX_TRANSPARENT 让鼠标得以穿透窗口*\
      CW_USEDEFAULT, 0,200, 200, NULL, NULL, hInstance, NULL);

if (!hWnd)
   {
      return FALSE;
   }
  
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

第二步:创建包含Alpha信息的图片(实现渐变透明)

    1. 首先我们新建一张图片:

    2. 其次我们再创建Alpha通道
    3. 然后填充Alpha通道

4.最后保存一下

提醒一下:只有Alpha通道没有图像渲染出来还是什么都不是 这样编辑图像内容:

第三步:使用图片实现渐变透明(实现渐变透明)

HDC hdc= GetDC(hWnd);

\\获得我们需要填充的DC
   POINT pt;
   pt.x=0;pt.y=0;
   SIZE size;
   size.cx=200;
   size.cy=200;
   HDC rc=CreateCompatibleDC(hdc);
   HBITMAP bp =(HBITMAP) LoadImage(0,_T("pic.bmp"),IMAGE_BITMAP,200,200,LR_LOADFROMFILE);\\载入图片
   SelectObject(rc,bp);
   BLENDFUNCTION blend;
   blend.AlphaFormat = AC_SRC_ALPHA;
   blend.BlendOp = AC_SRC_OVER;
   blend.BlendFlags = 0;
   blend.SourceConstantAlpha = 255; \\由于我们使用的是图片内的Alpha通道,这项数值设为最大值255,意思是Alpha完全取决于你图片内的数值

UpdateLayeredWindow(hWnd,hdc,&pt,&size,rc,&pt,0,&blend,ULW_ALPHA);
   
    ReleaseDC(hWnd,hdc);
    SetWindowPos(hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);

但是….请注意,第三步没有结束,这段代码是有问题的!!!

我们会发现输出的结果不对,为什么呢??让我们来看一个印度小伙的解释:

you must ensure your pixel colors have been premultiplied against their respective alpha values. This is a requirement of the Win32 API in order to achieve the proper and intended results. In my case, I was not able to export from Photoshop a proper 32-bit BMP file with premultiplied alpha, so I perform this at load time. If you run an off-line tool or otherwise have premultiplied pixels, then it is not necessary to perform this step. To see an example of pre-multiplying, see the downloadable source code attached to this post.

你必须确定你的每个像素已经预先和你的Alpha数值乘过了,这是Win32API输出正确结果的特殊癖好.就我来说,用PS我做不到这一点,所以我在加载图像时预乘一下,如果你有方法解决这个问题,就不需要这一步.

重要概念——预乘

  • 什么是预乘?

预乘就是预先将图片的RGB通道乘以它的Alpha通道数值

  • 怎么预乘?

公式:(单个颜色值*Alpha/255)

  • 为什么要预乘?

答案是为了节约系统在混合时需要进行的运算:

我们知道半透明混合的公式大概是这样的:

结果值 =( 数值1 * ( 1 - Alpha/255) + 数值2 * Alpha /255 )

(MSDN:Dst.Red= Src.Red+ (1 - Src.Alpha) * Dst.Red)(这里的Alpha是1以内的小数)(这里的SRC已经经过预乘了)

如果进行了预乘,显然 数值2 * Alpha /255 这一步就可以快一些了。

PS:其实还是因为微软要求这样搞,要不我才不愿意- - 哼…..

已经写好的预乘算法:

for(int j = 0; j < g_iSplashImageHeight; ++j)
{
    for(int i = 0; i < g_iSplashImageWidth; ++i)
    {
        int index = ( g_iSplashImageHeight - j - 1 ) * g_iSplashImageWidth + i;

DWORD d = pImageData[index];

BYTE a = d >> 24;
        BYTE pmR = static_cast<BYTE>( ((d & 0x00FF0000) >> 16) * a / 255 );
        BYTE pmG = static_cast<BYTE>( ((d & 0x0000FF00) >> 8) * a / 255 );
        BYTE pmB = static_cast<BYTE>( ((d & 0x000000FF)) * a / 255 );
        d = pmB | (pmG << 8) | (pmR << 16) | (a << 24);
       
        pImageData[index] = d;
    }
}

被我玩坏的预处理器:

每次程序启动都要处理好麻烦,我就写了个预处理器,代码如下:

int _tmain(int argc, _TCHAR* argv[])
{
    printf("请将需要处理的图片拖入:\n");
    char* path = new char[261]  ;ZeroMemory(path,sizeof(path));gets_s(path,261);
    FILE* f;
    FILE* f2;
    if(*path==*"\"")
        {path = path+ 1 ;path[strlen(path)-1] = 0;}
    f = fopen(path,"rb+");
    f2 = fopen("pic.bmp","wb+");
    fseek(f,0,SEEK_END);
    int s = ftell(f);
    printf("filesize:%d",s);
    fseek(f,0L,SEEK_SET);
    fseek(f2,0L,SEEK_SET);
    void* date = new unsigned char [s];
    fread(date,s,1,f);
    // access the image bytes
    BITMAPFILEHEADER* pImageHeader =(BITMAPFILEHEADER*)date;
   
   
    DWORD* pImageData = (DWORD*)(pImageHeader->bfOffBits + (BYTE*)date);
    BITMAPINFOHEADER* bi=(BITMAPINFOHEADER *)((BYTE*)date+14);
    bi->biWidth;
    bi->biHeight;
    // store dimensions globally

// modify image bytes so the pixels are premultiplied
    for(int j = 0; j < bi->biHeight; ++j)
    {
        for(int i = 0; i < bi->biWidth; ++i)
        {
            int index = ( bi->biHeight - j - 1 ) * bi->biWidth + i;

DWORD d = pImageData[index];

BYTE a = d >> 24;
            BYTE pmR = static_cast<BYTE>( ((d & 0x00FF0000) >> 16) * a / 255 );
            BYTE pmG = static_cast<BYTE>( ((d & 0x0000FF00) >> 8) * a / 255 );
            BYTE pmB = static_cast<BYTE>( ((d & 0x000000FF)) * a / 255 );
            d = pmB | (pmG << 8) | (pmR << 16) | (a << 24);

pImageData[index] = d;
        }
    }
    fseek(f,0,SEEK_SET);
    fwrite(date,s,1,f2);
    printf(" writebytes:%d",ftell(f2));
    fclose(f);
    fclose(f2);
    //delete[] date;
    getchar();
    return 0;
}

     处理前                                 处理后           

总结:写完博客之后,突然发现如何创建不规则窗体也豁然开朗了起来…于是- -

成果:233333 233333 快感谢博主大人的神PS技术!啊哈哈哈,幽幽子粉们!

 

时间: 2024-10-10 14:35:50

[实例]鼠标穿透窗口 & 窗口渐变透明 By 小鸟喳喳叫的相关文章

[随笔]关于如何实现鼠标穿透窗口和窗口半透明

资料准备 WS_EX_TRANSPARENT | WS_EX_LAYERED WM_NCHITTEST & return HTTRANSPARENT SetLayeredWindowAttributes(-) UpdateLayeredWindow(-) 旁征博引 窗口类型 Overlapped Windows Pop-up Windows Child Windows Layered Windows             \\分层窗口(这是我们这节课研究的重点) Message-Only Wi

qt 窗口鼠标穿透

Qt 不规则窗体 – 鼠标点击穿透 qt实现鼠标穿透,如果要被穿透窗口只有一层,也即没有嵌套窗口,直接只用对子窗口使用setAttribute (Qt::WA_TransparentForMouseEvents,true);就ok,意味着鼠标事件让父窗口响应. 如果要穿透的窗口含有嵌套窗口,那么接着往下看 快速使用 /////////////////////////////////////////////////////////////// #ifdef Q_OS_LINUX  XShapeCo

把鼠标限制在窗口内

玩魔兽和LOL时注意到即时游戏窗口最小化,鼠标仍限制在窗口内,这具体是怎么实现的呢? Msdn上有个例子 Confining a Cursor: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 RECT rcClip;           // new area for ClipCursor RECT rcOldClip;        // previous area for ClipCursor   // Record t

JavaScript特效实例001-打开新窗口显示广告信息

实例001                 打开新窗口显示广告信息 实例说明 本实例要在窗口每次被加载的时候弹出一个广告对话框. 技术要点 本实例主要应用JavaScript的window对象. window对象的常用方法 方法 说明 alert() 弹出一个警告对话框 confirm() 在确认对话框中显示指定的字符串 prompt() 弹出一个提示对话框 close() 关闭被引用的窗口 focus() 将被引用的窗口放在所有打开窗口的前面 open() 打开新浏览器窗口并且显示由URL或名

JavaScript特效实例002-定时打开窗口

实例002                                  定时打开窗口 实例说明 本实例要在指定的时间后打开窗口. 技术要点 主要应用window对象setTimeout()方法实现定时打开窗口. setTime()方法的语法格式如下. <span style="font-size:14px;">setTimeout(function,milliseconds)</span> 参数说明如下. function:要调用的JavaScript自定

JavaScript特效实例016-动态显示窗口

实例016                      动态显示窗口 实例说明 在打开窗口时,将窗口设置为最小的宽度,并使窗口的高度逐渐增加,当窗口的高度与屏幕的高度一致时,使窗口的宽度不断增加,直到与屏幕的宽度相一致. 技术要点 本实例主要应用了screen对象的availWidth和availHeight属性获得当前屏幕的宽度和高度,并使用resizeTo()方法来自动增加窗口的高度和宽度. 实现过程 实现功能主页面Index.html <html> <head> <tit

winform和wpf如何实现鼠标穿透的效果

先看一下鼠标穿透的效果: 可以看到Form1这个程序虽然遮在了桌面的上面,但是我们还可以在窗体上点击桌面上的必应词典和网易邮箱大师,好像这个叫“Form1”的窗口被“穿透”一样. winform版本: 1.设置TransparencyKey=BackColor=control颜色,鼠标会穿透窗体 this.painel.BackColor = System.Drawing.SystemColors.Control;   //这里的Painel是一个System.Windows.Forms.Pan

linux下鼠标穿透和取消穿透--linux小白,大神无视

最近在用qt写一个跨平台的软件,因为设置了无边框,并且我自己给程序窗口加了阴影,阴影范围又比较大 所以必须给阴影区域加上鼠标穿透才能有更好的体验. 上网查了一下,在windows下使用SetWindowLong就行 在linux下的搜了好久也找到了一个方法 就是XShapeCombineRectangles(QX11Info::display(), winId(), ShapeInput, 0,0, NULL, 0, ShapeSet, YXBanded);(需要#include <X11/ex

C#窗体设计------鼠标穿透窗体

技巧解析:主要用到了两个系统API函数 SetWindowLong和GetWindowLong 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9