import javax.imageio.metadata.*; import javax.imageio.stream.*; sclass GIFRewriter { settable File inputFile; settable bool debug; byte[] data; int numImages; ImageReader reader; S metaFormatName; Image get() ctex { if (!inputFile.exists()) null; // Load anything but GIF the normal way if (!isGIF(inputFile)) ret ImageIO.read(inputFile); reader = ImageIO.getImageReadersByFormatName("gif").next(); // Give it the stream to decode from reader.setInput(ImageIO.createImageInputStream(inputFile)); numImages = reader.getNumImages(true); // Get 'metaFormatName'. Need first frame for that. IIOMetadata imageMetaData = reader.getImageMetadata(0); metaFormatName = imageMetaData.getNativeMetadataFormatName(); // 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)); ret Toolkit.getDefaultToolkit().createImage(data); } } bool animationSpeedIsBugged() ctex { 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")) { cast node to IIOMetadataNode; if (parseInt(node.getAttribute("delayTime")) == 0) true; } } } false; } swappable BufferedImage transformImage(BufferedImage image) { ret image; } }