Java Gif Animation Not Repainting Correctly

Java GIF animation not repainting correctly

Okay, so after much mucking about, I was able to, finally, change the disposal method for the frame to restoreToBackgroundColor. Basically, what this means is that the animation is not an incremental change, but a complete frame replacement...

PicaGif

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class AnimatedGifTest1 {

public static void main(String[] args) {
new AnimatedGifTest1();
}

public AnimatedGifTest1() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}

JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PaintPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public class PaintPane extends JPanel {

private ImageIcon image;

public PaintPane() {
image = new ImageIcon(getClass().getResource("/ertcM02.gif"));
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}

@Override
public Dimension getPreferredSize() {
return image == null ? new Dimension(200, 200) : new Dimension(image.getIconWidth(), image.getIconHeight());
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // This is very important!
int x = (getWidth() - image.getIconWidth()) / 2;
int y = (getHeight() - image.getIconHeight()) / 2;
image.paintIcon(this, g, x, y);
}

}

}

Updated...

So, I was finally able to have a look at the disposal method for the gif you are using, which is set to restoreToPrevious, which, according to GRAPHICS INTERCHANGE FORMAT Version 89a means:

Restore to previous. The decoder is required to restore the area overwritten by the graphic with what was there prior to rendering the graphic.

Where as the image I've provided above uses restoreToBackgroundColor, which according to to GRAPHICS INTERCHANGE FORMAT Version 89a means:

Restore to background color. The area used by the graphic must be restored to the background color

You can check it yourself using the following code...

public static class AnimatedGif {

public enum DisposalMethod {

RESTORE_TO_BACKGROUND,
RESTORE_TO_PREVIOUS,
DO_NOT_DISPOSE,
UNSPECIFIED;

public static DisposalMethod find(String text) {

DisposalMethod dm = UNSPECIFIED;
System.out.println(text);

switch (text) {
case "restoreToBackgroundColor":
dm = RESTORE_TO_BACKGROUND;
break;
case "restoreToPrevious":
dm = RESTORE_TO_PREVIOUS;
break;
}

return dm;

}
}

private List<ImageFrame> frames;
private int frame;

public AnimatedGif(JComponent player, URL url) throws IOException {
frames = new ArrayList<>(25);
try (InputStream is = url.openStream(); ImageInputStream stream = ImageIO.createImageInputStream(is)) {
Iterator readers = ImageIO.getImageReaders(stream);
if (!readers.hasNext()) {
throw new RuntimeException("no image reader found");
}
ImageReader reader = (ImageReader) readers.next();
reader.setInput(stream); // don't omit this line!
int n = reader.getNumImages(true); // don't use false!
System.out.println("numImages = " + n);
for (int i = 0; i < n; i++) {
BufferedImage image = reader.read(i);
ImageFrame imageFrame = new ImageFrame(image);

IIOMetadata imd = reader.getImageMetadata(i);
Node tree = imd.getAsTree("javax_imageio_gif_image_1.0");
NodeList children = tree.getChildNodes();

for (int j = 0; j < children.getLength(); j++) {
Node nodeItem = children.item(j);
NamedNodeMap attr = nodeItem.getAttributes();
switch (nodeItem.getNodeName()) {
case "ImageDescriptor":
ImageDescriptor id = new ImageDescriptor(
getIntValue(attr.getNamedItem("imageLeftPosition")),
getIntValue(attr.getNamedItem("imageTopPosition")),
getIntValue(attr.getNamedItem("imageWidth")),
getIntValue(attr.getNamedItem("imageHeight")),
getBooleanValue(attr.getNamedItem("interlaceFlag")));
imageFrame.setImageDescriptor(id);
break;
case "GraphicControlExtension":
GraphicControlExtension gc = new GraphicControlExtension(
DisposalMethod.find(getNodeValue(attr.getNamedItem("disposalMethod"))),
getBooleanValue(attr.getNamedItem("userInputFlag")),
getBooleanValue(attr.getNamedItem("transparentColorFlag")),
getIntValue(attr.getNamedItem("delayTime")) * 10,
getIntValue(attr.getNamedItem("transparentColorIndex")));
imageFrame.setGraphicControlExtension(gc);
break;
}
}
frames.add(imageFrame);
}
} finally {
}
}

protected String getNodeValue(Node node) {
return node == null ? null : node.getNodeValue();
}

protected int getIntValue(Node node) {
return node == null ? 0 : getIntValue(node.getNodeValue());
}

protected boolean getBooleanValue(Node node) {
return node == null ? false : getBooleanValue(node.getNodeValue());
}

protected int getIntValue(String value) {
return value == null ? 0 : Integer.parseInt(value);
}

protected boolean getBooleanValue(String value) {
return value == null ? false : Boolean.parseBoolean(value);
}

public class ImageFrame {

private BufferedImage image;
private ImageDescriptor imageDescriptor;
private GraphicControlExtension graphicControlExtension;

public ImageFrame(BufferedImage image) {
this.image = image;
}

protected void setImageDescriptor(ImageDescriptor imageDescriptor) {
this.imageDescriptor = imageDescriptor;
}

protected void setGraphicControlExtension(GraphicControlExtension graphicControlExtension) {
this.graphicControlExtension = graphicControlExtension;
System.out.println(graphicControlExtension.getDisposalMethod());
}

public GraphicControlExtension getGraphicControlExtension() {
return graphicControlExtension;
}

public BufferedImage getImage() {
return image;
}

public ImageDescriptor getImageDescriptor() {
return imageDescriptor;
}

}

public class GraphicControlExtension {

private DisposalMethod disposalMethod;
private boolean userInputFlag;
private boolean transparentColorFlag;
private int delayTime;
private int transparentColorIndex;

public GraphicControlExtension(DisposalMethod disposalMethod, boolean userInputFlag, boolean transparentColorFlag, int delayTime, int transparentColorIndex) {
this.disposalMethod = disposalMethod;
this.userInputFlag = userInputFlag;
this.transparentColorFlag = transparentColorFlag;
this.delayTime = delayTime;
this.transparentColorIndex = transparentColorIndex;
}

public int getDelayTime() {
return delayTime;
}

public DisposalMethod getDisposalMethod() {
return disposalMethod;
}

public int getTransparentColorIndex() {
return transparentColorIndex;
}

public boolean isTransparentColorFlag() {
return transparentColorFlag;
}

public boolean isUserInputFlag() {
return userInputFlag;
}

}

public class ImageDescriptor {

private int imageLeftPosition;
private int imageTopPosition;
private int imageHeight;
private int imageWeight;
private boolean interlaced;

public ImageDescriptor(int imageLeftPosition, int imageTopPosition, int imageHeight, int imageWeight, boolean interlaced) {
this.imageLeftPosition = imageLeftPosition;
this.imageTopPosition = imageTopPosition;
this.imageHeight = imageHeight;
this.imageWeight = imageWeight;
this.interlaced = interlaced;
}

public int getImageHeight() {
return imageHeight;
}

public int getImageLeftPosition() {
return imageLeftPosition;
}

public int getImageTopPosition() {
return imageTopPosition;
}

public int getImageWeight() {
return imageWeight;
}

public boolean isInterlaced() {
return interlaced;
}

}

}

This code comes from .gif image doesn't moves on adding it to the JTabbed pane

Why gif animation doesn't animate when using it in paintComponent()?

The reason is that the standard Java ImageIO API only loads the first image of the gif.
How to fix? Google for a Gif Loader for Java, which loads every image of the gif. Then you have to paint the right image at the right time. An alternative way would be to have different png files representing each time one frame of the animation.

Update: Well... Actually, after doing some research, it looks like the way you did it actually loads all the frames of the animated gif. The reason for it is that the ImageIcon's method getImage() always returns the first image.

To fix it, you can try this (I'm not sure if it will work...)

Instead of using Grahpics.drawImage(), use ImageIcon.paintIcon(). Like this:

imageIcon.paintIcon(this, g2d, getWidth() / 2 - imageIcon.getIconWidth() / 2, getHeight() / 2);

Why is my gif animation in paintComponent not working?

Never mind, I had to make a duplicate of "explosion.gif". It works fine now!
However, please let me know if there is another way of doing this, much faster AND efficiently. Thanks!

Java problems with gif in label

I tried to put into a JPanel isn't showing up after clicking the button

When you add (or remove) components from a visible GUI the basic code is:

panel.add(...);
panel.revalidate();
panel.repaint();

The revalidate() is need to invoke the layout manager so the component is given a size.

is not animated.

Use a JLabel with an ImageIcon to display images. A JLabel will animated the gif.

When it does show up, it does not fit the JPanel and

You can try the Stretch Icon which is designed to fill the space available to the label.

Animated GIF, displayed on JPanel, not screenshoting in file with paint method, but JPG and PNG are ok

So, I put together a quick test, basically, I took a PNG image a animated gif image, I used ImageIO to load both and attempted to save the resulting panel and then I used ImageIcon to load the image and save it

My inputs...

PNG Image

PNG Image

GIF Image

Gif Image

The "code"

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

public static void main(String[] args) {
new Test();
}

public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}

BufferedImage png = ImageIO.read(...));
BufferedImage gif = ImageIO.read(...);
ImageIcon gifImg = new ImageIcon(...);

TestPane pngPane = new TestPane(png);
TestPane gifPane = new TestPane(gif);
TestPane imgPane = new TestPane(gifImg.getImage());

save(pngPane, "PNGTest.png");
save(gifPane, "gifTest.png");
save(imgPane, "imgGifTest.png");

} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}

public void save(JPanel pane, String to) throws IOException {
pane.setSize(pane.getPreferredSize());
BufferedImage img = new BufferedImage(pane.getWidth(), pane.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img.createGraphics();
pane.printAll(g2d);
g2d.dispose();

ImageIO.write(img, "png", new File(to));
}

public class TestPane extends JPanel {

private Image img;

public TestPane(Image img) {
this.img = img;
}

@Override
public Dimension getPreferredSize() {
return new Dimension(img.getWidth(this), img.getHeight(this));
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(img, 0, 0, this);
g2d.dispose();
}

}

}

The Results...

PNG Image via ImageIO

PNG Image via <code>ImageIO</code>

GIF Image via ImageIO

GIF Image via <code>ImageIO</code>

GIF Image via ImageIcon

GIF Image via <code>ImageIcon</code>

I don't want to get into the hows and whys of it all, needless to say, ImageIcon does use a background thread to load the image and relies on the ImageObserver support of the component to get it repainted as it's state changes, this is how it gets the animated GIF to update on the screen, so, one might surmise that we're just missing that update notification.

I've not tested it, but you could use a MediaTracker to monitor the ImageIcon's state and wait till it's loaded, but given the simplicity of using ImageIO I'd question whether it's worth it or not.

Cavert

You will need to use ImageIcon if you want the animated gif to play on a live component! So you can only use this "cheat" when generating the preview!

Java: Animated GIFs go automatically partially transparent

When ever you have these types of problems, you want to start playing around with the disposalMethod of the frames.

I ran your gif through some inspection code and found the disposalMethod to be set to RESTORE_TO_BACKGROUND

So, basically, I took your gif and ran it through the following code, which created a new gif with the disposalMethod of none

Example

So your original image is on top and the "fixed" image is on the bottom

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class MirrorImage {

public static void main(String[] args) {
new MirrorImage();
}

public MirrorImage() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public class TestPane extends JPanel {

private ImageIcon orig;
private ImageIcon mirror;

public TestPane() {
mirror(new File("Qzlxj.gif"), new File("Test.gif"));
orig = new ImageIcon("Qzlxj.gif");
mirror = new ImageIcon("Test.gif");
}

@Override
public Dimension getPreferredSize() {
return mirror == null ? new Dimension(200, 200) : new Dimension(orig.getIconWidth(), orig.getIconHeight() * 2);
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (orig != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - orig.getIconWidth()) / 2;
int y = (getHeight() - (orig.getIconHeight() * 2)) / 2;
g2d.drawImage(orig.getImage(), x, y, this);

// AffineTransform at = new AffineTransform();
// at.setToScale(1, -1);
// at.translate(0, -mirror.getIconHeight());
// g2d.setTransform(at);
g2d.drawImage(mirror.getImage(), x, y + mirror.getIconHeight(), this);
g2d.dispose();
}
}
}

public static void mirror(File source, File dest) {

List<BufferedImage> images = new ArrayList<>(25);
List<Integer> delays = new ArrayList<>(25);
int delay = 0;

ImageOutputStream output = null;
GifSequenceWriter writer = null;

try {

String[] imageatt = new String[]{
"imageLeftPosition",
"imageTopPosition",
"imageWidth",
"imageHeight"
};

ImageReader reader = (ImageReader) ImageIO.getImageReadersByFormatName("gif").next();
ImageInputStream ciis = ImageIO.createImageInputStream(source);
reader.setInput(ciis, false);
int noi = reader.getNumImages(true);
BufferedImage master = null;

for (int i = 0; i < noi; i++) {

BufferedImage image = reader.read(i);
IIOMetadata metadata = reader.getImageMetadata(i);

Node tree = metadata.getAsTree("javax_imageio_gif_image_1.0");
NodeList children = tree.getChildNodes();
for (int j = 0; j < children.getLength(); j++) {
Node nodeItem = children.item(j);
System.out.println(nodeItem.getNodeName());
if (nodeItem.getNodeName().equals("ImageDescriptor")) {
Map<String, Integer> imageAttr = new HashMap<String, Integer>();
NamedNodeMap attr = nodeItem.getAttributes();
// for (int index = 0; index < attr.getLength(); index++) {
// Node node = attr.item(index);
// System.out.println("----> " + node.getNodeName() + "=" + node.getNodeValue());
// }
for (int k = 0; k < imageatt.length; k++) {
Node attnode = attr.getNamedItem(imageatt[k]);
imageAttr.put(imageatt[k], Integer.valueOf(attnode.getNodeValue()));
}

if (master == null) {
master = new BufferedImage(imageAttr.get("imageWidth"), imageAttr.get("imageHeight"), BufferedImage.TYPE_INT_ARGB);
}

Graphics2D g2d = master.createGraphics();
g2d.drawImage(image, imageAttr.get("imageLeftPosition"), imageAttr.get("imageTopPosition"), null);
g2d.dispose();

// BufferedImage frame = mirror(copyImage(master));
BufferedImage frame = copyImage(master);
ImageIO.write(frame, "png", new File("img" + i + ".png"));
images.add(frame);

} else if (nodeItem.getNodeName().equals("GraphicControlExtension")) {
NamedNodeMap attr = nodeItem.getAttributes();
Node delayNode = attr.getNamedItem("delayTime");
if (delayNode != null) {
delay = Math.max(delay, Integer.valueOf(delayNode.getNodeValue()));
delays.add(delay);
}
}
}

}

output = new FileImageOutputStream(dest);
writer = new GifSequenceWriter(output, images.get(0).getType(), delay * 10, true);

for (int i = 0; i < images.size(); i++) {
BufferedImage nextImage = images.get(i);
writer.writeToSequence(nextImage);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
writer.close();
} catch (Exception e) {
}
try {
output.close();
} catch (Exception e) {
}
}
}

public static BufferedImage mirror(BufferedImage img) {

BufferedImage mirror = createCompatibleImage(img);
Graphics2D g2d = mirror.createGraphics();
AffineTransform at = new AffineTransform();
at.setToScale(1, -1);
at.translate(0, -img.getHeight());
g2d.setTransform(at);
g2d.drawImage(img, 0, 0, null);
g2d.dispose();

return mirror;

}

public static BufferedImage copyImage(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();

BufferedImage newImage = createCompatibleImage(img);
Graphics graphics = newImage.createGraphics();

int x = (width - img.getWidth()) / 2;
int y = (height - img.getHeight()) / 2;

graphics.drawImage(img, x, y, img.getWidth(), img.getHeight(), null);
graphics.dispose();

return newImage;
}

public static BufferedImage createCompatibleImage(BufferedImage image) {
return getGraphicsConfiguration().createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency());
}

public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}

public static class GifSequenceWriter {

protected ImageWriter gifWriter;
protected ImageWriteParam imageWriteParam;
protected IIOMetadata imageMetaData;

/**
* Creates a new GifSequenceWriter
*
* @param outputStream the ImageOutputStream to be written to
* @param imageType one of the imageTypes specified in BufferedImage
* @param timeBetweenFramesMS the time between frames in miliseconds
* @param loopContinuously wether the gif should loop repeatedly
* @throws IIOException if no gif ImageWriters are found
*
* @author Elliot Kroo (elliot[at]kroo[dot]net)
*/
public GifSequenceWriter(
ImageOutputStream outputStream,
int imageType,
int timeBetweenFramesMS,
boolean loopContinuously) throws IIOException, IOException {
// my method to create a writer
gifWriter = getWriter();
imageWriteParam = gifWriter.getDefaultWriteParam();
ImageTypeSpecifier imageTypeSpecifier
= ImageTypeSpecifier.createFromBufferedImageType(imageType);

imageMetaData
= gifWriter.getDefaultImageMetadata(imageTypeSpecifier,
imageWriteParam);

String metaFormatName = imageMetaData.getNativeMetadataFormatName();

IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName);

IIOMetadataNode graphicsControlExtensionNode = getNode(
root,
"GraphicControlExtension");

//restoreToBackgroundColor
//restoreToPrevious
graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
graphicsControlExtensionNode.setAttribute(
"transparentColorFlag",
"FALSE");
graphicsControlExtensionNode.setAttribute(
"delayTime",
Integer.toString(timeBetweenFramesMS / 10));
graphicsControlExtensionNode.setAttribute(
"transparentColorIndex",
"0");

IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
commentsNode.setAttribute("CommentExtension", "Created by MAH");

IIOMetadataNode appEntensionsNode = getNode(
root,
"ApplicationExtensions");

IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");

child.setAttribute("applicationID", "NETSCAPE");
child.setAttribute("authenticationCode", "2.0");

int loop = loopContinuously ? 0 : 1;

child.setUserObject(new byte[]{0x1, (byte) (loop & 0xFF), (byte) ((loop >> 8) & 0xFF)});
appEntensionsNode.appendChild(child);

imageMetaData.setFromTree(metaFormatName, root);

gifWriter.setOutput(outputStream);

gifWriter.prepareWriteSequence(null);
}


Related Topics



Leave a reply



Submit