Can WM_PAINT message handler run asynchronously in Win32 C++ application?

  • Thread starter Thread starter Purple_Apple
  • Start date Start date
P

Purple_Apple

Guest
Hello. I am writing a Delaunay triangulator, which is a Win 32 application in C ++. The application works as follows:

1) The thread of triangulation execution starts.
2) The edges of the formed (during triangulation) triangles are written to the buffer.
3) The “technological” triangles are removed by removing their edges from the buffer. Only the “true” triangles that make up the triangulation remain.
4) The coordinates of the edges of the "true" triangles are placed from buffer to concurrent_queue.
5) After filling the concurrent_queue, the triangulation thread sends a message to the UI thread.
6) The UI thread receives this message and call InvalidateRect() to start WM_PAINT message.
7) WM_PAINT message starts.
8) The OnPaint handler reads from the concurrent_queue the coordinates of the edges and calls Graphics::DrawLine to draw each edge.

Below is listing of OnPaint handler:

VOID OnPaint(HWND hWnd, HDC hdc)
{
if (!isnan(xMin) && !isnan(xMax) && !isnan(yMin) && !isnan(yMax))
{
Graphics* g = Graphics::FromHDC(hdc);

// Get toolbar height.
RECT toolbarRect;
GetWindowRect(g_hWndToolbar, &toolbarRect);
LONG toolbarHeight = toolbarRect.bottom - toolbarRect.top;
// Get statusbar height.
RECT statusbarRect;
GetWindowRect(g_hWndStatusbar, &statusbarRect);
LONG statusbarHeight = statusbarRect.bottom - statusbarRect.top;
// Get height and width of the client area of the application window.
RECT r;
::GetClientRect(hWnd, &r);
int width = r.right - r.left;
int height = r.bottom - r.top - toolbarHeight - statusbarHeight;

// Scale factor.
double scale = 0

// move the origin:
if (xMin < 0 && xMax > 0 && yMin < 0 && yMax > 0)
{
// to center if all quadrants of coordinate system are used
g->TranslateTransform(width / 2, height / 2 + toolbarHeight);
scale = min(width/2, height/2) / max(xMax, yMax);
}
else if (xMin < 0 && xMax > 0 && yMin >= 0 && yMax > 0)
{
// to bottom center if 1-st and 2-d quadrants of coordinate system are used
g->TranslateTransform((r.right - r.left) / 2, height);
scale = min(width / 2, height) / max(xMax, yMax);
}
else if (xMin < 0 && xMax <= 0 && yMin < 0 && yMax > 0)
{
// to right center if 2-d and 3-d quadrants of coordinate system are used
g->TranslateTransform(width, height / 2 + toolbarHeight);
scale = min(width, height / 2) / max(xMax, yMax);
}
else if (xMin < 0 && xMax > 0 && yMin < 0 && yMax <= 0)
{
// to top center if 3-d and 4-th quadrants of coordinate system are used
g->TranslateTransform((r.right - r.left) / 2, 0.0);
scale = min(width / 2, height) / max(xMax, yMax);
}
else if (xMin >= 0 && xMax > 0 && yMin < 0 && yMax > 0)
{
// to left center if 4-th and 1-st quadrants of coordinate system are used
g->TranslateTransform(0.0, height / 2 + toolbarHeight);
scale = min(width, height / 2) / max(xMax, yMax);
}
else if (xMin >= 0 && xMax > 0 && yMin >= 0 && yMax > 0)
{
// to left bottom if 1-st quiadrant of coordinate system is used
g->TranslateTransform(0.0, height + toolbarHeight);
scale = min(width, height) / max(xMax, yMax);
}
else if (xMin < 0 && xMax <= 0 && yMin >= 0 && yMax > 0)
{
// to right bottom if 2-d quadrant of coordinate system is used
g->TranslateTransform(width, height);
scale = min(width, height) / max(xMax, yMax);
}
else if (xMin < 0 && xMax <= 0 && yMin < 0 && yMax <= 0)
{
// to right top if 3-d quadrant of coordinate system is used
g->TranslateTransform(width, 0.0/*(r.bottom - r.top) + toolbarHeight*/);
scale = min(width, height) / max(xMax, yMax);
}
else if (xMin >= 0 && xMax > 0 && yMin < 0 && yMax <= 0)
{
// to left top if 4-th quadrant of coordinate system is used
g->TranslateTransform(0.0, 0.0);
scale = min(width, height) / max(xMax, yMax);

}
// set the bottom-up orientation of Y
g->ScaleTransform(1, -1);
// Do scaling.
g->ScaleTransform(scale, scale);

Pen pen(Color(255, 0, 0, 255), 0.02f/*100.0f*/);
g->SetSmoothingMode(SmoothingModeAntiAlias);
// Draw next edge.
EdgeToDraw* drawnEdge = new EdgeToDraw();
while (MeshEdgesBuffer.try_pop(*drawnEdge))
{
g->DrawLine(&pen, drawnEdge->Begin, drawnEdge->End);
}
}
}

The problem is that when drawing triangulation meshes with a large number of edges (several tens of thousands), such as, for example, the triangulation of the island of Greenland below:

1571141.png

the application is busy only with drawing and does not respond to user requests. And the drawing itself can last no less than, for example, 20 seconds.

My question is: Can I draw in the background thread (and not in the UI thread)? Or will I have to, in my situation, have to limit myself and use either a bitmap as written here: Walkthrough: Removing Work from a User-Interface Thread or drawing at time intervals as written here: https://docs.microsoft.com/en-us/windows/win32/gdi/drawing-at-timed-intervals?

Continue reading...
 
Back
Top