Lehden aiheesta projektikäytäntöön
Artikkeliin liittyvät palvelu- ja tekniikkasivut
Kuka tahansa, joka ajaa VCL-sovelluksia moderneilla Windows-asiakkailla, törmää ennemmin tai myöhemmin samaan oireeseen: kuvakkeet näyttävät 125%/150%/200% skaalauksella sumeilta, repsottavilta tai niihin ilmestyy harmaa reuna. Juuri tässä VCL High-DPI Icons -aihe muuttuu käytännön ongelmaksi: ei siksi, että High-DPI olisi uusi asia, vaan siksi, että ongelmat ilmenevät yleensä vasta arjessa — terminaalipalvelimilla, kannettavan telakoinnin yhteydessä tai kun näytöittäin on eri DPI-arvot.
Keskeinen ongelma ei lähes koskaan ole, että PNG olisi rikki, vaan pipeline: mistä kuvake tulee (resurssi, tiedosto, SVG, fontti), missä koossa se tarjotaan, miten se päätyy TImageList:iin ja kuka skaalauksen tekee ja milloin. VCL:ssä kohtaa useita käsitteitä, jotka täytyy erottaa: DPI-Awareness (skaalaako Windows sovelluksen vai sovellus itse), Per-Monitor-DPI (jokainen näyttö voi olla erilainen) ja ImageList-Strategie (pitääkö tarjota useita resoluutioita vai rasteroida ajossa).
Tässä kirjoituksessa ei tarkoituksellisesti käydä läpi käyttöliittymäsuunnittelun keskusteluja, vaan esitellään puhdas, tuotantovarma lähestymistapa: kuvakkeiden skaalaus ajon aikana siten, että ne eivät muutu pikseliseksi mössöksi, että alpha-kanavat pysyvät ehjinä ja että DPI-muutokset toimivat ilman välkkymistä tai väärän kokoisia kuvia. Mukana ovat myös sudenkuopat, debug-vinkit ja rehellinen arvio siitä, milloin lisätyö kannattaa.
Miksi pikselimössö syntyy: skaalausketjun ymmärtäminen VCL:ssä
Yleisin syy epätarkkoihin kuvakkeisiin on kertaluonteinen down-/upscaling väärään aikaan. Perinteinen kulku legacy-VCL:ssä:
- Sovellus toimittaa kuvakkeet vain 16×16- tai 24×24-kokoisina.
- Windows tai VCL skaalaa ne 20×20 / 32×32 / 48×48 -kokoihin.
- Skaalausalgoritmi käyttää interpolointia, joka sopii valokuville, mutta sumentaa reunoja pikselikuvituksessa.
- Lisäksi läpinäkyvyys (alpha) ajetaan maskilogikaan tai muunnetaan useaan kertaan.
Tilanne pahenee erityisesti, kun useita skaalausvaiheita tapahtuu peräkkäin: esimerkiksi kun ImageList toimittaa jo skaalatun bitmappin, ja Windows skaalautuu uudelleen, koska sovellus on DPI-unaware tai järjestelmän DPI poikkeaa. Tuloksena on kaksinkertainen pehmennys.
Toinen projektissa usein aliarvostettu seikka on skaalauksen ajoitus. Per-Monitor-DPI:ssä (bzw. PMv2, eli Per-Monitor-DPI-Awareness v2) efektiivinen DPI voi muuttua, kun ikkuna siirtyy näytöltä toiselle tai kun etätyöpöytäasiakas säätää DPI:tä dynaamisesti. Jos tällöin TImageList tai välimuisti ei rakennu oikein uudelleen, kuvakkeet voivat yhtäkkiä näkyä väärässä koossa tai väärällä rasterilla.
TImageList High-DPI-tilanteessa: tyypilliset sudenkuopat oikeissa sovelluksissa
TImageList on historiallisesti suunniteltu pienille bitmapeille, kiinteillä mitoilla, indekseillä ja suhteellisen jäykällä tallennuslogiikalla. Korkean DPI:n ympäristössä tästä seuraa käytännön ansoja:
1) Kovakoodatut Width/Height
Monet VCL-lomakkeet asettavat ImageList.Width/Height suunnitteluaikana ja jättävät sen siihen. 150 %:n skaalauksessa Windows haluaisi esimerkiksi kasvattaa 16:sta 24:ään. Jos lista pysyy 16:ssa, kuva joko leikataan tai skaalaus tapahtuu muualla — molemmat epämiellyttäviä.
2) PNG-Alpha und Maskenlogik
Riippuen Delphi-versiosta ja käytetyistä VCL-komponenteista päädyt helposti sekakäyttöön: alfa-PNG:t pidetään sisäisesti osin 32-bittisinä bitmapeina, osin Mask+Color-muotoisina. Kun konvertoit useampaan kertaan (PNG → Bitmap → ImageList → Draw), syntyy harmaita haloja tai teräviä reunoja. Ilmiö on usein taustariippuvainen: tummalla työkalurivillä se näkyy pahemmin kuin vaalealla panelilla.
3) DPI-vaihdokset ajonaikana: Caches, Handles, OwnerDraw
Jotkin kontrollit välimuistittavat kuvan esityksen tai ottavat ImageList-kahvat käyttöön vaiheessa, jossa DPI ei ole vielä lopullinen. Erityisesti työkaluriveillä, TreeView/ ListView -komponenteissa ja OwnerDraw-skenaarioissa DPI-vaihdosten jälkeen nähdään ajoittaista väärän kokoisia kuvia tai tyhjiä kuvakkeita, kunnes suoritetaan repaint tai RecreateWnd.
4) Terminalserver und Remote-Desktop als Realitetstest
Kun sovellusta käytetään RDP:n kautta, DPI-vaihdokset ja istunnon uudelleenyhteydet eivät ole harvinaisia. Juuri siellä epäluja ImageList-strategia pettää: käyttäjä näkee uudelleenyhteyden jälkeen sumeita kuvakkeita tai väärin skaalattuja työkalurivejä, vaikka paikallisesti kaikki oli kunnossa.
Siisti lähestymistapa: Tarjoa useita resoluutioita brutaalin ylenskalauksen sijaan
Tärkein päätös on konseptuaalinen: Haluatko skaalata kuvakkeita ajonaikaisesti yhdestä peruskuvasta (esim. 16 → 32), vai tarjoatko useita natiiviresoluutioita ja valitset DPI:n mukaan sopivan?
Käytännössä lähes aina voittaa „useat resoluutiot“. Upscaling on hyväksyttävää, jos lähde on vektori (SVG, icon-font) tai tarvitset todella vain maltillisia kertoimia. Kun kasvatat pientä rasterikuvaa voimakkaasti, reunojen laatu kärsii, ja se näkyy nykyaikaisilla näytöillä välittömästi.
VCL-maailmassa tätä lähestymistapaa tukevat nykyään pääasiassa kaksi komponenttia:
- TImageCollection: säiliö kuville useissa kooissa/variantteina.
- TVirtualImageList: luo niistä ajonaikaisen ImageListin nykyiseen tarpeelliseen kokoon ja reagoi DPI-muutoksiin.
Tämä ei poista kaikkia ongelmia, mutta siirtää vastuun oikeaan paikkaan: määrittelet kuvavirrat selkeästi, ja skaalaus/valinta tapahtuu johdonmukaisesti.
VCL High-DPI -kuvakkeiden skaalaus ajonaikana: milloin se on järkevää (ja milloin ei)
On perusteltuja syitä skaalata kuvakkeita ajonaikaisesti:
- Lataat kuvakkeita dynaamisesti (esim. plugin-kansiosta, asiakaskohtaisista brändipaketeista tai konfiguraatiopaketeista).
- Haluat yhtenäisen pipelinein eri lähteille (ICO, PNG, SVG) etkä halua sitoa kaikkia variantteja build-aikana.
- Luot kuvakkeita ohjelmallisesti (status-badget, overlayt, koostetut symbolit).
Ei ole järkevää tehdä ajonaikaista skaalausta, jos sinulla on käytössä vain perinteiset työkalurivin ikonit kiinteästä joukosta. Silloin vähiten ylläpitotyötä vaativa tapa on: toimittaa useita resoluutioita siististi ja antaa VCL:n valita.
Jos teet ajonaikaista skaalausta, tee se selkein säännöin:
- Älä koskaan skaalaa jo skaalattua bitmapia uudelleen. Lähde aina master‑lähteestä (ihanteellisesti vektori tai korkearesoluutioinen).
- Välimuisti kullekin kohdekoolle ja DPI:lle, muuten skaalaat joka Paintissa uudelleen — se kuluttaa CPU:ta ja voi nykiä.
- Pidä alpha: minimoi konversiot, käytä 32‑bitistä RGBA:ta, älä rasteroi taustaa.
Pragmaattinen arkkitehtuuri: ikoniputki omana komponenttina
Suuremmissa sovelluksissa kannattaa aiheen sijaan hajauttamisen sijaan rakentaa pieni putki. Sen ei tarvitse olla framework — ennemminkin selkeä vastuualue:
- Ikonin lähde: mistä master‑assetit tulevat (resurssit, tiedostot, tietokanta, API)?
- Rasteroija/Skaalaaja: miten master muunnetaan kohdekokoon (interpolaatio, tarvittaessa SVG‑renderointi)?
- Välimuisti: avain (Icon‑ID, kohdepikselit, DPI, Theme) ja elinkaari (invalidointi DPI‑vaihdossa, Theme‑vaihdossa, paketinvaihdossa).
- Consumer‑Adapter: miten tulos päätyy VCL‑rakenteisiin (TImageList/TVirtualImageList, OwnerDraw, PaintBox)?
Etuna: voit toistettavasti debugata DPI‑bugit yhdestä paikasta sen sijaan, että etsisit 40 Formeista „missä taas skaalataan“.
DPI‑vaihdot käsitellään oikein: Ereignisse, Rebuild, Repaint
Alustalla Windows DPI‑vaihdot muodostavat oman elinkaarensa. VCL:ssä on versiosta ja DPI‑awarenessista riippuen useita tapahtumia/mekanismeja, mutta perusperiaate säilyy:
- Kun ikkunan DPI muuttuu, pikseleihin sidotut kuvaresurssit tulee tarjota uudelleen.
- Jos täytät ImageLists dynaamisesti, pelkkä ‚Invalidate‘ ei usein riitä — tarvitset ein Rebuild kuville uuteen kohdekokoon.
Käytännöllinen malli: DPI‑muutoksen yhteydessä (esim. Form‑Scale/monitorinvaihto) invalidioit ikonivälimuistin kyseiselle DPI:lle ja rakennat vaikuttavat ImageListit uudelleen. Tärkeää on älä skaalaa Paint‑tapahtumissa, vaan tehdä se kontrolloidussa päivityslohkossa (Toolbar.BeginUpdate/EndUpdate, ListView‑Redraw pois päältä, sitten takaisin päälle). Näin vältät välkkymistä ja keskeneräisiä käyttöliittymätiloja.
Miksi TImageList usein muuttuu epäteräväksi: Interpolaatio, DPI‑pyöristys, reunat
Kun ymmärrät, ettei ongelmana ole pelkkä DPI vaan interpolaatio plus pyöristys, monet ilmiöt selittyvät:
- DPI-pyöristys: 125 % ei ole siisti kaksinkertaistus. 16 px:stä tulee 20 px (16 * 1,25). 24 px:stä tulee 30 px. Nämä ovat epäsäännöllisiä arvoja, jotka vaikeuttavat pikselireunojen hallintaa.
- Resampling-suodattimet: Bilinear/Bicubic pehmentää reunoja. Se sopii valokuville, mutta usein ei ikooneille.
- Subpixel-ilmiöt: Windows voi renderöintipolusta riippuen hyödyntää subpixel-antialiasingia tai olla hyödyntämättä. Ikooneissa haluat hallitut reunat — ja mieluiten ei useita peräkkäisiä suodatusasteita.
Jos sinulla on rasteri-ikonit, on monissa tiimeissä yleinen käytäntö toimittaa kohdekokoja varten erilliset PNG:t (16/20/24/32/40/48). Kuulostaa paljon, mutta usein se on vähemmän työtä kuin vuosien ajan selvittää, miksi se näyttää kummalliselta juuri tällä näytöllä.
Debuggaus: Tee pikselimössöstä toistettava ongelma sen sijaan, että korjaat mutu-tuntumalta
High-DPI-virheet vaikuttavat usein satunnaisilta. Muutamalla tarkistuksella ne muutuvat deterministisiksi:
1) Kirjaa suoritusaikaisesti DPI ja ImageList-koot
Kirjaa käynnistyksessä ja DPI-vaihdossa: Formin CurrentPPI, Screen.PixelsPerInch (huom: tämä voi olla järjestelmän DPI), sekä kohteena olevien listojen ImageList.Width/Height. Jos monitorinvaihdon jälkeen näet edelleen 16, vaikka odotat 32:ta, syy on selvä: uudelleenrakennus puuttuu tai tapahtuu liian myöhään.
2) Suurenna ikonit näkyvästi
Nopea testi on asettaa työkalurivin ikonit väliaikaisesti 48 px:iin. Huono skaalaus hyppää silloin heti silmään. Hyvät putket pysyvät myös 48 px:ssä terävinä, koska ne rasteroidaan sopivasta lähteestä.
3) Testaa teema- ja taustanvaihdot
Reunan haloilmiöt ovat usein alpha-/premultiply-ongelmia. Testaa vaalealla/tummalla taustalla ja liukuväripinnoilla. Jos reuna näyttää eri taustoilla erilaiselta, läpinäkyvyyden käsittely on väärin.
4) Remote Desktop / näyttöjen vaihto testiskriptinä
Laadi lyhyt testiskripti QS:lle: käynnistä sovellus näytöllä A (100 %), siirrä ikkuna näytölle B (150 %), palauta, tee RDP-yhteyden uudelleenmuodostus. Jos tämä toimii stabiilisti, monet asiakkaiden ongelmat ovat jo ratkaistu.
Migraatio olemassa olevissa sovelluksissa: vaiheittain ennemmin kuin Big Bang
Laajentuneissa Delphi-VCL-sovelluksissa ikonilogiikka on usein hajallaan monessa paikassa: valikot, työkalurivit, ActionLists, TreeViews, tilanäytöt. Big-Bang-uudistus tuo riskiä. Toimivaksi on osoittautunut vaiheittainen lähestymistapa:
- Inventaario: Mitkä ImageListit on olemassa? Mitä kontrolleja ne käyttävät? Mitä kokoja odotetaan?
- Priorisointi: Aloita näkyvimmistä alueista (päätyökalurivi, navigaatio, kontekstivalikot).
- Yhtenäinen lähde: Keskitä ikonit (ImageCollection tai oma lataaja) sen sijaan, että jokaiselle lomakkeelle ladataan erilliset tiedostot.
- DPI-vaihtojen testaus: Ensimmäisestä muutetusta moduulista alkaen aja johdonmukaisesti DPI-vaihtotestejä.
Tärkeää: Jos ylläpidät rinnakkain vanhaa ja uutta pipelinea, dokumentoi selkeät säännöt. Muussa tapauksessa syntyy sekamelska, jossa osa ikoneista on teräviä ja toiset näkyvästi epäteräviä.
Suorituskyky ja muisti: ajonaikainen skaalaus ilman sivuvaikutuksia
Skaalaus kuluttaa CPU:ta ja muistia. Liiketoimintaohjelmassa se harvoin näkyy tyhjäkäynnillä, mutta ikkunan siirtäminen uudelle näytölle tai käynnistys monilla lomakkeilla voi pätkiä. Kolme käytännön ohjetta:
- Rajoita välimuistien kokoja: Älä pidä jokaista väliastetta ikuisesti. Jos tarvitset vain 100 % ja 150 %, välimuista vain näille.
- Lazy Build: Rasteroi ikonit vasta, kun näyttö ne todella tarvitsee. Suurissa valikoissa se säästää käynnistysaikaa.
- Batch-Rebuild: DPI-muutoksessa älä laukaise jokaisen kontrollin uudelleenrakennusta erikseen. Keskitetty rebuild estää päällekkäistä skaalausta.
Jos käytät TVirtualImageList:iä, suuri osa tästä on jo konseptina mukana, mutta sinun on silti varottava ettet lisää vielä omaa ylimääräistä skaalausta päälle.
Fallback-strategiat: mitä tehdä, jos kaikkia ikonikokoja ei ole saatavilla?
Reaaliolosuhteissa sinulla ei aina ole kaikkia assetteja kaikissa kooissa. Tarvitset selkeän fallback-strategian satunnaisten tulosten välttämiseksi:
- Prefer Downscale: Mieluummin skaalataan alaspäin 64 px → 32 px kuin ylös 16 px → 32 px.
- Määrittele tasot: Määrittele, mitä kohdekokoja todella tuet (esim. 16/20/24/32/40/48) ja kartoita DPI niihin johdonmukaisesti.
- Testaa läpinäkyvyys: Fallbackeissa kiinnitä erityisesti huomiota alfadefekteihin, sillä juuri siellä syntyy haloja.
Tyypillinen virhe on käyttää vain „jonkin seuraavan“ kokoa. Silloin havaittu terävyys vaihtelee ikonista riippuen. Parempi on tiukka, dokumentoitu mapping-suunnitelma.
Milloin vaiva kannattaa oikeasti?
On kolme selkeää indikaattoria, että siisti High-DPI-ikoniputki kannattaa rakentaa:
- Käyttäjäsi käyttävät sekoitettuja näyttöjä (kannettava + ulkoinen) tai paljon RDP-yhteyksiä.
- Sovellus on pitkäikäinen ja sitä ylläpidetään vuosia — käyttöliittymän havaittavuus vaikuttaa hyväksyntään.
- Sinulla on joka tapauksessa modernisointiaskeleita edessä (DPI-awarenessin nosto, kontrollien vaihto, työkalurivin layoutin uudelleenkatselu).
Jos sovellus sen sijaan ajetaan vain kiinteässä kiosk-järjestelmässä, jossa resoluutio on identtinen, voi aiheen pitää minimissään: toimita sopiva ikonikoko, aseta DPI-awareness oikein ja sillä selvä.
Yhteenveto: High-DPI ei ole kosmeettinen yksityiskohta vaan renderointipäätös
Epäterävät ikonit VCL:ssä eivät ole usein erillinen virhe, vaan osoitus epätäydellisestä ketjusta lähteistä, skaalauksesta ja cachingistä. Varmuin ratkaisu on tarjota ikonit useissa resoluutioissa ja toimittaa ne keskitetyn putken kautta (ImageCollection/VirtualImageList tai oma ikonikerros) johdonmukaisesti. Ajonaikainen skaalaus on järkevää, kun sinulla on dynaamisia lähteitä tai koostettuja symboleja, mutta vain silloin kun käytössä on ensisijainen lähde, DPI-perusteinen välimuisti ja selkeät uudelleenrakennussäännöt.
Jos sinulla on konkreettisia oireita (DPI-muutos rikkoo ikonit, haloja reunoilla, väärät koot RDP:n jälkeen), kannattaa aihe eristää ja käsitellä pienenä arkkitehtuurikomponenttina sen sijaan, että kasaat workaroundeja lomakkeittain. Jos tarvitset tukea debuggaamiseen tai vaiheittaiseen modernisointiin, löydät täältä sopivan aloituksen: Ota yhteyttä Net-Base Software GmbH.
Tämän aiheen kannalta myös Timagelist High Dpi ja Delphi Vcl Dpi-Awareness ovat tärkeitä. Artikkeli jäsentää nämä näkökohdat selkeästi ja näyttää, mihin arjessa on kiinnitettävä huomiota.
Keskustele projektista tai modernisointihankkeesta Net-Base kanssa.
Seuraava vaihe
Kun aiheesta muodostuu todellinen projekti, arkkitehtuuri, nykytila ja operointi on tarkasteltava yhdessä varhaisessa vaiheessa.
Emme tue pelkästään yksittäiskysymyksissä, vaan myös silloin, kun lähdekoodipalasista, legacy-aiheista tai portaali-ideoista halutaan muodostaa luotettava yrityshanke.
- Nykytila, tavoitetila ja tekniset riskit arvioidaan yhdessä.
- REST, tietojen käyttö, portaalit ja käyttöönotto eivät siirry myöhempään vaiheeseen.
- Näette ajoissa, mikä vaihtoehto on taloudellisesti ja operatiivisesti kannattava.