c++ 绘制算法适用于正常设备上下文,但不适用于兼容设备上下文+ StretchBlt

lvmkulzt  于 2023-07-01  发布在  其他
关注(0)|答案(1)|浏览(109)

我正在编写一个应用程序,它可以作为Java代码的API使用JNI。Java应用程序假定窗口客户端区域为800x400像素。无法修改此值。图像元素的所有大小和位置都与此格式相关。因此,必须将800x400表示“转换”为客户端区域内容。到目前为止,我是通过阻止用户调整窗口大小来做到这一点的:

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

显然,这在用户体验方面是不好的。因此,我想要一种方法来调整800x400区域的内容。
到目前为止,该区域是通过以下算法绘制的,该算法运行良好:

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;
[...]

我的想法是将上面算法的结果绘制到800x400格式的Compatible Device Context中,然后使用StretchBlt将其拉伸到真实的窗口中,同时保持比例为2:1。为此,我编写了以下算法:

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;
[...]

这里的问题是,即使直接在BeginPaint返回的HDC上使用paint操作也可以工作,但当以这种方式使用时,会出现错误,窗口只是黑色的。这意味着,FillRect成功,但StretchBlt似乎默默地失败了(它返回1,但没有应用更改)。我已经不知道该怎么办了,在网上什么也没找到,问ChatGPT也没用。

mitkmikd

mitkmikd1#

CreateCompatibleDC创建的位图为1x11位。你需要为它创建更有趣的位图。

HBITMAP drawBM = CreateCompatibleBitmap(hdc, 800, 400);
HBITMAP oldBM = SelectObject(drawDC, drawBM);
... drawing
SelectObject(drawDC, oldBM);
DeleteObject(drawBM);

相关问题