Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Чтобы эффективно программировать с помощью графики Windows, необходимо понять два связанных понятия:
- Точки на дюйм (DPI)
- Независимые от устройства пиксели (DIPs).
Начнем с DPI. Для этого потребуется короткий обход в типографию. В типографии размер типа измеряется в единицах, называемых точками. Одна точка равна 1/72 дюйма.
- 1 pt = 1/72 дюйма
Заметка
Это определение публикации на рабочем столе. Исторически точное измерение точки разными.
Например, шрифт с 12 точками предназначен для размещения в строке текста 1/6 (12/72). Очевидно, что это не означает, что каждый символ в шрифте в точности 1/6" высотой. На самом деле некоторые символы могут быть выше 1/6". Например, во многих шрифтах символ Å выше номинальной высоты шрифта. Для правильного отображения шрифту требуется дополнительное пространство между текстом. Это пространство называется ведущих.
На следующем рисунке показан 72-точечный шрифт. Сплошные линии показывают 1"высокий ограничивающий прямоугольник вокруг текста. Пунктирная строка называется базовой . Большинство символов в шрифте не зависят от базового плана. Высота шрифта включает часть над базовым показателем (восхождение) и часть ниже базовой (спуск). На шрифте, показанном здесь, восхождение составляет 56 точек, и спуск составляет 16 очков.
Однако, когда дело доходит до экрана компьютера, измерение размера текста проблематично, так как пиксели не все одинаковые размеры. Размер пикселя зависит от двух факторов: разрешения дисплея и физического размера монитора. Таким образом, физические дюймы не являются полезной мерой, поскольку нет фиксированной связи между физическими дюймами и пикселями. Вместо этого шрифты измеряются в логических единицах. Шрифт с 72 точками определяется как один логический дюйм высотой. Затем логические дюймы преобразуются в пиксели. В течение многих лет Windows использовала следующее преобразование: один логический дюйм равен 96 пикселей. С помощью этого коэффициента масштабирования шрифт 72-точечный отображается как 96 пикселей высотой. 12-точечный шрифт имеет высоту 16 пикселей.
- 12 точек = 12/72 логический дюйм = 1/6 логический дюйм = 96/6 пикселей = 16 пикселей
Этот коэффициент масштабирования описывается как 96 точек на дюйм (DPI). Термин точек является производным от печати, где физические точки рукописного ввода помещаются на бумагу. Для отображения компьютеров было бы более точно сказать 96 пикселей на логический дюйм, но термин DPI застрял.
Поскольку фактические размеры пикселей зависят, текст, доступный для чтения на одном мониторе, может быть слишком мал на другом мониторе. Кроме того, люди имеют разные предпочтения— некоторые люди предпочитают более крупный текст. По этой причине Windows позволяет пользователю изменить параметр DPI. Например, если пользователь задает для отображения значение 144 DPI, то 72-точечный шрифт имеет высоту 144 пикселей. Стандартные параметры DPI: 100% (96 DPI), 125% (120 DPI) и 150% (144 DPI). Пользователь также может применить настраиваемый параметр. Начиная с Windows 7 DPI — это параметр для каждого пользователя.
Масштабирование DWM
Если программа не учитывает DPI, следующие дефекты могут быть очевидны при параметрах высокого уровня DPI:
- Обрезанные элементы пользовательского интерфейса.
- Неправильный макет.
- Пиксельные растровые изображения и значки.
- Неправильные координаты мыши, которые могут повлиять на тестирование попаданий, перетаскивание и т. д.
Чтобы обеспечить работу старых программ с высоким уровнем DPI, DWM реализует полезный резервный вариант. Если программа не помечена как соответствующая DPI, DWM масштабируется весь пользовательский интерфейс, чтобы он соответствовал параметру DPI. Например, в 144 DPI пользовательский интерфейс масштабируется на 150%, включая текст, графику, элементы управления и размеры окон. Если программа создает 500 × 500 окон, окно фактически отображается как 750 × 750 пикселей, а содержимое окна масштабируется соответствующим образом.
Это означает, что старые программы "просто работают" в параметрах высокого уровня DPI. Однако масштабирование также приводит к несколько размытию внешнего вида, так как масштабирование применяется после рисования окна.
Приложения с поддержкой DPI
Чтобы избежать масштабирования DWM, программа может пометить себя как DPI-поддерживающая. Это указывает DWM не выполнять автоматическое масштабирование DPI. Все новые приложения должны быть предназначены для поддержки DPI, так как осведомленность о DPI улучшает внешний вид пользовательского интерфейса при более высоких параметрах DPI.
Программа объявляет себя с поддержкой DPI через манифест приложения. Манифест — это просто XML-файл, описывающий библиотеку DLL или приложение. Манифест обычно внедряется в исполняемый файл, хотя его можно указать как отдельный файл. Манифест содержит такие сведения, как зависимости DLL, запрошенный уровень привилегий и какая версия Windows была разработана для программы.
Чтобы объявить, что программа учитывает DPI, добавьте в манифест следующие сведения.
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
Приведенный здесь список представляет собой только частичный манифест, но компоновщик Visual Studio автоматически создает остальную часть манифеста. Чтобы включить частичный манифест в проект, выполните следующие действия в Visual Studio.
- В меню Проект щелкните Свойство.
- В левой области разверните свойства конфигурации, разверните инструмент манифеста, а затем щелкните входные и выходные данные.
- В текстовом поле дополнительные файлы манифеста введите имя файла манифеста и нажмите кнопку ОК.
Помечая программу как DPI, вы сообщаете DWM не масштабировать окно приложения. Теперь, если создать 500 × 500 окон, окно займет 500 × 500 пикселей независимо от параметра DPI пользователя.
GDI и DPI
Рисунок GDI измеряется в пикселях. Это означает, что если программа помечена как функция DPI, и вы попросите GDI нарисовать прямоугольник 200 × 100, результирующий прямоугольник будет иметь ширину 200 пикселей и 100 пикселей высотой на экране. Однако размеры шрифта GDI масштабируются до текущего параметра DPI. Другими словами, если вы создаете 72-точечный шрифт, размер шрифта будет составлять 96 пикселей в 96 DPI, но 144 пикселей в 144 DPI. Вот 72-точечный шрифт, отрисованный в 144 DPI с помощью GDI.
Если приложение учитывает DPI и используете GDI для рисования, масштабируйте все координаты документа, чтобы соответствовать DPI.
Direct2D и DPI
Direct2D автоматически выполняет масштабирование в соответствии с параметром DPI. В Direct2D координаты измеряются в единицах, называемых независимых от устройства пикселей (DIPs). DIP определяется как 1/96-е значение логического дюйма. В Direct2D все операции рисования указываются в dips, а затем масштабируются до текущего параметра DPI.
Параметр DPI | Размер DIP |
---|---|
96 | 1 пиксель |
120 | 1,25 пикселя |
144 | 1,5 пикселя |
Например, если параметр DPI пользователя равен 144 DPI, и вы попросите Direct2D нарисовать прямоугольник 200 × 100, прямоугольник будет 300 × 150 физических пикселей. Кроме того, DirectWrite измеряет размеры шрифтов в DIPs, а не точки. Чтобы создать шрифт с 12 точками, укажите 16 DIPs (12 точек = 1/6 логический дюйм = 96/6 DIPs). При рисовании текста на экране Direct2D преобразует dips в физические пиксели. Преимущество этой системы заключается в том, что единицы измерения согласованы как для текста, так и для рисования независимо от текущего параметра DPI.
Слово предостережения: координаты мыши и окна по-прежнему задаются в физических пикселях, а не dips. Например, если вы обрабатываете сообщение WM_LBUTTONDOWN, то положение вниз по указателю отображается в физических пикселях. Чтобы нарисовать точку в этой позиции, необходимо преобразовать координаты пикселей в DIPs.
Преобразование физических пикселей в DIPs
Базовое значение DPI определяется как USER_DEFAULT_SCREEN_DPI
значение 96. Чтобы определить коэффициент масштабирования, примите значение DPI и разделите на USER_DEFAULT_SCREEN_DPI
.
Преобразование из физических пикселей в DIPs использует следующую формулу.
DIPs = pixels / (DPI / USER_DEFAULT_SCREEN_DPI)
Чтобы получить параметр DPI, вызовите функцию GetDpiForWindow. DPI возвращается в виде значения с плавающей запятой. Вычислите коэффициент масштабирования для обоих осей.
float g_DPIScale = 1.0f;
void InitializeDPIScale(HWND hwnd)
{
float dpi = GetDpiForWindow(hwnd);
g_DPIScale = dpi / USER_DEFAULT_SCREEN_DPI;
}
template <typename T>
float PixelsToDipsX(T x)
{
return static_cast<float>(x) / g_DPIScale;
}
template <typename T>
float PixelsToDipsY(T y)
{
return static_cast<float>(y) / g_DPIScale;
}
Ниже приведен альтернативный способ получения параметра DPI, если вы не используете Direct2D:
void InitializeDPIScale(HWND hwnd)
{
HDC hdc = GetDC(hwnd);
g_DPIScaleX = (float)GetDeviceCaps(hdc, LOGPIXELSX) / USER_DEFAULT_SCREEN_DPI;
g_DPIScaleY = (float)GetDeviceCaps(hdc, LOGPIXELSY) / USER_DEFAULT_SCREEN_DPI;
ReleaseDC(hwnd, hdc);
}
Заметка
Мы рекомендуем использовать классическое приложение GetDpiForWindow; и для приложения универсальной платформы Windows (UWP) используйте DisplayInformation::LogicalDpi. Хотя мы не рекомендуем это сделать, можно установить осведомленность по умолчанию по умолчанию с помощью SetProcessDpiAwarenessContext. После создания окна (HWND) в процессе изменение режима осведомленности о DPI больше не поддерживается. Если вы задаете режим осведомленности по умолчанию, необходимо вызвать соответствующий API перед созданием HWND. Дополнительные сведения см. в разделе Настройка осведомленности о DPI по умолчанию для процесса.
Изменение размера целевого объекта отрисовки
Если размер окна изменяется, необходимо изменить размер целевого объекта отрисовки для сопоставления. В большинстве случаев вам также потребуется обновить макет и перезапаковать окно. В следующем коде показаны эти действия.
void MainWindow::Resize()
{
if (pRenderTarget != NULL)
{
RECT rc;
GetClientRect(m_hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right, rc.bottom);
pRenderTarget->Resize(size);
CalculateLayout();
InvalidateRect(m_hwnd, NULL, FALSE);
}
}
Функция GetClientRect получает новый размер клиентской области в физических пикселях (а не в dips). Метод ID2D1HwndRenderTarget::Resize обновляет размер целевого объекта отрисовки, который также указан в пикселях. Функция InvalidateRect принудительно перенаправляться путем добавления всей клиентской области в область обновления окна. (См. рисование окнав модуле 1.)
По мере роста или сжатия окна обычно необходимо пересчитывать положение рисуемых объектов. Например, в программе круга необходимо обновить радиус и центральную точку:
void MainWindow::CalculateLayout()
{
if (pRenderTarget != NULL)
{
D2D1_SIZE_F size = pRenderTarget->GetSize();
const float x = size.width / 2;
const float y = size.height / 2;
const float radius = min(x, y);
ellipse = D2D1::Ellipse(D2D1::Point2F(x, y), radius, radius);
}
}
Метод ID2D1RenderTarget::GetSize возвращает размер целевого объекта отрисовки в dips (не пикселях), который является подходящим единицей для вычисления макета. Существует тесно связанный метод, ID2D1RenderTarget::GetPixelSize, который возвращает размер в физических пикселях. Для целевого объекта отрисовки HWND это значение соответствует размеру, возвращаемого GetClientRect. Но помните, что рисование выполняется в dips, а не в пикселях.