Sinhrono upodabljanje blokirа nit za svojo celotno trajanje. Za preprosto stran to ni opazno. Za kompleksno stran z gradientnimi senčili, maskami prosojna ali visoko ločljivostjo je trajanje upodabljanja merljivo in je od perspektive UI nerazlikljivo od zamrznjene aplikacije. Dve rešitvi obstajata: premaknite upodabljanje v nit ozadja ali posnemite vmesne točke za ustavitev in nadzirajte trajanje iz zanke, ki teče tam, kjer teče upodabljanje. PDFium ponuja mehanizem za slednje prek strukture IFSDK_PAUSE, in vezava PDFium VCL ga izpostavlja prek IPdfCancellationToken
Postopni API upodabljanja, ki ga PDFium že dostavi
PDFium razkrije tri funkcije za postopno upodabljanje: FPDF_RenderPageBitmap_Start, FPDF_RenderPageBitmap_Continue in FPDF_RenderPageBitmap_Close. Start inicializira kontekst upodabljanja in začne upodabljati, dokler ne prispe do vmesne točke ali zaključi. Vrne eno od treh vrednosti: FPDF_RENDER_DONE (zaključeno), FPDF_RENDER_TOBECONTINUED (delno, znova pokliči) ali FPDF_RENDER_FAILED. Continue nadaljuje od zadnje vmesne točke in vrne iste tri vrednosti. Close sprosti kontekst upodabljanja ne glede na to, ali je upodabljanje zaključeno ali ne
Vmesne točke so kontrolirane s kazalcem na strukturo IFSDK_PAUSE, ki ga posredujete Start in Continue. Struktura vsebuje en sam povratni klic -- NeedToPauseNow -- ki ga PDFium kliče periodično med upodabljanjem. Ko ta povratni klic vrne neničlo vrednost, PDFium shrani stanje upodabljanja in vrne FPDF_RENDER_TOBECONTINUED. Ko vrne nič, upodabljanje nadaljuje brez prekinitve
// The pause structure, as the PDFium C header defines it
type
IFSDK_PAUSE = record
version: Integer;
NeedToPauseNow: function(pThis: PIFSDK_PAUSE): FPDF_BOOL; cdecl;
user: Pointer; // caller-defined context; passed back on each callback
end;
Prenamembanje pavze kot preklica
Privzeto NeedToPauseNow vrne neničlo vrednost po fiksnem intervalu časa, tako da upodabljanje naredi napredek v majhnih korakih in klic Continue teče postopoma v zanki. Da bi to spremenili v odpovedljivo upodabljanje, zamenjate logiko vmesne točke: namesto merjenja časa preverite odpovedni žeton. Ko je žeton nastavljen, NeedToPauseNow vrne neničlo vrednost pri vsakem klicu. PDFium zaznava premik prekinitve vmesne točke, shrani stanje in vrne FPDF_RENDER_TOBECONTINUED. Zanka ga dobita in se zaključi
function NeedToPauseCallback(pThis: PIFSDK_PAUSE): FPDF_BOOL; cdecl;
var
Token: IPdfCancellationToken;
begin
// pThis^.user holds the cancellation token interface reference
Token := IPdfCancellationToken(pThis^.user);
if Token.IsCancelled then
Result := 1 // non-zero: pause immediately
else
Result := 0; // zero: keep rendering
end;
Ohranjanje žetona živega čez zanko
Kazalec user v strukturi IFSDK_PAUSE je surov kazalec. Delphi ne doda štetja referenc na vmesnik, shranjen prek surovega kazalca, kar pomeni, da mora biti vmesnik žeton ohranjen živ prek drugega mehanizma, dokler je struktura IFSDK_PAUSE v uporabi. Pravilna oblika je hraniti vmesnik v lokalni spremenljivki, ki živi na klicnem skladu za čas trajanja zanke upodabljanja. Ko se zanka zaključi, se vmesnik sprosti po naravni poti
procedure RenderWithCancellation(
Page: FPDF_PAGE; Bitmap: FPDF_BITMAP;
Token: IPdfCancellationToken);
var
Pause: IFSDK_PAUSE;
Status: Integer;
begin
Pause.version := 1;
Pause.NeedToPauseNow := @NeedToPauseCallback;
Pause.user := Pointer(Token); // raw pointer; Token kept alive by local var above
Status := FPDF_RenderPageBitmap_Start(Bitmap, Page, 0, 0,
BitmapWidth, BitmapHeight, 0, FPDF_ANNOT, @Pause);
while Status = FPDF_RENDER_TOBECONTINUED do
begin
if Token.IsCancelled then Break;
Status := FPDF_RenderPageBitmap_Continue(Bitmap, Page, @Pause);
end;
FPDF_RenderPageBitmap_Close(Page);
end;
Zapiranje konteksta upodabljanja ne glede na to, kako se zanka konča
Klic FPDF_RenderPageBitmap_Close mora priti ne glede na to, ali je upodabljanje zaključeno ali preklicano. PDFium ohrani notranje stanje za trajanje konteksta postopnega upodabljanja in ne sprosti tega stanja, dokler ni Close poklican. Izpustitev klica Close pušča puščanje v PDFiumovem rezervatniku pomnilnika, ki se kopiči z vsakim preklicanjem. Vzorec try/finally je priporočena oblika
Status := FPDF_RenderPageBitmap_Start(...);
try
while Status = FPDF_RENDER_TOBECONTINUED do
begin
if Token.IsCancelled then Break;
Status := FPDF_RenderPageBitmap_Continue(Bitmap, Page, @Pause);
end;
finally
FPDF_RenderPageBitmap_Close(Page); // always runs
end;
Tri izide in kaj bitna slika drži po preklicu
Zanka upodabljanja se konča z enim od treh izidov. Prvič, Status = FPDF_RENDER_DONE: upodabljanje je zaključeno in bitna slika je polno upodobljena. Drugič, Token.IsCancelled prekine zanko pred FPDF_RENDER_DONE: bitna slika drži delno upodabljanje -- kar koli je PDFium narisal do zadnje vmesne točke je v bitmapi, preostanek pa je pobarvan z barvo ozadja ali prejšnjo vsebino, odvisno od tega, kako je bila bitna slika inicializirana. Tretjič, Status = FPDF_RENDER_FAILED: upodabljanje je naletelo na notranjo napako in bitna slika morda drži nič ali delno vsebino
Preklicana bitna slika ni nujno neuporabna. Ker PDFium upodablja v naravnem vrstnem redu operaterjev toka vsebine in so operaterji pogosto razporejeni od ozadja do ospredja, je delno upodabljanje pogosto prepoznavno vsebino strani. Aplikacija se lahko odloči prikazati delno bitno sliko med čakanjem na zamenjavo ali jo takoj zavrže
Ničelni žeton in pot povratnega klica brez veje
Ko je odpovedni žeton neobvezna lastnost -- to je, ko klicatelj ne poda žetona -- je mehanizem povratnega klica manjši stres, če je mogoče brez veje. Eden od načinov je privzet ničelni žeton, ki vedno vrne False za IsCancelled. Ko je ta ničelni žeton priložen povratnemu klicu NeedToPauseNow, zanka upodabljanja deluje normalno, nikoli ne zaustavi zgodaj in kontekst IFSDK_PAUSE deluje povsem normalno. Klicatelj, ki ne poda žetona, dobi sinhrono vedenje brez posebnih poti v kodi upodabljanja
// A nil token always returns False for IsCancelled
type
TNullCancellationToken = class(TInterfacedObject, IPdfCancellationToken)
public
function IsCancelled: Boolean;
end;
function TNullCancellationToken.IsCancelled: Boolean;
begin
Result := False;
end;
Ta vzorec ohranja logiko upodabljanja enotno ne glede na to, ali je odpovednost vklopljena ali ne. Ni blokov if Assigned(Token), razpršenih po zanki upodabljanja; ničelni objekt jih naredi odvečne. Postopno upodabljanje PDFium in odpovedni žeton sta del komponente PDFium VCL za Delphi in C++Builder