Van magazinethema naar projectpraktijk
Relevante dienst- en technische pagina's bij het artikel
Wie VCL-toepassingen op moderne Windows-clients worden ingezet, stuit men vroeg of laat op hetzelfde symptoom: iconen ogen bij 125%/150%/200% wazig, rafelig of krijgen een grijze rand. Precies hier wordt het onderwerp VCL High-DPI Icons praktisch relevant: niet omdat High-DPI nieuw is, maar omdat de problemen zich meestal pas in de dagelijkse praktijk openbaren — op terminalservers, bij het wisselen tussen notebook en dock of zodra per monitor verschillende DPI in het spel zijn.
Het kernthema is bijna nooit dat het PNG kapot is, maar de pipeline: waar komt het icoon vandaan (resource, bestand, SVG, font), in welke resolutie wordt het geleverd, hoe belandt het in de TImageList en wie schaalt wanneer en hoe. In de VCL komen daarbij meerdere begrippen samen die je moet scheiden: DPI-Awareness (of Windows de app schaalt of de app zichzelf schaalt), Per-Monitor-DPI (elke monitor mag anders zijn) en ImageList-Strategie (meerdere resoluties bewaren of tijdens runtime rasteren).
In dit artikel gaat het bewust niet over UI-designdebatten, maar over een zuivere, bedrijfszekere aanpak: iconen tijdens runtime schalen, maar zodanig dat ze niet veranderen in pixelbrij, dat alpha-kanalen intact blijven en dat DPI-wissels zonder flikkeren of onjuiste afbeeldingsformaten werken. Daarbij komen valkuilen, debugtips en een eerlijke inschatting wanneer de meerinspanning de moeite waard is.
Waarom vervaging ontstaat: de schaalingsketen in de VCL begrijpen
De meest voorkomende reden voor onscherpe iconen is een eenmalige down-/upscaling op het verkeerde moment. Klassiek verloop in legacy-VCL:
- De toepassing levert iconen alleen in 16×16 of 24×24.
- Windows of de VCL schaalt naar 20×20 / 32×32 / 48×48.
- De scaler gebruikt een interpolatie die voor foto’s acceptabel is, maar bij pixelgrafiek de randen vervaagt.
- Bovendien wordt de transparantie (alpha) in een maskeringslogica geperst of meerdere keren geconverteerd.
Het wordt extra vervelend wanneer meerdere schalingen achter elkaar plaatsvinden: bijvoorbeeld wanneer de ImageList al een geschaalde bitmap levert en Windows vanwege DPI-unaware of systeem-DPI nogmaals erover schaalt. Resultaat: dubbel wazig gemaakt.
Een tweede, in projecten onderschat punt is het moment van schalen. Bij Per-Monitor-DPI (bzw. PMv2, dus Per-Monitor-DPI-Awareness v2) kan de effectieve DPI veranderen wanneer een venster van monitor wisselt of wanneer een Remote-Desktop-client de DPI dynamisch aanpast. Als dan een TImageList of een cache niet correct wordt herbouwd, ziet men plotseling iconen in de verkeerde grootte of met het verkeerde raster.
TImageList onder High-DPI: typische valkuilen in echte toepassingen
De TImageList is historisch gezien bedoeld voor kleine bitmaps, met vaste afmetingen, indices en een relatief starre opslaglogica. Onder High-DPI ontstaan daardoor praktijkgerichte valkuilen:
1) Vast ingestelde Width/Height
Veel VCL-formulieren zetten ImageList.Width/Height tijdens de ontwerptijd en laten het daarbij. Bij 150% wil Windows bijvoorbeeld een 16×16 liever 24×24 maken. Als de lijst op 16×16 blijft, wordt het ofwel afgesneden of op een andere plek geschaald — beide onaangenaam.
2) PNG-Alpha en maskeringslogica
Afhankelijk van de Delphi-versie en de VCL-controls belandt men snel in een gemengd bedrijf: PNG’s met alpha worden intern deels als 32-bit bitmap gehouden, deels als Mask+Color. Zodra je meerdere keren converteert (PNG → Bitmap → ImageList → Draw), ontstaan grijze halos of harde randen. Het effect is vaak achtergrondgevoelig: op een donkere toolbar ziet het er erger uit dan op een licht paneel.
3) DPI-wissel tijdens runtime: caches, handles, OwnerDraw
Sommige controls cachen de afbeeldingsweergave of nemen ImageList-handles over op een moment waarop de DPI nog niet definitief is. Vooral bij toolbars, TreeViews/ListViews en OwnerDraw-scenario’s zie je na DPI-wijzigingen sporadisch onjuiste afbeeldingsgroottes of lege iconen, totdat een Repaint of een RecreateWnd plaatsvindt.
4) Terminalserver en Remote-Desktop als realiteitstest
Als de app via RDP gebruikt wordt, zijn DPI-wijzigingen en sessie-reconnects niet uitzonderlijk. Juist daar loopt een niet-robuuste ImageList-strategie spaak: de gebruiker ziet na het reconnecten vervaagde iconen of verkeerd geschaalde werkbalken, hoewel lokaal alles in orde leek.
Schone aanpak: meerdere resoluties aanbieden in plaats van hardnekkig upscalen
De belangrijkste beslissing is conceptueel: Wil je iconen tijdens runtime uit één enkele basisgrafiek opschalen (bijv. 16×16 → 32×32), of bied je meerdere native resoluties aan en kies je per DPI de passende?
In de praktijk wint bijna altijd het principe van meerdere resoluties. Upscaling kan werken als de bron vectorgebaseerd is (SVG, Icon-Font) of als je echt alleen matige factoren nodig hebt. Zodra je vanuit een klein rasterbeeld sterk opschaalt, verlies je randdefinitie, en dat zie je direct op moderne displays.
In de VCL-wereld zijn voor deze aanpak momenteel twee bouwstenen relevant:
- TImageCollection: container voor afbeeldingen in meerdere groottes/varianten.
- TVirtualImageList: genereert daaruit tijdens runtime een ImageList in de momenteel benodigde grootte en reageert op DPI-wijzigingen.
Dat neemt niet alle problemen weg, maar het verplaatst het probleem naar de juiste laag: je definieert beeldbronnen netjes, en de schaal/selectie gebeurt consequent.
VCL High-DPI-iconen tijdens runtime schalen: wanneer het zinvol is (en wanneer niet)
Er zijn legitieme redenen om iconen tijdens runtime te schalen:
- Je laadt iconen dynamisch (bijv. uit een plugin-map, klant-specifieke branding-pakketten, configuratiepakketten).
- Je wilt een uniforme pipeline voor verschillende bronnen (ICO, PNG, SVG) en wilt niet alle varianten al bij build-time binden.
- Je genereert iconen programmatig (status-badges, overlays, samengestelde symbolen).
Runtime-scaling is niet zinvol als je eigenlijk alleen klassieke toolbar-iconen uit een vaste set hebt. Dan is de meest onderhoudsarme aanpak: meerdere resoluties netjes leveren en de VCL het laten kiezen.
Als je runtime-scaling toepast, doe het dan met duidelijke regels:
- Nooit vanuit een al geschaald bitmap opnieuw schalen. Ga altijd uit van een masterbron (idealiter vectorgebaseerd of hoogresolutief).
- Cache per doelgrootte en DPI, anders schaal je bij elke paint opnieuw — dat kost CPU en kan haperen.
- Alpha behouden: minimaliseer conversies, zet op 32-bit RGBA, raster de achtergrond niet.
Pragmatische architectuur: icon-pipeline als eigen component
In grotere toepassingen loont het om het onderwerp niet overal te verspreiden, maar een kleine pipeline te bouwen. Het hoeft geen framework te zijn — eerder een duidelijk verantwoordelijkheidsgebied:
- Icon-bron: Waar komen de master-assets vandaan (Ressourcen, bestanden, database, API)?
- Rasterizer/Scaler: Hoe wordt uit de master de doelgrootte gegenereerd (interpolatie, eventueel SVG-render)?
- Cache: Sleutel (Icon-ID, doelpixels, DPI, Theme) en levenscyclus (invalidatie bij DPI-wissel, Theme-wissel, pakketwisseling).
- Consumer-adapter: Hoe komt het resultaat in VCL-structuren (TImageList/TVirtualImageList, OwnerDraw, PaintBox)?
Voordeel: Je kunt DPI-bugs reproduceerbaar op één plek debuggen, in plaats van in 40 Forms te zoeken naar „waar wordt nogmaals geschaald“.
DPI-wissel correct behandelen: events, Rebuild, Repaint
Onder Windows zijn DPI-wissels een eigen levenscyclus. In de VCL zijn er afhankelijk van de versie en DPI-awareness meerdere events/mechanismen, maar het basisprincipe blijft:
- Als de DPI van het venster verandert, moeten beeldgebaseerde resources die pixelnauwkeurig moeten zijn opnieuw worden geleverd.
- Als je ImageLists dynamisch vult, volstaat een eenvoudig ‚Invalidate‘ vaak niet — je hebt een Rebuild van de images in de nieuwe doelgrootte nodig.
Een praktijkgericht patroon is: bij DPI-wijziging (bijv. Form-Scale/monitorwisseling) invalideer je de icon-cache voor die DPI en bouw je de betreffende ImageLists opnieuw op. Belangrijk is daarbij niet in Paint-events te schalen, maar in een gecontroleerde update-block (Toolbar.BeginUpdate/EndUpdate, ListView-Redraw uit, daarna weer aan). Zo voorkom je knipperen en half-af UI-toestanden.
Waarom TImageList zo vaak onscherp wordt: interpolatie, DPI-afronding, randen
Als je eenmaal begrijpt dat het probleem niet de DPI zelf is, maar interpolatie plus afronding, kun je veel effecten verklaren:
- 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 für Fotos ok, für 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 für 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.
Belangrijk: als je tegelijk een oude en een nieuwe pipeline draait, documenteer dan heldere regels. Anders ontstaat er een gemengde omgeving waarin sommige iconen scherp zijn en andere zichtbaar wazig lijken.
Performance en geheugen: runtime-schaalbaarheid zonder bijwerkingen
Schaalvergroting kost CPU en geheugen. In businesssoftware valt dat zelden in idle op, maar bij het verplaatsen van een venster naar een nieuwe monitor of bij het starten met veel Forms kan het haperen. Drie praktische richtlijnen:
- Cache-groottes begrenzen: Houd niet elke tussenstap eeuwig. Als je alleen 100% en 150% nodig hebt, cache dan ook alleen die waarden.
- Lazy build: Rasteriseer iconen pas wanneer het scherm ze daadwerkelijk nodig heeft. Bij grote menu’s bespaart dat opstarttijd.
- Batch-rebuild: Trigger bij een DPI-wissel niet elk control afzonderlijk. Een centrale rebuild voorkomt redundante schaalacties.
Als je met TVirtualImageList werkt, zit veel hiervan al als concept ingebouwd — maar je moet toch opletten dat je niet extra eigen schaallogica bovenop bouwt.
Fallback-strategieën: wat te doen als niet alle icoon-groottes aanwezig zijn?
In de praktijk heb je niet altijd alle assets in alle groottes. Dan heb je een duidelijke fallback-strategie nodig om willekeurige resultaten te voorkomen:
- Prefer downscale: Liever van 64 px naar 32 px schalen dan van 16 px naar 32 px opschalen.
- Definieer stappen: Bepaal welke doelsizes je echt ondersteunt (bijv. 16/20/24/32/40/48) en map DPI daar nauwkeurig op.
- Transparantie testen: Let bij fallbacks extra op de alpha-kanaal; precies daar ontstaan halos.
Een typische fout is om willekeurig de volgende beschikbare grootte te nemen. Daardoor varieert de waargenomen scherpte per icoon. Beter is een harde, gedocumenteerde mapping-planning.
Wanneer is de inspanning echt de moeite waard?
Er zijn drie duidelijke indicatoren dat een nette High-DPI-icoon-pipeline de moeite waard is:
- Je gebruikers werken met gemixte monitoren (laptop + extern) of veel via RDP.
- De applicatie is levensduurgericht en wordt over jaren onderhouden — UI-beleving is onderdeel van acceptatie.
- Je staat al voor moderniseringsstappen (DPI-awareness verhogen, controls vervangen, toolbar-layout herzien).
Als de app daarentegen alleen op een vast kiosk-systeem met identieke resolutie draait, kun je het onderwerp minimaal houden: lever één passende icoon-grootte, stel DPI-awareness correct in, klaar.
Conclusie: High-DPI is geen cosmetisch detail maar een render-keuze
Wazige iconen in de VCL zijn zelden een geïsoleerde fout; ze wijzen op een onzuivere keten van bronnen, schaalingen en caching. De meest robuuste aanpak is om iconen in meerdere resoluties beschikbaar te stellen en ze via een centrale pipeline (bijv. ImageCollection/VirtualImageList of een eigen icoonlaag) consistent te serveren. Runtime-schaalwerk is dan zinvol als je dynamische bronnen of samengestelde symbolen hebt — maar alleen met een master-bron, DPI-gebaseerde cache en duidelijke rebuild-regels.
Als je concrete symptomen hebt (DPI-wissel maakt iconen kapot, halos aan de rand, verkeerde groottes na RDP), verdient het de voorkeur het onderwerp doelgericht te isoleren en als klein architectuuronderdeel te behandelen in plaats van per form workarounds te stapelen. Als je daarbij ondersteuning nodig hebt bij debugging of stapsgewijze modernisering, vind je hier het juiste startpunt: Kontakt zur Net-Base Software GmbH.
Voor dit onderwerp zijn ook Timagelist High Dpi en Delphi Vcl Dpi-Awareness belangrijk. Het artikel plaatst deze aspecten op begrijpelijke wijze in context en laat zien waarop het in de dagelijkse praktijk aankomt.
volgende stap
Wanneer het onderwerp een concreet project wordt, moeten architectuur, bestaande omgeving en exploitatie vroegtijdig samen worden bekeken.
We ondersteunen niet alleen bij individuele vragen, maar ook wanneer uit broncodefragmenten, legacy-onderwerpen of portalideeën een robuust bedrijfsproject moet ontstaan.
- Huidige situatie, doelbeeld en technische risico's worden gezamenlijk beoordeeld.
- REST, toegang tot gegevens, portalen en rollout worden niet naar latere fasen verschoven.
- U ziet vroeg welke weg economisch en operationeel levensvatbaar is.