Od tématu magazínu k projektové praxi
Vhodné stránky služeb a technické stránky k příspěvku
Kdo provozuje VCL aplikace na moderních Windows klientech, dříve nebo později narazí na stejný symptom: ikony při 125%/150%/200% vypadají rozmazaně, roztřepeně nebo mají šedý okraj. Právě zde se prakticky ukazuje téma VCL High-DPI Icons: ne proto, že by High‑DPI bylo nové, ale protože se problémy většinou projeví až v provozu — na terminálových serverech, při přechodu notebooku do dokovací stanice nebo jakmile jsou v provozu monitory s různým DPI.
Hlavním problémem téměř nikdy není „PNG je poškozené“, ale pipeline: odkud ikona pochází (resource, soubor, SVG, font), v jaké velikosti je dodána, jak se dostane do TImageList a kdo kdy jak škáluje. Ve VCL se setkává několik pojmů, které je třeba rozlišit: DPI-Awareness (zda Windows škáluje aplikaci nebo se aplikace škáluje sama), Per-Monitor-DPI (každý monitor může mít jiné DPI) a ImageList-Strategie (uchovávat více rozlišení nebo rasterizovat za běhu).
V tomto příspěvku nejde úmyslně o debaty o UI designu, ale o čistý, provozně spolehlivý přístup: škálovat ikony za běhu tak, aby se nepřeměnily v pixelovou kaši, aby alfa kanály zůstaly nedotčené a aby změny DPI fungovaly bez blikání nebo nesprávných rozměrů obrázků. Dále jsou popsány úskalí, tipy pro ladění a upřímné zhodnocení, kdy se zvýšené úsilí vyplatí.
Proč vzniká pixelová kaše: pochopení řetězce škálování ve VCL
Nejčastější příčinou neostrých ikon je jednorázové down-/upscaling ve špatný moment. Klasický průběh v legacy‑VCL:
- Aplikace dodává ikony pouze v 16×16 nebo 24×24.
- Windows nebo VCL škáluje na 20×20 / 32×32 / 48×48.
- Škálovač používá interpolaci, která je pro fotografie v pořádku, ale u pixelové grafiky rozmazává hrany.
- Navíc je průhlednost (alfa) vtlačena do logiky masky nebo opakovaně konvertována.
Obzvlášť nepříjemné je, když proběhne několik škálování za sebou: například když ImageList už dodá škálované bitmapy a Windows kvůli DPI‑unaware nebo systémovému DPI provede další škálování. Výsledek: dvojité rozostření.
Druhým, v projektech často podceňovaným bodem je časování škálování. Při Per‑Monitor‑DPI (PMv2, tedy Per‑Monitor‑DPI‑Awareness v2) se může efektivní DPI změnit, když okno přejde na jiný monitor nebo když Remote‑Desktop klient dynamicky upraví DPI. Pokud se pak TImageList nebo cache nepřebuduje správně, náhle uvidíte ikony v nesprávné velikosti nebo se špatnou rastrovou mřížkou.
TImageList při High‑DPI: typické pasti v reálných aplikacích
TImageList byla historicky určena pro malé bitmapy, s pevnými rozměry, indexy a relativně striktní logikou ukládání. Pod High-DPI z toho plynou praktické pasti:
1) Pevně nastavené Width/Height
Mnoho VCL formulářů nastaví ImageList.Width/Height v době návrhu a už s tím nic nedělá. Při 150 % však Windows například očekává, že se z 16 raději udělá 24. Pokud seznam zůstane na 16, bude obraz buď ořezán, nebo jinde škálován — obojí nevzhledné.
2) PNG-Alpha a logika masek
V závislosti na Delphi-verzi a použitých VCL komponentech se snadno dostanete do smíšeného režimu: PNG s alfakanálem se interně někdy uchovávají jako 32bitové bitmapy, jindy jako maska + barva. Jakmile provádíte vícenásobné konverze (PNG -> Bitmap -> ImageList -> Draw), vznikají šedé halo efekty nebo ostré hrany. Efekt je často závislý na pozadí: na tmavém panelu nástrojů vypadá problém hůř než na světlém panelu.
3) Změny DPI za běhu: cache, handles, OwnerDraw
Některé komponenty kešují vykreslení obrázků nebo přebírají ImageList-handles v okamžiku, kdy DPI ještě není definitivní. Zejména u toolbars, TreeViews/ListViews a OwnerDraw scénářů se po změnách DPI občas objeví nesprávné velikosti obrázků nebo prázdné ikony, dokud nenastane repaint nebo recreateWnd.
4) Terminalserver a Remote-Desktop jako reálný test
Pokud se aplikace používá přes RDP, změny DPI a obnovení session nejsou ničím výjimečným. Právě tam se neodolná ImageList-strategie rychle projeví: po reconnectu uživatel vidí rozmazané ikony nebo nesprávně škálované panely nástrojů, i když lokálně bylo vše v pořádku.
Čistý přístup: poskytnout více rozlišení místo brutálního upscalingu
Nejdůležitější rozhodnutí je koncepční: chceš ikony za běhu škálovat z jediné základní grafiky (např. z 16 na 32), nebo poskytnout několik nativních rozlišení a podle DPI vybrat to vhodné?
V praxi téměř vždy vyhraje varianta s více rozlišeními. Upscaling dává smysl, pokud je zdroj vektorový (SVG, icon-font) nebo pokud potřebuješ opravdu jen mírné škálovací faktory. Jakmile silně zvětšíš malý rastrový obrázek, ztratíš kvalitu hran a to je na moderních displejích okamžitě patrné.
Ve světě VCL jsou pro tento přístup dnes relevantní dva stavební kameny:
- TImageCollection: kontejner pro obrázky v několika velikostech/variantách.
- TVirtualImageList: z nich za běhu vytvoří ImageList v aktuálně požadované velikosti a reaguje na změny DPI.
To nevyřeší všechny problémy, ale přesune problém na správné místo: definujete zdroje obrázků čistě a škálování/volba probíhá konzistentně.
Škálování VCL High-DPI ikon za běhu: kdy má smysl (a kdy ne)
Existují legitimní důvody pro škálování ikon za běhu:
- Nahrávás ikony dynamicky (např. z adresáře pluginů, zákaznických balíčků brandingu nebo konfiguračních balíčků).
- Chceš jednotnou pipeline pro různé zdroje (ICO, PNG, SVG) a nechceš vázat všechny varianty při sestavení.
- Generuješ ikony programově (stavové odznaky, overlaye, složené symboly).
Není rozumné provádět škálování za běhu, pokud máš vlastně jen klasické toolbar-ikony z pevné sady. Tehdy je nejméně náročné na údržbu: doručit několik rozlišení a nechat VCL vybrat.
Pokud provádíš škálování za běhu, pak s jasnými pravidly:
- Nikdy z již škálovaného bitmapu znovu nepřeskalovávat. Vždy vycházet ze zdroje master (ideálně vektorového nebo vysoce rozlišeného).
- Cache pro cílovou velikost a DPI, jinak budeš při každém paintu znovu škálovat — to stojí CPU a může způsobit zadrhávání.
- Zachovat alfakanál: minimalizovat konverze, používat 32-bit RGBA, nez rasterizovat pozadí.
Pragmatische Architektur: Icon-Pipeline als eigener Baustein
V rozsáhlejších aplikacích se vyplatí téma nerozptýlit všude, ale vybudovat malou Pipeline. Nemusí to být framework — spíš jasně definovaná oblast odpovědnosti:
- Icon-Quelle: Odkud pocházejí die Master-Assets (Ressourcen, Dateien, Datenbank, API)?
- Rasterizer/Scaler: Jak se z masteru vytváří cílová velikost (interpolace, případně SVG-render)?
- Cache: Klíč (Icon-ID, cílové pixely, DPI, Theme) a životní cyklus (invalidace při změně DPI, změně Theme, Paketwechsel).
- Consumer-Adapter: Jak se výsledek dostane do VCL-Strukturen (TImageList/TVirtualImageList, OwnerDraw, PaintBox)?
Výhoda: DPI chyby můžeš reprodukovatelně debugovat na jednom místě, místo hledání ve 40 formulářích, kde se znovu škáluje.
DPI-Wechsel korrekt behandeln: Ereignisse, Rebuild, Repaint
Unter Windows sind DPI-Wechsel ein eigener Lebenszyklus. In der VCL gibt es je nach Version und DPI-Awareness mehrere Ereignisse/Mechanismen, aber das Grundprinzip bleibt:
- Wenn sich die DPI des Fensters ändert, müssen bitmapové zdroje, die pixelgenau sein sollen, neu bereitgestellt werden.
- Wenn du ImageLists dynamisch füllst, reicht ein bloßes Invalidate oft nicht — du brauchst ein Rebuild der Images in der neuen Zielgröße.
Praktický vzor: při změně DPI (např. Form-Scale/Monitorwechsel) invaliduješ Icon-Cache pro tuto DPI a znovu sestavíš postižené ImageListy. Důležité je přitom ne škálovat v Paint-událostech, ale v kontrolovaném update-bloku (Toolbar.BeginUpdate/EndUpdate, ListView-Redraw aus, dann wieder an). Tím se vyhneš blikání a nedokončeným stavům UI.
Warum TImageList so oft unscharf wird: Interpolation, DPI-Rundung, Kanten
Jakmile pochopíš, že problém není samotné DPI, ale interpolace plus zaokrouhlování, lze vysvětlit mnoho efektů:
- DPI-Rundung: 125a0% ist kein sauberer Verdoppler. Aus 16 px werden 20 px (16 * 1,25). Aus 24 px werden 30 px. Das sind krumme Zahlen, die Pixelkanten erschweren.
- Resampling-Filter: Bilinear/Bicubic macht Kanten weicher. Das ist ffcr Fotos ok, ffcr Icons oft nicht.
- Subpixel-Effekte: Windows kann je nach Renderpfad Subpixel-Antialiasing nutzen oder nicht. Bei Icons willst du kontrollierte Kanten e4a0b7 und mf6glichst keine mehrfachen Filterstufen.
Wenn du Raster-Icons hast, ist es in vielen Teams ge4ngige Praxis, pro Zielgrf6dfe eigene PNGs zu liefern (16/20/24/32/40/48). Klingt nach viel, ist aber oft weniger Aufwand als jahrelang bbwarum sieht das auf diesem Monitor komisch ausab zu debuggen.
Debugging: Pixelmatsch reproduzierbar machen statt nach Bauchgeffchl zu fixen
High-DPI-Bugs wirken oft zufe4llig. Mit ein paar Checks werden sie deterministisch:
1) DPI und ImageList-Grf6dfe zur Laufzeit loggen
Logge bei Start und bei DPI-Wechsel: CurrentPPI des Forms, Screen.PixelsPerInch (Achtung: das kann System-DPI sein), sowie ImageList.Width/Height der betroffenen Listen. Wenn du nach einem Monitorwechsel noch 16d716 siehst, obwohl du 32 erwartest, ist die Ursache klar: Rebuild fehlt oder kommt zu spe4t.
2) Icons sichtbar vergrf6dfern
Ein schneller Test ist, die Toolbar-Icons tempore4r auf 48 px zu setzen. Schlechte Skalierung springt dann sofort ins Auge. Gute Pipelines bleiben auch bei 48 px scharf, weil sie aus einer passenden Quelle rastern.
3) Theme- und Hintergrundwechsel testen
Halos am Rand sind oft Alpha-/Premultiply-Probleme. Teste auf hell/dunkel und auf Fle4chen mit Farbverlauf. Wenn die Kante je nach Hintergrund anders aussieht, stimmt die Transparenzbehandlung nicht.
4) Remote Desktop / Monitorwechsel als Testskript
Erstelle ein kurzes Testskript ffcr die QS: App starten auf Monitor A (100a0%), Fenster auf Monitor B (150a0%), zurfcck, dann RDP reconnect. Wenn das stabil ist, sind viele Kundenprobleme bereits erschlagen.
Migration in bestehenden Anwendungen: Schrittweise statt Big Bang
In gewachsenen Delphi-VCL-Anwendungen steckt die Icon-Logik oft an vielen Stellen: Menfcs, Toolbars, ActionLists, TreeViews, Statusanzeigen. Ein Big-Bang-Umbau bringt Risiko. Bewe4hrt hat sich ein schrittweises Vorgehen:
- Inventur: Welche ImageLists gibt es? Welche Controls nutzen sie? Welche Grf6dfen werden erwartet?
- Priorisieren: Zuerst die prominentesten Fle4chen (Haupttoolbar, Navigation, Kontextmenfcs).
- Einheitliche Quelle: Icons zentralisieren (ImageCollection oder eigener Loader), statt pro Form Einzeldateien zu laden.
- DPI-Wechsel testen: Ab dem ersten umgebauten Modul konsequent DPI-Wechsel-Tests fahren.
Důležité: Pokud provozuješ současně starou a novou pipeline, zdokumentuj jasná pravidla. Jinak vznikne smíšené prostředí, kde jsou některé ikony ostré a jiné zřetelně rozmazané.
Výkon a paměť: škálování za běhu bez vedlejších efektů
Škálování stojí CPU a paměť. V podnikové aplikaci to málokdy padne do oka v klidovém stavu, ale při přetažení okna na nový monitor nebo při spuštění s mnoha formuláři může dojít k trhání. Tři praktické mantinely:
- Omezit velikost cache: Nedržet každou mezistupeň věčně. Pokud potřebuješ jen 100 % a 150 %, kešuj jen tyto hodnoty.
- Odložené sestavení: Ikony rasterizovat až ve chvíli, kdy je obrazovka skutečně potřebuje. U velkých menu to šetří čas při spuštění.
- Hromadné přepočítání: Při změně DPI netriggeruj každé ovládací prvky jednotlivě. Centrální přepočet zabrání redundantnímu škálování.
Pokud pracuješ s TVirtualImageList, je mnoho z toho už zahrnuto v konceptu, ale musíš přesto dávat pozor, abys navíc nepřidával vlastní škálování navrch.
Fallback strategie: Co dělat, když nejsou všechny velikosti ikon k dispozici?
V realitě nemáš vždy všechny assety ve všech velikostech. Potřebuješ pak jasnou fallback strategii, aby se předešlo náhodným výsledkům:
- Preferovat downscale: Raději zmenšit z 64 px na 32 px než zvětšovat z 16 px na 32 px.
- Definuj stupně: Urči, které cílové velikosti skutečně podporuješ (např. 16/20/24/32/40/48) a mapuj na ně DPI konzistentně.
- Testovat průhlednost: U fallbacků věnuj zvláštní pozornost alfakanálu — právě tam vznikají haló.
Typickou chybou je prostě vzít nějakou další dostupnou velikost. Vnímaná ostrost se tím liší podle ikony. Lepší je pevný, zdokumentovaný plán mapování.
Kdy se to úsilí opravdu vyplatí?
Existují tři jasné indikátory, že se vyplatí čistá High‑DPI ikonová pipeline:
- Uživatelé pracují se smíšenými monitory (notebook + externí) nebo často přes RDP.
- Aplikace je dlouhodobá a bude po léta udržována — vnímání UI je součástí akceptace.
- Máte plánované modernizační kroky (zvýšení DPI‑awareness, výměna ovládacích prvků, přepracování rozložení panelu nástrojů).
Pokud aplikace běží jen na fixním kioskovém systému se stejným rozlišením, lze téma držet minimálně: dodat vhodnou velikost ikon, správně nastavit DPI‑awareness a hotovo.
Závěr: High‑DPI není kosmetický detail, ale rozhodnutí o renderingu
Neostré ikony ve VCL zřídka představují izolovanou chybu; jsou spíše indikátorem nečistého řetězce zdrojů, škálování a cachování. Nejrobustnější přístup je poskytnout ikony ve více rozlišeních a dodávat je konzistentně přes centrální pipeline (např. ImageCollection/VirtualImageList nebo vlastní vrstvu ikon). Škálování za běhu má smysl, pokud máš dynamické zdroje nebo složené symboly, ale pouze s master‑zdrojem, cache založenou na DPI a jasnými pravidly pro přepočet.
Máte‑li konkrétní symptomy (změna DPI rozbije ikony, haló na okrajích, nesprávné velikosti po RDP), vyplatí se problém cíleně izolovat a řešit jako malý architektonický prvek místo hromadění workarounds po formuláři. Pokud při tom potřebuješ podporu při debugování nebo při postupné modernizaci, najdeš zde vhodný vstup: Kontakt na Net-Base Software GmbH.
Pro toto téma jsou důležité také Timagelist High Dpi a Delphi Vcl Dpi-Awareness. Příspěvek tyto aspekty srozumitelně zařadí a ukáže, na co je v praxi třeba dbát.
další krok
Když se z tématu stane reálný projekt, měly by být architektura, stávající systém a provoz posuzovány společně již v rané fázi.
Podporujeme nejen při jednotlivých otázkách, ale i v případě, že se z útržků zdrojového kódu, legacy témat nebo nápadů na portál má vyvinout robustní podnikový projekt.
- Současný stav, cílový stav a technická rizika jsou hodnoceny společně.
- REST, přístup k datům, portály a rollout nebudou přesunuty do pozdějších fází.
- Včas zjistíte, která varianta je ekonomicky i provozně životaschopná.