
org.dvb.ui.BufferedAnimation Maven / Gradle / Ivy
package org.dvb.ui;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Dimension;
import javax.media.Clock;
import javax.media.Time;
import javax.media.IncompatibleTimeBaseException;
/**
* 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.
*
* OPEN ISSUES:
*
* - Is IncompatibleTimebaseException needed? Are there timebases
* that can't easily be related to the video output pipe?
*
- What's the right failure mode if a Clock isn't running?
*
- In MHP, what is the mapping from the underlying MPEG timebase to
* the media time from a JMF Clock? (Note from Bill: DSMCCStream
* has NPT, which is also a long and is also nanoseconds. Are these
* related? Also, I note that the trick play API uses Clock.setRate,
* so certainly there is a linkage from the JMF media time to what
* is being presented...)
*
* @since MHP 1.1.3
**/
public class BufferedAnimation extends java.awt.Component {
/**
* Constant representing a common video framerate, approximately
* 23.98 frames per second, and equal to
* 24000f/1001f
.
*
* @see #getFramerate()
* @see #setFramerate(float)
**/
static public float FRAME_23_98 = 24000f/1001f;
/**
* Constant representing a common video framerate, equal to
* 24f
.
*
* @see #getFramerate()
* @see #setFramerate(float)
**/
static public float FRAME_24 = 24f;
/**
* Constant representing a common video framerate, equal to
* 25f
.
*
* @see #getFramerate()
* @see #setFramerate(float)
**/
static public float FRAME_25 = 25f;
/**
* Constant representing a common video framerate, approximately
* 29.97 frames per second, and equal to30000f/1001f
.
*
* @see #getFramerate()
* @see #setFramerate(float)
**/
static public float FRAME_29_97 = 30000f/1001f;
/**
* Constant representing a common video framerate, equal to
* 50f
.
*
* @see #getFramerate()
* @see #setFramerate(float)
**/
static public float FRAME_50 = 50f;
/**
* Constant representing a common video framerate, approximately
* 59.94 frames per second, and equal to60000f/1001f
.
*
* @see #getFramerate()
* @see #setFramerate(float)
**/
static public float FRAME_59_94 = 59.94f;
/**
* 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.
**/
public BufferedAnimation() {
}
/**
* 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.
*
* @param bufSize The requested buffer size
* @param numBuffers The number of image buffers to allocate
* @param forceSize Force sizing the buffers to the requested size,
* even if this means clipping or having unpainted
* areas.
* @param forceAcceleration
* Force allocation of all buffers in accelerated
* memory.
*
* @throws IllegalArgumentException if d.width or d.height is less
* than one, or numBuffers is less
* than one.
* @throws IllegalStateException if getBuffersGraphics has been called
* for this component.
* @throws IllegalStateException If this component isn't displayable.
* @throws OutOfMemoryException If there isn't enough memory to allocate
* the needed buffers.
*
* @see java.awt.Component#isDisplayable()
**/
public void setBuffers(Dimension bufSize, int numBuffers, boolean forceSize,
boolean forceAcceleration) {
}
/**
* 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.
*
* @return An array of graphics objects that can be used to draw into
* the internal image buffers.
*
* @throws IllegalStateException if the setBuffers()
hasn't
* been successfully called.
*
* @see #setBuffers(java.awt.Dimension, int, boolean, boolean)
**/
public Graphics2D[] getBuffersGraphics() {
return null;
}
/**
* Get the size of the internal image buffers. If setBuffers has
* not yet been successfully called, then null is returned.
*
* @see #getBuffersGraphics()
* @see #setBuffers(java.awt.Dimension, int, boolean, boolean)
**/
public Dimension getBuffersSize() {
return null;
}
/**
* 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.
**/
public void paint(Graphics g) {
}
/**
* 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.
*
* @param frameNumber The frame number to draw. The first frame
* is frame 0.
*
* @return 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 IllegalArgumentException if frameNumber is less than or
* equal to a number previously
* supplied to this animation, or is
* less than zero.
*
* @throws IllegalStateException if startFrame()
* has already been
* successfully
* called without a corresponding
* finishFrame()
.
*
* @throws 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 #getBuffersGraphics()
* @see #isStarted()
**/
public int startFrame(int frameNumber) throws InterruptedException {
return -1;
}
/**
* 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.
*
* @param frameNumber The frame number that is finished. This must
* match the value passed into startFrame(int).
*
* @throws 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 #startFrame(int)
**/
public void finishFrame(int frameNumber) {
}
/**
* 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 IllegalStateException If this component is not displayable.
*
* @see java.awt.Component#isDisplayable()
* @see #startFrame(int)
**/
public void setFramerate(float rate) {
}
/**
* 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_
".
**/
public float getFramerate() {
return 0.0f;
}
/**
* 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 #isStarted()
**/
public void startAnimation() {
}
/**
* 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 previoius 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(...)
.
*
*
* @param c A JMF Clock that is associated with some media.
* @param t A media time of that clock when the
* animation should start.
*
* @throws 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.
*
* @see #isStarted()
* @see #getMediaTime(javax.media.Clock, int)
**/
public void startAnimationAt(Clock c, Time t)
throws IncompatibleTimeBaseException
{
}
/**
* 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.
*
* @param immediate If the component should immediately stop copying
* buffers to the screen,
* instead of letting any queued animation frames
* be output.
*
* @see #isStarted()
* @see #finishFrame(int)
**/
public void stopAnimation(boolean immediate) {
}
/**
* 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)
*
* @return true if this BufferedAnimation
is
* started, false otherwise.
*
* @see #stopAnimation(boolean)
**/
public boolean isStarted() {
return false;
}
/**
* 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.
*
* @param c The clock to calculate media time relative to
* @param frameNumber the desired frame number of this animation
*
* @return The media time, in nanoseconds.
*
* @throws IllegalStateException if this animation is not in the
* started state.
*
* @throws 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.
**/
public long getMediaTime(Clock c, int frameNumber) {
return 0;
}
/**
* 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.
**/
public void removeNotify() {
}
/**
* 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.
**/
public void addNotify() {
}
}