diff --git a/src/main/java/com/bric/math/Equations.java b/src/main/java/com/bric/math/Equations.java index 1abd67ae6..73acc1c35 100755 --- a/src/main/java/com/bric/math/Equations.java +++ b/src/main/java/com/bric/math/Equations.java @@ -377,7 +377,7 @@ public static void solve(BigDecimal[][] coefficients, boolean sort) { int row = 0; int a, i; BigDecimal t; - BigDecimal tolerance = new BigDecimal(0.0000000001); + BigDecimal tolerance = new BigDecimal("0.0000000001"); int errorCounter = 0; //println(coefficients); while (ctr < b.length) { diff --git a/src/main/java/pd/AnimatedGifEncoder.java b/src/main/java/pd/AnimatedGifEncoder.java index db26a2751..2844c0ed6 100644 --- a/src/main/java/pd/AnimatedGifEncoder.java +++ b/src/main/java/pd/AnimatedGifEncoder.java @@ -927,21 +927,15 @@ private void alterneigh(int rad, int i, int b, int g, int r) { a = radpower[m++]; if (j < hi) { p = network[j++]; - try { - p[0] -= (a * (p[0] - b)) / alpharadbias; - p[1] -= (a * (p[1] - g)) / alpharadbias; - p[2] -= (a * (p[2] - r)) / alpharadbias; - } catch (Exception e) { - } // prevents 1.3 miscompilation + p[0] -= (a * (p[0] - b)) / alpharadbias; + p[1] -= (a * (p[1] - g)) / alpharadbias; + p[2] -= (a * (p[2] - r)) / alpharadbias; } if (k > lo) { p = network[k--]; - try { - p[0] -= (a * (p[0] - b)) / alpharadbias; - p[1] -= (a * (p[1] - g)) / alpharadbias; - p[2] -= (a * (p[2] - r)) / alpharadbias; - } catch (Exception e) { - } + p[0] -= (a * (p[0] - b)) / alpharadbias; + p[1] -= (a * (p[1] - g)) / alpharadbias; + p[2] -= (a * (p[2] - r)) / alpharadbias; } } } diff --git a/src/main/java/pixelitor/Composition.java b/src/main/java/pixelitor/Composition.java index 9024e4e72..ef01e0efb 100644 --- a/src/main/java/pixelitor/Composition.java +++ b/src/main/java/pixelitor/Composition.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -558,7 +558,7 @@ public ImageLayer addNewEmptyImageLayer(String name, boolean bellowActive) { getHolderForNewLayers().adder() .withHistory("New Empty Layer") .atPosition(bellowActive ? BELLOW_ACTIVE : ABOVE_ACTIVE) - .noRefresh() + .noUpdate() .add(newLayer); return newLayer; @@ -575,7 +575,7 @@ public void addNewLayerFromComposite() { new LayerAdder(this) .withHistory("New Layer from Visible") - .noRefresh() + .noUpdate() .atIndex(layerList.size()) .add(newLayer); } @@ -612,7 +612,7 @@ public void flattenImage() { Layer flattened = new ImageLayer(this, bi, "flattened"); adder() .atIndex(numLayers) // add to the top - .noRefresh() + .noUpdate() .add(flattened); for (int i = numLayers - 1; i >= 0; i--) { // delete the rest diff --git a/src/main/java/pixelitor/Pixelitor.java b/src/main/java/pixelitor/Pixelitor.java index 5e0b7153c..1248b51bf 100644 --- a/src/main/java/pixelitor/Pixelitor.java +++ b/src/main/java/pixelitor/Pixelitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -121,28 +121,7 @@ private static void createAndShowGUI(String[] args) { Theme theme = AppPreferences.loadTheme(); Themes.install(theme, false, true); - - int uiFontSize = AppPreferences.loadUIFontSize(); - String uiFontType = AppPreferences.loadUIFontType(); - - Font defaultFont = UIManager.getFont("defaultFont"); - if (defaultFont != null) { // if null, we don't know how to set the font - if (uiFontSize != 0 || !uiFontType.isEmpty()) { - Font newFont; - if (!uiFontType.isEmpty()) { - newFont = new Font(uiFontType, Font.PLAIN, uiFontSize); - } else { - newFont = defaultFont.deriveFont((float) uiFontSize); - } - - FontUIResource fontUIResource = new FontUIResource(newFont); - UIManager.put("defaultFont", fontUIResource); - - if (theme.isNimbus()) { - UIManager.getLookAndFeel().getDefaults().put("defaultFont", fontUIResource); - } - } - } + loadUIFonts(theme); var pw = PixelitorWindow.get(); Dialogs.setMainWindowInitialized(true); @@ -167,6 +146,35 @@ private static void createAndShowGUI(String[] args) { .exceptionally(Messages::showExceptionOnEDT); } + private static void loadUIFonts(Theme theme) { + int uiFontSize = AppPreferences.loadUIFontSize(); + String uiFontType = AppPreferences.loadUIFontType(); + if (uiFontSize == 0 || uiFontType.isEmpty()) { + // no saved settings found + return; + } + + Font defaultFont = UIManager.getFont("defaultFont"); + if (defaultFont == null) { + // if null, we don't know how to set the font + return; + } + + Font newFont; + if (!uiFontType.isEmpty()) { + newFont = new Font(uiFontType, Font.PLAIN, uiFontSize); + } else { + newFont = defaultFont.deriveFont((float) uiFontSize); + } + + FontUIResource fontUIResource = new FontUIResource(newFont); + UIManager.put("defaultFont", fontUIResource); + + if (theme.isNimbus()) { + UIManager.getLookAndFeel().getDefaults().put("defaultFont", fontUIResource); + } + } + /** * Schedules the opening of the files given as command-line arguments */ @@ -174,19 +182,19 @@ private static CompletableFuture openCLFilesAsync(String[] args) { List> openedFiles = new ArrayList<>(); for (String fileName : args) { - File f = new File(fileName); - if (f.exists()) { - openedFiles.add(IO.openFileAsync(f, false)); + File file = new File(fileName); + if (file.exists()) { + openedFiles.add(IO.openFileAsync(file, false)); } else { Messages.showError("File not found", - format("The file \"%s\" doesn't exist.", f.getAbsolutePath())); + format("The file \"%s\" doesn't exist.", file.getAbsolutePath())); } } return Utils.allOf(openedFiles); } - public static void exitApp(PixelitorWindow pw) { + public static void warnAndExit(PixelitorWindow pw) { assert calledOnEDT() : threadInfo(); var paths = IOTasks.getCurrentWritePaths(); @@ -205,27 +213,31 @@ public static void exitApp(PixelitorWindow pw) { // can be updated while waiting new Thread(() -> { Utils.sleep(10, TimeUnit.SECONDS); - EventQueue.invokeLater(() -> exitApp(pw)); + EventQueue.invokeLater(() -> warnAndExit(pw)); }).start(); return; } } - var unsavedComps = Views.getUnsavedComps(); - if (!unsavedComps.isEmpty()) { - String msg = createUnsavedChangesMsg(unsavedComps); - - if (Dialogs.showYesNoWarningDialog(pw, "Unsaved Changes", msg)) { - pw.setVisible(false); - AppPreferences.savePrefsAndExit(); + List unsaved = Views.getUnsavedComps(); + if (!unsaved.isEmpty()) { + boolean yesClicked = Dialogs.showYesNoWarningDialog(pw, + "Unsaved Changes", createUnsavedChangesMsg(unsaved)); + if (yesClicked) { + exit(pw); } } else { - pw.setVisible(false); - AppPreferences.savePrefsAndExit(); + exit(pw); } } + private static void exit(PixelitorWindow pw) { + pw.setVisible(false); + AppPreferences.savePreferences(); + System.exit(0); + } + private static String createUnsavedChangesMsg(List unsavedComps) { String msg; if (unsavedComps.size() == 1) { diff --git a/src/main/java/pixelitor/filters/AbstractLights.java b/src/main/java/pixelitor/filters/AbstractLights.java index b65334fe2..0a58050b3 100644 --- a/src/main/java/pixelitor/filters/AbstractLights.java +++ b/src/main/java/pixelitor/filters/AbstractLights.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -174,7 +174,7 @@ private List createPoints(int width, int height, Random random, float return points; } - private void connectParticles(int width, int height, int numPoints, double speed, ArrayList points) { + private void connectParticles(int width, int height, int numPoints, double speed, List points) { int connect = typeParam.getValue(); if (connect == TYPE_CHAOS || connect == TYPE_FRAME) { for (int i = 0; i < numPoints; i++) { diff --git a/src/main/java/pixelitor/filters/CommandLineFilter.java b/src/main/java/pixelitor/filters/CommandLineFilter.java index 89ad765c2..25c8f9327 100644 --- a/src/main/java/pixelitor/filters/CommandLineFilter.java +++ b/src/main/java/pixelitor/filters/CommandLineFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -77,4 +77,9 @@ private static List parseCommands(String input) { public FilterGUI createGUI(Filterable layer, boolean reset) { return new CommandLineGUI(this, layer, true, reset); } + + @Override + public boolean supportsGray() { + return false; + } } diff --git a/src/main/java/pixelitor/filters/ValueNoise.java b/src/main/java/pixelitor/filters/ValueNoise.java index b24cf501a..989ae2da7 100644 --- a/src/main/java/pixelitor/filters/ValueNoise.java +++ b/src/main/java/pixelitor/filters/ValueNoise.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -174,7 +174,7 @@ private float noise(int x, int y) { int n = x + y * 57; n = (n << 13) ^ n; - return (1.0f - ((n * (n * n * r1 + r2) + r3) & 0x7F_FF_FF_FF) / 1.07374182E+9f); + return (1.0f - ((n * (n * n * r1 + r2) + r3) & 0x7F_FF_FF_FF) / 1.0737418E+9f); } private static float interpolate(float x, float y, float a, NoiseInterpolation interp) { diff --git a/src/main/java/pixelitor/filters/gmic/GMICFilter.java b/src/main/java/pixelitor/filters/gmic/GMICFilter.java index 1b922d322..3be671a31 100644 --- a/src/main/java/pixelitor/filters/gmic/GMICFilter.java +++ b/src/main/java/pixelitor/filters/gmic/GMICFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -53,6 +53,11 @@ public BufferedImage doTransform(BufferedImage src, BufferedImage dest) { public abstract List getArgs(); + @Override + public boolean supportsGray() { + return false; + } + public static IntChoiceParam createValueAction() { return new IntChoiceParam("Value Action", new String[]{ "None", "Cut", "Normalize"}); diff --git a/src/main/java/pixelitor/filters/gui/ParamSet.java b/src/main/java/pixelitor/filters/gui/ParamSet.java index 45941a744..d8e10d676 100644 --- a/src/main/java/pixelitor/filters/gui/ParamSet.java +++ b/src/main/java/pixelitor/filters/gui/ParamSet.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -402,7 +402,7 @@ public FilterButtonModel createReseedCachedAndNoiseAction() { seedChangedAction = newSeed -> { Noise.reseed(newSeed); random.setSeed(newSeed); - CachedFloatRandom.reseedCache(random); + CachedFloatRandom.reBuildCache(random); }; return FilterButtonModel.createReseed(() -> { diff --git a/src/main/java/pixelitor/gui/GlobalEvents.java b/src/main/java/pixelitor/gui/GlobalEvents.java index 2f06db778..2bfe85e76 100644 --- a/src/main/java/pixelitor/gui/GlobalEvents.java +++ b/src/main/java/pixelitor/gui/GlobalEvents.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -129,6 +129,8 @@ public static void init() { backwardKeys = new HashSet<>(backwardKeys); // make modifiable backwardKeys.remove(Keys.CTRL_SHIFT_TAB); keyboardFocusManager.setDefaultFocusTraversalKeys(BACKWARD_TRAVERSAL_KEYS, backwardKeys); + + addBrushSizeActions(); } private static void keyPressed(KeyEvent e) { @@ -239,7 +241,7 @@ public static int getNumModalDialogs() { return numModalDialogs; } - public static void addBrushSizeActions() { + private static void addBrushSizeActions() { addHotKey(']', INCREASE_ACTIVE_BRUSH_SIZE_ACTION, true); addHotKey('[', DECREASE_ACTIVE_BRUSH_SIZE_ACTION, true); } @@ -247,7 +249,7 @@ public static void addBrushSizeActions() { public static void registerDebugMouseWatching(boolean postEvents) { Toolkit.getDefaultToolkit().addAWTEventListener(event -> { MouseEvent e = (MouseEvent) event; - String msg = Tools.getCurrent().getName() + ": " + Debug.debugMouseEvent(e); + String msg = Tools.getCurrent().getName() + ": " + Debug.mouseEventAsString(e); if (postEvents) { Events.postMouseEvent(msg); } else { diff --git a/src/main/java/pixelitor/gui/PixelitorWindow.java b/src/main/java/pixelitor/gui/PixelitorWindow.java index 844506b5d..c5c631a7d 100644 --- a/src/main/java/pixelitor/gui/PixelitorWindow.java +++ b/src/main/java/pixelitor/gui/PixelitorWindow.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -80,7 +80,6 @@ private PixelitorWindow() { setupIcons(); GlobalEvents.init(); - GlobalEvents.addBrushSizeActions(); AppPreferences.loadFramePosition(this, screenSize); @@ -99,7 +98,7 @@ private void setupWindowEvents() { new WindowAdapter() { @Override public void windowClosing(WindowEvent we) { - Pixelitor.exitApp(PixelitorWindow.this); + Pixelitor.warnAndExit(PixelitorWindow.this); } @Override @@ -129,7 +128,7 @@ private void setupMacHandlers() { desktop.setPreferencesHandler(e -> PreferencesPanel.showInDialog()); } if (desktop.isSupported(APP_QUIT_HANDLER)) { - desktop.setQuitHandler((e, r) -> Pixelitor.exitApp(this)); + desktop.setQuitHandler((e, r) -> Pixelitor.warnAndExit(this)); } } } @@ -173,17 +172,16 @@ private void addStatusBar() { } private void setupIcons() { - URL imgURL32 = getClass().getResource("/images/pixelitor_icon32.png"); - URL imgURL48 = getClass().getResource("/images/pixelitor_icon48.png"); - URL imgURL256 = getClass().getResource("/images/pixelitor_icon256.png"); + var clazz = getClass(); + URL imgURL32 = clazz.getResource("/images/pixelitor_icon32.png"); + URL imgURL48 = clazz.getResource("/images/pixelitor_icon48.png"); + URL imgURL256 = clazz.getResource("/images/pixelitor_icon256.png"); if (imgURL32 != null && imgURL48 != null && imgURL256 != null) { List icons = new ArrayList<>(2); - Image img32 = new ImageIcon(imgURL32).getImage(); - Image img48 = new ImageIcon(imgURL48).getImage(); + icons.add(new ImageIcon(imgURL32).getImage()); + icons.add(new ImageIcon(imgURL48).getImage()); Image img256 = new ImageIcon(imgURL256).getImage(); - icons.add(img32); - icons.add(img48); icons.add(img256); setIconImages(icons); @@ -194,8 +192,8 @@ private void setupIcons() { } } } else { - String msg = "icon imgURL is null"; - Dialogs.showErrorDialog(this, "Error", msg); + Dialogs.showErrorDialog(this, "Error", + "icon imgURL is null"); } } diff --git a/src/main/java/pixelitor/history/PartialImageEdit.java b/src/main/java/pixelitor/history/PartialImageEdit.java index 9f2d4c062..1ab2f1069 100644 --- a/src/main/java/pixelitor/history/PartialImageEdit.java +++ b/src/main/java/pixelitor/history/PartialImageEdit.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -22,6 +22,7 @@ import pixelitor.utils.ImageUtils; import pixelitor.utils.debug.Debug; import pixelitor.utils.debug.DebugNode; +import pixelitor.utils.debug.DebugNodes; import javax.swing.*; import javax.swing.undo.CannotRedoException; @@ -206,6 +207,7 @@ public DebugNode createDebugNode(String key) { node.addInt("backup image width", width); node.addInt("backup image height", height); + node.add(DebugNodes.createRectangleNode(saveRect, "saveRect")); return node; } diff --git a/src/main/java/pixelitor/io/FileFormat.java b/src/main/java/pixelitor/io/FileFormat.java index bf888728d..22d070704 100644 --- a/src/main/java/pixelitor/io/FileFormat.java +++ b/src/main/java/pixelitor/io/FileFormat.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -142,7 +142,7 @@ public FileFilter getFileFilter() { @Override public String toString() { - return super.toString().toLowerCase(); + return super.toString().toLowerCase(Locale.ENGLISH); } public static Optional fromFile(File file) { diff --git a/src/main/java/pixelitor/io/IO.java b/src/main/java/pixelitor/io/IO.java index cee671e3b..a1b740185 100644 --- a/src/main/java/pixelitor/io/IO.java +++ b/src/main/java/pixelitor/io/IO.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -447,7 +447,7 @@ public static Result runCommandLineFilter(BufferedImage s if (out == null) { // There was an error. Try to get an error message. try (InputStream processError = p.getErrorStream()) { - errorMsg = new String(processError.readAllBytes()); + errorMsg = new String(processError.readAllBytes(), UTF_8); } } p.waitFor(); diff --git a/src/main/java/pixelitor/io/LayerAnimation.java b/src/main/java/pixelitor/io/LayerAnimation.java index ea7d3cc56..52b7e03c9 100644 --- a/src/main/java/pixelitor/io/LayerAnimation.java +++ b/src/main/java/pixelitor/io/LayerAnimation.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -21,6 +21,8 @@ import pixelitor.Composition; import pixelitor.gui.utils.GUIUtils; import pixelitor.layers.Layer; +import pixelitor.utils.ProgressTracker; +import pixelitor.utils.StatusBarProgressTracker; import java.awt.image.BufferedImage; import java.io.File; @@ -33,11 +35,17 @@ public class LayerAnimation { private final int delayMillis; private final List images = new ArrayList<>(); + private int numFrames; public LayerAnimation(Composition comp, int delayMillis, boolean pingPong) { this.delayMillis = delayMillis; int numLayers = comp.getNumLayers(); + numFrames = numLayers; + if (pingPong && numLayers > 2) { + numFrames += (numLayers - 1); + } + for (int i = 0; i < numLayers; i++) { addLayer(comp, i); } @@ -58,11 +66,17 @@ private void addLayer(Composition comp, int layerIndex) { } private void export(File f) { + ProgressTracker pt = new StatusBarProgressTracker("Writing " + f.getName(), numFrames); + AnimatedGifEncoder e = new AnimatedGifEncoder(); e.start(f); e.setDelay(delayMillis); e.setRepeat(0); - images.forEach(e::addFrame); + for (BufferedImage image : images) { + e.addFrame(image); + pt.unitDone(); + } + pt.finished(); e.finish(); } diff --git a/src/main/java/pixelitor/io/magick/ImageMagick.java b/src/main/java/pixelitor/io/magick/ImageMagick.java index 09106fc80..923848b6d 100644 --- a/src/main/java/pixelitor/io/magick/ImageMagick.java +++ b/src/main/java/pixelitor/io/magick/ImageMagick.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -35,6 +35,7 @@ import java.util.Locale; import java.util.concurrent.CompletableFuture; +import static java.nio.charset.StandardCharsets.UTF_8; import static pixelitor.utils.AppPreferences.magickDirName; import static pixelitor.utils.Threads.onEDT; import static pixelitor.utils.Threads.onIOThread; @@ -244,7 +245,7 @@ private static boolean checkInstalled() { // read the first line of the standard output String magickFullPath; try (BufferedReader reader = new BufferedReader( - new InputStreamReader(process.getInputStream()))) { + new InputStreamReader(process.getInputStream(), UTF_8))) { magickFullPath = reader.readLine(); } int exitValue = process.waitFor(); diff --git a/src/main/java/pixelitor/layers/ImageLayer.java b/src/main/java/pixelitor/layers/ImageLayer.java index cb0a977e2..f5f652e06 100644 --- a/src/main/java/pixelitor/layers/ImageLayer.java +++ b/src/main/java/pixelitor/layers/ImageLayer.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -413,7 +413,7 @@ private BufferedImage replaceSelectedRegion(BufferedImage src, boolean isUndoRedo) { assert src != null; assert newImg != null; - assert Assertions.checkRasterMinimum(newImg); + assert Assertions.rasterStartsAtZero(newImg); var selection = comp.getSelection(); if (selection == null) { @@ -464,7 +464,7 @@ public void setImage(BufferedImage newImage) { image = requireNonNull(newImage); imageRefChanged(); - assert Assertions.checkRasterMinimum(newImage); + assert Assertions.rasterStartsAtZero(newImage); comp.invalidateImageCache(); diff --git a/src/main/java/pixelitor/layers/LayerAdder.java b/src/main/java/pixelitor/layers/LayerAdder.java index ca83fddc5..58216d1cb 100644 --- a/src/main/java/pixelitor/layers/LayerAdder.java +++ b/src/main/java/pixelitor/layers/LayerAdder.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -35,7 +35,7 @@ public class LayerAdder { private final Composition comp; private String editName; // null if the add should not be added to history private int targetLayerIndex = -1; - private boolean refresh = true; + private boolean update = true; public enum Position {TOP, ABOVE_ACTIVE, BELLOW_ACTIVE} @@ -69,8 +69,8 @@ public LayerAdder noUI() { /** * Used when the composite image doesn't change. */ - public LayerAdder noRefresh() { - refresh = false; + public LayerAdder noUpdate() { + update = false; return this; } @@ -132,7 +132,7 @@ public void add(Layer layer) { assert GUIMode.isUnitTesting() || layer.hasUI(); comp.setDirty(true); - if (refresh) { + if (update) { holder.update(); } } diff --git a/src/main/java/pixelitor/layers/LayerGUI.java b/src/main/java/pixelitor/layers/LayerGUI.java index 9776d4f88..803281457 100644 --- a/src/main/java/pixelitor/layers/LayerGUI.java +++ b/src/main/java/pixelitor/layers/LayerGUI.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -284,7 +284,7 @@ protected void processMouseEvent(MouseEvent e) { if (smartFilter) { super.processMouseEvent(e); } else if (e.getID() == MouseEvent.MOUSE_CLICKED) { - //String s = Debug.debugMouseEvent(e); + //String s = Debug.mouseEventAsString(e); boolean altDown = (e.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) == MouseEvent.ALT_DOWN_MASK; if (altDown) { layer.isolate(); @@ -708,7 +708,8 @@ public boolean checkInvariants() { } if (!layer.isTopLevel()) { if (parentUI == null) { - throw new AssertionError("null parentUI in " + getLayerName() + " UI"); + throw new AssertionError("null parentUI in " + getLayerName() + + " UI, holder class = " + layer.getHolder().getClass().getSimpleName()); } LayerUI holderUI = ((CompositeLayer) layer.getHolder()).getUI(); diff --git a/src/main/java/pixelitor/menus/MenuBar.java b/src/main/java/pixelitor/menus/MenuBar.java index bceb751d2..54d62aef6 100644 --- a/src/main/java/pixelitor/menus/MenuBar.java +++ b/src/main/java/pixelitor/menus/MenuBar.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -201,7 +201,7 @@ protected void process(Drawable dr) { // exit String exitName = JVM.isMac ? texts.getString("exit_mac") : texts.getString("exit"); - fileMenu.add(new PAction(exitName, () -> Pixelitor.exitApp(pw))); + fileMenu.add(new PAction(exitName, () -> Pixelitor.warnAndExit(pw))); return fileMenu; } @@ -1439,7 +1439,8 @@ private static JMenu createTestSubmenu() { sub.addFilter(ParamTest.NAME, ParamTest::new); - sub.add(new PAction("Random GUI Test", RandomGUITest::start), CTRL_R); + sub.add(new PAction("Random GUI Test", () -> + RandomGUITest.get().start()), CTRL_R); sub.add(new OpenViewEnabledAction( "Save in All Formats...", diff --git a/src/main/java/pixelitor/tools/AbstractBrushTool.java b/src/main/java/pixelitor/tools/AbstractBrushTool.java index c9eeaaf19..551ef4bfb 100644 --- a/src/main/java/pixelitor/tools/AbstractBrushTool.java +++ b/src/main/java/pixelitor/tools/AbstractBrushTool.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -558,7 +558,8 @@ private void doTrace(Drawable dr, Shape shape) { prepareProgrammaticBrushStroke(dr, pathPoint); brushStrokePrepared = true; } - if (subPathIndex != 0) { + if (subPathIndex > 0) { + // finish the previous brush stroke brush.finishBrushStroke(); } brush.startAt(pathPoint); diff --git a/src/main/java/pixelitor/tools/BrushType.java b/src/main/java/pixelitor/tools/BrushType.java index 15d1b140b..50166df60 100644 --- a/src/main/java/pixelitor/tools/BrushType.java +++ b/src/main/java/pixelitor/tools/BrushType.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -21,7 +21,6 @@ import javax.swing.*; import java.util.IdentityHashMap; -import java.util.Map; import java.util.function.Supplier; import static pixelitor.tools.brushes.AngleSettings.NOT_ANGLED; @@ -142,7 +141,7 @@ public boolean hasRadius() { // The settings are shared between the symmetry-brushes of a // tool, but they are different between the different tools - private Map settingsByTool; + private IdentityHashMap settingsByTool; BrushType(String guiName, boolean hasSettings) { this.guiName = guiName; diff --git a/src/main/java/pixelitor/tools/DrawDestination.java b/src/main/java/pixelitor/tools/DrawDestination.java index ccd12ecc9..7883c3ede 100644 --- a/src/main/java/pixelitor/tools/DrawDestination.java +++ b/src/main/java/pixelitor/tools/DrawDestination.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -67,7 +67,7 @@ public Graphics2D createGraphics(Drawable dr, Composite composite) { public void prepareBrushStroke(Drawable dr) { BufferedImage image = dr.getImage(); - assert Assertions.checkRasterMinimum(image); + assert Assertions.rasterStartsAtZero(image); backupImg = ImageUtils.copyImage(image); } diff --git a/src/main/java/pixelitor/tools/pen/PenTool.java b/src/main/java/pixelitor/tools/pen/PenTool.java index c78d79970..589c78512 100644 --- a/src/main/java/pixelitor/tools/pen/PenTool.java +++ b/src/main/java/pixelitor/tools/pen/PenTool.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -46,10 +46,15 @@ import java.awt.event.MouseEvent; import java.awt.geom.*; -import static java.awt.BasicStroke.*; +import static java.awt.BasicStroke.CAP_BUTT; +import static java.awt.BasicStroke.CAP_ROUND; +import static java.awt.BasicStroke.JOIN_MITER; +import static java.awt.BasicStroke.JOIN_ROUND; import static java.awt.RenderingHints.KEY_ANTIALIASING; import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON; -import static pixelitor.tools.pen.PenToolMode.*; +import static pixelitor.tools.pen.PenToolMode.BUILD; +import static pixelitor.tools.pen.PenToolMode.EDIT; +import static pixelitor.tools.pen.PenToolMode.TRANSFORM; /** * The pen tool. @@ -396,11 +401,11 @@ public static boolean checkPathConsistency() { private void setPathFromComp(Composition comp) { if (comp == null) { - setNullPath(); + setNoPath(); } else { Path compPath = comp.getActivePath(); if (compPath == null) { - setNullPath(); + setNoPath(); } else { path = compPath; PenToolMode preferredMode = compPath.getPreferredPenToolMode(); @@ -413,7 +418,7 @@ private void setPathFromComp(Composition comp) { enableActions(path != null); } - private void setNullPath() { + private void setNoPath() { path = null; if (mode.requiresExistingPath()) { startBuilding(false); @@ -436,7 +441,7 @@ public void setPath(Path path) { public void removePath() { Views.setActivePath(null); - setNullPath(); + setNoPath(); enableActions(false); } diff --git a/src/main/java/pixelitor/utils/AppPreferences.java b/src/main/java/pixelitor/utils/AppPreferences.java index 1a5cd4f14..911a47872 100644 --- a/src/main/java/pixelitor/utils/AppPreferences.java +++ b/src/main/java/pixelitor/utils/AppPreferences.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -393,12 +393,7 @@ private static void saveCropGuideStyles() { mainNode.putInt(CROP_GUIDE_STROKE_KEY, style.getStrokeType().ordinal()); } - public static void savePrefsAndExit() { - savePreferences(); - System.exit(0); - } - - private static void savePreferences() { + public static void savePreferences() { saveDesktopMode(); saveRecentFiles(RecentFilesMenu.INSTANCE.getRecentFileInfosForSaving()); saveFramePosition(PixelitorWindow.get()); @@ -508,8 +503,7 @@ private static void saveLastToolName() { public static Theme loadTheme() { String code = mainNode.get(THEME_KEY, Themes.DEFAULT.getSaveCode()); - Theme[] themes = Theme.values(); - for (Theme theme : themes) { + for (Theme theme : Theme.values()) { if (code.equals(theme.getSaveCode())) { return theme; } diff --git a/src/main/java/pixelitor/utils/CachedFloatRandom.java b/src/main/java/pixelitor/utils/CachedFloatRandom.java index b32fdf407..0b63bcd4d 100644 --- a/src/main/java/pixelitor/utils/CachedFloatRandom.java +++ b/src/main/java/pixelitor/utils/CachedFloatRandom.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -34,10 +34,10 @@ public class CachedFloatRandom { private final Random instanceRandom = new Random(); static { - reseedCache(new Random()); + reBuildCache(new Random()); } - public static void reseedCache(Random staticRandom) { + public static void reBuildCache(Random staticRandom) { for (int i = 0; i < CACHE_SIZE; i++) { cache[i] = staticRandom.nextFloat(); } diff --git a/src/main/java/pixelitor/utils/ColorSpaces.java b/src/main/java/pixelitor/utils/ColorSpaces.java index 208ac2f18..865c0dcfa 100644 --- a/src/main/java/pixelitor/utils/ColorSpaces.java +++ b/src/main/java/pixelitor/utils/ColorSpaces.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -19,6 +19,9 @@ import com.jhlabs.image.ImageMath; +/** + * Utility methods related to color spaces. + */ public class ColorSpaces { /** * A lookup table for transforming sRGB (ints in the 0..255 range) @@ -46,7 +49,7 @@ public class ColorSpaces { } private ColorSpaces() { - // private constructor to prevent initialization + // prevent initialization } private static double toLinear(double sRGB) { diff --git a/src/main/java/pixelitor/utils/DebugProgressTracker.java b/src/main/java/pixelitor/utils/DebugProgressTracker.java index 77a640bf4..fc1cc5bb9 100644 --- a/src/main/java/pixelitor/utils/DebugProgressTracker.java +++ b/src/main/java/pixelitor/utils/DebugProgressTracker.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -20,6 +20,8 @@ import java.util.ArrayList; import java.util.List; +import static java.lang.String.format; + /** * A progress tracker that is used only for development * to check that the work units were correctly estimated. @@ -84,7 +86,6 @@ public void finished() { } printCallInfoStatistics(); - System.out.println(); } private void printCallInfoStatistics() { @@ -97,6 +98,7 @@ private void printCallInfoStatistics() { .map(s -> s.replace("jhlabsproxies.", "")) .map(s -> s.replace("utils.", "")) .forEach(System.out::println); + System.out.println(); } private void log(String method) { @@ -123,25 +125,22 @@ public CallInfo(String method, long time, long lastTime, this.method = method; this.time = time; this.totalUnits = totalUnits; - duration = time - lastTime; + this.duration = time - lastTime; this.ste = ste; } public String asString(long totalDuration) { double timeSeconds = time / 1000.0; double durationSeconds = duration / 1000.0; - double durationPercentage = (duration * 100.0) / totalDuration; - String whatWithPercent = String - .format("%s (%.1f%%=>%.2fu)", method, durationPercentage, - (durationPercentage / 100.0) * totalUnits); + String whatWithPercent = format("%s (%.1f%%=>%.2fu)", + method, durationPercentage, (durationPercentage / 100.0) * totalUnits); - return String - .format("%.2fs (dur=%.2fs): %-21s at %s.%s(%s:%d)", - timeSeconds, durationSeconds, whatWithPercent, - ste.getClassName(), ste.getMethodName(), - ste.getFileName(), ste.getLineNumber()); + return format("%.2fs (dur=%.2fs): %-21s at %s.%s(%s:%d)", + timeSeconds, durationSeconds, whatWithPercent, + ste.getClassName(), ste.getMethodName(), + ste.getFileName(), ste.getLineNumber()); } } } diff --git a/src/main/java/pixelitor/utils/Icons.java b/src/main/java/pixelitor/utils/Icons.java index ad4c71807..c6116611e 100644 --- a/src/main/java/pixelitor/utils/Icons.java +++ b/src/main/java/pixelitor/utils/Icons.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -41,7 +41,6 @@ public final class Icons { private static final Icon dice2Icon = load("dice2.png"); private static final Icon northArrowIcon = loadThemed("north_arrow.gif", ThemedImageIcon.BLUE); private static final Icon southArrowIcon = loadThemed("south_arrow.gif", ThemedImageIcon.BLUE); - // private static final Icon textLayerIcon = load("text_layer.png", "text_layer_icon_dark.png"); private static final Icon textLayerIcon = VectorIcon.createNonTransparentThemed(TEXT_LAYER_ICON_SHAPE); private static final Icon adjLayerIcon = VectorIcon.createNonTransparentThemed(ADJ_LAYER_ICON_SHAPE); diff --git a/src/main/java/pixelitor/utils/Mirror.java b/src/main/java/pixelitor/utils/Mirror.java index 7d44e2d0d..6e7063ecb 100644 --- a/src/main/java/pixelitor/utils/Mirror.java +++ b/src/main/java/pixelitor/utils/Mirror.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -20,51 +20,31 @@ import pixelitor.tools.Symmetry; /** - * A mirroring effect achieved with negative scaling - * Not to be confused with {@link Symmetry} + * A mirroring effect achieved with negative scaling. + * Not to be confused with {@link Symmetry}. */ public enum Mirror { - NONE("None") { - @Override - public double getScaleX(double scaleAbs) { - return scaleAbs; - } - - @Override - public double getScaleY(double scaleAbs) { - return scaleAbs; - } - }, VERTICAL("Vertical") { - @Override - public double getScaleX(double scaleAbs) { - return -1 * scaleAbs; - } - - @Override - public double getScaleY(double scaleAbs) { - return scaleAbs; - } - }, HORIZONTAL("Horizontal") { - @Override - public double getScaleX(double scaleAbs) { - return scaleAbs; - } - - @Override - public double getScaleY(double scaleAbs) { - return -1 * scaleAbs; - } - }; + NONE("None", 1.0, 1.0), + VERTICAL("Vertical", -1.0, 1.0), + HORIZONTAL("Horizontal", 1.0, -1.0); private final String guiName; + private final double multX; + private final double multY; - Mirror(String guiName) { + Mirror(String guiName, double multX, double multY) { this.guiName = guiName; + this.multX = multX; + this.multY = multY; } - public abstract double getScaleX(double scaleAbs); + public double getScaleX(double scaleAbs) { + return scaleAbs * multX; + } - public abstract double getScaleY(double scaleAbs); + public double getScaleY(double scaleAbs) { + return scaleAbs * multY; + } @Override public String toString() { diff --git a/src/main/java/pixelitor/utils/Shapes.java b/src/main/java/pixelitor/utils/Shapes.java index 0e06edb79..046bc3728 100644 --- a/src/main/java/pixelitor/utils/Shapes.java +++ b/src/main/java/pixelitor/utils/Shapes.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -574,7 +574,7 @@ public static Shape createFlower(int n, double cx, double cy, double radius, dou public static Shape createHexagon(double cx, double cy, double radius) { double cos60 = 0.5; - double sin60 = 0.86602540378443864676372317075294; + double sin60 = 0.8660254037844386; double rCos60 = radius * cos60; double rSin60 = radius * sin60; diff --git a/src/main/java/pixelitor/utils/debug/Ansi.java b/src/main/java/pixelitor/utils/debug/Ansi.java index 358d181da..8ee80e559 100644 --- a/src/main/java/pixelitor/utils/debug/Ansi.java +++ b/src/main/java/pixelitor/utils/debug/Ansi.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -18,7 +18,8 @@ package pixelitor.utils.debug; /** - * Helper class with ANSI colors for debugging + * Utility class for creating ANSI-colored strings. + * Used only for debugging. */ public class Ansi { private static final String RESET = "\u001B[0m"; @@ -32,6 +33,7 @@ public class Ansi { private static final String WHITE = "\u001B[37m"; private Ansi() { + // prevent instantiation } public static String red(Object o) { diff --git a/src/main/java/pixelitor/utils/debug/AppNode.java b/src/main/java/pixelitor/utils/debug/AppNode.java index a6052058d..17947afba 100644 --- a/src/main/java/pixelitor/utils/debug/AppNode.java +++ b/src/main/java/pixelitor/utils/debug/AppNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -35,10 +35,10 @@ public AppNode() { add(Tools.getCurrent().createDebugNode("active tool")); add(History.createDebugNode()); - addImageNodes(); + addViewNodes(); } - private void addImageNodes() { + private void addViewNodes() { addInt("# open images", Views.getNumViews()); Views.forEachView(view -> { diff --git a/src/main/java/pixelitor/utils/debug/Debug.java b/src/main/java/pixelitor/utils/debug/Debug.java index 41fcb1bb2..c43ae0b6b 100644 --- a/src/main/java/pixelitor/utils/debug/Debug.java +++ b/src/main/java/pixelitor/utils/debug/Debug.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -209,7 +209,6 @@ public static void debugImage(Image img, String name) { } BufferedImage copy = ImageUtils.copyToBufferedImage(img); - View previousView = Views.getActive(); findCompByName(name).ifPresentOrElse( @@ -227,12 +226,12 @@ public static String debugColor(Color c) { c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha()); } - private static void replaceImageInDebugComp(Composition comp, BufferedImage copy) { + private static void replaceImageInDebugComp(Composition comp, BufferedImage newImg) { Canvas canvas = comp.getCanvas(); - comp.getActiveDrawableOrThrow().setImage(copy); + comp.getActiveDrawableOrThrow().setImage(newImg); - if (canvas.hasDifferentSizeThan(copy)) { - canvas.resize(copy.getWidth(), copy.getHeight(), comp.getView(), true); + if (canvas.hasDifferentSizeThan(newImg)) { + canvas.resize(newImg.getWidth(), newImg.getHeight(), comp.getView(), true); } comp.repaint(); @@ -260,23 +259,19 @@ public static void debugShape(Shape shape, String name) { } public static void debugRaster(Raster raster, String name) { - ColorModel colorModel; - int numBands = raster.getNumBands(); - - if (numBands == 4) { // normal color image - colorModel = new DirectColorModel( + ColorModel colorModel = switch (raster.getNumBands()) { + // normal color image + case 4 -> new DirectColorModel( ColorSpace.getInstance(ColorSpace.CS_sRGB), 32, 0x00_FF_00_00, 0x00_00_FF_00, 0x00_00_00_FF, 0xFF_00_00_00, true, DataBuffer.TYPE_INT); - } else if (numBands == 1) { // grayscale image - int[] nBits = {8}; - colorModel = new ComponentColorModel( - ColorSpace.getInstance(ColorSpace.CS_GRAY), nBits, + // grayscale image + case 1 -> new ComponentColorModel( + ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[]{8}, false, true, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); - } else { - throw new IllegalStateException("numBands = " + numBands); - } + default -> throw new IllegalStateException("numBands = " + raster.getNumBands()); + }; Raster correctlyTranslated = raster.createChild( raster.getMinX(), raster.getMinY(), @@ -289,6 +284,7 @@ public static void debugRaster(Raster raster, String name) { } public static void debugRasterWithEmptySpace(Raster raster) { + // This image will include the minX/minY part of the Raster BufferedImage debugImage = new BufferedImage( raster.getMinX() + raster.getWidth(), raster.getMinY() + raster.getHeight(), @@ -303,9 +299,8 @@ public static void keepSwitchingToolsRandomly() { //noinspection InfiniteLoopStatement while (true) { Utils.sleep(1, TimeUnit.SECONDS); - - Runnable changeToolOnEDTTask = () -> Tools.getRandomTool().activate(); - GUIUtils.invokeAndWait(changeToolOnEDTTask); + GUIUtils.invokeAndWait(() -> + Tools.getRandomTool().activate()); } }; new Thread(backgroundTask).start(); @@ -336,6 +331,11 @@ public static void showAddTextLayerDialog() { AddTextLayerAction.INSTANCE.actionPerformed(null); } + public static void startFilter(String filterName) { + FilterAction action = Filters.getFilterActionByName(filterName); + EventQueue.invokeLater(() -> action.actionPerformed(null)); + } + public static void addMaskAndShowIt() { AddLayerMaskAction.INSTANCE.actionPerformed(null); View view = Views.getActive(); @@ -343,11 +343,6 @@ public static void addMaskAndShowIt() { MaskViewMode.SHOW_MASK.activate(view, layer); } - public static void startFilter(String filterName) { - FilterAction action = Filters.getFilterActionByName(filterName); - EventQueue.invokeLater(() -> action.actionPerformed(null)); - } - public static void addNewImageWithMask() { NewImage.addNewImage(FillType.WHITE, 600, 400, "Test"); Views.getActiveLayer().addMask(LayerMaskAddType.PATTERN); @@ -430,10 +425,6 @@ public static void throwTestError() { } } - private static String debugDimension(Dimension d) { - return d.width + "x" + d.height; - } - public static String debugJComponent(JComponent c) { return String.format(""" size = %s @@ -445,16 +436,20 @@ public static String debugJComponent(JComponent c) { border insets = %s doubleBuffered = %s """, - debugDimension(c.getSize()), - debugDimension(c.getPreferredSize()), - debugDimension(c.getMinimumSize()), - debugDimension(c.getMaximumSize()), + dimensionAsString(c.getSize()), + dimensionAsString(c.getPreferredSize()), + dimensionAsString(c.getMinimumSize()), + dimensionAsString(c.getMaximumSize()), c.getInsets().toString(), c.getBorder().toString(), c.getBorder().getBorderInsets(c).toString(), c.isDoubleBuffered()); } + private static String dimensionAsString(Dimension d) { + return d.width + "x" + d.height; + } + public static void serializeAllFilters() { Filters.forEachSmartFilter(Debug::serialize); } @@ -529,7 +524,7 @@ private static void addSmartFilter(Composition comp, Filter filter) { smartObject.updateIconImage(); } - public static String debugMouseEvent(MouseEvent e) { + public static String mouseEventAsString(MouseEvent e) { String action = switch (e.getID()) { case MouseEvent.MOUSE_CLICKED -> "CLICKED"; case MouseEvent.MOUSE_PRESSED -> "PRESSED"; diff --git a/src/main/java/pixelitor/utils/debug/DebugNode.java b/src/main/java/pixelitor/utils/debug/DebugNode.java index dadc1a50e..160d4235c 100644 --- a/src/main/java/pixelitor/utils/debug/DebugNode.java +++ b/src/main/java/pixelitor/utils/debug/DebugNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -69,7 +69,7 @@ public String toJSON() { if (child instanceof DebugNode dn) { text = dn.toJSON(); } else if (child instanceof DefaultMutableTreeNode defaultNode) { - text = ((StringUserObject) defaultNode.getUserObject()).toJSON(); + text = ((StringKeyValue) defaultNode.getUserObject()).toJSON(); } else { throw new IllegalStateException(); } @@ -151,7 +151,7 @@ public void addAsClass(String name, Object o) { } private void addNode(String key, String value) { - add(new DefaultMutableTreeNode(new StringUserObject(key, value))); + add(new DefaultMutableTreeNode(new StringKeyValue(key, value))); } private static void indent(StringBuilder sb, int indentLevel) { @@ -162,7 +162,7 @@ private static void indent(StringBuilder sb, int indentLevel) { /** * Allow a leaf node to have two string representations: a GUI text and a JSON. */ - private record StringUserObject(String key, String value) { + private record StringKeyValue(String key, String value) { public String toJSON() { return "\"" + key + "\": " + value + ","; } diff --git a/src/main/java/pixelitor/utils/debug/DebugNodes.java b/src/main/java/pixelitor/utils/debug/DebugNodes.java index 46226b1e7..4a7006b5a 100644 --- a/src/main/java/pixelitor/utils/debug/DebugNodes.java +++ b/src/main/java/pixelitor/utils/debug/DebugNodes.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -21,6 +21,7 @@ import pixelitor.utils.Utils; import java.awt.GraphicsEnvironment; +import java.awt.Rectangle; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.image.*; @@ -155,4 +156,15 @@ public static DebugNode createTransformNode(AffineTransform at, String name) { return node; } + + public static DebugNode createRectangleNode(Rectangle rect, String name) { + DebugNode node = new DebugNode(name, rect); + + node.addInt("x", rect.x); + node.addInt("y", rect.y); + node.addInt("width", rect.width); + node.addInt("height", rect.height); + + return node; + } } diff --git a/src/main/java/pixelitor/utils/test/Assertions.java b/src/main/java/pixelitor/utils/test/Assertions.java index 76c8df4fd..4b49aa113 100644 --- a/src/main/java/pixelitor/utils/test/Assertions.java +++ b/src/main/java/pixelitor/utils/test/Assertions.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -34,10 +34,10 @@ public static boolean callingClassIs(String name) { } @SuppressWarnings("SameReturnValue") - public static boolean checkRasterMinimum(BufferedImage newImage) { + public static boolean rasterStartsAtZero(BufferedImage newImage) { var raster = newImage.getRaster(); if (raster.getMinX() != 0 || raster.getMinY() != 0) { - throw new IllegalArgumentException("Raster " + raster + + throw new IllegalStateException("Raster " + raster + " has minX or minY not equal to zero: " + raster.getMinX() + ' ' + raster.getMinY()); } diff --git a/src/main/java/pixelitor/utils/test/PixelitorEventListener.java b/src/main/java/pixelitor/utils/test/PixelitorEventListener.java index d5231c1ea..97b021390 100644 --- a/src/main/java/pixelitor/utils/test/PixelitorEventListener.java +++ b/src/main/java/pixelitor/utils/test/PixelitorEventListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -34,7 +34,8 @@ public class PixelitorEventListener implements ActiveHolderListener, public PixelitorEventListener() { if (GUIMode.isFinal()) { - throw new IllegalStateException("should be used only for debugging"); + // should be used only for debugging + throw new IllegalStateException(); } } @@ -52,7 +53,7 @@ public void numLayersChanged(LayerHolder holder, int newLayerCount) { @Override public void layerActivated(Layer layer) { - String type = "layer targeted: " + layer.getName(); + String type = "layer activated: " + layer.getName(); Events.postListenerEvent(type, layer.getComp(), layer); } @@ -78,8 +79,8 @@ public void allViewsClosed() { @Override public void viewActivated(View oldView, View newView) { - String oldCVName = oldView == null ? "null" : oldView.getName(); - String type = format("view activated %s => %s", oldCVName, newView.getName()); + String oldViewName = oldView == null ? "null" : oldView.getName(); + String type = format("view activated %s => %s", oldViewName, newView.getName()); Events.postListenerEvent(type, newView.getComp(), null); } } diff --git a/src/main/java/pixelitor/utils/test/RandomGUITest.java b/src/main/java/pixelitor/utils/test/RandomGUITest.java index 93b8f4ca3..9a1dbf1ae 100644 --- a/src/main/java/pixelitor/utils/test/RandomGUITest.java +++ b/src/main/java/pixelitor/utils/test/RandomGUITest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -46,10 +46,7 @@ import pixelitor.tools.Tools; import pixelitor.tools.gui.ToolSettingsPanelContainer; import pixelitor.tools.pen.PenTool; -import pixelitor.utils.MemoryInfo; -import pixelitor.utils.Messages; -import pixelitor.utils.Rnd; -import pixelitor.utils.Utils; +import pixelitor.utils.*; import pixelitor.utils.debug.Debug; import javax.swing.*; @@ -86,41 +83,49 @@ public class RandomGUITest { public static final char EXIT_KEY_CHAR = 'Q'; public static final char PAUSE_KEY_CHAR = 'A'; - private static final Random rand = new Random(); + + private static final boolean PRINT_MEMORY = false; + private static final boolean NO_HIDE_SHOW = true; // no view operations if set to true + + private final Random rand = new Random(); // set to null to select random tools - private static final Tool preferredTool = null; + private final Tool preferredTool = null; // set to null to select random filters - private static final Filter preferredFilter = null; - private static final ParametrizedFilter preferredTweenFilter = null; + private final Filter preferredFilter = null; + private final ParametrizedFilter preferredTweenFilter = null; - private static final boolean singleImageTest = false; - private static final boolean noHideShow = true; // no view operations if set to true - private static final DateTimeFormatter DATE_FORMAT + private final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"); - private static volatile boolean stopRunning = false; + private boolean running = false; + private volatile boolean stopRunning = false; - private static final WeightedCaller weightedCaller = new WeightedCaller(); - private static final boolean PRINT_MEMORY = false; + private final WeightedCaller weightedCaller = new WeightedCaller(); - private static boolean running = false; + private final boolean verbose = "true".equals(System.getProperty("verbose")); - private static final boolean verbose = "true".equals(System.getProperty("verbose")); - - private static Rectangle startBounds; + private Rectangle startBounds; private static final boolean enableCopyPaste = false; - private static int numPastedImages = 0; + private int numPastedImages = 0; + + private static RandomGUITest INSTANCE = null; - /** - * Utility class with static methods - */ private RandomGUITest() { } - public static void start() { + public static RandomGUITest get() { + assert Threads.calledOnEDT(); + if (INSTANCE == null) { + //noinspection NonThreadSafeLazyInitialization + INSTANCE = new RandomGUITest(); + } + return INSTANCE; + } + + public void start() { if (GUIMode.isFinal()) { Messages.showError("Error", "Build is FINAL"); return; @@ -130,7 +135,7 @@ public static void start() { return; } running = true; - startBounds = getWindowBounds(); + startBounds = PixelitorWindow.get().getBounds(); PixelitorWindow.get().setAlwaysOnTop(true); @@ -162,7 +167,7 @@ public static void start() { try { robot = new Robot(); } catch (AWTException e) { - e.printStackTrace(); + Messages.showException(e); } setupActions(robot); @@ -179,11 +184,18 @@ public static void start() { } public static boolean isRunning() { - return running; + if (INSTANCE == null) { + return false; + } + return INSTANCE.running; } public static void stop() { - stopRunning = true; + if (INSTANCE == null) { + return; + } + INSTANCE.stopRunning = true; + var pw = PixelitorWindow.get(); // the window can be null if an exception is thrown at startup, // and we get here from the uncaught exception handler @@ -192,7 +204,7 @@ public static void stop() { } } - private static SwingWorker createSwingWorker(Robot robot) { + private SwingWorker createSwingWorker(Robot robot) { return new SwingWorker<>() { @Override public Void doInBackground() { @@ -201,7 +213,7 @@ public Void doInBackground() { }; } - private static Void backgroundRunner(Robot robot) { + private Void backgroundRunner(Robot robot) { int numTests = 8000; // must be > 100 int onePercent = numTests / 100; @@ -261,7 +273,7 @@ private static void printProgressPercentage(int numTests, int onePercent, int ro private static void tryToRegainWindowFocus(int attempts) { if (attempts <= 0) { - return; + return; // give up } System.out.println("RandomGUITest: trying to regain window focus"); @@ -273,17 +285,14 @@ private static void tryToRegainWindowFocus(int attempts) { if (!GUIUtils.appHasFocus()) { Utils.sleep(1, TimeUnit.SECONDS); + //noinspection TailRecursion tryToRegainWindowFocus(attempts - 1); } } - private static Rectangle getWindowBounds() { - return PixelitorWindow.get().getBounds(); - } - // generates a random point within the main window relative to the screen - private static Point generateRandomPoint() { - Rectangle windowBounds = getWindowBounds(); + private Point generateRandomPoint() { + Rectangle windowBounds = PixelitorWindow.get().getBounds(); if (!windowBounds.equals(startBounds)) { // Window moved. Shouldn't happen, but as a workaround // restore it to the starting state @@ -316,25 +325,27 @@ private static Point generateRandomPoint() { Rnd.intInRange(minY, maxY)); } - private static void finishRunning() { + private void finishRunning() { resetGUI(); running = false; } private static void resetGUI() { WorkSpace.resetDefaults(PixelitorWindow.get()); - resetTabsUI(); + if (ImageArea.currentModeIs(FRAMES)) { + ImageArea.changeUI(); + } PixelitorWindow.get().setAlwaysOnTop(false); } - private static void log(String msg) { + private void log(String msg) { if (verbose) { System.out.println(msg); } Events.postRandomTestEvent(msg); } - private static void randomMove(Robot robot) { + private void randomMove(Robot robot) { Point randomPoint = generateRandomPoint(); int x = randomPoint.x; int y = randomPoint.y; @@ -349,7 +360,7 @@ private static boolean canUseTool(Tool tool) { return true; } - private static void randomDrag(Robot robot) { + private void randomDrag(Robot robot) { Tool tool = Tools.getCurrent(); if (!canUseTool(tool)) { return; @@ -369,7 +380,7 @@ private static void randomDrag(Robot robot) { + "drag to (" + x + ", " + y + ')'); } - private static void randomClick(Robot robot) { + private void randomClick(Robot robot) { Tool tool = Tools.getCurrent(); if (!canUseTool(tool)) { return; @@ -380,7 +391,7 @@ private static void randomClick(Robot robot) { log(msg); } - private static String runWithModifiers(Robot robot, Runnable task) { + private String runWithModifiers(Robot robot, Runnable task) { boolean shiftDown = rand.nextBoolean(); if (shiftDown) { robot.keyPress(VK_SHIFT); @@ -433,12 +444,12 @@ private static String runWithModifiers(Robot robot, Runnable task) { return Debug.modifiersToString(ctrlDown, altDown, shiftDown, rightMouse, false); } - private static void randomColors() { + private void randomColors() { log("randomize colors"); randomizeColors(); } - private static void randomFilter() { + private void randomFilter() { Drawable dr = Views.getActiveDrawable(); if (dr == null) { return; @@ -506,7 +517,7 @@ private static void randomFilter() { } } - private static void randomTween() { + private void randomTween() { Drawable dr = Views.getActiveDrawable(); if (dr == null) { return; @@ -560,7 +571,7 @@ private static void randomTween() { } } - private static void randomFitTo() { + private void randomFitTo() { double r = Math.random(); if (r > 0.75) { log("fit active to space"); @@ -577,7 +588,7 @@ private static void randomFitTo() { } } - private static final int[] keyCodes = {VK_1, + private final int[] keyCodes = {VK_1, VK_ENTER, VK_ESCAPE, VK_BACK_SPACE, // skip A, because it's the stop keystroke VK_B, VK_C, @@ -600,7 +611,7 @@ private static void randomFitTo() { VK_RIGHT, VK_LEFT, VK_UP, VK_DOWN }; - private static void randomKey(Robot robot) { + private void randomKey(Robot robot) { int randomIndex = rand.nextInt(keyCodes.length); int keyCode = keyCodes[randomIndex]; @@ -624,11 +635,11 @@ private static void pressCtrlKey(Robot robot, int keyCode) { robot.keyRelease(VK_CONTROL); } - private static void randomZoom() { - Views.onActiveView(RandomGUITest::setRandomZoom); + private void randomZoom() { + Views.onActiveView(this::setRandomZoom); } - private static void setRandomZoom(View view) { + private void setRandomZoom(View view) { ZoomLevel randomZoomLevel = calcRandomZoomLevel(); log("zoom " + view.getName() + ", zoom level = " + randomZoomLevel); @@ -663,7 +674,7 @@ private static Point pickRandomPointOn(View view) { return new Point(randX, randY); } - private static void randomZoomOut() { + private void randomZoomOut() { View view = Views.getActive(); if (view != null) { log("zoom out " + view.getName()); @@ -677,13 +688,13 @@ private static void randomZoomOut() { } } - private static void repeat() { + private void repeat() { log("repeat (dispatch Ctrl-F)"); dispatchKey(VK_F, 'F', CTRL_DOWN_MASK); } - private static void randomUndoRedo() { + private void randomUndoRedo() { if (History.canUndo()) { log("undo " + History.getEditToBeUndoneName()); @@ -704,14 +715,14 @@ private static void randomUndoRedo() { } } - private static void randomCrop() { + private void randomCrop() { boolean enabled = SelectionActions.areEnabled(); if (enabled) { runAction(SelectionActions.getCrop()); } } - private static void randomFade() { + private void randomFade() { if (!History.canFade()) { return; } @@ -726,12 +737,12 @@ private static void randomFade() { dr.startFilter(fade, FILTER_WITHOUT_DIALOG); } - private static void randomizeToolSettings() { + private void randomizeToolSettings() { log("randomize tool settings for " + Tools.getCurrent()); ToolSettingsPanelContainer.get().randomizeToolSettings(); } - private static void arrangeWindows() { + private void arrangeWindows() { if (ImageArea.currentModeIs(TABS)) { return; } @@ -745,51 +756,51 @@ private static void arrangeWindows() { } } - private static void changeImageArea() { + private void changeImageArea() { log("change image area from " + ImageArea.getMode()); ImageArea.changeUI(); } - private static void deselect() { + private void deselect() { if (SelectionActions.areEnabled()) { runAction(SelectionActions.getDeselect()); } } - private static void showHideSelection(Robot robot) { + private void showHideSelection(Robot robot) { log("showHideSelection"); pressCtrlKey(robot, VK_H); } - private static void layerToCanvasSize() { + private void layerToCanvasSize() { log("layer to canvas size"); Views.onActiveComp(Composition::activeLayerToCanvasSize); } - private static void fitCanvasToLayers() { + private void fitCanvasToLayers() { log("fit canvas to layers"); Views.onActiveComp(Composition::fitCanvasToLayers); } - private static void invertSelection() { + private void invertSelection() { if (SelectionActions.areEnabled()) { runAction(SelectionActions.getInvert()); } } - private static void traceWithCurrentBrush() { + private void traceWithCurrentBrush() { if (canTrace()) { runAction(PenTool.getTraceWithBrushAction()); } } - private static void traceWithCurrentEraser() { + private void traceWithCurrentEraser() { if (canTrace()) { runAction(PenTool.getTraceWithEraserAction()); } } - private static void traceWithCurrentSmudge() { + private void traceWithCurrentSmudge() { if (canTrace()) { runAction(PenTool.getTraceWithSmudgeAction()); } @@ -803,7 +814,7 @@ private static boolean canTrace() { return comp.hasActivePath() && comp.activeLayerAcceptsToolDrawing(); } - private static void randomRotateFlip() { + private void randomRotateFlip() { int r = rand.nextInt(5); switch (r) { case 0 -> runAction(new Rotate(ANGLE_90)); @@ -815,14 +826,14 @@ private static void randomRotateFlip() { } } - private static void activateRandomView() { + private void activateRandomView() { View view = Views.activateRandomView(); if (view != null) { log("activated random view " + view.getName()); } } - private static void layerOrderChange() { + private void layerOrderChange() { var comp = Views.getActiveComp(); int r = rand.nextInt(6); switch (r) { @@ -836,37 +847,37 @@ private static void layerOrderChange() { } } - private static void moveActiveLayerToTop(Composition comp) { + private void moveActiveLayerToTop(Composition comp) { log("layer order change: active to top"); comp.getActiveHolder().moveActiveLayerToTop(); } - private static void moveActiveLayerToBottom(Composition comp) { + private void moveActiveLayerToBottom(Composition comp) { log("layer order change: active to bottom"); comp.getActiveHolder().moveActiveLayerToBottom(); } - private static void raiseLayerSelection(Composition comp) { + private void raiseLayerSelection(Composition comp) { log("layer selection change: raise selection"); comp.getActiveHolder().raiseLayerSelection(); } - private static void lowerLayerSelection(Composition comp) { + private void lowerLayerSelection(Composition comp) { log("layer selection change: lower selection"); comp.getActiveHolder().lowerLayerSelection(); } - private static void moveActiveLayerUp(Composition comp) { + private void moveActiveLayerUp(Composition comp) { log("layer order change: active up"); comp.getActiveHolder().moveActiveLayer(true); } - private static void moveActiveLayerDown(Composition comp) { + private void moveActiveLayerDown(Composition comp) { log("layer order change: active down"); comp.getActiveHolder().moveActiveLayer(false); } - private static void layerMerge() { + private void layerMerge() { var comp = Views.getActiveComp(); if (rand.nextBoolean()) { @@ -881,7 +892,7 @@ private static void layerMerge() { } } - private static void layerAddDelete() { + private void layerAddDelete() { if (rand.nextBoolean()) { if (AddNewLayerAction.INSTANCE.isEnabled()) { runAction(AddNewLayerAction.INSTANCE); @@ -893,8 +904,8 @@ private static void layerAddDelete() { } } - private static void randomHideShow() { - if (noHideShow) { + private void randomHideShow() { + if (NO_HIDE_SHOW) { return; } @@ -909,7 +920,7 @@ private static void randomHideShow() { } } - private static void randomCopy() { + private void randomCopy() { if (rand.nextBoolean()) { runAction(CopyAction.COPY_LAYER); } else { @@ -917,7 +928,7 @@ private static void randomCopy() { } } - private static void runAction(Action action) { + private void runAction(Action action) { String msg = format("action \"%s\" (class: \"%s\")", action.getValue(Action.NAME), action.getClass().getSimpleName()); @@ -926,15 +937,12 @@ private static void runAction(Action action) { action.actionPerformed(new ActionEvent("", 0, "")); } - private static void randomPaste() { + private void randomPaste() { if (numPastedImages > 3) { return; } int r = rand.nextInt(10); if (r == 0) { - if (singleImageTest) { - return; - } runAction(new PasteAction(PasteDestination.NEW_IMAGE)); numPastedImages++; } else if (r == 1) { @@ -944,7 +952,7 @@ private static void randomPaste() { // paste as mask? } - private static void randomChangeLayerOpacityOrBlending() { + private void randomChangeLayerOpacityOrBlending() { Layer layer = Views.getActiveLayer(); if (rand.nextBoolean()) { float opacity = layer.getOpacity(); @@ -967,7 +975,7 @@ private static void randomChangeLayerOpacityOrBlending() { } } - private static void randomChangeLayerVisibility() { + private void randomChangeLayerVisibility() { Layer layer = Views.getActiveLayer(); boolean visible = layer.isVisible(); if (rand.nextBoolean()) { @@ -985,7 +993,7 @@ private static void randomChangeLayerVisibility() { } } - private static void randomTool() { + private void randomTool() { Tool tool; if (preferredTool != null) { if (rand.nextBoolean()) { @@ -1008,7 +1016,7 @@ private static void randomTool() { } } - private static void newRandomTextLayer() { + private void newRandomTextLayer() { Composition comp = Views.getActiveComp(); MaskViewMode oldMaskViewMode = comp.getView().getMaskViewMode(); Layer activeLayerBefore = comp.getActiveLayer(); @@ -1023,32 +1031,32 @@ private static void newRandomTextLayer() { log("new text layer: " + textLayer.getName()); } - private static void newColorFillLayer() { + private void newColorFillLayer() { log("new color fill layer"); ColorFillLayer.createNew(Views.getActiveComp()); } - private static void newGradientFillLayer() { + private void newGradientFillLayer() { log("new gradient fill layer"); GradientFillLayer.createNew(Views.getActiveComp()); } - private static void newShapesLayer() { + private void newShapesLayer() { log("new shapes layer"); ShapesLayer.createNew(Views.getActiveComp()); } - private static void newEmptyLayerGroup() { + private void newEmptyLayerGroup() { log("new empty layer group"); Views.getActiveComp().getHolderForNewLayers().addEmptyGroup(); } - private static void convertVisibleToGroup() { + private void convertVisibleToGroup() { log("convert visible to group"); Views.getActiveComp().getHolderForGrouping().convertVisibleLayersToGroup(); } - private static void convertToSmartObject() { + private void convertToSmartObject() { Layer layer = Views.getActiveLayer(); if (!(layer instanceof SmartObject)) { log("Convert Layer to Smart Object"); @@ -1056,7 +1064,7 @@ private static void convertToSmartObject() { } } - private static void randomRasterizeLayer() { + private void randomRasterizeLayer() { Layer layer = Views.getActiveLayer(); if (layer.isRasterizable()) { log("rasterize " + layer.getTypeStringLC() + " " + layer.getName()); @@ -1064,7 +1072,7 @@ private static void randomRasterizeLayer() { } } - private static void randomNewAdjustmentLayer() { + private void randomNewAdjustmentLayer() { log("new adj layer"); var comp = Views.getActiveComp(); var adjustmentLayer = new AdjustmentLayer(comp, "Invert", new Invert()); @@ -1074,7 +1082,7 @@ private static void randomNewAdjustmentLayer() { .add(adjustmentLayer); } - private static void randomSetLayerMaskEditMode() { + private void randomSetLayerMaskEditMode() { Layer layer = Views.getActiveLayer(); if (!layer.hasMask()) { return; @@ -1100,7 +1108,7 @@ private static void randomSetLayerMaskEditMode() { } // (add, delete, apply, link) - private static void randomLayerMaskAction() { + private void randomLayerMaskAction() { Layer layer = Views.getActiveLayer(); if (!layer.hasMask()) { assert AddLayerMaskAction.INSTANCE.isEnabled(); @@ -1132,7 +1140,7 @@ private static void randomLayerMaskAction() { } } - private static ParametrizedFilter getRandomTweenFilter() { + private ParametrizedFilter getRandomTweenFilter() { if (preferredTweenFilter != null) { assert !preferredTweenFilter.excludedFromAnimation(); assert preferredTweenFilter.getParamSet().isAnimatable(); @@ -1142,7 +1150,7 @@ private static ParametrizedFilter getRandomTweenFilter() { return (ParametrizedFilter) filterAction.getFilter(); } - private static void randomEnlargeCanvas() { + private void randomEnlargeCanvas() { int north = rand.nextInt(3); int east = rand.nextInt(3); int south = rand.nextInt(3); @@ -1153,7 +1161,7 @@ private static void randomEnlargeCanvas() { new EnlargeCanvas(north, east, south, west).process(comp); } - private static void randomGuides() { + private void randomGuides() { var comp = Views.getActiveComp(); float v = rand.nextFloat(); if (v < 0.2) { @@ -1162,10 +1170,10 @@ private static void randomGuides() { return; } new Guides.Builder(comp.getView(), false) - .build(false, RandomGUITest::randomGuidesSetup); + .build(false, this::randomGuidesSetup); } - private static void randomGuidesSetup(Guides guides) { + private void randomGuidesSetup(Guides guides) { if (rand.nextBoolean()) { log("add relative horizontal guide"); guides.addHorRelative(rand.nextFloat()); @@ -1175,7 +1183,7 @@ private static void randomGuidesSetup(Guides guides) { } } - private static void reload(Robot robot) { + private void reload(Robot robot) { if (rand.nextFloat() < 0.1) { var comp = Views.getActiveComp(); if (comp.getFile() != null) { @@ -1186,7 +1194,7 @@ private static void reload(Robot robot) { } // prevents paths from growing too large - private static void setPathsToNull() { + private void setPathsToNull() { log("set paths to null"); Views.forEachView(view -> { // don't touch the active, as its path might be edited just now @@ -1204,71 +1212,65 @@ private static void dispatchKey(int keyCode, char keyChar, int modifiers) { System.currentTimeMillis(), modifiers, keyCode, keyChar)); } - private static void resetTabsUI() { - if (ImageArea.currentModeIs(FRAMES)) { - ImageArea.changeUI(); - } - } - - private static void setupActions(Robot robot) { + private void setupActions(Robot robot) { // random move weightedCaller.registerAction(10, () -> randomMove(robot)); weightedCaller.registerAction(20, () -> randomDrag(robot)); weightedCaller.registerAction(5, () -> randomClick(robot)); - weightedCaller.registerAction(2, RandomGUITest::repeat); - weightedCaller.registerAction(5, RandomGUITest::randomUndoRedo); - weightedCaller.registerAction(1, RandomGUITest::randomCrop); - weightedCaller.registerAction(1, RandomGUITest::randomFade); - weightedCaller.registerAction(2, RandomGUITest::randomizeToolSettings); - weightedCaller.registerAction(1, RandomGUITest::arrangeWindows); - weightedCaller.registerAction(3, RandomGUITest::changeImageArea); - weightedCaller.registerAction(1, RandomGUITest::randomColors); - weightedCaller.registerAction(3, RandomGUITest::randomFilter); - weightedCaller.registerAction(1, RandomGUITest::randomTween); - weightedCaller.registerAction(1, RandomGUITest::randomFitTo); + weightedCaller.registerAction(2, this::repeat); + weightedCaller.registerAction(5, this::randomUndoRedo); + weightedCaller.registerAction(1, this::randomCrop); + weightedCaller.registerAction(1, this::randomFade); + weightedCaller.registerAction(2, this::randomizeToolSettings); + weightedCaller.registerAction(1, this::arrangeWindows); + weightedCaller.registerAction(3, this::changeImageArea); + weightedCaller.registerAction(1, this::randomColors); + weightedCaller.registerAction(3, this::randomFilter); + weightedCaller.registerAction(1, this::randomTween); + weightedCaller.registerAction(1, this::randomFitTo); weightedCaller.registerAction(3, () -> randomKey(robot)); weightedCaller.registerAction(1, () -> reload(robot)); - weightedCaller.registerAction(1, RandomGUITest::randomZoom); - weightedCaller.registerAction(1, RandomGUITest::randomZoomOut); - weightedCaller.registerAction(10, RandomGUITest::deselect); + weightedCaller.registerAction(1, this::randomZoom); + weightedCaller.registerAction(1, this::randomZoomOut); + weightedCaller.registerAction(10, this::deselect); weightedCaller.registerAction(1, () -> showHideSelection(robot)); - weightedCaller.registerAction(1, RandomGUITest::layerToCanvasSize); - weightedCaller.registerAction(1, RandomGUITest::fitCanvasToLayers); - weightedCaller.registerAction(1, RandomGUITest::invertSelection); - weightedCaller.registerAction(1, RandomGUITest::traceWithCurrentBrush); - weightedCaller.registerAction(1, RandomGUITest::traceWithCurrentEraser); - weightedCaller.registerAction(1, RandomGUITest::traceWithCurrentSmudge); - weightedCaller.registerAction(1, RandomGUITest::randomRotateFlip); - weightedCaller.registerAction(5, RandomGUITest::activateRandomView); - weightedCaller.registerAction(1, RandomGUITest::layerOrderChange); - weightedCaller.registerAction(5, RandomGUITest::layerMerge); - weightedCaller.registerAction(3, RandomGUITest::layerAddDelete); - weightedCaller.registerAction(1, RandomGUITest::randomHideShow); + weightedCaller.registerAction(1, this::layerToCanvasSize); + weightedCaller.registerAction(1, this::fitCanvasToLayers); + weightedCaller.registerAction(1, this::invertSelection); + weightedCaller.registerAction(1, this::traceWithCurrentBrush); + weightedCaller.registerAction(1, this::traceWithCurrentEraser); + weightedCaller.registerAction(1, this::traceWithCurrentSmudge); + weightedCaller.registerAction(1, this::randomRotateFlip); + weightedCaller.registerAction(5, this::activateRandomView); + weightedCaller.registerAction(1, this::layerOrderChange); + weightedCaller.registerAction(5, this::layerMerge); + weightedCaller.registerAction(3, this::layerAddDelete); + weightedCaller.registerAction(1, this::randomHideShow); if (enableCopyPaste) { - weightedCaller.registerAction(1, RandomGUITest::randomCopy); - weightedCaller.registerAction(1, RandomGUITest::randomPaste); - } - weightedCaller.registerAction(1, RandomGUITest::randomChangeLayerOpacityOrBlending); - weightedCaller.registerAction(1, RandomGUITest::randomChangeLayerVisibility); - weightedCaller.registerAction(5, RandomGUITest::randomTool); - weightedCaller.registerAction(1, RandomGUITest::randomEnlargeCanvas); - weightedCaller.registerAction(2, RandomGUITest::newRandomTextLayer); - weightedCaller.registerAction(1, RandomGUITest::newColorFillLayer); - weightedCaller.registerAction(1, RandomGUITest::newGradientFillLayer); - weightedCaller.registerAction(1, RandomGUITest::newShapesLayer); - weightedCaller.registerAction(1, RandomGUITest::newEmptyLayerGroup); - weightedCaller.registerAction(1, RandomGUITest::convertVisibleToGroup); - weightedCaller.registerAction(1, RandomGUITest::convertToSmartObject); - weightedCaller.registerAction(5, RandomGUITest::randomRasterizeLayer); - weightedCaller.registerAction(4, RandomGUITest::randomGuides); - weightedCaller.registerAction(4, RandomGUITest::setPathsToNull); + weightedCaller.registerAction(1, this::randomCopy); + weightedCaller.registerAction(1, this::randomPaste); + } + weightedCaller.registerAction(1, this::randomChangeLayerOpacityOrBlending); + weightedCaller.registerAction(1, this::randomChangeLayerVisibility); + weightedCaller.registerAction(5, this::randomTool); + weightedCaller.registerAction(1, this::randomEnlargeCanvas); + weightedCaller.registerAction(2, this::newRandomTextLayer); + weightedCaller.registerAction(1, this::newColorFillLayer); + weightedCaller.registerAction(1, this::newGradientFillLayer); + weightedCaller.registerAction(1, this::newShapesLayer); + weightedCaller.registerAction(1, this::newEmptyLayerGroup); + weightedCaller.registerAction(1, this::convertVisibleToGroup); + weightedCaller.registerAction(1, this::convertToSmartObject); + weightedCaller.registerAction(5, this::randomRasterizeLayer); + weightedCaller.registerAction(4, this::randomGuides); + weightedCaller.registerAction(4, this::setPathsToNull); if (Features.enableExperimental) { - weightedCaller.registerAction(2, RandomGUITest::randomNewAdjustmentLayer); + weightedCaller.registerAction(2, this::randomNewAdjustmentLayer); } - weightedCaller.registerAction(7, RandomGUITest::randomSetLayerMaskEditMode); - weightedCaller.registerAction(10, RandomGUITest::randomLayerMaskAction); + weightedCaller.registerAction(7, this::randomSetLayerMaskEditMode); + weightedCaller.registerAction(10, this::randomLayerMaskAction); } } diff --git a/src/main/java/pixelitor/utils/test/SplashImageCreator.java b/src/main/java/pixelitor/utils/test/SplashImageCreator.java index 7ea1aa598..dc51e4db0 100644 --- a/src/main/java/pixelitor/utils/test/SplashImageCreator.java +++ b/src/main/java/pixelitor/utils/test/SplashImageCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -167,13 +167,7 @@ private static Font createSplashFont(String name, int size) { return font; } - private static ImageLayer addNewLayer(Composition comp, String name) { - ImageLayer imageLayer = comp.addNewEmptyImageLayer(name, false); - imageLayer.setName(name, true); - return imageLayer; - } - - private static TextLayer addNewTextLayer(Composition comp, String name, TextSettings settings, int translationY) { + private static void addNewTextLayer(Composition comp, String name, TextSettings settings, int translationY) { var layer = new TextLayer(comp, name, settings); layer.setTranslation(0, translationY); @@ -182,7 +176,6 @@ private static TextLayer addNewTextLayer(Composition comp, String name, TextSett .withHistory("add " + name) .atPosition(TOP) .add(layer); - return layer; } private static void addTextLayer(Composition comp, String text, diff --git a/src/main/java/pixelitor/utils/test/WeightedCaller.java b/src/main/java/pixelitor/utils/test/WeightedCaller.java index 2bed86a50..4a62cb4f4 100644 --- a/src/main/java/pixelitor/utils/test/WeightedCaller.java +++ b/src/main/java/pixelitor/utils/test/WeightedCaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -17,16 +17,16 @@ package pixelitor.utils.test; +import pixelitor.utils.Rnd; + import java.util.ArrayList; import java.util.List; -import java.util.Random; /** * Calls the registered functions with a probability * proportional to their weights. */ public class WeightedCaller { - private final Random random = new Random(); private final List tasks = new ArrayList<>(); public void registerAction(int weight, Runnable r) { @@ -36,7 +36,6 @@ public void registerAction(int weight, Runnable r) { } public void runRandomAction() { - int index = random.nextInt(tasks.size()); - tasks.get(index).run(); + Rnd.chooseFrom(tasks).run(); } } diff --git a/src/test/java/pixelitor/guitest/AssertJSwingTest.java b/src/test/java/pixelitor/guitest/AssertJSwingTest.java index 79e4f7353..ccc1c035a 100644 --- a/src/test/java/pixelitor/guitest/AssertJSwingTest.java +++ b/src/test/java/pixelitor/guitest/AssertJSwingTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -78,6 +78,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.Random; import java.util.function.Consumer; @@ -238,12 +239,13 @@ private static TestTarget decideTarget() { if (targetProp == null || targetProp.equalsIgnoreCase("all")) { return TestTarget.ALL; // default target } + targetProp = targetProp.toUpperCase(Locale.ENGLISH); TestTarget target = null; try { - target = TestTarget.valueOf(targetProp.toUpperCase()); + target = TestTarget.valueOf(targetProp); } catch (IllegalArgumentException e) { - String msg = "Target " + targetProp.toUpperCase() + " not found.\n" + + String msg = "Target " + targetProp + " not found.\n" + "Available targets: " + Arrays.toString(TestTarget.values()); System.err.println(msg); System.exit(1); @@ -252,24 +254,25 @@ private static TestTarget decideTarget() { } private static MaskMode[] decideMaskModes() { - MaskMode[] usedMaskModes; String maskMode = System.getProperty("mask.mode"); if (maskMode == null || maskMode.equalsIgnoreCase("all")) { - usedMaskModes = MaskMode.values(); // Collections.shuffle(Arrays.asList(usedMaskModes)); - } else { - // if a specific test mode was configured, test only that - MaskMode mode = null; - try { - mode = MaskMode.valueOf(maskMode.toUpperCase()); - } catch (IllegalArgumentException e) { - String msg = "Mask mode " + maskMode.toUpperCase() + " not found.\n" + - "Available mask modes: " + Arrays.toString(MaskMode.values()); - System.err.println(msg); - System.exit(1); - } - usedMaskModes = new MaskMode[]{mode}; + return MaskMode.values(); + } + + maskMode = maskMode.toUpperCase(Locale.ENGLISH); + MaskMode[] usedMaskModes; + // if a specific test mode was configured, test only that + MaskMode mode = null; + try { + mode = MaskMode.valueOf(maskMode); + } catch (IllegalArgumentException e) { + String msg = "Mask mode " + maskMode + " not found.\n" + + "Available mask modes: " + Arrays.toString(MaskMode.values()); + System.err.println(msg); + System.exit(1); } + usedMaskModes = new MaskMode[]{mode}; return usedMaskModes; } diff --git a/src/test/java/pixelitor/utils/ResultTest.java b/src/test/java/pixelitor/utils/ResultTest.java index 0a9612bc4..fb64e704c 100644 --- a/src/test/java/pixelitor/utils/ResultTest.java +++ b/src/test/java/pixelitor/utils/ResultTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 Laszlo Balazs-Csiki and Contributors + * Copyright 2024 Laszlo Balazs-Csiki and Contributors * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU @@ -19,6 +19,8 @@ import org.junit.jupiter.api.Test; +import java.util.Locale; + import static org.assertj.core.api.Assertions.assertThat; class ResultTest { @@ -45,7 +47,8 @@ void map_Error() { @Test void flatMap_OK_OK() { Result result = new Success<>("a"); - Result mapped = result.flatMap(s -> new Success<>(s.toUpperCase())); + Result mapped = result.flatMap(s -> + new Success<>(s.toUpperCase(Locale.ENGLISH))); // expect successful mapping assertThat(mapped.wasSuccess()).isTrue(); @@ -65,7 +68,8 @@ void flatMap_OK_Error() { @Test void flatMap_Error_OK() { Result result = new Error<>(2); - Result mapped = result.flatMap(s -> new Success<>(s.toUpperCase())); + Result mapped = result.flatMap(s -> + new Success<>(s.toUpperCase(Locale.ENGLISH))); // expect error ignoring the mapping assertThat(mapped.wasSuccess()).isFalse();