releng: Add SWTBot integration tests
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui.swtbot.tests / shared / org / eclipse / tracecompass / tmf / ui / swtbot / tests / shared / ImageHelper.java
1 /*******************************************************************************
2 * Copyright (c) 2015, 2016 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Matthew Khouzam - Initial API and implementation
11 *******************************************************************************/
12 package org.eclipse.tracecompass.tmf.ui.swtbot.tests.shared;
13
14 import java.awt.AWTException;
15 import java.io.File;
16 import java.io.IOException;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.List;
20
21 import javax.imageio.ImageIO;
22
23 import org.eclipse.swt.events.PaintEvent;
24 import org.eclipse.swt.events.PaintListener;
25 import org.eclipse.swt.graphics.Color;
26 import org.eclipse.swt.graphics.Point;
27 import org.eclipse.swt.graphics.RGB;
28 import org.eclipse.swt.graphics.Rectangle;
29 import org.eclipse.swt.widgets.Display;
30 import org.eclipse.swt.widgets.Shell;
31 import org.eclipse.swtbot.swt.finder.SWTBot;
32 import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
33 import org.eclipse.swtbot.swt.finder.results.Result;
34 import org.eclipse.swtbot.swt.finder.utils.SWTUtils;
35 import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
36
37 import com.google.common.collect.HashMultiset;
38 import com.google.common.collect.Multiset;
39
40 /**
41 * Test helpers, allow looking up the frame buffer and testing what is really
42 * displayed
43 */
44 public final class ImageHelper {
45
46 private final int[] fPixels;
47 private final Rectangle fBounds;
48
49 /**
50 * Constructor
51 *
52 * @param pixels
53 * the pixel map
54 * @param bounds
55 * the bounds
56 */
57 private ImageHelper(int[] pixels, Rectangle bounds) {
58 if (pixels.length != bounds.height * bounds.width) {
59 throw new IllegalArgumentException("Incoherent image");
60 }
61 fPixels = Arrays.copyOf(pixels, pixels.length);
62 fBounds = bounds;
63 }
64
65 /**
66 * Gets a screen grab of the rectangle r; the way to access a given pixel is
67 * <code>pixel = rect.width * y + x;</code>
68 *
69 * @param rect
70 * the area to grab in display relative coordinates (top left is
71 * the origin)
72 * @return an ImageHelper, cannot be null
73 */
74 public static ImageHelper grabImage(final Rectangle rect) {
75 return UIThreadRunnable.syncExec(new Result<ImageHelper>() {
76 @Override
77 public ImageHelper run() {
78 try {
79 // note: awt is explicitly called until we can use SWT to
80 // replace it.
81 java.awt.Robot rb = new java.awt.Robot();
82 java.awt.image.BufferedImage bi = rb.createScreenCapture(new java.awt.Rectangle(rect.x, rect.y, rect.width, rect.height));
83 return new ImageHelper(bi.getRGB(0, 0, rect.width, rect.height, null, 0, rect.width), rect);
84 } catch (AWTException e) {
85 }
86 return new ImageHelper(new int[0], new Rectangle(0, 0, 0, 0));
87 }
88 });
89 }
90
91 /**
92 * Get the bounds
93 *
94 * @return the bounds
95 */
96 public Rectangle getBounds() {
97 return fBounds;
98 }
99
100 /**
101 * Get the pixel for a given set of coordinates
102 *
103 * @param x
104 * x
105 * @param y
106 * y
107 * @return the RGB, can return an {@link ArrayIndexOutOfBoundsException}
108 */
109 public RGB getPixel(int x, int y) {
110 return getRgbFromRGBPixel(fPixels[x + y * fBounds.width]);
111 }
112
113 /**
114 * Sample an image at n points
115 *
116 * @param samplePoints
117 * a list of points to sample at
118 * @return a list of RGBs corresponding to the pixel coordinates. Can throw
119 * an {@link IllegalArgumentException} if the point is outside of
120 * the image bounds
121 */
122 public List<RGB> sample(List<Point> samplePoints) {
123 for (Point p : samplePoints) {
124 if (!getBounds().contains(p)) {
125 throw new IllegalArgumentException("Point outside of the image");
126 }
127
128 }
129 List<RGB> retVal = new ArrayList<>(samplePoints.size());
130 for (Point p : samplePoints) {
131 retVal.add(getPixel(p.x, p.y));
132 }
133 return retVal;
134 }
135
136 /**
137 * Get the color histogram of the image
138 *
139 * @return The color density of the image
140 */
141 public Multiset<RGB> getHistogram() {
142 Multiset<RGB> colors = HashMultiset.create();
143 for (int pixel : fPixels) {
144 RGB pixelColor = getRgbFromRGBPixel(pixel);
145 colors.add(pixelColor);
146 }
147 return colors;
148 }
149
150 /**
151 * Get the color histogram of the row of the image
152 *
153 * @param row
154 * the row to lookup
155 *
156 * @return The x oriented line
157 */
158 public List<RGB> getPixelRow(int row) {
159 List<RGB> retVal = new ArrayList<>();
160 for (int x = 0; x < getBounds().width; x++) {
161 retVal.add(getPixel(x, row));
162 }
163 return retVal;
164 }
165
166 /**
167 * Get the color histogram of a column of the image
168 *
169 * @param col
170 * the column to lookup
171 *
172 * @return The y oriented line
173 */
174 public List<RGB> getPixelColumn(int col) {
175 List<RGB> retVal = new ArrayList<>();
176 for (int y = 0; y < getBounds().height; y++) {
177 retVal.add(getPixel(col, y));
178 }
179 return retVal;
180 }
181
182 /**
183 * Difference between two images (this - other)
184 *
185 * @param other
186 * the other image to compare
187 * @return an {@link ImageHelper} that is the per pixel difference between
188 * the two images
189 *
190 */
191 public ImageHelper diff(ImageHelper other) {
192 if (other.getBounds().width != fBounds.width && other.getBounds().height != fBounds.height) {
193 throw new IllegalArgumentException("Different sized images");
194 }
195 int[] fBuffer = new int[fPixels.length];
196 for (int i = 0; i < fPixels.length; i++) {
197 RGB local = getRgbFromRGBPixel(fPixels[i]);
198 RGB otherPixel = getRgbFromRGBPixel(other.fPixels[i]);
199 byte r = (byte) (local.red - otherPixel.red);
200 byte g = (byte) (local.green - otherPixel.green);
201 byte b = (byte) (local.blue - otherPixel.blue);
202 fBuffer[i] = r << 16 + g << 8 + b;
203 }
204 return new ImageHelper(fBuffer, getBounds());
205 }
206
207 /**
208 * Write the image to disk in PNG form
209 *
210 * @param outputFile
211 * the file to write it to
212 * @throws IOException
213 * file not found and such
214 */
215 public void writePng(File outputFile) throws IOException {
216 java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(fBounds.width, fBounds.height, java.awt.image.BufferedImage.TYPE_INT_RGB);
217 image.setRGB(0, 0, fBounds.width, fBounds.height, fPixels, 0, fBounds.width);
218 ImageIO.write(image, "png", outputFile);
219 }
220
221 private static RGB getRgbFromRGBPixel(int pixel) {
222 return new RGB(((pixel >> 16) & 0xff), ((pixel >> 8) & 0xff), ((pixel) & 0xff));
223 }
224
225 @Override
226 public int hashCode() {
227 final int prime = 31;
228 int result = 1;
229 result = prime * result + ((fBounds == null) ? 0 : fBounds.hashCode());
230 result = prime * result + Arrays.hashCode(fPixels);
231 return result;
232 }
233
234 @Override
235 public boolean equals(Object obj) {
236 if (this == obj) {
237 return true;
238 }
239 if (obj == null) {
240 return false;
241 }
242 if (getClass() != obj.getClass()) {
243 return false;
244 }
245 ImageHelper other = (ImageHelper) obj;
246 if (fBounds == null) {
247 if (other.fBounds != null) {
248 return false;
249 }
250 } else if (!fBounds.equals(other.fBounds)) {
251 return false;
252 }
253 if (!Arrays.equals(fPixels, other.fPixels)) {
254 return false;
255 }
256 return true;
257 }
258
259 /**
260 * On Mac, RGB values that are captured with ImageHelper are affected by
261 * monitor color profiles. To account for this, we can draw the expected
262 * color in a simple shell and use that color as expected value instead.
263 *
264 * @param original
265 * original color to adjust
266 * @return adjusted color
267 */
268 public static RGB adjustExpectedColor(RGB original) {
269 if (!SWTUtils.isMac()) {
270 return original;
271 }
272
273 /* Create shell with desired color as background */
274 boolean painted[] = new boolean[1];
275 final Shell shell = UIThreadRunnable.syncExec(new Result<Shell>() {
276 @Override
277 public Shell run() {
278 Shell s = new Shell(Display.getDefault());
279 s.setSize(100, 100);
280 Color color = new Color(Display.getDefault(), original);
281 s.setBackground(color);
282 s.addPaintListener(new PaintListener() {
283 @Override
284 public void paintControl(PaintEvent e) {
285 painted[0] = true;
286 }
287 });
288 s.open();
289 return s;
290 }
291 });
292
293 /* Make sure the shell has been painted before getting the color */
294 new SWTBot().waitUntil(new DefaultCondition() {
295
296 @Override
297 public boolean test() throws Exception {
298 return painted[0];
299 }
300
301 @Override
302 public String getFailureMessage() {
303 return "Shell was not painted";
304 }
305 });
306
307 /* Get the color */
308 return UIThreadRunnable.syncExec(new Result<RGB>() {
309 @Override
310 public RGB run() {
311 shell.update();
312 RGB rgb = ImageHelper.grabImage(shell.getBounds()).getPixel(50, 50);
313 shell.close();
314 return rgb;
315 }
316 });
317 }
318 }
This page took 0.041279 seconds and 5 git commands to generate.