Artykuł techniczny

Maski miękkie i spłaszczanie przezroczystości w Delphi

PDF Library for Delphi tworzy z Delphi oba rodzaje maski miękkiej PDF. SetImageSoftMaskEx dołącza obraz maski do obrazu docelowego i potrafi zapisać wpis /Matte, którego potrzebuje grafika z przemnożonym kanałem alfa (pre-multiplied). NewSoftMaskGraphicsState buduje maskę alfa albo luminancji z przechwyconej treści i zwraca stan graficzny, który stosujesz do kolejnego rysowania. A gdy odbiorca w ogóle nie potrafi obsłużyć przezroczystości, FlattenTransparency renderuje wynik wizualny i zastępuje nim każdą stronę

Maski miękkie to sposób, w jaki PDF wyraża gradientowe zanikanie, winietę, zdjęcie wtopione w tło i każdy inny efekt, w którym nieprzezroczystość zmienia się w obrębie regionu. Stała przezroczystość to pojedyncza liczba w stanie graficznym; maska miękka to cała funkcja pozycji, i dopóki nie potrafisz jej stworzyć, cała kategoria projektów pozostaje niemożliwa do odtworzenia

Dwa typy masek i co każdy z nich odczytuje

Maska alfa pobiera swoje wartości z kanału alfa treści maskującej. Maska luminancji pobiera je z jasności treści maskującej, więc biały obszar jest w pełni nieprzezroczysty, a czarny w pełni przezroczysty. Luminancja jest tym, po co sięgają projektanci, ponieważ namalowanie gradientu w skali szarości to naturalny sposób opisania zanikania

Maski miękkie formularza są budowane z przechwyconej treści, a kilka szczegółów jest ustalonych, a nie opcjonalnych. Przechwycona treść staje się izolowaną grupą przezroczystości w DeviceRGB, więc maska jest obliczana w znanej przestrzeni kolorów. Kolor tła musi więc mieć trzy składowe, pasujące do tej grupy. Funkcja transferu przyjmuje /Identity albo prawidłowy obiekt funkcji, pozwalając przekształcić krzywą maski bez ponownego malowania grafiki

Diagram porównujący maski miękkie luminancji i alfa zasilające NewSoftMaskGraphicsState, z przypomnieniem, że stan graficzny utrzymuje się, dopóki nie zostanie wyczyszczony
Maski luminancji odczytują jasność, podczas gdy maski alfa odczytują pokrycie, a NewSoftMaskGraphicsState opakowuje którąkolwiek z nich w trwały stan graficzny
uses
  PDFlibrary;

var
  Lib: TPDFlib;
  MaskCapture, MaskGS, PhotoID: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('brochure.pdf', '');

    // Strona 4 zawiera gradient w skali szarości namalowany do użycia jako maska
    MaskCapture := Lib.CapturePage(4);

    MaskGS := Lib.NewSoftMaskGraphicsState(MaskCapture,
      PDF_SOFT_MASK_LUMINOSITY, '0 0 0', '/Identity');

    Lib.SelectPage(1);
    Lib.SetGraphicsState(MaskGS);
    PhotoID := Lib.AddImageFromFile('hero.jpg', 0);
    Lib.SelectImage(PhotoID);
    Lib.DrawImage(40, 700, 515, 260);        // zanika pod maską

    // Ustawienia ExtGState utrzymują się: jawnie zakończ zakres
    Lib.SetGraphicsState(Lib.NewSoftMaskGraphicsState(0,
      PDF_SOFT_MASK_NONE, '', ''));

    Lib.SaveToFile('brochure-masked.pdf');
  finally
    Lib.Free;
  end;
end;

Ten ostatni krok to nie ozdobnik. Parametry stanu graficznego utrzymują się, dopóki nie zostaną zmienione, a zastosowanie potem zwykłego stanu graficznego nie czyści aktywnej maski miękkiej. Bez jawnego /SMask /None wszystko narysowane po zamaskowanej grafice dziedziczy maskę, co jest jednym z bardziej zagadkowych błędów do zdiagnozowania wizualnie

Maski obrazów i wpis Matte

Dla maskowania na poziomie obrazu obraz maski jest najpierw normalizowany do DeviceGray, więc maska dostarczona w dowolnej przestrzeni kolorów zachowuje się przewidywalnie. Opcjonalny wpis /Matte, od jednej do czterech składowych w zależności od przestrzeni kolorów obrazu, informuje odbiorcę, że dane obrazu są przemnożone przez ten kolor, co zapobiega ciemnej aureoli pojawiającej się, gdy grafika z przemnożonym kanałem jest kompozytowana tak, jakby miała zwykłą alfę

Diagram pokazujący, jak brakujący wpis Matte daje ciemne aureole, podczas gdy zadeklarowany Matte kompozytuje czysto, obok barier PDF 1.4 oraz PDF/A i PDF/X
Zadeklarowany /Matte zapobiega ciemnej aureoli grafiki z przemnożonym kanałem, podczas gdy pliki PDF/A-1 i PDF/X-1 są odrzucane, zanim jakikolwiek obiekt zostanie zmieniony

Liczą się tu dwie bariery. PDF/A-1 i PDF/X-1 zabraniają przezroczystości, a wywołanie odmawia, zanim zmodyfikuje jakikolwiek obiekt, zamiast produkować plik, który później nie przejdzie walidacji. A udana maska miękka wymaga PDF 1.4, co biblioteka ustawia automatycznie — przezroczystość nie istniała przed tą wersją, a nagłówek 1.3 w pliku zawierającym maski miękkie jest po prostu fałszywy

Powtarzające się stany są używane ponownie: ten sam przechwycony formularz, typ maski, tło i sygnatura funkcji transferu dają ten sam obiekt stanu graficznego zamiast nowego przy każdym wywołaniu. W dokumencie maskującym identycznie sto obrazów to dziewięćdziesiąt dziewięć obiektów, które nigdy nie zostają zapisane

Dlaczego spłaszczać i co spłaszczanie faktycznie robi

Przezroczystość wciąż jest najczęstszym źródłem sytuacji „na ekranie wygląda dobrze, a na maszynie drukarskiej źle”. Starsze RIP-y, niektóre systemy zgłoszeń regulacyjnych i PDF/X-1a wymagają pliku w ogóle bez żywej przezroczystości

Spłaszczanie jest tu celowo dosłowne: każda strona w żądanym zakresie jest renderowana przy wybranym DPI na nieprzezroczystym tle, a treść i zasoby strony są zastępowane tym pojedynczym obrazem. Ponieważ renderer produkuje ostateczny skompozytowany wynik, obejmuje to wszystko naraz — grupy przezroczystości, tryby mieszania, maski miękkie, wzory i zagnieżdżone formularze — bez żadnej logiki przybliżania dla poszczególnych konstrukcji, która mogłaby zawieść. Szczytowe zużycie pamięci rastrowej pozostaje ograniczone do jednej strony

Diagram potoku FlattenTransparency renderującego każdą żądaną stronę przy 300 DPI na nieprzezroczystym tle i zapisującego płaski, gotowy do druku plik PDF 1.3
Każda żądana strona renderuje się raz przy wybranym DPI i wraca jako pojedynczy nieprzezroczysty obraz zapisany w prawdziwym pliku PDF 1.3
// Spłaszcz strony 1-8 przy 300 DPI na białym tle, następnie zapisz jako PDF 1.3
Lib.FlattenTransparency('1-8', 300, $FFFFFF, 0);
Lib.SaveToFile('press-ready.pdf');

Kompromis jest równie dosłowny: wyjściem jest obraz, więc tekst nie jest już zaznaczalny ani przeszukiwalny, a krawędzie wektorowe są ponownie próbkowane przy wybranym DPI. Spłaszczanie to krok dostarczenia, nie krok edycji. Zachowaj przezroczysty oryginał i spłaszcz kopię

Obrócone strony, jednostki użytkownika i pola stron

Obrócona strona wymaga staranności, którą naiwna implementacja pomija. Po renderowaniu wizualna szerokość i wysokość są zapisywane z powrotem do MediaBox, jednostka użytkownika jest wliczana w efektywne DPI i nowy rozmiar strony, a wpis /Rotate, jednostka użytkownika i pomocnicze pola stron są usuwane

Pominięcie tego daje plik, w którym spłaszczony obraz jest poprawny, ale przeglądarka obraca go ponownie albo skaluje przez jednostkę użytkownika, która już nie ma zastosowania. Usunięcie pól zamiast ich ponownego obliczenia jest właściwą decyzją, ponieważ TrimBox opisujący geometrię sprzed spłaszczenia jest gorszy niż brak TrimBox w ogóle

Adnotacje i pola formularza są domyślnie renderowane do statycznego obrazu, a interaktywne obiekty usuwane, czego potrzebuje dostarczana wersja gotowa do druku. Jeśli zamiast tego zachowasz obiekty interaktywne, przebieg adnotacji renderera jest wyłączany, aby nic nie zostało narysowane dwukrotnie

Kontrola wersji, która skanuje wszystko

Zapis jako PDF 1.3 wymaga, by nigdzie nie została żadna przezroczystość, a kontrola przed zapisem sprawdza każdy obiekt pośredni, nie tylko te osiągalne ze stron. To surowsze, niż wygląda na pierwszy rzut oka, i jest poprawne: osierocona maska miękka pozostawiona przez wcześniejszą edycję wciąż jest w pliku, a odbiorca przechodzący po obiektach, a nie po stronach, ją znajdzie

Aby to obniżenie wersji było osiągalne, przebieg oznaczania i zamiatania (mark-and-sweep) domyślnie uruchamia się, gdy tylko każda docelowa strona zostanie pomyślnie zastąpiona, zwalniając nieosiągalne obiekty. Konsekwencją, z którą trzeba się liczyć, jest to, że identyfikatory obrazu, przechwyconej strony i stanu graficznego uzyskane przed tym zamiataniem mogą potem już nie być ważne. Uzyskaj ponownie identyfikatory zasobów, których potrzebujesz, zanim będziesz kontynuować edycję, albo wyłącz zamiatanie i zaakceptuj, że wersji nie da się wtedy obniżyć

Obraz uzupełniają dwaj towarzysze. Grafika wektorowa wykorzystująca cieniowania i gradienty zamiast masek jest opisana w grafice wektorowej, shaderach i gradientach, i często jest lepszą drogą, gdy efekt da się wyrazić jako cieniowanie. A jeśli powodem spłaszczania jest wymóg kolorystyczny, a nie przezroczystości, ścieżka konwersji w przekolorowywaniu dokumentu do innej przestrzeni kolorów zachowuje żywy tekst tam, gdzie spłaszczanie by go nie zachowało

Tworzenie masek miękkich, spłaszczanie i stojący za nimi silnik renderujący są dostarczane w jednej bibliotece dla Delphi, C++Builder i Free Pascal; pełną listę funkcji znajdziesz na stronie PDF Library for Delphi