2D grafika s využitím knihovny OpenVG: práce s cestami (dokončení)

14. 7. 2016
Doba čtení: 47 minut

Sdílet

S konceptem „cest“ v knihovně OpenVG jsme se již seznámili. Ještě si však musíme vysvětlit tvorbu na sebe hladce navazujících Bézierových křivek a taktéž tvorbu kružnicových a popř. i eliptických oblouků.

Obsah

1. 2D grafika s využitím knihovny OpenVG (nejenom) na Raspberry Pi: práce s cestami (dokončení)

2. Rekapitulace již popsaných příkladů pro tvorbu a vykreslení cest

3. Kvadratické a kubické Bézierovy křivky v knihovně OpenVG

4. Desátý demonstrační příklad: vykreslení kvadratické Bézierovy křivky

5. Hladké napojení Bézierových křivek

6. Jedenáctý demonstrační příklad: hladké napojení tří kvadratických Bézierových křivek

7. Dvanáctý demonstrační příklad: hladké napojení dvou kubických Bézierových křivek

8. Kruhové a eliptické oblouky

9. Třináctý demonstrační příklad: křivka složená z kruhového oblouku

10. Čtrnáctý demonstrační příklad: eliptické oblouky

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: práce s cestami (dokončení)

V předchozím článku o grafické knihovně OpenVG určené pro tvorbu kvalitní 2D grafiky s případnou podporou GPU (čipu s grafickým akcelerátorem) jsme si nejprve popsali všechny základní funkce používané při práci s takzvanými cestami (paths) a následně jsme si ukázali způsob použití těchto funkcí na čtyřech demonstračních příkladech. Připomeňme si, že cesty jsou tvořeny segmenty, přičemž definice každého segmentu je specifikována příkazem typu VG_LINE_TO, VG_QUAD_TO atd. a samozřejmě taktéž souřadnicemi koncových bodů úseček, řídicích bodů křivek či dalších údajů (poloměr oblouku apod.). Mezi funkce určené pro vytváření, modifikaci i rušení cest patří především funkce pojmenované vgCreatePath(), vgDestroyPath(), vgClearPath(), vgDrawPath() a konečně pak nejsložitější funkce nazvaná vgAppendPathData() sloužící pro přidání dalších segmentů do vytvářené cesty (je totiž nutné si uvědomit, že každá cesta vytvořená pomocí vgCreatePath je zpočátku prázdná).

2. Rekapitulace již popsaných příkladů pro tvorbu a vykreslení cest

V celkovém pořadí šestém demonstračním příkladu jsme si ukázali vykreslení úsečky s využitím cesty specifikované pouze dvěma příkazy: VG_MOVE_TO a VG_LINE_TO. Oba dva zmíněné příkazy je samozřejmě možné použít i pro vykreslení lomené čáry (polyčáry) popř. i několika úseček, které na sebe nemusí navazovat (cesta může obsahovat „skoky“). V případě, že je zapotřebí vytvořit (a popř. i vyplnit) uzavřený tvar, je nutné použít cestu končící příkazem VG_CLOSE_PATH. Tento příkaz spojí naposledy zapamatovaný vrchol s počátečním bodem cesty a pokud je nastavený styl výplně a je povoleno vyplňování uzavřených cest (VG_FILL_PATH) je cesta skutečně vyplněna. Tímto způsobem, který byl minule prakticky ukázán v sedmém demonstračním příkladu, je možné tvořit objekty vyplněné konstantní barvou, gradientním přechodem, vzorkem, poloprůhlednou barvou atd.

Osmý demonstrační příklad popsaný minule byl určen pro ukázku použití příkazů VG_HLINE_TO a VG_VLINE_TO. Jak již názvy těchto příkazů naznačují, jsou určeny pro vykreslení vodorovných či svislých čar. Ve skutečnosti je však možné celý vykreslovaný tvar (cestu) natočit, a to volbou vhodné transformační matice, takže pojem „vodorovná“ a „svislá“ je nutné chápat pouze v rámci lokálního souřadného systému vytvořeného pro každou cestu. A konečně jsme se v devátém příkladu seznámili s problematikou tvorby Bézierových křivek. V knihovně OpenVG jsou podporovány jak kvadratické tak i kubické Bézierovy křivky, což je jen dobře, protože kvadratické křivky jsou často používány například při definici fontů zatímco křivky kubické najdeme například v mnoha vektorových grafických editorech a tím pádem i v souborech exportovaných z těchto nástrojů.

3. Kvadratické a kubické Bézierovy křivky v knihovně OpenVG

A u Bézierových křivek se ještě na chvíli zastavíme, protože si musíme vysvětlit jeden poměrně důležitý koncept – způsob navazování jednotlivých křivek. Začneme u kvadratických Bézierových křivek, protože ty jsou v tomto ohledu jednodušší. Připomeňme si, že kvadratické Bézierovy křivky jsou specifikovány trojicí řídicích bodů, přičemž křivka obecně prochází pouze svým prvním a posledním bodem (jedná se o takzvané kotvicí body) a prostřední bod „pouze“ ovlivňuje tvar křivky (tvar a velikost oblouku). Ve speciálních případech však křivka může procházet i prostředním řídicím bodem, například tehdy, když všechny tři body leží na jedné přímce nebo jsou dva či dokonce všechny tři body totožné. Pokud se má do vytvářené cesty přidat kvadratická Bézierova křivka, použije se příkaz VG_QUAD_TO_ABS a VG_QUAD_TO_REL. Navíc se pochopitelně musí do pole vrcholů přidat souřadnice řídicích bodů této křivky, ovšem ve skutečnosti se musí specifikovat jen dva řídicí body – prostřední a koncový – a to proto, že počáteční bod odpovídá naposledy definovanému bodu na cestě.

Obrázek 1: Řídicí body kvadratických a kubických Bézierových křivek.

V praxi může cesta složená ze dvou kvadratických Bézierových křivek vypadat následovně:

Obrázek 2: Cesta složená ze dvou kvadratických Bézierových křivek.

Popis jednotlivých vrcholů na obrázku:

Barva Význam
šedá vrchol nastavený předchozím příkazem, například VG_MOVE_TO
červená vrcholy první kvadratické Bézierovy křivky
modrá vrcholy druhé kvadratické Bézierovy křivky

Povšimněte si, že kromě společného vrcholu není tvar obou Bézierových křivek vytvořených příkazy VG_QUAD_TO_* nijak omezen – cesta se může v místě napojení lámat atd.

4. Desátý demonstrační příklad: vykreslení kvadratické Bézierovy křivky

Ještě předtím, než si ukážeme, jak je možné automaticky hladce spojit dvě Bézierovy křivky, se seznámíme se způsobem vykreslení jediné kvadratické Bézierovy křivky. To je ve skutečnosti velmi jednoduché a postačí nám cesta s pouhými dvěma příkazy: VG_MOVE_TO* a VG_QUAD_TO*:

/* vykresleni druhe cesty slozene z kvadraticke Bezierovy krivky */
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_QUAD_TO_REL};       /* Bezierova kvadraticka krivka */

Pole obsahující souřadnice vrcholů musí mít šest prvků, což odpovídá třem bodům: první bod je pro příkaz VG_MOVE_TO_ABS a druhé dva body pro příkaz VG_QUAD_TO_REL (řídicí bod a koncový či kotvicí bod):

VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1,
                         150, 200,           /* ridici bod Bezierovy krivky */
                         300, 000};          /* koncovy bod Bezierovy krivky */
 
/* pridani vsech segmentu */
vgAppendPathData(path, 2, commands, coordinates);

Nakonec vykreslíme obrys cesty:

/* Bezierova krivka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */
vgDrawPath(path, VG_STROKE_PATH);
vgDestroyPath(path);

Úplný zdrojový kód upravený pro Raspberry Pi vypadá následovně:

/* OpenVG (nejenom) na Raspberry Pi - desaty demonstracni priklad */
/* Kvadraticka Bezierova krivka. */
 
#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 prvni 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,        /* prvni useckovy segment */
                              VG_LINE_TO_REL,
                              VG_LINE_TO_REL};       /* dalsi useckove segmenty */
 
        VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1,
                                 150,  200,
                                 150, -200};
 
        /* pridani vsech useckovych 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 druhe cesty slozene z kvadraticke Bezierovy krivky */
        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_QUAD_TO_REL};       /* Bezierova kvadraticka krivka */
 
        VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1,
                                 150, 200,           /* ridici bod Bezierovy krivky */
                                 300, 000};          /* koncovy bod Bezierovy krivky */
 
        /* pridani vsech segmentu */
        vgAppendPathData(path, 2, commands, coordinates);
 
        /* Bezierova krivka 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:

# Makefile pro preklad desateho 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=example10
 
# 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 $@

5. Hladké napojení Bézierových křivek

To však není všechno. V některých situacích potřebujeme, aby na sebe Bézierovy křivky hladce navazovaly s geometrickou spojitostí G1, což je použito například v definicích tvaru znaků ve vektorových či obrysových fontech. Knihovna OpenVG tuto vlastnost podporuje, což vlastně není nic překvapivého, neboť se jedná o vlastnost převzatou z grafického formátu SVG, s nímž musí být knihovna OpenVG co nejvíce kompatibilní. V případě kvadratické Bézierovy křivky se pro její hladké navázání na předchozí segment používají příkazy VG_SQUAD_TO_ABS a VG_SQUAD_TO_REL, u nichž se specifikuje pouze jediný vrchol, kterým je koncový bod křivky. Její začátek samozřejmě známe a jediný řídicí body je vypočten z pozice řídicího bodu předchozí křivky (podobně je tomu v TrueType fontech). Interně si knihovna OpenVG při tvorbě cesty udržuje následující informace: poslední bod předchozího segmentu [ox, oy] a poslední interní řídicí bod předchozího segmentu [px, py]. V případě použití příkazů VG_SQUAD_TO_* se prostřední řídicí bod kvadratické Bézierovy křivky vypočte následovně:

[x,y]=[2*ox-px, 2*oy-py]

Tatáž hodnota (dvě souřadnice) se následně uloží do interní proměnné [px, py], takže je možné na sebe navázat větší množství křivek. První křivka je zadána příkazem VG_QUAD_TO_*, další pak příkazy VG_SQUAD_TO_*; ovšem v případě potřeby lze použít i kombinaci kvadratických a kubických Bézierových křivek.

Dvě na sebe hladce navazující segmenty vytvořené z Bézierových kvadratických křivek:

Obrázek 3: Cesta složená ze dvou hladce navázaných kvadratických Bézierových křivek.

Popis jednotlivých vrcholů na obrázku:

Barva Význam
šedá vrchol nastavený předchozím příkazem, například VG_MOVE_TO
červená vrcholy první kvadratické Bézierovy křivky
modrá explicitně zadaný vrchol druhé kvadratické Bézierovy křivky
zelená automaticky dopočtený vrchol druhé kvadratické Bézierovy křivky (není explicitně zadán)

Poznámka: u úsečkových segmentů se interně zapamatované body [ox, oy] a [px, py] nastaví na stejné hodnoty (tím jsou souřadnice koncového bodu úsečky), což je škoda, protože to znamená, že nelze snadno zkombinovat příkazy VG_LINE_TO_*VG_SQUAD_TO_*. Totéž platí i pro oblouky a navíc i pro příkaz VG_CLOSE_PATH, který je možné považovat za speciální případ úsečkového segmentu.

6. Jedenáctý demonstrační příklad: hladké napojení tří kvadratických Bézierových křivek

Podívejme se, jak lze hladce napojit tři kvadratické Bézierovy křivky a vytvořit tak na obrazovce „vlny“. Nejprve zkonstruujeme prázdnou cestu:

/* vykresleni druhe cesty slozene z kvadratickych Bezierovych krivek */
VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO);

Dále vytvoříme pole, v němž jsou uloženy příkazy pro vytvoření cesty složené z úplně zadané Bézierovy křivky a dvou křivek s hladkým napojením (specifikovaným jen jediným bodem):

/* deklarace useckoveho segmentu */
VGubyte commands[] = {VG_MOVE_TO_ABS,        /* pocatecni bod */
                      VG_QUAD_TO_REL,        /* Bezierova kvadraticka krivka */
                      VG_SQUAD_TO_REL,       /* Bezierova kvadraticka krivka s hladkym napojenim */
                      VG_SQUAD_TO_REL};      /* Bezierova kvadraticka krivka s hladkym napojenim */

Pole s vrcholy musí v tomto případě obsahovat deset hodnot odpovídajících pěti bodům: začátek cesty, dva body pro první Bézierovu křivku, jediný bod pro druhou Bézierovu křivku a konečně poslední bod pro třetí Bézierovu křivku:

VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1,
                         150, 200,           /* ridici bod Bezierovy krivky */
                         300, 000,           /* koncovy bod Bezierovy krivky */
                         300, 000,           /* koncovy bod druhe Bezierovy krivky s hladkym napojenim */
                         300, 000};          /* koncovy bod treti Bezierovy krivky s hladkym napojenim */
 
/* pridani vsech segmentu */
vgAppendPathData(path, 4, commands, coordinates);

Způsob vykreslení již dobře známe:

/* Bezierova krivka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */
vgDrawPath(path, VG_STROKE_PATH);
vgDestroyPath(path);

Úplný zdrojový kód celého demonstračního příkladu upravený pro Raspberry Pi vypadá následovně:

/* OpenVG (nejenom) na Raspberry Pi - jedenacty demonstracni priklad */
/* Hladke napojeni tri kvadratickych Bezierovych 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 prvni 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,        /* prvni useckovy segment */
                              VG_LINE_TO_REL,
                              VG_LINE_TO_REL};       /* dalsi useckove segmenty */
 
        VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1,
                                 150,  200,
                                 150, -200};
 
        /* pridani vsech useckovych 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 druhe cesty slozene z kvadratickych 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_QUAD_TO_REL,        /* Bezierova kvadraticka krivka */
                              VG_SQUAD_TO_REL,       /* Bezierova kvadraticka krivka s hladkym napojenim */
                              VG_SQUAD_TO_REL};      /* Bezierova kvadraticka krivka s hladkym napojenim */
 
        VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1,
                                 150, 200,           /* ridici bod Bezierovy krivky */
                                 300, 000,           /* koncovy bod Bezierovy krivky */
                                 300, 000,           /* koncovy bod druhe Bezierovy krivky s hladkym napojenim */
                                 300, 000};          /* koncovy bod treti Bezierovy krivky s hladkym napojenim */
 
        /* pridani vsech segmentu */
        vgAppendPathData(path, 4, commands, coordinates);
 
        /* Bezierova krivka 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;
}

7. Dvanáctý demonstrační příklad: hladké napojení dvou kubických Bézierových křivek

U kvadratických Bézierových křivek, které se na sebe hladce napojovaly příkazem VG_SQUAD_TO_* se nikdy nezadávaly prostřední řídicí body, protože ty byly automaticky dopočteny. U hladkého napojení kubických Bézierových křivek je tomu poněkud jinak, a to z toho důvodu, že musíme pracovat se dvěma „prostředními“ řídicími body. První z těchto bodů je spočten stejným způsobem, jako tomu bylo u kvadratických křivek, ovšem druhý bod je nutné určit explicitně. Výhodou je větší flexibilita a obecně menší množství křivek nutných pro vytvoření požadovaného tvaru. Zkusme si tedy vytvořit cestu, v níž se zobrazí dvě hladce napojené kubické Bézierovy křivky:

/* vykresleni druhe cesty slozene z kubickychBezierovych krivek */
VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO);

Následuje deklarace pole s trojicí příkazů pro tvorbu cesty. Povšimněte si, že hladké napojení kubických křivek zajišťuje příkaz VG_SCUBIC_TO_*, kde písmeno „s“ znamená „smooth“:

/* deklarace useckoveho segmentu a dvou krivek */
VGubyte commands[] = {VG_MOVE_TO_ABS,        /* pocatecni bod */
                      VG_CUBIC_TO_REL,       /* Bezierova kubicka krivka */
                      VG_SCUBIC_TO_REL};     /* Bezierova kubicka krivka s hladkym napojenim */

Počet vrcholů musí být roven šesti (dvanáct prvků pole), přičemž jen poslední dva vrcholy platí pro druhou Bézierovu křivku:

VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1,
                         100, 200,           /* prvni ridici bod Bezierovy krivky */
                         200, 200,           /* druhy ridici bod Bezierovy krivky */
                         300, 000,           /* koncovy bod prvni Bezierovy krivky */
                         200, 300,           /* ridici bod druhe Bezierovy krivky s hladkym napojenim */
                         300, 000};          /* koncovy bod druhe Bezierovy krivky s hladkym napojenim */
 
/* pridani vsech segmentu */
vgAppendPathData(path, 3, commands, coordinates);

Způsob vykreslení cesty již dobře známe z předchozích dvou demonstračních příkladů:

/* Bezierova krivka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */
vgDrawPath(path, VG_STROKE_PATH);
vgDestroyPath(path);

Opět si ukažme úplný kód tohoto příkladu odladěného pro Raspberry Pi:

/* OpenVG (nejenom) na Raspberry Pi - dvanacty demonstracni priklad */
/* Hladke napojeni dvou kubickych Bezierovych 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 prvni 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,        /* prvni useckovy segment */
                              VG_LINE_TO_REL,
                              VG_LINE_TO_REL,
                              VG_LINE_TO_REL};       /* dalsi useckove segmenty */
 
        VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1,
                                 100,  200,
                                 100,    0,
                                 100, -200};
 
        /* pridani vsech useckovych segmentu */
        vgAppendPathData(path, 5, 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 druhe cesty slozene z kubickychBezierovych 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 */
                              VG_SCUBIC_TO_REL};     /* Bezierova kubicka krivka s hladkym napojenim */
 
        VGfloat coordinates[] = {(state->window_width >> 1) - 400, state->window_height >> 1,
                                 100, 200,           /* prvni ridici bod Bezierovy krivky */
                                 200, 200,           /* druhy ridici bod Bezierovy krivky */
                                 300, 000,           /* koncovy bod prvni Bezierovy krivky */
                                 200, 300,           /* ridici bod druhe Bezierovy krivky s hladkym napojenim */
                                 300, 000};          /* koncovy bod druhe Bezierovy krivky s hladkym napojenim */
 
        /* pridani vsech segmentu */
        vgAppendPathData(path, 3, commands, coordinates);
 
        /* Bezierova krivka 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;
}

8. Kruhové a eliptické oblouky

Jedním z problémů, které musí řešit především uživatelé CAD nástrojů, je fakt, že kvadratické ani kubické Bézierovy křivky nemohou geometricky přesně nahradit kruhové a eliptické oblouky (k tomuto účelu by bylo nutné použít racionální křivky, například NURBS, se všemi komplikacemi, které to přináší). Z tohoto důvodu byly v knihovně OpenVG do repertoáru příkazů určených pro vytváření cest přidány i příkazy sloužící pro přidání segmentu vytvořeného z eliptického oblouku do konstruované cesty. Kruhový oblouk je samozřejmě speciálním případem eliptického oblouku, stejně jako elipsa či kruh – pro vytvoření všech těchto tvarů si tedy vystačíme s pouhými čtyřmi příkazy, které se od sebe odlišují v tom, zda se vykreslí delší či kratší část oblouku a zda je oblouk specifikován po směru či proti směru hodinových ručiček (ve skutečnosti není možné jedním segmentem vykreslit přesný kruh či kružnici, jak si ostatně ukážeme dále).

Příkaz Parametry Význam
VG_SCCWARC_TO rh,rv,rot,x0,y0 menší oblouk, proti směru hodinových ručiček
VG_SCWARC_TO rh,rv,rot,x0,y0 menší oblouk, po směru hodinových ručiček
VG_LCCWARC_TO rh,rv,rot,x0,y0 větší oblouk, proti směru hodinových ručiček
VG_LCWARC_TO rh,rv,rot,x0,y0 větší oblouk, po směru hodinových ručiček

Význam parametrů lze zjistit z nákresu:

Obrázek 4: Čtyři možné eliptické oblouky při znalosti dvou bodů, dvou poloměrů (a zde neuvedené rotace celé elipsy).

Pokud jsou oba poloměry rh a rv shodné, vykreslí se kruhový oblouk. Třetí parametr rot může být použit pro natočení oblouku libovolným směrem (hodnota je zadána ve stupních, nikoli v radiánech, což je praktické, zejména tehdy, pokud pole vrcholů obsahuje celočíselné hodnoty). Hodnoty x0 a y0 představují koncový bod oblouku, protože počáteční bod je znám, podobně jako u Bézierových křivek.

9. Třináctý demonstrační příklad: křivka složená z kruhového oblouku

V tomto demonstračním příkladu jsou vykresleny dva kruhové oblouky. První oblouk je „kratší“, což určuje jeho natočení, protože oba koncové body oblouku leží na stejné vodorovné přímce. Poloměry jsou stejné, takže se vykreslí kruhový oblouk (zde konkrétně polovina kružnice):

/* 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 oblouku - polokruhu */
VGubyte commands[] = {VG_MOVE_TO_ABS,        /* pocatecni bod */
                      VG_SCCWARC_TO_REL};    /* oblouk */
 
VGfloat coordinates[] = {(state->window_width >> 1) - 300, state->window_height >> 1,
                         100,  100,          /* polomery */
                         0,                  /* rotace */
                         200,    0};         /* koncovy bod (relativni souradnice) */
 
/* 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);

Druhý oblouk je vytvořen tak, aby se vykreslila prakticky celá kružnice, protože oba krajní body jsou od sebe vodorovně vzdáleny pouze o jednu jednotku délky (typicky jeden pixel). Body nemohou být shodné, takže by bylo nutné cestu uzavřít příkazem VG_CLOSE_PATH:

/* vykresleni druhe cesty - "kruznice" */
VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO);
 
/* deklarace oblouku */
VGubyte commands[] = {VG_MOVE_TO_ABS,        /* pocatecni bod */
                      VG_LCCWARC_TO_REL};    /* oblouk */
 
VGfloat coordinates[] = {(state->window_width >> 1) + 300, state->window_height >> 1,
                         100,  100,          /* polomery */
                          0,                 /* rotace */
                          1,     0};         /* koncovy bod (relativni souradnice) */
 
/* pridani vsech segmentu segmentu */
vgAppendPathData(path, 2, commands, coordinates);
 
/* usecka nema "vnitrek", proto se pouzije pouze VG_STROKE_PATH */
vgDrawPath(path, VG_STROKE_PATH);
vgDestroyPath(path);

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

/* OpenVG (nejenom) na Raspberry Pi - trinacty demonstracni priklad */
/* Krivka slozena z oblouku. */
 
#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 cesty */
    {
        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 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 oblouku - polokruhu */
        VGubyte commands[] = {VG_MOVE_TO_ABS,        /* pocatecni bod */
                              VG_SCCWARC_TO_REL};    /* oblouk */
 
        VGfloat coordinates[] = {(state->window_width >> 1) - 300, state->window_height >> 1,
                                 100,  100,          /* polomery */
                                 0,                  /* rotace */
                                 200,    0};         /* koncovy bod (relativni souradnice) */
 
        /* 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);
    }
 
    /* 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 druhe 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);
    }
 
    {
        /* vykresleni druhe cesty - "kruznice" */
        VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO);
 
        /* deklarace oblouku */
        VGubyte commands[] = {VG_MOVE_TO_ABS,        /* pocatecni bod */
                              VG_LCCWARC_TO_REL};    /* oblouk */
 
        VGfloat coordinates[] = {(state->window_width >> 1) + 300, state->window_height >> 1,
                                 100,  100,          /* polomery */
                                  0,                 /* rotace */
                                  1,     0};         /* koncovy bod (relativni souradnice) */
 
        /* pridani vsech segmentu 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;
}

10. Čtrnáctý demonstrační příklad: eliptické oblouky

V dnešním posledním příkladu je ukázána práce s eliptickými oblouky. Ty se tvoří, jak jste již zajisté uhodli, zadáním rozdílných poloměrů ve směru horizontální a vertikální osy. My v jediné cestě vykreslíme úplnou elipsu složenou ze dvou prakticky shodných (jen zrcadlově otočených) eliptických oblouků:

/* deklarace oblouku - poloelipsy */
VGubyte commands[] = {VG_MOVE_TO_ABS,        /* pocatecni bod */
                      VG_LCCWARC_TO_REL,     /* oblouk */
                      VG_LCCWARC_TO_REL};    /* oblouk */
 
VGfloat coordinates[] = {(state->window_width >> 1) - 300, state->window_height >> 1,
                         /* prvni elipticky oblouk */
                         100,   60,          /* polomery */
                         0,                  /* rotace */
                         200,    0,          /* koncovy bod (relativni souradnice) */
                         /* druhu elipticky oblouk */
                         100,   60,          /* polomery */
                         0,                  /* rotace */
                         -200,   0};         /* koncovy bod (relativni souradnice) */

Následuje úplný zdrojový kód posledního demonstračního příkladu:

bitcoin_skoleni

/* OpenVG (nejenom) na Raspberry Pi - ctrnacty demonstracni priklad */
/* Elipticke oblouky. */
 
#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 cesty */
    {
        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 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 oblouku - polokruhu */
        VGubyte commands[] = {VG_MOVE_TO_ABS,        /* pocatecni bod */
                              VG_LCCWARC_TO_REL,     /* oblouk */
                              VG_LCCWARC_TO_REL};    /* oblouk */
 
        VGfloat coordinates[] = {(state->window_width >> 1) - 300, state->window_height >> 1,
                                 /* prvni elipticky oblouk */
                                 100,   60,          /* polomery */
                                 0,                  /* rotace */
                                 200,    0,          /* koncovy bod (relativni souradnice) */
                                 /* druhu elipticky oblouk */
                                 100,   60,          /* polomery */
                                 0,                  /* rotace */
                                 -200,   0};         /* koncovy bod (relativni souradnice) */
 
        /* pridani useckoveho segmentu */
        vgAppendPathData(path, 3, 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, 1);
    vgSeti(VG_STROKE_CAP_STYLE, VG_CAP_ROUND);
    vgSeti(VG_STROKE_JOIN_STYLE, VG_JOIN_ROUND);
 
    /* barva stetce druhe 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);
    }
 
    {
        /* vykresleni druhe cesty - "kruznice" */
        VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_APPEND_TO);
 
        /* deklarace oblouku */
        VGubyte commands[] = {VG_MOVE_TO_ABS,        /* pocatecni bod */
                              VG_LCCWARC_TO_REL,     /* oblouk */
                              VG_LCCWARC_TO_REL};    /* oblouk */
 
        VGfloat coordinates[] = {(state->window_width >> 1) + 300, state->window_height >> 1,
                                 /* prvni elipticky oblouk */
                                 60,   100,          /* polomery */
                                 0,                  /* rotace */
                                 200,    0,          /* koncovy bod (relativni souradnice) */
                                 /* druhu elipticky oblouk */
                                 60,   100,          /* polomery */
                                 0,                  /* rotace */
                                 -200,   0};         /* koncovy bod (relativni souradnice) */
 
        /* pridani vsech segmentu segmentu */
        vgAppendPathData(path, 3, 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;
}

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

Všech pět demonstračních příkladů, které jsme si v dnešním článku popsali, bylo uloženo 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 všech pěti 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 (otestovaný na Raspberry Pi).

12. Odkazy na Internetu

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

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.