Java2D a dokonalé vykreslování

Java Pro pokročilé Java2D a dokonalé vykreslování

Tenhle článek bude pojednávat o stylech vykreslování a jak z Javy vyždímat to nejlepší ;-) Budu předpokládat, že již něco máte v hlavě, tudíž nebudu základní věci rozebírat dopodrobna.

Tipy na lepší výkon

Akcelerujeme

Jako první věc co bych udělal pro zvýšení výkonu naší aplikace při vykreslování, je zapnutí opengl. Defaultně je toto vypnuté, ale dokáže to zvýšit fps o stovky, i když fps většinou u her omezujeme z jistých důvodů, ale o tomhle tento článek nepojednává. Takže jak na to? Stačí při spouštění aplikace dopsat někam tento řádek:

System.setProperty("sun.java2d.opengl","True");
Poznámka

Tohle funguje na linuxech hlavně, u Windowsů je potřeba stejným způsobem vypnout Direct3D a DirectDraw. (d3d a noddraw)

Antialiasing

Další věc co stojí za zmínku je antialiasing. Kdo neví co je antialiasing tak si to vygooglí, v kostce to text, obrazce a jiné věci pěkně vyhladí a není to pak takové kostrbaté. Opět stačí na to jeden řádek, který využívá metodu třídy Graphics2D/Grap­hics.

instanceTridyGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

Ukázka(nahoře s antialiasingem a dole bez):

Antialiasing

Antialiasing

Další postřehy

Jako další věc co bych uvedl je metoda, která informuje hardware o tom, že je potřeba překreslení, tedy lépe synchronizuje komunikaci s HW.

Toolkit.getDefaultToolkit().sync();

Tuto řádku bych doporučil dávat na konec metody, která vykresluje.

Poznámka

Důležité hlavně u linuxů kde se obrazovka celého systému nepřekresluje nějak moc často, u Windowsů není potřeba používat.

Na vykreslování raději používejte metodu paintComponent, má větší prioritu než paint a tím pádem je i rychlejší.

Poznámka

Ještě rychlejší je použít aktivní renderování, což znamená že si uděláme vlastní metodu na kreslení, kde si přes metodu komponenty zavoláme o instanci Graphics a kreslíme co potřebujeme.

Při aktivním renderování je třeba použít metodu setIgnoreRepaint, která zapříčiní že se nebudou volat metody paint a paintComponent, které by mohli zapříčinit nějakou nekalost při vykreslování.

Pokud by Vás zajímalo víc takových "vychytávek", tak doporučuji tuto stránku: http://docs.oracle.com/…d/flags.html

Jak vykreslovat

Existuje spoustu postupů, možností jak vykreslovat, nejlepší budou nejspíš ty co jsou již ověřené a napsané. Já začnu od těch podle mého názoru nejpomalejších. Neberte to jako 100% pravdivé informace, jsou to jen moje postřehy.

Normální vykreslování

Toto snad bude každému jasné, prostě máme ve třídě přepsanou metodu paint, kterou voláme pomocí repaint(), a v ní nějaké ty příkazy na vykreslení. Na hry to sice není nejvhodnější, ale na jednoduché ukázkové programy/hry to stačí.

Page Flipping

Moc často se s tím nesetkáte, k dispozici je to většinou jen u FSEM (Full-Screen Exclusive Mode) a například bufferstrategy už to má v sobě, takže není třeba o tom něco vědět.

Double Buffering

Toto je jeden vůbec z nejrozšířenějších způsobů. Double Buffering bych rozdělil do pár menších podkapitol, protože způsobů jak to udělat je mnoho, takže uvedu aspoň pár z nich.

Jak to funguje?

Nejspíš nebude na škodu si nejdříve vysvětlit jak to vůbec funguje. Princip je vlastně jednoduchý, nejdřív si všechno vyrenderujeme(vy­kreslíme) na nějaký BufferedImage a pak jej teprve vykreslíme, tím předejdeme různým blikáním a podivnostem při vykreslování.

Vytvoření ideálního BufferedImage

Tady Vám poskytnu metodu, která ukazuje jak získat ideální obrázek na renderování pro každý počítač.

private BufferedImage toCompatibleImage(BufferedImage image)
{
        GraphicsConfiguration cfg = GraphicsEnvironment.
                getLocalGraphicsEnvironment().getDefaultScreenDevice().
                getDefaultConfiguration();

        //pokud již je obrázek ideální tak ho prostě vrátí zpět
        if (image.getColorModel().equals(cfg.getColorModel()))
                return image;

        //pokud není tak ho vytvoříme
        BufferedImage new_image = cfg.createCompatibleImage(
                        image.getWidth(), image.getHeight(), image.getTransparency());

        //a vrátíme ho
        return new_image;
}

Tuto metodu bych volal někde v konstruktoru GUIcka při inicializování BufferedImage.

Jednoduchý Double Buffering

Toto je úplně snadné, v nějaké smyčce která nám obstarává chod hry/aplikace, budeme volat 2 metody, render, kterou si vytvoříme a tam budeme vykreslovat celou hru do nějakého BufferedImage a repaint která nám zavolá paint nebo paintComponent která bude už jen vykreslovat ten náš narenderovaný obrázek.

Použití VolatileImage

Používání VolatileImage je trošku složitější (ukládá se do VRAM v grafické kartě) a je zakomponován v dalším příkladě, ale alespoň si ukážeme jak na to.

Nejdříve si musíme deklarovat dvě důležíté proměnné, které budeme hojně používat při validaci obrázku.

private static GraphicsEnvironment env=GraphicsEnvironment.getLocalGraphicsEnvironment();
private static GraphicsConfiguration conf=env.getDefaultScreenDevice().getDefaultConfiguration();

Dále deklarace obrázku pro render.

private VolatileImage renderImage;

Inicializovat budeme v konstruktoru GUIcka takto.

renderImage=createRenderImage(WIDTH,HEIGHT,Transparency.OPAQUE);
//WIDTH a HEIGHT si stanovíte sami ;)

Teď si budeme muset vytvořit tu metodu createRenderI­mage.

private VolatileImage createRenderImage(int width, int height, int transparency){
                VolatileImage image=null;

                image=conf.createCompatibleVolatileImage(width, height, transparency);

                int valid=image.validate(conf);

                if(valid==VolatileImage.IMAGE_INCOMPATIBLE){
                        image=this.createRenderImage(width, height, transparency);
                        return image;
                }

                return image;
        }

Nyní se podíváme jak napsat metodu pro render.

private void render(){
                Graphics2D g2=null;

                do{

                        int valid=renderImage.validate(conf);

                        if(valid==VolatileImage.IMAGE_INCOMPATIBLE){
                                renderImage=createRenderImage(WIDTH,HEIGHT,Transparency.OPAQUE);
                        }

                        try{
                                g2=renderImage.createGraphics();

                                g2.clearRect(0,0,WIDTH,HEIGHT);

                                //nějaké to vykreslování
                                }
                        }finally{
                                g2.dispose();
                        }

                }while(renderImage.contentsLost()); //dělá dokud se to zprávně neuloží do obrázku
        }

No a nakonec si v metodě pro vykreslení přidáme toto a máme hotovo.

if(renderImage.validate(conf)!=VolatileImage.IMAGE_OK){
                        renderImage=createRenderImage(WIDTH,HEIGHT,Transparency.OPAQUE);
                        render();
                }

                if(renderImage!=null){
                        instanceTridyGraphics2D.drawImage(renderImage, 0, 0, getWidth(), getHeight(), null);
                }
Použití BufferStrategy

Jako poslední si ukážeme BufferStrategy, podle mého názoru i spoustu jiných je to nejjednodušší a zároveň i nejefektivnější cesta pro vykreslování.

Nejdříve musíme BufferStrategy vytvořit. To lze buď v Frame, JFrame nebo Canvas (možná lze v Dialog/JDialog ale nezkoušel jsem), pokud to budete chtít dostat do JPanel tak jedině přes parametr v konstruktoru to poslat.

createBufferStrategy(2); //dvojka znamená že budou 2 buffery, můžeme použít klidně i 3

Po vytvoření se lze k BS dostat přes metodu getBufferStra­tegy() a uložit si do nějaké proměnné abychom ji nemuseli furt volat.

Když máme BufferStrategy tak nepotřebujeme volat žádný repaint, nebo jiné blbosti. Stačí nám jedna metoda kde se udělá vše, vyrenderuje a vykreslí. Taková metoda by mohla vypadat nějak takhle. (volat se samozřejmně bude v nějaké smyčce)

private void updateGui() {
                Graphics2D g2 = (Graphics2D)bs.getDrawGraphics();

                g2.setColor(Color.WHITE);
                g2.fillRect(0, 0, getWidth(), getHeight());

                //vykreslení něčeho

                g2.dispose();

                bs.show(); //zobrazení

                Toolkit.getDefaultToolkit().sync();
        }

Konec

Tohle je ode mně vše. Doufám, že pro Vás bude článek užitečný, sepsal jsem snad vše co umím ohledně tohoto tématu. Pokud si časem ještě na něco vzpomenu udělám edit ;-) Jakékoliv připomínky, dotazy pište do komentů :-)


 

  Aktivity (1)

Článek pro vás napsal Fugiczek
Avatar

Jak se ti líbí článek?
Celkem (7 hlasů) :
4.857144.857144.857144.857144.85714


 


Miniatura
Všechny články v sekci
Java - Pro pokročilé
Miniatura
Následující článek
Obfuskace Java kódu

 

 

Komentáře

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:

Dobrý článek, zvážil bych dodání nějakých obrázků (bez antialiasingu a s ním), aby to člověk viděl, názorné tutoriály se nejlépe zapamatují :)

Odpovědět 21.9.2012 22:27
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Fugiczek
Redaktor
Avatar
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Fugiczek
David Čápka:

Prostě je vidět, že víš, kam sáhnout, že víš, o čem mluvíš. Jsem rád, že tu takové lidi máme :)

Odpovědět  +3 21.9.2012 22:39
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 3 zpráv z 3.