Techninis straipsnis

Šeši PDFium iškvietimai Delphi kalboje pamiršo atvaizdavimo užraktą

PDFiumPas atvaizdavimo užraktas turi apgaubti kiekvieną rastravimo iškvietimą, kad puslapis nebūtų iškrautas vykstančio atvaizdavimo metuEnterRenderLockLeaveRenderLockTRTLCriticalSectionTPdfTPdfTPdfView

Šis auditas tikrina šešias PDFium atvaizdavimo vietas ir paaiškina, kodėl be užrakto kylanti lenktynė sunkiai atkuriamacdecl

Ką iš tikrųjų saugo atvaizdavimo užraktas

TPdf turi FRenderLock ir FRenderLockReady, todėl po sunaikinimo atėjęs iškvietimas saugiai tampa tuščia operacijaTPdfTRTLCriticalSectionFRenderLockFRenderLockReadyEnterRenderLockLeaveRenderLock

procedure TPdf.EnterRenderLock;
begin
  if FRenderLockReady then
    EnterCriticalSection(FRenderLock);
end;

procedure TPdf.LeaveRenderLock;
begin
  if FRenderLockReady then
    LeaveCriticalSection(FRenderLock);
end;

TPdf.RenderPage, RenderTile ir RenderPageProgressive jau naudojo užraktą, tačiau šeši metodai atrodė kaip prieigos metodai, nors rastravo pikseliusTPdf.RenderPageRenderTileRenderPageProgressivefinallyUnloadPageTPdf

Kurie šeši iškvietimai praleido atvaizdavimo užraktą

TPdf.GetObjectBitmap, TPdf.GetBitmap, TPdf.GetThumbnail ir atitinkami TPdfView metodai galiausiai kviečia FPDFImageObj_GetBitmap arba FPDFPage_GetThumbnailAsBitmapTPdf.GetObjectBitmapTPdf.GetBitmapTPdf.GetThumbnailTPdfView.GetObjectBitmapTPdfView.GetBitmapTPdfView.GetThumbnailFPDFImageObj_GetBitmapFPDFPage_GetThumbnailAsBitmapRenderPageRenderTile

function TPdf.GetObjectBitmap(Index: Integer): TBitmap;
var
  Bitmap: FPDF_BITMAP;
begin
  Result:= nil;
  EnterRenderLock;
  try
    Bitmap:= FPDFImageObj_GetBitmap(GetObjectHandle(Index));
  finally
    LeaveRenderLock;
  end;
  if Bitmap<> nil then
    try
      Result:= ToBitmap(Bitmap);
    finally
      FPDFBitmap_Destroy(Bitmap);
    end;
end;

Kodėl TPdfView užrakto iškvietimą tikrina su Nil

TPdfView užrakto kvietimus perduoda į FPdf.EnterRenderLock ir FPdf.LeaveRenderLock tik tada, kai susietas TPdf nėra nilTPdfViewFPdf.EnterRenderLockFPdf.LeaveRenderLockTPdfTPdfViewTPdfFPdf

function TPdfView.GetThumbnail: TBitmap;
var
  PdfBitmap: FPDF_BITMAP;
begin
  CheckActive;
  Result:= nil;
  if FPdf<> nil then
    FPdf.EnterRenderLock;
  try
    PdfBitmap:= FPDFPage_GetThumbnailAsBitmap(Page);
  finally
    if FPdf<> nil then
      FPdf.LeaveRenderLock;
  end;
  if PdfBitmap<> nil then
    try
      Result:= ToBitmap(PdfBitmap);
    finally
      FPDFBitmap_Destroy(PdfBitmap);
    end;
end;

Kodėl RenderPage(HDC) priklauso tam pačiam auditui

TPdfView.RenderPage HDC perkrova ankstesnėje v2.25.0 versijoje taip pat praleido EnterRenderLock ir SetArithmeticMaskTPdfView.RenderPageFPDF_RenderPageEnterRenderLockSetArithmeticMaskTBitmap

procedure TPdfView.RenderPage(DeviceContext: HDC; Left, Top, Width,
  Height: Integer; Rotation: TRotation; Options: TRenderOptions);
var
  ArithmeticMask: TArithmeticMask;
begin
  CheckActive;
  if FPdf<> nil then
    FPdf.EnterRenderLock;
  ArithmeticMask:= SetArithmeticMask;
  try
    FPDF_RenderPage(DeviceContext, FPage, Left, Top, Width, Height,
      Ord(Rotation), EncodeRenderOptions(Options));
  finally
    RestoreArithmeticMask(ArithmeticMask);
    if FPdf<> nil then
      FPdf.LeaveRenderLock;
  end;
end;

Kodėl šią lenktynę beveik neįmanoma atkartoti

Lenktynė pasireiškia tik sutapus rastravimui ir lygiagrečiam UnloadPage arba ReloadPage tam pačiam TPdfTPdfUnloadPageReloadPagePDF fono išankstinį atvaizdavimą su atšaukiamais futures

Atlaisvintos PDFium struktūros gali grąžinti sugadintus pikselius arba vėliau sugadinti krūvos metaduomenisFPDFImageObj_GetBitmapFPDFPage_GetThumbnailAsBitmapUnloadPage

Kas pasikeičia iškvietėjams

Viešosios GetBitmap, GetObjectBitmap, GetThumbnail ir RenderPage signatūros nesikeičia, nes pataisa yra vidinis užraktasGetBitmapGetObjectBitmapGetThumbnailRenderPageTPdfstraipsnį apie PDFium atvaizdavimo podėlį ir mastelio našumą

Užraktas taikomas vienam TPdf, todėl atskiri dokumentai gali būti atvaizduojami lygiagrečiaiPDFium Component