Проблемы разрешения экрана и масштабирования в WinRT

137

Большинство приложений Windows 8 строится на основе экземпляров класса Page. Конечно, это не является обязательным требованием, но предоставляет некоторые удобства, например упрощенную интеграцию строк приложений. До этой статьи рассматривались только программы, имеющие единственный экземпляр класса, производного от Page, с именем MainPage. Пора заняться программами, поддерживающими многостраничную навигацию в стиле веб-приложений.

В Visual Studio входят два шаблона проектов многостраничных приложений - Grid App и Split App. Эти шаблоны строятся на базе мощных элементов управления ListView и GridView и используют их в моделях представлений. Эти шаблоны также реагируют на изменения ориентации экрана и режима Snap View, так что эта и последующие статьи станут хорошей отправной точкой для изучения проблем, связанных с изменением размеров окна.

Обработка изменения размеров окна - задача, хорошо знакомая Windows-программистам. В большинстве традиционных настольных Windows-программ имеется рамка изменения размеров, при помощи которой пользователь может управлять размером и пропорциями окна приложения. Windows-программистов 25 лет учили писать программы, адаптирующиеся к размеру окна, выбранному пользователем. Конечно, это возможно не всегда: что должна делать электронная таблица, если пользователь уменьшит окно так, что не останется ни одной видимой ячейки? Некоторые программы - например, Калькулятор - просто устанавливают фиксированный размер окна, достаточный для отображения всего содержимого программы. Для традиционных настольных приложений такое решение приемлемо только в том случае, если окно заведомо меньше экрана.

Приложения Windows 8 в основном работают в полноэкранном режиме, и проблема минимального размера экрана для них менее актуальна. Однако приложения Windows 8 также подвержены изменениям ориентации экрана и включению Snap View, и многие приложения должны обрабатывать такие изменения.

Проблемы разрешения экрана

Экран компьютера имеет фиксированный горизонтальный и вертикальный размер в пикселах, а также физический размер, который обычно задается как величина диагонали в дюймах. Зная эти размеры, по теореме Пифагора можно вычислить разрешение в пикселах на дюйм (эта единица также обозначается сокращением DPI - Dots Per Inch).

Например, диагональ экрана с размерами 1024 x 768 пикселов равна 1280 пикселам. Если физический размер диагонали составляет 12 дюймов, то разрешение составляет 106 DPI. Диагональ 23-дюймового настольного монитора со стандартным режимом высокого разрешения 1920 x 1080 пикселов содержит около 2203 пикселов с разрешением 96 DPI. У 27-дюймового монитора с экраном 2560 x 1440 пикселов разрешение около 109 DPI.

Ранее я говорил, что разрешение экрана можно считать равным 96 пикселам на дюйм. Как видите, эта оценка неплохо подходит для трех мониторов из приведенного примера, хотя вам могут встретиться мониторы, для которых это правило заметно искажено: при работе я использовал планшет Samsung с размером экрана 1366 x 768 и диагональю 11,6 дюйма с разрешением 135 DPI. Если нарисовать на этом экране квадрат со стороной 96 пикселов, то вместо дюйма длина стороны составит около 0,7 дюйма.

Предположение о разрешении 96 DPI чаще всего нарушается для малых экранов с большим количеством пикселов. Для примера возьмем экран с диагональю 10,6 дюйма и размером 1920 x 1080 пикселов. Разрешение такого экрана составляет 208 DPI; соответственно дюймовый (по мнению программиста) объект будет занимать на нем менее половины дюйма. Текст уменьшается, и хотя его все еще можно прочитать из-за высокой плотности пикселов, выполнять с ним операции на сенсорном экране уже неудобно.

По этой причине Windows 8 пытается компенсировать высокое разрешение экрана способом, относительно прозрачным для приложений: если размер экрана в пикселах составляет 2560 x 1440 и выше, а физический размер, допустим, равен 12 дюймам (что дает разрешение 240 DPI и выше), Windows изменяет координаты в пикселах и размеры, используемые в приложении, на 180 %. С точки зрения приложения экран 2560 x 1440 имеет размер 1422 x 800 пикселов.

Если экран не обладает такой высокой плотностью пикселов, но обладает размером не менее 1920 x 1080, а физический размер достаточно мал для обеспечения разрешения 174 DPI и выше, Windows 8 изменяет все размеры в пикселах на 140 %; таким образом, экран 1920 x 1080 с точки зрения приложения имеет размер 1371 x 771 пикселов.

Помните, что все эти автоматические корректировки применяются только для физически малых экранов с большим количеством пикселов. Физически большой экран с фактическим разрешением ниже 174 DPI изменяться не будет, а следовательно, приложение будет «видеть» его полный размер.

В Windows Runtime предполагаемое разрешение экрана называется логическим разрешением (logical DPI). Обычно логическое разрешение равно 96, но для экранов с большой плотностью пикселов оно может составлять 134,4 (96 DPI, умноженное на 140 %) или 172,8 (96 DPI, умноженное на 180 %).

Давайте посмотрим, как работает эта система. Программа WhatRes похожа на программу WhatSize, представленную в статье События изменения размера и ориентации в WinRT, но помимо размера окна (который совпадает с размером страницы) она получает информацию о разрешении экрана.

Файл XAML в проекте WhatRes просто создает экземпляр TextBlock:

<Grid Background="#FF1D1D1D">
        <TextBlock Name="txb"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center"
                   FontSize="32" Foreground="LimeGreen" />
</Grid>

Файл фонового кода назначает обработчики для события SizeChanged страницы, а также для статического события LogicalDpiChanged класса DisplayProperties, определенного в пространстве имен Windows.Graphics.Display:

using System;
using Windows.Graphics.Display;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace WinRTTestApp
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();

            SizeChanged += Page_SizeChanged;
            DisplayProperties.LogicalDpiChanged += Logical_DpiChanged;

            Loaded += (s, e) =>
            {
                UpdateDisplay();
            };
        }

        private void Page_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            UpdateDisplay();
        }

        private void Logical_DpiChanged(object sender)
        {
            UpdateDisplay();
        }

        private void UpdateDisplay()
        {
            double dpi = DisplayProperties.LogicalDpi;
            int realWidth = (int)Math.Round(dpi * ActualWidth / 96);
            int realHeight = (int)Math.Round(dpi * ActualHeight / 96);

            txb.Text =
                String.Format("Размер окна = {0} x {1}\r\n" +
                              "Масштаб разрешения = {2}\r\n" +
                              "DPI = {3}\r\n" +
                              "Фактический размер = {4} x {5}",
                              ActualWidth, ActualHeight,
                              DisplayProperties.ResolutionScale,
                              DisplayProperties.LogicalDpi,
                              realWidth, realHeight);
        }
    }
}

В реальном приложении событие DisplayProperties.LogicalDpiChanged будет инициироваться довольно редко, потому что размер экрана в пикселах или его физический размер обычно не изменяются во время выполнения программы. Впрочем, событие может быть инициировано при подключении к компьютеру с Windows 8 второго монитора, имеющего другое логическое разрешение, и перемещении программы с одного монитора на другой.

Программа WhatRes сначала получает размер окна из свойств ActualWidth и ActualHeight страницы, а затем вычисляет фактический размер в пикселах по данным DisplayProperties.LogicalDpi.

Программа WhatRes хорошо подходит для запуска в эмуляторе Windows 8, который можно выбрать на стандартной панели инструментов Visual Studio. Эмулятор позволяет запускать приложение с некоторыми распространенными размерами экранов. Например вот как выглядит WhatRes на эмулируемом экране 1920 х 1080 с диагональю 10,6 дюйма:

Вычисление DPI и размеров окна просмотра

Для приложения Windows 8 окно имеет размер 1371 х 771, и весь отображаемый в нем текст и графика базируются на этом размере. Вычисленный размер в пикселах совпадает с размером экрана в пикселах. Как видите, шрифт с размером 18 пунктов занимает ту же относительную площадь, как и на экране 1366 х 768.

Проблемы масштабирования

Windows-программисты привыкли работать с координатами и размерами в пикселах. Когда программа выполняется на физически малом экране с высокой плотностью пикселов, Windows масштабирует эти координаты и размеры в зависимости от размера и разрешения экрана.

Итак, правильнее будет сказать, что графический вывод или задание размеров осуществляются не в пикселах, а в аппаратно-независимых единицах (DIU, Device-Independent Units), или просто в единицах (units). Некоторые авторы называют эти единицы «аппаратно-независимыми пикселами», но этот термин слишком сильно отдает оксюмороном.

В первом столбце следующей таблицы приведены некоторые значения в DIU, используемые при рисовании и определении размеров, а в других столбцах показано, как эти значения преобразуются в непосредственные пикселы на экране:

Масштаб разрешения, %
DIU 100 140 180
5 5 7 9
10 10 14 18
15 15 21 27
20 20 28 36

Из таблицы видно, что если вы будете придерживаться размеров и координат, кратных 5 единицам, эти значения преобразуются в целое число пикселов. Целочисленное преобразование иногда помогает сохранить точность графики.

Когда Windows вносит эти изменения, текст и векторная графика масштабируются без потери разрешения. Например, если задать свойству FontSize значение 20, а ваша программа выполняется на экране с масштабом разрешения 180%, вы не получите шрифт с размером 20 пикселов, увеличенный на 180% с соответствующей потерей четкости и неровностями. Вместо этого используется сглаженный, полноценный шрифт с размером 36 пикселов.

С растровыми изображениями дело обстоит иначе. Растровое изображение имеет конкретный размер в пикселах, а при выводе 200-пиксельного квадратного изображения с фактическим размером у Windows не остается другого выбора, кроме как масштабировать изображение на 140 или 180% с потерей четкости. Чтобы этого не произошло, вы можете создать для своего приложения растровые изображения трех разных размеров (например, 200, 280 и 360 пикселов). Их можно даже сохранить в ресурсах программы, чтобы система Windows автоматически выбрала правильную версию!

Пройди тесты
Лучший чат для C# программистов