All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.image.Animation Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2012 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/
package com.day.image;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageOutputStream;

import com.day.imageio.plugins.GIFImageMetadata;
import com.day.imageio.plugins.GIFStreamMetadata;

/**
 * The Animation class takes a series of Layers and
 * puts them in a single output graphic stream adding animation code for the
 * number of loops and the delay time and disposal method of the single patches.
 *
 * 

* What we do in this class is accept a series of layers, which will make up * the single images. Currently layers are inserted into the animation in the * succession of their addition. For each layer added, you may define the * display time in 1/100th seconds and the disposal method used at the end of * the display time. * *

* In addition you may set global looping instruction, on how many times the * sequence of images should be displayed. To disable setting the loop control * value in the output file, set the global loop value to a negative value. * *

* Properties *

* The Animation class supports the following properties, which * are all backed by setter and getter methods : *

*

*
loops *   * The number of loops to go through the animation. The default is not set * byte the constructor or the setter is 0, which boils down to an endless * loop. * *
defaultDisposal *   * The action the image decoder should take after the display time of an * image has elapsed. This must be one of the defined DISPOSAL_* constants. * The default if not set by a constructor or the setter is * DISPOSAL_BACKGROUND. * *
backgroundColor *   * The background color of the animation. The default if not set by a * constructor or the setter is to take the background color of the first * image in the animation as the background color. * *
* *

* Supported Image Format *

* Currently the Animation class is solely implemented based on the * GIF image file format, whose general structure is described here. You will * also note, that the API of the Animation class is based on the * functionality the GIF format offers for animated image files. * *

* CompuServe Graphics Interchange Format (GIF) *

* The CompuServe Graphics Interchange Format (GIF) is a multi-image graphics * format, which provides the capability for simple animations provided the * image viewer is capable of interpreting the multi-image nature of the image * file and the instructions for display. * *

* Generally a GIF file is made up of the following structural elements : *

    *
  • GIF89a Header *
  • Logical Screen Descriptor Block, with optional global color table *
  • optional Netscape Application Extension Block *
  • stream of graphics *
      *
    • optional Graphic Control Block *
    • Image Descriptor or Plain Text Block with optional local color table *
    *
  • GIF Trailer *
* *

* For more details, look at the * GIF89 Specification. * Generally this page * contains many interesting information on animated GIFs. * * @version $Revision$, $Date$ * @author fmeschbe * @since coati * @audience wad */ public class Animation { /** * No disposal specifed. The decoder is not required to take any action. */ public static int DISPOSAL_NONE = 0; /** * Do not dispose. The graphic is to be left in place. */ public static int DISPOSAL_NO = 1; /** * Restore to background color. The area used by the graphic must be * restored to the background color. */ public static int DISPOSAL_BACKGROUND = 2; /** * Restore to previous. The decoder is required to restore the area * overwritten by the graphic with what was there prior to rendering * the graphic. */ public static int DISPOSAL_PREVIOUS = 4; //---------- fields -------------------------------------------------------- /** * The list of {@link Patch}es in the animation */ private List layers; /** * Number of loops for the animation.
* Special values : *

*
negative number
*
Do not add a loop information block
* *
0
*
infinite loop
* *
positive number
*
Iterate this number of times through the images
*
*/ private int loops; /** * Default disposal method used, if none is specifed * to addLayer or addLayers */ private int defaultDisposal; /** * The desired background color for the animated GIF. If this is undefined, * that is -1, the background color of the first layer in the animation is * taken. */ private int bgColor; /** * Initializes the {@link ImageSupport} class to (1) register the Gif * image writer with the ImageIO but also to load the correct JRE release * dependent classes. */ static { ImageSupport.initialize(); } /** * Creates the Animation object using the given properties. * * @param loops Number of times the application should loop through the * images. Use a negative value to prevent looping at all. * @param defaultDisposal default disposal method for images after the * delay time has elapsed * @param bgColor The background color to set on the animated GIF. Only the * red, green and blue values are used, the alpha value is ignored * as GIF does not support alpha values. */ public Animation(int loops, int defaultDisposal, int bgColor) { this.layers = new ArrayList(); this.loops = loops; this.defaultDisposal = defaultDisposal; this.bgColor = bgColor & 0xffffff; } /** * Creates the Animation object using the given properties. The * background color is taken from the first image in the animation while the * default disposal method is set to DISPOSAL_BACKGROUND. * * @param loops Number of times the application should loop through the * images. Use a negative value to prevent looping at all. */ public Animation(int loops) { this(loops, DISPOSAL_BACKGROUND, -1); } /** * Creates the Animation object using the default values. The * background color is taken from the first image in the animation while the * default disposal method is set to DISPOSAL_BACKGROUND. The * loop counter is initialized to 0, which means endless loop. */ public Animation() { this(0, DISPOSAL_BACKGROUND, -1); } /** * Change the number of iterations through the images to the new value. * * @param loops Number of times the application should loop through the * images. Use a negative value to prevent looping at all or set * to 0 for endless looping. */ public void setLoops(int loops) { this.loops = loops; } /** * Returns the number of iterations through the images currently set. * * @return Number of iterations through the images. */ public int getLoops() { return loops; } /** * Sets the default disposal method for newly added Layers. * This setting does not affect Layers already added to * the animation object ! * * @param defaultDisposal The disposal method to use. This must be one of * DISPOSAL_* constants defined above. * * @throws IllegalArgumentException if the disposal value is illegal. * * @see #DISPOSAL_NONE * @see #DISPOSAL_NO * @see #DISPOSAL_BACKGROUND * @see #DISPOSAL_PREVIOUS */ public void setDefaultDisposal(int defaultDisposal) { this.defaultDisposal = defaultDisposal; } /** * Return the current default disposal method used for newly added * Layers. * * @return current default disposal method used */ public int getDefaultDisposal() { return defaultDisposal; } /** * Sets the desired background color. If the desired color is -1, the * background color of the first layer in the animation is taken as the * animations background color. * * @param bgColor The new background color to set. Only the * red, green and blue values are used, the alpha value is ignored * as GIF does not support alpha values. */ public void setBackgroundColor(int bgColor) { this.bgColor = bgColor & 0xffffff; } /** * Returns the current desired background color, -1 if undefined. * @return the current desired background color, -1 if undefined. */ public int getBackgroundColor() { return bgColor; } /** * Add a layer for display during the indicated delay time after which the * image is disposed of using the default disposal method. * * @param layer the Layer object to add * @param delay the display time of the image in 1/100 sec. * * @throws NullPointerException if the layer is null. */ public void addLayer(Layer layer, int delay) { addLayer(layer, delay, defaultDisposal); } /** * Add a layer for display during the indicated delay time after which the * image is disposed of using the disposal method. * * @param layer the Layer object to add * @param delay the display time of the image in 1/100 sec. * @param disposal the disposal method for the object. This must be one * of the predefined DISPOSAL_* constants. * * @throws NullPointerException if the layer is null. * @throws IllegalArgumentException if the disposal value is not one * of the predefined DISPOSAL_* constants. * * @see #DISPOSAL_NONE * @see #DISPOSAL_NO * @see #DISPOSAL_BACKGROUND * @see #DISPOSAL_PREVIOUS */ public void addLayer(Layer layer, int delay, int disposal) { layers.add(new Patch(layer, delay, disposal)); } /** * Remove the layer with the given index from the Animation. * * @param idx index of the Layer in the list * * @return the layer removed from the Animation * * @throws IndexOutOfBoundsException if the index is less than zero or * bigger than the number of layers in the animation. * @throws NullPointerException if no layer is present at the given * position. */ public Layer removeLayer(int idx) { Patch p = (Patch)layers.remove(idx); return (p != null) ? p.getLayer() : null; } /** * Removes the first layer (in chronological order) matching the given * layer from the animation. If a layer is contained more than once * within the animation, you must use multiple calls to * removeLayer. * * @param layer Layer object to remove from the animation * * @return the layer removed from the animation or null if * the layer was not part of the animation. */ public Layer removeLayer(Layer layer) { Iterator iter = layers.iterator(); while (iter.hasNext()) { Patch patch = (Patch)iter.next(); if (patch.getLayer() == layer) { layers.remove(patch); return layer; } } // if we come here, no match was found return null; } /** * Write the animation to the output indicated.
* * @param mimeType MIME type indicating the graphics output file format * to use. Currently only image/gif is * supported. * @param numColors The number of colors to generate in the animated GIF, if * not in the range [2 .. 256], 256 is taken as the * default. * @param outs the OutputStream used for output * * @throws IOException got from called write methods * @throws IllegalArgumentException or if the mimeType is * not image/gif or if the * OutputStream is null. */ public void write(String mimeType, int numColors, OutputStream outs) throws IOException { // Check the mimeType if ((mimeType == null) || (mimeType.toLowerCase().indexOf("gif") < 0)) { throw new IllegalArgumentException("image/gif support only"); } // Check the output stream if (outs == null) { throw new NullPointerException("outs"); } // Check the numColors if (numColors < 2 || numColors > 256) { numColors = 256; } // try to get an image writer from the ImageIO API for the MIME type Iterator writers = ImageIO.getImageWritersByMIMEType(mimeType); ImageWriter writer = (ImageWriter)writers.next(); // Attach the outStream to the ImageIO ImageOutputStream ios = ImageIO.createImageOutputStream(outs); writer.setOutput(ios); // The stream meta data - max size and background color IIOMetadata streamMetadata = getStreamMetaData(writer); IIOMetadata imageMetadata = null; Iterator iter = layers.iterator(); while (iter.hasNext()) { Patch patch = (Patch)iter.next(); Layer layer = patch.getLayer(); IIOMetadata gifMeta[] = ImageSupport.createGIFMetadata(layer, writer, numColors); if (gifMeta[1] != null) { imageMetadata = gifMeta[1]; } else { imageMetadata = writer.getDefaultImageMetadata(null, null); } // define the animation stuff.. GIFImageMetadata gifIM = (GIFImageMetadata)imageMetadata; gifIM.delayTime = patch.getDelay(); gifIM.disposalMethod = patch.getDisposal(); // write the image IIOImage image = new IIOImage(layer.getImage(),null,imageMetadata); writer.write(streamMetadata, image, null); } // Release resources writer.dispose(); ios.close(); // ensure stream is written completely outs.flush(); } //---------- Object overwrites --------------------------------------------- /** * Convert the Animation to some string representation for intelligent * display. * @return the string representation of the Animation object. */ public String toString() { return "Animation : loops=" + loops + ", defaultDisposal=" + defaultDisposal + ", patches:" + layers.size(); } //---------- internal ------------------------------------------------------ /** * Calculate the bound of all the layers in the animation to get * the screen size of the logical screen. * * @return a Rect object indicating the rectangle within * which all layers can be placed. */ private IIOMetadata getStreamMetaData (ImageWriter writer) { int height = 0; int width = 0; long bgcolor = bgColor; Iterator iter = layers.iterator(); while (iter.hasNext()) { Patch patch = (Patch)iter.next(); Layer l = patch.getLayer(); if (width < l.getWidth()) width = l.getWidth(); if (height < l.getHeight()) height = l.getHeight(); if (bgcolor == -1) bgcolor = l.getBackgroundColor().getRGB(); } GIFStreamMetadata streamMetadata = (GIFStreamMetadata) writer.getDefaultStreamMetadata(null); streamMetadata.logicalScreenHeight = height; streamMetadata.logicalScreenWidth = width; streamMetadata.backgroundColorIndex = (int)bgcolor; if (loops >= 0) { streamMetadata.setLoops(loops); } return streamMetadata; } //----------- internal class ----------------------------------------------- /** * The Patch class encapsulates a patch representing one * single image added to the animation. The class is simply a container * for the data. There is no application logic contained in the class. * * @version $Revision$, $Date$ * @author fmeschbe * @since coati */ private static class Patch { /** * The delay time before disposing of this patch's image. The time * is measured in 1/100th of seconds, i.e. 1 second is 100 */ private final int delay; /** * Disposal method for the patch's image. The method is one of the * constants defined in the {@link Animation} class. */ private final int disposal; /** * Image layer for this patch. */ private final Layer layer; /** * Create a patch with the layer given, to be displayed for the * delay time given and disposed using the indicated method. * * @param layer the layer for this patch * @param delay the deley in 1/100th of seconds * @param disposal the disposal method used. * * @throws NullPointerException if the layer is null. * @throws IllegalArgumentException if the disposal value is not one * of the predefined DISPOSAL_* constants. */ public Patch(Layer layer, int delay, int disposal) { this.layer = layer; this.delay = delay; this.disposal = disposal; } /** * Returns the delay time of the image in 1/100th seconds. * * @return the delay time */ public int getDelay() { return delay; } /** * Returns the disposal method value, which presumably is one of the * predefined constants. * * @return the disposal method of the image */ public int getDisposal() { return disposal; } /** * Returns the layer associated with the patch. The layer constitutes * the image to be shown for the patch. * * @return the Layer of the patch */ public Layer getLayer() { return layer; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy