mirror of
https://gitlab.com/AutumnMeowMeow/jexer
synced 2024-09-19 11:50:19 -06:00
Animate gifs in TImage
This commit is contained in:
parent
2484a4da2d
commit
dd557079e8
5 changed files with 603 additions and 16 deletions
|
@ -30,6 +30,7 @@ package jexer;
|
|||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import jexer.bits.Animation;
|
||||
import jexer.bits.Cell;
|
||||
import jexer.bits.ImageUtils;
|
||||
import jexer.event.TCommandEvent;
|
||||
|
@ -40,7 +41,7 @@ import static jexer.TCommand.*;
|
|||
import static jexer.TKeypress.*;
|
||||
|
||||
/**
|
||||
* TImage renders a piece of a bitmap image on screen.
|
||||
* TImage renders a piece of a bitmap image or an animated image on screen.
|
||||
*/
|
||||
public class TImage extends TWidget implements EditMenuUser {
|
||||
|
||||
|
@ -151,6 +152,11 @@ public class TImage extends TWidget implements EditMenuUser {
|
|||
*/
|
||||
private int lastTextHeight = -1;
|
||||
|
||||
/**
|
||||
* Animation to display.
|
||||
*/
|
||||
private Animation animation;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Constructors -----------------------------------------------------------
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -168,8 +174,8 @@ public class TImage extends TWidget implements EditMenuUser {
|
|||
* @param top top row of the image. 0 is the top-most row.
|
||||
*/
|
||||
public TImage(final TWidget parent, final int x, final int y,
|
||||
final int width, final int height,
|
||||
final BufferedImage image, final int left, final int top) {
|
||||
final int width, final int height, final BufferedImage image,
|
||||
final int left, final int top) {
|
||||
|
||||
this(parent, x, y, width, height, image, left, top, null);
|
||||
}
|
||||
|
@ -188,9 +194,8 @@ public class TImage extends TWidget implements EditMenuUser {
|
|||
* @param clickAction function to call when mouse is pressed
|
||||
*/
|
||||
public TImage(final TWidget parent, final int x, final int y,
|
||||
final int width, final int height,
|
||||
final BufferedImage image, final int left, final int top,
|
||||
final TAction clickAction) {
|
||||
final int width, final int height, final BufferedImage image,
|
||||
final int left, final int top, final TAction clickAction) {
|
||||
|
||||
// Set parent and window
|
||||
super(parent, x, y, width, height);
|
||||
|
@ -204,6 +209,56 @@ public class TImage extends TWidget implements EditMenuUser {
|
|||
sizeToImage(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
* @param parent parent widget
|
||||
* @param x column relative to parent
|
||||
* @param y row relative to parent
|
||||
* @param width number of text cells for width of the image
|
||||
* @param height number of text cells for height of the image
|
||||
* @param animation the animation to display
|
||||
* @param left left column of the image. 0 is the left-most column.
|
||||
* @param top top row of the image. 0 is the top-most row.
|
||||
*/
|
||||
public TImage(final TWidget parent, final int x, final int y,
|
||||
final int width, final int height, final Animation animation,
|
||||
final int left, final int top) {
|
||||
|
||||
this(parent, x, y, width, height, animation, left, top, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
* @param parent parent widget
|
||||
* @param x column relative to parent
|
||||
* @param y row relative to parent
|
||||
* @param width number of text cells for width of the image
|
||||
* @param height number of text cells for height of the image
|
||||
* @param animation the animation to display
|
||||
* @param left left column of the image. 0 is the left-most column.
|
||||
* @param top top row of the image. 0 is the top-most row.
|
||||
* @param clickAction function to call when mouse is pressed
|
||||
*/
|
||||
public TImage(final TWidget parent, final int x, final int y,
|
||||
final int width, final int height, final Animation animation,
|
||||
final int left, final int top, final TAction clickAction) {
|
||||
|
||||
// Set parent and window
|
||||
super(parent, x, y, width, height);
|
||||
|
||||
setCursorVisible(false);
|
||||
animation.start(getApplication());
|
||||
this.animation = animation;
|
||||
this.originalImage = animation.getFrame();
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
this.clickAction = clickAction;
|
||||
|
||||
sizeToImage(true);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Event handlers ---------------------------------------------------------
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -339,6 +394,16 @@ public class TImage extends TWidget implements EditMenuUser {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the animation on close.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
if (animation != null) {
|
||||
animation.stop();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// TWidget ----------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -348,7 +413,18 @@ public class TImage extends TWidget implements EditMenuUser {
|
|||
*/
|
||||
@Override
|
||||
public void draw() {
|
||||
sizeToImage(false);
|
||||
if (animation != null) {
|
||||
BufferedImage newFrame = animation.getFrame();
|
||||
if (newFrame != originalImage) {
|
||||
originalImage = newFrame;
|
||||
image = null;
|
||||
sizeToImage(true);
|
||||
} else {
|
||||
sizeToImage(false);
|
||||
}
|
||||
} else {
|
||||
sizeToImage(false);
|
||||
}
|
||||
|
||||
// We have already broken the image up, just draw the previously
|
||||
// created set of cells.
|
||||
|
@ -545,6 +621,27 @@ public class TImage extends TWidget implements EditMenuUser {
|
|||
this.originalImage = image;
|
||||
this.image = null;
|
||||
sizeToImage(true);
|
||||
if (animation != null) {
|
||||
animation.stop();
|
||||
animation = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the image space to an animation, and reprocess to make the visible
|
||||
* image.
|
||||
*
|
||||
* @param animation the new animation
|
||||
*/
|
||||
public void setAnimation(final Animation animation) {
|
||||
if (this.animation != null) {
|
||||
this.animation.stop();
|
||||
this.animation = null;
|
||||
}
|
||||
this.animation = animation;
|
||||
originalImage = animation.getFrame();
|
||||
image = null;
|
||||
sizeToImage(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,6 +34,8 @@ import java.io.IOException;
|
|||
import java.util.ResourceBundle;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import jexer.bits.Animation;
|
||||
import jexer.bits.ImageUtils;
|
||||
import jexer.event.TKeypressEvent;
|
||||
import jexer.event.TMouseEvent;
|
||||
import jexer.event.TResizeEvent;
|
||||
|
@ -102,10 +104,18 @@ public class TImageWindow extends TScrollableWindow {
|
|||
|
||||
super(parent, file.getName(), x, y, width, height, RESIZABLE);
|
||||
|
||||
BufferedImage image = ImageIO.read(file);
|
||||
BufferedImage image = null;
|
||||
Animation animation = null;
|
||||
if (file.getName().toLowerCase().endsWith(".gif")) {
|
||||
animation = ImageUtils.getAnimation(file);
|
||||
imageField = addImage(0, 0, getWidth() - 2, getHeight() - 2,
|
||||
animation, 0, 0);
|
||||
} else {
|
||||
image = ImageIO.read(file);
|
||||
imageField = addImage(0, 0, getWidth() - 2, getHeight() - 2,
|
||||
image, 0, 0);
|
||||
}
|
||||
|
||||
imageField = addImage(0, 0, getWidth() - 2, getHeight() - 2,
|
||||
image, 0, 0);
|
||||
setTitle(file.getName());
|
||||
|
||||
setupAfterImage();
|
||||
|
|
|
@ -34,6 +34,7 @@ import java.util.List;
|
|||
import java.util.ArrayList;
|
||||
|
||||
import jexer.backend.Screen;
|
||||
import jexer.bits.Animation;
|
||||
import jexer.bits.Cell;
|
||||
import jexer.bits.CellAttributes;
|
||||
import jexer.bits.Clipboard;
|
||||
|
@ -2919,7 +2920,6 @@ public abstract class TWidget implements Comparable<TWidget> {
|
|||
moveAction, singleClickAction);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convenience function to add an image to this container/window.
|
||||
*
|
||||
|
@ -2932,8 +2932,8 @@ public abstract class TWidget implements Comparable<TWidget> {
|
|||
* @param top top row of the image. 0 is the top-most row.
|
||||
*/
|
||||
public final TImage addImage(final int x, final int y,
|
||||
final int width, final int height,
|
||||
final BufferedImage image, final int left, final int top) {
|
||||
final int width, final int height, final BufferedImage image,
|
||||
final int left, final int top) {
|
||||
|
||||
return new TImage(this, x, y, width, height, image, left, top);
|
||||
}
|
||||
|
@ -2951,14 +2951,51 @@ public abstract class TWidget implements Comparable<TWidget> {
|
|||
* @param clickAction function to call when mouse is pressed
|
||||
*/
|
||||
public final TImage addImage(final int x, final int y,
|
||||
final int width, final int height,
|
||||
final BufferedImage image, final int left, final int top,
|
||||
final TAction clickAction) {
|
||||
final int width, final int height, final BufferedImage image,
|
||||
final int left, final int top, final TAction clickAction) {
|
||||
|
||||
return new TImage(this, x, y, width, height, image, left, top,
|
||||
clickAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to add an image to this container/window.
|
||||
*
|
||||
* @param x column relative to parent
|
||||
* @param y row relative to parent
|
||||
* @param width number of text cells for width of the image
|
||||
* @param height number of text cells for height of the image
|
||||
* @param animation the animation to display
|
||||
* @param left left column of the image. 0 is the left-most column.
|
||||
* @param top top row of the image. 0 is the top-most row.
|
||||
*/
|
||||
public final TImage addImage(final int x, final int y,
|
||||
final int width, final int height, final Animation animation,
|
||||
final int left, final int top) {
|
||||
|
||||
return new TImage(this, x, y, width, height, animation, left, top);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to add an image to this container/window.
|
||||
*
|
||||
* @param x column relative to parent
|
||||
* @param y row relative to parent
|
||||
* @param width number of text cells for width of the image
|
||||
* @param height number of text cells for height of the image
|
||||
* @param animation the animation to display
|
||||
* @param left left column of the image. 0 is the left-most column.
|
||||
* @param top top row of the image. 0 is the top-most row.
|
||||
* @param clickAction function to call when mouse is pressed
|
||||
*/
|
||||
public final TImage addImage(final int x, final int y,
|
||||
final int width, final int height, final Animation animation,
|
||||
final int left, final int top, final TAction clickAction) {
|
||||
|
||||
return new TImage(this, x, y, width, height, animation, left, top,
|
||||
clickAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to add an editable 2D data table to this
|
||||
* container/window.
|
||||
|
|
213
src/jexer/bits/Animation.java
Normal file
213
src/jexer/bits/Animation.java
Normal file
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Jexer - Java Text User Interface
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (C) 2021 Autumn Lamonte
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @author Autumn Lamonte [AutumnWalksTheLake@gmail.com] ⚧ Trans Liberation Now
|
||||
* @version 1
|
||||
*/
|
||||
package jexer.bits;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.List;
|
||||
|
||||
import jexer.TAction;
|
||||
import jexer.TApplication;
|
||||
import jexer.TTimer;
|
||||
|
||||
/**
|
||||
* An animation that can be displayed in an image.
|
||||
*/
|
||||
public class Animation {
|
||||
|
||||
/**
|
||||
* List of images for an animation.
|
||||
*/
|
||||
private List<BufferedImage> frames;
|
||||
|
||||
/**
|
||||
* The index of the frame that is currently visible.
|
||||
*/
|
||||
private int currentFrame = 0;
|
||||
|
||||
/**
|
||||
* Whether or not the current frame has been returned.
|
||||
*/
|
||||
private boolean gotFrame = false;
|
||||
|
||||
/**
|
||||
* Number of millis to wait until next animation frame.
|
||||
*/
|
||||
private int frameDelay = 0;
|
||||
|
||||
/**
|
||||
* Number of times to loop the animation. 1 means play it once. 0 means
|
||||
* to play it forever.
|
||||
*/
|
||||
private int frameLoops = 0;
|
||||
|
||||
/**
|
||||
* The number of loops executed so far.
|
||||
*/
|
||||
private int loopCount = 0;
|
||||
|
||||
/**
|
||||
* If true, the animation is running.
|
||||
*/
|
||||
private boolean running;
|
||||
|
||||
/**
|
||||
* The timer that is incrementing the current frame index.
|
||||
*/
|
||||
private TTimer timer;
|
||||
|
||||
/**
|
||||
* Public constructor.
|
||||
*
|
||||
* @param frames the frames
|
||||
* @param frameDelay the number of millis to wait until next animation
|
||||
* frame
|
||||
* @param frameLoops the number of times to loop the animation. 0 means
|
||||
* play it once. -1 means to play it forever.
|
||||
*/
|
||||
public Animation(final List<BufferedImage> frames, final int frameDelay,
|
||||
final int frameLoops) {
|
||||
|
||||
assert (frames != null);
|
||||
assert (frames.size() > 0);
|
||||
|
||||
this.frames = frames;
|
||||
this.frameDelay = frameDelay;
|
||||
this.frameLoops = frameLoops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this animation is running.
|
||||
*
|
||||
* @return true if the animation is running
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the animation.
|
||||
*
|
||||
* @param application the application
|
||||
*/
|
||||
public void start(final TApplication application) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
running = true;
|
||||
|
||||
assert (application != null);
|
||||
|
||||
/*
|
||||
System.err.printf("start() %d frames loops %d delay %d\n",
|
||||
frames.size(), frameLoops, frameDelay);
|
||||
*/
|
||||
|
||||
if (frames.size() > 1) {
|
||||
timer = application.addTimer(frameDelay, true,
|
||||
new TAction() {
|
||||
public void DO() {
|
||||
if (running) {
|
||||
if (gotFrame) {
|
||||
currentFrame++;
|
||||
gotFrame = false;
|
||||
}
|
||||
if (currentFrame >= frames.size()) {
|
||||
currentFrame = 0;
|
||||
}
|
||||
loopCount++;
|
||||
if ((frameLoops > 0) && (loopCount >= frameLoops)) {
|
||||
if (timer != null) {
|
||||
timer.setRecurring(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (timer != null) {
|
||||
timer.setRecurring(false);
|
||||
}
|
||||
}
|
||||
application.doRepaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the animation.
|
||||
*/
|
||||
public void stop() {
|
||||
running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the animation.
|
||||
*/
|
||||
public void reset() {
|
||||
loopCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of frames in this animation.
|
||||
*
|
||||
* @return the number of frames
|
||||
*/
|
||||
public int count() {
|
||||
return frames.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of the current frame in view.
|
||||
*
|
||||
* @return the frame number
|
||||
*/
|
||||
public int currentFrameNumber() {
|
||||
return currentFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a frame by number.
|
||||
*
|
||||
* @param frameNumber the frame number
|
||||
* @return the frame
|
||||
*/
|
||||
public BufferedImage getFrame(final int frameNumber) {
|
||||
gotFrame = true;
|
||||
return frames.get(frameNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the frame currently in view.
|
||||
*
|
||||
* @return the frame
|
||||
*/
|
||||
public BufferedImage getFrame() {
|
||||
gotFrame = true;
|
||||
return frames.get(currentFrame);
|
||||
}
|
||||
|
||||
}
|
|
@ -28,7 +28,22 @@
|
|||
*/
|
||||
package jexer.bits;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
/**
|
||||
* ImageUtils contains methods to:
|
||||
|
@ -36,6 +51,8 @@ import java.awt.image.BufferedImage;
|
|||
* - Check if an image is fully transparent.
|
||||
*
|
||||
* - Scale an image and preserve aspect ratio.
|
||||
*
|
||||
* - Open an animated image as an Animation.
|
||||
*/
|
||||
public class ImageUtils {
|
||||
|
||||
|
@ -178,4 +195,217 @@ public class ImageUtils {
|
|||
return newImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an image as an Animation.
|
||||
*
|
||||
* @param filename the name of the file that contains an animation
|
||||
* @return the animation, or null on error
|
||||
*/
|
||||
public static Animation getAnimation(final String filename) {
|
||||
return getAnimation(new File(filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an image as an Animation.
|
||||
*
|
||||
* @param file the file that contains an animation
|
||||
* @return the animation, or null on error
|
||||
*/
|
||||
public static Animation getAnimation(final File file) {
|
||||
try {
|
||||
List<BufferedImage> frames = new LinkedList<BufferedImage>();
|
||||
List<String> disposals = new LinkedList<String>();
|
||||
int delays = 0;
|
||||
|
||||
/*
|
||||
* Assume infinite loop. Finite-count looping in GIFs is an
|
||||
* Application Extension made popular by Netscape 2.0: see
|
||||
* http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html
|
||||
* .
|
||||
*
|
||||
* Unfortunately the Sun GIF decoder did not read and expose
|
||||
* this.
|
||||
*/
|
||||
int loopCount = 0;
|
||||
|
||||
ImageReader reader = null;
|
||||
ImageInputStream stream;
|
||||
stream = ImageIO.createImageInputStream(new FileInputStream(file));
|
||||
Iterator<ImageReader> iter = ImageIO.getImageReaders(stream);
|
||||
while (iter.hasNext()) {
|
||||
reader = iter.next();
|
||||
break;
|
||||
}
|
||||
if (reader == null) {
|
||||
return null;
|
||||
}
|
||||
reader.setInput(stream);
|
||||
|
||||
int width = -1;
|
||||
int height = -1;
|
||||
java.awt.Color backgroundColor = null;
|
||||
|
||||
IIOMetadata metadata = reader.getStreamMetadata();
|
||||
if (metadata != null) {
|
||||
IIOMetadataNode gblRoot;
|
||||
gblRoot = (IIOMetadataNode) metadata.getAsTree(metadata.
|
||||
getNativeMetadataFormatName());
|
||||
NodeList gblScreenDesc;
|
||||
gblScreenDesc = gblRoot.getElementsByTagName(
|
||||
"LogicalScreenDescriptor");
|
||||
if ((gblScreenDesc != null)
|
||||
&& (gblScreenDesc.getLength() > 0)
|
||||
) {
|
||||
IIOMetadataNode screenDescriptor;
|
||||
screenDescriptor = (IIOMetadataNode) gblScreenDesc.item(0);
|
||||
|
||||
if (screenDescriptor != null) {
|
||||
width = Integer.parseInt(screenDescriptor.
|
||||
getAttribute("logicalScreenWidth"));
|
||||
height = Integer.parseInt(screenDescriptor.
|
||||
getAttribute("logicalScreenHeight"));
|
||||
}
|
||||
}
|
||||
NodeList gblColorTable = gblRoot.getElementsByTagName(
|
||||
"GlobalColorTable");
|
||||
|
||||
if ((gblColorTable != null)
|
||||
&& (gblColorTable.getLength() > 0)
|
||||
) {
|
||||
IIOMetadataNode colorTable = (IIOMetadataNode) gblColorTable.item(0);
|
||||
|
||||
if (colorTable != null) {
|
||||
String bgIndex = colorTable.getAttribute(
|
||||
"backgroundColorIndex");
|
||||
|
||||
IIOMetadataNode color;
|
||||
color = (IIOMetadataNode) colorTable.getFirstChild();
|
||||
while (color != null) {
|
||||
if (color.getAttribute("index").equals(bgIndex)) {
|
||||
int red = Integer.parseInt(
|
||||
color.getAttribute("red"));
|
||||
int green = Integer.parseInt(
|
||||
color.getAttribute("green"));
|
||||
int blue = Integer.parseInt(
|
||||
color.getAttribute("blue"));
|
||||
backgroundColor = new java.awt.Color(red,
|
||||
green, blue);
|
||||
break;
|
||||
}
|
||||
|
||||
color = (IIOMetadataNode) color.getNextSibling();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
BufferedImage master = null;
|
||||
Graphics2D masterGraphics = null;
|
||||
int lastx = 0;
|
||||
int lasty = 0;
|
||||
boolean hasBackround = false;
|
||||
|
||||
for (int frameIndex = 0; ; frameIndex++) {
|
||||
BufferedImage image;
|
||||
try {
|
||||
image = reader.read(frameIndex);
|
||||
} catch (IndexOutOfBoundsException io) {
|
||||
break;
|
||||
}
|
||||
assert (image != null);
|
||||
|
||||
if (width == -1 || height == -1) {
|
||||
width = image.getWidth();
|
||||
height = image.getHeight();
|
||||
}
|
||||
IIOMetadataNode root;
|
||||
root = (IIOMetadataNode) reader.getImageMetadata(frameIndex).
|
||||
getAsTree("javax_imageio_gif_image_1.0");
|
||||
IIOMetadataNode gce;
|
||||
gce = (IIOMetadataNode) root.getElementsByTagName(
|
||||
"GraphicControlExtension").item(0);
|
||||
int delay = Integer.valueOf(gce.getAttribute("delayTime"));
|
||||
String disposal = gce.getAttribute("disposalMethod");
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
if (master == null) {
|
||||
master = new BufferedImage(width, height,
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
masterGraphics = master.createGraphics();
|
||||
masterGraphics.setBackground(new java.awt.Color(0, 0, 0, 0));
|
||||
if ((image.getWidth() == width)
|
||||
&& (image.getHeight() == height)
|
||||
) {
|
||||
hasBackround = true;
|
||||
}
|
||||
} else {
|
||||
NodeList children = root.getChildNodes();
|
||||
for (int nodeIndex = 0; nodeIndex < children.getLength();
|
||||
nodeIndex++) {
|
||||
|
||||
Node nodeItem = children.item(nodeIndex);
|
||||
if (nodeItem.getNodeName().equals("ImageDescriptor")) {
|
||||
NamedNodeMap map = nodeItem.getAttributes();
|
||||
x = Integer.valueOf(map.getNamedItem(
|
||||
"imageLeftPosition").getNodeValue());
|
||||
y = Integer.valueOf(map.getNamedItem(
|
||||
"imageTopPosition").getNodeValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
masterGraphics.drawImage(image, x, y, null);
|
||||
lastx = x;
|
||||
lasty = y;
|
||||
|
||||
BufferedImage copy = new BufferedImage(master.getColorModel(),
|
||||
master.copyData(null), master.isAlphaPremultiplied(), null);
|
||||
frames.add(copy);
|
||||
disposals.add(disposal);
|
||||
delays += delay;
|
||||
|
||||
if (disposal.equals("restoreToPrevious")) {
|
||||
BufferedImage from = null;
|
||||
for (int i = frameIndex - 1; i >= 0; i--) {
|
||||
if (!disposals.get(i).equals("restoreToPrevious")
|
||||
|| (frameIndex == 0)
|
||||
) {
|
||||
from = frames.get(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
master = new BufferedImage(from.getColorModel(),
|
||||
from.copyData(null), from.isAlphaPremultiplied(), null);
|
||||
masterGraphics = master.createGraphics();
|
||||
masterGraphics.setBackground(new java.awt.Color(0, 0, 0, 0));
|
||||
} else if (disposal.equals("restoreToBackgroundColor")
|
||||
&& (backgroundColor != null)) {
|
||||
|
||||
if (!hasBackround || (frameIndex > 1)) {
|
||||
master.createGraphics().fillRect(lastx, lasty,
|
||||
frames.get(frameIndex - 1).getWidth(),
|
||||
frames.get(frameIndex - 1).getHeight());
|
||||
}
|
||||
}
|
||||
}
|
||||
reader.dispose();
|
||||
|
||||
if (frames.size() == 1) {
|
||||
loopCount = 1;
|
||||
}
|
||||
if (frames.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
Animation animation = new Animation(frames,
|
||||
(delays * 10 / frames.size()), loopCount);
|
||||
return animation;
|
||||
|
||||
} catch (IOException e) {
|
||||
// SQUASH
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue