★ wanayoo — archive 1999 http://developer.java.sun.com/developer/technicalArticles/Interviews/SlidingBuffers/index.htmlNouvelle recherche | Portail wanayoo
Java Technology Home Page
A-Z Index

Java Developer Connection(SM)
Technical Articles

Downloads, APIs, Documentation
Java Developer Connection
Tutorials, Tech Articles, Training
Online Support
Community Discussion
News & Events from Everywhere
Products from Everywhere
How Java Technology is Used Worldwide
 
Articles Index

Sliding Buffers

by Tim Stefanini

One area of Java where you can get into trouble is trying to balance memory management with performance. Take, for example, double buffering. Usually when you are drawing on a buffer, you aren't exactly sure how large a buffer you will need. In this example, we are using a 600-pixels-by-600-pixels applet window. The image we want to buffer is a grid of squares, which is 2000-pixels-by-2000 pixels. Here are some rough numbers on how much memory the buffer will use.

Base App                        4.392M
Sliding Buffer (600x600)        1,220k
Regular Buffer (2kx2k)          8,348k
If we design it with a buffer big enough for our image, our simple applet exceeds 12MB of memory. And if your applet has a large memory profile, the constant paging will defeat any performance gains of double buffering. What you really need is a solution that combines the best of both worlds: a sliding double buffer. (A sliding double buffer is one in which the image is larger than your buffer, and you can slide the image across the buffer.)

The problem is to manage this move efficiently, because it requires you to redraw the buffer. First, create an applet with a buffering image:

   public class BufferTest extends java.applet.Applet
   { 
     Image     offscreenImage=null;
     Graphics  offscreenGraphics=null;

     private void setupBuffer ()
     {  
       try
       {   
         //set up image for double buffering
         offscreenImage = createImage ( 
           this.miBuffersize, this.miBuffersize);
         offscreenGraphics = 
           offscreenImage.getGraphics ();
       }//end try
       catch (Exception e)
       { 
         e.printStackTrace ();
       }//end catch
    }//end setupBuffer()
To slide the image across the buffer, add some sliding parameters to the drawImage method. Here we use the global parameters miBufferShiftx (move image x pixels right on the buffer) and miBufferShifty (move image y pixels down on the buffer):
  private void drawGrid ()
  { 
    int iXpos =0;
    int iYpos =0;
    offscreenGraphics.setFont(new Font("TimesRoman", 
                       Font.PLAIN,8));
        
    for (int x=0; x <= 2000; x+= 150)
      for (int y=0; y<=2000; y+= 150)
    {  
      iXpos = x + miBufferShiftX +25 ;
      iYpos = y + miBufferShiftY +25;
      offscreenGraphics.drawRect(iXpos, iYpos, 50, 50);
      offscreenGraphics.drawString(x+ "," +y, iXpos +5, 
                       iYpos +20);
    }
  }//end drawGrid ()
Now that we have the ability to slide our image on the buffer, we need to maintain the position of our view of this buffer, in other words, the area the user sees.
  public void paint (Graphics g)
  { 
    g.drawImage (offscreenImage, miXpan, miYpan, this);
  }//end paint
Here the two variables--miXpan and miYpan-- representing the position of the "View Window" relative to the buffer. In this program, we want the user to be able to drag the image with the mouse, so we want to make sure that when the user releases his mouse button, we slide the image in the background. The visual effect of this is to make part of the image (which might not have been visible) suddenly appear. We use mfDragInit to remember if we have saved the starting mouse down position already:
  public boolean mouseUp(Event evt, int x, int y)
  { 
    SlideBuffer ();
    return true;
  }//emd mouseUp(.,.,.)
 
  public boolean mouseDown(Event evt, int x, int y)
  {   
    mfDragInit = false;
    return true;
  }
Now we want to add code that ensures that when a user drags his mouse, the image appears to slide across the screen. We do this by taking the change in mouse position since the MouseDown event, and setting our panning variables to this:
  public boolean mouseDrag (Event evt, int x, int y)
  {  
    if (mfDragInit)
    {  
      miXpan = ( x - miOldx);
      miYpan = (y - miOldy);
      repaint();
    }//end if
    else
    {  
      miOldy = y;
      miOldx = x;
      mfDragInit = true;
    }//end else
    return true;
  } //end mouseDrag
This solution works great, except that the user may notice that part of the image seems to disappear until they MouseUp(). If you can redraw your image in under 100 milliseconds, you can call your sliding routine after certain intervals to get rid of this side-effect. However, if you are drawing a very complex image, doing a redraw in the middle of a mouseDrag method will make dragging the mouse appear jerky. Try this:
  public boolean mouseDrag (Event evt, int x, int y)
  {   
    if (mfDragInit)
    {  
      miXpan = ( x - miOldx);
      miYpan = (y - miOldy);
      repaint();
    }//end if
    else
    {  
      miOldy = y;
      miOldx = x;
      mfDragInit = true;
    }//end else
    
    //Add 100 pixel mouse drag interval 
    //to do a buffer slide
    int miMouseDragInterval = 100;
    if ((miXpan >= miMouseInterval)
      ||(miXpan <= - miMouseInterval)
      ||(miYpan >= miDragInterval)
      ||(miYpan <=- miDragInterval) )
      SlideBuffer ();
    return true;
  } //end mouseDrag
Even if you have an image that takes several seconds to draw, you may be able to optimize it enough to use this technique. One way to do this is to only draw the part of your image equal to your view size plus your mouse drag interval. You know you don't need to draw something that is a thousand pixels away from your view window.

This technique is useful anywhere you need to double buffer, but I usually reserve it for cases where I have really large images to scroll, or where the size of the image is hard to predict. (It isn't really applicable to an image where you don't control the drawing, such as a .GIF image.) Still, it's a very helpful technique for which you'll doubtless find lots of uses.


[ This page was updated: 12-Jan-2000 ]
Products & APIs | Developer Connection | Docs & Training | Online Support
Community Discussion | Industry News | Solutions Marketplace | Case Studies
Glossary - Applets - Tutorial - Employment - Business & Licensing - Java Store - Java in the Real World
FAQ | Feedback | Map | A-Z Index
For more information on Java technology
and other software from Sun Microsystems, call:
(800) 786-7638
Outside the U.S. and Canada, dial your country's AT&T Direct Access Number first.
Sun Microsystems, Inc.
Copyright © 1995-2000 Sun Microsystems, Inc.
All Rights Reserved. Terms of Use. Privacy Policy.