7 import ij.plugin.filter.*;
9 import ij.plugin.frame.Recorder;
10 import ij.macro.Interpreter;
11 import ij.measure.Calibration;
12 import java.awt.event.*;
16 import java.applet.Applet;
18 import java.lang.reflect.*;
21 /** This class consists of static utility methods. */
23 public static final int ALL_KEYS = 0x32;
25 public static boolean debugMode;
26 public static boolean hideProcessStackDialog;
28 public static final char micronSymbol = '\u00B5';
29 public static final char angstromSymbol = '\u00C5';
30 public static final char degreeSymbol = '\u00B0';
32 private static ImageJ ij;
33 private static java.applet.Applet applet;
34 private static ProgressBar progressBar;
35 private static TextPanel textPanel;
36 private static String osname, osarch;
37 private static boolean isMac, isWin, isJava2, isJava14, isJava15, isJava16, isLinux, isVista, is64Bit;
38 private static boolean altDown, spaceDown, shiftDown;
39 private static boolean macroRunning;
40 private static Thread previousThread;
41 private static TextPanel logPanel;
42 private static boolean notVerified = true;
43 private static ClassLoader classLoader;
44 private static boolean memMessageDisplayed;
45 private static long maxMemory;
46 private static boolean escapePressed;
47 private static boolean redirectErrorMessages;
48 private static boolean suppressPluginNotFoundError;
49 private static Dimension screenSize;
50 private static Hashtable commandTable;
54 osname = System.getProperty("os.name");
55 isWin = osname.startsWith("Windows");
56 isMac = !isWin && osname.startsWith("Mac");
57 isLinux = osname.startsWith("Linux");
58 isVista = isWin && osname.indexOf("Vista")!=-1;
59 String version = System.getProperty("java.version").substring(0,3);
60 if (version.compareTo("2.9")<=0) { // JVM on Sharp Zaurus PDA claims to be "3.1"!
61 isJava2 = version.compareTo("1.1")>0;
62 isJava14 = version.compareTo("1.3")>0;
63 isJava15 = version.compareTo("1.4")>0;
64 isJava16 = version.compareTo("1.5")>0;
68 static void init(ImageJ imagej, Applet theApplet) {
69 if (theApplet == null)
70 System.setSecurityManager(null);
73 progressBar = ij.getProgressBar();
76 /**Returns a reference to the "ImageJ" frame.*/
77 public static ImageJ getInstance() {
81 /** Runs the macro contained in the string <code>macro</code>.
82 Returns any string value returned by the macro, or null.
83 The equivalent macro function is eval(). */
84 public static String runMacro(String macro) {
85 return runMacro(macro, "");
88 /** Runs the macro contained in the string <code>macro</code>.
89 The optional string argument can be retrieved in the
90 called macro using the getArgument() macro function.
91 Returns any string value returned by the macro, or null. */
92 public static String runMacro(String macro, String arg) {
93 Macro_Runner mr = new Macro_Runner();
94 return mr.runMacro(macro, arg);
97 /** Runs the specified macro file. The file is assumed to be in the macros
98 folder unless <code>name</code> is a full path. ".txt" is
99 added if <code>name</code> does not have an extension.
100 The optional string argument (<code>arg</code>) can be retrieved in the called
101 macro using the getArgument() macro function.
102 Returns any string value returned by the macro or null.
103 The equivalent macro function is runMacro(). */
104 public static String runMacroFile(String name, String arg) {
105 if (ij==null && Menus.getCommands()==null)
107 Macro_Runner mr = new Macro_Runner();
108 return mr.runMacroFile(name, arg);
111 /** Runs the specified macro file. */
112 public static String runMacroFile(String name) {
113 return runMacroFile(name, null);
116 /** Runs the specified plugin using the specified image. */
117 public static Object runPlugIn(ImagePlus imp, String className, String arg) {
118 WindowManager.setTempCurrentImage(imp);
119 Object o = runPlugIn("", className, arg);
120 WindowManager.setTempCurrentImage(null);
124 /** Runs the specified plugin and returns a reference to it. */
125 public static Object runPlugIn(String className, String arg) {
126 return runPlugIn("", className, arg);
129 /** Runs the specified plugin and returns a reference to it. */
130 static Object runPlugIn(String commandName, String className, String arg) {
132 IJ.log("runPlugin: "+className+" "+arg);
133 // Use custom classloader if this is a user plugin
134 // and we are not running as an applet
135 if (!className.startsWith("ij") && applet==null) {
136 boolean createNewClassLoader = altKeyDown();
137 return runUserPlugIn(commandName, className, arg, createNewClassLoader);
139 Object thePlugIn=null;
141 Class c = Class.forName(className);
142 thePlugIn = c.newInstance();
143 if (thePlugIn instanceof PlugIn)
144 ((PlugIn)thePlugIn).run(arg);
146 new PlugInFilterRunner(thePlugIn, commandName, arg);
148 catch (ClassNotFoundException e) {
149 if (IJ.getApplet()==null)
150 log("Plugin or class not found: \"" + className + "\"\n(" + e+")");
152 catch (InstantiationException e) {log("Unable to load plugin (ins)");}
153 catch (IllegalAccessException e) {log("Unable to load plugin, possibly \nbecause it is not public.");}
154 redirectErrorMessages = false;
158 static Object runUserPlugIn(String commandName, String className, String arg, boolean createNewLoader) {
159 if (applet!=null) return null;
161 // check for duplicate classes in the plugins folder
162 IJ.runPlugIn("ij.plugin.ClassChecker", "");
165 if (createNewLoader) classLoader = null;
166 ClassLoader loader = getClassLoader();
167 Object thePlugIn = null;
169 thePlugIn = (loader.loadClass(className)).newInstance();
170 if (thePlugIn instanceof PlugIn)
171 ((PlugIn)thePlugIn).run(arg);
172 else if (thePlugIn instanceof PlugInFilter)
173 new PlugInFilterRunner(thePlugIn, commandName, arg);
175 catch (ClassNotFoundException e) {
176 if (className.indexOf('_')!=-1 && !suppressPluginNotFoundError)
177 error("Plugin or class not found: \"" + className + "\"\n(" + e+")");
179 catch (NoClassDefFoundError e) {
180 int dotIndex = className.indexOf('.');
181 String cause = e.getMessage();
182 int parenIndex = cause.indexOf('(');
184 cause = cause.substring(0, parenIndex - 1);
185 boolean correctClass = cause.endsWith(dotIndex < 0 ?
186 className : className.substring(dotIndex + 1));
187 if (!correctClass && !suppressPluginNotFoundError)
188 error("Plugin " + className +
189 " did not find required class: " +
191 if (correctClass && dotIndex >= 0)
192 return runUserPlugIn(commandName, className.substring(dotIndex + 1), arg, createNewLoader);
193 if (className.indexOf('_')!=-1 && !suppressPluginNotFoundError)
194 error("Plugin or class not found: \"" + className + "\"\n(" + e+")");
196 catch (InstantiationException e) {error("Unable to load plugin (ins)");}
197 catch (IllegalAccessException e) {error("Unable to load plugin, possibly \nbecause it is not public.");}
198 redirectErrorMessages = false;
199 suppressPluginNotFoundError = false;
203 static void wrongType(int capabilities, String cmd) {
204 String s = "\""+cmd+"\" requires an image of type:\n \n";
205 if ((capabilities&PlugInFilter.DOES_8G)!=0) s += " 8-bit grayscale\n";
206 if ((capabilities&PlugInFilter.DOES_8C)!=0) s += " 8-bit color\n";
207 if ((capabilities&PlugInFilter.DOES_16)!=0) s += " 16-bit grayscale\n";
208 if ((capabilities&PlugInFilter.DOES_32)!=0) s += " 32-bit (float) grayscale\n";
209 if ((capabilities&PlugInFilter.DOES_RGB)!=0) s += " RGB color\n";
213 /** Starts executing a menu command in a separete thread and returns immediately. */
214 public static void doCommand(String command) {
216 ij.doCommand(command);
219 /** Runs an ImageJ command. Does not return until
220 the command has finished executing. To avoid "image locked",
221 errors, plugins that call this method should implement
222 the PlugIn interface instead of PlugInFilter. */
223 public static void run(String command) {
227 /** Runs an ImageJ command, with options that are passed to the
228 GenericDialog and OpenDialog classes. Does not return until
229 the command has finished executing. */
230 public static void run(String command, String options) {
231 //IJ.log("run1: "+command+" "+Thread.currentThread().hashCode());
232 if (ij==null && Menus.getCommands()==null)
235 Macro.setOptions(options);
236 Thread thread = Thread.currentThread();
237 if (previousThread==null || thread!=previousThread) {
238 String name = thread.getName();
239 if (!name.startsWith("Run$_"))
240 thread.setName("Run$_"+name);
242 command = convert(command);
243 previousThread = thread;
245 Executer e = new Executer(command);
247 macroRunning = false;
248 Macro.setOptions(null);
250 //IJ.log("run2: "+command+" "+Thread.currentThread().hashCode());
253 /** Converts commands that have been renamed so
254 macros using the old names continue to work. */
255 private static String convert(String command) {
256 if (commandTable==null) {
257 commandTable = new Hashtable(23); // initial capacity should be increased as needed
258 commandTable.put("New...", "Image...");
259 commandTable.put("Threshold", "Make Binary");
260 commandTable.put("Display...", "Appearance...");
261 commandTable.put("Start Animation", "Start Animation [\\]");
262 commandTable.put("Convert Images to Stack", "Images to Stack");
263 commandTable.put("Convert Stack to Images", "Stack to Images");
264 commandTable.put("Convert Stack to RGB", "Stack to RGB");
265 commandTable.put("Convert to Composite", "Make Composite");
266 commandTable.put("New HyperStack...", "New Hyperstack...");
267 commandTable.put("Stack to HyperStack...", "Stack to Hyperstack...");
268 commandTable.put("HyperStack to Stack", "Hyperstack to Stack");
269 commandTable.put("RGB Split", "Split Channels");
270 commandTable.put("RGB Merge...", "Merge Channels...");
271 commandTable.put("Channels...", "Channels Tool...");
273 String command2 = (String)commandTable.get(command);
280 /** Runs an ImageJ command using the specified image and options. */
281 public static void run(ImagePlus imp, String command, String options) {
282 if (imp!=null) WindowManager.setTempCurrentImage(imp);
283 run(command, options);
284 if (imp!=null) WindowManager.setTempCurrentImage(null);
288 Menus m = new Menus(null, null);
293 private static void testAbort() {
298 /** Returns true if the run(), open() or newImage() method is executing. */
299 public static boolean macroRunning() {
303 /** Returns true if a macro is running, or if the run(), open()
304 or newImage() method is executing. */
305 public static boolean isMacro() {
306 return macroRunning || Interpreter.getInstance()!=null;
309 /**Returns the Applet that created this ImageJ or null if running as an application.*/
310 public static java.applet.Applet getApplet() {
314 /**Displays a message in the ImageJ status bar.*/
315 public static void showStatus(String s) {
316 if (ij!=null) ij.showStatus(s);
317 ImagePlus imp = WindowManager.getCurrentImage();
318 ImageCanvas ic = imp!=null?imp.getCanvas():null;
320 ic.setShowCursorStatus(s.length()==0?true:false);
323 /** Displays a line of text in the "Results" window. Writes to
324 System.out.println if the "ImageJ" frame is not present. */
325 public static void write(String s) {
326 if (textPanel==null && ij!=null)
331 System.out.println(s);
334 private static void showResults() {
335 TextWindow resultsWindow = new TextWindow("Results", "", 300, 200);
336 textPanel = resultsWindow.getTextPanel();
337 textPanel.setResultsTable(Analyzer.getResultsTable());
339 textPanel.addKeyListener(ij);
342 public static synchronized void log(String s) {
344 if (logPanel==null && ij!=null) {
345 TextWindow logWindow = new TextWindow("Log", "", 350, 250);
346 logPanel = logWindow.getTextPanel();
347 logPanel.setFont(new Font("SansSerif", Font.PLAIN, 16));
349 if (logPanel!=null) {
350 if (s.startsWith("\\"))
355 System.out.println(s);
358 static void handleLogCommand(String s) {
359 if (s.equals("\\Closed"))
361 else if (s.startsWith("\\Update:")) {
362 int n = logPanel.getLineCount();
363 String s2 = s.substring(8, s.length());
367 logPanel.setLine(n-1, s2);
368 } else if (s.startsWith("\\Update")) {
369 int cindex = s.indexOf(":");
371 {logPanel.append(s); return;}
372 String nstr = s.substring(7, cindex);
373 int line = (int)Tools.parseDouble(nstr, -1);
374 if (line<0 || line>25)
375 {logPanel.append(s); return;}
376 int count = logPanel.getLineCount();
377 while (line>=count) {
381 String s2 = s.substring(cindex+1, s.length());
382 logPanel.setLine(line, s2);
383 } else if (s.equals("\\Clear"))
389 /** Clears the "Results" window and sets the column headings to
390 those in the tab-delimited 'headings' String. Writes to
391 System.out.println if the "ImageJ" frame is not present.*/
392 public static void setColumnHeadings(String headings) {
393 if (textPanel==null && ij!=null)
396 textPanel.setColumnHeadings(headings);
398 System.out.println(headings);
401 /** Returns true if the "Results" window is open. */
402 public static boolean isResultsWindow() {
403 return textPanel!=null;
406 /** Returns a reference to the "Results" window TextPanel.
407 Opens the "Results" window if it is currently not open. */
408 public static TextPanel getTextPanel() {
414 /** TextWindow calls this method with a null argument when the "Results" window is closed. */
415 public static void setTextPanel(TextPanel tp) {
419 /**Displays a "no images are open" dialog box.*/
420 public static void noImage() {
421 error("No Image", "There are no images open.");
424 /** Displays an "out of memory" message to the "Log" window. */
425 public static void outOfMemory(String name) {
428 String tot = Runtime.getRuntime().totalMemory()/1048576L+"MB";
429 if (!memMessageDisplayed)
430 log(">>>>>>>>>>>>>>>>>>>>>>>>>>>");
431 log("<Out of memory>");
432 if (!memMessageDisplayed) {
433 log("<All available memory ("+tot+") has been>");
434 log("<used. Instructions for making more>");
435 log("<available can be found in the \"Memory\" >");
436 log("<sections of the installation notes at>");
437 log("<http://rsb.info.nih.gov/ij/docs/install/>");
438 log(">>>>>>>>>>>>>>>>>>>>>>>>>>>");
439 memMessageDisplayed = true;
444 /** Updates the progress bar, where 0<=progress<=1.0. The progress bar is
445 not shown in BatchMode and erased if progress>=1.0. The progress bar is
446 updated only if more than 90 ms have passes since the last call. Does nothing
447 if the ImageJ window is not present. */
448 public static void showProgress(double progress) {
449 if (progressBar!=null) progressBar.show(progress, false);
452 /** Updates the progress bar, where the length of the bar is set to
453 (<code>currentValue+1)/finalValue</code> of the maximum bar length.
454 The bar is erased if <code>currentValue>=finalValue</code>.
455 The bar is updated only if more than 90 ms have passed since the last call.
456 Does nothing if the ImageJ window is not present. */
457 public static void showProgress(int currentIndex, int finalIndex) {
458 if (progressBar!=null) progressBar.show(currentIndex, finalIndex);
461 /** Displays a message in a dialog box titled "Message".
462 Writes the Java console if ImageJ is not present. */
463 public static void showMessage(String msg) {
464 showMessage("Message", msg);
467 /** Displays a message in a dialog box with the specified title.
468 Writes the Java console if ImageJ is not present. */
469 public static void showMessage(String title, String msg) {
470 if (redirectErrorMessages) {
471 IJ.log(title + ": " + msg);
472 redirectErrorMessages = false;
476 if (msg!=null && msg.startsWith("<html>"))
477 new HTMLDialog(title, msg);
479 new MessageDialog(ij, title, msg);
481 System.out.println(msg);
484 /** Displays a message in a dialog box titled "ImageJ". If a
485 macro is running, it is aborted. Writes to the Java console
486 if the ImageJ window is not present.*/
487 public static void error(String msg) {
488 showMessage("ImageJA", msg);
489 showMessage("ImageJ", msg);
490 if (Thread.currentThread().getName().endsWith("JavaScript"))
491 throw new RuntimeException(Macro.MACRO_CANCELED);
496 /** Displays a message in a dialog box with the specified title.
497 If a macro is running, it is aborted. Writes to the Java
498 console if ImageJ is not present. */
499 public static synchronized void error(String title, String msg) {
500 showMessage(title, msg);
504 /** Displays a message in a dialog box with the specified title.
505 Returns false if the user pressed "Cancel". */
506 public static boolean showMessageWithCancel(String title, String msg) {
507 GenericDialog gd = new GenericDialog(title);
510 return !gd.wasCanceled();
513 public static final int CANCELED = Integer.MIN_VALUE;
515 /** Allows the user to enter a number in a dialog box. Returns the
516 value IJ.CANCELED (-2,147,483,648) if the user cancels the dialog box.
517 Returns 'defaultValue' if the user enters an invalid number. */
518 public static double getNumber(String prompt, double defaultValue) {
519 GenericDialog gd = new GenericDialog("");
520 int decimalPlaces = (int)defaultValue==defaultValue?0:2;
521 gd.addNumericField(prompt, defaultValue, decimalPlaces);
523 if (gd.wasCanceled())
525 double v = gd.getNextNumber();
526 if (gd.invalidNumber())
532 /** Allows the user to enter a string in a dialog box. Returns
533 "" if the user cancels the dialog box. */
534 public static String getString(String prompt, String defaultString) {
535 GenericDialog gd = new GenericDialog("");
536 gd.addStringField(prompt, defaultString, 20);
538 if (gd.wasCanceled())
540 return gd.getNextString();
543 /**Delays 'msecs' milliseconds.*/
544 public static void wait(int msecs) {
545 try {Thread.sleep(msecs);}
546 catch (InterruptedException e) { }
549 /** Emits an audio beep. */
550 public static void beep() {
551 java.awt.Toolkit.getDefaultToolkit().beep();
554 /** Runs the garbage collector and returns a string something
555 like "64K of 256MB (25%)" that shows how much of
556 the available memory is in use. This is the string
557 displayed when the user clicks in the status bar. */
558 public static String freeMemory() {
560 long inUse = currentMemory();
561 String inUseStr = inUse<10000*1024?inUse/1024L+"K":inUse/1048576L+"MB";
563 long max = maxMemory();
565 double percent = inUse*100/max;
566 maxStr = " of "+max/1048576L+"MB ("+(percent<1.0?"<1":d2s(percent,0)) + "%)";
568 return inUseStr + maxStr;
571 /** Returns the amount of memory currently being used by ImageJ. */
572 public static long currentMemory() {
573 long freeMem = Runtime.getRuntime().freeMemory();
574 long totMem = Runtime.getRuntime().totalMemory();
575 return totMem-freeMem;
578 /** Returns the maximum amount of memory available to ImageJ or
579 zero if ImageJ is unable to determine this limit. */
580 public static long maxMemory() {
582 Memory mem = new Memory();
583 maxMemory = mem.getMemorySetting();
584 if (maxMemory==0L) maxMemory = mem.maxMemory();
589 public static void showTime(ImagePlus imp, long start, String str) {
590 showTime(imp, start, str, 1);
593 public static void showTime(ImagePlus imp, long start, String str, int nslices) {
594 if (Interpreter.isBatchMode()) return;
595 long elapsedTime = System.currentTimeMillis() - start;
596 double seconds = elapsedTime / 1000.0;
597 long pixels = imp.getWidth() * imp.getHeight();
598 int rate = (int)((double)pixels*nslices/seconds);
602 else if (rate<1000000)
603 str2 = ", "+rate+" pixels/second";
605 str2 = ", "+d2s(rate/1000000.0,1)+" million pixels/second";
606 showStatus(str+seconds+" seconds"+str2);
609 /** Converts a number to a formatted string using
610 2 digits to the right of the decimal point. */
611 public static String d2s(double n) {
615 private static DecimalFormat[] df;
617 /** Converts a number to a rounded formatted string.
618 The 'decimalPlaces' argument specifies the number of
619 digits to the right of the decimal point (0-9). */
620 public static String d2s(double n, int decimalPlaces) {
623 if (n==Float.MAX_VALUE) // divide by 0 in FloatProcessor
627 if ((np<0.001 && np!=0.0 && np<1.0/Math.pow(10,decimalPlaces)) || np>999999999999d)
628 return Float.toString((float)n); // use scientific notation
630 DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US);
631 df = new DecimalFormat[10];
632 df[0] = new DecimalFormat("0", dfs);
633 df[1] = new DecimalFormat("0.0", dfs);
634 df[2] = new DecimalFormat("0.00", dfs);
635 df[3] = new DecimalFormat("0.000", dfs);
636 df[4] = new DecimalFormat("0.0000", dfs);
637 df[5] = new DecimalFormat("0.00000", dfs);
638 df[6] = new DecimalFormat("0.000000", dfs);
639 df[7] = new DecimalFormat("0.0000000", dfs);
640 df[8] = new DecimalFormat("0.00000000", dfs);
641 df[9] = new DecimalFormat("0.000000000", dfs);
643 if (decimalPlaces<0) decimalPlaces = 0;
644 if (decimalPlaces>9) decimalPlaces = 9;
645 return df[decimalPlaces].format(n);
648 /** Adds the specified class to a Vector to keep it from being garbage
649 collected, which would cause the classes static fields to be reset.
650 Probably not needed with Java 1.2 or later. */
651 public static void register(Class c) {
652 if (ij!=null) ij.register(c);
655 /** Returns true if the space bar is down. */
656 public static boolean spaceBarDown() {
660 /** Returns true if the alt key is down. */
661 public static boolean altKeyDown() {
665 /** Returns true if the shift key is down. */
666 public static boolean shiftKeyDown() {
670 public static void setKeyDown(int key) {
671 //IJ.showStatus("setKeyDown: "+key);
673 case KeyEvent.VK_ALT:
676 case KeyEvent.VK_SHIFT:
678 if (debugMode) beep();
680 case KeyEvent.VK_SPACE: {
682 ImageWindow win = WindowManager.getCurrentWindow();
683 if (win!=null) win.getCanvas().setCursor(-1,-1,-1, -1);
686 case KeyEvent.VK_ESCAPE: {
687 escapePressed = true;
693 public static void setKeyUp(int key) {
694 //IJ.showStatus("setKeyUp: "+key);
696 case KeyEvent.VK_ALT: altDown=false; break;
697 case KeyEvent.VK_SHIFT: shiftDown=false; if (debugMode) beep(); break;
698 case KeyEvent.VK_SPACE: {
700 ImageWindow win = WindowManager.getCurrentWindow();
701 if (win!=null) win.getCanvas().setCursor(-1,-1,-1,-1);
704 case ALL_KEYS: altDown=shiftDown=spaceDown=false; break;
708 public static void setInputEvent(InputEvent e) {
709 altDown = e.isAltDown();
710 shiftDown = e.isShiftDown();
713 /** Returns true if this machine is a Macintosh. */
714 public static boolean isMacintosh() {
718 /** Returns true if this machine is a Macintosh running OS X. */
719 public static boolean isMacOSX() {
720 return isMacintosh();
723 /** Returns true if this machine is running Windows. */
724 public static boolean isWindows() {
728 /** Always returns true. */
729 public static boolean isJava2() {
733 /** Returns true if ImageJ is running on a Java 1.4 or greater JVM. */
734 public static boolean isJava14() {
738 /** Returns true if ImageJ is running on a Java 1.5 or greater JVM. */
739 public static boolean isJava15() {
743 /** Returns true if ImageJ is running on a Java 1.6 or greater JVM. */
744 public static boolean isJava16() {
748 /** Returns true if ImageJ is running on Linux. */
749 public static boolean isLinux() {
753 /** Returns true if ImageJ is running on Windows Vista. */
754 public static boolean isVista() {
758 /** Returns true if ImageJ is running a 64-bit version of Java. */
759 public static boolean is64Bit() {
761 osarch = System.getProperty("os.arch");
762 return osarch!=null && osarch.indexOf("64")!=-1;
765 /** Displays an error message and returns false if the
766 ImageJ version is less than the one specified. */
767 public static boolean versionLessThan(String version) {
768 boolean lessThan = ImageJ.VERSION.compareTo(version)<0;
770 error("This plugin or macro requires ImageJA "+version+" or later.");
774 /** Displays a "Process all images?" dialog. Returns
775 'flags'+PlugInFilter.DOES_STACKS if the user selects "Yes",
776 'flags' if the user selects "No" and PlugInFilter.DONE
777 if the user selects "Cancel".
779 public static int setupDialog(ImagePlus imp, int flags) {
780 if (imp==null || (ij!=null&&ij.hotkey) || hideProcessStackDialog)
782 int stackSize = imp.getStackSize();
784 if (imp.isComposite() && ((CompositeImage)imp).getMode()==CompositeImage.COMPOSITE)
785 return flags+PlugInFilter.DOES_STACKS;
786 String macroOptions = Macro.getOptions();
787 if (macroOptions!=null) {
788 if (macroOptions.indexOf("stack ")>=0)
789 return flags+PlugInFilter.DOES_STACKS;
793 YesNoCancelDialog d = new YesNoCancelDialog(getInstance(),
794 "Process Stack?", "Process all "+stackSize+" images? There is\n"
795 +"no Undo if you select \"Yes\".");
796 if (d.cancelPressed())
797 return PlugInFilter.DONE;
798 else if (d.yesPressed()) {
799 if (imp.getStack().isVirtual()) {
800 error("Custom code is required to process virtual stacks.");
801 return PlugInFilter.DONE;
804 Recorder.recordOption("stack");
805 return flags+PlugInFilter.DOES_STACKS;
808 Recorder.recordOption("slice");
813 /** Creates a rectangular selection. Removes any existing
814 selection if width or height are less than 1. */
815 public static void makeRectangle(int x, int y, int width, int height) {
816 if (width<=0 || height<0)
817 getImage().killRoi();
819 ImagePlus img = getImage();
820 img.setRoi(x, y, width, height);
824 /** Creates an elliptical selection. Removes any existing
825 selection if width or height are less than 1. */
826 public static void makeOval(int x, int y, int width, int height) {
827 if (width<=0 || height<0)
828 getImage().killRoi();
830 ImagePlus img = getImage();
831 img.setRoi(new OvalRoi(x, y, width, height));
835 /** Creates a straight line selection. */
836 public static void makeLine(int x1, int y1, int x2, int y2) {
837 getImage().setRoi(new Line(x1, y1, x2, y2));
840 /** Creates a straight line selection using double coordinates. */
841 public static void makeLine(double x1, double y1, double x2, double y2) {
842 getImage().setRoi(new Line(x1, y1, x2, y2));
845 /** Sets the minimum and maximum displayed pixel values. */
846 public static void setMinAndMax(double min, double max) {
847 ImagePlus img = getImage();
848 if (img.getBitDepth()==16) {
849 Calibration cal = img.getCalibration();
850 min = cal.getRawValue(min);
851 max = cal.getRawValue(max);
853 img.setDisplayRange(min, max);
857 /** Resets the minimum and maximum displayed pixel values
858 to be the same as the min and max pixel values. */
859 public static void resetMinAndMax() {
860 ImagePlus img = getImage();
861 img.resetDisplayRange();
865 /** Sets the lower and upper threshold levels and displays the image
866 using red to highlight thresholded pixels. May not work correctly on
867 16 and 32 bit images unless the display range has been reset using IJ.resetMinAndMax().
869 public static void setThreshold(double lowerThreshold, double upperThresold) {
870 setThreshold(lowerThreshold, upperThresold, null);
873 /** Sets the lower and upper threshold levels and displays the image using
874 the specified <code>displayMode</code> ("Red", "Black & White", "Over/Under" or "No Update"). */
875 public static void setThreshold(double lowerThreshold, double upperThreshold, String displayMode) {
876 int mode = ImageProcessor.RED_LUT;
877 if (displayMode!=null) {
878 displayMode = displayMode.toLowerCase(Locale.US);
879 if (displayMode.indexOf("black")!=-1)
880 mode = ImageProcessor.BLACK_AND_WHITE_LUT;
881 else if (displayMode.indexOf("over")!=-1)
882 mode = ImageProcessor.OVER_UNDER_LUT;
883 else if (displayMode.indexOf("no")!=-1)
884 mode = ImageProcessor.NO_LUT_UPDATE;
886 ImagePlus img = getImage();
887 if (img.getBitDepth()==16) {
888 Calibration cal = img.getCalibration();
889 lowerThreshold = cal.getRawValue(lowerThreshold);
890 upperThreshold = cal.getRawValue(upperThreshold);
892 img.getProcessor().setThreshold(lowerThreshold, upperThreshold, mode);
893 if (mode != ImageProcessor.NO_LUT_UPDATE) {
894 img.getProcessor().setLutAnimation(true);
899 /** Disables thresholding. */
900 public static void resetThreshold() {
901 ImagePlus img = getImage();
902 ImageProcessor ip = img.getProcessor();
904 ip.setLutAnimation(true);
908 /** For IDs less than zero, activates the image with the specified ID.
909 For IDs greater than zero, activates the Nth image. */
910 public static void selectWindow(int id) {
912 id = WindowManager.getNthImageID(id);
913 ImagePlus imp = WindowManager.getImage(id);
915 error("Macro Error", "Image "+id+" not found or no images are open.");
916 if (Interpreter.isBatchMode()) {
917 ImagePlus imp2 = WindowManager.getCurrentImage();
918 if (imp2!=null && imp2!=imp) imp2.saveRoi();
919 WindowManager.setTempCurrentImage(imp);
920 WindowManager.setWindow(null);
922 ImageWindow win = imp.getWindow();
924 WindowManager.setWindow(win);
925 long start = System.currentTimeMillis();
926 // timeout after 2 seconds unless current thread is event dispatch thread
927 String thread = Thread.currentThread().getName();
928 int timeout = thread!=null&&thread.indexOf("EventQueue")!=-1?0:2000;
931 imp = WindowManager.getCurrentImage();
932 if (imp!=null && imp.getID()==id)
933 return; // specified image is now active
934 if ((System.currentTimeMillis()-start)>timeout) {
935 WindowManager.setCurrentWindow(win);
942 /** Activates the window with the specified title. */
943 public static void selectWindow(String title) {
944 long start = System.currentTimeMillis();
945 while (System.currentTimeMillis()-start<3000) { // 3 sec timeout
946 Frame frame = WindowManager.getFrame(title);
947 if (frame!=null && !(frame instanceof ImageWindow)) {
951 int[] wList = WindowManager.getIDList();
952 int len = wList!=null?wList.length:0;
953 for (int i=0; i<len; i++) {
954 ImagePlus imp = WindowManager.getImage(wList[i]);
956 if (imp.getTitle().equals(title)) {
957 selectWindow(imp.getID());
964 error("Macro Error", "No window with the title \""+title+"\" found.");
967 static void selectWindow(Frame frame) {
969 long start = System.currentTimeMillis();
972 if (WindowManager.getFrontWindow()==frame)
973 return; // specified window is now in front
974 if ((System.currentTimeMillis()-start)>1000) {
975 WindowManager.setWindow(frame);
976 return; // 1 second timeout
981 /** Sets the foreground color. */
982 public static void setForegroundColor(int red, int green, int blue) {
983 setColor(red, green, blue, true);
986 /** Sets the background color. */
987 public static void setBackgroundColor(int red, int green, int blue) {
988 setColor(red, green, blue, false);
991 static void setColor(int red, int green, int blue, boolean foreground) {
992 if (red<0) red=0; if (green<0) green=0; if (blue<0) blue=0;
993 if (red>255) red=255; if (green>255) green=255; if (blue>255) blue=255;
994 Color c = new Color(red, green, blue);
996 Toolbar.setForegroundColor(c);
997 ImagePlus img = WindowManager.getCurrentImage();
999 img.getProcessor().setColor(c);
1001 Toolbar.setBackgroundColor(c);
1004 /** Switches to the specified tool, where id = Toolbar.RECTANGLE (0),
1005 Toolbar.OVAL (1), etc. */
1006 public static void setTool(int id) {
1007 Toolbar.getInstance().setTool(id);
1010 /** Switches to the specified tool, where 'name' is "rect", "elliptical",
1011 "brush", etc. Returns 'false' if the name is not recognized. */
1012 public static boolean setTool(String name) {
1013 return Toolbar.getInstance().setTool(name);
1016 /** Equivalent to clicking on the current image at (x,y) with the
1017 wand tool. Returns the number of points in the resulting ROI. */
1018 public static int doWand(int x, int y) {
1019 ImagePlus img = getImage();
1020 ImageProcessor ip = img.getProcessor();
1021 if ((img.getType()==ImagePlus.GRAY32) && Double.isNaN(ip.getPixelValue(x,y)))
1023 Wand w = new Wand(ip);
1024 double t1 = ip.getMinThreshold();
1025 if (t1==ImageProcessor.NO_THRESHOLD)
1026 w.autoOutline(x, y);
1028 w.autoOutline(x, y, t1, ip.getMaxThreshold());
1030 Roi previousRoi = img.getRoi();
1031 Roi roi = new PolygonRoi(w.xpoints, w.ypoints, w.npoints, Roi.TRACED_ROI);
1034 // add/subtract this ROI to the previous one if the shift/alt key is down
1035 if (previousRoi!=null)
1036 roi.update(shiftKeyDown(), altKeyDown());
1041 /** Sets the transfer mode used by the <i>Edit/Paste</i> command, where mode is "Copy", "Blend", "Average", "Difference",
1042 "Transparent", "Transparent2", "AND", "OR", "XOR", "Add", "Subtract", "Multiply", or "Divide". */
1043 public static void setPasteMode(String mode) {
1044 mode = mode.toLowerCase(Locale.US);
1045 int m = Blitter.COPY;
1046 if (mode.startsWith("ble") || mode.startsWith("ave"))
1047 m = Blitter.AVERAGE;
1048 else if (mode.startsWith("diff"))
1049 m = Blitter.DIFFERENCE;
1050 else if (mode.indexOf("zero")!=-1)
1051 m = Blitter.COPY_ZERO_TRANSPARENT;
1052 else if (mode.startsWith("tran"))
1053 m = Blitter.COPY_TRANSPARENT;
1054 else if (mode.startsWith("and"))
1056 else if (mode.startsWith("or"))
1058 else if (mode.startsWith("xor"))
1060 else if (mode.startsWith("sub"))
1061 m = Blitter.SUBTRACT;
1062 else if (mode.startsWith("add"))
1064 else if (mode.startsWith("div"))
1066 else if (mode.startsWith("mul"))
1067 m = Blitter.MULTIPLY;
1068 else if (mode.startsWith("min"))
1070 else if (mode.startsWith("max"))
1072 Roi.setPasteMode(m);
1075 /** Returns a reference to the active image. Displays an error
1076 message and aborts the macro if no images are open. */
1077 public static ImagePlus getImage() { //ts
1078 ImagePlus img = WindowManager.getCurrentImage();
1086 /** Switches to the specified stack slice, where 1<='slice'<=stack-size. */
1087 public static void setSlice(int slice) {
1088 getImage().setSlice(slice);
1091 /** Returns the ImageJ version number as a string. */
1092 public static String getVersion() {
1093 return ImageJ.VERSION;
1096 /** Returns the path to the home ("user.home"), startup (ImageJ directory), plugins, macros,
1097 temp, current or image directory if <code>title</code> is "home", "startup",
1098 "plugins", "macros", "temp", "current" or "image", otherwise, displays a dialog
1099 and returns the path to the directory selected by the user.
1100 Returns null if the specified directory is not found or the user
1101 cancels the dialog box. Also aborts the macro if the user cancels
1103 public static String getDirectory(String title) {
1104 if (title.equals("plugins"))
1105 return Menus.getPlugInsPath();
1106 else if (title.equals("macros"))
1107 return Menus.getMacrosPath();
1108 else if (title.equals("luts"))
1109 return Prefs.getHomeDir()+File.separator+"luts"+File.separator;
1110 else if (title.equals("home"))
1111 return System.getProperty("user.home") + File.separator;
1112 else if (title.equals("startup")||title.equals("imagej"))
1113 return Prefs.getHomeDir() + File.separator;
1114 else if (title.equals("current"))
1115 return OpenDialog.getDefaultDirectory();
1116 else if (title.equals("temp")) {
1117 String dir = System.getProperty("java.io.tmpdir");
1118 if (dir!=null && !dir.endsWith(File.separator)) dir += File.separator;
1120 } else if (title.equals("image")) {
1121 ImagePlus imp = WindowManager.getCurrentImage();
1122 FileInfo fi = imp!=null?imp.getOriginalFileInfo():null;
1123 if (fi!=null && fi.directory!=null)
1124 return fi.directory;
1128 DirectoryChooser dc = new DirectoryChooser(title);
1129 String dir = dc.getDirectory();
1132 else if (Recorder.record)
1133 Recorder.record("getDirectory", dir);
1138 /** Displays a file open dialog box and then opens the tiff, dicom,
1139 fits, pgm, jpeg, bmp, gif, lut, roi, or text file selected by
1140 the user. Displays an error message if the selected file is not
1141 in one of the supported formats, or if it is not found. */
1142 public static void open() {
1146 /** Opens and displays a tiff, dicom, fits, pgm, jpeg, bmp, gif, lut,
1147 roi, or text file. Displays an error message if the specified file
1148 is not in one of the supported formats, or if it is not found. */
1149 public static void open(String path) {
1150 if (ij==null && Menus.getCommands()==null) init();
1151 Opener o = new Opener();
1152 macroRunning = true;
1153 if (path==null || path.equals(""))
1157 macroRunning = false;
1160 /** Open the specified file as a tiff, bmp, dicom, fits, pgm, gif
1161 or jpeg image and returns an ImagePlus object if successful.
1162 Calls HandleExtraFileTypes plugin if the file type is not recognised.
1163 Note that 'path' can also be a URL. */
1164 public static ImagePlus openImage(String path) {
1165 return (new Opener()).openImage(path);
1168 /** Saves the current image, lookup table, selection or text window to the specified file path.
1169 The path must end in ".tif", ".jpg", ".gif", ".zip", ".raw", ".avi", ".bmp", ".fits", ".pgm", ".png", ".lut", ".roi" or ".txt". */
1170 public static void save(String path) {
1174 /** Saves the specified image, lookup table or selection to the specified file path.
1175 The path must end in ".tif", ".jpg", ".gif", ".zip", ".raw", ".avi", ".bmp", ".fits", ".pgm", ".png", ".lut", ".roi" or ".txt". */
1176 public static void save(ImagePlus imp, String path) {
1177 int dotLoc = path.lastIndexOf('.');
1179 saveAs(imp, path.substring(dotLoc+1), path);
1181 error("The save() macro function requires a file name extension.\n \n"+path);
1184 /* Saves the active image, lookup table, selection, measurement results, selection XY
1185 coordinates or text window to the specified file path. The format argument must be "tiff",
1186 "jpeg", "gif", "zip", "raw", "avi", "bmp", "fits", "pgm", "png", "text image", "lut", "selection", "measurements",
1187 "xy Coordinates" or "text". If <code>path</code> is null or an emply string, a file
1188 save dialog is displayed. */
1189 public static void saveAs(String format, String path) {
1190 saveAs(null, format, path);
1193 /* Saves the specified image. The format argument must be "tiff",
1194 "jpeg", "gif", "zip", "raw", "avi", "bmp", "fits", "pgm", "png",
1195 "text image", "lut", "selection" or "xy Coordinates". */
1196 public static void saveAs(ImagePlus imp, String format, String path) {
1197 if (format==null) return;
1198 if (path!=null && path.length()==0) path = null;
1199 format = format.toLowerCase(Locale.US);
1200 if (format.indexOf("tif")!=-1) {
1201 path = updateExtension(path, ".tif");
1203 } else if (format.indexOf("jpeg")!=-1 || format.indexOf("jpg")!=-1) {
1204 path = updateExtension(path, ".jpg");
1206 } else if (format.indexOf("gif")!=-1) {
1207 path = updateExtension(path, ".gif");
1209 } else if (format.indexOf("text image")!=-1) {
1210 path = updateExtension(path, ".txt");
1211 format = "Text Image...";
1212 } else if (format.indexOf("text")!=-1 || format.indexOf("txt")!=-1) {
1213 if (path!=null && !path.endsWith(".xls"))
1214 path = updateExtension(path, ".txt");
1216 } else if (format.indexOf("zip")!=-1) {
1217 path = updateExtension(path, ".zip");
1219 } else if (format.indexOf("raw")!=-1) {
1220 path = updateExtension(path, ".raw");
1221 format = "Raw Data...";
1222 } else if (format.indexOf("avi")!=-1) {
1223 path = updateExtension(path, ".avi");
1225 } else if (format.indexOf("bmp")!=-1) {
1226 path = updateExtension(path, ".bmp");
1228 } else if (format.indexOf("fits")!=-1) {
1229 path = updateExtension(path, ".fits");
1231 } else if (format.indexOf("png")!=-1) {
1232 path = updateExtension(path, ".png");
1234 } else if (format.indexOf("pgm")!=-1) {
1235 path = updateExtension(path, ".pgm");
1237 } else if (format.indexOf("lut")!=-1) {
1238 path = updateExtension(path, ".lut");
1240 } else if (format.indexOf("measurements")!=-1) {
1241 format = "Measurements...";
1242 } else if (format.indexOf("selection")!=-1 || format.indexOf("roi")!=-1) {
1243 path = updateExtension(path, ".roi");
1244 format = "Selection...";
1245 } else if (format.indexOf("xy")!=-1 || format.indexOf("coordinates")!=-1) {
1246 path = updateExtension(path, ".txt");
1247 format = "XY Coordinates...";
1249 error("Unrecognized format: "+format);
1253 run(imp, format, "save=["+path+"]");
1256 static String updateExtension(String path, String extension) {
1257 if (path==null) return null;
1258 int dotIndex = path.lastIndexOf(".");
1260 path = path.substring(0, dotIndex) + extension;
1266 /** Creates a new imagePlus. <code>Type</code> should contain "8-bit", "16-bit", "32-bit" or "RGB".
1267 In addition, it can contain "white", "black" or "ramp" (the default is "white"). <code>Width</code>
1268 and <code>height</code> specify the width and height of the image in pixels.
1269 <code>Depth</code> specifies the number of stack slices. */
1270 public static ImagePlus createImage(String title, String type, int width, int height, int depth) {
1271 type = type.toLowerCase(Locale.US);
1273 if (type.indexOf("16")!=-1) bitDepth = 16;
1274 if (type.indexOf("24")!=-1||type.indexOf("rgb")!=-1) bitDepth = 24;
1275 if (type.indexOf("32")!=-1) bitDepth = 32;
1276 int options = NewImage.FILL_WHITE;
1277 if (bitDepth==16 || bitDepth==32)
1278 options = NewImage.FILL_BLACK;
1279 if (type.indexOf("white")!=-1)
1280 options = NewImage.FILL_WHITE;
1281 else if (type.indexOf("black")!=-1)
1282 options = NewImage.FILL_BLACK;
1283 else if (type.indexOf("ramp")!=-1)
1284 options = NewImage.FILL_RAMP;
1285 options += NewImage.CHECK_AVAILABLE_MEMORY;
1286 return NewImage.createImage(title, width, height, depth, bitDepth, options);
1289 /** Opens a new image. <code>Type</code> should contain "8-bit", "16-bit", "32-bit" or "RGB".
1290 In addition, it can contain "white", "black" or "ramp" (the default is "white"). <code>Width</code>
1291 and <code>height</code> specify the width and height of the image in pixels.
1292 <code>Depth</code> specifies the number of stack slices. */
1293 public static void newImage(String title, String type, int width, int height, int depth) {
1294 ImagePlus imp = createImage(title, type, width, height, depth);
1296 macroRunning = true;
1298 macroRunning = false;
1302 /** Returns true if the <code>Esc</code> key was pressed since the
1303 last ImageJ command started to execute or since resetEscape() was called. */
1304 public static boolean escapePressed() {
1305 return escapePressed;
1308 /** This method sets the <code>Esc</code> key to the "up" position.
1309 The Executer class calls this method when it runs
1310 an ImageJ command in a separate thread. */
1311 public static void resetEscape() {
1312 escapePressed = false;
1315 /** Causes IJ.error() and IJ.showMessage() output to be temporarily redirected to the "Log" window. */
1316 public static void redirectErrorMessages() {
1317 redirectErrorMessages = true;
1320 /** Returns the state of the 'redirectErrorMessages' flag. The File/Import/Image Sequence command sets this flag.*/
1321 public static boolean redirectingErrorMessages() {
1322 return redirectErrorMessages;
1325 /** Temporarily suppress "plugin not found" errors. */
1326 public static void suppressPluginNotFoundError() {
1327 suppressPluginNotFoundError = true;
1330 /** Returns an instance of the class loader ImageJ uses to run plugins. */
1331 public static ClassLoader getClassLoader() {
1332 if (classLoader==null) {
1333 String pluginsDir = Menus.getPlugInsPath();
1334 if (pluginsDir==null) {
1335 String home = System.getProperty("plugins.dir");
1337 if (!home.endsWith(Prefs.separator)) home+=Prefs.separator;
1338 pluginsDir = home+"plugins"+Prefs.separator;
1339 if (!(new File(pluginsDir)).isDirectory()) pluginsDir = home;
1342 if (pluginsDir==null)
1343 return IJ.class.getClassLoader();
1346 classLoader = new PluginClassLoader(pluginsDir, true);
1348 classLoader = new PluginClassLoader(pluginsDir);
1354 /** Returns the size, in pixels, of the primary display. */
1355 public static Dimension getScreenSize() {
1356 if (screenSize==null) {
1357 if (isWindows()) { // GraphicsEnvironment.getConfigurations is *very* slow on Windows
1358 screenSize = Toolkit.getDefaultToolkit().getScreenSize();
1361 if (GraphicsEnvironment.isHeadless())
1362 screenSize = new Dimension(0, 0);
1364 // Can't use Toolkit.getScreenSize() on Linux because it returns
1365 // size of all displays rather than just the primary display.
1366 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
1367 GraphicsDevice[] gd = ge.getScreenDevices();
1368 GraphicsConfiguration[] gc = gd[0].getConfigurations();
1369 Rectangle bounds = gc[0].getBounds();
1370 if (bounds.x==0&&bounds.y==0)
1371 screenSize = new Dimension(bounds.width, bounds.height);
1373 screenSize = Toolkit.getDefaultToolkit().getScreenSize();
1379 static void abort() {
1380 if (ij!=null || Interpreter.isBatchMode())
1381 throw new RuntimeException(Macro.MACRO_CANCELED);