【问题标题】:D2D coordinates not matching screen coordinatesD2D 坐标与屏幕坐标不匹配
【发布时间】:2020-07-16 05:22:55
【问题描述】:

我将 D2D 与 D3D11 一起使用。我有一些代码使用 Windows API 中的 GetCursorpos(),然后将其转换为客户端坐标,然后使用 D2D FillEllipse() 在该位置绘制一个小圆圈。屏幕到客户端坐标的工作完美,但由于某种原因,D2D 在距离预期位置(几十个像素)一小段距离处绘制圆,就好像坐标已按一个小因子缩放一样,因此随着圆的进一步绘制,误差会变大从 (0, 0)。 我注意到更改 D2D1_RENDER_TARGET_PROPERTIES 的 dpi 会影响这种“缩放”,所以我怀疑问题与 dpi 有关。这是从我的 D3D11 代码中的交换链获得的 DXGI 表面创建 D2D 渲染目标的代码。

    // Create render target
    float dpiX, dpiY;
    this->factory->GetDesktopDpi(&dpiX, &dpiY);
    D2D1_RENDER_TARGET_PROPERTIES rtDesc = D2D1::RenderTargetProperties(
        D2D1_RENDER_TARGET_TYPE_HARDWARE,
        D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
        dpiX,
        dpiY
    );
    AssertHResult(this->factory->CreateDxgiSurfaceRenderTarget(
        surface.Get(),
        &rtDesc,
        &this->renderTarget
    ), "Failed to create D2D render target");

在这里,dpiX 和 dpiY 变为 96,我注意到这也是 Windows API 中的 GetDpiForWindow() 在不知道 dpi 时返回的常量。

我想知道如何修复我的代码,以便它在 GetCursorPos() 给出的位置绘制圆。

更多相关代码:

驱动程序代码

    Vector3f cursPos = input.GetCursorPos();
    DrawCircle(Colour::Green, cursPos.x, cursPos.y, 3/*radius*/);

输入

    POINT pt{};
    ::GetCursorPos(&pt);
    // Convert from screen pixels to client pixels
    return ConvertPixelSpace(this->hWnd, (float)pt.x, (float)pt.x, PixelSpace::Screen, PixelSpace::Client);

Direct2D

void DrawCircle(const Colour& c, float centreX, float centreY, float radius, PixelSpace ps)
{
    Vector3f centre = ConvertPixelSpace(this->gfx.hWnd, centreX, centreY, ps, PixelSpace::Client);
    centreX = centre.x;
    centreY = centre.y;
    D2D1_ELLIPSE el{};
    el.point.x = centreX;
    el.point.y = centreY;
    el.radiusX = radius;
    el.radiusY = radius;
    auto brush = this->CreateBrush(c);
    this->renderTarget->FillEllipse(
        &el,
        brush.Get()
    );
}

像素空间转换

Vector3f ConvertPixelSpace(HWND hWnd, float x, float y, PixelSpace curSpace, PixelSpace newSpace)
{
    RECT rc = GetClientRectOfWindow(hWnd);
    struct
    {
        float top, left, width, height;
    } rectf;
    rectf.top = static_cast<float>(rc.top);
    rectf.left = static_cast<float>(rc.left);
    rectf.width = static_cast<float>(rc.right - rc.left);
    rectf.height = static_cast<float>(rc.bottom - rc.top);

    // Convert to client space
    if (curSpace == PixelSpace::Screen)
    {
        x -= rectf.left;
        y -= rectf.top;
    }

    // Convert to new space
    if (newSpace == PixelSpace::Screen)
    {
        x += rectf.left;
        y += rectf.top;
    }

    return Vector3f(x, y);
}

RECT GetClientRectOfWindow(HWND hWnd)
{
    RECT rc;
    ::GetClientRect(hWnd, &rc);
    // Pretty sure these are valid casts.
    // rc.top is stored directly after rc.left and this forms a POINT struct
    ClientToScreen(hWnd, reinterpret_cast<POINT*>(&rc.left));
    ClientToScreen(hWnd, reinterpret_cast<POINT*>(&rc.right));
    return rc;
}

【问题讨论】:

  • 您的代码中可能存在错误,但您没有显示相关代码或重现示例。
  • @Simon Mourier 感谢您的回复,我已经添加了更多相关代码
  • 一些提示:1) 您可以在 D2D1_RENDER_TARGET_PROPERTIES 中为 dpi 传递 0,2) 当您看到带有 D2D 的浮点坐标时,这意味着它说的是 DIPS 而不是像素,除非您之前使用过 ID2D1DeviceContext::SetUnitMode 方法, 3) D2D 通常是在屏幕坐标上说话,而不是客户端(它的客户端是什么?),除非您使用变换、效果等。据我了解,您的 FillEllipse 似乎使用客户端坐标,应该是“屏幕",但这取决于你如何创建和设置渲染目标......或者给我们一个完整的小再现样本。
  • @Simon Mourier 原来我正在创建 d3d11 设备和使用窗口尺寸而不是客户区的交换链,这导致了“缩放”。非常感谢,因为如果不是你,我就不会创建导致我发现错误的复制样本。

标签: c++ direct3d direct2d


【解决方案1】:

问题是我使用窗口区域而不是客户区域创建 D3D11 交换链。

    RECT rect{};
    GetWindowRect(hWnd, &rect); // !!! This should be GetClientRect()
    this->width = rect.right - rect.left;
    this->height = rect.bottom - rect.top;

    DXGI_SWAP_CHAIN_DESC scDesc{};
    scDesc.BufferDesc.Width = width;
    scDesc.BufferDesc.Height = height;
    //...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-29
    • 1970-01-01
    • 1970-01-01
    • 2018-01-29
    • 1970-01-01
    • 2018-02-27
    • 1970-01-01
    相关资源
    最近更新 更多