gpt4 book ai didi

c++ - 双缓冲故障(2013 年 12 月 17 日更新)

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:40:57 25 4
gpt4 key购买 nike

介绍和相关信息:

我有一个复杂的绘画要在我的主窗口的 WM_PAINT 处理程序中实现。

我已经提交了下面的图片来说明它:

enter image description here

主窗口具有静态控件,而不是具有 SS_NOTIFY 样式的按钮。

当用户点击它们时,程序中会发生某些操作,这些操作目前无关紧要。

下图显示了主窗口中静态控件的位置:

enter image description here

橙色面板上的 map 是一个 EMF 文件,左上角和右上角的 logo 是 PNG 文件,其他图片是 bitmap s。
Visual Styles 通过 #pragma 指令启用。我还使用 GDI+GDI

项目被创建为空项目,我已经从“头”开始编码了所有内容。

为了实现这个任务,我决定在 WM_PAINT 中绘制 整张图片,并在 将透明的 static control s 放在与它们对应的图片 上。

为了保持我的代码干净和简单,我制作了实现上述功能的函数,因此我的 WM_PAINT 处理程序可以尽可能小。

更新 #1(2013 年 12 月 17 日更新):

为了实现从成员 arx 获得的建议,我发布了一个可以编译并且可以重现问题的源代码:

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <gdiplus.h>

#pragma comment( linker, "/manifestdependency:\"type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
language='*'\"" )

#pragma comment( lib, "comctl32.lib")
#pragma comment( lib, "Msimg32.lib" )
#pragma comment( lib, "Gdiplus.lib" )

using namespace Gdiplus;

// variable for storing the instance

static HINSTANCE hInst;

// variables for painting the window

static HBRUSH hbPozadina, // gray background brush for grid on the top
BlueFrame, // needed to frame blue static controls
hbr; // orange brush for orange panel

/********* helper functions for WM_PAINT **********/

// Fills triangle with gradient brush

void GradientTriangle( HDC MemDC,
LONG x1, LONG y1,
LONG x2, LONG y2,
LONG x3, LONG y3,
COLORREF top, COLORREF bottom )
{
TRIVERTEX vertex[3];

vertex[0].x = x1;
vertex[0].y = y1;
vertex[0].Red = GetRValue(bottom) << 8;
vertex[0].Green = GetGValue(bottom) << 8;
vertex[0].Blue = GetBValue(bottom) << 8;
vertex[0].Alpha = 0x0000;

vertex[1].x = x2;
vertex[1].y = y2;
vertex[1].Red = GetRValue(top) << 8;
vertex[1].Green = GetGValue(top) << 8;
vertex[1].Blue = GetBValue(top) << 8;
vertex[1].Alpha = 0x0000;

vertex[2].x = x3;
vertex[2].y = y3;
vertex[2].Red = GetRValue(bottom) << 8;
vertex[2].Green = GetGValue(bottom) << 8;
vertex[2].Blue = GetBValue(bottom) << 8;
vertex[2].Alpha = 0x0000;

// Create a GRADIENT_TRIANGLE structure that
// references the TRIVERTEX vertices.

GRADIENT_TRIANGLE gTriangle;

gTriangle.Vertex1 = 0;
gTriangle.Vertex2 = 1;
gTriangle.Vertex3 = 2;

// Draw a shaded triangle.

GradientFill( MemDC, vertex, 3, &gTriangle, 1, GRADIENT_FILL_TRIANGLE);
}

// draws the background for the part of the window between header and footer

void drawBackground( HDC MemDC, RECT r )
{
/******* main window's gradient background ********/

GradientTriangle( MemDC, r.right, r.bottom - r.top - 30,
r.left, r.bottom - r.top - 30,
r.left, r.top + 120,
RGB( 0x95, 0xB3, 0xD7 ),
RGB( 0xDB, 0xE5, 0xF1 ) );

GradientTriangle( MemDC, r.right, r.bottom - r.top - 30,
r.right, r.top + 120,
r.left, r.top + 120,
RGB( 0x95, 0xB3, 0xD7 ),
RGB( 0xDB, 0xE5, 0xF1 ) );
}

// draws the header of the main window

void drawHeader( HDC MemDC, RECT rect, HBRUSH hbPozadina )
{
FillRect( MemDC, &rect, hbPozadina );
}

// fills rectangle with gradient brush

void GradientRectangle( HDC MemDC,
LONG x1, LONG y1,
LONG x2, LONG y2,
COLORREF top,
COLORREF bottom )
{
// vertexes for static's gradient color

TRIVERTEX vertexS[2];

vertexS[0].x = x1;
vertexS[0].y = y1;
vertexS[0].Red = GetRValue(top) << 8;
vertexS[0].Green = GetGValue(top) << 8;
vertexS[0].Blue = GetBValue(top) << 8;
vertexS[0].Alpha = 0x0000;

vertexS[1].x = x2;
vertexS[1].y = y2;
vertexS[1].Red = GetRValue(bottom) << 8;
vertexS[1].Green = GetGValue(bottom) << 8;
vertexS[1].Blue = GetBValue(bottom) << 8;
vertexS[1].Alpha = 0x0000;

// Create a GRADIENT_RECT structure that
// references the TRIVERTEX vertices.

GRADIENT_RECT gRect;

gRect.UpperLeft = 0;
gRect.LowerRight = 1;

// Draw a shaded rectangle.

GradientFill( MemDC, vertexS, 2, &gRect, 1, GRADIENT_FILL_RECT_V );
}

// fills the "button" with blue gradient and frames it with blue brush

void FillButton( HDC MemDC, RECT rect, HBRUSH BlueFrame )
{
// fill upper half of the rectangle

GradientRectangle( MemDC,
rect.left, rect.top,
rect.right, rect.top + ( rect.bottom - rect.top ) / 2,
RGB( 0x95, 0xB3, 0xD7 ),
RGB( 0x4F, 0x8B, 0xBD ) );

// fill bottom half of the rectangle

GradientRectangle( MemDC,
rect.left, rect.top + ( rect.bottom - rect.top ) / 2,
rect.right, rect.bottom,
RGB( 0x4F, 0x8B, 0xBD ),
RGB( 0x95, 0xB3, 0xD7 ) );

FrameRect( MemDC, &rect, BlueFrame );
}

// draws the "status bar" at the bottom of the main window

void drawFooter( HDC MemDC, RECT r, COLORREF top, COLORREF bottom )
{
// down triangle

GradientTriangle( MemDC,
r.right, r.bottom,
( r.right - r.left ) / 2,
r.bottom - r.top - 15,
r.left, r.bottom,
top, bottom );

// upper triangle

GradientTriangle( MemDC,
r.right, r.bottom - r.top - 30,
( r.right - r.left ) / 2, r.bottom - r.top - 15,
r.left, r.bottom - r.top - 30,
top, bottom );

// left triangle

GradientTriangle( MemDC,
r.left, r.bottom,
( r.right - r.left ) / 2, r.bottom - r.top - 15,
r.left, r.bottom - r.top - 30,
top, bottom );

// right triangle

GradientTriangle( MemDC,
r.right, r.bottom - r.top - 30,
( r.right - r.left ) / 2, r.bottom - r.top - 15,
r.right, r.bottom,
top, bottom );
}

// draw orange panel on which map and 3 static controls will be drawn

void drawOrangePanel( HDC MemDC, RECT r, COLORREF top, COLORREF bottom )
{
// down triangle

GradientTriangle( MemDC,
r.right, r.bottom,
r.left + ( r.right - r.left ) / 2,
r.top + ( r.bottom - r.top ) / 2,
r.left, r.bottom,
top, bottom );

// upper triangle

GradientTriangle( MemDC,
r.right, r.top,
r.left + ( r.right - r.left ) / 2,
r.top + ( r.bottom - r.top ) / 2,
r.left, r.top,
top, bottom );

// left triangle

GradientTriangle( MemDC,
r.left, r.bottom,
r.left + ( r.right - r.left ) / 2,
r.top + ( r.bottom - r.top ) / 2,
r.left, r.top,
top, bottom );

// right triangle

GradientTriangle( MemDC,
r.right, r.top,
r.left + ( r.right - r.left ) / 2,
r.top + ( r.bottom - r.top ) / 2,
r.right, r.bottom,
top, bottom );
}

void onPaint( HWND hwnd, WPARAM wParam, LPARAM lParam )
{
PAINTSTRUCT ps;

HDC hdc = BeginPaint( hwnd, &ps);

RECT r; // rectangle for main window's client area

GetClientRect( hwnd, &r);

HDC MemDC = CreateCompatibleDC(hdc); // back buffer

// compatible bitmap for MemDC

HBITMAP bmp = CreateCompatibleBitmap( hdc, r.right - r.left, r.bottom - r.top ),
oldBmp = (HBITMAP)SelectObject( MemDC, bmp ); // needed for cleanup

// draw background for middle part of the window

drawBackground( MemDC, r );

// draw header with grid lines

RECT rect; // position it properly at the top

rect.left = r.left;
rect.top = r.top;
rect.right = r.right;
rect.bottom = 120;

drawHeader( MemDC, rect, hbPozadina );

// draw "status bar"

drawFooter( MemDC, r, RGB( 0x48, 0xAC, 0xC6), RGB( 0x31, 0x83, 0x99 ) );

/******* draw static control's background ****/

//======== top left static control ======//

//position it properly

rect.left = ( 3 * ( r.right - r.left ) / 4 - 340 ) / 3;
rect.top = 120 + ( r.bottom - r.top - 450 ) / 3;
rect.right = 150 + ( 3 * ( r.right - r.left ) / 4 - 340 ) / 3;
rect.bottom = 270 + ( r.bottom - r.top - 450 ) / 3;

// draw gradient button

FillButton( MemDC, rect, BlueFrame );

//======================== draw orange panel =================//

//position it properly

rect.left = 3 * ( r.right - r.left ) / 4 - 40;
rect.top = r.top + 140;
rect.right = rect.left + ( r.right - r.left ) / 4;
rect.bottom = rect.top + ( r.bottom - r.top - 190 );

drawOrangePanel( MemDC, rect, RGB( 0xFF, 0xC8, 0xAA ), RGB( 0xFF, 0x96, 0x48 ) );

/****** draw back buffer on the screen DC *******/

BitBlt( hdc, 0, 0, r.right - r.left, r.bottom - r.top, MemDC, 0, 0, SRCCOPY );

/************** cleanup *******************/

SelectObject( MemDC, oldBmp );

DeleteObject(bmp); // compatible bitmap for MemDC

DeleteDC(MemDC);

EndPaint( hwnd, &ps);
}

// WinMain's procedure

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
//******** brushes ***********//

// load gray background brush for the top banner

hbPozadina = CreateSolidBrush( RGB( 230, 230, 230 ) );

// brush for orange panel that holds 3 static controls and a map

hbr = CreateSolidBrush( RGB( 255, 163, 94 ) );

// blue frame for blue static controls

BlueFrame = CreateSolidBrush( RGB(79, 129, 189) );

/*******************************************/
}
return (LRESULT)0;

case WM_ERASEBKGND:
return (LRESULT)1; // so we avoid flicker ( all painting is in WM_PAINT )

case WM_PAINT:
{
// paint the picture
onPaint( hwnd, wParam, lParam );
}
return (LRESULT)0;

case WM_SIZE:
InvalidateRect( hwnd, NULL, FALSE );
return (LRESULT)0;

case WM_CLOSE:

// destroy brushes

DeleteObject(hbPozadina);
DeleteObject(hbr);
DeleteObject(BlueFrame);

DestroyWindow(hwnd);

return (LRESULT)0;

case WM_DESTROY:
PostQuitMessage(0);
return (LRESULT)0;

default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}

// WinMain

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
{
// store hInstance in global variable for later use

hInst = hInstance;

WNDCLASSEX wc;
HWND hwnd;
MSG Msg;

/**** variables for GDI+ initialization ******/

GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;

/********* Initialize GDI+. ********/

GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

/**** finished GDI+ initialisation *****/

// initialize common controls

INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_STANDARD_CLASSES ;
InitCommonControlsEx(&iccex);

// register main window class

wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground = NULL;//(HBRUSH)GetStockObject( WHITE_BRUSH );
wc.lpszMenuName = NULL;
wc.lpszClassName = L"Main_Window";
wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);

if(!RegisterClassEx(&wc))
{
MessageBox( NULL, L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK );

return 0;
}

// create main window

hwnd = CreateWindowEx( 0, // WS_EX_COMPOSITED "improved" drawing of the edges
L"Main_Window",
L"Геотермист",
WS_OVERLAPPEDWINDOW,
( GetSystemMetrics(SM_CXMAXIMIZED) - 1020 ) / 2,
( GetSystemMetrics(SM_CYMAXIMIZED) - 600 ) / 2,
1020, 600, NULL, NULL, hInstance, 0 );

if(hwnd == NULL)
{
MessageBox( NULL, L"Window creation failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK );

return 0;
}

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}

// shutdownd GDI+

GdiplusShutdown(gdiplusToken);

return Msg.wParam;
}

我使用 Windows XPMS Visual Studio C++ 2008 Express Edition 处理 pure Win32 API

一个注意事项: 因为 VS 的 Express 版没有 有资源编辑器,资源文件和资源头是使用 ResEdit 从这里创建的: http://www.resedit.net/

问题:

为了避免闪烁,我使用了双缓冲,这是我从 *Paul Watt* 的 CodeProject 文章、Charles Petzold 的 Windows 编程第 5 版和 Forger 的 WIN32 教程中学到的。

从理论上讲,一切正常,我的代码编译没有任何错误。

我在这里粘贴了代码:http://pastebin.com/zSYT1i8L

我的英语不够好,无法准确描述我面临的问题(我只能说主窗口的边缘和静态控件的重绘“慢”并且闪烁)所以我创建了一个演示应用程序来演示它们:http://www.filedropper.com/geotermistgrafika

我解决问题的努力:

我已经处理了 WM_ERASEBKGND (返回的 (LRESULT)1 ),并且我已经从我的窗口类中排除了样式 CS_VREDRAWCS_HREDRAW - 因此不应因此导致闪烁。

我的窗口没有 WS_CLIPCHILDREN 样式,因为在静态控件所在的位置可以看到部分桌面图片。

在我的 WM_SIZE 处理程序中,我有:
  • 使用 SetWindowPos(...) API 重新定位静态控件,并通过添加 SWP_NOCOPYBITS 标志减少闪烁。
  • 使用 InvalidateRect( hWnd, NULL, FALSE ) 使整个窗口无效,因此该 API 在无效时不发送 WM_ERASEBKGND (第三个参数是 FALSE ),但即使我尝试使用 TRUE 效果也是一样的。

  • 我已经为 WM_PAINT 处理程序实现了双缓冲,就像上述书籍/文章/教程中的示例一样(通过在内存 DC 中执行所有操作并在屏幕 DC 上执行 BitBlt(...) 以避免闪烁)。

    我没有处理 WM_SIZINGWM_WINDOWPOSCHANGINGWM_MOVING 消息。

    我使用工具 GDIView ( http://www.nirsoft.net/utils/gdi_handles.html ) 来追踪 GDI leaks

    每次我调整窗口大小/最大化我的窗口时,GDIView 在区域的列中显示 +4,这应该意味着我泄漏了区域,但我无法弄清楚这怎么可能,因为我不使用与区域一起操作的 API,并且有仔细检查一切。

    在我看来,一切都应该没问题,也许这无关紧要,但我只是想提一下,也许这很重要。

    如果我在主窗口中添加 WS_EX_COMPOSITED 样式,性能并没有提高。

    我试图找到可以帮助我解决问题的在线示例,但所有教程都很简单,不包括此类复杂的图片。

    重要说明:

    将我的WM_PAINT处理程序留空并使用onPaint API获取的设备上下文调用WM_ERASEBKGND中的GetDC(..)函数后,闪烁消失了,但在调整大小的过程中,窗口的重绘是“尖刺”的,并且主窗口边缘的问题没有解决了。

    尽管如此,这还是比原始代码更好的改进。

    题:

    如何摆脱上面提供的演示应用程序中演示的问题?

    我在此感谢任何投入时间和精力来帮助我的人。

    此致。

    最佳答案

    我在 Windows 7 上编译并运行了代码。使用标准主题(使用 DWM )在调整大小时看起来很好。

    我切换到 Windows 经典主题(禁用 DWM),当窗口调整大小时,按钮边缘有大量撕裂。我怀疑这是你看到的问题。

    当绘画与屏幕的物理更新不同步时,就会发生撕裂。这导致部分屏幕显示旧图像,部分屏幕显示新图像。这种效果在水平移动的垂直线上尤为明显。

    您还在使用 Windows XP 吗?

    据我所知,防止在 XP 上撕裂的唯一方法是使用 DirectX 进行绘画并与 VSYNC 显式同步。尽管您可能能够使用现有的绘画代码并使用 DirectX 绘制最终位图。或者可能有其他一些同步机制。我不知道。

    但是,由于该问题会在更高版本的 Windows 上自行修复,因此我不会做任何事情。

    关于c++ - 双缓冲故障(2013 年 12 月 17 日更新),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20641566/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com