Net-Base Списание

18.08.2026

VCL High-DPI: Скалиране на икони по време на изпълнение и избягване на размазване на пикселите в TImageList

High-DPI в VCL не е просто отметка, а верига от настройка на ImageList, събития при промяна на DPI и коректно рендиране. Този практически текст показва как да мащабираш иконите по време на изпълнение, да предотвратиш артефакти и да дебъгваш по възпроизведим начин типичните капани на TImageList.

18.08.2026

От темата в списанието към проектната практика

Подходящи страници за услуги и технологии към публикацията

Който поддържа VCL-приложения на модерни Windows-клиенти, рано или късно се сблъсква със същия симптом: иконите изглеждат размазани, с износени ръбове или добиват сив кант при 125%/150%/200%. Точно тук темата VCL High-DPI Icons става практична: не защото High-DPI е ново, а защото проблемите обикновено изплуват в ежедневието — на терминални сървъри, при смяна на дока на лаптопа или веднага щом всеки монитор има различно DPI.

Основният проблем почти никога не е „PNG-то е счупено“, а pipeline-ът: откъде идва иконата (ресурс, файл, SVG, шрифт), в какъв размер се предоставя, как попада в TImageList и кой кога и как я скалира. В VCL се срещат няколко понятия, които трябва да се разграничат: DPI-Awareness (дали Windows или самото приложение извършва скалирането), Per-Monitor-DPI (всеки монитор може да е различен) и ImageList-Strategie (поддържане на няколко резолюции или растеризация при изпълнение).

В тази статия умишлено не навлизаме в дебати за UI дизайн, а представяме чист, оперативно надежден подход: скалиране на иконите по време на изпълнение, но така, че те да не се превръщат в пикселна каша, да се запазят alpha-каналите и смяната на DPI да работи без мерцане или грешни размери. Към това добавяме капани, насоки за отстраняване на грешки и честна оценка кога допълнителната работа си заслужава.

Защо възниква пикселна кашa: разбиране на веригата на скалиране в VCL

Grafik zur Icon-Skalierung: Master-Icon wird in mehrere Zielgrf6dfen gerastert, doppelte Skalierung ffchrt zu Unsche4rfe
Еднократното скалиране от master-източник е контролируемо — двойните стъпки на ресемплиране бързо водят до видимо размазване.

Най-честата причина за неясни икони е еднократно down-/upscaling в неправилния момент. Класически поток в Legacy-VCL:

  • Приложението доставя икони само в 16×16 или 24×24.
  • Windows или VCL ги скалира до 20×20 / 32×32 / 48×48.
  • Скалерът използва интерполация, която за снимки е приемлива, но за пикселна графика размазва ръбовете.
  • Освен това прозрачността (alpha) се натъпква в логика с маски или се конвертира няколко пъти.

Особено неприятно става, когато се извършат няколко скалирания последователно: например когато ImageList вече връща скалиран битмап, а Windows поради липса на DPI-awareness или заради системното DPI го скалира отново. Резултатът: двойно размазване.

Вторият, в проекти често подценяван аспект е моментът на скалиране. При Per-Monitor-DPI (PMv2, т.е. Per-Monitor-DPI-Awareness v2) ефективното DPI може да се промени, когато прозорецът смени монитора или когато Remote-Desktop клиент динамично коригира DPI. Ако тогава TImageList или кеш не бъдат правилно презаредени, изведнъж ще виждате икони в грешен размер или с грешна мрежа.

TImageList unter High-DPI: typische Fallen in echten Anwendungen

TImageList исторически е предназначен за малки растерни изображения, с фиксирани размери, индекси и относително твърда логика на съхранение. При High-DPI това води до практични капани:

1) Фиксирани Width/Height

Много VCL форми задават ImageList.Width/Height по време на дизайн и не го променят. При 150% Windows обаче може да иска например от 16×16 да получи 24×24. Ако списъкът остане 16×16, изображението или се изрязва, или се мащабира на друго място — и двете са неприемливи.

2) PNG‑алфа и логика с маски

В зависимост от Delphi-версията и VCL контролите лесно се получава смесен режим: PNG с алфа каналът понякога се държи като 32‑битова Bitmap, понякога като Mask+Color. При многократни конверсии (PNG -> Bitmap -> ImageList -> Draw) възникват сиви ореоли или твърди ръбове. Ефектът често зависи от фона: на тъмна тулбар икони изглеждат по-зле, отколкото на светъл панел.

3) Промени на DPI по време на изпълнение: кешове, handles, OwnerDraw

Някои контроли кешират представянето на изображението или поемат ImageList‑handles в момент, когато DPI все още не е финално установено. Особено при Toolbars, TreeViews/ListViews и OwnerDraw сценарии след промяна на DPI понякога се виждат неправилни размери на изображенията или празни икони, докато не се извърши Repaint или RecreateWnd.

4) Terminalserver и Remote‑Desktop като тест за реални условия

Ако приложението се използва по RDP, промени на DPI и възстановяване на сесии не са екзотика. Именно там неустойчива стратегия с ImageList се проваля: след reconnect потребителят вижда размазани икони или неправилно скалирани тулбарове, въпреки че локално всичко е било наред.

Чист подход: предоставяне на няколко резолюции вместо грубо мащабиране

Най‑важното решение е концептуално: Искаш ли да мащабираш иконите в движение от една базова графика (например 16×16 -> 32×32), или ще предоставиш няколко нативни резолюции и ще избираш подходящата според DPI?

На практика почти винаги печели варианта с няколко резолюции. Мащабирането е приемливо, ако източникът е векторен (SVG, Icon‑Font) или ако наистина ти трябват само умерени фактори. Веднага щом мащабираш силно от малка растерна графика, губиш качеството на ръбовете и това се вижда веднага на съвременните дисплеи.

В VCL‑среда за този подход днес са релевантни два компонента:

  • TImageCollection: контейнер за изображения в няколко размера/варианта.
  • TVirtualImageList: генерира от тях по време на изпълнение една ImageList в актуално необходимия размер и реагира на DPI‑промени.

Това не решава всички проблеми, но премества решението на правилното място: дефинираш източниците на изображения чисто, а скалирането/изборът се извършва консистентно.

Мащабиране на VCL High‑DPI икони в движение: кога е разумно (и кога не)

Има легитимни причини да мащабираш икони по време на изпълнение:

  • Зареждаш икони динамично (например от папка с плъгини, клиентски пакети за брандиране, конфигурационни пакети).
  • Искаш единна обработваща верига за различни източници (ICO, PNG, SVG) и не желаеш да привързваш всички варианти към изграждането.
  • Генерираш икони програмно (status‑badges, overlays, комбинирани символи).

Няма смисъл от скалиране по време на изпълнение, ако всъщност имаш само класически икони за тулбара от фиксиран набор. В този случай най-малко трудоемкият за поддръжка подход е: доставяне на няколко резолюции и оставяне на VCL да избере.

Ако правиш скалиране по време на изпълнение, прави го с ясни правила:

  • Никога не скалирай повторно от вече оразмерен Bitmap. Винаги тръгвай от един master-източник (по възможност векторен или с висока резолюция).
  • Кеширай за целева големина и DPI, в противен случай ще скалираш при всеки Paint — това натоварва CPU и може да предизвика заекване.
  • Запази алфа: минимизирай конверсиите, използвай 32-bit RGBA, не растеризирай фона.

Прагматична архитектура: Icon-Pipeline като отделен компонент

В по-големи приложения има смисъл да не разпръскваш темата навсякъде, а да изградиш малка пайплайн. Това не трябва да е framework — по-скоро ясен обхват на отговорност:

  • Icon-Quelle: Откъде идват master-активите (ресурси, файлове, база данни, API)?
  • Rasterizer/Scaler: Как се създава целевият размер от master (интерполация, евентуално SVG-Render)?
  • Cache: ключ (Icon-ID, целеви пиксели, DPI, Theme) и жизнен цикъл (инвалидиране при DPI-Wechsel, Theme-Wechsel, Paketwechsel).
  • Consumer-Adapter: Как резултатът попада в VCL-Strukturen (TImageList/TVirtualImageList, OwnerDraw, PaintBox)?

Предимство: можеш да дебъгнеш DPI-Bugs възпроизводимо на едно място, вместо да търсиш „къде пак се скалира“ в 40 Forms.

DPI-Wechsel korrekt behandeln: Ereignisse, Rebuild, Repaint

Лаптоп с два монитора с различни DPI, смяната на прозорец показва различни размери на иконите в десктоп приложение
Per-Monitor-DPI е тестът на реалността: при смяна на монитор Icon-Pipeline трябва да бъде повторно растеризирана, не само прерисувана.

Под Windows смяната на DPI е собствен жизнен цикъл. В VCL в зависимост от версията и DPI-Awareness има няколко Ereignisse/Mechanismen, но основният принцип остава:

  • Когато DPI на прозореца се промени, базираните на изображения ресурси, които трябва да са пиксел-точни, трябва да бъдат преосигурени.
  • Ако напълваш ImageLists динамично, едно просто Invalidate често не е достатъчно — нуждаеш се от Rebuild на Images в новата целева големина.

Практичен шаблон е: при промяна на DPI (напр. Form-Scale/Monitorwechsel) инвалидираш Icon-Cache за тази DPI и реконструираш засегнатите ImageLists. Важно е да не се скалира в Paint-Events, а в контролиран Update-Block (Toolbar.BeginUpdate/EndUpdate, ListView-Redraw изключен, после включен). Така се избягва трептене и недовършени UI-Zustände.

Warum TImageList so oft unscharf wird: Interpolation, DPI-Rundung, Kanten

Когато веднъж разбереш, че проблемът не е самият DPI, а интерполацията плюс закръгляването, много ефекти се обясняват:

  • 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.
  • Филтри за ресемплиране: Bilinear/Bicubic macht Kanten weicher. Das ist für Fotos ok, für Icons oft nicht.
  • Субпикселни ефекти: 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.

    Отстраняване на грешки: направете пикселното замазване възпроизводимо, statt да го поправяте по усет

    Работно място с бележки за дебъг относно скалиране на икони и тестове на фона за алфа-ръб
    Добри DPI-Debugging-Routinen тестen Größen, Hintergrunde 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

    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 px 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.

    Миграция в съществуващи приложения: стъпково 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:

    • Инвентаризация: Welche ImageLists gibt es? Welche Controls nutzen sie? Welche Größen werden erwartet?
    • Приоритизиране: Zuerst die prominentesten Flächen (Haupttoolbar, Navigation, Kontextmenüs).
    • Единен източник: 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.

    Важно: Ако работиш паралелно със стара и нова pipeline, документирай ясни правила. В противен случай се получава смесена среда, в която някои икони са остри, а други изглеждат видимо размазани.

    Производителност и памет: мащабиране по време на работа без странични ефекти

    Мащабирането струва CPU и памет. В бизнес-софтуер това рядко се забелязва в спокойно състояние, но при преместване на прозореца на друг монитор или при стартиране с много формуляри може да има трептене. Три практични водещи принципа:

    • Ограничаване на cache-големините: Не пази всяка междинна версия вечно. Ако ти трябват само 100% и 150%, кеширай само тези.
    • Lazy Build: Растерирай иконите едва когато екранът наистина ги показва. При големи менюта това спестява време при стартиране.
    • Batch-Rebuild: При смяна на DPI не тригерирай всяко контролче поотделно. Централен rebuild предотвратява излишно мащабиране.

    Ако използваш TVirtualImageList, много от тези идеи вече са вградени като концепция — но все пак трябва да внимавш да не надграждаш с допълнителни собствени слоеве за мащабиране.

    Fallback-стратегии: Какво правиш, когато не разполагаш с всички икони в нужните размери?

    В реалността не винаги имаш всички assets във всички размери. Тогава ти трябва ясна fallback-стратегия, за да избегнеш случайни или непредсказуеми резултати:

    • Prefer Downscale: По-добре е да намалиш от 64 px до 32 px, отколкото да увеличаваш от 16 px до 32 px.
    • Определи стъпки: Ясно дефинирай кои целеви размери наистина поддържаш (напр. 16/20/24/32/40/48) и мапвай DPI спрямо тях.
    • Тествай прозрачността: При fallback-ове обърни специално внимание на алфа-канала — именно там се появяват halos.

    Често срещана грешка е да се вземе просто някакъв „следващ“ размер. Това кара възприеманата острота да варира в зависимост от иконата. По-добре е да имаш строг, документиран mapping план.

    Кога усилията наистина си струват?

    Има три ясни индикатора, че си струва да направиш солидна High-DPI-иконна pipeline:

    • Потребителите ти работят с микс от монитори (лаптоп + външен) или често използват RDP.
    • Приложението е дългоживотно и се поддържа през години — възприятие на UI е част от приемането.
    • Вече имаш планирани модернизации (вдигане на DPI-awareness, смяна на контроли, преработка на layout-а на тулбара).

    Ако приложението пък работи само на фиксирана киоск-система с една и съща резолюция, темата може да се държи минимално: достави подходящ размер икони, настрои DPI-awareness правилно и толкова.

    Заключение: High-DPI не е козметичен детайл, а решение за рендеринг

    Размазани икони в VCL рядко са изолиран проблем; те подсказват за нездрава верига от източници, мащабиране и кеширане. Най-устойчивият подход е да предоставяш икони в няколко резолюции и да ги доставяш консистентно през централен pipeline (напр. ImageCollection/VirtualImageList или собствен иконен слой). Мащабирането в изпълнение е оправдано при динамични източници или композитни символи — но само с master-източник, DPI-базиран кеш и ясни правила за rebuild.

    Ако имаш конкретни симптоми (смяна на DPI поврежда иконите, halos по ръба, грешни размери след RDP), си струва темата да се изолира целенасочено и да се третира като малък архитектурен модул, вместо да се трупат workaround-и по формуляри. Ако ти трябва поддръжка при дебъгване или при поетапна модернизация, тук е подходящата отправна точка: Kontakt zur Net-Base Software GmbH.

    За тази тема са важни също Timagelist High Dpi и Delphi Vcl Dpi-Awareness. Статията систематизира тези аспекти по разбираем начин и показва на какво да се обръща внимание в ежедневната работа.

    Обсъдете проект или инициатива за модернизация с Net-Base.

    Следваща стъпка

    Когато темата прерасне в реален проект, архитектурата, съществуващите активи и експлоатацията трябва да се разглеждат заедно още в ранния етап.

    Подпомагаме не само при отделни въпроси, но и когато от фрагменти от изходен код, проблеми с наследени системи или идеи за портал трябва да бъде реализиран надежден корпоративен проект.

    • Сегашното състояние, целевото състояние и техническите рискове се оценяват съвместно.
    • REST, достъпът до данни, порталите и разгръщането не се отлагат като по-късни последващи задачи.
    • Вие виждате навреме кой път е икономически и оперативно жизнеспособен.

    Сподели публикацията

    Споделете тази публикация директно

    LinkedIn, X, XING, Facebook, WhatsApp и електронна поща са налични веднага. За Instagram подготвяме директно връзка и кратък текст.

    Електронна поща

    Instagram се отваря в нов раздел. Връзката и краткият текст се копират предварително в клипборда.