2D grafika s využitím knihovny OpenVG (nejenom) na Raspberry Pi

Pavel Tišnovský 10. 3. 2016

Na předchozí článek o základních postupech používaných v knihovně EGL dnes navážeme, protože si vysvětlíme, jak je možné s využitím EGL vytvořit kreslicí plochu a následně do ní vykreslovat 2D grafiku s OpenVG.

Obsah

1. 2D grafika s využitím knihovny OpenVG (nejenom) na Raspberry Pi

2. Výběr vhodné konfigurace framebufferu

3. Vytvoření kontextu pro kreslení a kreslicí plochy (surface)

4. Specifikace chování zadního bufferu při operaci „swap“ a propojení kontextu pro kreslení s kreslicí plochou

5. Vymazání obrazovky černou barvou

6. Nakreslení vyplněného obdélníku

7. Úplný zdrojový kód dnešního prvního demonstračního příkladu

8. Vykreslování úseček, polyčar a polygonů

9. Styly štětce

10. Úplný zdrojový kód dnešního druhého demonstračního příkladu

11. Repositář s demonstračními příklady

12. Odkazy na Internetu

1. 2D grafika s využitím knihovny OpenVG (nejenom) na Raspberry Pi

V předchozím článku jsme si vysvětlili, jaká je úloha knihovny EGL při programování aplikací využívajících OpenGL, OpenGL ES či OpenVG. Připomeňme si, že EGL tvoří poměrně tenkou, ale unifikovanou vrstvu ležící mezi zmíněnými platformově nezávislými knihovnami (OpenGL, OpenGL ES či OpenVG) na jedné straně a nativními knihovnami určenými pro ovládání oken či celoobrazovkových režimů na straně druhé. Použitím EGL je tak možné z aplikací odstranit platformově závislý kód, který by jinak bylo nutné implementovat několikrát, což je neefektivní a současně to i klade větší požadavky na dostupnost testovacího hardware atd. Minule jsme si taktéž ukázali, jakým způsobem je možné použít funkce poskytované knihovnou EGL pro základní inicializaci framebufferu při spouštění aplikace (několik kroků si však ještě budeme muset popsat dnes) a jak naopak při ukončování aplikace provést „finalizaci“ framebufferu a na něj navázaných datových struktur.

Před použitím knihovny OpenVG však ještě musíme provést několik dalších operací, které budou stručně popsány v navazujících kapitolách (pro většinu aplikací psaných v OpenVG je možné popsané funkce přímo převzít a začít používat; většinou není nutné je podrobněji zkoumat). Druhá polovina článku již bude konečně věnována použití OpenVG. Pokud vás tedy zajímá hlavně OpenVG, klidně přeskočte na pátou kapitolu.

V demonstračních příkladech jsme stav grafického subsystému ukládali do jednoduché datové struktury, kterou budeme používat i dnes:

/*
 * 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;

První verze funkce pro inicializaci grafického subsystému s využitím EGL vypadala takto:

/*
 * Inicializace EGL.
 */
void initialize_egl(EGL_STATE_T *state)
{
    EGLBoolean result;
 
    /* nutne pro RPi */
    bcm_host_init();
 
    /* pro jistotu vymazeme datovou strukturu nesouci stav EGL */
    memset(state, 0, sizeof(*state));
 
    /* propojeni na vychozi displej */
    state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
 
    /* inicializace displeje */
    result = eglInitialize(state->display, NULL, NULL);
 
    /* kontrola, zda inicializace probehla v poradku */
    if (result == EGL_FALSE) {
        puts("EGL init failed");
        exit(1);
    }
}

Ještě si připomeňme, jak vypadá „finalizér“ (ten je již dokonalý a nebudeme ho tedy muset měnit):

/*
 * Ukonceni prace s EGL.
 */
void finalize_egl(EGL_STATE_T *state)
{
    /* nyni jsou tyto kroky prozatim zbytecne, v dalsich prikladech se vsak budou hodit */
    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);
}

2. Výběr vhodné konfigurace framebufferu

Po prvních krocích spočívajících ve volání funkcí eglGetDisplay a eglInitialize je nutné si vybrat vhodnou konfiguraci framebufferu takovým způsobem, aby to přesně odpovídalo požadavkům aplikace. Již naposledy si připomeňme, že konkrétně na Raspberry Pi je k dispozici celkem 28 různých kombinací framebufferu, které se od sebe odlišují jak hloubkou barvového bufferu, tak i přítomností alpha bufferu (resp. průhlednosti v color bufferu), Z-bufferu či stencil bufferu. Jedná se o tyto kombinace (získané demonstračním příkladem popsaným minule):

EGL has 28 configurations available
Configuration  R   G   B   A   bpp   depth stencil bind RGB/RGBA
  0            8   8   8   8   32     24       8      no   yes
  1            8   8   8   0   24     24       8      yes  yes
  2            8   8   8   8   32     24       0      no   yes
  3            8   8   8   0   24     24       0      yes  yes
  4            8   8   8   8   32      0       8      no   yes
  5            8   8   8   0   24      0       8      yes  yes
  6            8   8   8   8   32      0       0      no   yes
  7            8   8   8   0   24      0       0      yes  yes
  8            8   8   8   8   32     24       8      no   no
  9            8   8   8   0   24     24       8      no   no
 10            8   8   8   8   32     24       0      no   no
 11            8   8   8   0   24     24       0      no   no
 12            8   8   8   8   32      0       8      no   no
 13            8   8   8   0   24      0       8      no   no
 14            8   8   8   8   32      0       0      no   no
 15            8   8   8   0   24      0       0      no   no
 16            5   6   5   0   16     24       8      yes  yes
 17            5   6   5   0   16     24       0      yes  yes
 18            5   6   5   0   16      0       8      yes  yes
 19            5   6   5   0   16      0       0      yes  yes
 20            5   6   5   0   16     24       8      no   no
 21            5   6   5   0   16     24       0      no   no
 22            5   6   5   0   16      0       8      no   no
 23            5   6   5   0   16      0       0      no   no
 24            8   8   8   8   32      0       0      no   yes
 25            8   8   8   0   24      0       0      yes  yes
 26            5   6   5   0   16      0       0      yes  yes
 27            5   6   5   0   16     16       0      yes  yes

Při práci s knihovnou OpenVG, která je orientovaná výhradně na kvalitní 2D grafiku, se počet vhodných kombinací zmenšuje – nepotřebujeme totiž používat paměť hloubky (Z-buffer, depth buffer), nepotřebujeme dokonce ani stencil buffer a většinou nás nezajímá vazba vykreslovacích rutin na textury. Počet vhodných kombinací se tak rapidně snižuje:

Configuration  R   G   B   A   bpp   depth stencil bind RGB/RGBA
 14            8   8   8   8   32      0       0      no   no
 15            8   8   8   0   24      0       0      no   no
 23            5   6   5   0   16      0       0      no   no

Ukažme si nyní, jak se vhodná konfigurace framebufferu vybírá. Používá se k tomu funkce nazvaná (celkem logicky) eglChooseConfig. Hlavička této funkce vypadá poměrně děsivě:

EGLBoolean eglChooseConfig(EGLDisplay    dpy,
                           const EGLint *attrib_list,
                           EGLConfig    *configs,
                           EGLint        config_size,
                           EGLint       *num_config)

Ve skutečnosti není použití této funkce tak složité, jak to možná na první pohled může vypadat. Prvním parametrem je struktura obsahující informace o primárním displeji; tu již máme k dispozici. Zajímavější je druhý parametr, což je pole obsahující požadované atributy framebufferu, a to konkrétně dvojice identifikátor_atributu (celočíselná konstanta) a hodnota. V céčku jsou prvky 2D polí uloženy za sebou, takže není problém všechny požadované atributy uložit za sebou do jediného 1D pole. Celé pole je ukončeno speciální hodnotou EGL_NONE. Pokud požadujeme vytvoření framebufferu s hloubkou 24bpp a osmibitovým alfa kanálem, bude pole s atributy vypadat takto:

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, // typ kreslicí plochy
    EGL_NONE
};

Ve čtvrtém parametru se specifikuje, kolik konfigurací framebufferu odpovídajících zadaným atributům se má vrátit. Nás bude zajímat jediná (první vhodná) kombinace, tudíž zde předáme jedničku. Pole s konfiguracemi se předává přes třetí parametr. Pole=ukazatel na jeho první prvek, takže v našem případě postačuje předat přímo ukazatel na strukturu typu EGLConfig. Poslední parametr je ukazatel na proměnnou typu EGLint (celé číslo). Do této proměnné se přes předaný ukazatel předá počet nalezených konfigurací (přičemž předpokládáme, že se nalezne aspoň jedna vhodná konfigurace). Celý kód volající funkci eglChooseConfig může vypadat takto:

EGLint     num_config;
EGLConfig  config;
EGLDisplay display;
 
/* vychozi displej */
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
 
/* inicializace displeje */
eglInitialize(display, NULL, NULL);
 
/* ziskani konfigurace framebufferu */
eglChooseConfig(display, attribute_list, &config, 1, &num_config);

Samozřejmě musíme ještě přidat kontrolu, zda všechny funkce proběhly v pořádku, takže vylepšený kód může vypadat následovně:

EGLBoolean result;
EGLint     num_config;
EGLConfig  config;
EGLDisplay display;
 
/* vychozi displej */
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
 
/* inicializace displeje */
result = eglInitialize(display, NULL, NULL);
/* kontrola, zda operace probehla v poradku */
if (result == EGL_FALSE) {
    puts("EGL init failed");
    exit(1);
}
 
/* ziskani konfigurace framebufferu */
result = eglChooseConfig(display, attribute_list, &config, 1, &num_config);
/* kontrola, zda operace probehla v poradku */
if (result == EGL_FALSE) {
    puts("EGL choose config failed");
    exit(1);
}

3. Vytvoření kontextu pro kreslení a kreslicí plochy (surface)

Předpokládejme, že předchozí operace proběhla v pořádku, takže v proměnné config máme uloženou vhodnou konfiguraci framebufferu. V dalším kroku specifikujeme, že na knihovnu EGL je navázána knihovna OpenVG. To je snadné – použije se funkce eglBindAPI, která akceptuje hodnoty EGL_OPENGL_API, EGL_OPENGL_ES_API nebo EGL_OPENVG_API (význam je zřejmý):

/* navazani EGL na OpenVG */
eglBindAPI(EGL_OPENVG_API);

Nyní již nastal čas na využití proměnné config, kterou jsme tak pracně naplnili. Vytvoříme takzvaný rendering kontext zavoláním funkce eglCreateContext. V nejjednodušším případě bude volání této funkce vypadat takto (poslední dva parametry by se použily při sdílení kontextu):

EGLContext context;
 
/* vytvoreni kontextu */
context = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL);
 
/* kontrola, zda operace probehla v poradku */
if (context == EGL_NO_CONTEXT) {
    puts("EGL create context failed");
    exit(1);
}

Další část programového kódu slouží k inicializaci takzvaného nativního okna či celoobrazovkového režimu. V tomto úryvku kódu se volají funkce z knihovny Dispman, kterou si prozatím (alespoň dnes) nebudeme popisovat; proto prosím následující řádky pouze použijte, nic zlého se vašemu Raspberry Pi ani připojenému monitoru nestane :-)

/* vytvoreni surface */
int32_t success = graphics_get_display_size(0, &screen_width, &screen_height);
 
/* kontrola, zda operace probehla v poradku */
if (success < 0) {
    puts("get display size failed");
    exit(1);
}
 
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);

Následuje již pochopitelnější kód sloužící pro vytvoření kreslicí plochy (surface). Ta se vytváří pomocí funkce eglCreateWindowSurface s následující hlavičkou:

EGLSurface eglCreateWindowSurface(EGLDisplay dpy,
                                  EGLConfig  config,
                                  EGLNativeWindowType win,
                                  const EGLint *attrib_list)

Hodnoty dosazované pro první dva parametry již máme připraveny (viz popis eglGetDisplay a eglChooseConfig). Ve třetím parametru se předává struktura popisující okno či celoobrazovkový režim (zde získaná přes knihovnu Dispman), čtvrtý parametr může (ale nemusí) opět obsahovat seznam požadovaných atributů:

EGLSurface surface;
 
/* vytvoreni surface */
surface = eglCreateWindowSurface(display, config, &nativewindow, NULL);
 
/* kontrola, zda operace probehla v poradku */
if (surface == EGL_NO_SURFACE) {
    puts("no surface!");
    exit(1);
}

4. Specifikace chování zadního bufferu při operaci „swap“ a propojení kontextu pro kreslení s kreslicí plochou

Už jsme skoro připraveni na použití knihovny OpenVG; zbývá nám pouze specifikovat chování zadního bufferu při prohazování předního a zadního bufferu (překreslení scény). Prozatím netvoříme žádné dynamické scény, které by se neustále překreslovaly, takže nám bude vyhovovat, když se nový zadní buffer automaticky NEvymaže. Nastavení tohoto chování je jednoduché:

/* nastaveni chovani bufferu pri operaci swap */
result = eglSurfaceAttrib(display, 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);
}

Úplně nakonec specifikujeme, která kreslicí plocha bude výchozí pro všechny další operace. Knihovna EGL a OpenVG bude nastavenou kreslicí plochu implicitně používat, aniž by bylo nutné neustále přenášet její identifikátor při volání ostatních funkcí (což je vlastnost, která může být někdy výhodná a někdy nikoli, záleží na množství připojených displejů atd.):

/* propojeni kontextu se surface */
result = eglMakeCurrent(display, surface, surface, context);
 
/* kontrola, zda operace probehla v poradku */
if (result == EGL_FALSE) {
    puts("can not connect context with the surface!");
    exit(1);
}

5. Vymazání obrazovky černou barvou

Inicializace grafického subsystému Raspberry Pi (či jiného počítače s EGL) se snad podařila, takže se konečně dostáváme k popisu možností knihovny OpenVG. Tato knihovna je určena pro tvorbu kvalitní 2D grafiky, protože právě s 2D grafikou se uživatelé v současnosti setkávají při práci na desktopu, tabletu či smartphonu nejčastěji. Typickou 2D aplikací dneška je webový prohlížeč, od něhož se mj. vyžaduje i co nejrychlejší vykreslení mnohdy velmi složitého layoutu stránek, plynulý scrolling, kvalitně zobrazené písmo, dynamické změny ve stránkách, podpora HTML 5 canvasu apod. Dalšími 2D aplikacemi mohou být některé oblíbené hry, ať již se jedná o typicky dvourozměrné Angry Birds či o hry s isometrickou grafikou. Na tabletech, smartphonech a některých „moderních“ desktopech se taktéž velmi často požaduje zmenšení aplikace do živé ikony či použití vektorových ikon a widgetů (škálovatelné grafické uživatelské rozhraní).

Právě na tento segment se soustředí knihovna OpenVG, která dokonce vyžaduje, aby její implementace prošly poměrně velkým množstvím testů, které zjišťují, jakým způsobem a jak kvalitně je rendering 2D grafiky proveden. Podívejme se na některé základní operace. Začneme skutečně od triviálních operací, ovšem i na nich si lze ukázat některé vlastnosti OpenVG. První nutnou operací je vymazání obrazovky (resp. většinou zadního bufferu). Pro tyto účely použijeme funkci vgClear, které se předají souřadnice jednoho vrcholu a rozměry osově orientovaného obdélníku, který se má vymazat (rozměry framebufferu už máme zjištěné z předchozího kódu):

vgClear(0, 0, window_width, window_height);

Ještě předtím je ale nutné specifikovat barvu mazání, a to s využitím „setteru“ nazvaného vgSetfv. U názvu této funkce se na chvíli zastavme, protože je z něj patrné, že se používá podobný (nikoli však totožný) způsob pojmenování, jaký možná čtenáři znají z knihovny OpenGL. Ve skutečnosti je „setter“ realizován hned několika funkcemi, které se odlišují typem a počtem předávaných parametrů:

void vgSeti(VGParamType paramType, VGint val) nastavení jednoho parametru typu int
void vgSetf(VGParamType paramType, VGfloat val) nastavení jednoho parametru typu float
void vgSetiv(VGParamType paramType, VGint cnt, const VGint * val) nastavení pole parametrů typu int
void vgSetfv(VGParamType paramType, VGint cnt, const VGfloat * val) nastavení pole parametrů typu float

Pokud je zapotřebí nastavit barvu mazání, je jméno příslušného parametru VG_CLEAR_COLOR a jeho hodnotou může být například pole čtyř hodnot typu float (RGBA), i když existují i další možnosti. Délka pole (počet prvků) je tedy 4 a celý kód pro vymazání bufferu může vypadat takto:

/* vymazani pozadi cernou barvou */
VGfloat color1[4] = {0.0f, 0.0f, 0.0f, 1.0f};
vgSetfv(VG_CLEAR_COLOR, 4, color1);
vgClear(0, 0, window_width, window_height);

Poznámka: v OpenVG se pro nastavení stavu (narozdíl od OpenGL) skutečně důsledně používají výše zmíněné čtyři funkce. Stav vykreslovací pipeline je součástí aktuálního kontextu nastaveného funkcí eglMakeCurrent (viz též předchozí kapitoly).

6. Nakreslení vyplněného obdélníku

Pohled na černou obrazovku asi není příliš zajímavý, proto do scény přidáme další grafický prvek. Bude se jednat o vyplněný osově orientovaný obdélník. Barva výplně a barva okrajů obdélníka bude odlišná. Nejprve se podívejme, jak se nastaví barva okrajů vykreslovaných objektů (to není zcela přesný termín, protože se nastavuje barva vykreslované „cesty“, která je u obdélníka současně i jeho hranicí, u otevřených cest to však samozřejmě neplatí, neboť ty žádný vyplňovatelný vnitřek nemají). Nejprve se vytvoří objekt typu „paint“ funkcí vgCreatePaint. Přes tento objekt se nastaví vnitřní stav knihovny OpenVG, která si udržuje dvě důležité informace: jak vykreslovat cesty a jak vyplňovat vnitřky uzavřených cest. Posléze nastavíme dva parametry udržované tímto objektem: typ (vykreslování cest) a barvu. Následně se pomocí funkce vgSetPaint určí, že nastavené parametry se mají uložit do vnitřního stavu OpenVG udržujícího informace o vykreslování cest (a nikoli výplní). Nakonec se může objekt typu „paint“ zrušit a uvolnit tak jeho paměť:

/* barva stetce */
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);

Zcela stejný postup použijeme při nastavení barvy výplně. Zde se bude lišit pouze typ objektu typu „paint“: namísto VG_STROKE_PATH použijeme VG_FILL_PATH a následně se objekt opět zruší:

/* barva vyplne */
VGfloat color3[4] = {0.25f, 0.25f, 0.75f, 1.0f};
VGPaint fillPaint = vgCreatePaint();
vgSetParameteri(fillPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
vgSetParameterfv(fillPaint, VG_PAINT_COLOR, 4, color3);
vgSetPaint(fillPaint, VG_FILL_PATH);
vgDestroyPaint(fillPaint);

Zbývá nám nastavit šířku vykreslovaných cest, způsob vykreslení jejich zalomení a taktéž konců. Tímto tématem se budeme zabývat v dalších kapitolách, proto jen stručně:

/* 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);

Samotný obdélník je tvořen „cestou“ vytvořenou pomocí funkce vgCreatePath. Při definici vrcholů vytvářené křivky máme více možností, já jsem zde pro jednoduchost použil knihovnu VGU a v ní deklarovanou funkci vguRect. Cesta se vykreslí do zadního bufferu příkazem vgDrawPath a nakonec se může paměť alokovaná pro cestu uvolnit přes funkci vgDestroyPath:

/* vykresleni obdelnika */
VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO);
vguRect(path, x, y, width, height);
vgDrawPath(path, VG_FILL_PATH | VG_STROKE_PATH);
vgDestroyPath(path);

Parametry funkce vgCreatePath budou přesněji popsány příště.

7. Úplný zdrojový kód dnešního prvního demonstračního příkladu

Všechny kroky popsané ve druhé až šesté kapitole jsou součástí dnešního prvního demonstračního příkladu, jehož úplný zdrojový kód i příslušný soubor Makefile samozřejmě naleznete na GitHubu. Příklad je plně funkční na Raspberry Pi (konkrétně byl otestován na RPi 1 Modelu B):

/* OpenVG (nejenom) na Raspberry Pi - ctvrty demonstracni priklad */
 
#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 vyplneneho obdelnika.
 */
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 */
    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);
 
    /* barva vyplne */
    VGfloat color3[4] = {0.25f, 0.25f, 0.75f, 1.0f};
    VGPaint fillPaint = vgCreatePaint();
    vgSetParameteri(fillPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
    vgSetParameterfv(fillPaint, VG_PAINT_COLOR, 4, color3);
    vgSetPaint(fillPaint, VG_FILL_PATH);
    vgDestroyPaint(fillPaint);
 
    /* 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 obdelnika */
    VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO);
    vguRect(path, state->window_width/3, state->window_height/3, state->window_width/3, state->window_height/3);
    vgDrawPath(path, VG_FILL_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 použitý pro překlad dnešního prvního demonstračního příkladu vypadá následovně:

# Makefile pro preklad ctvrteho 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=example4
 
# 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 $@

8. Vykreslování úseček, polyčar a polygonů

Pro vykreslování samostatných úseček, polyčar (lomených čar) či polygonů (uzavřených polyčar) je nejjednodušší postupovat následovně:

  1. Vytvořit cestu pomocí funkce vgCreatePath
  2. Zavolat funkci vguPolygon a předat jí pole vrcholů (jednorozměrné pole či pole dvojic)
  3. Vykreslit cestu funkcí vgDrawPath
  4. Zrušit cestu funkcí vgDestroyPath

V praxi to může vypadat následovně:

/*
 * Vykresleni polycary slozene ze dvou usesek.
 */
void drawPolyline(int xoffset, int yoffset)
{
    /* vykresleni polycary */
    VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO);
    float vertexes2[] = {xoffset+100, yoffset+100, xoffset+200, yoffset+100, xoffset+200, yoffset+200};
    vguPolygon(path, vertexes2, 3, VG_FALSE);
    vgDrawPath(path, VG_STROKE_PATH);
    vgDestroyPath(path);
}

9. Styly štětce

Styly vykreslování cest (a tedy i úseček, polyčar apod.) se nastavují funkcemi vgSeti, vgSetf, vgSetiv a vgSetfv (viz též pátou kapitolu). Nás nyní budou zajímat tři základní parametry: šířka vykreslované stopy, tvar stopy při zakončení křivek (konce úseček) a tvar stopy při lomení křivek (jak se například vykreslí rohy obdélníka):

Parametr Funkce Hodnota parametru
Šířka stopy vgSetf(VG_STROKE_LINE_WIDTH, …) float (šířka v pixelech)
Tvar zakončení vgSeti(VG_STROKE_CAP_STYLE, …) VG_CAP_BUTT, VG_CAP_ROUND, VG_CAP_SQUARE
Tvar zalomení vgSeti(VG_STROKE_JOIN_STYLE, …) VG_JOIN_MITER, VG_JOIN_BEVEL, VG_JOIN_ROUND

Pokud tedy budeme požadovat stopu kreslenou šířkou deseti pixelů se zakulacenými konci i zalomeními, stačí napsat:

vgSetf(VG_STROKE_LINE_WIDTH, 10);
vgSeti(VG_STROKE_CAP_STYLE,  VG_CAP_ROUND);
vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND);

Funkci drawPolyline, kterou jsme si ukázali v předchozí kapitole, můžeme rozšířit takovým způsobem, aby bylo možné nastavit šířku štětce, způsob vykreslení zakončení křivek a taktéž způsob vykreslení zalomení křivek:

/*
 * Vykresleni polycary slozene ze dvou usesek.
 */
void drawPolyline(int xoffset, int yoffset, VGfloat strokeWidth, VGCapStyle capStyle, VGJoinStyle joinStyle)
{
    /* sirka a styl stetce */
    vgSetf(VG_STROKE_LINE_WIDTH, strokeWidth);
    vgSeti(VG_STROKE_CAP_STYLE, capStyle);
    vgSeti(VG_STROKE_JOIN_STYLE, joinStyle);
 
    /* vykresleni polycary */
    VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO);
    float vertexes2[] = {xoffset+100, yoffset+100, xoffset+200, yoffset+100, xoffset+200, yoffset+200};
    vguPolygon(path, vertexes2, 3, VG_FALSE);
    vgDrawPath(path, VG_STROKE_PATH);
    vgDestroyPath(path);
}

Všechny kombinace stylů zakončení a zalomení si můžeme ihned odzkoušet na dostatečně širokých polyčárách:

/* vykresleni nekolika polycar */
drawPolyline(  0,   0,  1, VG_CAP_BUTT, VG_JOIN_MITER);
 
drawPolyline(  0, 150, 20, VG_CAP_BUTT,   VG_JOIN_MITER);
drawPolyline(150, 150, 20, VG_CAP_ROUND,  VG_JOIN_MITER);
drawPolyline(300, 150, 20, VG_CAP_SQUARE, VG_JOIN_MITER);
 
drawPolyline(  0, 300, 20, VG_CAP_BUTT,   VG_JOIN_ROUND);
drawPolyline(150, 300, 20, VG_CAP_ROUND,  VG_JOIN_ROUND);
drawPolyline(300, 300, 20, VG_CAP_SQUARE, VG_JOIN_ROUND);
 
drawPolyline(  0, 450, 20, VG_CAP_BUTT,   VG_JOIN_BEVEL);
drawPolyline(150, 450, 20, VG_CAP_ROUND,  VG_JOIN_BEVEL);
drawPolyline(300, 450, 20, VG_CAP_SQUARE, VG_JOIN_BEVEL);

10. Úplný zdrojový kód dnešního druhého demonstračního příkladu

Předchozí úryvek kódu byl zakomponován do dnešního druhého demonstračního příkladu, jehož úplný zdrojový kód i příslušný soubor Makefile opět naleznete na GitHubu:

/* OpenVG (nejenom) na Raspberry Pi - paty demonstracni priklad */
 
#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 polycary slozene ze dvou usesek.
 */
void drawPolyline(int xoffset, int yoffset, VGfloat strokeWidth, VGCapStyle capStyle, VGJoinStyle joinStyle)
{
    /* sirka a styl stetce */
    vgSetf(VG_STROKE_LINE_WIDTH, strokeWidth);
    vgSeti(VG_STROKE_CAP_STYLE, capStyle);
    vgSeti(VG_STROKE_JOIN_STYLE, joinStyle);
 
    /* vykresleni polycary */
    VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO);
    float vertexes2[] = {xoffset+100, yoffset+100, xoffset+200, yoffset+100, xoffset+200, yoffset+200};
    vguPolygon(path, vertexes2, 3, VG_FALSE);
    vgDrawPath(path, VG_STROKE_PATH);
    vgDestroyPath(path);
}
 
 
 
/*
 * Vykresleni vyplneneho obdelnika.
 */
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 */
    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 nekolika polycar */
    drawPolyline(  0,   0,  1, VG_CAP_BUTT, VG_JOIN_MITER);
 
    drawPolyline(  0, 150, 20, VG_CAP_BUTT,   VG_JOIN_MITER);
    drawPolyline(150, 150, 20, VG_CAP_ROUND,  VG_JOIN_MITER);
    drawPolyline(300, 150, 20, VG_CAP_SQUARE, VG_JOIN_MITER);
 
    drawPolyline(  0, 300, 20, VG_CAP_BUTT,   VG_JOIN_ROUND);
    drawPolyline(150, 300, 20, VG_CAP_ROUND,  VG_JOIN_ROUND);
    drawPolyline(300, 300, 20, VG_CAP_SQUARE, VG_JOIN_ROUND);
 
    drawPolyline(  0, 450, 20, VG_CAP_BUTT,   VG_JOIN_BEVEL);
    drawPolyline(150, 450, 20, VG_CAP_ROUND,  VG_JOIN_BEVEL);
    drawPolyline(300, 450, 20, VG_CAP_SQUARE, VG_JOIN_BEVEL);
 
    /* 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 použitý pro překlad dnešního prvního demonstračního příkladu vypadá následovně:

# Makefile pro preklad pateho 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=example5
 
# 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. Repositář s demonstračními příklady

Oba dva 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/pre­sentations. V tabulce zobrazené pod tímto odstavcem naleznete na zdrojové kódy oboi zmíněných demonstračních příkladů přímé odkazy:

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.

Poznámka2: nenechte se prosím zmást číslováním – příklady example1 až example3 byly totiž popsány v předchozí části tohoto seriálu.

12. Odkazy na Internetu

  1. EGL quick reference card
    https://www.khronos.org/files/egl-1–4-quick-reference-card.pdf
  2. EGL Reference Pages Index
    https://www.khronos.org/re­gistry/egl/sdk/docs/man/html/in­dexflat.php
  3. Funkce eglInitialize
    https://www.khronos.org/re­gistry/egl/sdk/docs/man/html/e­glInitialize.xhtml
  4. Funkce eglGetDisplay
    https://www.khronos.org/re­gistry/egl/sdk/docs/man/html/e­glGetDisplay.xhtml
  5. Funkce eglGetConfigs
    https://www.khronos.org/re­gistry/egl/sdk/docs/man/html/e­glGetConfigs.xhtml
  6. Funkce eglGetConfigAttrib
    https://www.khronos.org/re­gistry/egl/sdk/docs/man/html/e­glGetConfigAttrib.xhtml
  7. Funkce eglDestroySurface
    https://www.khronos.org/re­gistry/egl/sdk/docs/man/html/e­glDestroySurface.xhtml
  8. Funkce eglDestroyContext
    https://www.khronos.org/re­gistry/egl/sdk/docs/man/html/e­glDestroyContext.xhtml
  9. Funkce eglTerminate
    https://www.khronos.org/re­gistry/egl/sdk/docs/man/html/e­glTerminate.xhtml
  10. Khronos Native Platform Graphics Interface
    https://www.khronos.org/re­gistry/egl/specs/eglspec.1­.4.pdf
  11. Khronos Group
    https://www.khronos.org/
  12. Khronos Group (Wikipedia)
    https://en.wikipedia.org/wi­ki/Khronos_Group
  13. Raspberry Pi VideoCore APIs
    http://elinux.org/Raspberry_Pi_Vi­deoCore_APIs
  14. Programming AudioVideo on the Raspberry Pi GPU
    https://jan.newmarch.name/RPi/in­dex.html
  15. The Standard for Vector Graphics Acceleration
    https://www.khronos.org/openvg/
  16. OpenVG (Wikipedia)
    https://en.wikipedia.org/wiki/OpenVG
  17. OpenVG Quick Reference Card
    https://www.khronos.org/files/openvg-quick-reference-card.pdf
  18. OpenVG on the Raspberry Pi
    http://mindchunk.blogspot­.cz/2012/09/openvg-on-raspberry-pi.html
  19. ShivaVG: open-source ANSI C OpenVG
    http://ivanleben.blogspot­.cz/2007/07/shivavg-open-source-ansi-c-openvg.html
  20. Testbed for exploring OpenVG on the Raspberry Pi
    https://github.com/ajstarks/openvg
  21. 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/
  22. 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/
  23. 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/
  24. 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/
  25. 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/
  26. 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/
  27. Seriál Grafické karty a grafické akcelerátory
    http://www.root.cz/serialy/graficke-karty-a-graficke-akceleratory/
  28. Grafika na osmibitových počítačích firmy Sinclair II
    http://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair-ii/
  29. Xiaolin_Wu's Line Algorithm
    https://en.wikipedia.org/wi­ki/Xiaolin_Wu's_line_algo­rithm
  30. Grafické čipy v osmibitových počítačích Atari
    http://www.root.cz/clanky/graficke-cipy-v-osmibitovych-pocitacich-atari/
  31. Osmibitové počítače Commodore a čip VIC-II
    http://www.root.cz/clanky/osmibitove-pocitace-commodore-a-cip-vic-ii/
  32. Grafika na osmibitových počítačích firmy Apple
    http://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-apple/
  33. Počátky grafiky na PC: grafické karty CGA a Hercules
    http://www.root.cz/clanky/pocatky-grafiky-na-pc-graficke-karty-cga-a-hercules/
  34. Karta EGA: první použitelná barevná grafika na PC
    http://www.root.cz/clanky/karta-ega-prvni-pouzitelna-barevna-grafika-na-pc/
  35. Grafické karty MCGA a VGA
    http://www.root.cz/clanky/graficke-karty-mcga-a-vga/
  36. Grafický subsystém počítačů Amiga
    http://www.root.cz/clanky/graficky-subsystem-pocitacu-amiga/
  37. Grafický subsystém počítačů Amiga II
    http://www.root.cz/clanky/graficky-subsystem-pocitacu-amiga-ii/
  38. Raspberry Pi pages
    https://www.raspberrypi.org/
  39. BCM2835 registers
    http://elinux.org/BCM2835_registers
  40. VideoCore (archiv stránek společnosti Alphamosaic)
    http://web.archive.org/web/20030209213838/www­.alphamosaic.com/videocore/
  41. VideoCore (Wikipedia)
    https://en.wikipedia.org/wi­ki/Videocore
  42. RPi lessons: Lesson 6 Screen01
    http://www.cl.cam.ac.uk/pro­jects/raspberrypi/tutorial­s/os/screen01.html
  43. Raspberry Pi forum: Bare metal
    https://www.raspberrypi.or­g/forums/viewforum.php?f=72
  44. C library for Broadcom BCM 2835 as used in Raspberry Pi
    http://www.airspayce.com/mi­kem/bcm2835/
  45. Raspberry Pi Hardware Components
    http://elinux.org/RPi_Har­dware#Components
  46. (Linux) Framebuffer
    http://wiki.linuxquestion­s.org/wiki/Framebuffer
  47. (Linux) Framebuffer HOWTO
    http://tldp.org/HOWTO/Framebuffer-HOWTO/
  48. Linux framebuffer (Wikipedia)
    https://en.wikipedia.org/wi­ki/Linux_framebuffer
  49. RPi Framebuffer
    http://elinux.org/RPi_Framebuffer
  50. 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/
  51. Zdrojový kód fb.c pro RPI
    https://github.com/jncronin/rpi-boot/blob/master/fb.c
  52. RPiconfig
    http://elinux.org/RPi_config.txt
  53. Mailbox framebuffer interface
    https://github.com/raspbe­rrypi/firmware/wiki/Mailbox-framebuffer-interface
  54. Seriál Grafické formáty
    http://www.root.cz/serialy/graficke-formaty/
Našli jste v článku chybu?
Měšec.cz: Udali ho na nelegální software a přišla Policie

Udali ho na nelegální software a přišla Policie

120na80.cz: Bonbon si schovejte na přistání

Bonbon si schovejte na přistání

120na80.cz: Jak se zbavit nadměrného pocení?

Jak se zbavit nadměrného pocení?

DigiZone.cz: Android TV: s jakým pracuje rozlišením?

Android TV: s jakým pracuje rozlišením?

Vitalia.cz: Největší chyby při podávání vína?

Největší chyby při podávání vína?

Vitalia.cz: Pepsi Cola mění sirup za cukr

Pepsi Cola mění sirup za cukr

Podnikatel.cz: Tahle praktika stála šmejdy přes milion

Tahle praktika stála šmejdy přes milion

Podnikatel.cz: Italské těstoviny nebyly k mání, tak je začal vyrábět

Italské těstoviny nebyly k mání, tak je začal vyrábět

Podnikatel.cz: Prodej na Alibabě? Malí hráči utřou nos

Prodej na Alibabě? Malí hráči utřou nos

Podnikatel.cz: Od baletu k požární ochraně. A jiné rarity

Od baletu k požární ochraně. A jiné rarity

Podnikatel.cz: Profese budoucnosti? Úředník nepřežije

Profese budoucnosti? Úředník nepřežije

120na80.cz: Tipy pro odvodnění organismu

Tipy pro odvodnění organismu

Lupa.cz: Největší pitominy s logem “nyní smart a připojené”

Největší pitominy s logem “nyní smart a připojené”

Vitalia.cz: Klíšťata letos řádí, skvrna se udělá jen někomu

Klíšťata letos řádí, skvrna se udělá jen někomu

Lupa.cz: Tudy proudí váš hlas i data. V zákulisí CETINu

Tudy proudí váš hlas i data. V zákulisí CETINu

DigiZone.cz: Sat novinky: Skylink skončil s kanály ČT

Sat novinky: Skylink skončil s kanály ČT

120na80.cz: I tuto vodu můžete pít

I tuto vodu můžete pít

Měšec.cz: Kurzy platebních karet: vyplatí se platit? (TEST)

Kurzy platebních karet: vyplatí se platit? (TEST)

Root.cz: Bitcoin začal vyplácet jen půlku odměn

Bitcoin začal vyplácet jen půlku odměn

Měšec.cz: Co s reklamací, když e-shop krachuje?

Co s reklamací, když e-shop krachuje?