net.groboclown.gui.tripleBuffer.v1
Class TripleBufferCanvas

java.lang.Object
  |
  +--java.awt.Component
        |
        +--java.awt.Canvas
              |
              +--net.groboclown.gui.tripleBuffer.v1.TripleBufferCanvas
All Implemented Interfaces:
Accessible, ImageObserver, MenuContainer, Runnable, Serializable
Direct Known Subclasses:
TerrainCanvas, Test

public abstract class TripleBufferCanvas
extends Canvas
implements Runnable

Triple buffering of images. To use in your own applications, create a subclass of the TripleBufferCanvas, implement the method protected void drawImage( Graphics buffer ), which performs the offscreen drawing to the image img once. To start the animation thread, use the command [myCanvas].start(); If you need to monitor the screen size, override the method protected void windowChangedSize(); the variables width and height are set to the proper values. This is only called when both width and height are > 0. Current important points of interest: 1. does not use Thread.stop() or Thread.suspend(), since JDK1.2 deprecated these. Instead, we supply interface methods which perform the same task without calling the Thread methods. 2. handles resizing calls 3. subclasses can use the integers width and height to check for the current dimensions. These values are only changed in resizing calls, and are used by the paint method. Why this is important: Standard drawing draws directly to the screen, which can show flickering due to the pauses between each draw step. Double buffering is writing to an offscreen image before displaying that image, so that the flicker from normal drawing doesn't appear. Double buffering can be inadequate if your image takes a while to draw. Hence, repeated calls to paint() with a slow drawing routine will reduce the speed of the windowing system as well. Triple buffering solves this by 1. storing the current image to show, so that any calls to paint() will display the already created image, 2. drawing the next frame in an offscreen buffer in a separate thread, and 3. synchronizing the paint and draw routines for seamless execution. It is possible to create a subclass which only draws one image at a time, waiting for an outside source to signal when to paint another frame. To do this, at the end of your drawImage() routine, call suspend(), which will pause the execution after the canvas has sent the signal to draw the new image, and all frame skip checking is completed. Then, in user code when the next frame is ready to be painted, call resume() to draw the next frame. Room for improvement: 2. The pause loop in run() should be re-written to do a wait() on a pause object. Needs to be tested for synchronizing against other threads. 3. Repaint() has been replaced to pass the paint command within 50 millis. This could be adjusted by users. Also, could use a separate set of variables to specify where in the region to repaint, in case only a small portion needs to be updated.

See Also:
Serialized Form

Inner classes inherited from class java.awt.Canvas
Canvas.AccessibleAWTCanvas
 
Inner classes inherited from class java.awt.Component
Component.AccessibleAWTComponent, Component.AWTTreeLock
 
Field Summary
protected  Color bgcolor
          The drawImage class can modify this at will to change the background color of the drawn image
private  int currentIndex
          Specify which of the 2 images/graphics is to be painted on the screen.
private  Thread drawer
          The drawing thread - this class should totally control it.
private  boolean drawEveryFrame
          Should we wait for each frame to be drawn?
private  int drawingIndex
          Specify which of the 2 images/graphics is to be drawn to in the background.
private  Graphics[] gfx
          2 Graphics correlating to the drawing buffers
protected  int height
          Canvas height, in pixels (<= 0 means we're currently undrawable)
private  Image[] img
          2 Images for drawing buffers
private  boolean isPaused
          Thread state flags: is the process paused
private  boolean isRunning
          Thread state flags: is the process still running
private  int repaintCount
           
private  int waitTime
          The time to wait between each drawImage() call, in milliseconds; <= 0 means no wait at all.
protected  int width
          Canvas width, in pixels (<= 0 means we're currently undrawable)
 
Fields inherited from class java.awt.Canvas
base, nameCounter, serialVersionUID
 
Fields inherited from class java.awt.Component
accessibleContext, actionListenerK, adjustmentListenerK, appContext, background, BOTTOM_ALIGNMENT, CENTER_ALIGNMENT, changeSupport, componentListener, componentListenerK, componentOrientation, componentSerializedDataVersion, containerListenerK, cursor, dbg, dropTarget, enabled, eventMask, focusListener, focusListenerK, font, foreground, graphicsConfig, hasFocus, hierarchyBoundsListener, hierarchyBoundsListenerK, hierarchyListener, hierarchyListenerK, incRate, inputMethodListener, inputMethodListenerK, isInc, isPacked, itemListenerK, keyListener, keyListenerK, LEFT_ALIGNMENT, locale, LOCK, metrics, minSize, mouseListener, mouseListenerK, mouseMotionListener, mouseMotionListenerK, name, nameExplicitlySet, newEventsOnly, ownedWindowK, parent, peer, peerFont, popups, prefSize, privateKey, RIGHT_ALIGNMENT, textListenerK, TOP_ALIGNMENT, valid, visible, windowClosingException, windowListenerK, x, y
 
Fields inherited from interface java.awt.image.ImageObserver
ABORT, ALLBITS, ERROR, FRAMEBITS, HEIGHT, PROPERTIES, SOMEBITS, WIDTH
 
Constructor Summary
TripleBufferCanvas()
          Default constructor: do nothing
 
Method Summary
protected  void createImages()
          creates images to match the current size, and sets up paint so it does not draw the wrong sized image last made.
abstract  void drawImage(Graphics img)
          user supplied drawing routine.
 void finalize()
          Quit and cleanup
 int getWaitTime()
           
 boolean isAlive()
           
 boolean isFrameSkippingAllowed()
           
 void paint(Graphics g)
          draws the currentImg to the screen, if there is an image to draw.
 void resume()
          pauses the execution of the drawing thread in a safe manner, after the current image has been drawn and repaint() has been called, and waitTime has elapsed.
 void run()
          Thread execution loop, which synchs with the paint routine to seamlessly and contiuously animate the canvas until stopped or paused.
 void setBounds(int x, int y, int w, int h)
          Intercepts all resizing calls.
 void setFrameSkippingAllowed(boolean allowFrameSkipping)
          This routine must be done in a thread-safe way to prevent dead-locking.
 void setWaitTime(int millis)
           
 void start()
          Starts the drawing thread.
 void stop()
          Stops the drawing thread in a safe manner.
 void update(Graphics g)
          modify the update routine so that there isn't any flicker when we repaint the screen
protected  void windowChangedSize()
          user supplied routine to capture resizing calls.
 
Methods inherited from class java.awt.Canvas
, addNotify, constructComponentName, getAccessibleContext, postsOldMouseEvents
 
Methods inherited from class java.awt.Component
action, add, addComponentListener, addFocusListener, addHierarchyBoundsListener, addHierarchyListener, addInputMethodListener, addKeyListener, addMouseListener, addMouseMotionListener, addPropertyChangeListener, addPropertyChangeListener, areInputMethodsEnabled, bounds, checkGD, checkImage, checkImage, checkWindowClosingException, coalesceEvents, contains, contains, createChildHierarchyEvents, createHierarchyEvents, createImage, createImage, deliverEvent, disable, disableEvents, dispatchEvent, dispatchEventImpl, doLayout, enable, enable, enableEvents, enableInputMethods, eventEnabled, firePropertyChange, getAccessibleIndexInParent, getAccessibleStateSet, getAlignmentX, getAlignmentY, getBackground, getBounds, getBounds, getColorModel, getComponentAt, getComponentAt, getComponentOrientation, getCursor, getDropTarget, getFont_NoClientCode, getFont, getFontMetrics, getForeground, getGraphics, getGraphicsConfiguration, getHeight, getInputContext, getInputMethodRequests, getListeners, getLocale, getLocation, getLocation, getLocationOnScreen_NoTreeLock, getLocationOnScreen, getMaximumSize, getMinimumSize, getName, getNativeContainer, getParent_NoClientCode, getParent, getPeer, getPreferredSize, getSize, getSize, getToolkit, getToolkitImpl, getTreeLock, getWidth, getWindowForObject, getX, getY, gotFocus, handleEvent, hasFocus, hide, imageUpdate, initIDs, inside, invalidate, isDisplayable, isDoubleBuffered, isEnabled, isEnabledImpl, isFocusTraversable, isLightweight, isOpaque, isRecursivelyVisible, isShowing, isValid, isVisible, keyDown, keyUp, layout, lightweightPaint, lightweightPrint, list, list, list, list, list, locate, location, lostFocus, minimumSize, mouseDown, mouseDrag, mouseEnter, mouseExit, mouseMove, mouseUp, move, nextFocus, numListening, paintAll, paintHeavyweightComponents, paramString, postEvent, preferredSize, prepareImage, prepareImage, print, printAll, printHeavyweightComponents, processComponentEvent, processEvent, processFocusEvent, processHierarchyBoundsEvent, processHierarchyEvent, processInputMethodEvent, processKeyEvent, processMouseEvent, processMouseMotionEvent, readObject, remove, removeComponentListener, removeFocusListener, removeHierarchyBoundsListener, removeHierarchyListener, removeInputMethodListener, removeKeyListener, removeMouseListener, removeMouseMotionListener, removeNotify, removePropertyChangeListener, removePropertyChangeListener, repaint, repaint, repaint, repaint, requestFocus, resetGC, reshape, resize, resize, setBackground, setBounds, setComponentOrientation, setCursor, setDropTarget, setEnabled, setFont, setForeground, setLocale, setLocation, setLocation, setName, setSize, setSize, setVisible, show, show, size, toString, transferFocus, validate, writeObject
 
Methods inherited from class java.lang.Object
clone, equals, getClass, hashCode, notify, notifyAll, registerNatives, wait, wait, wait
 

Field Detail

width

protected int width
Canvas width, in pixels (<= 0 means we're currently undrawable)

height

protected int height
Canvas height, in pixels (<= 0 means we're currently undrawable)

bgcolor

protected Color bgcolor
The drawImage class can modify this at will to change the background color of the drawn image

img

private Image[] img
2 Images for drawing buffers

gfx

private Graphics[] gfx
2 Graphics correlating to the drawing buffers

currentIndex

private int currentIndex
Specify which of the 2 images/graphics is to be painted on the screen. -1 means don't draw anything this time

drawingIndex

private int drawingIndex
Specify which of the 2 images/graphics is to be drawn to in the background. -1 means no buffer available at this time

drawer

private Thread drawer
The drawing thread - this class should totally control it.

isRunning

private boolean isRunning
Thread state flags: is the process still running

isPaused

private boolean isPaused
Thread state flags: is the process paused

waitTime

private int waitTime
The time to wait between each drawImage() call, in milliseconds; <= 0 means no wait at all.

drawEveryFrame

private boolean drawEveryFrame
Should we wait for each frame to be drawn?

repaintCount

private int repaintCount
Constructor Detail

TripleBufferCanvas

public TripleBufferCanvas()
Default constructor: do nothing
Method Detail

drawImage

public abstract void drawImage(Graphics img)
user supplied drawing routine.
Parameters:
buffer - the buffer to draw to; when we return this buffer will be sent to paint() for drawing.

windowChangedSize

protected void windowChangedSize()
user supplied routine to capture resizing calls. Doesn't need to be re-implemented, but if your drawing method depends on heavily precalculated variables, based on window size, then implement this to recalculate everything. This is already synchronized.

setWaitTime

public void setWaitTime(int millis)
Parameters:
millis - the (new) time to wait in between each draw, in milliseconds. If is <= 0, then don't wait.

getWaitTime

public int getWaitTime()

isFrameSkippingAllowed

public boolean isFrameSkippingAllowed()

setFrameSkippingAllowed

public void setFrameSkippingAllowed(boolean allowFrameSkipping)
This routine must be done in a thread-safe way to prevent dead-locking.
Parameters:
allowFrameSkipping - true = we allow for frames to be skipped if we draw them faster than we paint them, false = we require that each frame be painted.

isAlive

public boolean isAlive()

start

public void start()
Starts the drawing thread. Does not start another drawing thread if the current one is still running.

stop

public void stop()
Stops the drawing thread in a safe manner. If the drawImage() routine is pourly written and enters an infinite or nearly infinite loop, this will not stop the thread.

resume

public void resume()
pauses the execution of the drawing thread in a safe manner, after the current image has been drawn and repaint() has been called, and waitTime has elapsed. public void suspend() { isPaused = true; } /** Unpauses the drawing thread if it was paused.

update

public void update(Graphics g)
modify the update routine so that there isn't any flicker when we repaint the screen
Overrides:
update in class Component

paint

public void paint(Graphics g)
draws the currentImg to the screen, if there is an image to draw. Otherwise sets the currentImg to tempImg, which is usually done only at startup for proper synchronization.
Overrides:
paint in class Canvas

setBounds

public void setBounds(int x,
                      int y,
                      int w,
                      int h)
Intercepts all resizing calls. When we get a new size, we recreate all the images to match the new size, and setup paint to not draw the next image, since it would be of the wrong size.
Overrides:
setBounds in class Component

createImages

protected void createImages()
creates images to match the current size, and sets up paint so it does not draw the wrong sized image last made.

run

public final void run()
Thread execution loop, which synchs with the paint routine to seamlessly and contiuously animate the canvas until stopped or paused. Room for improvement: the pause routine is on a poll of the system. A more efficient way would be to do a wait() notify() on a pause object. But I haven't had time to work on a proper implementation.
Specified by:
run in interface Runnable

finalize

public void finalize()
Quit and cleanup
Overrides:
finalize in class Object


Written under the LGPL