1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 package org.openoffice.test.common;
25 
26 import java.awt.AWTException;
27 import java.awt.Color;
28 import java.awt.Graphics;
29 import java.awt.Point;
30 import java.awt.Rectangle;
31 import java.awt.Robot;
32 import java.awt.Toolkit;
33 import java.awt.image.BufferedImage;
34 import java.io.File;
35 import java.io.FileInputStream;
36 import java.io.FileOutputStream;
37 import java.io.IOException;
38 
39 import javax.imageio.ImageIO;
40 
41 
42 /**
43  * Utilities related to graphics
44  *
45  */
46 public class GraphicsUtil {
47 	/**
48 	 * Error tolerance for rectangle
49 	 */
50 	static final double ERR_RANGLE_RECTANGLE = 0.0;
51 
52 	/**
53 	 * Error tolerance for ellipse
54 	 */
55 	static final double ERR_RANGLE_ELLIPSE = 1;
56 
57 	static Robot robot = null;
58 
59 	static {
60 		try {
61 			robot = new Robot();
62 		} catch (AWTException e) {
63 			e.printStackTrace();
64 		}
65 	}
66 
67 	/**
68 	 * Load a image file as buffered image
69 	 * @param file
70 	 * @return
71 	 */
loadImage(String file)72 	public static BufferedImage loadImage(String file) {
73 		BufferedImage image = null;
74 		FileInputStream in = null;
75 		try {
76 			in = new FileInputStream(file);
77 			image = ImageIO.read(in);
78 		} catch (Exception e) {
79 			e.printStackTrace();
80 		} finally {
81 			try {
82 				if (in != null)
83 					in.close();
84 			} catch (IOException e) {
85 				// ignore
86 			}
87 		}
88 		return image;
89 	}
90 
91 	/**
92 	 * Store a buffered image in the given file
93 	 *
94 	 * @param image
95 	 * @param imgFile
96 	 */
storeImage(BufferedImage image, String imgFile)97 	public static void storeImage(BufferedImage image, String imgFile) {
98 		File file = new File(imgFile);
99 		if (!file.getParentFile().exists())
100 			file.getParentFile().mkdirs();
101 		FileOutputStream fos = null;
102 		try {
103 			fos = new FileOutputStream(file);
104 			ImageIO.write(image, FileUtil.getFileExtName(imgFile), fos);
105 		} catch (Exception e) {
106 			//
107 			e.printStackTrace();
108 		} finally {
109 			try {
110 				if (fos != null)
111 					fos.close();
112 			} catch (IOException e) {
113 				//ignore
114 			}
115 		}
116 
117 	}
118 
getScreenRectangle()119 	public static Rectangle getScreenRectangle() {
120 		return new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
121 	}
122 
123 	/**
124 	 * Get a BufferedImage including the full current screen shot
125 	 * @return
126 	 */
screenshot()127 	public static BufferedImage screenshot() {
128 		return screenshot(null, null);
129 	}
130 
131 	/**
132 	 * Get a BufferedImage including the area of current screen shot
133 	 * @param area
134 	 * @return
135 	 */
screenshot(Rectangle area)136 	public static BufferedImage screenshot(Rectangle area) {
137 		return screenshot(null, area);
138 	}
139 
140 
141 	/**
142 	 *  Store the screen shot as a image file
143 	 * @param filename
144 	 */
screenShot(String filename)145 	public static BufferedImage screenShot(String filename) {
146 		return screenshot(filename, null);
147 	}
148 
149 	/**
150 	 * Store the specified area of the screen as a image file
151 	 * @param filename
152 	 * @param area
153 	 */
screenshot(String filename, Rectangle area)154 	public static BufferedImage screenshot(String filename, Rectangle area) {
155 		if (area == null)
156 			area = getScreenRectangle();
157 		BufferedImage capture = robot.createScreenCapture(area);
158 		if (filename != null)
159 			storeImage(capture, filename);
160 		return capture;
161 	}
162 
163 
164 	/**
165 	 * Find a rectangle in the screen.
166 	 * Note: The rectangle must be filled with solid color and the color must be different from the background color
167 	 *
168 	 * @param rect the area in the screen to search
169 	 * @param color the rectangle color.
170 	 * @return The found rectangle's location and size. If no rectangle is
171 	 *         found, return null
172 	 */
findRectangle(Rectangle rect, int color)173 	public static Rectangle findRectangle(Rectangle rect, int color) {
174 		return findRectangle(screenshot(rect), color);
175 	}
176 
177 	/**
178 	 * find a rectangle in an image
179 	 * Note: The rectangle must be filled with solid color and the color must be different from the background color
180 	 * @param src
181 	 * @param color
182 	 *            the rectangle color.
183 	 * @return The found rectangle's location and size. If no rectangle is
184 	 *         found, return null
185 	 */
findRectangle(BufferedImage src, int color)186 	public static Rectangle findRectangle(BufferedImage src, int color) {
187 		Rectangle re = new Rectangle();
188 
189 		BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(),
190 				BufferedImage.TYPE_INT_ARGB);
191 
192 		for (int x = 0; x < dst.getWidth(); x++) {
193 			for (int y = 0; y < dst.getHeight(); y++) {
194 				dst.setRGB(x, y, 0xFFFFFFFF);
195 			}
196 		}
197 
198 		Graphics g = dst.getGraphics();
199 		g.setColor(Color.black);
200 
201 		int sx = -1, sy = 0, ex = 0, ey = 0;
202 		for (int x = 0; x < src.getWidth(); x++) {
203 			for (int y = 0; y < src.getHeight(); y++) {
204 				int rgbSrc = src.getRGB(x, y);
205 				if (rgbSrc == color) {
206 					if (sx == -1) {
207 						sx = x;
208 						sy = y;
209 					}
210 					ex = x;
211 					ey = y;
212 				}
213 			}
214 		}
215 
216 		g.fillRect(sx, sy, ex - sx + 1, ey - sy + 1);
217 		// g.fillRect(0, 0, dst.getWidth(), dst.getHeight());
218 		int perimeter = 2 * (ex - sx + ey - sy);
219 		int errMax = (int)(perimeter * ERR_RANGLE_RECTANGLE);
220 
221 		if (!(detect(src, color, dst, 0xff000000, errMax) && detect(dst, 0xff000000,
222 				src, color, errMax)))
223 			return null;
224 		re.setBounds(sx, sy, ex - sx, ey - sy);
225 		if (re.width < 2 || re.height < 2) {
226 			return null;
227 		}
228 		return re;
229 	}
230 
231 
detect(BufferedImage src, int colorSrc, BufferedImage dst, int colorDst, double errMax)232 	protected static boolean detect(BufferedImage src, int colorSrc,
233 			BufferedImage dst, int colorDst, double errMax) {
234 		int errCount = 0;
235 		for (int x = 0; x < src.getWidth(); x++) {
236 			for (int y = 0; y < src.getHeight(); y++) {
237 				int rgbSrc = src.getRGB(x, y);
238 				if (rgbSrc == colorSrc) {
239 					int rgbDst = dst.getRGB(x, y);
240 					if (!(rgbDst == colorDst)) {
241 						errCount++;
242 					}
243 				}
244 			} // end for y
245 		}// end for x
246 		// System.out.println(errCount);
247 		if (errCount <= errMax)
248 			return true;
249 		return false;
250 	}
251 
252 
getBoundingBox(BufferedImage image, int color)253 	public static Rectangle getBoundingBox(BufferedImage image, int color) {
254 		return getBoundingBox(image, color, true);
255 	}
256 
getBoundingBox(BufferedImage image, int color, boolean include)257 	public static Rectangle getBoundingBox(BufferedImage image, int color, boolean include) {
258 		int w = image.getWidth();
259 		int h = image.getHeight();
260 		int left=w, top=h, right = -1, bottom = -1;
261 		for (int i = 0; i < w; i++) {
262 			for (int j = 0; j < h; j++) {
263 				if ((color == image.getRGB(i, j)) == include) {
264 					if (j < top)
265 						top = j;
266 					if (j > bottom)
267 						bottom = j;
268 					if (i < left)
269 						left = i;
270 					if (i > right)
271 						right = i;
272 				}
273 			}
274 
275 
276 		}
277 		if (right == -1)
278 			return null;
279 		return new Rectangle(left, top, right - left + 1, bottom - top + 1);
280 	}
281 
282 	/**
283 	 * Check if the rectangle in screen is filled with the given color
284 	 *
285 	 * @param color
286 	 * @param rect
287 	 * @return
288 	 */
isFilledWith(int color, Rectangle rect)289 	public static boolean isFilledWith(int color, Rectangle rect) {
290 		BufferedImage capture = screenshot(rect);
291 		int w = capture.getWidth();
292 		int h = capture.getHeight();
293 		for (int i = 0; i < w; i++) {
294 			for (int j = 0; j < h; j++) {
295 				if (color != capture.getRGB(i, j))
296 					return false;
297 			}
298 		}
299 
300 		return true;
301 	}
302 
303 	/**
304 	 * Find a image on the current screen
305 	 * @param image
306 	 * @param rect
307 	 * @return
308 	 */
findImage(BufferedImage image, Rectangle rect)309 	public static Point findImage(BufferedImage image, Rectangle rect) {
310 		BufferedImage capture = screenshot(rect);
311 		int w = capture.getWidth();
312 		int h = capture.getHeight();
313 		int iw = image.getWidth();
314 		int ih = image.getHeight();
315 
316 		for (int i = 0; i < w; i++) {
317 			out: for (int j = 0; j < h; j++) {
318 				for (int m = 0; m < iw; m++) {
319 					for (int n = 0; n < ih; n++) {
320 						if (image.getRGB(m, n) != capture.getRGB(i + m, j + n))
321 							continue out;
322 					}
323 				}
324 				return new Point(i, j);
325 			}
326 		}
327 
328 		return null;
329 	}
330 
331 	/**
332 	 * Find a color on the current screen
333 	 * @param color
334 	 * @param rect
335 	 * @return
336 	 */
findColor(int color, Rectangle rect)337 	public static Point findColor(int color, Rectangle rect) {
338 		BufferedImage capture = screenshot(rect);
339 		int w = capture.getWidth();
340 		int h = capture.getHeight();
341 		for (int i = 0; i < w; i++) {
342 			for (int j = 0; j < h; j++) {
343 				if (color == capture.getRGB(i, j))
344 					return new Point(i, j);
345 			}
346 		}
347 		return null;
348 	}
349 
350 
351 	/**
352 	 * Check if two BufferedImages equal
353 	 *
354 	 * @param expected
355 	 * @param actual
356 	 * @return
357 	 */
imageEquals(BufferedImage expected, BufferedImage actual)358 	public static boolean imageEquals(BufferedImage expected, BufferedImage actual) {
359 		if (expected == null || actual == null)
360 			return false;
361 
362 		if (expected.getHeight() != actual.getHeight() || expected.getWidth() != actual.getWidth())
363 			return false;
364 
365 		for (int y = 0; y < expected.getHeight(); ++y) {
366 			for (int x = 0; x < expected.getWidth(); ++x) {
367 				if (expected.getRGB(x, y) != actual.getRGB(x, y))
368 					return false;
369 			}
370 		}
371 
372 		return true;
373 	}
374 
375 	/**
376 	 * Check if two image files equal
377 	 *
378 	 * @param expectedImage
379 	 * @param actualImage
380 	 * @return
381 	 */
imageEquals(String expectedImage, String actualImage)382 	public static boolean imageEquals(String expectedImage, String actualImage) {
383 		BufferedImage expected = loadImage(expectedImage), actual = loadImage(actualImage);
384 		return imageEquals(expected, actual);
385 	}
386 
387 }
388