Net-Base Magasin

18.08.2026

VCL High-DPI: Skala ikoner vid körning och undvik pixlig suddighet i TImageList

High-DPI i VCL är inte bara en kryssruta, utan en kedja av ImageList-setup, DPI-ändringshändelser och korrekt rendering. Denna praktiska artikel visar hur du skalar ikoner under körning, undviker artefakter och reproducerbart felsöker typiska TImageList-fällor.

18.08.2026

Från magasinets tema till projektpraxis

Passande tjänste- och tekniksidor för inlägget

Vem som helst som kör VCL-applikationer på moderna Windows-klienter stöter förr eller senare på samma symptom: ikoner ser vid 125%/150%/200% ut att vara suddiga, fransiga eller får en grå kant. Precis här blir ämnet VCL High-DPI-ikoner praktiskt: inte för att High-DPI är nytt, utan för att problemen oftast först visar sig i vardagen — på terminalservrar, vid byte mellan laptop och dockningsstation eller så snart olika DPI används per skärm.

Kärnfrågan är nästan aldrig „PNG:en är trasig“, utan pipelinen: varifrån kommer ikonen (resurs, fil, SVG, font), i vilken storlek tillhandahålls den, hur hamnar den i TImageList och vem skalar när hur. I VCL sammanfaller flera begrepp som måste hållas isär: DPI-Awareness (om Windows skalar appen eller appen skalar sig själv), Per-Monitor-DPI (varje monitor får vara olika) och ImageList-Strategie (att tillhandahålla flera upplösningar eller att rasterisera vid körtid).

I det här inlägget handlar det medvetet inte om UI-design-debatter, utan om ett rent, driftssäkert förhållningssätt: att skala ikoner vid körtid, men så att de inte blir pixelmos, att alfakanalerna förblir intakta och att DPI-ändringar fungerar utan flimrande eller felaktiga bildstorlekar. Dessutom fallgropar, felsökningsanvisningar och en ärlig bedömning av när den extra insatsen lönar sig.

Warum Pixelmatsch entsteht: Skalierungskette in der VCL verstehen

Grafik zur Icon-Skalierung: Master-Icon wird in mehrere Zielgrf6dfen gerastert, doppelte Skalierung ffchrt zu Unsche4rfe
En enda skalning från en masterkälla är kontrollerbar — dubbla resamplingssteg leder snabbt till synlig oskärpa.

Den vanligaste orsaken till suddiga ikoner är en enkel ned-/uppskalning vid fel tidpunkt. Klassiskt förlopp i Legacy-VCL:

  • Applikationen levererar ikoner endast i 16×16 eller 24×24.
  • Windows eller VCL skalar till 20×20 / 32×32 / 48×48.
  • Skalaren använder en interpolation som är okej för fotografier men som suddar ut kanter i pixelgrafik.
  • Dessutom pressas transparensen (alpha) in i en masklogik eller konverteras flera gånger.

Det blir särskilt illa när flera skalningar sker i följd: till exempel när ImageList redan levererar ett skalat bitmap och Windows på grund av DPI-Unaware eller system-DPI skalar om det igen. Resultat: dubbelt suddigt.

En annan punkt som underskattas i projekt är skalerings-tidpunkten. Vid Per-Monitor-DPI (bzw. PMv2, alltså Per-Monitor-DPI-Awareness v2) kan den effektiva DPI ändras när ett fönster byter skärm eller när en Remote-Desktop-klient justerar DPI dynamiskt. Om en TImageList eller en cache inte byggs upp på nytt korrekt, ser man plötsligt ikoner i fel storlek eller med fel raster.

TImageList unter High-DPI: typische Fallen in echten Anwendungen

Die TImageList ist historisch für kleine Bitmapbilder 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 z. B. aus 16×16 eher 24×24 machen. Wenn die Liste auf 16×16 bleibt, wird entweder avklippt eller skalad an anderer Stelle — beides är oönskat.

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 bakgrundsberoende: 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 (z. B. 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 (z. B. 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).

Skalning i körtid är inte meningsfullt om du i praktiken bara använder klassiska verktygsradsikoner från en fast uppsättning. Då är det minst underhållskrävande: leverera flera upplösningar och låt VCL välja.

Om du gör skalning i körtid, gör det med tydliga regler:

  • Skala aldrig om ett redan skalat bitmap-objekt. Utgå alltid från en masterkälla (helst vektorbaserad eller högupplöst).
  • Cache per målstorlek och DPI, annars skalar du om vid varje paint — det kostar CPU och kan ge ryckig rendering.
  • Behåll alfakanalen: minimera konverteringar, använd 32-bitars RGBA, rastera inte bakgrunden.

Pragmatisk arkitektur: ikonpipeline som egen byggsten

I större applikationer lönar det sig att inte sprida frågan överallt utan bygga en liten pipeline. Det behöver inte vara ett ramverk — snarare ett tydligt ansvarsområde:

  • Ikonkälla: Var kommer masterassets ifrån (resurser, filer, databas, API)?
  • Rasterizer/Scaler: Hur genereras målstorleken från mastern (interpolation, eventuellt SVG-rendering)?
  • Cache: nyckel (Icon-ID, målpixlar, DPI, Theme) och livscykel (invalidiering vid DPI-ändring, Theme-ändring, paketbyte).
  • Consumer-Adapter: Hur förs resultatet in i VCL-strukturer (TImageList/TVirtualImageList, OwnerDraw, PaintBox)?

Fördel: Du kan reproducera och debugga DPI-buggar på ett ställe istället för att leta i 40 formulär var det skalas igen.

DPI-ändringar korrekt hantera: händelser, rebuild, repaint

Bärbar dator med två skärmar med olika DPI, fönsterbyte visar olika ikonstorlekar i en desktop-app
Per-monitor-DPI är verklighetstestet: vid monitorbyte måste ikonpipeline rasteriseras på nytt, inte bara ritas om.

Under Windows är DPI-ändringar en egen livscykel. I VCL finns, beroende på version och DPI-awareness, flera händelser/mekanismer, men grundprincipen är densamma:

  • När fönstrets DPI ändras måste bildbaserade resurser som ska vara pixelnoggranna tillhandahållas på nytt.
  • Om du fyller ImageLists dynamiskt räcker ofta inte en ren Invalidate — du behöver ett Rebuild av bilderna i den nya målstorleken.

Ett praktiskt mönster är: vid DPI-ändring (t.ex. Form-Scale/monitorbyte) invalidierar du ikon-cachen för den DPI:n och bygger upp de berörda ImageLists på nytt. Viktigt är att inte skala i Paint-händelser, utan i en kontrollerad uppdateringsblock (Toolbar.BeginUpdate/EndUpdate, ListView-Redraw av, sedan på igen). Så undviker du flimmer och halvfärdiga UI-tillstånd.

Varför TImageList så ofta blir oskarp: interpolation, DPI-avrundning, kanter

När du väl förstått att det inte är DPI som är problemet utan interpolation plus avrundning, kan många effekter förklaras:

  • DPI-avrundning: 125% är ingen ren fördubbling. Från 16 px blir det 20 px (16 * 1,25). Från 24 px blir det 30 px. Det är ojämna tal som försvårar pixelkanter.
  • Resamplingfilter: Bilinear/Bicubic gör kanter mjukare. Det är för foton okej, för ikoner ofta inte.
  • Subpixel-effekter: Windows kan beroende på renderväg använda subpixel-antialiasing eller inte. För ikoner vill du kontrollerade kanter — och helst inga flera filtreringssteg.

Om du har rasterikoner är det i många team vanlig praxis att leverera separata PNG-filer per målstorlek (16/20/24/32/40/48). Låter mycket, men är ofta mindre arbete än att i åratal felsöka varför det ser konstigt ut på en viss skärm.

Felsökning: göra pixelmudd reproducerbart istället för att fixa efter magkänsla

Arbetsplats med debug-anteckningar för ikonskalning och bakgrundstester för alfakanten
Goda DPI-debuggningsrutiner testar storlekar, bakgrunder och rebuild-tidpunkter — inte bara den första skärmbilden.

High-DPI-buggar uppträder ofta slumpmässigt. Med några kontroller blir de deterministiska:

1) Logga DPI och ImageList-storlekar i körtid

Logga vid start och vid DPI-ändring: CurrentPPI des Forms, Screen.PixelsPerInch (obs: det kan vara system-DPI), samt ImageList.Width/Height för de berörda listorna. Om du efter en skärmväxling fortfarande ser 16 när du förväntar dig 32 är orsaken klar: en rebuild saknas eller sker för sent.

2) Förstora ikoner synligt

Ett snabbt test är att temporärt sätta verktygsfältsikonerna till 48 px. Då syns dålig skalning direkt. Bra pipelines förblir skarpa även vid 48 px eftersom de rasteriseras från en lämplig källa.

3) Testa tema- och bakgrundsbyte

Halo vid kanten är ofta alpha-/premultipliceringsproblem. Testa på ljust/mörkt och på ytor med färggradient. Om kanten ser olika ut beroende på bakgrund är transparenshanteringen fel.

4) Remote Desktop / skärmväxling som testskript

Skapa ett kort testskript för QS: starta appen på skärm A (100%), flytta fönstret till skärm B (150%), tillbaka, sedan RDP reconnect. Om det är stabilt ligger många kundproblem redan åtgärdade.

Migration i befintliga applikationer: stegvis istället för Big Bang

I befintliga Delphi-VCL-applikationer sitter ofta ikonlogiken på många ställen: menyer, verktygsfält, ActionLists, TreeViews, statusvisningar. Ett Big-Bang-ombyggnad medför risk. Ett beprövat tillvägagångssätt är en stegvis strategi:

  • Inventering: Vilka ImageLists finns? Vilka kontroller använder dem? Vilka storlekar förväntas?
  • Prioritera: Börja med de mest framträdande ytorna (huvudverktygsfält, navigation, kontextmenyer).
  • Enhetlig källa: Centralisera ikoner (ImageCollection eller egen loader), istället för att ladda enskilda filer per form.
  • Testa DPI-ändringar: Från det första ombyggda modulen konsekvent köra DPI-ändringstester.

Viktigt: Om du kör både den gamla och den nya pipelinen parallellt, dokumentera tydliga regler. Annars uppstår ett blandat landskap där vissa ikoner är skarpa och andra synligt suddiga.

Prestanda och minne: Körningsskala utan sidoeffekter

Skalning kostar CPU och minne. I affärsprogram märks det sällan i vila, men när du drar fönstret till en ny monitor eller vid start med många Forms kan det hakiga. Tre praktiska riktlinjer:

  • Begränsa cache-storlek: Håll inte varje mellanläge för evigt. Om du bara behöver 100 % och 150 % så cacha bara dessa.
  • Lazy Build: Rasterisera ikoner först när skärmen verkligen behöver dem. Vid stora menyer sparar det starttid.
  • Batch-Rebuild: Vid DPI-ändring, trigga inte varje Control individuellt. En central rebuild förhindrar redundant skalning.

Om du arbetar med TVirtualImageList är mycket av detta redan tänkt som koncept, men du måste ändå vara noga med att inte lägga på ytterligare egen skalning ovanpå.

Fallback-strategier: Vad göra om inte alla ikonstorlekar finns?

I verkligheten har du inte alltid alla assets i alla storlekar. Då behöver du en tydlig fallback-strategi för att undvika slumpmässiga resultat:

  • Föredra nedskalning: Hellre skala ner från 64 px till 32 px än skala upp från 16 px till 32 px.
  • Definiera nivåer: Bestäm vilka målstorlekar du faktiskt stödjer (t.ex. 16/20/24/32/40/48) och mappa DPI noggrant mot dem.
  • Testa transparens: Vid fallbacks var särskilt uppmärksam på alfa — just där uppstår ofta halos.

Ett typiskt misstag är att ta någon av de närmaste storlekarna. Det gör att upplevd skärpa varierar mellan ikoner. Bättre är en strikt, dokumenterad mapping-plan.

När är arbetet verkligen värt det?

Det finns tre tydliga indikatorer på att en ordentlig pipeline för High-DPI-ikoner är motiverad:

  • Dina användare arbetar med blandade skärmar (laptop + extern) eller mycket via RDP.
  • Applikationen är livslång och kommer underhållas över år — UI-uppfattning är en del av acceptansen.
  • Du har ändå moderniseringssteg framför dig (höja DPI-medvetenhet, byta Controls, revidera verktygsrads-layout).

Om appen däremot bara körs på ett fast kiosksystem med identisk upplösning kan ämnet hållas minimalt: leverera en lämplig ikonstorlek, sätt DPI-medvetenheten korrekt, klart.

Slutsats: High-DPI är ingen kosmetisk detalj, utan ett renderingsbeslut

Suddiga ikoner i VCL är sällan ett isolerat fel, utan en indikation på en osäker kedja av källor, skalning och caching. Den mest robusta vägen är att tillhandahålla ikoner i flera upplösningar och leverera dem konsekvent via en central pipeline (t.ex. ImageCollection/VirtualImageList eller ett eget ikonlager). Körningsskalning är då rimligt när du har dynamiska källor eller sammansatta symboler — men endast med en master-källa, DPI-baserad cache och tydliga rebuild-regler.

Om du har konkreta symptom (DPI-ändring gör ikoner trasiga, halos i kanten, fel storlekar efter RDP) är det värt att isolera frågan och behandla den som en liten arkitekturkomponent istället för att stapla workarounds per Form. Om du behöver stöd vid debugging eller vid en stegvis modernisering hittar du här en lämplig ingång: Kontakt till Net-Base Software GmbH.

För detta ämne är även Timagelist High Dpi och Delphi Vcl Dpi-Awareness viktiga. Artikeln placerar dessa aspekter i ett begripligt sammanhang och visar vad som är viktigt i det dagliga arbetet.

Diskutera projekt eller moderniseringsprojekt med Net-Base.

nästa steg

När ett ämne blir ett verkligt projekt bör arkitektur, befintligt bestånd och drift tidigt ses över gemensamt.

Vi stöder inte bara vid enstaka frågor, utan även när kodsfragment, legacy-frågor eller portalidéer ska utvecklas till ett robust företagsprojekt.

  • Nuläge, målbild och tekniska risker bedöms tillsammans.
  • REST, dataåtkomst, portaler och utrullning skjuts inte upp som sena följder.
  • Ni ser tidigt vilken väg som är ekonomiskt och driftmässigt hållbar.

Dela inlägg

Dela det här inlägget direkt

LinkedIn, X, XING, Facebook, WhatsApp och e-post är omedelbart tillgängliga. För Instagram förbereder vi länken och en kort text direkt.

E-post

Instagram öppnas i en ny flik. Länken och korttexten kopieras till urklipp först.