理论讲解请点击这里查看 ==》 传送门
注:本教程需要原料 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信息的图片(实现渐变透明)
- 首先我们新建一张图片:
- 其次我们再创建Alpha通道
- 然后填充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技术!啊哈哈哈,幽幽子粉们!