/* * 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.automate; import net.jafama.FastMath; import pixelitor.Composition; import pixelitor.MessageHandler; import pixelitor.colors.FgBgColors; import pixelitor.filters.gui.RangeParam; import pixelitor.gui.ImageComponent; import pixelitor.gui.utils.GridBagHelper; import pixelitor.gui.utils.IntTextField; import pixelitor.gui.utils.OKCancelDialog; import pixelitor.history.History; import pixelitor.history.ImageEdit; import pixelitor.layers.ImageLayer; import pixelitor.selection.IgnoreSelection; import pixelitor.tools.AbstractBrushTool; import pixelitor.tools.Tool; import pixelitor.tools.UserDrag; import pixelitor.tools.shapestool.ShapesTool; import pixelitor.utils.Messages; import javax.swing.*; import java.awt.Color; import java.awt.GridBagLayout; import java.awt.Point; import java.awt.image.BufferedImage; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; import static pixelitor.tools.Tools.BRUSH; import static pixelitor.tools.Tools.CLONE; import static pixelitor.tools.Tools.ERASER; import static pixelitor.tools.Tools.SMUDGE; public class AutoPaint { // public static final Tool[] ALLOWED_TOOLS = {SMUDGE, BRUSH, CLONE, ERASER, SHAPES}; public static final Tool[] ALLOWED_TOOLS = {SMUDGE, BRUSH, CLONE, ERASER}; private AutoPaint() { } public static void showDialog(ImageLayer imageLayer) { ConfigPanel configPanel = new ConfigPanel(); JDialog d = new OKCancelDialog(configPanel, "Auto Paint") { @Override protected void dialogAccepted() { close(); Settings settings = configPanel.getSettings(); paintStrokes(imageLayer, settings); } }; d.setVisible(true); } private static void paintStrokes(ImageLayer layer, Settings settings) { assert SwingUtilities.isEventDispatchThread(); Color origFg = null; Color origBg = null; if (settings.useRandomColors()) { origFg = FgBgColors.getFG(); origBg = FgBgColors.getBG(); } String msg = String.format("Auto Paint with %s Tool: ", settings.getTool()); MessageHandler msgHandler = Messages.getMessageHandler(); msgHandler.startProgress(msg, settings.getNumStrokes()); BufferedImage backupImage = layer.getImageOrSubImageIfSelected(true, true); History.setIgnoreEdits(true); try { runStrokes(settings, layer, msgHandler); } finally { History.setIgnoreEdits(false); ImageEdit edit = new ImageEdit(layer.getComp(), "Auto Paint", layer, backupImage, IgnoreSelection.NO, false); History.addEdit(edit); msgHandler.stopProgress(); msgHandler.showStatusMessage(msg + "finished."); if (settings.useRandomColors()) { FgBgColors.setFG(origFg); FgBgColors.setBG(origBg); } } } private static void runStrokes(Settings settings, ImageLayer layer, MessageHandler msgHandler) { Random random = new Random(); Composition comp = layer.getComp(); ImageComponent ic = comp.getIC(); int numStrokes = settings.getNumStrokes(); for (int i = 0; i < numStrokes; i++) { msgHandler.updateProgress(i); paintSingleStroke(layer, settings, comp.getCanvasWidth(), comp.getCanvasHeight(), random); ic.paintImmediately(ic.getBounds()); } } private static void paintSingleStroke(ImageLayer layer, Settings settings, int canvasWidth, int canvasHeight, Random rand) { if (settings.useRandomColors()) { FgBgColors.randomize(); } int strokeLength = settings.genStrokeLength(); Point start = new Point(rand.nextInt(canvasWidth), rand.nextInt(canvasHeight)); double randomAngle = rand.nextDouble() * 2 * Math.PI; int endX = (int) (start.x + strokeLength * FastMath.cos(randomAngle)); int endY = (int) (start.y + strokeLength * FastMath.sin(randomAngle)); Point end = new Point(endX, endY); try { Tool tool = settings.getTool(); // tool.randomize(); if (tool instanceof AbstractBrushTool) { AbstractBrushTool abt = (AbstractBrushTool) tool; abt.drawBrushStrokeProgrammatically(layer, start, end); } else if (tool instanceof ShapesTool) { ShapesTool st = (ShapesTool) tool; st.paintShapeOnIC(layer, new UserDrag(start.x, start.y, end.x, end.y)); } else { throw new IllegalStateException("tool = " + tool.getClass().getName()); } } catch (Exception e) { Messages.showException(e); } } private static class ConfigPanel extends JPanel { private final JComboBox<Tool> toolSelector; private static Tool defaultTool = SMUDGE; private final IntTextField numStrokesTF; private static int defaultNumStrokes = 100; private final IntTextField lengthTF; private static int defaultLength = 100; private final JCheckBox randomColorsCB; private final JLabel randomColorsLabel; private static int defaultLengthVariability = 50; private final RangeParam lengthVariability = new RangeParam("", 0, defaultLengthVariability, 100); private static boolean defaultRandomColors = true; private ConfigPanel() { super(new GridBagLayout()); GridBagHelper gbh = new GridBagHelper(this); toolSelector = new JComboBox<>(ALLOWED_TOOLS); toolSelector.setSelectedItem(defaultTool); toolSelector.setName("toolSelector"); gbh.addLabelWithControl("Tool:", toolSelector); numStrokesTF = new IntTextField(String.valueOf(defaultNumStrokes)); numStrokesTF.setName("numStrokesTF"); gbh.addLabelWithControl("Number of Strokes:", numStrokesTF); lengthTF = new IntTextField(String.valueOf(defaultLength)); // TODO stroke length variability // gbh.addLabelWithControl("Stroke Length Average:", lengthTF); gbh.addLabelWithControl("Stroke Length:", lengthTF); // // TODO stroke length variability // lengthVariability.setValueNoTrigger(defaultLengthVariability); // gbh.addLabelWithControl("Stroke Length Variability:", // new SliderSpinner(lengthVariability, // SliderSpinner.TextPosition.NONE, false)); randomColorsLabel = new JLabel("Random Colors:"); randomColorsCB = new JCheckBox(); randomColorsCB.setSelected(defaultRandomColors); gbh.addTwoControls(randomColorsLabel, randomColorsCB); toolSelector.addActionListener(e -> updateRandomColorsEnabledState()); updateRandomColorsEnabledState(); } private void updateRandomColorsEnabledState() { Tool tool = (Tool) toolSelector.getSelectedItem(); if (tool == BRUSH) { randomColorsLabel.setEnabled(true); randomColorsCB.setEnabled(true); } else { randomColorsLabel.setEnabled(false); randomColorsCB.setEnabled(false); } } public Settings getSettings() { int numStrokes = numStrokesTF.getIntValue(); defaultNumStrokes = numStrokes; int strokeLength = lengthTF.getIntValue(); defaultLength = strokeLength; Tool tool = (Tool) toolSelector.getSelectedItem(); defaultTool = tool; boolean randomColorsEnabled = randomColorsCB.isEnabled(); boolean randomColorsSelected = randomColorsCB.isSelected(); boolean randomColors = randomColorsEnabled && randomColorsSelected; defaultRandomColors = randomColorsSelected; float lengthRandomnessPercentage = lengthVariability.getValueAsPercentage(); defaultLengthVariability = lengthVariability.getValue(); // TODO stroke length variability // return new Settings(tool, numStrokes, strokeLength, randomColors, lengthRandomnessPercentage); return new Settings(tool, numStrokes, strokeLength, randomColors, 0); } } private static class Settings { private final Tool tool; private final int numStrokes; private final int minStrokeLength; private final int maxStrokeLength; private final boolean randomColors; private Settings(Tool tool, int numStrokes, int strokeLength, boolean randomColors, float lengthVariability) { this.tool = tool; this.numStrokes = numStrokes; if (lengthVariability == 0.0f) { minStrokeLength = strokeLength; maxStrokeLength = strokeLength; } else { minStrokeLength = (int) (strokeLength - lengthVariability * strokeLength); maxStrokeLength = (int) (strokeLength + lengthVariability * strokeLength); } this.randomColors = randomColors; } public Tool getTool() { return tool; } public int getNumStrokes() { return numStrokes; } public int genStrokeLength() { if (minStrokeLength == maxStrokeLength) { return minStrokeLength; } else { return ThreadLocalRandom.current().nextInt(minStrokeLength, maxStrokeLength + 1); } } public boolean useRandomColors() { return randomColors; } } }