[纯GDI]23行代码实现不规则半透明窗口(附源码) 本帖最后由 VisualEleven 于 2012-09-26 13:40:14 编辑 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 csdn资源上传了,还没给我地址= = 源码:#include "StdAfx.h"#include <windows.h>#include <crtdbg.h>#include "WYImgHelper.h"#define WND_WIDTH 800#define WND_HEIGHT 600#define WND_CLASSNAME "MainWnd"#define WND_TITLENAME "Test"#define FULLSCREEN 0struct 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;UINT* g_pBmp = NULL;int g_bmpWidth = 0;int g_bmpHeight = 0;void AlphaBlend32(UINT* pDstBmp, int dst_width, UINT* pSrcBmp, int src_width, int blend_width, int blend_height){ 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 pxor mm2, mm2 ; 把MM2清0 punpcklbw mm0, mm2 ; src:8 bit到16 bit以容纳结果,32bit expand to 64 bit punpcklbw mm1, mm2 ; dst:8 bit到16 bit以容纳结果.32bit expand to 64 bit movq mm3, mm1 ; 因为要用dst的Alpha值 punpckhwd mm3, mm3 ; 高字移动到双字 punpckhdq mm3, mm3 ; 双字移动到四字,现在有八个像素的Alpha了! movq mm4, mm0 ; mm4 = dst movq mm5, mm1 ; mm5 = src psubusw mm4, mm1 ; dst-src,饱和减,小于0为0 psubusw mm5, mm0 ; src-dst,饱和减,小于0为0 pmullw mm4, mm3 ; Alpha * (src-dst) pmullw mm5, mm3 ; Alpha * (dst-src) psrlw mm4, 8 ; 除以256,now mm4 get the result,(src-dst)<0 部分 psrlw mm5, 8 ; 除以256,now mm5 get the result,(dst-src)>0 部分 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 ; 紧缩到低32bit ; 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 }; float scale_x = 0; float scale_y = 0; float tscale_x = 1.0f; float tscale_y = 1.0f; float sumW = 0; float sumH = 0; int visibleW = 0; int visibleH = 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; } visibleW = srcRect.right - srcRect.left + 1; 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);// for (int h=0; h<visibleH; h++)// {// memcpy(pDst, pSrc, visibleW*4);// pSrc += width;// pDst += g_GDI.m_Width;// }}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 = new UINT[g_GDI.m_Width*g_GDI.m_Height]; 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分量 } { WuYuan::STGA_HEADER tgaHeader; FILE* pFile = WuYuan::TGA_OpenFile("test.tga", &tgaHeader); g_pBmp = new UINT[tgaHeader.ImageWidth*tgaHeader.ImageHeight]; WuYuan::TGA_ReadPixel(pFile, &tgaHeader, g_pBmp, true); fclose(pFile); g_bmpWidth = tgaHeader.ImageWidth; g_bmpHeight = tgaHeader.ImageHeight; } 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_pBmp != NULL) { delete [] g_pBmp; g_pBmp = NULL; } if (g_GDI.m_hMainSurface != 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(){ // 将主表面选入内存DC g_GDI.m_hOldBitmap = (HBITMAP)::SelectObject(g_GDI.m_hMemoryDC, g_GDI.m_hMainSurface); // 清空后台像素 memset(g_GDI.m_pBackBuffer, 0, g_GDI.m_Width*g_GDI.m_Height*4); DrawBmp(g_pBmp, 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 = 0; blend.BlendFlags = 0; blend.AlphaFormat = AC_SRC_ALPHA; blend.SourceConstantAlpha = 255; ::UpdateLayeredWindow(g_GDI.m_hWnd, g_GDI.m_hMainDC, &dstPos, &dstSize, g_GDI.m_hMemoryDC, &srcPos, 0, &blend, ULW_ALPHA); } } if (g_GDI.m_hOldBitmap != NULL) { ::SelectObject(g_GDI.m_hMemoryDC, g_GDI.m_hOldBitmap); g_GDI.m_hOldBitmap = NULL; }}LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ switch (uMsg) { case WM_LBUTTONDOWN: // 使鼠标单击窗口任何一个地方都能移动整个窗口 ::SendMessageA(hWnd, WM_SYSCOMMAND, 0xF012, 0); break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); return 0; } break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, uMsg, wParam, lParam);}int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int nCmdShow){ // 内存泄漏检测#if defined(DEBUG) | defined(_DEBUG) _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );#endif WNDCLASSEXA wndClass; wndClass.cbSize = sizeof(wndClass); wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = NULL;//LoadIcon(hInstance, MAKEINTRESOURCE(IDI_NORMAL)); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = NULL;//MAKEINTRESOURCE(IDC_DAHUAXY2); wndClass.lpszClassName = WND_CLASSNAME; wndClass.hIconSm = NULL;//LoadIcon(wndClass.hInstance, MAKEINTRESOURCE(IDI_SMALL)); RegisterClassExA(&wndClass); DWORD wndStyle = WS_POPUP | WS_MINIMIZEBOX | WS_SYSMENU; // 无标题栏:WS_POPUP | WS_MINIMIZEBOX | WS_SYSMENU; if (FULLSCREEN) wndStyle = WS_POPUP; RECT rcWnd = { 0, 0, WND_WIDTH, WND_HEIGHT }; AdjustWindowRect(&rcWnd, wndStyle, FALSE); HWND hWnd = CreateWindowExA(0, WND_CLASSNAME, WND_TITLENAME, wndStyle, (GetSystemMetrics(SM_CXSCREEN)-WND_WIDTH)/2, // SM_CXSCREEN:以像素为单位的屏幕的宽度 (GetSystemMetrics(SM_CYSCREEN)-WND_HEIGHT)/2, // SM_CYSCREEN:以像素为单位的屏幕的高度 (rcWnd.right-rcWnd.left), (rcWnd.bottom-rcWnd.top), NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); Init(hWnd); MSG msg; memset(&msg, 0, sizeof(msg)); while (TRUE) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } // 帧开始 const int constFps = 60; float timeInOneFps = 1000.0f/constFps; // 每秒60帧,则1帧就是约16毫秒 DWORD timeBegin = GetTickCount(); MainLoop(); // 限帧 DWORD timeTotal = GetTickCount() - timeBegin; if (timeTotal < timeInOneFps) { // Sleep(DWORD(timeInOneFps-timeTotal)); } } End(); UnregisterClassA(WND_CLASSNAME, hInstance); return 0;} 完整下载地址:http://download.csdn.net/detail/weiwuyuan/4585498 用CreateDIBSection好点如果显示属性那里设置颜色质量为16位,CreateCompatibleBitmap创建的就不是32位位图了 可惜vc6上不能显示‘"test.tga"’ 内存leak:// leak if (g_GDI.m_pBackBuffer != NULL) { delete [] g_GDI.m_pBackBuffer; g_GDI.m_pBackBuffer = NULL; } 代码写的有点匆忙,csdn的贴子又不让修改。还是人家GameRes比较开放和民主. 修改后的完整源码:(图像数据在代码里,但内容需要自己填充)#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;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){ 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 = 0; blend.BlendFlags = 0; blend.AlphaFormat = AC_SRC_ALPHA; blend.SourceConstantAlpha = 255; ::UpdateLayeredWindow(g_GDI.m_hWnd, g_GDI.m_hMainDC, &dstPos, &dstSize, g_GDI.m_hMemoryDC, &srcPos, 0, &blend, ULW_ALPHA); } }}LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ switch (uMsg) { case WM_LBUTTONDOWN: // 使鼠标单击窗口任何一个地方都能移动整个窗口 ::SendMessageA(hWnd, WM_SYSCOMMAND, 0xF012, 0); break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); return 0; } break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, uMsg, wParam, lParam);}int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int nCmdShow){ // 内存泄漏检测#if defined(DEBUG) | defined(_DEBUG) _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );#endif WNDCLASSEXA wndClass; wndClass.cbSize = sizeof(wndClass); wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = NULL;//LoadIcon(hInstance, MAKEINTRESOURCE(IDI_NORMAL)); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndClass.lpszMenuName = NULL;//MAKEINTRESOURCE(IDC_DAHUAXY2); wndClass.lpszClassName = WND_CLASSNAME; wndClass.hIconSm = NULL;//LoadIcon(wndClass.hInstance, MAKEINTRESOURCE(IDI_SMALL)); RegisterClassExA(&wndClass); DWORD wndStyle = WS_POPUP | WS_MINIMIZEBOX | WS_SYSMENU; // 无标题栏:WS_POPUP | WS_MINIMIZEBOX | WS_SYSMENU; RECT rcWnd = { 0, 0, WND_WIDTH, WND_HEIGHT }; AdjustWindowRect(&rcWnd, wndStyle, FALSE); HWND hWnd = CreateWindowExA(0, WND_CLASSNAME, WND_TITLENAME, wndStyle, (GetSystemMetrics(SM_CXSCREEN)-WND_WIDTH)/2, // SM_CXSCREEN:以像素为单位的屏幕的宽度 (GetSystemMetrics(SM_CYSCREEN)-WND_HEIGHT)/2, // SM_CYSCREEN:以像素为单位的屏幕的高度 (rcWnd.right-rcWnd.left), (rcWnd.bottom-rcWnd.top), NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); Init(hWnd); MSG msg; memset(&msg, 0, sizeof(msg)); while (TRUE) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } // 帧开始 const int constFps = 60; float timeInOneFps = 1000.0f/constFps; // 每秒60帧,则1帧就是约16毫秒 DWORD timeBegin = GetTickCount(); MainLoop(); // 限帧 DWORD timeTotal = GetTickCount() - timeBegin; if (timeTotal < timeInOneFps) { // Sleep(DWORD(timeInOneFps-timeTotal)); } } End(); UnregisterClassA(WND_CLASSNAME, hInstance); return 0;} 我的vc6上可以显示tga了,但不知道为什么背景是 黑的 ! 补充:我不敢保证vc6所使用的win32库能够很好的支持SetDIBitsToDevice()和UpdateLayeredWindow()函数 UpdateLayeredWindow() 这样得到: HINSTANCE hInstTmp = LoadLibrary("User32.DLL"); if(hInstTmp) { //取得 UpdateLayeredWindow 函数指针 g_fun=(MYFUNC)GetProcAddress(hInstTmp, "UpdateLayeredWindow"); if(g_fun==0) MessageBox(hWnd,"UpdateLayeredWindow not found","Error",MB_OK); FreeLibrary(hInstTmp); } SetDIBitsToDevice() 有变化?我的系统是 xp sp2。 void WuYuan::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指令将寄存器清空 }}这是我的原始代码,看到里面被注释掉的部分了吧?他是C实现,如果你觉得mmx看不懂,你可以换成这个,这上面发的代码,就是把这段给删了而已。 vc如何同时打开超过一万个文件?在线等 怎么向打印机串口发送命令啊 VC下如何在主窗口和非模式对话框上同时播放动画 程序在运行的时候创建另外一个exe文件,怎么实现 求DDE数据交换的技术文章,急用。 如果这里版主再乱结帖的话,我也是会刷屏的 请问MSComm控件,怎样直接发送十六进制?急急。。。。 怎样做WH_JOURNALPLAYBACK和WH_JOURNALRECORD钩子? CListCtrl,如何编辑表项? report样式的 ListCtrl ,如何使某一 column 总在可视区域的最左边? 内存池 指定地址(比如全局共享区)分配内存 clear case 只读属性的文件写入
#include <windows.h>
#include <crtdbg.h>#include "WYImgHelper.h"#define WND_WIDTH 800
#define WND_HEIGHT 600
#define WND_CLASSNAME "MainWnd"
#define WND_TITLENAME "Test"
#define FULLSCREEN 0
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;UINT* g_pBmp = NULL;
int g_bmpWidth = 0;
int g_bmpHeight = 0;
void AlphaBlend32(UINT* pDstBmp, int dst_width, UINT* pSrcBmp, int src_width, int blend_width, int blend_height)
{
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
pxor mm2, mm2 ; 把MM2清0
punpcklbw mm0, mm2 ; src:8 bit到16 bit以容纳结果,32bit expand to 64 bit
punpcklbw mm1, mm2 ; dst:8 bit到16 bit以容纳结果.32bit expand to 64 bit
movq mm3, mm1 ; 因为要用dst的Alpha值
punpckhwd mm3, mm3 ; 高字移动到双字
punpckhdq mm3, mm3 ; 双字移动到四字,现在有八个像素的Alpha了!
movq mm4, mm0 ; mm4 = dst
movq mm5, mm1 ; mm5 = src
psubusw mm4, mm1 ; dst-src,饱和减,小于0为0
psubusw mm5, mm0 ; src-dst,饱和减,小于0为0
pmullw mm4, mm3 ; Alpha * (src-dst)
pmullw mm5, mm3 ; Alpha * (dst-src)
psrlw mm4, 8 ; 除以256,now mm4 get the result,(src-dst)<0 部分
psrlw mm5, 8 ; 除以256,now mm5 get the result,(dst-src)>0 部分
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 ; 紧缩到低32bit
; 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 }; float scale_x = 0;
float scale_y = 0;
float tscale_x = 1.0f;
float tscale_y = 1.0f;
float sumW = 0;
float sumH = 0; int visibleW = 0;
int visibleH = 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;
} visibleW = srcRect.right - srcRect.left + 1;
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);
// for (int h=0; h<visibleH; h++)
// {
// memcpy(pDst, pSrc, visibleW*4);
// pSrc += width;
// pDst += g_GDI.m_Width;
// }
}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 = new UINT[g_GDI.m_Width*g_GDI.m_Height]; 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分量
} {
WuYuan::STGA_HEADER tgaHeader;
FILE* pFile = WuYuan::TGA_OpenFile("test.tga", &tgaHeader);
g_pBmp = new UINT[tgaHeader.ImageWidth*tgaHeader.ImageHeight];
WuYuan::TGA_ReadPixel(pFile, &tgaHeader, g_pBmp, true);
fclose(pFile); g_bmpWidth = tgaHeader.ImageWidth;
g_bmpHeight = tgaHeader.ImageHeight;
} 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_pBmp != NULL)
{
delete [] g_pBmp;
g_pBmp = NULL;
} if (g_GDI.m_hMainSurface != 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()
{
// 将主表面选入内存DC
g_GDI.m_hOldBitmap = (HBITMAP)::SelectObject(g_GDI.m_hMemoryDC, g_GDI.m_hMainSurface); // 清空后台像素
memset(g_GDI.m_pBackBuffer, 0, g_GDI.m_Width*g_GDI.m_Height*4); DrawBmp(g_pBmp, 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 = 0;
blend.BlendFlags = 0;
blend.AlphaFormat = AC_SRC_ALPHA;
blend.SourceConstantAlpha = 255;
::UpdateLayeredWindow(g_GDI.m_hWnd, g_GDI.m_hMainDC, &dstPos, &dstSize, g_GDI.m_hMemoryDC, &srcPos, 0, &blend, ULW_ALPHA);
}
} if (g_GDI.m_hOldBitmap != NULL)
{
::SelectObject(g_GDI.m_hMemoryDC, g_GDI.m_hOldBitmap);
g_GDI.m_hOldBitmap = NULL;
}
}LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
// 使鼠标单击窗口任何一个地方都能移动整个窗口
::SendMessageA(hWnd, WM_SYSCOMMAND, 0xF012, 0);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps);
return 0;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
} return DefWindowProc(hWnd, uMsg, wParam, lParam);
}int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int nCmdShow)
{
// 内存泄漏检测
#if defined(DEBUG) | defined(_DEBUG)
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif WNDCLASSEXA wndClass;
wndClass.cbSize = sizeof(wndClass);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = NULL;//LoadIcon(hInstance, MAKEINTRESOURCE(IDI_NORMAL));
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.lpszMenuName = NULL;//MAKEINTRESOURCE(IDC_DAHUAXY2);
wndClass.lpszClassName = WND_CLASSNAME;
wndClass.hIconSm = NULL;//LoadIcon(wndClass.hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassExA(&wndClass); DWORD wndStyle = WS_POPUP | WS_MINIMIZEBOX | WS_SYSMENU; // 无标题栏:WS_POPUP | WS_MINIMIZEBOX | WS_SYSMENU;
if (FULLSCREEN)
wndStyle = WS_POPUP; RECT rcWnd = { 0, 0, WND_WIDTH, WND_HEIGHT };
AdjustWindowRect(&rcWnd, wndStyle, FALSE); HWND hWnd = CreateWindowExA(0,
WND_CLASSNAME,
WND_TITLENAME,
wndStyle,
(GetSystemMetrics(SM_CXSCREEN)-WND_WIDTH)/2, // SM_CXSCREEN:以像素为单位的屏幕的宽度
(GetSystemMetrics(SM_CYSCREEN)-WND_HEIGHT)/2, // SM_CYSCREEN:以像素为单位的屏幕的高度
(rcWnd.right-rcWnd.left), (rcWnd.bottom-rcWnd.top),
NULL,
NULL,
hInstance,
NULL); ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd); Init(hWnd); MSG msg;
memset(&msg, 0, sizeof(msg)); while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break; TranslateMessage(&msg);
DispatchMessage(&msg);
} // 帧开始
const int constFps = 60;
float timeInOneFps = 1000.0f/constFps; // 每秒60帧,则1帧就是约16毫秒
DWORD timeBegin = GetTickCount(); MainLoop(); // 限帧
DWORD timeTotal = GetTickCount() - timeBegin;
if (timeTotal < timeInOneFps)
{
// Sleep(DWORD(timeInOneFps-timeTotal));
}
} End(); UnregisterClassA(WND_CLASSNAME, hInstance); return 0;
}
http://download.csdn.net/detail/weiwuyuan/4585498
如果显示属性那里设置颜色质量为16位,CreateCompatibleBitmap创建的就不是32位位图了
// leak
if (g_GDI.m_pBackBuffer != NULL)
{
delete [] g_GDI.m_pBackBuffer;
g_GDI.m_pBackBuffer = NULL;
}
代码写的有点匆忙,csdn的贴子又不让修改。还是人家GameRes比较开放和民主.
#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;
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)
{
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 = 0;
blend.BlendFlags = 0;
blend.AlphaFormat = AC_SRC_ALPHA;
blend.SourceConstantAlpha = 255;
::UpdateLayeredWindow(g_GDI.m_hWnd, g_GDI.m_hMainDC, &dstPos, &dstSize, g_GDI.m_hMemoryDC, &srcPos, 0, &blend, ULW_ALPHA);
}
}
}LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
// 使鼠标单击窗口任何一个地方都能移动整个窗口
::SendMessageA(hWnd, WM_SYSCOMMAND, 0xF012, 0);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps);
return 0;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
} return DefWindowProc(hWnd, uMsg, wParam, lParam);
}int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int nCmdShow)
{
// 内存泄漏检测
#if defined(DEBUG) | defined(_DEBUG)
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif WNDCLASSEXA wndClass;
wndClass.cbSize = sizeof(wndClass);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = NULL;//LoadIcon(hInstance, MAKEINTRESOURCE(IDI_NORMAL));
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.lpszMenuName = NULL;//MAKEINTRESOURCE(IDC_DAHUAXY2);
wndClass.lpszClassName = WND_CLASSNAME;
wndClass.hIconSm = NULL;//LoadIcon(wndClass.hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassExA(&wndClass); DWORD wndStyle = WS_POPUP | WS_MINIMIZEBOX | WS_SYSMENU; // 无标题栏:WS_POPUP | WS_MINIMIZEBOX | WS_SYSMENU; RECT rcWnd = { 0, 0, WND_WIDTH, WND_HEIGHT };
AdjustWindowRect(&rcWnd, wndStyle, FALSE); HWND hWnd = CreateWindowExA(0,
WND_CLASSNAME,
WND_TITLENAME,
wndStyle,
(GetSystemMetrics(SM_CXSCREEN)-WND_WIDTH)/2, // SM_CXSCREEN:以像素为单位的屏幕的宽度
(GetSystemMetrics(SM_CYSCREEN)-WND_HEIGHT)/2, // SM_CYSCREEN:以像素为单位的屏幕的高度
(rcWnd.right-rcWnd.left), (rcWnd.bottom-rcWnd.top),
NULL,
NULL,
hInstance,
NULL); ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd); Init(hWnd); MSG msg;
memset(&msg, 0, sizeof(msg)); while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break; TranslateMessage(&msg);
DispatchMessage(&msg);
} // 帧开始
const int constFps = 60;
float timeInOneFps = 1000.0f/constFps; // 每秒60帧,则1帧就是约16毫秒
DWORD timeBegin = GetTickCount(); MainLoop(); // 限帧
DWORD timeTotal = GetTickCount() - timeBegin;
if (timeTotal < timeInOneFps)
{
// Sleep(DWORD(timeInOneFps-timeTotal));
}
} End(); UnregisterClassA(WND_CLASSNAME, hInstance); return 0;
}
补充:
我不敢保证vc6所使用的win32库能够很好的支持SetDIBitsToDevice()和UpdateLayeredWindow()函数
HINSTANCE hInstTmp = LoadLibrary("User32.DLL");
if(hInstTmp)
{ //取得 UpdateLayeredWindow 函数指针
g_fun=(MYFUNC)GetProcAddress(hInstTmp, "UpdateLayeredWindow");
if(g_fun==0) MessageBox(hWnd,"UpdateLayeredWindow not found","Error",MB_OK);
FreeLibrary(hInstTmp);
} SetDIBitsToDevice() 有变化?我的系统是 xp sp2。
void WuYuan::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指令将寄存器清空
}
}
这是我的原始代码,看到里面被注释掉的部分了吧?他是C实现,如果你觉得mmx看不懂,你可以换成这个,这上面发的代码,就是把这段给删了而已。