HotPDF renderiza un grupo de transparencia PDF pintado con un blend mode distinto de Normal dejando que el grupo calcule su propio blend: Cb(1 − α) + α·B(Cb, Cs), usando el alfa del grupo que rastrea o que recupera de una pasada sobre negro y una sobre blanco. Desde v2.770.0 la propiedad THotPDF.RenderColorimetricLuminosityMasks también elige si las soft masks de luminosidad leen el color como RGB ponderado, como los visores comunes, o como CIE Y, como describe ISO 32000-1
Ambos cambios salieron del mismo tipo de archivo: una página exportada desde una herramienta de diseño, donde la foto de un producto se apoya sobre una sombra suave. La sombra es un Form XObject dibujado como grupo de transparencia con /BM /Multiply, y su desvanecimiento es una soft mask de luminosidad. En otros visores la sombra es un gris claro y neutro. En builds antiguos de HotPDF salía casi negra y teñida hacia el color del fondo. Dos bugs separados estaban apilados uno encima del otro, y vale la pena desarmarlos porque cualquier renderizador que crezca desde un diseño de "objetos opacos primero" va a chocar con ambos
¿Por qué una sombra Multiply se renderiza demasiado oscura?
Una sombra Multiply se renderiza demasiado oscura cuando el renderizador mezcla el resultado compuesto del grupo en lugar del color y alfa propios del grupo. Los píxeles parcialmente transparentes de ese resultado ya contienen el fondo, así que mezclarlos con el fondo multiplica el fondo por sí mismo. El blending Normal con una opacidad es lineal y sobrevive a este error sin que se note; Multiply, Screen, Darken y todos los demás modos no lineales no
El pipeline de render de HotPDF manejaba originalmente los blend modes por objeto pintado con un modelo simple y rápido. Antes de pintar un objeto, el renderizador toma una foto de los píxeles afectados; después de pintar, compara los bitmaps "antes" y "después" y trata el color "después" como una fuente opaca Cs, calculando B(Cb, Cs) y componiéndola con la opacidad actual. Para un path relleno, un glifo o una imagen opaca eso es exactamente correcto: la fuente de verdad es opaca dentro de su forma, y la forma es donde los píxeles cambiaron
Un grupo de transparencia rompe esa suposición. ISO 32000-1 §11.4 define un grupo como algo que primero se compone en su propio resultado, con su propio color y su propio alfa, y solo entonces se mezcla con el fondo como un único objeto. Donde una sombra es 30 por ciento opaca, el bitmap "después" carga aproximadamente 0.3 del color de la sombra más 0.7 de la página de debajo. Alimente eso a Multiply como fuente opaca y la fórmula se vuelve fondo por (un valor que ya contiene el fondo): la sombra se oscurece y agarra el tono del fondo. Nada crashea, ningún diagnóstico se dispara, y la página simplemente se ve mal, que es la peor forma de fallar para un renderizador
Cómo HotPDF deja que un grupo de transparencia haga su propio blending
Desde v2.769.3, HotPDF mueve el blend de un grupo pintado bajo un blend mode distinto de Normal fuera de la ruta genérica por objeto y hacia el renderizador del grupo mismo. El grupo calcula Cb(1 − α) + α·B(Cb, Cs) directamente desde su propio color, su propio alfa y la foto del fondo tomada para el operador Do, y luego marca el resultado como ya mezclado para que el Do envolvente solo lo componga con la opacidad actual en lugar de mezclarlo una segunda vez. Los objetos planos conservan el modelo rápido de antes y después, y los grupos bajo blending Normal no se ven afectados porque el caso lineal nunca estuvo mal
La parte difícil es el alfa. Un grupo aislado (/I true) arranca desde un fondo totalmente transparente, así que el renderizador ya rastrea un plano de alfa separado para él y puede leer α directamente. Un grupo no aislado se compone sobre lo que ya esté en la página y no tiene plano de alfa propio. HotPDF lo recupera dibujando el grupo dos veces, una sobre negro y otra sobre blanco:
- Sobre negro, cada píxel termina como
α·Cs - Sobre blanco, el mismo píxel termina como
α·Cs + (1 − α) - Restar da
α = 1 − (white − black), y luegoCs = black / α
Esto funciona porque cada composite Normal que el renderizador ejecuta, incluido el AlphaBlend de GDI para imágenes con soft masks y bordes con anti-aliasing, es lineal en el fondo. El truco tiene una precondición, eso sí: el contenido del grupo mismo debe mezclarse de forma Normal. Si los recursos ExtGState del propio grupo fijan un /BM distinto de Normal, las dos pasadas ya no difieren en un término lineal limpio, así que HotPDF no divide el grupo y cae de vuelta al comportamiento anterior. La revisión cubre solo los recursos del propio grupo, no los forms anidados. El costo también es real: esas páginas renderizan algo más lento porque el grupo se dibuja dos veces. Las soft masks construidas dentro del grupo se conservan hasta que ambas pasadas terminan, así que la segunda pasada golpea el cache de máscaras en lugar de reconstruirlas
// Ilustración de la recuperación de alfa por canal (valores 0..1).
// Black = grupo dibujado sobre fondo negro, White = sobre fondo blanco.
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; // totalmente transparente: el color es irrelevante
end;
// Blend Multiply, luego el composite del grupo Cb(1 - a) + a * B(Cb, Cs)
function CompositeMultiply(Backdrop, Source, Alpha: Double): Double;
begin
Result := Backdrop * (1.0 - Alpha) + Alpha * (Backdrop * Source);
end;
El código de arriba es una ilustración de la matemática, no una API de HotPDF; el renderizador hace lo mismo por canal y por píxel dentro de THotPDF.RenderLoadedPageToBitmap. En la práctica, el alfa recuperado de dos pasadas de 8 bits pierde precisión al acercarse a cero, así que los bordes muy tenues cargan algo de ruido de cuantización, que es por lo que la división solo tiene sentido donde el alfa está claramente sobre cero
El blend mode se reinicia a Normal dentro del grupo
El blend mode, igual que el alfa constante y la soft mask, aplica al grupo de transparencia como un todo y no a cada objeto dentro de él, así que el contenido del grupo debe arrancar desde blending Normal (ISO 32000-1 §11.6.6). HotPDF antes reiniciaba la opacidad y la soft mask al entrar al grupo pero dejaba el blend mode tranquilo. El resultado dentro de un grupo aislado era dramático: el contenido se multiplicaba a sí mismo sobre el fondo inicial vacío del grupo, que es negro, y todo el grupo se pintaba negro
El mismo reinicio aplica cuando se construye una soft mask. Si un ExtGState fija a la vez /BM /Multiply y /SMask, el grupo de la máscara debe dibujarse con blending Normal; de lo contrario un grupo de máscara blanco se vuelve negro y esconde exactamente lo que la máscara debía revelar. Ambos reinicios son parte de v2.769.3, y ambos son invisibles en documentos simples, que es como vivieron tanto tiempo
¿Qué valor debe tomar una soft mask de luminosidad para gris medio?
Bajo ISO 32000-1, una soft mask de luminosidad cuyo grupo usa un espacio de blending basado en CIE debe tomar CIE 1931 Y, y la Y de un gris medio sRGB de 0.5 es solo alrededor de 0.21 porque Y es luz lineal. Los visores PDF comunes en cambio resuelven el color del grupo a RGB y ponderan los valores codificados aproximadamente 0.30 / 0.59 / 0.11, lo que da alrededor de 0.5 para el mismo gris. Desde v2.770.0 HotPDF usa por defecto la ponderación compatible con visores y ofrece THotPDF.RenderColorimetricLuminosityMasks para la lectura de la especificación
El hueco no es académico. Un desvanecimiento en una exportación de herramienta de diseño suele ser un gradiente en un grupo ICCBased o calibrado, y los tonos medios son exactamente donde la Y lineal y el gris con codificación gamma más divergen. Con CIE Y, una máscara de gris medio deja pasar solo alrededor de un quinto de la sombra en lugar de la mitad, así que las sombras proyectadas y las viñetas salen notablemente más claras que en cualquier otro visor donde el cliente abra el archivo. Tener razón por la letra del estándar y estar mal ante los ojos de todos es un mal intercambio para un valor predeterminado, así que el default sigue a los visores y el modo estricto está a una propiedad de distancia
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('catalog-page.pdf') > 0 then
begin
// Default (False): las luminosity masks pesan el color resuelto
// como DeviceRGB, igual que lo que muestran los visores comunes.
// True: tomar CIE Y cuando el grupo de la máscara tiene un espacio CIE-based.
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;
La propiedad solo importa cuando el espacio de blending del grupo de la máscara es basado en CIE: CalGray, CalRGB, Lab o ICCBased. Las máscaras cuyo grupo usa DeviceGray, DeviceRGB o DeviceCMYK renderizan exactamente igual que antes bajo cualquier ajuste, ya que la conversión del espacio de dispositivo a gris es la misma en ambos modos
Trampas: /BC, /CS y el cache de render
Una soft mask de luminosidad es tan consistente como su fondo: el color /BC que llena el área de la máscara fuera del grupo debe convertirse con la misma regla que la pintura dentro del grupo. HotPDF cambió ambos juntos en v2.770.0. Convertir la pintura con RGB ponderado pero el fondo con CIE Y produce una costura visible donde termina el bounding box del grupo, un bug que es fácil de introducir cuando el fondo pasa por un helper distinto al del contenido
Los atributos del grupo además tienen que ser válidos antes de que todo esto suceda. HotPDF lee el espacio de blending de la entrada /CS del grupo y exige que /BC cargue exactamente un número por componente de ese espacio: tres para un espacio basado en RGB, cuatro para CMYK, uno para gris. Un /CS faltante o un /BC con aridad equivocada hace que la máscara falle antes de que el grupo se ejecute, y la pintura se dibuja entonces sin máscara en lugar de enmascararse con un fondo adivinado. Separation, DeviceN y NChannel no son espacios de blending legales, así que la pintura de spot color dentro de un grupo de máscara pasa por su tint transform hacia el espacio alternativo antes de calcular la luminosidad
Por último, el ajuste cambia píxeles, así que es parte de la llave del cache. Los bitmaps renderizados con RenderColorimetricLuminosityMasks activado y desactivado se cachean por separado tanto en el cache de render de memoria como en el de disco, y alternar la propiedad entre llamadas a THotPDF.RenderLoadedPageToBitmapCached nunca devuelve un bitmap del otro modo. Una nota operativa: v2.769.3 introdujo una regresión solo de Win64 en la que las páginas con un grupo de transparencia que no fijaba soft mask propia fallaban al renderizar; está corregida en v2.770.1, así que las aplicaciones de 64 bits deben usar esa release o posterior
// Prueba lado a lado: renderice la misma página en ambos modos.
// Cada modo se cachea bajo su propia llave, así que repetir llamadas es barato.
Pdf.RenderColorimetricLuminosityMasks := False;
ViewerBmp := Pdf.RenderLoadedPageToBitmapCached(PageIndex, 96);
Pdf.RenderColorimetricLuminosityMasks := True;
StrictBmp := Pdf.RenderLoadedPageToBitmapCached(PageIndex, 96);
// ... muestre ambas y luego libérelas: las llamadas cacheadas devuelven copias propiedad del caller
ViewerBmp.Free;
StrictBmp.Free;
Para la API de render más amplia, incluida la elección de DPI, el cache de páginas y lo que el renderizador todavía aproxima, vea renderizar una página PDF a un TBitmap de Delphi con HotPDF. Si su problema es la dirección contraria, quitar transparencia de un archivo para impresión o archivo en lugar de dibujarla, soft masks y aplanado de transparencia en PDFlibPas cubre ese lado
Preguntas frecuentes
¿HotPDF renderiza correctamente los blend modes PDF dentro de grupos de transparencia?
Sí, desde v2.769.3. Un grupo pintado con Multiply u otro blend mode distinto de Normal mezcla su propio color y alfa con el fondo, y su contenido arranca desde blending Normal como exige ISO 32000-1 §11.6.6
¿Por qué HotPDF renderiza dos veces los grupos no aislados?
Un grupo no aislado no tiene plano de alfa propio. HotPDF lo dibuja sobre negro y sobre blanco y recupera el alfa como 1 − (white − black), lo que cuesta algo de velocidad pero hace correctas las sombras Multiply
¿Debo poner RenderColorimetricLuminosityMasks en True?
Déjelo en False para coincidir con lo que muestran los visores PDF comunes. Póngalo en True solo cuando necesite la lectura CIE Y de ISO 32000-1 para grupos de máscara basados en CIE, por ejemplo en una comparación de conformidad
Los grupos de transparencia con blend mode, las soft masks de luminosidad y el renderizador de páginas que los dibuja son parte del HotPDF Delphi Component, una biblioteca VCL nativa para Delphi y C++Builder que crea, edita y renderiza documentos PDF sin DLL externas