Pohled pod kapotu JVM – double buffering v praxi (dokončení)

7. 1. 2014
Doba čtení: 20 minut

Sdílet

V dnešní části seriálu o jazyce Java i o virtuálním stroji Javy dokončíme popis způsobů využití double bufferingu v javovských aplikacích založených na knihovnách AWT či Swing. Ukážeme si taktéž způsob využití třídy BufferStrategy při explicitním vytvoření zadního bufferu, a to i v celoobrazovkovém režimu.

Obsah

1. Pohled pod kapotu JVM – double buffering v praxi (dokončení)

2. První demonstrační příklad: vykreslování do objektu typu Canvas bez použití double bufferingu

3. Úprava příkladu takovým způsobem, aby se vykreslování provádělo do zadního bufferu

4. Druhý demonstrační příklad: vykreslování do objektu typu Canvas s využitím double bufferingu

5. Double buffering a exkluzivní celoobrazovkové grafické režimy

6. Nastavení celoobrazovkového grafického režimu a vytvoření zadního bufferu

7. Třetí demonstrační příklad: double buffering v celoobrazovkovém grafickém režimu

8. Repositář se zdrojovými kódy dnešních demonstračních příkladů

9. Odkazy na Internetu

1. Pohled pod kapotu JVM – double buffering v praxi (dokončení)

V předchozí části seriálu o programovacím jazyce Java i o virtuálním stroji Javy jsme si řekli, jakým způsobem je double buffering použit v knihovně Swing a jak je možné v případě potřeby využití zadního bufferu vypnout. Dnes si na několika demonstračních příkladech ukážeme využití double bufferingu při výběru knihovny AWT namísto Swing a při vykreslování grafiky s využitím celoobrazovkových exkluzivních grafických režimů (kde lze taktéž využít možností nabízených knihovnou AWT). Ihned na začátku tohoto článku je možná vhodné si připomenout základní rozdíly mezi knihovnami AWT (Abstract Window Toolkit) a Swing. Knihovna AWT se snaží využívat nativní ovládací prvky (widgety), takže na tuto knihovnu lze pohlížet jako na určitý most mezi Javou a nativními toolkity. To například znamená, že vzhled ovládacích prvků i jejich chování je možné měnit jen v určitých (relativně malých) mezích a současně se vzhled aplikace může na různých platformách odlišovat (což někdy může být vhodné chování).

Naproti tomu jsou v knihovně Swing všechny komponenty (ovládací prvky atd.) vykreslovány s využitím metod naprogramovaných v Javě s tím, že nízkoúrovňové kreslicí operace jsou implementovány v nativních funkcích resp. voláním metod Java2D (teprve ty interně volají nativní metody). Díky tomu je možné do značné míry měnit vzhled i chování všech ovládacích prvků, zajistit stejný vzhled aplikace na všech systémech apod. Navíc je při vykreslování všech komponent implicitně používán double buffering, což je problematika, kterou jsme se podrobněji zabývali minule.

Zatímco většina javovských aplikací s GUI již využívá možnosti knihovny Swing, je při návrhu hry či jiné graficky náročné aplikace, která využívá celoobrazovkové režimy, situace poněkud odlišná, protože zde většinou nevyužijeme všech vlastností Swingu a navíc může být použití swingovských komponent vykreslovaných s využitím kódu napsaného v Javě zbytečně limitující a pomalé. Proto si dnes ukážeme, jak lze využít komponenty java.awt.Framejava.awt.Canvas pro tvorbu základní kostry grafického subsystému hry.

2. První demonstrační příklad: vykreslování do objektu typu Canvas bez použití double bufferingu

První demonstrační příklad je velmi jednoduchý, ovšem poměrně dobře nám ukazuje některé základní vlastnosti knihovny AWT. Po spuštění tohoto příkladu se vytvoří nové okno představované instancí třídy odvozené od třídy java.awt.Frame. Do okna je vložen prvek typu java.awt.Canvas, resp. přesněji řečeno se opět jedná o instanci třídy odvozené od java.awt.Canvas. Důležité je, že v odvozené třídě došlo k překrytí metody public void paint(Graphics g), díky čemuž je možné na Canvas (kreslicí plátno) vykreslovat libovolný obrazec. Zde však narazíme na jednu potenciálně nepříjemnou vlastnost – vzhledem k tomu, že se v knihovně AWT implicitně nepoužívá double buffering, bude na pomalejších počítačích viditelné pomalé překreslování obrazce, což je nejlépe patrné při postupném zvětšování a/nebo zmenšování okna (rámce). Pokud je překreslování na vašem počítači dostatečně rychlé, je možné nežádoucí grafické efekty jednoduše vyvolat tak, že se změní krok vykreslovací smyčky z hodnoty 0.005 na hodnotu 0.001 či ještě nižší hodnotu. Aplikace se jednoduše ukončí stiskem tlačítka myši kamkoli na kreslicí plochu.

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
 
 
 
/**
 * Test vykreslovani na objekt typu Canvas v pripade, ze neni
 * povoleno pouzivani double bufferingu.
 *
 * @author Pavel Tisnovsky
 */
public class DoubleBufferTest1 extends Frame {
 
    /**
     * Generated serial version UID.
     */
    private static final long serialVersionUID = 6423061537074933572L;
 
    /**
     * Kreslici plocha - Canvas.
     */
    private Drawing1 drawing = new Drawing1();
 
    /**
     * Vytvoreni okna a pridani kresby do tohoto okna.
     */
    private void run() {
        // pri postupnem zvetsovani okna bude prosvitat modre pozadi
        this.setBackground(Color.BLUE);
        // potrebujeme aktivni okraje okna
        this.setUndecorated(false);
        // pridani kresby do okna
        this.add(this.drawing);
        this.pack();
        this.setVisible(true);
    }
 
    /**
     * Spusteni testu.
     */
    public static void main(String[] args) {
        new DoubleBufferTest1().run();
    }
 
}
 
 
 
/**
 * Kreslici plocha odvozena od objektu typu Canvas.
 *
 * @author Pavel Tisnovsky
 */
class Drawing1 extends Canvas {
 
    /**
     * Generated serial version UID.
     */
    private static final long serialVersionUID = -3936987144006140956L;
 
    /**
     * Konstruktor - nastaveni zakladnich parametru kreslici plochy.
     */
    public Drawing1() {
        this.setBackground(Color.WHITE);
        this.setPreferredSize(new Dimension(800, 600));
        addCustomMouseListener();
    }
 
    /**
     * Mouse listener zajisti ukonceni programu po kliku na kreslici plochu.
     */
    private void addCustomMouseListener() {
        addMouseListener(new MouseListener() {
 
            public void mouseClicked(MouseEvent e) {
                System.exit(0);
            }
 
            public void mousePressed(MouseEvent e) {
                // prazdny blok
            }
 
            public void mouseReleased(MouseEvent e) {
                // prazdny blok
            }
 
            public void mouseEntered(MouseEvent e) {
                // prazdny blok
            }
 
            public void mouseExited(MouseEvent e) {
                // prazdny blok
            }});
    }
 
    @Override
    public void paint(Graphics g) {
        final Graphics2D graphics2d = (Graphics2D)g;
        graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        final int width = this.getWidth();
        final int height = this.getHeight();
        final double radius = Math.min(width, height) / 3.0;
 
        // casove narocne vykresleni obrazce
        // (na rychlejsich pocitacich lze zmenit krok 0.005 na 0.001)
        int xold=0, yold=0;
        for (double d = 0; d < Math.PI *2*7; d += 0.005) {
            int x = (int)(width / 2 + radius * Math.cos(d) + radius/2 * Math.sin(d/7));
            int y = (int)(height / 2 + radius * Math.sin(d) + radius/2 * Math.cos(d/7));
            graphics2d.setColor(Color.BLACK);
            if (d>0) {
                graphics2d.drawLine(xold, yold, x, y);
            }
            xold = x;
            yold = y;
        }
    }
 
}

3. Úprava příkladu takovým způsobem, aby se vykreslování provádělo do zadního bufferu

Nežádoucí postupné vykreslování obrazce je možné jednoduše před uživateli skrýt takovým způsobem, že se obrazec nejprve vykreslí do neviditelného zadního bufferu a teprve poté se buď přesune do viditelného předního bufferu, popř. dojde k velmi rychlému přepnutí mezi předním a zadním bufferem. Pro explicitní řízení double bufferingu se používá třída BufferStrategy, o níž jsme se zmínili minule. Připomeňme si tedy, že se instance této třídy vytvoří zavoláním java.awt.Canvas.createBuf­ferStrategy(int) popř. (což je asi méně časté) zavoláním metody java.awt.Window.createBuf­ferStrategy(int). Celočíselná hodnota, která se těmto metodám předává, reprezentuje celkový počet bufferů. Přitom je zaručeno, že se daný počet bufferů skutečně vytvoří, ale v závislosti na možnostech konkrétního počítače nemusí být podporován page flipping a dokonce ani akcelerované přenášení obsahu zadního bufferu do bufferu předního.

Jakmile je korektně nastaven double buffering, změní se i vykreslovací rutina, protože ta již nebude provádět vykreslování přímo na java.awt.Canvas s využitím objektu typu Graphics předaného metodě public void paint(), ale do zadního bufferu:

BufferStrategy bufferStrategy = canvas.getBufferStrategy();
 
while (!done) {
    Graphics graphics;
    try {
        graphics = bufferStrategy.getDrawGraphics();
        ... zde se provádí vykreslování ...
        ... s využitím objektu "graphics" ...
    } finally {
        graphics.dispose();
    }
    bufferStrategy.show();
}

V našem konkrétním případě dojde ve vykreslovací rutině k následující změně (zde je použit formát unifikovaného diffu):

--- DoubleBufferTest1.java      2014-01-05 21:21:39.000000000 +0100
+++ DoubleBufferTest2.java      2014-01-05 21:21:50.000000000 +0100
@@ -1,6 +1,9 @@
     @Override
     public void paint(Graphics g) {
-        final Graphics2D graphics2d = (Graphics2D)g;
+        // ziskat objekt typu Graphics2D pro zadni buffer
+        BufferStrategy bufferStrategy = getBufferStrategy();
+        final Graphics2D graphics2d = (Graphics2D)bufferStrategy.getDrawGraphics();
+
         graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
         final int width = this.getWidth();
         final int height = this.getHeight();
@@ -18,4 +21,7 @@
             xold = x;
             yold = y;
         }
+        graphics2d.dispose();
+        // prohozeni bufferu a/nebo prekresleni zadniho bufferu do bufferu predniho
+        bufferStrategy.show();
     }

Grafické zvýraznění rozdílu mezi oběma vykreslovacími rutinami:

4. Druhý demonstrační příklad: vykreslování do objektu typu Canvas s využitím double bufferingu

Dnešní druhý demonstrační příklad již při vykreslování do objektu typu java.awt.Canvas využívá double buffering. Princip práce vykreslovací rutiny jsme si vysvětlili v předchozí kapitole, ovšem důležitá je taktéž rutina použitá pro vytvoření předního a zadního bufferu. Tato rutina je součástí metody DoubleBufferTest2.run(), v níž se konfigurace bufferů provádí až poté, kdy je objekt odvozený od java.awt.Canvas vložen do okna/rámce. To je důležité, protože v případě, že by objekt java.awt.Canvas ještě nebyl vložen do jiné komponenty, došlo by k běhové chybě (což si můžete snadno otestovat):

    /**
     * Vytvoreni okna a pridani kresby do tohoto okna.
     */
    private void run() {
        // pri postupnem zvetsovani okna bude prosvitat modre pozadi
        this.setBackground(Color.BLUE);
        // potrebujeme aktivni okraje okna
        this.setUndecorated(false);
        // pridani kresby do okna
        this.add(this.drawing);
        this.pack();
        // nastaveni double bufferingu
        this.drawing.createBufferStrategy(2);
        this.setVisible(true);
    }

Následuje výpis zdrojového kódu dnešního druhého demonstračního příkladu:

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferStrategy;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
 
 
 
/**
 * Test vykreslovani na objekt typu Canvas v pripade, ze je
 * povoleno pouzivani double bufferingu.
 *
 * @author Pavel Tisnovsky
 */
public class DoubleBufferTest2 extends Frame {
 
    /**
     * Generated serial version UID.
     */
    private static final long serialVersionUID = 6423061537074933572L;
 
    /**
     * Kreslici plocha - Canvas.
     */
    private Drawing2 drawing = new Drawing2();
 
    /**
     * Vytvoreni okna a pridani kresby do tohoto okna.
     */
    private void run() {
        // pri postupnem zvetsovani okna bude prosvitat modre pozadi
        this.setBackground(Color.BLUE);
        // potrebujeme aktivni okraje okna
        this.setUndecorated(false);
        // pridani kresby do okna
        this.add(this.drawing);
        this.pack();
        // nastaveni double bufferingu
        this.drawing.createBufferStrategy(2);
        this.setVisible(true);
    }
 
    /**
     * Spusteni testu.
     */
    public static void main(String[] args) {
        new DoubleBufferTest2().run();
    }
 
}
 
 
 
/**
 * Kreslici plocha odvozena od objektu typu Canvas.
 *
 * @author Pavel Tisnovsky
 */
class Drawing2 extends Canvas {
 
    /**
     * Generated serial version UID.
     */
    private static final long serialVersionUID = -3936987144006140956L;
 
    /**
     * Konstruktor - nastaveni zakladnich parametru kreslici plochy.
     */
    public Drawing2() {
        this.setBackground(Color.WHITE);
        this.setPreferredSize(new Dimension(800, 600));
        addCustomMouseListener();
    }
 
    /**
     * Mouse listener zajisti ukonceni programu po kliku na kreslici plochu.
     */
    private void addCustomMouseListener() {
        addMouseListener(new MouseListener() {
 
            public void mouseClicked(MouseEvent e) {
                System.exit(0);
            }
 
            public void mousePressed(MouseEvent e) {
                // prazdny blok
            }
 
            public void mouseReleased(MouseEvent e) {
                // prazdny blok
            }
 
            public void mouseEntered(MouseEvent e) {
                // prazdny blok
            }
 
            public void mouseExited(MouseEvent e) {
                // prazdny blok
            }});
    }
 
    @Override
    public void paint(Graphics g) {
        // ziskat objekt typu Graphics2D pro zadni buffer
        BufferStrategy bufferStrategy = getBufferStrategy();
        final Graphics2D graphics2d = (Graphics2D)bufferStrategy.getDrawGraphics();
 
        graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        final int width = this.getWidth();
        final int height = this.getHeight();
        final double radius = Math.min(width, height) / 3.0;
 
        // casove narocne vykresleni obrazce
        // (na rychlejsich pocitacich lze zmenit krok 0.005 na 0.001)
        int xold=0, yold=0;
        for (double d = 0; d < Math.PI *2*7; d += 0.005) {
            int x = (int)(width / 2 + radius * Math.cos(d) + radius/2 * Math.sin(d/7));
            int y = (int)(height / 2 + radius * Math.sin(d) + radius/2 * Math.cos(d/7));
            graphics2d.setColor(Color.BLACK);
            if (d>0) {
                graphics2d.drawLine(xold, yold, x, y);
            }
            xold = x;
            yold = y;
        }
        graphics2d.dispose();
        // prohozeni bufferu a/nebo prekresleni zadniho bufferu do bufferu predniho
        bufferStrategy.show();
    }
 
}

5. Double buffering a exkluzivní celoobrazovkové grafické režimy

Velmi časté je použití programově řízeného double bufferingu v těch případech, kdy aplikace (většinou nějaká hra) provádí vykreslování na celou obrazovku, tj. při využití exkluzivních celoobrazovkových grafických režimů. V předchozích částech tohoto seriálu jsme si již ukázali, jakým způsobem je možné provést přepnutí do celoobrazovkového grafického režimu a jak lze v případě potřeby využít rastrové obrázky typu VolatileImage namísto obecně pomalejších bitmap typu BufferedImage. Teoreticky je možné VolatileImage použít ve funkci zadního bufferu, ovšem většinou bývá výhodnější se spolehnout na to, že grafický subsystém počítače umožní v celoobrazovkovém režimu použít přední i zadní buffer s tím, že bude podporovat operaci typu flip pro prohození funkcí obou bufferů.

Proč je použití flip mnohem výhodnější než volání operace typu BitBlt pro VolatileImage, je zřejmé – v prvním případě se jedná o jednoduchou operaci, při níž se prakticky nepřenáší žádná data, kdežto v případě druhém je nutné přenést všechny pixely zdrojového obrázku do bufferu, a to mnohdy několikrát (20×) za sekundu. Tento přenos se sice v ideálním případě provádí pouze v rámci obrazové paměti (tudíž akcelerovaně), i tak se však z hlediska celkového výkonu a popř. i energetických požadavků (notebooky tablety atd.) nejedná o ideální způsob, jak implementovat double buffering.

6. Nastavení celoobrazovkového grafického režimu a vytvoření zadního bufferu

Nastavení celoobrazovkového grafického režimu je poměrně snadné, protože můžeme využít znalostí, o nichž jsme se v tomto seriálu již zmínili. Následující metoda nejprve nastaví parametry vybraného okna/rámce tak, aby bylo zobrazeno přes celou obrazovku (bez okrajů) a následně vybere a nastaví grafický režim s požadovaným rozlišením GRAPHICS_MODE_WIDTH×GRAPHIC­S_MODE_HEIGHT a libovolnou bitovou (barvovou) hloubkou:

    /**
     * Nastaveni grafickeho rezimu.
     */
    private void setFullScreenMode(Frame frame) {
        GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice graphicsDevice = env.getDefaultScreenDevice();
        graphicsDevice.setFullScreenWindow(frame);
        DisplayMode selectedDisplayMode = null;
        // pokusime se vyhledat pozadovany graficky rezim.
        for (DisplayMode displayMode : graphicsDevice.getDisplayModes()) {
            if (displayMode.getWidth() == GRAPHICS_MODE_WIDTH
                    && displayMode.getHeight() == GRAPHICS_MODE_HEIGHT) {
                selectedDisplayMode = displayMode;
                break;
            }
        }
        graphicsDevice.setDisplayMode(selectedDisplayMode);
    }

Při použití celoobrazovkového režimu navíc není ani nutné využívat objekt typu java.awt.Canvas. Namísto toho je vykreslování možné provádět přímo na rámec. Vzhledem k tomu, že se vůbec nepoužívá callback metoda public void paint(), je vhodné před zobrazením rámce zavolat metodu Frame.setIgnoreRepaint(true). Vlastní vykreslení obrazce je posléze „vynuceno“ programovým prohozením funkce předního a zadního bufferu:

    /**
     * Prekresleni obsahu ramce.
     */
    private void repaintFrame(Frame frame, double angle) {
        // ziskat objekt typu Graphics2D pro zadni buffer
        BufferStrategy bufferStrategy = frame.getBufferStrategy();
        final Graphics2D graphics2d = (Graphics2D) bufferStrategy.getDrawGraphics();
 
        graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        final int width = frame.getWidth();
        final int height = frame.getHeight();
        final double radius = Math.min(width, height) / 3.0;
 
        // casove narocne vykresleni obrazce
        // (na rychlejsich pocitacich lze zmenit krok 0.005 na 0.001)
        int xold = 0, yold = 0;
        for (double d = 0; d < Math.PI * 2 * 7; d += 0.005) {
            int x = (int) (width / 2 + radius * Math.cos(d) + radius / 2 * Math.sin(d / 7 + angle));
            int y = (int) (height / 2 + radius * Math.sin(d) + radius / 2 * Math.cos(d / 7 + angle));
            graphics2d.setColor(Color.BLACK);
            if (d > 0) {
                graphics2d.drawLine(xold, yold, x, y);
            }
            xold = x;
            yold = y;
        }
        graphics2d.dispose();
 
        // prohozeni bufferu a/nebo prekresleni zadniho bufferu do bufferu
        // predniho
        bufferStrategy.show();
    }

7. Třetí demonstrační příklad: double buffering v celoobrazovkovém grafickém režimu

V dnešním třetím a současně i posledním demonstračním příkladu jsou využity principy vysvětlené v předchozích dvou kapitolách. Po spuštění aplikace je vytvořeno okno (rámec), následně jsou skryty okraje tohoto okna (dekorace) a posléze je nastaven celoobrazovkový grafický režim a obsah okna je zvětšen tak, aby obsáhl celou plochu obrazovky. Po těchto operacích je pro toto okno vytvořen přední a zadní buffer a poté se ve smyčce volá překreslovací rutina, která postupně (bez mazání předního či zadního bufferu) postupně dokresluje obrazec do následující podoby:

bitcoin_skoleni

Poté je aplikace automaticky ukončena. Úplný zdrojový kód tohoto příkladu je vypsán pod tímto odstavcem:

import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.image.BufferStrategy;
 
/**
 * Test vykreslovani v celoobrazovkovem grafickem rezimu pri povoleni
 * double bufferingu.
 * 
 * @author Pavel Tisnovsky
 */
public class DoubleBufferTest3 {
 
    /**
     * Generated serial version UID.
     */
    private static final long serialVersionUID = 6423061537074933572L;
 
    /**
     * Horizontalni rozliseni pozadovaneho grafickeho rezimu.
     */
    private static final int GRAPHICS_MODE_WIDTH = 1024;
 
    /**
     * Vertikalni rozliseni pozadovaneho grafickeho rezimu.
     */
    private static final int GRAPHICS_MODE_HEIGHT = 768;
 
    /**
     * Vytvoreni okna a pridani kresby do tohoto okna.
     */
    private void run() {
        Frame frame = createFrame();
        // nastaveni celoobrazovkoveho rezimu
        setFullScreenMode(frame);
        // vytvoreni zadniho bufferu
        frame.createBufferStrategy(2);
        // prekresleni ramce - jednoducha animace
        for (double angle = 0.0; angle < 1; angle += 0.1) {
            repaintFrame(frame, angle);
        }
        // ukonceni aplikace
        exitFromFullScreenMode();
        System.exit(0);
    }
 
    /**
     * Vytvoreni ramce a nastaveni jeho vlastnosti. 
     * @return
     */
    private Frame createFrame() {
        Frame frame = null;
        GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice graphicsDevice = env.getDefaultScreenDevice();
        GraphicsConfiguration graphicsConfiguration = graphicsDevice.getDefaultConfiguration();
        // vytvoreni ramce
        frame = new Frame(graphicsConfiguration);
        // pozadi ramce
        frame.setBackground(Color.WHITE);
        // bez aktivnich okraju okna
        frame.setUndecorated(true);
        // nechceme, aby system sam okno prekreslil
        frame.setIgnoreRepaint(true);
        return frame;
    }
 
    /**
     * Nastaveni grafickeho rezimu.
     */
    private void setFullScreenMode(Frame frame) {
        GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice graphicsDevice = env.getDefaultScreenDevice();
        graphicsDevice.setFullScreenWindow(frame);
        DisplayMode selectedDisplayMode = null;
        // pokusime se vyhledat pozadovany graficky rezim.
        for (DisplayMode displayMode : graphicsDevice.getDisplayModes()) {
            if (displayMode.getWidth() == GRAPHICS_MODE_WIDTH
                    && displayMode.getHeight() == GRAPHICS_MODE_HEIGHT) {
                selectedDisplayMode = displayMode;
                break;
            }
        }
        graphicsDevice.setDisplayMode(selectedDisplayMode);
    }
 
    /**
     * Ukonceni grafickeho rezimu.
     */
    private void exitFromFullScreenMode() {
        GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice graphicsDevice = env.getDefaultScreenDevice();
        graphicsDevice.setFullScreenWindow(null);
    }
 
    /**
     * Prekresleni obsahu ramce.
     */
    private void repaintFrame(Frame frame, double angle) {
        // ziskat objekt typu Graphics2D pro zadni buffer
        BufferStrategy bufferStrategy = frame.getBufferStrategy();
        final Graphics2D graphics2d = (Graphics2D) bufferStrategy.getDrawGraphics();
 
        graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        final int width = frame.getWidth();
        final int height = frame.getHeight();
        final double radius = Math.min(width, height) / 3.0;
 
        // casove narocne vykresleni obrazce
        // (na rychlejsich pocitacich lze zmenit krok 0.005 na 0.001)
        int xold = 0, yold = 0;
        for (double d = 0; d < Math.PI * 2 * 7; d += 0.005) {
            int x = (int) (width / 2 + radius * Math.cos(d) + radius / 2 * Math.sin(d / 7 + angle));
            int y = (int) (height / 2 + radius * Math.sin(d) + radius / 2 * Math.cos(d / 7 + angle));
            graphics2d.setColor(Color.BLACK);
            if (d > 0) {
                graphics2d.drawLine(xold, yold, x, y);
            }
            xold = x;
            yold = y;
        }
        graphics2d.dispose();
 
        // prohozeni bufferu a/nebo prekresleni zadniho bufferu do bufferu
        // predniho
        bufferStrategy.show();
    }
 
    /**
     * Spusteni testu.
     */
    public static void main(String[] args) {
        new DoubleBufferTest3().run();
    }
 
}

8. Repositář se zdrojovými kódy dnešních demonstračních příkladů

Následují v tomto seriálu již tradiční odkazy na zdrojové kódy uložené do Mercurial repositáře. V tabulce zobrazené pod tímto odstavcem najdete linky na prozatím nejnovější verze všech tří dnes popsaných demonstračních příkladů:

9. Odkazy na Internetu

  1. Class java.awt.Frame (JDK7)
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Fra­me.html
  2. Class java.awt.Canvas (JDK7)
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Can­vas.html
  3. Class java.awt.image.BufferStrategy (JDK6)
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/i­mage/BufferStrategy.html
  4. Class java.awt.Graphics
    http://docs.oracle.com/ja­vase/1.5.0/docs/api/java/aw­t/Graphics.html
  5. Double Buffering and Page Flipping
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/doublebuf.html
  6. BufferStrategy and BufferCapabilities
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/bufferstrategy.html
  7. Java:Tutorials: Double Buffering
    http://content.gpwiki.org/in­dex.php/Java:Tutorials:Dou­ble_Buffering
  8. Double buffer in standard Java AWT
    http://www.codeproject.com/Ar­ticles/2136/Double-buffer-in-standard-Java-AWT
  9. Java 2D: Hardware Accelerating – Part 1 – Volatile Images
    http://www.javalobby.org/fo­rums/thread.jspa?threadID=16840&tstar­t=0
  10. Java 2D: Hardware Accelerating – Part 2 – Buffer Strategies
    http://www.javalobby.org/ja­va/forums/t16867.html
  11. How does paintComponent work?
    http://stackoverflow.com/qu­estions/15544549/how-does-paintcomponent-work
  12. A Swing Architecture Overview
    http://www.oracle.com/technet­work/java/architecture-142923.html
  13. Class javax.swing.JComponent
    http://docs.oracle.com/ja­vase/6/docs/api/javax/swin­g/JComponent.html
  14. Class java.awt.Component
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/Com­ponent.html
  15. Class java.awt.Component.BltBufferStrategy
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/Com­ponent.BltBufferStrategy.html
  16. Class java.awt.Component.FlipBufferStrategy
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/Com­ponent.FlipBufferStrategy­.html
  17. Metoda java.awt.Component.isDoubleBuffered()
    http://docs.oracle.com/ja­vase/6/docs/api/java/awt/Com­ponent.html#isDoubleBuffe­red()
  18. Metoda javax.swing.JComponent.is­DoubleBuffered()
    http://docs.oracle.com/ja­vase/6/docs/api/javax/swin­g/JComponent.html#isDouble­Buffered()
  19. Metoda javax.swing.JComponent.set­DoubleBuffered()
    http://docs.oracle.com/ja­vase/6/docs/api/javax/swin­g/JComponent.html#setDouble­Buffered(boolean)
  20. Javadoc – třída GraphicsDevice
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Grap­hicsDevice.html
  21. Javadoc – třída GraphicsEnvironment
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Grap­hicsEnvironment.html
  22. Javadoc – třída GraphicsConfiguration
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Grap­hicsConfiguration.html
  23. Javadoc – třída DisplayMode
    http://docs.oracle.com/ja­vase/7/docs/api/java/awt/Dis­playMode.html
  24. Lesson: Full-Screen Exclusive Mode API
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/
  25. Full-Screen Exclusive Mode
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/exclusivemode.html
  26. Display Mode
    http://docs.oracle.com/ja­vase/tutorial/extra/fullscre­en/displaymode.html
  27. Using the Full-Screen Exclusive Mode API in Java
    http://www.developer.com/ja­va/other/article.php/3609776/U­sing-the-Full-Screen-Exclusive-Mode-API-in-Java.htm
  28. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mobilefish.com/tu­torials/java/java_quickgu­ide_jvm_instruction_set.html
  29. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  30. MultiMedia eXtensions
    http://softpixel.com/~cwrig­ht/programming/simd/mmx.phpi
  31. SSE (Streaming SIMD Extentions)
    http://www.songho.ca/misc/sse/sse­.html
  32. Timothy A. Chagnon: SSE and SSE2
    http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf
  33. Intel corporation: Extending the Worldr's Most Popular Processor Architecture
    http://download.intel.com/techno­logy/architecture/new-instructions-paper.pdf
  34. SIMD architectures:
    http://arstechnica.com/ol­d/content/2000/03/simd.ar­s/
  35. GC safe-point (or safepoint) and safe-region
    http://xiao-feng.blogspot.cz/2008/01/gc-safe-point-and-safe-region.html
  36. Safepoints in HotSpot JVM
    http://blog.ragozin.info/2012/10/sa­fepoints-in-hotspot-jvm.html
  37. Java theory and practice: Synchronization optimizations in Mustang
    http://www.ibm.com/develo­perworks/java/library/j-jtp10185/
  38. How to build hsdis
    http://hg.openjdk.java.net/jdk7/hot­spot/hotspot/file/tip/src/sha­re/tools/hsdis/README
  39. Java SE 6 Performance White Paper
    http://www.oracle.com/technet­work/java/6-performance-137236.html
  40. Lukas Stadler's Blog
    http://classparser.blogspot­.cz/2010/03/hsdis-i386dll.html
  41. How to build hsdis-amd64.dll and hsdis-i386.dll on Windows
    http://dropzone.nfshost.com/hsdis.htm
  42. PrintAssembly
    https://wikis.oracle.com/dis­play/HotSpotInternals/Prin­tAssembly
  43. The Java Virtual Machine Specification: 3.14. Synchronization
    http://docs.oracle.com/ja­vase/specs/jvms/se7/html/jvms-3.html#jvms-3.14
  44. The Java Virtual Machine Specification: 8.3.1.4. volatile Fields
    http://docs.oracle.com/ja­vase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4
  45. The Java Virtual Machine Specification: 17.4. Memory Model
    http://docs.oracle.com/ja­vase/specs/jls/se7/html/jls-17.html#jls-17.4
  46. The Java Virtual Machine Specification: 17.7. Non-atomic Treatment of double and long
    http://docs.oracle.com/ja­vase/specs/jls/se7/html/jls-17.html#jls-17.7
  47. Open Source ByteCode Libraries in Java
    http://java-source.net/open-source/bytecode-libraries
  48. ASM Home page
    http://asm.ow2.org/
  49. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  50. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  51. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  52. BCEL Home page
    http://commons.apache.org/bcel/
  53. Byte Code Engineering Library (před verzí 5.0)
    http://bcel.sourceforge.net/
  54. Byte Code Engineering Library (verze >= 5.0)
    http://commons.apache.org/pro­per/commons-bcel/
  55. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  56. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  57. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  58. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  59. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  60. Javassist
    http://www.jboss.org/javassist/
  61. Byteman
    http://www.jboss.org/byteman
  62. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  63. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  64. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  65. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  66. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  67. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  68. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  69. aspectj (Eclipse)
    http://www.eclipse.org/aspectj/
  70. Aspect-oriented programming (Wikipedia)
    http://en.wikipedia.org/wi­ki/Aspect_oriented_program­ming
  71. AspectJ (Wikipedia)
    http://en.wikipedia.org/wiki/AspectJ
  72. EMMA: a free Java code coverage tool
    http://emma.sourceforge.net/
  73. Cobertura
    http://cobertura.sourceforge.net/
  74. jclasslib bytecode viewer
    http://www.ej-technologies.com/products/jclas­slib/overview.html

Autor článku

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