Net-Base Revistă

18.08.2026

VCL High-DPI: scalarea icoanelor la runtime și evitarea estompării pixelilor în TImageList

High-DPI în VCL nu este doar o bifă, ci un lanț format din configurarea ImageList, evenimentele de schimbare a DPI și un rendering curat. Acest articol practic arată cum să redimensionezi icoanele la runtime, să eviți artefactele și să depanezi în mod reproductibil capcanele tipice ale TImageList.

18.08.2026

De la tema din revistă la practica în proiecte

Pagini relevante de servicii și pagini tehnice pentru articol

Cei care rulează aplicații VCL pe clienți moderni Windows întâmpină, mai devreme sau mai târziu, același simptom: iconițele par, la 125%/150%/200%, neclare, zimțate sau capătă o margine gri. Exact aici devine practic subiectul VCL High-DPI Icons: nu pentru că High-DPI ar fi nou, ci pentru că problemele apar de obicei în operare — pe terminal servere, la schimbarea între laptop și dock sau atunci când fiecare monitor are DPI diferite.

Problema centrală este rar „PNG-ul este stricat”, ci pipeline-ul: de unde provine iconița (resursă, fișier, SVG, font), în ce dimensiune este furnizată, cum ajunge în TImageList și cine scalează, când și cum. În VCL apar mai mulți termeni pe care trebuie să-i distingem: DPI-Awareness (dacă Windows scalează aplicația sau aplicația se scalează singură), Per-Monitor-DPI (fiecare monitor poate avea DPI diferit) și ImageList-Strategie (păstrarea mai multor rezoluții sau rasterizarea la runtime).

În acest articol nu discutăm despre dezbateri de design UI, ci despre o abordare curată, sigură pentru operare: scalarea iconițelor la rulare, dar astfel încât să nu devină pastă de pixeli, ca canalele Alpha să rămână intacte și ca schimbările DPI să funcționeze fără pâlpâiri sau dimensiuni de imagine greșite. Vom acoperi și capcanele, indicații de debugging și o evaluare sinceră când merită efortul suplimentar.

De ce apar pixele estompate: înțelegerea lanțului de scalare în VCL

Grafik zur Icon-Skalierung: Master-Icon wird in mehrere Zielgrf6dfen gerastert, doppelte Skalierung ffchrt zu Unsche4rfe
O scalare unică dintr-o sursă master poate fi controlată — pașii de resampling în dublu rând duc rapid la neclaritate vizibilă.

Motivul cel mai frecvent pentru iconițele neclare este un Down-/Upscaling unic la momentul nepotrivit. Flux clasic în VCL legacy:

  • Aplicația furnizează iconițe doar la 16×16 sau 24×24.
  • Windows sau VCL scalează la 20×20 / 32×32 / 48×48.
  • Scalerul folosește o interpolare care e ok pentru fotografii, dar pentru grafică pe pixeli estompează muchiile.
  • În plus, transparența (Alpha) este forțată într-o logică de mască sau convertită de mai multe ori.

Devine deosebit de problematic când au loc mai multe scalări succesive: de exemplu când ImageList deja furnizează un bitmap scalat, iar Windows din cauza DPI-Unaware sau System-DPI îl scalează din nou. Rezultat: estompare dublă.

Un alt punct subestimat în proiecte este momentul scalării. La Per-Monitor-DPI (PMv2, adică Per-Monitor-DPI-Awareness v2) DPI-ul efectiv se poate schimba când o fereastră trece pe alt monitor sau când un client Remote Desktop ajustează DPI-ul dinamic. Dacă apoi o TImageList sau un cache nu este reconstruit corect, vei vedea brusc iconițe în dimensiuni greșite sau cu rasterul greșit.

TImageList unter High-DPI: typische Fallen in echten Anwendungen

TImageList a fost istoric gândită pentru bitmapuri foarte mici, cu dimensiuni fixe, indici și o logică de stocare relativ rigidă. În contextul High-DPI apar din aceasta capcane practice:

1) Width/Height fixate

Multe formulare VCL setează ImageList.Width/Height la timpul de design și rămân așa. La 150% Windows ar vrea, de ex., să transforme 16 în 24. Dacă lista rămâne la 16, imaginea fie este decupată, fie este scalată în altă parte — ambele variante sunt inestetice.

2) PNG-Alpha și logica măștilor

În funcție de versiunea Delphi și de controalele VCL, ajungi rapid într-un regim mixt: PNG-urile cu canal alpha sunt intern tratate uneori ca bitmapuri pe 32 de biți, alteori ca Mask+Color. De îndată ce faci conversii succesive (PNG -> Bitmap -> ImageList -> Draw), apar halouri gri sau muchii dure. Efectul depinde frecvent de fundal: pe o bară de unelte întunecată arată mai rău decât pe un panou deschis la culoare.

3) Schimbări DPI la runtime: cache-uri, handle-uri, OwnerDraw

Unele controale păstrează în cache redarea imaginilor sau preiau ImageList-Handles într-un moment în care DPI-ul nu este încă final. În special la Toolbars, TreeViews/ListViews și în scenarii OwnerDraw se observă, după schimbări DPI, dimensiuni de imagine incorecte sau pictograme goale, până când are loc un Repaint sau un RecreateWnd.

4) Terminalserver și Remote-Desktop ca test de realitate

Dacă aplicația este folosită prin RDP, schimbările DPI și reconnect-urile de sesiune nu sunt rare. Tocmai acolo o strategie ImageList nefiabilă îți creează probleme: utilizatorul vede, după reconectare, pictograme estompate sau bare de instrumente scalate greșit, deși local totul părea în regulă.

Abordare curată: oferă mai multe rezoluții în loc de upscaling brutal

Decizia cea mai importantă este conceptuală: Dorești să redimensionezi la runtime icoane dintr-o singură grafică de bază (de ex. 16 -> 32), sau pui la dispoziție mai multe rezoluții native și alegi în funcție de DPI pe cea potrivită?

În practică câștigă aproape întotdeauna varianta cu mai multe rezoluții. Upscaling se poate folosi când sursa este vectorială (SVG, Icon-Font) sau când ai nevoie doar de factori moderați. De îndată ce mărești puternic dintr-o imagine raster mică pierzi calitatea muchiilor, iar acest lucru se vede imediat pe ecrane moderne.

În lumea VCL două componente relevante pentru această abordare sunt astăzi:

  • TImageCollection: container pentru imagini în mai multe dimensiuni/variante.
  • TVirtualImageList: generează din acestea, la runtime, o ImageList în dimensiunea necesară și răspunde la schimbările DPI.

Aceasta nu rezolvă toate problemele, dar mută decizia în locul potrivit: definești clar sursele imaginilor, iar scalarea/seleția se întâmplă consecvent.

Scalarea icoanelor VCL High-DPI la runtime: când are sens (și când nu)

Există motive valide pentru a scala icoane la runtime:

  • Încarci icoane dinamic (de ex. dintr-un folder de pluginuri, pachete de branding specifice clientului, pachete de configurare).
  • Dorești o pipeline unificată pentru surse diferite (ICO, PNG, SVG) și nu vrei să legi toate variantele la build-time.
  • Generezi icoane programatic (status badges, overlays, simboluri compuse).

Nu este justificată scalarea la runtime dacă ai, de fapt, doar icoane clasice de toolbar dintr-un set fix. În acest caz, cea mai puţin costisitoare soluţie din punct de vedere al mentenanţei este să livrezi curat mai multe rezoluţii şi să laşi VCL să le aleagă.

Dacă aplici scalarea la runtime, atunci respectă reguli clare:

  • Niciodată nu scala dintr-un bitmap deja scalat. Porneşte întotdeauna de la o sursă master (ideal vectorială sau la rezoluţie înaltă).
  • Cache pe dimensiunea ţintă şi DPI, altfel vei scala la fiecare paint — asta consumă CPU şi poate provoca sacadări.
  • Păstrează alpha: minimizează conversiile, foloseşte 32-bit RGBA, nu aplati/flatten-ui fundalul la rasterizare.

Arhitectură pragmatică: pipeline de icoane ca componentă separată

În aplicaţii mai mari merită să nu împrăştii problema peste tot, ci să construieşti o mică pipeline. Nu trebuie un framework — mai degrabă un domeniu clar de responsabilitate:

  • Sursa de icoane: De unde vin master‑asset‑urile (resurse, fişiere, bază de date, API)?
  • Rasterizer/Scaler: Cum se generează din master dimensiunile ţintă (interpolare, eventual render SVG)?
  • Cache: Cheie (Icon‑ID, pixeli ţintă, DPI, theme) şi ciclul de viaţă (invalidare la schimbare DPI, schimbare theme, schimbare pachet).
  • Consumer‑Adapter: Cum ajunge rezultatul în structurile VCL (TImageList/TVirtualImageList, OwnerDraw, PaintBox)?

Avantaj: poţi reproduce şi debuga erorile DPI la un singur loc, în loc să cauţi în 40 de formulare unde se mai scale‑uieşte.

Tratarea corectă a schimbării DPI: evenimente, rebuild, repaint

Laptop cu două monitoare cu DPI diferite, schimbarea ferestrei arată dimensiuni diferite ale iconiţelor într-o aplicaţie desktop
DPI per‑monitor este testul de realitate: la schimbarea monitorului pipeline‑ul de icoane trebuie să rastroreze din nou, nu doar să redea.

La Windows schimbările de DPI sunt un ciclu de viaţă separat. În VCL, în funcţie de versiune şi DPI‑awareness, există mai multe evenimente/mecanisme, dar principiul de bază rămâne:

  • Dacă DPI ale ferestrei se modifică, resursele bazate pe imagini care trebuie să fie pixel‑perfect trebuie reprovizionate.
  • Dacă umpli ImageLists dinamic, un simplu Invalidate de cele mai multe ori nu este suficient — ai nevoie de un Rebuild al imaginilor în noua dimensiune ţintă.

Un pattern practic este: la schimbare DPI (de exemplu Form‑Scale/trecere între monitoare) invalidezi cache‑ul de icoane pentru acea DPI şi reconstruieşti ImageList‑urile afectate. Important: nu scala în evenimentele de Paint, ci într‑un bloc de update controlat (Toolbar.BeginUpdate/EndUpdate, ListView‑Redraw oprit şi repornit). Astfel eviţi pâlpâirea şi stările UI neterminate.

De ce TImageList devine atât de des neclar: interpolare, rotunjire DPI, margini

Dacă ai înţeles că problema nu e DPI în sine, ci interpolarea plus rotunjirea, multe efecte se pot explica:

  • Rotunjirea DPI: 125a0% nu este un dublu curat. Din 16 px rezulta 20 px (16 * 1,25). Din 24 px rezulta 30 px. Sunt valori nepotrivite care complică marginile pixelilor.
  • Filtre de resampling: Bilinear/Bicubic înmoaie marginile. Asta este în regulă pentru fotografii, dar adesea nu pentru icoane.
  • Efecte subpixel: Windows poate, în funcție de traseul de renderizare, să folosească sau nu Subpixel-Antialiasing. Pentru icoane vrei margini controlate și, pe cât posibil, niciun strat de filtrare multiplă.

Dacă folosești icoane raster, în multe echipe este practică uzuală să livrezi PNG-uri separate pentru fiecare dimensiune țintă (16/20/24/32/40/48). Sună mult, dar de multe ori este mai puțin efort decât să depanezi ani de zile „de ce arată ciudat pe acest monitor”.

Debugging: faceți reproducibil pixel-mess-ul în loc să-l remediați după instinct

Arbeitsplatz mit Debug-Notizen ffcr Icon-Skalierung und Hintergrundtests zur Alpha-Kante
Rutinile bune de DPI-debugging testează dimensiuni, fundaluri și momentele de rebuild — nu doar primul screenshot.

Bug-urile High-DPI par adesea întâmplătoare. Cu câteva verificări le faci deterministe:

1) Logghează DPI și dimensiunile ImageList la runtime

Loghează la pornire și la schimbarea DPI: CurrentPPI al form-ului, Screen.PixelsPerInch (Atenție: acesta poate fi DPI-ul sistemului), precum și ImageList.Width/Height ale listelor afectate. Dacă după schimbarea monitorului vezi încă 16, deși aștepți 32, cauza este clară: lipsește rebuild-ul sau acesta vine prea târziu.

2) Mărirea vizibilă a icoanelor

Un test rapid este să setezi temporar icoanele din toolbar la 48 px. Scalarea proastă sare imediat în evidență. Pipeline-urile bine puse păstrează claritatea și la 48 px, pentru că rastrarează dintr-o sursă adecvată.

3) Testează schimbări de theme și fundal

Halouri la margine sunt adesea probleme de Alpha/Premultiply. Testează pe fundaluri deschise/întunecate și pe suprafețe cu gradient. Dacă marginea arată diferit în funcție de fundal, tratarea transparenței nu este corectă.

4) Remote Desktop / schimbare de monitor ca script de test

Creează un script scurt pentru QA: pornește aplicația pe Monitor A (100a0%), mută fereastra pe Monitor B (150a0%), înapoi, apoi reconnect RDP. Dacă asta rămâne stabil, multe probleme ale clienților sunt deja eliminate.

Migrarea în aplicații existente: pas cu pas în loc de Big Bang

În aplicații Delphi-VCL dezvoltate în timp, logica icoanelor se ascunde adesea în multe locuri: meniuri, Toolbars, ActionLists, TreeViews, indicatori de stare. O refactorizare de tip Big-Bang aduce risc. S-a dovedit eficient un parcurs pas cu pas:

  • Inventariere: Care ImageLists există? Ce controale le folosesc? Ce dimensiuni sunt așteptate?
  • Prioritizare: Începe cu suprafețele cele mai vizibile (Haupttoolbar, navigație, meniuri contextuale).
  • Sursă unificată: Centralizează icoanele (ImageCollection sau un loader propriu), în loc să încarci fișiere individuale pe fiecare form.
  • Teste de schimbare DPI: De la primul modul refactorizat, rulează consecvent teste de schimbare DPI.

Important: Dacă rulezi în paralel pipeline-ul vechi și pe cel nou, documentează reguli clare. Altfel apare un peisaj mixt în care unele icoane sunt clare, iar altele par vizibil neclare.

Performanță și memorie: scalare la rulare fără efecte secundare

Scalarea costă CPU și memorie. Într-un software de business acest lucru rar se observă în repaus, dar la mutarea ferestrei pe un monitor nou sau la pornire cu multe formulare poate apărea sacadare. Trei principii practice:

  • Limitează dimensiunile cache-ului: Nu păstra fiecare stare intermediară la nesfârșit. Dacă ai nevoie doar de 100% și 150%, cachează doar acestea.
  • Lazy Build: Rasterizează icoanele doar când ecranul le solicită efectiv. La meniuri mari asta economisește timpul de pornire.
  • Batch-Rebuild: La schimbarea DPI-ului nu declanșa fiecare control individual. Un rebuild central previne scalarea redundantă.

Dacă lucrezi cu TVirtualImageList, multe din acestea sunt deja incluse ca și concept, dar trebuie totuși să ai grijă să nu construiești deasupra o scalare proprie suplimentară.

Fallback-Strategien: Was tun, wenn nicht alle Icon-Größen vorliegen?

În realitate nu ai întotdeauna toate asset-urile în toate dimensiunile. Atunci ai nevoie de o strategie clară de fallback pentru a evita rezultate întâmplătoare:

  • Prefer Downscale: Mai bine să redimensionezi în jos de la 64 px la 32 px decât să faci upscaling de la 16 px la 32 px.
  • Definiere Stufen: Definește trepte: stabilește ce dimensiuni țintă susții cu adevărat (de ex. 16/20/24/32/40/48) și mapează DPI în mod clar pe ele.
  • Transparenz testen: Testează transparența: la fallback-uri acordă o atenție specială canalului alpha — exact acolo apar aureole.

O greșeală tipică este să iei pur și simplu următoarea dimensiune disponibilă. Astfel variază percepția clarității în funcție de icon. Mai bine e un plan de mapping clar și documentat.

Când merită efortul cu adevărat?

Există trei indicatori clari că merită o pipeline de iconițe High-DPI bine pusă la punct:

  • Utilizatorii tăi lucrează pe monitoare mixte (laptop + extern) sau mult prin RDP.
  • Aplicația este de lungă durată și va fi întreținută ani de zile — percepția UI face parte din acceptare.
  • Ai oricum pași de modernizare planificați (creșterea DPI-awareness, schimbarea controlelor, revizuirea layout-ului barei de instrumente).

Dacă aplicația rulează doar pe un sistem kiosk fix cu aceeași rezoluție, poți ține tema la minim: furnizează o dimensiune potrivită a iconițelor, setează DPI-awareness corect, gata.

Fazit: High-DPI ist kein kosmetisches Detail, sondern eine Rendering-Entscheidung

Icoanele neclare în VCL rar sunt o eroare izolată, ci indică un lanț necurat de surse, scalare și caching. Cea mai robustă abordare este să pui la dispoziție icoane în mai multe rezoluții și să le livrezi consistent printr-o pipeline centrală (de ex. ImageCollection/VirtualImageList sau un strat propriu de icoane). Scalarea la runtime are sens când ai surse dinamice sau simboluri compuse — dar numai cu o sursă master, cache bazat pe DPI și reguli clare de rebuild.

Dacă ai simptome concrete (schimbarea DPI deteriorează icoanele, aureole la margine, dimensiuni greșite după RDP), merită să izolezi problema și să o tratezi ca un mic bloc arhitectural, în loc să aduni workaround-uri per form. Dacă ai nevoie de suport la debugging sau la o modernizare pas cu pas, găsești aici punctul de plecare potrivit: Contact la Net-Base Software GmbH.

Pentru această temă sunt importante și Timagelist High Dpi și Delphi Vcl Dpi-Awareness. Articolul ordonează aceste aspecte într-un mod clar și arată la ce trebuie să se acorde atenție în practică.

Discutați un proiect sau o inițiativă de modernizare cu Net-Base.

Pasul următor

Dacă un subiect devine un proiect real, arhitectura, starea existentă și operarea ar trebui analizate împreună încă din faza incipientă.

Nu oferim sprijin doar pentru întrebări punctuale, ci și atunci când fragmente de cod sursă, probleme legacy sau idei de portal trebuie transformate într-un proiect robust la nivel de companie.

  • Situația curentă, starea țintă și riscurile tehnice sunt evaluate împreună.
  • REST, accesul la date, portalurile și implementarea nu sunt amânate pentru etape ulterioare.
  • Veți vedea din timp care opțiune este viabilă din punct de vedere economic și operațional.

Partajează postarea

Distribuiți această postare direct

LinkedIn, X, XING, Facebook, WhatsApp și E-Mail sunt disponibile imediat. Pentru Instagram pregătim direct linkul și textul scurt.

E-mail

Instagram se deschide într-o filă nouă. Linkul și textul scurt se copiază în prealabil în clipboard.