HotPDF renderer en PDF-transparensgruppe malet med en non-Normal blend mode ved at lade gruppen beregne sin egen blend: Cb(1 − α) + α·B(Cb, Cs), med den gruppe-alpha, den sporer, eller genvinder fra et sort og et hvidt gennemløb. Siden v2.770.0 vælger THotPDF.RenderColorimetricLuminosityMasks-egenskaben også, om luminosity soft masks læser farve som vægtet RGB, ligesom almindelige visere, eller som CIE Y, som ISO 32000-1 beskriver
Begge ændringer kom ud af samme slags fil: en side eksporteret fra et designværktøj, hvor et produktfoto ligger på en blød drop shadow. Skyggen er en Form XObject tegnet som en transparensgruppe med /BM /Multiply, og dens fade-out er en luminosity soft mask. I andre visere er skyggen en lys, neutral grå. I ældre HotPDF-builds kom den næsten sort og tonet mod baggrundsfarven. To separate bugs lå stablet oven på hinanden, og de er værd at skille ad, for enhver renderer, der vokser ud af et "opaque objects first"-design, rammer begge
Hvorfor renderer en Multiply drop shadow for mørk?
En Multiply-skygge renderer for mørk, når rendereren blender gruppens sammensatte resultat i stedet for gruppens egen farve og alpha. Resultatets delvist transparente pixels indeholder allerede baggrunden, så at blande dem med baggrunden multiplicerer baggrunden med sig selv. Normal blending med en opacitet er lineær og overlever denne fejl ubemærket; Multiply, Screen, Darken og alle andre non-lineære modes gør ikke
HotPDF-renderingspipelinen håndterede oprindeligt blend modes pr. malet objekt med en simpel og hurtig model. Før et objekt males, tager rendereren snapshot af de berørte pixels; efter maleriet sammenligner den "before"- og "after"-bitmapmerne og behandler "after"-farven som en opaque kilde Cs, beregner B(Cb, Cs) og compositere den med den aktuelle opacitet. For en udfyldt path, en glyf eller et opaque billede er det præcis rigtigt: kilden er virkelig opaque inde i sin form, og formen er, hvor pixelsne ændrede sig
En transparensgruppe bryder den antagelse. ISO 32000-1 §11.4 definerer en gruppe som noget, der først compositere ind i sit eget resultat med sin egen farve og sin egen alpha og først derefter blandes med baggrunden som ét objekt. Hvor en skygge er 30 procent opaque, holder "after"-bitmap'en omtrent 0,3 af skyggefarven plus 0,7 af siden under den. Føres det ind i Multiply som en opaque kilde, bliver formlen baggrund gange (en værdi, der allerede indeholder baggrunden): skyggen mørkner og får baggrundens kulør. Intet crasher, ingen diagnostik udløses, og siden ser blot forkert ud, hvilket er den værste måde for en renderer at fejle på
Hvordan HotPDF lader en transparensgruppe lave sin egen blending
Siden v2.769.3 flytter HotPDF blenden for en gruppe malet under en non-Normal blend mode ud af den generiske per-objekt-vej og ind i gruppe-rendereren selv. Gruppen beregner Cb(1 − α) + α·B(Cb, Cs) direkte ud fra sin egen farve, sin egen alpha og baggrunds-snapshotet taget til Do-operatoren og markerer så resultatet som allerede blandet, så den omsluttende Do kun compositere det med den aktuelle opacitet i stedet for at blande en gang til. Almindelige objekter beholder den hurtige before-and-after-model, og grupper under Normal blending påvirkes ikke, for det lineære tilfælde var aldrig forkert
Det svære er alpha. En isoleret gruppe (/I true) starter fra en helt transparent baggrund, så rendereren sporer allerede et separat alpha-plan til den og kan læse α direkte. En ikke-isoleret gruppe compositere på, hvad der allerede er på siden, og har intet alpha-plan af sin egen. HotPDF genvinder den ved at tegne gruppen to gange, én gang på sort og én gang på hvid:
- På sort ender hver pixel som
α·Cs - På hvid ender samme pixel som
α·Cs + (1 − α) - Subtraktion giver
α = 1 − (white − black), og derefterCs = black / α
Det virker, fordi hver Normal-composite, rendereren udfører, inklusive GDI AlphaBlend til billeder med soft masks og anti-aliasede kanter, er lineær i baggrunden. Tricket har dog en forudsætning: gruppeindholdet selv skal blande normalt. Sætter gruppens egne ExtGState-ressourcer en non-Normal /BM, adskiller de to gennemløb sig ikke længere ved et rent lineært led, så HotPDF splitter ikke gruppen og falder tilbage på den tidligere adfærd. Tjekket dækker kun gruppens egne ressourcer, ikke indlejrede forms. Omkostningen er også reel: sådanne sider renderer noget langsommere, for gruppen tegnes to gange. Soft masks bygget inde i gruppen bevares, til begge gennemløb er færdige, så andet gennemløb rammer mask-cacheen i stedet for at bygge dem igen
// Illustration af alpha-genvinding pr. kanal (værdier 0..1).
// Black = gruppe tegnet på sort baggrund, White = på hvid baggrund.
procedure RecoverGroupPixel(Black, White: Double;
out Alpha, Source: Double);
begin
Alpha := 1.0 - (White - Black);
if Alpha < 0.0 then Alpha := 0.0;
if Alpha > 1.0 then Alpha := 1.0;
if Alpha > 0.0 then
Source := Black / Alpha // un-premultiply
else
Source := 0.0; // helt transparent: farven er irrelevant
end;
// Multiply blend, derefter gruppe-compositen Cb(1 - a) + a * B(Cb, Cs)
function CompositeMultiply(Backdrop, Source, Alpha: Double): Double;
begin
Result := Backdrop * (1.0 - Alpha) + Alpha * (Backdrop * Source);
end;
Koden ovenfor er en illustration af matematikken, ikke et HotPDF-API; rendereren gør det samme pr. kanal og pr. pixel inde i THotPDF.RenderLoadedPageToBitmap. I praksis mister alpha genvundet fra to 8-bit-gennemløb præcision, når den nærmer sig nul, så meget svage kanter bærer noget kvantiseringsstøj, hvilket er derfor, divisionen kun er meningsfuld, hvor alpha er klart over nul
Blend mode nulstilles til Normal inde i gruppen
Blend mode, ligesom konstant alpha og soft masken, gælder transparensgruppen som helhed og ikke hvert objekt indeni, så gruppeindhold skal starte fra Normal blending (ISO 32000-1 §11.6.6). HotPDF nulstillede tidligere opacitet og soft mask ved gruppe-indgang, men lod blend mode være. Resultatet inde i en isoleret gruppe var dramatisk: indholdet multiplicerede sig selv ind i gruppens tomme begyndelsesbaggrund, som er sort, og hele gruppen malede sort
Samme nulstilling gælder, når en soft mask bygges. Sætter én ExtGState både /BM /Multiply og /SMask, skal maskegruppen tegnes med Normal blending; ellers bliver en hvid maskegruppe sort og skjuler præcis det, masken skulle vise. Begge nulstillinger er en del af v2.769.3, og begge er usynlige på enkle dokumenter, hvilket er, hvordan de levet så længe, som de gjorde
Hvilken værdi bør en luminosity soft mask tage for mellemgrå?
Under ISO 32000-1 bør en luminosity soft mask, hvis gruppe bruger et CIE-baseret blending space, tage CIE 1931 Y, og Y for en sRGB-mellemgrå på 0,5 er kun omkring 0,21, for Y er lineært lys. Almindelige PDF-visere opløser i stedet gruppefarven til RGB og vægter de kodede værdier omtrent 0,30 / 0,59 / 0,11, hvilket giver omkring 0,5 for samme grå. Siden v2.770.0 bruger HotPDF som standard den viser-kompatible vægtning og tilbyder THotPDF.RenderColorimetricLuminosityMasks til specifikations-læsningen
Forskellen er ikke akademisk. En fade i et designværktøjs-eksport er normalt en gradient i en ICCBased- eller kalibreret gruppe, og mellemtoner er præcis der, hvor lineær Y og gamma-kodet grå afviger mest. Med CIE Y slipper en mellemgrå maske kun omkring en femtedel af skyggen igennem i stedet for halvdelen, så drop shadows og vignetter kommer markant lysere ud end i alle de andre visere, kunden åbner filen i. At have ret efter standardens bogstav og forkert efter alles øjne er en dårlig handel for en standardværdi, så standarden følger viserne, og den strikte mode er én egenskab væk
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('catalog-page.pdf') > 0 then
begin
// Standard (False): luminosity-masker vægter den opløste farve
// som DeviceRGB, i tråd med hvad almindelige visere viser.
// True: tag CIE Y, når maskegruppen har et CIE-baseret space.
Pdf.RenderColorimetricLuminosityMasks := False;
Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
if Bmp <> nil then
try
Bmp.SaveToFile('catalog-page-viewer.bmp');
finally
Bmp.Free;
end;
end;
finally
Pdf.Free;
end;
end;
Egenskaben betyder kun noget, når maskegruppens blending space er CIE-baseret: CalGray, CalRGB, Lab eller ICCBased. Masker, hvis gruppe bruger DeviceGray, DeviceRGB eller DeviceCMYK, renderer præcis som før under begge indstillinger, da device-space-konverteringen til grå er den samme i begge modes
Fælder: /BC, /CS og render-cacheen
En luminosity soft mask er kun lige så konsistent som sin baggrund: /BC-farven, der fylder maskeområdet uden for gruppen, skal konverteres med samme regel som malet inde i gruppen. HotPDF ændrede begge sammen i v2.770.0. Konverterer man maleriet med vægtet RGB, men baggrunden med CIE Y, fremkommer en synlig søm, hvor gruppens bounding box slutter, en bug, der er let at indføre, når baggrunden går gennem en anden helper end indholdet
Gruppeattributterne skal også være gyldige, før noget af dette sker. HotPDF læser blending space fra gruppens /CS-post og kræver, at /BC bærer præcis ét tal pr. komponent i det space: tre for et RGB-baseret space, fire for CMYK, én for grå. Mangler /CS, eller har /BC forkert aritet, fejler masken, før gruppen eksekveres, og maleriet tegnes så umaskeret frem for maskeret med en gættet baggrund. Separation, DeviceN og NChannel er ikke lovlige blending spaces, så spot-farve-maling inde i en maskegruppe går gennem sin tint-transform til det alternative space, før luminosity beregnes
Til sidst ændrer indstillingen pixels, så den er en del af cache-nøglen. Bitmapmer renderet med RenderColorimetricLuminosityMasks til og fra caches separat i både hukommelses- og disk-render-cacheen, og at skifte egenskaben mellem kald til THotPDF.RenderLoadedPageToBitmapCached returnerer aldrig en bitmap fra den anden mode. Én operationel note: v2.769.3 indførte en Win64-only regression, hvor sider med en transparensgruppe, der ikke satte nogen soft mask af egen hånd, fejlede i at rendere; den er fikset i v2.770.1, så 64-bit-applikationer bør bruge den udgave eller senere
// Side-om-side-korrektur: render samme side i begge modes.
// Hver mode caches under sin egen nøgle, så gentagne kald er billige.
Pdf.RenderColorimetricLuminosityMasks := False;
ViewerBmp := Pdf.RenderLoadedPageToBitmapCached(PageIndex, 96);
Pdf.RenderColorimetricLuminosityMasks := True;
StrictBmp := Pdf.RenderLoadedPageToBitmapCached(PageIndex, 96);
// ... vis begge, og frigiv dem så: cachede kald returnerer kalder-ejede kopier
ViewerBmp.Free;
StrictBmp.Free;
Til den bredere rendering-API, inklusive DPI-valg, side-cacheen og hvad rendereren stadig approksimerer, se rendering af en PDF-side til en Delphi TBitmap med HotPDF. Er problemet den modsatte retning, at fjerne transparens fra en fil til print eller arkivering frem for at tegne den, dækker soft masks og transparens-flattening i PDFlibPas den side
FAQ
Renderer HotPDF PDF-blend modes inde i transparensgrupper korrekt?
Ja, siden v2.769.3. En gruppe malet med Multiply eller en anden non-Normal blend mode blender sin egen farve og alpha med baggrunden, og dens indhold starter fra Normal blending, som ISO 32000-1 §11.6.6 kræver
Hvorfor renderer HotPDF ikke-isolerede grupper to gange?
En ikke-isoleret gruppe har intet alpha-plan af egen hånd. HotPDF tegner den på sort og på hvid og genvinder alpha som 1 − (white − black), hvilket koster noget fart, men gør Multiply-skygger korrekte
Bør man sætte RenderColorimetricLuminosityMasks til True?
Lad den stå på False for at matche, hvad almindelige PDF-visere viser. Sæt den til True kun, når man har brug for CIE Y-læsningen af ISO 32000-1 til CIE-baserede maskegrupper, for eksempel i en conformance-sammenligning
Blend mode-transparensgrupper, luminosity soft masks og side-rendereren, der tegner dem, er en del af HotPDF Delphi Component, et native VCL-bibliotek til Delphi og C++Builder, der opretter, redigerer og renderer PDF-dokumenter uden eksterne DLL'er