Технічна стаття

3D Color LUT у PDF з використанням семплованих функцій типу 0

Функції PDF — це один із найменш помітних розділів специфікації. Більшість розробників стикаються з ними лише один раз, коли осьовому затіненню типу 2 (Type 2 axial shading) потрібно виконати плавний перехід між двома кольорами, і більше до них не повертаються. Це прикро, оскільки механізм функцій є невеликим інтерпретатором загального призначення, який формат повторно використовує для затінення, передавальних функцій, півтонових точкових функцій, плашкових відтінків та кривих передачі м'яких масок. З чотирьох типів функцій тип 0 є найпотужнішим і водночас найменш зрозумілим. Це семплована функція (sampled function): багатовимірна сітка вихідних значень, між якими програма читання виконує інтерполяцію. Оскільки сітка може містити будь-які числа, які ви туди запишете, функція типу 0 може виражати довільне нелінійне відображення, що є точною формою таблиці пошуку кольорів (color lookup table)

У цій статті ми розглянемо словник типу 0, як його визначає параграф §7.10.2 стандарту ISO 32000-1, а потім покажемо два випадки, які мають найбільше значення в конвеєрі обробки документів: коригування кольору RGB-в-RGB з трьома входами та перетворення відтінку плашкового кольору з одним входом. Один і той самий конструктор семплованих функцій обслуговує обидва випадки, і різниця між ними полягає виключно в кількості входів сітки

Вибіркова функція — це сітка, яку інтерполює програма читання

Функція типу 0 відображає вектор з m входів у вектор з n виходів шляхом збереження семплів на регулярній сітці та інтерполяції між ними. Параграф §7.10.2 стандарту ISO 32000-1 перераховує ключі, що описують цю сітку. /Domain містить по два числа на кожен вхід — нижню та верхню межі кожної осі входу. /Range містить по два числа для кожного вихідного компонента. /Size — це масив з m цілих чисел, який вказує кількість семплів уздовж кожної осі входу; наприклад, сітка з дванадцятьма семплами на кожній стороні у трьох вимірах має /Size [12 12 12] і зберігає 1728 точок сітки. /BitsPerSample задає точність кожного збереженого значення; HotPDF приймає 1, 2, 4, 8, 12, 16, 24 та 32 біти, що відповідає значенням, дозволеним таблицею 38

Потік семплів зчитується у фіксованому порядку. Перший вимір входу змінюється найшвидше, потім другий і так далі, і в кожній точці сітки послідовно зберігаються n вихідних компонентів. Для таблиці RGB-в-RGB це три байти на точку сітки при 8 бітах, які розташовані в порядку: червоний вихід, зелений вихід, синій вихід, починаючи з першого кроку по червоному входу. Еще два ключі відображають безперервний світ на цілочисельну сітку. /Encode відображає кожен вхід з його інтервалу /Domain у діапазон індексів семплів від 0 до Size[i] - 1, а /Decode відображає вихідні збережені цілі числа назад в інтервали /Range. Якщо залишити їх за замовчуванням, вхід у діапазоні [0 1] акуратно лягає на всю сітку, а збережений байт 255 декодується у верхню межу свого вихідного діапазону, що і потрібно для [0,1]-нормалізованого Color LUT

Анатомія функції PDF Type 0 з вибірками в HotPDF: Domain і Encode відображають кожен вхід на індекси сітки, вибірки інтерполюються, потім Decode і Range масштабують виходи
Читач інтерполює регулярну сітку семплів, форма якої походить зі Size, прогортаючи перший вхід найшвидше. Encode і Decode прив'язують неперервний світ до збережених цілих чисел

Порядок 1 проти Порядку 3

Між точками сітки програма читання має виконувати інтерполяцію, і ключ /Order вибирає спосіб її виконання. /Order 1 — це мультилінійна інтерполяція: лінійна за однією віссю, білінійна за двома, трилінійна за трьома. Вона швидка, це саме те, що робить апаратне забезпечення у більшості засобів перегляду, і для плавного перетворення кольору її зазвичай не відрізнити від складніших методів. /Order 3 запитує кубічну сплайн-інтерполяцію, яка будує плавнішу криву через семпли за рахунок більшого обсягу обчислень і ширшої області підтримки навколо кожної обчислюваної точки

Компроміс полягає між щільністю сітки та плавністю кривої. Кубічний порядок виправдовує себе, коли сітка рідка, а відображення має помітну кривизну, оскільки пряма лінія між двома віддаленими семплами може спотворити тонову криву так, що це буде помітно на градієнтах. Коли сітка щільна, сегменти досить короткі, щоб лінійна інтерполяція точно повторювала криву, і кубічна дає мало переваг. Практичне правило полягає в тому, щоб використовувати /Order 3 лише для малих сіток або крутих перетворень, а в інших випадках залишати лінійне значення за замовчуванням. Зверніть увагу, що /Order застосовується лише до функцій типу 0, і HotPDF відхиляє будь-які значення, крім 1 або 3

3D LUT: три входи, три виходи

Корекція кольору RGB-в-RGB є класичним прикладом для сітки з трьома входами — це традиційний 3D LUT, що використовується для градації кольорів та калібрування пристроїв. Кожна вісь куба є одним вхідним каналом, кожна точка сітки зберігає скориговану трійку RGB для цієї координати входу, а програма читання трилінійно інтерполює кутові семпли навколо будь-якого вхідного кольору. Три входи тут є неминучими, оскільки скоригований червоний колір може залежати від вхідних зеленого та синього, а не лише від червоного; крива для окремого каналу не може виразити взаємний вплив каналів, а куб може

HotPDF: колірна корекція RGB в RGB, зображена як тривимірний куб LUT: кожна вісь — один вхідний канал, а вісім навколишніх кутів живлять трилінійну інтерполяцію для вхідного кольору
Кожен кут зберігає виправлену трійку, обчислену за ICC-керованої конверсії. Трилінійне змішування відтворює міжканальний перехресний вплив, який поканальні криві не здатні виразити

HotPDF створює потік типу 0 за допомогою методу RegisterSampledFunction, який приймає безпосередньо /Domain, /Range, /Size, /BitsPerSample та байти семплів і повертає об'єкт функції. Для стандартного нормалізованого куба ви передаєте межі [0,1] для всіх трьох вхідних і трьох вихідних осей, розмір N x N x N та лінійну таблицю семплів. Конструктор перевіряє сумісність кількості байтів із сіткою: для вирівняної по байтах глибини він очікує OutputCount x (BitsPerSample div 8) x добуток розмірів, і видає помилку, якщо масив має неправильну довжину, тому помилково обчислений крок призведе до збою на етапі реєстрації, а не до відображення сміття згодом

const
  N = 17;  // куб 17 x 17 x 17, поширена роздільність ICC LUT
var
  LutFn: THPDFStreamObject;
  Samples: TBytes;
begin
  // Заповнити Samples точками сітки N*N*N, по 3 байти на кожну (вихід R,G,B),
  // червоний вхід змінюється найшвидше. Побудувати скориговану трійку для кожної
  // координати сітки за допомогою перетворення під керуванням ICC, потім зберегти її
  SetLength(Samples, N * N * N * 3);
  BuildCorrectedCube(Samples, N);   // your color-managed fill

  LutFn := Pdf.RegisterSampledFunction(
    [0,1, 0,1, 0,1],   // /Domain: три вхідні осі на [0,1]
    [0,1, 0,1, 0,1],   // /Range:  три вихідні канали на [0,1]
    [N, N, N],         // /Size:   роздільність куба на вісь
    8,                 // /BitsPerSample
    Samples,
    1);                // /Order 1 = trilinear
end;

Колориметрична коректність куба залежить від того, як ви його заповнюєте, а не від функції PDF. Найнадійніший шлях — обчислити кожну точку сітки за допомогою перетворення під керуванням ICC (такого ж двигуна, який керує програмною кольоропробою), щоб числа в сітці відповідали визначеним вхідним і вихідним профілям. Зареєструйте профілі, що обмежують перетворення, за допомогою RegisterICCProfile, який створює колірний простір ICCBased (1, 3 або 4 компоненти) і повертає ім'я ресурсу, яке ви можете призначити вмісту, який використовує LUT. Функція типу 0 містить таблицю інтерполяції; профіль ICC визначає значення кінцевих точок

Одновимірний випадок: перетворення відтінку плашкового кольору

Плашкаві (Separation) колірні простори використовують той самий механізм для зовсім іншого завдання. Колірний простір Separation, визначений в ISO 32000-1 §8.6.6.4, представляє один барвник — плашкову фарбу на кшталт Pantone або лак, пов'язуючи ім'я з перетворенням відтінку (tint transform): функцією, яка відображає одновимірне значення відтінку (від 0 для відсутності фарби до 1 для повної заливки) в альтернативний колірний простір, який пристрій може реально відтворити, зазвичай CMYK. Тобто перетворення відтінку часто реалізується як функція типу 0, і в цьому випадку сітка має рівно одну вісь входу

Це чіткий контраст із 3D LUT. Плашкова фарба — це один ступінь свободи, тому її перетворенню відтінку потрібен один вхід, а сітка є лінією семплів, кожен з яких містить значення CMYK (або інше альтернативне значення) для цього рівня відтінку. Куб RGB потребує трьох входів, оскільки його область визначення є тривимірною і канали взаємодіють між собою. Один і той самий тип функції, однакові правила інтерполяції, але різна розмірність; специфікація повторно використовує один обчислювач і дозволяє /Size визначати, чи ви працюєте з лінією, площиною чи кубом. HotPDF обгортає все розділення в RegisterSeparationLUT, який створює одновхідне перетворення відтінку типу 0 із плоскаго масиву байтів всередині системи та повертає назву ресурсу колірного простору

HotPDF: одновхідне перетворення відтінку Type 0 для плашкового кольору: лінія зразків відтінку, кожен із значенням DeviceCMYK для альтернативного простору
Плашкова фарба має один степінь свободи, тож її сітка зсікається до лінії семплів. Той самий оцінювач проходить цю лінію, площину або RGB-куб
var
  SpotCS: AnsiString;
begin
  // Чотири вихідні байти CMYK на точку сітки відтінку, область відтінку [0..1]
  // Тут 0% фарби -> усі нулі, 100% фарби -> насичена плашкова суміш,
  // із двома внутрішніми кроками; перетворення відтінку інтерполює між ними
  SpotCS := Pdf.RegisterSeparationLUT(
    'PANTONE 286 C',         // colorant name
    'DeviceCMYK',            // альтернативний колірний простір
    [  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]);   // відтінок 1.00 -> повна суміш фарби
  // Використати SpotCS із SetFillColorSpace / SetFillColor на сторінці
end;

Кількість семплів має бути цілою кількістю точок сітки: позитивним кратним кількості компонентів альтернативного простору і мати щонайменше дві точки, щоб існував сегмент для інтерполяції. Передайте три байти на точку для CMYK-альтернативи, і виклик буде відхилено — це той самий захисний механізм перевірки сумісності, який застосовує 3D-конструктор, що дозволяє уникнути непомітного збою під час друку

Де цей самий механізм з'являється знову

Коли ви почнете сприймати тип 0 як універсальну таблицю інтерполяції, ще дві функції керування пристроєм перестануть виглядати як окремі випадки. Передавальна функція (transfer function) коригує значення компонентів на їхньому шляху до пристрою виводу, і це просто окрема функція для кожного журналу; HotPDF реєструє її як ExtGState за допомогою RegisterTransferFunctionState, яка приймає або одну комбіновану функцію, або масив функцій для кожного каналу. Оскільки ці функції є звичайними об'єктами функцій, ви можете передати їй той самий THPDFStreamObject, який повертає RegisterSampledFunction, і керувати кривою передачі на основі таблиці семплів, а не математичної формули

var
  ToneFn: THPDFStreamObject;
  GsName: AnsiString;
begin
  // Вибіркова тонова крива з одним входом і одним виходом на [0,1]
  ToneFn := Pdf.RegisterSampledFunction(
    [0,1], [0,1], [256], 8, ToneCurveBytes, 1);

  // Застосувати її до всіх каналів як об’єднану передавальну функцію /TR2
  GsName := Pdf.RegisterTransferFunctionState(ToneFn, []);
  // Вибрати GsName на сторінці перед малюванням відповідного вмісту
end;

Генерація чорного кольору (black generation) та вилучення підкладкових кольорів (undercolor removal) належуть до того ж сімейства. Коли пристрій перетворює RGB в CMYK, він вирішує, яка частина сірої складової має бути виведена як чорна фарба, і специфікація виражає це рішення як функцію — записи /BG2 та /UCR2 у словнику стану графіки, кожна з яких є кривою з одним входом від обчисленого сірого до кількості чорного. Вони також є функціями типу 0, якщо вам потрібна виміряна крива замість аналітичної, і створюються так само через RegisterSampledFunction та розміщуються у стані графіки. Важливий урок полягає в тому, що керування кольором ніколи не відбувається всередині функції PDF; функція — це просто таблиця пошуку кольорів, яка переносить рішення, прийняте вами за допомогою справжнього колірного двигуна, а тип 0 є єдиним типом функції, достатньо гнучким, щоб перенести будь-яке рішення взагалі

Для отримання ширшого уявлення про те, як шрифти, зображення та колірні ресурси виводяться у готовий документ, див. нашу інструкцію з виведення звітів із шрифтами та зображеннями. Коли вихідний файл має успішно пройти перевірку відповідності архівним стандартам або стандартам друку, правила колірних просторів та намірів виводу (output intent), опис яких наведено в посібнику з перевірки PDF/A, PDF/X та PDF/UA, визначають, які з цих функцій є дозволеними та як мають бути теговані кольори пристрою. Усе це постачається у складі HotPDF Delphi Component для Delphi та C++Builder разом з API затінення, ICC та Separation, які побудовані на одному ядрі типу 0