首页 > 解决方案 > winAPI 旋转屏幕上的图像,同时保持居中并更新背景

问题描述

我正在编写代码,用户可以从他们的计算机中选择图像并在屏幕上旋转它。但是,我目前遇到两个问题。首先,我意识到如果图像是矩形的,您可以在新旋转图像后面看到旋转之前的旧图片。其次,当我旋转图像时,它似乎围绕图片上的某个点旋转,有时会使图片脱离屏幕。所以我想知道如何在旋转 iamge 后不显示旧图像,以及如何使图像保持在屏幕中心。

这是我的旋转代码:

int flip = 1;
void rotateImage(HWND hWnd)
{
    HDC hdc = GetDC(hWnd);
    Graphics graphic(hdc);
    Image* image = Image::FromFile(filePath);
    int x = (GetSystemMetrics(SM_CXSCREEN) - image->GetWidth()) / 2;
    int y = (GetSystemMetrics(SM_CYSCREEN) - image->GetHeight()) / 2 - 50;
    int xx = image->GetWidth();
    int yy = image->GetHeight();
    if (flip == 1)
        image->RotateFlip(Rotate90FlipNone);
    else if (flip == 2)
        image->RotateFlip(Rotate180FlipNone);
    else if (flip == 3)
        image->RotateFlip(Rotate270FlipNone);

    RECT rc;
    HBRUSH hBr;
    SetRect(&rc, x, y, x + xx, y + yy);
    hBr = CreateSolidBrush(RGB(255, 255, 255));
    FillRect(hdc, &rc, hBr);

    Status status = graphic.DrawImage(image, x, y);
    RECT updateRect = { 0 };
    updateRect.left = x;
    updateRect.top = y;
    updateRect.right = updateRect.left + image->GetWidth();
    updateRect.bottom = updateRect.top + image->GetHeight();
    flip++;
    if (flip > 4) flip = 1;
    ReleaseDC(hWnd, hdc);
}

这部分代码

RECT rc;
HBRUSH hBr;
SetRect(&rc, x, y, x + xx, y + yy);
hBr = CreateSolidBrush(RGB(255, 255, 255));
FillRect(hdc, &rc, hBr);

我是否试图解决旋转后出现旧图像的问题,但似乎它切掉了太多的窗口并删除了窗口上的控件。

标签: c++winapi

解决方案


一些技巧:

  1. 每个呼叫GdiplusStartup都应与呼叫配对GdiplusShutdown
  2. 将窗口宽度和高度与图像进行比较。
  3. WM_PAINT在消息中进行绘图工作
  4. 使用窗口客户端RECT而不是屏幕RECT来绘制图像。
  5. 旋转图像后获取图像宽度和高度。

winAPI 旋转屏幕上的图像,同时保持居中并更新背景

下面的代码片段显示了提到的修改,可以为我实现这个目的。你可以参考。

BOOL fRotate = FALSE;
BOOL fFileOpened = FALSE;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HRESULT hr;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_CREATE:
        AddMenus(hWnd);
        AddControls(hWnd);
        hr = BufferedPaintInit();
        break;
    case WM_COMMAND:
        switch (wParam)
        {
        case FILE_MENU_EXIT:
            // File Menu Exit
            DestroyWindow(hWnd);
            break;
        case FILE_OPEN:
            fFileOpened = FALSE;
            OpenFileWindow(hWnd);
            InvalidateRect(hWnd, NULL, FALSE);
            break;
        case PIC_EDIT:
            if (fFileOpened)
            {
                displayDialogW(hWnd);
            }
            else
                MessageBoxA(NULL, "Pick an Image File to Edit!", "Error!", MB_ICONINFORMATION | MB_OKCANCEL);
            break;
        case PIC_ROTATE:
            if (fFileOpened)
            {
                fRotate = TRUE;
                InvalidateRect(hWnd, NULL, TRUE);
            }
            else
                MessageBoxA(NULL, "Pick an Image File to Rotate!", "Error!", MB_ICONINFORMATION | MB_OKCANCEL);
            break;
        }
        break;
    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC screen = BeginPaint(hWnd, &ps);

        // 3. Do draw work in WM_PAINT message
        if (fRotate)
        {
            rotateImage(hWnd);
            fRotate = FALSE;
        }
        else if (fFileOpened)
        {
            HPAINTBUFFER hbuff = BeginBufferedPaint(ps.hdc, &ps.rcPaint, BPBF_COMPATIBLEBITMAP, NULL, &screen);
            if (hbuff)
            {
                RECT rc;
                GetClientRect(hWnd, &rc);
                FillRect(screen, &rc, GetSysColorBrush(COLOR_WINDOW)); 
                putImage(screen, hWnd);
                hr = EndBufferedPaint(hbuff, TRUE);
            }
        }
        EndPaint(hWnd, &ps); } break;
    case WM_DESTROY:
        BufferedPaintUnInit();
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
        break;
    }

    return 0;
}

void OpenFileWindow(HWND hWnd)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (SUCCEEDED(hr))
    {
        IFileOpenDialog* pFileOpen;

        // Create the FileOpenDialog object.
        hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
            IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

        if (SUCCEEDED(hr))
        {
            // Show the Open dialog box.
            hr = pFileOpen->Show(NULL);

            // Get the file name from the dialog box.
            if (SUCCEEDED(hr))
            {
                IShellItem* pItem;
                hr = pFileOpen->GetResult(&pItem);
                if (SUCCEEDED(hr))
                {
                    PWSTR pszFilePath;
                    hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);

                    // Display the file name to the user.
                    if (SUCCEEDED(hr))
                    {
                        char szBuffer[255];
                        WideCharToMultiByte(CP_ACP, 0, pszFilePath, -1, szBuffer, sizeof(szBuffer), NULL, NULL);
                        // JPG/JPEG/PNG
                        filePath = pszFilePath;
                        fFileOpened = TRUE;
                        CoTaskMemFree(pszFilePath);
                    }
                    pItem->Release();
                }
            }
            pFileOpen->Release();
        }
        CoUninitialize();
    }
}

void putImage(HDC hdc, HWND hWnd)
{
    Graphics graphic(hdc);
    Image* image = Image::FromFile(filePath);

    // 4. Use window client RECT instead of screen RECT to draw image.
    RECT clientRect;
    GetClientRect(hWnd, &clientRect);
    LONG cx = clientRect.right - clientRect.left;
    LONG cy = clientRect.bottom - clientRect.top;

    // TODO: 2. compare window width and height with image's before subtract.
    int x = (cx - image->GetWidth()) / 2;
    int y = (cy - image->GetHeight()) / 2;
    Status status = graphic.DrawImage(image, x, y);

#if 1 //For testing purpose and feel free to remove.
    // Draw a line at window middle for checking is the image is centered
    LONG xMid = cx / 2;
    LONG yMid = cy / 2;
    Pen      pen(Color(255, 0, 0, 255));
    graphic.DrawLine(&pen, xMid, 0, xMid, clientRect.bottom);
    graphic.DrawLine(&pen, 0, yMid, clientRect.right, yMid);
#endif // 
}

int flip = 1;
void rotateImage(HWND hWnd)
{
    HDC hdc = GetDC(hWnd);
    Graphics graphic(hdc);
    Image* image = Image::FromFile(filePath);

    // 4. Use window client RECT instead of screen RECT to draw image.
    RECT clientRect;
    GetClientRect(hWnd, &clientRect);
    LONG cx = clientRect.right - clientRect.left;
    LONG cy = clientRect.bottom - clientRect.top;

    if (flip == 1)
        image->RotateFlip(Rotate90FlipNone);
    else if (flip == 2)
        image->RotateFlip(Rotate180FlipNone);
    else if (flip == 3)
        image->RotateFlip(Rotate270FlipNone);

    // 5. Get image width and heigth after rotate
    int xx = image->GetWidth();
    int yy = image->GetHeight();

    // TODO: 2. compare window width and height with image's before subtract.
    int x = (cx - xx) / 2;
    int y = (cy - yy) / 2;

    RECT rc;
    HBRUSH hBr;
    SetRect(&rc, x, y, x + xx, y + yy);
    hBr = CreateSolidBrush(RGB(255, 255, 255));
    FillRect(hdc, &rc, hBr);

    Status status = graphic.DrawImage(image, x, y);
    // Draw a line at window middle
    LONG xMid = cx / 2;
    LONG yMid = cy / 2;
    Pen      pen(Color(255, 0, 0, 255));
    graphic.DrawLine(&pen, xMid, 0, xMid, clientRect.bottom);
    graphic.DrawLine(&pen, 0, yMid, clientRect.right, yMid);

    flip++;
    if (flip > 4) flip = 1;
    ReleaseDC(hWnd, hdc);
}

更新:添加不适用于 OP 的图像结果。

在此处输入图像描述


推荐阅读