|
1 | 1 | package org.approvaltests.awt; |
2 | | - |
3 | 2 | // GifSequenceWriter.java |
| 3 | + |
4 | 4 | // |
5 | 5 | // Created by Elliot Kroo on 2009-04-25. |
6 | 6 | // |
7 | 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported |
8 | 8 | // License. To view a copy of this license, visit |
9 | 9 | // http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative |
10 | 10 | // Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. |
11 | | - |
12 | | - |
13 | 11 | import javax.imageio.*; |
14 | 12 | import javax.imageio.metadata.*; |
15 | 13 | import javax.imageio.stream.*; |
16 | 14 | import java.awt.image.*; |
17 | 15 | import java.io.*; |
18 | 16 | import java.util.Iterator; |
19 | 17 |
|
20 | | -public class GifSequenceWriter implements AutoCloseable{ |
21 | | - protected ImageWriter gifWriter; |
22 | | - protected ImageWriteParam imageWriteParam; |
23 | | - protected IIOMetadata imageMetaData; |
24 | | - |
25 | | - /** |
26 | | - * Creates a new GifSequenceWriter |
27 | | - * |
28 | | - * @param outputStream the ImageOutputStream to be written to |
29 | | - * @param imageType one of the imageTypes specified in BufferedImage |
30 | | - * @param timeBetweenFramesMS the time between frames in miliseconds |
31 | | - * @param loopContinuously wether the gif should loop repeatedly |
32 | | - * @throws IIOException if no gif ImageWriters are found |
33 | | - * |
34 | | - * @author Elliot Kroo (elliot[at]kroo[dot]net) |
35 | | - */ |
36 | | - public GifSequenceWriter( |
37 | | - ImageOutputStream outputStream, |
38 | | - int imageType, |
39 | | - int timeBetweenFramesMS, |
40 | | - boolean loopContinuously) throws IIOException, IOException { |
41 | | - // my method to create a writer |
42 | | - gifWriter = getWriter(); |
43 | | - imageWriteParam = gifWriter.getDefaultWriteParam(); |
44 | | - ImageTypeSpecifier imageTypeSpecifier = |
45 | | - ImageTypeSpecifier.createFromBufferedImageType(imageType); |
46 | | - |
47 | | - imageMetaData = |
48 | | - gifWriter.getDefaultImageMetadata(imageTypeSpecifier, |
49 | | - imageWriteParam); |
50 | | - |
51 | | - String metaFormatName = imageMetaData.getNativeMetadataFormatName(); |
52 | | - |
53 | | - IIOMetadataNode root = (IIOMetadataNode) |
54 | | - imageMetaData.getAsTree(metaFormatName); |
55 | | - |
56 | | - IIOMetadataNode graphicsControlExtensionNode = getNode( |
57 | | - root, |
58 | | - "GraphicControlExtension"); |
59 | | - |
60 | | - graphicsControlExtensionNode.setAttribute("disposalMethod", "none"); |
61 | | - graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE"); |
62 | | - graphicsControlExtensionNode.setAttribute( |
63 | | - "transparentColorFlag", |
64 | | - "FALSE"); |
65 | | - graphicsControlExtensionNode.setAttribute( |
66 | | - "delayTime", |
67 | | - Integer.toString(timeBetweenFramesMS / 10)); |
68 | | - graphicsControlExtensionNode.setAttribute( |
69 | | - "transparentColorIndex", |
70 | | - "0"); |
71 | | - |
72 | | - IIOMetadataNode commentsNode = getNode(root, "CommentExtensions"); |
73 | | - commentsNode.setAttribute("CommentExtension", "Created by MAH"); |
74 | | - |
75 | | - IIOMetadataNode appEntensionsNode = getNode( |
76 | | - root, |
77 | | - "ApplicationExtensions"); |
78 | | - |
79 | | - IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension"); |
80 | | - |
81 | | - child.setAttribute("applicationID", "NETSCAPE"); |
82 | | - child.setAttribute("authenticationCode", "2.0"); |
83 | | - |
84 | | - int loop = loopContinuously ? 0 : 1; |
85 | | - |
86 | | - child.setUserObject(new byte[]{ 0x1, (byte) (loop & 0xFF), (byte) |
87 | | - ((loop >> 8) & 0xFF)}); |
88 | | - appEntensionsNode.appendChild(child); |
89 | | - |
90 | | - imageMetaData.setFromTree(metaFormatName, root); |
91 | | - |
92 | | - gifWriter.setOutput(outputStream); |
93 | | - |
94 | | - gifWriter.prepareWriteSequence(null); |
| 18 | +public class GifSequenceWriter implements AutoCloseable |
| 19 | +{ |
| 20 | + protected ImageWriter gifWriter; |
| 21 | + protected ImageWriteParam imageWriteParam; |
| 22 | + protected IIOMetadata imageMetaData; |
| 23 | + /** |
| 24 | + * Creates a new GifSequenceWriter |
| 25 | + * |
| 26 | + * @param outputStream the ImageOutputStream to be written to |
| 27 | + * @param imageType one of the imageTypes specified in BufferedImage |
| 28 | + * @param timeBetweenFramesMS the time between frames in miliseconds |
| 29 | + * @param loopContinuously wether the gif should loop repeatedly |
| 30 | + * @throws IIOException if no gif ImageWriters are found |
| 31 | + * |
| 32 | + * @author Elliot Kroo (elliot[at]kroo[dot]net) |
| 33 | + */ |
| 34 | + public GifSequenceWriter(ImageOutputStream outputStream, int imageType, int timeBetweenFramesMS, |
| 35 | + boolean loopContinuously) throws IIOException, IOException |
| 36 | + { |
| 37 | + // my method to create a writer |
| 38 | + gifWriter = getWriter(); |
| 39 | + imageWriteParam = gifWriter.getDefaultWriteParam(); |
| 40 | + ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(imageType); |
| 41 | + imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam); |
| 42 | + String metaFormatName = imageMetaData.getNativeMetadataFormatName(); |
| 43 | + IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName); |
| 44 | + IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension"); |
| 45 | + graphicsControlExtensionNode.setAttribute("disposalMethod", "none"); |
| 46 | + graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE"); |
| 47 | + graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE"); |
| 48 | + graphicsControlExtensionNode.setAttribute("delayTime", Integer.toString(timeBetweenFramesMS / 10)); |
| 49 | + graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0"); |
| 50 | + IIOMetadataNode commentsNode = getNode(root, "CommentExtensions"); |
| 51 | + commentsNode.setAttribute("CommentExtension", "Created by MAH"); |
| 52 | + IIOMetadataNode appEntensionsNode = getNode(root, "ApplicationExtensions"); |
| 53 | + IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension"); |
| 54 | + child.setAttribute("applicationID", "NETSCAPE"); |
| 55 | + child.setAttribute("authenticationCode", "2.0"); |
| 56 | + int loop = loopContinuously ? 0 : 1; |
| 57 | + child.setUserObject(new byte[]{0x1, (byte) (loop & 0xFF), (byte) ((loop >> 8) & 0xFF)}); |
| 58 | + appEntensionsNode.appendChild(child); |
| 59 | + imageMetaData.setFromTree(metaFormatName, root); |
| 60 | + gifWriter.setOutput(outputStream); |
| 61 | + gifWriter.prepareWriteSequence(null); |
| 62 | + } |
| 63 | + public void writeToSequence(RenderedImage img) throws IOException |
| 64 | + { |
| 65 | + gifWriter.writeToSequence(new IIOImage(img, null, imageMetaData), imageWriteParam); |
| 66 | + } |
| 67 | + /** |
| 68 | + * Close this GifSequenceWriter object. This does not close the underlying |
| 69 | + * stream, just finishes off the GIF. |
| 70 | + */ |
| 71 | + public void close() throws IOException |
| 72 | + { |
| 73 | + gifWriter.endWriteSequence(); |
| 74 | + } |
| 75 | + /** |
| 76 | + * Returns the first available GIF ImageWriter using |
| 77 | + * ImageIO.getImageWritersBySuffix("gif"). |
| 78 | + * |
| 79 | + * @return a GIF ImageWriter object |
| 80 | + * @throws IIOException if no GIF image writers are returned |
| 81 | + */ |
| 82 | + private static ImageWriter getWriter() throws IIOException |
| 83 | + { |
| 84 | + Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif"); |
| 85 | + if (!iter.hasNext()) |
| 86 | + { |
| 87 | + throw new IIOException("No GIF Image Writers Exist"); |
95 | 88 | } |
96 | | - |
97 | | - public void writeToSequence(RenderedImage img) throws IOException { |
98 | | - gifWriter.writeToSequence( |
99 | | - new IIOImage( |
100 | | - img, |
101 | | - null, |
102 | | - imageMetaData), |
103 | | - imageWriteParam); |
| 89 | + else |
| 90 | + { |
| 91 | + return iter.next(); |
104 | 92 | } |
105 | | - |
106 | | - /** |
107 | | - * Close this GifSequenceWriter object. This does not close the underlying |
108 | | - * stream, just finishes off the GIF. |
109 | | - */ |
110 | | - public void close() throws IOException { |
111 | | - gifWriter.endWriteSequence(); |
| 93 | + } |
| 94 | + /** |
| 95 | + * Returns an existing child node, or creates and returns a new child node (if |
| 96 | + * the requested node does not exist). |
| 97 | + * |
| 98 | + * @param rootNode the <tt>IIOMetadataNode</tt> to search for the child node. |
| 99 | + * @param nodeName the name of the child node. |
| 100 | + * |
| 101 | + * @return the child node, if found or a new node created with the given name. |
| 102 | + */ |
| 103 | + private static IIOMetadataNode getNode(IIOMetadataNode rootNode, String nodeName) |
| 104 | + { |
| 105 | + int nNodes = rootNode.getLength(); |
| 106 | + for (int i = 0; i < nNodes; i++) |
| 107 | + { |
| 108 | + if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) == 0) |
| 109 | + { return ((IIOMetadataNode) rootNode.item(i)); } |
112 | 110 | } |
113 | | - |
114 | | - /** |
115 | | - * Returns the first available GIF ImageWriter using |
116 | | - * ImageIO.getImageWritersBySuffix("gif"). |
117 | | - * |
118 | | - * @return a GIF ImageWriter object |
119 | | - * @throws IIOException if no GIF image writers are returned |
120 | | - */ |
121 | | - private static ImageWriter getWriter() throws IIOException { |
122 | | - Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif"); |
123 | | - if(!iter.hasNext()) { |
124 | | - throw new IIOException("No GIF Image Writers Exist"); |
125 | | - } else { |
126 | | - return iter.next(); |
127 | | - } |
| 111 | + IIOMetadataNode node = new IIOMetadataNode(nodeName); |
| 112 | + rootNode.appendChild(node); |
| 113 | + return (node); |
| 114 | + } |
| 115 | + /** |
| 116 | + public GifSequenceWriter( |
| 117 | + BufferedOutputStream outputStream, |
| 118 | + int imageType, |
| 119 | + int timeBetweenFramesMS, |
| 120 | + boolean loopContinuously) { |
| 121 | + */ |
| 122 | + public static void main(String[] args) throws Exception |
| 123 | + { |
| 124 | + if (args.length > 1) |
| 125 | + { |
| 126 | + // grab the output image type from the first image in the sequence |
| 127 | + BufferedImage firstImage = ImageIO.read(new File(args[0])); |
| 128 | + // create a new BufferedOutputStream with the last argument |
| 129 | + ImageOutputStream output = new FileImageOutputStream(new File(args[args.length - 1])); |
| 130 | + // create a gif sequence with the type of the first image, 1 second |
| 131 | + // between frames, which loops continuously |
| 132 | + GifSequenceWriter writer = new GifSequenceWriter(output, firstImage.getType(), 1, false); |
| 133 | + // write out the first image to our sequence... |
| 134 | + writer.writeToSequence(firstImage); |
| 135 | + for (int i = 1; i < args.length - 1; i++) |
| 136 | + { |
| 137 | + BufferedImage nextImage = ImageIO.read(new File(args[i])); |
| 138 | + writer.writeToSequence(nextImage); |
| 139 | + } |
| 140 | + writer.close(); |
| 141 | + output.close(); |
128 | 142 | } |
129 | | - |
130 | | - /** |
131 | | - * Returns an existing child node, or creates and returns a new child node (if |
132 | | - * the requested node does not exist). |
133 | | - * |
134 | | - * @param rootNode the <tt>IIOMetadataNode</tt> to search for the child node. |
135 | | - * @param nodeName the name of the child node. |
136 | | - * |
137 | | - * @return the child node, if found or a new node created with the given name. |
138 | | - */ |
139 | | - private static IIOMetadataNode getNode( |
140 | | - IIOMetadataNode rootNode, |
141 | | - String nodeName) { |
142 | | - int nNodes = rootNode.getLength(); |
143 | | - for (int i = 0; i < nNodes; i++) { |
144 | | - if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) |
145 | | - == 0) { |
146 | | - return((IIOMetadataNode) rootNode.item(i)); |
147 | | - } |
148 | | - } |
149 | | - IIOMetadataNode node = new IIOMetadataNode(nodeName); |
150 | | - rootNode.appendChild(node); |
151 | | - return(node); |
152 | | - } |
153 | | - |
154 | | - /** |
155 | | - public GifSequenceWriter( |
156 | | - BufferedOutputStream outputStream, |
157 | | - int imageType, |
158 | | - int timeBetweenFramesMS, |
159 | | - boolean loopContinuously) { |
160 | | - */ |
161 | | - |
162 | | - public static void main(String[] args) throws Exception { |
163 | | - if (args.length > 1) { |
164 | | - // grab the output image type from the first image in the sequence |
165 | | - BufferedImage firstImage = ImageIO.read(new File(args[0])); |
166 | | - |
167 | | - // create a new BufferedOutputStream with the last argument |
168 | | - ImageOutputStream output = |
169 | | - new FileImageOutputStream(new File(args[args.length - 1])); |
170 | | - |
171 | | - // create a gif sequence with the type of the first image, 1 second |
172 | | - // between frames, which loops continuously |
173 | | - GifSequenceWriter writer = |
174 | | - new GifSequenceWriter(output, firstImage.getType(), 1, false); |
175 | | - |
176 | | - // write out the first image to our sequence... |
177 | | - writer.writeToSequence(firstImage); |
178 | | - for(int i=1; i<args.length-1; i++) { |
179 | | - BufferedImage nextImage = ImageIO.read(new File(args[i])); |
180 | | - writer.writeToSequence(nextImage); |
181 | | - } |
182 | | - |
183 | | - writer.close(); |
184 | | - output.close(); |
185 | | - } else { |
186 | | - System.out.println( |
187 | | - "Usage: java GifSequenceWriter [list of gif files] [output file]"); |
188 | | - } |
| 143 | + else |
| 144 | + { |
| 145 | + System.out.println("Usage: java GifSequenceWriter [list of gif files] [output file]"); |
189 | 146 | } |
| 147 | + } |
190 | 148 | } |
0 commit comments