/*
* Copyright 2017 Laszlo Balazs-Csiki
*
* This file is part of Pixelitor. Pixelitor is free software: you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License, version 3 as published by the Free
* Software Foundation.
*
* Pixelitor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Pixelitor. If not, see <http://www.gnu.org/licenses/>.
*/
package pixelitor.utils.test;
import com.bric.util.JVM;
import pixelitor.Build;
import pixelitor.ChangeReason;
import pixelitor.Composition;
import pixelitor.ConsistencyChecks;
import pixelitor.colors.FgBgColors;
import pixelitor.filters.Fade;
import pixelitor.filters.Filter;
import pixelitor.filters.FilterAction;
import pixelitor.filters.FilterUtils;
import pixelitor.filters.FilterWithParametrizedGUI;
import pixelitor.filters.Invert;
import pixelitor.filters.RandomFilter;
import pixelitor.filters.animation.Interpolation;
import pixelitor.filters.animation.TweenAnimation;
import pixelitor.filters.comp.EnlargeCanvas;
import pixelitor.filters.comp.Flip;
import pixelitor.filters.comp.Rotate;
import pixelitor.filters.gui.FilterWithGUI;
import pixelitor.filters.gui.ParamSet;
import pixelitor.filters.gui.ParamSetState;
import pixelitor.filters.painters.TextSettings;
import pixelitor.gui.Desktop;
import pixelitor.gui.GlobalKeyboardWatch;
import pixelitor.gui.ImageComponent;
import pixelitor.gui.ImageComponents;
import pixelitor.gui.PixelitorWindow;
import pixelitor.gui.utils.GUIUtils;
import pixelitor.history.History;
import pixelitor.layers.AddLayerMaskAction;
import pixelitor.layers.AddNewLayerAction;
import pixelitor.layers.AdjustmentLayer;
import pixelitor.layers.BlendingMode;
import pixelitor.layers.ContentLayer;
import pixelitor.layers.DeleteActiveLayerAction;
import pixelitor.layers.Drawable;
import pixelitor.layers.ImageLayer;
import pixelitor.layers.Layer;
import pixelitor.layers.LayerMask;
import pixelitor.layers.TextLayer;
import pixelitor.menus.edit.CopyAction;
import pixelitor.menus.edit.CopySource;
import pixelitor.menus.edit.PasteAction;
import pixelitor.menus.edit.PasteDestination;
import pixelitor.menus.view.ShowHideAllAction;
import pixelitor.menus.view.ShowHideHistogramsAction;
import pixelitor.menus.view.ShowHideLayersAction;
import pixelitor.menus.view.ShowHideStatusBarAction;
import pixelitor.menus.view.ShowHideToolsAction;
import pixelitor.menus.view.ZoomLevel;
import pixelitor.selection.SelectionActions;
import pixelitor.tools.Tool;
import pixelitor.tools.ToolSettingsPanelContainer;
import pixelitor.tools.Tools;
import pixelitor.utils.AppPreferences;
import pixelitor.utils.MemoryInfo;
import pixelitor.utils.Messages;
import pixelitor.utils.Utils;
import javax.swing.*;
import java.awt.AWTException;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import static pixelitor.filters.comp.Flip.Direction.HORIZONTAL;
import static pixelitor.filters.comp.Flip.Direction.VERTICAL;
import static pixelitor.filters.comp.Rotate.SpecialAngle.ANGLE_180;
import static pixelitor.filters.comp.Rotate.SpecialAngle.ANGLE_270;
import static pixelitor.filters.comp.Rotate.SpecialAngle.ANGLE_90;
/**
* An automatic test using java.awt.Robot.
* Can be dangerous because of the random native mouse events that can control other apps as well if they escape.
*/
public class RandomGUITest {
final static Random rand = new Random();
private static final Tool preferredTool = null; // set to null to select random tools
private static final boolean singleImageTest = false;
private static final boolean noHideShow = true; // no view operations if set to true
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
private static boolean continueRunning = true;
private static final WeightedCaller weightedCaller = new WeightedCaller();
private static final boolean PRINT_MEMORY = false;
private static KeyStroke stopKeyStroke;
private static int numPastedImages = 0;
private static boolean running = false;
/**
* Utility class with static methods
*/
private RandomGUITest() {
}
public static void runTest() {
if (Build.CURRENT != Build.DEVELOPMENT) {
Messages.showError("Error", "Build is not DEVELOPMENT");
return;
}
running = true;
PixelitorWindow.getInstance().setAlwaysOnTop(true);
new PixelitorEventListener().register();
numPastedImages = 0;
// make sure it can be stopped by pressing the u key
stopKeyStroke = KeyStroke.getKeyStroke('w');
GlobalKeyboardWatch.addKeyboardShortCut(stopKeyStroke, "stopTest", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("RandomGUITest: \"" + stopKeyStroke + "\" pressed");
continueRunning = false;
}
});
continueRunning = true;
// and the j key exits the app
KeyStroke exitKeyStroke = KeyStroke.getKeyStroke('j');
GlobalKeyboardWatch.addKeyboardShortCut(exitKeyStroke, "exit", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("RandomGUITest: exiting app because '" + exitKeyStroke.getKeyChar() + "' was pressed");
System.exit(1);
}
});
System.out.printf("RandomGUITest.runTest CALLED at %s, the '%s' key stops, the '%s' key exits.%n",
DATE_FORMAT.format(new Date()),
stopKeyStroke.getKeyChar(), exitKeyStroke.getKeyChar());
Robot r = null;
try {
r = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}
setupWeightedCaller(r);
Point p = generateRandomPoint();
r.mouseMove(p.x, p.y);
log("initial splash");
SplashImageCreator.createSplashImage();
randomCopy(); // ensure an image is on the clipboard
SwingWorker<Void, Void> worker = createOneRoundSwingWorker(r, true);
worker.execute();
}
public static boolean isRunning() {
return running;
}
public static void stop() {
continueRunning = false;
PixelitorWindow.getInstance().setAlwaysOnTop(false);
}
private static SwingWorker<Void, Void> createOneRoundSwingWorker(Robot r, boolean forever) {
return new SwingWorker<Void, Void>() {
@Override
public Void doInBackground() {
int numTests = 8000;
int onePercent = numTests / 100;
int max = forever ? Integer.MAX_VALUE : numTests;
for (int i = 0; i < max; i++) {
if ((i % onePercent) == 0) {
int percent = 100 * i / numTests;
System.out.print(percent + "% ");
if (PRINT_MEMORY) {
String memoryString = new MemoryInfo().toString();
System.out.println(memoryString);
} else {
if (((percent + 1) % 20) == 0) {
System.out.println();
}
}
}
if (!GUIUtils.appIsActive()) {
if (JVM.isWindows) { // might be some "upgrade to windows 10" window
tryToActivateWindow(3);
}
if (!GUIUtils.appIsActive()) {
System.out.println("\nRandomGUITest app focus lost.");
cleanUp();
break;
}
}
if (!continueRunning) {
System.out.println("\nRandomGUITest stopped.");
cleanUp();
break;
}
r.delay(100 + rand.nextInt(400));
Runnable runnable = () -> {
try {
weightedCaller.callRandomAction();
Composition comp = ImageComponents.getActiveComp().orElseThrow(() ->
new IllegalStateException("no active composition"));
ConsistencyChecks.checkAll(
comp,
true);
} catch (Throwable e) {
Messages.showException(e);
}
};
try {
EventQueue.invokeAndWait(runnable);
} catch (InterruptedException | InvocationTargetException e) {
Messages.showException(e);
}
}
System.out.println("\nRandomGUITest.runTest FINISHED at " + new Date());
cleanUp();
Toolkit.getDefaultToolkit().beep();
return null;
} // end of doInBackground()
};
}
private static void tryToActivateWindow(int attempts) {
if (attempts <= 0) {
return;
}
System.out.println("RandomGUITest: trying to regain window focus");
try {
SwingUtilities.invokeAndWait(() -> {
PixelitorWindow pw = PixelitorWindow.getInstance();
pw.toFront();
pw.repaint();
});
} catch (InterruptedException | InvocationTargetException e) {
e.printStackTrace();
}
if (GUIUtils.appIsActive()) { // success
return;
} else {
Utils.sleep(1, TimeUnit.SECONDS);
tryToActivateWindow(attempts - 1);
}
}
private static Point generateRandomPoint() {
Container contentPane = PixelitorWindow.getInstance().getContentPane();
Dimension winDim = contentPane.getSize();
Point locationOnScreen = contentPane.getLocationOnScreen();
int safetyGapX = 5;
int safetyGapY = 5;
int minX = locationOnScreen.x + safetyGapX;
int minY = locationOnScreen.y + safetyGapY;
int maxX = locationOnScreen.x + winDim.width - 2 * minX;
int maxY = locationOnScreen.y + winDim.height - 2 * minY;
Point randomPoint = new Point(minX + rand.nextInt(maxX), minY + rand.nextInt(maxY));
return randomPoint;
}
private static void cleanUp() {
AppPreferences.WorkSpace.setDefault(PixelitorWindow.getInstance());
running = false;
}
private static void randomResize() {
log("resize");
FilterTests.randomResize();
}
private static void log(String msg) {
Events.postRandomTestEvent(msg);
}
private static void randomMove(Robot r) {
Point randomPoint = generateRandomPoint();
int x = randomPoint.x;
int y = randomPoint.y;
log("move to (" + x + ", " + y + ')');
r.mouseMove(x, y);
}
private static void randomDrag(Robot r) {
Point randomPoint = generateRandomPoint();
int x = randomPoint.x;
int y = randomPoint.y;
Tool tool = Tools.getCurrent();
String stateInfo = tool.getStateInfo();
if (stateInfo == null) {
stateInfo = "";
} else {
stateInfo = "(" + stateInfo + ")";
}
log(tool.getName() + " Tool " + stateInfo + " drag to (" + x + ", " + y + ')');
r.mousePress(InputEvent.BUTTON1_MASK);
r.mouseMove(x, y);
r.mouseRelease(InputEvent.BUTTON1_MASK);
}
private static void click(Robot r) {
log("click");
r.mousePress(InputEvent.BUTTON1_MASK);
r.delay(50);
r.mouseRelease(InputEvent.BUTTON1_MASK);
}
private static void randomRightClick(Robot r) {
log("right click");
r.mousePress(InputEvent.BUTTON3_MASK);
r.delay(50);
r.mouseRelease(InputEvent.BUTTON3_MASK);
}
private static void randomColors() {
log("randomize colors");
FgBgColors.randomize();
}
private static void randomFilter() {
if (!ImageComponents.getActiveIC().activeIsDrawable()) {
return;
}
Filter f = FilterUtils.getRandomFilter(filter ->
(!(filter instanceof Fade)) &&
(!(filter instanceof RandomFilter)));
String filterName = f.getName();
log("filter: " + filterName);
long runCountBefore = Filter.runCount;
f.randomizeSettings();
Drawable dr = ImageComponents.getActiveDrawableOrNull();
if (f instanceof FilterWithGUI) {
dr.startPreviewing();
try {
f.randomizeSettings();
f.execute(dr, ChangeReason.OP_PREVIEW);
} catch (Throwable e) {
BufferedImage src = dr.getFilterSourceImage();
if (f instanceof FilterWithParametrizedGUI) {
ParamSet paramSet = ((FilterWithParametrizedGUI) f).getParamSet();
System.out.println(String.format(
"RandomGUITest::randomFilter: name = %s, width = %d, height = %d, params = %s",
filterName, src.getWidth(), src.getHeight(), paramSet.toString()));
} else {
System.out.println(String.format(
"RandomGUITest::randomFilter: name = %s, width = %d, height = %d",
filterName, src.getWidth(), src.getHeight()));
}
throw e;
}
if (Math.random() > 0.3) {
dr.onDialogAccepted(filterName);
} else {
dr.onDialogCanceled();
}
} else {
BufferedImage src = dr.getFilterSourceImage();
try {
f.execute(dr, ChangeReason.OP_WITHOUT_DIALOG);
} catch (Throwable e) {
System.out.println(String.format(
"RandomGUITest::randomFilter: name = %s, width = %d, height = %d",
filterName, src.getWidth(), src.getHeight()));
throw e;
}
}
long runCountAfter = Filter.runCount;
if (runCountAfter != (runCountBefore + 1)) {
throw new IllegalStateException("runCountBefore = " + runCountBefore + ", runCountAfter = " + runCountAfter);
}
}
private static void randomTween() {
Drawable dr = ImageComponents.getActiveDrawableOrNull();
if (dr == null) {
return;
}
long runCountBefore = Filter.runCount;
FilterWithParametrizedGUI filter = getRandomTweenFilter();
String filterName = filter.getName();
log("tween: " + filterName);
TweenAnimation animation = new TweenAnimation();
animation.setFilter(filter);
Interpolation[] interpolations = Interpolation.values();
Interpolation randomInterpolation = interpolations[(int) (Math.random() * interpolations.length)];
animation.setInterpolation(randomInterpolation);
ParamSet paramSet = filter.getParamSet();
paramSet.randomize();
animation.copyInitialStateFromCurrent();
paramSet.randomize();
animation.copyFinalStateFromCurrent();
double randomTime = Math.random();
ParamSetState intermediateState = animation.tween(randomTime);
paramSet.setState(intermediateState);
// execute everything without showing a modal dialog
dr.tweenCalculatingStarted();
PixelitorWindow busyCursorParent = PixelitorWindow.getInstance();
try {
filter.executeWithBusyCursor(dr, ChangeReason.OP_PREVIEW, busyCursorParent);
} catch (Throwable e) {
BufferedImage src = dr.getFilterSourceImage();
String msg = String.format(
"Exception in random tween: filter name = %s, srcWidth = %d, srcHeight = %d, isMaskEditing = %b, params = %s",
filterName, src.getWidth(), src.getHeight(), dr.isMaskEditing(), paramSet.toString());
throw new IllegalStateException(msg, e);
}
dr.tweenCalculatingEnded();
long runCountAfter = Filter.runCount;
if (runCountAfter != (runCountBefore + 1)) {
throw new IllegalStateException("runCountBefore = " + runCountBefore + ", runCountAfter = " + runCountAfter);
}
}
private static void randomFitToScreen() {
if (Math.random() > 0.1) {
log("fitActiveToScreen");
ImageComponents.fitActiveToScreen();
} else {
log("fitActiveToActualPixels");
ImageComponents.fitActiveToActualPixels();
}
}
private static final int[] keyEvents = {KeyEvent.VK_1, KeyEvent.VK_A,
KeyEvent.VK_ENTER, KeyEvent.VK_ESCAPE, KeyEvent.VK_BACK_SPACE,
KeyEvent.VK_M, KeyEvent.VK_C, KeyEvent.VK_Z,
KeyEvent.VK_G, KeyEvent.VK_B,
KeyEvent.VK_E, KeyEvent.VK_I,
KeyEvent.VK_S, KeyEvent.VK_Q,
KeyEvent.VK_R, KeyEvent.VK_D,
KeyEvent.VK_X, KeyEvent.VK_H,
KeyEvent.VK_P, KeyEvent.VK_B,
KeyEvent.VK_ALT, KeyEvent.VK_TAB,
KeyEvent.VK_COMMA, KeyEvent.VK_HOME,
KeyEvent.VK_RIGHT, KeyEvent.VK_LEFT, KeyEvent.VK_UP, KeyEvent.VK_DOWN
// , KeyEvent.VK_V, // do not trigger move tool
};
private static void randomKey(Robot r) {
int randomIndex = rand.nextInt(keyEvents.length);
int keyEvent = keyEvents[randomIndex];
log("key = " + keyEvent);
pressKey(r, keyEvent);
}
private static void pressKey(Robot r, int keyEvent) {
r.keyPress(keyEvent);
r.delay(50);
r.keyRelease(keyEvent);
}
private static void pressCtrlKey(Robot r, int keyEvent) {
r.keyPress(KeyEvent.VK_CONTROL);
r.delay(50);
r.keyPress(keyEvent);
r.delay(50);
r.keyRelease(keyEvent);
r.delay(50);
r.keyRelease(KeyEvent.VK_CONTROL);
}
private static void randomZoom() {
ImageComponent ic = ImageComponents.getActiveIC();
if (ic != null) {
ZoomLevel randomZoomLevel = null;
double percentValue = 0;
while (percentValue < 49) {
randomZoomLevel = ZoomLevel.getRandomZoomLevel(rand);
percentValue = randomZoomLevel.getPercentValue();
}
log("zoom zoomLevel = " + randomZoomLevel);
if (rand.nextBoolean()) {
ic.setZoom(randomZoomLevel, false, null);
} else {
Point mousePos = getRandomPointOnIC(ic);
ic.setZoom(randomZoomLevel, false, mousePos);
}
}
}
private static Point getRandomPointOnIC(ImageComponent ic) {
int randX = rand.nextInt(ic.getWidth());
int randY = rand.nextInt(ic.getWidth());
return new Point(randX, randY);
}
private static void randomZoomOut() {
log("zoomOut");
ImageComponent ic = ImageComponents.getActiveIC();
if (ic != null) {
ZoomLevel newZoom = ic.getZoomLevel().zoomOut();
if (rand.nextBoolean()) {
ic.setZoom(newZoom, false, null);
} else {
Point mousePos = getRandomPointOnIC(ic);
ic.setZoom(newZoom, false, mousePos);
}
}
}
private static void repeat() {
log("repeat");
PixelitorWindow pw = PixelitorWindow.getInstance();
pw.dispatchEvent(new KeyEvent(pw, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), KeyEvent.CTRL_MASK, KeyEvent.VK_F, 'F'));
}
private static void randomUndoRedo() {
if (History.canUndo()) {
log("undo");
History.undo();
// for some reason, redo might not be available even if we are right
// after an undo which didn't throw a CannotUndoException
// This is not a problem in Pixelitor, because the RedoMenuItem
// also checks History.canRedo() after an undo
if (!History.canRedo()) {
return;
}
// assert History.canRedo();
if (rand.nextInt(10) > 3) {
log("redo");
History.redo();
}
}
}
private static void randomCrop() {
boolean enabled = SelectionActions.areEnabled();
if (enabled) {
log("crop");
SelectionActions.getCropAction().actionPerformed(new ActionEvent("", 0, ""));
}
}
private static void randomFade() {
if (!History.canFade()) {
return;
}
int opacity = rand.nextInt(100);
log("fade, opacity = " + opacity + " %");
Fade fade = new Fade();
fade.setOpacity(opacity);
Drawable dr = ImageComponents.getActiveDrawableOrNull();
fade.execute(dr, ChangeReason.OP_WITHOUT_DIALOG);
}
private static void randomizeToolSettings() {
log("randomize tool settings");
ToolSettingsPanelContainer.INSTANCE.randomizeToolSettings();
}
private static void arrangeWindows() {
double r = Math.random();
if (r < 0.8) {
log("arrange windows - tile");
Desktop.INSTANCE.tileWindows();
} else {
log("arrange windows - cascade");
Desktop.INSTANCE.cascadeWindows();
}
}
private static void deselect() {
if (SelectionActions.areEnabled()) {
log("deselect");
SelectionActions.getDeselectAction().actionPerformed(new ActionEvent("", 0, ""));
}
}
private static void showHideSelection(Robot robot) {
log("showHideSelection");
pressCtrlKey(robot, KeyEvent.VK_H);
}
private static void layerToCanvasSize() {
log("layer to canvas size");
ImageComponents.getActiveCompOrNull().activeLayerToCanvasSize();
}
private static void invertSelection() {
if (SelectionActions.areEnabled()) {
log("invert selection");
SelectionActions.getInvertSelectionAction().actionPerformed(new ActionEvent("", 0, ""));
}
}
private static void traceWithCurrentBrush() {
if (SelectionActions.areEnabled()) {
log("trace with current brush");
SelectionActions.getTraceWithBrush().actionPerformed(new ActionEvent("", 0, ""));
}
}
private static void traceWithCurrentEraser() {
if (SelectionActions.areEnabled()) {
log("trace with current eraser");
SelectionActions.getTraceWithEraser().actionPerformed(new ActionEvent("", 0, ""));
}
}
private static void randomRotateFlip() {
int r = rand.nextInt(5);
Action action = null;
switch (r) {
case 0:
log("rotate 90 CW");
action = new Rotate(ANGLE_90);
break;
case 1:
log("rotate 180");
action = new Rotate(ANGLE_180);
break;
case 2:
log("rotate 90 CCW");
action = new Rotate(ANGLE_270);
break;
case 3:
log("flip horizontal");
action = new Flip(HORIZONTAL);
break;
case 4:
log("flip vertical");
action = new Flip(VERTICAL);
break;
}
action.actionPerformed(new ActionEvent("", 0, ""));
}
private static void layerOrderChange() {
Composition comp = ImageComponents.getActiveCompOrNull();
int r = rand.nextInt(6);
switch (r) {
case 0:
log("layer order change: active to top");
comp.moveActiveLayerToTop();
break;
case 1:
log("layer order change: active to bottom");
comp.moveActiveLayerToBottom();
break;
case 2:
log("layer order change: selection up");
comp.moveLayerSelectionUp();
break;
case 3:
log("layer order change: selection down");
comp.moveLayerSelectionDown();
break;
case 4:
log("layer order change: active up");
comp.moveActiveLayerUp();
break;
case 5:
log("layer order change: active down");
comp.moveActiveLayerDown();
break;
}
}
private static void layerMerge() {
Composition comp = ImageComponents.getActiveCompOrNull();
if (rand.nextBoolean()) {
log("layer merge down");
comp.mergeDown(true);
} else {
log("layer flatten image");
comp.flattenImage(true);
}
}
private static void layerAddDelete() {
if (rand.nextBoolean()) {
if (AddNewLayerAction.INSTANCE.isEnabled()) {
log("add new layer");
AddNewLayerAction.INSTANCE.actionPerformed(new ActionEvent("", 0, ""));
}
} else {
if (DeleteActiveLayerAction.INSTANCE.isEnabled()) {
log("delete active layer");
DeleteActiveLayerAction.INSTANCE.actionPerformed(new ActionEvent("", 0, ""));
}
}
}
private static void randomHideShow() {
if (noHideShow) {
return;
}
int r = rand.nextInt(5);
if (r == 0) {
log("show-hide histograms");
new ShowHideHistogramsAction().actionPerformed(new ActionEvent("", 0, ""));
} else if (r == 1) {
log("show-hide layers");
new ShowHideLayersAction().actionPerformed(new ActionEvent("", 0, ""));
} else if (r == 2) {
log("show-hide tools");
new ShowHideToolsAction().actionPerformed(new ActionEvent("", 0, ""));
} else if (r == 4) {
log("show-hide status bar");
new ShowHideStatusBarAction().actionPerformed(new ActionEvent("", 0, ""));
} else if (r == 5) {
log("show-hide all");
ShowHideAllAction.INSTANCE.actionPerformed(new ActionEvent("", 0, ""));
}
}
private static void randomCopy() {
if (rand.nextBoolean()) {
log("copy layer");
new CopyAction(CopySource.LAYER).actionPerformed(new ActionEvent("", 0, ""));
} else {
log("copy composite");
new CopyAction(CopySource.COMPOSITE).actionPerformed(new ActionEvent("", 0, ""));
}
}
private static void randomPaste() {
if (numPastedImages > 3) {
return;
}
int r = rand.nextInt(10);
if (r == 0) {
if (singleImageTest) {
return;
}
log("paste as new image");
new PasteAction(PasteDestination.NEW_IMAGE).actionPerformed(new ActionEvent("", 0, ""));
numPastedImages++;
} else if (r == 1) {
log("paste as new layer");
new PasteAction(PasteDestination.NEW_LAYER).actionPerformed(new ActionEvent("", 0, ""));
numPastedImages++;
}
}
private static void randomChangeLayerOpacityOrBlending() {
Layer layer = ImageComponents.getActiveLayerOrNull();
if (rand.nextBoolean()) {
float opacity = layer.getOpacity();
float f = rand.nextFloat();
if (f > opacity) {
// always increase
log("increase opacity");
layer.setOpacity(f, true, true, true);
} else if (rand.nextFloat() > 0.75) { // sometimes decrease
log("decrease opacity");
layer.setOpacity(f, true, true, true);
}
} else {
log("change layer blending mode");
BlendingMode[] blendingModes = BlendingMode.values();
BlendingMode randomBlendingMode = blendingModes[rand.nextInt(blendingModes.length)];
layer.setBlendingMode(randomBlendingMode, true, true, true);
}
}
private static void randomChangeLayerVisibility() {
Layer layer = ImageComponents.getActiveLayerOrNull();
boolean visible = layer.isVisible();
if (rand.nextBoolean()) {
if (!visible) {
log("show layer");
layer.setVisible(true, true);
}
} else {
if (visible) {
if (rand.nextFloat() > 0.8) { // sometimes hide
log("hide layer");
layer.setVisible(false, true);
}
}
}
}
private static void randomTool() {
Tool tool;
if (preferredTool != null) {
tool = preferredTool;
} else {
tool = Tools.getRandomTool(rand);
// The move tool can cause out of memory errors, so don't test it
if (tool == Tools.MOVE) {
return;
}
}
Tool currentTool = Tools.getCurrent();
if (currentTool != tool) {
log("tool click on " + currentTool);
tool.getButton().doClick();
}
}
private static void randomException() {
// logRobotEvent("random exception");
// throw new IllegalStateException("test");
}
private static void randomNewTextLayer() {
log("new text layer");
Composition comp = ImageComponents.getActiveCompOrNull();
TextLayer textLayer = new TextLayer(comp);
TextSettings randomSettings = TextSettings.createRandomSettings(rand);
textLayer.setSettings(randomSettings);
comp.addLayer(textLayer, true, "New Random Text Layer", true, false);
textLayer.setName(randomSettings.getText(), true);
}
private static void randomTextLayerRasterize() {
Layer layer = ImageComponents.getActiveLayerOrNull();
if (layer instanceof TextLayer) {
log("text layer rasterize");
((TextLayer) layer).replaceWithRasterized();
}
}
private static void randomNewAdjustmentLayer() {
log("new adj layer");
Composition comp = ImageComponents.getActiveCompOrNull();
AdjustmentLayer adjustmentLayer = new AdjustmentLayer(comp, "Invert", new Invert());
comp.addLayer(adjustmentLayer, true, "New Random Adj Layer", true, false);
}
private static void randomSetLayerMaskEditMode() {
Layer layer = ImageComponents.getActiveLayerOrNull();
if (!layer.hasMask()) {
return;
}
PixelitorWindow pw = PixelitorWindow.getInstance();
double d = rand.nextDouble();
int keyCode;
char keyChar;
if (d < 0.33) {
keyCode = KeyEvent.VK_1;
keyChar = '1';
log("Ctrl-1");
} else if (d < 0.66) {
keyCode = KeyEvent.VK_2;
keyChar = '2';
log("Ctrl-2");
} else {
keyCode = KeyEvent.VK_3;
keyChar = '3';
log("Ctrl-3");
}
pw.dispatchEvent(new KeyEvent(pw, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), KeyEvent.CTRL_MASK, keyCode, keyChar));
}
// (add, delete, apply, link)
private static void randomLayerMaskAction() {
Layer layer = ImageComponents.getActiveLayerOrNull();
if (!layer.hasMask()) {
log("add layer mask");
AddLayerMaskAction.INSTANCE.actionPerformed(null);
} else {
if (rand.nextFloat() < 0.2 && layer instanceof ContentLayer) {
LayerMask mask = layer.getMask();
if (mask.isLinked()) {
log("unlink layer mask");
mask.setLinked(false, true);
} else {
log("re-link layer mask");
mask.setLinked(true, true);
}
} else if (layer instanceof ImageLayer) {
double d = rand.nextDouble();
if (d > 0.5) {
log("apply layer mask");
((ImageLayer) layer).applyLayerMask(true);
} else {
log("delete layer mask");
layer.deleteMask(true);
}
} else {
log("delete layer mask");
layer.deleteMask(true);
}
}
}
private static FilterWithParametrizedGUI getRandomTweenFilter() {
FilterAction[] filterActions = FilterUtils.getAnimationFiltersSorted();
FilterAction filterAction = filterActions[(int) (Math.random() * filterActions.length)];
return (FilterWithParametrizedGUI) filterAction.getFilter();
}
private static void randomEnlargeCanvas() {
int north = rand.nextInt(3);
int east = rand.nextInt(3);
int south = rand.nextInt(3);
int west = rand.nextInt(3);
log(String.format("enlargeCanvas north = %d, east = %d, south = %d, west = %d",
north, east, south, west));
Composition comp = ImageComponents.getActiveCompOrNull();
new EnlargeCanvas(north, east, south, west).process(comp);
}
private static void reload(Robot r) {
if (rand.nextFloat() < 0.1) {
Composition comp = ImageComponents.getActiveCompOrNull();
if (comp.getFile() != null) {
log("f5 reload");
pressKey(r, KeyEvent.VK_F5);
}
}
}
private static void setupWeightedCaller(Robot r) {
// random move
weightedCaller.registerCallback(10, () -> randomMove(r));
weightedCaller.registerCallback(70, () -> randomDrag(r));
weightedCaller.registerCallback(5, () -> click(r));
weightedCaller.registerCallback(1, () -> randomRightClick(r));
weightedCaller.registerCallback(3, RandomGUITest::randomResize);
weightedCaller.registerCallback(2, RandomGUITest::repeat);
weightedCaller.registerCallback(1, RandomGUITest::randomUndoRedo);
weightedCaller.registerCallback(1, RandomGUITest::randomCrop);
weightedCaller.registerCallback(1, RandomGUITest::randomFade);
weightedCaller.registerCallback(2, RandomGUITest::randomizeToolSettings);
weightedCaller.registerCallback(1, RandomGUITest::arrangeWindows);
weightedCaller.registerCallback(1, RandomGUITest::randomColors);
weightedCaller.registerCallback(5, RandomGUITest::randomFilter);
weightedCaller.registerCallback(25, RandomGUITest::randomTween);
weightedCaller.registerCallback(10, RandomGUITest::randomFitToScreen);
weightedCaller.registerCallback(3, () -> randomKey(r));
weightedCaller.registerCallback(1, () -> reload(r));
weightedCaller.registerCallback(1, RandomGUITest::randomZoom);
weightedCaller.registerCallback(1, RandomGUITest::randomZoomOut);
weightedCaller.registerCallback(3, RandomGUITest::deselect);
weightedCaller.registerCallback(1, () -> showHideSelection(r));
weightedCaller.registerCallback(1, RandomGUITest::layerToCanvasSize);
weightedCaller.registerCallback(1, RandomGUITest::invertSelection);
weightedCaller.registerCallback(1, RandomGUITest::traceWithCurrentBrush);
weightedCaller.registerCallback(1, RandomGUITest::traceWithCurrentEraser);
weightedCaller.registerCallback(1, RandomGUITest::randomRotateFlip);
weightedCaller.registerCallback(1, RandomGUITest::layerOrderChange);
weightedCaller.registerCallback(5, RandomGUITest::layerMerge);
weightedCaller.registerCallback(3, RandomGUITest::layerAddDelete);
weightedCaller.registerCallback(1, RandomGUITest::randomHideShow);
weightedCaller.registerCallback(1, RandomGUITest::randomCopy);
weightedCaller.registerCallback(1, RandomGUITest::randomPaste);
weightedCaller.registerCallback(1, RandomGUITest::randomChangeLayerOpacityOrBlending);
weightedCaller.registerCallback(1, RandomGUITest::randomChangeLayerVisibility);
weightedCaller.registerCallback(3, RandomGUITest::randomTool);
weightedCaller.registerCallback(1, RandomGUITest::randomEnlargeCanvas);
weightedCaller.registerCallback(7, RandomGUITest::randomNewTextLayer);
weightedCaller.registerCallback(7, RandomGUITest::randomTextLayerRasterize);
if (Build.enableAdjLayers) {
weightedCaller.registerCallback(2, RandomGUITest::randomNewAdjustmentLayer);
}
weightedCaller.registerCallback(7, RandomGUITest::randomSetLayerMaskEditMode);
weightedCaller.registerCallback(20, RandomGUITest::randomLayerMaskAction);
// Not called now:
// randomCloseImageWOSaving();
// randomLoadImage();
// randomSaveInAllFormats();
// randomException();
}
}