Package com.sun.dtv.lwuit

Source Code of com.sun.dtv.lwuit.StaticAnimation$Frame

/*
* Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.  Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.dtv.lwuit;

import com.sun.dtv.lwuit.animations.Animation;
import com.sun.dtv.lwuit.geom.Dimension;
import com.sun.dtv.lwuit.geom.Rectangle;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Vector;


/**
* An animation with pre-existing
*
* @author Shai Almog
*/
public class StaticAnimation extends IndexedImage implements Animation {
    private Frame[] frames;
   
    /**
     * The current frame doesn't point directly at the frames array since the first frame
     *  is the image itself
     */
    private int currentFrame;
   
    private long animationStartTime;
    private int totalAnimationTime;
   
    private boolean loop;
   
    /**
     * Invoked with the first frame value
     */
    StaticAnimation(int width, int height, int[] palette, byte[] data) {
        super(width, height, palette, data);
    }

    /**
     * Returns the number of frames in the animation including the initial frame
     */
    public int getFrameCount() {
        // +1 for this image
        return frames.length + 1;
    }
   
    /**
     * The time in which the given frame should appear
     *
     * @param frame must be a number bigger than -1 and smaller than getFrameCount()
     * @return the time in milliseconds for the frame to appear
     */
    public int getFrameTime(int frame) {
        if(frame == 0) {
            return 0;
        }
        return frames[frame - 1].time;
    }

    /**
     * Returns the duration for the entire animation used to determin when to loop
     * @return total animation time in milliseconds
     */
    public int getTotalAnimationTime() {
        return totalAnimationTime;
    }
   
    /**
     * Returns the RGB for the given frame, this method is relatively slow and
     * it is recommended that you do not use it for realtime animations.
     *
     * @param frame must be a number bigger than -1 and smaller than getFrameCount()
     * @return ARGB pixels within the given frame
     */
    public int[] getFrameRGB(int frame) {
        if(frame == 0) {
            return getRGB();
        }
        Frame f = frames[frame - 1];
        int height = getHeight();
        int width = getWidth();
        int[] array = new int[width * height];
        byte[] imageDataByte = f.getKeyFrame();
        int[] palette = getPalette();
       
        for(int line = 0 ; line < height ; line++) {
            byte[] lineArray = f.getModifiedRow(line);
            if(lineArray != null) {
                for(int position = 0 ; position < width ; position++) {
                    int i = lineArray[position] & 0xff;               
                    array[position + (width * line)] = palette[i];
                }
            } else {
                int currentPos = line * width;
                for(int position = 0 ; position < width ; position++) {
                    int i = imageDataByte[position + currentPos] & 0xff;               
                    array[position + (width * line)] = palette[i];
                }
            }
        }
        return array;
    }
   
    /**
     * Creates an animation from the given stream, this method is used internally
     * by Resources normally you should not create static animations manually.
     *
     * @param data input stream from which the animation is loaded
     * @return An instance of a static animation
     */
    public static StaticAnimation createAnimation(DataInputStream data) throws IOException {
        // read the length of the palette;
        byte pSize = data.readByte();
        int[] palette = new int[pSize & 0xff];
        for(int iter = 0 ; iter < palette.length ; iter++) {
            palette[iter] = data.readInt();
        }
        int width = data.readShort();
        int height = data.readShort();
        int numberOfFrames = data.readByte() & 0xff;
        int totalAnimationTime = Math.max(1, data.readInt());
        boolean loop = data.readBoolean();
        byte[] array = new byte[width * height];
        data.readFully(array);
       
        // create the first frame of the animation
        StaticAnimation animation = new StaticAnimation(width, height, palette, array);
        animation.frames = new Frame[numberOfFrames -1];
        animation.totalAnimationTime = totalAnimationTime;
        animation.loop = loop;
        int currentTime = 0;
        byte[] lastKeyframe = array;
        Vector rowNumbers = new Vector();
        Vector rowValues = new Vector();
       
        // read the rest of the frames in the animation
        for(int iter = 1 ; iter < numberOfFrames ; iter++) {
            currentTime += data.readInt();
           
            // if this is a keyframe then just read the data else read the specific
            // modified row offsets
            if(data.readBoolean()) {
                array = new byte[width * height];
                data.readFully(array);
                animation.frames[iter - 1] = new Frame(currentTime, array);
                lastKeyframe = array;
                rowValues.removeAllElements();
                rowNumbers.removeAllElements();
            } else {
                boolean drawPrevious = data.readBoolean();
                int nextRow = data.readShort();

                while(nextRow != -1) {
                    byte[] rowData = new byte[width];
                    data.readFully(rowData);

                    // if this row was already modified in a previous frame we can remove
                    // the old row
                    Integer rowInteger = new Integer(nextRow);
                    int index = rowNumbers.indexOf(rowInteger);
                    if(index > -1) {
                        rowNumbers.removeElementAt(index);
                        rowValues.removeElementAt(index);
                    }
                   
                    rowNumbers.addElement(rowInteger);
                    rowValues.addElement(rowData);
                    nextRow = data.readShort();
                }
                animation.frames[iter - 1] = new Frame(currentTime, lastKeyframe, rowNumbers, rowValues, drawPrevious);
            }
        }
       
        return animation;
    }
   
    /**
     * @inheritDoc
     */
    public boolean animate() {
        if(animationStartTime == 0) {
            animationStartTime = System.currentTimeMillis();
            return false;
        }
        long currentTime = System.currentTimeMillis();
        int position = (int)(currentTime - animationStartTime);
        if(loop) {
            position %= totalAnimationTime;
        } else {
            if(position > totalAnimationTime) {
                return false;
            }
        }
       
        // special case for last frame
        if(currentFrame == frames.length) {
            if(position >= totalAnimationTime || position < frames[frames.length - 1].getTime()) {
                currentFrame = 0;
                return true;
            }
        } else {
            if(position >= frames[currentFrame].getTime()) {
                currentFrame++;
                if(currentFrame > frames.length + 1) {
                    currentFrame = 0;
                }
                return true;
            }
        }
       
        return false;
    }
   
    /**
     * Restarts the animation
     */
    public void restart() {
        animationStartTime = System.currentTimeMillis();
    }

    /**
     * @inheritDoc
     */
    public void paint(Graphics g) {
        drawImage(g, 0, 0);
    }

    /**
     * Indicates whether the animation will run in a loop or run only once
     */
    public boolean isLoop() {
        return loop;
    }

    /**
     * Indicates whether the animation will run in a loop or run only once
     */
    public void setLoop(boolean loop) {
        this.loop = loop;
    }
   
    /**
     * Overriden to allow animation frames
     */
    void drawImage(Graphics g, int x, int y) {
        if(currentFrame == 0) {
            super.drawImage(g, x, y);
            return;
        }
        int width = getWidth();
       
        if(lineCache == null || lineCache.length < width) {
            lineCache = new int[width];
        }
       
        // for performance we can calculate the visible drawing area so we don't have to
        // calculate the whole array
        int clipY = g.getClipY();
        int clipBottomY = g.getClipHeight() + clipY;
        int firstLine = 0;
        int lastLine = getHeight();
        if(clipY > y) {
            firstLine = clipY - y;
        }
        if(clipBottomY < y + getHeight()) {
            lastLine = clipBottomY - y;
        }
       
        Frame f = frames[currentFrame - 1];
        byte[] imageDataByte = f.getKeyFrame();
        int[] palette = getPalette();
       
        // we draw based on the last keyframe and if a row was modified then we
        // will draw that row rather than the keyframe
        for(int line = firstLine ; line < lastLine ; line += 1) {
            byte[] lineArray = f.getModifiedRow(line);
            if(lineArray != null) {
                for(int position = 0 ; position < width ; position++) {
                    int i = lineArray[position] & 0xff;               
                    lineCache[position] = palette[i];
                }
            } else {
                //if(!f.isDrawPrevious()) {
                    int currentPos = line * width;
                    for(int position = 0 ; position < width ; position++) {
                        int i = imageDataByte[position + currentPos] & 0xff;               
                        lineCache[position] = palette[i];
                    }
                //}
            }
            g.drawRGB(lineCache, 0, width, x, y + line, width, 1, true);
        }
    }   
   
    /**
     * @inheritDoc
     */
    public void scale(int width, int height) {
        StaticAnimation s = (StaticAnimation)scaled(width, height);
        super.scale(width, height);
        frames = s.frames;
    }
   
    /**
     * @inheritDoc
     */
    public Image scaled(int width, int height) {
        int srcWidth = getWidth();
        int srcHeight = getHeight();

        // no need to scale
        if(srcWidth == width && srcHeight == height){
            return this;
        }

        // save the previous keyframe all the time this allows us to swap a keyframe
        // based on its pointer rather than scale it twice
        byte[] lastKeyFrame = getImageDataByte();

        // scale the first frame
        StaticAnimation result = new StaticAnimation(width, height, getPalette(), scaleArray(lastKeyFrame, width, height));
        result.loop = loop;
        result.totalAnimationTime = totalAnimationTime;
        result.animationStartTime = animationStartTime;
        result.currentFrame = currentFrame;
        result.frames = new Frame[frames.length];
       
        byte[] lastKeyFrameAfterScale = result.getImageDataByte();
       
        int yRatio = (srcHeight << 16) / height;
        int xRatio = (srcWidth << 16) / width;

        // now we need to traverse all the frames and scale them one by one
        for(int iter = 0 ; iter < frames.length ; iter++) {
            byte[] currentKeyFrame = frames[iter].getKeyFrame();
            if(currentKeyFrame != lastKeyFrame) {
                lastKeyFrame = currentKeyFrame;
                currentKeyFrame = scaleArray(currentKeyFrame, width, height);
                lastKeyFrameAfterScale = currentKeyFrame;
            }
            result.frames[iter] = new Frame(frames[iter].getTime(), lastKeyFrameAfterScale, frames[iter], xRatio, yRatio, width, height);
        }
        return result;
    }
   
    /**
     * @inheritDoc
     */
    public boolean isAnimation() {
        return true;
    }
   
    /**
     * Used by the resource editor
     */
    boolean isKeyframe(int offset) {
        if(offset == 0) {
            return true;
        }
        if(offset == 1) {
            return frames[offset - 1].getKeyFrame() != imageDataByte;
        }
        return frames[offset - 1].getKeyFrame() != frames[offset - 2].getKeyFrame();
    }
   
    /**
     * Used by the resource editor
     */
    byte[] getKeyframe(int offset) {
        return frames[offset - 1].getKeyFrame();
    }

    /**
     * Used by the resource editor
     */
    boolean isDrawPrevious(int offset) {
        return frames[offset - 1].isDrawPrevious();
    }
   
    /**
     * Used by the resource editor
     */
    byte[][] getModifiedRows(int offset) {
        return frames[offset - 1].modifiedRows;
    }
   
    /**
     * Used by the resource editor
     */
    int[] getModifiedRowOffsets(int offset) {
        return frames[offset - 1].modifiedRowOffsets;
    }
   
    Rectangle getDirtyRegion(){
        int frame = currentFrame;
        if(frame == 0){
            return null;
        }
        if(frame == frames.length){
            frame = 0;
        }
        int [] modified = frames[frame].modifiedRowOffsets;
        Rectangle rect = new Rectangle(0, frames[frame].smallestChangedRow, new Dimension(getWidth(), frames[frame].highestChangedRow -frames[frame].smallestChangedRow));
        return rect;
    }
   
    /**
     * Represents a frame within the animation that is not the first frame
     */
    static class Frame {
        /**
         * Offset since the beginning of the animation
         */
        private int time;
       
        private byte[] keyFrame;
       
        /**
         * Relevant only for standard frames, this represents the rows that
         * were modified for this specific frame
         */
        byte[][] modifiedRows;
        int[] modifiedRowOffsets;
        private boolean drawPrevious;
        int smallestChangedRow;
        int highestChangedRow;
       
       
        public Frame(int time, byte[] keyFrame, Vector rowNumbers, Vector rowValues, boolean drawPrevious) {
            this.time = time;
            this.keyFrame = keyFrame;
            this.drawPrevious = drawPrevious;
            initArrays(rowNumbers, rowValues);
        }

        private void initArrays(Vector rowNumbers, Vector rowValues) {
            modifiedRowOffsets = new int[rowNumbers.size()];
            modifiedRows = new byte[modifiedRowOffsets.length][];
            for(int iter = 0 ; iter < modifiedRowOffsets.length ; iter++) {
                modifiedRowOffsets[iter] = ((Integer)rowNumbers.elementAt(iter)).intValue();
                modifiedRows[iter] = (byte[])rowValues.elementAt(iter);
            }
            smallestChangedRow = modifiedRowOffsets[0];
            highestChangedRow = modifiedRowOffsets[0];
            for(int i=1;i<modifiedRowOffsets.length;i++){
                smallestChangedRow = Math.min(smallestChangedRow, modifiedRowOffsets[i]) ;
                highestChangedRow = Math.max(highestChangedRow, modifiedRowOffsets[i]) ;
            }
        }
       
        public Frame(int time, byte[] keyFrame) {
            this.time = time;
            this.keyFrame = keyFrame;
            modifiedRowOffsets = new int[0];
        }
       

        /**
         * This constructor is used for scaling the original frame according to the given ratios
         */
        public Frame(int time, byte[] keyFrame, Frame original, int xRatio, int yRatio, int width, int height) {
            this.time = time;
            this.keyFrame = keyFrame;
           
            // if the original was a keyframe then no work...
            if(original.modifiedRowOffsets.length == 0) {
                modifiedRowOffsets = original.modifiedRowOffsets;
                return;
            }
            Vector newRows = new Vector();
            Vector newValues = new Vector();
           
            int xPos = xRatio / 2;
            int yPos = yRatio / 2;

            for(int y = 0 ; y < height ; y++) {
                int srcY = yPos >> 16;
               
                // do we have the row at srcY???
                int rowAtY = -1;
                for(int iter = 0 ; iter < original.modifiedRowOffsets.length ; iter++) {
                    if(original.modifiedRowOffsets[iter] == srcY) {
                        rowAtY = iter;
                        break;
                    }
                }
                if(rowAtY != -1) {
                    byte[] newRow = new byte[width];
                    for (int x = 0; x < width; x++) {
                        int srcX = xPos >> 16;
                        newRow[x] = original.modifiedRows[rowAtY][srcX];
                        xPos += xRatio;
                    }
                    newRows.addElement(new Integer(y));
                    newValues.addElement(newRow);
                }
                yPos += yRatio;
                xPos = xRatio / 2;
            }
           
            initArrays(newRows, newValues);
        }
       
        public boolean isDrawPrevious() {
            return drawPrevious;
        }
       
        public int getTime() {
            return time;
        }
       
        private byte[] getModifiedRow(int row) {
            for(int iter = 0 ; iter < modifiedRowOffsets.length ; iter++) {
                if(modifiedRowOffsets[iter] == row) {
                    return modifiedRows[iter];
                }
            }
            return null;
        }
       
        private byte[] getKeyFrame() {
            return keyFrame;
        }
       
        private void setKeyFrame(byte[] keyFrame) {
            this.keyFrame = keyFrame;
        }
    }
}
TOP

Related Classes of com.sun.dtv.lwuit.StaticAnimation$Frame

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.