Net-Base Magazyn

18.08.2026

VCL High-DPI: skalowanie ikon w czasie wykonywania i unikanie rozmycia pikseli w TImageList

High-DPI w VCL to nie tylko pole wyboru, lecz łańcuch obejmujący konfigurację ImageList, zdarzenia zmiany DPI i poprawne renderowanie. Ten artykuł praktyczny pokazuje, jak skalować ikony w czasie wykonywania, unikać artefaktów i powtarzalnie debugować typowe pułapki TImageList.

18.08.2026

Od tematu magazynowego do praktyki projektowej

Pasujące strony usługowe i techniczne do artykułu

Kto uruchamia aplikacje VCL na nowoczesnych klientach Windows, wcześniej czy później natknie się na ten sam objaw: ikony przy 125%/150%/200% wyglądają na rozmyte, poszarpane lub mają szarą obwódkę. Właśnie tutaj praktycznie wchodzi w grę temat Ikony VCL High-DPI: nie dlatego, że High-DPI jest nowe, lecz dlatego, że problemy zwykle ujawniają się dopiero w codziennym użytkowaniu — na terminalserverach, przy przełączaniu laptopa z dokiem lub gdy na pojedynczych monitorach występują różne wartości DPI.

Główny problem rzadko polega na tym, że „PNG jest uszkodzony”, a częściej na przepływie danych: skąd pochodzi ikona (zasób, plik, SVG, font), w jakiej rozdzielczości jest dostarczana, jak trafia do TImageList i kto i kiedy ją skaluje. W VCL łączy się tu kilka pojęć, które trzeba odróżnić: DPI-Awareness (czy Windows skaluje aplikację, czy aplikacja skaluje się sama), Per-Monitor-DPI (każdy monitor może mieć inną wartość) oraz strategia ImageList (przechowywać wiele rozdzielczości czy rasteryzować w czasie wykonywania).

W tym artykule celowo nie zajmujemy się debatami o projektowaniu interfejsu, lecz przedstawiamy czyste, operacyjnie bezpieczne podejście: skalować ikony w czasie wykonywania, ale tak, aby nie zamienić ich w „pixelmatsch”, zachować nienaruszone kanały alfa i umożliwić zmiany DPI bez migotania czy błędnych rozmiarów obrazów. Dodajemy też pułapki, wskazówki do debugowania oraz uczciwą ocenę, kiedy dodatkowy wysiłek ma sens.

Dlaczego powstaje „pixelmatsch“: zrozumieć łańcuch skalowania w VCL

Grafik zur Icon-Skalierung: Master-Icon wird in mehrere Zielgrößen gerastert, doppelte Skalierung führt zu Unschärfe
Jednokrotne skalowanie z jednej źródłowej grafiki jest kontrolowalne — podwójne kroki resamplingu szybko prowadzą do widocznej nieostrości.

Najczęstszą przyczyną nieostrych ikon jest jednokrotne down-/upscalowanie wykonane w niewłaściwym momencie. Klasyczny przebieg w legacy-VCL:

  • Aplikacja dostarcza ikony tylko w rozmiarach 16×16 lub 24×24.
  • Windows lub VCL skaluje je do 20×20 / 32×32 / 48×48.
  • Skalujący używa interpolacji, która dla fotografii jest akceptowalna, ale dla grafiki pikselowej rozmywa krawędzie.
  • Dodatkowo przezroczystość (kanał alfa) jest tłumiona do logiki maski lub wielokrotnie konwertowana.

Szczególnie uciążliwe jest, gdy zachodzi kilka kolejnych skalowań: na przykład gdy ImageList już dostarcza skalowany bitmap, a Windows z powodu braku świadomości DPI lub ustawień systemowych skaluje go ponownie. Efekt: podwójne zmiękczenie krawędzi.

Drugi, w projektach często niedoceniany aspekt to moment skalowania. Przy Per-Monitor-DPI (PMv2, czyli Per-Monitor-DPI-Awareness v2) efektywne DPI może się zmieniać, gdy okno przechodzi na inny monitor albo gdy klient Remote Desktop dynamicznie dostosowuje DPI. Jeśli wtedy TImageList lub cache nie zostanie poprawnie odtworzony, nagle zobaczymy ikony w niewłaściwym rozmiarze lub z nieprawidłowym rastrem.

TImageList przy High-DPI: typowe pułapki w rzeczywistych aplikacjach

TImageList jest historycznie przeznaczona do małych bitmap, z ustalonymi wymiarami, indeksami i stosunkowo sztywną logiką przechowywania. Przy High-DPI wynikają z tego praktyczne pułapki:

1) Na stałe przypisane Width/Height

Wiele formularzy VCL ustawia ImageList.Width/Height w czasie projektowania i pozostawia to bez zmian. Przy 150% Windows będzie jednak chciał np. zamienić 16 na 24. Jeśli lista pozostanie na 16, obrazy będą albo obcięte, albo gdzie indziej skalowane — oba przypadki są nieestetyczne.

2) PNG-Alpha i logika masek

W zależności od Delphi-wersji i używanych kontrolek VCL szybko trafisz w tryb mieszany: PNG z kanałem alfa są wewnętrznie przechowywane częściowo jako 32-bitowe bitmapy, częściowo jako kombinacja maski i koloru. Kiedy wykonujesz wielokrotne konwersje (PNG → bitmapa → ImageList → Draw), powstają szare halo lub ostre krawędzie. Efekt często zależy od tła: na ciemnym pasku narzędzi wygląda gorzej niż na jasnym panelu.

3) Zmiana DPI w czasie działania: Caches, Handles, OwnerDraw

Niektóre kontrolki cachują renderowanie obrazów lub przejmują uchwyty ImageList w momencie, gdy DPI nie jest jeszcze ostateczne. Szczególnie na Toolbars, TreeViews/ListViews i w scenariuszach OwnerDraw po zmianie DPI sporadycznie widać nieprawidłowe rozmiary obrazków lub puste ikony, dopóki nie nastąpi repaint lub RecreateWnd.

4) Terminalserver i Remote-Desktop jako test rzeczywisty

Jeśli aplikacja jest używana przez RDP, zmiany DPI i ponowne nawiązywanie sesji nie są rzadkością. Właśnie tam zawodna strategia ImageList daje o sobie znać: po reconnect użytkownik widzi rozmyte ikony lub błędnie skalowane paski narzędzi, choć lokalnie wszystko było w porządku.

Czyste podejście: dostarczać kilka rozdzielczości zamiast brutalnego upscalowania

Najważniejsza decyzja jest koncepcyjna: Czy chcesz skalować ikony w czasie działania z jednej bazowej grafiki (np. 16 → 32), czy dostarczysz kilka natywnych rozdzielczości i wybierzesz odpowiednią w zależności od DPI?

W praktyce zwykle wygrywa podejście z wieloma rozdzielczościami. Upscaling ma sens, gdy źródło jest wektorowe (SVG, Icon-Font) lub gdy potrzebujesz jedynie umiarkowanych współczynników. Gdy bardzo powiększasz małą bitmapę, tracisz jakość krawędzi i zauważysz to natychmiast na współczesnych ekranach.

W świecie VCL dla tego podejścia istotne są dziś dwa elementy:

  • TImageCollection: kontener na obrazy w wielu rozmiarach/wariantach.
  • TVirtualImageList: generuje z tego w czasie działania ImageList o aktualnie potrzebnym rozmiarze i reaguje na zmiany DPI.

To nie rozwiąże wszystkich problemów, ale przesuwa odpowiedzialność we właściwe miejsce: definiujesz źródła obrazów w sposób uporządkowany, a skalowanie i wybór odbywa się konsekwentnie.

Skalowanie ikon VCL High-DPI w czasie działania: kiedy ma sens (a kiedy nie)

Istnieją uzasadnione powody do skalowania ikon w czasie działania:

  • Ładujesz ikony dynamicznie (np. z folderu wtyczek, z pakietów brandingu klienta, z pakietów konfiguracyjnych).
  • Chcesz zunifikowaną pipeline dla różnych źródeł (ICO, PNG, SVG) i nie chcesz wiązać wszystkich wariantów w czasie kompilacji.
  • Generujesz ikony programowo (oznaczenia statusu, nakładki, symbole złożone).

Skalowanie w czasie wykonywania nie ma sensu, jeśli tak naprawdę używasz jedynie klasycznych ikon paska narzędzi z ustalonego zestawu. Najmniej pracochłonną drogą jest dostarczyć kilka rozdzielczości i pozwolić VCL je wybrać.

Jeśli stosujesz skalowanie w czasie wykonywania, rób to według jasnych zasad:

  • Nie skalować ponownie bitmapy, która została już wcześniej przeskalowana. Zawsze wychodzić od źródła głównego (najlepiej wektorowego lub o wysokiej rozdzielczości).
  • Cache dla każdej docelowej wielkości i DPI, w przeciwnym razie przy każdym paint będziesz skalować na nowo — to obciąża CPU i może powodować zacinanie.
  • Zachować kanał alfa: minimalizować konwersje, używać 32-bitowego RGBA, nie rasteryzować tła.

Pragmatyczna architektura: pipeline ikon jako osobny moduł

W większych aplikacjach warto nie rozpraszać tego tematu po całym kodzie, lecz zbudować niewielką pipeline. Nie musi to być framework — raczej wyraźny obszar odpowiedzialności:

  • Źródło ikon: Skąd pochodzą master-assety (zasoby, pliki, baza danych, API)?
  • Rasterizer/Scaler: Jak z mastera tworzone są obrazy w docelowych rozmiarach (interpolacja, ewentualne renderowanie SVG)?
  • Cache: Klucz (Icon-ID, docelowe piksele, DPI, motyw) i cykl życia (unieważnianie przy zmianie DPI, zmianie motywu, zmianie pakietu).
  • Adapter konsumenta: Jak wynik trafia do struktur VCL (TImageList/TVirtualImageList, OwnerDraw, PaintBox)?

Zaletą: możesz odtwarzać i debugować błędy DPI w jednym miejscu, zamiast szukać w 40 formularzach, gdzie ponownie dochodzi do skalowania.

Prawidłowe obsługiwanie zmiany DPI: zdarzenia, przebudowa, ponowne rysowanie

Notebook mit zwei Monitoren unterschiedlicher DPI, Fensterwechsel zeigt unterschiedliche Icon-Grf6dfen in einer Desktop-App
Per-Monitor-DPI to test rzeczywistości: przy zmianie monitora pipeline ikon musi zostać ponownie zrasteryzowana, nie tylko ponownie narysowana.

W ramach Windows zmiany DPI mają własny cykl życia. W VCL, w zależności od wersji i ustawień DPI-Awareness, istnieje kilka zdarzeń/mechanizmów, ale zasada jest ta sama:

  • Jeśli DPI okna się zmieni, zasoby oparte na obrazach, które mają być pikselowo precyzyjne, muszą zostać ponownie dostarczone/przygotowane.
  • Jeśli dynamicznie wypełniasz ImageListy, samo wywołanie Invalidate często nie wystarczy — potrzebujesz Rebuild obrazów w nowym rozmiarze docelowym.

Praktyczny wzorzec: przy zmianie DPI (np. skalowanie formularza/przełączenie monitora) unieważniasz cache ikon dla tej DPI i odbudowujesz dotknięte ImageListy. Ważne, by nie skalować w zdarzeniach Paint, lecz w kontrolowanym bloku aktualizacji (Toolbar.BeginUpdate/EndUpdate, wyłączenie ListView-Redraw, potem włączenie). Dzięki temu unikniesz migotania i niedokończonych stanów UI.

Dlaczego TImageList tak często staje się nieostry: interpolacja, zaokrąglanie DPI, krawędzie

Gdy raz zrozumiesz, że problemem nie jest DPI, lecz interpolacja plus zaokrąglanie, wiele efektów da się wyjaśnić:

  • Zaokrąglanie DPI: 125% 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.
  • Filtry resamplingu: Bilinear/Bicubic macht Kanten weicher. Das ist für Fotos ok, für Icons oft nicht.
  • Efekty subpikselowe: Windows kann je nach Renderpfad Subpixel-Antialiasing nutzen oder nicht. Bei Icons willst du kontrollierte Kanten — und möglichst keine mehrfachen Filterstufen.
  • Wenn du Raster-Icons hast, ist es in vielen Teams gängige Praxis, pro Zielgröße eigene PNGs zu liefern (16/20/24/32/40/48). Klingt nach viel, ist aber oft weniger Aufwand als jahrelang „warum sieht das auf diesem Monitor komisch aus“ zu debuggen.

    Debugging: Pixelmatsch reproduzierbar machen statt nach Bauchgefühl zu fixen

    Arbeitsplatz mit Debug-Notizen für Icon-Skalierung und Hintergrundtests zur Alpha-Kante
    Gute DPI-Debugging-Routinen testen Größen, Hintergründe und Rebuild-Zeitpunkte — nicht nur den ersten Screenshot.

    High-DPI-Bugs wirken oft zufällig. Mit ein paar Checks werden sie deterministisch:

    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.

    Ważne: Jeśli równolegle utrzymujesz starą i nową Pipeline, udokumentuj jasne zasady. W przeciwnym razie powstanie mieszany krajobraz, w którym niektóre ikony będą ostre, a inne będą widocznie rozmyte.

    Wydajność i pamięć: skalowanie w czasie wykonywania bez efektów ubocznych

    Skalowanie kosztuje CPU i pamięć. W oprogramowaniu biznesowym rzadko to przeszkadza w stanie bezczynności, ale przy przeciąganiu okna na nowy monitor lub przy starcie z wieloma formularzami może wystąpić przycinanie. Trzy praktyczne wytyczne:

    • Ogranicz rozmiary cache: Nie przechowuj każdej pośredniej wersji na zawsze. Jeśli potrzebujesz tylko 100% i 150%, buforuj tylko te.
    • Lazy Build: Rasteryzuj ikony dopiero, gdy ekran ich rzeczywiście potrzebuje. Przy dużych menu oszczędza to czas uruchamiania.
    • Batch-Rebuild: Przy zmianie DPI nie wywołuj odświeżania dla każdej kontrolki osobno. Centralny rebuild zapobiega nadmiarowemu skalowaniu.

    Wenn du mit TVirtualImageList arbeitest, ist vieles davon bereits als Konzept enthalten — aber du musst trotzdem darauf achten, nicht zusätzlich noch eigene Skalierung oben drauf zu bauen.

    Fallback-Strategien: Co robić, gdy nie wszystkie rozmiary ikon są dostępne?

    W praktyce nie zawsze masz wszystkie zasoby we wszystkich rozmiarach. Potrzebujesz wtedy jasnej strategii fallback, aby uniknąć przypadkowych rezultatów:

    • Prefer Downscale: Lepiej przeskalować w dół z 64 px do 32 px niż powiększać z 16 px do 32 px.
    • Zdefiniuj poziomy: Określ, jakie docelowe rozmiary faktycznie wspierasz (np. 16/20/24/32/40/48) i odwzoruj na nie DPI w sposób jednoznaczny.
    • Testuj przezroczystość: Przy fallbackach zwracaj szczególną uwagę na kanał alfa — to właśnie tam powstają halo.

    Typowym błędem jest wzięcie dowolnego następnego rozmiaru. W efekcie postrzegana ostrość będzie różna w zależności od ikony. Lepszy jest rygorystyczny, udokumentowany plan mapowania.

    Kiedy wysiłek naprawdę się opłaca?

    Istnieją trzy wyraźne wskaźniki, że opłaca się uporządkowana High-DPI-icon-pipeline:

    • Twoi użytkownicy pracują na mieszanych monitorach (laptop + monitor zewnętrzny) lub często korzystają przez RDP.
    • Aplikacja jest długowieczna i będzie utrzymywana przez lata — percepcja interfejsu użytkownika jest elementem akceptacji.
    • Masz w planach kroki modernizacyjne (DPI-Awareness podnieść, wymiana kontrolek, przeprojektowanie układu paska narzędzi).

    Jeśli aplikacja działa tylko na stałym systemie kioskowym z identyczną rozdzielczością, można ograniczyć temat do minimum: dostarczyć odpowiedni rozmiar ikon, poprawnie ustawić DPI-Awareness i gotowe.

    Wniosek: High-DPI to nie detal kosmetyczny, lecz decyzja renderowania

    Niezbyt ostre ikony w VCL rzadko są izolowanym błędem, zwykle wskazują na nieczyste ogniwo w łańcuchu źródła, skalowania i cachowania. Najbardziej solidnym podejściem jest dostarczenie ikon w kilku rozdzielczościach i konsekwentne serwowanie ich przez centralną Pipeline (np. ImageCollection/VirtualImageList lub własną warstwę ikon). Skalowanie w czasie wykonywania ma sens, gdy masz dynamiczne źródła lub symbole składane — ale tylko z master-źródłem, cache’em opartym na DPI i jasnymi regułami rebuildu.

    Jeżeli masz konkretne symptomy (zmiana DPI powoduje uszkodzenie ikon, halo na krawędziach, nieprawidłowe rozmiary po RDP), warto celowo odizolować temat i potraktować go jako mały element architektury, zamiast nakładać obejścia per formularz. Jeśli potrzebujesz wsparcia przy debugowaniu lub stopniowej modernizacji, znajdziesz tu odpowiedni punkt startowy: Kontakt do Net-Base Software GmbH.

    W kontekście tego zagadnienia istotne są także Timagelist High Dpi oraz Delphi Vcl Dpi-Awareness. Artykuł przedstawia te aspekty w sposób zrozumiały i pokazuje, na co zwracać uwagę w codziennej pracy.

    Omów projekt lub przedsięwzięcie modernizacyjne z Net-Base.

    Następny krok

    Jeżeli temat stanie się rzeczywistym projektem, architektura, stan istniejący i eksploatacja powinny być rozpatrywane razem na wczesnym etapie.

    Wspieramy nie tylko w pojedynczych zagadnieniach, lecz także wtedy, gdy z fragmentów kodu źródłowego, kwestii związanych z systemami legacy lub koncepcji portalu ma powstać solidny projekt dla przedsiębiorstwa.

    • Stan istniejący, obraz docelowy i ryzyka techniczne są oceniane łącznie.
    • REST, dostęp do danych, portale i Rollout nie będą przesuwane na później.
    • Wcześnie widzą Państwo, która droga jest ekonomicznie i operacyjnie wykonalna.

    Udostępnij wpis

    Udostępnij ten wpis bezpośrednio

    LinkedIn, X, XING, Facebook, WhatsApp i e-mail są natychmiast dostępne. Dla Instagrama przygotowujemy bezpośrednio link i krótki tekst.

    E-mail

    Instagram otwiera się w nowej karcie. Link i krótki tekst są wcześniej kopiowane do schowka.