c++ - 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);
我是否试图解决旋转后出现旧图像的问题,但似乎它切掉了太多的窗口并删除了窗口上的控件。
解决方案
一些技巧:
- 每个呼叫
GdiplusStartup
都应与呼叫配对GdiplusShutdown
。 - 将窗口宽度和高度与图像进行比较。
WM_PAINT
在消息中进行绘图工作- 使用窗口客户端
RECT
而不是屏幕RECT
来绘制图像。 - 旋转图像后获取图像宽度和高度。
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 的图像结果。
推荐阅读
- python - 如何向 Pandas 中的现有列添加值?
- react-native - React Native Webview:不支持浏览器
- ruby-on-rails - 如何在 Rails 6 中使用 Webpacker 跨多个客户端 JavaScript 文件共享变量和函数?
- javascript - 呼叫说无法读取属性'然后未定义(看起来有效)
- visual-studio-code - 插件如何获取宿主vscode的当前版本号?
- node.js - fetch() 对 Express 的 POST 请求生成空正文 {}
- python - 在 Python 中将浮动千位 5s 向上舍入
- jquery - 如何使用 jQuery 强制函数以特定顺序执行?
- javascript - 我的 XML 文件中的空/空“getElementsByTagName”
- swift - 在 Firebase 调用之外访问数组