HotPDF rendert een PDF-transparantiegroep getekend met een non-Normal blend mode door de groep zijn eigen blend te laten berekenen: Cb(1 − α) + α·B(Cb, Cs), met de groeps-alpha die hij bijhoudt of terugwint uit een zwarte en een witte passage. Sinds v2.770.0 kiest de property THotPDF.RenderColorimetricLuminosityMasks er ook of luminosity soft masks kleur lezen als gewogen RGB, zoals gangbare viewers, of als CIE Y, zoals ISO 32000-1 beschrijft
Beide wijzigingen kwamen uit hetzelfde soort bestand: een pagina geëxporteerd uit een designtool, waar een productfoto op een zachte slagschaduw staat. De schaduw is een Form XObject getekend als transparantiegroep met /BM /Multiply, en zijn uitfading is een luminosity soft mask. In andere viewers is de schaduw een licht, neutraal grijs. In oudere HotPDF-builds kwam hij er bijna zwart uit en getint richting de achtergrondkleur. Twee aparte bugs waren op elkaar gestapeld, en ze zijn het ontleden waard, want elke renderer die groeit uit een "opaque objects first"-ontwerp raakt ze allebei
Waarom rendert een Multiply-slagschaduw te donker?
Een Multiply-schaduw rendert te donker wanneer de renderer het gecompositede resultaat van de groep blend in plaats van de eigen kleur en alpha van de groep. De half transparante pixels van dat resultaat bevatten de achtergrond al, dus ze er mee vermengen vermenigvuldigt de achtergrond met zichzelf. Normal blending met een opacity is lineair en overleeft deze fout onopgemerkt; Multiply, Screen, Darken en elke andere non-lineaire modus niet
De renderingpijplijn van HotPDF behandelde blend modes oorspronkelijk per getekend object met een simpel en snel model. Voordat een object getekend wordt, maakt de renderer een momentopname van de geraakte pixels; na het tekenen vergelijkt hij de "before"- en "after"-bitmaps en behandelt de "after"-kleur als een ondoorlatende bron Cs, berekent B(Cb, Cs) en compositeert die met de huidige opacity. Voor een gevuld pad, een glyph of een dekkend beeld is dat precies goed: de bron is echt ondoorlatend binnen zijn vorm, en de vorm is waar de pixels veranderden
Een transparantiegroep breekt die aanname. ISO 32000-1 §11.4 definieert een groep als iets dat eerst in zijn eigen resultaat gecompositeerd wordt, met zijn eigen kleur en zijn eigen alpha, en pas daarna als één object met de achtergrond gemengd wordt. Waar een schaduw voor 30 procent dekkend is, bevat de "after"-bitmap grofweg 0.3 van de schaduwkleur plus 0.7 van de pagina eronder. Stop dat als ondoorlatende bron in Multiply en de formule wordt achtergrond keer (een waarde die de achtergrond al bevat): de schaduw wordt donkerder en neemt de tint van de achtergrond aan. Niets crasht, geen diagnostic slaat aan, en de pagina ziet er simpelweg verkeerd uit — de slechtste manier waarop een renderer kan falen
Hoe HotPDF een transparantiegroep zijn eigen blending laat doen
Sinds v2.769.3 verplaatst HotPDF de blend voor een groep getekend onder een non-Normal blend mode uit het generieke per-object-pad naar de groepsrenderer zelf. De groep berekent Cb(1 − α) + α·B(Cb, Cs) rechtstreeks uit zijn eigen kleur, zijn eigen alpha en de achtergrondmomentopname die voor de Do-operator genomen werd, en markeert het resultaat als al geblend, zodat de omsluitende Do het alleen nog met de huidige opacity compositeert in plaats van een tweede keer te blenden. Gewone objecten behouden het snelle before-and-after-model, en groepen onder Normal blending blijven onaangetast omdat het lineaire geval nooit fout zat
Het moeilijke deel is alpha. Een isolated group (/I true) vertrekt vanaf een volledig transparante achtergrond, dus de renderer houdt er al een apart alphavlak voor bij en kan α rechtstreeks lezen. Een non-isolated groep compositeert op wat er al op de pagina staat en heeft geen eigen alphavlak. HotPDF wint hem terug door de groep twee keer te tekenen, een keer op zwart en een keer op wit:
- Op zwart eindigt elke pixel als
α·Cs - Op wit eindigt dezelfde pixel als
α·Cs + (1 − α) - Aftrekken geeft
α = 1 − (white − black), en daarnaCs = black / α
Dit werkt omdat elke Normal-composite die de renderer uitvoert, inclusief GDI AlphaBlend voor beelden met soft masks en geantialiaste randen, lineair in de achtergrond is. De truc heeft wel een voorwaarde: de groepsinhoud zelf moet normaal blenden. Zet de eigen ExtGState-resources van de groep een non-Normal /BM, dan verschillen de twee passages niet meer met een schone lineaire term, dus splitst HotPDF de groep niet en valt hij terug op het vorige gedrag. De controle ziet alleen op de eigen resources van de groep, niet op geneste forms. De kosten zijn ook echt: zulke pagina's renderen wat trager omdat de groep twee keer getekend wordt. Soft masks die binnen de groep gebouwd worden, blijven bewaard tot beide passages klaar zijn, dus de tweede passage treft de mask-cache in plaats van ze opnieuw te bouwen
// Illustratie van het alpha-herstel per kanaal (waarden 0..1).
// Black = groep getekend op een zwarte achtergrond, White = op een witte.
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; // volledig transparant: kleur is irrelevant
end;
// Multiply-blend, daarna de groepscomposite Cb(1 - a) + a * B(Cb, Cs)
function CompositeMultiply(Backdrop, Source, Alpha: Double): Double;
begin
Result := Backdrop * (1.0 - Alpha) + Alpha * (Backdrop * Source);
end;
De code hierboven is een illustratie van de wiskunde, geen HotPDF-API; de renderer doet hetzelfde per kanaal en per pixel binnen THotPDF.RenderLoadedPageToBitmap. In de praktijk verliest alpha die uit twee 8-bits passages teruggewonnen wordt precisie naarmate hij naar nul nadert, dus heel flauwe randen dragen wat kwantiseringsruis — vandaar dat de deling alleen zin heeft waar alpha duidelijk boven nul zit
De blend mode reset naar Normal binnen de groep
De blend mode geldt, net als de constante alpha en het soft mask, voor de transparantiegroep als geheel en niet voor elk object erin, dus groepsinhoud moet vertrekken vanaf Normal blending (ISO 32000-1 §11.6.6). HotPDF resette voorheen opacity en soft mask bij groepingang maar liet de blend mode met rust. Het resultaat binnen een isolated group was dramatisch: de inhoud vermenigvuldigde zichzelf in de lege beginachtergrond van de groep, die zwart is, en de hele groep tekende zwart
Dezelfde reset geldt wanneer een soft mask gebouwd wordt. Zet één ExtGState zowel /BM /Multiply als /SMask, dan moet de maskergroep met Normal blending getekend worden; anders wordt een witte maskergroep zwart en verbergt precies wat het masker had moeten tonen. Beide resets maken deel uit van v2.769.3, en beide zijn onzichtbaar op simpele documenten — daarom hebben ze zo lang geleefd als ze deden
Welke waarde hoort een luminosity soft mask aan te houden voor midgrijs?
Volgens ISO 32000-1 hoort een luminosity soft mask waarvan de groep een CIE-based blending space gebruikt CIE 1931 Y aan te houden, en Y voor een sRGB-midgrijs van 0.5 is maar zo'n 0.21, want Y is lineair licht. Gangbare PDF-viewers lossen de groepskleur in plaats daarvan op naar RGB en wegen de gecodeerde waarden ruwweg 0.30 / 0.59 / 0.11, wat voor hetzelfde grijs zo'n 0.5 oplevert. Sinds v2.770.0 kiest HotPDF als default de viewer-compatibele weging en biedt THotPDF.RenderColorimetricLuminosityMasks voor de specificatie-lezing
Het gat is niet academisch. Een fade in een export uit een designtool is meestal een gradiente in een ICCBased- of calibrated groep, en middentonen zijn precies waar lineaire Y en gamma-gecodeerd grijs het sterkst uiteenlopen. Met CIE Y laat een midgrijs masker maar ongeveer een vijfde van de schaduw door in plaats van de helft, dus slagschaduwen en vignetten komen merkbaar lichter uit dan in elke andere viewer waarin de klant het bestand opent. Volgens de letter van de standaard gelijk en volgens ieders ogen ongelijk zijn een slechte ruil voor een default, dus de default volgt de viewers en de strenge modus ligt één property ver
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('catalog-page.pdf') > 0 then
begin
// Default (False): luminosity-maskers wegen de opgeloste kleur
// als DeviceRGB, passend bij wat gangbare viewers tonen.
// True: neem CIE Y wanneer de maskergroep een CIE-based space heeft.
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;
De property telt alleen wanneer de blending space van de maskergroep CIE-based is: CalGray, CalRGB, Lab of ICCBased. Maskers waarvan de groep DeviceGray, DeviceRGB of DeviceCMYK gebruikt, renderen onder beide instellingen exact als voorheen, want de device-space-omzetting naar grijs is in beide modi gelijk
Valkuilen: /BC, /CS en de rendercache
Een luminosity soft mask is alleen zo consistent als zijn achtergrond: de /BC-kleur die het maskergebied buiten de groep vult, moet met dezelfde regel geconverteerd worden als de verf binnen de groep. HotPDF veranderde beide tegelijk in v2.770.0. De verf converteren met gewogen RGB maar de achtergrond met CIE Y levert een zichtbare naad op waar de bounding box van de groep eindigt — een bug die makkelijk ontstaat wanneer de achtergrond door een andere helper gaat dan de inhoud
De groepsattributen moeten ook geldig zijn voordat dit alles plaatsvindt. HotPDF leest de blending space uit de /CS-entry van de groep en eist dat /BC precies één getal per component van die space voert: drie voor een RGB-based space, vier voor CMYK, één voor grijs. Een ontbrekende /CS of een /BC met verkeerde ariteit laat het masker falen voordat de groep uitvoert, en de verf wordt dan zonder masker getekend in plaats van gemaskerd met een gegokte achtergrond. Separation, DeviceN en NChannel zijn geen legale blending spaces, dus steunkleurverf binnen een maskergroep gaat via zijn tint transform naar de alternate space voordat de luminositeit berekend wordt
Tot slot: de instelling verandert pixels, dus hij hoort bij de cache-key. Bitmaps gerenderd met RenderColorimetricLuminosityMasks aan en uit worden apart gecached in zowel de memory- als de disk-rendercache, en het omschakelen van de property tussen aanroepen van THotPDF.RenderLoadedPageToBitmapCached geeft nooit een bitmap uit de andere modus terug. Eén operationele noot: v2.769.3 introduceerde een Win64-only regressie waarbij pagina's met een transparantiegroep die geen eigen soft mask zette, niet renderten; die is gefixt in v2.770.1, dus 64-bit-toepassingen doen er goed aan die release of nieuwer te gebruiken
// Proof naast elkaar: render dezelfde pagina in beide modi.
// Elke modus wordt onder eigen key gecached, dus herhaald aanroepen is goedkoop.
Pdf.RenderColorimetricLuminosityMasks := False;
ViewerBmp := Pdf.RenderLoadedPageToBitmapCached(PageIndex, 96);
Pdf.RenderColorimetricLuminosityMasks := True;
StrictBmp := Pdf.RenderLoadedPageToBitmapCached(PageIndex, 96);
// ... toon beide en geef ze vrij: cached calls geven exemplaren in eigendom van de aanroeper terug
ViewerBmp.Free;
StrictBmp.Free;
Voor de bredere rendering-API, inclusief de DPI-keuze, de pagina-cache en wat de renderer nog benadert, zie een PDF-pagina renderen naar een Delphi TBitmap met HotPDF. Ligt uw probleem in de tegenovergestelde richting — transparantie uit een bestand halen voor drukwerk of archivering in plaats van het te tekenen — dan behandelt soft masks en transparency flattening in PDFlibPas die kant
FAQ
Rendert HotPDF PDF-blend modes binnen transparantiegroepen correct?
Ja, sinds v2.769.3. Een groep getekend met Multiply of een andere non-Normal blend mode blendt zijn eigen kleur en alpha met de achtergrond, en zijn inhoud vertrekt vanaf Normal blending zoals ISO 32000-1 §11.6.6 voorschrijft
Waarom rendert HotPDF non-isolated groepen twee keer?
Een non-isolated groep heeft geen eigen alphavlak. HotPDF tekent hem op zwart en op wit en wint alpha terug als 1 − (white − black), wat wat snelheid kost maar Multiply-schaduwen correct maakt
Moet ik RenderColorimetricLuminosityMasks op True zetten?
Laat hem op False staan om te matchen wat gangbare PDF-viewers tonen. Zet hem alleen op True wanneer u de CIE Y-lezing van ISO 32000-1 nodig heeft voor CIE-based maskgroepen, bijvoorbeeld in een conformantie-vergelijking
Blend-mode-transparantiegroepen, luminosity soft masks en de paginarenderder die ze tekent, maken deel uit van de HotPDF Delphi Component, een native VCL-bibliotheek voor Delphi en C++Builder die PDF-documenten creëert, bewerkt en rendert zonder externe DLL's