View Javadoc

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