W robieniu zrzutów ekranowych (screenshotów) nie ma, wydawałoby się, nic nadzwyczajnego. Wciskamy po prostu klawisz Print Screen i obraz ekranu ląduje nam w systemowym Schowku, po czym możemy go użyć w dowolny sposób. Przynajmniej tego właśnie się spodziewamy zazwyczaj.
W rzeczywistości robienie screenów to dość śliski temat, jeśli mamy do czynienia z czymś więcej niż zwykłymi aplikacjami okienkowymi. Dotyczy to chociażby pełnoekranowych gier, korzystających z bibliotek graficznych typu DirectX i OpenGL. Wykorzystują one mechanizm znany jako hardware overlay, pozwalający – w skrócie – na ominięcie narzutu systemu operacyjnego związanego z zarządzeniem oknami wielu aplikacji na współdzielonym ekranie monitora. Skutkiem ubocznym jest między innymi to, że wbudowany w system mechanizm tworzenia zrzutów ekranu staje się w tej sytuacji bezużyteczny, jeśli chcemy wykonać screenshota tego rodzaju aplikacji.
Skąd w takim razie biorą się screeny, a nawet całe filmy ze współczesnych gier?… Cóż, istnieją oczywiście metody pozwalające na przechwytywanie obrazów generowanych przez aplikację – można bowiem skłonić ją do wywoływania naszej własnej wersji metody IDirect3DDeviceN::Present
zamiast tej wbudowanej :) Brzmi to pewnie tajemniczo i nieco hakersko, ale tak mniej więcej działają programy do przechwytywania wideo typu Fraps.
Do zwykłych screenów zazwyczaj jednak przewiduje się odpowiednie rozwiązania w samych grach. Jednym ze sposobów jest, w przypadku wykrycia wciśnięcia klawisza odpowiadającego za screenshoty (czyli zwykle Print Screen), wyrenderowanie sceny najpierw do tekstury, a ją potem na ekran. Rzeczoną teksturę można później zapisać do pliku.
Czy to dobre rozwiązanie? Otóż nie, i to aż z trzech powodów. Stosunkowo najmniej ważnym jest to, iż implementacja może być kłopotliwa, bo wymaga dobrania się do początku i do końca potoku renderingu w aplikacji z kolejnym render targetem. Bardziej istotny jest brak multisamplingu (a więc wygładzania krawędzi wielokątów znanego jako anti-aliasing) w powstałym obrazku. Nie będzie on więc wyglądać zbyt ładnie, już nie wspominając o tym, że nie będzie on odpowiadał temu, co widzimy na ekranie.
Jak więc należy robić screeny? Dokładnie tak, jak nam podpowiada intuicja: należy wziąć to, co widać na ekranie – czyli zawartość bufora przedniego (front buffer) i zapisać to do pliku. W DirectX mamy do tego metodę urządzenia o nazwie GetFrontBufferData
, która pobiera nam to jako powierzchnię:
Ważne jest, by pamiętać o właściwych rozmiarach powierzchni – takich, jakie ma bufor przedni. W trybie pełnoekranowym odpowiada to buforowi tylnemu, ale dla aplikacji okienkowej wcale nie musi, jeśli rozmiar jej okna możemy zmieniać.
W OpenGL podobną rolę spełnia do GetFrontBufferData
spełnia funkcja glReadPixels
. Z zapisaniem pobranego obrazu może być tylko “nieco” trudniej ;-)
To, co pokazałeś na obrazku, to nie tylko aliasing, ale jeszcze specjalne optymalizowanie rysowanego tekstu pod monitory LCD :).