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