V dnešní části seriálu, 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, 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ů. 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 dnes budeme věnovat.

Obsah

1. Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: práce s bitmapami a TrueType fonty

2. Konverze objektů typu Surface do takzvaného kompatibilního formátu

3. Zachování alfa kanálu při konverzi objektů typu Surface

4. Rastrové operace prováděné v průběhu BitBLT (Blit)

5. Vyžití TrueType fontů v knihovně Pygame

6. Vykreslení textu vybraným fontem do samostatné bitmapy

7. Ukázka vlivu použití antialiasingu na kvalitu vykresleného textu

8. Pozadí textu: využití průhlednosti či vybrané konstantní barvy

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. Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: práce s bitmapami a TrueType fonty

Jednou z nejdůležitějších operací, kterou knihovna Pygame svým uživatelům nabízí, je minule popsaná operace typu BitBLT neboli též blit. Aby bylo možné tuto operaci provádět co nejrychleji, je nutné, aby zdrojové bitmapy používaly stejný formát uložení pixelů, jako bitmapy cílové, což je téma, kterému se budeme věnovat ve druhé a taktéž ve třetí kapitole. V kapitole čtvrté se zmíníme o rastrových operacích (raster ops, rops) aplikovaných při kopírování bitmap a v navazujících kapitolách se již budeme zabývat další důležitou součástí knihovny Pygame – podporou pro TrueType fonty.

10

Obrázek 1: Tento rastrový obrázek bude použit (podobně jako minule) ve všech demonstračních příkladech, v nichž bude použita funkce blit().

2. Konverze objektů typu Surface do takzvaného kompatibilního formátu

Při frekventovaném vykreslování mnoha bitmap do herní scény poměrně rychle zjistíme, že se v herním algoritmu vyskytl jeden dosti závažný problém: vykreslování bude pomalé ve chvíli, kdy formát uložení hodnot (barev) pixelů ve framebufferu (resp. přesněji řečeno v zadním bufferu) je odlišný od formátu uložení pixelů v načtených bitmapách. Ostatně stačí si vzpomenout na jeden z demonstračních příkladů, který jsme si popsali v předchozí části tohoto seriálu. V tomto příkladu se po jeho spuštění zjistily informace o použitém grafickém režimu; mezi tyto informace samozřejmě patří i způsob uložení hodnot (barev) pixelů v bufferu. Informace zjištěné o aktuálně nastaveném grafickém režimu vypadaly takto:

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)

Ve chvíli, kdy načtené bitmapy mají odlišný formát uložení pixelů (což je v praxi více než pravděpodobné), bude muset knihovna Pygame při každém vykreslení takové bitmapy aplikovat konverzní funkci pro každý vykreslovaný pixel, což je samozřejmě časově náročné. Řešení tohoto problému je však velmi jednoduché – bitmapy je možné ihned po jejich načtení jednorázově konvertovat do formátu kompatibilního s bufferem:

# Načtení bitmapy a její okamžitá konverze do formátu kompatibilního s framebufferem
image_surface = (pygame.image.load(os.path.join('images', 'gnome-globe.png')).convert())

A právě způsob konverze bitmapy do formátu kompatibilního s framebufferem je ukázána v dnešním prvním demonstračním příkladu:

#!/usr/bin/python
# vim: set fileencoding=utf-8

# Demonstrační příklady využívající knihovnu Pygame

# Příklad číslo 12: konverze 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 #12')

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

# Načtení bitmapy a její okamžitá konverze do formátu kompatibilního s framebufferem
image_surface = (pygame.image.load(os.path.join('images', 'gnome-globe.png')).convert())

# 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

01

Obrázek 2: Obrazovka prvního demonstračního příkladu. Povšimněte si toho, že při konverzi bitmapy muselo dojít ke ztrátě informací uložených v alfa kanálu, protože celá bitmapa (planeta i její pozadí) je zcela neprůhledná.

3. Zachování alfa kanálu při konverzi objektů typu Surface

Pokud se podíváme na obrázek číslo 2 zobrazený před začátkem této kapitoly, ihned zjistíme, že konverze bitmapy do formátu kompatibilního s framebufferem nedopadla zcela korektně, a to z toho důvodu, že se ztratily informace o alfa kanálu. Ve zdrojové bitmapě totiž bylo pozadí planety průhledné, ovšem při konverzi došlo ke ztrátě této mnohdy velmi důležité informace. Tento problém je snadno řešitelný, pouze je nutné původní programový kód:

# Načtení bitmapy a její okamžitá konverze do formátu kompatibilního s framebufferem
image_surface = (pygame.image.load(os.path.join('images', 'gnome-globe.png')).convert())

nahradit za:

# Načtení bitmapy a její okamžitá konverze do formátu kompatibilního s framebufferem
# Při konverzi se zachová i alfa kanál
image_surface = (pygame.image.load(os.path.join('images', 'gnome-globe.png')).convert_alpha())

V závislosti na tom, jaký formát má zdrojová bitmapa a jaký je formát framebufferu může dojít ke třem typům konverzí:

  • Ve výsledné bitmapě bude plnohodnotný osmibitový alfa kanál (použito u 24bpp a 32bpp).
  • Ve výsledné bitmapě bude jeden bit rezervovaný pro uložení informace o průhlednosti (použito u 15bpp a 16bpp).
  • Ve výsledné bitmapě bude jeden index barvy rezervovaný pro uložení informace o průhlednosti (použito u 8bpp).

V dnešním druhém demonstračním příkladu je problém zachování alfa kanálu vyřešen:

#!/usr/bin/python
# vim: set fileencoding=utf-8

# Demonstrační příklady využívající knihovnu Pygame

# Příklad číslo 13: konverze objektů typu Surface.
#                   Zachování alfa kanálu.


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 #13')

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

# Načtení bitmapy a její okamžitá konverze do formátu kompatibilního s framebufferem
# Při konverzi se zachová i alfa kanál
image_surface = (pygame.image.load(os.path.join('images', 'gnome-globe.png')).convert_alpha())

# 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

02

Obrázek 3: Obrazovka prvního demonstračního příkladu. Zde je již zobrazení korektní, protože se informace uložené v alfa kanálu zdrojové bitmapy při konverzi zachovaly..

4. Rastrové operace prováděné v průběhu BitBLT (Blit)

Operace BitBLT (blit), jejímž popisem jsme se zabývali v předchozí části tohoto seriálu, je většinou používána „pouze“ pro kopii obsahu jedné bitmapy do bitmapy druhé. Ve skutečnosti jsou však možnosti této operace větší, a to z toho důvodu, že při přesunu jednotlivých pixelů je možné provádět takzvané „rastrové operace“, které jsou někdy zkráceně nazývány Raster Op, Raster Ops či dokonce jen ROPS. V minulosti byly tyto operace implementovány logickými funkcemi, ovšem v knihovně Pygame se setkáme spíše s funkcemi využívajícími alfa kanál či barvové složky jednotlivých pixelů. Všechny verze knihovny Pygame podporují tyto operace: BLEND_ADD, BLEND_SUB, BLEND_MULT, BLEND_MIN, BLEND_MAX. Od verze 1.8.1 jsou pak dostupné i složitější operace: BLEND_RGBA_ADD, BLEND_RGBA_SUB, BLEND_RGBA_MULT, BLEND_RGBA_MIN, BLEND_RGBA_MAX BLEND_RGB_ADD, BLEND_RGB_SUB, BLEND_RGB_MULT, BLEND_RGB_MIN a BLEND_RGB_MAX.

14

Obrázek 4: Příklad použití rastrové operace BLEND_ADD. Zajímavé je, že bitmapa je vykreslena po vykreslení všech úseček a přesto úsečky přes bitmapu prosvítají.

V následujícím demonstračním příkladu je do framebufferu vykreslena jedna a tatáž bitmapa, ovšem pokaždé s použitím odlišné rastrové operace. Povšimněte si, že specifikace požadované rastrové operace se provádí přes nepovinný parametr special_flags:

# Vykreslení obrázku, použití různých operací při vykreslování
display.blit(image_surface, ( 25,  25), special_flags = BLEND_ADD)
display.blit(image_surface, (125,  25), special_flags = BLEND_SUB)
display.blit(image_surface, (225,  25), special_flags = BLEND_MULT)
display.blit(image_surface, ( 25, 125), special_flags = BLEND_MIN)
display.blit(image_surface, (125, 125), special_flags = BLEND_MAX)

Následuje úplný zdrojový kód tohoto příkladu:

#!/usr/bin/python
# vim: set fileencoding=utf-8

# Demonstrační příklady využívající knihovnu Pygame

# Příklad číslo 14: konverze objektů typu Surface.
#                   Operace s alfa kanálem.


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 #14')

# 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,12):
    pygame.draw.line(display, WHITE, (10 + i*15, 90), (10 + i*15, 230), i)


# Načtení a konverze obrázku
image_surface = (pygame.image.load(os.path.join('images', 'gnome-globe.png')).convert_alpha())

# Vykreslení obrázku, použití různých operací při vykreslování
display.blit(image_surface, ( 25,  25), special_flags = BLEND_ADD)
display.blit(image_surface, (125,  25), special_flags = BLEND_SUB)
display.blit(image_surface, (225,  25), special_flags = BLEND_MULT)
display.blit(image_surface, ( 25, 125), special_flags = BLEND_MIN)
display.blit(image_surface, (125, 125), special_flags = BLEND_MAX)
display.blit(image_surface, (225, 125))


# 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

13

Obrázek 5: Výsledek běhu předchozího demonstračního příkladu.

5. Vyžití TrueType fontů v knihovně Pygame

Při programování grafických aplikací a samozřejmě i při vytváření počítačových her se v naprosté většině případů setkáme s nutností vykreslit na obrazovku nějaký text, ať již se jedná o pouhý titulek hry, zobrazení dosaženého skóre či o součást grafického uživatelského rozhraní aplikace (hlavní menu, nápověda atd.). V knihovně Pygame samozřejmě existuje podpora pro vykreslování znaků i delších textů, přičemž veškerá funkcionalita je ve skutečnosti zajištěna nativní knihovnou nazvanou SDL_ttf verze 1.2 či 2.0, která navíc při své činnosti využívá další nativní knihovnu pojmenovanou (lib)FreeType (http://www.freetype.org/) určenou pro práci s TrueType fonty. V praxi to znamená, že i když se pro vykreslování používá jiný driver než SDL, tak při práci s fonty by měla být knihovna SDL_ttf přítomná (alternativně lze použít knihovnu nazvanou pygame.freetype, která je vybrána automaticky ve chvíli, kdy SDL_ttf není v systému nalezena.

Jak jsme si již řekli v předchozím odstavci, dokáže knihovna Pygame pracovat s fonty typu TTF (True Type Font), v nichž jsou tvary jednotlivých znaků uloženy ve formě obrysů složených ze sekvence na sebe navazujících úseček a kvadratických Bézierových křivek. Ve skutečnosti se však v naprosté většině případů programátoři nemusí zabývat přesným algoritmem vykreslování jednotlivých znaků, což je ostatně dosti komplikované, především při menších velikostech znaků a použití takzvaného hintingu. Díky modulům nabízených knihovnou Pygame je možné jednoduše načíst vybraný soubor typu TTF s uvedením velikosti výsledného písma. Knihovna Pygame se s využitím nativních částí SDL_ttf a FreeType sama postará o takzvanou rasterizaci jednotlivých znaků, tj. o vykreslení a vyplnění již zmíněných úseček a Bézierových křivek představujících obrysy znaků obsažených v textu, který se má zobrazit. Základem při práci s písmy je třída pygame.font.Font. Inicializace písma se provede následujícím způsobem:

# Načtení fontu (zadává se soubor se jménem fontu a požadovaná velikost písma
font = pygame.font.Font("fonts/FreeSans.ttf", 40)

Následně je možné využít instanci třídy pygame.font.Font pro vykreslení řetězce do samostatné automaticky vytvořené bitmapy (Surface), se kterou je možné pracovat jako s každou jinou bitmapou. Velikost bitmapy je vypočtena na základě tvaru znaků v použitém fontu, zadané velikosti textu a samozřejmě i na základě toho, jaký text se má vykreslit. Samotné vykreslení, tj. již výše zmíněná „rasterizace“ je provedena metodou pygame.font.Font.render(), vykreslení pak zajišťuje nám již dobře známá metoda pygame.Surface.blit():

# Vytvoření obrázku s vykresleným textem
# - první parametr obsahuje řetězec, který se má vykreslit
# - druhý parametr řídí použití antialiasingu
# - třetí parametr volí barvu fontu
font_surface = font.render("mojefedora.cz", False, WHITE)

# Vykreslení obrázku s nápisem do bufferu
display.blit(font_surface, ( 25,  125))

Pro běh dále popsaných demonstračních příkladů je taktéž nutné mít v pracovním adresáři vytvořený podadresář nazvaný „fonts“, v němž je umístěn soubor „FreeSans.ttf“ s fontem. Tento soubor je dostupný na adrese http://www.fontspace.com/gnu-freefont/freesans. Ze staženého archivu ve skutečnosti postačuje rozbalit pouze jediný uvedený soubor „freesans.ttf“ či „FreeSans.ttf“ – pozor na správné použití velkých a malých písmen v demonstračních příkladech!).

Důležité upozornění: pokud se má do herní scény vykreslit jiný text, je nutné postup pro vytvoření nové bitmapy a jejího následného přenosu do zadního bufferu s využitím metody pygame.Surface.blit() opakovat. Samotná rasterizace je relativně složitá a tím pádem i pomalá operace, proto se vyplatí si bitmapy s texty připravit již ve fázi inicializace hry.

6. Vykreslení textu vybraným fontem do samostatné bitmapy

Výše zmíněný postup pro načtení fontu, rasterizaci textu do bitmapy a přenos bitmapy do zadního bufferu s využitím funkce blit() je implementován v dalším demonstračním příkladu, jehož úplný zdrojový kód naleznete na adrese https://github.com/tisnik/presentations/blob/master/pygame/pygame15.py. V tomto příkladu se po inicializaci grafického režimu či po otevření okna nejprve vykreslí řada úseček (což již známe z předchozích příkladů) a následně se do vytvořené scény vypíše i text fontem vysokým 40 pixelů. Při rasterizaci textu se používá bílá barva. Na řádku:

font_surface = font.render("mojefedora.cz", False, WHITE)

je možné druhým parametrem povolit či zakázat použití antialiasingu, což je problematika, které se budeme podrobněji věnovat v navazující kapitole. Následuje výpis zdrojového kódu demonstračního příkladu:

#!/usr/bin/python
# vim: set fileencoding=utf-8

# Demonstrační příklady využívající knihovnu Pygame

# Příklad číslo 15: použití TrueType fontů


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 #15')

# 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)
GRAY    = (128, 128, 128)
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,12):
    pygame.draw.line(display, GRAY, (10 + i*15, 90), (10 + i*15, 230), i)


# Načtení fontu (zadává se soubor se jménem fontu a velikost
font = pygame.font.Font("fonts/FreeSans.ttf", 40)

# Vytvoření obrázku s vykresleným textem
# - první parametr obsahuje řetězec, který se má vykreslit
# - druhý parametr řídí použití antialiasingu
# - třetí parametr volí barvu fontu
font_surface = font.render("mojefedora.cz", False, WHITE)

# Vykreslení obrázku s nápisem do bufferu
display.blit(font_surface, ( 25,  125))



# 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 6: Výpis textu bez použití antialiasingu.

04

Obrázek 7: Výpis textu s využitím antialiasingu.

7. Ukázka vlivu použití antialiasingu na kvalitu vykresleného textu

Při vykreslování textu s využitím výše popsaných metod se většinou vytvoří bitmapa s bitovou hloubkou 8bpp. Samotné vykreslování takového bitmapy s použitím nám již známé metody pygame.Surface.blit() je velmi rychlé a i samotný způsob uložení bitmapy v paměti je poměrně efektivní, ovšem na druhou stranu zde neexistuje podpora pro skutečný antialiasing textů, takže šikmé tahy znaků budou vykazovat typické „schody“. V případě, že je antialiasing nutné použít (což nemusí být případ hry, kde se většinou upřednostňuje rychlejší vykreslování před kvalitou!), je možné v metodě pygame.font.Font.render() pomocí hodnoty druhého parametru specifikovat, aby se při rasterizaci vytvořila bitmapa s bitovou hloubkou 32bpp a tím pádem i s plnohodnotným osmibitovým alfa kanálem. A právě alfa kanál je využit při antialiasingu, zejména při rasterizaci okrajů písma (zde budou použity pixely s variabilní průhledností).

Podívejme se na rozdíl mezi vykreslením textu bez použití antialiasingu a s použitím antialiasingu:

05

Obrázek 8: Detailní pohled na scénu vykreslenou v předchozím demonstračním příkladu zavoláním metody font_surface = font.render(„mojefedora.cz“, False, WHITE) (zákaz antialiasingu).

06

Obrázek 9: Detailní pohled na scénu vykreslenou v předchozím demonstračním příkladu zavoláním metody font_surface = font.render(„mojefedora.cz“, True, WHITE) (povolení antialiasingu).

Nevýhody použití antialiasingu při práci s texty jsou tři:

  1. Pomalejší vykreslování výsledné bitmapy, neboť je nutné provádět výpočet průhlednosti s každým pixelem (násobení!)
  2. Větší paměťové nároky (projeví se při meziukládání bitmap a větším množství textů)
  3. Problematická práce s těmito bitmapami ve chvíli, kdy není nastaven celoobrazovkový grafický režim s hloubkou 24bpp či 32bpp – a tyto režimy se v mnoha hrách nepoužívají, právě z důvodu větších nároků při práci s grafikou (přesun větších bitmap při operaci blit() atd.)

Je tedy nutné pečlivě zvážit, kterou metodou se budou texty do (herních) scén vykreslovat a mnohdy převáží sice méně kvalitní, ale zato rychlejší rendering textů bez antialiasingu.

8. Pozadí textu: využití průhlednosti či vybrané konstantní barvy

Bitmapa (resp. přesněji řečeno objekt typu Surface), která je automaticky vytvořena při vykreslení fontu, může obsahovat buď průhlednou barvu pozadí (kde je průhlednost nastavena na 100%) nebo naopak libovolnou zvolenou barvu. To, jaká možnost se při vykreslování využije, závisí na tom, zda se při volání metody pygame.font.Font.render() použije čtvrtý nepovinný parametr obsahující zvolenou barvu pozadí. V prvním případě (= čtvrtý nepovinný parametr není použit) je vytvořena bitmapa s bitovou hloubkou 8bpp obsahující v místech, kde se nenachází žádný znak, zcela průhledné pixely. Pokud je zapnut antialiasing, je použita bitmapa s hloubkou 32bpp a průhlednost je tedy reprezentována plnohodnotným osmibitovým alfa kanálem, barva pozadí tedy nemusí být na hranách písma přesně 100%, ale i méně. Při použití druhé metody jsou průhledné či poloprůhledné pixely vyplněny zadanou barvou.

V dnešním posledním demonstračním příkladu je tato možnost ukázána. Na screenshotu zobrazeném pod výpisem zdrojového kódu si povšimněte vlivu použití antialiasingu na kvalitu zobrazeného textu i vliv rastrové operace BLEND_ADD:

#!/usr/bin/python
# vim: set fileencoding=utf-8

# Demonstrační příklady využívající knihovnu Pygame

# Příklad číslo 16: použití TrueType fontů


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 #16')

# 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)
GRAY    = (128, 128, 128)
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,12):
    pygame.draw.line(display, GRAY, (10 + i*15, 90), (10 + i*15, 230), i)


# Načtení fontu (zadává se soubor se jménem fontu a velikost
font = pygame.font.Font("fonts/FreeSans.ttf", 40)

# Vytvoření obrázku s vykresleným textem
# - první parametr obsahuje řetězec, který se má vykreslit
# - druhý parametr řídí použití antialiasingu
# - třetí parametr volí barvu fontu
font_surface1 = font.render("mojefedora.cz", True, WHITE, RED)

# Vykreslení obrázku s nápisem do bufferu
display.blit(font_surface1, ( 25,  90), special_flags = BLEND_ADD)
display.blit(font_surface1, ( 25,  150))



# 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

07

Obrázek 10: První text byl do své bitmapy vykreslen bez použití antialiasingu, při operaci blit() se ovšem použila rastrová operace BLEND_ADD. Druhý text byl do své bitmapy vykreslen s použitím antialiasingu a při přenosu bitmapy se použila běžná rastrová operace COPY.

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

Prakticky žádná hra odehrávající se v 2D prostoru se neobejde bez množství objektů, které se pohybují v herním světě. Tyto objekty spolu mohou kolidovat (narážet do sebe atd.), popř. mohou nastat kolize mezi pohyblivým objektem a nepohyblivým pozadím herní scény. Aby nebylo nutné výpočet kolizí pro každou vznikající hru znovu programovat, nabízí knihovna Pygame svým uživatelům řešení ve formě modulu pygame.sprite. A právě popisem některých možností nabízených tímto modulem se budeme zabývat v navazující části tohoto seriálu.

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, podobně jako v předchozích částech tohoto seriálu, uloženy do Git repositáře umístěného na GitHubu (https://github.com/tisnik/presentations):

# Příklad Zdrojový kód
1 pygame12.py https://github.com/tisnik/presentations/blob/master/pygame/pygame12.py
2 pygame13.py https://github.com/tisnik/presentations/blob/master/pygame/pygame13.py
3 pygame14.py https://github.com/tisnik/presentations/blob/master/pygame/pygame14.py
4 pygame15.py https://github.com/tisnik/presentations/blob/master/pygame/pygame15.py
5 pygame16.py https://github.com/tisnik/presentations/blob/master/pygame/pygame16.py
6 images/gnome-globe.png https://github.com/tisnik/presentations/tree/master/pygame/images
7 fonts/FreeSans.ttf https://github.com/tisnik/presentations/blob/master/pygame/fonts/FreeSans.ttf

Poznámka: 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. Podobné pravidlo je nutné dodržet i pro soubor nazvaný „FreeSans.ttf“, který je očekáván v podadresáři „fonts“.

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.quit()
    http://www.pygame.org/docs/ref/display.html#pygame.display.quit
  6. pygame.display.update()
    http://www.pygame.org/docs/ref/display.html#pygame.display.update
  7. pygame.Surface.blit()
    http://www.pygame.org/docs/ref/surface.html#pygame.Surface.blit
  8. pygame.Surface.convert()
    http://www.pygame.org/docs/ref/surface.html#pygame.Surface.convert
  9. pygame.Surface.convert_alpha()
    http://www.pygame.org/docs/ref/surface.html#pygame.Surface.convert_alpha
  10. pygame.event.get()
    http://www.pygame.org/docs/ref/event.html#pygame.event.get
  11. pygame.time.Clock.tick()
    http://www.pygame.org/docs/ref/time.html#pygame.time.Clock.tick
  12. pygame.draw.line()
    http://www.pygame.org/docs/ref/draw.html#pygame.draw.line
  13. pygame.draw.aaline()
    http://www.pygame.org/docs/ref/draw.html#pygame.draw.aaline
  14. pygame.image.load()
    http://www.pygame.org/docs/ref/image.html#pygame.image.load
  15. pygame.font.Font()
    http://www.pygame.org/docs/ref/font.html#pygame.font.Font
  16. pygame.font.Font.render()
    http://www.pygame.org/docs/ref/font.html#pygame.font.Font.render

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