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
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_* s 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:
/* 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/presentations. 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
- 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/ - EGL quick reference card
https://www.khronos.org/files/egl-1–4-quick-reference-card.pdf - EGL Reference Pages Index
https://www.khronos.org/registry/egl/sdk/docs/man/html/indexflat.php - Funkce eglInitialize
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglInitialize.xhtml - Funkce eglGetDisplay
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglGetDisplay.xhtml - Funkce eglGetConfigs
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglGetConfigs.xhtml - Funkce eglGetConfigAttrib
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglGetConfigAttrib.xhtml - Funkce eglDestroySurface
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglDestroySurface.xhtml - Funkce eglDestroyContext
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglDestroyContext.xhtml - Funkce eglTerminate
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglTerminate.xhtml - Khronos Native Platform Graphics Interface
https://www.khronos.org/registry/egl/specs/eglspec.1.4.pdf - Khronos Group
https://www.khronos.org/ - Khronos Group (Wikipedia)
https://en.wikipedia.org/wiki/Khronos_Group - Raspberry Pi VideoCore APIs
http://elinux.org/Raspberry_Pi_VideoCore_APIs - Programming AudioVideo on the Raspberry Pi GPU
https://jan.newmarch.name/RPi/index.html - The Standard for Vector Graphics Acceleration
https://www.khronos.org/openvg/ - OpenVG (Wikipedia)
https://en.wikipedia.org/wiki/OpenVG - OpenVG Quick Reference Card
https://www.khronos.org/files/openvg-quick-reference-card.pdf - OpenVG on the Raspberry Pi
http://mindchunk.blogspot.cz/2012/09/openvg-on-raspberry-pi.html - ShivaVG: open-source ANSI C OpenVG
http://ivanleben.blogspot.cz/2007/07/shivavg-open-source-ansi-c-openvg.html - Testbed for exploring OpenVG on the Raspberry Pi
https://github.com/ajstarks/openvg - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: knihovna Pygame
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-knihovna-pygame/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: knihovna Pygame prakticky
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-knihovna-pygame-prakticky/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: práce s bitmapami a TrueType fonty
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-prace-s-bitmapami-a-truetype-fonty/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: sprity v knihovně Pygame
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-sprity-v-knihovne-pygame/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: detekce kolize spritů
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-detekce-kolize-spritu/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: transformace rastrových obrázků
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-transformace-rastrovych-obrazku/ - Seriál Grafické karty a grafické akcelerátory
http://www.root.cz/serialy/graficke-karty-a-graficke-akceleratory/ - Grafika na osmibitových počítačích firmy Sinclair II
http://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair-ii/ - Xiaolin_Wu's Line Algorithm
https://en.wikipedia.org/wiki/Xiaolin_Wu's_line_algorithm - Grafické čipy v osmibitových počítačích Atari
http://www.root.cz/clanky/graficke-cipy-v-osmibitovych-pocitacich-atari/ - Osmibitové počítače Commodore a čip VIC-II
http://www.root.cz/clanky/osmibitove-pocitace-commodore-a-cip-vic-ii/ - Grafika na osmibitových počítačích firmy Apple
http://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-apple/ - Počátky grafiky na PC: grafické karty CGA a Hercules
http://www.root.cz/clanky/pocatky-grafiky-na-pc-graficke-karty-cga-a-hercules/ - Karta EGA: první použitelná barevná grafika na PC
http://www.root.cz/clanky/karta-ega-prvni-pouzitelna-barevna-grafika-na-pc/ - Grafické karty MCGA a VGA
http://www.root.cz/clanky/graficke-karty-mcga-a-vga/ - Grafický subsystém počítačů Amiga
http://www.root.cz/clanky/graficky-subsystem-pocitacu-amiga/ - Grafický subsystém počítačů Amiga II
http://www.root.cz/clanky/graficky-subsystem-pocitacu-amiga-ii/ - Raspberry Pi pages
https://www.raspberrypi.org/ - BCM2835 registers
http://elinux.org/BCM2835_registers - VideoCore (archiv stránek společnosti Alphamosaic)
http://web.archive.org/web/20030209213838/www.alphamosaic.com/videocore/ - VideoCore (Wikipedia)
https://en.wikipedia.org/wiki/Videocore - RPi lessons: Lesson 6 Screen01
http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/screen01.html - Raspberry Pi forum: Bare metal
https://www.raspberrypi.org/forums/viewforum.php?f=72 - C library for Broadcom BCM 2835 as used in Raspberry Pi
http://www.airspayce.com/mikem/bcm2835/ - Raspberry Pi Hardware Components
http://elinux.org/RPi_Hardware#Components - (Linux) Framebuffer
http://wiki.linuxquestions.org/wiki/Framebuffer - (Linux) Framebuffer HOWTO
http://tldp.org/HOWTO/Framebuffer-HOWTO/ - Linux framebuffer (Wikipedia)
https://en.wikipedia.org/wiki/Linux_framebuffer - RPi Framebuffer
http://elinux.org/RPi_Framebuffer - HOWTO: Boot your Raspberry Pi into a fullscreen browser kiosk
http://blogs.wcode.org/2013/09/howto-boot-your-raspberry-pi-into-a-fullscreen-browser-kiosk/ - Zdrojový kód fb.c pro RPI
https://github.com/jncronin/rpi-boot/blob/master/fb.c - RPiconfig
http://elinux.org/RPi_config.txt - Mailbox framebuffer interface
https://github.com/raspberrypi/firmware/wiki/Mailbox-framebuffer-interface - Seriál Grafické formáty
http://www.root.cz/serialy/graficke-formaty/