View Javadoc

1   package com.atlassian.selenium.visualcomparison.utils;
2   
3   import java.awt.image.BufferedImage;
4   import java.io.IOException;
5   import java.util.ArrayList;
6   import java.util.List;
7   
8   public class ScreenshotDiffer
9   {
10      public ScreenshotDiff diff(Screenshot oldScreen, Screenshot newScreen,
11                                 List<BoundingBox> ignoreAreas, boolean ignoreSingleLines) throws IOException
12      {
13          final BufferedImage thisImage = oldScreen.getImage();
14          final BufferedImage otherImage = newScreen.getImage();
15          final int thisWidth = thisImage.getWidth();
16          final int thisHeight = thisImage.getHeight();
17          final int otherWidth = otherImage.getWidth();
18          final int otherHeight = otherImage.getHeight();
19          final int maxWidth = Math.max(thisWidth, otherWidth);
20          final int maxHeight = Math.max(thisHeight, otherHeight);
21  
22          // Iterate through the pixels in both images and create a diff image.
23          BufferedImage diffImage = new BufferedImage(maxWidth, maxHeight, BufferedImage.TYPE_INT_RGB);
24          ArrayList<BoundingBox> boxes = new ArrayList<BoundingBox>();
25          for (int x = 0; x < maxWidth; x++)
26          {
27              for (int y = 0; y < maxHeight; y++)
28              {
29                  // The images aren't necessarily the same size, only compare the pixels if they're
30                  // present in both.
31                  if ((x < thisWidth) && (x < otherWidth) && (y < thisHeight) && (y < otherHeight))
32                  {
33                      int thisPixel = thisImage.getRGB(x, y);
34                      int otherPixel = otherImage.getRGB(x, y);
35                      if (shouldIgnorePixel (x, y, ignoreAreas) || thisPixel == otherPixel)
36                      {
37                          // The pixels are the same or we don't care if they're different, draw it as-is in the diff.
38                          diffImage.setRGB(x, y, thisPixel);
39                      }
40                      else
41                      {
42                          // The pixels are different, set the pixel to red in the diff.
43                          diffImage.setRGB(x, y, 0xFF0000);
44  
45                          // If this pixel is near any bounding boxes, expand them to include it.
46                          boolean foundBox = false;
47                          for (BoundingBox box : boxes)
48                          {
49                              if (box.isNear(x, y))
50                              {
51                                  box.merge(x, y);
52                                  foundBox = true;
53                              }
54                          }
55                          // Otherwise, start a new bounding box.
56                          if (!foundBox)
57                          {
58                              boxes.add(new BoundingBox(x, y));
59                          }
60                      }
61                  }
62                  else
63                  {
64                      // The two images are different sizes and this pixel is out of bounds in one of them.
65                      // Set the pixel to black in the diff.
66                      diffImage.setRGB(x, y, 0x000000);
67                  }
68              }
69          }
70          if (ignoreSingleLines)
71          {
72              BoundingBox.deleteSingleLineBoxes(boxes);
73          }
74  
75          BoundingBox.mergeOverlappingBoxes(boxes);
76  
77          thisImage.flush();
78          otherImage.flush();
79  
80          return new ScreenshotDiff(oldScreen, newScreen, oldScreen.getId(), oldScreen.getResolution(), diffImage, boxes, ignoreAreas);
81      }
82  
83      private boolean shouldIgnorePixel (int x, int y, List<BoundingBox> ignoreAreas)
84      {
85          if (ignoreAreas == null)
86          {
87              return false;
88          }
89          for (BoundingBox ignoreArea : ignoreAreas)
90          {
91              if (ignoreArea.contains (x,y))
92              {
93                  return true;
94              }
95          }
96          return false;
97      }
98  }