-
Notifications
You must be signed in to change notification settings - Fork 3
/
JavaRobotPainter.java
289 lines (250 loc) · 9.38 KB
/
JavaRobotPainter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
package javaRobotPainter;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import javax.imageio.ImageIO;
/**
* When this class is instantiated a bot is created, that draws a specified picture in Microsoft Paint.
* This program has only been tested on Windows Vista with MS Paint version 6.0.
* <br /><br />
* NOTE: The delay between each operation and the time to start MS Paint may have to be adjusted.
* The shortcut key to maximize the window, in {@code initMsPaint()}, may also have to be changed.
* It works with the Norwegian version of Vista.
*
* @author teaprog
* @version 1.0, 30/04/12
*/
public class JavaRobotPainter {
/** The size of the screen. */
private Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
/** Where to start painting on the x-axis. */
private static final int START_POSITION_X = 61;
/** Where to start painting on the y-axis. */
private static final int START_POSITION_Y = 94;
/** The size of the rectangles being drawn. */
private int rectangleSize = 5;
/** The colors this bot has at its disposal. */
private ArrayList<Color> availableColors = new ArrayList<Color>();
/** The position for each color. */
private HashMap<Color, Point> colorPosition = new HashMap<Color, Point>();
/** The colors available. */
private enum ColorNr {COLOR_ONE, COLOR_TWO};
/** The current color selected. */
private ColorNr currentColor = ColorNr.COLOR_ONE;
/** The selected colors. (By default color one is black and color two is white) */
private Color[] selectedColors = new Color[]{new Color(0, 0, 0), new Color(255, 255, 255)};
/** The image that is going to be drawn in MS Paint. */
private BufferedImage image;
/** The process of MS Paint. */
private Process msPaint;
/** The robot that is going to draw the image. */
private Robot robot;
/** The delay between each bot operation. This may have to be adjusted for different computers. */
private int robotDelay = 1;
/** The time that is required to start MS Paint. This may have to be adjusted for different computers. */
private long timeRequiredToStartMsPaint = 1000;
/**
* Executes MS Paint and starts drawing.
*
* @throws AWTException
* @throws IOException
*/
public JavaRobotPainter() throws AWTException, IOException {
image = ImageIO.read(new File("img/image.jpg"));
robot = new Robot();
robot.setAutoDelay(robotDelay);
initMsPaint();
startPainting();
}
/**
* Executes MS Paint, sets the attributes of the picture, initializes the available colors
* and chooses a filled rectangle as drawing utility.
*
* @throws IOException
* @throws InterruptedException
*/
private void initMsPaint() throws IOException {
msPaint = Runtime.getRuntime().exec("mspaint.exe");
// wait until mspaint.exe starts
long time = System.currentTimeMillis();
while(System.currentTimeMillis() - time < timeRequiredToStartMsPaint);
// maximize the window
// NOTE: the shortcut may vary, depending on the language your windows version has
robot.keyPress(KeyEvent.VK_ALT);
robot.keyPress(KeyEvent.VK_SPACE);
robot.keyPress(KeyEvent.VK_M);
robot.keyRelease(KeyEvent.VK_M);
robot.keyRelease(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_ALT);
setAttributes(image.getWidth(), image.getHeight());
initColors();
chooseFilledRectangle();
}
/**
* Sets the canvas size in MS Paint.
*
* @param width - The width of the image to be drawn.
* @param height - The height of the image to be drawn.
*/
private void setAttributes(int width, int height) {
// if the width or height is too large then exit program
if (width + START_POSITION_X > screenSize.width || height + START_POSITION_Y > screenSize.height) {
System.out.println("The width or height is too large!");
msPaint.destroy();
System.exit(0);
}
// open attributes window
robot.keyPress(KeyEvent.VK_CONTROL);
robot.keyPress(KeyEvent.VK_E);
robot.keyRelease(KeyEvent.VK_E);
robot.keyRelease(KeyEvent.VK_CONTROL);
// set width and height
setCliboard(new Integer(width).toString());
pasteDataFromClipboard();
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);
setCliboard(new Integer(height).toString());
pasteDataFromClipboard();
robot.keyPress(KeyEvent.VK_ENTER);
robot.keyRelease(KeyEvent.VK_ENTER);
}
/**
* Initializes the available colors. That is the 28 colors in the color panel above the canvas.
*/
private void initColors() {
int interval = 16; // The distance the mouse is going to be moved from each color.
int startX = 95; // The x position to the first color in the panel.
int startY = 55; // The y position to the first color in the panel.
// Finds the colors and adds them to avaibleColors, and puts the color position in colorPosition.
for (int i = 0; i < 14; i++) {
for (int j = 0; j < 2; j++) {
Color color = robot.getPixelColor(startX + i*interval, startY + j*interval);
availableColors.add(color);
colorPosition.put(color, new Point(startX + i*interval, startY + j*interval));
}
}
}
/**
* Sets a filled rectangle as drawing tool.
*/
private void chooseFilledRectangle() {
// choose rectangle
robot.mouseMove(15, 210);
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
// choose filled
robot.mouseMove(15, 300);
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
}
/**
* Presses the keys "Ctrl" and "V" to paste the content from the clipboard.
*/
private void pasteDataFromClipboard() {
robot.keyPress(KeyEvent.VK_CONTROL);
robot.keyPress(KeyEvent.VK_V);
robot.keyRelease(KeyEvent.VK_V);
robot.keyRelease(KeyEvent.VK_CONTROL);
}
/**
* Sets the cliboard to the specified string.
*
* @param string - The string to be copied into clipboard.
*/
public void setCliboard(String string) {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(new StringSelection(string), null);
}
/**
* Starts painting the image in MS Paint.
* If the user moves the mouse then the program terminates.
*/
private void startPainting() {
Point point = new Point(MouseInfo.getPointerInfo().getLocation().x, MouseInfo.getPointerInfo().getLocation().y);
for (int i = START_POSITION_Y; i-START_POSITION_Y < image.getHeight(); i += rectangleSize) {
for (int j = START_POSITION_X; j-START_POSITION_X < image.getWidth(); j += rectangleSize) {
// if user moves the mouse then the program terminates
if (point.x != MouseInfo.getPointerInfo().getLocation().x || point.y != MouseInfo.getPointerInfo().getLocation().y)
System.exit(0);
setColor(new Color(image.getRGB(j-START_POSITION_X, i-START_POSITION_Y)));
int mouseButton = (currentColor == ColorNr.COLOR_ONE) ? InputEvent.BUTTON1_MASK : InputEvent.BUTTON3_MASK;
robot.mouseMove(j, i);
robot.mousePress(mouseButton);
robot.mouseMove(j + rectangleSize, i + rectangleSize);
robot.mouseRelease(mouseButton);
// set the position of the last point where the mouse was
point.setLocation(j + rectangleSize, i + rectangleSize);
}
}
}
/**
* Changes the color if necessary, i.e. if the color is not in {@code selectedColors[0]} or {@code selectedColors[1]}.
*
* @param pixelColor - The pixel color, from the image, where the program has to find an approximated color in {@code availableColors}.
*/
private void setColor(Color pixelColor) {
Color mostAccurateColor = findMostAccurateColorAvailable(pixelColor);
if (selectedColors[0].equals(mostAccurateColor)) {
currentColor = ColorNr.COLOR_ONE;
}
else if (selectedColors[1].equals(mostAccurateColor)) {
currentColor = ColorNr.COLOR_TWO;
}
else { // If the color is not in selectedColors, then select new color from the panel.
Point p = colorPosition.get(mostAccurateColor);
int mouseButton;
if (currentColor == ColorNr.COLOR_ONE) {
mouseButton = InputEvent.BUTTON3_MASK;
currentColor = ColorNr.COLOR_TWO;
selectedColors[1] = mostAccurateColor;
}
else {
mouseButton = InputEvent.BUTTON1_MASK;
currentColor = ColorNr.COLOR_ONE;
selectedColors[0] = mostAccurateColor;
}
robot.mouseMove(p.x, p.y);
robot.mousePress(mouseButton);
robot.mouseRelease(mouseButton);
}
}
/**
* Finds the most accurate color that is available in {@code availableColors}.
*
* @param pixelColor - The pixel color in the image.
* @return the most accurate color from {@code availableColors}
*/
private Color findMostAccurateColorAvailable(Color pixelColor) {
double minError = Double.MAX_VALUE;
int bestColorIndex = 0;
for (int i = 0; i < availableColors.size(); i++) {
int r1 = availableColors.get(i).getRed();
int g1 = availableColors.get(i).getGreen();
int b1 = availableColors.get(i).getBlue();
int r2 = pixelColor.getRed();
int g2 = pixelColor.getGreen();
int b2 = pixelColor.getBlue();
// 2-norm distance
double error = Math.sqrt((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2));
if (error < minError) {
minError = error;
bestColorIndex = i;
}
}
return availableColors.get(bestColorIndex);
}
/**
* The main method.
*
* @param args
* @throws AWTException
* @throws IOException
* @throws InterruptedException
*/
public static void main(String[] args) throws AWTException, IOException {
new JavaRobotPainter();
}
}