c++ – Buffer does not match the specified parameters Dpi problems

Question:

I am making a program that will draw on top of the target window what I need, but when I draw, drawing goes to the wrong coordinates, which I indicated. Moreover, the buffer size for some reason does not correspond to the parameters that I set. So I initialize d2d:

ID2D1Factory* pFactory;
ID2D1HwndRenderTarget* pRenderTarget;
ID2D1SolidColorBrush* ColorBrush;

bool init_render()
{
    D2D1_FACTORY_OPTIONS CreateOpt = { D2D1_DEBUG_LEVEL_NONE };
    if (S_OK != D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), &CreateOpt, (void**)&pFactory))
    {
        MessageBox(0, "D2D1CreateFactory", "ERROR", MB_OK | MB_ICONERROR);
        return 0;
    }
    create_canvas();
    return 1;
}
void create_canvas()
{
    RECT rc;
    GetClientRect(targetHWND, &rc);
    //std::cout<< rc.right - rc.left <<" "<< rc.bottom - rc.top<<std::endl;

    pFactory->CreateHwndRenderTarget(
        D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_HARDWARE, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)),//D2D1_RENDER_TARGET_TYPE_DEFAULT
        D2D1::HwndRenderTargetProperties(myHWND, D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)),
        &pRenderTarget);
    pRenderTarget->CreateSolidColorBrush(color_brush, &ColorBrush);
}

I adjust the size of my window in a loop to the size of the window over which we will draw. Also, in the message handler of my window, I change the buffer size when changing the size of the target window:

LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_SIZE:
        if (pRenderTarget != NULL)pRenderTarget->Resize(D2D1::SizeU((UINT)LOWORD(lParam), (UINT)HIWORD(lParam)));
        return 0;
    case WM_SYSCOMMAND:
        if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
            return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
}

But if you get the size of the buffer, then it is not equal to the sizes that I gave it, more precisely, it is not equal to the size of the target window in pixels:

void rect(float x,float y,float h,float w) {
    D2D1_SIZE_F rtSize= pRenderTarget->GetSize();//получаем размер буфера 
    int width = static_cast<int>(rtSize.width);
    int height = static_cast<int>(rtSize.height);
    std::cout <<"Render target: "<< width<<" " << height << std::endl;//выводим размер буфера

    ColorBrush->SetColor({0.0f,1.0f,0.0f,1.0f});
    pRenderTarget->DrawRectangle(D2D1::RectF(x, y, x+h, y+w), ColorBrush,1.0f);//пробуем рисовать по заданным координатам
}

As a result, the buffer is of the wrong size, and the drawing is done in the wrong place. The effect is as if the coordinates (in pixels), where I say to draw one and a half times (approximately) more.

Answer:

Dug around and found the answer. As it turns out, windows uses scaling and when the user sets a value greater than 100%, the system DPI changes. When we create a directx context, it defaults to the system dpi. To set the dpi manually (in my case, I need 100% – this is 96dpi horizontally and vertically, since I have square pixels), you need to set the dpi manually in the ID2D1RenderTarget interface:

pRenderTarget->SetDpi(96, 96);

Or, during initialization:

pFactory->CreateHwndRenderTarget(
        D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_HARDWARE, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),96,96),//D2D1_RENDER_TARGET_TYPE_DEFAULT
        D2D1::HwndRenderTargetProperties(cheatEspHWND, D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)),
        &pRenderTarget);

Thanks to @Qwertiy for the suggestive answer.

Scroll to Top