【问题标题】:Force Win32 common controls to draw on `ID2D1HwndRenderTarget`?强制 Win32 常用控件在“ID2D1HwndRenderTarget”上绘制?
【发布时间】:2020-09-06 22:22:13
【问题描述】:

我使用 ID2D1HwndRenderTarget 绘制大部分 UI,但我想要一些经典的窗口控件:buttonedit。如何

ID2D1HwndRenderTarget * canvas = nullptr; // it's global object
HWND button = nullptr; // it's global object
HWND edit = nullptr; // it's global object
HWND custom = nullptr; // it's global object

// mainWindow WinPproc
case WM_CREATE:
    button = CreateWindowExW(0, L"button", L"Send", WS_CHILD | WS_VISIBLE, 10, 10, 120, 30, hWnd, BUTTON_ID, hInstance, 0); // win32 control
    edit = CreateWindowExW(0, L"edit", L"Edit", WS_CHILD | WS_VISIBLE, 10, 50, 120, 30, hWnd, BUTTON_ID, hInstance, 0); // win32 control
    custom = CreateWindowExW(0, L"custom", L"Custom", WS_CHILD | WS_VISIBLE, 10, 90, 120, 30, hWnd, BUTTON_ID, hInstance, 0); // it's my custom class
    break;

case WM_PAINT:
    BeginPaint(hWnd, nullptr);
    render_target->BeginPaint();
    ... GUI rendering stuff ....
    HRESULT result = render_target->EndDraw();
    if(result != S_OK)
    {
       // Error handling
       ...
    }
    EndPaint(hWnd, nullptr);
    break;
// custom WinProc
case WM_PAINT:
    BeginPaint(hWnd, nullptr);
    render_target->BeginPaint();
    ... rendering stuff ....
    HRESULT result = render_target->EndDraw();
    if(result != S_OK)
    {
       // Error handling
       ...
    }
    EndPaint(hWnd, nullptr);
    break;

只有用render_target 绘制的东西是可见的。我明白为什么:因为buttonedit 是默认的win32 控件,内部使用PAINTSTRUCT->HDC 上下文绘制。我阅读了Direct2D and GDI Interoperability Overview 并了解了这个概念,但仍然不知道这个 HDC intrecpet 应该发生在哪里?我不想触摸默认控件 WM_PAINT。我必须supclass所有默认的win32控件?

如何强制那些 Win32 控件绘制到我的 render_target 上?

【问题讨论】:

  • Direct2D 和 GDI 可以互操作,因此您可以将控件绘制到 HDC 并使用该 DC 使用 Direct2D 进行渲染:docs.microsoft.com/en-us/windows/win32/Direct2D/…
  • 他们可能已经在内部这样做了。
  • 我将问题编辑得更准确。
  • @SimonMourier 我读到了,了解了总体思路,但仍然如此。我可以问你一些代码吗?
  • @MichaelChourdakis 不,它不会在内部发生。

标签: winapi gdi direct2d dxgi


【解决方案1】:

如果您希望在 GDI 和 Direct2D 之间共享一个设备上下文 (HDC),您可以在该 HDC 上进行渲染时使用ID2D1DCRenderTargetBind。 p>

这在这个官方示例中得到了证明:GDI/Direct2D Interoperability Sample

请注意,它不能与当今的 Visual Studio 一起编译/工作。所以,这里有一个类似的代码,带有一个简单的按钮和文本框:

#include <windows.h>
#include <stdlib.h>
#include <math.h>
#include <d2d1.h>

template<class Interface>
inline void
SafeRelease(Interface** ppInterfaceToRelease)
{
    if (*ppInterfaceToRelease != NULL)
    {
        (*ppInterfaceToRelease)->Release();
        (*ppInterfaceToRelease) = NULL;
    }
}

EXTERN_C IMAGE_DOS_HEADER __ImageBase;

class DemoApp
{
public:
    DemoApp();
    ~DemoApp();

    HRESULT Initialize();

private:
    HRESULT CreateDeviceIndependentResources();
    HRESULT CreateDeviceResources();
    void DiscardDeviceResources();
    HRESULT OnRender(const PAINTSTRUCT& ps);
    static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

private:
    HWND m_hwnd;
    ID2D1Factory* m_pD2DFactory;
    ID2D1DCRenderTarget* m_pDCRT;
    ID2D1SolidColorBrush* m_pBlackBrush;
};

int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/)
{
    if (SUCCEEDED(CoInitialize(NULL)))
    {
        DemoApp app;
        if (SUCCEEDED(app.Initialize()))
        {
            MSG msg;
            while (GetMessage(&msg, NULL, 0, 0))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        CoUninitialize();
    }
    return 0;
}

DemoApp::DemoApp() :
    m_hwnd(NULL),
    m_pD2DFactory(NULL),
    m_pDCRT(NULL),
    m_pBlackBrush(NULL)
{
}

DemoApp::~DemoApp()
{
    SafeRelease(&m_pD2DFactory);
    SafeRelease(&m_pDCRT);
    SafeRelease(&m_pBlackBrush);
}

HRESULT DemoApp::Initialize()
{
    HRESULT hr;

    hr = CreateDeviceIndependentResources();
    if (SUCCEEDED(hr))
    {
        // Register the window class.
        WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
        wcex.style = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc = DemoApp::WndProc;
        wcex.cbClsExtra = 0;
        wcex.cbWndExtra = sizeof(LONG_PTR);
        wcex.hInstance = (HINSTANCE)&__ImageBase;
        wcex.hbrBackground = NULL;
        wcex.lpszMenuName = NULL;
        wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
        wcex.lpszClassName = L"D2DDemoApp";
        RegisterClassEx(&wcex);

        // Create the application window.
        // Because the CreateWindow function takes its size in pixels, we obtain the system DPI and use it to scale the window size.
        FLOAT dpiX, dpiY;
        m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY);

        m_hwnd = CreateWindow(
            L"D2DDemoApp",
            L"Direct2D Demo App",
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            static_cast<UINT>(ceil(640.f * dpiX / 96.f)),
            static_cast<UINT>(ceil(480.f * dpiY / 96.f)),
            NULL,
            NULL,
            (HINSTANCE)&__ImageBase,
            this
        );

        hr = m_hwnd ? S_OK : E_FAIL;
        if (SUCCEEDED(hr))
        {
            ShowWindow(m_hwnd, SW_SHOWNORMAL);

            UpdateWindow(m_hwnd);
        }
    }

    return hr;
}

HRESULT DemoApp::CreateDeviceIndependentResources()
{
    // Create D2D factory
    return D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);
}

HRESULT DemoApp::CreateDeviceResources()
{
    HRESULT hr = S_OK;
    if (!m_pDCRT)
    {
        // Create a DC render target.
        D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
            D2D1_RENDER_TARGET_TYPE_DEFAULT,
            D2D1::PixelFormat(
                DXGI_FORMAT_B8G8R8A8_UNORM,
                D2D1_ALPHA_MODE_IGNORE),
            0,
            0,
            D2D1_RENDER_TARGET_USAGE_NONE,
            D2D1_FEATURE_LEVEL_DEFAULT
        );

        hr = m_pD2DFactory->CreateDCRenderTarget(&props, &m_pDCRT);
        if (SUCCEEDED(hr))
        {
            // Create a black brush.
            hr = m_pDCRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &m_pBlackBrush);
        }
    }

    return hr;
}

void DemoApp::DiscardDeviceResources()
{
    SafeRelease(&m_pDCRT);
    SafeRelease(&m_pBlackBrush);
}

HRESULT DemoApp::OnRender(const PAINTSTRUCT& ps)
{
    HRESULT hr;
    RECT rc;

    // Get the dimensions of the client drawing area.
    GetClientRect(m_hwnd, &rc);

    // Draw the pie chart with Direct2D.

    // Create the DC render target.
    hr = CreateDeviceResources();

    if (SUCCEEDED(hr))
    {
        // Bind the DC to the DC render target.
        hr = m_pDCRT->BindDC(ps.hdc, &rc);

        m_pDCRT->BeginDraw();
        m_pDCRT->SetTransform(D2D1::Matrix3x2F::Identity());
        m_pDCRT->Clear(D2D1::ColorF(D2D1::ColorF::White));

        m_pDCRT->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(150.0f, 150.0f), 100.0f, 100.0f), m_pBlackBrush, 3.0);

        m_pDCRT->DrawLine(
            D2D1::Point2F(150.0f, 150.0f),
            D2D1::Point2F((150.0f + 100.0f * 0.15425f), (150.0f - 100.0f * 0.988f)), m_pBlackBrush, 3.0
        );

        m_pDCRT->DrawLine(
            D2D1::Point2F(150.0f, 150.0f),
            D2D1::Point2F((150.0f + 100.0f * 0.525f), (150.0f + 100.0f * 0.8509f)), m_pBlackBrush, 3.0
        );

        m_pDCRT->DrawLine(
            D2D1::Point2F(150.0f, 150.0f),
            D2D1::Point2F((150.0f - 100.0f * 0.988f), (150.0f - 100.0f * 0.15425f)), m_pBlackBrush, 3.0
        );

        hr = m_pDCRT->EndDraw();

        if (SUCCEEDED(hr))
        {
            // Draw the pie chart with GDI.

            // Save the original object.
            HGDIOBJ original = NULL;
            original = SelectObject(ps.hdc, GetStockObject(DC_PEN));

            HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
            SelectObject(ps.hdc, blackPen);

            Ellipse(ps.hdc, 300, 50, 500, 250);

            POINT pntArray1[2];
            pntArray1[0].x = 400;
            pntArray1[0].y = 150;
            pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
            pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);

            POINT pntArray2[2];
            pntArray2[0].x = 400;
            pntArray2[0].y = 150;
            pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
            pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);


            POINT pntArray3[2];
            pntArray3[0].x = 400;
            pntArray3[0].y = 150;
            pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
            pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);

            Polyline(ps.hdc, pntArray1, 2);
            Polyline(ps.hdc, pntArray2, 2);
            Polyline(ps.hdc, pntArray3, 2);

            DeleteObject(blackPen);

            // Restore the original object.
            SelectObject(ps.hdc, original);
        }
    }

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }

    return hr;
}

LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message == WM_CREATE)
    {
        LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pcs->lpCreateParams);

        auto button = CreateWindowExW(0, L"button", L"Send", WS_CHILD | WS_VISIBLE, 10, 10, 120, 30, hwnd, (HMENU)1, (HINSTANCE)&__ImageBase, 0); // win32 control
        auto edit = CreateWindowExW(0, L"edit", L"Edit", WS_CHILD | WS_VISIBLE, 10, 50, 120, 30, hwnd, (HMENU)2, (HINSTANCE)&__ImageBase, 0); // win32 control
        return 1;
    }

    LRESULT result = 0;
    DemoApp* pDemoApp = (DemoApp*)(GetWindowLongPtr(hwnd, GWLP_USERDATA));
    bool wasHandled = false;
    if (pDemoApp)
    {
        switch (message)
        {
        case WM_PAINT:
        case WM_DISPLAYCHANGE:
        {
            PAINTSTRUCT ps;
            BeginPaint(hwnd, &ps);
            pDemoApp->OnRender(ps);
            EndPaint(hwnd, &ps);
        }
        result = 0;
        wasHandled = true;
        break;

        case WM_DESTROY:
        {
            PostQuitMessage(0);
        }
        result = 1;
        wasHandled = true;
        break;
        }
    }

    if (!wasHandled)
    {
        result = DefWindowProc(hwnd, message, wParam, lParam);
    }

    return result;
}

这是它的渲染方式(左圈是 Direct2D,右圈是别名 GDI):

【讨论】:

    【解决方案2】:

    如何强制 Win32 控件绘制到我的 ID2D1Bitmap1 上?这可能吗?

    是的。您可以将GDI 内容写入Direct2D GDI 兼容的渲染目标。这种方法对于主要使用 Direct2D 呈现但具有可扩展性模型或其他需要能够使用 GDI 呈现的遗留内容的应用程序很有用。

    步骤:

    要将 GDI 内容渲染到 Direct2D GDI 兼容的渲染目标,请使用 ID2D1GdiInteropRenderTarget,提供对设备的访问 可以接受 GDI 绘制调用的上下文。与其他接口不同,一个 ID2D1GdiInteropRenderTarget 对象不是直接创建的。反而, 使用现有渲染目标实例的QueryInterface 方法。

    参考:Draw GDI Content to a Direct2D GDI-Compatible Render Target

    【讨论】:

    • @Sonny D 如果您有任何问题,请随时告诉我。
    • 我编辑了我的问题。如果我正确理解 MSDN 的解决方案,则需要更改默认 WM_PAINT。我不想碰那个代码。
    猜你喜欢
    • 2021-12-04
    • 2010-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-29
    • 1970-01-01
    • 2012-08-06
    相关资源
    最近更新 更多