DPI a pixely nezávislé na zařízení
Chcete-li efektivně programovat pomocí grafiky systému Windows, musíte porozumět dvěma souvisejícím konceptům:
- Tečky na palec (DPI)
- Pixely nezávislé na zařízení (DIPS).
Začněme dpi. To bude vyžadovat krátkou obchůdku do typografie. V typografii se velikost typu měří v jednotkách nazývaných body. Jeden bod se rovná 1/72 palce.
- 1 bod = 1/72 palce
Poznámka
Toto je definice publikování na ploše bodu. Historicky se přesná míra bodu lišila.
Například 12bodové písmo je navržené tak, aby se vešlo do řádku textu 12,6" (12/72). Samozřejmě to neznamená, že každý znak písma je přesně 1/6" vysoký. Některé znaky můžou být ve skutečnosti vyšší než 1/6". Například v mnoha písmech je znak Å vyšší než nominální výška písma. Aby se písmo zobrazilo správně, potřebuje mezi textem další mezeru. Tato mezera se nazývá úvodní.
Následující obrázek znázorňuje 72bodové písmo. Plné čáry zobrazují kolem textu 1" ohraničující rámeček. Přerušovaná čára se nazývá směrný. Většina znaků v písmu leží na účaří. Výška písma zahrnuje část nad účařím (ascentní) a část pod směrným plánem (sestup). Ve zde zobrazeném písmu je vzestup 56 bodů a sestup je 16 bodů.
Pokud ale jde o zobrazení počítače, měření velikosti textu je problematické, protože pixely nejsou všechny stejné velikosti. Velikost pixelu závisí na dvou faktorech: rozlišení displeje a fyzické velikosti monitoru. Fyzické palce proto nejsou užitečnou mírou, protože neexistuje pevný vztah mezi fyzickými palci a pixely. Místo toho se písma měří v logických jednotkách. Písmo s 72 body je definováno jako jedno logické písmo na výšku. Logické palce se pak převedou na pixely. Po mnoho let systém Windows použil následující převod: Jeden logický paleč se rovná 96 pixelů. Pomocí tohoto faktoru měřítka se 72bodové písmo vykreslí jako 96 pixelů na výšku. Písmo o velikosti 12 bodů je 16 pixelů vysoké.
- 12 bodů = 12/72 logický paleč = 1/6 logický paleč = 96/6 pixelů = 16 pixelů
Tento faktor škálování je popsán jako 96 bodů na palec (DPI). Termín tečky pochází z tisku, kde jsou na papír vloženy fyzické tečky. U počítačových displejů by bylo přesnější říct 96 pixelů na logický palec, ale termín DPI se zasekl.
Vzhledem k tomu, že se skutečné velikosti pixelů liší, může být text, který je čitelný na jednom monitoru, příliš malý na jiném monitoru. Lidé mají také jiné předvolby – někteří lidé dávají přednost většímu textu. Z tohoto důvodu systém Windows umožňuje uživateli změnit nastavení DPI. Pokud například uživatel nastaví zobrazení na 144 DPI, bude písmo 72 bodů vysoké o velikosti 144 pixelů. Standardní nastavení DPI je 100% (96 DPI), 125% (120 DPI) a 150% (144 DPI). Uživatel může také použít vlastní nastavení. Počínaje Windows 7 je DPI nastavením pro jednotlivé uživatele.
Škálování DWM
Pokud program nebere v úvahu DPI, můžou se při nastavení s vysokým rozlišením DPI projevit následující vady:
- Oříznuté prvky uživatelského rozhraní
- Nesprávné rozložení
- Pixelované rastrové obrázky a ikony
- Nesprávné souřadnice myši, které můžou ovlivnit testování, přetažení atd.
Aby starší programy fungovaly v nastavení s vysokým rozlišením DPI, implementuje DWM užitečnou záložní verzi. Pokud program není označený jako dpi, dwm škáluje celé uživatelské rozhraní tak, aby odpovídalo nastavení DPI. Například při 144 DPI se uživatelské rozhraní škáluje o 150%, včetně textu, grafiky, ovládacích prvků a velikostí oken. Pokud program vytvoří okno 500 × 500, okno se ve skutečnosti zobrazí jako 750 × 750 pixelů a obsah okna se odpovídajícím způsobem škáluje.
Toto chování znamená, že starší programy "fungují jen" v nastavení s vysokým rozlišením DPI. Měřítko má ale také poněkud rozmazaný vzhled, protože měřítko se použije po vykreslení okna.
Aplikace pracující s DPI
Aby se zabránilo škálování DWM, může se program označit jako pracující s DPI. To říká DWM, aby neprováděl automatické škálování DPI. Všechny nové aplikace by měly být navrženy tak, aby byly podporující DPI, protože povědomí o DPI zlepšuje vzhled uživatelského rozhraní při vyšším nastavení DPI.
Program deklaruje vlastní DPI prostřednictvím manifestu aplikace. Manifest je jednoduše soubor XML, který popisuje knihovnu DLL nebo aplikaci. Manifest je obvykle vložen do spustitelného souboru, i když ho lze zadat jako samostatný soubor. Manifest obsahuje informace, jako jsou závislosti knihovny DLL, požadovaná úroveň oprávnění a verze systému Windows, pro kterou byl program navržen.
Chcete-li deklarovat, že váš program je pracující s DPI, zahrňte do manifestu následující informace.
<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>
Uvedený výpis je jenom částečný manifest, ale linker sady Visual Studio automaticky vygeneruje zbytek manifestu. Pokud chcete do projektu zahrnout částečný manifest, proveďte v sadě Visual Studio následující kroky.
- V nabídce Project klepněte na Vlastnost.
- V levém podokně rozbalte Vlastnosti konfigurace, rozbalte Nástroj manifestua klepněte na tlačítko Vstup a výstup.
- Do textového pole Další soubory manifestu zadejte název souboru manifestu a klepněte na tlačítko OK.
Když program označíte jako pracující s DPI, říkáte dwm, že nechcete škálovat okno aplikace. Když teď vytvoříte okno 500 × 500, bude okno zabírat 500 × 500 pixelů bez ohledu na nastavení DPI uživatele.
GDI a DPI
Výkres GDI se měří v pixelech. To znamená, že pokud je váš program označený jako DPI a požádáte GDI, aby nakreslil obdélník 200 × 100, výsledný obdélník bude široký 200 pixelů a 100 pixelů na obrazovce. Velikosti písem GDI se ale škálují na aktuální nastavení DPI. Jinými slovy, pokud vytvoříte 72bodové písmo, velikost písma bude 96 pixelů při 96 DPI, ale 144 pixelů při 144 DPI. Tady je 72bodové písmo vykreslené při rozlišení 144 DPI pomocí GDI.
Pokud je vaše aplikace s rozlišením DPI a pro kreslení používáte GDI, škálujte všechny souřadnice výkresu tak, aby odpovídaly dpi.
Direct2D a DPI
Direct2D automaticky provádí škálování tak, aby odpovídalo nastavení DPI. V Direct2D se souřadnice měří v jednotkách nazývaných pixely nezávislé na zařízení (DIPS). DIP je definován jako 1/96. z logického palce. V režimu Direct2D jsou všechny operace kreslení zadány v sadě DIP a pak se škálují na aktuální nastavení DPI.
Nastavení DPI | Velikost dynamické IP adresy |
---|---|
96 | 1 pixel |
120 | 1,25 pixelů |
144 | 1,5 pixelů |
Pokud je například nastavení DPI uživatele 144 DPI a požádáte Direct2D o nakreslení obdélníku 200 × 100, bude obdélník 300 × 150 fyzických pixelů. Kromě toho DirectWrite měří velikosti písem v rozpisech dat, nikoli v bodech. Chcete-li vytvořit 12bodové písmo, zadejte 16 BODŮ (12 bodů = 1/6 logických bodů = 96/6 BODŮ). Při vykreslení textu na obrazovce direct2D převede dips na fyzické pixely. Výhodou tohoto systému je, že měrné jednotky jsou konzistentní pro text i kreslení bez ohledu na aktuální nastavení DPI.
Upozornění: Souřadnice myši a okna jsou stále uvedeny ve fyzických pixelech, nikoli v dips. Pokud například zpracujete WM_LBUTTONDOWN zprávu, zobrazí se umístění myši směrem dolů ve fyzických pixelech. Pokud chcete nakreslit bod na této pozici, musíte převést souřadnice pixelů na dips.
Převod fyzických pixelů na ROZDÍLY
Základní hodnota DPI je definována jako USER_DEFAULT_SCREEN_DPI
, která je nastavená na 96. Pokud chcete zjistit faktor měřítka, převezměte hodnotu DPI a vydělte ji USER_DEFAULT_SCREEN_DPI
.
Převod fyzických pixelů na dips používá následující vzorec.
DIPs = pixels / (DPI / USER_DEFAULT_SCREEN_DPI)
Pokud chcete získat nastavení DPI, zavolejte funkci GetDpiForWindow. Dpi se vrátí jako hodnota s plovoucí desetinou čárkou. Výpočet faktoru měřítka pro obě osy
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;
}
Toto je alternativní způsob, jak získat nastavení DPI, pokud nepoužíváte 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);
}
Poznámka
Doporučujeme, abyste pro desktopovou aplikaci používali GetDpiForWindow; a pro aplikaci univerzální platformy Windows (UPW) použijte DisplayInformation::LogicalDpi. I když to nedoporučujeme, je možné nastavit výchozí rozpoznávání DPI programově pomocí SetProcessDpiAwarenessContext. Po vytvoření okna (HWND) v procesu se už nepodporuje změna režimu rozpoznávání DPI. Pokud programově nastavujete režim rozpoznávání DPI výchozího procesu, je nutné volat odpovídající rozhraní API před vytvořením jakýchkoli disků HWND. Další informace najdete v tématu Nastavení výchozího rozpoznávání DPI pro proces.
Změna velikosti cíle vykreslení
Pokud se velikost okna změní, musíte změnit velikost cíle vykreslení tak, aby odpovídala. Ve většině případů budete také muset aktualizovat rozložení a překreslit okno. Následující kód ukazuje tyto kroky.
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);
}
}
Funkce GetClientRect získá novou velikost klientské oblasti ve fyzických pixelech (nikoli VSP). ID2D1HwndRenderTarget::Resize metoda aktualizuje velikost cíle vykreslení, která je také zadaná v pixelech. Funkce InvalidateRect vynutí překreslení tím, že do oblasti aktualizace okna přidá celou oblast klienta. (Viz Malování okna, v modulu 1.)
Při růstu nebo zmenšení okna budete obvykle muset přepočítat pozici objektů, které nakreslete. Například v kruhovém programu musí být aktualizován poloměr a středový bod:
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 vrátí velikost cíle vykreslení v dips (nikoli pixelech), což je vhodná jednotka pro výpočet rozložení. Existuje úzce související metoda, ID2D1RenderTarget::GetPixelSize, která vrátí velikost ve fyzických pixelech. Pro HWND cíl vykreslení odpovídá této hodnotě velikost vrácená GetClientRect. Mějte ale na paměti, že výkres se provádí v dips, nikoli pixelech.
Další
Použití barvy v Direct2D