windows api(GDI)实现图片旋转
GDI实现图片旋转,博主在网上找了好多资料,都不太如意。
并且在尝试中发现,如果先用SetViewportOrgEx将HDC上的坐标原点移动到图片中心;再在HDC上的获取每个点,用三角函数进行变换,算得新坐标进行点绘制。理论可行,但是因为double与int转换的关系,会丢失精度,旋转后图像会很模糊。
附:三角函数公式
逆时针:
x1=xcos(β)-ysin(β);
y1=ycos(β)+xsin(β);
顺时针:
x1=xcos(β)+ysin(β);
y1=ycos(β)-xsin(β);
笔者兜兜转转找了很多资料,发现有大神利用函数PlgBlt实现图片的旋转,但是用的是VB。根据理解,本人进行了修改,并译成了C++。代码如下,重点在于Rotate函数,并附上使用方法:
#include <cmath> using namespace std; extern "C" { #include <windows.h> #include <tchar.h> #include "resource.h" } #define ID_TIMER 1 /* 逆时针 x1=xcos(β)-ysin(β); y1=ycos(β)+xsin(β); 顺时针 x1=xcos(β)+ysin(β); y1=ycos(β)-xsin(β); */ LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam); void Rotate(HDC hdcDest,int xPos,int yPos,int angle,HDC hdcSrc,int xSrc,int ySrc,int srcWidth,int srcHeight); HWND hwnd; HINSTANCE hInst; int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) { TCHAR szAppName[]=TEXT("first win"); MSG msg; WNDCLASS wndclass; hInst=hInstance; wndclass.style=CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc=WndProc; wndclass.cbClsExtra=0; wndclass.cbWndExtra=0; wndclass.hInstance=hInstance; wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION); wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName=NULL; wndclass.lpszClassName=szAppName; if(!RegisterClass(&wndclass)) { MessageBox(NULL,TEXT("error"),szAppName,MB_ICONERROR); return 0; } hwnd=CreateWindow(szAppName, TEXT("hello"), WS_OVERLAPPEDWINDOW^WS_MAXIMIZEBOX^WS_THICKFRAME, 20, //CW_USEDEFAULT 50, 600, //CW_USEDEFAULT, 400, //CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hwnd,nShowCmd); UpdateWindow(hwnd); while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } HDC hdc; PAINTSTRUCT ps; HDC dcWin,dcBmp1,dcBmp2; HBITMAP hBmp1,hBmp2; POINT pt; RECT rect; int angle=0; void Rotate(HDC hdcDest,int xPos,int yPos,int angle,HDC hdcSrc,int xSrc,int ySrc,int srcWidth,int srcHeight,COLORREF col) { POINT pt[3]; POINT defPt[3]; double notPI=3.14/180; double thetS,thetC; int ret; pt[0].x=-srcWidth * 0.5; pt[0].y=-srcHeight * 0.5; pt[1].x = pt[0].x + srcWidth; pt[1].y = pt[0].y; pt[2].x = pt[0].x; pt[2].y = pt[0].y + srcHeight; thetS = sin(angle * notPI); thetC = cos(angle * notPI); defPt[0].x = (pt[0].x * thetC - pt[0].y * thetS) + xPos; defPt[0].y = (pt[0].x * thetS + pt[0].y * thetC) + yPos; defPt[1].x = (pt[1].x * thetC - pt[1].y * thetS) + xPos; defPt[1].y = (pt[1].x * thetS + pt[1].y * thetC) + yPos; defPt[2].x = (pt[2].x * thetC - pt[2].y * thetS) + xPos; defPt[2].y = (pt[2].x * thetS + pt[2].y * thetC) + yPos; HBRUSH hBrush=CreateSolidBrush(col); RECT rect; rect.left=rect.top=0; rect.right=rect.left+srcWidth; rect.bottom=rect.top+srcHeight; FillRect(hdcDest,&rect,hBrush); DeleteObject(hBrush); PlgBlt(hdcDest, &defPt[0], hdcSrc, xSrc, ySrc, srcWidth, srcHeight, 0, 0, 0); } LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) { switch(message) { case WM_CREATE: dcWin=GetDC(hwnd); dcBmp1=CreateCompatibleDC(dcWin); dcBmp2=CreateCompatibleDC(dcWin); hBmp1=LoadBitmap(hInst,MAKEINTRESOURCE(IDB_BITMAP1)); hBmp2=CreateCompatibleBitmap(dcWin,98,98); SelectObject(dcBmp1,hBmp1); SelectObject(dcBmp2,hBmp2); //将dcBmp1以(49,49)为坐标中心旋转angle角度后后画到dcBmp2中 Rotate(dcBmp2,49,49,angle,dcBmp1,0,0,98,98,0xffffff); SetTimer(hwnd,ID_TIMER,500,NULL); break; case WM_PAINT: hdc=BeginPaint(hwnd,&ps); SetBkMode(hdc,TRANSPARENT); BitBlt(hdc,0,0,98,98,dcBmp1,0,0,SRCCOPY); BitBlt(hdc,100,0,98,98,dcBmp2,0,0,SRCCOPY); EndPaint(hwnd,&ps); break; case WM_TIMER: if(wParam==ID_TIMER) { angle=(angle+15)%360; Rotate(dcBmp2,49,49,angle,dcBmp1,0,0,98,98,0xffffff); InvalidateRect(hwnd,NULL,true); UpdateWindow(hwnd); } break; case WM_DESTROY: ReleaseDC(hwnd,dcWin); DeleteDC(dcBmp1); DeleteDC(dcBmp2); DeleteObject(hBmp1); DeleteObject(hBmp2); KillTimer(hwnd,ID_TIMER); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd,message,wParam,lParam); }