Drawing Algorithm works with normal Device Context but not with Compatible Device Context + StretchBlt

huangapple go评论67阅读模式
英文:

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:

  1. 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().

  2. 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.

  3. HDC Handle Validity: Ensure that the HDC handles (drawDC and compatible) are valid and properly created.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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.

  9. 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.

  10. 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);

huangapple
  • 本文由 发表于 2023年6月29日 05:06:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76576702.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定