GDI完美实现半透明不规则窗口(总代码量381行,1个cpp文件)

  • M0_738967
    了解作者
  • 7.3KB
    文件大小
  • rar
    文件格式
  • 0
    收藏次数
  • VIP专享
    资源类型
  • 0
    下载次数
  • 2022-05-21 08:33
    上传日期
以前在研究半透明不规则窗口的时候,用的是gdi的TextOut/DrawText绘制字体,但绘制出的字体是透明的, 现在我完全放弃了GDI的TextOut()、BitBlt()的方式,而用SetDIBitsToDevice()代替,就是直接贴像素到DC里。 今天尝试用SetDIBitsToDevice()+UpdateLayeredWindow()实现半透明不规则窗口,结果非常顺利! 贴出来,共享给需要的朋友. 另外,本程序还附带了一个使用MMX汇编优化的32位alpha混合函数(57行,有详尽注释),需要的朋友可以直接copy了。 PS:关于字体的绘制 我建议大家用GetGlyphOutline()手动提取字形像素去绘制,因为TextOut这类绘制函数功能有限,只适合初级软件使用,而要想画出丰富多彩的界面,比如字体alpha渐变,就必须自己操作像素,所以GetGlyphOutline()才是专业级的,而且兼容性好,可以抽象出来提供给ddraw、d3d、opengl等使用
new.rar
  • GDI完美实现半透明不规则窗口
  • Main.vcproj
    3.9KB
  • Main.cpp
    27.3KB
内容介绍
#include <windows.h> #include <crtdbg.h> #define WND_WIDTH 100 #define WND_HEIGHT 100 #define WND_CLASSNAME "MainWnd" #define WND_TITLENAME "Test" struct SBmpInfo { BITMAPINFO m_BitmapInfo; RGBQUAD m_bmiColors[2]; // 为BITMAPINFO的m_bmiColors补充两个元素空间 }; struct SGDI { HWND m_hWnd; HDC m_hMainDC; HDC m_hMemoryDC; HBITMAP m_hMainSurface; HBITMAP m_hOldBitmap; int m_Width; int m_Height; UINT* m_pBackBuffer; SBmpInfo m_BmpInfo; }; SGDI g_GDI; extern UINT g_Bmp32[40*39]; int g_bmpWidth = 40; int g_bmpHeight = 39; void AlphaBlend32(UINT* pDstBmp, int dst_width, UINT* pSrcBmp, int src_width, int blend_width, int blend_height) { // C实现 // { // const int nextLineOffset_src = (src_width - blend_width) * 4; // 混合完一行像素后,通过加上该值,便可直接定位到下行起始像素 // const int nextLineOffset_dst = (dst_width - blend_width) * 4; // BYTE* pSrc = (BYTE*)pSrcBmp; // BYTE* pDst = (BYTE*)pDstBmp; // int below_A, below_R, below_G, below_B; // int above_A, above_R, above_G, above_B; // // for (int h=0, w=0; h<blend_height; h++) // { // for (w=0; w<blend_width; w++) // { // above_B = *pSrc++; // above_G = *pSrc++; // above_R = *pSrc++; // above_A = *pSrc++; // // if (above_A == 0) // { // pDst += 4; // continue; // } // // below_B = *pDst; // below_G = *(pDst+1); // below_R = *(pDst+2); // below_A = *(pDst+3); // // *pDst++ = below_B - (below_B-above_B)*above_A/255; // *pDst++ = below_G - (below_G-above_G)*above_A/255; // *pDst++ = below_R - (below_R-above_R)*above_A/255; // // if (below_A == 255) // *pDst++ = 255; // else // *pDst++ = below_A - (below_A-above_A)*above_A/255; // } // // pSrc += nextLineOffset_src; // pDst += nextLineOffset_dst; // } // return; // } const int nextLineOffset_src = (src_width - blend_width) * 4; // 混合完一行像素后,通过加上该值,便可直接定位到下行起始像素 const int nextLineOffset_dst = (dst_width - blend_width) * 4; __asm { mov edi, pDstBmp ; 目的像素 mov esi, pSrcBmp ; 源像素 xor ebx, ebx ; 已混合的高度 mov ecx, blend_width ; 要混合的宽度 BLEND_BEGIN: cmp dword ptr[esi], 0x00FFFFFF ; 如果alpha为0,则跳过混合部分 jna BLEND_END movd mm0, [edi] ; 把目的像素值移入mm0寄存器的低32位 movd mm1, [esi] ; 把源像素值移入mm1寄存器的低32位 ; Core Begin:result = b - (b-a)*a_alpha/255 (a为源像素分量,b为目的像素分量) pxor mm2, mm2 ; ① 把MM2清0 punpcklbw mm0, mm2 ; 将mm0与mm2按字节交叉组合,存入mm0,mm0 = 0x00AA00BB00GG00RR punpcklbw mm1, mm2 ; 将mm1与mm2按字节交叉组合,存入mm1,mm1 = 0x00AA00BB00GG00RR movq mm3, mm1 ; ② mm3 = 0x00AA00BB00GG00RR punpckhwd mm3, mm3 ; 将高32位按16位交错排列,mm3 = 0x00AA00AA00BB00BB punpckhdq mm3, mm3 ; 将高32位按32位交错排列,mm3 = 0x00AA00AA00AA00AA movq mm4, mm0 ; ③ mm4 = 目的像素 = 0x00AA00BB00GG00RR movq mm5, mm1 ; mm5 = 源像素 = 0x00AA00BB00GG00RR psubusw mm4, mm1 ; ④ dst-src,按字饱和减,小于0为0 psubusw mm5, mm0 ; src-dst,按字饱和减,小于0为0 pmullw mm4, mm3 ; (dst-src) * alpha,若dst-src为0,则mm4为0 pmullw mm5, mm3 ; (src-dst) * alpha,若src-dst为0,则mm5为0 psrlw mm4, 8 ; 按字右移8位,即除以256 psrlw mm5, 8 ; 按字右移8位,即除以256 paddusw mm0, mm5 ; 饱和加到原图象:D=Alpha*(O-S)+S,(src-dst)<0 部分 psubusw mm0, mm4 ; 饱和加到原图象D=S-Alpha*(S-O),(dst-src)>0 部分 packuswb mm0, mm0 ; 按16位有符号数压缩为8位无符号数 ; Core End movd [edi], mm0 ; 混合结果写进目的像素 BLEND_END: add edi, 4 add esi, 4 loop BLEND_BEGIN ; 循环 add esi, nextLineOffset_src ; 加上偏移量,使定位到下行起始处 add edi, nextLineOffset_dst inc ebx mov ecx, blend_width cmp ebx, blend_height ; 若ebx小于blend_height,则转移到上面继续混合 jb BLEND_BEGIN EMMS ; 因为从mm0到mm7,这些寄存器是“借用”浮点寄存器的低64位,所以每次在用完MMX指令后一定要用EMMS指令将寄存器清空 } } void DrawBmp(UINT* pBmp, int width, int height, int x, int y) { RECT srcRect = { 0, 0, width-1, height-1 }; RECT dstRect = { x, y, 0, 0 }; // 边界校正 { if (srcRect.right >= width) srcRect.right = width - 1; if (srcRect.bottom >= height) srcRect.bottom = height - 1; // 超出左边界 if (dstRect.left < 0) { srcRect.left += -dstRect.left; dstRect.left = 0; } // 超出上边界 if (dstRect.top < 0) { srcRect.top += -dstRect.top; dstRect.top = 0; } int visibleW = srcRect.right - srcRect.left + 1; int visibleH = srcRect.bottom - srcRect.top + 1; // 超出右边界 if (dstRect.left+visibleW > g_GDI.m_Width) { visibleW = g_GDI.m_Width - dstRect.left; srcRect.right = srcRect.left + visibleW-1; } // 超出下边界 if (dstRect.top+visibleH > g_GDI.m_Height) { visibleH = g_GDI.m_Height - dstRect.top; srcRect.bottom = srcRect.top + visibleH-1; } dstRect.right = dstRect.left + (srcRect.right-srcRect.left); dstRect.bottom = dstRect.top + (srcRect.bottom-srcRect.top); if (visibleW<=0 || visibleH<=0) return; } UINT* pSrc = pBmp + srcRect.top*width+srcRect.left; UINT* pDst = g_GDI.m_pBackBuffer + dstRect.top*g_GDI.m_Width+dstRect.left; AlphaBlend32(pDst, g_GDI.m_Width, pSrc, width, srcRect.right-srcRect.left+1, srcRect.bottom-srcRect.top+1); } bool Init(HWND hWnd) { // 初始化g_GDI { memset(&g_GDI, 0, sizeof(g_GDI)); g_GDI.m_hWnd = hWnd; g_GDI.m_Width = WND_WIDTH; g_GDI.m_Height = WND_HEIGHT; g_GDI.m_hMainDC = ::GetDC(hWnd); g_GDI.m_hMemoryDC = ::CreateCompatibleDC(NULL); g_GDI.m_hMainSurface = ::CreateCompatibleBitmap(g_GDI.m_hMainDC, g_GDI.m_Width, g_GDI.m_Height); g_GDI.m_pBackBuffer = (UINT*)malloc(g_GDI.m_Width*g_GDI.m_Height*4); BITMAPINFO& bmpInfo = g_GDI.m_BmpInfo.m_BitmapInfo; bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader); bmpInfo.bmiHeader.biWidth = g_GDI.m_Width; bmpInfo.bmiHeader.biHeight = -g_GDI.m_Height; bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biBitCount = 32; bmpInfo.bmiHeader.biCompression = BI_BITFIELDS; *(UINT*)(bmpInfo.bmiColors+0) = 0xFF0000; // red分量 *(UINT*)(bmpInfo.bmiColors+1) = 0x00FF00; // green分量 *(UINT*)(bmpInfo.bmiColors+2) = 0x0000FF; // blue分量 // 将主表面选入内存DC g_GDI.m_hOldBitmap = (HBITMAP)::SelectObject(g_GDI.m_hMemoryDC, g_GDI.m_hMainSurface); } DWORD dwExStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE); if (!(dwExStyle & WS_EX_LAYERED)) ::SetWindowLong(hWnd, GWL_EXSTYLE, dwExStyle|WS_EX_LAYERED); return true; } void End() { if (g_GDI.m_pBackBuffer != NULL) { free(g_GDI.m_pBackBuffer); g_GDI.m_pBackBuffer = NULL; } if (g_GDI.m_hMainSurface != NULL) { if (g_GDI.m_hOldBitmap != NULL) { ::SelectObject(g_GDI.m_hMemoryDC, g_GDI.m_hOldBitmap); g_GDI.m_hOldBitmap = NULL; } ::DeleteObject(g_GDI.m_hMainSurface); g_GDI.m_hMainSurface = NULL; } if (g_GDI.m_hMemoryDC != NULL) { ::DeleteDC(g_GDI.m_hMemoryDC); g_GDI.m_hMemoryDC = NULL; } if (g_GDI.m_hMainDC != NULL) { ReleaseDC(g_GDI.m_hWnd, g_GDI.m_hMainDC); g_GDI.m_hMainDC = NULL; } } void MainLoop() { // 清空后台像素 memset(g_GDI.m_pBackBuffer, 0, g_GDI.m_Width*g_GDI.m_Height*4); DrawBmp(g_Bmp32, g_bmpWidth, g_bmpHeight, 0, 0); // Flip { ::SetDIBitsToDevice(g_GDI.m_hMemoryDC, 0, 0, g_GDI.m_Width, g_GDI.m_Height, 0, 0, 0, g_GDI.m_Height, g_GDI.m_pBackBuffer, &g_GDI.m_BmpInfo.m_BitmapInfo, DIB_RGB_COLORS); { RECT rcWnd; ::GetWindowRect(g_GDI.m_hWnd, &rcWnd); POINT srcPos = { 0, 0 }; POINT dstPos = { rcWnd.left, rcWnd.top }; SIZE dstSize = { rcWnd.right-rcWnd.left, rcWnd.bottom-rcWnd.top }; BLENDFUNCTION blend; blend.BlendOp =
评论
    相关推荐
    • iPIC3D
      iPIC3D 大规模并行执行隐式粒子模拟方法在等离子体模拟中的应用 编译 创建一个构建目录(例如,构建)并调用CMake。 完成后,应在构建文件夹中生成可执行的iPIC3D *和库libiPIC3Dlib.a。 mkdir build cd build ...
    • cub3d
      cub3d
    • cub3d
      cub3d
    • 3D重建
      3D重建 在这个项目中,为了进行3D重建和对象化,我遵循了3个步骤: 1-对极整流:为了减少计算成本。 2点匹配3-3D点三角测量 点检测和匹配,三角测量部分将很快添加。
    • Milkshape3D
      Milkshape3D.zipMilkshape3D.zipMilkshape3D.zipMilkshape3D.zipMilkshape3D.zipMilkshape3D.zipMilkshape3D.zipMilkshape3D.zipMilkshape3D.zipMilkshape3D.zipMilkshape3D.zipMilkshape3D.zip
    • spindle3d
      一个斐济插件,用于自动测量有丝分裂纺锤体体积图像的3D形态学参数。 安装 安装 启动斐济并: 主轴3D 3D ImageJ套件 重新启动斐济
    • cub3d
      cub3d
    • PerspectiveMatrix3D
      开发Stage3D时所用到的两个adobe工具包中的两个类PerspectiveMatrix3D 和 AGALMiniAssembler。
    • 3d
      3D
    • viztool3d
      Viztool 3D 3D可视化以解释3D图形和数学