Nuo žurnalo temos iki projekto įgyvendinimo
Tinkami puslapiai apie paslaugas ir techninę informaciją šiam įrašui
Kas eksploatuoja VCL programas moderniuose Windows klientuose anksčiau ar vėliau susidurs su tuo pačiu simptomu: piktogramos prie 125%/150%/200% atrodo kaip išplautos, suapvalėjusiais kraštais arba gauna pilką apvadą. Būtent čia praktinė reikšmė atsiranda temai VCL High-DPI piktogramos: ne todėl, kad High‑DPI būtų naujiena, o todėl, kad problemos dažniausiai atsiskleidžia kasdieniame naudojime — terminalų serveriuose, keičiant nešiojamojo kompiuterio doką arba kai monitoriai turi skirtingas DPI reikšmes.
Pagrindinė problema beveik niekada nėra „PNG yra sugadintas“, o pipeline: iš kur atkeliauja piktograma (resursas, failas, SVG, šrifto glyfas), kokiu dydžiu ji pateikiama, kaip ji patenka į TImageList ir kas kada kaip ją skalē. VCL kontekste susikaupia keli terminai, kuriuos reikia atskirti: DPI‑Awareness (ar Windows mastelį taiko už aplikaciją arba aplikacija pati save skalē), Per‑Monitor‑DPI (kiekvienas monitorius gali turėti skirtingą DPI) ir ImageList‑strategija (laikyti kelias rezoliucijas arba rasterizuoti vykdymo metu).
Šiame straipsnyje sąmoningai nekalbama apie UI‑dizaino diskusijas, o pateikiamas tvarkingas, eksploataciškai patikimas požiūris: skalė piktogramas vykdymo metu, bet taip, kad jos netaptų „pikselių košė“, kad alpha kanalai išliktų sveiki ir kad DPI pasikeitimai veiktų be mirksėjimo ar neteisingų vaizdo dydžių. Pridedami spąstai, derinimo patarimai ir sąžininga vertinimo nuomonė, kada papildoma pastanga atsiperka.
Warum Pixelmatsch entsteht: Skalierungskette in der VCL verstehen
Pats dažniausias neryškių piktogramų priežastis yra vienkartinis down/up‑scaling netinkamu laiku. Klasikinis procesas legacy‑VCL aplinkoje:
- Programa pateikia piktogramas tik 16×16 arba 24×24 dydžiais.
- Windows arba VCL jas skalē į 20×20 / 32×32 / 48×48.
- Skalavimo variklis naudoja interpolaciją, kuri tinkama nuotraukoms, bet pikselinei grafikai išsklaidė kraštus.
- Be to, skaidrumas (alpha) spaudžiamas į kaukės logiką arba konvertuojamas kelis kartus.
Ypač erzinančiai tampa, kai vyksta kelios skalės paeiliui: pavyzdžiui, kai ImageList jau duoda skaliuotą bitmapą, o Windows dėl to, kad nėra DPI‑aware arba dėl sisteminio DPI, skalē dar kartą. Rezultatas: dvigubai išplautas vaizdas.
Antras, projektuose dažnai nuvertinamas aspektas yra skalavimo laikas. Su Per‑Monitor‑DPI (bzw. PMv2, tai yra Per‑Monitor‑DPI‑Awareness v2) efektyvus DPI gali pasikeisti, kai langas perkeliamas ant kito monitoriaus arba kai nuotolinio darbalaukio klientas dinamiškai koreguoja DPI. Jei tada TImageList arba talpykla (cache) nebus tinkamai perpildoma, staiga matysite piktogramas neteisingo dydžio arba su netinkamu rastru.
TImageList unter High-DPI: typische Fallen in echten Anwendungen
TImageList iš pradžių buvo skirta mažoms bitmapoms, su fiksuotais matmenimis, indeksais ir santykinai standžia atminties logika. High‑DPI sąlygomis iš to kyla praktinės spąstys:
1) Fiksuoti Width/Height
Daugelis VCL formų nustato ImageList.Width/Height dizaino metu ir taip palieka. Esant 150% Windows dažnai norėtų pvz. 16×16 pakeisti į 24×24. Jei sąrašas lieka 16×16, vaizdas arba apkarpomas, arba kitoje vietoje skalė pritaikoma — abu variantai nepageidaujami.
2) PNG‑alfa ir maskavimo logika
Priklausomai nuo Delphi-versijos ir VCL komponentų, greitai susidaro mišrus režimas: PNG su alfa kanalais viduje kartais laikomi kaip 32-bit Bitmap, kartais kaip Mask+Color. Kai kelis kartus konvertuojama (PNG → Bitmap → ImageList → Draw), atsiranda pilki šešėliai arba aštrios briaunos. Efektas dažnai priklauso nuo fono: tamsioje įrankių juostoje tai atrodo blogiau nei ant šviesaus panelio.
3) DPI keitimai vykdymo metu: Caches, Handles, OwnerDraw
Kai kurie Controls talpina vaizdo atvaizdavimą į cache arba perima ImageList handles tuo momentu, kai DPI dar nėra galutinė. Ypač įrankių juostose, TreeViews/ListViews ir OwnerDraw scenarijuose po DPI keitimų kartais matomi neteisingi vaizdų dydžiai arba iš pirmo žvilgsnio tuščios ikonėlės, kol neįvykdomas Repaint arba RecreateWnd.
4) Terminalserver ir Remote‑Desktop kaip realybės testas
Jei programa naudojama per RDP, DPI keitimai ir sesijos atkūrimai nėra reti. Būtent ten nepatikima ImageList strategija išryškėja: vartotojas po sesijos atkūrimo mato išplautas arba neteisingai masteliuotas ikonėles ir įrankių juostas, nors lokaliai viskas atrodė gerai.
Tvarkingas požiūris: tiekti kelias raiškas vietoje brutalaus upscaling
Svarbiausias sprendimas yra konceptualus: ar norite vykdymo metu iš vienos bazinės grafikos didinti ikonas (pvz. 16×16 → 32×32), ar pateikti kelias natyvias raiškas ir pagal DPI pasirinkti tinkamą?
Praktikoje dažniausiai laimi variantas su keliomis raiškomis. Upscaling prasmingas, jei šaltinis yra vektorinė grafika (SVG, Icon‑font) arba jei reikia tik vidutinių mastelio faktorų. Kai iš mažo rasterinio vaizdo stipriai didinate, prarandate briaunų kokybę — tai iškart matoma ant modernių ekranų.
VCL pasaulyje šiam požiūriui šiandien aktualūs du komponentai:
- TImageCollection: konteineris vaizdams keliose dydžių/variantų versijose.
- TVirtualImageList: iš to generuoja vykdymo metu ImageList reikiamoje dydžio versijoje ir reaguoja į DPI‑pakeitimus.
Tai neišsprendžia visų problemų, bet perkelia sprendimo atsakomybę į tinkamą sluoksnį: apibrėžiate vaizdų šaltinius aiškiai, o skalavimas/atranka vyksta konsekventiškai.
VCL High‑DPI ikonų skalavimas vykdymo metu: kada tai prasminga (ir kada ne)
Yra teisėtų priežasčių skalauti ikonėles vykdymo metu:
- Jūs dinamiškai įkeliate ikonėles (pvz., iš plugin katalogo, kliento branding paketų, konfigūracinių paketų).
- Norite vieningos apdorojimo grandinės skirtingiems šaltiniams (ICO, PNG, SVG) ir nenorite susieti visų variantų build‑laiku.
- Generuojate ikonėles programiškai (statuso ženkliukai, overlay’ai, sudėtiniai simboliai).
Paleidimo laiko skalavimas nėra prasmingas, jei iš esmės turi tik klasikines toolbar ikonėles iš fiksuoto rinkinio. Tada mažiausiai priežiūros reikalaujantis būdas yra: pateikti kelias raiškas ir leisti VCL jas pasirinkti.
Jei darai paleidimo laiko skalavimą, daryk tai pagal aiškias taisykles:
- Niekada iš jau suskalauto bitmapo neskalauk iš naujo. Visada remkis master šaltiniu (pageidautina vektorinėmis priemonėmis arba didelės raiškos šaltiniu).
- Cache kiekvienai tikslinei dydžio ir DPI kombinacijai, kitu atveju nuo kiekvieno Paint vykdomas perskalavimas — tai apkrauna CPU ir gali sukelti užstrigimus.
- Išsaugok alfa kanalą: minimizuok konvertacijas, naudok 32-bitų RGBA, venk fono papildomo rastrizavimo.
Pragmatiška architektūra: ikonų pipeline kaip atskiras modulis
Didesnėse programose verta šio dalyko nebarstyti po visą kodą, o sukurti nedidelę pipeline. Tai nebūtinai turi būti framework — greičiau aiškiai apibrėžta atsakomybės sritis:
- Icon-Quelle: Iš kur ateina master asset’ai (resursai, failai, duomenų bazė, API)?
- Rasterizer/Scaler: Kaip iš master generuojama tikslinei dydžiui (interpolacija, esant reikalui SVG-rendavimas)?
- Cache: raktas (Icon-ID, tikslūs pikseliai, DPI, tema) ir gyvenimo ciklas (invaliduoti prie DPI-keitimo, temų keitimo, paketų atnaujinimo).
- Consumer-Adapter: Kaip rezultatas patenka į VCL struktūras (TImageList/TVirtualImageList, OwnerDraw, PaintBox)?
Privalumas: DPI klaidas galima atkuriamai debuginti vienoje vietoje, užuot naršius per 40 Forms ieškant „kur dar kartą skalaujama“.
DPI-keitimus tvarkykite teisingai: įvykiai, Rebuild, Repaint
Pagal Windows DPI keitimai turi atskirą gyvenimo ciklą. VCL, priklausomai nuo versijos ir DPI-awareness, gali turėti kelis įvykius/mechanizmus, bet pagrindinis principas išlieka:
- Jei lango DPI pasikeičia, vaizdiniai (bitmap) resursai, kurie turi būti pikseliškai tikslūs, turi būti paruošti iš naujo.
- Jei dinamiškai pildai ImageLists, vien tik Invalidate dažnai nepakanka — reikia Rebuild paveikslėlių naujam tiksliam dydžiui.
Praxėje tinkamas modelis yra toks: prie DPI pakeitimo (pvz., Form-Scale/monitoriaus keitimas) invaliduok Icon-cache šiai DPI ir atkurk paveiktas ImageLists. Svarbu ne skalauti esančiuose Paint-įvykiuose, o atlikti pakeitimus kontroliuojamame update bloke (Toolbar.BeginUpdate/EndUpdate, ListView-Redraw išjungtas, tada vėl įjungtas). Taip išvengsi mirgėjimo ir nepilnų UI būsenų.
Kodėl TImageList dažnai tampa neryškus: Interpolacija, DPI-apvalinimas, kraštai
Kai suprasi, kad ne DPI savaime yra problema, o interpolacija kartu su apvalinimu, galima paaiškinti daugelį efektų:
Jei turite rastrines piktogramas, daugelyje komandų įprasta praktika yra tiekti kiekvienai tiksliajai dydžio versijai atskirus PNG (16/20/24/32/40/48). Skamba daug, bet dažnai tai mažiau darbo nei metų metus derinti „kodėl šiame monitoriuje tai atrodo keistai“.
Derinimas: padaryti pikselių netikslumus reproducuojamus, o ne taisyti pagal nuojautą
High-DPI klaidos dažnai atrodo atsitiktinės. Su keliomis patikromis jas galima paversti deterministinėmis:
1) DPI ir ImageList dydžių registravimas vykdymo metu
Registruok paleidimo metu ir DPI pasikeitimo atveju: Formos CurrentPPI, Screen.PixelsPerInch (dėmesio: tai gali būti sistemos DPI), taip pat paveiktų sąrašų ImageList.Width/Height. Jei po monitoriaus pakeitimo vis dar matai 16, nors tikiesi 32, priežastis aiški: trūksta perstatymo arba jis vyksta per vėlai.
2) Piktogramų aiškus padidinimas
Greitas testas — laikinai nustatyti įrankių juostos piktogramas į 48 px. Prasta skalė tuojau pat išryškės. Geros procedūros irgi išlieka aštrios prie 48 px, nes rastrojamos iš tinkamo šaltinio.
3) Temų ir fonų keitimo testavimas
Halos kraštuose dažnai būna Alpha/Premultiply problemos. Išbandyk tamsiame/šviesiame fone ir plokštėse su spalvų perėjimais. Jei kraštas atrodo skirtingai priklausomai nuo fono, nepavyko teisingai apdoroti permatomumo.
4) Remote Desktop / monitoriaus perjungimas kaip testavimo skriptas
Sukurk trumpą testavimo skriptą QA: paleisti programą monitoriuje A (100a0%), perkelti langą į monitorių B (150a0%), grįžti, tada RDP reconnect. Jei tai stabili, dauguma klientų problemų jau bus išspręstos.
Migracija esamose programose: palaipsniui vietoje Big Bang
Užaugusiuose Delphi-VCL programuose ikonų logika dažnai pasklidusi daugelyje vietų: meniu, įrankių juostos, ActionLists, TreeViews, būsenos rodmenys. Big-Bang pertvarkymas kelia riziką. Patikrintas metodas — palaipsninis požiūris:
- Inventūra: Kokios ImageLists yra? Kurie kontroliai jas naudoja? Kokie dydžiai laukiami?
- Prioritetizavimas: Pirmiausia labiausiai matomos vietos (pagrindinė įrankių juosta, navigacija, kontekstiniai meniu).
- Vieningas šaltinis: Centralizuok ikonų šaltinį (ImageCollection arba savas užkrovėjas), vietoje to, kad kiekvienai formai krauti atskirus failus.
- DPI pokyčių testavimas: Nuo pirmojo pertvarkyto modulio nuosekliai vykdyk DPI pokyčių testus.
Svarbu: jei paraleliai palaikai seną ir naują pipeline, dokumentuok aiškias taisykles. Priešingu atveju susiformuoja mišri aplinka, kur kai kurios ikonėlės yra aštrios, o kitos akivaizdžiai neryškios.
Veikimas ir atmintis: vykdymo laiko skalavimas be šalutinių poveikių
Skalavimas kainuoja CPU ir atmintį. Verslo programoje to retai pastebėsi tuščios eigos metu, tačiau vilkdami langą į naują monitorių arba paleidžiant programą su daug formų gali pasireikšti trūkčiojimai. Trys praktinės gairės:
- Apriboti talpyklos dydžius: Nelaikyk kiekvienos tarpinių versijų amžinai. Jei reikalingi tik 100% ir 150%, talpink tik jas.
- Lazy Build: Ikonas rasterizuok tik tada, kai ekranas jų tikrai reikalauja. Dideliuose meniu tai sutrumpina paleidimo laiką.
- Batch-Rebuild: DPI pakeitimo metu netrikdyk kiekvieno valdiklio atskirai. Centralizuotas perkompiliavimas užkerta kelią pertekliniam skalavimui.
Jei dirbama su TVirtualImageList, daug kas iš to jau yra įdėta kaip koncepcija, bet vis tiek reikia atkreipti dėmesį, kad nepridėtumėte savo papildomo skalavimo viršuje.
Fallback strategijos: ką daryti, jeigu nėra visų ikonų dydžių?
Realybėje ne visada būna visi resursai visuose dydžiuose. Tada reikia aiškios fallback strategijos, kad išvengtumėte atsitiktinių rezultatų:
- Prefer Downscale: Geriau sumažinti nuo 64 px iki 32 px, nei padidinti nuo 16 px iki 32 px.
- Apibrėžti lygius: Nustatyk, kokius tikslinius dydžius išties palaikai (pvz. 16/20/24/32/40/48) ir aiškiai priskirk DPI šiems dydžiams.
- Ištestuoti permatomumą: Fallback atvejais ypač atkreipk dėmesį į alpha kanalą — būtent čia atsiranda aureolės.
Tipinė klaida yra tiesiog paimti bet kurią artimiausią dydį. Dėl to suvokiamas aštrumas skirsis priklausomai nuo ikonos. Geriau turėti griežtą, dokumentuotą priskyrimų planą.
Kada šis darbas iš tiesų atsiperka?
Yra trys aiškūs indikatoriai, kada verta sukurti tvarkingą High‑DPI ikonų pipeline:
- Vartotojai dirba su mišriais monitoriais (nešiojamas + išorinis) arba dažnai naudojama RDP.
- Programinė įranga yra ilgaamžė ir prižiūrima per metus — UI suvokimas yra priimtinumą lemiantis faktorius.
- Vis tiek planuoji modernizacijos žingsnius (padidinti DPI‑awareness, keisti valdiklius, pertvarkyti įrankių juostos išdėstymą).
Jei programa veikia tik fiksuotame kiosko sistemos konfigūracijoje su vienoda raiška, galima temą laikyti minimalia: pateikti tinkamo dydžio ikonas, teisingai nustatyti DPI‑awareness ir tiek.
Fazit: High‑DPI nėra kosmetinis niuansas, o renderinimo sprendimas
Neryškios ikonėlės VCL aplinkoje retai būna vienintelis klaidos šaltinis — tai ženklas apie netvarkingą grandinę iš šaltinių, skalavimo ir kešavimo. Patikimiausias kelias yra pateikti ikonų versijas keliuose dydžiuose ir tiekti jas nuosekliai per centralizuotą pipeline (pvz. ImageCollection/VirtualImageList arba per savą ikonų sluoksnį). Vykdymo laiko skalavimas prasmingas tada, kai turi dinamiškus šaltinius arba sudėtinius simbolius — bet tik turint pagrindinį šaltinį, DPI pagrindu veikiančią talpyklą ir aiškias perkompiliavimo taisykles.
Jei pastebi konkrečius simptomus (DPI pakeitimas ardo ikonėles, aureolės kraštuose, neteisingi dydžiai po RDP), verta temą izoliuoti ir spręsti kaip mažą architektūrinį komponentą, o ne krauti darbo aplinkkelių po forma. Jei reikia pagalbos su debuggingu arba žingsnis po žingsnio modernizacija, čia rasi tinkamą pradžią: Kontaktas prie Net-Base Software GmbH.
Šiam klausimui taip pat svarbūs Timagelist High Dpi ir Delphi Vcl Dpi-Awareness. Straipsnis aiškiai įvertina šiuos aspektus ir parodo, į ką reikia atkreipti dėmesį kasdienėje praktikoje.
Sekantis žingsnis
Kai iš temos tampa realus projektas, architektūrą, esamą aplinką ir eksploatavimą reikėtų anksti nagrinėti kartu.
Mes padedame ne tik pavienėse užklausose, bet ir tuomet, kai iš šaltinio kodo fragmentų, paveldėtų temų ar portalo idėjų turi tapti patikimas įmonės projektas.
- Esama padėtis, tikslinis vaizdas ir techninės rizikos vertinami kartu.
- REST, duomenų prieiga, portalai ir diegimas nebus atidedami į vėlesnes stadijas.
- Jūs anksti matote, kuris kelias yra ekonomiškai ir įmonės veiklos požiūriu tvarus.