I'm making a Java app that displays certain GIF files from a folder. I'm currently using the code
final JLabel imageLabel = new JLabel();
imageLabel.setIcon(new ImageIcon(fileName));
contentPane.add(imageLabel, java.awt.BorderLayout.CENTER);
This works flawlessly, except that many (thousands) of my .GIF files have a misconfigured frame rate which makes them display at infinite speed (frameDelay=0), assuming that the browser will fix this automatically. Java does not do this by default. How can I override the frameDelay Java has to use for those animated gifs with frameDelay=0?
解决方案
I've found this, and it works well for one gif I tried.
I have no idea what he's exactly doing, but at a glance it looks like if the first frame has a delay of 0 it overwrites the delay for all frames with 10. Then he 'writes' a new GIF file in memory and loads that to image.
[edit] I polished it up a bit and ironed out the bugs.
No proprietary API
Doesn't just check the 1st frame to determine if it's bugged,
Replaces delay only for frames where it's zero.
public static Image readImgFromFile(String filename, Component parent) {
File file = new File(filename);
if (!file.exists()) {
return null;
}
// Fix for bug when delay is 0
try {
// Load anything but GIF the normal way
if (!filename.substring(filename.length() - 4).equalsIgnoreCase(".gif")) {
return ImageIO.read(file);
}
// Get GIF reader
ImageReader reader = ImageIO.getImageReadersByFormatName("gif").next();
// Give it the stream to decode from
reader.setInput(ImageIO.createImageInputStream(file));
int numImages = reader.getNumImages(true);
// Get 'metaFormatName'. Need first frame for that.
IIOMetadata imageMetaData = reader.getImageMetadata(0);
String metaFormatName = imageMetaData.getNativeMetadataFormatName();
// Find out if GIF is bugged
boolean foundBug = false;
for (int i = 0; i < numImages && !foundBug; i++) {
// Get metadata
IIOMetadataNode root = (IIOMetadataNode)reader.getImageMetadata(i).getAsTree(metaFormatName);
// Find GraphicControlExtension node
int nNodes = root.getLength();
for (int j = 0; j < nNodes; j++) {
Node node = root.item(j);
if (node.getNodeName().equalsIgnoreCase("GraphicControlExtension")) {
// Get delay value
String delay = ((IIOMetadataNode)node).getAttribute("delayTime");
// Check if delay is bugged
if (Integer.parseInt(delay) == 0) {
foundBug = true;
}
break;
}
}
}
// Load non-bugged GIF the normal way
Image image;
if (!foundBug) {
image = Toolkit.getDefaultToolkit().createImage(filename);
} else {
// Prepare streams for image encoding
ByteArrayOutputStream baoStream = new ByteArrayOutputStream();
try (ImageOutputStream ios = ImageIO.createImageOutputStream(baoStream)) {
// Get GIF writer that's compatible with reader
ImageWriter writer = ImageIO.getImageWriter(reader);
// Give it the stream to encode to
writer.setOutput(ios);
writer.prepareWriteSequence(null);
for (int i = 0; i < numImages; i++) {
// Get input image
BufferedImage frameIn = reader.read(i);
// Get input metadata
IIOMetadataNode root = (IIOMetadataNode)reader.getImageMetadata(i).getAsTree(metaFormatName);
// Find GraphicControlExtension node
int nNodes = root.getLength();
for (int j = 0; j < nNodes; j++) {
Node node = root.item(j);
if (node.getNodeName().equalsIgnoreCase("GraphicControlExtension")) {
// Get delay value
String delay = ((IIOMetadataNode)node).getAttribute("delayTime");
// Check if delay is bugged
if (Integer.parseInt(delay) == 0) {
// Overwrite with a valid delay value
((IIOMetadataNode)node).setAttribute("delayTime", "10");
}
break;
}
}
// Create output metadata
IIOMetadata metadata = writer.getDefaultImageMetadata(new ImageTypeSpecifier(frameIn), null);
// Copy metadata to output metadata
metadata.setFromTree(metadata.getNativeMetadataFormatName(), root);
// Create output image
IIOImage frameOut = new IIOImage(frameIn, null, metadata);
// Encode output image
writer.writeToSequence(frameOut, writer.getDefaultWriteParam());
}
writer.endWriteSequence();
}
// Create image using encoded data
image = Toolkit.getDefaultToolkit().createImage(baoStream.toByteArray());
}
// Trigger lazy loading of image
MediaTracker mt = new MediaTracker(parent);
mt.addImage(image, 0);
try {
mt.waitForAll();
}
catch (InterruptedException e) {
image = null;
}
return image;
}
catch (IOException e) {
e.printStackTrace();
return null;
}
}