Link to home
Start Free TrialLog in
Avatar of miredo
miredo

asked on

How does this double buffering code work....

I've been posting questions pertaining to this silly little applet for awhile now - finally it looks the way I want, but I have no idea why this double buffering code works.  I cut-and-pasted it from the Sun Microsystems site.

Questions:
1) I have a component (a CirGge) contained in an applet (a GgeApl) and both have a paint() method.  The container's paint method utilizes double buffering, the component's paint method draws the component.  I suppose both of these get called, but I don't see how the component somehow gets drawn in the offscreen buffer.  How does the container's paint() access the info in the components paint().  I hope my question is clear.

2) What's the reason for calling super.invalidate()?
(why call it on the parent?)

3) Is there a reason to not declare Graphics bufferGraphics (in applet's paint() method) at the beginning of the applet.   As it is the applet has to recreate that Graphics object over and over.



**************************************************
// GgeApl.java
// This applet displays a lightweight component gauge that is fed
// values by a random number generator.  The component is a black
// square with a white circle inside it.  The circle shrinks and
// swells in response to the values the generator feeds it.

/*
   <applet
   code="GgeApl" width=200 height=200>
      </applet>
*/
 

  import java.util.*;
  import java.text.*;
  import java.awt.*;
  import java.awt.event.*;
  import java.applet.*;
 
  // the applet
  public class GgeApl extends Applet
                      implements Runnable
  {  
     // Will be the width of the applet and lightweight
     // component (square shape).
     int siz = 200;
     
     // Width of applet.
     int aplWid;
     
     // Give applet a gauge.
     CirGge gge;
     
     // Give applet a value generator to pass values to the gauge.
     ValGen gen;
     
     // The value.
     int newVal;
     
     // Image buffer for double buffering.
     Image buffer;
     
     public void invalidate()
     {
        super.invalidate();
        buffer = null;
     }
     
     public void update(Graphics g)
     {
        paint(g);
     }
     
     public void paint (Graphics g)
     {
        if (buffer == null)
        {
           buffer = createImage(getSize().width, getSize().height);
        }
        Graphics bufferGraphics = buffer.getGraphics();
        bufferGraphics.setClip(0,0,getSize().width, getSize().height);
        super.paint(bufferGraphics);
        g.drawImage(buffer,0,0,null);
        bufferGraphics.dispose();
     }
     
                                                                 
     public void init()
     {
        aplWid = siz;
        resize(aplWid,aplWid);
        setLayout(new BorderLayout());
        gge = new CirGge(siz);
        add("Center", gge);
        gen = new ValGen();
     }
     
     public void start()
     {
       
       
        // Start a new thread for number generator to
        // send values to gauge.
        new Thread(this).start();
     }
     
     public void stop()
     {
     // suspend the thread here  
     }
     public void run()
     {
        while (true)
        {
           // Generate a new value.
           newVal = gen.genVal();
       
           // Change the size of the circle to
           // the new value.
           gge.chgPplSiz(newVal);
           try
           {  
              // Wait a second.
              Thread.sleep(1000);                    
           }
           catch (InterruptedException e)
           {
           }
        }    
       
     }      
  }
 
  // the lightweight component gauge
  class CirGge extends Component
  {
     // length of side of black backround box
     private int boxWid;
     
     // diameter of white circle
     private int pplSiz;    
     
     CirGge(int siz)
     {
        // Set size of black background box.
        boxWid = siz;
       
        // Set initial size of circle.
        pplSiz = 95;
     }
     public void paint(Graphics g)
     {
        // Draws the black background
        g.setColor(Color.black);
        g.drawRect(0,0,boxWid,boxWid);
        g.fillRect(0,0,boxWid,boxWid);
       
        // Draws the white circle
        g.setColor(Color.white);
        g.drawArc((boxWid - pplSiz)/2,(boxWid - pplSiz)/2,pplSiz,pplSiz,0,360);
        g.fillArc((boxWid - pplSiz)/2,(boxWid - pplSiz)/2,pplSiz,pplSiz,0,360);    
     }
     public void update (Graphics g)
     {
        // Don't erase, just paint.
        paint(g);
     }
     
     // Change the size of the circle.
     synchronized void chgPplSiz(int newPplSiz)
     {
        pplSiz = newPplSiz;
        repaint();
     }
  }
 
   // number generator
   class ValGen
   {
      Random numGen;
      int nxtVal;
     
      ValGen()
      {
         numGen = new Random();
      }
     
      // Generate a value between 90 and 99.
      synchronized int genVal()
      {
         nxtVal = numGen.nextInt();
         nxtVal = Math.abs((nxtVal % 10) + 90);
         return nxtVal;
      }
   }
 
   
 
 
 
 
 
 
Avatar of heyhey_
heyhey_

I will post some 'try it yourself and you'll see why' notes :)
1. container paint() method will call the paint() method os all of its childs.
there is even special paintComponents(Graphics g) method
(put some System.out.println()'s in the paint() and update() methods and you'll see when each method is called :)
(btw. - you see the super.paint(bufferGraphics); ? :)
remove this line and test the code :)
CirGge is lightweight component (CirGge extends Component), so it does not have its own Graphics object. it transparently uses the same bufferGraphics object);

2. invalidate() is pretty important method. it is called whe the Component has changed (resized). you definitely has to call super.invalidate(), bucause it DOES all the dirty job.
and the author has overriden this method so that he can clear the buffer object when the component has been resized.
(remove super.invalidate() and test the code :)

3.
if (buffer == null)
{
buffer = createImage(getSize().width, getSize().height);
}
Graphics bufferGraphics = buffer.getGraphics();

as you can see the buffer object is recreated each time when it REALLY has to. but every Image object has its own Graphics objects - so you don't recreate the Graphics object each time.

of course you can use member variable if you want :)
ASKER CERTIFIED SOLUTION
Avatar of heyhey_
heyhey_

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
here is another - slightly different d-b code

Image offscreen;
Dimension offscreensize;
Graphics offgraphics;

public synchronized void update(Graphics g) {
Dimension d = getSize();
if ((offscreen == null) || (d.width != offscreensize.width) || (d.height != offscreensize.height)) {
    offscreen = createImage(d.width, d.height);
    offscreensize = d;
    offgraphics = offscreen.getGraphics();
    offgraphics.setFont(getFont());
}