View Javadoc

1   package com.atlassian.selenium.visualcomparison.utils;
2   
3   import com.atlassian.annotations.Internal;
4   import org.apache.velocity.VelocityContext;
5   
6   import javax.imageio.ImageIO;
7   import java.awt.*;
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  import java.util.Collection;
15  import java.util.List;
16  
17  @Internal
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       * For tests.
52       * @return a list of {@link BoundingBox} elements.
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             // Once we've written the sub-images, draw a black box around each of the bounding boxes on the diff.
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                 // Draw an orange box around each of the ignored areas on the diff.
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         // Write the full diff image to the output directory.
144         File diffImageFile = new File(imageOutputDir + "diff-" + id + "." + resolution + ".png");
145         ImageIO.write(diffImage, "png", diffImageFile);
146         diffImage.flush();
147 
148         // Copy the full baseline image to the output directory.
149         File oldImageFile = new File(imageOutputDir + "old-" + oldScreenshot.getFileName());
150         ImageIO.write(oldScreenshot.getImage(), "png", oldImageFile);
151 
152         // Copy the new image to the output directory.
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 }