0

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?

user1111929
  • 6,050
  • 9
  • 43
  • 73

1 Answers1

1

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;
    }
}
Mark Jeronimus
  • 9,278
  • 3
  • 37
  • 50
  • That code was able to fix my current .gif files, thanks! Only one problem: it gives me a warning "GIFImageMetadata is internal proprietary API and may be removed in a future release". Would there be any way around this? I would of course would like my program to keep working in a future Java release, so if this package is about to disappear then that is a problem. – user1111929 Nov 09 '14 at 18:07
  • Never mind, using the code in this topic http://stackoverflow.com/questions/20077913/read-delay-between-frames-in-animated-gif I managed to fix that warning, now the code no longer uses the GIFImageMetadata class. It all works now. Thanks a bunch! – user1111929 Nov 09 '14 at 18:25