org.dvb.ui
Class BufferedAnimation

java.lang.Object
  extended by java.awt.Component
      extended by org.dvb.ui.BufferedAnimation
All Implemented Interfaces:
java.awt.image.ImageObserver, java.awt.MenuContainer, java.io.Serializable

public class BufferedAnimation
extends java.awt.Component

A BufferedAnimation is an AWT component that maintains a queue of one or more image buffers. This permits efficient flicker-free animation by allowing a caller to draw to an off-screen buffer, which the system then copies to the framebuffer in coordination with the video output subsystem. This class also allows an application to request a series of buffers, so that it can get a small number of frames ahead in an animation. This allows an application to be robust in the presence of short delays, e.g. from garbage collection. A relatively small number of buffers is recommended, perhaps three or four. A BufferedAnimation with one buffer provides little or no protection from pauses, but does provide double-buffered animation.

This class can be used for frame-synchronous animation. When animation is in progress, it maintains a count of the frame number in the underlying video output device. This frame number increases monotonically by one for each video frame output. It is not influenced by trick play of any video that might be playing on the same screen. However, the framerate of the BufferedAnimation may be determined by the framerate of such video; see setFramerate(float) and getFramerate() for details.

The implementation shall prevent tearing artifacts whenever possible. The maximum size and animation rate that can be achieved without tearing artifacts may be specified by the system model of a specification that includes this class. If it is necessary to avoid a tearing artifact, an implementation shall delay the copying of an internal buffer to the frame buffer by up to one frame.

The size of this component is set using the normal AWT mechanisms. When the method setBuffers(Dimension, int, boolean, boolean) is called, initialization is performed. At this time, the size of the graphics buffers is set.

When one of this component's buffers is copied to the frame buffer, it is done without regard to any AWT components which may overlap with this component. This is like the behavior when an application draws directly to the screen using a graphics object obtained with java.awt.Component.getGraphics().

When the system copies a buffer to the frame buffer for a given frame f, it shall select the valid buffer associated with the highest-numbered frame fb such that fb <= f. A buffer is valid if startFrame(int) has been called for that buffer, finishFrame(int) has been called, and the given buffer has not been re-used for a subsequent frame as a result of another call to startFrame(int).

The animation task proceeds at a high relative CPU priority, and can be considered to execute at a priority greater than Java's Thread.NORM_PRIORITY. However, when no new image buffer is ready, the system task must always block until one is. Drawing into the buffer is done within a Java thread, which is subject to the normal scheduling guarantees. In this way, a CPU-bound caller can avoid starving more important activities, such as responding to remote control input. CPU-bound applications may wish to invoke Thread.yield() after each frame, however, particularly if the application's animation thread is at the same priority level as other application threads.

A component that is not visible and is in the started state will run, but it will not display any buffers to the screen. It will block in the call to startFrame() until the component becomes visible, or until it is too late to draw the requested frame, whichever comes first. Once it is too late, it will, of course, return -1, thus ensuring that the caller doesn't waste time drawing to an internal graphics buffer that wouldnt' be displayed.

For the behavior when this component is destroyed, see removeNotify().

Sample usage:


     BufferedAnimation anim = new BufferedAnimation();
      ... put anim in a component hierarchy
     Dimension d = new Dimension(...);
     int numBuffers = 4;
     for (;;) {
         try {
             anim.setBuffers(d, 4, false, true);
             break;
         } catch (OutOfMemoryError err) {
             ... try smaller buffers, or fewer of them
         }
      }
      ... set framerate, if needed
      ... Make anim visible
     Graphics2D[] bufs = anim.getBuffersGraphics();
     anim.startAnimation();
     //  Animate 1000 frames...
     try {
         for (int f = 0; f < 1000; f++) {
             int n;
             try {
                  n = anim.startFrame(f);       // blocks until a buffer is free
             } catch (InterruptedException ex) {
                  // someone else called stopAnimation. or removeNotify() was
                  // called.  In any case, we're being asked to stop the 
                  // animation immediately.
                  break;
             }
             if (n > -1) {
                  try {
                      myDrawFrame(f, bufs[n]);
                  } finally {
                      anim.finishFrame(f);
                  }
              }
          }
      } finally {
          anim.stopAnimation(false);
      }

 

This class does not specify a return value for Component.isDoubleBuffered(). That method reports on a different kind of buffering, related to the repaint() call. BufferedAnimation objects might or might not be double-buffered, in the repaint-related sense meant by Component.isDoubleBuffered().

NOTE: A future version of this API could potentially allow simultaneous drawing to two frames, by relaxing the synchronization condition on startFrame(int). However, it is unclear if this would yield benefits e.g. on a multi-core system, given memory bandwith limitations and the already existing ability to have parallel threads drawing into one frame or doing other computations.

Since:
MHP 1.1.3
See Also:
Serialized Form

Field Summary
static float FRAME_23_98
          Constant representing a common video framerate, approximately 23.98 frames per second, and equal to 24000f/1001f.
static float FRAME_24
          Constant representing a common video framerate, equal to 24f.
static float FRAME_25
          Constant representing a common video framerate, equal to 25f.
static float FRAME_29_97
          Constant representing a common video framerate, approximately 29.97 frames per second, and equal to30000f/1001f.
static float FRAME_50
          Constant representing a common video framerate, equal to 50f.
static float FRAME_59_94
          Constant representing a common video framerate, approximately 59.94 frames per second, and equal to60000f/1001f.
 
Fields inherited from class java.awt.Component
BOTTOM_ALIGNMENT, CENTER_ALIGNMENT, LEFT_ALIGNMENT, RIGHT_ALIGNMENT, TOP_ALIGNMENT
 
Fields inherited from interface java.awt.image.ImageObserver
ABORT, ALLBITS, ERROR, FRAMEBITS, HEIGHT, PROPERTIES, SOMEBITS, WIDTH
 
Constructor Summary
BufferedAnimation()
          Create a new BufferedAnimation component.
 
Method Summary
 void addNotify()
          Makes this Component displayable by connecting it to a native screen resource.
 void finishFrame(int frameNumber)
          Notify the system that the frame currently being drawn is finished.
 java.awt.Graphics2D[] getBuffersGraphics()
          Get the graphics objects for drawing into this component's internal image buffers.
 java.awt.Dimension getBuffersSize()
          Get the size of the internal image buffers.
 float getFramerate()
          Get the actual framerate of the screen associated with this component.
 long getMediaTime(javax.media.Clock c, int frameNumber)
          Get the predicted media time of the given frameNumber for this animation.
 boolean isStarted()
          Return true if this animation is started.
 void paint(java.awt.Graphics g)
          If this component has an active animation, then this method paints either the last valid image buffer or the next valid image buffer to the given graphics object.
 void removeNotify()
          Make this component undisplayable by destroying any native resources, and freeing its image buffers.
 void setBuffers(java.awt.Dimension bufSize, int numBuffers, boolean forceSize, boolean forceAcceleration)
          Set the size and number of the internal image buffers.
 void setFramerate(float rate)
          Attempt to set the framerate of the screen associated with this component.
 void startAnimation()
          Start this animation immediately, and reset the frame number to zero.
 void startAnimationAt(javax.media.Clock c, javax.media.Time t)
          Start this animation keyed to the clock at the given media time.
 int startFrame(int frameNumber)
          Start drawing the given frame.
 void stopAnimation(boolean immediate)
          Stops this animation.
 
Methods inherited from class java.awt.Component
action, add, addComponentListener, addFocusListener, addHierarchyBoundsListener, addHierarchyListener, addInputMethodListener, addKeyListener, addMouseListener, addMouseMotionListener, addMouseWheelListener, addPropertyChangeListener, addPropertyChangeListener, applyComponentOrientation, areFocusTraversalKeysSet, bounds, checkImage, checkImage, contains, contains, createImage, createImage, createVolatileImage, createVolatileImage, deliverEvent, disable, dispatchEvent, doLayout, enable, enable, enableInputMethods, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, getAccessibleContext, getAlignmentX, getAlignmentY, getBackground, getBounds, getBounds, getColorModel, getComponentAt, getComponentAt, getComponentListeners, getComponentOrientation, getCursor, getDropTarget, getFocusCycleRootAncestor, getFocusListeners, getFocusTraversalKeys, getFocusTraversalKeysEnabled, getFont, getFontMetrics, getForeground, getGraphics, getGraphicsConfiguration, getHeight, getHierarchyBoundsListeners, getHierarchyListeners, getIgnoreRepaint, getInputContext, getInputMethodListeners, getInputMethodRequests, getKeyListeners, getListeners, getLocale, getLocation, getLocation, getLocationOnScreen, getMaximumSize, getMinimumSize, getMouseListeners, getMouseMotionListeners, getMousePosition, getMouseWheelListeners, getName, getParent, getPeer, getPreferredSize, getPropertyChangeListeners, getPropertyChangeListeners, getSize, getSize, getToolkit, getTreeLock, getWidth, getX, getY, gotFocus, handleEvent, hasFocus, hide, imageUpdate, inside, invalidate, isBackgroundSet, isCursorSet, isDisplayable, isDoubleBuffered, isEnabled, isFocusable, isFocusCycleRoot, isFocusOwner, isFocusTraversable, isFontSet, isForegroundSet, isLightweight, isMaximumSizeSet, isMinimumSizeSet, isOpaque, isPreferredSizeSet, isShowing, isValid, isVisible, keyDown, keyUp, layout, list, list, list, list, list, locate, location, lostFocus, minimumSize, mouseDown, mouseDrag, mouseEnter, mouseExit, mouseMove, mouseUp, move, nextFocus, paintAll, postEvent, preferredSize, prepareImage, prepareImage, print, printAll, remove, removeComponentListener, removeFocusListener, removeHierarchyBoundsListener, removeHierarchyListener, removeInputMethodListener, removeKeyListener, removeMouseListener, removeMouseMotionListener, removeMouseWheelListener, removePropertyChangeListener, removePropertyChangeListener, repaint, repaint, repaint, repaint, requestFocus, requestFocusInWindow, reshape, resize, resize, setBackground, setBounds, setBounds, setComponentOrientation, setCursor, setDropTarget, setEnabled, setFocusable, setFocusTraversalKeys, setFocusTraversalKeysEnabled, setFont, setForeground, setIgnoreRepaint, setLocale, setLocation, setLocation, setMaximumSize, setMinimumSize, setName, setPreferredSize, setSize, setSize, setVisible, show, show, size, toString, transferFocus, transferFocusBackward, transferFocusUpCycle, update, validate
 
Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

FRAME_23_98

public static float FRAME_23_98
Constant representing a common video framerate, approximately 23.98 frames per second, and equal to 24000f/1001f.

See Also:
getFramerate(), setFramerate(float)

FRAME_24

public static float FRAME_24
Constant representing a common video framerate, equal to 24f.

See Also:
getFramerate(), setFramerate(float)

FRAME_25

public static float FRAME_25
Constant representing a common video framerate, equal to 25f.

See Also:
getFramerate(), setFramerate(float)

FRAME_29_97

public static float FRAME_29_97
Constant representing a common video framerate, approximately 29.97 frames per second, and equal to30000f/1001f.

See Also:
getFramerate(), setFramerate(float)

FRAME_50

public static float FRAME_50
Constant representing a common video framerate, equal to 50f.

See Also:
getFramerate(), setFramerate(float)

FRAME_59_94

public static float FRAME_59_94
Constant representing a common video framerate, approximately 59.94 frames per second, and equal to60000f/1001f.

See Also:
getFramerate(), setFramerate(float)
Constructor Detail

BufferedAnimation

public BufferedAnimation()
Create a new BufferedAnimation component. The BufferedAnimation functionality may be optional. Applications written to device specifications that do not make this functionality mandatory should be prepared to catch UnsupportedOperationException when invoking this constructor.

Throws:
java.lang.UnsupportedOperationException - If this feature is not supported on the device.
Method Detail

setBuffers

public void setBuffers(java.awt.Dimension bufSize,
                       int numBuffers,
                       boolean forceSize,
                       boolean forceAcceleration)
Set the size and number of the internal image buffers. If the system is capable of scaling a BufferedAnimation's buffers to the component's size in real-time, then the internal buffers will be of the requested size, and scaling will occur. If the system is not, then the results will depend on the value of forceSize.

On a system that cannot perform a requested scaling, if forceSize is true, then the buffers' sizes will be set to the requested size regardless. The displayed result will be clipped or will have areas that are unpainted, as needed. In all cases, the upper-left hand corner of the buffers will be painted at to the upper-left hand corner of the component.

On a system that cannot perform scaling, if forceSize is false, the buffers' size will be set to the current size of the component. That is, the requested buffer dimension will be ignored.

The system model of specifications that include this class may specify a set of supported scalings.

Note that the framebuffer itself might be scaled for display on the output device. For example, a specification including this class might include half-resolution mode, e.g. for half-resolution computer graphics over 1080i video.

Graphics Acceleration

Some systems have special faster video memory that gives accelerated graphics performance. The system model of a specification adopting this API may define a minimum amount of such memory. Other hardware architectures, such as "unified memory architecture" platforms, don't have special accelerated memory. On these platforms, all video memory is considered "accelerated", that is, the forceAccelerated parameter has no effect, and does not cause automatic failure.

On platforms with special accelerated video memory, the caller may indicate that all of the graphics buffers must be allocated from this accelerated memory. It does this by setting the forceAcceleration parameter true. This may make an OutOfMemoryError more likely. If forceAcceleration is not true, then the implementation will make a "best-effort" attempt to put the buffers in accelerated memory, but will fall back to normal heap memory, if required.

This method may be called more than once. If it exits with an exception, the state of this object will not be changed. If it returns normally, the new values will override anything set previously.

Parameters:
bufSize - The requested buffer size
numBuffers - The number of image buffers to allocate
forceSize - Force sizing the buffers to the requested size, even if this means clipping or having unpainted areas.
forceAcceleration - Force allocation of all buffers in accelerated memory.
Throws:
java.lang.IllegalArgumentException - if d.width or d.height is less than one, or numBuffers is less than one.
java.lang.IllegalStateException - if getBuffersGraphics has been called for this component.
java.lang.IllegalStateException - If this component isn't displayable.
OutOfMemoryException - If there isn't enough memory to allocate the needed buffers.
See Also:
Component.isDisplayable()

getBuffersGraphics

public java.awt.Graphics2D[] getBuffersGraphics()
Get the graphics objects for drawing into this component's internal image buffers. The size and number of buffers is determined by the setBuffers method.

After the first call, subsequent invocations of this method shall return the identical value (i.e. multiple calls will return values that are == to each other).

Other than the setBuffers mechanism, the size of the internal buffers will never change. If the component is resized, the system might scale the resulting animation, but this behavior is not guaranteed by the specification of this class. In all cases, the upper-left hand corner of the image buffer will be displayed in the upper-left hand corner of the component.

The initial contents of the graphics buffers is undefined. Callers may wish to initialize the buffers to a known state, such as fully transparent, before starting an animation. Once an animation is started, drawing into a buffer outside of a startFrame/finishFrame pair may produce unpredictable results on the screen.

Returns:
An array of graphics objects that can be used to draw into the internal image buffers.
Throws:
java.lang.IllegalStateException - if the setBuffers() hasn't been successfully called.
See Also:
setBuffers(java.awt.Dimension, int, boolean, boolean)

getBuffersSize

public java.awt.Dimension getBuffersSize()
Get the size of the internal image buffers. If setBuffers has not yet been successfully called, then null is returned.

See Also:
getBuffersGraphics(), setBuffers(java.awt.Dimension, int, boolean, boolean)

paint

public void paint(java.awt.Graphics g)
If this component has an active animation, then this method paints either the last valid image buffer or the next valid image buffer to the given graphics object. If there is no available valid image buffer and a startFrame/finishFrame sequence is in progress, this method will block until finishFrame is called, thus generating a valid image buffer. If no animation is in progress or no valid frames have yet been generated, then this method does nothing.

Note that in normal operation, this method should be called by the platform very infrequently, if at all. It might be called, for example, due to an "expose event," or due to a call to Component.print(Graphics). Application authors should not request a call to paint via the repaint mechanism to animate this component, because this class uses a different model for animation.

Overrides:
paint in class java.awt.Component

startFrame

public int startFrame(int frameNumber)
               throws java.lang.InterruptedException
Start drawing the given frame. The return value gives the index into the array obtained from getBuffersGraphics() for drawing of this frame, or -1 if the animation has fallen behind, and a later frame should now be drawn. After calling this method, if a value other than -1 is returned, the caller may draw to the indicated graphics buffer. When it is finished, it shall call finishFrame().

If a buffer is not available for the given frame, this method will block until one is ready.

The caller can always skip frames. For example, a caller wishing to animate at half of the component's framerate could request frames 0, 2, 4, 6, 8, 10, etc. In this example, if there are four buffers and animation does not fall behind, the caller would be instructed to draw into buffer 0, 1, 2, 3, 0, 1, etc. A caller that wishes to start animating at a frame greater than 0 may do so by simply starting with a number greater than zero; when the lower-numbered frames are being presented, the component will simply do no drawing.

The content of the framebuffers is not modified by the system. Thus, a caller that is drawing into buffer number n could function correctly if it only drew to pixels that have changed since it last drew into buffer number n.

Parameters:
frameNumber - The frame number to draw. The first frame is frame 0.
Returns:
An index into the array of graphics objects for drawing the given frame, or -1 if animation has fallen behind, and a later frame should now be drawn.
Throws:
java.lang.IllegalArgumentException - if frameNumber is less than or equal to a number previously supplied to this animation, or is less than zero.
java.lang.IllegalStateException - if startFrame() has already been successfully called without a corresponding finishFrame().
java.lang.InterruptedException - If this animation is in the stopped state, either when this method is called or due to a state transition while it is blocked waiting for a graphics buffer.
See Also:
getBuffersGraphics(), isStarted()

finishFrame

public void finishFrame(int frameNumber)
Notify the system that the frame currently being drawn is finished. Drawing the current frame is initiated with startFrame(int). Once finishFrame(int) is called, the system can copy that frame to the framebuffer, and the caller can move on to preparing the next frame.

Parameters:
frameNumber - The frame number that is finished. This must match the value passed into startFrame(int).
Throws:
java.lang.IllegalStateException - if a startFrame call has not returned successfully for the given frame number (with a return value other than -1), if finishFrame(int) has already been called for this frame number since the startFrame call, or if stopAnimation(boolean) has been called since the corresponding startFrame call.
See Also:
startFrame(int)

setFramerate

public void setFramerate(float rate)
Attempt to set the framerate of the screen associated with this component. Other factors, such as video being output to the same device or device limitations, might determine the framerate, thus causing this method to have no effect. The frameright might subsequently be changed, e.g. by video being presented on the screen or by other APIs. Unless other behavior is mandated by the system model of a specification incorporating this class, it is an allowable implementation option for this method to never change the framerate.

The system model of specifications including this class might determine under what conditions the framerate can be set, and what framerates are guaranteed to be supported. This class defines a number of common framerates as constants whose name begin with "FRAME_".

Note that an application that wishes to animate at a lower framerate than that of the hardware may do so, by simply skipping frames. This is discussed in the startFrame(int) method.

Throws:
java.lang.IllegalStateException - If this component is not displayable.
See Also:
Component.isDisplayable(), startFrame(int)

getFramerate

public float getFramerate()
Get the actual framerate of the screen associated with this component. This class defines a number of common framerates as constants whose name begin with "FRAME_".


startAnimation

public void startAnimation()
Start this animation immediately, and reset the frame number to zero. Frame zero will be the first frame that can be displayed; typically it will be one or two frames after the frame visible on the screen at the time this method is called.

Applications should only call this method on a stopped animation. However, if this method is called when an animation is already started, it is re-started; the effect is equivalent to calling stopAnimation(true) followed by startAnimation().

See Also:
isStarted()

startAnimationAt

public void startAnimationAt(javax.media.Clock c,
                             javax.media.Time t)
                      throws javax.media.IncompatibleTimeBaseException
Start this animation keyed to the clock at the given media time. If the clock's media time is already greater than the given time, this is equivalent to startAnimation(). Otherwise, once the clock's media time is greater than or equal to the given the given value, the animation will be started. Callers should not assume that frame zero of the animation will coincide with the desired time in all cases; for example, the clock's media time might advance in a discontinuous manner. Callers should always consult getMediaTime(...).

This method can be used to initiate frame-accurate animation that is synchronized to video that is being presented on the same screen. The animation enters the started state, and drawing to graphics buffers can begin. The system will start copying these buffers to the framebuffer automatically, when the clock reaches the given time.

Subsequent calls to this method override any previous calls. If this method is called when an animation is already started, it is re-started; the effect is equivlanet to calling stopAnimation(true) followed by startAnimationAt(...).

Parameters:
c - A JMF Clock that is associated with some media.
t - A media time of that clock when the animation should start.
Throws:
javax.media.IncompatibleTimeBaseException - If this Clock is incompatible with this animation. This will never be thrown if the Clock is associated with video being displayed on the same screen as this animation.
java.lang.IllegalStateException - If this clock is not in the started state.
See Also:
isStarted(), getMediaTime(javax.media.Clock, int)

stopAnimation

public void stopAnimation(boolean immediate)
Stops this animation. If it is already in the stopped state, this method has no effect. If it is in the started state, it is set to the stopped state. Once this component is in the stopped state, it will not draw into any pixels to the screen, or as a result of a call to the paint() method.

The caller may request that queued fames of animation be output to the screen. This will be done if the animation is in the started state, and immediate is set false.

If a successful call to startFrame(int) has not yet been matched with a call to finishFrame(int), this object is set to the stopped state, which will cause finishFrame(int) ti fail. See that method for details.

After this method returns, isStarted() will return false. If this animation is not in the started state, calling this method will have no effect.

Parameters:
immediate - If the component should immediately stop copying buffers to the screen, instead of letting any queued animation frames be output.
See Also:
isStarted(), finishFrame(int)

isStarted

public boolean isStarted()
Return true if this animation is started. A BufferedAnimation can either be in the started or stopped state. A stopped BufferedAnimation will only draw to the framebuffer if it is in the process of flushing animation buffers due to a call to stopAnimation(false)

Returns:
true if this BufferedAnimation is started, false otherwise.
See Also:
stopAnimation(boolean)

getMediaTime

public long getMediaTime(javax.media.Clock c,
                         int frameNumber)
Get the predicted media time of the given frameNumber for this animation. The predicted media time is calculated from the media time of the frame being presented on the screen, and extrapolating assuming a clock rate of 1.0.

The return value can be converted into a javax.media.Time value by calling the javax.media.Time(long) constructor.

This method is useful for keeping an animation aligned with a video source, even if "trick play" operations cause the media position to change. Note that because the computer generated animation might be a small number of frames "ahead" of the video due to the buffering this class provides, there might be a perceptable "lag" during trick play itself, but once the video returns to normal play mode, the animation would once again be frame-synchronized.

Parameters:
c - The clock to calculate media time relative to
frameNumber - the desired frame number of this animation
Returns:
The media time, in nanoseconds.
Throws:
java.lang.IllegalStateException - if this animation is not in the started state.
javax.media.IncompatibleTimeBaseException - If this Clock is incompatible with this animation. This will never be thrown if the Clock is associated with video being displayed on the same screen as this BufferedAnimation component.
java.lang.IllegalStateException - If this clock is not in the started state.

removeNotify

public void removeNotify()
Make this component undisplayable by destroying any native resources, and freeing its image buffers. stopAnimation(true) shall be called by the implementation of this method.

Overrides:
removeNotify in class java.awt.Component

addNotify

public void addNotify()
Makes this Component displayable by connecting it to a native screen resource. This method is called internally by the toolkit and should not be called directly by programs.

Overrides:
addNotify in class java.awt.Component