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

 

posted on 2019-10-06 19:11  咚..咚  阅读(1381)  评论(0编辑  收藏  举报

导航