Paina zoom-painiketta pohjassa naiivissa PDF-katselimessa ja katso CPU-kuvaajaa. Yksi autotoistavan zoom-kontrollin painallus laukaisee tusina tai useamman zoom-askelta sekunnissa, ja jos jokainen askel käynnistää täytilaatuisen uudelleenrenderöinnin näkyvästä sivusta, renderöinnit kasaantuvat nopeammin kuin ne valmistuvat. Sivu rasteroituu mainio erillään, ehkä 180 ms A4-skannaukselle, mutta ajat nyt tusinaa 180 ms renderöintiä työtä vasten, jonka ohi käyttäjä on jo siirtynyt. Katselin lukkiutuu, ydin piikittyy 100 %:iin, ja siihen mennessä kun ruutu saa kiinni, käyttäjä on pysähtynyt zoomitasolle neljä renderöintiä sitten. Parannus ei ole nopeampi rasteroija. Se on välimuisti, joka palauttaa valmiit sivut välittömästi, ja renderöintiluuppi, joka on valmis hylkäämään työn heti kun se vanhenee
PDFium Component antaa sinulle osat molempiin ja pysyy poissa politiikasta. Saat kutsujan omistamat bitmapit, progressiivisen renderöijän joka ottaa peruutustokenin, fit-tilat jotka laskevat zoomin uudelleen koolla, ja tiling-kutsun sivuille jotka ovat liian suuria kokonaisena rasteroitaviksi. Se mitä se tahallisesti ei tarjoa, on itse välimuisti, koska oikea poistamispolitiikka riippuu näkymästäsi, alustasi muistikatosta ja siitä miten käyttäjäsi vierittää. Tuo päätös on sinun saatettava oikein, ja seuraamuksen sen saamisesta väärin ovat juuri tuo jumiutuminen ja vuoto
Minne millisekunnit ja megatavut menevät
Laita numeroita kustannukselle ennen kuin suunnittelet mitään. A4-sivu 96 DPI:ssä on suunnilleen 794 kertaa 1123 pikseliä, noin 3.5 MB 32-bittisenä bitmapina. Zoomaa 200 %:iin ja se nelinkertaistuu. 400 %:ssa korkean DPI:n näytöllä varaat ja täytät yhden sivun bitmapin 50–60 MB, ja jatkuva vierityskatselin pitää useita sivuja elossa kerralla. Rasterointikustannus seuraa tulostepikseleitä, joten jokainen zoomin kaksinkertaistaminen nelinkertaistaa suunnilleen sekä renderöintiajan että muistin yhdessä
Kaksi seurausta putoaa suoraan tuosta aritmetiikasta. Välimuisti, jonka avain sivuuttaa zoomitason, on arvoton, koska juuri ele, jota sen pitää nopeuttaa — zoomaus — tuottaa uuden bitmapin joka kerta. Ja rajoittamaton välimuisti ajaa 32-bittisen prosessin ulos osoitetilasta juuri niissä dokumenteissa, joissa ihmiset zoomaavat kovimmin: tiheät lainhuolto-skannaukset, insinööripiirustukset, suurikokoiset kartat. Välimuisti on avainnnettava oikein ja korkattava lujasti, eikä kumpikaan ole valinnainen
Mitä kuuluu välimuistiavaimeen
Välimuistitettu bitmap on turvallinen uudelleenkäyttää vain kun kaikki syötteet, jotka muovasivat sen pikselit, yhä täsmäävät. Se tarkoittaa sivunumeroa, efektiivistä zoomia (tai vastaavasti tulostepikseliulottuvuuksia), kiertoa, näytön DPI:tä ja renderöintivalintoja, jotka olivat voimassa kun se tuotettiin. reAnnotations:llä renderöity sivu on eri kuva kuin sama sivu ilman niitä, ja harmaasävy-läpikäynti reGrayscale:n kautta on jälleen eri. Pudota yksikin näistä avaimesta ja viat ovat ennustettavia: annotaatio-overlay joka viipyy sen jälkeen kun tarkastaja poistaa kommentin, tai sivu joka sumentuu heti kun käyttäjä raahaa ikkunan kannettavan paneelilta ulkoiselle 4K-näytölle ja DPI muuttuu vanhentuneen bitmapin alla
function TPageCache.Acquire(Pdf: TPdf; PageNo: Integer; ZoomPct: Single;
Rotation: TRotation; Opts: TRenderOptions): TBitmap;
var
Key: string;
begin
Key := Format('%d|%.0f|%d|%d|%d',
[PageNo, ZoomPct, Ord(Rotation), Screen.PixelsPerInch, OptionsMask(Opts)]);
if FBitmaps.TryGetValue(Key, Result) then
Exit;
Pdf.PageNumber := PageNo;
Result := Pdf.RenderPage(0, 0, OutputWidth(PageNo, ZoomPct),
OutputHeight(PageNo, ZoomPct), Rotation, Opts);
FBitmaps.Add(Key, Result); // välimuisti omistaa nyt tämän bittikartan
end;
Osumalla tämä palauttaa mikrosekunneissa, mikä on koko pointti. Vaikeampi kysymys on mitä tapahtuu bitmapeille, jotka putoavat välimuistista, ja se osoittautuu kysymykseksi siitä kuka omistaa ne
Kuka vapauttaa bitmapin
RenderPage:n funktiomuoto palauttaa TBitmap:n, jonka kutsuja omistaa. Kertaviennissä omistajuus on ilmeinen ja helppo kunnioittaa. Välimuistin sisällä siitä tulee yksittäisin yleisin vuoto Delphi-PDF-katselimissa, koska sanakirja pitää nyt ainoan viitteen kuhunkin bitmapiin, ja tavallinen TDictionary vapauttaa avaimet ja arvot sinulle vain jos ne ovat hallittuja tyyppejä. TBitmap ei ole. Poista kohde kutsumatta Free:tä ja pikselit pysyvät varattuina ilman mitään niihin osoittavaa
Syy miksi tämä lipsahdtaa läpi on ajoitus. Kymmenen minuutin savutesti ei koskaan zoomaa tarpeeksi erillisiä sivuja huomatakseen; vuoto näyttäytyy vasta kun joku on vierittänyt ja zoomannut pitkää dokumenttia pari tuntia, jolloin prosessi pitää satoja orvoja sivubitmappeja ja kone alkaa sivuttaa. Siksi poisto kuuluu välimuistin ensimmäiseen versioon, ei myöhempään. Korkkaa välimuisti arvioiduilla tavuilla, laskettuna leveys kertaa korkeus kertaa neljä, poista vähiten-viimeksi-käytetyt sivut jotka istuvat näkymän ja prefetch-ikkunan ulkopuolella, ja vapauta jokainen bitmap kun poistat sen. Aidosti ohimeneville piirroille ylikuormat, jotka renderöivät kutsujan tarjoamaan TBitmap:iin tai suoraan HDC:lle, antavat sinun ohittaa omistajatanssin kokonaan. Tulostuksen esikatselu on ilmeinen tapaus, koska renderöit kunkin arkin kerran ja sen välimuistittaminen ei tuo mitään
Progressiivinen renderöinti ja rehellinen peruutus
Tavalliset RenderPage-ylikuormat estävät kunnes sivu on valmis, mikä on tarkalleen se käytös jota et halua silloin kun käyttäjä yhä liikuttaa zoom-kontrollia. Sitä varten tavoittelet RenderPageProgressive:a. Se ottaa IPdfCancellationToken:n ja palauttaa jonkin prsDone, prsCancelled tai prsFailed. Käytösyksityiskohta joka nappaa ihmiset on, ettei peruutus ole välitön. Tokenia äänestetään palo-rajoilla renderöinnin sisällä, joten token jonka signaloit palan keskellä astuu voimaan vasta kun tuo pala valmistuu. Monimutkaisella sivulla viive pyynnön ja pysäyttämisen välillä juoksee kymmeniin millisekunteihin. Suunnittele tuon kuilun ympärille ennemmin kuin toivo se pois: peruuta edellinen token heti kun uusi zoom-arvo saapuu, mutta älä oleta vanhan renderöinnin pysähtyvän heti kun pyydät sitä
procedure TViewerForm.RequestRender(TargetZoom: Single);
var
Status: TPdfProgressiveStatus;
begin
if FTokenSource <> nil then
FTokenSource.Cancel; // hylkää edellinen käynnissä oleva renderöinti
FTokenSource := TPdfCancellationTokenSource.New; // FPdfAsync unit
Status := Pdf.RenderPageProgressive(FBackBuffer, 0, 0,
FBackBuffer.Width, FBackBuffer.Height, FTokenSource.Token,
ro0, [reAnnotations]);
case Status of
prsDone: PresentBackBuffer;
prsCancelled: ; // superseded by a newer request: drop silently
prsFailed: ShowRenderFailure;
end;
end;
Vuorovaikutuksen aikana prsCancelled on normaali lopputulos, ei poikkeuksellinen. Suurin osa renderöinneistä, joita zoom-ele aloittaa, syrjäytetään ennen kuin ne valmistuvat, joten kohtele peruutusta rutiinina ja pudota tulos hiljaa. Renderöintijono, joka lokittaa jokaisen peruutuksen varoituksena, hautaa sen yhden epäonnistumisen, jolla oikeasti on merkitystä, tuhansien rivien kohinan alle. Pidääksesi ruudun näyttämästä kuolleelta kun todellinen renderöinti ajaa, parita progressiivinen polku halvalla sijaisella: skaalaa edellinen välimuistitettu bitmap uuteen zoomiin ja esitä se välittömästi. Se näyttää pehmeältä sadan tai parin millisekunnin ajan, mutta se luetaan välittömäksi, ja se antaa täyden laadun renderöinnille ajan jonka se tarvitsee joko valmistuakseen tai peruutuakseen seuraavan eleen toimesta
Fit-tila jonka zoom hiljaa sammuttaa
Katselimen FitMode-ominaisuus, asetettu pfmFitPage:en tai pfmFitWidth:iin, laskee zoomin uudelleen jokaisella koolla niin, että sivu jatkaa sopimista kun ikkuna muuttuu. Saali on, että Zoom:n suora asettaminen resetoi FitMode:n takaisin pfmNone:en. Oletuksena tuo on oikein: käyttäjä, joka tahallisesti kirjoitti 150 %, ei halua seuraavan ikkunan koon muutoksen heittävän sitä pois. Mutta se yllättää kenet tahansa, joka kytkkee zoom-in-painikkeen muotoon Zoom := Zoom * 1.25 eikä sitten keksi miksi fit-to-width lakkasi vastaamasta ensimmäisen klikkauksen jälkeen. Jos työkalurivesi tarjoaa sekä eksplisiittisen zoomin että fit-tilat, sinun on muistettava käyttäjän viimeisin fit-valinta itse ja asetettava se uudelleen kun hän painaa fit-painiketta taas. Komponentti ei palauta tilaa, jonka zoom-asennus juuri tyhjensi, eikä sen pitäkään
Muistibudjetti jota voit puolustaa
Budjetti jonka voit kirjoittaa ylös on budjetti jota voit perustella koodikatselmuksessa, joten aloita konkreettisesta skenaariosta. Sano että jatkuva vieritys pitää näkyvän sivun plus yhden prefetchatun sivun ylä- ja alaviereen, thumbnail-nauhan rinnalla. 100 %:ssa 96-DPI:n näytöllä nuo kolme kokokokoista bitmapia tulevat noin 3.5 MB kukin, mikä ei ole mitään. 300 %:ssa 4K-näytöllä samat kolme bitmapia ovat suunnilleen 30 MB kukin, ja se on ennen kuin välimuisti on pidättänyt yksikään historiallinen sivu. Kasvu on eleessä, ei dokumentissa
Terve oletus 32-bittiselle Delphi-prosessille on 256 MB bitmap-budjetti LRU-poiston alla. 64-bittisellä voit skaalata fyysisen RAM:n kanssa, mutta pidä kova katto joka tapauksessa, koska vika jota vartioit ei ole prosessisi kaatuminen. Se on koko koneen sivutustiedoston trashaus silloin kun katselimesi teknisesti jatkaa pyörimistä ja käyttäjä ihmettelee miksi kaikki muu hidastui. Kova korkko epäonnistuu ennustettavasti; rajoittamaton välimuisti epäonnistuu viemällä työpöydän mukanaan. Thumbnailit ansaitsevat oman käsittelynsä: renderöi kukin kerran sen pienessä kohdekoossa ja pidä se erillisessä altaassa jota LRU-logiikka ei koske. 120-pikselisen thumbnailin uudelleentuottaminen 60 MB koko sivun bitmapia pienentämällä on tuhlailevin mahdollinen tapa tuottaa postimerkki
Jotkut yksittäiset sivut voittavat minkä tahansa budjetin. E-kokoinen insinööripiirustus tai suuri kartta renderöitynä kokonaisena 400 %:ssa on useiden satojen megatavujen varaus, eikä mikään poistopolitiikka tee sitä hyväksyttäväksi. Vastaus siellä on lopettaa kokonaisten sivujen renderöinti. RenderTile rasteroi vain alueen pikseli-offsetissa (Left, Top) sivun sisällä, joka notionaalisesti skaalataan PageWidth kertaa PageHeight, joten renderöit vain näkyvän suorakulion plus yhden laatan marginaalin sen ympärille sulkeaan panoraamista varten, ja taitat laita-offsetit välimuistiavaimeen zoomin rinnalle. Pidä laitaulottuvuudet kiinteinä dokumentin yli. Kiinteät laitat tarkoittavat, että DPI-muutos invalidoi koko ruudukon siististi, missä muuttuvat laitat jättävät sinut jahtaamaan näkyviä saumoja alueiden välillä jotka on renderöity hieman eri skaaloissa
Kaksi vierekkäistä ominaisuutta lisää hiljaa kaikkeen tähän. Värifilter-läpikäynnit kuten harmaasävy tai invertointi ajavat renderöinnin jälkeen ja tuottavat toisen kokokokoisen bitmapin joka kerta, kaksinkertaistaen sivukohtaisen jalanjäljen missä tahansa näkymässä joka käyttää niitä; tuo kustannus on heikkonäköisen värifilteröinnin Delphi-PDF-katselimille aihe. Ja katselin joka korostaa sanoja teksti-puheeksi muuttamisen aikana invalidoi renderöidyn näkymän jokaisella puhutulla sanalla, joten vuorovaikutus korostus-uudelleenpiirtojen ja puhenopeuden välillä merkitsee enemmän kuin aluksi näyttää, kuten on käsitelty sana-sanalta TTS-korostuksessa
Renderöintiylikuormat, progressiiviset tilakoodit ja itse katselinkomponentti on dokumentoitu PDFium Component:n tuotesivulla