{"id":2240,"date":"2013-09-03T21:11:19","date_gmt":"2013-09-03T19:11:19","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=2240"},"modified":"2013-09-03T21:11:19","modified_gmt":"2013-09-03T19:11:19","slug":"doppelpufferung-double-buffering","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2013\/09\/doppelpufferung-double-buffering\/","title":{"rendered":"Doppelpufferung (Double-Buffering)"},"content":{"rendered":"<p>Stellt eine paint()-Methode (und damit paintComponent()) komplexe Zeichnungen dar, die zum Beispiel aus hunderten kleiner Linien und B\u00f6gen besteht, so braucht das seine Zeit. Zeichnen wir in paint() immer alles neu, so haben wir gro\u00dfe Zeitverz\u00f6gerungen, wenn sich etwa die Gr\u00f6\u00dfe des Zeichenbereichs \u00e4ndert und der Repaint-Manager \u00fcber ein repaint() wieder zum paint() f\u00fchrt und alles wieder neu gezeichnet wird, ohne dass sich wirklich etwas an der Grafik ge\u00e4ndert hat.<\/p>\n<p>Eine einfache und elegante Methode, diesem Problem zu entkommen, ist die Technik der Doppelpufferung (engl. double-buffering). Eine zweite Zeichenebene, so gro\u00df wie das Original, wird angelegt und alle Grafikoperationen finden auf diesem Hintergrundbild statt. Immer wenn das zu zeichnende Bild komplett ist, kopieren wir es zur passenden Zeit in den sichtbaren Bereich. Kommt ein Repaint-Ereignis, und hat sich die Grafik bis dahin nicht aktualisiert, so muss nur der entsprechende Teil der Hintergrundgrafik neu gezeichnet werden.<\/p>\n<p>Um ein Programm auf die neue Technik umzustellen, muss zuerst die paint()-Methode umgebaut werden, die direkt die Zeichenbefehle erteilt. Nehmen wir folgende Implementierung an:<\/p>\n<pre>private void bigPaint( Graphics g )\n{\n Random r = new Random();\n\n for ( int i = 0; i &lt; 1000; i++ )\n {\n  g.drawOval( r.nextInt(getWidth()-100), r.nextInt(getHeight()-100), 100, 100 );\n  g.setColor( new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255), r.nextInt(255)) );\n }\n}\n\n@Override protected void paintComponent( Graphics g )\n{\n bigPaint( g );\n}<\/pre>\n<p>Um das Programm schon etwas zu vereinfachen, enth\u00e4lt paintComponent() nun schon keine direkten Zeichenbefehle mehr, sondern delegiert an das eigene bigPaint(). Im n\u00e4chsten Schritt m\u00fcssen wir das Programm so umbauen, dass es auf das Graphics-Objekt unseres Hintergrundbildes geht. Dazu ist zuerst ein Hintergrundbild n\u00f6tig. Eine Variante ist, die den Hintergrundpuffer in paintComponent() aufzubauen, denn dann gibt es Zugriff auf die H\u00f6hen und Breiten, die sich ver\u00e4ndert haben k\u00f6nnen \u2013 nat\u00fcrlich kann auch die Fl\u00e4che immer gleich gro\u00df bleiben.<\/p>\n<pre>import java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.util.Random;\nimport javax.swing.JFrame;\nimport javax.swing.JPanel;\n\npublic class DoubleBuffering extends JPanel\n{\n private void bigPaint( Graphics g )\n {\n  g.setColor( Color.WHITE ); g.fillRect( 0, 0, getWidth(), getHeight() );\n\n  Random r = new Random();\n\n  for ( int i = 0; i &lt; 1000; i++ )\n  {\n   g.drawOval( r.nextInt(getWidth()-100), r.nextInt(getHeight()-100), 100, 100 );\n   g.setColor( new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255), r.nextInt(255)) );\n  }\n }\n\n private final GraphicsConfiguration gfxConf = GraphicsEnvironment\n                         .getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();\n\n private BufferedImage offImg;\n\n @Override protected void paintComponent( Graphics g )\n {\n  if ( offImg == null || offImg.getWidth() != getWidth() || offImg.getHeight() != getHeight() )\n  {\n   offImg = gfxConf.createCompatibleImage( getWidth(), getHeight() );\n   bigPaint( offImg.createGraphics() );\n  }\n\n  g.drawImage( offImg, 0, 0, this );\n  \/\/ bigPaint( g );\n }\n\n public static void main( String[] args )\n {\n  JFrame f = new JFrame();\n  f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );\n  f.setSize( 800, 600 );\n  f.add( new DoubleBuffering() );\n  f.setVisible( true );\n }\n}<\/pre>\n<p><b>Hinweis<\/b>: Oft f\u00e4llt ein Flackern bei Grafikoperationen auf. Das Problem ist, dass die Zeichenoperationen so lange dauern, dass die gesamte Zeichnung nicht im Zyklus einer Bildschirmwiederholfrequenz auf den Schirm kommt. W\u00e4hrend Teile gezeichnet werden, sendet die Grafikkarte die Teilbilder zum Display und bei jedem Update sehen wir einen aktualisierten Teil unserer Grafik. Bei aufw\u00e4ndigen Zeichenoperationen sind nun einmal viele Durchl\u00e4ufe n\u00f6tig, bis das Bild komplett ist.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Stellt eine paint()-Methode (und damit paintComponent()) komplexe Zeichnungen dar, die zum Beispiel aus hunderten kleiner Linien und B\u00f6gen besteht, so braucht das seine Zeit. Zeichnen wir in paint() immer alles neu, so haben wir gro\u00dfe Zeitverz\u00f6gerungen, wenn sich etwa die Gr\u00f6\u00dfe des Zeichenbereichs \u00e4ndert und der Repaint-Manager \u00fcber ein repaint() wieder zum paint() f\u00fchrt und [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[1],"tags":[],"class_list":["post-2240","post","type-post","status-publish","format-standard","hentry","category-allgemein"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2240","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=2240"}],"version-history":[{"count":1,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2240\/revisions"}],"predecessor-version":[{"id":2241,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2240\/revisions\/2241"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=2240"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=2240"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=2240"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}