[实例]鼠标穿透窗口 & 窗口渐变透明 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. 首先我们新建一张图片:1
    2. 其次我们再创建Alpha通道2
    3. 然后填充Alpha通道34

4.最后保存一下

56

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

7

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

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;
}

picpic_1

     处理前                                 处理后           

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

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

 UUZ_结果

posted @ 2015-05-23 23:37  小鸟喳喳叫  阅读(2437)  评论(1编辑  收藏  举报