V sedmém článku, v němž se věnujeme programovacím jazykům a taktéž knihovnám, které mohou být použity pro výuku programování i základů počítačové grafiky, si popíšeme některé další důležité třídy použité v knihovně Pygame. Nejprve si ukážeme způsob získání informací o dostupných grafických režimech a posléze si vysvětlíme způsob použití antialiasingu a na závěr princip načítání a vykreslování rastrových obrázků s využitím operace BitBLT/blit.
Obsah
1. Získání informací o dostupných grafických režimech
2. Získání informací o použitém grafickém subsystému
3. Vykreslování úseček bez využití antialiasingu
4. Vykreslování úseček s využitím antialiasingu
5. Práce s rastrovými obrázky – základ pro tvorbu 2D her
6. Vznik operace BitBLT (Blit)
7. Použití operace BitBLT (Blit) v knihovně Pygame
8. Vykreslení vycentrovaného obrázku
10. Repositář se zdrojovými kódy dnešních demonstračních příkladů
11. Funkce použité v dnešních demonstračních příkladech
1. Získání informací o dostupných grafických režimech
V předchozí části článku o programovacích jazycích a taktéž knihovnách, které mohou být použity pro výuku programování i základů počítačové grafiky, jsme si řekli základní informace o knihovně Pygame, která umožňuje vytvářet 2D hry s využitím dnes velmi populárního programovacího jazyka Python. Víme již, že Pygame tvoří vrstvu mezi nízkoúrovňovými knihovnami typu SDL, X11, DirectX atd. a vysokoúrovňovým Pythonem. To mj. znamená, že hra či jiná graficky orientovaná aplikace používající knihovnu Pygame může být bez nutnosti změny zdrojového kódu provozována na různých operačních systémech i na různých architekturách.
Nicméně v některých případech může být velmi užitečné zjistit, jaké grafické režimy popř. jaký počet barev (a tím pádem i bitovou hloubku) konkrétní systém podporuje. K získání informací o aktuálně používaném driveru slouží funkce pygame.display.get_driver() a pro přečtení seznamu nabízených grafických režimů pak funkce pygame.display.list_modes(). Této funkci je možné předat bitovou hloubku a tím seznam grafických režimů omezit, například pouze na režimy s 256 barvami atd. Použití obou zmíněných funkcí je ukázáno na dnešním prvním demonstračním příkladu. Povšimněte si, že rozlišení grafických režimů vrácených funkcí pygame.display.list_modes() je rozděleno na šířku a výšku, kterou je možné získat z vrácené sekvence na indexech 0 a 1:
#!/usr/bin/python # vim: set fileencoding=utf-8 # Demonstrační příklady využívající knihovnu Pygame import pygame, sys # Příklad číslo 6: výpis seznamu všech dostupných grafických # režimů pro zadané bitové hloubky. # Nutno importovat kvůli konstantám QUIT atd. from pygame.locals import * # Inicializace knihovny Pygame pygame.init() print("Driver: " + pygame.display.get_driver()) print("") # Budou nás zajímat grafické režimy s bitovou hloubkou # 8bpp (256 barev), 16bpp (hi-color), 24bpp a 32bpp (True Color) for depth in [8,16,24,32]: print("Graphics modes for %d bpp:" % depth) # Získat seznam grafických režimů pro danou bitovou hloubku gfx_modes = pygame.display.list_modes(depth) # Vypsat všechny získané režimy for gfx_mode in gfx_modes: print(" %d x %d pixels" % (gfx_mode[0], gfx_mode[1])) pygame.quit() # finito
Příklad výstupu na systému Raspberry Pi s X Window systémem. Povšimněte si, že je nabízen jen jediný režim (právě aktivní) a pouze šestnáctibitová hloubka (hi-color):
Driver: x11 Graphics modes for 8 bpp: Graphics modes for 16 bpp: 1280 x 1024 pixels Graphics modes for 24 bpp: Graphics modes for 32 bpp:
Příklad výstupu na pracovním notebooku s X Window systémem. Opět je nabízen jediný režim, ovšem s 32bitovou hloubkou (true color):
Driver: x11 Graphics modes for 8 bpp: Graphics modes for 16 bpp: Graphics modes for 24 bpp: Graphics modes for 32 bpp: 1440 x 900 pixels
Při spuštění hry na konzoli (bez X Window) jsou k dispozici buď režimy podporované knihovnou fbcon, alternativně pak pseudografické režimy založené na AAlib:
Driver: fbcon Graphics modes for 8 bpp: 1600 x 1200 pixels 1280 x 1024 pixels 1024 x 1024 pixels 1280 x 960 pixels 1152 x 864 pixels 1024 x 768 pixels 800 x 600 pixels 768 x 576 pixels 640 x 480 pixels Graphics modes for 16 bpp: 1600 x 1200 pixels 1280 x 1024 pixels 1280 x 1024 pixels 1024 x 1024 pixels 1280 x 960 pixels 1152 x 864 pixels 1024 x 768 pixels 800 x 600 pixels 768 x 576 pixels 640 x 480 pixels Graphics modes for 24 bpp: 1600 x 1200 pixels 1280 x 1024 pixels 1024 x 1024 pixels 1280 x 960 pixels 1152 x 864 pixels 1024 x 768 pixels 800 x 600 pixels 768 x 576 pixels 640 x 480 pixels Graphics modes for 32 bpp: 1600 x 1200 pixels 1280 x 1024 pixels 1024 x 1024 pixels 1280 x 960 pixels 1152 x 864 pixels 1024 x 768 pixels 800 x 600 pixels 768 x 576 pixels 640 x 480 pixels
2. Získání informací o použitém grafickém subsystému
Další důležité informace je možné v případě potřeby získat po zavolání funkce pygame.display.Info(). Tato funkce po svém zavolání vrátí datovou strukturu obsahující například informaci o kapacitě obrazové paměti (celého framebufferu), způsobu ukládání pixelů, příznaky, které operace mohou být akcelerovány v závislosti na použitém driveru a taktéž informace o tom, jak přesně jsou uloženy barvové složky jednotlivých pixelů. Vrací se masky pro barvové složky Red, Green a Blue i počet bitů nutných pro posun jednotlivých složek při kódování a dekódování barev (tyto nízkoúrovňové operace prozatím nevyužijeme). Způsob získání a následného výpisu údajů vracených funkcí pygame.display.Info() je ukázán v dnešním druhém demonstračním příkladu:
#!/usr/bin/python # vim: set fileencoding=utf-8 # Demonstrační příklady využívající knihovnu Pygame import pygame, sys # Příklad číslo 7: výpis všech zjištěných informací o grafickém # subsystému využívaném knihovnou Pygame. # Nutno importovat kvůli konstantám QUIT atd. from pygame.locals import * # Inicializace knihovny Pygame pygame.init() def yesno(val): if val: return "yes" else: return "no" # Odkomentování si můžete vyzkoušet nastavit různé režimy #pygame.display.set_mode([1024,768], pygame.FULLSCREEN | pygame.HWSURFACE, 32) # Přečíst informaci o aktuálním nastavení grafického subsystému displayInfo = pygame.display.Info() pygame.display.quit() # Vypsat přečtené informace o grafickém subsystému print("Desktop resolution: %d x %d pixels" % (displayInfo.current_w, displayInfo.current_h)) # Údaj o kapacitě operační paměti nemusí být vždy k dispozici if displayInfo.video_mem != 0: print("Video memory size: %d MB" % displayInfo.video_mem) # Tato hodnota není nutně osminásobkem následujícíc hodnoty (neplatí u 15bitových režimů): print("bits per pixel: %d" % displayInfo.bitsize) print("bytes per pixel: %d" % displayInfo.bytesize) print("windowed mode: " + yesno(displayInfo.hw)) print("") print("HW acceleration: " + yesno(displayInfo.hw)) print("HW blitting: " + yesno(displayInfo.blit_hw)) print("HW colorkey blitting: " + yesno(displayInfo.blit_hw_CC)) print("HW alpha blitting: " + yesno(displayInfo.blit_hw_A)) print("") print("SW blitting: " + yesno(displayInfo.blit_sw)) print("SW colorkey blitting: " + yesno(displayInfo.blit_sw_CC)) print("SW alpha blitting: " + yesno(displayInfo.blit_sw_A)) print("") print("Masks: " + str(displayInfo.masks)) print("Shifts: " + str(displayInfo.shifts)) print("Losses: " + str(displayInfo.losses)) pygame.quit() # finito
Příklad výstupu na systému Raspberry Pi s X Window systémem:
Desktop resolution: 1280 x 1024 pixels Video memory size: 2560 MB bits per pixel: 16 bytes per pixel: 2 windowed mode: yes HW acceleration: yes HW blitting: no HW colorkey blitting: no HW alpha blitting: no SW blitting: no SW colorkey blitting: no SW alpha blitting: no Masks: (63488, 2016, 31, 0) Shifts: (11, 5, 0, 0) Losses: (3, 2, 3, 8)
Příklad výstupu na pracovním notebooku s X Window systémem:
Desktop resolution: 1440 x 900 pixels bits per pixel: 32 bytes per pixel: 4 windowed mode: no HW acceleration: no HW blitting: no HW colorkey blitting: no HW alpha blitting: no SW blitting: no SW colorkey blitting: no SW alpha blitting: no Masks: (16711680, 65280, 255, 0) Shifts: (16, 8, 0, 0) Losses: (0, 0, 0, 8)
3. Vykreslování úseček bez využití antialiasingu
Připomeňme si, že v knihovně Pygame je k dispozici několik funkcí sloužících pro vykreslování základních geometrických tvarů do libovolné bitmapy. Mezi podporované tvary patří samozřejmě i úsečky, které se používají v mnoha počítačových hrách, a to již od doby, kdy se pro tyto hry využívaly vektorové displeje. Pro vykreslení úsečky slouží funkce nazvaná pygame.draw.line(), které se předá jak bitmapa, do níž se má vykreslení provést (například zadní buffer), tak i barva úsečky, souřadnice koncových vrcholů úsečky a popř. taktéž její šířka. Pro vykreslení úsečky je použit slavný Bresenhamův algoritmus, který je jen nepatrně upraven pro vykreslení širších úseček. Tento algoritmus je velmi rychlý, neboť používá pouze jednoduché celočíselné operace, ovšem jeho nevýhodou je, že na výsledných úsečkách jsou jasně patrné „schody“, což bude ostatně patrné i po spuštění následujícího demonstračního příkladu, který vykresluje úsečky s různou barvou a šířkou:
#!/usr/bin/python # vim: set fileencoding=utf-8 # Demonstrační příklady využívající knihovnu Pygame # Příklad číslo 8: vykreslení úseček různé barvy, tloušťky # a sklonu bez použití antialiasingu. import pygame, sys, math # Nutno importovat kvůli konstantám QUIT atd. from pygame.locals import * # Velikost okna aplikace WIDTH = 320 HEIGHT = 240 # Inicializace knihovny Pygame pygame.init() clock = pygame.time.Clock() # Vytvoření okna pro vykreslování display = pygame.display.set_mode([WIDTH, HEIGHT]) # Nastavení titulku okna pygame.display.set_caption('Pygame test #8') # Konstanty s n-ticemi představujícími základní barvy BLACK = ( 0, 0, 0) BLUE = ( 0, 0, 255) CYAN = ( 0, 255, 255) GREEN = ( 0, 255, 0) YELLOW = (255, 255, 0) RED = (255, 0, 0) MAGENTA = (255, 0, 255) WHITE = (255, 255, 255) # Vyplnění plochy okna černou barvou display.fill(BLACK) # Vykreslení čar různou barvou pygame.draw.line(display, BLUE, (10, 10), (160, 20)) pygame.draw.line(display, CYAN, (10, 20), (160, 30)) pygame.draw.line(display, GREEN, (10, 30), (160, 40)) pygame.draw.line(display, YELLOW, (10, 40), (160, 50)) pygame.draw.line(display, RED, (10, 50), (160, 60)) pygame.draw.line(display, MAGENTA, (10, 60), (160, 70)) # Vykreslení čar s různým sklonem for i in range(1,90,5): # převod ze stupňů na radiány angle = math.radians(i) radius = 150 # výpočet koncových bodů úseček x = radius * math.sin(math.radians(i)) y = radius * math.cos(math.radians(i)) # vykreslení jedné úsečky pygame.draw.line(display, WHITE, (WIDTH-1, 0), (WIDTH-x, y)) # Vykreslení čar různou šířkou for i in range(1,10): pygame.draw.line(display, WHITE, (10 + i*15, 90), (20 + i*15, 230), i) # Hlavní herní smyčka while True: # Načtení a zpracování všech událostí z fronty for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == KEYDOWN and event.key == K_ESCAPE: pygame.quit() sys.exit() pygame.display.update() clock.tick(20) # finito
Obrázek 1: Různobarevné úsečky vykreslené bez použití antialiasingu.
Obrázek 2: Zvětšený pohled na výsledek demonstračního příkladu, který vykreslil úsečky bez použití antialiasingu (na obrázek je nutné kliknout, aby se zobrazil v plné velikosti).
4. Vykreslování úseček s využitím antialiasingu
Aby se alespoň do určité míry zabránilo kresbě „schodovitých“ úseček, je možné namísto funkce pygame.draw.line() použít podobně pojmenovanou funkci pygame.draw.aaline() sloužící pro vykreslení úseček algoritmem, jehož autorem je Xiaolin Wu. Tento algoritmus při vykreslování úsečky implementuje lokální antialasing, ovšem v počítačové grafice prakticky vždy platí, že za kvalitu zaplatíme výpočetní rychlostí. To je pravda i v případě použití funkce pygame.draw.aaline(), která je navíc omezena pouze na použití v bitmapách s bitovou hloubkou 24bpp či 32bpp, tj. v truecolor režimech. Druhým omezením je možnost kresby úseček se šířkou pouze jednoho pixelu, i když se ve skutečnosti při vykreslování mění barvy vždy dvou sousedních pixelů. V posledním parametru funkce pygame.draw.aaline() je pravdivostní hodnota, kterou se řídí režim přepisování již vykreslených pixelů (v našem demonstračním příkladu je vliv této hodnoty patrný v pravém horním rohu vykreslené scény):
#!/usr/bin/python # vim: set fileencoding=utf-8 # Demonstrační příklady využívající knihovnu Pygame # Příklad číslo 9: vykreslení úseček různé barvy a sklonu # s použitím jednoduchého antialiasingu. import pygame, sys, math # Nutno importovat kvůli konstantám QUIT atd. from pygame.locals import * # Velikost okna aplikace WIDTH = 320 HEIGHT = 240 # Inicializace knihovny Pygame pygame.init() clock = pygame.time.Clock() # Vytvoření okna pro vykreslování display = pygame.display.set_mode([WIDTH, HEIGHT]) # Nastavení titulku okna pygame.display.set_caption('Pygame test #9') # Konstanty s n-ticemi představujícími základní barvy BLACK = ( 0, 0, 0) BLUE = ( 0, 0, 255) CYAN = ( 0, 255, 255) GREEN = ( 0, 255, 0) YELLOW = (255, 255, 0) RED = (255, 0, 0) MAGENTA = (255, 0, 255) WHITE = (255, 255, 255) # Vyplnění plochy okna černou barvou display.fill(BLACK) # Vykreslení čar různou barvou pygame.draw.aaline(display, BLUE, (10, 10), (160, 20)) pygame.draw.aaline(display, CYAN, (10, 20), (160, 30)) pygame.draw.aaline(display, GREEN, (10, 30), (160, 40)) pygame.draw.aaline(display, YELLOW, (10, 40), (160, 50)) pygame.draw.aaline(display, RED, (10, 50), (160, 60)) pygame.draw.aaline(display, MAGENTA, (10, 60), (160, 70)) # Vykreslení čar s různým sklonem for i in range(1,90,5): # převod ze stupňů na radiány angle = math.radians(i) radius = 150 # výpočet koncových bodů úseček x = radius * math.sin(math.radians(i)) y = radius * math.cos(math.radians(i)) # vykreslení jedné úsečky, blend je nastaveno na True pygame.draw.aaline(display, WHITE, (WIDTH-1, 0), (WIDTH-x, y), True) # Vykreslení čar s jednotnou šířkou for i in range(1,10): pygame.draw.aaline(display, WHITE, (10 + i*15, 90), (20 + i*15, 230), False) # Hlavní herní smyčka while True: # Načtení a zpracování všech událostí z fronty for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == KEYDOWN and event.key == K_ESCAPE: pygame.quit() sys.exit() pygame.display.update() clock.tick(20) # finito
Obrázek 3: Různobarevné úsečky vykreslené s použitím antialiasingu.
Obrázek 4: Zvětšený pohled na výsledek demonstračního příkladu, který vykreslil úsečky s použitím antialiasingu (na obrázek je nutné kliknout, aby se zobrazil v plné velikosti). Povšimněte si, že u šikmých úseček se vykreslují vždy dva sousední pixely.
5. Práce s rastrovými obrázky – základ pro tvorbu 2D her
Většina historických i velká část soudobých počítačových her s dvoudimenzionální (2D) grafikou je charakteristická tím, že objekty v těchto hrách jsou reprezentovány s využitím rastrových obrázků (bitmap) o různé velikosti, které se postupně vykreslují do vytvářené dvoudimenzionální scény. Aby bylo přes některé části těchto rastrových obrázků viditelné i pozadí, používají se tři metody pro zajištění úplné či částečné průhlednosti. Buď je stanoveno, že určitá hodnota (tj. barva) pixelů má být zcela průhledná (typicky se jedná o jasně fialovou barvu, která se v typických scénách stejně nikde neobjevuje), dále je alternativně možné jeden bit v hodnotě pixelu použít pro určení průhlednosti (typické pro 16bitovou hloubku), nebo se může stanovit průhlednost pixelů doplněním bitmapy o takzvaný alfa kanál (alpha channel).
Obrázek 5: Některé starší herní konzole obsahovaly specializované čipy pro zobrazování malých pohyblivých bitmap – spritů.
S využitím grafické operace BitBLT (Bit Block Transfer) lze provádět, jak ostatně její název naznačuje, blokové přenosy bitmap nebo jejich výřezů, popř. v rámci přenosu nad bitmapami provádět různé další operace, například negaci barev zdrojové či cílové bitmapy, provedení bitové operace AND, XOR atd. (posléze se přidalo i zpracování alfa kanálu, o němž se zmíníme v dalších kapitolách). První implementace operace BitBLT byla použita v roce 1975 ve Smalltalku-72 a od té doby ji najdeme prakticky v každé implementaci tohoto programovacího jazyka, která obsahuje i knihovny pro práci s grafikou (mj. se jedná i o Squeak). Pro Smalltalk-74 vytvořil Daniel Ingalls optimalizovanou variantu operace BitBLT implementovanou v mikrokódu. Operace BitBLT se tak stala součástí operačního systému a bylo ji možné volat jak z assembleru, tak i z programů napsaných v jazyce BCPL a samozřejmě i ze Smalltalku (právě tuto implementaci můžeme považovat za vůbec první grafickou akceleraci). Posléze se díky své univerzalitě tato funkce rozšířila i do mnoha dalších operačních systémů a grafických knihoven.
Obrázek 6: Rastrové obrázky (zde zvětšené), které tvoří základ jedné RPG.
Vzhledem k tomu, že vykreslování rastrových obrázků do vytvářené 2D scény je velmi často používaná operace, není příliš překvapující, že se s touto operaci můžeme setkat v API mnoha grafických knihoven či dokonce v API operačních systémů (asi nejznámějším příkladem je WinAPI). Tyto operace se většinou nazývají BitBlt, BitBLT, Blit či méně často PIXT (Pixel Transfer) a PIXBLT. Kdy a na jakém systému se zkratka BitBlt objevila, se dozvíme v navazující kapitole.
Obrázek 7: Relativně moderní hra Warcraft II také používá téměř výhradně bitmapy pro zobrazení budov i postaviček.
6. Vznik operace BitBLT (Blit)
Jedním z velmi důležitých mezníků, který se odehrál ve vývoji osobních počítačů, je vznik konceptu grafického uživatelského rozhraní na počítači nazvaném Xerox Alto. Tento počítač používal pro zobrazování všech informací na monitoru výhradně rastrovou grafiku, konkrétně se jednalo o černobílé bitmapové obrázky (každý pixel byl reprezentován jediným bitem, podobně jako později na počítačích Apple Macintosh). Při programování grafických rutin pro tento počítač a začleňování vytvářených rutin do operačního systému si autoři programového vybavení uvědomili, že poměrně velkou část již implementovaných funkcí lze zobecnit do jediné operace, která všechny tyto funkce může elegatně nahradit.
Obrázek 8: Systém Cedar pro počítač Xerox Alto byl plně založen na interaktivní práci uživatele pomocí grafického uživatelského rozhraní a bitmapových obrázků.
Těmito autory byli Daniel Ingalls, Larry Tesler, Bob Sproull a Diana Merry, kteří svoji zobecněnou rastrovou operaci pojmenovali BitBLT, což je zkratka operace Bit Block Transfer. První část názvu, tj. slovo Bit naznačuje, že se jedná o operaci prováděnou nad bitmapami (původně, jak již víme z předchozího textu, vytvořených z jednobitových pixelů). Druhá polovina názvu, tj. zkratka BLT, byla odvozena ze jména instrukce pro blokový přenos dat, jenž byla používaná v assembleru počítače DEC PDP-10.
Obrázek 9: Část originálního kódu původní implementace operace BitBLT naprogramované Danielem Ingallsem.
7. Použití operace BitBLT (Blit) v knihovně Pygame
I v knihovně Pygame operaci typu BitBLT/Blit samozřejmě nalezneme a dokonce se bude jednat o jednu z nejčastěji volaných operací vůbec. Rastrové obrázky jsou zde totiž představovány objekty typu Surface, přičemž minimálně jeden takový objekt musí být vytvořen a používán v každé aplikaci, která přes knihovnu Pygame implementuje vykreslování. Tímto objektem je samotný (zadní) buffer vytvořený s využitím již minule popsané funkce pygame.display.set_mode() (my jsme návratovou hodnotu této funkce pouze přiřadili do proměnné nazvané display, ovšem dále jsme se o tom, že tato hodnota představuje reálný objekt, nezmínili). Další bitmapy je možné načíst s využitím metody pygame.image.load(), s níž se seznámíme v navazujících demonstračních příkladech. Pro objekty typu Surface je deklarována metoda nazvaná blit(), které se předá cílová bitmapa (objekt typu Surface) a taktéž souřadnice v cílové bitmapě, kde má vykreslení zdrojové bitmapy začít. Pokud obsahuje zdrojová bitmapa pixely s alfa kanálem, je informace o průhlednosti pixelů v průběhu operace BitBLT/Blit automaticky použita (pokud není specifikováno jinak).
Obrázek 10: Tento rastrový obrázek bude použit ve všech demonstračních příkladech, v nichž bude použita funkce blit().
V dalším demonstračním příkladu je ukázáno, jakým způsobem je možné do aplikace používající knihovnu Pygame načíst rastrový obrázek uložený ve formátu PNG a jak následně tento obrázek s využitím funkce blit() vykreslit do zadního bufferu, který je následně zobrazen:
#!/usr/bin/python # vim: set fileencoding=utf-8 # Demonstrační příklady využívající knihovnu Pygame # Příklad číslo 10: použití objektů typu Surface a metoda blit(). import pygame, sys, os # Nutno importovat kvůli konstantám QUIT atd. from pygame.locals import * # Velikost okna aplikace WIDTH = 320 HEIGHT = 240 # Inicializace knihovny Pygame pygame.init() clock = pygame.time.Clock() # Vytvoření okna pro vykreslování display = pygame.display.set_mode([WIDTH, HEIGHT]) # Nastavení titulku okna pygame.display.set_caption('Pygame test #10') # Konstanty s n-ticemi představujícími základní barvy BLACK = ( 0, 0, 0) # Vyplnění plochy okna černou barvou display.fill(BLACK) image_surface = pygame.image.load(os.path.join('images', 'gnome-globe.png')) # Cílový Zdrojový Souřadnice # objekt typu objekt typu v cílovém objektu # surface surface display.blit(image_surface, (0, 0)) display.blit(image_surface, (100, 100)) # Hlavní herní smyčka while True: # Načtení a zpracování všech událostí z fronty for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == KEYDOWN and event.key == K_ESCAPE: pygame.quit() sys.exit() pygame.display.update() clock.tick(20) # finito
Povšimněte si především sémantiky funkce blit(). Objekt, nad nímž je tato funkce volána, představuje cílový obrázek, zatímco zdrojový obrázek je předán této funkci v prvním parametru. Za tímto povinným parametrem následují souřadnice udávající, do kterého místa cílového obrázku má být vykreslení provedeno. Ve skutečnosti je možné funkci blit() předat i další parametry, o nichž se zmíníme níže.
Dále si povšimněte, že se v hlavní smyčce událostí reaguje i na stisk klávesy Escape. Zpracování této události bylo do příkladu přidáno z toho důvodu, aby tuto jednoduchou aplikaci bylo možné spustit a především pak i ukončit z konzole, tj. v celoobrazovkovém grafickém režimu. Reakce na stisk klávesy Escape bude součástí i všech navazujících demonstračních příkladů.
Obrázek 11: Výsledek předchozího demonstračního příkladu.
8. Vykreslení vycentrovaného obrázku
Všechny objekty typu Surface, tedy i načtené obrázky, nabízí programátorům celou řadu metod sloužících pro zjištění jejich vlastností. Jedná se například o metody get_width() a get_height() vracející rozměry obrázku. Jak je možné tyto dvě základní metody použít, je ukázáno v následujícím demonstračním příkladu, který po svém spuštění provede následující operace:
- Inicializace aplikace pomocí funkce pygame.init()
- Nastavení grafického režimu či otevření okna s využitím pygame.display.set_mode()
- Nastavení titulku okna s využitím funkce pygame.display.set_caption()
- Vymazání zadního bufferu, resp. jeho vyplnění černými pixely
- Vykreslení několika úseček do bufferu
- Načtení rastrového obrázku
- Výpočet umístění obrázku do bufferu na základě rozměru obrázku i bufferu
- Vykreslení obrázku pomocí nám již známé metody Surface.blit()
- Načtení všech událostí z fronty událostí přes pygame.event.get()
- Ukončení aplikace po příchodu události QUIT zavoláním funkce pygame.quit() a sys.exit()
- Ukončení aplikace po stisku Escape zavoláním funkce pygame.quit() a sys.exit()
Následuje výpis zdrojového kódu:
#!/usr/bin/python # vim: set fileencoding=utf-8 # Demonstrační příklady využívající knihovnu Pygame # Příklad číslo 11: některé další vlastnosti objektů typu Surface. import pygame, sys, os, math # Nutno importovat kvůli konstantám QUIT atd. from pygame.locals import * # Velikost okna aplikace WIDTH = 320 HEIGHT = 240 # Inicializace knihovny Pygame pygame.init() clock = pygame.time.Clock() # Vytvoření okna pro vykreslování display = pygame.display.set_mode([WIDTH, HEIGHT]) # Nastavení titulku okna pygame.display.set_caption('Pygame test #11') # Konstanty s n-ticemi představujícími základní barvy BLACK = ( 0, 0, 0) BLUE = ( 0, 0, 255) CYAN = ( 0, 255, 255) GREEN = ( 0, 255, 0) YELLOW = (255, 255, 0) RED = (255, 0, 0) MAGENTA = (255, 0, 255) WHITE = (255, 255, 255) # Vyplnění plochy okna černou barvou display.fill(BLACK) # Vykreslení čar různou barvou pygame.draw.line(display, BLUE, (10, 10), (160, 10)) pygame.draw.line(display, CYAN, (10, 20), (160, 20)) pygame.draw.line(display, GREEN, (10, 30), (160, 30)) pygame.draw.line(display, YELLOW, (10, 40), (160, 40)) pygame.draw.line(display, RED, (10, 50), (160, 50)) pygame.draw.line(display, MAGENTA, (10, 60), (160, 60)) # Vykreslení čar s různým sklonem for i in range(1,90,5): # převod ze stupňů na radiány angle = math.radians(i) radius = 150 # výpočet koncových bodů úseček x = radius * math.sin(math.radians(i)) y = radius * math.cos(math.radians(i)) if display.get_bitsize() >= 24: # vykreslení jedné antialiasované úsečky, blend je nastaveno na True pygame.draw.aaline(display, WHITE, (WIDTH-1, 0), (WIDTH-x, y), True) else: # vykreslení jedné úsečky pygame.draw.line(display, WHITE, (WIDTH-1, 0), (WIDTH-x, y)) # Vykreslení čar různou šířkou for i in range(1,10): pygame.draw.line(display, WHITE, (10 + i*15, 90), (10 + i*15, 230), i) image_surface = pygame.image.load(os.path.join('images', 'gnome-globe.png')) # Výpočet souřadnic pro umístění obrázku přesně doprostřed okna center_x = (display.get_width() - image_surface.get_width()) / 2 center_y = (display.get_height() - image_surface.get_height()) / 2 # Vykreslení obrázku display.blit(image_surface, (center_x, center_y)) # Hlavní herní smyčka while True: # Načtení a zpracování všech událostí z fronty for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == KEYDOWN and event.key == K_ESCAPE: pygame.quit() sys.exit() pygame.display.update() clock.tick(20) # finito
Obrázek 12: Výsledek běhu předchozího demonstračního příkladu.
9. Obsah další části seriálu
V následující části tohoto seriálu se seznámíme s některými dalšími možnostmi, které lze využít při kopírování a vykreslování rastrových obrázků. Na toto téma pak navážeme popisem použití alfa kanálu či průhlednosti nastavené pro celou bitmapu. S problematikou vykreslování bitmap v knihovně Pygame úzce souvisí i práce s písmem a fonty, což je taktéž téma, kterému se budeme věnovat příště.
Obrázek 13: Ukázka rastrových operací prováděných v rámci funkce blit().
Obrázek 14: Detail předchozího screenshotu.
10. Repositář se zdrojovými kódy dnešních demonstračních příkladů
Všechny demonstrační příklady, s nimiž jsme se v dnešním článku seznámili, byly uloženy do Git repositáře umístěného na GitHubu (https://github.com/tisnik/presentations):
# | Příklad | Zdrojový kód |
---|---|---|
1 | pygame6.py | https://github.com/tisnik/presentations/blob/master/pygame/pygame6.py |
2 | pygame7.py | https://github.com/tisnik/presentations/blob/master/pygame/pygame7.py |
3 | pygame8.py | https://github.com/tisnik/presentations/blob/master/pygame/pygame8.py |
4 | pygame9.py | https://github.com/tisnik/presentations/blob/master/pygame/pygame9.py |
5 | pygame10.py | https://github.com/tisnik/presentations/blob/master/pygame/pygame10.py |
6 | pygame11.py | https://github.com/tisnik/presentations/blob/master/pygame/pygame11.py |
7 | images/gnome-globe.png | https://github.com/tisnik/presentations/tree/master/pygame/images |
Poznámka: poslední soubor nazvaný „gnome-globe.png“ musí být uložen v podadresáři „images“, protože právě z tohoto podadresáře je načítán demonstračními příklady.
11. Funkce použité v dnešních demonstračních příkladech
- pygame.init()
http://www.pygame.org/docs/ref/pygame.html#pygame.init - pygame.quit()
http://www.pygame.org/docs/ref/pygame.html#pygame.quit - pygame.display.set_mode()
http://www.pygame.org/docs/ref/display.html#pygame.display.set_mode - pygame.display.set_caption()
http://www.pygame.org/docs/ref/display.html#pygame.display.set_caption - pygame.display.get_driver()
http://www.pygame.org/docs/ref/display.html#pygame.display.get_driver - pygame.display.list_modes()
http://www.pygame.org/docs/ref/display.html#pygame.display.list_modes - pygame.display.Info()
http://www.pygame.org/docs/ref/display.html#pygame.display.Info - pygame.display.quit()
http://www.pygame.org/docs/ref/display.html#pygame.display.quit - pygame.display.update()
http://www.pygame.org/docs/ref/display.html#pygame.display.update - pygame.surface.blit()
http://www.pygame.org/docs/ref/surface.html#pygame.Surface.blit - pygame.event.get()
http://www.pygame.org/docs/ref/event.html#pygame.event.get - pygame.time.Clock.tick()
http://www.pygame.org/docs/ref/time.html#pygame.time.Clock.tick - pygame.draw.line()
http://www.pygame.org/docs/ref/draw.html#pygame.draw.line - pygame.draw.aaline()
http://www.pygame.org/docs/ref/draw.html#pygame.draw.aaline - pygame.image.load()
http://www.pygame.org/docs/ref/image.html#pygame.image.load
12. Odkazy na Internetu
- Pygame.org
http://pygame.org/hifi.html - Pygame - instalační soubory pro různé operační systémy
http://pygame.org/download.shtml - Pygame: documentation
http://www.pygame.org/docs/ - Pygame Wiki: Getting Started
http://www.pygame.org/wiki/GettingStarted - Pygame Tutorials: Tutorials Basic
http://pygametutorials.wikidot.com/tutorials-basic - Python.org (dokumentace k jazyku, odkazy na instalační soubory atd.)
https://www.python.org/ - How to Draw with Pygame on Your Raspberry Pi
http://www.dummies.com/how-to/content/how-to-draw-with-pygame-on-your-raspberry-pi.html - Bresenham's line algorithm
https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm - Xiaolin Wu's line algorithm
https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm - Pyglet
https://pypi.python.org/pypi/pyglet/1.2.4 - Pyglet documentation
http://pythonhosted.org/pyglet/ - PyOpenGL
https://pypi.python.org/pypi/PyOpenGL/ - Graphical user interface (Wikipedia)
http://en.wikipedia.org/wiki/Graphical_user_interface - The Real History of the GUI
http://articles.sitepoint.com/article/real-history-gui - Computer-History: Xerox Alto
http://www.mr-gadget.de/apple/2004-01-15/computer-history-xerox-alto - bitsavers.org
http://www.bitsavers.org/ - Dokumenty k počítači Xerox Alto na bitsavers.org
http://www.bitsavers.org/pdf/xerox/alto/ - The ALTO Computer
http://www.maniacworld.com/alto-computer-video.html - Xerox Alto Operating System and Alto Applications
http://www.digibarn.com/collections/software/alto/index.html - Xerox Alto (Wikipedia)
http://en.wikipedia.org/wiki/Xerox_Alto - BitBLT routines (1975)
http://www.bitsavers.org/pdf/xerox/alto/BitBLT_Nov1975.pdf - BitBlt in Squeak
http://wiki.squeak.org/squeak/189 - Bitmaps, Device Contexts and BitBlt
http://www.winprog.org/tutorial/bitmaps.html - BitBlt Game Programming Tutorial
http://www.freevbcode.com/ShowCode.asp?ID=3677 - Bit blit (Wikipedia)
http://en.wikipedia.org/wiki/BitBLT - The Xerox Alto
http://toastytech.com/guis/alto3.html - History of the graphical user interface
http://en.wikipedia.org/wiki/History_of_the_graphical_user_interface - Domovská stránka systému LÖVE
http://love2d.org/ - Dokumentace k systému LÖVE
http://love2d.org/wiki/love - Domovská stránka programovacího jazyka Lua
http://www.lua.org/ - Seriál o programovacím jazyku Lua (root.cz):
http://www.root.cz/serialy/programovaci-jazyk-lua/ - Domovská stránka systému LÖVE
http://love2d.org/ - Domovská stránka programovacího jazyka Lua
http://www.lua.org/ - Web o Lieru, Gusanos, GeneRally, Atari atd.
http://karelik.wz.cz/ - Web o Lieru, Gusanos
http://karelik.wz.cz/gusanos.php - GUSANOS
http://gusanos.sourceforge.net/ - GUSANOS Download
http://sourceforge.net/projects/gusanos/ - Lua
http://www.linuxexpres.cz/praxe/lua - Lua
http://cs.wikipedia.org/wiki/Lua - Lua (programming language)
http://en.wikipedia.org/wiki/Lua_(programming_language) - The Lua Programming Language
http://www.tiobe.com/index.php/paperinfo/tpci/Lua.html - Lua Programming Gems
http://www.lua.org/gems/ - LuaForge
http://luaforge.net/ - Forge project tree
http://luaforge.net/softwaremap/trove_list.php - SdlBasic home page
http://www.sdlbasic.altervista.org/main/ - SdlBasic examples
http://nitrofurano.linuxkafe.com/sdlbasic/ - SdlBasic na Wikipedii
http://en.wikipedia.org/wiki/SdlBasic - Simple DirectMedia Layer
http://en.wikipedia.org/wiki/Simple_DirectMedia_Layer - SDLBASIC – The high-level interpreter for all?
http://openbytes.wordpress.com/2008/11/08/sdlbasic-the-high-level-interpreter-for-all/ - FreeBasic home page
http://www.freebasic.net/ - FreeBASIC (Wikipedia EN)
https://en.wikipedia.org/wiki/FreeBASIC - FreeBASIC Wiki
http://www.freebasic.net/wiki/wikka.php?wakka=FBWiki - FreeBASIC Manual
http://www.freebasic.net/wiki/wikka.php?wakka=DocToc - FreeBASIC (Wikipedia CZ)
http://cs.wikipedia.org/wiki/FreeBASIC - The Griffon Legend
http://syn9.thingie.net/?table=griffonlegend - Seriál Letní škola programovacího jazyka Logo
http://www.root.cz/serialy/letni-skola-programovaciho-jazyka-logo/ - Scratch: oficiální stránka projektu
http://scratch.mit.edu/ - Scratch: galerie projektů vytvořených ve Scratchi
http://scratch.mit.edu/galleries/browse/newest - Scratch: nápověda
file:///usr/share/scratch/Help/en/index.html - Scratch: obrazovky nápovědy
file:///usr/share/scratch/Help/en/allscreens.html - Scratch (Wikipedie CZ)
http://cs.wikipedia.org/wiki/Scratch - Scratch (programming language)
http://en.wikipedia.org/wiki/Scratch_(programming_language) - Scratch Modification
http://wiki.scratch.mit.edu/wiki/Scratch_Modification - Scratch Lowers Resistance to Programming
http://www.wired.com/gadgetlab/2009/03/scratch-lowers/ - Snap!
http://snap.berkeley.edu/ - Prostředí Snap!
http://snap.berkeley.edu/snapsource/snap.html - Alternatives to Scratch
http://wiki.scratch.mit.edu/wiki/Alternatives_to_Scratch - Basic-256 home page
http://www.basic256.org/index_en - Basic-256 Language Documentation
http://doc.basic256.org/doku.php - Basic-256 Art Gallery
http://www.basic256.org/artgallery - Basic-256 Tutorial
http://www.basic256.org/tutorials - Why BASIC?
http://www.basic256.org/whybasic - A book to teach ANYBODY how to program a computer (using BASIC)
http://www.basicbook.org/ - BASIC Computer Games (published 1978) - Hammurabi
http://atariarchives.org/basicgames/showpage.php?page=78 - Hamurabi - zdrojový kód v BASICu
http://www.dunnington.u-net.com/public/basicgames/HMRABI