Координаты D2D не соответствуют координатам экрана - PullRequest
0 голосов
/ 04 апреля 2020

Я использую D2D с D3D11. У меня есть некоторый код, который использует GetCursorpos () из windows API, который затем преобразуется в клиентские координаты, а затем др aws небольшой круг в этой позиции с помощью D2D FillEllipse (). Координаты экрана к клиенту работают отлично, но по какой-то причине D2D dr aws окружность на небольшом расстоянии от ожидаемого местоположения (десятки пикселей), как если бы координата была масштабирована с небольшим коэффициентом, так что ошибка становится больше по мере окружности рисуется дальше от (0, 0). Я заметил, что изменение dpi для D2D1_RENDER_TARGET_PROPERTIES влияет на это «масштабирование», поэтому я подозреваю, что проблема как-то связана с dpi. Это код для создания цели рендеринга D2D из поверхности DXGI, которую я получил из цепочки обмена в моем коде D3D11.

    // 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, и я замечаю, что это также константа, которую GetDpiForWindow () из API windows возвращает, когда он не поддерживает 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()
    );
}

Преобразование PixelSpace

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

1 Ответ

0 голосов
/ 07 апреля 2020

Проблема заключалась в том, что я создавал swapchain 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;
    //...
...