Skip to content

Commit 2130f86

Browse files
isidorejmasonlee
andcommitted
F!! verify sequence to an animated gif
Co-Authored-By: jmasonlee <[email protected]>
1 parent 80ea552 commit 2130f86

File tree

6 files changed

+294
-4
lines changed

6 files changed

+294
-4
lines changed

approvaltests-tests/src/test/java/org/approvaltests/awt/ApprovalsTest.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
package org.approvaltests.awt;
22

33
import org.approvaltests.core.Options;
4-
import org.approvaltests.reporters.FileCaptureReporter;
5-
import org.approvaltests.reporters.UseReporter;
4+
import org.approvaltests.reporters.*;
65
import org.junit.jupiter.api.Disabled;
76
import org.junit.jupiter.api.Test;
87
import org.junit.jupiter.api.condition.DisabledOnJre;
98
import org.junit.jupiter.api.condition.EnabledOnJre;
109
import org.junit.jupiter.api.condition.JRE;
1110

12-
@UseReporter({FileCaptureReporter.class})
11+
//@UseReporter({FileCaptureReporter.class})
1312
public class ApprovalsTest
1413
{
1514
@DisabledOnJre({JRE.JAVA_8})
@@ -34,4 +33,11 @@ void customPanelOnJre8()
3433
final CustomPanel panel = new CustomPanel();
3534
AwtApprovals.verify(panel);
3635
}
36+
37+
@Test
38+
@UseReporter(ImageWebReporter.class)
39+
void testSequence() {
40+
SquareDrawer squareDrawer = new SquareDrawer();
41+
AwtApprovals.verifySequence(5, f -> squareDrawer.setSquareSize(f*10) );
42+
}
3743
}
1.28 KB
Loading
Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,32 @@
1-
package org.approvaltests.awt;public class SquareDrawer {
1+
package org.approvaltests.awt;
2+
3+
import com.spun.swing.Paintable;
4+
import com.spun.util.Colors;
5+
import org.lambda.actions.Action0;
6+
7+
import java.awt.*;
8+
9+
public class SquareDrawer implements Paintable {
10+
private int size;
11+
12+
public Paintable setSquareSize(int size){
13+
this.size = size;
14+
return this;
15+
}
16+
17+
@Override
18+
public Dimension getSize() {
19+
return new Dimension(100, 100);
20+
}
21+
22+
@Override
23+
public void paint(Graphics g) {
24+
g.setColor(Colors.Purples.Thistle);
25+
g.fillRect(10,10,size,size);
26+
}
27+
28+
@Override
29+
public void registerRepaint(Action0 repaint) {
30+
31+
}
232
}

approvaltests/src/main/java/org/approvaltests/awt/AwtApprovals.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.approvaltests.writers.ComponentApprovalWriter;
1010
import org.approvaltests.writers.ImageApprovalWriter;
1111
import org.approvaltests.writers.PaintableApprovalWriter;
12+
import org.lambda.functions.Function1;
1213

1314
import java.awt.Component;
1415
import java.awt.Image;
@@ -43,4 +44,7 @@ public static void verify(Paintable c, Options options)
4344
{
4445
Approvals.verify(new PaintableApprovalWriter(c), options);
4546
}
47+
public static void verifySequence(int numberOfFrames, Function1<Integer, Paintable> sequenceRenderer) {
48+
Approvals.verify(new PaintableMultiframeWriter(numberOfFrames, sequenceRenderer), new Options());
49+
}
4650
}
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package org.approvaltests.awt;
2+
3+
// GifSequenceWriter.java
4+
//
5+
// Created by Elliot Kroo on 2009-04-25.
6+
//
7+
// This work is licensed under the Creative Commons Attribution 3.0 Unported
8+
// License. To view a copy of this license, visit
9+
// http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative
10+
// Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
11+
12+
13+
import javax.imageio.*;
14+
import javax.imageio.metadata.*;
15+
import javax.imageio.stream.*;
16+
import java.awt.image.*;
17+
import java.io.*;
18+
import java.util.Iterator;
19+
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);
95+
}
96+
97+
public void writeToSequence(RenderedImage img) throws IOException {
98+
gifWriter.writeToSequence(
99+
new IIOImage(
100+
img,
101+
null,
102+
imageMetaData),
103+
imageWriteParam);
104+
}
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();
112+
}
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+
}
128+
}
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+
}
189+
}
190+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package org.approvaltests.awt;
2+
3+
import com.spun.swing.Paintable;
4+
import com.spun.util.ObjectUtils;
5+
import com.spun.util.persistence.ExecutableQuery;
6+
import org.approvaltests.approvers.ApprovalApprover;
7+
import org.approvaltests.core.ApprovalWriter;
8+
import org.approvaltests.writers.PaintableApprovalWriter;
9+
import org.lambda.functions.Function1;
10+
11+
import javax.imageio.stream.FileImageOutputStream;
12+
import javax.imageio.stream.ImageOutputStream;
13+
import java.awt.image.BufferedImage;
14+
import java.io.File;
15+
import java.sql.ResultSet;
16+
import java.util.ArrayList;
17+
import java.util.Map;
18+
19+
public class PaintableMultiframeWriter implements ApprovalWriter {
20+
private int numberOfFrames;
21+
private Function1<Integer, Paintable> frameGetter;
22+
23+
public PaintableMultiframeWriter(int numberOfFrames, Function1<Integer, Paintable> frameGetter) {
24+
this.numberOfFrames = numberOfFrames;
25+
this.frameGetter = frameGetter;
26+
}
27+
28+
@Override
29+
public File writeReceivedFile(File received) {
30+
try {
31+
ArrayList<BufferedImage> images = getBufferedImages();
32+
33+
try (ImageOutputStream output = new FileImageOutputStream(received)) {
34+
try (GifSequenceWriter writer = new GifSequenceWriter(output, images.get(0).getType(), 500, true)) {
35+
for (BufferedImage image : images) {
36+
writer.writeToSequence(image);
37+
}
38+
}
39+
}
40+
41+
return received;
42+
} catch (Exception e) {
43+
throw ObjectUtils.throwAsError(e);
44+
}
45+
}
46+
47+
private ArrayList<BufferedImage> getBufferedImages() {
48+
ArrayList<BufferedImage> images = new ArrayList<>();
49+
for (int i = 0; i < numberOfFrames ; i++) {
50+
BufferedImage image = PaintableApprovalWriter.drawComponent(frameGetter.call(i));
51+
images.add(image);
52+
}
53+
return images;
54+
}
55+
56+
@Override
57+
public String getFileExtensionWithDot() {
58+
return ".gif";
59+
}
60+
}

0 commit comments

Comments
 (0)