Package net.sf.jiga.xtended.impl

Source Code of net.sf.jiga.xtended.impl.Animation

package net.sf.jiga.xtended.impl;

import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
import java.io.*;
import java.lang.ref.ReferenceQueue;
import java.net.URISyntaxException;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.util.List;
import java.util.Map.Entry;
import java.util.*;
import javax.imageio.event.IIOReadProgressListener;
import javax.imageio.event.IIOReadWarningListener;
import javax.imageio.event.IIOWriteProgressListener;
import javax.imageio.event.IIOWriteWarningListener;
import javax.media.jai.PerspectiveTransform;
import javax.swing.JComponent;
import javax.swing.Timer;
import net.sf.jiga.xtended.JXAException;
import net.sf.jiga.xtended.impl.game.Custom;
import net.sf.jiga.xtended.impl.game.RenderingScene;
import net.sf.jiga.xtended.impl.game.RenderingSceneComponent;
import net.sf.jiga.xtended.impl.game.RenderingSceneListener;
import net.sf.jiga.xtended.impl.game.gl.AnimationGLHandler;
import net.sf.jiga.xtended.impl.game.gl.GLFX;
import net.sf.jiga.xtended.impl.game.gl.GLHandler;
import net.sf.jiga.xtended.impl.game.gl.GLObjectHandler;
import net.sf.jiga.xtended.impl.game.gl.RenderingSceneGL;
import net.sf.jiga.xtended.impl.game.gl.geom.GLGeom;
import net.sf.jiga.xtended.impl.system.BufferIO;
import net.sf.jiga.xtended.kernel.*;

/**
* Animation of Sprite's The implemented timer is the most important part of an
* animation design pattern. It must implement a real time iterator to avoid
* visual artefacts while drawing the animation on screen. Yet correctly
* Threaded, the timer, a.k.a animator, calls a small timing framework to mesure
* a informal framerate and to retrieve next animation frame. Thereby nothing is
* done until the first paint-frame call occurs, whereas the buffer/cache is
* call-back to get the most current frame from the image file cache.
*
* @see RenderingSceneListener#reset()
*
* @see Sprite
* @author www.b23prodtm.info
*/
public class Animation implements Debugger, CompositeCapable, Resource, RenderingSceneComponent, Sf3Renderable, Externalizable, Threaded, AnimationHandler<Sprite> {

    /**
     * the DataFlavor used for the mime-type by this Animation class @default DataFlavor.javaSerializedObjectMimeType
     */
    public static final String _MIME_TYPE = DataFlavor.javaSerializedObjectMimeType;
    /**
     * the file extensions supported by this Animation class @default mc2 MC2
     */
    public static final String[] _MIME_EXT = new String[]{"mc2", "MC2"};
    /**
     * the DataFlavor used for identifying a Animation instance
     */
    public static final DataFlavor _dataFlavor = new DataFlavor(Animation.class, "sf3jswing Animation");

    static {
        Custom._storeMIMETYPES(_MIME_TYPE, _MIME_EXT);
    }
    /**
     * serial version UID to identify serialize operations
     */
    private static final long serialVersionUID = 2323;
    /**
     * starting frame n
     */
    protected int startingFrame;

    /**
     * @return returns the starting frame number
     */
    @Override
    public int getStartingFrame() {
        return startingFrame;
    }
    /**
     * prefix to each frame file
     */
    protected String prefix;

    /**
     * @return returns the prefix to each frame file
     */
    public String getPrefix() {
        return prefix;
    }
    /**
     * suffix to each frame file
     */
    protected String suffix;

    /**
     * @return returns the suffix to each frame file
     */
    public String getSuffix() {
        return suffix;
    }
    /**
     * current frame index
     */
    protected int animator;
    /**
     * reverse playing @default false protected boolean reverse = false;
     */
    /**
     * timer
     */
    protected transient Timer timer;
    /**
     * frames sorted map
     */
    protected transient SortedMap<Integer, Sprite> frames;
    /**
     * cache map
     */
    protected SpritesCacheManager<Integer, Sprite> spm;

    /**
     * returns the current cache instance to permit external access. NOTICE : as
     * of SpritesCacheManager specs, the returned map may not equal sizes for
     * keySet(),size() and values(). <i>To obtain the full size of the cache
     * (living and swapped), please use {@linkplain Map#keySet()}.</i>
     *
     * @return the current cache instance to permit external access to the cache
     * methods.
     * @see #accessSynchCache()
     */
    public SpritesCacheManager<Integer, Sprite> accessCache() {
        return spm;
    }

    /**
     * returns a synchronized Collections-view of the current cache instance.
     * NOTICE : as of SpritesCacheManager specs, the returned map may not equal
     * sizes for keySet(),size() and values(). <i>To obtain the full size of the
     * cache (living and swapped), please use {@linkplain Map#keySet()}.</i>
     *
     * @return a synchronized Collections-view of the currrent cache instance.
     */
    public SortedMap<Integer, Sprite> accessSynchCache() {
        return frames;
    }
    /**
     * image file cache map
     */
    protected final SortedMap<Integer, File> imageFiles = Collections.synchronizedSortedMap(new TreeMap<Integer, File>());
    /**
     * image file cache map protected final SortedMap<Integer, File> texFiles =
     * Collections.synchronizedSortedMap(new TreeMap<Integer, File>());
     */
    /**
     * sound fx
     */
    protected transient SoundInput sfx;

    public SoundInput getSfx() {
        return sfx;
    }

    public int getSfx_frame() {
        return sfx_frame;
    }

    public String getSfx_dir() {
        return sfx_dir;
    }

    public String getSfx_path() {
        return sfx_path;
    }

    public boolean isSfxIsRsrc() {
        return sfxIsRsrc;
    }
    /**
     * sound fx file path
     */
    protected String sfx_path;
    /**
     * sound directory path
     */
    protected String sfx_dir = "cache" + File.separator + "soundIO";
    /**
     * start time stamp (nanos)
     */
    protected long start = 0;
    /**
     * last timer tick time stamp (ms)
     */
    protected long lastTick = 0;
    private final static BitStack bits = new BitStack();
    /**
     * paused state
     */
    public final static int PAUSED = bits._newBitRange();
    /**
     * playing state
     */
    public final static int PLAYING = bits._newBitRange();
    /**
     * stopped state
     */
    public final static int STOPPED = bits._newBitRange();
    /**
     *
     */
    public final static int STATE_ACTIVE_RENDERING = bits._newBitRange();
    /**
     *
     */
    public final static int STATE_COMPRESSION = bits._newBitRange();
    /**
     *
     */
    public final static int STATE_SWAP = bits._newBitRange();
    /**
     *
     */
    public final static int STATE_REVERSED = bits._newBitRange();
    /**
     *
     */
    public final static int STATE_JLAYER = bits._newBitRange();
    /**
     *
     */
    public final static int STATE_TRANSFORM = bits._newBitRange();
    /**
     * resource LOADED state
     */
    public final static int LOADED = bits._newBitRange();
    /**
     * resource CLEARED state
     */
    public final static int CLEARED = bits._newBitRange();
    /**
     * resource LOADING state
     */
    public final static int LOADING = bits._newBitRange();
    /**
     * current state
     *
     * @see #PLAYING
     * @see #STOPPED
     * @see #PAUSED
     * @see #playerStatus()
     */
    protected int statusID = CLEARED | STOPPED | STATE_ACTIVE_RENDERING | STATE_SWAP | STATE_TRANSFORM;
    /**
     * frames amount count
     */
    public int length;
    /**
     * current framerate mesured in nanos, default is 24FPS
     *
     * @see #timer
     */
    public long frameRate = Math.round(1000000000. / 24.); /*
     * nanoseconds
     */

    /**
     * last drawn frame index
     */
    protected int lastFramePosition = 0;
    /**
     * next constant value, used for iterating
     *
     * @see #getAnimatorValue(int, int)
     */
    public static final int NEXT = 0;
    /**
     * previous constant value, used for iterating
     *
     * @see #getAnimatorValue(int, int)
     */
    public static final int PREVIOUS = 1;
    /**
     * sound inner-resource dis/enabled
     */
    protected boolean sfxIsRsrc;

    public Animation() {
        this(1, "image/x-png", SpriteIO._WRITE_TILES_DIMENSION);
    }

    /**
     * Initializes an animation with the specified disk base link and frame
     * indexes.
     *
     * @see net.sf.jiga.sf3.system.Resource
     * @see Sprite#mime
     * @see Sprite#size @discussion (comprehensive description)
     * @param startingFrame starting frame number that appears in frame sprites
     * file names
     * @param endingFrame ending frame number that appears in frame sprites file
     * names
     * @param baseLink string path to base directory of the frame sprites, if
     * rsrcMode is true, then only reachable resources paths may be available.
     * @param prefix string prefix to every frame sprites filenames
     * @param suffix string suffix to every frame sprites filenames (usually
     * file .extension)
     * @param format image mime type of Sprites
     * @param size desired image size of Sprites
     * @param rsrcMode inner-resource mode for frame sprites files dis/enabled
     * @throws java.net.URISyntaxException if the base path does not exist
     */
    public Animation(String baseLink, boolean rsrcMode, int startingFrame, int endingFrame, String prefix, String suffix, String format, Dimension size) throws URISyntaxException {
        renderableImpl = new Sprite(false, baseLink, rsrcMode, format, size, true);
        sfxIsRsrc = rsrcMode;
        this.length = endingFrame - startingFrame + 1;
        frames = Collections.synchronizedSortedMap(spm = new SpritesCacheManager<Integer, Sprite>(Math.max(1, length)));
        spm.setCompressionEnabled(isCompressedCacheEnabled());
        spm.setSwapDiskEnabled(true);
        this.startingFrame = startingFrame;
        this.prefix = prefix;
        this.suffix = suffix;
        animator = (isReverseEnabled()) ? length - 1 : 0;
        stop();
    }

   
    @Override
    public void setUseIIOCacheEnabled(boolean b) {
        renderableImpl.setUseIIOCacheEnabled(b);
    }

    @Override
    public boolean isUseIIOCacheEnabled() {
        return renderableImpl.isUseIIOCacheEnabled();
    }
   
    /**
     * Initializes the animation with blank frames that can be drawn on using
     * the getImage() method on each Sprite.
     *
     * @see #getImage(Component)
     *
     * @param length amount of frames to put in this Animation
     * @param size desired dimension of each sprite image
     * @param format sprite image mime type to use (e.g. image/jpeg)
     * @see #loadBlank(int) @discussion (comprehensive description)
     */
    public Animation(int length, String format, Dimension size) {
        renderableImpl = new Sprite(false, SpriteIO.createBufferedImage(size, Sprite.DEFAULT_TYPE), format, size);
        renderableImpl.setUseIIOCacheEnabled(false);
        sfxIsRsrc = renderableImpl.innerResource;
        this.length = length;
        this.frames = Collections.synchronizedSortedMap((spm = new SpritesCacheManager<Integer, Sprite>(Math.max(1, length))));
        spm.setCompressionEnabled(isCompressedCacheEnabled());
        spm.setSwapDiskEnabled(true);
        animator = (isReverseEnabled()) ? length - 1 : 0;
        stop();
    }

    /**
     * will automatically switch to {@linkplain #setActiveRenderingEnabled(boolean) passive rendering}
     * when the returned component is added to a layout Container. NOTICE : {@link #play() call to play}
     * after the component's been added to a container !
     */
    public static JComponent _makeAnimationJComponent(final Animation anim) {
        JComponent render = new JComponent() {

            @Override
            public void addNotify() {
                super.addNotify();
                anim.setActiveRenderingEnabled(false);
            }

            @Override
            public void removeNotify() {
                super.removeNotify();
                anim.setActiveRenderingEnabled(true);
            }

            @Override
            protected void paintComponent(Graphics g) {
                anim.paintComponentImpl(g, this);
            }
        };
        render.addComponentListener(new ComponentAdapter() {

            @Override
            public void componentResized(ComponentEvent e) {
                /*
                 * to avoid crappy affect if the component is stretched out*
                 * super.componentResized(e); anim.clearResource();-- already
                 * clearresource (at draw())
                 */
            }
        });
        anim.renderableImpl.setObs(render);
        render.setMinimumSize(anim.getSize());
        render.setPreferredSize(anim.getSize());
        return render;
    }

    /**
     * method that clears the resources on finalization.
     *
     * @see #clearResource()
     * @throws java.lang.Throwable thrown by the super class
     */
    @Override
    public void finalize() throws Throwable {
        stop();
        frames.clear();
        super.finalize();
    }

    private void writeFilesMap(SortedMap<Integer, File> map, ObjectOutput out) throws IOException {
        synchronized (map) {
            for (Iterator<Integer> i = map.keySet().iterator(); i.hasNext();) {
                int key;
                File f = map.get(key = i.next());
                out.writeInt(key);
                out.writeObject(f);
                if (f.exists()) {
                    RandomAccessFile raf = new RandomAccessFile(f, "r");
                    out.writeLong(raf.length());
                    byte[] b = new byte[FileHelper._SMALLBUFFFER_SIZE];
                    int read = 0;
                    while ((read = raf.read(b)) != -1) {
                        out.write(b, 0, read);
                    }
                    raf.close();
                } else {
                    out.writeLong(0L);
                }
            }
        }
    }

    private void readFilesMap(SortedMap<Integer, File> map, ObjectInput in) throws IOException, ClassNotFoundException {
        synchronized (map) {
            for (Iterator<Integer> i = map.keySet().iterator(); i.hasNext();) {
                int key, rkey;
                File f, rf;
                f = map.get(key = i.next());
                rkey = in.readInt();
                rf = (File) in.readObject();
                if (key != rkey) {
                    throw new JXAException("wrong key in Animation " + renderableImpl.base + " image files base : " + key + " read : " + rkey);
                }
                if (!f.equals(rf)) {
                    throw new JXAException("wrong file in Animation " + renderableImpl.base + " image files base : " + f + " read : " + rf);
                }
                long len;
                boolean skipped = false;
                if ((len = in.readLong()) > 0) {
                    if (!f.exists() || f.length() != len) {
                        if (!f.exists()) {
                            if ((f = new File(FileHelper._TMPDIRECTORY.getPath() + File.separator + SpriteIO.image_dir + File.separator + f.getName())).isFile()) {
                                skipped = true;
                            }
                        }
                        if (!skipped) {
                            f.getParentFile().mkdirs();
                            FileHelper._makeWritable(f.getParentFile());
                            RandomAccessFile raf = new RandomAccessFile(f, "rw");
                            raf.setLength(len);
                            FileOutputStream fos = new FileOutputStream(raf.getFD());
                            byte[] b = new byte[FileHelper._SMALLBUFFFER_SIZE];
                            int readBytes = 0;
                            int rBytes;
                            boolean fread = true;
                            do {
                                if (readBytes + b.length > len) {
                                    in.readFully(b, 0, rBytes = ((int) len) - readBytes);
                                } else {
                                    rBytes = in.read(b);
                                }
                                if (rBytes != -1) {
                                    fos.write(b, 0, rBytes);
                                    readBytes += rBytes;
                                } else {
                                    fread = false;
                                }
                                if (readBytes >= len) {
                                    fread = false;
                                }
                            } while (fread);
                            fos.close();
                            raf.close();
                        }
                    } else {
                        skipped = true;
                    }
                    if (skipped) {
                        int skip = in.skipBytes((int) len);
                    }
                    f.deleteOnExit();
                }
            }
        }
    }

    private void prepareWrite(ObjectOutput out) throws IOException {
        stop();
        /*
         * setSwapDiskCacheEnabled(true);
         */
        loadResource();
        /**
         * storing mode
         */
        List<Integer> removals = Collections.synchronizedList(new ArrayList<Integer>());
        synchronized (frames) {
            for (int s : frames.keySet()) {
                Sprite sp = frames.get(s);
                if (sp instanceof Sprite) {
                    refreshFiles(sp, s);
                } else {
                    removals.add(s);
                }
            }
            for (Integer s : removals) {
                frames.remove(s);
            }
        }
    }

    private void endWrite(ObjectOutput out) throws IOException {
        if ((getStoreMode() & MODE_JAVA2D) != 0) {
            writeFilesMap(imageFiles, out);
        }
        /*
         * if ((getStoreMode() & MODE_TEXTURE) != 0) { writeFilesMap(texFiles,
         * out);
        }
         */
        if (sfx.isResourceLoaded()) {
            out.writeBoolean(true);
            FileInputStream fis = null;
            BufferedInputStream bis = (sfxIsRsrc) ? new BufferedInputStream(getClass().getResourceAsStream(sfx_path)) : new BufferedInputStream(fis = new FileInputStream(sfx_path));
            File d = new File(FileHelper._TMPDIRECTORY.getPath() + File.separator + sfx_dir);
            d.mkdirs();
            FileHelper._makeWritable(d);
            File sfxFile = FileHelper._createTempFile("sfx_" + hashCode(), d, true);
            out.writeObject(sfxFile);
            RandomAccessFile raf = new RandomAccessFile(sfxFile, "rw");
            raf.setLength(bis.available());
            FileOutputStream fos = new FileOutputStream(raf.getFD());
            byte[] b = new byte[FileHelper._SMALLBUFFFER_SIZE];
            int read = 0;
            while ((read = bis.read(b)) != -1) {
                fos.write(b, 0, read);
            }
            fos.close();
            bis.close();
            raf.close();
            raf = new RandomAccessFile(sfxFile, "r");
            out.writeLong(sfxFile.length());
            b = new byte[FileHelper._SMALLBUFFFER_SIZE];
            read = 0;
            while ((read = raf.read(b)) != -1) {
                out.write(b, 0, read);
            }
            raf.close();
            if (fis instanceof FileInputStream) {
                fis.close();
            }
        } else {
            out.writeBoolean(false);
        }
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        int pty = Thread.currentThread().getPriority();
        Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
        if (isDebugEnabled()) {
            System.out.println("*            Animation's serializing (Externally)...");
        }
        prepareWrite(out);
        /*
         * super.writeExternal(out);
         */

        /*
         *
         */
        out.writeObject(renderableImpl);
        out.writeInt(animator);
        out.writeLong(frameRate);
        synchronized (imageFiles) {
            out.writeInt(imageFiles.size());
            for (Iterator<Entry<Integer, File>> it = imageFiles.entrySet().iterator(); it.hasNext();) {
                Entry<Integer, File> e = it.next();
                out.writeInt(e.getKey());
                out.writeObject(e.getValue());
            }
        }
        out.writeInt(lastFramePosition);
        out.writeLong(lastTick);
        out.writeInt(length);
        out.writeUTF(prefix);
        out.writeBoolean(sfxIsRsrc);
        if (sfx_dir != null) {
            out.writeBoolean(true);
            out.writeUTF(sfx_dir);
        } else {
            out.writeBoolean(false);
        }
        out.writeInt(sfx_frame);
        if (sfx_path != null) {
            out.writeBoolean(true);
            out.writeUTF(sfx_path);
        } else {
            out.writeBoolean(false);
        }
        out.writeObject(spm);
        out.writeLong(start);
        out.writeInt(startingFrame);
        out.writeInt(statusID);
        out.writeUTF(suffix);
        endWrite(out);
        if (isDebugEnabled()) {
            System.out.println("*            Animation's been serialized (Externally).");
        }
        out.flush();
        Thread.currentThread().setPriority(pty);
    }

    private void endRead(ObjectInput in) throws IOException, ClassNotFoundException {
        sfx_played = false;
        frames = Collections.synchronizedSortedMap(spm);
        if ((getStoreMode() & MODE_JAVA2D) != 0) {
            readFilesMap(imageFiles, in);
        }
        /*
         * if ((getStoreMode() & MODE_TEXTURE) != 0) { readFilesMap(texFiles,
         * in);
        }
         */
        if (in.readBoolean()) {
            File dir = new File(FileHelper._TMPDIRECTORY.getPath() + File.separator + sfx_dir);
            dir.mkdirs();
            FileHelper._makeWritable(dir);
            File _sfx = (File) in.readObject();
            if (_sfx == null) {
                _sfx = FileHelper._createTempFile("sfx_" + hashCode(), dir, true);
            }
            long len = in.readLong();
            boolean skipped = false;
            if (!_sfx.exists() || _sfx.length() != len) {
                if (!_sfx.exists()) {
                    if ((_sfx = new File(FileHelper._TMPDIRECTORY.getPath() + File.separator + sfx_dir + File.separator + _sfx.getName())).isFile()) {
                        skipped = true;
                    }
                }
                if (!skipped) {
                    _sfx.getParentFile().mkdirs();
                    FileHelper._makeWritable(_sfx.getParentFile());
                    RandomAccessFile raf = new RandomAccessFile(_sfx, "rw");
                    raf.setLength(len);
                    FileOutputStream fos = new FileOutputStream(raf.getFD());
                    byte[] b = new byte[FileHelper._SMALLBUFFFER_SIZE];
                    int readBytes = 0;
                    int rBytes;
                    boolean fread = true;
                    do {
                        if (readBytes + b.length > len) {
                            in.readFully(b, 0, rBytes = ((int) len) - readBytes);
                        } else {
                            rBytes = in.read(b);
                        }
                        if (rBytes != -1) {
                            fos.write(b, 0, rBytes);
                            readBytes += rBytes;
                        } else {
                            fread = false;
                        }
                        if (readBytes >= len) {
                            fread = false;
                        }
                    } while (fread);
                    fos.close();
                    raf.close();
                    if (isDebugEnabled()) {
                        System.out.println("Animation serialized sfx path: " + sfx_path);
                    }
                }
            } else {
                skipped = true;
            }
            if (skipped) {
                in.skipBytes((int) len);
            }
            _sfx.deleteOnExit();
            sfx_path = _sfx.getAbsolutePath();
            setSfx(sfx_path, false, sfx_frame);
        }
        goLoadState(CLEARED);
        if (isDebugEnabled()) {
            System.out.println("*            Animation's been deserialized (Externally).");
        }
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        int pty = Thread.currentThread().getPriority();
        Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
        if (isDebugEnabled()) {
            System.out.println("*            Animation is deserializing...");
        }
        renderableImpl = (Sprite) in.readObject();
        if (isDebugEnabled()) {
            System.out.println("*            Animation " + renderableImpl.base + " is deserializing...");
        }
        animator = in.readInt();
        frameRate = in.readLong();
        int s = in.readInt();
        for (int i = 0; i < s; i++) {
            imageFiles.put(in.readInt(), (File) in.readObject());
        }
        lastFramePosition = in.readInt();
        lastTick = in.readLong();
        length = in.readInt();
        prefix = in.readUTF();
        sfxIsRsrc = in.readBoolean();
        if (in.readBoolean()) {
            sfx_dir = in.readUTF();
        }
        sfx_frame = in.readInt();
        if (in.readBoolean()) {
            sfx_path = in.readUTF();
        }
        spm = (SpritesCacheManager<Integer, Sprite>) in.readObject();
        start = in.readLong();
        startingFrame = in.readInt();
        statusID = in.readInt();
        suffix = in.readUTF();
        endRead(in);
        Thread.currentThread().setPriority(pty);
    }

    /**
     * sets the animation duration which modifies the current framerate, e.g.
     * 3000 ms for an amount 3 frames will set the framerate to 1FPS for this
     * animation.
     *
     * @param millis the desired duration in ms
     */
    @Override
    public void setRealTimeLength(long millis) {
        frameRate = Math.round((double) millis * 1000000. / (double) length);
    }

    /**
     * returns duration based on framerate and frame length of the animation.
     *
     * @return current duration in millis
     */
    @Override
    public long realTimeLength() {
        return Math.round(realTimeLengthNanos() / 1000000.);
    }

    /**
     *
     */
    private long realTimeLengthNanos() {
        return frameRate * length;
    }

    /**
     *
     */
    /**
     * elapsed period of frames since the animator player started
     *
     * @return elapsed amount of frames (always positive or zero)
     * @see #elapsedTime()
     */
    private int elapsedFrames() {
        return Math.round((float) elapsedTime() / (float) realTimeLengthNanos() * (float) length);
    }

    /**
     * elapsed period of time since the last tick occured on animator timer
     *
     * @return elapsed time in ms since last tick of the timer
     * @see #lastTick
     */
    private long elapsedTickTime() {
        long t = System.nanoTime() - lastTick;
        if (playerStatus() != PLAYING) {
            t = 0;
        }
        return t;
    }

    /**
     * elapsed frames since last tick occured on animator timer
     *
     * @return elapsed amount of frames since last tick of the timer
     * @see #elapsedTickTime()
     */
    private int elapsedTickFrames() {
        return Math.round((float) elapsedTickTime() / (float) realTimeLengthNanos() * (float) length);
    }

    /**
     * re-adjusts startTime to the animation current timeframe if needed,
     * thereby the animator can loop. But use play() to make loops is the
     * correct manner.
     *
     * @return new start time in ms if current is to old
     * @see #start
     * @see #play()
     */
    private long adjustStartTime() {
        if (elapsedTime() >= realTimeLengthNanos() || playerStatus() != PLAYING) {
            lastTick = System.nanoTime();
            start = lastTick - getTimeFramePositionNanos();
        }
        return start;
    }

    /**
     * loads and returns one blank frame with the specified index with the
     * current settings (size, mime type, etc.)
     *
     * @param i frame index (it is painted on the background of the sprite)
     * @return frame Sprite
     */
    private Sprite loadBlank(int i) {
        if (isDebugEnabled()) {
            System.out.println("Animation : loading blank sprite at frame " + i);
        }
        Image img = (!renderableImpl.isBufferedImageEnabled()) ? (Image) Sprite.createVolatileImage(renderableImpl.getSize(), Sprite._getTransparency(renderableImpl._type)) : SpriteIO.createBufferedImage(renderableImpl.getSize(), renderableImpl._type);
        Graphics dataG = Sprite._createImageGraphics(img);
        Graphics2D g = Sprite.wrapRendering(dataG);
        g.setFont(new Font("Arial", Font.ITALIC, 20));
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, renderableImpl.getBounds().width, renderableImpl.getBounds().height);
        g.setColor(Color.GRAY);
        g.drawString("(" + (i + 1) + ")", (int) ((float) renderableImpl.getBounds().width / 2.0f), (int) ((float) renderableImpl.getBounds().height / 2.0f));
        dataG.dispose();
        Sprite sp = (!renderableImpl.isBufferedImageEnabled()) ? new Sprite((VolatileImage) img, renderableImpl.mime, renderableImpl.getSize()) : new Sprite((BufferedImage) img, renderableImpl.mime, renderableImpl.getSize());
        return sp;
    }

    /**
     * marks the FPS monitor to mesure the actual framerate
     *
     * @return actual fps
     */
    protected double markFPS() {
        int newFramePosition = 0;
        double fps = 0;
        if ((newFramePosition = getPosition()) != lastFramePosition) {
            fps = renderableImpl.markFPS();
            lastFramePosition = newFramePosition;
        }
        return fps;
    }

    /**
     * instances a new Sound interface
     *
     * @return one sound interface
     * @see Sound
     */
    private SoundInput initSound() {
        return initSound(sfx_path, sfxIsRsrc);
    }

    private SoundInput initSound(String sfx_path, boolean sfxIsRsrc) {
        return sfx = new SoundInput(this.sfx_path = sfx_path, this.sfxIsRsrc = sfxIsRsrc);
    }

    /**
     * computes the hashcode of the animation
     *
     * @return the hashcode
     */
    @Override
    public int hashCode() {
        return renderableImpl.hashCode() + ((frames != null) ? frames.hashCode() : 0);
    }

    /**
     * checks for equality with the specified object
     *
     * @return true or false
     * @param o the specified object to check for equality
     */
    @Override
    public boolean equals(Object o) {
        return o != null ? hashCode() == o.hashCode() : false;
    }

    /**
     * gets the playerStatus of the animation
     *
     * @return playerStatus id
     * @see #STOPPED
     * @see #PLAYING
     * @see #PAUSED
     * @see #STATE_ACTIVE_RENDERING
     * @see #state
     */
    protected int playerStatus() {
        return statusID & (STOPPED | PLAYING | PAUSED);
    }

    /**
     * player state
     *
     * @param state go to the specified state (private use only, this is not
     * safe)
     * @see #PLAYING
     * @see #PAUSED
     * @see #STOPPED
     */
    private void goPlayerState(int state) {
        statusID = (statusID & ~(PLAYING | PAUSED | STOPPED)) | state;
    }

    /**
     * resource loader state
     *
     * @param state go to the specified LOAD state (private use only, this is
     * not safe)
     * @see #LOADED
     * @see #CLEARED
     * @see #LOADING
     */
    private void goLoadState(int state) {
        statusID = (statusID & ~(LOADING | LOADED | CLEARED)) | state;
    }

    /**
     * same as getState() but the result is not a bitwise-OR combination.
     *
     * @return {@linkplain #PLAYING}, {@linkplain #STOPPED} or {@linkplain #PAUSED}
     * @see #getState()
     */
    @Override
    public int getPlayerStatus() {
        return playerStatus();
    }

    /**
     * activates compressed cache map
     *
     * @param b compressed cachee enabled
     * @see SpritesCacheManager#setCompressionEnabled(boolean)
     */
    public void setCompressedCacheEnabled(boolean b) {
        statusID = b ? statusID | STATE_COMPRESSION : statusID & ~STATE_COMPRESSION;
        spm.setCompressionEnabled(b);
    }

    /**
     * @return true or false, whether cache is compressed or not, resp.
     */
    public boolean isCompressedCacheEnabled() {
        return (statusID & STATE_COMPRESSION) != 0;
    }

    /**
     * gets the state of the current animation
     *
     * @return current state, a bitwise-OR combination of the player
     * playerStatus and rendering mode.
     * ({@link #PLAYING}^{@link #PAUSED}^{@link #STOPPED}) | {@link #STATE_ACTIVE_RENDERING}
     * | ...
     * @see #playerStatus()
     */
    public int getState() {
        return statusID;
    }

    /**
     * gets the current allocation size of cache
     *
     * @return current allocation size in percent
     * @see SpritesCacheManager#allocSize()
     */
    public double allocSize() {
        return spm.allocSize();
    }

    /**
     * gets the animation length
     *
     * @return frames amount of this animation
     * @see #length
     */
    @Override
    public int length() {
        return length;
    }

    /**
     * positions the animator to the corresponding frame index
     *
     * @param i frame index position
     * @return timeframe position in ms
     * @see #getPosition()
     */
    @Override
    public long position(int i) {
        final Monitor monitor = renderableImpl.renderableImpl.imageSynch;
        synchronized (monitor) {
            assert (i < length && i >= 0) : getClass().getCanonicalName() + " iterator is not in the correct interval!";
            animator = i;
        }
        adjustStartTime();
        return getTimeFramePosition();
    }

    /**
     * returns the current position of the animator
     *
     * @return current animator value corresponding to frame index
     */
    @Override
    public int getPosition() {
        final Monitor monitor = renderableImpl.renderableImpl.imageSynch;
        synchronized (monitor) {
            return animator;
        }
    }

    public boolean isValid() {
        return isResourceLoaded();
    }

    public void invalidate() {
        clearResource();
    }

    /**
     * refreshes the animation and the cache map to current params and prints
     * current iterator value. if activerendering is activated, it also runs the
     * player thread to retrieve the animator index value to paint. it must be
     * called each time refreshing the current frame image is required.
     * @discussion (comprehensive description)
     *
     * @see #spm
     * @see #setZoomEnabled(boolean, double)
     * @see #setFlipEnabled(boolean, int)
     * @see Sprite#runValidate()
     */
    @Override
    public Animation runValidate() {
        spm.setCompressionEnabled(isCompressedCacheEnabled());
        spm.setSwapDiskEnabled(true);
        setZoomEnabled(isTransformEnabled(), renderableImpl.zoom);
        renderableImpl.setFlipEnabled(isTransformEnabled(), renderableImpl.mirror);
        if (!isResourceLoaded()) {
            loadResource();
        }

        try {
            final Monitor monitor0 = renderableImpl.renderableImpl.paintMonitor;
            synchronized (monitor0) {
                if (sfx instanceof Sound) {
                    if (!sfx.isResourceLoaded()) {
                        sfx.loadResource();
                    }
                } else {
                    initSound().loadResource();
                }
                while (renderableImpl.painting) {
                    if (isDebugEnabled()) {
                        System.out.print(".");
                    }
                    monitor0.wait(1000);
                }
                renderableImpl.validating = true;
                final Monitor monitor2 = renderableImpl.renderableImpl.imageSynch;
                synchronized (monitor2) {
                    if (playerStatus() == PLAYING) {
                        if (elapsedTime() >= realTimeLengthNanos()) {
                            stop();
                        } else {
                            if (isReverseEnabled()) {
                                animator -= elapsedTickFrames();
                            } else {
                                animator += elapsedTickFrames();
                            }
                            lastTick = System.nanoTime();
                        }
                    }
                    animator = Math.min(animator, length - 1);
                    animator = Math.max(animator, 0);

                    if (playerStatus() == PLAYING) {
                        if ((isReverseEnabled() ? sfx_frame >= animator : sfx_frame <= animator) && sfx.isResourceLoaded()) {
                            if (!sfx_played) {
                                sfx_played = playSfx();
                            }
                        } else {
                            sfx_played = false;
                        }
                    }
                }
            }
        } catch (InterruptedException e) {
            if (isDebugEnabled()) {
                e.printStackTrace();
            }
        } finally {
            final Monitor monitor1 = renderableImpl.renderableImpl.validateMonitor;
            synchronized (monitor1) {
                renderableImpl.validating = false;
                monitor1.notifyAll();
            }
            return this;
        }
    }

    /**
     * returns the current frame sprite that the animator is pointing on
     *
     * @see Sprite
     * @return current frame sprite
     */
    @Override
    public Sprite getCurrentSprite() {
        final Monitor monitor = renderableImpl.renderableImpl.imageSynch;
        synchronized (monitor) {
            return getSprite(animator);
        }
    }

    /**
     * returns the desired sprite instance at index
     *
     * @param index the specified index of the animation Sprite to return [start
     * index; end index]
     * @return the Sprite at the specified index
     */
    @Override
    public Sprite getSprite(int index) {
        Sprite sp = null;
        if ((sp = frames.get(index)) == null) {
            sp = loadFrame(index);
        }
        return refreshSpriteData(sp, (isReverseEnabled()) ? length - 1 - index : index);
    }

    /**
     * refreshes the specified Sprite instance to the current settings
     *
     * @param sp Sprite instance to refresh
     * @param id the id on which the MediaTracker will track
     * @return the refreshed Sprite instance
     */
    private Sprite refreshSpriteData(Sprite sp, int id) {
        synchronized (renderableImpl.renderableImpl.io.rProgList) {
            for (IIOReadProgressListener iiorp : renderableImpl.renderableImpl.io.rProgList) {
                sp.getIO().addIIOReadProgressListener(iiorp);
            }
        }
        synchronized (renderableImpl.renderableImpl.io.rWarnList) {
            for (IIOReadWarningListener iiorw : renderableImpl.renderableImpl.io.rWarnList) {
                sp.getIO().addIIOReadWarningListener(iiorw);
            }
        }
        synchronized (renderableImpl.renderableImpl.io.wProgList) {
            for (IIOWriteProgressListener iiowp : renderableImpl.renderableImpl.io.wProgList) {
                sp.getIO().addIIOWriteProgressListener(iiowp);
            }
        }
        synchronized (renderableImpl.renderableImpl.io.wWarnList) {
            for (IIOWriteWarningListener iioww : renderableImpl.renderableImpl.io.wWarnList) {
                sp.getIO().addIIOWriteWarningListener(iioww);
            }
        }
        sp.setBufferedType(renderableImpl._type);
        sp.setRenderingScene(getRenderingScene());
        Sf3RenderableImpl.modeCopy(this, sp);
        sp.setTexPty(renderableImpl.getTexPty());
        sp.setMt(renderableImpl.renderableImpl.io.mt, renderableImpl.renderableImpl.io.obs);
        sp.setZoomEnabled(isTransformEnabled(), renderableImpl.zoom);
        sp.setFlipEnabled(isTransformEnabled(), renderableImpl.mirror);
        if (isTransformEnabled()) {
            sp.setTX(renderableImpl.transforming);
            sp.setPTX(renderableImpl.perspectiveTransforming);
        }
        /**
         * avoid resizing after a transform has completed
         */
        try {
            if (renderableImpl._nextTransformSize(sp, sp.renderableImpl.bounds.getSize()).equals(sp.renderableImpl.bounds.getSize()) && (sp.isZoomEnabled() || (isTransformEnabled() && (!renderableImpl.transforming.isIdentity() || !renderableImpl.perspectiveTransforming.isIdentity())))) {
                /*
                 * transform was performed, no resize to bounds is allowed
                 */
            } else {
                sp.renderableImpl.bounds.setSize(renderableImpl.getSize());
                sp.invalidate();
            }
        } catch (NoninvertibleTransformException ex) {
            if (isDebugEnabled()) {
                ex.printStackTrace();
            }
        } finally {
            sp.setOpaque(isOpaque());
            sp.setCompositeEnabled(renderableImpl.compositeEnabled);
            sp.setComposite(renderableImpl.cps);
            sp.setPaint(renderableImpl.pnt);
            sp.setColor(renderableImpl.clr);
            sp.setTrackerPty(id);
            sp.setSPM(spm);
            return sp;
        }
    }
    private static SortedMap<Integer, SortedMap<Integer, Integer>> spritesHashes = Collections.synchronizedSortedMap(new TreeMap<Integer, SortedMap<Integer, Integer>>());

    /**
     *
     */
    public static SortedMap<Integer, Integer> _GLgetSpritesHashes(int animHash) {
        if (!spritesHashes.containsKey(animHash)) {
            spritesHashes.put(animHash, new TreeMap<Integer, Integer>());
        }
        return spritesHashes.get(animHash);
    }

    @Override
    public boolean isResourceLoaded() {
        return (statusID & LOADED) != 0;
    }

    /**
     * @param a an Animation instance or its hashcode
     */
    public static boolean _GLIsLoaded(Object a) {
        return RenderingSceneGL._GLgetLoadState(a) == RenderingSceneGL._GLLOADSTATE_Loaded;
    }

    /**
     * refresh sprite on swap and cache files
     */
    private void refreshFiles(Sprite sp, int i) {
        sp = refreshSpriteData(sp, (isReverseEnabled()) ? length - 1 - i : i);
        frames.put(i, sp);
        if (sp.src instanceof File) {
            imageFiles.put(i, (File) sp.src);
        }
        /*
         * if (sp.texSrc instanceof File) { texFiles.put(i, (File) sp.texSrc);
        }
         */
    }

    /**
     * loads the frame sprites indexed by the specified number
     *
     * @param i index of the frame
     * @return frame sprite loaded at index i
     */
    private Sprite loadFrame(int i) {
        Sprite sp = null;
        if (!frames.containsKey(i)) {
            if (isDebugEnabled()) {
                System.out.println("Animation load " + i + "...");
            }
            if (renderableImpl.base != null) {
                String path = renderableImpl.src + ((renderableImpl.innerResource) ? "/" : File.separator) + prefix + (int) (startingFrame + i) + suffix;
                if (isDebugEnabled()) {
                    System.out.println("loading frame: " + ((renderableImpl.innerResource) ? getClass().getResource(path) : path));
                }
                try {
                    sp = new Sprite(path, renderableImpl.innerResource, renderableImpl.mime, renderableImpl.getSize(), renderableImpl.isBufferedImageEnabled());
                } catch (Exception ex) {
                    ex.printStackTrace();
                    sp = loadError(i);
                }
            } else {
                sp = loadBlank(i);
            }
            refreshFiles(sp, i);
        } else {
            sp = frames.get(i);
            sp = refreshSpriteData(sp, (isReverseEnabled()) ? length - 1 - i : i);
        }
        sp.loadResource();
        /*
         * SpriteGLHandler spHdr = (SpriteGLHandler)
         * Sprite._GLHandlers.getHandler(sp);
         * spHdr.setrCoord(Sf3Texture3D._3DTexLayersAmount * (float) i);
         */
        _GLgetSpritesHashes(hashCode()).put(i, sp.hashCode());/*
         * spHdr);
         */
        return sp;
    }

    /**
     * loads and returns a frame sprite instance for error at index i
     *
     * @param i the index to give error at
     * @return frame sprite instance
     */
    private Sprite loadError(int i) {
        Image img = (!renderableImpl.isBufferedImageEnabled()) ? (Image) Sprite.createVolatileImage(renderableImpl.getSize(), Sprite._getTransparency(renderableImpl._type)) : SpriteIO.createBufferedImage(renderableImpl.getSize(), renderableImpl._type);
        Graphics imgG = Sprite._createImageGraphics(img);
        Graphics2D g = Sprite.wrapRendering(imgG);
        g.setFont(new Font("Arial", Font.ITALIC, 20));
        g.setColor(Color.RED);
        g.fillRect(0, 0, renderableImpl.getBounds().width, renderableImpl.getBounds().height);
        g.setColor(Color.GRAY);
        g.drawString("error_" + i, (int) ((float) renderableImpl.getBounds().width / 2.0f), (int) ((float) renderableImpl.getBounds().height / 2.0f));
        imgG.dispose();
        Sprite sp = (!renderableImpl.isBufferedImageEnabled()) ? new Sprite((VolatileImage) img, renderableImpl.mime, renderableImpl.getSize()) : new Sprite((BufferedImage) img, renderableImpl.mime, renderableImpl.getSize());
        return sp;
    }

    /**
     * unloads the frame sprite instance indexed with the argument. this sprite
     * instance can be recovered if the isSwapDiskCacheEnabled() returns true.
     * @discussion (comprehensive description)
     *
     * @param i index of the frame sprite instance
     */
    protected void unloadFrame(int i) {
        if (frames.containsKey(i)) {
            Sprite sp = frames.get(i);
            sp.clearResource();
            try {
                spm.memorySensitiveCallback("memoryClear", spm, new Object[]{sp}, new Class[]{Object.class});
            } catch (Throwable ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * elapsed period of time since the animator timer started; the playerStatus
     * is changed to stopped-state upon the end of the Animation if it is
     * reached or passed.
     *
     * @return elapsed time since started in nanos
     * @see #realTimeLengthNanos()
     */
    private long elapsedTime() {
        long t = System.nanoTime() - start;
        if (playerStatus() != PLAYING) {
            t = 0;
        }
        return t;
    }

    /**
     * current time-frame position
     *
     * @return current time-frame position in ms
     * @see #getPosition()
     * @see #realTimeLength()
     */
    @Override
    public long getTimeFramePosition() {
        return Math.round(getTimeFramePositionNanos() / 1000000.);
    }

    private long getTimeFramePositionNanos() {
        long posTime = length > 1 ? (long) Math.round(((double) getPosition() / ((double) length - 1.)) * (double) realTimeLengthNanos()) : 0;
        return isReverseEnabled() ? realTimeLengthNanos() - posTime : posTime;
    }

    /**
     * positions the animator to the corresponding time-frame
     *
     * @return corresponding new positioned frame index value
     * @see #position(int)
     * @param timeFrame time frame position in ms (must be positive and less
     * than the total real length time)
     * @see #realTimeLength()
     */
    @Override
    public int position(long timeFrame) {
        final Monitor monitor = renderableImpl.renderableImpl.imageSynch;
        synchronized (monitor) {
            if (isReverseEnabled()) {
                timeFrame = realTimeLength() - timeFrame;
            }
            animator = Math.min(length - 1, Math.max(0, (int) Math.round(((float) timeFrame / (float) realTimeLength()) * (float) length)));
            position(animator);
            return animator;
        }
    }

    /**
     * draws with the specified AffineTransform and PerspectiveTransform
     * instances
     *
     * @return true or false whether the drawing's completed or not (this value
     * cannot be used for synchronization)
     * @param g2 graphics instance to draw on
     * @param tx transform instance
     * @param ptx perspective transform instance
     * @param obs the component that is observing this animation
     */
    @Override
    public boolean draw(Component obs, Graphics2D g2, AffineTransform tx, PerspectiveTransform ptx) {
        try {
            return __draw(obs, g2, tx, ptx, 0, new Point(0, 0), null);
        } catch (Exception ex) {
            if (isDebugEnabled()) {
                ex.printStackTrace();
            }
            return false;
        }
    }

    /**
     * draws with the specified OR-bitwise-combination FX
     *
     * @return true or false whether the drawing's completed or not (this value
     * cannot be used for synchronization)
     * @param g2 graphics instance to draw on
     * @param fx the FX OR-bitwise-combination to use, or 0 for NONE
     * @param fx_loc the FX translation to add to the location, or new Point(0,
     * 0) for NONE
     * @param fx_color the FX color or null for NONE
     * @param obs the component that is observing this animation
     * @throws java.lang.InterruptedException if the current Thread gets
     * interrupted
     * @see #__draw(Component, Graphics2D, AffineTransform,
     * PerspectiveTransform, int, Point, Color)
     * @see Sprite#draw(Component, Graphics2D, int, Point, Color)
     */
    @Override
    public boolean draw(Component obs, Graphics2D g2, int fx, Point fx_loc, Color fx_color) {
        try {
            return __draw(obs, g2, null, null, fx, fx_loc, fx_color);
        } catch (Exception ex) {
            if (isDebugEnabled()) {
                ex.printStackTrace();
            }
            return false;
        }
    }

    /**
     * draws the animation on the specified graphics and component context
     *
     * @return true or false whether the drawing's completed or not (this value
     * cannot be used for synchronization)
     * @see #draw(Component, Graphics2D, AffineTransform, PerspectiveTransform)
     * @param g2 graphics instance to draw on
     * @param obs the component that is observing this animation
     */
    @Override
    public boolean draw(Component obs, Graphics2D g2) {
        try {
            return __draw(obs, g2, null, null, 0, new Point(0, 0), null);
        } catch (Exception ex) {
            if (isDebugEnabled()) {
                ex.printStackTrace();
            }
            return false;
        }
    }
    /**
     * the current sprite instance that this animation is using
     */
    private transient Sprite currentSprite = null;
    /**
     * sprites in animation will show blitting if this is false (due to some
     * unidentified incorrect buffering issues) @default true (can be changed
     * on-the-fly, or with net.sf.jiga.xtended.impl.game.rendering.properties
     * FIXclearBlitSprite=false property)
     */
    public static boolean FIXclearBlitSprite = Boolean.parseBoolean(RenderingScene.rb.getString("FIXclearBlitSprite"));

    /**
     * draws with the specified AffineTransform and PerspectiveTransform
     * instances
     *
     * @return true or false whether the drawing's completed or not (this value
     * cannot be used for synchronization)
     * @param g2 graphics instance to draw on
     * @param tx transform instance
     * @param ptx perspective transform instance
     * @param obs the component that is observing this animation
     * @param fx a {@linkplain Sprite#gfx} constant or 0 for none
     * @param fx_loc an x,y Point location for the fx (depends on the fx used)
     * or set new Point(0,0) for null
     * @param fx_color a Color for the fx or null
     * @throws java.lang.InterruptedException if the current Thread gets
     * interrupted
     */
    private boolean __draw(Component obs, Graphics2D g2, AffineTransform tx, PerspectiveTransform ptx, int fx, Point fx_loc, Color fx_color) throws InterruptedException {
        boolean interrupt_ = false;
        boolean complete = true;
        try {
            currentSprite = getCurrentSprite();
            /**
             * no bounds because of transform issues (see refreshSprite)
             */
            currentSprite.setLocation(getLocation());
            currentSprite.runValidate();
            final Monitor monitor0 = renderableImpl.renderableImpl.validateMonitor;
            synchronized (monitor0) {
                while (renderableImpl.validating) {
                    if (isDebugEnabled()) {
                        System.out.print(".");
                    }
                    monitor0.wait(1000);
                }
                renderableImpl.painting = true;
                if (isDebugEnabled()) {
                    System.out.println("Animation render...");
                }
                if (fx == GLFX.gfx.FX_NONE) {
                    complete = (Boolean) spm.memorySensitiveCallback("draw", currentSprite, new Object[]{obs, g2, tx, ptx}, new Class[]{Component.class, Graphics2D.class, AffineTransform.class, PerspectiveTransform.class}) && complete;
                } else {
                    complete = (Boolean) spm.memorySensitiveCallback("draw", currentSprite, new Object[]{obs, g2, fx, fx_loc, fx_color}, new Class[]{Component.class, Graphics2D.class, int.class, Point.class, Color.class}) && complete;
                }
                markFPS();
            }
        } catch (Throwable t) {
            if (isDebugEnabled()) {
                t.printStackTrace();
            }
            interrupt_ = true;
        } finally {
            if (FIXclearBlitSprite) {
                currentSprite.clearResource(); /*
                 * or BLITTTING!!!
                 */
            }
            if (isDebugEnabled()) {
                System.out.println(complete && !interrupt_ ? "...render OK" : "...render ERROR!");
            }
            final Monitor monitor1 = renderableImpl.renderableImpl.paintMonitor;
            synchronized (monitor1) {
                renderableImpl.painting = false;
                monitor1.notifyAll();
            }
            if (interrupt_) {
                throw new JXAException("Animation " + renderableImpl.base + " caught an interruption.");
            }
            return complete;
        }
    }

    /**
     * sets the animation SFX to play.
     *
     * @see #sfx
     * @see #initSound()
     * @param frameMark frame index telling when to play the sfx
     * @param sfx_path full resource path to the sound file (currently
     * supporting mpeg-layer-3 or PCM wave files)
     * @param rsrcMode specifies the inner-resource mode dis/enabled
     * @see net.sf.jiga.sf3.system.Resource
     */
    public void setSfx(String sfx_path, boolean rsrcMode, int frameMark) {
        assert ((frameMark < length || frameMark == 0) && frameMark >= 0) : getClass().getCanonicalName() + " frame mark for sfx is not in the correct interval!";
        sfx_frame = frameMark;
        initSound(sfx_path, rsrcMode);
        sfx.setUseJLayerEnabled(isSfxUseJLayerEnabled());
        sfx.loadResource();
    }

    /**
     *
     * @param b dis/enables the mp3 layer for the associated sound fx of this
     * Animation
     */
    public void setSfxUseJLayerEnabled(boolean b) {
        statusID = b ? statusID | STATE_JLAYER : statusID & ~STATE_JLAYER;
    }

    /**
     *
     * @return true or false, whether the mp3 layer is enabled or not, resp. for
     * the sound fx associated with this Animation
     */
    public boolean isSfxUseJLayerEnabled() {
        return (statusID & STATE_JLAYER) != 0;
    }

    /**
     * sets whether to reverse or not this animation when playing it
     *
     * @param b true to reverse animation
     */
    @Override
    public void setReverseEnabled(boolean b) {
        statusID = b ? statusID | STATE_REVERSED : statusID & ~STATE_REVERSED;
    }

    /**
     * @return true or false, whether the reverse play switch is enabled or not,
     * resp.
     */
    @Override
    public boolean isReverseEnabled() {
        return (statusID & STATE_REVERSED) != 0;
    }
    /**
     * tells which frame index to use to play the associated sound FX
     *
     * @see #sfx
     * @see #initSound()
     */
    protected int sfx_frame = 0;
    /**
     * sound has been played at least once
     */
    protected transient boolean sfx_played = false;

    /**
     * returns the current frame image instance, seen by the current Component
     * observer.
     *
     * @return the current frame image instance
     * @see #obs
     * @param obs the component that will observe the image
     */
    public Image getImage(
            Component obs) {
        return getCurrentSprite().getImage(obs);
    }

    /**
     * returns the synchronized frames map of this animation
     *
     * @return a synchronized frames map view
     */
    @Override
    public SortedMap<Integer, Sprite> getFrames() {
        return frames;
    }

    /**
     * tells whether mirror transform is enabled
     *
     * @see #getMirrorOrientation()
     * @return true or false
     */
    @Override
    public boolean isMirrored() {
        return (renderableImpl.mirror == renderableImpl.NONE) ? false : true;
    }

    /**
     * returns the current mirror orientation or
     *
     * @see #NONE
     * @see #HORIZONTAL
     * @see #VERTICAL
     * @see #mirror
     * @return mirror orientation
     */
    @Override
    public int getMirrorOrientation() {
        return renderableImpl.mirror;
    }

    /**
     * tells whether the zoom transform is enabled
     *
     * @see #getZoomValue()
     * @return true or false
     */
    @Override
    public boolean isZoomed() {
        return (renderableImpl.zoom == 1.0) ? false : true;
    }

    /**
     * returns current zoom value
     *
     * @return zoom value (1.0 is no zoom)
     */
    @Override
    public double getZoomValue() {
        return renderableImpl.zoom;
    }

    @Override
    public void setFrameRate(long delay) {
        if (delay < 0) {
            throw new JXAException("framerate delay must be different from Zero!");
        }
        frameRate = delay * 1000000;
    }

    /**
     * returns the timestamp used by the animator timer when it has started
     *
     *
     * @return the timestamp got when started playing
     * @see #start
     * @see #play()
     */
    @Override
    public long getStartTime() {
        return System.currentTimeMillis() - Math.round(elapsedTime() / 1000000.);
    }

    /**
     * dis/enables active rendering as defined by the Swing API.
     *
     * @param b dis/enables active rendering for this Animation
     */
    public void setActiveRenderingEnabled(boolean b) {
        if ((statusID & PLAYING) != 0 && b) {
            if (timer instanceof Timer) {
                timer.stop();
            }
        }
        statusID = b ? statusID | STATE_ACTIVE_RENDERING : statusID & ~STATE_ACTIVE_RENDERING;
    }

    /**
     * returns true or false whether active rendering is enabled or not, resp.
     *
     * @return true or false, whether the active rendering is enabled or not,
     * resp.
     */
    public boolean isActiveRenderingEnabled() {
        return (statusID & STATE_ACTIVE_RENDERING) != 0;
    }

    /**
     * starts playing this animation. Actually the animator is incremented or
     * decreased, depending on reverse playerStatus. if it is called more than
     * once it will safely manage to get it to continue (start time will be
     * updated !) even if it has been stopped or paused. The sound FX will be
     * played when the animator comes to set frame index for the sound if any
     * available.
     *
     * @see #sfx_frame
     * @see #setSfx(String, boolean, int)
     */
    @Override
    public void play() {
        int status = playerStatus();
        if (status == PAUSED) {
        }
        if (status == STOPPED) {
            rewind();
            if (!isActiveRenderingEnabled()) {
                if (!(timer instanceof Timer)) {
                    timer = new Timer(Math.round(frameRate / 1000000f), new ActionListener() {

                                            @Override
                        public void actionPerformed(ActionEvent e) {
                            if (!isActiveRenderingEnabled()) {
                                if (renderableImpl.getObs() instanceof JComponent) {
                                    Sprite._quickPaintImmediately((JComponent) Animation.this.renderableImpl.getObs());
                                } else if (renderableImpl.getObs() instanceof Component) {
                                    renderableImpl.getObs().repaint();
                                }
                            }
                        }
                    });
                }
                timer.start();
            }
        }
        start = adjustStartTime();
        goPlayerState(PLAYING);

    }

    /**
     * pauses this animation. animator timer is stopped. next time play() is
     * called it will restart at the current animator index.
     */
    @Override
    public void pause() {
        if (timer != null) {
            timer.stop();
        }
        goPlayerState(PAUSED);

    }

    /**
     * stops playing the animation. the animator will be reset. the animator
     * timer is cancelled.
     */
    @Override
    public void stop() {
        pause();
        goPlayerState(STOPPED);
        timer = null;
    }

    /**
     * returns the animator for the specified operation-mode
     *
     * @param mode operation-mode
     * @see #NEXT
     * @see #PREVIOUS
     * @param position where to get the iterator from
     * @return the animator requested value (current animator won't be modified)
     */
    private int getAnimatorValue(int mode, int position) {
        int i = position;
        switch (mode) {
            case NEXT:
                if (isReverseEnabled()) {
                    if (i > 0) {
                        i--;
                    } else {
                        i = length - 1;
                    }
                } else {
                    if (i < length - 1) {
                        i++;
                    } else {
                        i = 0;
                    }
                }
                break;
            case PREVIOUS:
                if (isReverseEnabled()) {
                    if (i < length - 1) {
                        i++;
                    } else {
                        i = 0;
                    }
                } else {
                    if (i > 0) {
                        i--;
                    } else {
                        i = length - 1;
                    }
                }
                break;
            default:
                break;
        }
        return i;
    }

    /**
     * moves the animator to the next frame index and returns the associated
     * Sprite instance.
     *
     * @return the next frame sprite instance
     * @see #getAnimatorValue(int, int)
     */
    @Override
    public Sprite next() {
        final Monitor monitor = renderableImpl.renderableImpl.imageSynch;
        synchronized (monitor) {
            pause();
            animator = getAnimatorValue(NEXT, animator);
            return getCurrentSprite();
        }
    }

    /**
     * moves the animator to the previous frame index and returns the associated
     * Sprite instance.
     *
     * @return previous frame sprite
     * @see #getAnimatorValue(int, int)
     */
    @Override
    public Sprite previous() {
        final Monitor monitor = renderableImpl.renderableImpl.imageSynch;
        synchronized (monitor) {
            pause();
            animator = getAnimatorValue(PREVIOUS, animator);
            return getCurrentSprite();
        }
    }

    /**
     * rewinds to the animator first index value.
     */
    @Override
    public void rewind() {
        final Monitor monitor = renderableImpl.renderableImpl.imageSynch;
        synchronized (monitor) {
            pause();
            if (isReverseEnabled()) {
                animator = length - 1;
            } else {
                animator = 0;
            }
            sfx_played = false;
        }
    }

    /**
     * Forwards to the animator end index value.
     */
    @Override
    public void end() {
        final Monitor monitor = renderableImpl.renderableImpl.imageSynch;
        synchronized (monitor) {
            if (!isReverseEnabled()) {
                animator = length - 1;
            } else {
                animator = 0;
            }
        }
    }

    /**
     * clears the animation and cache resources. resources can be recovered with
     * loadResource(). a new thread is registered in the buffer threads map.
     * animation is paused. the Timer (if any) lives.
     *
     * @return Thread id of that command
     * @see #loadResource()
     */
    @Override
    public Object clearResource() {
        Object ret = renderableImpl.clearResource();
        pause();
        goLoadState(LOADING);
        currentSprite = null;
        spm.clear();
        spm.cleanup();
        goLoadState(CLEARED);
        return ret;
    }

    /**
     * caches the resources of this animation. a new thread is registered in the
     * buffer threads map.
     *
     * @return Thread id of that command
     * @see #clearResource()
     */
    @Override
    public Object loadResource() {
        Object ret = renderableImpl.loadResource();
        goLoadState(LOADING);
        stop();
        for (int i = 0; i < length; i++) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("Loading of Animation is interrupted. returning...");
                return clearResource();
            }
            loadFrame(i).clearResource();
        }
        currentSprite = getCurrentSprite();
        initSound();
        sfx.loadResource();
        goLoadState(LOADED);
        return ret;
    }

    /**
     * cancels the animation Timer instance if any and clears the resource.
     *
     * @deprecated clearResouce() is better.
     */
    public void cancel() {
        int pty = Thread.currentThread().getPriority();
        Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
        timer = null;
        clearResource();
        Thread.currentThread().setPriority(pty);
    }

    /**
     * removes current frame sprite instance from mapping, it cannot be
     * recovered.
     *
     * @deprecated functional but usually unused
     */
    @Override
    public void remove() {
        final Monitor monitor = renderableImpl.renderableImpl.imageSynch;
        synchronized (monitor) {
            frames.remove(animator);
        }
    }

    /**
     * checks for next frame sprite instance availability
     *
     * @return true or false
     */
    @Override
    public boolean hasNext() {
        final Monitor monitor = renderableImpl.renderableImpl.imageSynch;
        synchronized (monitor) {
            int next = (isReverseEnabled()) ? -1 : +1;
            return frames.containsKey(animator + next);
        }
    }

    /**
     * change cache capacity to allow more frames to be mapped
     *
     * @param n the new cache capacity
     * @see SpritesCacheManager#setListCapacity(int)
     * @deprecated
     */
    public void setCacheCapacity(int n) {
        spm.setListCapacity(n);
    }

    /**
     * returns the cache capacity
     *
     * @return the cache capacity
     * @deprecated
     */
    public int getCacheCapacity() {
        return spm.getListCapacity();
    }

    /**
     * returns the cache ReferenceQueue that repeatly polls to free resource of
     * unused sprites instance.
     *
     * @return the ReferenceQueue that polls for sprites instance to free
     * resources
     * @see SpritesCacheManager#_cacheBack
     * @deprecated null is returned
     */
    public ReferenceQueue getCacheBack() {
        return null;
    }

    /**
     * dis/enables transform. if it is disabled no transform will be made.
     *
     * @see #setFlipEnabled(boolean, int)
     * @see #setZoomEnabled(boolean, double)
     * @param transform dis/enable
     */
    @Override
    public void setTransformEnabled(boolean transform) {
        statusID = transform ? statusID | STATE_TRANSFORM : statusID & ~STATE_TRANSFORM;
    }

    /**
     * @return true or false, whether the transform is enabled or not, resp.
     */
    @Override
    public boolean isTransformEnabled() {
        return (statusID & STATE_TRANSFORM) != 0;
    }

    /**
     * plays the associated Sound in this Animation
     *
     * @return true or false, whether the sound played or not
     * @see #setSfx(String, boolean, int)
     */
    @Override
    public boolean playSfx() {
        if (sfx instanceof Sound) {
            sfx.play();
            return true;
        } else {
            return false;
        }
    }

    /**
     * renders the Animation within an OpenGL context
     *
     * @param gld the RenderingSceneGL instance that is to be rendered onto
     * @param anim the Animation instance to render
     * @param bounds 2D Rectangle bounds for the rendering (0,0 is the upper
     * left corner)
     * @param z the z-depth to render at
     */
    public static void _GLRenderAnimation(RenderingSceneGL gld, AnimationGLHandler anim, Rectangle bounds, double z) {
        int transform = 0;
        DoubleBuffer scale = null;
        DoubleBuffer translate = null;
        if (anim.isTransformEnabled()) {
            if (scale == null) {
                scale = BufferIO._wrapd(new double[]{1, 1});
            }
            if (translate == null) {
                translate = BufferIO._wrapd(new double[]{0, 0});
            }
            if (anim.isZoomed()) {
                scale.put(0, scale.get(0) * anim.getZoomValue());
                scale.put(1, scale.get(1) * anim.getZoomValue());
                transform |= GLHandler._GL_TRANSFORM_SCALE_BIT;
            }
            if (anim.isMirrored()) {
                switch (anim.getMirrorOrientation()) {
                    case Sprite.HORIZONTAL:
                        transform |= GLHandler._GL_TRANSFORM_FLIP_HORIZONTAL_BIT;
                        break;
                    case Sprite.VERTICAL:
                        transform |= GLHandler._GL_TRANSFORM_FLIP_VERTICAL_BIT;
                        break;
                    default:
                        break;
                }
            }
        }
        if ((anim.getLoadState() & RenderingSceneGL._GLLOADSTATE_Loaded) != 0) {
            if (DebugMap._getInstance().isDebugLevelEnabled(Sprite.DBUG_RENDER_LOW)) {
                System.out.println(">>>>>>>>>>>>>>>>GL render Animation " + anim.hashLinkToGLObject() + "...");
            }
            Sprite._GLRenderSprite(gld, anim.getCurrentSprite(), bounds, z, 0, null, null, transform, scale, null, translate);
        }
    }

    /**
     * renders the Animation within an OpenGL context
     *
     * @param gld the RenderingSceneGL that is to be rendered onto
     * @param anim the Animation to render
     * @param bounds 2D Rectangle bounds to render at
     * @param z the z-depth to render at
     * @param fx a {@linkplain Sprite#gfx} or 0 for none
     * @param fx_loc a x,y Point location for the fx, use Point(0,0) for none
     * @param fx_color a Color for the fx or null
     *
     */
    public static void _GLRenderAnimation(RenderingSceneGL gld, AnimationGLHandler anim, Rectangle bounds, double z, int fx, Point fx_loc, Color fx_color, int transform, DoubleBuffer scale, DoubleBuffer rotate, DoubleBuffer translate) {
        if (anim.isTransformEnabled()) {
            if (scale == null) {
                scale = BufferIO._wrapd(new double[]{1, 1});
            }
            if (translate == null) {
                translate = BufferIO._wrapd(new double[]{0, 0});
            }
            if (anim.isZoomed()) {
                scale.put(0, scale.get(0) * anim.getZoomValue());
                scale.put(1, scale.get(1) * anim.getZoomValue());
                transform |= GLHandler._GL_TRANSFORM_SCALE_BIT;
            }
            if (anim.isMirrored()) {
                switch (anim.getMirrorOrientation()) {
                    case Sprite.HORIZONTAL:
                        transform |= GLHandler._GL_TRANSFORM_FLIP_HORIZONTAL_BIT;
                        break;
                    case Sprite.VERTICAL:
                        transform |= GLHandler._GL_TRANSFORM_FLIP_VERTICAL_BIT;
                        break;
                    default:
                        break;
                }
            }
        }
        if ((anim.getLoadState() & RenderingSceneGL._GLLOADSTATE_Loaded) != 0) {
            if (DebugMap._getInstance().isDebugLevelEnabled(Sprite.DBUG_RENDER_LOW)) {
                System.out.println(">>>>>>>>>>>>>>>>GL render Animation " + anim.hashLinkToGLObject() + "...");
            }
            Sprite._GLRenderSprite(gld, anim.getCurrentSprite(), bounds, z, fx, fx_loc, fx_color, transform, scale, rotate, translate);
        }
    }

    /**
     * renders the Animation within an OpenGL context
     *
     * @param gld the RenderingSceneGL that is to be rendered onto
     * @param anim the Animation to render
     * @param bounds 2D Rectangle bounds to render at
     * @param z z-depth to render at
     * @param transform a Rendering transform bit ({@linkplain RenderingScene#_GL_TRANSFORM_ROTATE_BIT}, {@linkplain RenderingScene#_GL_TRANSFORM_SCALE_BIT}, {@linkplain RenderingScene#_GL_TRANSFORM_TRANSLATE_BIT})
     * transform bitwise-OR combination or 0 for no transform
     * @param scaleArgs double array with the scaling args (scaleX, scaleY)
     * @param rotateArgs double array with the rotation args (rotationDEGREES,
     * centerX, centerY)
     * @param translateArgs double array with the translation args (translateX,
     * translateY)
     * @param colorBlend float array with a 3- or 4-components (RGB(A)) color
     * for blending or null for none
     *
     */
    public static void _GLRenderAnimation(RenderingSceneGL gld, AnimationGLHandler anim, Rectangle bounds, double z, int transform, DoubleBuffer scaleArgs, DoubleBuffer rotateArgs, DoubleBuffer translateArgs, FloatBuffer colorBlend) {
        DoubleBuffer scale = scaleArgs;
        DoubleBuffer translate = translateArgs;
        if (anim.isTransformEnabled()) {
            if (scale == null) {
                scale = BufferIO._wrapd(new double[]{1, 1});
            }
            if (translate == null) {
                translate = BufferIO._wrapd(new double[]{0, 0});
            }
            if (anim.isZoomed()) {
                scale.put(0, scale.get(0) * anim.getZoomValue());
                scale.put(1, scale.get(1) * anim.getZoomValue());
                transform |= GLHandler._GL_TRANSFORM_SCALE_BIT;
            }
            if (anim.isMirrored()) {
                switch (anim.getMirrorOrientation()) {
                    case Sprite.HORIZONTAL:
                        transform |= GLHandler._GL_TRANSFORM_FLIP_HORIZONTAL_BIT;
                        break;
                    case Sprite.VERTICAL:
                        transform |= GLHandler._GL_TRANSFORM_FLIP_VERTICAL_BIT;
                        break;
                    default:
                        break;
                }
            }
        }
        if ((anim.getLoadState() & RenderingSceneGL._GLLOADSTATE_Loaded) != 0) {
            if (DebugMap._getInstance().isDebugLevelEnabled(Sprite.DBUG_RENDER_LOW)) {
                System.out.println(">>>>>>>>>>>>>>>>GL render Animation " + anim.hashLinkToGLObject() + "...");
            }
            Sprite._GLRenderSprite(gld, anim.getCurrentSprite(), bounds, z, 0, null, GLGeom.getColorFromComponentsArray(colorBlend), transform, scaleArgs, rotateArgs, translateArgs);
        }
    }
    public static GLObjectHandler<AnimationGLHandler> _GLHandlers = RenderingSceneGL._GLAnimations;

    @Override
    public void setMirrorEnabled(boolean b, int mirror) {
        renderableImpl.setFlipEnabled(b, mirror);
    }
    private Sprite renderableImpl;

    /**
     * Animation Sprite's behaviours-capabilities can be controlled with this
     * function.
     *
     * @return a single Sprite instance that is handling Sprite capabilities
     */
    public Sprite accessSpriteCaps() {
        return renderableImpl;
    }

    public boolean isDebugEnabled() {
        return renderableImpl.isDebugEnabled();
    }

    public void setDebugEnabled(boolean bln) {
        renderableImpl.setDebugEnabled(bln);
    }

    @Override
    public boolean isCompositeEnabled() {
        return isCompositeEnabled();
    }

    @Override
    public void setCompositeEnabled(boolean b) {
        renderableImpl.setCompositeEnabled(b);
    }

    @Override
    public Composite getComposite() {
        return renderableImpl.getComposite();
    }

    @Override
    public void setComposite(Composite cps) {
        renderableImpl.setComposite(cps);
    }

    @Override
    public Paint getPaint() {
        return renderableImpl.getPaint();
    }

    @Override
    public void setPaint(Paint pnt) {
        renderableImpl.setPaint(pnt);
    }

    @Override
    public Color getColor() {
        return renderableImpl.getColor();
    }

    @Override
    public void setColor(Color clr) {
        renderableImpl.setColor(clr);
    }

    @Override
    public boolean isOpaque() {
        return renderableImpl.isOpaque();
    }

    @Override
    public void setOpaque(boolean b) {
        renderableImpl.setOpaque(b);
    }

    @Override
    public RenderingScene getRenderingScene() {
        return renderableImpl.getRenderingScene();
    }

    @Override
    public void setRenderingScene(RenderingScene renderingScene) {
        renderableImpl.setRenderingScene(renderingScene);
    }

    /** does nothing */
    @Override
    public void setHardwareAccel(boolean b) {
       
    }

    @Override
    public boolean isHardwareAccel() {
        return renderableImpl.isHardwareAccel();
    }

    @Override
    public int getStoreMode() {
        return renderableImpl.getStoreMode();
    }

    @Override
    public boolean isJava2DModeEnabled() {
        return renderableImpl.isJava2DModeEnabled();
    }

    @Override
    public boolean isTextureModeEnabled() {
        return renderableImpl.isTextureModeEnabled();
    }

    @Override
    public boolean isTileModeEnabled() {
        return renderableImpl.isTileModeEnabled();
    }

    @Override
    public void setJava2DModeEnabled(boolean b) {
        renderableImpl.setJava2DModeEnabled(b);
    }

    @Override
    public void setStoreMode(int mode) {
        renderableImpl.setStoreMode(mode);
    }

    @Override
    public void setTextureModeEnabled(boolean b) {
        renderableImpl.setTextureModeEnabled(b);
    }

    @Override
    public void setTileModeEnabled(boolean b) {
        renderableImpl.setTileModeEnabled(b);
    }

    @Override
    public Rectangle getBounds() {
        return renderableImpl.getBounds();
    }

    @Override
    public Rectangle getBounds(Rectangle rv) {
        return renderableImpl.getBounds(rv);
    }

    @Override
    public Point getLocation() {
        return renderableImpl.getLocation();
    }

    @Override
    public Dimension getSize() {
        return renderableImpl.getSize();
    }

    @Override
    public void setBounds(Rectangle r) {
        renderableImpl.setBounds(r);
    }

    @Override
    public void setBounds(int x, int y, int width, int height) {
        renderableImpl.setBounds(x, y, width, height);
    }

    @Override
    public void setLocation(Point p) {
        renderableImpl.setLocation(p);
    }

    @Override
    public void setLocation(int x, int y) {
        renderableImpl.setLocation(x, y);
    }

    @Override
    public void setSize(Dimension size) {
        renderableImpl.setSize(size);
    }

    @Override
    public void setSize(int width, int height) {
        renderableImpl.setSize(width, height);
    }

    @Override
    public boolean isMultiThreadingEnabled() {
        return renderableImpl.isMultiThreadingEnabled();
    }

    @Override
    public void setMultiThreadingEnabled(boolean bln) {
        renderableImpl.setMultiThreadingEnabled(bln);
    }

    @Override
    public Monitor[] getGroupMonitor() {
        return renderableImpl.getGroupMonitor();
    }

    @Override
    public void setGroupMonitor(Monitor... mntrs) {
        renderableImpl.setGroupMonitor(mntrs);
    }

    @Override
    public void setZoomEnabled(boolean b, double zoom) {
        renderableImpl.setZoomEnabled(b, zoom);
    }

    /**
     * repaints the component with the Sprite data. used by Swing EDT when
     * called
     *
     * @see JComponent#repaint()
     * @see JComponent#update(Graphics)
     * @param g1 Graphics instance
     */
    public void paintComponentImpl(Graphics g1, JComponent comp) {
        renderableImpl.setJava2DModeEnabled(true);
        boolean myOpaque = renderableImpl.isOpaque();
        setOpaque(comp.isOpaque());
        Component myObs = renderableImpl.renderableImpl.io.obs;
        renderableImpl.setObs(comp);
        Rectangle myBounds = renderableImpl.getBounds();
        setBounds(comp.getBounds());
        Graphics2D g = renderableImpl.wrapRendering(g1);
        Shape clip = g.getClip();
        g.translate(-renderableImpl.renderableImpl.bounds.x, -renderableImpl.renderableImpl.bounds.y);
        g.clip(getBounds());
        if (isOpaque()) {
            Color c = getColor();
            g.setColor(g.getBackground());
            g.fill(getBounds());
            g.setColor(c);
        }
        runValidate();
        draw(renderableImpl.renderableImpl.io.obs, g);
        g.translate(renderableImpl.renderableImpl.bounds.x, renderableImpl.renderableImpl.bounds.y);
        g.setClip(clip);
    }

    @Override
    public int getWidth() {
        return renderableImpl.getWidth();
    }

    @Override
    public int getHeight() {
        return renderableImpl.getHeight();
    }
}
TOP

Related Classes of net.sf.jiga.xtended.impl.Animation

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.