Dpi i piksele niezależne od urządzenia
Aby efektywnie programować za pomocą grafiki systemu Windows, należy zrozumieć dwie powiązane pojęcia:
- Kropki na cal (DPI)
- Piksel niezależny od urządzenia (DIPs).
Zacznijmy od dpi. Będzie to wymagało krótkiego objazdu do typografii. W typografii rozmiar typu jest mierzony w jednostkach o nazwie punktów. Jeden punkt jest równy 1/72 cala.
- 1 pkt = 1/72 cala
Nuta
Jest to definicja punktu publikowania pulpitu. Historycznie dokładna miara punktu różniła się.
Na przykład czcionka 12-punktowa została zaprojektowana tak, aby mieściła się w wierszu tekstu 1/6" (12/72). Oczywiście nie oznacza to, że każdy znak czcionki jest dokładnie 1/6" wysoki. W rzeczywistości niektóre znaki mogą być wyższe niż 1/6". Na przykład w wielu czcionkach znak Å jest wyższy niż nominalna wysokość czcionki. Aby wyświetlić poprawnie, czcionka wymaga dodatkowego odstępu między tekstem. Ta przestrzeń jest nazywana wiodącym .
Poniższa ilustracja przedstawia czcionkę 72-punktową. Linie stałe pokazują 1" wysokie pole ograniczenia wokół tekstu. Linia przerywana jest nazywana punktem odniesienia . Większość znaków w czcionki spoczywa na linii bazowej. Wysokość czcionki zawiera część powyżej punktu odniesienia (wznoszenia) i część poniżej punktu odniesienia (spadek ). W czcionki pokazanej tutaj wzrost wynosi 56 punktów, a spadek wynosi 16 punktów.
Jeśli chodzi o ekran komputera, jednak pomiar rozmiaru tekstu jest problematyczny, ponieważ piksele nie są takie same. Rozmiar piksela zależy od dwóch czynników: rozdzielczości ekranu i fizycznego rozmiaru monitora. W związku z tym cale fizyczne nie są przydatną miarą, ponieważ nie ma stałej relacji między calami fizycznymi i pikselami. Zamiast tego czcionki są mierzone w jednostkach logicznych. Czcionka 72-punktowa jest definiowana jako jedna logiczna wysokość cala. Cale logiczne są następnie konwertowane na piksele. Przez wiele lat system Windows używał następującej konwersji: jeden logiczny cal jest równy 96 pikseli. Przy użyciu tego współczynnika skalowania czcionka 72-punktowa jest renderowana jako 96 pikseli wysokości. Czcionka 12-punktowa ma 16 pikseli wysokości.
- 12 punktów = 12/72 cal logiczny = 1/6 cala logicznego = 96/6 pikseli = 16 pikseli
Ten współczynnik skalowania jest opisany jako 96 kropek na cal (DPI). Termin kropki pochodzą z drukowania, gdzie fizyczne kropki atramentu są umieszczane na papierze. W przypadku wyświetlaczy komputerów byłoby bardziej dokładne, aby powiedzieć 96 pikseli na cal logiczny, ale termin DPI utknął.
Ponieważ rzeczywiste rozmiary pikseli różnią się, tekst, który można odczytać na jednym monitorze, może być zbyt mały na innym monitorze. Ponadto ludzie mają różne preferencje — niektórzy wolą większy tekst. Z tego powodu system Windows umożliwia użytkownikowi zmianę ustawienia DPI. Jeśli na przykład użytkownik ustawia ekran na 144 DPI, czcionka 72-punktowa ma wysokość 144 pikseli. Standardowe ustawienia DPI to 100% (96 DPI), 125% (120 DPI) i 150% (144 DPI). Użytkownik może również zastosować ustawienie niestandardowe. Począwszy od systemu Windows 7, dpi jest ustawieniem dla poszczególnych użytkowników.
Skalowanie usługi DWM
Jeśli program nie uwzględnia dpi, następujące wady mogą być widoczne w ustawieniach wysokiej rozdzielczości DPI:
- Obcięte elementy interfejsu użytkownika.
- Nieprawidłowy układ.
- Pikselowe mapy bitowe i ikony.
- Nieprawidłowe współrzędne myszy, które mogą mieć wpływ na testowanie trafień, przeciąganie i upuszczanie itd.
Aby zapewnić, że starsze programy działają w ustawieniach o wysokiej rozdzielczości DPI, usługa DWM implementuje przydatny rezerwowy. Jeśli program nie jest oznaczony jako obsługujący dpi, usługa DWM będzie skalować cały interfejs użytkownika w celu dopasowania do ustawienia DPI. Na przykład przy rozdzielczości 144 DPI interfejs użytkownika jest skalowany przez 150%, w tym tekst, grafikę, kontrolki i rozmiary okien. Jeśli program utworzy okno 500 × 500, okno faktycznie będzie wyświetlane jako 750 × 750 pikseli, a zawartość okna jest odpowiednio skalowana.
To zachowanie oznacza, że starsze programy "po prostu działają" w ustawieniach wysokiej rozdzielczości DPI. Jednak skalowanie powoduje również nieco rozmyty wygląd, ponieważ skalowanie jest stosowane po narysowaniu okna.
Aplikacje obsługujące dpi
Aby uniknąć skalowania w usłudze DWM, program może oznaczyć się jako obsługujący dpi. Informuje to, że usługa DWM nie wykonuje żadnego automatycznego skalowania DPI. Wszystkie nowe aplikacje powinny być zaprojektowane tak, aby uwzględniały dpi, ponieważ rozpoznawanie dpi zwiększa wygląd interfejsu użytkownika przy wyższych ustawieniach DPI.
Program deklaruje się z obsługą dpi za pośrednictwem manifestu aplikacji. manifestu to po prostu plik XML opisujący bibliotekę DLL lub aplikację. Manifest jest zwykle osadzony w pliku wykonywalnym, chociaż można go podać jako oddzielny plik. Manifest zawiera informacje, takie jak zależności bibliotek DLL, żądany poziom uprawnień i wersja systemu Windows, dla której program został zaprojektowany.
Aby zadeklarować, że program uwzględnia dpi, dołącz następujące informacje w manifeście.
<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>
Lista wyświetlana w tym miejscu jest tylko częściowym manifestem, ale konsolidator programu Visual Studio automatycznie generuje resztę manifestu. Aby uwzględnić częściowy manifest w projekcie, wykonaj następujące kroki w programie Visual Studio.
- W menu project kliknij pozycję Property.
- W okienku po lewej stronie rozwiń węzeł właściwości konfiguracji , rozwiń narzędzie manifestu, a następnie kliknij pozycję Dane wejściowe i wyjściowe.
- W polu tekstowym Dodatkowe pliki manifestu wpisz nazwę pliku manifestu, a następnie kliknij przycisk OK.
Oznaczając program jako obsługujący dpi, informujesz dwM, aby nie skalować okna aplikacji. Teraz, jeśli utworzysz okno 500 × 500, okno zajmie 500 × 500 pikseli, niezależnie od ustawienia DPI użytkownika.
GDI i DPI
Rysunek GDI jest mierzony w pikselach. Oznacza to, że jeśli program jest oznaczony jako obsługujący dpi, i poprosisz GDI o narysowanie 200 × 100 prostokątów, wynikowy prostokąt będzie mieć 200 pikseli szerokości i 100 pikseli wysokości na ekranie. Jednak rozmiary czcionek GDI są skalowane do bieżącego ustawienia DPI. Innymi słowy, jeśli utworzysz czcionkę 72-punktową, rozmiar czcionki będzie wynosić 96 pikseli przy rozdzielczości 96 DPI, ale 144 pikseli przy rozdzielczości 144 DPI. Oto czcionka 72-punktowa renderowana przy rozdzielczości 144 DPI przy użyciu interfejsu GDI.
Jeśli aplikacja uwzględnia dpi i używasz interfejsu GDI do rysowania, przeprowadź skalowanie wszystkich współrzędnych rysunku w celu dopasowania do dpi.
Direct2D i DPI
Funkcja Direct2D automatycznie wykonuje skalowanie w celu dopasowania do ustawienia DPI. W trybie Direct2D współrzędne są mierzone w jednostkach o nazwie pikselach niezależnych od urządzenia (DIPs). DIP jest definiowany jako 1/96 część logicznej cala. W trybie Direct2D wszystkie operacje rysowania są określane w dips, a następnie skalowane do bieżącego ustawienia DPI.
Ustawienie DPI | Rozmiar dipu |
---|---|
96 | 1 piksel |
120 | 1,25 pikseli |
144 | 1,5 pikseli |
Jeśli na przykład ustawienie DPI użytkownika wynosi 144 DPI, a monit Direct2D o narysowanie prostokąta 200 × 100, prostokąt będzie wynosić 300 × 150 pikseli fizycznych. Ponadto funkcja DirectWrite mierzy rozmiary czcionek w dips, a nie punkty. Aby utworzyć czcionkę 12-punktową, określ 16 dips (12 punktów = 1/6 cala logicznego = 96/6 DIPs). Gdy tekst zostanie narysowany na ekranie, funkcja Direct2D konwertuje adresy IP na piksele fizyczne. Zaletą tego systemu jest to, że jednostki miary są spójne zarówno dla tekstu, jak i rysunku, niezależnie od bieżącego ustawienia DPI.
Słowo ostrożności: Współrzędne myszy i okna są nadal podane w pikselach fizycznych, a nie DIPs. Jeśli na przykład przetworzysz komunikat WM_LBUTTONDOWN, pozycja myszy w dół zostanie podana w pikselach fizycznych. Aby narysować punkt w tej pozycji, należy przekonwertować współrzędne pikseli na adresy IP.
Konwertowanie pikseli fizycznych na dips
Wartość podstawowa DPI jest definiowana jako USER_DEFAULT_SCREEN_DPI
ustawiona na 96. Aby określić współczynnik skalowania, należy wziąć wartość DPI i podzielić przez USER_DEFAULT_SCREEN_DPI
.
Konwersja z pikseli fizycznych na dips używa następującej formuły.
DIPs = pixels / (DPI / USER_DEFAULT_SCREEN_DPI)
Aby uzyskać ustawienie DPI, wywołaj funkcję GetDpiForWindow. DPI jest zwracana jako wartość zmiennoprzecinkowa. Oblicz współczynnik skalowania dla obu osi.
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;
}
Oto alternatywny sposób uzyskiwania ustawienia DPI, jeśli nie używasz 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);
}
Nuta
Zalecamy, aby w przypadku aplikacji klasycznej używać GetDpiForWindow; w przypadku aplikacji platformy uniwersalnej systemu Windows (UWP) użyj DisplayInformation::LogicalDpi. Mimo że nie jest to zalecane, można programowo ustawić domyślną świadomość DPI przy użyciu SetProcessDpiAwarenessContext. Po utworzeniu okna (HWND) w procesie zmiana trybu rozpoznawania dpi nie jest już obsługiwana. Jeśli programowo ustawiasz domyślny tryb rozpoznawania DPI procesu, musisz wywołać odpowiedni interfejs API przed utworzeniem jakichkolwiek identyfikatorów HWND. Aby uzyskać więcej informacji, zobacz Ustawienie domyślnej świadomości DPI dla procesu.
Zmiana rozmiaru obiektu docelowego renderowania
Jeśli rozmiar okna zmieni się, musisz zmienić rozmiar elementu docelowego renderowania, aby był zgodny. W większości przypadków konieczne będzie również zaktualizowanie układu i przemalowania okna. Poniższy kod przedstawia te kroki.
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);
}
}
Funkcja GetClientRect pobiera nowy rozmiar obszaru klienta w pikselach fizycznych (a nie dips). ID2D1HwndRenderTarget::Resize metoda aktualizuje rozmiar obiektu docelowego renderowania, również określony w pikselach. Funkcja InvalidateRect wymusza przemalowanie przez dodanie całego obszaru klienta do regionu aktualizacji okna. (Zobacz Malowanie okna, w module 1).
W miarę wzrostu lub zmniejszania okna zazwyczaj trzeba będzie ponownie obliczyć położenie rysujących obiektów. Na przykład w programie circle należy zaktualizować promień i punkt środkowy:
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);
}
}
Metoda ID2D1RenderTarget::GetSize zwraca rozmiar obiektu docelowego renderowania w dips (a nie pikselach), która jest odpowiednią jednostką do obliczania układu. Istnieje ściśle powiązana metoda ID2D1RenderTarget::GetPixelSize, która zwraca rozmiar w pikselach fizycznych. W przypadku obiektu docelowego renderowania HWND ta wartość jest zgodna z rozmiarem zwróconym przez GetClientRect. Pamiętaj jednak, że rysunek jest wykonywany w dips, a nie pikselach.
Następny
Używanie koloru w Direct2D