Obsah
2. Formát ukládání pixelů ve framebufferu a v bitmapách
3. Třída sdljava.video.SDLPixelFormat
4. Demonstrační příklad SDLTest8 – zobrazení základních informací o framebufferu
5. Spuštění demonstračního příkladu SDLTest8 a ukázka výstupu
6. Demonstrační příklad SDLTest9 – zobrazení základních informací o načtené bitmapě
7. Spuštění demonstračního příkladu SDLTest9 a ukázka výstupu
8. Vlastnosti použitého video systému
9. Demonstrační příklad SDLTest10 – zobrazení informací o video systému
10. Spuštění demonstračního příkladu SDLTest10 a ukázka výstupu
11. Nízkoúrovňový přístup k obsahu framebufferu a bitmap
12. Demonstrační příklad SDLTest11 – negace všech pixelů uložených v bitmapě
13. Repositář se zdrojovými kódy všech čtyř dnešních demonstračních příkladů
1. Pohled pod kapotu JVM – nízkoúrovňový přístup k framebufferu a bitmapám s využitím knihovny SDLJava
V dnešní části seriálu o programovacím jazyku Java i o virtuálním stroji tohoto jazyka navážeme na předchozí díl, v němž byl mj. vysvětlen způsob načtení bitmap z externích souborů s následným zobrazením (resp. přenosem) těchto bitmap do framebufferu. Pro úplné pochopení vlastností knihovny SDL (verze 1.x) si však ještě musíme ukázat způsoby manipulace s obsahem bitmap a popř. i s obsahem framebufferu. Jedná se o dosti nízkoúrovňové operace, které neoplývají velkou rychlostí, ovšem v některých případech se jim nemůžeme vyhnout. Příkladem může být například grafický editor, v němž je nutné přistupovat k jednotlivým pixelům popř. k jejich barvovým složkám. Knihovna SDLJava zajišťující rozhraní mezi javovskou aplikací a knihovnou SDL samozřejmě tyto operace podporuje, ovšem s několika omezeními, která vyplývají z toho, že v Javě nelze přímo manipulovat s ukazateli použitými v nativních datových strukturách knihovny SDL.
Nejdříve si popíšeme způsoby získání základních informací o libovolné bitmapě či framebufferu, tedy o objektech, které jsou při využití knihovny SDLJava reprezentovány instancemi třídy sdljava.video.SDLSurface. Následně se seznámíme s třídou sdljava.video.SDLPixelFormat, což je obalující třída céčkové datové struktury nazvané SDL_PixelFormat, z níž lze získat všechny informace nutné pro zakódování či dekódování barev na hodnoty ukládané do bitmapy a/nebo do framebufferu. Nesmíme zapomenout ani na třídu sdljava.video.SDLVideoInfo (obalující céčkovou datovou strukturu SDL_VideoInfo) s informacemi o možnostech video systému. V závěrečné části tohoto článku si ukážeme jeden z jednoduchých (ale současně i pomalých) způsobů přístupu k jednotlivým pixelům uloženým v bitmapě. Všechny informace, které se dnes dozvíme, budou opět ukázány na několika demonstračních příkladech.
2. Formát ukládání pixelů ve framebufferu a v bitmapách
V předchozích dvou částech tohoto seriálu jsme se již seznámili se třídou sdljava.video.SDLSurface, jejíž instance reprezentují buď bitmapy nebo framebuffer. Bitmapy je možné načíst z externích souborů s využitím metody sdljava.video.SDLVideo.loadBMP() popř. sdljava.image.SDLImage.load(), nebo lze (pokud je to nutné) bitmapy tvořit programově, tj. konstrukcí prázdné bitmapy s následným nakreslením nějakého obrazce do bitmapy. Pokud je nutno do bitmapy zapisovat či z ní číst jednotlivé pixely, musíme znát následující údaje: rozměry bitmapy, skutečnou délku obrazového řádku a přesný formát uložení pixelů. Pro zjištění těchto údajů lze využít následující čtveřici metod třídy sdljava.video.SDLSurface:
# | Metoda | Popis |
---|---|---|
1 | int getWidth() | vrátí šířku bitmapy, tj. počet viditelných pixelů na obrazovém řádku |
2 | int getHeight() | vrátí výšku bitmapy, tj. počet viditelných obrazových řádků |
3 | int getPitch() | vrátí skutečnou délku obrazového řádku (viz poznámka níže) |
4 | SDLPixelFormat getFormat() | vrátí instanci třídy SDLPixelFormat obsahující všechny informace o způsobu uložení pixelů v bitmapě |
Údaje o šířce a výšce bitmapy jsou uváděny v pixelech, zatímco údaj pitch je používán pro adresaci a tudíž je vrácen jako celková délka obrazového řádku uváděná v bajtech. Tento údaj je nutné znát ze dvou důvodů: u některých typů bitmap i u framebufferů je délka obrazového řádku zarovnána například na násobky čtyř, osmi atd. bajtů, a to proto, aby se jednodušeji prováděly blokové přenosy dat. U některých typů framebufferů je jeho „neviditelná“ část využita pro uložení spritů atd., což může znamenat, že hodnota pitch nebude v žádném případě odpovídat šířce bitmapy vynásobené počtem bajtů nutných pro uložení jednoho pixelu. Můžeme si uvést příklad bitmapy/framebufferu s šířkou 800 pixelů a bitovou hloubkou 32bpp (tj. čtyři bajty na pixel). V tomto případě může hodnota pitch být 800×4=3200 bajtů, ovšem může také být rovna 4096 bajtům atd. Druhý důvod použití hodnoty pitch v praxi spočívá v tom, že se při adresaci pixelů nemusí násobit šířka bitmapy hodnotou bpp (dělitelnou osmi).
3. Třída sdljava.video.SDLPixelFormat
Dalším důležitým údajem o bitmapě a/nebo framebufferu je informace o přesném formátu uložení pixelů. Pro reprezentaci těchto informací se v nativní knihovně SDL používá céčková struktura nazvaná SDL_PixelFormat, která vypadá následovně:
typedef struct SDL_PixelFormat { SDL_Palette *palette; Uint8 BitsPerPixel; Uint8 BytesPerPixel; Uint8 Rloss, Gloss, Bloss, Aloss; Uint8 Rshift, Gshift, Bshift, Ashift; Uint32 Rmask, Gmask, Bmask, Amask; Uint32 colorkey; Uint8 alpha; } SDL_PixelFormat;
V knihovně SDLJava je tato datová struktura obalena třídou SDLPixelFormat a najdeme v ní všechny potřebné gettery, které jsou vypsány v následující tabulce:
# | Metoda | Popis |
---|---|---|
1 | short getRloss() | počet bitů, které se ztratí při konverzi hodnoty červené barvové složky v rozsahu 0..255 |
2 | long getRmask() | maska, která z hodnoty pixelu odstraní všechny informace kromě červené složky |
3 | short getRshift() | hodnota bitového posunu proto, aby bylo možné získat hodnotu červené složky |
4 | short getGloss() | počet bitů, které se ztratí při konverzi hodnoty zelené barvové složky v rozsahu 0..255 |
5 | long getGmask() | maska, která z hodnoty pixelu odstraní všechny informace kromě zelené složky |
6 | short getGshift() | hodnota bitového posunu proto, aby bylo možné získat hodnotu zelené složky |
7 | short getBloss() | počet bitů, které se ztratí při konverzi hodnoty modré barvové složky v rozsahu 0..255 |
8 | long getBmask() | maska, která z hodnoty pixelu odstraní všechny informace kromě modré složky |
9 | short getBshift() | hodnota bitového posunu proto, aby bylo možné získat hodnotu modré složky |
10 | short getAloss() | počet bitů, které se ztratí při konverzi hodnoty alfa složky v rozsahu 0..255 |
11 | long getAmask() | maska, která z hodnoty pixelu odstraní všechny informace kromě alfa složky |
12 | short getAshift() | hodnota bitového posunu proto, aby bylo možné získat hodnotu alfa složky |
13 | short getBitsPerPixel() | počet bitů, kterými je reprezentovaný pixel |
14 | short getBytesPerPixel() | počet bajtů, kterými je reprezentovaný pixel |
15 | long getColorkey() | klíčová barva (resp. hodnota pixelu), průhledná při operaci typu BitBLT |
Význam těchto getterů si zaslouží zvláštní vysvětlení. SDL rozlišuje bitmapy s barvovou paletou a bitmapy bez palety. U bitmap s barvovou paletou má význam pouze zjištění bitové hloubky, tj. jedná se o gettery getBitsPerPixel(), getBytesPerPixel() a getColorkey(). Zajímavější je to u bitmap bez palety, které mají většinou bitovou hloubku 16bpp, 24bpp a 32bpp. U těchto bitmap je každá barvová složka (red, green, blue) a popř. i průhlednost pixelu (alfa kanál) uložena ve dvou, třech či čtyřech bajtech a výše popsané gettery #1 až #12 slouží k tomu, aby byl programátor schopen tyto barvové složky získat, popř. naopak ze tří barvových složek (popř. průhlednosti) vypočítat hodnotu pro zápis do bitmapu.
Příkladem může být bitmapa s bitovou hloubkou 24bpp, v níž je každá barvová složka (red, green, blue) reprezentována osmi bity. V tomto případě budou masky jednotlivých barvových složek nastaveny na hodnoty 0×ff000, 0×00ff00 a 0×0000ff a bitové posuny na hodnoty 16, 8 a 0. První barvovou složku (většinou red) získáme maskováním hodnoty pixelu maskou 0×ff0000 a posunem o 16 bitů doprava, druhou barvovou složku (typicky green) maskováním hodnoty pixelu maskou 0×00ff00 a posunem o 8 bitů atd. Hodnota loss je použita v případě, kdy má bitmapa hloubku 16bpp, aby se například pětibitová hodnota získaná maskováním a posunem zkonvertovala na osmibitovou hodnotu.
4. Demonstrační příklad SDLTest8 – zobrazení základních informací o framebufferu
Informace, s nimiž jsme se seznámili v předchozích třech kapitolách, si můžeme ověřit na demonstračním příkladu nazvaném SDLTest8. Po spuštění tohoto demonstračního příkladu se nastaví zvolený video režim (implicitně je použito rozlišení 800×600 pixelů) a následně se zjistí všechny důležité informace jak o rozlišení framebufferu, tak i o hodnotě pitch zmíněné ve druhé kapitole a o formátu uložení pixelů, což bylo téma, kterému jsme se věnovali v kapitole třetí. Všechny zjištěné informace o framebufferu jsou následně vypsány na standardní výstup. Sami si můžete vyzkoušet použití dalších grafických režimů, různých bitových hloubek atd.:
import sdljava.SDLMain; import sdljava.video.SDLPixelFormat; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; /** * Osmy demonstracni priklad vyuzivajici knihovnu SDLjava. * * Zobrazeni zakladnich informaci o vybranem grafickem rezimu * a o formatu pixelu pouzitych v tomto rezimu. * * @author Pavel Tisnovsky */ public class SDLTest8 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu. */ private static final int GFX_WIDTH = 800; /** * Vertikalni rozliseni vybraneho grafickeho rezimu. */ private static final int GFX_HEIGHT = 600; /** * 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; /** * Zobrazeni ziskanych informaci o vybranem grafickem rezimu. * * @param pixelFormat * format pixelu * @param pitch * skutecna delka obrazoveho radku v bajtech * @param width * pocet viditelnych pixelu na obrazovem radku * @param height * pocet viditelnych obrazovych radku */ private static void printInfo(SDLPixelFormat pixelFormat, int pitch, int width, int height) { print("Width: " + width); print("Height: " + height); print("Pitch: " + pitch); print("Bpp: " + pixelFormat.getBitsPerPixel()); // informace o cervene barvove slozce print("Red:"); print(" shift: " + pixelFormat.getRShift()); print(" mask: 0x" + Long.toHexString(pixelFormat.getRMask())); print(" loss: " + pixelFormat.getRLoss()); // informace o zelene barvove slozce print("Green:"); print(" shift: " + pixelFormat.getGShift()); print(" mask: 0x" + Long.toHexString(pixelFormat.getGMask())); print(" loss: " + pixelFormat.getGLoss()); // informace o modre barvove slozce print("Blue:"); print(" shift: " + pixelFormat.getBShift()); print(" mask: 0x" + Long.toHexString(pixelFormat.getBMask())); print(" loss: " + pixelFormat.getBLoss()); // informace o alfa kanalu print("Alpha:"); print(" shift: " + pixelFormat.getAShift()); print(" mask: 0x" + Long.toHexString(pixelFormat.getAMask())); print(" loss: " + pixelFormat.getALoss()); } /** * Vypis retezce na standardni vystup. * * @param string * vypisovany retezec */ private static void print(String string) { System.out.println(string); } /** * Spusteni osmeho demonstracniho prikladu. */ public static void main(String[] args) { // informace o formatu pixelu SDLPixelFormat pixelFormat = null; // skutecna delka obrazoveho radku int pitch = 0; // realne rozliseni (viditelna bitmapa) int width = 0; int height = 0; try { // inicializace video subsystemu knihovny SDL. SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace celoobrazovkoveho grafickeho rezimu final SDLSurface screen = SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, 0); Thread.sleep(500); // precteni zakladnich informaci o grafickem rezimu pixelFormat = screen.getFormat(); pitch = screen.getPitch(); width = screen.getWidth(); height = screen.getHeight(); Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } // zobrazit ziskane informace printInfo(pixelFormat, pitch, width, height); } }
5. Spuštění demonstračního příkladu SDLTest8 a ukázka výstupu
Připomeňme si, jakým způsobem je nutné demonstrační příklady využívající knihovnu SDLJava přeložit a následně spustit. Nesmíme zapomenout uvést cestu k javovské části této knihovny (sdljava.jar) a při spuštění ani k nativní části knihovny (libsdljava.so/sdljava.dll):
Skript pro překlad (Linux):
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest8.java
Skript pro překlad (Microsoft Windows):
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest8.java
Skript pro spuštění (Linux):
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest8.java
Skript pro spuštění (Microsoft Windows):
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest8
Zajímavé jsou výsledky spuštění demonstračního příkladu na různých systémech, popř. s různě nastavenými konstantami GFX_WIDTH, GFX_HEIGHT a GFX_BPP.
Spuštění při specifikaci osmibitové hloubky, při níž se používá barvová paleta tudíž masky ani bitové posuny nemají význam:
Width: 800 Height: 600 Pitch: 800 Bpp: 8 Red: shift: 0 mask: 0x0 loss: 8 Green: shift: 0 mask: 0x0 loss: 8 Blue: shift: 0 mask: 0x0 loss: 8 Alpha: shift: 0 mask: 0x0 loss: 8
Při volbě 16bitové hloubky je zajímavé sledovat hodnotu masky i počty bitů, které se u jednotlivých barvových složek ztratí (3 bity pro červenou složku, 2 bity pro složku zelenou a 3 bity pro složku modrou):
Width: 800 Height: 600 Pitch: 1600 Bpp: 16 Red: shift: 11 mask: 0xf800 loss: 3 Green: shift: 5 mask: 0x7e0 loss: 2 Blue: shift: 0 mask: 0x1f loss: 3 Alpha: shift: 0 mask: 0x0 loss: 8
Spuštění při specifikaci 24 bitové hloubky ve skutečnosti na testovaném stroji použilo framebuffer s formátem 32bpp, ovšem alfa kanál je nevyužit (maskován):
Width: 800 Height: 600 Pitch: 2400 Bpp: 24 Red: shift: 16 mask: 0xff0000 loss: 0 Green: shift: 8 mask: 0xff00 loss: 0 Blue: shift: 0 mask: 0xff loss: 0 Alpha: shift: 0 mask: 0x0 loss: 8
Skutečná 32bitová hloubka framebufferu:
Width: 800 Height: 600 Pitch: 3200 Bpp: 32 Red: shift: 16 mask: 0xff0000 loss: 0 Green: shift: 8 mask: 0xff00 loss: 0 Blue: shift: 0 mask: 0xff loss: 0 Alpha: shift: 0 mask: 0x0 loss: 8
6. Demonstrační příklad SDLTest9 – zobrazení základních informací o načtené bitmapě
Demonstrační příklad SDLTest9 se v mnoha ohledech podobá předchozímu demonstračnímu příkladu SDLTest8, až na ten rozdíl, že se zjistí a následně i zobrazí informace o bitmapě uložené do souboru xscorch.bmp (http://icedtea.classpath.org/people/ptisnovs/jvm-tools/raw-file/6fe0d3a6edeb/sdljava/img/xscorch.bmp) a nikoli informace o framebufferu. Povšimněte si, že v tomto příkladu vůbec není nutné nastavovat grafický režim, protože jedinými operacemi, které je nutné provést, je načtení bitmapy pomocí metody sdljava.video.SDLVideo.loadBMP(), přečtení informací o uložení pixelů v bitmapě přes metodu sdljava.video.SDLSurface.getFormat() a následné volání getterů, jenž jsou v této třídě deklarovány (viz třetí kapitolu s podrobnějším popisem):
import sdljava.SDLMain; import sdljava.video.SDLPixelFormat; import sdljava.video.SDLSurface; import sdljava.video.SDLVideo; /** * Devaty demonstracni priklad vyuzivajici knihovnu SDLjava. * * Zobrazeni zakladnich informaci o bitmape nactene ze souboru * typu BMP (Windows Bitmap). * * @author Pavel Tisnovsky */ public class SDLTest9 { /** * Nazev bitmapy, ktera se ma nacist a nasledne zobrazit. */ private static final String IMAGE_NAME = "xscorch.bmp"; /** * Zobrazeni ziskanych informaci o bitmape (surface). * * @param pixelFormat * format pixelu * @param pitch * skutecna delka obrazoveho radku v bajtech * @param width * pocet viditelnych pixelu na jednom radku bitmapy * @param height * pocet viditelnych obrazovych radku bitmapy */ private static void printInfo(SDLPixelFormat pixelFormat, int pitch, int width, int height) { print("Width: " + width); print("Height: " + height); print("Pitch: " + pitch); print("Bpp: " + pixelFormat.getBitsPerPixel()); // informace o cervene barvove slozce print("Red:"); print(" shift: " + pixelFormat.getRShift()); print(" mask: 0x" + Long.toHexString(pixelFormat.getRMask())); print(" loss: " + pixelFormat.getRLoss()); // informace o zelene barvove slozce print("Green:"); print(" shift: " + pixelFormat.getGShift()); print(" mask: 0x" + Long.toHexString(pixelFormat.getGMask())); print(" loss: " + pixelFormat.getGLoss()); // informace o modre barvove slozce print("Blue:"); print(" shift: " + pixelFormat.getBShift()); print(" mask: 0x" + Long.toHexString(pixelFormat.getBMask())); print(" loss: " + pixelFormat.getBLoss()); // informace o alfa kanalu print("Alpha:"); print(" shift: " + pixelFormat.getAShift()); print(" mask: 0x" + Long.toHexString(pixelFormat.getAMask())); print(" loss: " + pixelFormat.getALoss()); } /** * Vypis retezce na standardni vystup. * * @param string * vypisovany retezec */ private static void print(String string) { System.out.println(string); } /** * Spusteni devateho demonstracniho prikladu. */ public static void main(String[] args) { // informace o formatu pixelu SDLPixelFormat pixelFormat = null; // skutecna delka obrazoveho radku int pitch = 0; // realne rozliseni nactene bitmapy int width = 0; int height = 0; try { // inicializace video subsystemu knihovny SDL. SDLMain.init(SDLMain.SDL_INIT_VIDEO); // nacteni bitmapy z externiho souboru final SDLSurface bitmap = SDLVideo.loadBMP(IMAGE_NAME); // ziskani informaci o nactene bitmape pixelFormat = bitmap.getFormat(); pitch = bitmap.getPitch(); width = bitmap.getWidth(); height = bitmap.getHeight(); Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } // zobrazit ziskane informace printInfo(pixelFormat, pitch, width, height); } }
7. Spuštění demonstračního příkladu SDLTest9 a ukázka výstupu
Překlad i spuštění příkladu SDLTest9 se provádí naprosto stejným způsobem, jako tomu bylo u předchozího příklad:
Skript pro překlad (Linux):
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest9.java
Skript pro překlad (Microsoft Windows):
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib javac -cp %SDL_JAVA_LIBS%\sdljava.jar SDLTest9.java
Skript pro spuštění (Linux):
#!/bin/sh SDL_JAVA_LIBS=./sdljava-0.9.1/lib javac -cp $SDL_JAVA_LIBS/sdljava.jar SDLTest9.java
Skript pro spuštění (Microsoft Windows):
set SDL_JAVA_LIBS=.\sdljava-0.9.1\lib java -cp .;%SDL_JAVA_LIBS%\sdljava.jar -Djava.library.path=%SDL_JAVA_LIBS% SDLTest9
Po spuštění tohoto příkladu by se na standardním výstupu měla objevit následující zpráva přesně v tom formátu, v jakém je zde uvedena. Povšimněte si zejména absence alfa kanálu, který značí že v této bitmapě (pokud by nebyla provedena její konverze) nelze ukládat poloprůhledné pixely. Hodnota pitch přesně odpovídá šířce bitmapy vynásobené počtem bajtů na pixel:
Width: 300 Height: 195 Pitch: 900 Bpp: 24 Red: shift: 16 mask: 0xff0000 loss: 0 Green: shift: 8 mask: 0xff00 loss: 0 Blue: shift: 0 mask: 0xff loss: 0 Alpha: shift: 0 mask: 0x0 loss: 8
8. Vlastnosti použitého video systému
Knihovna SDL dokáže přečíst i vlastnosti použitého video systému. Tyto informace jsou předány v nativní struktuře (bitovém poli) pojmenované SDL_VideoInfo:
typedef struct{ Uint32 hw_available:1; Uint32 wm_available:1; Uint32 blit_hw:1; Uint32 blit_hw_CC:1; Uint32 blit_hw_A:1; Uint32 blit_sw:1; Uint32 blit_sw_CC:1; Uint32 blit_sw_A:1; Uint32 blit_fill; Uint32 video_mem; SDL_PixelFormat *vfmt; } SDL_VideoInfo;
Tato struktura je v knihovně SDLJava obalena třídou sdljava.video.SDLVideoInfo, v níž jsou deklarovány následující gettery:
# | Metoda | Popis | |
---|---|---|---|
1 | getHwAvailable() | vrací true pokud se bitmapy vytváří ve video paměti | |
2 | getWmAvailable() | vrací true pokud je přítomen správce oken (neplatí například pro FB režimy) | |
3 | getVideoMemory() | kapacita VRAM (ne vždy dostupný údaj) | |
4 | getBlit_fill() | možnosti akcelerace operace typu rectangle fill | |
5 | getBlit_hw_A() | možnosti akcelerace operace typu BitBLT s průhledností pixelů (přenos z VRAM) | |
6 | getBlit_hw_CC() | možnosti akcelerace operace typu BitBLT s klíčovou (zcela průhlednou) barvou (přenos z VRAM) | |
7 | getBlit_hw() | možnosti akcelerace operace typu BitBLT (přenos z VRAM) | |
8 | getBlit_sw_A() | možnosti akcelerace operace typu BitBLT s průhledností pixelů (přenos z operační paměti do VRAM) | |
9 | getBlit_sw_CC() | možnosti akcelerace operace typu BitBLT s klíčovou (zcela průhlednou) barvou (přenos z operační paměti do VRAM) | |
10 | getBlit_sw() | možnosti akcelerace operace typu BitBLT (přenos z operační paměti do VRAM) |
9. Demonstrační příklad SDLTest10 – zobrazení informací o video systému
V demonstračním příkladu SDLTest10 je ukázáno, jakým způsobem lze přečíst informace o video systému nainstalovaném na počítači, na němž je tento příklad spuštěn. Povšimněte si, že instanci třídy sdljava.video.SDLVideoInfo popsané v předchozí kapitole lze získat zavoláním statické metody sdljava.video.SDLVideo.getVideoInfo():
import sdljava.SDLMain; import sdljava.video.SDLVideo; import sdljava.video.SDLVideoInfo; /** * Desaty demonstracni priklad vyuzivajici knihovnu SDLjava. * * Zobrazeni zakladnich informaci o video subsystemu. * * @author Pavel Tisnovsky */ public class SDLTest10 { /** * Zobrazeni ziskanych informaci o video subsystemu. * * @param videoInfo * informace o video subsystemu */ private static void printInfo(SDLVideoInfo videoInfo) { print("Window manager available: " + trueFalse(videoInfo.getWmAvailable())); print("Hardware surfaces available: " + trueFalse(videoInfo.getHwAvailable())); print("Total video RAM (kB): " + videoInfo.getVideoMemory()); print("Accelerated BitBLT (HW): " + trueFalse(videoInfo.getBlit_hw())); print("Accelerated Color key BitBLT (HW): " + trueFalse(videoInfo.getBlit_hw_CC())); print("Accelerated Alpha BitBLT (HW): " + trueFalse(videoInfo.getBlit_hw_A())); print("Accelerated BitBLT (SW): " + trueFalse(videoInfo.getBlit_sw())); print("Accelerated Color key BitBLT (SW): " + trueFalse(videoInfo.getBlit_sw_CC())); print("Accelerated Alpha BitBLT (SW): " + trueFalse(videoInfo.getBlit_sw_A())); print("Accelerated BitBLT fill: " + trueFalse(videoInfo.getBlit_fill())); } /** * Prevod priznaku na retezec "true" ci "false". * * @param flag * priznak * @return retezec "true" ci "false" */ private static String trueFalse(long flag) { return flag == 0 ? "no" : "yes"; } /** * Vypis retezce na standardni vystup. * * @param string * vypisovany retezec */ private static void print(String string) { System.out.println(string); } /** * Spusteni desateho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace video subsystemu knihovny SDL. SDLMain.init(SDLMain.SDL_INIT_VIDEO); // ziskat informace o video subsystemu final SDLVideoInfo videoInfo = SDLVideo.getVideoInfo(); // zobrazit ziskane informace printInfo(videoInfo); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } } }
10. Spuštění demonstračního příkladu SDLTest10 a ukázka výstupu
Příklad SDLTest10 se překládá a spouští naprosto stejným způsobem jako oba dva předchozí příklady SDLTest8 a SDLTest9, proto zde již nejsou příslušné skripty uvedeny. Zajímavější jsou zprávy, které získáme na standardním výstupu. Pro testovaný systém (Linux, X Window + uzavřené ovladače nVidia) se získaly následující výsledky naznačující, že většina klíčových operací prováděných knihovnou SDL není akcelerována:
Window manager available: yes Hardware surfaces available: no Total video RAM (kB): 0 Accelerated BitBLT (HW): no Accelerated Color key BitBLT (HW): no Accelerated Alpha BitBLT (HW): no Accelerated BitBLT (SW): no Accelerated Color key BitBLT (SW): no Accelerated Alpha BitBLT (SW): no Accelerated BitBLT fill: no
11. Nízkoúrovňový přístup k obsahu framebufferu a bitmap
Pro nízkoúrovňový přístup k obsahu framebufferu a/nebo bitmap lze v případě použití knihovny SDL získat přímo ukazatel (pointer), přes nějž je možné přistupovat k jednotlivým pixelům. V Javě a knihovně SDLJava sice tuto možnost nemáme, namísto toho je ovšem obsah bitmapy přístupný ve formě objektu typu java.nio.ByteBuffer. Ovšem ještě předtím, než je možné číst či zapisovat obsah bitmapy, je nutné bitmapu takzvaně uzamknout s využitím metody lockSurface() a následně opět odemnknout metodou unlockSurface(). Tyto metody sice nemusí být volány pro všechny typy bitmap, ovšem při přímém přístupu do framebufferu se jim nevyhneme:
# | Metoda | Popis |
---|---|---|
1 | boolean lockSurface() | |
2 | void unlockSurface() | |
3 | java.nio.ByteBuffer getPixelData() |
Postup při přímém přístupu k obsahu bitmapy/framebufferu je následující:
private static void updateBitmap(SDLSurface bitmap) throws SDLException { bitmap.lockSurface(); ByteBuffer pixelBuffer = bitmap.getPixelData(); ... zde je možné pracovat s pixely bitmap.unlockSurface(); }
Pixely se čtou a zapisují v interní podobě, tj. pro jejich převod na hodnoty RGB či RGBA je nutné použít informace získané metodami vysvětlenými v předchozích kapitolách, popř. využít dvojici konverzních funkcí, které však nejsou nijak závratně rychlé.
12. Demonstrační příklad SDLTest11 – negace všech pixelů uložených v bitmapě
Dnešní poslední demonstrační příklad se jmenuje SDLTest11. Po jeho spuštění se načte bitmapa ze souboru xscorch.bmp a následně se vykreslí na obrazovku (do framebufferu). Po přibližně dvou sekundách se získá buffer obsahující hodnoty všech pixelů v bitmapě a následně jsou tyto hodnoty invertovány (to si můžeme dovolit udělat velmi jednoduše, protože bitmapa neobsahuje alfa kanál). Po inverzi všech pixelů se bitmapa opět zobrazí a posléze se uloží do nového souboru s názvem xscorch2.bmp:
Obrázek 1: Originální bitmapa.
Obrázek 2: Bitmapa vytvořená demonstračním příkladem.
import java.nio.ByteBuffer; import sdljava.SDLMain; import sdljava.SDLException; import sdljava.video.SDLRect; import sdljava.video.SDLVideo; import sdljava.video.SDLSurface; /** * Jedenacty demonstracni priklad vyuzivajici knihovnu SDLjava. * * Po spusteni se provede prepnuti do grafickeho rezimu 800x600x16 * Po inicializaci grafickeho rezimu se nacte bitmapa "xscorch.bmp", * ktera je upravena (pixel po pixelu). * Bitmapa nasledne vykresli na obrazovku operaci typu BitBLT a navic * se ulozi do souboru s nazvem "xscorch2.bmp". * * @author Pavel Tisnovsky */ public class SDLTest11 { /** * Horizontalni rozliseni vybraneho grafickeho rezimu. */ private static final int GFX_WIDTH = 800; /** * Vertikalni rozliseni vybraneho grafickeho rezimu. */ private static final int GFX_HEIGHT = 600; /** * Bitova hloubka vybraneho grafickeho rezimu. */ private static final int GFX_BPP = 16; /** * Nazev bitmapy, ktera se ma nacist a nasledne zobrazit. */ private static final String INPUT_IMAGE_NAME = "xscorch.bmp"; /** * Nazev vysledne bitmapy. */ private static final String OUTPUT_IMAGE_NAME = "xscorch2.bmp"; /** * Vykresleni bitmapy na obrazovku. * * @param screen * framebuffer * @param bitmap * bitmapa, ktery se ma na obrazovku vykreslit */ private static void drawOnScreen(SDLSurface screen, SDLSurface bitmap) throws SDLException { // vypocitat umisteni bitmapy na obrazovce. final SDLRect rect = computePositionOnScreen(screen, bitmap); // provest operaci typu BitBLT bitmap.blitSurface(screen, rect); // nutno volat i v pripade, ze neni pouzit double buffering screen.updateRect(); screen.flip(); } /** * Vypocitat umisteni bitmapy na obrazovce. * * @param screen * framebuffer * @param bitmap * bitmapa, ktery se ma na obrazovku vykreslit * @return obdelnik predstavujici pozici bitmapy na obrazovce */ private static SDLRect computePositionOnScreen(SDLSurface screen, SDLSurface bitmap) { // ziskat rozmery obrazovky i bitmapy final int screenWidth = screen.getWidth(); final int screenHeight = screen.getHeight(); final int bitmapWidth = bitmap.getWidth(); final int bitmapHeight = bitmap.getHeight(); // vypocitat umisteni bitmapy na obrazovce final int x = (screenWidth - bitmapWidth) >> 1; final int y = (screenHeight - bitmapHeight) >> 1; return new SDLRect(x, y, bitmapWidth, bitmapHeight); } /** * Uprava bitmapy na urovni jednotlivych pixelu. * * @param bitmap * @throws SDLException */ private static void updateBitmap(SDLSurface bitmap) throws SDLException { bitmap.lockSurface(); ByteBuffer pixelBuffer = bitmap.getPixelData(); // negace barvovych slozek vsech pixelu for (int i = 0; i < pixelBuffer.capacity(); i++) { pixelBuffer.put(i, (byte) ~pixelBuffer.get(i)); } bitmap.unlockSurface(); } /** * Spusteni jedenacteho demonstracniho prikladu. */ public static void main(String[] args) { try { // inicializace video subsystemu knihovny SDL. SDLMain.init(SDLMain.SDL_INIT_VIDEO); // inicializace celoobrazovkoveho grafickeho rezimu final SDLSurface screen = SDLVideo.setVideoMode(GFX_WIDTH, GFX_HEIGHT, GFX_BPP, SDLVideo.SDL_FULLSCREEN); // nacteni bitmapy z externiho souboru final SDLSurface bitmap = SDLVideo.loadBMP(INPUT_IMAGE_NAME); // vykresleni bitmapy na obrazovku drawOnScreen(screen, bitmap); Thread.sleep(2000); // uprava pixelu v bitmape updateBitmap(bitmap); // vykresleni bitmapy na obrazovku drawOnScreen(screen, bitmap); Thread.sleep(2000); bitmap.saveBMP(OUTPUT_IMAGE_NAME); } catch (Exception e) { e.printStackTrace(); } finally { // musime obnovit puvodni graficky rezim // i v tom pripade, ze nastane nejaka vyjimka SDLMain.quit(); } } }
13. Repositář se zdrojovými kódy všech čtyř dnešních demonstračních příkladů
Všechny čtyři dnešní demonstrační příklady byly společně s podpůrnými skripty určenými pro jejich překlad a následné spuštění uloženy 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 dvou dílech, 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:
Pro správnou činnost demonstračních příkladů SDLTest9 a SDLTest11 je nutné mít v pracovním adresáři uložen i obrázek xscorch.bmp:
# | Bitmapa | Umístění souboru v repositáři |
---|---|---|
1 | xscorch.bmp | http://icedtea.classpath.org/people/ptisnovs/jvm-tools/raw-file/6fe0d3a6edeb/sdljava/img/xscorch.bmp |
14. Odkazy na Internetu
- 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 - 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