Obsah
1. Práce s cestami v knihovně OpenVG
2. Základní funkce určené pro práci s cestami
9. Druhý demonstrační příklad (sedmý v celkovém pořadí): vykreslení uzavřené cesty složené z úseček
11. Čtvrtý demonstrační příklad (devátý v celkovém pořadí): vykreslení Bézierovy křivky
12. Vysokoúrovňové funkce vguLine, vguRect, vguRoundRect a vguPolygon
13. Repositář s demonstračními příklady
1. Práce s cestami v knihovně OpenVG
V předchozí části článku o grafické knihovně OpenVG jsme si vysvětlili koncept takzvaných cest (paths), protože právě s využitím cest jsou v této knihovně vykreslovány prakticky všechny složitější dvourozměrné objekty, ať již se jedná o objekty otevřené (úsečky, lomené čáry – polyčáry, oblouky, křivky) či objekty uzavřené (kruhy, vyplněné mnohoúhelníky apod.). Připomeňme si, že každá cesta se skládá z prakticky libovolného množství segmentů (segments), přičemž je zajímavé a pro některé účely i velmi důležité, že jednotlivé segmenty na sebe nutně nemusí graficky navazovat, ale mohou být mezi nimi mezery. Minule jsme si taktéž řekli, že použití konceptu cest není v oblasti 2D grafiky samozřejmě nic nového, protože jsme se s nimi mohli setkat například v PostScriptu či v grafickém formátu SVG (PostScript lze přitom podle jeho konkrétního způsobu použití chápat jako souborový formát, programovací jazyk a současně i renderovací/vykreslovací knihovnu).
2. Základní funkce určené pro práci s cestami
Pro práci s cestami je v grafické knihovně OpenVG připraveno několik funkcí, ovšem pro začátek si vystačíme s pouhými pěti funkcemi, jejichž jména a stručný popis je vypsán v následující tabulce:
# | Funkce | Stručný popis funkce |
---|---|---|
1 | vgCreatePath() | vytvoření datové struktury obsahující (prozatím) prázdnou cestu |
2 | vgDestroyPath() | odstranění cesty z aktuálního kontextu a uvolnění operační paměti |
3 | vgAppendPathData() | přidání dalších segmentů do existující cesty (původně je cesta bez segmentů) |
4 | vgClearPath() | odstranění všech segmentů z existující cesty (lze použít i pro právě vytvořenou cestu bez segmentů) |
5 | vgDrawPath() | vykreslení cesty s využitím aktuálního kontextu a tedy i nastaveného stylu vykreslování |
Můžeme se však setkat i s dalšími funkcemi, které souvisí s cestami, například:
# | Funkce | Stručný popis funkce |
---|---|---|
1 | vgGetPathCapabilities() | získání aktuálního nastavení parametrů cesty |
2 | vgRemovePathCapabilities() | odstranění některého nastavení cesty (zadává se bitovým polem) |
3 | vgAppendPath() | spojení dvou cest do cesty jediné |
4 | vgTransformPath() | aplikace lineární transformace na vybranou cestu (posun, změna měřítka, …) |
5 | vgPathLength() | výpočet délky cesty |
6 | vgPathBounds() | výpočet obalového obdélníku pro zadanou cestu |
Další zajímavou (ale ne vždy intuitivně použitelnou) funkcí je vgInterpolatePath, kterou si popíšeme příště:
VGboolean vgInterpolatePath( VGPath dstPath, VGPath startPath, VGPath endPath, VGfloat amount)
3. Funkce vgCreatePath
Z předchozí kapitoly již víme, že se prázdná cesta vytváří s využitím funkce vgCreatePath(). Tato funkce má následující hlavičku:
VGPath vgCreatePath( VGint pathFormat, VGPathDatatype datatype, VGfloat scale, VGfloat bias, VGint segmentCapacityHint, VGint coordCapacityHint, VGbitfield capabilities);
Funkce vgCreatePath sice vyžaduje předání sedmi parametrů, ale ve skutečnosti se do mnoha parametrů většinou předává stále stejná hodnota. To je případ prvního parametru, který bývá nastaven na konstantu se jménem VG_PATH_FORMAT_STANDARD. Ve druhém parametru se specifikuje formát hodnot použitých pro uložení vrcholů jednotlivých segmentů (určuje se tím způsob zpracování pole s vrcholy segmentů):
datatype | Význam |
---|---|
VG_PATH_DATATYPE_S8 | osmibitové hodnoty se znaménkem |
VG_PATH_DATATYPE_S16 | 16bitové hodnoty se znaménkem |
VG_PATH_DATATYPE_S32 | 32bitové hodnoty se znaménkem |
VG_PATH_DATATYPE_F | formát s plovoucí řádovou čárkou podle IEEE 754 (float) |
Následuje dvojice parametrů scale a bias, které slouží pro jednoduchou transformaci všech souřadnic podle vzorce scale*x+bias. Typicky se předává scale=1.0 a bias=0.0, pokud je ovšem vstupní formát souřadnic nějak specifický, můžete hodnoty těchto parametrů pochopitelně změnit, a to právě na tomto jediném místě (nemusí se zdlouhavě přepočítávat jednotlivé souřadnice). Parametry segmentCapacityHint a coordCapacityHint slouží pouze pro optimalizaci alokátoru paměti – předává se zde předpokládaná velikost cesty (počet segmentů, celkový počet souřadnic). Pokud tyto hodnoty dopředu neznáte, nebo je nechcete počítat, předává se 0 značící unknown.
Poslední formát určuje možnosti a povolené operace, které budeme chtít s cestou provádět. Jedná se o celočíselnou konstantu získanou operací OR či + nad následujícími konstantami:
VG_PATH_CAPABILITY_APPEND_FROM VG_PATH_CAPABILITY_APPEND_TO VG_PATH_CAPABILITY_MODIFY VG_PATH_CAPABILITY_TRANSFORM_FROM VG_PATH_CAPABILITY_TRANSFORM_TO VG_PATH_CAPABILITY_INTERPOLATE_FROM VG_PATH_CAPABILITY_INTERPOLATE_TO VG_PATH_CAPABILITY_PATH_LENGTH VG_PATH_CAPABILITY_POINT_ALONG_PATH VG_PATH_CAPABILITY_TANGENT_ALONG_PATH VG_PATH_CAPABILITY_PATH_BOUNDS VG_PATH_CAPABILITY_PATH_TRANSFORMED_BOUNDS VG_PATH_CAPABILITY_ALL
Důležitá je především konstanta nazvaná VG_PATH_CAPABILITY_APPEND_TO, protože právě ta povoluje použití funkce vgAppendPathData() popsané v dalších kapitolách.
4. Funkce vgDestroyPath
Druhou důležitou funkcí při práci s cestami je funkce nazvaná vgDestroyPath(). Tato funkce, která je vlastně opakem výše popsané funkce vgCreatePath(), cestu z aktuálního kontextu odstraní a uvolní paměť (buď na grafickém akcelerátoru nebo „obyčejnou“ operační paměť). V aplikacích je vhodné tuto funkci zavolat co nejdříve a nenechávat v paměti uloženo zbytečně velké množství cest (paměť pro cesty totiž může být alokována v paměťovém prostoru grafického akcelerátoru, záleží ovšem na konkrétní implementaci):
void vgDestroyPath( VGPath path);
Povšimněte si návratového typu, který naznačuje, že tato funkce nevrací žádnou hodnotu, ovšem případná chyba může být nahlášena: VG_BAD_HANDLE_ERROR.
5. Funkce vgClearPath
Segmenty se z vybrané cesty dají kdykoli odstranit s využitím funkce vgClearPath(), jejíž hlavička je velmi jednoduchá:
void vgClearPath( VGPath path, VGbitfield capabilities);
Tuto funkci je možné zavolat i v případě, že se jedná o novou cestu bez segmentů.
U hlavičky této funkce si povšimněte toho, že se kromě odstranění všech segmentů, z nichž se cesta skládá, mohou nastavit nové vlastnosti cesty, a to díky druhému parametru pojmenovanému capabilities.
Tato funkce má význam ve chvíli, kdy je zapotřebí vykreslit větší množství cest. V takovém okamžiku je neustálé používání dvojice funkcí vgCreatePath() a vgDestroyPath() neefektivní; lepší je jednu cestu vykreslit, smazat její data pomocí vgClearPath() a nadefinovat cestu novou.
6. Funkce vgDrawPath
Čtvrtá funkce důležitá při práci s cestami se jmenuje vgDrawPath a její hlavička vypadá následovně:
void vgDrawPath( VGPath path, VGbitfield paintModes)
Prvním parametrem této funkce je reference na již vytvořenou a inicializovanou cestu (ideálně obsahující nějaké segmenty, které se mají vykreslit, ve skutečnosti to však není nutné – vykreslit lze i prázdnou cestu, ovšem s pochopitelným výsledkem). Zajímavý je druhý parametr, kterým se určuje styl vykreslení cesty:
paintModes | Operace |
---|---|
VG_STROKE_PATH | vykreslí se obrys cesty |
VG_FILL_PATH | vyplní se vnitřek cesty |
VG_STROKE_PATH | VG_FILL_PATH | kombinace vykreslení a současného vyplnění |
Poznámka: styl vykreslení a styl výplně se může odlišovat, což znamená, že obrys může mít jinou barvu, než samotná výplň.
7. Funkce vgAppendPathData
Poslední důležitou funkcí, s níž se dnes musíme seznámit, je funkce nazvaná vgAppendPathData. Tato funkce slouží k připojení dalších segmentů ke specifikované cestě. Funkce vgAppendPathData má následující hlavičku:
void vgAppendPathData( VGPath dstPath, VGint numSeg, const VGubyte *pathSeg, const void *pathData)
Význam prvního argumentu je zřejmý – jde o referenci na vytvořenou cestu, k níž se mají připojit další segmenty. Ve druhém parametru je předán počet připojovaných segmentů, protože ze samotných dat není možné tuto informaci zjistit. Zajímavý je třetí argument. Jedná se o pole obsahující jednotlivé příkazy pro tvorbu segmentů (jejich význam jsme si vysvětlili minule):
# | Symbolická konstanta | Hodnota | Význam |
---|---|---|---|
1 | VG_CLOSE_PATH | 0 | uzavření cesty |
2 | VG_MOVE_TO | 2 | přesun aktivního bodu bez kreslení |
3 | VG_LINE_TO | 4 | lineární segment (úsečka) |
4 | VG_HLINE_TO | 6 | horizontální úsečka |
5 | VG_VLINE_TO | 8 | vertikální úsečka |
6 | VG_QUAD_TO | 10 | kvadratická Bézierova křivka |
7 | VG_CUBIC_TO | 12 | kubická Bézierova křivka |
8 | VG_SQUAD_TO | 14 | segment s G1 spojitostí tvořený kvadratickou křivkou |
9 | VG_SCUBIC_TO | 16 | segment s G1 spojitostí tvořený kubickou křivkou |
10 | VG_SCCWARC_TO | 18 | kratší segment tvořený eliptickým obloukem |
11 | VG_SCWARC_TO | 20 | kratší segment tvořený eliptickým obloukem |
12 | VG_LCCWARC_TO | 22 | delší segment tvořený eliptickým obloukem |
13 | VG_LCWARC_TO | 24 | delší segment tvořený eliptickým obloukem |
Každý příkaz může používat absolutní souřadnice (vztažené vůči počátku souřadného systému) či souřadnice relativní (vztažené vůči poslední zadané či vypočtené souřadnici). Rozhodnutí o použití relativních či absolutních souřadnicích se provádí na základě nejnižšího bitu příkazu, který může nabývat hodnot:
# | Symbolická konstanta | Hodnota | Význam |
---|---|---|---|
1 | VG_ABSOLUTE | 0 | příznak použití absolutních souřadnic |
2 | VG_RELATIVE | 1 | příznak použití relativních souřadnic |
Výsledkem jsou následující kombinace:
# | Symbolická konstanta | Vypočteno z |
---|---|---|
1 | VG_MOVE_TO_ABS | VG_MOVE_TO | VG_ABSOLUTE |
2 | VG_MOVE_TO_REL | VG_MOVE_TO | VG_RELATIVE |
3 | VG_LINE_TO_ABS | VG_LINE_TO | VG_ABSOLUTE |
4 | VG_LINE_TO_REL | VG_LINE_TO | VG_RELATIVE |
5 | VG_HLINE_TO_ABS | VG_HLINE_TO | VG_ABSOLUTE |
6 | VG_HLINE_TO_REL | VG_HLINE_TO | VG_RELATIVE |
7 | VG_VLINE_TO_ABS | VG_VLINE_TO | VG_ABSOLUTE |
8 | VG_VLINE_TO_REL | VG_VLINE_TO | VG_RELATIVE |
9 | VG_QUAD_TO_ABS | VG_QUAD_TO | VG_ABSOLUTE |
10 | VG_QUAD_TO_REL | VG_QUAD_TO | VG_RELATIVE |
11 | VG_CUBIC_TO_ABS | VG_CUBIC_TO | VG_ABSOLUTE |
12 | VG_CUBIC_TO_REL | VG_CUBIC_TO | VG_RELATIVE |
13 | VG_SQUAD_TO_ABS | VG_SQUAD_TO | VG_ABSOLUTE |
14 | VG_SQUAD_TO_REL | VG_SQUAD_TO | VG_RELATIVE |
15 | VG_SCUBIC_TO_ABS | VG_SCUBIC_TO | VG_ABSOLUTE |
16 | VG_SCUBIC_TO_REL | VG_SCUBIC_TO | VG_RELATIVE |
17 | VG_SCCWARC_TO_ABS | VG_SCCWARC_TO | VG_ABSOLUTE |
18 | VG_SCCWARC_TO_REL | VG_SCCWARC_TO | VG_RELATIVE |
19 | VG_SCWARC_TO_ABS | VG_SCWARC_TO | VG_ABSOLUTE |
20 | VG_SCWARC_TO_REL | VG_SCWARC_TO | VG_RELATIVE |
21 | VG_LCCWARC_TO_ABS | VG_LCCWARC_TO | VG_ABSOLUTE |
22 | VG_LCCWARC_TO_REL | VG_LCCWARC_TO | VG_RELATIVE |
23 | VG_LCWARC_TO_ABS | VG_LCWARC_TO | VG_ABSOLUTE |
24 | VG_LCWARC_TO_REL | VG_LCWARC_TO | VG_RELATIVE |
Poslední parametr obsahuje pole souřadnic pro jednotlivé segmenty. Formát souřadnic (celé číslo, float…) byl určen již při vytváření cesty funkcí vgCreatePath(). Souřadnice jsou v poli uloženy za sebou: x1, y1, x2, y2, …
8. První demonstrační příklad (šestý v celkovém pořadí): vykreslení cesty obsahující jediný segment – úsečku
Všech pět funkcí důležitých pro tvorbu cest jsme si již popsali, takže nám již zbývá si ukázat jejich použití v demonstračních příkladech. Dnešní první demonstrační příklad (který je celkově již osmým příkladem na OpenVG) je sice poměrně dlouhý, ovšem jeho základem je vykreslení jednoduché úsečky. Po inicializaci EGL apod. (viz též předchozí části tohoto seriálu) se zavolá funkce draw, která:
- Vymaže obrazovku černou barvou
- Nastaví barvu štětce pro vykreslovanou úsečku
- Nastaví šířku štětce i styl zakončení (zde konkrétně kulaté konce)
- Vytvoří cestu
- Přidá do cesty segment dvěma příkazy: VG_MOVE_TO_ABS, VG_LINE_TO_REL
- Cestu zruší
- Prohodí přední a zadní buffer
Podívejme se na kód této funkce:
/* * Vykresleni usecky sestrojene pomoci "cesty" (path). */ void draw(EGL_STATE_T *state) { /* vymazani pozadi cernou barvou */ VGfloat color1[4] = {0.0f, 0.0f, 0.0f, 1.0f}; vgSetfv(VG_CLEAR_COLOR, 4, color1); vgClear(0, 0, state->window_width, state->window_height); /* barva stetce vykreslovane usecky */ VGfloat color2[4] = {0.75f, 0.25f, 0.25f, 1.0f}; VGPaint strokePaint = vgCreatePaint(); vgSetParameteri(strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv(strokePaint, VG_PAINT_COLOR, 4, color2); vgSetPaint(strokePaint, VG_STROKE_PATH); vgDestroyPaint(strokePaint); /* sirka a styl stetce pouziteho pro kresbu usecky */ vgSetf(VG_STROKE_LINE_WIDTH, 10); vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_ROUND); vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND); /* vykresleni prvni usecky */ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO); /* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, VG_LINE_TO_REL}; VGfloat coordinates[] = {state->window_width >> 1, state->window_height >> 1, 100, 50}; /* pridani useckoveho segmentu */ vgAppendPathData(path, 2, commands, coordinates); /* usecka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */ vgDrawPath(path, VG_STROKE_PATH); vgDestroyPath(path); /* prohozeni predniho a zadniho bufferu (pokud je to zapotrebi) */ eglSwapBuffers(state->display, state->surface); }
Úplný zdrojový kód demonstračního příkladu je vypsán pod tímto odstavcem:
/* OpenVG (nejenom) na Raspberry Pi - sesty demonstracni priklad */ /* Vykresleni usecky s vyuzitim jednoho "segmentu" cesty. */ #include <stdio.h> #include <VG/openvg.h> #include <VG/vgu.h> #include <EGL/egl.h> #include <bcm_host.h> /* * Datova struktura obsahujici cely stav EGL "sezeni". */ typedef struct { uint32_t screen_width; uint32_t screen_height; uint32_t window_x; uint32_t window_y; int32_t window_width; int32_t window_height; EGLDisplay display; EGLSurface surface; EGLContext context; EGLConfig config; } EGL_STATE_T; /* * Inicializace EGL. */ void initialize_egl(EGL_STATE_T *state) { EGLBoolean result; EGLint num_config; EGLConfig config; /* nutne pro RPi */ bcm_host_init(); /* pro jistotu vymazeme datovou strukturu nesouci stav EGL */ memset(state, 0, sizeof(*state)); static EGL_DISPMANX_WINDOW_T nativewindow; DISPMANX_ELEMENT_HANDLE_T dispman_element; DISPMANX_DISPLAY_HANDLE_T dispman_display; DISPMANX_UPDATE_HANDLE_T dispman_update; VC_RECT_T dst_rect; VC_RECT_T src_rect; static VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0 }; static const EGLint attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; /* vychozi displej */ state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); /* inicializace displeje */ result = eglInitialize(state->display, NULL, NULL); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("EGL init failed"); exit(1); } /* navazani EGL na OpenVG */ eglBindAPI(EGL_OPENVG_API); /* ziskani konfigurace framebufferu */ result = eglChooseConfig(state->display, attribute_list, &config, 1, &num_config); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("EGL choose config failed"); exit(1); } /* vytvoreni kontextu */ state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, NULL); /* kontrola, zda operace probehla v poradku */ if (state->context == EGL_NO_CONTEXT) { puts("EGL create context failed"); exit(1); } /* vytvoreni surface */ int32_t success = graphics_get_display_size(0, &state->screen_width, &state->screen_height); /* kontrola, zda operace probehla v poradku */ if (success < 0) { puts("get display size failed"); exit(1); } if ((state->window_width == 0) || (state->window_width > state->screen_width)) state->window_width = state->screen_width; if ((state->window_height == 0) || (state->window_height > state->screen_height)) state->window_height = state->screen_height; dispman_display = vc_dispmanx_display_open(0); dispman_update = vc_dispmanx_update_start(0); dispman_element = vc_dispmanx_element_add(dispman_update, dispman_display, 0 /*layer */ , &dst_rect, 0 /*src */ , &src_rect, DISPMANX_PROTECTION_NONE, &alpha, 0 /*clamp */ , 0 /*transform */ ); nativewindow.element = dispman_element; nativewindow.width = state->window_width; nativewindow.height = state->window_height; vc_dispmanx_update_submit_sync(dispman_update); /* vytvoreni surface */ state->surface = eglCreateWindowSurface(state->display, config, &nativewindow, NULL); /* kontrola, zda operace probehla v poradku */ if (state->surface == EGL_NO_SURFACE) { puts("no surface!"); exit(1); } /* nastaveni chovani bufferu pri operaci swap */ result = eglSurfaceAttrib(state->display, state->surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("can not set surface attributes!"); exit(1); } /* propojeni kontextu se surface */ result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("can not connect context with the surface!"); exit(1); } } /* * Ukonceni prace s EGL. */ void finalize_egl(EGL_STATE_T *state) { eglSwapBuffers(state->display, state->surface); eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroySurface(state->display, state->surface); eglDestroyContext(state->display, state->context); eglTerminate(state->display); } /* * Vykresleni usecky sestrojene pomoci "cesty" (path). */ void draw(EGL_STATE_T *state) { /* vymazani pozadi cernou barvou */ VGfloat color1[4] = {0.0f, 0.0f, 0.0f, 1.0f}; vgSetfv(VG_CLEAR_COLOR, 4, color1); vgClear(0, 0, state->window_width, state->window_height); /* barva stetce vykreslovane usecky */ VGfloat color2[4] = {0.75f, 0.25f, 0.25f, 1.0f}; VGPaint strokePaint = vgCreatePaint(); vgSetParameteri(strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv(strokePaint, VG_PAINT_COLOR, 4, color2); vgSetPaint(strokePaint, VG_STROKE_PATH); vgDestroyPaint(strokePaint); /* sirka a styl stetce pouziteho pro kresbu usecky */ vgSetf(VG_STROKE_LINE_WIDTH, 10); vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_ROUND); vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND); /* vykresleni prvni usecky */ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO); /* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, VG_LINE_TO_REL}; VGfloat coordinates[] = {state->window_width >> 1, state->window_height >> 1, 100, 50}; /* pridani useckoveho segmentu */ vgAppendPathData(path, 2, commands, coordinates); /* usecka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */ vgDrawPath(path, VG_STROKE_PATH); vgDestroyPath(path); /* prohozeni predniho a zadniho bufferu (pokud je to zapotrebi) */ eglSwapBuffers(state->display, state->surface); } /* * Vstupni bod do programu. */ int main(int argc, char *argv[]) { EGL_STATE_T egl_state; initialize_egl(&egl_state); puts("initialize_egl OK"); draw(&egl_state); getchar(); finalize_egl(&egl_state); puts("finalize_egl OK"); return 0; }
Soubor Makefile určený pro překlad tohoto příkladu:
# Makefile pro preklad sesteho prikladu ukazujiciho # praci s OpenVG a EGL. # Parametry prekladace. CFLAGS=-Wall # Dalsi parametry prekladace, zde adresare, kde se maji # hledat hlavickove soubory. INCLUDES=-I/opt/vc/include/ -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux # Parametry linkeru. LDFLAGS=-L/opt/vc/lib/ -lGLESv2 -lEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm PROGNAME=example6 # Vychozi pravidlo pro vytvoreni vysledne spustitelne aplikace. all: $(PROGNAME) clean: rm -f *.o rm -f $(PROGNAME) # Pravidlo pro slinkovani vsech objektovych souboru a vytvoreni # vysledne spustitelne aplikace. $(PROGNAME): $(PROGNAME).o $(CC) -o $@ $(LDFLAGS) $< # Pravidlo pro preklad kazdeho zdrojoveho souboru do prislusneho # objektoveho souboru. %.o: %.c $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
9. Druhý demonstrační příklad (sedmý v celkovém pořadí): vykreslení uzavřené cesty složené z úseček
V dnešním druhém demonstračním příkladu je ukázána tvorba uzavřené cesty. Okolo celé obrazovky je vykreslen široký žlutý okraj se zkosenými hranami. Nastavení stylu vykreslování je provedeno následovně:
/* barva stetce druhe cesty */ VGfloat color2[4] = {0.75f, 0.75f, 0.25f, 1.0f}; VGPaint strokePaint = vgCreatePaint(); vgSetParameteri(strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv(strokePaint, VG_PAINT_COLOR, 4, color2); vgSetPaint(strokePaint, VG_STROKE_PATH); vgDestroyPaint(strokePaint); /* sirka a styl stetce druhe cesty */ vgSetf(VG_STROKE_LINE_WIDTH, 20); vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_BUTT); /* siroke stopy konci presne na stanovenych souradnicich */ vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_BEVEL); /* vnejsi okraje spoju jsou "oseknute" */
Pro uzavřenou cestu bylo zvoleno pět příkazů (povšimněte si způsobu uzavření – to je důležité, aby byly správně zkoseny všechny okraje cesty):
/* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, /* pocatecni bod */ VG_LINE_TO_REL, /* prvni usecka (vodorovna) */ VG_LINE_TO_REL, /* druha usecka (svisla) */ VG_LINE_TO_REL, /* treti usecka (vodorovna) */ VG_CLOSE_PATH}; /* uzavreni cesty */
se souřadnicemi jednotlivých vrcholů:
VGfloat coordinates[] = {20, 20, /* pocatecni bod */ state->window_width-40, 0, /* prvni usecka (vodorovna) */ 0, state->window_height-40, /* druha usecka (svisla) */ -state->window_width+40, 0}; /* treti usecka (vodorovna) */
Úplný zdrojový kód tohoto příkladu vypadá následovně:
/* OpenVG (nejenom) na Raspberry Pi - sedmy demonstracni priklad */ /* Vykresleni uzavreneho segmentu slozeneho z usecek. */ #include <stdio.h> #include <VG/openvg.h> #include <VG/vgu.h> #include <EGL/egl.h> #include <bcm_host.h> /* * Datova struktura obsahujici cely stav EGL "sezeni". */ typedef struct { uint32_t screen_width; uint32_t screen_height; uint32_t window_x; uint32_t window_y; int32_t window_width; int32_t window_height; EGLDisplay display; EGLSurface surface; EGLContext context; EGLConfig config; } EGL_STATE_T; /* * Inicializace EGL. */ void initialize_egl(EGL_STATE_T *state) { EGLBoolean result; EGLint num_config; EGLConfig config; /* nutne pro RPi */ bcm_host_init(); /* pro jistotu vymazeme datovou strukturu nesouci stav EGL */ memset(state, 0, sizeof(*state)); static EGL_DISPMANX_WINDOW_T nativewindow; DISPMANX_ELEMENT_HANDLE_T dispman_element; DISPMANX_DISPLAY_HANDLE_T dispman_display; DISPMANX_UPDATE_HANDLE_T dispman_update; VC_RECT_T dst_rect; VC_RECT_T src_rect; static VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0 }; static const EGLint attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; /* vychozi displej */ state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); /* inicializace displeje */ result = eglInitialize(state->display, NULL, NULL); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("EGL init failed"); exit(1); } /* navazani EGL na OpenVG */ eglBindAPI(EGL_OPENVG_API); /* ziskani konfigurace framebufferu */ result = eglChooseConfig(state->display, attribute_list, &config, 1, &num_config); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("EGL choose config failed"); exit(1); } /* vytvoreni kontextu */ state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, NULL); /* kontrola, zda operace probehla v poradku */ if (state->context == EGL_NO_CONTEXT) { puts("EGL create context failed"); exit(1); } /* vytvoreni surface */ int32_t success = graphics_get_display_size(0, &state->screen_width, &state->screen_height); /* kontrola, zda operace probehla v poradku */ if (success < 0) { puts("get display size failed"); exit(1); } if ((state->window_width == 0) || (state->window_width > state->screen_width)) state->window_width = state->screen_width; if ((state->window_height == 0) || (state->window_height > state->screen_height)) state->window_height = state->screen_height; dispman_display = vc_dispmanx_display_open(0); dispman_update = vc_dispmanx_update_start(0); dispman_element = vc_dispmanx_element_add(dispman_update, dispman_display, 0 /*layer */ , &dst_rect, 0 /*src */ , &src_rect, DISPMANX_PROTECTION_NONE, &alpha, 0 /*clamp */ , 0 /*transform */ ); nativewindow.element = dispman_element; nativewindow.width = state->window_width; nativewindow.height = state->window_height; vc_dispmanx_update_submit_sync(dispman_update); /* vytvoreni surface */ state->surface = eglCreateWindowSurface(state->display, config, &nativewindow, NULL); /* kontrola, zda operace probehla v poradku */ if (state->surface == EGL_NO_SURFACE) { puts("no surface!"); exit(1); } /* nastaveni chovani bufferu pri operaci swap */ result = eglSurfaceAttrib(state->display, state->surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("can not set surface attributes!"); exit(1); } /* propojeni kontextu se surface */ result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("can not connect context with the surface!"); exit(1); } } /* * Ukonceni prace s EGL. */ void finalize_egl(EGL_STATE_T *state) { eglSwapBuffers(state->display, state->surface); eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroySurface(state->display, state->surface); eglDestroyContext(state->display, state->context); eglTerminate(state->display); } /* * Vykresleni nekolika usecek. */ void draw(EGL_STATE_T *state) { /* vymazani pozadi cernou barvou */ VGfloat color1[4] = {0.0f, 0.0f, 0.0f, 1.0f}; vgSetfv(VG_CLEAR_COLOR, 4, color1); vgClear(0, 0, state->window_width, state->window_height); /* barva stetce prvni cesty */ { VGfloat color2[4] = {0.75f, 0.25f, 0.25f, 1.0f}; VGPaint strokePaint = vgCreatePaint(); vgSetParameteri(strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv(strokePaint, VG_PAINT_COLOR, 4, color2); vgSetPaint(strokePaint, VG_STROKE_PATH); vgDestroyPaint(strokePaint); } /* sirka a styl stetce */ vgSetf(VG_STROKE_LINE_WIDTH, 10); vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_ROUND); vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND); { /* vykresleni prvni cesty */ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO); /* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, /* pocatecni bod */ VG_LINE_TO_REL}; /* vodorovna usecka */ VGfloat coordinates[] = {(state->window_width >> 1) - 100, state->window_height >> 1, 200, 0}; /* pridani useckoveho segmentu */ vgAppendPathData(path, 2, commands, coordinates); /* usecka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */ vgDrawPath(path, VG_STROKE_PATH); vgDestroyPath(path); } /* barva stetce druhe cesty */ { VGfloat color2[4] = {0.75f, 0.75f, 0.25f, 1.0f}; VGPaint strokePaint = vgCreatePaint(); vgSetParameteri(strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv(strokePaint, VG_PAINT_COLOR, 4, color2); vgSetPaint(strokePaint, VG_STROKE_PATH); vgDestroyPaint(strokePaint); } /* sirka a styl stetce druhe cesty */ vgSetf(VG_STROKE_LINE_WIDTH, 20); vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_BUTT); /* siroke stopy konci presne na stanovenych souradnicich */ vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_BEVEL); /* vnejsi okraje spoju jsou "oseknute" */ { /* vykresleni polycary */ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO); /* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, /* pocatecni bod */ VG_LINE_TO_REL, /* prvni usecka (vodorovna) */ VG_LINE_TO_REL, /* druha usecka (svisla) */ VG_LINE_TO_REL, /* treti usecka (vodorovna) */ VG_CLOSE_PATH}; /* uzavreni cesty */ VGfloat coordinates[] = {20, 20, /* pocatecni bod */ state->window_width-40, 0, /* prvni usecka (vodorovna) */ 0, state->window_height-40, /* druha usecka (svisla) */ -state->window_width+40, 0}; /* treti usecka (vodorovna) */ /* pridani useckoveho segmentu */ vgAppendPathData(path, 5, commands, coordinates); /* usecka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */ vgDrawPath(path, VG_STROKE_PATH); vgDestroyPath(path); } /* prohozeni predniho a zadniho bufferu (pokud je to zapotrebi) */ eglSwapBuffers(state->display, state->surface); } /* * Vstupni bod do programu. */ int main(int argc, char *argv[]) { EGL_STATE_T egl_state; initialize_egl(&egl_state); puts("initialize_egl OK"); draw(&egl_state); getchar(); finalize_egl(&egl_state); puts("finalize_egl OK"); return 0; }
Soubor Makefile určený pro překlad tohoto příkladu se vlastně nijak neliší od předchozího Makefile:
# Makefile pro preklad sedmeho prikladu ukazujiciho # praci s OpenVG a EGL. # Parametry prekladace. CFLAGS=-Wall # Dalsi parametry prekladace, zde adresare, kde se maji # hledat hlavickove soubory. INCLUDES=-I/opt/vc/include/ -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux # Parametry linkeru. LDFLAGS=-L/opt/vc/lib/ -lGLESv2 -lEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm PROGNAME=example7 # Vychozi pravidlo pro vytvoreni vysledne spustitelne aplikace. all: $(PROGNAME) clean: rm -f *.o rm -f $(PROGNAME) # Pravidlo pro slinkovani vsech objektovych souboru a vytvoreni # vysledne spustitelne aplikace. $(PROGNAME): $(PROGNAME).o $(CC) -o $@ $(LDFLAGS) $< # Pravidlo pro preklad kazdeho zdrojoveho souboru do prislusneho # objektoveho souboru. %.o: %.c $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
10. Třetí demonstrační příklad (osmý v celkovém pořadí): použití příkazů pro vykreslení vodorovných a svislých úseček
Pokud si ještě pamatujete, že jsme v třetí části tohoto seriálku hovořili o možnosti jednoduché kresby vodorovných a svislých úseček, pak vás nepřekvapí, že příkazy z předchozího příkladu:
/* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, /* pocatecni bod */ VG_LINE_TO_REL, /* prvni usecka (vodorovna) */ VG_LINE_TO_REL, /* druha usecka (svisla) */ VG_LINE_TO_REL, /* treti usecka (vodorovna) */ VG_CLOSE_PATH}; /* uzavreni cesty */
je možné převést na příkazy pro vykreslení vodorovných čar (horizontal line) a čar vertikálních (vertical line):
/* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, /* pocatecni bod */ VG_HLINE_TO_REL, /* prvni usecka (vodorovna) */ VG_VLINE_TO_REL, /* druha usecka (svisla) */ VG_HLINE_TO_REL, /* treti usecka (vodorovna) */ VG_CLOSE_PATH}; /* uzavreni cesty */
Změní se především pole se souřadnicemi (resp. se toto pole zmenší, což je jeden z účelů použití horizontálních a vertikálních čar, který se projeví zejména u komplikovaných cest). Staré pole:
VGfloat coordinates[] = {20, 20, /* pocatecni bod */ state->window_width-40, 0, /* prvni usecka (vodorovna) */ 0, state->window_height-40, /* druha usecka (svisla) */ -state->window_width+40, 0}; /* treti usecka (vodorovna) */
Nové pole (chybí zde nuly na druhém, třetím i čtvrtém řádku):
VGfloat coordinates[] = {20, 20, /* pocatecni bod */ state->window_width-40, /* prvni usecka (vodorovna) */ state->window_height-40, /* druha usecka (svisla) */ -state->window_width+40}; /* treti usecka (vodorovna) */
Úplný zdrojový kód tohoto příkladu vypadá následovně:
/* OpenVG (nejenom) na Raspberry Pi - osmy demonstracni priklad */ /* Vykresleni uzavreneho segmentu slozeneho z vodorovnych a svislych usecek. */ #include <stdio.h> #include <VG/openvg.h> #include <VG/vgu.h> #include <EGL/egl.h> #include <bcm_host.h> /* * Datova struktura obsahujici cely stav EGL "sezeni". */ typedef struct { uint32_t screen_width; uint32_t screen_height; uint32_t window_x; uint32_t window_y; int32_t window_width; int32_t window_height; EGLDisplay display; EGLSurface surface; EGLContext context; EGLConfig config; } EGL_STATE_T; /* * Inicializace EGL. */ void initialize_egl(EGL_STATE_T *state) { EGLBoolean result; EGLint num_config; EGLConfig config; /* nutne pro RPi */ bcm_host_init(); /* pro jistotu vymazeme datovou strukturu nesouci stav EGL */ memset(state, 0, sizeof(*state)); static EGL_DISPMANX_WINDOW_T nativewindow; DISPMANX_ELEMENT_HANDLE_T dispman_element; DISPMANX_DISPLAY_HANDLE_T dispman_display; DISPMANX_UPDATE_HANDLE_T dispman_update; VC_RECT_T dst_rect; VC_RECT_T src_rect; static VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0 }; static const EGLint attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; /* vychozi displej */ state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); /* inicializace displeje */ result = eglInitialize(state->display, NULL, NULL); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("EGL init failed"); exit(1); } /* navazani EGL na OpenVG */ eglBindAPI(EGL_OPENVG_API); /* ziskani konfigurace framebufferu */ result = eglChooseConfig(state->display, attribute_list, &config, 1, &num_config); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("EGL choose config failed"); exit(1); } /* vytvoreni kontextu */ state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, NULL); /* kontrola, zda operace probehla v poradku */ if (state->context == EGL_NO_CONTEXT) { puts("EGL create context failed"); exit(1); } /* vytvoreni surface */ int32_t success = graphics_get_display_size(0, &state->screen_width, &state->screen_height); /* kontrola, zda operace probehla v poradku */ if (success < 0) { puts("get display size failed"); exit(1); } if ((state->window_width == 0) || (state->window_width > state->screen_width)) state->window_width = state->screen_width; if ((state->window_height == 0) || (state->window_height > state->screen_height)) state->window_height = state->screen_height; dispman_display = vc_dispmanx_display_open(0); dispman_update = vc_dispmanx_update_start(0); dispman_element = vc_dispmanx_element_add(dispman_update, dispman_display, 0 /*layer */ , &dst_rect, 0 /*src */ , &src_rect, DISPMANX_PROTECTION_NONE, &alpha, 0 /*clamp */ , 0 /*transform */ ); nativewindow.element = dispman_element; nativewindow.width = state->window_width; nativewindow.height = state->window_height; vc_dispmanx_update_submit_sync(dispman_update); /* vytvoreni surface */ state->surface = eglCreateWindowSurface(state->display, config, &nativewindow, NULL); /* kontrola, zda operace probehla v poradku */ if (state->surface == EGL_NO_SURFACE) { puts("no surface!"); exit(1); } /* nastaveni chovani bufferu pri operaci swap */ result = eglSurfaceAttrib(state->display, state->surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("can not set surface attributes!"); exit(1); } /* propojeni kontextu se surface */ result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("can not connect context with the surface!"); exit(1); } } /* * Ukonceni prace s EGL. */ void finalize_egl(EGL_STATE_T *state) { eglSwapBuffers(state->display, state->surface); eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroySurface(state->display, state->surface); eglDestroyContext(state->display, state->context); eglTerminate(state->display); } /* * Vykresleni nekolika usecek. */ void draw(EGL_STATE_T *state) { /* vymazani pozadi cernou barvou */ VGfloat color1[4] = {0.0f, 0.0f, 0.0f, 1.0f}; vgSetfv(VG_CLEAR_COLOR, 4, color1); vgClear(0, 0, state->window_width, state->window_height); /* barva stetce prvni cesty */ { VGfloat color2[4] = {0.75f, 0.25f, 0.25f, 1.0f}; VGPaint strokePaint = vgCreatePaint(); vgSetParameteri(strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv(strokePaint, VG_PAINT_COLOR, 4, color2); vgSetPaint(strokePaint, VG_STROKE_PATH); vgDestroyPaint(strokePaint); } /* sirka a styl stetce */ vgSetf(VG_STROKE_LINE_WIDTH, 10); vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_ROUND); vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND); { /* vykresleni prvni cesty */ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO); /* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, /* pocatecni bod */ VG_HLINE_TO_REL}; /* vodorovna usecka */ VGfloat coordinates[] = {(state->window_width >> 1) - 100, state->window_height >> 1, 200}; /* delka vodorovne usecky */ /* pridani useckoveho segmentu */ vgAppendPathData(path, 2, commands, coordinates); /* usecka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */ vgDrawPath(path, VG_STROKE_PATH); vgDestroyPath(path); } /* barva stetce druhe cesty */ { VGfloat color2[4] = {0.75f, 0.75f, 0.25f, 1.0f}; VGPaint strokePaint = vgCreatePaint(); vgSetParameteri(strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv(strokePaint, VG_PAINT_COLOR, 4, color2); vgSetPaint(strokePaint, VG_STROKE_PATH); vgDestroyPaint(strokePaint); } /* sirka a styl stetce druhe cesty */ vgSetf(VG_STROKE_LINE_WIDTH, 20); vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_BUTT); /* siroke stopy konci presne na stanovenych souradnicich */ vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_BEVEL); /* vnejsi okraje spoju jsou "oseknute" */ { /* vykresleni polycary */ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO); /* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, /* pocatecni bod */ VG_HLINE_TO_REL, /* prvni usecka (vodorovna) */ VG_VLINE_TO_REL, /* druha usecka (svisla) */ VG_HLINE_TO_REL, /* treti usecka (vodorovna) */ VG_CLOSE_PATH}; /* uzavreni cesty */ VGfloat coordinates[] = {20, 20, /* pocatecni bod */ state->window_width-40, /* prvni usecka (vodorovna) */ state->window_height-40, /* druha usecka (svisla) */ -state->window_width+40}; /* treti usecka (vodorovna) */ /* pridani useckoveho segmentu */ vgAppendPathData(path, 5, commands, coordinates); /* usecka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */ vgDrawPath(path, VG_STROKE_PATH); vgDestroyPath(path); } /* prohozeni predniho a zadniho bufferu (pokud je to zapotrebi) */ eglSwapBuffers(state->display, state->surface); } /* * Vstupni bod do programu. */ int main(int argc, char *argv[]) { EGL_STATE_T egl_state; initialize_egl(&egl_state); puts("initialize_egl OK"); draw(&egl_state); getchar(); finalize_egl(&egl_state); puts("finalize_egl OK"); return 0; }
Soubor Makefile určený pro překlad tohoto příkladu se opět nijak neliší od předchozího Makefile:
# Makefile pro preklad osmeho prikladu ukazujiciho # praci s OpenVG a EGL. # Parametry prekladace. CFLAGS=-Wall # Dalsi parametry prekladace, zde adresare, kde se maji # hledat hlavickove soubory. INCLUDES=-I/opt/vc/include/ -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux # Parametry linkeru. LDFLAGS=-L/opt/vc/lib/ -lGLESv2 -lEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm PROGNAME=example8 # Vychozi pravidlo pro vytvoreni vysledne spustitelne aplikace. all: $(PROGNAME) clean: rm -f *.o rm -f $(PROGNAME) # Pravidlo pro slinkovani vsech objektovych souboru a vytvoreni # vysledne spustitelne aplikace. $(PROGNAME): $(PROGNAME).o $(CC) -o $@ $(LDFLAGS) $< # Pravidlo pro preklad kazdeho zdrojoveho souboru do prislusneho # objektoveho souboru. %.o: %.c $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
11. Čtvrtý demonstrační příklad (devátý v celkovém pořadí): vykreslení Bézierovy křivky
Kubické Bézierovy křivky většina uživatelů používajících vektorové grafické editory již velmi dobře zná (ale nalezneme je i v rastrových grafických editorech, například v GIMPu). Připomeňme si tedy, že tyto křivky jsou definovány počátečním bodem, koncovým bodem a dvojicí řídicích bodů. Větší množství řídicích bodů dává uživatelům i větší možnosti tvarování křivky, protože je možné vytvořit i esíčko atd. V knihovně OpenVG se tyto křivky (resp. segmenty složené z kubických Bézierových křivek) specifikují příkazy VG_CUBIC_TO_ABS a VG_CUBIC_TO_REL, přičemž se očekávají tři body (tedy šest souřadnic), protože počáteční bod již známe – je jím dočasný poslední bod aktuálně vytvářené cesty. Pozor je zapotřebí dávat na to, že i relativně zadané souřadnice jsou vždy vztaženy k tomu bodu, kde křivka začíná, nikoli k poslednímu zadanému bodu:
/* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, /* pocatecni bod */ VG_CUBIC_TO_REL}; /* Bezierova kubicka krivka */ VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1, 200, 250, 400, -250, 600, 0}; /* koncovy bod Bezierovy kubicke krivky */
Kromě samotné kubické Bézierovy křivky je do 2D scény vykreslena i polyčára spojující koncové body křivky s jejími řídicími body. Zde jsou samozřejmě relativní souřadnice vztaženy vždy k poslední vykreslené úsečce:
/* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, /* pocatecni bod */ VG_LINE_TO_REL, VG_LINE_TO_REL, VG_LINE_TO_REL}; VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1, 200, 250, 200, -500, 200, 250};
Úplný zdrojový kód tohoto příkladu vypadá takto:
/* OpenVG (nejenom) na Raspberry Pi - devaty demonstracni priklad */ /* Segmenty slozene z Berierovych krivek. */ #include <stdio.h> #include <VG/openvg.h> #include <VG/vgu.h> #include <EGL/egl.h> #include <bcm_host.h> /* * Datova struktura obsahujici cely stav EGL "sezeni". */ typedef struct { uint32_t screen_width; uint32_t screen_height; uint32_t window_x; uint32_t window_y; int32_t window_width; int32_t window_height; EGLDisplay display; EGLSurface surface; EGLContext context; EGLConfig config; } EGL_STATE_T; /* * Inicializace EGL. */ void initialize_egl(EGL_STATE_T *state) { EGLBoolean result; EGLint num_config; EGLConfig config; /* nutne pro RPi */ bcm_host_init(); /* pro jistotu vymazeme datovou strukturu nesouci stav EGL */ memset(state, 0, sizeof(*state)); static EGL_DISPMANX_WINDOW_T nativewindow; DISPMANX_ELEMENT_HANDLE_T dispman_element; DISPMANX_DISPLAY_HANDLE_T dispman_display; DISPMANX_UPDATE_HANDLE_T dispman_update; VC_RECT_T dst_rect; VC_RECT_T src_rect; static VC_DISPMANX_ALPHA_T alpha = { DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0 }; static const EGLint attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; /* vychozi displej */ state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); /* inicializace displeje */ result = eglInitialize(state->display, NULL, NULL); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("EGL init failed"); exit(1); } /* navazani EGL na OpenVG */ eglBindAPI(EGL_OPENVG_API); /* ziskani konfigurace framebufferu */ result = eglChooseConfig(state->display, attribute_list, &config, 1, &num_config); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("EGL choose config failed"); exit(1); } /* vytvoreni kontextu */ state->context = eglCreateContext(state->display, config, EGL_NO_CONTEXT, NULL); /* kontrola, zda operace probehla v poradku */ if (state->context == EGL_NO_CONTEXT) { puts("EGL create context failed"); exit(1); } /* vytvoreni surface */ int32_t success = graphics_get_display_size(0, &state->screen_width, &state->screen_height); /* kontrola, zda operace probehla v poradku */ if (success < 0) { puts("get display size failed"); exit(1); } if ((state->window_width == 0) || (state->window_width > state->screen_width)) state->window_width = state->screen_width; if ((state->window_height == 0) || (state->window_height > state->screen_height)) state->window_height = state->screen_height; dispman_display = vc_dispmanx_display_open(0); dispman_update = vc_dispmanx_update_start(0); dispman_element = vc_dispmanx_element_add(dispman_update, dispman_display, 0 /*layer */ , &dst_rect, 0 /*src */ , &src_rect, DISPMANX_PROTECTION_NONE, &alpha, 0 /*clamp */ , 0 /*transform */ ); nativewindow.element = dispman_element; nativewindow.width = state->window_width; nativewindow.height = state->window_height; vc_dispmanx_update_submit_sync(dispman_update); /* vytvoreni surface */ state->surface = eglCreateWindowSurface(state->display, config, &nativewindow, NULL); /* kontrola, zda operace probehla v poradku */ if (state->surface == EGL_NO_SURFACE) { puts("no surface!"); exit(1); } /* nastaveni chovani bufferu pri operaci swap */ result = eglSurfaceAttrib(state->display, state->surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("can not set surface attributes!"); exit(1); } /* propojeni kontextu se surface */ result = eglMakeCurrent(state->display, state->surface, state->surface, state->context); /* kontrola, zda operace probehla v poradku */ if (result == EGL_FALSE) { puts("can not connect context with the surface!"); exit(1); } } /* * Ukonceni prace s EGL. */ void finalize_egl(EGL_STATE_T *state) { eglSwapBuffers(state->display, state->surface); eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroySurface(state->display, state->surface); eglDestroyContext(state->display, state->context); eglTerminate(state->display); } /* * Vykresleni nekolika usecek a Bezierovych krivek. */ void draw(EGL_STATE_T *state) { /* vymazani pozadi cernou barvou */ VGfloat color1[4] = {0.0f, 0.0f, 0.0f, 1.0f}; vgSetfv(VG_CLEAR_COLOR, 4, color1); vgClear(0, 0, state->window_width, state->window_height); /* sirka a styl stetce */ vgSetf(VG_STROKE_LINE_WIDTH, 1); vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_ROUND); vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND); /* barva stetce prvni polycary */ { VGfloat color2[4] = {0.25f, 0.25f, 0.75f, 1.0f}; VGPaint strokePaint = vgCreatePaint(); vgSetParameteri(strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv(strokePaint, VG_PAINT_COLOR, 4, color2); vgSetPaint(strokePaint, VG_STROKE_PATH); vgDestroyPaint(strokePaint); } { /* vykresleni druhe cesty slozene z usecek */ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO); /* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, /* pocatecni bod */ VG_LINE_TO_REL, VG_LINE_TO_REL, VG_LINE_TO_REL}; VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1, 200, 250, 200, -500, 200, 250}; /* pridani useckoveho segmentu */ vgAppendPathData(path, 4, commands, coordinates); /* usecka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */ vgDrawPath(path, VG_STROKE_PATH); vgDestroyPath(path); } /* sirka a styl stetce */ vgSetf(VG_STROKE_LINE_WIDTH, 3); vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_ROUND); vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND); /* barva stetce Bezierovych krivek */ { VGfloat color2[4] = {0.75f, 0.25f, 0.25f, 1.0f}; VGPaint strokePaint = vgCreatePaint(); vgSetParameteri(strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv(strokePaint, VG_PAINT_COLOR, 4, color2); vgSetPaint(strokePaint, VG_STROKE_PATH); vgDestroyPaint(strokePaint); } { /* vykresleni prvni cesty slozene z Bezierovych krivek */ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO); /* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, /* pocatecni bod */ VG_CUBIC_TO_REL}; /* Bezierova kubicka krivka */ VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1, 200, 250, 400, -250, 600, 0}; /* koncovy bod Bezierovy kubicke krivky */ /* pridani useckoveho segmentu */ vgAppendPathData(path, 2, commands, coordinates); /* usecka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */ vgDrawPath(path, VG_STROKE_PATH); vgDestroyPath(path); } /* barva stetce polycary */ { VGfloat color2[4] = {0.75f, 0.75f, 0.25f, 1.0f}; VGPaint strokePaint = vgCreatePaint(); vgSetParameteri(strokePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); vgSetParameterfv(strokePaint, VG_PAINT_COLOR, 4, color2); vgSetPaint(strokePaint, VG_STROKE_PATH); vgDestroyPaint(strokePaint); } /* sirka a styl stetce */ vgSetf(VG_STROKE_LINE_WIDTH, 20); vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_BUTT); /* siroke stopy konci presne na stanovenych souradnicich */ vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_BEVEL); /* vnejsi okraje spoju jsou "oseknute" */ { /* vykresleni polycary */ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO); /* deklarace useckoveho segmentu */ VGubyte commands[] = {VG_MOVE_TO_ABS, /* pocatecni bod */ VG_LINE_TO_REL, /* prvni usecka (vodorovna) */ VG_LINE_TO_REL, /* druha usecka (svisla) */ VG_LINE_TO_REL, /* treti usecka (vodorovna) */ VG_CLOSE_PATH}; /* uzavreni cesty */ VGfloat coordinates[] = {20, 20, /* pocatecni bod */ state->window_width-40, 0, /* prvni usecka (vodorovna) */ 0, state->window_height-40, /* druha usecka (svisla) */ -state->window_width+40, 0}; /* treti usecka (vodorovna) */ /* pridani useckoveho segmentu */ vgAppendPathData(path, 5, commands, coordinates); /* usecka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */ vgDrawPath(path, VG_STROKE_PATH); vgDestroyPath(path); } /* prohozeni predniho a zadniho bufferu (pokud je to zapotrebi) */ eglSwapBuffers(state->display, state->surface); } /* * Vstupni bod do programu. */ int main(int argc, char *argv[]) { EGL_STATE_T egl_state; initialize_egl(&egl_state); puts("initialize_egl OK"); draw(&egl_state); getchar(); finalize_egl(&egl_state); puts("finalize_egl OK"); return 0; }
Soubor Makefile určený pro překlad tohoto příkladu se opět podle očekávání nijak neliší od předchozího Makefile:
# Makefile pro preklad devateho prikladu ukazujiciho # praci s OpenVG a EGL. # Parametry prekladace. CFLAGS=-Wall # Dalsi parametry prekladace, zde adresare, kde se maji # hledat hlavickove soubory. INCLUDES=-I/opt/vc/include/ -I/opt/vc/include/interface/vcos/pthreads -I/opt/vc/include/interface/vmcs_host/linux # Parametry linkeru. LDFLAGS=-L/opt/vc/lib/ -lGLESv2 -lEGL -lopenmaxil -lbcm_host -lvcos -lvchiq_arm -lpthread -lrt -lm PROGNAME=example9 # Vychozi pravidlo pro vytvoreni vysledne spustitelne aplikace. all: $(PROGNAME) clean: rm -f *.o rm -f $(PROGNAME) # Pravidlo pro slinkovani vsech objektovych souboru a vytvoreni # vysledne spustitelne aplikace. $(PROGNAME): $(PROGNAME).o $(CC) -o $@ $(LDFLAGS) $< # Pravidlo pro preklad kazdeho zdrojoveho souboru do prislusneho # objektoveho souboru. %.o: %.c $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
12. Vysokoúrovňové funkce vguLine, vguRect, vguRoundRect a vguPolygon
V některých případech nemá příliš význam ručně tvořit cesty, pokud je vykreslovaný tvar podporovaný pomocnou knihovnou VGU (OpenVG Utility Library). Jedná se o samostatnou úsečku, osově orientovaný obdélník, osově orientovaný obdélník se zaoblenými rohy a obecný polygon:
vguErrorCode vguLine(VGPath path, VGfloat x0, VGfloat y0, VGfloat x1, VGfloat y1);
vguErrorCode vguRect(VGPath path, VGfloat x, VGfloat y, VGfloat width, VGfloat height);
vguErrorCode vguRoundRect(VGPath path, VGfloat x, VGfloat y, VGfloat width, VGfloat height, VGfloat arcW, VGfloat arcH);
vguErrorCode vguPolygon(VGPath path, const VGfloat * points, VGint count, VGboolean closed);
Praktické příklady si ukážeme příště.
13. Repositář s demonstračními příklady
Všechny čtyři demonstrační příklady, které jsme si v dnešním článku popsali, byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/presentations. V tabulce zobrazené pod tímto odstavcem naleznete na zdrojové kódy všech čtyř zmíněných demonstračních příkladů přímé odkazy:
# | Příklad/knihovna | Github |
---|---|---|
1 | example6 | https://github.com/tisnik/presentations/tree/master/openvg/example6 |
2 | example7 | https://github.com/tisnik/presentations/tree/master/openvg/example7 |
3 | example8 | https://github.com/tisnik/presentations/tree/master/openvg/example8 |
4 | example9 | https://github.com/tisnik/presentations/tree/master/openvg/example9 |
Poznámka1: pro zjednodušení překladu je ke každému demonstračnímu příkladu přiložen i příslušný soubor Makefile (otestovaný na Raspberry Pi).
Poznámka2: nenechte se prosím zmást číslováním – příklady example1 až example5 byly totiž popsány v předchozích třech částech tohoto seriálu.
14. Odkazy na Internetu
- EGL quick reference card
https://www.khronos.org/files/egl-1–4-quick-reference-card.pdf - EGL Reference Pages Index
https://www.khronos.org/registry/egl/sdk/docs/man/html/indexflat.php - Funkce eglInitialize
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglInitialize.xhtml - Funkce eglGetDisplay
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglGetDisplay.xhtml - Funkce eglGetConfigs
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglGetConfigs.xhtml - Funkce eglGetConfigAttrib
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglGetConfigAttrib.xhtml - Funkce eglDestroySurface
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglDestroySurface.xhtml - Funkce eglDestroyContext
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglDestroyContext.xhtml - Funkce eglTerminate
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglTerminate.xhtml - Khronos Native Platform Graphics Interface
https://www.khronos.org/registry/egl/specs/eglspec.1.4.pdf - Khronos Group
https://www.khronos.org/ - Khronos Group (Wikipedia)
https://en.wikipedia.org/wiki/Khronos_Group - Raspberry Pi VideoCore APIs
http://elinux.org/Raspberry_Pi_VideoCore_APIs - Programming AudioVideo on the Raspberry Pi GPU
https://jan.newmarch.name/RPi/index.html - The Standard for Vector Graphics Acceleration
https://www.khronos.org/openvg/ - OpenVG (Wikipedia)
https://en.wikipedia.org/wiki/OpenVG - OpenVG Quick Reference Card
https://www.khronos.org/files/openvg-quick-reference-card.pdf - OpenVG on the Raspberry Pi
http://mindchunk.blogspot.cz/2012/09/openvg-on-raspberry-pi.html - ShivaVG: open-source ANSI C OpenVG
http://ivanleben.blogspot.cz/2007/07/shivavg-open-source-ansi-c-openvg.html - Testbed for exploring OpenVG on the Raspberry Pi
https://github.com/ajstarks/openvg - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: knihovna Pygame
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-knihovna-pygame/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: knihovna Pygame prakticky
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-knihovna-pygame-prakticky/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: práce s bitmapami a TrueType fonty
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-prace-s-bitmapami-a-truetype-fonty/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: sprity v knihovně Pygame
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-sprity-v-knihovne-pygame/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: detekce kolize spritů
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-detekce-kolize-spritu/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: transformace rastrových obrázků
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-transformace-rastrovych-obrazku/ - Seriál Grafické karty a grafické akcelerátory
http://www.root.cz/serialy/graficke-karty-a-graficke-akceleratory/ - Grafika na osmibitových počítačích firmy Sinclair II
http://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair-ii/ - Xiaolin_Wu's Line Algorithm
https://en.wikipedia.org/wiki/Xiaolin_Wu's_line_algorithm - Grafické čipy v osmibitových počítačích Atari
http://www.root.cz/clanky/graficke-cipy-v-osmibitovych-pocitacich-atari/ - Osmibitové počítače Commodore a čip VIC-II
http://www.root.cz/clanky/osmibitove-pocitace-commodore-a-cip-vic-ii/ - Grafika na osmibitových počítačích firmy Apple
http://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-apple/ - Počátky grafiky na PC: grafické karty CGA a Hercules
http://www.root.cz/clanky/pocatky-grafiky-na-pc-graficke-karty-cga-a-hercules/ - Karta EGA: první použitelná barevná grafika na PC
http://www.root.cz/clanky/karta-ega-prvni-pouzitelna-barevna-grafika-na-pc/ - Grafické karty MCGA a VGA
http://www.root.cz/clanky/graficke-karty-mcga-a-vga/ - Grafický subsystém počítačů Amiga
http://www.root.cz/clanky/graficky-subsystem-pocitacu-amiga/ - Grafický subsystém počítačů Amiga II
http://www.root.cz/clanky/graficky-subsystem-pocitacu-amiga-ii/ - Raspberry Pi pages
https://www.raspberrypi.org/ - BCM2835 registers
http://elinux.org/BCM2835_registers - VideoCore (archiv stránek společnosti Alphamosaic)
http://web.archive.org/web/20030209213838/www.alphamosaic.com/videocore/ - VideoCore (Wikipedia)
https://en.wikipedia.org/wiki/Videocore - RPi lessons: Lesson 6 Screen01
http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/screen01.html - Raspberry Pi forum: Bare metal
https://www.raspberrypi.org/forums/viewforum.php?f=72 - C library for Broadcom BCM 2835 as used in Raspberry Pi
http://www.airspayce.com/mikem/bcm2835/ - Raspberry Pi Hardware Components
http://elinux.org/RPi_Hardware#Components - (Linux) Framebuffer
http://wiki.linuxquestions.org/wiki/Framebuffer - (Linux) Framebuffer HOWTO
http://tldp.org/HOWTO/Framebuffer-HOWTO/ - Linux framebuffer (Wikipedia)
https://en.wikipedia.org/wiki/Linux_framebuffer - RPi Framebuffer
http://elinux.org/RPi_Framebuffer - HOWTO: Boot your Raspberry Pi into a fullscreen browser kiosk
http://blogs.wcode.org/2013/09/howto-boot-your-raspberry-pi-into-a-fullscreen-browser-kiosk/ - Zdrojový kód fb.c pro RPI
https://github.com/jncronin/rpi-boot/blob/master/fb.c - RPiconfig
http://elinux.org/RPi_config.txt - Mailbox framebuffer interface
https://github.com/raspberrypi/firmware/wiki/Mailbox-framebuffer-interface - Seriál Grafické formáty
http://www.root.cz/serialy/graficke-formaty/