Technical Article

Trójwymiarowe tabele LUT kolorów w PDF z funkcjami próbkowanymi typu 0

Funkcje PDF (PDF Functions) to jeden ze spokojniejszych zakątków specyfikacji. Większość programistów spotyka je raz, jako element wymagany przez cieniowanie osiowe typu 2 do przejścia między dwoma kolorami, i nigdy więcej do nich nie wraca. A szkoda, ponieważ mechanizm funkcji to mały ewaluator ogólnego przeznaczenia, który format ten wykorzystuje ponownie do cieniowania, funkcji transferu, funkcji rastra półtonowego, odcieni separacji i krzywych przenoszenia miękkiej maski. Spośród czterech typów funkcji, typ 0 jest najpotężniejszy i najmniej rozumiany. Jest to funkcja próbkowana (sampled function): wielowymiarowa siatka wartości wyjściowych, pomiędzy którymi czytnik wykonuje interpolację. Ponieważ siatka może zawierać dowolne wprowadzone liczby, funkcja typu 0 pozwala wyrazić dowolne nieliniowe mapowanie, czyli dokładnie to, czym jest tabela przeglądowa kolorów (color LUT).

W tym artykule omówiono słownik typu 0 w postaci zdefiniowanej w standardzie ISO 32000-1 w §7.10.2, a następnie przedstawiono dwa przypadki mające największe znaczenie w procesie przetwarzania dokumentów: trójwejściową tabelę LUT korekcji kolorów RGB-do-RGB oraz jednowejściową transformację odcieni kolorów dodatkowych (spot-color). Ten sam kreator funkcji próbkowanej obsługuje oba te scenariusze, a różnica między nimi sprowadza się wyłącznie do liczby wejść posiadanych przez siatkę.

Funkcja próbkowana to siatka interpolowana przez czytnik

Funkcja typu 0 mapuje wektor o m wejściach na wektor o n wyjściach poprzez przechowywanie próbek w regularnej siatce i interpolowanie między nimi. Norma ISO 32000-1 §7.10.2 wymienia klucze opisujące tę siatkę. Klucz /Domain zawiera dwie liczby na każde wejście, oznaczające dolną i górną granicę każdej osi wejściowej. Klucz /Range przechowuje dwie liczby dla każdej składowej wyjściowej. /Size to tablica m liczb całkowitych określających liczbę próbek wzdłuż każdej osi wejściowej, więc siatka o boku dwunastu próbek w trzech wymiarach ma postać /Size [12 12 12] i przechowuje 1728 punktów siatki. /BitsPerSample ustawia precyzję każdej przechowywanej wartości; HotPDF akceptuje 1, 2, 4, 8, 12, 16, 24 i 32 bity, co odpowiada wartościom dopuszczonym przez tabelę 38.

Strumień próbek jest odczytywany w określonej kolejności. Pierwszy wymiar wejściowy zmienia się najszybciej, potem drugi i tak dalej, a w każdym punkcie siatki kolejno zapisywane są n składowe wyjściowe. W przypadku tabeli RGB-do-RGB daje to trzy bajty na punkt siatki przy kodowaniu 8-bitowym, ułożone jako wyjście czerwone, wyjście zielone, wyjście niebieskie, przebiegane najpierw wzdłuż wejścia czerwonego. Dwa kolejne klucze mapują ciągły świat na siatkę liczb całkowitych. Klucz /Encode mapuje każde wejście z jego przedziału /Domain na zakres indeksu próbki od 0 do Size[i] - 1, a /Decode mapuje surowe zapisane liczby całkowite z powrotem na przedziały /Range. Pozostawienie wartości domyślnych sprawia, że wejście w zakresie [0 1] trafia precyzyjnie na pełną siatkę, a zapisany bajt o wartości 255 jest dekodowany na samą górę zakresu wyjściowego, co idealnie pasuje do znormalizowanej w przedziale [0,1] tabeli LUT kolorów.

Rząd 1 kontra Rząd 3

Między punktami siatki czytnik musi przeprowadzić interpolację, a klucz /Order decyduje o jej sposobie. Wartość /Order 1 oznacza interpolację wieloliniową: liniową wzdłuż jednej osi, dwuliniową dla dwóch wymiarów, trójliniową dla trzech. Jest szybka, odpowiada dokładnie temu, co robi sprzęt w większości przeglądarek, a przy płynnej transformacji kolorów jest zazwyczaj nie do odróżnienia od bardziej skomplikowanych metod. /Order 3 wymaga interpolacji sklejanej trzeciego stopnia (cubic-spline), która dopasowuje gładszą krzywą do próbek kosztem większego nakładu obliczeniowego i szerszego obszaru wokół każdego szacowanego punktu.

Kompromis polega na zbalansowaniu gęstości siatki i gładkości krzywej. Rząd trzeci (cubic) ma sens, gdy siatka jest rzadka, a mapowanie wykazuje wyraźną krzywiznę, ponieważ linia prosta między dwoma odległymi punktami próbkowania może spłaszczyć krzywą tonalną w sposób zauważalny dla oka na gradientach. Gdy siatka jest gęsta, segmenty są na tyle krótkie, że interpolacja liniowa dokładnie odwzorowuje krzywą, a interpolacja trzeciego stopnia niewiele wnosi. Praktyczną zasadą jest sięganie po /Order 3 tylko przy małych siatkach lub stromych transformacjach, a w innych przypadkach pozostawienie domyślnej interpolacji liniowej. Warto zauważyć, że /Order dotyczy wyłącznie funkcji typu 0, a HotPDF odrzuca wartości inne niż 1 lub 3.

3D LUT: trzy wejścia, trzy wyjścia

Korekcja kolorów RGB-do-RGB to podręcznikowy przykład zastosowania siatki o trzech wejściach — klasycznej tabeli 3D LUT stosowanej w gradacji kolorów i dopasowywaniu urządzeń. Każda oś sześcianu reprezentuje jeden kanał wejściowy, każdy punkt siatki przechowuje skorygowaną trójkę RGB dla danej współrzędnej wejściowej, a czytnik wykonuje trójliniową interpolację próbek narożnych wokół każdego wprowadzanego koloru. Trzy wejścia są tutaj nieuniknione, ponieważ skorygowana składowa czerwona może zależeć od wejściowej składowej zielonej i niebieskiej, a no nie tylko od wejściowej czerwieni; krzywa na kanał nie jest w stanie wyrazić przesłuchu (crosstalk) między kanałami, podczas gdy sześcian to umożliwia.

HotPDF tworzy strumień typu 0 za pomocą funkcji RegisterSampledFunction, która bezpośrednio przyjmuje parametry /Domain, /Range, /Size, /BitsPerSample oraz bajty próbek, po czym zwraca obiekt funkcji. Dla standardowego, znormalizowanego sześcianu przekazuje się granice [0,1] na wszystkich trzech osiach wejściowych i wszystkich trzech wyjściach, rozmiar N x N x N oraz spłaszczoną tabelę próbek. Kreator weryfikuje, czy liczba bajtów odpowiada siatce: dla głębi wyrównanej do bajtów oczekuje wartości OutputCount x (BitsPerSample div 8) x iloczyn rozmiarów, i zgłasza błąd, jeśli długość tablicy jest nieprawidłowa. Dzięki temu błędnie obliczony krok (stride) powoduje jawny błąd przy rejestracji, zamiast renderować zniekształcony obraz w późniejszym czasie.

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;

Kolorymetryczna poprawność sześcianu zależy od sposobu jego wypełnienia, a nie od samej funkcji PDF. Właściwym podejściem jest obliczenie każdego punktu siatki za pomocą konwersji zarządzanej przez profil ICC — tego samego silnika, który obsługuje próbę ekranową (soft-proof) — dzięki czemu liczby w siatce mają znaczenie w odniesieniu do zdefiniowanego profilu źródłowego i docelowego. Zarejestruj profile ograniczające konwersję za pomocą RegisterICCProfile, co zapisuje przestrzeń barw ICCBased (1, 3 lub 4 składowe) i zwraca nazwę zasobu, którą można dołączyć do zawartości zasilanej przez LUT. Funkcja typu 0 przenosi tabelę interpolacji; profil ICC nadaje znaczenie punktom skrajnym.

Przypadek jednowymiarowy (1D): transformacja odcieni koloru dodatkowego

Rozdzielone przestrzenie barw (Separation color spaces) opierają się na tym samym mechanizmie do zupełnie innych zadań. Przestrzeń Separation, zdefiniowana w ISO 32000-1 §8.6.6.4, reprezentuje pojedynczy barwnik, na przykład atrament dodatkowy Pantone lub lakier, poprzez powiązanie nazwy z transformacją odcienia (tint transform): funkcją, która mapuje jednowymiarową wartość odcienia (od 0 dla braku atramentu do 1 dla pełnego pokrycia) na alternatywną przestrzeń barw, którą urządzenie może faktycznie wyrenderować, najczęściej CMYK. Ta transformacja odcienia jest bardzo często funkcją typu 0, w której siatka posiada dokładnie jedną oś wejściową.

Stanowi to wyraźny kontrast z tabelą 3D LUT. Atrament dodatkowy to jeden stopień swobody, więc jego transformacja odcienia wymaga jednego wejścia, a siatka jest linią próbek, z których każda przechowuje wartość CMYK (lub inną alternatywną) dla danego poziomu odcienia. Sześcian RGB wymaga trzech wejść, ponieważ jego dziedzina jest trójwymiarowa i kanały oddziałują na siebie. Ten sam typ funkcji, te same reguły interpolacji, ale inna wymiarowość; specyfikacja wykorzystuje ten sam ewaluator, pozwalając parametrowi /Size decydować o tym, czy poruszamy się po linii, płaszczyźnie czy sześcianie. HotPDF opakowuje całą separację w metodzie RegisterSeparationLUT, która wewnętrznie buduje jednowejściową transformację odcienia typu 0 z płaskiej tablicy bajtów i zwraca nazwę zasobu przestrzeni barw.

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;

Liczba próbek musi być całkowitą wielokrotnością punktów siatki: dodatnią wielokrotnością liczby składowych przestrzeni alternatywnej i musi wynosić co najmniej dwa punkty, aby istniał segment do interpolacji. Przekazanie trzech bajtów na punkt przy przestrzeni alternatywnej CMYK spowoduje odrzucenie wywołania — to samo zabezpieczenie, które stosuje kreator 3D, co jest pożądane w przypadku funkcji, która w przeciwnym razie uległaby cichemu uszkodzeniu podczas drukowania.

Gdzie ten sam mechanizm pojawia się ponownie

Gdy spojrzysz na typ 0 jak na ogólną tabelę interpolacji, dwie kolejne funkcje sterowania urządzeniem przestają wyglądać jak przypadki szczególne. Funkcja transferu (transfer function) dostosowuje wartości składowych przed wysłaniem ich do urządzenia wyjściowego i jest po prostu osobną funkcją dla każdego kanału; HotPDF rejestruje ją jako ExtGState poprzez RegisterTransferFunctionState, która przyjmuje jedną połączoną funkcję lub tablicę funkcji na kanał. Ponieważ funkcje te są zwykłymi obiektami funkcji, można przekazać dokładnie ten obiekt THPDFStreamObject, który zwraca RegisterSampledFunction, i sterować krzywą transferu z tabeli próbkowanej zamiast wzoru.

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;

Generowanie czerni (black generation) i usuwanie koloru podkładowego (undercolor removal) należą do tej samej rodziny. Gdy urządzenie konwertuje RGB na CMYK, decyduje, ile składowej szarej przenieść jako czarny atrament, a specyfikacja wyraża tę decyzję jako funkcję we wpisach /BG2 i /UCR2 słownika stanu grafiki, z których każdy jest jednowejściową krzywą od obliczonej szarości do ilości czerni. Są to również funkcje typu 0, jeśli zależy nam na krzywej zmierzonej, a nie analitycznej, budowane w ten sam sposób za pomocą RegisterSampledFunction i umieszczane w stanie grafiki. Warto pamiętać o lekcji, że funkcja PDF nigdy nie jest miejscem, w którym odbywa się zarządzanie kolorami; jest to tabela przeglądowa niosąca decyzję podjętą w rzeczywistym silniku kolorów, a typ 0 to jedyny typ funkcji na tyle elastyczny, by przenieść dowolną decyzję.

Szerszy obraz sposobu przesyłania czcionek, obrazów i zasobów kolorów do gotowego dokumentu przedstawia nasz poradnik na temat generowania raportów z czcionkami i obrazami. Gdy dane wyjściowe muszą przejść testy weryfikacyjne dla archiwizacji lub druku, zasady dotyczące przestrzeni barw i intencji wyjściowych (output intent) omówione w przewodniku walidacji PDF/A, PDF/X i PDF/UA określają, które z tych funkcji są dozwolone i jak należy oznaczać kolory urządzenia. Całość jest dostarczana w pakiecie HotPDF Component dla Delphi i C++Builder, obok interfejsów API do cieniowania, profili ICC oraz separacji bazujących na tym samym jądrze typu 0.