StaticAnimation.java
上传用户:haobig99
上传日期:2022-06-15
资源大小:369k
文件大小:19k
源码类别:

J2ME

开发平台:

Java

  1. /*
  2.  * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
  3.  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4.  *
  5.  * This code is free software; you can redistribute it and/or modify it
  6.  * under the terms of the GNU General Public License version 2 only, as
  7.  * published by the Free Software Foundation.  Sun designates this
  8.  * particular file as subject to the "Classpath" exception as provided
  9.  * by Sun in the LICENSE file that accompanied this code.
  10.  *
  11.  * This code is distributed in the hope that it will be useful, but WITHOUT
  12.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14.  * version 2 for more details (a copy is included in the LICENSE file that
  15.  * accompanied this code).
  16.  *
  17.  * You should have received a copy of the GNU General Public License version
  18.  * 2 along with this work; if not, write to the Free Software Foundation,
  19.  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20.  *
  21.  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22.  * CA 95054 USA or visit www.sun.com if you need additional information or
  23.  * have any questions.
  24.  */
  25. package com.sun.lwuit;
  26. import com.sun.lwuit.animations.Animation;
  27. import com.sun.lwuit.geom.Dimension;
  28. import com.sun.lwuit.geom.Rectangle;
  29. import java.io.DataInputStream;
  30. import java.io.IOException;
  31. import java.util.Vector;
  32. /**
  33.  * An animation with pre-existing 
  34.  *
  35.  * @deprecated this class shouldn't be referenced directly, use the Image base class
  36.  * for all functionality
  37.  * @author Shai Almog
  38.  */
  39. public class StaticAnimation extends IndexedImage implements Animation {
  40.     private Frame[] frames;
  41.     
  42.     /**
  43.      * The current frame doesn't point directly at the frames array since the first frame
  44.      *  is the image itself
  45.      */
  46.     private int currentFrame;
  47.     
  48.     private long animationStartTime;
  49.     private int totalAnimationTime;
  50.     
  51.     private boolean loop;
  52.     
  53.     /**
  54.      * Invoked with the first frame value
  55.      */
  56.     StaticAnimation(int width, int height, int[] palette, byte[] data) {
  57.         super(width, height, palette, data);
  58.     }
  59.     /**
  60.      * Returns the number of frames in the animation including the initial frame
  61.      * 
  62.      * @return number of frames in the animation
  63.      */
  64.     public int getFrameCount() {
  65.         // +1 for this image
  66.         return frames.length + 1;
  67.     }
  68.     
  69.     /**
  70.      * The time in which the given frame should appear
  71.      * 
  72.      * @param frame must be a number bigger than -1 and smaller than getFrameCount()
  73.      * @return the time in milliseconds for the frame to appear
  74.      */
  75.     public int getFrameTime(int frame) {
  76.         if(frame == 0) {
  77.             return 0;
  78.         }
  79.         return frames[frame - 1].time;
  80.     }
  81.     /**
  82.      * Returns the duration for the entire animation used to determine when to loop
  83.      * @return total animation time in milliseconds
  84.      */
  85.     public int getTotalAnimationTime() {
  86.         return totalAnimationTime;
  87.     }
  88.     
  89.     /**
  90.      * Returns the RGB for the given frame, this method is relatively slow and 
  91.      * it is recommended that you do not use it for realtime animations.
  92.      * 
  93.      * @param frame must be a number bigger than -1 and smaller than getFrameCount()
  94.      * @return ARGB pixels within the given frame
  95.      */
  96.     public int[] getFrameRGB(int frame) {
  97.         if(frame == 0) {
  98.             return getRGBCached();
  99.         }
  100.         Frame f = frames[frame - 1];
  101.         int height = getHeight();
  102.         int width = getWidth();
  103.         int[] array = new int[width * height];
  104.         byte[] imageDataByte = f.getKeyFrame();
  105.         int[] palette = getPalette();
  106.         
  107.         for(int line = 0 ; line < height ; line++) {
  108.             byte[] lineArray = f.getModifiedRow(line);
  109.             if(lineArray != null) {
  110.                 for(int position = 0 ; position < width ; position++) {
  111.                     int i = lineArray[position] & 0xff;                
  112.                     array[position + (width * line)] = palette[i];
  113.                 }
  114.             } else {
  115.                 int currentPos = line * width;
  116.                 for(int position = 0 ; position < width ; position++) {
  117.                     int i = imageDataByte[position + currentPos] & 0xff;                
  118.                     array[position + (width * line)] = palette[i];
  119.                 }
  120.             }
  121.         }
  122.         return array;
  123.     }
  124.     
  125.     /**
  126.      * Creates an animation from the given stream, this method is used internally
  127.      * by Resources normally you should not create static animations manually.
  128.      * 
  129.      * @param data input stream from which the animation is loaded
  130.      * @return An instance of a static animation
  131.      * @throws IOException when the stream throws it
  132.      */
  133.     public static StaticAnimation createAnimation(DataInputStream data) throws IOException {
  134.         // read the length of the palette;
  135.         byte pSize = data.readByte();
  136.         int[] palette = new int[pSize & 0xff];
  137.         for(int iter = 0 ; iter < palette.length ; iter++) {
  138.             palette[iter] = data.readInt();
  139.         }
  140.         int width = data.readShort();
  141.         int height = data.readShort();
  142.         int numberOfFrames = data.readByte() & 0xff;
  143.         int totalAnimationTime = Math.max(1, data.readInt());
  144.         boolean loop = data.readBoolean();
  145.         byte[] array = new byte[width * height];
  146.         data.readFully(array);
  147.         
  148.         // create the first frame of the animation
  149.         StaticAnimation animation = new StaticAnimation(width, height, palette, array);
  150.         animation.frames = new Frame[numberOfFrames -1];
  151.         animation.totalAnimationTime = totalAnimationTime;
  152.         animation.loop = loop;
  153.         int currentTime = 0;
  154.         byte[] lastKeyframe = array;
  155.         Vector rowNumbers = new Vector();
  156.         Vector rowValues = new Vector();
  157.         
  158.         // read the rest of the frames in the animation
  159.         for(int iter = 1 ; iter < numberOfFrames ; iter++) {
  160.             currentTime += data.readInt();
  161.             
  162.             // if this is a keyframe then just read the data else read the specific
  163.             // modified row offsets
  164.             if(data.readBoolean()) {
  165.                 array = new byte[width * height];
  166.                 data.readFully(array);
  167.                 animation.frames[iter - 1] = new Frame(currentTime, array);
  168.                 lastKeyframe = array;
  169.                 rowValues.removeAllElements();
  170.                 rowNumbers.removeAllElements();
  171.             } else {
  172.                 boolean drawPrevious = data.readBoolean();
  173.                 int nextRow = data.readShort();
  174.                 while(nextRow != -1) {
  175.                     byte[] rowData = new byte[width];
  176.                     data.readFully(rowData);
  177.                     // if this row was already modified in a previous frame we can remove
  178.                     // the old row
  179.                     Integer rowInteger = new Integer(nextRow);
  180.                     int index = rowNumbers.indexOf(rowInteger);
  181.                     if(index > -1) {
  182.                         rowNumbers.removeElementAt(index);
  183.                         rowValues.removeElementAt(index);
  184.                     }
  185.                     
  186.                     rowNumbers.addElement(rowInteger);
  187.                     rowValues.addElement(rowData);
  188.                     nextRow = data.readShort();
  189.                 }
  190.                 animation.frames[iter - 1] = new Frame(currentTime, lastKeyframe, rowNumbers, rowValues, drawPrevious);
  191.             }
  192.         }
  193.         
  194.         return animation;
  195.     }
  196.     
  197.     /**
  198.      * @inheritDoc
  199.      */
  200.     public boolean animate() {
  201.         if(animationStartTime == 0) {
  202.             animationStartTime = System.currentTimeMillis();
  203.             return false;
  204.         }
  205.         long currentTime = System.currentTimeMillis();
  206.         int position = (int)(currentTime - animationStartTime);
  207.         if(loop) {
  208.             position %= totalAnimationTime;
  209.         } else {
  210.             if(position > totalAnimationTime) {
  211.                 return false;
  212.             }
  213.         }
  214.         
  215.         // special case for last frame
  216.         if(currentFrame == frames.length) {
  217.             if(position >= totalAnimationTime || position < frames[frames.length - 1].getTime()) {
  218.                 currentFrame = 0;
  219.                 return true;
  220.             }
  221.         } else {
  222.             if(position >= frames[currentFrame].getTime()) {
  223.                 currentFrame++;
  224.                 if(currentFrame > frames.length + 1) {
  225.                     currentFrame = 0;
  226.                 }
  227.                 return true;
  228.             }
  229.         }
  230.         
  231.         return false;
  232.     }
  233.     
  234.     /**
  235.      * Restarts the animation
  236.      */
  237.     public void restart() {
  238.         animationStartTime = System.currentTimeMillis();
  239.     }
  240.     /**
  241.      * @inheritDoc
  242.      */
  243.     public void paint(Graphics g) {
  244.         drawImage(g, g.getGraphics(), 0, 0);
  245.     }
  246.     /**
  247.      * Indicates whether the animation will run in a loop or run only once
  248.      * 
  249.      * @return true for a loop animation
  250.      */
  251.     public boolean isLoop() {
  252.         return loop;
  253.     }
  254.     /**
  255.      * Indicates whether the animation will run in a loop or run only once
  256.      * 
  257.      * @param loop true for looping the animation
  258.      */
  259.     public void setLoop(boolean loop) {
  260.         this.loop = loop;
  261.     }
  262.     
  263.     /**
  264.      * @inheritDoc
  265.      */
  266.     protected void drawImage(Graphics g, Object nativeGraphics, int x, int y) {
  267.         if(currentFrame == 0) {
  268.             super.drawImage(g, nativeGraphics, x, y);
  269.             return;
  270.         }
  271.         int width = getWidth();
  272.         
  273.         if(lineCache == null || lineCache.length < width) {
  274.             lineCache = new int[width];
  275.         }
  276.         
  277.         // for performance we can calculate the visible drawing area so we don't have to
  278.         // calculate the whole array
  279.         int clipY = g.getClipY();
  280.         int clipBottomY = g.getClipHeight() + clipY;
  281.         int firstLine = 0;
  282.         int lastLine = getHeight();
  283.         if(clipY > y) {
  284.             firstLine = clipY - y;
  285.         } 
  286.         if(clipBottomY < y + getHeight()) {
  287.             lastLine = clipBottomY - y;
  288.         }
  289.         
  290.         Frame f = frames[currentFrame - 1];
  291.         byte[] imageDataByte = f.getKeyFrame();
  292.         int[] palette = getPalette();
  293.         
  294.         // we draw based on the last keyframe and if a row was modified then we
  295.         // will draw that row rather than the keyframe
  296.         for(int line = firstLine ; line < lastLine ; line += 1) {
  297.             byte[] lineArray = f.getModifiedRow(line);
  298.             if(lineArray != null) {
  299.                 for(int position = 0 ; position < width ; position++) {
  300.                     int i = lineArray[position] & 0xff;                
  301.                     lineCache[position] = palette[i];
  302.                 }
  303.             } else {
  304.                 //if(!f.isDrawPrevious()) {
  305.                     int currentPos = line * width;
  306.                     for(int position = 0 ; position < width ; position++) {
  307.                         int i = imageDataByte[position + currentPos] & 0xff;                
  308.                         lineCache[position] = palette[i];
  309.                     }
  310.                 //}
  311.             }
  312.             g.drawRGB(lineCache, 0, x, y + line, width, 1, true);
  313.         }
  314.     }    
  315.     
  316.     /**
  317.      * @inheritDoc
  318.      */
  319.     public void scale(int width, int height) {
  320.         StaticAnimation s = (StaticAnimation)scaled(width, height);
  321.         super.scale(width, height);
  322.         frames = s.frames;
  323.     }
  324.     
  325.     /**
  326.      * @inheritDoc
  327.      */
  328.     public Image scaled(int width, int height) {
  329.         int srcWidth = getWidth();
  330.         int srcHeight = getHeight();
  331.         // no need to scale
  332.         if(srcWidth == width && srcHeight == height){
  333.             return this;
  334.         }
  335.         // save the previous keyframe all the time this allows us to swap a keyframe
  336.         // based on its pointer rather than scale it twice
  337.         byte[] lastKeyFrame = getImageDataByte();
  338.         // scale the first frame
  339.         StaticAnimation result = new StaticAnimation(width, height, getPalette(), scaleArray(lastKeyFrame, width, height));
  340.         result.loop = loop;
  341.         result.totalAnimationTime = totalAnimationTime;
  342.         result.animationStartTime = animationStartTime;
  343.         result.currentFrame = currentFrame;
  344.         result.frames = new Frame[frames.length];
  345.         
  346.         byte[] lastKeyFrameAfterScale = result.getImageDataByte();
  347.         
  348.         int yRatio = (srcHeight << 16) / height;
  349.         int xRatio = (srcWidth << 16) / width;
  350.         // now we need to traverse all the frames and scale them one by one
  351.         for(int iter = 0 ; iter < frames.length ; iter++) {
  352.             byte[] currentKeyFrame = frames[iter].getKeyFrame();
  353.             if(currentKeyFrame != lastKeyFrame) {
  354.                 lastKeyFrame = currentKeyFrame;
  355.                 currentKeyFrame = scaleArray(currentKeyFrame, width, height);
  356.                 lastKeyFrameAfterScale = currentKeyFrame;
  357.             }
  358.             result.frames[iter] = new Frame(frames[iter].getTime(), lastKeyFrameAfterScale, frames[iter], xRatio, yRatio, width, height);
  359.         }
  360.         return result;
  361.     }
  362.     
  363.     /**
  364.      * @inheritDoc
  365.      */
  366.     public boolean isAnimation() {
  367.         return true;
  368.     }
  369.     
  370.     /**
  371.      * Used by the resource editor
  372.      */
  373.     boolean isKeyframe(int offset) {
  374.         if(offset == 0) {
  375.             return true;
  376.         }
  377.         if(offset == 1) {
  378.             return frames[offset - 1].getKeyFrame() != imageDataByte;
  379.         }
  380.         return frames[offset - 1].getKeyFrame() != frames[offset - 2].getKeyFrame();
  381.     }
  382.     
  383.     /**
  384.      * Used by the resource editor
  385.      */
  386.     byte[] getKeyframe(int offset) {
  387.         return frames[offset - 1].getKeyFrame();
  388.     }
  389.     /**
  390.      * Used by the resource editor
  391.      */
  392.     boolean isDrawPrevious(int offset) {
  393.         return frames[offset - 1].isDrawPrevious();
  394.     }
  395.     
  396.     /**
  397.      * Used by the resource editor
  398.      */
  399.     byte[][] getModifiedRows(int offset) {
  400.         return frames[offset - 1].modifiedRows;
  401.     }
  402.     
  403.     /**
  404.      * Used by the resource editor
  405.      */
  406.     int[] getModifiedRowOffsets(int offset) {
  407.         return frames[offset - 1].modifiedRowOffsets;
  408.     }
  409.     
  410.     Rectangle getDirtyRegion(){
  411.         int frame = currentFrame;
  412.         if(frame == 0){
  413.             return null;
  414.         }
  415.         if(frame == frames.length){
  416.             frame = 0;
  417.         }
  418.         Rectangle rect = new Rectangle(0, frames[frame].smallestChangedRow, new Dimension(getWidth(), frames[frame].highestChangedRow -frames[frame].smallestChangedRow));
  419.         return rect;
  420.     }
  421.     
  422.     /**
  423.      * Represents a frame within the animation that is not the first frame
  424.      */
  425.     static class Frame {
  426.         /**
  427.          * Offset since the beginning of the animation
  428.          */
  429.         private int time;
  430.         
  431.         private byte[] keyFrame;
  432.         
  433.         /**
  434.          * Relevant only for standard frames, this represents the rows that
  435.          * were modified for this specific frame
  436.          */
  437.         byte[][] modifiedRows;
  438.         int[] modifiedRowOffsets;
  439.         private boolean drawPrevious;
  440.         int smallestChangedRow;
  441.         int highestChangedRow;
  442.         
  443.         
  444.         public Frame(int time, byte[] keyFrame, Vector rowNumbers, Vector rowValues, boolean drawPrevious) {
  445.             this.time = time;
  446.             this.keyFrame = keyFrame;
  447.             this.drawPrevious = drawPrevious;
  448.             initArrays(rowNumbers, rowValues);
  449.         }
  450.         private void initArrays(Vector rowNumbers, Vector rowValues) {
  451.             modifiedRowOffsets = new int[rowNumbers.size()];
  452.             modifiedRows = new byte[modifiedRowOffsets.length][];
  453.             for(int iter = 0 ; iter < modifiedRowOffsets.length ; iter++) {
  454.                 modifiedRowOffsets[iter] = ((Integer)rowNumbers.elementAt(iter)).intValue();
  455.                 modifiedRows[iter] = (byte[])rowValues.elementAt(iter);
  456.             }
  457.             smallestChangedRow = modifiedRowOffsets[0];
  458.             highestChangedRow = modifiedRowOffsets[0];
  459.             for(int i=1;i<modifiedRowOffsets.length;i++){
  460.                 smallestChangedRow = Math.min(smallestChangedRow, modifiedRowOffsets[i]) ;
  461.                 highestChangedRow = Math.max(highestChangedRow, modifiedRowOffsets[i]) ;
  462.             }
  463.         }
  464.         
  465.         public Frame(int time, byte[] keyFrame) {
  466.             this.time = time;
  467.             this.keyFrame = keyFrame;
  468.             modifiedRowOffsets = new int[0];
  469.         }
  470.         
  471.         /**
  472.          * This constructor is used for scaling the original frame according to the given ratios
  473.          */
  474.         public Frame(int time, byte[] keyFrame, Frame original, int xRatio, int yRatio, int width, int height) {
  475.             this.time = time;
  476.             this.keyFrame = keyFrame;
  477.             
  478.             // if the original was a keyframe then no work...
  479.             if(original.modifiedRowOffsets.length == 0) {
  480.                 modifiedRowOffsets = original.modifiedRowOffsets;
  481.                 return;
  482.             }
  483.             Vector newRows = new Vector();
  484.             Vector newValues = new Vector();
  485.             
  486.             int xPos = xRatio / 2;
  487.             int yPos = yRatio / 2;
  488.             for(int y = 0 ; y < height ; y++) {
  489.                 int srcY = yPos >> 16;
  490.                 
  491.                 // do we have the row at srcY???
  492.                 int rowAtY = -1;
  493.                 for(int iter = 0 ; iter < original.modifiedRowOffsets.length ; iter++) {
  494.                     if(original.modifiedRowOffsets[iter] == srcY) {
  495.                         rowAtY = iter;
  496.                         break;
  497.                     }
  498.                 }
  499.                 if(rowAtY != -1) {
  500.                     byte[] newRow = new byte[width];
  501.                     for (int x = 0; x < width; x++) {
  502.                         int srcX = xPos >> 16;
  503.                         newRow[x] = original.modifiedRows[rowAtY][srcX];
  504.                         xPos += xRatio;
  505.                     }
  506.                     newRows.addElement(new Integer(y));
  507.                     newValues.addElement(newRow);
  508.                 }
  509.                 yPos += yRatio;
  510.                 xPos = xRatio / 2;
  511.             }
  512.             
  513.             initArrays(newRows, newValues);
  514.         }
  515.         
  516.         public boolean isDrawPrevious() {
  517.             return drawPrevious;
  518.         }
  519.         
  520.         public int getTime() {
  521.             return time;
  522.         }
  523.         
  524.         private byte[] getModifiedRow(int row) {
  525.             for(int iter = 0 ; iter < modifiedRowOffsets.length ; iter++) {
  526.                 if(modifiedRowOffsets[iter] == row) {
  527.                     return modifiedRows[iter];
  528.                 }
  529.             }
  530.             return null;
  531.         }
  532.         
  533.         private byte[] getKeyFrame() {
  534.             return keyFrame;
  535.         }
  536.         
  537.         private void setKeyFrame(byte[] keyFrame) {
  538.             this.keyFrame = keyFrame;
  539.         }
  540.     }
  541. }