繁体   English   中英

使用 Windows API 调整屏幕大小时,DPI 感知会扭曲文本

[英]DPI aware distorts text when screen resized with Windows API

我正在使用 Win32 API 在 C++ 中编写一个应用程序。 我首先注意到使用DrawTextLayout()渲染的文本非常模糊,所以我添加了以下行:

SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); 

起初,这似乎起到了作用:文本看起来更清晰,并且更多的是我对 Windows 应用程序的期望。

但是,当我调整 window 的大小(通过拖动底角)时,我注意到我绘制的文本变形了。

我怎样才能保持使用DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2的清晰度,同时又能防止文本被拉伸?

#include "targetver.h"
#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <dwrite.h>
#include <d2d1.h>
#define IDS_APP_TITLE           103
#define IDR_MAINFRAME           128
#define IDD_PRACTICE_DIALOG 102
#define IDD_ABOUTBOX            103
#define IDM_ABOUT               104
#define IDM_EXIT                105
#define IDI_PRACTICE            107
#define IDI_SMALL               108
#define IDC_PRACTICE            109
#define IDC_MYICON              2
#ifndef IDC_STATIC
#define IDC_STATIC              -1
#endif
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name
ID2D1Factory* m_pD2DFactory;
ID2D1HwndRenderTarget* m_pRenderTarget;
ID2D1SolidColorBrush* m_pBlackBrush;
IDWriteFactory* writeFactory;
IDWriteTextFormat* writeTextFormat;
IDWriteTextLayout* writeTextLayout;
RECT rc;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PRACTICE));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_PRACTICE);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hInst = hInstance; // Store instance handle in our global variable
    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);
    //create device independent resources
    {
        SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
        // Create a Direct2D factory.
        D2D1CreateFactory(
            D2D1_FACTORY_TYPE_SINGLE_THREADED,
            &m_pD2DFactory
        );
        DWriteCreateFactory(
            DWRITE_FACTORY_TYPE_SHARED,
            __uuidof(IDWriteFactory),
            reinterpret_cast<IUnknown**>(&writeFactory)
        );
    }
    if (!hWnd)
    {
        return FALSE;
    }
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return TRUE;
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
    // Initialize global strings
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_PRACTICE, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);
    // Perform application initialization:
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PRACTICE));
    MSG msg;
    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        //create device dependent resources
        {
            HRESULT hr = S_OK;
            if (!m_pRenderTarget) {
                GetClientRect(hWnd, &rc);
                D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
                // Create a Direct2D render target 
                hr = m_pD2DFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hWnd, size), &m_pRenderTarget);
                if (SUCCEEDED(hr))
                {
                    // Create a black brush
                    hr = m_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &m_pBlackBrush);
                }
            }
        }
        //render text
        {
            writeFactory->CreateTextFormat(
                L"Times New Roman",
                NULL,
                DWRITE_FONT_WEIGHT_NORMAL,
                DWRITE_FONT_STYLE_NORMAL,
                DWRITE_FONT_STRETCH_NORMAL,
                14.0f,
                L"EN-US",
                &writeTextFormat
            );
            writeFactory->CreateTextLayout(
                L"String",      // The string to be laid out and formatted.
                6,  // The length of the string.
                writeTextFormat,  // The text format to apply to the string (contains font information, etc).
                200,         // The length of the layout box.
                500,        // The width of the layout box.
                &writeTextLayout  // The IDWriteTextLayout interface pointer.
            );
        }
        m_pRenderTarget->BeginDraw();
        m_pRenderTarget->SetTransform(D2D1::IdentityMatrix());
        m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
        m_pRenderTarget->DrawTextLayout(D2D1::Point2F(0, 0), writeTextLayout, m_pBlackBrush);
        m_pRenderTarget->EndDraw();
        EndPaint(hWnd, &ps);
    }
    break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

我很抱歉这是大量的代码; 但是,为了能够完全运行,我必须包含这么多。

尽管设置 dpi-awareness 是避免高分辨率显示器模糊的关键步骤(当 dpi 不是 100% 时),但在 window 调整大小时文本被拉伸的问题是一个单独的问题。

这只是一个 DirectX (DirectWrite) 表面问题。 我对 DirectWrite 的经验有限,但对此的修复非常明显。 与 D3D 一样,DirectWrite 表面延伸到 window 的大小,但它不会直接知道 window 的大小变化,除非您告诉它。

可能有两个非常简单的修复:

在您的WndProc function 中,当大小发生变化时,将新大小传递给m_pRenderTarget事物。 捕捉WM_SIZE

case WM_SIZE:
{
    if (m_pRenderTarget)
    {
        GetClientRect(hWnd, &rc);
        D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
        m_pRenderTarget->Resize(size);
    }
    break;
}

或者,因为如果它是 null,您的WM_PAINT代码无论如何都会重新创建该东西:

case WM_SIZE:
{
    if (m_pRenderTarget)
    {
        m_pRenderTarget->Release();
        m_pRenderTarget = nullptr;
    }
    break;
}

另一个小修复。 在调用 CreateWindow之前调用SetProcessDpiAwarenessContext 在你开始做任何其他事情之前,在 WinMain 的早期阶段。 或者使用应用程序清单文件来设置它

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM