import javax.imageio.metadata.*; import javax.imageio.stream.*; sclass TransformingGIFLoader { settable File inputFile; settable bool debug; byte[] data; int numImages; ImageReader reader; BufferedImage get() { if (!file.exists()) null; // Load anything but GIF the normal way if (!isGIF(file)) ret ImageIO.read(file); reader = ImageIO.getImageReadersByFormatName("gif").next(); // Give it the stream to decode from reader.setInput(ImageIO.createImageInputStream(file)); // Get 'metaFormatName'. Need first frame for that. IIOMetadata imageMetaData = reader.getImageMetadata(0); String metaFormatName = imageMetaData.getNativeMetadataFormatName(); numImages = reader.getNumImages(true); // Prepare streams for image encoding new ByteArrayOutputStream baoStream; { temp 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 i to numImages: { // Get input image BufferedImage frameIn = reader.read(i); frameIn = transformImage(frameIn); // Get input metadata IIOMetadataNode root = (IIOMetadataNode)reader.getImageMetadata(i).getAsTree(metaFormatName); // Find GraphicControlExtension node int nNodes = root.getLength(); for j to nNodes: { org.w3c.dom.Node node = root.item(j); if (node.getNodeName().equalsIgnoreCase("GraphicControlExtension")) { cast node to IIOMetadataNode; if (parseInt(node.getAttribute("delayTime")) == 0) node.setAttribute("delayTime", "10"); } // 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 data = baoStream.toByteArray(); if (debug) print("Data size: " + l(data)); image = Toolkit.getDefaultToolkit().createImage(data); } ret image; } bool animationSpeedIsBugged() { for i to numImages: { IIOMetadataNode root = cast reader.getImageMetadata(i).getAsTree(metaFormatName); int nNodes = root.getLength(); for j to nNodes: { org.w3c.dom.Node node = root.item(j); if (node.getNodeName().equalsIgnoreCase("GraphicControlExtension")) { // Get delay value S delay = ((IIOMetadataNode)node).getAttribute("delayTime"); // Check if delay is bugged if (parseInt(delay) == 0) true; } } } } swappable BufferedImage transformImage(BufferedImage image) { ret image; } }