这是GDI绘制源码,怎么加上给他加上双缓存防止闪烁
#include <stdio.h>
#include<Windows.h>
#include<stdlib.h>
LRESULT __stdcall callback(HWND hwnd, UINT usermsg, WPARAM wparam, LPARAM lparam)
{
switch (usermsg) {
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(1);
break;
}
return DefWindowProc(hwnd, usermsg, wparam, lparam);
}
HWND createlaiedwindow(const char wndname[255], RECT rect_layedwindow)//创建一个透明的绘制窗口
{
WNDCLASSEX Layedwindow = { 0 };
Layedwindow.cbSize = sizeof WNDCLASSEX;
Layedwindow.lpfnWndProc = callback;
Layedwindow.lpszClassName = "w;";
Layedwindow.style = CS_HREDRAW | CS_VREDRAW;
Layedwindow.hCursor = LoadCursor(NULL, IDC_ARROW);
Layedwindow.hbrBackground = ((HBRUSH)RGB(0, 0, 0));
Layedwindow.hInstance = GetModuleHandle(0);
RegisterClassEx(&Layedwindow);//注册窗口类
//利用窗口类创建一个实体窗口
HWND hwnd_layedwindow = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, Layedwindow.lpszClassName, wndname,
WS_POPUP, rect_layedwindow.left, rect_layedwindow.top,
rect_layedwindow.right - rect_layedwindow.left, rect_layedwindow.bottom - rect_layedwindow.top,
NULL, NULL, Layedwindow.hInstance, NULL);
SetLayeredWindowAttributes(hwnd_layedwindow, RGB(0, 0, 0), 0, 1);//设置窗口透名
ShowWindow(hwnd_layedwindow, SW_SHOW);
UpdateWindow(hwnd_layedwindow);
return hwnd_layedwindow;
}
void UpdateLayedWindow(HWND hwnd_layedwindow, HWND hwnd_game)//刷新窗口
{
RECT rect_layedwindow{};
GetWindowRect(hwnd_game, &rect_layedwindow);
SetWindowPos(hwnd_layedwindow, HWND_TOPMOST,
rect_layedwindow.left, rect_layedwindow.top,
rect_layedwindow.right - rect_layedwindow.left, rect_layedwindow.bottom - rect_layedwindow.top, SWP_SHOWWINDOW);
MoveWindow(hwnd_layedwindow, rect_layedwindow.left, rect_layedwindow.top,
rect_layedwindow.right - rect_layedwindow.left, rect_layedwindow.bottom - rect_layedwindow.top, TRUE);
}
void clear(HWND hwnd_overlay, RECT rect_window)//删除上一贞数据,防满屏绘制
{
RECT rect = { 0,0,rect_window.right - rect_window.left,rect_window.bottom - rect_window.top };
HWND hwnd = GetForegroundWindow();
HDC hdc = GetDC(hwnd_overlay);
HBRUSH hbrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
SelectObject(hdc, hbrush);
FillRect(hdc, &rect, hbrush);
DeleteObject(hbrush);
ReleaseDC(hwnd_overlay, hdc);
}
namespace gdi
{
void drawbox(HDC hdc_layedwindow, int x, int y, int boxwidth, int boxheight)
{
Rectangle(hdc_layedwindow, x, y, x + boxwidth, y + boxheight);
}
void drawline(HDC hdc_layedwindow, int x1, int y1, int x2, int y2)
{
MoveToEx(hdc_layedwindow, x1, y1, NULL);
LineTo(hdc_layedwindow, x2, y2);
}
void drawstring(HDC hdc_layedwindow, int x, int y, COLORREF color, const char* text)
{
SetTextColor(hdc_layedwindow, color);
SetBkMode(hdc_layedwindow, RGB(0, 0, 0));
TextOutA(hdc_layedwindow, x, y, text, strlen(text));
}
}
#include <iostream>
#include "GDI.h"
HWND hwnd = NULL;
HWND hwnd_layedwindow = NULL;
unsigned long processid = NULL;
RECT rect = { 0 };
HDC hdc = NULL;
HPEN hpen = NULL;
HBRUSH hbrush = NULL;
void init(const char wndname[255]);
int main()
{
MessageBoxA(NULL, "按下确定开始绘制", "tips", MB_ICONERROR);
init("新建文本文档.txt - 记事本");
while (true)
{
for (size_t i = 0; i < 700; i=i+12)
{
GetWindowRect(hwnd, &rect);
UpdateLayedWindow(hwnd_layedwindow, hwnd);
clear(hwnd_layedwindow, rect);
hdc = GetDC(hwnd_layedwindow);
hpen = CreatePen(PS_INSIDEFRAME, 1, RGB(255, 0, 0));
hbrush = (HBRUSH)GetStockObject(DEFAULT_GUI_FONT);
SelectObject(hdc, hpen);
SelectObject(hdc, GetStockObject(NULL_BRUSH));
SelectObject(hdc, hbrush);//选择对象
gdi::drawstring(hdc, 30+i, 80, RGB(255, 0, 0), "草");
gdi::drawbox(hdc, 30+(i-5), 70, 20, 30);
gdi::drawstring(hdc, 30 + i, 150, RGB(255, 0, 0), "草");
gdi::drawbox(hdc, 30 + (i - 5), 140, 20, 30);
gdi::drawstring(hdc, 30 , 250, RGB(255, 0, 0), "草");
gdi::drawbox(hdc, 25 , 240, 20, 30);
DeleteObject(hbrush);
DeleteObject(hpen);
ReleaseDC(hwnd_layedwindow, hdc);
}
}
}
void init(const char wndname[255])//初始化函数
{
hwnd = FindWindow(NULL, wndname);
if (!hwnd) {
MessageBoxA(NULL, "未找到窗口", "tips", MB_ICONERROR);
PostQuitMessage(0);
}
GetWindowThreadProcessId(hwnd, &processid);
if (processid == NULL) {
MessageBoxA(NULL, "取Pid失败", "tips", MB_ICONERROR);
}
GetWindowRect(hwnd, &rect);
hwnd_layedwindow = createlaiedwindow(" ", rect);
if (!hwnd_layedwindow)
{
MessageBoxA(NULL, "创建窗口失败", "tips", MB_ICONERROR);
}
}
引用chatgpt部分指引作答:
要使用双缓存,需要在程序中创建两个内存DC,一个用于绘制图像,另一个用于显示。在每个绘制周期中,先绘制到内存DC中,然后再将其复制到显示DC中,从而消除闪烁。
以下是修改后的代码:
#include <stdio.h>
#include<Windows.h>
#include<stdlib.h>
LRESULT __stdcall callback(HWND hwnd, UINT usermsg, WPARAM wparam, LPARAM lparam)
{
switch (usermsg) {
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(1);
break;
}
return DefWindowProc(hwnd, usermsg, wparam, lparam);
}
HWND createlaiedwindow(const char wndname[255], RECT rect_layedwindow)//创建一个透明的绘制窗口
{
WNDCLASSEX Layedwindow = { 0 };
Layedwindow.cbSize = sizeof WNDCLASSEX;
Layedwindow.lpfnWndProc = callback;
Layedwindow.lpszClassName = "w;";
Layedwindow.style = CS_HREDRAW | CS_VREDRAW;
Layedwindow.hCursor = LoadCursor(NULL, IDC_ARROW);
Layedwindow.hbrBackground = ((HBRUSH)RGB(0, 0, 0));
Layedwindow.hInstance = GetModuleHandle(0);
RegisterClassEx(&Layedwindow);//注册窗口类
//利用窗口类创建一个实体窗口
HWND hwnd_layedwindow = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, Layedwindow.lpszClassName, wndname,
WS_POPUP, rect_layedwindow.left, rect_layedwindow.top,
rect_layedwindow.right - rect_layedwindow.left, rect_layedwindow.bottom - rect_layedwindow.top,
NULL, NULL, Layedwindow.hInstance, NULL);
SetLayeredWindowAttributes(hwnd_layedwindow, RGB(0, 0, 0), 0, 1);//设置窗口透名
ShowWindow(hwnd_layedwindow, SW_SHOW);
UpdateWindow(hwnd_layedwindow);
return hwnd_layedwindow;
}
void UpdateLayedWindow(HWND hwnd_layedwindow, HWND hwnd_game)//刷新窗口
{
RECT rect_layedwindow{};
GetWindowRect(hwnd_game, &rect_layedwindow);
SetWindowPos(hwnd_layedwindow, HWND_TOPMOST,
rect_layedwindow.left, rect_layedwindow.top,
rect_layedwindow.right - rect_layedwindow.left, rect_layedwindow.bottom - rect_layedwindow.top, SWP_SHOWWINDOW);
MoveWindow(hwnd_layedwindow, rect_layedwindow.left, rect_layedwindow.top,
rect_layedwindow.right - rect_layedwindow.left, rect_layedwindow.bottom - rect_layedwindow.top, TRUE);
}
void clear(HDC hdc_overlay, RECT rect_window)//删除上一贞数据,防满屏绘制
{
HBRUSH hbrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
SelectObject(hdc_overlay, hbrush);
FillRect(hdc_overlay, &rect_window, hbrush);
DeleteObject(hbrush);
}
namespace gdi
{
void drawbox(HDC hdc, int x, int y, int boxwidth, int boxheight)
{
Rectangle(hdc, x, y, x + boxwidth,x + boxwidth, y + boxheight);
}
void drawtext(HDC hdc, const char* text, int x, int y, int width, int height, UINT format)
{
RECT rect = { x, y, x + width, y + height };
DrawTextA(hdc, text, -1, &rect, format);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd_game = FindWindow(NULL, "MyGame");
if (!hwnd_game)
{
MessageBox(NULL, "Could not find game window!", "Error", MB_OK);
return 1;
}
HWND hwnd_overlay = createlaiedwindow("MyOverlay", RECT{ 0, 0, 800, 600 });
HDC hdc_game = GetDC(hwnd_game);
HDC hdc_overlay = CreateCompatibleDC(hdc_game);
HBITMAP hbm_overlay = CreateCompatibleBitmap(hdc_game, 800, 600);
HGDIOBJ hOld = SelectObject(hdc_overlay, hbm_overlay);
MSG msg{};
while (GetMessage(&msg, NULL, 0, 0))
{
switch (msg.message)
{
case WM_QUIT:
break;
default:
clear(hdc_overlay, RECT{ 0, 0, 800, 600 });
//绘制Overlay内容
gdi::drawbox(hdc_overlay, 100, 100, 50, 50);
gdi::drawtext(hdc_overlay, "Hello, world!", 200, 200, 100, 20, DT_CENTER | DT_VCENTER);
//将Overlay绘制到屏幕上
UpdateLayedWindow(hwnd_overlay, hwnd_game);
BitBlt(hdc_game, 0, 0, 800, 600, hdc_overlay, 0, 0, SRCCOPY);
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//释放资源
SelectObject(hdc_overlay, hOld);
DeleteObject(hbm_overlay);
DeleteDC(hdc_overlay);
ReleaseDC(hwnd_game, hdc_game);
return 0;
}
你这是无停顿无限绘图呢? 每次绘图之间增加Sleep即可
ReleaseDC(hwnd_layedwindow, hdc);
Sleep(33);
为了避免GDI绘制时出现画面闪烁,可以使用双缓冲技术,即先在内存中作图,然后使用BitBlt函数将做好的图复制到前台,同时禁止背景重绘,从而消除闪烁。下面是具体的代码实现步骤:
HDC hMemDC = CreateCompatibleDC(hDC);
其中hDC为当前窗口的DC句柄。
HBITMAP hBitmap = CreateCompatibleBitmap(hDC, nWidth, nHeight);
其中nWidth和nHeight分别为位图的宽度和高度。
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
在内存DC中进行绘制操作。
使用BitBlt函数将做好的图复制到前台,同时禁止背景重绘,使用以下代码:
RECT rect;
GetClientRect(hWnd, &rect);
HDC hdc = GetDC(hWnd);
BitBlt(hdc, 0, 0, rect.right, rect.bottom, hMemDC, 0, 0, SRCCOPY);
ReleaseDC(hWnd, hdc);
其中hWnd为当前窗口的句柄。
SelectObject(hMemDC, hOldBitmap);
DeleteDC(hMemDC);
DeleteObject(hBitmap);
在代码中添加上述步骤后,就可以使用双缓冲技术避免GDI绘制时出现画面闪烁了。
引用chatGPT作答,要实现双缓存防止闪烁,可以创建一个和窗口大小相同的内存缓冲区,并在内存缓冲区中进行绘制。完成绘制后,再将内存缓冲区中的内容复制到窗口绘图区域中。
以下是在原代码的基础上进行修改的部分:
1.在全局变量中添加缓冲区句柄和位图句柄:
HDC hdc_mem = NULL;
HBITMAP hbitmap_mem = NULL;
2.在 init 函数中创建缓冲区,并将句柄保存到全局变量中:
void init(const char wndname[255]) {
// ... 窗口创建等代码 ...
hdc_mem = CreateCompatibleDC(hdc);
hbitmap_mem = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
SelectObject(hdc_mem, hbitmap_mem);
}
3.在绘制函数中,先在内存缓冲区中进行绘制:
// 清空缓冲区
FillRect(hdc_mem, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
// 绘制图形
SelectObject(hdc_mem, hpen);
SelectObject(hdc_mem, hbrush);
gdi::drawbox(hdc_mem, i, i, 100, 100);
// ...
4.在绘制完成后,将缓冲区中的内容复制到窗口绘图区域中:
BitBlt(hdc_layedwindow, 0, 0, rect.right - rect.left, rect.bottom - rect.top, hdc_mem, 0, 0, SRCCOPY);
修改后的主函数代码如下:
int main()
{
MessageBoxA(NULL, "按下确定开始绘制", "tips", MB_ICONERROR);
init("新建文本文档.txt - 记事本");
while (true)
{
for (size_t i = 0; i < 700; i = i + 12)
{
GetWindowRect(hwnd, &rect);
UpdateLayedWindow(hwnd_layedwindow, hwnd);
// 清空缓冲区
FillRect(hdc_mem, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
// 绘制图形
SelectObject(hdc_mem, hpen);
SelectObject(hdc_mem, hbrush);
gdi::drawbox(hdc_mem, i, i, 100, 100);
// ...
// 将缓冲区内容复制到窗口
BitBlt(hdc_layedwindow, 0, 0, rect.right - rect.left, rect.bottom - rect.top, hdc_mem, 0, 0, SRCCOPY);
}
Sleep(30);
}
return 0;
}