c++ - 使用位图处理和重绘分层窗口的正确方法
问题描述
我正在制作一个在完全透明的窗口中显示图像的脚本,所以它只是屏幕上的图像本身,你可以移动它。结果如下:
简化代码:
#include <math.h>
#include <windows.h>
#include <windowsx.h>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
HWND hWnd;
double imageZoom = 0.5;
VOID OnPaint(HWND hWnd, HDC hdc)
{
Bitmap image(L"example.png");
// Get image dimensions
int imageWidth = image.GetWidth();
int imageHeight = image.GetHeight();
// Update the dimensions using the image zoom
imageWidth = static_cast<int>(floor((double)imageWidth * imageZoom));
imageHeight = static_cast<int>(floor((double)imageHeight * imageZoom));
// Make a DC on which to draw the image
HDC drawingDC = CreateCompatibleDC(hdc);
// Create a new bitmap for drawing on it
HBITMAP newBitmap = CreateCompatibleBitmap(hdc, imageWidth, imageHeight);
// Select the new bitmap to draw on it and save the old one
HBITMAP oldBitmap = (HBITMAP)SelectObject(drawingDC, newBitmap);
// Draw image to the newly created DC
Graphics graphics(drawingDC);
Rect point(0, 0, imageWidth, imageHeight);
graphics.DrawImage(&image, point);
// Create a blend function
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
// Set window info
RECT coord;
GetWindowRect(hWnd, &coord);
POINT windowPosition = { coord.left, coord.top };
SIZE windowSize = { imageWidth, imageHeight };
POINT imagePosition = { 0, 0 };
// Call UpdateLayeredWindow
UpdateLayeredWindow(hWnd, hdc, &windowPosition, &windowSize, drawingDC, &imagePosition, 0, &blend, ULW_ALPHA);
SelectObject(drawingDC, oldBitmap);
DeleteObject(newBitmap);
DeleteDC(drawingDC);
ReleaseDC(NULL, hdc);
}
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow)
{
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
MSG msg;
WNDCLASS wndClass;
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = 0;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = TEXT("Sticker");
RegisterClass(&wndClass);
hWnd = CreateWindowEx(
WS_EX_LAYERED,
TEXT("Sticker"), // window class name
TEXT("Sticker"), // window caption
NULL, // window style
0, // initial x position
0, // initial y position
500, // initial x size
500, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
ShowWindow(hWnd, iCmdShow);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GdiplusShutdown(gdiplusToken);
return msg.wParam;
} // WinMain
bool holding = false;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE:
hdc = BeginPaint(hWnd, &ps);
OnPaint(hWnd, hdc);
EndPaint(hWnd, &ps);
return 0;
case WM_LBUTTONDOWN:
// In short, this allows moving the image
PostMessage(hWnd, WM_NCLBUTTONDOWN, 2, 0);
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
OnPaint(hWnd, hdc);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_MBUTTONDOWN:
// Just to test the size changing
imageZoom += 0.1;
RedrawWindow(hWnd, NULL, NULL, RDW_INTERNALPAINT);
return 0;
case WM_RBUTTONDOWN:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
} // WndProc
我不确定这是否是使用 DC 和RedrawWindow()
. 问题是,如果我RedrawWindow()
使用新大小的图像(我包含一条 WM_MBUTTONDOWN 消息来测试它),图像会更新并使用新大小绘制,但分层窗口的大小似乎以某种方式保持不变。这是它的样子:
我不知道为什么会这样,所以也许有人知道为什么。
但主要问题是,创建、更新和渲染分层窗口的正确方法是什么?我想这种方式不好,但我在互联网上找不到任何其他可以理解的例子
更新:
我已经更新了代码。现在它不使用 WM_PAINT 消息,仅在需要时使用 UpdateLayeredWindow。
#include <math.h>
#include <windows.h>
#include <windowsx.h>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
#include <wincodec.h>
#include <wincodecsdk.h>
#pragma comment(lib, "WindowsCodecs.lib")
HWND hWnd;
Bitmap* image;
double imageZoom = 0.5;
int originalWidth;
int originalHeight;
void firstUpdateLayeredWindow()
{
image = Bitmap::FromFile(L"example.png");
// Get image dimensions
originalWidth = image->GetWidth();
originalHeight = image->GetHeight();
// Update the dimensions using the image zoom
int imageWidth = static_cast<int>(floor((double)originalWidth * imageZoom));
int imageHeight = static_cast<int>(floor((double)originalHeight * imageZoom));
HDC hdc = GetDC(NULL);
// Make a DC which which hold the bitmap for drawing
HDC drawingDC = CreateCompatibleDC(hdc);
// Create a new bitmap for drawing on it
HBITMAP newBitmap = CreateCompatibleBitmap(hdc, imageWidth, imageHeight);
// Select the new bitmap onto the drawing DC and save the old one
HBITMAP oldBitmap = (HBITMAP)SelectObject(drawingDC, newBitmap);
// Draw image to the newly created DC
Graphics graphics(drawingDC);
Rect point(0, 0, imageWidth, imageHeight);
graphics.DrawImage(image, point);
// Create a blend function
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
// Set window info
RECT coord;
GetWindowRect(hWnd, &coord);
POINT windowPosition = { coord.left, coord.top };
SIZE windowSize = { imageWidth, imageHeight };
POINT imagePosition = { 0, 0 };
// Call UpdateLayeredWindow
UpdateLayeredWindow(hWnd, hdc, &windowPosition, &windowSize, drawingDC, &imagePosition, 0, &blend, ULW_ALPHA);
SelectObject(drawingDC, oldBitmap);
DeleteObject(newBitmap);
DeleteDC(drawingDC);
ReleaseDC(NULL, hdc);
}
void followingUpdateLayeredWindow()
{
// Update the dimensions using the image zoom
int imageWidth = static_cast<int>(floor((double)originalWidth * imageZoom));
int imageHeight = static_cast<int>(floor((double)originalHeight * imageZoom));
HDC hdc = GetDC(NULL);
// Make a DC on which to draw the image
HDC drawingDC = CreateCompatibleDC(hdc);
// Create a new bitmap for drawing on it
HBITMAP newBitmap = CreateCompatibleBitmap(hdc, imageWidth, imageHeight);
// Select the new bitmap to draw on it and save the old one
HBITMAP oldBitmap = (HBITMAP)SelectObject(drawingDC, newBitmap);
// Draw image to the newly created DC
Graphics graphics(drawingDC);
Rect point(0, 0, imageWidth, imageHeight);
graphics.DrawImage(image, point);
// Create a blend function
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
// Set window info
RECT coord;
GetWindowRect(hWnd, &coord);
POINT windowPosition = { coord.left, coord.top };
SIZE windowSize = { imageWidth, imageHeight };
POINT imagePosition = { 0, 0 };
// Call UpdateLayeredWindow
UpdateLayeredWindow(hWnd, hdc, &windowPosition, &windowSize, drawingDC, &imagePosition, 0, &blend, ULW_ALPHA);
SelectObject(drawingDC, oldBitmap);
DeleteObject(newBitmap);
DeleteDC(drawingDC);
ReleaseDC(NULL, hdc);
}
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow)
{
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
MSG msg;
WNDCLASS wndClass;
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = 0;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = TEXT("Sticker");
RegisterClass(&wndClass);
hWnd = CreateWindowEx(
WS_EX_LAYERED,
TEXT("Sticker"), // window class name
TEXT("Sticker"), // window caption
WS_DLGFRAME, // window style
0, // initial x position
0, // initial y position
500, // initial x size
500, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
// Update the layered window for the first time, attaching a bitmap to it
firstUpdateLayeredWindow();
ShowWindow(hWnd, iCmdShow);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GdiplusShutdown(gdiplusToken);
return msg.wParam;
} // WinMain
bool holding = false;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:
// In short, this allows moving the image
PostMessage(hWnd, WM_NCLBUTTONDOWN, 2, 0);
return 0;
case WM_MBUTTONDOWN:
{
// Just to test the size changing
imageZoom += 0.1;
// Update the layered window with the function for updating it multiple times
followingUpdateLayeredWindow();
}
return 0;
case WM_RBUTTONDOWN:
PostQuitMessage(0);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
} // WndProc
不幸的是,它仍然无法正常工作。图像已正确调整大小,但结果与上一个屏幕截图相同(图像被剪裁)。它仅在增加图像大小时发生,这就是为什么我认为问题在于分层窗口本身的大小。更奇怪的是,如果我将 imageZoom 变量增加了很多,然后移动窗口,它实际上会出于某种原因自行更新并以实际大小绘制整个图像,但这只会发生在窗口传递了某个值或某些东西。
UpdateLayeredWindow()
在更新附加到它的位图之后,我什至尝试使用单独的函数更新分层窗口的大小。
更新:已解决但不知道为什么
我通过在分层窗口中包含 WS_POPUP 样式解决了图像被剪切的奇怪行为。现在它完美地工作了,虽然我不知道为什么
解决方案
推荐阅读
- python - 使用子进程打开 Acrobat 时,Tkinter 失去焦点
- android - 未找到 AndroidX GMS
- c# - 如何从 C# 将值发送到 cshtml 模板
- php - 发送“POST”信息后需要读取特定的响应头进行登录
- r - 识别满足条件的行并存储在矩阵中
- json - C++ Builder:TJSONPair 是否拥有所有权?
- linux-device-driver - 在 Linux i2c 驱动程序中进行芯片特定初始化的正确功能是什么
- javascript - 向CKeditor中的链接添加类
- python - 在 python g(f)[0](4,2) 中扩展 lambda 函数
- jquery - 在单击元素上禁用 jQuery 功能