Od teme magazina do projektne prakse
Povezane stranice usluga i tehnologije za članak
Tko na modernim Windows klijentima pokreće VCL-aplikacije, prije ili kasnije naiđe na isti simptom: ikone pri 125%/150%/200% izgledaju zamućeno, ispucano ili dobiju sivu obrubnu liniju. Upravo tu postaje praktično pitanje VCL High-DPI ikone: ne zato što je High-DPI nov, nego zato što se problemi obično pokažu tek u svakodnevnom radu — na terminal serverima, pri priključivanju/odspajanju prijenosnika iz doka ili kad monitori imaju različite DPI postavke.
Temeljni problem gotovo nikad nije „PNG je oštećen“, nego pipeline: odakle dolazi ikona (resurs, datoteka, SVG, font), u kojem obliku se isporučuje, kako završava u TImageList i tko kada kako skalira. U VCL-u se tu sreću nekoliko pojmova koje treba razdvojiti: DPI-Awareness (da li Windows skala aplikaciju ili aplikacija samu sebe), Per-Monitor-DPI (svaki monitor može imati drugačiji DPI) i ImageList-Strategie (držati više rezolucija ili rasterno skalirati u runtimeu).
U ovom tekstu svjesno ne raspravljamo o UI-dizajnerskim pitanjima, nego o čistom, robusnom pristupu: skalirati ikone u runtimeu, ali tako da ne postanu zamućeni pikseli, da alfa-kanali ostanu netaknuti i da promjene DPI-a prođu bez treperenja ili pogrešne veličine slike. Uz to obrađujemo zamke, smjernice za debug i iskrenu procjenu kad se dodatni napor isplati.
Zašto nastaje zamućenje piksela: razumjeti lanac skaliranja u VCL-u
Najčešći razlog za neoštre ikone je jednokratno down-/upscaling u pogrešno vrijeme. Klasični tijek u legacy VCL-u:
- Aplikacija isporučuje ikone samo u 16×16 ili 24×24.
- Windows ili VCL skalira na 20×20 / 32×32 / 48×48.
- Skaler koristi interpolaciju koja je prihvatljiva za fotografije, ali kod piksel-grafike zamućuje rubove.
- Uz to se transparentnost (alfa) često gura u logiku maske ili se više puta konvertira.
Posebno neugodno postane kada se dogode više uzastopnih skaliranja: npr. kada ImageList već daje skalirano bitmap-medij, a Windows zbog nedostatka DPI-awareness ili zbog sistemskog DPI-ja ponovno skalira preko toga. Rezultat: dvostruko zamućenje.
Druga, u projektima podcijenjena točka je vrijeme skaliranja. Kod Per-Monitor-DPI (PMv2, odnosno Per-Monitor-DPI-Awareness v2) efektivni DPI se može promijeniti kada prozor prijeđe na drugi monitor ili kada klijent udaljene radne površine dinamički prilagodi DPI. Ako tada TImageList ili cache nije ispravno rekonstruiran, iznenada ćete vidjeti ikone u pogrešnoj veličini ili s pogrešnim rastriranjem.
TImageList pod High-DPI: tipične zamke u stvarnim aplikacijama
TImageList je povijesno zamišljen za male bitmap-e, s fiksnim dimenzijama, indeksima i relativno rigidnom logikom pohrane. Pri High-DPI nastaju iz toga praktične zamke:
1) Čvrsto zadane Width/Height
Mnogi VCL-formulari postave ImageList.Width/Height u design-time i ondje ih ostave. Pri 150% korisnik bi Windows na primjer želio iz 16×16 radije 24×24 napraviti. Ako lista ostane na 16×16, ili se negdje ošiša (clipped) ili se skalira — u oba slučaja rezultat je neuredan.
2) PNG-alpha i logika maski
Ovisno o Delphi-verziji i VCL-komponentama brzo završiš u mješovitom režimu: PNG-ovi s alphom ponekad se interno drže kao 32-bit bitmape, ponekad kao maska+boja. Kad višestruko konvertiraš (PNG → Bitmap → ImageList → Draw), nastaju sivi halo-ovi ili oštre ivice. Efekt je često ovisan o pozadini: na tamnoj alatnoj traci izgleda gore nego na svijetlom panelu.
3) Promjena DPI u runtimeu: cache-ovi, handle-i, OwnerDraw
Neke komponente cache-iraju prikaz slike ili preuzimaju ImageList-handle u trenutku kad DPI još nije konačan. Posebno kod toolbara, TreeView/ListView i OwnerDraw-scenarija nakon DPI-promjena povremeno se vide pogrešne veličine slika ili prazni ikoni dok ne dođe do repaint-a ili RecreateWnd.
4) Terminal server i Remote Desktop kao test realnosti
Ako se aplikacija koristi preko RDP-a, promjene DPI i reconnect session-a nisu neobične. Upravo tamo se nekonzistentna ImageList-strategija pokaže: korisnik nakon reconnecta vidi zamagljene ikone ili krivo skalirane alatne trake, iako je lokalno sve bilo u redu.
Čist pristup: isporučiti više rezolucija umjesto brutalnog upsizinga
Najvažnija odluka je konceptualna: hoćeš li ikone u runtime-u skalirati iz jedne osnovne grafike (npr. 16×16 → 32×32), ili pripremiš više nativnih rezolucija i po DPI-ju izabereš odgovarajuću?
U praksi gotovo uvijek pobjeđuje pristup s više rezolucija. Upscaling ima smisla ako je izvor vektorski (SVG, icon-font) ili ako trebaš samo umjerene faktore. Kad snažno skaliraš iz malog rastera, gubiš kvalitetu ivica — i to se na modernim zaslonima odmah vidi.
U VCL-svijetu za ovaj pristup danas su relevantna dva gradivna bloka:
- TImageCollection: kontejner za slike u više veličina/varijanti.
- TVirtualImageList: iz toga generira za runtime ImageList u trenutačno potrebnoj veličini i reagira na promjene DPI-ja.
Time se ne rješavaju svi problemi, ali se premjesti odgovornost na pravo mjesto: definiraš izvore slika jasno, a skaliranje/odabir se događa konzistentno.
Skaliranje VCL High-DPI ikona u runtimeu: kada ima smisla (a kada ne)
Postoje legitimni razlozi za skaliranje ikona u runtimeu:
- Dinamički učitavaš ikone (npr. iz plugin-mape, korisničkih branding-paketa, konfiguracijskih paketa).
- Želiš jedinstvenu pipeline za različite izvore (ICO, PNG, SVG) i ne želiš vezati sve varijante u build-timeu.
- Generiraš ikone programatski (status-badževi, overlay-ji, sastavljeni simboli).
Skaliranje u vrijeme izvođenja nije smisleno ako zapravo koristiš samo klasične ikone alatne trake iz fiksnog skupa. U tom slučaju najpraktičniji, najmanje zahtjevan za održavanje pristup je: isporučiti više rezolucija i pustiti VCL da odabere.
Ako radiš skaliranje u runtimeu, radi to uz jasna pravila:
- Nikada ponovno ne skaliraj iz već skaliranog bitmapa. Uvijek polazi od master izvora (idealno vektorski ili visoke rezolucije).
- Cache po ciljnoj veličini i DPI, inače ćeš pri svakom Paintu ponovno skalirati — to troši CPU i može uzrokovati trzanje.
- Sačuvaj alpha: minimiziraj konverzije, koristi 32-bitni RGBA, ne gubi alpha-podatke na pozadini.
Pragmatična arhitektura: Icon-pipeline kao zaseban komponent
U većim aplikacijama isplati se temu ne širiti svuda po kodu, nego izgraditi malu pipeline. To ne mora biti framework, nego jasan odgovorni domen:
- Icon-izvor: Odakle dolaze master-asseti (resursi, datoteke, baza podataka, API)?
- Rasterizer/Scaler: Kako se iz mastera generira ciljna veličina (interpolacija, eventualno SVG-render)?
- Cache: Ključ (Icon-ID, cilj-pikseli, DPI, theme) i životni ciklus (invalidirati pri promjeni DPI, promjeni theme-a, promjeni paketa).
- Consumer-adapter: Kako rezultat ulazi u VCL-strukture (TImageList/TVirtualImageList, OwnerDraw, PaintBox)?
Prednost: DPI-bugove možeš reproducirati i debugirati na jednom mjestu, umjesto da tražiš „gdje se opet skalira“ kroz 40 formi.
Ispravno rukovanje promjenom DPI: eventi, rebuild, repaint
Pod Windows su promjene DPI-a zaseban životni ciklus. U VCL-u, ovisno o verziji i DPI-awarenessu, postoje različiti eventi/mehanizmi, ali osnovno načelo ostaje:
- Ako se DPI prozora promijeni, slikovni resursi koji moraju biti piksel-točni trebaju se ponovo pripremiti.
- Ako dinamički puniš ImageLists, često jedan običan Invalidate nije dovoljan — trebaš Rebuild slika u novoj ciljnoj veličini.
Praktični obrazac je: pri promjeni DPI-a (npr. Form-Scale/monitor-swap) invalidiraš icon-cache za taj DPI i ponovno gradiš zahvaćene ImageLists. Važno je pritom ne skalirati unutar Paint-eventa, nego u kontroliranom update-bloku (Toolbar.BeginUpdate/EndUpdate, ListView-Redraw isključi, pa ponovno uključi). Time izbjegavaš treperenje i polu‑završen UI-stanja.
Zašto TImageList često postane zamućen: interpolacija, okrugljivanje DPI-a, rubovi
Kad jednom shvatiš da problem nije sama DPI vrijednost, nego interpolacija plus zaokruživanje, možeš objasniti mnoge efekte:
- Zaokruživanje DPI-ja: 125% nije čist faktor dvostrukog povećanja. Iz 16 px postaje 20 px (16 * 1,25). Iz 24 px postaje 30 px. To su neparni brojevi koji otežavaju rubove piksela.
- Filtri za resampliranje: Bilinear/Bicubic čine rubove mekšima. To je za fotografije u redu, za ikone često nije.
- Subpikselni efekti: Windows može, ovisno o render-putu, koristiti ili ne koristiti subpixel-antialiasing. Kod ikona želite kontrolirane rubove — i po mogućnosti bez višestrukih slojeva filtera.
Ako imate raster-ikone, u mnogim timovima je uobičajena praksa isporučiti zasebne PNG-ove za svaku ciljnu veličinu (16/20/24/32/40/48). Izgleda kao puno, ali često je manje posla nego godinama debugirati „zašto na ovom monitoru izgleda čudno“.
Debugging: reproducirati piksel-noćnu moru umjesto popravljanja po osjećaju
Bugs vezani uz High-DPI često djeluju slučajno. Nekoliko provjera čini ih determinističkima:
1) Logiranje DPI-ja i ImageList-veličina u runtimeu
Logiraj pri pokretanju i pri promjeni DPI-ja: CurrentPPI des Forms, Screen.PixelsPerInch (pažnja: to može biti sistemski DPI), kao i ImageList.Width/Height pogođenih listi. Ako nakon promjene monitora i dalje vidiš 16 px, iako očekuješ 32, uzrok je jasan: nedostaje rebuild ili dolazi prekasno.
2) Vidljivo povećati ikone
Brzi test je privremeno postaviti toolbar-ikone na 48 px. Loše skaliranje odmah iskoči. Dobre pipeline-i ostaju oštre i pri 48 px jer rasteriziraju iz odgovarajućeg izvora.
3) Testirati promjene teme i pozadine
Haloi na rubu često su problemi s alpha/premultiply. Testiraj na svijetlom/tamnom i na područjima s gradijentom. Ako rub izgleda drugačije ovisno o pozadini, obrada transparentnosti nije ispravna.
4) Remote Desktop / promjena monitora kao test-skript
Napravi kratak test-skript za QA: start app na monitoru A (100%), premjesti prozor na monitor B (150%), vrati, pa RDP reconnect. Ako je to stabilno, mnogi problemi kod kupaca su već riješeni.
Migration u postojećim aplikacijama: korak-po-korak umjesto „Big Bang“
U rastućim Delphi-VCL-aplikacijama logika ikona često je razasuta po mnogim mjestima: izbornici, toolbar-ovima, ActionListama, TreeView-ima, statusima. Velika reorganizacija nosi rizik. Dokazano je djelotvorno postupno pristupanje:
- Inventura: Koje ImageList-ove imate? Koja kontrola ih koristi? Koje se veličine očekuju?
- Prioritizacija: Prvo najistaknutija područja (glavni toolbar, navigacija, kontekstni izbornici).
- Jedinstveni izvor: Centralizirati ikone (ImageCollection ili vlastiti loader), umjesto da svaka forma učitava vlastite datoteke.
- Testovi pri promjeni DPI-ja: Od prvog preuređenog modula dosljedno izvoditi testove promjene DPI-ja.
Važno: Ako paralelno upravljaš starom i novom pipeline, dokumentiraj jasna pravila. Inače nastaje mješoviti krajolik u kojem su neke ikone oštre, a druge vidljivo mutne.
Performanse i memorija: skaliranje u vrijeme izvođenja bez nuspojava
Skaliranje košta CPU i memoriju. U poslovnom softveru to rijetko upada u oči u mirovanju, ali pri povlačenju prozora na novi monitor ili pri pokretanju s mnogim Forms može se pojaviti trzanje. Tri praktične smjernice:
- Ograničiti veličinu cachea: Ne zadržavati svaku međuvrijednost zauvijek. Ako trebaš samo 100% i 150%, cacheiraj samo te veličine.
- Lazy Build: Ikone rasterizirati tek kada ih ekran stvarno treba. Pri velikim izbornicima to štedi vrijeme pokretanja.
- Batch-Rebuild: Pri promjeni DPI-a ne okidaj svaki Control zasebno. Jedan centralni rebuild sprječava redundantno skaliranje.
Ako radiš s TVirtualImageList, mnogo toga je već obuhvaćeno kao koncept, ali ipak moraš paziti da ne dodaš vlastito skaliranje preko toga.
Fallback-Strategien: Što učiniti kad nisu dostupne sve veličine ikona?
U stvarnosti nemaš uvijek sve grafičke resurse u svim veličinama. Treba ti jasna fallback-strategija da izbjegneš nepredvidive rezultate:
- Prefer Downscale: Radije smanjiti s 64 px na 32 px nego povećavati s 16 px na 32 px.
- Definiraj razine: Odredi koje ciljane veličine stvarno podržavaš (npr. 16/20/24/32/40/48) i mapiraj DPI konzistentno na te veličine.
- Testiranje prozirnosti: Kod fallbackova posebno pazi na alpha kanal — upravo tamo nastaju halo-efekti.
Tipična pogreška je uzeti bilo koju sljedeću veličinu. To dovodi do varijacija u percipiranoj oštrini ovisno o ikoni. Bolje je imati strogi, dokumentirani plan mapiranja.
Kada se trud doista isplati?
Postoje tri jasna indikatora da se isplati uredna High-DPI pipeline za ikone:
- Tvoji korisnici rade s miješanim monitorima (laptop + eksterni) ili često preko RDP-a.
- Aplikacija je dugovječna i održava se tijekom godina, a percepcija UI-a dio je prihvaćanja.
- Imaš ionako korake modernizacije pred sobom (podići DPI-awareness, zamijeniti Controls, preraditi layout alatne trake).
Ako aplikacija radi samo na fiksnom kiosku s identičnom rezolucijom, temu se može svesti na minimum: isporučiti odgovarajuću veličinu ikone, ispravno postaviti DPI-awareness, gotovo.
Zaključak: High-DPI nije kozmetički detalj, već odluka o renderiranju
Mutne ikone u VCL-u rijetko su izolirana greška, nego pokazatelj nečiste lanca izvora, skaliranja i cacheiranja. Najrobustniji put je isporučiti ikone u više rezolucija i dosljedno ih dostavljati kroz centralnu pipeline (npr. ImageCollection/VirtualImageList ili vlastiti sloj ikona). Skaliranje u runtime ima smisla ako imaš dinamičke izvore ili složene kompozitne simbole, ali samo s master-izvorom, cacheom temeljenim na DPI-ju i jasnim pravilima za rebuild.
Ako imaš konkretne simptome (promjena DPI-a uništava ikone, halo-efekti na rubu, pogrešne veličine nakon RDP-a), isplati se ciljano izolirati problem i tretirati ga kao mali arhitektonski modul umjesto gomilanja workarounda po formama. Ako trebaš podršku pri debugiranju ili pri postupnoj modernizaciji, pronaći ćeš ovdje prikladan ulaz: Kontakt za Net-Base Software GmbH.
Za ovu temu su također važni Timagelist High Dpi i Delphi Vcl Dpi-Awareness. Članak jasno razlaže te aspekte i pokazuje na što treba obratiti pažnju u svakodnevnoj praksi.
sljedeći korak
Ako se tema pretvori u stvarni projekt, arhitekturu, postojeće sustave i operacije trebalo bi rano zajednički razmotriti.
Podržavamo vas ne samo u pojedinačnim pitanjima, već i kada iz isječaka izvornog koda, naslijeđenih sustava ili ideja za portale treba nastati pouzdan poslovni projekt.
- Postojeće stanje, ciljna slika i tehnički rizici procjenjuju se zajedno.
- REST, pristup podacima, portali i rollout neće biti odgođeni kao naknadne posljedice.
- Rano prepoznajete koji je put ekonomski i operativno održiv.