Fra magasinetema til prosjektpraksis
Egnede tjeneste- og tekniske sider for innlegget
Den som kjører VCL-applikasjoner på moderne Windows-klienter, støter før eller senere på samme symptom: ikoner framstår ved 125%/150%/200% som uskarpe, oppfransede eller får en grå kant. Her blir temaet VCL High-DPI Icons praktisk: ikke fordi High-DPI er nytt, men fordi problemene som regel først viser seg i praksis — på terminalservere, ved bytte mellom dokking av bærbar og løsningen, eller så snart skjermer har ulik DPI.
Kjernespørsmålet er nesten aldri «PNG-en er ødelagt», men pipeline: Hvor kommer ikonet fra (ressurs, fil, SVG, font), i hvilken størrelse leveres det, hvordan havner det i TImageList og hvem skalerer når og hvordan. I VCL møtes flere begreper som må skilles fra hverandre: DPI-Awareness (om Windows skalerer appen eller om appen skalerer seg selv), Per-Monitor-DPI (hver skjerm kan være forskjellig) og ImageList-Strategie (holde flere oppløsninger tilgjengelig eller rastere ved kjøretid).
I dette innlegget handler det bevisst ikke om UI-design-debatter, men om en ryddig, driftssikker tilnærming: skalere ikoner ved kjøretid, men slik at de ikke blir til pixelgrøt, at alfa-kanalene forblir intakte og at DPI-bytter fungerer uten flimrende eller feil bildestørrelser. I tillegg kommer fallgruver, feilsøkingstips og en ærlig vurdering av når merarbeidet lønner seg.
Hvorfor pixelgrøt oppstår: forstørrelseskjeden i VCL
Den hyppigste årsaken til uskarpe ikoner er en enkeltstående ned-/oppskalering på feil tidspunkt. Klassisk forløp i legacy-VCL:
- Applikasjonen leverer ikoner bare i 16×16 eller 24×24.
- Windows eller VCL skalerer til 20×20 / 32×32 / 48×48.
- Skaleringsalgoritmen bruker en interpolasjon som er ok for fotografier, men som myker opp kanter i pikselgrafikk.
- I tillegg blir transparensen (alpha) presset inn i en maskelogikk eller konvertert flere ganger.
Det blir spesielt vrient når flere skaleringer skjer etter hverandre: for eksempel når ImageList allerede leverer et skalert bitmap, og Windows på grunn av DPI-unawareness eller system-DPI skalerer over igjen. Resultat: dobbel utmykning.
Et annet, i prosjekter undervurdert punkt er tidspunktet for skalering. Ved Per-Monitor-DPI (bzw. PMv2, altså Per-Monitor-DPI-Awareness v2) kan den effektive DPI endre seg når et vindu bytter skjerm eller når en Remote Desktop-klient justerer DPI dynamisk. Hvis en TImageList eller en cache ikke bygges opp på nytt korrekt da, ser man plutselig ikoner i feil størrelse eller med feil raster.
TImageList under High-DPI: typische Fallen in echten Anwendungen
Die TImageList ist historisch für kleine Bitmaps gedacht, mit festen Maßen, Indizes und relativ starrer Speicherlogik. Unter High-DPI ergeben sich daraus praxisnahe Fallen:
1) Fest verdrahtete Width/Height
Viele VCL-Formulare setzen ImageList.Width/Height zur Designzeit und lassen es dabei. Bei 150 % möchte Windows aber f.eks. aus 16×16 eher 24×24 machen. Wenn die Liste auf 16×16 bleibt, wird entweder geclippt oder an anderer Stelle skaliert — beides unschön.
2) PNG-Alpha und Maskenlogik
Je nach Delphi-Version und VCL-Controls landet man schnell in einem Mischbetrieb: PNGs mit Alpha werden intern teils als 32-bit Bitmap gehalten, teils als Mask+Color. Sobald du mehrfach konvertierst (PNG -> Bitmap -> ImageList -> Draw), entstehen graue Halos oder harte Kanten. Der Effekt ist häufig backgroundsensitiv: Auf dunkler Toolbar sieht es schlimmer aus als auf einem hellen Panel.
3) DPI-Wechsel zur Laufzeit: Caches, Handles, OwnerDraw
Einige Controls cachen die Bilddarstellung oder übernehmen ImageList-Handles zu einem Zeitpunkt, wo die DPI noch nicht final ist. Besonders bei Toolbars, TreeViews/ListViews und OwnerDraw-Szenarien sieht man nach DPI-Wechseln sporadisch falsche Bildgrößen oder leere Icons, bis ein Repaint oder ein RecreateWnd passiert.
4) Terminalserver und Remote-Desktop als Realitetstest
Wenn die App über RDP genutzt wird, sind DPI-Wechsel und Session-Reconnects nicht exotisch. Genau dort fliegt dir eine unrobuste ImageList-Strategie um die Ohren: Der Benutzer sieht nach dem Reconnect verwaschene Icons oder falsch skalierte Symbolleisten, obwohl lokal alles ok war.
Sauberer Ansatz: Mehrere Auflösungen bereitstellen statt brutales Upscaling
Die wichtigste Entscheidung ist konzeptionell: Willst du Icons zur Laufzeit aus einer einzigen Basisgrafik hochskalieren (f.eks. 16×16 -> 32×32), oder stellst du mehrere native Auflösungen bereit und wählst je nach DPI die passende?
In der Praxis gewinnt fast immer mehrere Auflösungen. Upscaling kann man machen, wenn die Quelle vektorbasiert ist (SVG, Icon-Font) oder wenn du wirklich nur moderate Faktoren brauchst. Sobald du aus einem kleinen Rasterbild stark hochskalierst, verlierst du Kantenqualität, und das siehst du auf modernen Displays sofort.
In der VCL-Welt sind für diesen Ansatz heute zwei Bausteine relevant:
- TImageCollection: Container für Bilder in mehreren Größen/Varianten.
- TVirtualImageList: erzeugt daraus zur Laufzeit eine ImageList in der aktuell benötigten Größe und reagiert auf DPI-Änderungen.
Das nimmt dir nicht alle Probleme ab, aber es verschiebt das Problem an die richtige Stelle: Du definierst Bildquellen sauber, und die Skalierung/Selektion passiert konsistent.
VCL High-DPI Icons zur Laufzeit skalieren: Wann es sinnvoll ist (und wann nicht)
Es gibt legitime Gründe, Icons zur Laufzeit zu skalieren:
- Du lädst Icons dynamisch (f.eks. aus einem Plugin-Ordner, kundenspezifische Branding-Pakete, Konfigurationspakete).
- Du willst eine einheitliche Pipeline für verschiedene Quellen (ICO, PNG, SVG) und möchtest nicht alle Varianten zur Build-Zeit binden.
- Du erzeugst Icons programmgesteuert (Status-Badges, Overlays, zusammengesetzte Symbole).
Skalering ved kjøretid gir liten mening hvis du egentlig bare har klassiske toolbar-ikoner fra et fast sett. Da er den mest vedlikeholdsvennlige løsningen: levere flere oppløsninger og la VCL velge.
Hvis du gjør skalering ved kjøretid, gjør det med klare regler:
- Aldri skalere et allerede skalert bitmap på nytt. Alltid utgå fra en masterkilde (ideelt vektorbassert eller høyoppløst).
- Cache per målstørrelse og DPI, ellers skalerer du på hver Paint på nytt — det koster CPU og kan gi hakking.
- Bevar alpha: minimér konverteringer, bruk 32-bit RGBA, ikke rasterisere bakgrunnen.
Pragmatiske arkitektur: Icon-Pipeline som egen komponent
I større applikasjoner lønner det seg å ikke spre temaet overalt, men å bygge en liten pipeline. Det trenger ikke være et rammeverk — heller et klart ansvarsområde:
- Icon-Quelle: Hvor kommer master-assetene fra (ressurser, filer, database, API)?
- Rasterizer/Scaler: Hvordan genereres målet fra masteren (interpolasjon, eventuelt SVG-render)?
- Cache: Nøkkel (Icon-ID, mål-piksler, DPI, Theme) og livssyklus (invalidér ved DPI-skift, Theme-skift, pakkeendring).
- Consumer-Adapter: Hvordan kommer resultatet inn i VCL-strukturer (TImageList/TVirtualImageList, OwnerDraw, PaintBox)?
Fordel: Du kan debugge DPI-bugs reproduserbart på ett sted i stedet for å lete gjennom 40 Forms etter hvor det skaleres igjen.
DPI-skift korrekt håndtert: hendelser, rebuild, repaint
Under Windows er DPI-endringer en egen livssyklus. I VCL finnes, avhengig av versjon og DPI-awareness, flere hendelser/mekanismer, men grunnprinsippet forblir:
- Når DPI for vinduet endres, må bildebaserte ressurser som skal være pikselnøyaktige, gjøres tilgjengelig på nytt.
- Hvis du fyller ImageLists dynamisk, er ofte et enkelt Invalidate ikke nok — du trenger et Rebuild av bildene i den nye målstørrelsen.
Et praktisk mønster er: Ved DPI-endring (f.eks. Form-Scale/Monitorwechsel) invalidér du Icon-Cache for denne DPI og bygger de berørte ImageLists opp på nytt. Viktig er å ikke skalere i Paint-hendelser, men i en kontrollert oppdateringsblokk (Toolbar.BeginUpdate/EndUpdate, ListView-Redraw av, så på igjen). Slik unngår du flimring og uferdige UI-tilstander.
Warum TImageList so oft unscharf wird: Interpolation, DPI-Rundung, Kanten
Når du først har forstått at ikke DPI i seg selv er problemet, men interpolasjon pluss avrunding, lar mange effekter seg forklare:
- DPI-avrunding: 125 % er ingen ren fordobling. Fra 16 px blir 20 px (16 * 1,25). Fra 24 px blir 30 px. Det gir ujevne tall som gjør pikselkanter vanskeligere.
- Resampling-filter: Bilinear/Bicubic gjør kanter mykere. Det er ok for bilder, men ofte ikke for ikoner.
- Subpixel-effekter: Windows kann je nach Renderpfad Subpixel-Antialiasing nutzen oder nicht. Bei Icons willst du kontrollierte Kanten und möglichst keine mehrfachen Filterstufen.
Hvis du har raster-ikoner, er det i mange team vanlig praksis å levere egne PNG-er per målstørrelse (16/20/24/32/40/48). Det høres mye ut, men er ofte mindre arbeid enn å i årevis feilsøke «hvorfor ser det merkelig ut på denne skjermen».
Debugging: Gjør pikselrot reproducerbart i stedet for å fikse etter magefølelse
High-DPI-feil fremstår ofte som tilfeldige. Med noen få sjekker blir de deterministiske:
1) DPI und ImageList-Größen 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 16 siehst, obwohl du 32 erwartest, ist die Ursache klar: Rebuild fehlt oder kommt zu spät.
2) Icons sichtbar vergrößern
Ein schneller Test ist, die Toolbar-Icons temporär 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 Flächen 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 (100 %), Fenster auf Monitor B (150 %), zurück, 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: Menüs, Toolbars, ActionLists, TreeViews, Statusanzeigen. Ein Big-Bang-Umbau bringt Risiko. Bewährt hat sich ein schrittweises Vorgehen:
- Inventur: Welche ImageLists gibt es? Welche Controls nutzen sie? Welche Größen werden erwartet?
- Priorisieren: Zuerst die prominentesten Flächen (Haupttoolbar, Navigation, Kontextmenüs).
- 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.
Viktig: Hvis du kjører gammel og ny pipeline parallelt, dokumenter klare regler. Ellers oppstår et blandet landskap der noen ikoner er skarpe og andre virker synlig uskarpe.
Ytelse og minne: Kjøringstidsskalaering uten bivirkninger
Skalering koster CPU og minne. I forretningsprogramvare merkes det sjelden i tomgang, men når du drar et vindu over på en ny skjerm eller ved oppstart med mange Forms kan det hakke. Tre praktiske retningslinjer:
- Begrens cache-størrelser: Ikke behold hver mellomstørrelse for evig. Hvis du bare trenger 100% og 150%, cache kun disse.
- Lazy build: Rasteriser ikoner først når skjermen faktisk trenger dem. Ved store menyer sparer dette oppstartstid.
- Batch-rebuild: Ved DPI-bytte, unngå å trigge hver kontroll individuelt. En sentral rebuild forhindrer redundant skalering.
Hvis du bruker TVirtualImageList, er mye av dette allerede tenkt som konsept, men du må likevel passe på å ikke i tillegg bygge egen skalering oppå.
Fallback-strategier: Hva gjøre når ikke alle ikon-størrelser foreligger?
I praksis har du ikke alltid alle assets i alle størrelser. Da trenger du en klar fallback-strategi for å unngå tilfeldige resultater:
- Foretrekk nedskalering: Hellere ned fra 64 px til 32 px enn oppskalering fra 16 px til 32 px.
- Definer trinn: Fastsett hvilke målstørrelser du virkelig støtter (f.eks. 16/20/24/32/40/48) og map DPI konsekvent mot disse.
- Test transparens: Ved fallbacks, vær særlig oppmerksom på alpha — nettopp der oppstår halos.
En typisk feil er å ta neste tilgjengelige størrelse. Det gjør at opplevd skarphet varierer mellom ikoner. Bedre er en streng, dokumentert mapping-plan.
Når er innsatsen virkelig verdt det?
Det finnes tre klare indikatorer på at en ren High‑DPI‑ikon‑pipeline lønner seg:
- Dine brukere jobber med blandede skjermer (bærbar + ekstern) eller mye via RDP.
- Applikasjonen er langsiktig og vil vedlikeholdes over år — UI‑opplevelse er en del av aksepten.
- Du har uansett modernisering på agendaen (heve DPI‑awareness, bytte kontroller, revidere verktøylinje‑layout).
Hvis appen derimot bare kjører på et fast kiosksystem med identisk oppløsning, kan temaet holdes minimalt: lever en passende ikon‑størrelse, sett DPI‑awareness korrekt, ferdig.
Konklusjon: High‑DPI er ikke et kosmetisk spørsmål, men en rendering‑beslutning
Uskarpe ikoner i VCL er sjelden en isolert feil; det er ofte et tegn på en ujevn kjede av kilder, skalering og caching. Den mest robuste tilnærmingen er å tilby ikoner i flere oppløsninger og levere dem konsekvent via en sentral pipeline (f.eks. ImageCollection/VirtualImageList eller et eget ikonlag). Kjøringstidsskalaering gir mening når du har dynamiske kilder eller sammensatte symboler — men kun med en master‑kilde, DPI‑basert cache og klare rebuild‑regler.
Hvis du har konkrete symptomer (DPI‑bytte ødelegger ikoner, halos i kanten, feil størrelser etter RDP), lønner det seg å isolere problemet og behandle det som en liten arkitekturkomponent i stedet for å stable workarounds per form. Hvis du trenger støtte ved debugging eller en trinnvis modernisering, finner du inngangen her: Kontakt zur Net-Base Software GmbH.
For dette temaet er også Timagelist High Dpi og Delphi Vcl Dpi-Awareness viktige. Artikkelen setter disse aspektene i en forståelig kontekst og viser hva som betyr noe i praksis.
Neste trinn
Når et tema blir et reelt prosjekt, bør arkitektur, eksisterende systemer og drift vurderes samlet allerede tidlig i prosessen.
Vi bistår ikke bare med enkeltspørsmål, men også når kodesnutter, legacy-temaer eller portalideer skal utvikles til et robust virksomhetsprosjekt.
- Eksisterende tilstand, målbildet og tekniske risikoer vurderes samlet.
- REST, datatilgang, portaler og utrulling blir ikke utsatt som etterfølgende oppgaver.
- Dere ser tidlig hvilken vei som er økonomisk og driftsmessig levedyktig.