P
Purple_Apple
Guest
I am write a Win32 application in C ++. I want my application to draw in asynchronous mode, so that during drawing it can respond to user requests. As a sample, I took an example from Walkthrough: Removing Work from a User-Interface Thread. But when drawing a bitmap created in the background stream, the client area of the application window is painted over in black - takes a black background. And there is no image in the client area of the application window. I create a Bitmap as follows:
1) Declare the collection of edges.
// Buffer of the edges of the triangles that make up
// the triangulation mesh drawn in the client area
// of the application window.
concurrent_queue<EdgeToDraw> MeshEdgesBuffer;
2) In the background thread, fill this collection of edges.
while (// Has data)
{
// Get initial point of edge.
beginOfEdge = GET_ORIGIN_VERTEX(triangleloop);
// Get coordinates of initial point.
double* pEdgeBounding = ((double*)beginOfEdge);
double X_begin = *pEdgeBounding;
pEdgeBounding = pEdgeBounding + 1;
double Y_begin = *pEdgeBounding;
// Get end point of the edge.
endOfEdge = GET_DESTINATION_VERTEX(triangleloop);
// Get coordinates of end point.
pEdgeBounding = ((double*)endOfEdge);
double X_end = *pEdgeBounding;
pEdgeBounding = pEdgeBounding + 1;
double Y_end = *pEdgeBounding;
// Add an edge for the drawn triangulation mesh.
MeshEdgesBuffer.push(EdgeToDraw(X_begin, Y_begin, X_end, Y_end));
}
the collection is filled wonderfully.
3) When the collection is full, send a message to the UI stating that it is necessary to redraw the contents of the window.
PostMessage(hWnd, WM_APP_DRAW_TRIMESH, 0, 0);
4) In the WndProc this message is received.
case WM_APP_DRAW_TRIMESH:
InvalidateRect(hWnd, nullptr, TRUE);
break;
5) Processing WM_PAINT message starts
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
OnPaint(hWnd, hdc);
EndPaint(hWnd, &ps);
}
break;
Below is OnPaint() function. (I try to follow the pattern as far as possible, the link to which is given above.)
VOID OnPaint(HWND hWnd, HDC hdc)
{
if (!isnan(xMin) && !isnan(xMax) && !isnan(yMin) && !isnan(yMax))
{
// If the unbounded_buffer object contains a Bitmap object,
// draw the image to the client area.
BitmapPtr pBitmap;
if (try_receive(m_TriMeshImages, pBitmap))
{
if (pBitmap != NULL)
{
// Draw the bitmap to the client area.
Graphics* graphics = Graphics::FromHDC(hdc);
graphics->DrawImage(pBitmap.get(), 0, 0);
}
}
// Draw the image on a worker thread if the image is not available.
else
{
RECT clientRect;
GetClientRect(hWnd, &clientRect);
m_DrawingTasks.run([clientRect, hWnd]()
{
DrawTriMesh(BitmapPtr(new Bitmap(clientRect.right, clientRect.bottom)), &triMesh, hWnd);
});
}
}
}
OnPaint function checks if a bitmap is created and, if so, it is drawn. Otherwise, DrawTriMesh function that creates the bitmap is called. Below is DrawTriMesh function listing. The calculation of translation and scaling factors is used the same. It may look awkward, but it works. (I’ll improve it as Tim Roberts advised, after the fact that drawing works correctly for me.)
// Draws a triangulation grid in the specified Bitmap object.
void DrawTriMesh(BitmapPtr pBitmap, Mesh* m, HWND hWnd)
{
// If Bitmap object is not created, then do not draw.
if (pBitmap == nullptr)
return;
// Get the size of the Bitmap object in which to draw.
const UINT bmWidth = pBitmap->GetWidth();
const UINT bmHeight = pBitmap->GetHeight();
// If the height or width of the Bitmap object is zero, then do not draw.
if (bmWidth == 0 || bmHeight == 0)
return;
// Lock Bitmap object in system memory.
BitmapData bitmapData;
Rect rectBmp(0, 0, bmWidth, bmHeight);
pBitmap->LockBits(&rectBmp, ImageLockModeWrite, PixelFormat32bppRGB, &bitmapData);
// Create an object that supports drawing capabilities in Bitmap object.
Graphics* bmGraphics = Graphics::FromImage(&(*pBitmap));
// Scaling 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
bmGraphics->TranslateTransform(bmWidth / 2, bmHeight / 2);
scale = min(bmWidth / 2, bmHeight / 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
bmGraphics->TranslateTransform(bmWidth / 2, bmHeight);
scale = min(bmWidth / 2, bmHeight) / 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
bmGraphics->TranslateTransform(bmWidth, bmHeight / 2);
scale = min(bmWidth, bmHeight / 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
bmGraphics->TranslateTransform(bmWidth / 2, 0.0);
scale = min(bmWidth / 2, bmHeight) / 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
bmGraphics->TranslateTransform(0.0, bmHeight / 2);
scale = min(bmWidth, bmHeight / 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
bmGraphics->TranslateTransform(0.0, bmHeight);
scale = min(bmWidth, bmHeight) / 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
bmGraphics->TranslateTransform(bmWidth, bmHeight);
scale = min(bmWidth, bmHeight) / 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
bmGraphics->TranslateTransform(bmWidth, 0.0);
scale = min(bmWidth, bmHeight) / 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
bmGraphics->TranslateTransform(0.0, 0.0);
scale = min(bmWidth, bmHeight) / max(xMax, yMax);
}
// set the bottom-up orientation of Y
bmGraphics->ScaleTransform(1, -1);
// Perform scaling.
bmGraphics->ScaleTransform(scale, scale);
// Set the characteristics of the pen with which to draw.
Pen pen(Color(255, 0, 0, 255), 0.02f);
bmGraphics->SetSmoothingMode(SmoothingModeAntiAlias);
// Draw next edge.
EdgeToDraw* drawnEdge = new EdgeToDraw();
while (MeshEdgesBuffer.try_pop(*drawnEdge))
{
bmGraphics->DrawLine(&pen, drawnEdge->Begin, drawnEdge->End);
}
// Unlock Bitmap object from system memory.
pBitmap->UnlockBits(&bitmapData);
// Add Bitmap of the triangulation grid to the image queue.
send(m_TriMeshImages, pBitmap);
// Post a paint message to the UI thread to draw the Bitmap.
PostMessage(hWnd, WM_APP_DRAW_TRIMESH, 0, 0);
}
And when the whole mechanism is triggered, the result is this kind of:
Why, instead of the image of the triangulation mesh, I see a black background. What am I doing wrong? Help me please.
Continue reading...
1) Declare the collection of edges.
// Buffer of the edges of the triangles that make up
// the triangulation mesh drawn in the client area
// of the application window.
concurrent_queue<EdgeToDraw> MeshEdgesBuffer;
2) In the background thread, fill this collection of edges.
while (// Has data)
{
// Get initial point of edge.
beginOfEdge = GET_ORIGIN_VERTEX(triangleloop);
// Get coordinates of initial point.
double* pEdgeBounding = ((double*)beginOfEdge);
double X_begin = *pEdgeBounding;
pEdgeBounding = pEdgeBounding + 1;
double Y_begin = *pEdgeBounding;
// Get end point of the edge.
endOfEdge = GET_DESTINATION_VERTEX(triangleloop);
// Get coordinates of end point.
pEdgeBounding = ((double*)endOfEdge);
double X_end = *pEdgeBounding;
pEdgeBounding = pEdgeBounding + 1;
double Y_end = *pEdgeBounding;
// Add an edge for the drawn triangulation mesh.
MeshEdgesBuffer.push(EdgeToDraw(X_begin, Y_begin, X_end, Y_end));
}
the collection is filled wonderfully.
3) When the collection is full, send a message to the UI stating that it is necessary to redraw the contents of the window.
PostMessage(hWnd, WM_APP_DRAW_TRIMESH, 0, 0);
4) In the WndProc this message is received.
case WM_APP_DRAW_TRIMESH:
InvalidateRect(hWnd, nullptr, TRUE);
break;
5) Processing WM_PAINT message starts
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
OnPaint(hWnd, hdc);
EndPaint(hWnd, &ps);
}
break;
Below is OnPaint() function. (I try to follow the pattern as far as possible, the link to which is given above.)
VOID OnPaint(HWND hWnd, HDC hdc)
{
if (!isnan(xMin) && !isnan(xMax) && !isnan(yMin) && !isnan(yMax))
{
// If the unbounded_buffer object contains a Bitmap object,
// draw the image to the client area.
BitmapPtr pBitmap;
if (try_receive(m_TriMeshImages, pBitmap))
{
if (pBitmap != NULL)
{
// Draw the bitmap to the client area.
Graphics* graphics = Graphics::FromHDC(hdc);
graphics->DrawImage(pBitmap.get(), 0, 0);
}
}
// Draw the image on a worker thread if the image is not available.
else
{
RECT clientRect;
GetClientRect(hWnd, &clientRect);
m_DrawingTasks.run([clientRect, hWnd]()
{
DrawTriMesh(BitmapPtr(new Bitmap(clientRect.right, clientRect.bottom)), &triMesh, hWnd);
});
}
}
}
OnPaint function checks if a bitmap is created and, if so, it is drawn. Otherwise, DrawTriMesh function that creates the bitmap is called. Below is DrawTriMesh function listing. The calculation of translation and scaling factors is used the same. It may look awkward, but it works. (I’ll improve it as Tim Roberts advised, after the fact that drawing works correctly for me.)
// Draws a triangulation grid in the specified Bitmap object.
void DrawTriMesh(BitmapPtr pBitmap, Mesh* m, HWND hWnd)
{
// If Bitmap object is not created, then do not draw.
if (pBitmap == nullptr)
return;
// Get the size of the Bitmap object in which to draw.
const UINT bmWidth = pBitmap->GetWidth();
const UINT bmHeight = pBitmap->GetHeight();
// If the height or width of the Bitmap object is zero, then do not draw.
if (bmWidth == 0 || bmHeight == 0)
return;
// Lock Bitmap object in system memory.
BitmapData bitmapData;
Rect rectBmp(0, 0, bmWidth, bmHeight);
pBitmap->LockBits(&rectBmp, ImageLockModeWrite, PixelFormat32bppRGB, &bitmapData);
// Create an object that supports drawing capabilities in Bitmap object.
Graphics* bmGraphics = Graphics::FromImage(&(*pBitmap));
// Scaling 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
bmGraphics->TranslateTransform(bmWidth / 2, bmHeight / 2);
scale = min(bmWidth / 2, bmHeight / 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
bmGraphics->TranslateTransform(bmWidth / 2, bmHeight);
scale = min(bmWidth / 2, bmHeight) / 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
bmGraphics->TranslateTransform(bmWidth, bmHeight / 2);
scale = min(bmWidth, bmHeight / 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
bmGraphics->TranslateTransform(bmWidth / 2, 0.0);
scale = min(bmWidth / 2, bmHeight) / 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
bmGraphics->TranslateTransform(0.0, bmHeight / 2);
scale = min(bmWidth, bmHeight / 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
bmGraphics->TranslateTransform(0.0, bmHeight);
scale = min(bmWidth, bmHeight) / 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
bmGraphics->TranslateTransform(bmWidth, bmHeight);
scale = min(bmWidth, bmHeight) / 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
bmGraphics->TranslateTransform(bmWidth, 0.0);
scale = min(bmWidth, bmHeight) / 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
bmGraphics->TranslateTransform(0.0, 0.0);
scale = min(bmWidth, bmHeight) / max(xMax, yMax);
}
// set the bottom-up orientation of Y
bmGraphics->ScaleTransform(1, -1);
// Perform scaling.
bmGraphics->ScaleTransform(scale, scale);
// Set the characteristics of the pen with which to draw.
Pen pen(Color(255, 0, 0, 255), 0.02f);
bmGraphics->SetSmoothingMode(SmoothingModeAntiAlias);
// Draw next edge.
EdgeToDraw* drawnEdge = new EdgeToDraw();
while (MeshEdgesBuffer.try_pop(*drawnEdge))
{
bmGraphics->DrawLine(&pen, drawnEdge->Begin, drawnEdge->End);
}
// Unlock Bitmap object from system memory.
pBitmap->UnlockBits(&bitmapData);
// Add Bitmap of the triangulation grid to the image queue.
send(m_TriMeshImages, pBitmap);
// Post a paint message to the UI thread to draw the Bitmap.
PostMessage(hWnd, WM_APP_DRAW_TRIMESH, 0, 0);
}
And when the whole mechanism is triggered, the result is this kind of:
Why, instead of the image of the triangulation mesh, I see a black background. What am I doing wrong? Help me please.
Continue reading...