1. Pohled pod kapotu JVM – kreslicí funkce dostupné ve třídě sdljavax.gfx.SDLGfx (2.část)
V předchozí části tohoto seriálu jsme si ukázali, jakým způsobem je možné využít funkce (tj. veřejné statické metody) nabízené třídou sdljavax.gfx.SDLGfx pro vykreslování základních grafických entit do vybrané plochy typu SDL_Surface, tj. mj. i do předního či zadního bufferu. Připomeňme si, že jsme si na pětici demonstračních příkladů ukázali využití funkcí sdljavax.gfx.SDLGfx.pixelRGBA(), sdljavax.gfx.SDLGfx.pixelColor(), sdljavax.gfx.SDLGfx.lineColor(), sdljavax.gfx.SDLGfx.lineRGBA(), sdljavax.gfx.SDLGfx.aalineColor() a konečně sdljavax.gfx.SDLGfx.aalineRGBA(). V knihovně SDLJava ovšem můžeme najít i mnoho dalších funkcí využitelných při kreslení složitějších grafických entit.
Obrázek 1: Vzorek vykreslený s využitím funkce pixelRGBA().
Dnes si ukážeme, jakým způsobem je možné vykreslovat kružnice, kruhy, trojúhelníky, ale i obecné polygony či Bézierovy křivky využívané v mnoha vektorových grafických editorech i například v PostScriptu či ve vektorovém formátu SVG. Vykreslování pixelů, úseček, kružnic či trojúhelníků vyžaduje předání konstantního počtu parametrů příslušným funkcím, ovšem v případě obecných polygonů a Bézierových křivek je situace poněkud složitější, neboť těmto funkcím se předává větší (proměnný) počet vrcholů. V knihovně SDLJava se tento problém řeší na první pohled poněkud nešikovným způsobem – použitím možností nabízených třídou java.nio.ShortBuffer. Podrobnosti si popíšeme v závěrečných kapitolách dnešního článku.
Obrázek 2: Vzorek vykreslený s využitím funkce aalineRGBA().
2. Kreslení kružnic a kruhů
Pro kreslení kružnic popř. kruhů je možné využít osm funkcí, které jsou vypsány v následující tabulce:
# | Název funkce | Parametry | Stručný popis |
---|---|---|---|
1 | circleColor | SDLSurface dst, int x, int y, int r, long color | kružnice |
2 | circleRGBA | SDLSurface dst, int x, int y, int rad, int r, int g, int b, int a | kružnice |
3 | aacircleColor | SDLSurface dst, int x, int y, int r, long color | kružnice s vyhlazeným okrajem |
4 | aacircleRGBA | SDLSurface dst, int x, int y, int rad, int r, int g, int b, int a | kružnice s vyhlazeným okrajem |
5 | filledCircleColor | SDLSurface dst, int x, int y, int r, long color | kruh |
6 | filledCircleRGBA | SDLSurface dst, int x, int y, int rad, int r, int g, int b, int a | kruh |
Povšimněte si, že první čtyři parametry těchto funkcí jsou vždy shodné – jedná se o objekt představující plochu, do níž se má kružnice či kruh vykreslit, souřadnice středu této grafické entity a konečně poloměr kruhu popř. kružnice. Poté již následuje určení barvy, která může být specifikována buď jediným 32bitovým číslem ve formátu 0×rrggbbaa, nebo alternativně čtveřicí osmibitových celočíselných parametrů představujících hodnoty barvových složek red, green, blue následované průhledností (alpha).
Určení barvy použitím 32bitové hodnoty je využito ve funkcích circleColor(), aacircleColor() a filledCircleColor(), zatímco trojice barvových složek a hodnota průhlednosti je použita ve funkcích circleRGBA(), aacircleRGBA() a filledCircleRGBA(). Kružnice lze vykreslit buď rychlým celočíselným Brehsemanovým algoritmem (funkce circleColor() a circleRGBA()) nebo naopak pomalejším algoritmem s antialiasingem (funkce aacircleColor() a aacircleRGBA()). Při kresbě kruhů není antialiasing (kupodivu) použit.
Obrázek 3: Čtyřikrát zvětšený detail obrazovky s kružnicemi vykreslenými rychlým Bresenhamovým celočíselným algoritmem.
Obrázek 4: Čtyřikrát zvětšený detail obrazovky s kružnicemi vykreslenými algoritmem s antialiasingem.
3. Demonstrační příklad SDLTest52: vykreslení jednoduchého vzorku s využitím funkce circleColor()
Dnešní první demonstrační příklad nazvaný SDLTest52 využívá funkci circleColor() pro vykreslení vzorku složeného z většího množství poloprůhledných kružnic se shodnou barvou. Barva je nejdříve uložena do čtveřice konstant pojmenovaných red, green, blue a alpha, z nichž je následně vypočtena 32bitová hodnota uložená do konstanty pojmenované color. Tato konstanta je následně použita v programové smyčce, v níž je celý vzorek vykreslen přímo do framebufferu a tedy i na obrazovku:
// vypocet barvovych slozek pixelu final int red = 0xff; final int green = 0xff; final int blue = 0xff; final int alpha = 0x80; // vypocet barvy final int color = (red << 24) | (green << 16) | (blue << 8) | alpha; // vykreslit vzorek s vyuzitim funkce circleColor() for (int i = 0; i < 90; i++) { final int radius = 90 - i; final int x = (width >> 1) + (int)((i * 2 + 70) * Math.cos(i/10.0)); final int y = (height >> 1) + (int)((i * 2 + 70) * Math.sin(i/10.0)); circleColor(screen, x, y, radius, color); }
Obrázek 5: Vzorek vykreslený demonstračním příkladem SDLTest52.
Obrázek 6: Detail, na němž je patrný výpočet průhlednosti a překryvu kružnic.
Následuje výpis zdrojového kódu tohoto demonstračního příkladu:
import sdljava.SDLMain; import sdljava.SDLException; import sdljava.event.SDLEvent; import sdljava.event.SDLKeyboardEvent; import sdljava.event.SDLKey; import sdljava.event.SDLQuitEvent; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; import sdljava.x.swig.SDLPressedState; import static sdljavax.gfx.SDLGfx.*; /** * Padesaty druhy demonstracni priklad vyuzivajici knihovnu SDLjava. * * Vykresleni jednoducheho vzorku s vyuzitim funkce circleColor(). * * @author Pavel Tisnovsky */ public class SDLTest52 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_WIDTH = 480; /** * Vertikalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_HEIGHT = 480; /** * Bitova hloubka vybraneho grafickeho rezimu. * (0 znamena automaticky vyber, ovsem lze samozrejme pouzit * i hodnoty 8, 16, 24 ci 32, podle vlastnosti graficke karty) */ private static final int GFX_BPP = 0; /** * Inicializace grafickeho rezimu ci otevreni okna pro vykreslovani. */ private static SDLSurface initVideo() throws SDLException { final long flags = SDLVideo.SDL_DOUBLEBUF; return SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, flags); } /** * Smycka pro zpracovani udalosti. */ private static void eventLoop() throws SDLException { while (true) { // precist udalost z fronty SDLEvent event = SDLEvent.waitEvent(); // vyskok ze smycky pro zpracovani udalosti pri vyskytu // udalosti typu SDLQuitEvent if (event instanceof SDLQuitEvent) { return; } // stisk ci pusteni klavesy if (event instanceof SDLKeyboardEvent) { // pretypovani final SDLKeyboardEvent keyEvent = (SDLKeyboardEvent)event; // symbol/kod klavesy final int symbol = keyEvent.getSym(); // ESC ukonci program if (symbol == SDLKey.SDLK_ESCAPE && keyEvent.getState() == SDLPressedState.PRESSED) { return; } } } } /** * Vykresleni sceny na obrazovku ci do okna aplikace. * * @param screen * framebuffer */ private static void drawOnScreen(SDLSurface screen) throws SDLException { // rozmery okna aplikace ci rozliseni obrazovky final int width = screen.getWidth(); final int height = screen.getHeight(); // vypocet barvovych slozek pixelu final int red = 0xff; final int green = 0xff; final int blue = 0xff; final int alpha = 0x80; // vypocet barvy final int color = (red << 24) | (green << 16) | (blue << 8) | alpha; // vykreslit vzorek s vyuzitim funkce circleColor() for (int i = 0; i < 90; i++) { final int radius = 90 - i; final int x = (width >> 1) + (int)((i * 2 + 70) * Math.cos(i/10.0)); final int y = (height >> 1) + (int)((i * 2 + 70) * Math.sin(i/10.0)); circleColor(screen, x, y, radius, color); } // nutno volat i v pripade, ze neni pouzit double buffering screen.updateRect(); screen.flip(); } /** * Spusteni padesateho druheho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace knihovny SDLJava SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace grafickeho rezimu ci otevreni okna pro vykreslovani final SDLSurface screen = initVideo(); // vykresleni sceny na obrazovku drawOnScreen(screen); // smycka pro zpracovani udalosti eventLoop(); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } } }
Skript pro překlad tohoto demonstračního příkladu na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest52.java
Dávkový soubor pro překlad tohoto demonstračního příkladu na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest52.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest52
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest52
4. Demonstrační příklad SDLTest53: vykreslení jednoduchého vzorku s využitím funkce circleRGBA()
Při vykreslování kružnic (či jiných grafických entit), u nichž se programově mění jejich barva popř. průhlednost, je výhodnější namísto 32bitové hodnoty předávané do funkcí *Color() použít čtveřici hodnot předávaných do funkcí *RGBA(). V tomto případě ani není nutné kontrolovat, zda každá hodnota leží v předpokládaném rozsahu 0 až 255. Právě funkce circleRGBA() je použita v dnešním druhém demonstračním příkladu pojmenovaném SDLTest53 jehož vykreslovací „jádro“ vypadá následovně:
// vypocet barvovych slozek pixelu int red = 0xff; int green = 0xff; int blue = 0x00; final int alpha = 0x80; // vykreslit vzorek s vyuzitim funkce circleRGBA() for (int i = 0; i < 90; i++) { final int radius = 90 - i; final int x = (width >> 1) + (int)((i * 2 + 70) * Math.cos(i/10.0)); final int y = (height >> 1) + (int)((i * 2 + 70) * Math.sin(i/10.0)); circleRGBA(screen, x, y, radius, red, green, blue, alpha); red-=2; blue+=2; }
Obrázek 7: Vzorek vykreslený demonstračním příkladem SDLTest53.
Obrázek 8: Detail, na němž je patrný výpočet průhlednosti a překryvu kružnic.
Celý zdrojový kód demonstračního příkladu SDLTest53:
import sdljava.SDLMain; import sdljava.SDLException; import sdljava.event.SDLEvent; import sdljava.event.SDLKeyboardEvent; import sdljava.event.SDLKey; import sdljava.event.SDLQuitEvent; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; import sdljava.x.swig.SDLPressedState; import static sdljavax.gfx.SDLGfx.*; /** * Padesaty treti demonstracni priklad vyuzivajici knihovnu SDLjava. * * Vykresleni jednoducheho vzorku s vyuzitim funkce circleRGBA(). * * @author Pavel Tisnovsky */ public class SDLTest53 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_WIDTH = 480; /** * Vertikalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_HEIGHT = 480; /** * Bitova hloubka vybraneho grafickeho rezimu. * (0 znamena automaticky vyber, ovsem lze samozrejme pouzit * i hodnoty 8, 16, 24 ci 32, podle vlastnosti graficke karty) */ private static final int GFX_BPP = 0; /** * Inicializace grafickeho rezimu ci otevreni okna pro vykreslovani. */ private static SDLSurface initVideo() throws SDLException { final long flags = SDLVideo.SDL_DOUBLEBUF; return SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, flags); } /** * Smycka pro zpracovani udalosti. */ private static void eventLoop() throws SDLException { while (true) { // precist udalost z fronty SDLEvent event = SDLEvent.waitEvent(); // vyskok ze smycky pro zpracovani udalosti pri vyskytu // udalosti typu SDLQuitEvent if (event instanceof SDLQuitEvent) { return; } // stisk ci pusteni klavesy if (event instanceof SDLKeyboardEvent) { // pretypovani final SDLKeyboardEvent keyEvent = (SDLKeyboardEvent)event; // symbol/kod klavesy final int symbol = keyEvent.getSym(); // ESC ukonci program if (symbol == SDLKey.SDLK_ESCAPE && keyEvent.getState() == SDLPressedState.PRESSED) { return; } } } } /** * Vykresleni sceny na obrazovku ci do okna aplikace. * * @param screen * framebuffer */ private static void drawOnScreen(SDLSurface screen) throws SDLException { // rozmery okna aplikace ci rozliseni obrazovky final int width = screen.getWidth(); final int height = screen.getHeight(); // vypocet barvovych slozek pixelu int red = 0xff; int green = 0xff; int blue = 0x00; final int alpha = 0x80; // vykreslit vzorek s vyuzitim funkce circleRGBA() for (int i = 0; i < 90; i++) { final int radius = 90 - i; final int x = (width >> 1) + (int)((i * 2 + 70) * Math.cos(i/10.0)); final int y = (height >> 1) + (int)((i * 2 + 70) * Math.sin(i/10.0)); circleRGBA(screen, x, y, radius, red, green, blue, alpha); red-=2; blue+=2; } // nutno volat i v pripade, ze neni pouzit double buffering screen.updateRect(); screen.flip(); } /** * Spusteni padesateho tretiho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace knihovny SDLJava SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace grafickeho rezimu ci otevreni okna pro vykreslovani final SDLSurface screen = initVideo(); // vykresleni sceny na obrazovku drawOnScreen(screen); // smycka pro zpracovani udalosti eventLoop(); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } } }
Skript pro překlad tohoto demonstračního příkladu na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest53.java
Dávkový soubor pro překlad tohoto demonstračního příkladu na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest53.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest53
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest53
5. Demonstrační příklad SDLTest54: vykreslení jednoduchého vzorku s využitím funkce aacircleColor()
Ve třetím demonstračním příkladu SDLTest54 se opět vykreslují kružnice, tentokrát však již s využitím algoritmu aplikujícího jednoduchý antialiasing, tj. namísto funkcí circleColor() popř. circleRGBA() se používají funkce aacircleColor() popř. aacircleRGBA(). Výsledkem použití vykreslovacího algoritmu s antialiasingem je následující scéna:
Obrázek 9: Vzorek vykreslený demonstračním příkladem SDLTest54.
Obrázek 10: Detail, na němž je vidět funkce algoritmu pro antialiasing.
Podobně jako tomu bylo i v případě úseček, je i zde dobré mít na paměti, že současné použití antialiasingu a poloprůhledných objektů může zapříčinit vznik nežádoucích moaré:
Obrázek 11: Vznik moaré při vykreslení poloprůhledných kružnic.
Obrázek 12: Detail, na němž je vidět funkce algoritmu pro antialiasing.
Zdrojový kód příkladu SDLTest54:
import sdljava.SDLMain; import sdljava.SDLException; import sdljava.event.SDLEvent; import sdljava.event.SDLKeyboardEvent; import sdljava.event.SDLKey; import sdljava.event.SDLQuitEvent; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; import sdljava.x.swig.SDLPressedState; import static sdljavax.gfx.SDLGfx.*; /** * Padesaty ctvrty demonstracni priklad vyuzivajici knihovnu SDLjava. * * Vykresleni jednoducheho vzorku s vyuzitim funkce aacircleColor(). * * @author Pavel Tisnovsky */ public class SDLTest54 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_WIDTH = 480; /** * Vertikalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_HEIGHT = 480; /** * Bitova hloubka vybraneho grafickeho rezimu. * (0 znamena automaticky vyber, ovsem lze samozrejme pouzit * i hodnoty 8, 16, 24 ci 32, podle vlastnosti graficke karty) */ private static final int GFX_BPP = 0; /** * Inicializace grafickeho rezimu ci otevreni okna pro vykreslovani. */ private static SDLSurface initVideo() throws SDLException { final long flags = SDLVideo.SDL_DOUBLEBUF; return SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, flags); } /** * Smycka pro zpracovani udalosti. */ private static void eventLoop() throws SDLException { while (true) { // precist udalost z fronty SDLEvent event = SDLEvent.waitEvent(); // vyskok ze smycky pro zpracovani udalosti pri vyskytu // udalosti typu SDLQuitEvent if (event instanceof SDLQuitEvent) { return; } // stisk ci pusteni klavesy if (event instanceof SDLKeyboardEvent) { // pretypovani final SDLKeyboardEvent keyEvent = (SDLKeyboardEvent)event; // symbol/kod klavesy final int symbol = keyEvent.getSym(); // ESC ukonci program if (symbol == SDLKey.SDLK_ESCAPE && keyEvent.getState() == SDLPressedState.PRESSED) { return; } } } } /** * Vykresleni sceny na obrazovku ci do okna aplikace. * * @param screen * framebuffer */ private static void drawOnScreen(SDLSurface screen) throws SDLException { // rozmery okna aplikace ci rozliseni obrazovky final int width = screen.getWidth(); final int height = screen.getHeight(); // vypocet barvovych slozek pixelu final int red = 0xff; final int green = 0xff; final int blue = 0xff; final int alpha = 0xff; // vypocet barvy final int color = (red << 24) | (green << 16) | (blue << 8) | alpha; // vykreslit vzorek s vyuzitim funkce aacircleColor() for (int i = 0; i < 140; i++) { final int radius = 140 - i; final int x = (width >> 1) + (int)((i + 80) * Math.cos(i/12.0)); final int y = (height >> 1) + (int)((i + 80) * Math.sin(i/12.0)); aacircleColor(screen, x, y, radius, color); } // nutno volat i v pripade, ze neni pouzit double buffering screen.updateRect(); screen.flip(); } /** * Spusteni padesateho ctvrteho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace knihovny SDLJava SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace grafickeho rezimu ci otevreni okna pro vykreslovani final SDLSurface screen = initVideo(); // vykresleni sceny na obrazovku drawOnScreen(screen); // smycka pro zpracovani udalosti eventLoop(); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } } }
Skript pro překlad tohoto demonstračního příkladu na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest54.java
Dávkový soubor pro překlad tohoto demonstračního příkladu na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest54.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest54
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest54
6. Demonstrační příklad SDLTest55: vykreslení jednoduchého vzorku s využitím funkce filledCircleRGBA()
Poslední příklad, v němž se používají funkce pro vykreslení kružnic a kruhů, nese pojmenování SDLTest55. V tomto příkladu se v jediné programové smyčce nejdříve vykreslí barevný kruh a přes něj černá kružnice, která zvýrazní hranice kruhu. Jádro vykreslovacího algoritmu se v mnoha ohledech podobá jádru použitému v předchozí trojici příkladů:
// vykreslit vzorek s vyuzitim funkce filledCircleRGBA() for (int i = 0; i < 140; i++) { final int radius = 140 - i; final int x = (width >> 1) + (int)((i + 80) * Math.cos(i/12.0)); final int y = (height >> 1) + (int)((i + 80) * Math.sin(i/12.0)); filledCircleRGBA(screen, x, y, radius, red, green, blue, alpha); aacircleColor(screen, x, y, radius, 0xff); // cerna nepruhledna barva red-=2; blue+=2; }
Povšimněte si, že černá neprůhledná barva je reprezentována 32bitovou konstantou 0×000000ff.
Obrázek 13: Vzorek vykreslený demonstračním příkladem SDLTest55.
Zdrojový kód příkladu SDLTest55 má tvar:
import sdljava.SDLMain; import sdljava.SDLException; import sdljava.event.SDLEvent; import sdljava.event.SDLKeyboardEvent; import sdljava.event.SDLKey; import sdljava.event.SDLQuitEvent; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; import sdljava.x.swig.SDLPressedState; import static sdljavax.gfx.SDLGfx.*; /** * Padesaty paty demonstracni priklad vyuzivajici knihovnu SDLjava. * * Vykresleni jednoducheho vzorku s vyuzitim funkce filledCircleRGBA(). * * @author Pavel Tisnovsky */ public class SDLTest55 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_WIDTH = 480; /** * Vertikalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_HEIGHT = 480; /** * Bitova hloubka vybraneho grafickeho rezimu. * (0 znamena automaticky vyber, ovsem lze samozrejme pouzit * i hodnoty 8, 16, 24 ci 32, podle vlastnosti graficke karty) */ private static final int GFX_BPP = 0; /** * Inicializace grafickeho rezimu ci otevreni okna pro vykreslovani. */ private static SDLSurface initVideo() throws SDLException { final long flags = SDLVideo.SDL_DOUBLEBUF; return SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, flags); } /** * Smycka pro zpracovani udalosti. */ private static void eventLoop() throws SDLException { while (true) { // precist udalost z fronty SDLEvent event = SDLEvent.waitEvent(); // vyskok ze smycky pro zpracovani udalosti pri vyskytu // udalosti typu SDLQuitEvent if (event instanceof SDLQuitEvent) { return; } // stisk ci pusteni klavesy if (event instanceof SDLKeyboardEvent) { // pretypovani final SDLKeyboardEvent keyEvent = (SDLKeyboardEvent)event; // symbol/kod klavesy final int symbol = keyEvent.getSym(); // ESC ukonci program if (symbol == SDLKey.SDLK_ESCAPE && keyEvent.getState() == SDLPressedState.PRESSED) { return; } } } } /** * Vykresleni sceny na obrazovku ci do okna aplikace. * * @param screen * framebuffer */ private static void drawOnScreen(SDLSurface screen) throws SDLException { // rozmery okna aplikace ci rozliseni obrazovky final int width = screen.getWidth(); final int height = screen.getHeight(); // vypocet barvovych slozek pixelu int red = 0xff; int green = 0xff; int blue = 0x00; final int alpha = 0x10; // vykreslit vzorek s vyuzitim funkce filledCircleRGBA() for (int i = 0; i < 140; i++) { final int radius = 140 - i; final int x = (width >> 1) + (int)((i + 80) * Math.cos(i/12.0)); final int y = (height >> 1) + (int)((i + 80) * Math.sin(i/12.0)); filledCircleRGBA(screen, x, y, radius, red, green, blue, alpha); aacircleColor(screen, x, y, radius, 0xff); // cerna nepruhledna barva red-=2; blue+=2; } // nutno volat i v pripade, ze neni pouzit double buffering screen.updateRect(); screen.flip(); } /** * Spusteni padesateho pateho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace knihovny SDLJava SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace grafickeho rezimu ci otevreni okna pro vykreslovani final SDLSurface screen = initVideo(); // vykresleni sceny na obrazovku drawOnScreen(screen); // smycka pro zpracovani udalosti eventLoop(); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } } }
Skript pro překlad tohoto demonstračního příkladu na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest55.java
Dávkový soubor pro překlad tohoto demonstračního příkladu na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest55.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest55
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest55
7. Kreslení trojúhelníků a obdélníků
Pro kreslení trojúhelníků a obdélníků je možné využít několika funkcí, které jsou vypsány v následující tabulce:
# | Název funkce | Parametry | Stručný popis |
---|---|---|---|
1 | rectangleColor | SDLSurface dst, int x1, int y1, int x2, int y2, long color | obdélník |
2 | rectangleRGBA | SDLSurface dst, int x1, int y1, int x2, int y2, int r, int g, int b, int a | obdélník |
3 | boxColor | SDLSurface dst, int x1, int y1, int x2, int y2, long color | vyplněný obdélník |
4 | boxRGBA | SDLSurface dst, int x1, int y1, int x2, int y2, int r, int g, int b, int a | vyplněný obdélník |
5 | trigonColor | SDLSurface dst, int x1, int y1, int x2, int y2, int x3, int y3, long color | trojúhelník |
6 | trigonRGBA | SDLSurface dst, int x1, int y1, int x2, int y2, int x3, int y3, int r, int g, int b, int a | trojúhelník |
7 | aatrigonColor | SDLSurface dst, int x1, int y1, int x2, int y2, int x3, int y3, long color | trojúhelník (antialiasing hran) |
8 | aatrigonRGBA | SDLSurface dst, int x1, int y1, int x2, int y2, int x3, int y3, int r, int g, int b, int a | trojúhelník (antialiasing hran) |
9 | filledTrigonColor | SDLSurface dst, int x1, int y1, int x2, int y2, int x3, int y3, int color | vyplněný trojúhelník |
10 | filledTrigonRGBA | SDLSurface dst, int x1, int y1, int x2, int y2, int x3, int y3, int r, int g, int b, int a | vyplněný trojúhelník |
Pravděpodobně není překvapující, že pro osově orientované obdélníky neexistují varianty funkcí pro vykreslování s využitím antialiasingu.
8. Demonstrační příklad SDLTest56: využití kreslicí funkce filledTrigonColor()
V dnešním posledním příkladu pojmenovaném SDLTest56 je ukázáno využití výše zmíněné funkce filledTrigonColor() při tvorbě vzorku složeného z vyplněných trojúhelníků. Z výpisu zdrojového kódu je patrné, že se této funkci musí předat osm parametrů – plocha typu SDL_Surface, do níž se má trojúhelník vykreslit, za ní následuje trojice souřadnic vrcholů trojúhelníku a posledním parametrem je barva, kterou je trojúhelník vyplněn:
// vykresleni vzorku funkci filledTrigonColor() for (int y = 0; y < height + width/3; y += 20) { for (int x = 0; x < width; x += 20) { final int off = x/3; filledTrigonColor(screen, x, y - off, x + 16, y - off, x + 8, y - 12 - off, 0xffff00ff); filledTrigonColor(screen, x + 7, y - off + 1, x + 23, y - off + 1, x + 15, y + 13 - off, 0xff0000ff); } }
Obrázek 14: Vzorek vykreslený pomocí funkce filledTrigonColor().
Zdrojový kód demonstračního příkladu SDLTest56 má tvar:
import sdljava.SDLMain; import sdljava.SDLException; import sdljava.event.SDLEvent; import sdljava.event.SDLKeyboardEvent; import sdljava.event.SDLKey; import sdljava.event.SDLQuitEvent; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; import sdljava.x.swig.SDLPressedState; import static sdljavax.gfx.SDLGfx.*; /** * Padesaty paty demonstracni priklad vyuzivajici knihovnu SDLjava. * * Vykresleni jednoducheho vzorku s vyuzitim funkce filledTrigonColor(). * * @author Pavel Tisnovsky */ public class SDLTest56 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_WIDTH = 480; /** * Vertikalni rozliseni vybraneho grafickeho rezimu ci okna. */ private static final int GFX_HEIGHT = 480; /** * Bitova hloubka vybraneho grafickeho rezimu. * (0 znamena automaticky vyber, ovsem lze samozrejme pouzit * i hodnoty 8, 16, 24 ci 32, podle vlastnosti graficke karty) */ private static final int GFX_BPP = 0; /** * Inicializace grafickeho rezimu ci otevreni okna pro vykreslovani. */ private static SDLSurface initVideo() throws SDLException { final long flags = SDLVideo.SDL_DOUBLEBUF; return SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, flags); } /** * Smycka pro zpracovani udalosti. */ private static void eventLoop() throws SDLException { while (true) { // precist udalost z fronty SDLEvent event = SDLEvent.waitEvent(); // vyskok ze smycky pro zpracovani udalosti pri vyskytu // udalosti typu SDLQuitEvent if (event instanceof SDLQuitEvent) { return; } // stisk ci pusteni klavesy if (event instanceof SDLKeyboardEvent) { // pretypovani final SDLKeyboardEvent keyEvent = (SDLKeyboardEvent)event; // symbol/kod klavesy final int symbol = keyEvent.getSym(); // ESC ukonci program if (symbol == SDLKey.SDLK_ESCAPE && keyEvent.getState() == SDLPressedState.PRESSED) { return; } } } } /** * Vykresleni sceny na obrazovku ci do okna aplikace. * * @param screen * framebuffer */ private static void drawOnScreen(SDLSurface screen) throws SDLException { // rozmery okna aplikace ci rozliseni obrazovky final int width = screen.getWidth(); final int height = screen.getHeight(); // vykresleni vzorku funkci filledTrigonColor() for (int y = 0; y < height + width/3; y += 20) { for (int x = 0; x < width; x += 20) { final int off = x/3; filledTrigonColor(screen, x, y - off, x + 16, y - off, x + 8, y - 12 - off, 0xffff00ff); filledTrigonColor(screen, x + 7, y - off + 1, x + 23, y - off + 1, x + 15, y + 13 - off, 0xff0000ff); } } // nutno volat i v pripade, ze neni pouzit double buffering screen.updateRect(); screen.flip(); } /** * Spusteni padesateho sesteho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace knihovny SDLJava SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace grafickeho rezimu ci otevreni okna pro vykreslovani final SDLSurface screen = initVideo(); // vykresleni sceny na obrazovku drawOnScreen(screen); // smycka pro zpracovani udalosti eventLoop(); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } } }
Skript pro překlad tohoto demonstračního příkladu na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest56.java
Dávkový soubor pro překlad tohoto demonstračního příkladu na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest56.java
Skript pro spuštění na Linuxu:
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib java -cp .:$SDL_JAVA_LIBS/sdljava.jar -Djava.library.path=$SDL_JAVA_LIBS SDLTest56
Dávkový soubor pro spuštění na Windows:
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest56
9. Kreslicí funkce pro grafické entity s proměnným počtem vrcholů a/nebo řídicích bodů
V knihovně SDLJava nalezneme i několik funkcí určených pro vykreslení složitějších grafických entit, konkrétně entit s proměnným počtem vrcholů. Jedná se jak o polygony, tak i o Bézierovy křivky. Programátoři využívající Javu by mohli předpokládat, že se v tomto případě použijí statické metody/funkce s proměnným počtem parametrů, ovšem vzhledem k tomu, že je SDLJava nízkoúrovňová knihovna, je implementováno poněkud překvapivé řešení – souřadnice vrcholů jsou předávány přes objekty typu java.nio.ShortBuffer, což sice není příliš elegantní řešení, ovšem je efektivní při přenosu většího množství dat mezi Javovskou částí aplikace a nativním kódem. Příklady použití při vykreslování obecných polygonů a Bézierových křivek si ukážeme v navazující části tohoto seriálu.
10. Repositář se zdrojovými kódy všech pěti dnešních demonstračních příkladů
Všech pět dnes popsaných demonstračních příkladů bylo společně s podpůrnými skripty určenými pro jejich překlad a následné spuštění uloženo do Mercurial repositáře dostupného na adrese http://icedtea.classpath.org/people/ptisnovs/jvm-tools/. Podobně jako tomu bylo i v předchozích několika dílech tohoto seriálu, i ke dnešním příkladům jsou přiloženy skripty využitelné pro jejich překlad a spuštění. Navíc byly přidány i skripty využitelné ve Windows:
11. Odkazy na Internetu
- SDLJava: package sdljava.ttf
http://sdljava.sourceforge.net/docs/api/sdljava/ttf/package-summary.html#package_description - SDLJava: class sdljava.ttf.SDLTTF
http://sdljava.sourceforge.net/docs/api/sdljava/ttf/SDLTTF.html - SDLJava: class sdljava.ttf.SDLTrueTypeFont
http://sdljava.sourceforge.net/docs/api/sdljava/ttf/SDLTrueTypeFont.html - SDL_ttf Documentation
http://www.libsdl.org/projects/SDL_ttf/docs/ - SDL_ttf 2.0 (není prozatím součástí SDLJava)
http://www.libsdl.org/projects/SDL_ttf/ - SDL_ttf doc
http://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf_frame.html - SDL 1.2 Documentation: SDL_Surface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsurface.html - SDL 1.2 Documentation: SDL_PixelFormat
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlpixelformat.html - SDL 1.2 Documentation: SDL_LockSurface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdllocksurface.html - SDL 1.2 Documentation: SDL_UnlockSurface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlunlocksurface.html - SDL 1.2 Documentation: SDL_LoadBMP
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlloadbmp.html - SDL 1.2 Documentation: SDL_SaveBMP
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsavebmp.html - SDL 1.2 Documentation: SDL_BlitSurface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlblitsurface.html - SDL 1.2 Documentation: SDL_VideoInfo
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlvideoinfo.html - SDL 1.2 Documentation: SDL_GetVideoInfo
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlgetvideoinfo.html - glDrawArrays
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawArrays.xml - glDrawElements
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawElements.xml - glDrawArraysInstanced
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawArraysInstanced.xml - glDrawElementsInstanced
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawElementsInstanced.xml - Root.cz: Seriál Grafická knihovna OpenGL
http://www.root.cz/serialy/graficka-knihovna-opengl/ - Root.cz: Seriál Tvorba přenositelných grafických aplikací využívajících knihovnu GLUT
http://www.root.cz/serialy/tvorba-prenositelnych-grafickych-aplikaci-vyuzivajicich-knihovnu-glut/ - Best Practices for Working with Vertex Data
https://developer.apple.com/library/ios/documentation/3ddrawing/conceptual/opengles_programmingguide/TechniquesforWorkingwithVertexData/TechniquesforWorkingwithVertexData.html - Class BufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/image/BufferStrategy.html - Class Graphics
http://docs.oracle.com/javase/1.5.0/docs/api/java/awt/Graphics.html - Double Buffering and Page Flipping
http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html - BufferStrategy and BufferCapabilities
http://docs.oracle.com/javase/tutorial/extra/fullscreen/bufferstrategy.html - Java:Tutorials:Double Buffering
http://content.gpwiki.org/index.php/Java:Tutorials:Double_Buffering - Double buffer in standard Java AWT
http://www.codeproject.com/Articles/2136/Double-buffer-in-standard-Java-AWT - Java 2D: Hardware Accelerating – Part 1 – Volatile Images
http://www.javalobby.org/forums/thread.jspa?threadID=16840&tstart=0 - Java 2D: Hardware Accelerating – Part 2 – Buffer Strategies
http://www.javalobby.org/java/forums/t16867.html - How does paintComponent work?
http://stackoverflow.com/questions/15544549/how-does-paintcomponent-work - A Swing Architecture Overview
http://www.oracle.com/technetwork/java/architecture-142923.html - Class javax.swing.JComponent
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html - Class java.awt.Component
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html - Class java.awt.Component.BltBufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.BltBufferStrategy.html - Class java.awt.Component.FlipBufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.FlipBufferStrategy.html - Metoda java.awt.Component.isDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html#isDoubleBuffered() - Metoda javax.swing.JComponent.isDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#isDoubleBuffered() - Metoda javax.swing.JComponent.setDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#setDoubleBuffered(boolean) - Javadoc – třída GraphicsDevice
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsDevice.html - Javadoc – třída GraphicsEnvironment
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsEnvironment.html - Javadoc – třída GraphicsConfiguration
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsConfiguration.html - Javadoc – třída DisplayMode
http://docs.oracle.com/javase/7/docs/api/java/awt/DisplayMode.html - Lesson: Full-Screen Exclusive Mode API
http://docs.oracle.com/javase/tutorial/extra/fullscreen/ - Full-Screen Exclusive Mode
http://docs.oracle.com/javase/tutorial/extra/fullscreen/exclusivemode.html - Display Mode
http://docs.oracle.com/javase/tutorial/extra/fullscreen/displaymode.html - Using the Full-Screen Exclusive Mode API in Java
http://www.developer.com/java/other/article.php/3609776/Using-the-Full-Screen-Exclusive-Mode-API-in-Java.htm - Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - MultiMedia eXtensions
http://softpixel.com/~cwright/programming/simd/mmx.phpi - SSE (Streaming SIMD Extentions)
http://www.songho.ca/misc/sse/sse.html - Timothy A. Chagnon: SSE and SSE2
http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf - Intel corporation: Extending the Worldr's Most Popular Processor Architecture
http://download.intel.com/technology/architecture/new-instructions-paper.pdf - SIMD architectures:
http://arstechnica.com/old/content/2000/03/simd.ars/ - GC safe-point (or safepoint) and safe-region
http://xiao-feng.blogspot.cz/2008/01/gc-safe-point-and-safe-region.html - Safepoints in HotSpot JVM
http://blog.ragozin.info/2012/10/safepoints-in-hotspot-jvm.html - Java theory and practice: Synchronization optimizations in Mustang
http://www.ibm.com/developerworks/java/library/j-jtp10185/ - How to build hsdis
http://hg.openjdk.java.net/jdk7/hotspot/hotspot/file/tip/src/share/tools/hsdis/README - Java SE 6 Performance White Paper
http://www.oracle.com/technetwork/java/6-performance-137236.html - Lukas Stadler's Blog
http://classparser.blogspot.cz/2010/03/hsdis-i386dll.html - How to build hsdis-amd64.dll and hsdis-i386.dll on Windows
http://dropzone.nfshost.com/hsdis.htm - PrintAssembly
https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly - The Java Virtual Machine Specification: 3.14. Synchronization
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.14 - The Java Virtual Machine Specification: 8.3.1.4. volatile Fields
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4 - The Java Virtual Machine Specification: 17.4. Memory Model
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 - The Java Virtual Machine Specification: 17.7. Non-atomic Treatment of double and long
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7 - Open Source ByteCode Libraries in Java
http://java-source.net/open-source/bytecode-libraries - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - BCEL Home page
http://commons.apache.org/bcel/ - Byte Code Engineering Library (před verzí 5.0)
http://bcel.sourceforge.net/ - Byte Code Engineering Library (verze >= 5.0)
http://commons.apache.org/proper/commons-bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - Javassist
http://www.jboss.org/javassist/ - Byteman
http://www.jboss.org/byteman - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - jclasslib bytecode viewer
http://www.ej-technologies.com/products/jclasslib/overview.html