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

9. Obsah další části seriálu

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

12. Odkazy na Internetu

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

01

Obrázek 1: Různobarevné úsečky vykreslené bez použití antialiasingu.

02

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

03

Obrázek 3: Různobarevné úsečky vykreslené s použitím antialiasingu.

04

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).

05

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.

06

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.

07

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.

08

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.

09

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).

10

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ů.

11

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

12

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ě.

13

Obrázek 13: Ukázka rastrových operací prováděných v rámci funkce blit().

14

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

  1. pygame.init()
    http://www.pygame.org/docs/ref/pygame.html#pygame.init
  2. pygame.quit()
    http://www.pygame.org/docs/ref/pygame.html#pygame.quit
  3. pygame.display.set_mode()
    http://www.pygame.org/docs/ref/display.html#pygame.display.set_mode
  4. pygame.display.set_caption()
    http://www.pygame.org/docs/ref/display.html#pygame.display.set_caption
  5. pygame.display.get_driver()
    http://www.pygame.org/docs/ref/display.html#pygame.display.get_driver
  6. pygame.display.list_modes()
    http://www.pygame.org/docs/ref/display.html#pygame.display.list_modes
  7. pygame.display.Info()
    http://www.pygame.org/docs/ref/display.html#pygame.display.Info
  8. pygame.display.quit()
    http://www.pygame.org/docs/ref/display.html#pygame.display.quit
  9. pygame.display.update()
    http://www.pygame.org/docs/ref/display.html#pygame.display.update
  10. pygame.surface.blit()
    http://www.pygame.org/docs/ref/surface.html#pygame.Surface.blit
  11. pygame.event.get()
    http://www.pygame.org/docs/ref/event.html#pygame.event.get
  12. pygame.time.Clock.tick()
    http://www.pygame.org/docs/ref/time.html#pygame.time.Clock.tick
  13. pygame.draw.line()
    http://www.pygame.org/docs/ref/draw.html#pygame.draw.line
  14. pygame.draw.aaline()
    http://www.pygame.org/docs/ref/draw.html#pygame.draw.aaline
  15. pygame.image.load()
    http://www.pygame.org/docs/ref/image.html#pygame.image.load

12. Odkazy na Internetu

  1. Pygame.org
    http://pygame.org/hifi.html
  2. Pygame - instalační soubory pro různé operační systémy
    http://pygame.org/download.shtml
  3. Pygame: documentation
    http://www.pygame.org/docs/
  4. Pygame Wiki: Getting Started
    http://www.pygame.org/wiki/GettingStarted
  5. Pygame Tutorials: Tutorials Basic
    http://pygametutorials.wikidot.com/tutorials-basic
  6. Python.org (dokumentace k jazyku, odkazy na instalační soubory atd.)
    https://www.python.org/
  7. 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
  8. Bresenham's line algorithm
    https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
  9. Xiaolin Wu's line algorithm
    https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm
  10. Pyglet
    https://pypi.python.org/pypi/pyglet/1.2.4
  11. Pyglet documentation
    http://pythonhosted.org/pyglet/
  12. PyOpenGL
    https://pypi.python.org/pypi/PyOpenGL/
  13. Graphical user interface (Wikipedia)
    http://en.wikipedia.org/wiki/Graphical_user_interface
  14. The Real History of the GUI
    http://articles.sitepoint.com/article/real-history-gui
  15. Computer-History: Xerox Alto
    http://www.mr-gadget.de/apple/2004-01-15/computer-history-xerox-alto
  16. bitsavers.org
    http://www.bitsavers.org/
  17. Dokumenty k počítači Xerox Alto na bitsavers.org
    http://www.bitsavers.org/pdf/xerox/alto/
  18. The ALTO Computer
    http://www.maniacworld.com/alto-computer-video.html
  19. Xerox Alto Operating System and Alto Applications
    http://www.digibarn.com/collections/software/alto/index.html
  20. Xerox Alto (Wikipedia)
    http://en.wikipedia.org/wiki/Xerox_Alto
  21. BitBLT routines (1975)
    http://www.bitsavers.org/pdf/xerox/alto/BitBLT_Nov1975.pdf
  22. BitBlt in Squeak
    http://wiki.squeak.org/squeak/189
  23. Bitmaps, Device Contexts and BitBlt
    http://www.winprog.org/tutorial/bitmaps.html
  24. BitBlt Game Programming Tutorial
    http://www.freevbcode.com/ShowCode.asp?ID=3677
  25. Bit blit (Wikipedia)
    http://en.wikipedia.org/wiki/BitBLT
  26. The Xerox Alto
    http://toastytech.com/guis/alto3.html
  27. History of the graphical user interface
    http://en.wikipedia.org/wiki/History_of_the_graphical_user_interface
  28. Domovská stránka systému LÖVE
    http://love2d.org/
  29. Dokumentace k systému LÖVE
    http://love2d.org/wiki/love
  30. Domovská stránka programovacího jazyka Lua
    http://www.lua.org/
  31. Seriál o programovacím jazyku Lua (root.cz):
    http://www.root.cz/serialy/programovaci-jazyk-lua/
  32. Domovská stránka systému LÖVE
    http://love2d.org/
  33. Domovská stránka programovacího jazyka Lua
    http://www.lua.org/
  34. Web o Lieru, Gusanos, GeneRally, Atari atd.
    http://karelik.wz.cz/
  35. Web o Lieru, Gusanos
    http://karelik.wz.cz/gusanos.php
  36. GUSANOS
    http://gusanos.sourceforge.net/
  37. GUSANOS Download
    http://sourceforge.net/projects/gusanos/
  38. Lua
    http://www.linuxexpres.cz/praxe/lua
  39. Lua
    http://cs.wikipedia.org/wiki/Lua
  40. Lua (programming language)
    http://en.wikipedia.org/wiki/Lua_(programming_language)
  41. The Lua Programming Language
    http://www.tiobe.com/index.php/paperinfo/tpci/Lua.html
  42. Lua Programming Gems
    http://www.lua.org/gems/
  43. LuaForge
    http://luaforge.net/
  44. Forge project tree
    http://luaforge.net/softwaremap/trove_list.php
  45. SdlBasic home page
    http://www.sdlbasic.altervista.org/main/
  46. SdlBasic examples
    http://nitrofurano.linuxkafe.com/sdlbasic/
  47. SdlBasic na Wikipedii
    http://en.wikipedia.org/wiki/SdlBasic
  48. Simple DirectMedia Layer
    http://en.wikipedia.org/wiki/Simple_DirectMedia_Layer
  49. SDLBASIC – The high-level interpreter for all?
    http://openbytes.wordpress.com/2008/11/08/sdlbasic-the-high-level-interpreter-for-all/
  50. FreeBasic home page
    http://www.freebasic.net/
  51. FreeBASIC (Wikipedia EN)
    https://en.wikipedia.org/wiki/FreeBASIC
  52. FreeBASIC Wiki
    http://www.freebasic.net/wiki/wikka.php?wakka=FBWiki
  53. FreeBASIC Manual
    http://www.freebasic.net/wiki/wikka.php?wakka=DocToc
  54. FreeBASIC (Wikipedia CZ)
    http://cs.wikipedia.org/wiki/FreeBASIC
  55. The Griffon Legend
    http://syn9.thingie.net/?table=griffonlegend
  56. Seriál Letní škola programovacího jazyka Logo
    http://www.root.cz/serialy/letni-skola-programovaciho-jazyka-logo/
  57. Scratch: oficiální stránka projektu
    http://scratch.mit.edu/
  58. Scratch: galerie projektů vytvořených ve Scratchi
    http://scratch.mit.edu/galleries/browse/newest
  59. Scratch: nápověda
    file:///usr/share/scratch/Help/en/index.html
  60. Scratch: obrazovky nápovědy
    file:///usr/share/scratch/Help/en/allscreens.html
  61. Scratch (Wikipedie CZ)
    http://cs.wikipedia.org/wiki/Scratch
  62. Scratch (programming language)
    http://en.wikipedia.org/wiki/Scratch_(programming_language)
  63. Scratch Modification
    http://wiki.scratch.mit.edu/wiki/Scratch_Modification
  64. Scratch Lowers Resistance to Programming
    http://www.wired.com/gadgetlab/2009/03/scratch-lowers/
  65. Snap!
    http://snap.berkeley.edu/
  66. Prostředí Snap!
    http://snap.berkeley.edu/snapsource/snap.html
  67. Alternatives to Scratch
    http://wiki.scratch.mit.edu/wiki/Alternatives_to_Scratch
  68. Basic-256 home page
    http://www.basic256.org/index_en
  69. Basic-256 Language Documentation
    http://doc.basic256.org/doku.php
  70. Basic-256 Art Gallery
    http://www.basic256.org/artgallery
  71. Basic-256 Tutorial
    http://www.basic256.org/tutorials
  72. Why BASIC?
    http://www.basic256.org/whybasic
  73. A book to teach ANYBODY how to program a computer (using BASIC)
    http://www.basicbook.org/
  74. BASIC Computer Games (published 1978) - Hammurabi
    http://atariarchives.org/basicgames/showpage.php?page=78
  75. Hamurabi - zdrojový kód v BASICu
    http://www.dunnington.u-net.com/public/basicgames/HMRABI