Technical Article

LUT colore 3D in PDF con funzioni campionate di Tipo 0

Le funzioni PDF rappresentano uno degli aspetti meno visibili della specifica. La maggior parte degli sviluppatori le incontra una sola volta, come l'elemento necessario a una sfumatura assiale di Tipo 2 per sfumare tra due colori, e non le approfondisce ulteriormente. È un peccato, perché il motore delle funzioni è un piccolo valutatore di carattere generale che il formato riutilizza per sfumature, funzioni di trasferimento, funzioni spot di retinatura, tinte di separazione e curve di trasferimento di maschere di trasparenza. Dei quattro tipi di funzione, il Tipo 0 is il più potente e il meno compreso. Si tratta di una funzione campionata (sampled function): una griglia multidimensionale di valori di output tra i quali il lettore esegue un'interpolazione. Poiché la griglia può contenere qualsiasi numero inserito al suo interno, una funzione di Tipo 0 può esprimere una mappatura non lineare arbitraria, che corrisponde esattamente alla struttura di una tabella di consultazione dei colori (color lookup table).

Questo articolo esamina il dizionario di Tipo 0 così come definito dallo standard ISO 32000-1 nel §7.10.2, per poi illustrare i due casi più rilevanti in un flusso di lavoro documentale: una LUT di correzione colore RGB-to-RGB a tre input e una trasformazione di tinta per colori spot a singolo input. Lo stesso costruttore di funzioni campionate serve entrambi i casi, e la differenza tra essi risiede unicamente nel numero di input della griglia.

Una funzione campionata è una griglia interpolata dal lettore

Una funzione di Tipo 0 mappa un vettore di m input in un vettore di n output memorizzando campioni su una griglia regolare e interpolando tra di essi. Lo standard ISO 32000-1 §7.10.2 elenca le chiavi che descrivono tale griglia. /Domain contiene due numeri per ciascun input, indicando i limiti inferiore e superiore di ogni asse di input. /Range contiene due numeri per ciascun componente di output. /Size è un array di m interi che fornisce il numero di campioni lungo ogni asse di input, perciò una griglia di dodici campioni per lato in tre dimensioni ha /Size [12 12 12] e memorizza 1.728 punti griglia. /BitsPerSample imposta la precisione di ciascun valore memorizzato; HotPDF accetta 1, 2, 4, 8, 12, 16, 24 e 32 bit, in linea con i valori consentiti dalla Tabella 38.

Il flusso di campioni viene letto in un ordine prestabilito. La prima dimensione di input varia più rapidamente, seguita dalla seconda e così via, e in ciascun punto della griglia gli n componenti di output sono memorizzati in ordine. Per una tabella RGB-to-RGB, ciò equivale a tre byte per punto griglia a otto bit, disposti nell'ordine output rosso, output verde, output blu, variando prima l'input rosso. Altre due chiavi mappano lo spazio continuo sulla griglia intera. /Encode mappa ogni input dal suo intervallo /Domain all'intervallo di indici del campione da 0 a Size[i] - 1, mentre /Decode mappa gli interi grezzi memorizzati sugli intervalli /Range. Lasciando i valori predefiniti, un input nell'intervallo [0 1] si adatta perfettamente all'intera griglia e un byte memorizzato pari a 255 decodifica al valore massimo del suo intervallo di output, che è esattamente ciò che richiede una LUT colore normalizzata in [0,1].

Ordine 1 contro Ordine 3

Tra i punti della griglia il lettore deve eseguire un'interpolazione, e /Order stabilisce come. /Order 1 corrisponde all'interpolazione multilineare: lineare lungo un asse, bilineare su due, trilineare su tre. È veloce, è esattamente ciò che fa l'hardware della maggior parte dei visualizzatori e, per una trasformazione colore fluida, è solitamente indistinguibile da soluzioni più complesse. /Order 3 richiede un'interpolazione con spline cubica, che adatta una curva più morbida attraverso i campioni al costo di un maggior carico di calcolo e di un'area di supporto più ampia attorno a ciascun punto valutato.

Il compromesso è tra la densità della griglia e la fluidità della curva. L'ordine cubico dimostra la sua utilità quando la griglia è rada e la mappatura presenta una curvatura evidente, poiché una linea retta tra due campioni distanti può appiattire una curva di tono in un modo che l'occhio avverte sulle sfumature. Quando la griglia è fitta, i segmenti sono abbastanza corti da permettere all'interpolazione lineare di seguire fedelmente la curva, rendendo quella cubica poco utile. Una regola pratica consiste nell'utilizzare /Order 3 solo con griglie ridotte o trasformazioni ripide, e lasciare altrimenti il valore predefinito lineare. Si noti che /Order si applica solo alle funzioni di Tipo 0, e HotPDF rifiuta qualsiasi valore diverso da 1 o 3.

La LUT 3D: tre input, tre output

La correzione colore da RGB a RGB è il caso classico per una griglia a tre input, la tipica LUT 3D utilizzata nella gradazione del colore e nella corrispondenza dei dispositivi. Ciascun asse del cubo rappresenta un canale di input, ogni punto della griglia memorizza la terna RGB corretta per quella coordinata di input e il lettore esegue un'interpolazione trilineare dei campioni d'angolo attorno a qualsiasi colore in ingresso. Tre input sono indispensabili in questo caso poiché il rosso corretto può dipendere dal verde e dal blu di input, non solo dal rosso; una curva per singolo canale non può esprimere l'interferenza tra canali (channel crosstalk), mentre un cubo può farlo.

HotPDF crea lo stream di Tipo 0 tramite RegisterSampledFunction, che accetta direttamente le voci /Domain, /Range, /Size, /BitsPerSample e i byte del campione, restituendo l'oggetto funzione. Per un cubo normalizzato standard si passano limiti [0,1] su tutti e tre gli assi di input e su tutti e tre gli output, una dimensione N x N x N e la tabella dei campioni linearizzata. Il costruttore verifica che il conteggio dei byte corrisponda alla griglia: per una profondità allineata ai byte si aspetta OutputCount x (BitsPerSample div 8) x il prodotto delle dimensioni, e solleva un'excezione se la lunghezza dell'array è errata, così che un passo calcolato male fallisca chiaramente in fase di registrazione invece di produrre un rendering errato in seguito.

const
  N = 17;  // 17 x 17 x 17 cube, the common ICC LUT resolution
var
  LutFn: THPDFStreamObject;
  Samples: TBytes;
begin
  // Fill Samples with N*N*N grid points, 3 bytes each (R,G,B output),
  // red input varying fastest. Build the corrected triple for each
  // grid coordinate with your ICC-managed conversion, then store it.
  SetLength(Samples, N * N * N * 3);
  BuildCorrectedCube(Samples, N);   // your color-managed fill

  LutFn := Pdf.RegisterSampledFunction(
    [0,1, 0,1, 0,1],   // /Domain: three input axes on [0,1]
    [0,1, 0,1, 0,1],   // /Range:  three output channels on [0,1]
    [N, N, N],         // /Size:   the cube resolution per axis
    8,                 // /BitsPerSample
    Samples,
    1);                // /Order 1 = trilinear
end;

La correttezza colorimetrica del cubo risiede nel modo in cui lo si popola, non nella funzione PDF. L'approccio corretto consiste nel calcolare ciascun punto della griglia tramite una conversione gestita dal motore ICC, lo stesso che gestisce una prova a monitor (soft-proof), in modo che i numeri nella griglia abbiano significato rispetto a un profilo di sorgente e di destinazione definito. Registra i profili che delimitano la conversione con RegisterICCProfile, che memorizza uno spazio colore ICCBased (a 1, 3 o 4 componenti) e restituisce un nome di risorsa che puoi associare al contenuto alimentato dalla LUT. La funzione di Tipo 0 contiene la tabella di interpolazione; il profilo ICC definisce il significato dei punti estremi.

Il caso 1D: una trasformazione di tinta per colori spot

Gli spazi colore di tipo Separation si basano sullo stesso meccanismo per un compito del tutto diverso. Uno spazio Separation, definito nello standard ISO 32000-1 §8.6.6.4, rappresenta un singolo colorante, un inchiostro spot come un Pantone o una vernice, associando un nome a una trasformazione di tinta (tint transform): una funzione che mappa il valore monodimensionale della tinta, da 0 per assenza di inchiostro a 1 per inchiostro pieno, su uno spazio colore alternativo che il dispositivo può effettivamente riproducere, solitamente CMYK. Tale trasformazione di tinta è spesso una funzione di Tipo 0, e in questo caso la griglia ha esattamente un asse di input.

Questo rappresenta un chiaro contrasto con la LUT 3D. Un inchiostro spot ha un solo grado di libertà, pertanto la sua trasformazione di tinta richiede un solo input e la griglia è una linea di campioni, ciascuno contenente il valore CMYK (or altro alternativo) a quel livello di tinta. Il cubo RGB richiede tre input poiché il suo dominio è tridimensionale e i canali interagiscono tra loro. Stesso tipo di funzione, stesse regole di interpolazione, diversa dimensionalità; la specifica riutilizza un unico valutatore e lascia che sia /Size a decidere se si sta esaminando una linea, un piano o un cubo. HotPDF racchiude l'intera separazione in RegisterSeparationLUT, che crea internamente la trasformazione di tinta di Tipo 0 a singolo input a partire da un array di byte lineare e restituisce il nome della risorsa dello spazio colore.

var
  SpotCS: AnsiString;
begin
  // Four CMYK output bytes per tint grid point, tint domain [0..1].
  // Here 0% ink -> all zero, 100% ink -> a rich spot build,
  // with two interior steps; the tint transform interpolates between.
  SpotCS := Pdf.RegisterSeparationLUT(
    'PANTONE 286 C',         // colorant name
    'DeviceCMYK',            // alternate color space
    [  0,   0,   0,   0,     // tint 0.00 -> 0,0,0,0
      90,  60,   0,   0,     // tint 0.33
     100,  80,   0,  10,     // tint 0.66
     100,  72,   0,  18]);   // tint 1.00 -> full ink build
  // Use SpotCS with SetFillColorSpace / SetFillColor on a page.
end;

Il conteggio dei campioni deve corrispondere a un numero intero di punti della griglia: un multiplo positivo del numero di componenti dello spazio alternativo, e almeno due punti per consentire l'interpolazione del segmento. Passando tre byte per punto a fronte di uno spazio alternativo CMYK la chiamata rifiuterà l'operazione, la stessa convalida difensiva applicata dal costruttore 3D, comportamento desiderato per evitare che una funzione fallisca poi silenziosamente in fase di stampa.

Altri contesti di utilizzo dello stesso meccanismo

Una volta inteso il Tipo 0 come una tabella di interpolazione generica, altre due funzionalità di controllo del dispositivo smettono di apparire come casi speciali. Una funzione di trasferimento (transfer function) regola i valori dei componenti inviati al dispositivo di output, ed è semplicemente una funzione per canale; HotPDF la registra come un ExtGState tramite RegisterTransferFunctionState, che accetta sia una funzione combinata singola sia un array di funzioni per ciascun canale. Poiché tali funzioni sono normali oggetti funzione, è possibile passarvi lo stesso THPDFStreamObject restituito da RegisterSampledFunction e gestire una curva di trasferimento a partire da una tabella campionata anziché da una formula.

var
  ToneFn: THPDFStreamObject;
  GsName: AnsiString;
begin
  // A single-input, single-output sampled tone curve on [0,1].
  ToneFn := Pdf.RegisterSampledFunction(
    [0,1], [0,1], [256], 8, ToneCurveBytes, 1);

  // Apply it to all channels as a combined /TR2 transfer function.
  GsName := Pdf.RegisterTransferFunctionState(ToneFn, []);
  // Select GsName on the page before drawing the affected content.
end;

La generazione del nero (black generation) e la rimozione del colore sottostante (undercolor removal) appartengono alla stessa famiglia. Quando un dispositivo converte RGB in CMYK, stabilisce quanta parte del componente grigio trasferire come inchiostro nero, e la specifica esprime tale decisione come una funzione, ovvero le voci /BG2 e /UCR2 di un dizionario di stato grafico, ciascuna delle quali è una curva a singolo input dal grigio calcolato a una quantità di nero. Anche queste sono funzioni di Tipo 0 quando si desidera una curva misurata anziché una analitica, create nello stesso modo tramite RegisterSampledFunction e inserite nello stato grafico. La lezione da ricordare è che la funzione PDF non è mai il luogo in cui avviene la gestione del colore; è la tabella di consultazione che contiene una decisione presa con un motore colore reale, e il Tipo 0 è l'unico tipo di funzione sufficientemente flessibile per gestire qualsiasi decisione.

Per un quadro più ampio su come font, immagini e risorse colore vengono emessi in un documento finale, si veda la nostra guida alla generazione di report con font e immagini. Quando l'output deve superare una verifica preliminare (preflight) di archiviazione o di stampa, le regole sullo spazio colore e sull'intento di rendering (output-intent) trattate nella guida alla convalida PDF/A, PDF/X e PDF/UA stabiliscono quali di queste funzioni sono consentite e come deve essere taggato il colore del dispositivo. Tutto questo è integrato nel HotPDF Component per Delphi e C++Builder, insieme alle API di sfumatura, ICC e separazione basate sullo stesso nucleo di Tipo 0.