1 package com.atlassian.selenium.visualcomparison.utils;
2
3 import org.apache.commons.lang.StringEscapeUtils;
4 import org.apache.velocity.VelocityContext;
5
6 import javax.imageio.ImageIO;
7 import java.awt.*;
8 import java.util.Collection;
9 import java.util.Iterator;
10 import java.util.List;
11 import java.awt.image.BufferedImage;
12 import java.io.File;
13 import java.io.FileOutputStream;
14 import java.io.IOException;
15 import java.io.OutputStreamWriter;
16 import java.util.ArrayList;
17
18 public class ScreenshotDiff
19 {
20 private Screenshot oldScreenshot;
21 private Screenshot newScreenshot;
22 private String id;
23 private ScreenResolution resolution;
24 private BufferedImage diffImage;
25 private List<BoundingBox> ignoreAreas;
26 private Collection<PageDifference> differences;
27
28 public ScreenshotDiff(Screenshot oldScreenshot, Screenshot newScreenshot,
29 String id, ScreenResolution resolution,
30 BufferedImage diffImage, ArrayList<BoundingBox> boxes, List<BoundingBox> ignoreAreas)
31 {
32 this.oldScreenshot = oldScreenshot;
33 this.newScreenshot = newScreenshot;
34 this.id = id;
35 this.resolution = resolution;
36 this.diffImage = diffImage;
37 this.ignoreAreas = ignoreAreas;
38 this.differences = new ArrayList<PageDifference>();
39 for(BoundingBox box : boxes)
40 {
41 differences.add(new PageDifference(box));
42 }
43 }
44
45 public Collection<PageDifference> getDifferences()
46 {
47 return differences;
48 }
49
50
51
52
53
54 List<BoundingBox> getDiffAreas()
55 {
56 List<BoundingBox> boxes = new ArrayList<BoundingBox>();
57 for (PageDifference difference : getDifferences())
58 {
59 boxes.add(difference.getBoundingBox());
60 }
61 return boxes;
62 }
63
64 public boolean hasDifferences()
65 {
66 return getDifferences().size() > 0;
67 }
68
69 public static String getImageOutputDir(String outputDir, String imageSubDir)
70 {
71 String imageOutputDir = outputDir + "/";
72 if (imageSubDir != null && !imageSubDir.equals(""))
73 {
74 imageOutputDir = imageOutputDir + imageSubDir + "/";
75 }
76 return imageOutputDir;
77 }
78
79 public static String relativePath(final File file, final String relativeRoot)
80 {
81 final String fullPath = file.getAbsolutePath();
82 String relativePath = fullPath.substring(fullPath.indexOf(relativeRoot)+relativeRoot.length());
83 if (relativePath.startsWith("/")) relativePath = "." + relativePath;
84 return relativePath;
85 }
86
87 public void writeDiffReport(String outputDir, String imageSubDir) throws Exception
88 {
89 if (!hasDifferences())
90 {
91 return;
92 }
93
94 String imageOutputDir = getImageOutputDir(outputDir, imageSubDir);
95
96 int i = 0;
97 for (PageDifference difference : getDifferences())
98 {
99 final BoundingBox box = difference.getBoundingBox();
100 Graphics2D graphics = diffImage.createGraphics();
101
102 String oldImagePath = imageOutputDir + "boxold" + i + "-" + id + "." + resolution + ".png";
103 String newImagePath = imageOutputDir + "boxnew" + i + "-" + id + "." + resolution + ".png";
104 String diffImagePath = imageOutputDir + "boxdiff" + i + "-" + id + "." + resolution + ".png";
105
106 File oldImageFile = writeSubImage(oldScreenshot.getImage(), box, oldImagePath);
107 File newImageFile = writeSubImage(newScreenshot.getImage(), box, newImagePath);
108 File diffImageFile = writeSubImage(diffImage, box, diffImagePath);
109 i++;
110
111
112 graphics.setColor(Color.BLACK);
113 BasicStroke stroke = new BasicStroke(2.0f,
114 BasicStroke.CAP_SQUARE,
115 BasicStroke.JOIN_MITER,
116 10.0f, new float[] { 10.0f }, 0.0f);
117 graphics.setStroke(stroke);
118 graphics.drawRect(box.getMarginLeft(), box.getMarginTop(), box.getMarginWidth(diffImage.getWidth() - 1), box.getMarginHeight(diffImage.getHeight() - 1));
119
120 difference.setImages(
121 new PageDifferenceImages(oldImageFile,
122 newImageFile,
123 diffImageFile,
124 outputDir)
125 );
126 }
127 if (ignoreAreas != null)
128 {
129 for (BoundingBox ignoreArea : ignoreAreas)
130 {
131 Graphics2D graphics = diffImage.createGraphics();
132
133 graphics.setColor(Color.ORANGE);
134 BasicStroke stroke = new BasicStroke(2.0f,
135 BasicStroke.CAP_SQUARE,
136 BasicStroke.JOIN_MITER,
137 10.0f, new float[] { 10.0f }, 0.0f);
138 graphics.setStroke(stroke);
139 graphics.drawRect(ignoreArea.getLeft(), ignoreArea.getTop(), ignoreArea.getWidth(), ignoreArea.getHeight());
140 }
141 }
142
143
144 File diffImageFile = new File(imageOutputDir + "diff-" + id + "." + resolution + ".png");
145 ImageIO.write(diffImage, "png", diffImageFile);
146 diffImage.flush();
147
148
149 File oldImageFile = new File(imageOutputDir + "old-" + oldScreenshot.getFileName());
150 ImageIO.write(oldScreenshot.getImage(), "png", oldImageFile);
151
152
153 final File newImageFile = new File(imageOutputDir + newScreenshot.getFileName());
154 ImageIO.write(newScreenshot.getImage(), "png", newImageFile);
155
156 VelocityContext context = ReportRenderer.createContext();
157 context.put("id", id);
158 context.put("resolution", resolution);
159 context.put("differences", differences);
160 context.put("oldImageFile", relativePath(oldImageFile, outputDir));
161 context.put("newImageFile", relativePath(newImageFile, outputDir));
162 context.put("diffImageFile", relativePath(diffImageFile, outputDir));
163 String report = ReportRenderer.render(context, "visual-regression-report-single.vm");
164
165 OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(outputDir + "/report-" + id + "-" + resolution + ".html")));
166 writer.append(report);
167 writer.close();
168 }
169
170 private File writeSubImage(BufferedImage image, BoundingBox box, String outputPath) throws IOException
171 {
172 BufferedImage boxImage = image.getSubimage(box.getMarginLeft(), box.getMarginTop(), box.getMarginWidth(image.getWidth() - 1), box.getMarginHeight(image.getHeight() - 1));
173 File outputFile = new File(outputPath);
174 ImageIO.write(boxImage, "png", outputFile);
175 return outputFile;
176 }
177
178 }