Skip to content

Commit cd3aa32

Browse files
committed
Refactor initialization to use background threads
Moved several initialization tasks to background threads for improved startup performance and responsiveness, including tool, font, UI, and suggestion generator setup. Deferred some tool initialization until first use, added caching for SVG rendering, and simplified string loading in PApplet. Also made minor changes to library list and variable inspector initialization. # Conflicts: # app/src/processing/app/Base.java # app/src/processing/app/platform/DefaultPlatform.java
1 parent e42055f commit cd3aa32

File tree

8 files changed

+114
-90
lines changed

8 files changed

+114
-90
lines changed

app/src/processing/app/Base.java

Lines changed: 44 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@
2323

2424
package processing.app;
2525

26+
import java.awt.*;
27+
import java.awt.event.ActionListener;
28+
import java.io.*;
29+
import java.lang.reflect.InvocationTargetException;
30+
import java.util.*;
31+
import java.util.List;
32+
import java.util.Map.Entry;
33+
34+
import javax.swing.*;
35+
import javax.swing.tree.DefaultMutableTreeNode;
36+
2637
import com.formdev.flatlaf.FlatDarkLaf;
2738
import com.formdev.flatlaf.FlatLaf;
2839
import com.formdev.flatlaf.FlatLightLaf;
@@ -34,19 +45,6 @@
3445
import processing.core.PApplet;
3546
import processing.data.StringList;
3647

37-
import javax.swing.*;
38-
import javax.swing.tree.DefaultMutableTreeNode;
39-
import java.awt.*;
40-
import java.awt.event.ActionListener;
41-
import java.io.File;
42-
import java.io.FileInputStream;
43-
import java.io.IOException;
44-
import java.io.InputStream;
45-
import java.lang.reflect.InvocationTargetException;
46-
import java.util.*;
47-
import java.util.List;
48-
import java.util.Map.Entry;
49-
5048
/**
5149
* The base class for the main processing application.
5250
* Primary role of this class is for platform identification and
@@ -231,7 +229,7 @@ static private void createAndShowGUI(String[] args) {
231229
Messages.log("Base() constructor succeeded");
232230

233231
// Prevent more than one copy of the PDE from running.
234-
SingleInstance.startServer(base);
232+
new Thread(() -> { SingleInstance.startServer(base); } ).start();
235233

236234
handleWelcomeScreen(base);
237235
handleCrustyDisplay();
@@ -850,7 +848,7 @@ public List<ToolContribution> getContribTools() {
850848
return contribTools;
851849
}
852850

853-
851+
private List<Tool> toolsToInit = new ArrayList<>();
854852
public void rebuildToolList() {
855853
// Only do these once because the list of internal tools will never change
856854
if (internalTools == null) {
@@ -870,43 +868,12 @@ public void rebuildToolList() {
870868
// Only init() these the first time they're loaded
871869
if (coreTools == null) {
872870
coreTools = ToolContribution.loadAll(Base.getToolsFolder());
873-
for (Tool tool : coreTools) {
874-
tool.init(this);
875-
}
871+
toolsToInit.addAll(coreTools);
876872
}
877873

878874
// Reset the contributed tools and re-init() all of them.
879875
contribTools = ToolContribution.loadAll(Base.getSketchbookToolsFolder());
880-
for (Tool tool : contribTools) {
881-
try {
882-
tool.init(this);
883-
884-
// With the exceptions, we can't call statusError because the window
885-
// isn't completely set up yet. Also not gonna pop up a warning because
886-
// people may still be running different versions of Processing.
887-
888-
} catch (VerifyError | AbstractMethodError ve) {
889-
System.err.println("\"" + tool.getMenuTitle() + "\" is not " +
890-
"compatible with this version of Processing");
891-
Messages.err("Incompatible Tool found during tool.init()", ve);
892-
893-
} catch (NoSuchMethodError nsme) {
894-
System.err.println("\"" + tool.getMenuTitle() + "\" is not " +
895-
"compatible with this version of Processing");
896-
System.err.println("The " + nsme.getMessage() + " method no longer exists.");
897-
Messages.err("Incompatible Tool found during tool.init()", nsme);
898-
899-
} catch (NoClassDefFoundError ncdfe) {
900-
System.err.println("\"" + tool.getMenuTitle() + "\" is not " +
901-
"compatible with this version of Processing");
902-
System.err.println("The " + ncdfe.getMessage() + " class is no longer available.");
903-
Messages.err("Incompatible Tool found during tool.init()", ncdfe);
904-
905-
} catch (Error | Exception e) {
906-
System.err.println("An error occurred inside \"" + tool.getMenuTitle() + "\"");
907-
e.printStackTrace();
908-
}
909-
}
876+
toolsToInit.addAll(contribTools);
910877
}
911878

912879

@@ -915,7 +882,7 @@ protected void initInternalTool(Class<?> toolClass) {
915882
final Tool tool = (Tool)
916883
toolClass.getDeclaredConstructor().newInstance();
917884

918-
tool.init(this);
885+
toolsToInit.add(tool);
919886
internalTools.add(tool);
920887

921888
} catch (Exception e) {
@@ -963,12 +930,40 @@ public void populateToolsMenu(JMenu toolsMenu) {
963930
toolsMenu.add(manageTools);
964931
}
965932

933+
void initTool(Tool tool) {
934+
if(!toolsToInit.contains(tool)) {return;}
935+
try {
936+
tool.init(this);
937+
toolsToInit.remove(tool);
938+
} catch (VerifyError | AbstractMethodError ve) {
939+
System.err.println("\"" + tool.getMenuTitle() + "\" is not " +
940+
"compatible with this version of Processing");
941+
Messages.err("Incompatible Tool found during tool.init()", ve);
942+
943+
} catch (NoSuchMethodError nsme) {
944+
System.err.println("\"" + tool.getMenuTitle() + "\" is not " +
945+
"compatible with this version of Processing");
946+
System.err.println("The " + nsme.getMessage() + " method no longer exists.");
947+
Messages.err("Incompatible Tool found during tool.init()", nsme);
948+
949+
} catch (NoClassDefFoundError ncdfe) {
950+
System.err.println("\"" + tool.getMenuTitle() + "\" is not " +
951+
"compatible with this version of Processing");
952+
System.err.println("The " + ncdfe.getMessage() + " class is no longer available.");
953+
Messages.err("Incompatible Tool found during tool.init()", ncdfe);
954+
955+
} catch (Error | Exception e) {
956+
System.err.println("An error occurred inside \"" + tool.getMenuTitle() + "\"");
957+
e.printStackTrace();
958+
}
959+
}
966960

967961
JMenuItem createToolItem(final Tool tool) { //, Map<String, JMenuItem> toolItems) {
968962
String title = tool.getMenuTitle();
969963
final JMenuItem item = new JMenuItem(title);
970964
item.addActionListener(e -> {
971965
try {
966+
initTool(tool);
972967
tool.run();
973968

974969
} catch (NoSuchMethodError | NoClassDefFoundError ne) {

app/src/processing/app/platform/DefaultPlatform.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323

2424
package processing.app.platform;
2525

26+
import java.awt.*;
27+
import java.io.File;
28+
29+
import javax.swing.*;
30+
import javax.swing.border.EmptyBorder;
31+
2632
import com.formdev.flatlaf.FlatLaf;
2733
import com.formdev.flatlaf.FlatLightLaf;
2834
import processing.app.Base;
@@ -31,10 +37,6 @@
3137
import processing.awt.ShimAWT;
3238
import processing.core.PApplet;
3339

34-
import javax.swing.*;
35-
import javax.swing.border.EmptyBorder;
36-
import java.awt.*;
37-
import java.io.File;
3840

3941

4042
/**
@@ -110,18 +112,29 @@ public void setLookAndFeel() throws Exception {
110112
// (i.e. Nimbus on Linux) with our custom components is badness.
111113

112114
// dummy font call so that it's registered for FlatLaf
113-
Font defaultFont = Toolkit.getSansFont(14, Font.PLAIN);
114-
UIManager.put("defaultFont", defaultFont);
115+
new Thread(() -> {
116+
Font defaultFont = Toolkit.getSansFont(14, Font.PLAIN);
117+
UIManager.put("defaultFont", defaultFont);
118+
}).start();
119+
115120

116121
// pull in FlatLaf.properties from the processing.app.laf folder
117122
FlatLaf.registerCustomDefaultsSource("processing.app.laf");
118123

119-
// start with Light, but updateTheme() will be called soon
120-
UIManager.setLookAndFeel(new FlatLightLaf());
124+
new Thread(() -> {
125+
// start with Light, but updateTheme() will be called soon
126+
try {
127+
UIManager.setLookAndFeel(new FlatLightLaf());
128+
} catch (UnsupportedLookAndFeelException e) {
129+
throw new RuntimeException(e);
130+
}
131+
}).start();
121132

122133
// Does not fully remove the gray hairline (probably from a parent
123134
// Window object), but is an improvement from the heavier default.
124-
UIManager.put("ToolTip.border", new EmptyBorder(0, 0, 0, 0));
135+
new Thread(() -> {
136+
UIManager.put("ToolTip.border", new EmptyBorder(0, 0, 0, 0));
137+
}).start();
125138

126139
/*
127140
javax.swing.UIDefaults defaults = UIManager.getDefaults();

app/src/processing/app/ui/Editor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ public void windowDeactivated(WindowEvent e) {
193193

194194
timer = new Timer();
195195

196-
buildMenuBar();
196+
new Thread(this::buildMenuBar).start();
197197

198198
JPanel contentPain = new JPanel();
199199
setContentPane(contentPain);

app/src/processing/app/ui/Toolkit.java

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,19 @@
4444
import java.awt.geom.AffineTransform;
4545
import java.awt.geom.GeneralPath;
4646
import java.awt.image.ImageObserver;
47+
import java.awt.image.RenderedImage;
4748
import java.io.BufferedInputStream;
4849
import java.io.File;
4950
import java.io.FileInputStream;
5051
import java.io.IOException;
52+
import java.nio.file.Files;
5153
import java.util.ArrayList;
5254
import java.util.Arrays;
5355
import java.util.Comparator;
5456
import java.util.List;
5557
import java.util.regex.Pattern;
5658

59+
import javax.imageio.ImageIO;
5760
import javax.swing.Action;
5861
import javax.swing.ImageIcon;
5962
import javax.swing.JButton;
@@ -68,14 +71,12 @@
6871
import javax.swing.text.html.HTMLEditorKit;
6972
import javax.swing.text.html.StyleSheet;
7073

71-
import processing.app.Language;
72-
import processing.app.Messages;
73-
import processing.app.Platform;
74-
import processing.app.Preferences;
75-
import processing.app.Util;
74+
import processing.app.*;
7675
import processing.awt.PGraphicsJava2D;
7776
import processing.awt.PShapeJava2D;
77+
import processing.awt.ShimAWT;
7878
import processing.core.PApplet;
79+
import processing.core.PImage;
7980
import processing.core.PShape;
8081
import processing.data.StringDict;
8182
import processing.data.StringList;
@@ -794,6 +795,7 @@ static private Image svgToImageMult(String xmlStr, int wide, int high) {
794795
*/
795796

796797

798+
797799
static public Image svgToImageMult(String xmlStr, int wide, int high, StringDict replacements) {
798800
/*
799801
for (StringDict.Entry entry : replacements.entries()) {
@@ -823,8 +825,25 @@ static private Image svgToImage(String xmlStr, int wide, int high) {
823825
pg.setSize(wide, high);
824826
pg.smooth();
825827

828+
829+
830+
831+
826832
pg.beginDraw();
827833

834+
var cacheKey = (xmlStr + "|" + wide + "x" + high).hashCode();
835+
var cachePath = Base.getSettingsFolder().toPath().resolve("svg_cache").resolve(String.valueOf(cacheKey) + ".png");
836+
if(!Base.DEBUG || true){
837+
if(Files.exists(cachePath)){
838+
byte[] bytes = PApplet.loadBytes(cachePath.toFile());
839+
if (bytes == null) {
840+
return null;
841+
} else {
842+
return new ImageIcon(bytes).getImage();
843+
}
844+
}
845+
}
846+
828847
try {
829848
XML xml = XML.parse(xmlStr);
830849
PShape shape = new PShapeJava2D(xml);
@@ -835,6 +854,12 @@ static private Image svgToImage(String xmlStr, int wide, int high) {
835854
}
836855

837856
pg.endDraw();
857+
try {
858+
Files.createDirectories(cachePath.getParent());
859+
pg.save(cachePath.toString());
860+
} catch (IOException e) {
861+
e.printStackTrace();
862+
}
838863
return pg.image;
839864
}
840865

@@ -1361,8 +1386,9 @@ static private Font initFont(String filename, int size) throws IOException, Font
13611386
Font font = Font.createFont(Font.TRUETYPE_FONT, input);
13621387
input.close();
13631388

1364-
// Register the font to be available for other function calls
1365-
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);
1389+
new Thread(() -> {
1390+
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);
1391+
}).start();
13661392

13671393
return font.deriveFont((float) size);
13681394
}

core/src/processing/core/PApplet.java

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6822,28 +6822,9 @@ static public String[] loadStrings(InputStream input) {
68226822

68236823
static public String[] loadStrings(BufferedReader reader) {
68246824
try {
6825-
String[] lines = new String[100];
6826-
int lineCount = 0;
6827-
String line;
6828-
while ((line = reader.readLine()) != null) {
6829-
if (lineCount == lines.length) {
6830-
String[] temp = new String[lineCount << 1];
6831-
System.arraycopy(lines, 0, temp, 0, lineCount);
6832-
lines = temp;
6833-
}
6834-
lines[lineCount++] = line;
6835-
}
6825+
var lines = reader.lines().toArray(String[]::new);
68366826
reader.close();
6837-
6838-
if (lineCount == lines.length) {
6839-
return lines;
6840-
}
6841-
6842-
// resize array to appropriate amount for these lines
6843-
String[] output = new String[lineCount];
6844-
System.arraycopy(lines, 0, output, 0, lineCount);
6845-
return output;
6846-
6827+
return lines;
68476828
} catch (IOException e) {
68486829
e.printStackTrace();
68496830
//throw new RuntimeException("Error inside loadStrings()");

java/src/processing/mode/java/JavaTextArea.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ public class JavaTextArea extends PdeTextArea {
5252
public JavaTextArea(TextAreaDefaults defaults, JavaEditor editor) {
5353
super(defaults, new JavaInputHandler(editor), editor);
5454

55-
suggestionGenerator = new CompletionGenerator((JavaMode) editor.getMode());
55+
new Thread(() -> {
56+
suggestionGenerator = new CompletionGenerator((JavaMode) editor.getMode());
57+
}).start();
5658
tweakMode = false;
5759
}
5860

java/src/processing/mode/java/PreprocService.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public class PreprocService {
8080
protected final JavaMode javaMode;
8181
protected final Sketch sketch;
8282

83-
protected final ASTParser parser = ASTParser.newParser(AST.JLS11);
83+
protected ASTParser parser;
8484

8585
private final Thread preprocessingThread;
8686
private final BlockingQueue<Boolean> requestQueue = new ArrayBlockingQueue<>(1);
@@ -116,6 +116,9 @@ public PreprocService(JavaMode javaMode, Sketch sketch) {
116116
* The "main loop" for the background thread that checks for code issues.
117117
*/
118118
private void mainLoop() {
119+
if(parser == null) {
120+
parser = ASTParser.newParser(AST.JLS11);
121+
}
119122
running = true;
120123
PreprocSketch prevResult = null;
121124
CompletableFuture<?> runningCallbacks = null;

0 commit comments

Comments
 (0)