英文:
Drawing Algorithm works with normal Device Context but not with Compatible Device Context + StretchBlt
问题
I'm sorry to hear that you're facing an issue with your code. It appears that you're trying to resize the content of a window from an 800x400 format to the actual client area size while maintaining a 2:1 aspect ratio. However, you're encountering problems with the StretchBlt
operation resulting in a black window.
Unfortunately, without access to your complete code and the ability to run it and debug, it's challenging to pinpoint the exact issue. Debugging complex graphical issues like this often requires close examination of the code and potential experimentation.
Here are a few general suggestions that might help you troubleshoot the issue:
-
Check for Errors: Ensure that there are no error codes being returned by the Windows API functions like
StretchBlt
. You mentioned that it returned 1, which usually indicates success. However, it's essential to double-check for any error codes or GetLastError(). -
DC Compatibility: Verify that the device contexts (DCs) you're using are compatible with each other. The source and destination DCs should have compatible attributes. You can use
GetDeviceCaps
to compare attributes like color depth, technology, etc., between the source and destination DCs. -
HDC Handle Validity: Ensure that the HDC handles (
drawDC
andcompatible
) are valid and properly created. -
Paint Order: Confirm that the drawing operations are performed in the correct order and that there are no conflicting operations that might overwrite or hide the content.
-
Error Handling: Implement robust error handling to log or display diagnostic information when something goes wrong. You can use
OutputDebugString
or other logging mechanisms to capture information during runtime. -
Try GDI+: As an alternative, you might consider using GDI+ for rendering. GDI+ provides more advanced graphics capabilities and might offer more straightforward solutions to your problem.
-
Double-Buffering: Consider using double-buffering techniques to avoid flickering during rendering. Double-buffering involves drawing to an off-screen buffer and then copying the buffer to the screen in a single operation.
-
Check Thread Safety: Ensure that your multi-threaded code is thread-safe. In your code, you're starting a separate thread for
RedrawWindow
, so ensure that there are no race conditions or synchronization issues causing problems. -
Consult Documentation: Carefully review the documentation for the Windows API functions you're using, paying attention to any special requirements or limitations that may apply to your scenario.
-
Debugging Tools: If you have access to debugging tools like Visual Studio, use them to step through your code and inspect the variables and DCs during runtime to identify the problem.
If none of these suggestions resolve your issue, you may consider seeking assistance on programming forums or communities where experienced developers can provide more specific guidance based on the complete code and any error messages you encounter.
英文:
Im writing an application which works as an API for Java Code using JNI. The Java application assumes the Window Client Area is 800x400 pixels. This value cannot be modified. All sizes and positions of image elements are relative to this format. Therefore, the 800x400-representation has to be "translated" into client-area content. Up until now, i did this by preventing the user from resizing the window:
LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE);
style &= ~(WS_THICKFRAME| WS_MAXIMIZEBOX);
SetWindowLongPtr(hwnd, GWL_STYLE, style);
SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
Obviously, this is bad in terms of user experience.
Therefore, i wanted a way to resize the contents of the 800x400 area.
Up until now, the area was drawn by the following algorithm, which worked fine:
LRESULT CALLBACK WindowCallbackF(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
WindowInterface* wi;
wi = getInterface(hwnd)/*This call always succeeds when HWND was created with this API;
What it does is irrelevant here(In case you are interested: it looks up hwnd in a global std::unordered_map of hwnd,WindowInterface* pairs*/;
switch (uMsg)
{
case WM_PAINT: {
PAINTSTRUCT ps;
ImageToRender* figure;
if (!hwnd) {
return 0;
}
HDC hdc = BeginPaint(hwnd, &ps);
HDC compatible = CreateCompatibleDC(hdc);
if ((wi->flags) && WindowFlags::BackgroundSet) {
SelectObject(compatible, wi->backgroundImage/*HBITMAP for the Background Image*/);
BitBlt(hdc, 0, 0, wi->initWidth/*800*/, wi->initHeight/*400*/, compatible, 0, 0, SRCCOPY);
}
wi->lock.lock();
/*wi->lock is a std::unique_lock<std::mutex>, and wi->figures is a std::vector<ImageToRender*>*/
for (size_t i = (wi->figures).size(); i > 0; --i) {
figure = (wi->figures)[i - 1];
if (figure->flags & ImageToRenderFlags::Visible) {
SelectObject(compatible, figure->bitmap/*Bitmap for the Item*/);
TransparentBlt(hdc,figure->xPos,figure->yPos/*Position of the figure in the client area*/,
figure->width/*width of the figure in the client area*/,
figure->height/*height of the figure in the client area*/,
compatible, 0, 0,
figure->bmpWidth/*Width of the original bitmap*/,
figure->bmpHeight/*Height of the original Bitmap*/,
RGB(0xFF,0xFF,0xFF));
}
}
DeleteDC(compatible);
wi->lock.unlock();
EndPaint(hwnd, &ps); }
return 0;
[...]
My idea was to draw the result of the algorithm above into a Compatible Device Context with the 800x400 format, and then use StretchBlt
to stretch it into the real window, while maintaining the ratio 2:1. To do so, i wrote the following algorithm:
LRESULT CALLBACK WindowCallbackF(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
WindowInterface* wi;
wi = getInterface(hwnd);
switch (uMsg)
{
case WM_PAINT: {
PAINTSTRUCT ps;
ImageToRender* figure;
if (!hwnd) {
return 0;
}
HDC hdc = BeginPaint(hwnd, &ps);
HDC drawDC = CreateCompatibleDC(hdc);
HDC compatible = CreateCompatibleDC(hdc);
if ((wi->flags) && WindowFlags::BackgroundSet) {
SelectObject(compatible, wi->backgroundImage/*HBITMAP for the Background Image*/);
BitBlt(drawDC, 0, 0, wi->initWidth/*800*/, wi->initHeight/*400*/, compatible, 0, 0, SRCCOPY);
}
wi->lock.lock();
/*wi->lock is a std::unique_lock<std::mutex>, and wi->figures is a std::vector<ImageToRender*>*/
for (size_t i = (wi->figures).size(); i > 0; --i) {
figure = (wi->figures)[i - 1];
if (figure->flags & ImageToRenderFlags::Visible) {
SelectObject(compatible, figure->bitmap/*Bitmap for the Item*/);
TransparentBlt(drawDC,figure->xPos,figure->yPos/*Position of the figure in the client area*/,
figure->width/*width of the figure in the client area*/,
figure->height/*height of the figure in the client area*/,
compatible, 0, 0,
figure->bmpWidth/*Width of the original bitmap*/,
figure->bmpHeight/*Height of the original Bitmap*/,
SRCCOPY);
}
}
DeleteDC(compatible);
int height = wi->height;
int width = wi->width;
int xPos = 0;
int yPos = 0;
if (wi->flags & WindowFlags::Y_DIM_OVERRIDE) {
height = wi->dimensionOverride;
yPos = wi->dimensionOffset;
}
else if (wi->flags & WindowFlags::X_DIM_OVERRIDE) {
width = wi->dimensionOverride;
xPos = wi->dimensionOffset;
}
RECT fillRect;
fillRect.top = 0;
fillRect.left = 0;
fillRect.right = wi->width + 1;
fillRect.bottom = wi->height + 1;
FillRect(hdc, &fillRect, reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));
//FillRect Sets the background to black. Due to the 2:1-Ratio being maintained, not everything is painted; The unpainted parts should have a black background. This call succeeds.
StretchBlt(hdc, 0, 0, width/*destination width*/, height/*destination height*/, drawDC, 0, 0, wi->initWidth/*800*/, wi->initHeight/*400*/, RGB(0xFF,0xFF,0xFF));
DeleteDC(drawDC);
wi->lock.unlock();
EndPaint(hwnd, &ps); }
return 0;
case WM_SIZE:
{
RECT clientRects;
GetClientRect(hwnd, &clientRects);
//Dimensions of the Window are updated here
(*wi).width = clientRects.right - clientRects.left;
(*wi).height = clientRects.bottom - clientRects.top;
if (wi->width > (wi->height * 2)) {
//Left+Right are filled with black background
(*wi).flags &= ~WindowFlags::Y_DIM_OVERRIDE;
(*wi).flags |= WindowFlags::X_DIM_OVERRIDE;
(*wi).dimensionOverride = wi->height * 2;
(*wi).dimensionOffset = (wi->width - wi->dimensionOverride) / 2;
}
else if (wi->width==wi->height*2) {
//No black background when width==height*2
(*wi).flags &= ~(WindowFlags::Y_DIM_OVERRIDE|WindowFlags::X_DIM_OVERRIDE);
}
else {
//Top+Bottom are filled with black background
(*wi).flags &= ~WindowFlags::X_DIM_OVERRIDE;
(*wi).flags |= WindowFlags::Y_DIM_OVERRIDE;
(*wi).dimensionOverride = wi->width / 2;
(*wi).dimensionOffset = (wi->height - wi->dimensionOverride) / 2;
}
std::thread thread{RedrawWindow, hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN};//Starting RedrawWindow in a new Thread prevents deadlocks where the window message thread waits for RedrawWindow, and RedrawWindow waits for the window message thread to process WM_PAINT
thread.detach();
};
return 0;
[...]
The problem here is that even though the paint operations worked when used directly on the HDC returned by BeginPaint
, when used this way, something goes wrong, and the window is just black.This means, that FillRect
succeeds, but StretchBlt
seems to silently fail(it returned 1, but the changes were not applied). I have no idea what to do anymore, found nothing on the Internet, and asking ChatGPT was of no use either.
答案1
得分: 2
CreateCompatibleDC
创建的位图为1x1 1位。
您需要为其创建更有趣的位图。
HBITMAP drawBM = CreateCompatibleBitmap(hdc, 800, 400);
HBITMAP oldBM = SelectObject(drawDC, drawBM);
... 绘制
SelectObject(drawDC, oldBM);
DeleteObject(drawBM);
英文:
Bitmap created by CreateCompatibleDC
is 1x1 1 bit.
You need to create more interesting bitmap for it.
HBITMAP drawBM = CreateCompatibleBitmap(hdc, 800, 400);
HBITMAP oldBM = SelectObject(drawDC, drawBM);
... drawing
SelectObject(drawDC, oldBM);
DeleteObject(drawBM);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论