From 4fec4052025b9f0ba36f1a9a9698c99aa19314f2 Mon Sep 17 00:00:00 2001 From: Wayne Rasband Date: Tue, 13 Oct 2009 20:36:44 -0400 Subject: [PATCH] Daily build 13 October 2009 --- .gitignore | 3 + Fakefile | 11 + IJ_Props.txt | 784 ++-- MANIFEST.MF | 4 +- Makefile | 42 + aREADME.txt | 14 +- applet.html | 26 +- build.xml | 146 +- compile.bat | 5 + compile.sh | 5 + ij/CommandListener.java | 17 +- ij/Executer.java | 314 +- ij/ImageJ.java | 1420 +++--- ij/ImageJApplet.java | 88 +- ij/ImageListener.java | 14 +- ij/ImagePlus.java | 3886 ++++++++--------- ij/ImageStack.java | 606 +-- ij/LookUpTable.java | 274 +- ij/Macro.java | 328 +- ij/Prefs.java | 1076 ++--- ij/RecentOpener.java | 38 +- ij/SocketListener.java | 66 - ij/Undo.java | 226 +- ij/VirtualStack.java | 181 +- ij/WindowManager.java | 900 ++-- ij/gui/ColorChooser.java | 103 +- ij/gui/GUI.java | 68 +- ij/gui/GenericDialog.java | 2426 +++++----- ij/gui/HTMLDialog.java | 70 +- ij/gui/HistogramWindow.java | 762 ++-- ij/gui/ImageCanvas.java | 43 +- ij/gui/ImageLayout.java | 208 +- ij/gui/ImageWindow.java | 1218 +++--- ij/gui/Line.java | 968 ++-- ij/gui/MessageDialog.java | 130 +- ij/gui/MultiLineLabel.java | 194 +- ij/gui/NewImage.java | 656 +-- ij/gui/Plot.java | 1320 +++--- ij/gui/PlotWindow.java | 817 ++-- ij/gui/ProfilePlot.java | 636 +-- ij/gui/ProgressBar.java | 232 +- ij/gui/Roi.java | 2436 +++++------ ij/gui/RoiBrush.java | 89 +- ij/gui/SaveChangesDialog.java | 196 +- ij/gui/StackWindow.java | 488 +-- ij/gui/TextRoi.java | 664 +-- ij/gui/Toolbar.java | 2088 ++++----- ij/gui/TrimmedButton.java | 42 +- ij/gui/WaitForUserDialog.java | 188 +- ij/gui/Wand.java | 694 +-- ij/gui/YesNoCancelDialog.java | 216 +- ij/io/DirectoryChooser.java | 248 +- ij/io/FileInfo.java | 426 +- ij/io/FileOpener.java | 1154 ++--- ij/io/FileSaver.java | 1234 +++--- ij/io/ImageReader.java | 1908 ++++---- ij/io/ImageWriter.java | 490 +-- ij/io/ImportDialog.java | 492 +-- ij/io/OpenDialog.java | 452 +- ij/io/Opener.java | 1884 ++++---- ij/io/PluginClassLoader.java | 168 +- ij/io/PluginInstaller.java | 202 +- ij/io/RandomAccessStream.java | 376 +- ij/io/RoiDecoder.java | 388 +- ij/io/RoiEncoder.java | 154 +- ij/io/SaveDialog.java | 406 +- ij/io/TextEncoder.java | 108 +- ij/io/TiffDecoder.java | 1492 +++---- ij/io/TiffEncoder.java | 952 ++-- ij/macro/Functions.java | 2 + ij/macro/MacroConstants.java | 132 +- ij/macro/MacroRunner.java | 282 +- ij/macro/ReturnException.java | 19 +- ij/macro/Symbol.java | 36 +- ij/macro/Tokenizer.java | 564 +-- ij/macro/Variable.java | 124 +- ij/measure/Calibration.java | 886 ++-- ij/measure/Measurements.java | 28 +- ij/measure/ResultsTable.java | 1153 ++--- ij/measure/SplineFitter.java | 66 +- ij/plugin/Animator.java | 650 +-- ij/plugin/BMP_Reader.java | 620 +-- ij/plugin/ClassChecker.java | 216 +- ij/plugin/ColorPicker.java | 546 +-- ij/plugin/Colors.java | 406 +- ij/plugin/CommandLister.java | 146 +- ij/plugin/Compiler.java | 278 +- ij/plugin/ContrastEnhancer.java | 634 +-- ij/plugin/Converter.java | 316 +- ij/plugin/DICOM.java | 3108 ++++++------- ij/plugin/Duplicator.java | 344 +- ij/plugin/FFT.java | 780 ++-- ij/plugin/FolderOpener.java | 890 ++-- ij/plugin/Histogram.java | 296 +- ij/plugin/Hotkeys.java | 252 +- ij/plugin/HyperStackConverter.java | 334 +- ij/plugin/ImageCalculator.java | 630 +-- ij/plugin/ImagesToStack.java | 446 +- ij/plugin/Installer.java | 306 +- ij/plugin/JavaProperties.java | 264 +- ij/plugin/LutLoader.java | 636 +-- ij/plugin/MacroInstaller.java | 846 ++-- ij/plugin/Macro_Runner.java | 404 +- ij/plugin/MeasurementsWriter.java | 104 +- ij/plugin/Memory.java | 328 +- ij/plugin/MemoryMonitor.java | 212 +- ij/plugin/MontageMaker.java | 422 +- ij/plugin/NewPlugin.java | 416 +- ij/plugin/PNG_Writer.java | 166 +- ij/plugin/PlugIn.java | 24 +- ij/plugin/RGBStackConverter.java | 480 +- ij/plugin/RGBStackMerge.java | 718 +-- ij/plugin/Raw.java | 46 +- ij/plugin/Resizer.java | 816 ++-- ij/plugin/RoiReader.java | 98 +- ij/plugin/Scaler.java | 692 +-- ij/plugin/Selection.java | 664 +-- ij/plugin/SimpleCommands.java | 200 +- ij/plugin/SpecifyROI.java | 352 +- ij/plugin/StackEditor.java | 344 +- ij/plugin/StackReverser.java | 64 +- ij/plugin/StackWriter.java | 338 +- ij/plugin/Straightener.java | 414 +- ij/plugin/TextFileReader.java | 36 +- ij/plugin/TextReader.java | 19 +- ij/plugin/TextWriter.java | 35 +- ij/plugin/ThreadLister.java | 158 +- ij/plugin/Thresholder.java | 566 +-- ij/plugin/Timer.java | 408 +- ij/plugin/URLOpener.java | 236 +- ij/plugin/WandToolOptions.java | 88 +- ij/plugin/WindowOrganizer.java | 338 +- ij/plugin/XYCoordinates.java | 264 +- ij/plugin/ZProjector.java | 1348 +++--- ij/plugin/Zoom.java | 172 +- ij/plugin/filter/Analyzer.java | 1780 ++++---- ij/plugin/filter/Benchmark.java | 208 +- ij/plugin/filter/Binary.java | 478 +- ij/plugin/filter/Calibrator.java | 826 ++-- ij/plugin/filter/Duplicater.java | 58 +- ij/plugin/filter/EDM.java | 904 ++-- ij/plugin/filter/ExtendedPlugInFilter.java | 144 +- ij/plugin/filter/FFTCustomFilter.java | 358 +- ij/plugin/filter/FFTFilter.java | 878 ++-- ij/plugin/filter/Filler.java | 452 +- ij/plugin/filter/Filters.java | 226 +- ij/plugin/filter/FractalBoxCounter.java | 482 +- ij/plugin/filter/ImageMath.java | 946 ++-- ij/plugin/filter/ImageProperties.java | 580 +-- ij/plugin/filter/Info.java | 536 +-- ij/plugin/filter/LineGraphAnalyzer.java | 144 +- ij/plugin/filter/LutApplier.java | 114 +- ij/plugin/filter/LutViewer.java | 306 +- ij/plugin/filter/ParticleAnalyzer.java | 1824 ++++---- ij/plugin/filter/PlugInFilter.java | 172 +- ij/plugin/filter/PlugInFilterRunner.java | 996 ++--- ij/plugin/filter/Profiler.java | 162 +- ij/plugin/filter/Projector.java | 763 +--- ij/plugin/filter/Resizer.java | 30 +- ij/plugin/filter/RoiWriter.java | 108 +- ij/plugin/filter/SaltAndPepper.java | 90 +- ij/plugin/filter/ScaleDialog.java | 374 +- ij/plugin/filter/Shadows.java | 172 +- ij/plugin/filter/ThresholdToSelection.java | 590 +-- ij/plugin/filter/Transformer.java | 98 +- ij/plugin/filter/Writer.java | 90 +- ij/plugin/filter/XYWriter.java | 142 +- ij/plugin/filter/ZAxisProfiler.java | 174 +- ij/plugin/frame/Channels.java | 440 +- ij/plugin/frame/ContrastAdjuster.java | 2222 +++++----- ij/plugin/frame/Editor.java | 1044 +---- ij/plugin/frame/Fitter.java | 528 +-- ij/plugin/frame/Fonts.java | 230 +- ij/plugin/frame/LineWidthAdjuster.java | 382 +- ij/plugin/frame/PasteController.java | 174 +- ij/plugin/frame/PlugInFrame.java | 126 +- ij/plugin/frame/Recorder.java | 862 ++-- ij/plugin/frame/RoiManager.java | 2958 ++++++------- ij/plugin/frame/ThresholdAdjuster.java | 1626 +++---- ij/process/BinaryProcessor.java | 346 +- ij/process/Blitter.java | 118 +- ij/process/ByteBlitter.java | 340 +- ij/process/ByteStatistics.java | 332 +- ij/process/ColorBlitter.java | 282 +- ij/process/ColorStatistics.java | 134 +- ij/process/EllipseFitter.java | 349 +- ij/process/FHT.java | 1102 ++--- ij/process/FloatBlitter.java | 286 +- ij/process/FloatStatistics.java | 448 +- ij/process/ImageConverter.java | 412 +- ij/process/ImageStatistics.java | 450 +- ij/process/MedianCut.java | 808 ++-- ij/process/ShortBlitter.java | 294 +- ij/process/StackConverter.java | 498 +-- ij/process/StackProcessor.java | 384 +- ij/process/StackStatistics.java | 530 +-- ij/process/TypeConverter.java | 486 +-- ij/text/TextCanvas.java | 406 +- ij/text/TextPanel.java | 1524 +++---- ij/text/TextWindow.java | 602 +-- ij/util/Java2.java | 110 +- ij/util/StringSorter.java | 64 +- ij/util/Tools.java | 374 +- macros/PropagateMinAndMax.txt | 14 +- macros/{Stack%20Tools.txt => Stack Tools.txt} | 0 nbproject/build-impl.xml | 497 +++ nbproject/genfiles.properties | 8 + nbproject/project.properties | 57 + nbproject/project.xml | 19 + policy | 8 + release-notes.html | 15 +- run_appletviewer.bat | 4 + 212 files changed, 50699 insertions(+), 53565 deletions(-) create mode 100644 .gitignore create mode 100644 Fakefile create mode 100644 Makefile create mode 100644 compile.bat create mode 100755 compile.sh delete mode 100644 ij/SocketListener.java rename macros/{Stack%20Tools.txt => Stack Tools.txt} (100%) create mode 100755 nbproject/build-impl.xml create mode 100755 nbproject/genfiles.properties create mode 100755 nbproject/project.properties create mode 100755 nbproject/project.xml create mode 100644 policy create mode 100644 run_appletviewer.bat diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..e2d08a6a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.class +*.jar +*~ diff --git a/Fakefile b/Fakefile new file mode 100644 index 000000000..1b9014dbf --- /dev/null +++ b/Fakefile @@ -0,0 +1,11 @@ +JAVAVERSION=1.4 +all <- ij.jar + +MAINCLASS(ij.jar)=ij.ImageJ +ij.jar <- ij/**/*.java \ + icon.gif[images/icon.gif] aboutja.jpg[images/aboutja.jpg] plugins/*.java \ + plugins/JavaScriptEvaluator.class plugins/MacAdapter.class \ + IJ_Props.txt macros/*.txt + +JARSIGNEROPTS=-signedjar signed-ij.jar ij.jar dscho +signed-ij.jar[jarsigner $JARSIGNEROPTS] <- ij.jar diff --git a/IJ_Props.txt b/IJ_Props.txt index b462bb329..09ff70dbd 100644 --- a/IJ_Props.txt +++ b/IJ_Props.txt @@ -1,393 +1,393 @@ -# IJ_Props.txt - This is the ImageJ properties file. ImageJ uses -# the information in this file to install plug-ins in menus. -# ImageJ looks for this file in ij.jar. It can be edited by -# opening ij.jar with a ZIP utility. With WinZip, you can open -# ij.jar and double click on IJ_Props.txt. - -# Note that commands must be unique. - -# Version 1.43 - -# Popup Menu -popup01=Undo -popup02=- -popup03=Cut -popup04=Copy -popup05=Paste -popup06=- -popup07=Clear -popup08=Fill -popup09=Draw -popup10=- -popup11=Rename... -popup12=Duplicate... - -# Plugins installed in the File/New submenu -new01="Image...[n]",ij.plugin.Commands("new") -new02="Hyperstack...",ij.plugin.HyperStackConverter("new") -new03="Text Window[N]",ij.plugin.NewPlugin("text") -new04="Internal Clipboard",ij.plugin.Clipboard("show") -new05="System Clipboard[V]",ij.plugin.Clipboard("showsys") - -# Plugins installed in the File/Import submenu -import01="Image Sequence...",ij.plugin.FolderOpener -import02="Raw...",ij.plugin.Raw -import03="LUT... ",ij.plugin.LutLoader -import04="Text Image... ",ij.plugin.TextReader -import05="Text File... ",ij.plugin.TextFileReader -import06="Results...",ij.plugin.SimpleCommands("import") -import07="URL...",ij.plugin.URLOpener -import08="Stack From List...",ij.plugin.ListVirtualStack -import09="TIFF Virtual Stack...",ij.plugin.FileInfoVirtualStack -import10="AVI...",ij.plugin.AVI_Reader -#import08="TWAIN...",ij.plugin.twain.Twain -#import09="Capture Video...",QT_Capture -#import10="QuickTime Movie...",Movie_Opener -#import11="Pict...",QuickTime_Opener - -# Plugins installed in the File/Save As submenu -save01="Tiff...",ij.plugin.filter.Writer("tiff") -save02="Gif...",ij.plugin.filter.Writer("gif") -save03="Jpeg...",ij.plugin.filter.Writer("jpeg") -save04="Text Image...",ij.plugin.filter.Writer("text") -save05="ZIP...",ij.plugin.filter.Writer("zip") -save06="Raw Data...",ij.plugin.filter.Writer("raw") -save07="Image Sequence... ",ij.plugin.StackWriter -save08="AVI... ",ij.plugin.filter.AVI_Writer -save09="BMP...",ij.plugin.filter.Writer("bmp") -save10="PNG...",ij.plugin.filter.Writer("png") +# IJ_Props.txt - This is the ImageJ properties file. ImageJ uses +# the information in this file to install plug-ins in menus. +# ImageJ looks for this file in ij.jar. It can be edited by +# opening ij.jar with a ZIP utility. With WinZip, you can open +# ij.jar and double click on IJ_Props.txt. + +# Note that commands must be unique. + +# Version 1.43 + +# Popup Menu +popup01=Undo +popup02=- +popup03=Cut +popup04=Copy +popup05=Paste +popup06=- +popup07=Clear +popup08=Fill +popup09=Draw +popup10=- +popup11=Rename... +popup12=Duplicate... + +# Plugins installed in the File/New submenu +new01="Image...[n]",ij.plugin.Commands("new") +new02="Hyperstack...",ij.plugin.HyperStackConverter("new") +new03="Text Window[N]",ij.plugin.NewPlugin("text") +new04="Internal Clipboard",ij.plugin.Clipboard("show") +new05="System Clipboard[V]",ij.plugin.Clipboard("showsys") + +# Plugins installed in the File/Import submenu +import01="Image Sequence...",ij.plugin.FolderOpener +import02="Raw...",ij.plugin.Raw +import03="LUT... ",ij.plugin.LutLoader +import04="Text Image... ",ij.plugin.TextReader +import05="Text File... ",ij.plugin.TextFileReader +import06="Results...",ij.plugin.SimpleCommands("import") +import07="URL...",ij.plugin.URLOpener +import08="Stack From List...",ij.plugin.ListVirtualStack +import09="TIFF Virtual Stack...",ij.plugin.FileInfoVirtualStack +import10="AVI...",ij.plugin.AVI_Reader +#import08="TWAIN...",ij.plugin.twain.Twain +#import09="Capture Video...",QT_Capture +#import10="QuickTime Movie...",Movie_Opener +#import11="Pict...",QuickTime_Opener + +# Plugins installed in the File/Save As submenu +save01="Tiff...",ij.plugin.filter.Writer("tiff") +save02="Gif...",ij.plugin.filter.Writer("gif") +save03="Jpeg...",ij.plugin.filter.Writer("jpeg") +save04="Text Image...",ij.plugin.filter.Writer("text") +save05="ZIP...",ij.plugin.filter.Writer("zip") +save06="Raw Data...",ij.plugin.filter.Writer("raw") +save07="Image Sequence... ",ij.plugin.StackWriter +save08="AVI... ",ij.plugin.filter.AVI_Writer +save09="BMP...",ij.plugin.filter.Writer("bmp") +save10="PNG...",ij.plugin.filter.Writer("png") save11="PGM...",ij.plugin.filter.Writer("pgm") -save12="FITS...",ij.plugin.filter.Writer("fits") -save13="LUT...",ij.plugin.filter.Writer("lut") -save14="Selection...",ij.plugin.filter.RoiWriter -save15="XY Coordinates...",ij.plugin.filter.XYWriter -save16="Measurements...",ij.plugin.MeasurementsWriter -save17="Text...",ij.plugin.TextWriter -#save18="QuickTime Movie... ",QT_Movie_Writer - -# Plugins installed in the Edit/Selection submenu -selection01="Select All[a]",ij.plugin.Selection("all") -selection02="Select None[A]",ij.plugin.Selection("none") -selection03="Restore Selection[E]",ij.plugin.Selection("restore") -selection04="Fit Spline",ij.plugin.Selection("spline") -selection05="Fit Ellipse",ij.plugin.Selection("ellipse") -selection06="Convex Hull",ij.plugin.Selection("hull") -selection07="Make Inverse",ij.plugin.Selection("inverse") -selection08="Create Mask",ij.plugin.Selection("mask") -selection09="Create Selection",ij.plugin.Selection("from") -selection10=- -selection11="Rotate...",ij.plugin.Selection("rotate") -selection12="Enlarge...",ij.plugin.Selection("enlarge") -selection13="Make Band...",ij.plugin.Selection("band") -selection14="Specify...",ij.plugin.SpecifyROI -selection15="Straighten...",ij.plugin.Straightener -selection16="Add to Manager [t]",ij.plugin.Selection("add") - -# Plugins installed in the Edit/Options submenu -options01="Line Width...",ij.plugin.Options("line") -options02="Input/Output...",ij.plugin.Options("io") -options03="Fonts...",ij.plugin.frame.Fonts -options04="Profile Plot Options...",ij.plugin.filter.Profiler("set") -options05="Point Tool...",ij.plugin.Colors("point") -options06="Wand Tool...",ij.plugin.WandToolOptions -options07="Colors...",ij.plugin.Colors -options08="Appearance...",ij.plugin.Options("display") -options09="Conversions...",ij.plugin.Options("conv") -options10="Memory & Threads...",ij.plugin.Memory -options11="Proxy Settings...",ij.plugin.ProxySettings -options12="Compiler...",ij.plugin.Compiler("options") -options13="Misc...",ij.plugin.Options("misc") - -# Plugins installed in the Image/Adjust submenu -adjust01="Brightness/Contrast...[C]",ij.plugin.frame.ContrastAdjuster -adjust02="Window/Level...",ij.plugin.frame.ContrastAdjuster("wl") -adjust03="Color Balance...",ij.plugin.frame.ContrastAdjuster("balance") -adjust04="Threshold...[T]",ij.plugin.frame.ThresholdAdjuster -adjust05="Size...",ij.plugin.Resizer -adjust06="Canvas Size...",ij.plugin.CanvasResizer -adjust07="Line Width... ",ij.plugin.frame.LineWidthAdjuster - -# Plugins installed in the Image/Color submenu -color01="Split Channels",ij.plugin.filter.RGBStackSplitter -color02="Merge Channels...",ij.plugin.RGBStackMerge -color03="Channels Tool... ",ij.plugin.frame.Channels -color04=- -color05="Stack to RGB",ij.plugin.RGBStackConverter -color06="Make Composite",ij.plugin.CompositeConverter -color07="Show LUT",ij.plugin.filter.LutViewer -color08="Edit LUT...",ij.plugin.LUT_Editor -color09="Color Picker...[K]",ij.plugin.ColorPicker - -# Plugins installed in the Image/Stacks submenu -stacks01="Add Slice",ij.plugin.StackEditor("add") -stacks02="Delete Slice",ij.plugin.StackEditor("delete") -stacks03="Next Slice [>]",ij.plugin.Animator("next") -stacks04="Previous Slice [<]",ij.plugin.Animator("previous") -stacks05="Set Slice...",ij.plugin.Animator("set") -stacks06=- -stacks07="Images to Stack",ij.plugin.ImagesToStack -stacks08="Stack to Images",ij.plugin.StackEditor("toimages") -stacks09="Make Montage...",ij.plugin.MontageMaker -stacks10="Reslice [/]...",ij.plugin.Slicer -stacks11="Orthogonal Views",ij.plugin.Orthogonal_Views -stacks12="Z Project...",ij.plugin.ZProjector -stacks13="3D Project...",ij.plugin.filter.Projector -stacks14="Plot Z-axis Profile",ij.plugin.filter.ZAxisProfiler -stacks15=- -stacks16="Label...",ij.plugin.filter.StackLabeler -stacks17="Combine...",ij.plugin.StackCombiner -stacks18="Concatenate...",ij.plugin.Concatenator -stacks19=- -stacks20="Start Animation [\\]",ij.plugin.Animator("start") -stacks21="Stop Animation",ij.plugin.Animator("stop") -stacks22="Animation Options...",ij.plugin.Animator("options") - -# Plugins installed in the Image/Hyperstacks submenu -hyperstacks01="New Hyperstack...",ij.plugin.HyperStackConverter("new") -hyperstacks02="Stack to Hyperstack...",ij.plugin.HyperStackConverter("stacktohs") -hyperstacks03="Hyperstack to Stack",ij.plugin.HyperStackConverter("hstostack") -hyperstacks04="Reduce Dimensionality...",ij.plugin.HyperStackReducer -hyperstacks05="Channels Tool...[Z]",ij.plugin.frame.Channels - -# Plugins installed in the Image/Transform submenu -transform01="Flip Horizontally",ij.plugin.filter.Transformer("fliph") -transform02="Flip Vertically",ij.plugin.filter.Transformer("flipv") -transform03="Flip Z",ij.plugin.StackReverser -transform04="Rotate 90 Degrees Right",ij.plugin.filter.Transformer("right") -transform05="Rotate 90 Degrees Left",ij.plugin.filter.Transformer("left") -transform06="Rotate... ",ij.plugin.filter.Rotator -transform07="Translate...",ij.plugin.filter.Translator - -# Plugins installed in the Image/Zoom submenu -zoom01="In[+]",ij.plugin.Zoom("in") -zoom02="Out[-]",ij.plugin.Zoom("out") -zoom03="Original Scale[4]",ij.plugin.Zoom("orig") -zoom04="View 100%[5]",ij.plugin.Zoom("100%") -zoom05="To Selection",ij.plugin.Zoom("to") -zoom06="Set... ",ij.plugin.Zoom("set") -#zoom07="Maximize",ij.plugin.Zoom("max") - -# Plugins installed in the Image/Lookup Tables submenu -lookup01="Invert LUT",ij.plugin.LutLoader("invert") -lookup02="Apply LUT",ij.plugin.filter.LutApplier -lookup03=- -lookup04="Fire",ij.plugin.LutLoader("fire") -lookup05="Grays",ij.plugin.LutLoader("grays") -lookup06="Ice",ij.plugin.LutLoader("ice") -lookup07="Spectrum",ij.plugin.LutLoader("spectrum") -lookup08="3-3-2 RGB",ij.plugin.LutLoader("3-3-2 RGB") -lookup09="Red",ij.plugin.LutLoader("red") -lookup10="Green",ij.plugin.LutLoader("green") -lookup11="Blue",ij.plugin.LutLoader("blue") -lookup12="Cyan",ij.plugin.LutLoader("cyan") -lookup13="Magenta",ij.plugin.LutLoader("magenta") -lookup14="Yellow",ij.plugin.LutLoader("yellow") -lookup15="Red/Green",ij.plugin.LutLoader("redgreen") - -# Plug-ins installed in the Process/Noise submenu -noise01="Add Noise",ij.plugin.filter.Filters("add") -noise02="Add Specified Noise...",ij.plugin.filter.Filters("noise") -noise03="Salt and Pepper",ij.plugin.filter.SaltAndPepper -noise04=- -noise05="Despeckle",ij.plugin.filter.RankFilters("despeckle") -noise06="Remove Outliers...",ij.plugin.filter.RankFilters("outliers") - -# Plugins installed in the Process/Shadows submenu -shadows01="North",ij.plugin.filter.Shadows("north") -shadows02="Northeast",ij.plugin.filter.Shadows("northeast") -shadows03="East",ij.plugin.filter.Shadows("east") -shadows04="Southeast",ij.plugin.filter.Shadows("southeast") -shadows05="South",ij.plugin.filter.Shadows("south") -shadows06="Southwest",ij.plugin.filter.Shadows("southwest") -shadows07="West",ij.plugin.filter.Shadows("west") -shadows08="Northwest",ij.plugin.filter.Shadows("northwest") -shadows09=- -shadows10="Shadows Demo",ij.plugin.filter.Shadows("demo") - -# Plugins installed in the Process/Binary submenu -binary01="Make Binary",ij.plugin.Thresholder -binary02="Convert to Mask",ij.plugin.Thresholder("mask") -binary03="Find Maxima...",ij.plugin.filter.MaximumFinder -binary04=- -binary05="Erode",ij.plugin.filter.Binary("erode") -binary06="Dilate",ij.plugin.filter.Binary("dilate") -binary07="Open",ij.plugin.filter.Binary("open") -# Can't use "Close" because it conflicts with File/Close -binary08="Close-",ij.plugin.filter.Binary("close") -binary09="Options...",ij.plugin.filter.Binary("options") -binary10=- -binary11="Outline",ij.plugin.filter.Binary("outline") -binary12="Fill Holes",ij.plugin.filter.Binary("fill") -binary13="Skeletonize",ij.plugin.filter.Binary("skel") -binary14=- -binary15="Distance Map",ij.plugin.filter.EDM("edm") -binary16="Ultimate Points",ij.plugin.filter.EDM("points") -binary17="Watershed",ij.plugin.filter.EDM("watershed") -binary18="Voronoi",ij.plugin.filter.EDM("voronoi") - -# Plugins installed in the Process/Math submenu -math01="Add...",ij.plugin.filter.ImageMath("add") -math02="Subtract...",ij.plugin.filter.ImageMath("sub") -math03="Multiply...",ij.plugin.filter.ImageMath("mul") -math04="Divide...",ij.plugin.filter.ImageMath("div") -math05="AND...",ij.plugin.filter.ImageMath("and") -math06="OR...",ij.plugin.filter.ImageMath("or") -math07="XOR...",ij.plugin.filter.ImageMath("xor") -math08="Min...",ij.plugin.filter.ImageMath("min") -math09="Max...",ij.plugin.filter.ImageMath("max") -math10="Gamma...",ij.plugin.filter.ImageMath("gamma") -math11="Set...",ij.plugin.filter.ImageMath("set") -math12="Log",ij.plugin.filter.ImageMath("log") -math13="Exp",ij.plugin.filter.ImageMath("exp") -math14="Square",ij.plugin.filter.ImageMath("sqr") -math15="Square Root",ij.plugin.filter.ImageMath("sqrt") -math16="Reciprocal",ij.plugin.filter.ImageMath("reciprocal") -math17="NaN Background",ij.plugin.filter.ImageMath("nan") -math18="Abs",ij.plugin.filter.ImageMath("abs") -math19="Macro...",ij.plugin.filter.ImageMath("macro") - -# Plugins installed in the Process/FFT submenu -fft01="FFT",ij.plugin.FFT("fft") -fft02="Inverse FFT",ij.plugin.FFT("inverse") -fft03="Redisplay Power Spectrum",ij.plugin.FFT("redisplay") -fft04="FFT Options...",ij.plugin.FFT("options") -fft05=- -fft06="Bandpass Filter...",ij.plugin.filter.FFTFilter -fft07="Custom Filter...",ij.plugin.filter.FFTCustomFilter -fft08="FD Math...",ij.plugin.FFTMath -fft09="Swap Quadrants",ij.plugin.FFT("swap") - -# Plugins installed in the Process/Filters submenu -filters01="Convolve...",ij.plugin.filter.Convolver -filters02="Gaussian Blur...",ij.plugin.filter.GaussianBlur -filters03="Median...",ij.plugin.filter.RankFilters("median") -filters04="Mean...",ij.plugin.filter.RankFilters("mean") -filters05="Minimum...",ij.plugin.filter.RankFilters("min") -filters06="Maximum...",ij.plugin.filter.RankFilters("max") -filters07="Unsharp Mask...",ij.plugin.filter.UnsharpMask -filters08="Variance...",ij.plugin.filter.RankFilters("variance") -filters09=- -filters10="Show Circular Masks...",ij.plugin.filter.RankFilters("masks") - -# Plugins installed in the File/Batch submenu -batch01="Measure...",ij.plugin.BatchMeasure -batch02="Convert...",ij.plugin.BatchConverter -batch03="Macro... ",ij.plugin.BatchProcesser -batch04="Virtual Stack...",ij.plugin.BatchProcesser("stack") - - -# Plugins installed in the Analyze/Gels submenu -gels01="Select First Lane[1]",ij.plugin.GelAnalyzer("first") -gels02="Select Next Lane[2]",ij.plugin.GelAnalyzer("next") -gels03="Plot Lanes[3]",ij.plugin.GelAnalyzer("plot") -gels04="Re-plot Lanes",ij.plugin.GelAnalyzer("replot") -gels05="Reset",ij.plugin.GelAnalyzer("reset") -gels06="Label Peaks",ij.plugin.GelAnalyzer("label") -gels07="Draw Lane Outlines",ij.plugin.GelAnalyzer("draw") -gels08="Gel Analyzer Options...",ij.plugin.GelAnalyzer("options") - -# Plugins installed in the Analyze/Tools submenu -tools01="Save XY Coordinates...",ij.plugin.XYCoordinates -tools02="Fractal Box Count...",ij.plugin.filter.FractalBoxCounter -tools03="Analyze Line Graph",ij.plugin.filter.LineGraphAnalyzer -tools04="Curve Fitting...",ij.plugin.frame.Fitter -tools05="ROI Manager...",ij.plugin.frame.RoiManager -tools06="Scale Bar...",ij.plugin.ScaleBar -tools07="Calibration Bar...",ij.plugin.filter.CalibrationBar - -# Plugins installed in the Plugins menu -plug-in01=>"Macros" -plug-in02=>"Shortcuts" -plug-in03=>"Utilities" -plug-in04=>"New_" -plug-in05="Compile and Run...",ij.plugin.Compiler -#plug-in06=- -#plug-in07=>"User_Plugins" - -# Install user plugins located in ij.jar to Plugins>User Plugins submenu -#user_plugins01="Red And Blue",RedAndBlue_ -#user_plugins02="Inverter",Inverter_ - - -# Plugins installed in the Plugins/Macros submenu -# 'MACROS_MENU_COMMANDS' in MacroInstaller must be updated when items are added -macros01="Install...[M]",ij.plugin.MacroInstaller -macros02="Run...",ij.plugin.Macro_Runner -macros03="Edit...",ij.plugin.Compiler("edit") -macros04="Startup Macros...",ij.plugin.Commands("startup") -macros05="Record...",ij.plugin.frame.Recorder -macros06=- - -# Plugins installed in the Plugins/Shortcuts submenu -shortcuts01="Create Shortcut... ",ij.plugin.Hotkeys("install") -shortcuts02="Install Plugin...",ij.plugin.Installer -shortcuts03="Remove...",ij.plugin.Hotkeys("remove") -shortcuts04=- - -# Plugins installed in the Plugins/Utilities submenu -utilities01="Control Panel...",ij.plugin.ControlPanel -utilities02="ImageJ Properties...",ij.plugin.JavaProperties -utilities03="List Shortcuts...",ij.plugin.CommandLister("shortcuts") -utilities04="Threads...",ij.plugin.ThreadLister -utilities05="Benchmark",ij.plugin.filter.Benchmark -utilities06="Reset...",ij.plugin.SimpleCommands("reset") -utilities07="Monitor Memory...",ij.plugin.MemoryMonitor -utilities08="Search...",ij.plugin.SimpleCommands("search") -utilities09="Capture Screen [G]",ij.plugin.ScreenGrabber -utilities10="Capture Image",ij.plugin.ScreenGrabber("image") -utilities11="Find Commands... [l]",ij.plugin.CommandFinder - -# Plugins installed in the Plugins/New submenu -new_01="Macro",ij.plugin.NewPlugin("macro") -new_02="JavaScript",ij.plugin.NewPlugin("javascript") -new_03=- -new_04="Plugin",ij.plugin.NewPlugin("plugin") -new_05="Plugin Filter",ij.plugin.NewPlugin("filter") -new_06="Plugin Frame",ij.plugin.NewPlugin("frame") -new_07=- -new_08="Text Window...",ij.plugin.NewPlugin("text+dialog") -new_09="Table...",ij.plugin.NewPlugin("table") - -# Plugins installed in the Help/About submenu -about01="About This Submenu...",ij.plugin.SimpleCommands("about") -about02=- - -# URL of directory containing the sample images -# Used when ImageJ is running as an application, -# otherwise applet.getDocumentBase()+"/images" is used. -images.location=http://rsb.info.nih.gov/ij/images/ - -# Images installed in the Open Samples submenu -# RawReader expects a string with "name width height nImages bitsPerPixel offset [white]" -open01="AuPbSn 40 (56K)",ij.plugin.URLOpener("AuPbSn40.jpg") -open02="Bat Cochlea Volume (19K)",ij.plugin.URLOpener("bat-cochlea-volume.zip") -open03="Bat Cochlea Renderings (449K)",ij.plugin.URLOpener("bat-cochlea-renderings.zip") -open04="Blobs (25K)[B]",ij.plugin.URLOpener("blobs.gif") -open05="Boats (356K)",ij.plugin.URLOpener("boats.gif") -open06="Bridge (174K)",ij.plugin.URLOpener("bridge.gif") -open07="Cardio (768K, RGB DICOM)",ij.plugin.URLOpener("cardio.dcm.zip") -open08="Cell Colony (31K)",ij.plugin.URLOpener("Cell_Colony.jpg") -open09="Clown (14K)",ij.plugin.URLOpener("clown.jpg") -open10="Confocal Series (2.2MB)",ij.plugin.URLOpener("confocal-series.zip") -open11="CT (420K, 16-bit DICOM)",ij.plugin.URLOpener("ct.dcm.zip") -open12="Dot Blot (7K)",ij.plugin.URLOpener("Dot_Blot.jpg") -open13="Embryos (42K)",ij.plugin.URLOpener("embryos.jpg") -open14="Fluorescent Cells (400K)",ij.plugin.URLOpener("FluorescentCells.zip") -open15="Fly Brain (1MB)",ij.plugin.URLOpener("flybrain.zip") -open16="Gel (105K)",ij.plugin.URLOpener("gel.gif") -open17="HeLa Cells (1.3M, 48-bit RGB)",ij.plugin.URLOpener("hela-cells.zip") -open18="Leaf (36K)",ij.plugin.URLOpener("leaf.jpg") -open19="Lena (68K)",ij.plugin.URLOpener("lena-std.tif") -open20="Line Graph (21K)",ij.plugin.URLOpener("LineGraph.jpg") -open21="Mitosis (26MB, 5D stack)",ij.plugin.URLOpener("Spindly-GFP.zip") -open22="MRI Stack (528K)",ij.plugin.URLOpener("mri-stack.zip") -open23="M51 Galaxy (177K, 16-bits)",ij.plugin.URLOpener("m51.zip") -open24="Neuron (1.6M, 5 channels)",ij.plugin.URLOpener("Rat_Hippocampal_Neuron.zip") -open25="Nile Bend (1.9M)",ij.plugin.URLOpener("NileBend.jpg") -open26="Organ of Corti (2.8M, 4D stack)",ij.plugin.URLOpener("organ-of-corti.zip") -open27="Particles (75K)",ij.plugin.URLOpener("particles.gif") -open28="T1 Head (2.4M, 16-bits)",ij.plugin.URLOpener("t1-head.zip") -open29="T1 Head Renderings (736K)",ij.plugin.URLOpener("t1-rendering.zip") -open30="TEM Filter (112K)",ij.plugin.URLOpener("TEM_filter_sample.jpg") -open31="Tree Rings (48K)",ij.plugin.URLOpener("Tree_Rings.jpg") - +save12="FITS...",ij.plugin.filter.Writer("fits") +save13="LUT...",ij.plugin.filter.Writer("lut") +save14="Selection...",ij.plugin.filter.RoiWriter +save15="XY Coordinates...",ij.plugin.filter.XYWriter +save16="Measurements...",ij.plugin.MeasurementsWriter +save17="Text...",ij.plugin.TextWriter +#save18="QuickTime Movie... ",QT_Movie_Writer + +# Plugins installed in the Edit/Selection submenu +selection01="Select All[a]",ij.plugin.Selection("all") +selection02="Select None[A]",ij.plugin.Selection("none") +selection03="Restore Selection[E]",ij.plugin.Selection("restore") +selection04="Fit Spline",ij.plugin.Selection("spline") +selection05="Fit Ellipse",ij.plugin.Selection("ellipse") +selection06="Convex Hull",ij.plugin.Selection("hull") +selection07="Make Inverse",ij.plugin.Selection("inverse") +selection08="Create Mask",ij.plugin.Selection("mask") +selection09="Create Selection",ij.plugin.Selection("from") +selection10=- +selection11="Rotate...",ij.plugin.Selection("rotate") +selection12="Enlarge...",ij.plugin.Selection("enlarge") +selection13="Make Band...",ij.plugin.Selection("band") +selection14="Specify...",ij.plugin.SpecifyROI +selection15="Straighten...",ij.plugin.Straightener +selection16="Add to Manager [t]",ij.plugin.Selection("add") + +# Plugins installed in the Edit/Options submenu +options01="Line Width...",ij.plugin.Options("line") +options02="Input/Output...",ij.plugin.Options("io") +options03="Fonts...",ij.plugin.frame.Fonts +options04="Profile Plot Options...",ij.plugin.filter.Profiler("set") +options05="Point Tool...",ij.plugin.Colors("point") +options06="Wand Tool...",ij.plugin.WandToolOptions +options07="Colors...",ij.plugin.Colors +options08="Appearance...",ij.plugin.Options("display") +options09="Conversions...",ij.plugin.Options("conv") +options10="Memory & Threads...",ij.plugin.Memory +options11="Proxy Settings...",ij.plugin.ProxySettings +options12="Compiler...",ij.plugin.Compiler("options") +options13="Misc...",ij.plugin.Options("misc") + +# Plugins installed in the Image/Adjust submenu +adjust01="Brightness/Contrast...[C]",ij.plugin.frame.ContrastAdjuster +adjust02="Window/Level...",ij.plugin.frame.ContrastAdjuster("wl") +adjust03="Color Balance...",ij.plugin.frame.ContrastAdjuster("balance") +adjust04="Threshold...[T]",ij.plugin.frame.ThresholdAdjuster +adjust05="Size...",ij.plugin.Resizer +adjust06="Canvas Size...",ij.plugin.CanvasResizer +adjust07="Line Width... ",ij.plugin.frame.LineWidthAdjuster + +# Plugins installed in the Image/Color submenu +color01="Split Channels",ij.plugin.filter.RGBStackSplitter +color02="Merge Channels...",ij.plugin.RGBStackMerge +color03="Channels Tool... ",ij.plugin.frame.Channels +color04=- +color05="Stack to RGB",ij.plugin.RGBStackConverter +color06="Make Composite",ij.plugin.CompositeConverter +color07="Show LUT",ij.plugin.filter.LutViewer +color08="Edit LUT...",ij.plugin.LUT_Editor +color09="Color Picker...[K]",ij.plugin.ColorPicker + +# Plugins installed in the Image/Stacks submenu +stacks01="Add Slice",ij.plugin.StackEditor("add") +stacks02="Delete Slice",ij.plugin.StackEditor("delete") +stacks03="Next Slice [>]",ij.plugin.Animator("next") +stacks04="Previous Slice [<]",ij.plugin.Animator("previous") +stacks05="Set Slice...",ij.plugin.Animator("set") +stacks06=- +stacks07="Images to Stack",ij.plugin.ImagesToStack +stacks08="Stack to Images",ij.plugin.StackEditor("toimages") +stacks09="Make Montage...",ij.plugin.MontageMaker +stacks10="Reslice [/]...",ij.plugin.Slicer +stacks11="Orthogonal Views",ij.plugin.Orthogonal_Views +stacks12="Z Project...",ij.plugin.ZProjector +stacks13="3D Project...",ij.plugin.filter.Projector +stacks14="Plot Z-axis Profile",ij.plugin.filter.ZAxisProfiler +stacks15=- +stacks16="Label...",ij.plugin.filter.StackLabeler +stacks17="Combine...",ij.plugin.StackCombiner +stacks18="Concatenate...",ij.plugin.Concatenator +stacks19=- +stacks20="Start Animation [\\]",ij.plugin.Animator("start") +stacks21="Stop Animation",ij.plugin.Animator("stop") +stacks22="Animation Options...",ij.plugin.Animator("options") + +# Plugins installed in the Image/Hyperstacks submenu +hyperstacks01="New Hyperstack...",ij.plugin.HyperStackConverter("new") +hyperstacks02="Stack to Hyperstack...",ij.plugin.HyperStackConverter("stacktohs") +hyperstacks03="Hyperstack to Stack",ij.plugin.HyperStackConverter("hstostack") +hyperstacks04="Reduce Dimensionality...",ij.plugin.HyperStackReducer +hyperstacks05="Channels Tool...[Z]",ij.plugin.frame.Channels + +# Plugins installed in the Image/Transform submenu +transform01="Flip Horizontally",ij.plugin.filter.Transformer("fliph") +transform02="Flip Vertically",ij.plugin.filter.Transformer("flipv") +transform03="Flip Z",ij.plugin.StackReverser +transform04="Rotate 90 Degrees Right",ij.plugin.filter.Transformer("right") +transform05="Rotate 90 Degrees Left",ij.plugin.filter.Transformer("left") +transform06="Rotate... ",ij.plugin.filter.Rotator +transform07="Translate...",ij.plugin.filter.Translator + +# Plugins installed in the Image/Zoom submenu +zoom01="In[+]",ij.plugin.Zoom("in") +zoom02="Out[-]",ij.plugin.Zoom("out") +zoom03="Original Scale[4]",ij.plugin.Zoom("orig") +zoom04="View 100%[5]",ij.plugin.Zoom("100%") +zoom05="To Selection",ij.plugin.Zoom("to") +zoom06="Set... ",ij.plugin.Zoom("set") +#zoom07="Maximize",ij.plugin.Zoom("max") + +# Plugins installed in the Image/Lookup Tables submenu +lookup01="Invert LUT",ij.plugin.LutLoader("invert") +lookup02="Apply LUT",ij.plugin.filter.LutApplier +lookup03=- +lookup04="Fire",ij.plugin.LutLoader("fire") +lookup05="Grays",ij.plugin.LutLoader("grays") +lookup06="Ice",ij.plugin.LutLoader("ice") +lookup07="Spectrum",ij.plugin.LutLoader("spectrum") +lookup08="3-3-2 RGB",ij.plugin.LutLoader("3-3-2 RGB") +lookup09="Red",ij.plugin.LutLoader("red") +lookup10="Green",ij.plugin.LutLoader("green") +lookup11="Blue",ij.plugin.LutLoader("blue") +lookup12="Cyan",ij.plugin.LutLoader("cyan") +lookup13="Magenta",ij.plugin.LutLoader("magenta") +lookup14="Yellow",ij.plugin.LutLoader("yellow") +lookup15="Red/Green",ij.plugin.LutLoader("redgreen") + +# Plug-ins installed in the Process/Noise submenu +noise01="Add Noise",ij.plugin.filter.Filters("add") +noise02="Add Specified Noise...",ij.plugin.filter.Filters("noise") +noise03="Salt and Pepper",ij.plugin.filter.SaltAndPepper +noise04=- +noise05="Despeckle",ij.plugin.filter.RankFilters("despeckle") +noise06="Remove Outliers...",ij.plugin.filter.RankFilters("outliers") + +# Plugins installed in the Process/Shadows submenu +shadows01="North",ij.plugin.filter.Shadows("north") +shadows02="Northeast",ij.plugin.filter.Shadows("northeast") +shadows03="East",ij.plugin.filter.Shadows("east") +shadows04="Southeast",ij.plugin.filter.Shadows("southeast") +shadows05="South",ij.plugin.filter.Shadows("south") +shadows06="Southwest",ij.plugin.filter.Shadows("southwest") +shadows07="West",ij.plugin.filter.Shadows("west") +shadows08="Northwest",ij.plugin.filter.Shadows("northwest") +shadows09=- +shadows10="Shadows Demo",ij.plugin.filter.Shadows("demo") + +# Plugins installed in the Process/Binary submenu +binary01="Make Binary",ij.plugin.Thresholder +binary02="Convert to Mask",ij.plugin.Thresholder("mask") +binary03="Find Maxima...",ij.plugin.filter.MaximumFinder +binary04=- +binary05="Erode",ij.plugin.filter.Binary("erode") +binary06="Dilate",ij.plugin.filter.Binary("dilate") +binary07="Open",ij.plugin.filter.Binary("open") +# Can't use "Close" because it conflicts with File/Close +binary08="Close-",ij.plugin.filter.Binary("close") +binary09="Options...",ij.plugin.filter.Binary("options") +binary10=- +binary11="Outline",ij.plugin.filter.Binary("outline") +binary12="Fill Holes",ij.plugin.filter.Binary("fill") +binary13="Skeletonize",ij.plugin.filter.Binary("skel") +binary14=- +binary15="Distance Map",ij.plugin.filter.EDM("edm") +binary16="Ultimate Points",ij.plugin.filter.EDM("points") +binary17="Watershed",ij.plugin.filter.EDM("watershed") +binary18="Voronoi",ij.plugin.filter.EDM("voronoi") + +# Plugins installed in the Process/Math submenu +math01="Add...",ij.plugin.filter.ImageMath("add") +math02="Subtract...",ij.plugin.filter.ImageMath("sub") +math03="Multiply...",ij.plugin.filter.ImageMath("mul") +math04="Divide...",ij.plugin.filter.ImageMath("div") +math05="AND...",ij.plugin.filter.ImageMath("and") +math06="OR...",ij.plugin.filter.ImageMath("or") +math07="XOR...",ij.plugin.filter.ImageMath("xor") +math08="Min...",ij.plugin.filter.ImageMath("min") +math09="Max...",ij.plugin.filter.ImageMath("max") +math10="Gamma...",ij.plugin.filter.ImageMath("gamma") +math11="Set...",ij.plugin.filter.ImageMath("set") +math12="Log",ij.plugin.filter.ImageMath("log") +math13="Exp",ij.plugin.filter.ImageMath("exp") +math14="Square",ij.plugin.filter.ImageMath("sqr") +math15="Square Root",ij.plugin.filter.ImageMath("sqrt") +math16="Reciprocal",ij.plugin.filter.ImageMath("reciprocal") +math17="NaN Background",ij.plugin.filter.ImageMath("nan") +math18="Abs",ij.plugin.filter.ImageMath("abs") +math19="Macro...",ij.plugin.filter.ImageMath("macro") + +# Plugins installed in the Process/FFT submenu +fft01="FFT",ij.plugin.FFT("fft") +fft02="Inverse FFT",ij.plugin.FFT("inverse") +fft03="Redisplay Power Spectrum",ij.plugin.FFT("redisplay") +fft04="FFT Options...",ij.plugin.FFT("options") +fft05=- +fft06="Bandpass Filter...",ij.plugin.filter.FFTFilter +fft07="Custom Filter...",ij.plugin.filter.FFTCustomFilter +fft08="FD Math...",ij.plugin.FFTMath +fft09="Swap Quadrants",ij.plugin.FFT("swap") + +# Plugins installed in the Process/Filters submenu +filters01="Convolve...",ij.plugin.filter.Convolver +filters02="Gaussian Blur...",ij.plugin.filter.GaussianBlur +filters03="Median...",ij.plugin.filter.RankFilters("median") +filters04="Mean...",ij.plugin.filter.RankFilters("mean") +filters05="Minimum...",ij.plugin.filter.RankFilters("min") +filters06="Maximum...",ij.plugin.filter.RankFilters("max") +filters07="Unsharp Mask...",ij.plugin.filter.UnsharpMask +filters08="Variance...",ij.plugin.filter.RankFilters("variance") +filters09=- +filters10="Show Circular Masks...",ij.plugin.filter.RankFilters("masks") + +# Plugins installed in the File/Batch submenu +batch01="Measure...",ij.plugin.BatchMeasure +batch02="Convert...",ij.plugin.BatchConverter +batch03="Macro... ",ij.plugin.BatchProcesser +batch04="Virtual Stack...",ij.plugin.BatchProcesser("stack") + + +# Plugins installed in the Analyze/Gels submenu +gels01="Select First Lane[1]",ij.plugin.GelAnalyzer("first") +gels02="Select Next Lane[2]",ij.plugin.GelAnalyzer("next") +gels03="Plot Lanes[3]",ij.plugin.GelAnalyzer("plot") +gels04="Re-plot Lanes",ij.plugin.GelAnalyzer("replot") +gels05="Reset",ij.plugin.GelAnalyzer("reset") +gels06="Label Peaks",ij.plugin.GelAnalyzer("label") +gels07="Draw Lane Outlines",ij.plugin.GelAnalyzer("draw") +gels08="Gel Analyzer Options...",ij.plugin.GelAnalyzer("options") + +# Plugins installed in the Analyze/Tools submenu +tools01="Save XY Coordinates...",ij.plugin.XYCoordinates +tools02="Fractal Box Count...",ij.plugin.filter.FractalBoxCounter +tools03="Analyze Line Graph",ij.plugin.filter.LineGraphAnalyzer +tools04="Curve Fitting...",ij.plugin.frame.Fitter +tools05="ROI Manager...",ij.plugin.frame.RoiManager +tools06="Scale Bar...",ij.plugin.ScaleBar +tools07="Calibration Bar...",ij.plugin.filter.CalibrationBar + +# Plugins installed in the Plugins menu +plug-in01=>"Macros" +plug-in02=>"Shortcuts" +plug-in03=>"Utilities" +plug-in04=>"New_" +plug-in05="Compile and Run...",ij.plugin.Compiler +#plug-in06=- +#plug-in07=>"User_Plugins" + +# Install user plugins located in ij.jar to Plugins>User Plugins submenu +#user_plugins01="Red And Blue",RedAndBlue_ +#user_plugins02="Inverter",Inverter_ + + +# Plugins installed in the Plugins/Macros submenu +# 'MACROS_MENU_COMMANDS' in MacroInstaller must be updated when items are added +macros01="Install...[M]",ij.plugin.MacroInstaller +macros02="Run...",ij.plugin.Macro_Runner +macros03="Edit...",ij.plugin.Compiler("edit") +macros04="Startup Macros...",ij.plugin.Commands("startup") +macros05="Record...",ij.plugin.frame.Recorder +macros06=- + +# Plugins installed in the Plugins/Shortcuts submenu +shortcuts01="Create Shortcut... ",ij.plugin.Hotkeys("install") +shortcuts02="Install Plugin...",ij.plugin.Installer +shortcuts03="Remove...",ij.plugin.Hotkeys("remove") +shortcuts04=- + +# Plugins installed in the Plugins/Utilities submenu +utilities01="Control Panel...",ij.plugin.ControlPanel +utilities02="ImageJ Properties...",ij.plugin.JavaProperties +utilities03="List Shortcuts...",ij.plugin.CommandLister("shortcuts") +utilities04="Threads...",ij.plugin.ThreadLister +utilities05="Benchmark",ij.plugin.filter.Benchmark +utilities06="Reset...",ij.plugin.SimpleCommands("reset") +utilities07="Monitor Memory...",ij.plugin.MemoryMonitor +utilities08="Search...",ij.plugin.SimpleCommands("search") +utilities09="Capture Screen [G]",ij.plugin.ScreenGrabber +utilities10="Capture Image",ij.plugin.ScreenGrabber("image") +utilities11="Find Commands... [l]",ij.plugin.CommandFinder + +# Plugins installed in the Plugins/New submenu +new_01="Macro",ij.plugin.NewPlugin("macro") +new_02="JavaScript",ij.plugin.NewPlugin("javascript") +new_03=- +new_04="Plugin",ij.plugin.NewPlugin("plugin") +new_05="Plugin Filter",ij.plugin.NewPlugin("filter") +new_06="Plugin Frame",ij.plugin.NewPlugin("frame") +new_07=- +new_08="Text Window...",ij.plugin.NewPlugin("text+dialog") +new_09="Table...",ij.plugin.NewPlugin("table") + +# Plugins installed in the Help/About submenu +about01="About This Submenu...",ij.plugin.SimpleCommands("about") +about02=- + +# URL of directory containing the sample images +# Used when ImageJ is running as an application, +# otherwise applet.getDocumentBase()+"/images" is used. +images.location=http://rsb.info.nih.gov/ij/images/ + +# Images installed in the Open Samples submenu +# RawReader expects a string with "name width height nImages bitsPerPixel offset [white]" +open01="AuPbSn 40 (56K)",ij.plugin.URLOpener("AuPbSn40.jpg") +open02="Bat Cochlea Volume (19K)",ij.plugin.URLOpener("bat-cochlea-volume.zip") +open03="Bat Cochlea Renderings (449K)",ij.plugin.URLOpener("bat-cochlea-renderings.zip") +open04="Blobs (25K)[B]",ij.plugin.URLOpener("blobs.gif") +open05="Boats (356K)",ij.plugin.URLOpener("boats.gif") +open06="Bridge (174K)",ij.plugin.URLOpener("bridge.gif") +open07="Cardio (768K, RGB DICOM)",ij.plugin.URLOpener("cardio.dcm.zip") +open08="Cell Colony (31K)",ij.plugin.URLOpener("Cell_Colony.jpg") +open09="Clown (14K)",ij.plugin.URLOpener("clown.jpg") +open10="Confocal Series (2.2MB)",ij.plugin.URLOpener("confocal-series.zip") +open11="CT (420K, 16-bit DICOM)",ij.plugin.URLOpener("ct.dcm.zip") +open12="Dot Blot (7K)",ij.plugin.URLOpener("Dot_Blot.jpg") +open13="Embryos (42K)",ij.plugin.URLOpener("embryos.jpg") +open14="Fluorescent Cells (400K)",ij.plugin.URLOpener("FluorescentCells.zip") +open15="Fly Brain (1MB)",ij.plugin.URLOpener("flybrain.zip") +open16="Gel (105K)",ij.plugin.URLOpener("gel.gif") +open17="HeLa Cells (1.3M, 48-bit RGB)",ij.plugin.URLOpener("hela-cells.zip") +open18="Leaf (36K)",ij.plugin.URLOpener("leaf.jpg") +open19="Lena (68K)",ij.plugin.URLOpener("lena-std.tif") +open20="Line Graph (21K)",ij.plugin.URLOpener("LineGraph.jpg") +open21="Mitosis (26MB, 5D stack)",ij.plugin.URLOpener("Spindly-GFP.zip") +open22="MRI Stack (528K)",ij.plugin.URLOpener("mri-stack.zip") +open23="M51 Galaxy (177K, 16-bits)",ij.plugin.URLOpener("m51.zip") +open24="Neuron (1.6M, 5 channels)",ij.plugin.URLOpener("Rat_Hippocampal_Neuron.zip") +open25="Nile Bend (1.9M)",ij.plugin.URLOpener("NileBend.jpg") +open26="Organ of Corti (2.8M, 4D stack)",ij.plugin.URLOpener("organ-of-corti.zip") +open27="Particles (75K)",ij.plugin.URLOpener("particles.gif") +open28="T1 Head (2.4M, 16-bits)",ij.plugin.URLOpener("t1-head.zip") +open29="T1 Head Renderings (736K)",ij.plugin.URLOpener("t1-rendering.zip") +open30="TEM Filter (112K)",ij.plugin.URLOpener("TEM_filter_sample.jpg") +open31="Tree Rings (48K)",ij.plugin.URLOpener("Tree_Rings.jpg") + diff --git a/MANIFEST.MF b/MANIFEST.MF index 4f01c6dc4..b3cca5b07 100644 --- a/MANIFEST.MF +++ b/MANIFEST.MF @@ -1,2 +1,2 @@ -Main-Class: ij.ImageJ - +Main-Class: ij.ImageJ + diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..942948da8 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +JAVAS=$(wildcard ij/*.java ij/*/*.java ij/*/*/*.java) +CLASSES=$(patsubst %.java,%.class,$(JAVAS)) +ALLCLASSES=ij/*.class ij/*/*.class ij/*/*/*.class +COPYFILES=icon.gif aboutja.jpg plugins/*.class +TEXTFILES=IJ_Props.txt $(wildcard macros/*.txt) + +ifeq ($(JAVA_HOME),) + JAVA_HOME=$(shell ../fiji --print-java-home)/.. +endif + +ifeq ($(shell javac > /dev/null 2>&1; echo $$?),127) + PATH:=$(PATH):$(JAVA_HOME)/bin +endif + +uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') +ifeq ($(uname_O),Cygwin) +PLUGINSHOME=$(shell cygpath --mixed "$(shell pwd)") +CPSEP=\; +TOOLSCP=$(shell cygpath --mixed "$(JAVA_HOME)")/lib/tools.jar +else +PLUGINSHOME=$(shell pwd) +CPSEP=: +TOOLSCP=$(JAVA_HOME)/lib/tools.jar +endif +CLASSPATH=../jars/javac.jar$(CPSEP)$(TOOLSCP)$(CPSEP)$(PLUGINSHOME)/../ImageJ/ij.jar$(CPSEP)$(PLUGINSHOME)/jzlib-1.0.7.jar$(CPSEP). +JAVACOPTS=-O -classpath "$(CLASSPATH)" -source 1.3 -target 1.3 + +ij.jar: $(COPYFILES) $(CLASSES) $(TEXTFILES) + jar cvmf MANIFEST.MF ij.jar $(COPYFILES) $(ALLCLASSES) $(TEXTFILES) + +signed-ij.jar: ij.jar + jarsigner -signedjar signed-ij.jar $(shell cat .jarsignerrc) ij.jar dscho + +icon.gif aboutja.jpg: %: images/% + cp $< $@ + +%.class: %.java + javac $(JAVACOPTS) $(JAVAS) + +clean: + rm -f $(COPYFILES) $(ALLCLASSES) + diff --git a/aREADME.txt b/aREADME.txt index 8539970dd..569279c61 100644 --- a/aREADME.txt +++ b/aREADME.txt @@ -1,7 +1,7 @@ -The ant utility (http://ant.apache.org/) will compile and run ImageJ using -the build file (build.xml) in this directory. There is a version of ant at - - http://rsb.info.nih.gov/ij/download/tools/ant/ant.zip - -set up to use the JVM distributed with the Windows version of ImageJ. -The README included in the ZIP archive has more information. +The ant utility (http://ant.apache.org/) will compile and run ImageJ using +the build file (build.xml) in this directory. There is a version of ant at + + http://rsb.info.nih.gov/ij/download/tools/ant/ant.zip + +set up to use the JVM distributed with the Windows version of ImageJ. +The README included in the ZIP archive has more information. diff --git a/applet.html b/applet.html index 5cb5bc9ea..49a9f7d79 100644 --- a/applet.html +++ b/applet.html @@ -1,13 +1,13 @@ - - - - -ImageJ Applet - - - - - - - - + + + + +ImageJ Applet + + + + + + + + diff --git a/build.xml b/build.xml index 83d94ac30..390938a65 100644 --- a/build.xml +++ b/build.xml @@ -1,73 +1,73 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/compile.bat b/compile.bat new file mode 100644 index 000000000..9c355d936 --- /dev/null +++ b/compile.bat @@ -0,0 +1,5 @@ +javac ij\ImageJ.java +javac ij\plugin\*.java +javac ij\plugin\filter\*.java +javac ij\plugin\frame\*.java +java ij.ImageJ diff --git a/compile.sh b/compile.sh new file mode 100755 index 000000000..35704574d --- /dev/null +++ b/compile.sh @@ -0,0 +1,5 @@ +#!/bin/sh +echo "Cleaning up ... " +ant clean +echo "Building ImageJA ..." +ant diff --git a/ij/CommandListener.java b/ij/CommandListener.java index 6b610a4ec..972ab28ee 100644 --- a/ij/CommandListener.java +++ b/ij/CommandListener.java @@ -1,16 +1 @@ -package ij; - - /** Plugins that implement this interface are notified when ImageJ - is about to run a menu command. There is an example plugin at - http://rsb.info.nih.gov/ij/plugins/download/misc/Command_Listener.java - */ - public interface CommandListener { - - /* The method is called when ImageJ is about to run a menu command, - where 'command' is the name of the command. Return this string - and ImageJ will run the command, return a different command name - and ImageJ will run that command, or return null to not run a command. - */ - public String commandExecuting(String command); - -} +package ij; /** Plugins that implement this interface are notified when ImageJ is about to run a menu command. There is an example plugin at http://rsb.info.nih.gov/ij/plugins/download/misc/Command_Listener.java */ public interface CommandListener { /* The method is called when ImageJ is about to run a menu command, where 'command' is the name of the command. Return this string and ImageJ will run the command, return a different command name and ImageJ will run that command, or return null to not run a command. */ public String commandExecuting(String command); } \ No newline at end of file diff --git a/ij/Executer.java b/ij/Executer.java index b5255f945..510b4ae24 100644 --- a/ij/Executer.java +++ b/ij/Executer.java @@ -1,157 +1,157 @@ -package ij; -import ij.util.Tools; -import ij.text.TextWindow; -import ij.plugin.MacroInstaller; -import ij.plugin.frame.Recorder; -import ij.io.OpenDialog; -import java.io.*; -import java.util.*; -import java.awt.event.KeyEvent; - -/** Runs ImageJ menu commands in a separate thread.*/ -public class Executer implements Runnable { - - private static String previousCommand; - private static CommandListener listener; - private static Vector listeners = new Vector(); - - private String command; - private Thread thread; - - /** Create an Executer to run the specified menu command - in this thread using the active image. */ - public Executer(String cmd) { - command = cmd; - } - - /** Create an Executer that runs the specified menu command - in a separate thread using the active image image. */ - public Executer(String cmd, ImagePlus ignored) { - if (cmd.startsWith("Repeat")) { - command = previousCommand; - IJ.setKeyUp(KeyEvent.VK_SHIFT); - } else { - command = cmd; - if (!(cmd.equals("Undo")||cmd.equals("Close"))) - previousCommand = cmd; - } - IJ.resetEscape(); - thread = new Thread(this, cmd); - thread.setPriority(Math.max(thread.getPriority()-2, Thread.MIN_PRIORITY)); - thread.start(); - } - - public void run() { - if (command==null) return; - if (listeners.size()>0) synchronized (listeners) { - for (int i=0; i", "+" and "-" shortcuts - } catch(Throwable e) { - IJ.showStatus(""); - IJ.showProgress(1, 1); - ImagePlus imp = WindowManager.getCurrentImage(); - if (imp!=null) imp.unlock(); - String msg = e.getMessage(); - if (e instanceof OutOfMemoryError) - IJ.outOfMemory(command); - else if (e instanceof RuntimeException && msg!=null && msg.equals(Macro.MACRO_CANCELED)) - ; //do nothing - else { - CharArrayWriter caw = new CharArrayWriter(); - PrintWriter pw = new PrintWriter(caw); - e.printStackTrace(pw); - String s = caw.toString(); - if (IJ.isMacintosh()) { - if (s.indexOf("ThreadDeath")>0) - return; - s = Tools.fixNewLines(s); - } - int w=350, h=250; - if (s.indexOf("UnsupportedClassVersionError")!=-1) { - if (s.indexOf("version 49.0")!=-1) { - s = e + "\n \nThis plugin requires Java 1.5 or later."; - w=700; h=150; - } - if (s.indexOf("version 50.0")!=-1) { - s = e + "\n \nThis plugin requires Java 1.6 or later."; - w=700; h=150; - } - if (s.indexOf("version 51.0")!=-1) { - s = e + "\n \nThis plugin requires Java 1.7 or later."; - w=700; h=150; - } - } - if (IJ.getInstance()!=null) - new TextWindow("Exception", s, w, h); - else - IJ.log(s); - } - } - } - - void runCommand(String cmd) { - Hashtable table = Menus.getCommands(); - String className = (String)table.get(cmd); - if (className!=null) { - String arg = ""; - if (className.endsWith("\")")) { - // extract string argument (e.g. className("arg")) - int argStart = className.lastIndexOf("(\""); - if (argStart>0) { - arg = className.substring(argStart+2, className.length()-2); - className = className.substring(0, argStart); - } - } - if (IJ.shiftKeyDown() && className.startsWith("ij.plugin.Macro_Runner") && !Menus.getShortcuts().contains("*"+cmd)) - IJ.open(IJ.getDirectory("plugins")+arg); - else - IJ.runPlugIn(cmd, className, arg); - } else { - // Is this command in Plugins>Macros? - if (MacroInstaller.runMacroCommand(cmd)) - return; - // Is this command a LUT name? - String path = IJ.getDirectory("luts")+cmd+".lut"; - File f = new File(path); - if (f.exists()) { - String dir = OpenDialog.getLastDirectory(); - IJ.open(path); - OpenDialog.setLastDirectory(dir); - } else - IJ.error("Unrecognized command: " + cmd); - } - } - - /** Returns the last command executed. Returns null - if no command has been executed. */ - public static String getCommand() { - return previousCommand; - } - - /** Adds the specified command listener. */ - public static void addCommandListener(CommandListener listener) { - listeners.addElement(listener); - } - - /** Removes the specified command listener. */ - public static void removeCommandListener(CommandListener listener) { - listeners.removeElement(listener); - } - -} - - +package ij; +import ij.util.Tools; +import ij.text.TextWindow; +import ij.plugin.MacroInstaller; +import ij.plugin.frame.Recorder; +import ij.io.OpenDialog; +import java.io.*; +import java.util.*; +import java.awt.event.KeyEvent; + +/** Runs ImageJ menu commands in a separate thread.*/ +public class Executer implements Runnable { + + private static String previousCommand; + private static CommandListener listener; + private static Vector listeners = new Vector(); + + private String command; + private Thread thread; + + /** Create an Executer to run the specified menu command + in this thread using the active image. */ + public Executer(String cmd) { + command = cmd; + } + + /** Create an Executer that runs the specified menu command + in a separate thread using the active image image. */ + public Executer(String cmd, ImagePlus ignored) { + if (cmd.startsWith("Repeat")) { + command = previousCommand; + IJ.setKeyUp(KeyEvent.VK_SHIFT); + } else { + command = cmd; + if (!(cmd.equals("Undo")||cmd.equals("Close"))) + previousCommand = cmd; + } + IJ.resetEscape(); + thread = new Thread(this, cmd); + thread.setPriority(Math.max(thread.getPriority()-2, Thread.MIN_PRIORITY)); + thread.start(); + } + + public void run() { + if (command==null) return; + if (listeners.size()>0) synchronized (listeners) { + for (int i=0; i", "+" and "-" shortcuts + } catch(Throwable e) { + IJ.showStatus(""); + IJ.showProgress(1, 1); + ImagePlus imp = WindowManager.getCurrentImage(); + if (imp!=null) imp.unlock(); + String msg = e.getMessage(); + if (e instanceof OutOfMemoryError) + IJ.outOfMemory(command); + else if (e instanceof RuntimeException && msg!=null && msg.equals(Macro.MACRO_CANCELED)) + ; //do nothing + else { + CharArrayWriter caw = new CharArrayWriter(); + PrintWriter pw = new PrintWriter(caw); + e.printStackTrace(pw); + String s = caw.toString(); + if (IJ.isMacintosh()) { + if (s.indexOf("ThreadDeath")>0) + return; + s = Tools.fixNewLines(s); + } + int w=350, h=250; + if (s.indexOf("UnsupportedClassVersionError")!=-1) { + if (s.indexOf("version 49.0")!=-1) { + s = e + "\n \nThis plugin requires Java 1.5 or later."; + w=700; h=150; + } + if (s.indexOf("version 50.0")!=-1) { + s = e + "\n \nThis plugin requires Java 1.6 or later."; + w=700; h=150; + } + if (s.indexOf("version 51.0")!=-1) { + s = e + "\n \nThis plugin requires Java 1.7 or later."; + w=700; h=150; + } + } + if (IJ.getInstance()!=null) + new TextWindow("Exception", s, w, h); + else + IJ.log(s); + } + } + } + + void runCommand(String cmd) { + Hashtable table = Menus.getCommands(); + String className = (String)table.get(cmd); + if (className!=null) { + String arg = ""; + if (className.endsWith("\")")) { + // extract string argument (e.g. className("arg")) + int argStart = className.lastIndexOf("(\""); + if (argStart>0) { + arg = className.substring(argStart+2, className.length()-2); + className = className.substring(0, argStart); + } + } + if (IJ.shiftKeyDown() && className.startsWith("ij.plugin.Macro_Runner") && !Menus.getShortcuts().contains("*"+cmd)) + IJ.open(IJ.getDirectory("plugins")+arg); + else + IJ.runPlugIn(cmd, className, arg); + } else { + // Is this command in Plugins>Macros? + if (MacroInstaller.runMacroCommand(cmd)) + return; + // Is this command a LUT name? + String path = IJ.getDirectory("luts")+cmd+".lut"; + File f = new File(path); + if (f.exists()) { + String dir = OpenDialog.getLastDirectory(); + IJ.open(path); + OpenDialog.setLastDirectory(dir); + } else + IJ.error("Unrecognized command: " + cmd); + } + } + + /** Returns the last command executed. Returns null + if no command has been executed. */ + public static String getCommand() { + return previousCommand; + } + + /** Adds the specified command listener. */ + public static void addCommandListener(CommandListener listener) { + listeners.addElement(listener); + } + + /** Removes the specified command listener. */ + public static void removeCommandListener(CommandListener listener) { + listeners.removeElement(listener); + } + +} + + diff --git a/ij/ImageJ.java b/ij/ImageJ.java index fb69f9c9e..10de31b31 100644 --- a/ij/ImageJ.java +++ b/ij/ImageJ.java @@ -1,710 +1,710 @@ -package ij; - -import java.awt.*; -import java.util.*; -import java.awt.event.*; -import java.io.*; -import java.net.*; -import java.awt.image.*; -import ij.gui.*; -import ij.process.*; -import ij.io.*; -import ij.plugin.*; -import ij.plugin.filter.*; -import ij.plugin.frame.*; -import ij.text.*; -import ij.macro.Interpreter; -import ij.io.Opener; -import ij.util.*; - -/** -This frame is the main ImageJ class. -

-ImageJ is a work of the United States Government. It is in the public domain -and open source. There is no copyright. You are free to do anything you want -with this source but I like to get credit for my work and I would like you to -offer your changes to me so I can possibly add them to the "official" version. - -

-The following command line options are recognized by ImageJ:
-
-  "file-name"
-     Opens a file
-     Example 1: blobs.tif
-     Example 2: /Users/wayne/images/blobs.tif
-     Example3: e81*.tif
-
-  -ijpath path
-     Specifies the path to the directory containing the plugins directory
-     Example: -ijpath /Applications/ImageJ
-
-  -port
-     Specifies the port ImageJ uses to determine if another instance is running
-     Example 1: -port1 (use default port address + 1)
-     Example 2: -port2 (use default port address + 2)
-     Example 3: -port0 (do not check for another instance)
-
-  -macro path [arg]
-     Runs a macro or script, passing it an optional argument,
-     which can be retieved using getArgument()
-     Example 1: -macro analyze.ijm
-     Example 2: -macro analyze /Users/wayne/images/stack1
-
-  -batch path [arg]
-    Runs a macro or script in batch (no GUI) mode, passing it an optional argument.
-    ImageJ exits when the macro finishes.
-
-  -eval "macro code"
-     Evaluates macro code
-     Example 1: -eval "print('Hello, world');"
-     Example 2: -eval "return getVersion();"
-
-  -run command
-     Runs an ImageJ menu command
-     Example: -run "About ImageJ..."
-
-@author Wayne Rasband (wsr@nih.gov) -*/ -public class ImageJ extends Frame implements ActionListener, - MouseListener, KeyListener, WindowListener, ItemListener, Runnable { - - /** Plugins should call IJ.getVersion() to get the version string. */ - public static final String VERSION = "1.43i"; - public static final String build = "-4"; - public static Color backgroundColor = new Color(220,220,220); //224,226,235 - /** SansSerif, 12-point, plain font. */ - public static final Font SansSerif12 = new Font("SansSerif", Font.PLAIN, 12); - /** Address of socket where Image accepts commands */ - public static final int DEFAULT_PORT = 57294; - public static final int STANDALONE=0, EMBEDDED=1; - - private static final String IJ_X="ij.x",IJ_Y="ij.y"; - private static int port = DEFAULT_PORT; - private static String[] arguments; - - private Toolbar toolbar; - private Panel statusBar; - private ProgressBar progressBar; - private Label statusLine; - private boolean firstTime = true; - private java.applet.Applet applet; // null if not running as an applet - private Vector classes = new Vector(); - private boolean exitWhenQuitting; - private boolean quitting; - private long keyPressedTime, actionPerformedTime; - private String lastKeyCommand; - private boolean embedded; - private boolean windowClosed; - - boolean hotkey; - - /** Creates a new ImageJ frame that runs as an application. */ - public ImageJ() { - this(null, STANDALONE); - } - - /** Creates a new ImageJ frame that runs as an applet. */ - public ImageJ(java.applet.Applet applet) { - this(applet, 0); - } - - /** If 'applet' is not null, creates a new ImageJ frame that runs as an applet. - If 'mode' is ImageJ.EMBEDDED and 'applet is null, creates an embedded - version of ImageJ which does not start the SocketListener. */ - public ImageJ(java.applet.Applet applet, int mode) { - super("ImageJ"); - embedded = applet==null && mode==EMBEDDED; - this.applet = applet; - String err1 = Prefs.load(this, applet); - if (IJ.isLinux()) { - backgroundColor = new Color(240,240,240); - setBackground(backgroundColor); - } - Menus m = new Menus(this, applet); - String err2 = m.addMenuBar(); - m.installPopupMenu(this); - setLayout(new GridLayout(2, 1)); - - // Tool bar - toolbar = new Toolbar(); - toolbar.addKeyListener(this); - add(toolbar); - - // Status bar - statusBar = new Panel(); - statusBar.setLayout(new BorderLayout()); - statusBar.setForeground(Color.black); - statusBar.setBackground(backgroundColor); - statusLine = new Label(); - statusLine.setFont(SansSerif12); - statusLine.addKeyListener(this); - statusLine.addMouseListener(this); - statusBar.add("Center", statusLine); - progressBar = new ProgressBar(120, 20); - progressBar.addKeyListener(this); - progressBar.addMouseListener(this); - statusBar.add("East", progressBar); - statusBar.setSize(toolbar.getPreferredSize()); - add(statusBar); - - IJ.init(this, applet); - addKeyListener(this); - addWindowListener(this); - setFocusTraversalKeysEnabled(false); - - Point loc = getPreferredLocation(); - Dimension tbSize = toolbar.getPreferredSize(); - int ijWidth = tbSize.width+10; - int ijHeight = 100; - setCursor(Cursor.getDefaultCursor()); // work-around for JDK 1.1.8 bug - if (IJ.isWindows()) try {setIcon();} catch(Exception e) {} - setBounds(loc.x, loc.y, ijWidth, ijHeight); // needed for pack to work - setLocation(loc.x, loc.y); - pack(); - setResizable(!(IJ.isMacintosh() || IJ.isWindows())); // make resizable on Linux - //if (IJ.isJava15()) { - // try { - // Method method = Frame.class.getMethod("setAlwaysOnTop", new Class[] {boolean.class}); - // method.invoke(this, new Object[]{Boolean.TRUE}); - // } catch(Exception e) {} - //} - show(); - if (err1!=null) - IJ.error(err1); - if (err2!=null) { - IJ.error(err2); - IJ.runPlugIn("ij.plugin.ClassChecker", ""); - } - if (IJ.isMacintosh()&&applet==null) { - Object qh = null; - qh = IJ.runPlugIn("MacAdapter", ""); - if (qh==null) - IJ.runPlugIn("QuitHandler", ""); - } - if (applet==null) - IJ.runPlugIn("ij.plugin.DragAndDrop", ""); - m.installStartupMacroSet(); - String str = m.getMacroCount()==1?" macro)":" macros)"; - String java = "Java "+System.getProperty("java.version")+(IJ.is64Bit()?" [64-bit]":" [32-bit]"); - IJ.showStatus("ImageJ "+VERSION + build + "/"+java+" ("+ m.getPluginCount() + " commands, " + m.getMacroCount() + str); - if (applet==null && !embedded && Prefs.runSocketListener) - new SocketListener(); - configureProxy(); - } - - void configureProxy() { - String server = Prefs.get("proxy.server", null); - if (server==null||server.equals("")) return; - int port = (int)Prefs.get("proxy.port", 0); - if (port==0) return; - String user = Prefs.get("proxy.user", null); - Properties props = System.getProperties(); - props.put("proxySet", "true"); - props.put("http.proxyHost", server); - props.put("http.proxyPort", ""+port); - if (user!=null) - props.put("http.proxyUser", user); - //IJ.log(server+" "+port+" "+user); - } - - void setIcon() throws Exception { - URL url = this.getClass().getResource("/microscope.gif"); - if (url==null) return; - Image img = createImage((ImageProducer)url.getContent()); - if (img!=null) setIconImage(img); - } - - public Point getPreferredLocation() { - if (!IJ.isJava14()) return new Point(0, 0); - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - Rectangle maxBounds = ge.getMaximumWindowBounds(); - int ijX = Prefs.getInt(IJ_X,-99); - int ijY = Prefs.getInt(IJ_Y,-99); - if (ijX>=0 && ijY>0 && ijX<(maxBounds.x+maxBounds.width-75)) - return new Point(ijX, ijY); - Dimension tbsize = toolbar.getPreferredSize(); - int ijWidth = tbsize.width+10; - double percent = maxBounds.width>832?0.8:0.9; - ijX = (int)(percent*(maxBounds.width-ijWidth)); - if (ijX<10) ijX = 10; - return new Point(ijX, maxBounds.y); - } - - void showStatus(String s) { - statusLine.setText(s); - } - - public ProgressBar getProgressBar() { - return progressBar; - } - - public Panel getStatusBar() { - return statusBar; - } - - /** Starts executing a menu command in a separate thread. */ - void doCommand(String name) { - new Executer(name, null); - } - - public void runFilterPlugIn(Object theFilter, String cmd, String arg) { - new PlugInFilterRunner(theFilter, cmd, arg); - } - - public Object runUserPlugIn(String commandName, String className, String arg, boolean createNewLoader) { - return IJ.runUserPlugIn(commandName, className, arg, createNewLoader); - } - - /** Return the current list of modifier keys. */ - public static String modifiers(int flags) { //?? needs to be moved - String s = " [ "; - if (flags == 0) return ""; - if ((flags & Event.SHIFT_MASK) != 0) s += "Shift "; - if ((flags & Event.CTRL_MASK) != 0) s += "Control "; - if ((flags & Event.META_MASK) != 0) s += "Meta "; - if ((flags & Event.ALT_MASK) != 0) s += "Alt "; - s += "] "; - return s; - } - - /** Handle menu events. */ - public void actionPerformed(ActionEvent e) { - if ((e.getSource() instanceof MenuItem)) { - MenuItem item = (MenuItem)e.getSource(); - String cmd = e.getActionCommand(); - if (item.getParent()==Menus.openRecentMenu) { - new RecentOpener(cmd); // open image in separate thread - return; - } - int flags = e.getModifiers(); - //IJ.log(""+KeyEvent.getKeyModifiersText(flags)); - hotkey = false; - actionPerformedTime = System.currentTimeMillis(); - long ellapsedTime = actionPerformedTime-keyPressedTime; - if (cmd!=null && (ellapsedTime>=200L||!cmd.equals(lastKeyCommand))) { - if ((flags & Event.ALT_MASK)!=0) - IJ.setKeyDown(KeyEvent.VK_ALT); - if ((flags & Event.SHIFT_MASK)!=0) - IJ.setKeyDown(KeyEvent.VK_SHIFT); - doCommand(cmd); - } - lastKeyCommand = null; - if (IJ.debugMode) IJ.log("actionPerformed: time="+ellapsedTime+", "+e); - } - } - - /** Handles CheckboxMenuItem state changes. */ - public void itemStateChanged(ItemEvent e) { - MenuItem item = (MenuItem)e.getSource(); - MenuComponent parent = (MenuComponent)item.getParent(); - String cmd = e.getItem().toString(); - if ((Menu)parent==Menus.window) - WindowManager.activateWindow(cmd, item); - else - doCommand(cmd); - } - - public void mousePressed(MouseEvent e) { - Undo.reset(); - System.gc(); - String info = "ImageJ "+ImageJ.VERSION+"; Java "+System.getProperty("java.version")+(IJ.is64Bit()?" (64-bit); ":" (32-bit); "); - IJ.showStatus(info+IJ.freeMemory()); - if (IJ.debugMode) - IJ.log("Windows: "+WindowManager.getWindowCount()); - } - - public void mouseReleased(MouseEvent e) {} - public void mouseExited(MouseEvent e) {} - public void mouseClicked(MouseEvent e) {} - public void mouseEntered(MouseEvent e) {} - - public void keyPressed(KeyEvent e) { - //if (e.isConsumed()) return; - int keyCode = e.getKeyCode(); - IJ.setKeyDown(keyCode); - hotkey = false; - if (keyCode==e.VK_CONTROL || keyCode==e.VK_SHIFT) - return; - char keyChar = e.getKeyChar(); - int flags = e.getModifiers(); - if (IJ.debugMode) IJ.log("keyPressed: code=" + keyCode + " (" + KeyEvent.getKeyText(keyCode) - + "), char=\"" + keyChar + "\" (" + (int)keyChar + "), flags=" - + KeyEvent.getKeyModifiersText(flags)); - boolean shift = (flags & e.SHIFT_MASK) != 0; - boolean control = (flags & e.CTRL_MASK) != 0; - boolean alt = (flags & e.ALT_MASK) != 0; - boolean meta = (flags & e.META_MASK) != 0; - String cmd = ""; - ImagePlus imp = WindowManager.getCurrentImage(); - boolean isStack = (imp!=null) && (imp.getStackSize()>1); - - if (imp!=null && !control && ((keyChar>=32 && keyChar<=255) || keyChar=='\b' || keyChar=='\n')) { - Roi roi = imp.getRoi(); - if (roi instanceof TextRoi) { - if ((flags & e.META_MASK)!=0 && IJ.isMacOSX()) return; - if (alt) - switch (keyChar) { - case 'u': case 'm': keyChar = IJ.micronSymbol; break; - case 'A': keyChar = IJ.angstromSymbol; break; - default: - } - ((TextRoi)roi).addChar(keyChar); - return; - } - } - - // Handle one character macro shortcuts - if (!control && !meta) { - Hashtable macroShortcuts = Menus.getMacroShortcuts(); - if (macroShortcuts.size()>0) { - if (shift) - cmd = (String)macroShortcuts.get(new Integer(keyCode+200)); - else - cmd = (String)macroShortcuts.get(new Integer(keyCode)); - if (cmd!=null) { - //MacroInstaller.runMacroCommand(cmd); - MacroInstaller.runMacroShortcut(cmd); - return; - } - } - } - - if (!Prefs.requireControlKey || control || meta) { - Hashtable shortcuts = Menus.getShortcuts(); - if (shift) - cmd = (String)shortcuts.get(new Integer(keyCode+200)); - else - cmd = (String)shortcuts.get(new Integer(keyCode)); - } - - if (cmd==null) { - switch (keyChar) { - case '<': case ',': cmd="Previous Slice [<]"; break; - case '>': case '.': case ';': cmd="Next Slice [>]"; break; - case '+': case '=': cmd="In"; break; - case '-': cmd="Out"; break; - case '/': cmd="Reslice [/]..."; break; - default: - } - } - - if (cmd==null) { - switch(keyCode) { - case KeyEvent.VK_TAB: WindowManager.putBehind(); return; - case KeyEvent.VK_BACK_SPACE: cmd="Clear"; hotkey=true; break; // delete - //case KeyEvent.VK_BACK_SLASH: cmd=IJ.altKeyDown()?"Animation Options...":"Start Animation"; break; - case KeyEvent.VK_EQUALS: cmd="In"; break; - case KeyEvent.VK_MINUS: cmd="Out"; break; - case KeyEvent.VK_SLASH: case 0xbf: cmd="Reslice [/]..."; break; - case KeyEvent.VK_COMMA: case 0xbc: cmd="Previous Slice [<]"; break; - case KeyEvent.VK_PERIOD: case 0xbe: cmd="Next Slice [>]"; break; - case KeyEvent.VK_LEFT: case KeyEvent.VK_RIGHT: case KeyEvent.VK_UP: case KeyEvent.VK_DOWN: // arrow keys - if (imp==null) return; - Roi roi = imp.getRoi(); - if (IJ.shiftKeyDown()&&imp==Orthogonal_Views.getImage()) - return; - boolean stackKey = imp.getStackSize()>1 && (roi==null||IJ.shiftKeyDown()); - boolean zoomKey = roi==null || IJ.shiftKeyDown() || IJ.controlKeyDown(); - if (stackKey && keyCode==KeyEvent.VK_RIGHT) - cmd="Next Slice [>]"; - else if (stackKey && keyCode==KeyEvent.VK_LEFT) - cmd="Previous Slice [<]"; - else if (zoomKey &&keyCode==KeyEvent.VK_DOWN) - cmd="Out"; - else if (zoomKey && keyCode==KeyEvent.VK_UP) - cmd="In"; - else if (roi!=null) { - if ((flags & KeyEvent.ALT_MASK) != 0) - roi.nudgeCorner(keyCode); - else - roi.nudge(keyCode); - return; - } - break; - case KeyEvent.VK_ESCAPE: - abortPluginOrMacro(imp); - return; - case KeyEvent.VK_ENTER: this.toFront(); return; - default: break; - } - } - - if (cmd!=null && !cmd.equals("")) { - if (cmd.equals("Fill")||cmd.equals("Draw")) - hotkey = true; - if (cmd.charAt(0)==MacroInstaller.commandPrefix) - MacroInstaller.runMacroShortcut(cmd); - else { - doCommand(cmd); - keyPressedTime = System.currentTimeMillis(); - lastKeyCommand = cmd; - } - } - } - - public void keyTyped(KeyEvent e) { - char keyChar = e.getKeyChar(); - int flags = e.getModifiers(); - if (IJ.debugMode) IJ.log("keyTyped: char=\"" + keyChar + "\" (" + (int)keyChar - + "), flags= "+Integer.toHexString(flags)+ " ("+KeyEvent.getKeyModifiersText(flags)+")"); - if (keyChar=='\\' || keyChar==171 || keyChar==223) { - if (((flags&Event.ALT_MASK)!=0)) - doCommand("Animation Options..."); - else - doCommand("Start Animation [\\]"); - } - } - - public void keyReleased(KeyEvent e) { - IJ.setKeyUp(e.getKeyCode()); - } - - void abortPluginOrMacro(ImagePlus imp) { - if (imp!=null) { - ImageWindow win = imp.getWindow(); - if (win!=null) { - win.running = false; - win.running2 = false; - } - } - Macro.abort(); - Interpreter.abort(); - if (Interpreter.getInstance()!=null) IJ.beep(); - } - - public void windowClosing(WindowEvent e) { - doCommand("Quit"); - windowClosed = true; - } - - public void windowActivated(WindowEvent e) { - if (IJ.isMacintosh() && !quitting) { - IJ.wait(10); // may be needed for Java 1.4 on OS X - setMenuBar(Menus.getMenuBar()); - } - } - - public void windowClosed(WindowEvent e) {} - public void windowDeactivated(WindowEvent e) {} - public void windowDeiconified(WindowEvent e) {} - public void windowIconified(WindowEvent e) {} - public void windowOpened(WindowEvent e) {} - - /** Adds the specified class to a Vector to keep it from being - garbage collected, causing static fields to be reset. */ - public void register(Class c) { - if (!classes.contains(c)) - classes.addElement(c); - } - - /** Called by ImageJ when the user selects Quit. */ - public void quit() { - Thread thread = new Thread(this, "Quit"); - thread.setPriority(Thread.NORM_PRIORITY); - thread.start(); - } - - /** Returns true if ImageJ is exiting. */ - public boolean quitting() { - return quitting; - } - - /** Called once when ImageJ quits. */ - public void savePreferences(Properties prefs) { - Point loc = getLocation(); - prefs.put(IJ_X, Integer.toString(loc.x)); - prefs.put(IJ_Y, Integer.toString(loc.y)); - //prefs.put(IJ_WIDTH, Integer.toString(size.width)); - //prefs.put(IJ_HEIGHT, Integer.toString(size.height)); - } - - public static void main(String args[]) { - if (System.getProperty("java.version").substring(0,3).compareTo("1.4")<0) { - javax.swing.JOptionPane.showMessageDialog(null,"ImageJ "+VERSION+" requires Java 1.4.1 or later."); - System.exit(0); - } - boolean noGUI = false; - int mode = STANDALONE; - arguments = args; - int nArgs = args!=null?args.length:0; - for (int i=0; i0 && DEFAULT_PORT+delta<65536) - port = DEFAULT_PORT+delta; - } - } - } - // If ImageJ is already running then isRunning() - // will pass the arguments to it using sockets. - if (nArgs>0 && !noGUI && (mode==STANDALONE) && isRunning(args)) - return; - ImageJ ij = IJ.getInstance(); - if (!noGUI && (ij==null || (ij!=null && !ij.isShowing()))) { - ij = new ImageJ(null, mode); - ij.exitWhenQuitting = true; - } - int macros = 0; - for (int i=0; iMenus.WINDOW_MENU_ITEMS && !(IJ.macroRunning()&&WindowManager.getImageCount()==0)) { - GenericDialog gd = new GenericDialog("ImageJ", this); - gd.addMessage("Are you sure you want to quit ImageJ?"); - gd.showDialog(); - quitting = !gd.wasCanceled(); - windowClosed = false; - } - if (!quitting) - return; - if (!WindowManager.closeAllWindows()) { - quitting = false; - return; - } - //IJ.log("savePreferences"); - if (applet==null) { - saveWindowLocations(); - Prefs.savePreferences(); - } - setVisible(false); - //IJ.log("dispose"); - dispose(); - if (exitWhenQuitting) - System.exit(0); - } - - void saveWindowLocations() { - Frame frame = WindowManager.getFrame("B&C"); - if (frame!=null) - Prefs.saveLocation(ContrastAdjuster.LOC_KEY, frame.getLocation()); - frame = WindowManager.getFrame("Threshold"); - if (frame!=null) - Prefs.saveLocation(ThresholdAdjuster.LOC_KEY, frame.getLocation()); - frame = WindowManager.getFrame("Results"); - if (frame!=null) { - Prefs.saveLocation(TextWindow.LOC_KEY, frame.getLocation()); - Dimension d = frame.getSize(); - Prefs.set(TextWindow.WIDTH_KEY, d.width); - Prefs.set(TextWindow.HEIGHT_KEY, d.height); - } - } - -} +package ij; + +import java.awt.*; +import java.util.*; +import java.awt.event.*; +import java.io.*; +import java.net.*; +import java.awt.image.*; +import ij.gui.*; +import ij.process.*; +import ij.io.*; +import ij.plugin.*; +import ij.plugin.filter.*; +import ij.plugin.frame.*; +import ij.text.*; +import ij.macro.Interpreter; +import ij.io.Opener; +import ij.util.*; + +/** +This frame is the main ImageJ class. +

+ImageJ is a work of the United States Government. It is in the public domain +and open source. There is no copyright. You are free to do anything you want +with this source but I like to get credit for my work and I would like you to +offer your changes to me so I can possibly add them to the "official" version. + +

+The following command line options are recognized by ImageJ:
+
+  "file-name"
+     Opens a file
+     Example 1: blobs.tif
+     Example 2: /Users/wayne/images/blobs.tif
+     Example3: e81*.tif
+
+  -ijpath path
+     Specifies the path to the directory containing the plugins directory
+     Example: -ijpath /Applications/ImageJ
+
+  -port
+     Specifies the port ImageJ uses to determine if another instance is running
+     Example 1: -port1 (use default port address + 1)
+     Example 2: -port2 (use default port address + 2)
+     Example 3: -port0 (do not check for another instance)
+
+  -macro path [arg]
+     Runs a macro or script, passing it an optional argument,
+     which can be retieved using getArgument()
+     Example 1: -macro analyze.ijm
+     Example 2: -macro analyze /Users/wayne/images/stack1
+
+  -batch path [arg]
+    Runs a macro or script in batch (no GUI) mode, passing it an optional argument.
+    ImageJ exits when the macro finishes.
+
+  -eval "macro code"
+     Evaluates macro code
+     Example 1: -eval "print('Hello, world');"
+     Example 2: -eval "return getVersion();"
+
+  -run command
+     Runs an ImageJ menu command
+     Example: -run "About ImageJ..."
+
+@author Wayne Rasband (wsr@nih.gov) +*/ +public class ImageJ extends Frame implements ActionListener, + MouseListener, KeyListener, WindowListener, ItemListener, Runnable { + + /** Plugins should call IJ.getVersion() to get the version string. */ + public static final String VERSION = "1.43i"; + public static final String build = "-5"; + public static Color backgroundColor = new Color(220,220,220); //224,226,235 + /** SansSerif, 12-point, plain font. */ + public static final Font SansSerif12 = new Font("SansSerif", Font.PLAIN, 12); + /** Address of socket where Image accepts commands */ + public static final int DEFAULT_PORT = 57294; + public static final int STANDALONE=0, EMBEDDED=1; + + private static final String IJ_X="ij.x",IJ_Y="ij.y"; + private static int port = DEFAULT_PORT; + private static String[] arguments; + + private Toolbar toolbar; + private Panel statusBar; + private ProgressBar progressBar; + private Label statusLine; + private boolean firstTime = true; + private java.applet.Applet applet; // null if not running as an applet + private Vector classes = new Vector(); + private boolean exitWhenQuitting; + private boolean quitting; + private long keyPressedTime, actionPerformedTime; + private String lastKeyCommand; + private boolean embedded; + private boolean windowClosed; + + boolean hotkey; + + /** Creates a new ImageJ frame that runs as an application. */ + public ImageJ() { + this(null, STANDALONE); + } + + /** Creates a new ImageJ frame that runs as an applet. */ + public ImageJ(java.applet.Applet applet) { + this(applet, 0); + } + + /** If 'applet' is not null, creates a new ImageJ frame that runs as an applet. + If 'mode' is ImageJ.EMBEDDED and 'applet is null, creates an embedded + version of ImageJ which does not start the SocketListener. */ + public ImageJ(java.applet.Applet applet, int mode) { + super("ImageJ"); + embedded = applet==null && mode==EMBEDDED; + this.applet = applet; + String err1 = Prefs.load(this, applet); + if (IJ.isLinux()) { + backgroundColor = new Color(240,240,240); + setBackground(backgroundColor); + } + Menus m = new Menus(this, applet); + String err2 = m.addMenuBar(); + m.installPopupMenu(this); + setLayout(new GridLayout(2, 1)); + + // Tool bar + toolbar = new Toolbar(); + toolbar.addKeyListener(this); + add(toolbar); + + // Status bar + statusBar = new Panel(); + statusBar.setLayout(new BorderLayout()); + statusBar.setForeground(Color.black); + statusBar.setBackground(backgroundColor); + statusLine = new Label(); + statusLine.setFont(SansSerif12); + statusLine.addKeyListener(this); + statusLine.addMouseListener(this); + statusBar.add("Center", statusLine); + progressBar = new ProgressBar(120, 20); + progressBar.addKeyListener(this); + progressBar.addMouseListener(this); + statusBar.add("East", progressBar); + statusBar.setSize(toolbar.getPreferredSize()); + add(statusBar); + + IJ.init(this, applet); + addKeyListener(this); + addWindowListener(this); + setFocusTraversalKeysEnabled(false); + + Point loc = getPreferredLocation(); + Dimension tbSize = toolbar.getPreferredSize(); + int ijWidth = tbSize.width+10; + int ijHeight = 100; + setCursor(Cursor.getDefaultCursor()); // work-around for JDK 1.1.8 bug + if (IJ.isWindows()) try {setIcon();} catch(Exception e) {} + setBounds(loc.x, loc.y, ijWidth, ijHeight); // needed for pack to work + setLocation(loc.x, loc.y); + pack(); + setResizable(!(IJ.isMacintosh() || IJ.isWindows())); // make resizable on Linux + //if (IJ.isJava15()) { + // try { + // Method method = Frame.class.getMethod("setAlwaysOnTop", new Class[] {boolean.class}); + // method.invoke(this, new Object[]{Boolean.TRUE}); + // } catch(Exception e) {} + //} + show(); + if (err1!=null) + IJ.error(err1); + if (err2!=null) { + IJ.error(err2); + IJ.runPlugIn("ij.plugin.ClassChecker", ""); + } + if (IJ.isMacintosh()&&applet==null) { + Object qh = null; + qh = IJ.runPlugIn("MacAdapter", ""); + if (qh==null) + IJ.runPlugIn("QuitHandler", ""); + } + if (applet==null) + IJ.runPlugIn("ij.plugin.DragAndDrop", ""); + m.installStartupMacroSet(); + String str = m.getMacroCount()==1?" macro)":" macros)"; + String java = "Java "+System.getProperty("java.version")+(IJ.is64Bit()?" [64-bit]":" [32-bit]"); + IJ.showStatus("ImageJ "+VERSION + build + "/"+java+" ("+ m.getPluginCount() + " commands, " + m.getMacroCount() + str); + if (applet==null && !embedded && Prefs.runSocketListener) + new SocketListener(); + configureProxy(); + } + + void configureProxy() { + String server = Prefs.get("proxy.server", null); + if (server==null||server.equals("")) return; + int port = (int)Prefs.get("proxy.port", 0); + if (port==0) return; + String user = Prefs.get("proxy.user", null); + Properties props = System.getProperties(); + props.put("proxySet", "true"); + props.put("http.proxyHost", server); + props.put("http.proxyPort", ""+port); + if (user!=null) + props.put("http.proxyUser", user); + //IJ.log(server+" "+port+" "+user); + } + + void setIcon() throws Exception { + URL url = this.getClass().getResource("/microscope.gif"); + if (url==null) return; + Image img = createImage((ImageProducer)url.getContent()); + if (img!=null) setIconImage(img); + } + + public Point getPreferredLocation() { + if (!IJ.isJava14()) return new Point(0, 0); + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + Rectangle maxBounds = ge.getMaximumWindowBounds(); + int ijX = Prefs.getInt(IJ_X,-99); + int ijY = Prefs.getInt(IJ_Y,-99); + if (ijX>=0 && ijY>0 && ijX<(maxBounds.x+maxBounds.width-75)) + return new Point(ijX, ijY); + Dimension tbsize = toolbar.getPreferredSize(); + int ijWidth = tbsize.width+10; + double percent = maxBounds.width>832?0.8:0.9; + ijX = (int)(percent*(maxBounds.width-ijWidth)); + if (ijX<10) ijX = 10; + return new Point(ijX, maxBounds.y); + } + + void showStatus(String s) { + statusLine.setText(s); + } + + public ProgressBar getProgressBar() { + return progressBar; + } + + public Panel getStatusBar() { + return statusBar; + } + + /** Starts executing a menu command in a separate thread. */ + void doCommand(String name) { + new Executer(name, null); + } + + public void runFilterPlugIn(Object theFilter, String cmd, String arg) { + new PlugInFilterRunner(theFilter, cmd, arg); + } + + public Object runUserPlugIn(String commandName, String className, String arg, boolean createNewLoader) { + return IJ.runUserPlugIn(commandName, className, arg, createNewLoader); + } + + /** Return the current list of modifier keys. */ + public static String modifiers(int flags) { //?? needs to be moved + String s = " [ "; + if (flags == 0) return ""; + if ((flags & Event.SHIFT_MASK) != 0) s += "Shift "; + if ((flags & Event.CTRL_MASK) != 0) s += "Control "; + if ((flags & Event.META_MASK) != 0) s += "Meta "; + if ((flags & Event.ALT_MASK) != 0) s += "Alt "; + s += "] "; + return s; + } + + /** Handle menu events. */ + public void actionPerformed(ActionEvent e) { + if ((e.getSource() instanceof MenuItem)) { + MenuItem item = (MenuItem)e.getSource(); + String cmd = e.getActionCommand(); + if (item.getParent()==Menus.openRecentMenu) { + new RecentOpener(cmd); // open image in separate thread + return; + } + int flags = e.getModifiers(); + //IJ.log(""+KeyEvent.getKeyModifiersText(flags)); + hotkey = false; + actionPerformedTime = System.currentTimeMillis(); + long ellapsedTime = actionPerformedTime-keyPressedTime; + if (cmd!=null && (ellapsedTime>=200L||!cmd.equals(lastKeyCommand))) { + if ((flags & Event.ALT_MASK)!=0) + IJ.setKeyDown(KeyEvent.VK_ALT); + if ((flags & Event.SHIFT_MASK)!=0) + IJ.setKeyDown(KeyEvent.VK_SHIFT); + doCommand(cmd); + } + lastKeyCommand = null; + if (IJ.debugMode) IJ.log("actionPerformed: time="+ellapsedTime+", "+e); + } + } + + /** Handles CheckboxMenuItem state changes. */ + public void itemStateChanged(ItemEvent e) { + MenuItem item = (MenuItem)e.getSource(); + MenuComponent parent = (MenuComponent)item.getParent(); + String cmd = e.getItem().toString(); + if ((Menu)parent==Menus.window) + WindowManager.activateWindow(cmd, item); + else + doCommand(cmd); + } + + public void mousePressed(MouseEvent e) { + Undo.reset(); + System.gc(); + String info = "ImageJ "+ImageJ.VERSION+"; Java "+System.getProperty("java.version")+(IJ.is64Bit()?" (64-bit); ":" (32-bit); "); + IJ.showStatus(info+IJ.freeMemory()); + if (IJ.debugMode) + IJ.log("Windows: "+WindowManager.getWindowCount()); + } + + public void mouseReleased(MouseEvent e) {} + public void mouseExited(MouseEvent e) {} + public void mouseClicked(MouseEvent e) {} + public void mouseEntered(MouseEvent e) {} + + public void keyPressed(KeyEvent e) { + //if (e.isConsumed()) return; + int keyCode = e.getKeyCode(); + IJ.setKeyDown(keyCode); + hotkey = false; + if (keyCode==e.VK_CONTROL || keyCode==e.VK_SHIFT) + return; + char keyChar = e.getKeyChar(); + int flags = e.getModifiers(); + if (IJ.debugMode) IJ.log("keyPressed: code=" + keyCode + " (" + KeyEvent.getKeyText(keyCode) + + "), char=\"" + keyChar + "\" (" + (int)keyChar + "), flags=" + + KeyEvent.getKeyModifiersText(flags)); + boolean shift = (flags & e.SHIFT_MASK) != 0; + boolean control = (flags & e.CTRL_MASK) != 0; + boolean alt = (flags & e.ALT_MASK) != 0; + boolean meta = (flags & e.META_MASK) != 0; + String cmd = ""; + ImagePlus imp = WindowManager.getCurrentImage(); + boolean isStack = (imp!=null) && (imp.getStackSize()>1); + + if (imp!=null && !control && ((keyChar>=32 && keyChar<=255) || keyChar=='\b' || keyChar=='\n')) { + Roi roi = imp.getRoi(); + if (roi instanceof TextRoi) { + if ((flags & e.META_MASK)!=0 && IJ.isMacOSX()) return; + if (alt) + switch (keyChar) { + case 'u': case 'm': keyChar = IJ.micronSymbol; break; + case 'A': keyChar = IJ.angstromSymbol; break; + default: + } + ((TextRoi)roi).addChar(keyChar); + return; + } + } + + // Handle one character macro shortcuts + if (!control && !meta) { + Hashtable macroShortcuts = Menus.getMacroShortcuts(); + if (macroShortcuts.size()>0) { + if (shift) + cmd = (String)macroShortcuts.get(new Integer(keyCode+200)); + else + cmd = (String)macroShortcuts.get(new Integer(keyCode)); + if (cmd!=null) { + //MacroInstaller.runMacroCommand(cmd); + MacroInstaller.runMacroShortcut(cmd); + return; + } + } + } + + if (!Prefs.requireControlKey || control || meta) { + Hashtable shortcuts = Menus.getShortcuts(); + if (shift) + cmd = (String)shortcuts.get(new Integer(keyCode+200)); + else + cmd = (String)shortcuts.get(new Integer(keyCode)); + } + + if (cmd==null) { + switch (keyChar) { + case '<': case ',': cmd="Previous Slice [<]"; break; + case '>': case '.': case ';': cmd="Next Slice [>]"; break; + case '+': case '=': cmd="In"; break; + case '-': cmd="Out"; break; + case '/': cmd="Reslice [/]..."; break; + default: + } + } + + if (cmd==null) { + switch(keyCode) { + case KeyEvent.VK_TAB: WindowManager.putBehind(); return; + case KeyEvent.VK_BACK_SPACE: cmd="Clear"; hotkey=true; break; // delete + //case KeyEvent.VK_BACK_SLASH: cmd=IJ.altKeyDown()?"Animation Options...":"Start Animation"; break; + case KeyEvent.VK_EQUALS: cmd="In"; break; + case KeyEvent.VK_MINUS: cmd="Out"; break; + case KeyEvent.VK_SLASH: case 0xbf: cmd="Reslice [/]..."; break; + case KeyEvent.VK_COMMA: case 0xbc: cmd="Previous Slice [<]"; break; + case KeyEvent.VK_PERIOD: case 0xbe: cmd="Next Slice [>]"; break; + case KeyEvent.VK_LEFT: case KeyEvent.VK_RIGHT: case KeyEvent.VK_UP: case KeyEvent.VK_DOWN: // arrow keys + if (imp==null) return; + Roi roi = imp.getRoi(); + if (IJ.shiftKeyDown()&&imp==Orthogonal_Views.getImage()) + return; + boolean stackKey = imp.getStackSize()>1 && (roi==null||IJ.shiftKeyDown()); + boolean zoomKey = roi==null || IJ.shiftKeyDown() || IJ.controlKeyDown(); + if (stackKey && keyCode==KeyEvent.VK_RIGHT) + cmd="Next Slice [>]"; + else if (stackKey && keyCode==KeyEvent.VK_LEFT) + cmd="Previous Slice [<]"; + else if (zoomKey &&keyCode==KeyEvent.VK_DOWN) + cmd="Out"; + else if (zoomKey && keyCode==KeyEvent.VK_UP) + cmd="In"; + else if (roi!=null) { + if ((flags & KeyEvent.ALT_MASK) != 0) + roi.nudgeCorner(keyCode); + else + roi.nudge(keyCode); + return; + } + break; + case KeyEvent.VK_ESCAPE: + abortPluginOrMacro(imp); + return; + case KeyEvent.VK_ENTER: this.toFront(); return; + default: break; + } + } + + if (cmd!=null && !cmd.equals("")) { + if (cmd.equals("Fill")||cmd.equals("Draw")) + hotkey = true; + if (cmd.charAt(0)==MacroInstaller.commandPrefix) + MacroInstaller.runMacroShortcut(cmd); + else { + doCommand(cmd); + keyPressedTime = System.currentTimeMillis(); + lastKeyCommand = cmd; + } + } + } + + public void keyTyped(KeyEvent e) { + char keyChar = e.getKeyChar(); + int flags = e.getModifiers(); + if (IJ.debugMode) IJ.log("keyTyped: char=\"" + keyChar + "\" (" + (int)keyChar + + "), flags= "+Integer.toHexString(flags)+ " ("+KeyEvent.getKeyModifiersText(flags)+")"); + if (keyChar=='\\' || keyChar==171 || keyChar==223) { + if (((flags&Event.ALT_MASK)!=0)) + doCommand("Animation Options..."); + else + doCommand("Start Animation [\\]"); + } + } + + public void keyReleased(KeyEvent e) { + IJ.setKeyUp(e.getKeyCode()); + } + + void abortPluginOrMacro(ImagePlus imp) { + if (imp!=null) { + ImageWindow win = imp.getWindow(); + if (win!=null) { + win.running = false; + win.running2 = false; + } + } + Macro.abort(); + Interpreter.abort(); + if (Interpreter.getInstance()!=null) IJ.beep(); + } + + public void windowClosing(WindowEvent e) { + doCommand("Quit"); + windowClosed = true; + } + + public void windowActivated(WindowEvent e) { + if (IJ.isMacintosh() && !quitting) { + IJ.wait(10); // may be needed for Java 1.4 on OS X + setMenuBar(Menus.getMenuBar()); + } + } + + public void windowClosed(WindowEvent e) {} + public void windowDeactivated(WindowEvent e) {} + public void windowDeiconified(WindowEvent e) {} + public void windowIconified(WindowEvent e) {} + public void windowOpened(WindowEvent e) {} + + /** Adds the specified class to a Vector to keep it from being + garbage collected, causing static fields to be reset. */ + public void register(Class c) { + if (!classes.contains(c)) + classes.addElement(c); + } + + /** Called by ImageJ when the user selects Quit. */ + public void quit() { + Thread thread = new Thread(this, "Quit"); + thread.setPriority(Thread.NORM_PRIORITY); + thread.start(); + } + + /** Returns true if ImageJ is exiting. */ + public boolean quitting() { + return quitting; + } + + /** Called once when ImageJ quits. */ + public void savePreferences(Properties prefs) { + Point loc = getLocation(); + prefs.put(IJ_X, Integer.toString(loc.x)); + prefs.put(IJ_Y, Integer.toString(loc.y)); + //prefs.put(IJ_WIDTH, Integer.toString(size.width)); + //prefs.put(IJ_HEIGHT, Integer.toString(size.height)); + } + + public static void main(String args[]) { + if (System.getProperty("java.version").substring(0,3).compareTo("1.4")<0) { + javax.swing.JOptionPane.showMessageDialog(null,"ImageJ "+VERSION+" requires Java 1.4.1 or later."); + System.exit(0); + } + boolean noGUI = false; + int mode = STANDALONE; + arguments = args; + int nArgs = args!=null?args.length:0; + for (int i=0; i0 && DEFAULT_PORT+delta<65536) + port = DEFAULT_PORT+delta; + } + } + } + // If ImageJ is already running then isRunning() + // will pass the arguments to it using sockets. + if (nArgs>0 && !noGUI && (mode==STANDALONE) && isRunning(args)) + return; + ImageJ ij = IJ.getInstance(); + if (!noGUI && (ij==null || (ij!=null && !ij.isShowing()))) { + ij = new ImageJ(null, mode); + ij.exitWhenQuitting = true; + } + int macros = 0; + for (int i=0; iMenus.WINDOW_MENU_ITEMS && !(IJ.macroRunning()&&WindowManager.getImageCount()==0)) { + GenericDialog gd = new GenericDialog("ImageJ", this); + gd.addMessage("Are you sure you want to quit ImageJ?"); + gd.showDialog(); + quitting = !gd.wasCanceled(); + windowClosed = false; + } + if (!quitting) + return; + if (!WindowManager.closeAllWindows()) { + quitting = false; + return; + } + //IJ.log("savePreferences"); + if (applet==null) { + saveWindowLocations(); + Prefs.savePreferences(); + } + setVisible(false); + //IJ.log("dispose"); + dispose(); + if (exitWhenQuitting) + System.exit(0); + } + + void saveWindowLocations() { + Frame frame = WindowManager.getFrame("B&C"); + if (frame!=null) + Prefs.saveLocation(ContrastAdjuster.LOC_KEY, frame.getLocation()); + frame = WindowManager.getFrame("Threshold"); + if (frame!=null) + Prefs.saveLocation(ThresholdAdjuster.LOC_KEY, frame.getLocation()); + frame = WindowManager.getFrame("Results"); + if (frame!=null) { + Prefs.saveLocation(TextWindow.LOC_KEY, frame.getLocation()); + Dimension d = frame.getSize(); + Prefs.set(TextWindow.WIDTH_KEY, d.width); + Prefs.set(TextWindow.HEIGHT_KEY, d.height); + } + } + +} diff --git a/ij/ImageJApplet.java b/ij/ImageJApplet.java index b701ca921..eb4353a30 100644 --- a/ij/ImageJApplet.java +++ b/ij/ImageJApplet.java @@ -1,44 +1,44 @@ -package ij; -import java.applet.Applet; - -/** - Runs ImageJ as an applet and optionally opens up to - nine images using URLs passed as a parameters. -

- Here is an example applet tag that launches ImageJ as an applet - and passes it the URLs of two images: -

-	<applet archive="../ij.jar" code="ij.ImageJApplet.class" width=0 height=0>
-	<param name=url1 value="http://rsb.info.nih.gov/ij/images/FluorescentCells.jpg">
-	<param name=url2 value="http://rsb.info.nih.gov/ij/images/blobs.gif">
-	</applet>
-	
- To use plugins, add them to ij.jar and add entries to IJ_Props.txt file (in ij.jar) that will - create commands for them in the Plugins menu, or a submenu. There are examples - of such entries in IJ.Props.txt, in the "Plugins installed in the Plugins menu" section. -

- Macros contained in a file named "StartupMacros.txt", in the same directory as the HTML file - containing the applet tag, will be installed on startup. -*/ -public class ImageJApplet extends Applet { - - /** Starts ImageJ if it's not already running. */ - public void init() { - ImageJ ij = IJ.getInstance(); - if (ij==null || (ij!=null && !ij.isShowing())) - new ImageJ(this); - for (int i=1; i<=9; i++) { - String url = getParameter("url"+i); - if (url==null) break; - ImagePlus imp = new ImagePlus(url); - if (imp!=null) imp.show(); - } - } - - public void destroy() { - ImageJ ij = IJ.getInstance(); - if (ij!=null) ij.quit(); - } - -} - +package ij; +import java.applet.Applet; + +/** + Runs ImageJ as an applet and optionally opens up to + nine images using URLs passed as a parameters. +

+ Here is an example applet tag that launches ImageJ as an applet + and passes it the URLs of two images: +

+	<applet archive="../ij.jar" code="ij.ImageJApplet.class" width=0 height=0>
+	<param name=url1 value="http://rsb.info.nih.gov/ij/images/FluorescentCells.jpg">
+	<param name=url2 value="http://rsb.info.nih.gov/ij/images/blobs.gif">
+	</applet>
+	
+ To use plugins, add them to ij.jar and add entries to IJ_Props.txt file (in ij.jar) that will + create commands for them in the Plugins menu, or a submenu. There are examples + of such entries in IJ.Props.txt, in the "Plugins installed in the Plugins menu" section. +

+ Macros contained in a file named "StartupMacros.txt", in the same directory as the HTML file + containing the applet tag, will be installed on startup. +*/ +public class ImageJApplet extends Applet { + + /** Starts ImageJ if it's not already running. */ + public void init() { + ImageJ ij = IJ.getInstance(); + if (ij==null || (ij!=null && !ij.isShowing())) + new ImageJ(this); + for (int i=1; i<=9; i++) { + String url = getParameter("url"+i); + if (url==null) break; + ImagePlus imp = new ImagePlus(url); + if (imp!=null) imp.show(); + } + } + + public void destroy() { + ImageJ ij = IJ.getInstance(); + if (ij!=null) ij.quit(); + } + +} + diff --git a/ij/ImageListener.java b/ij/ImageListener.java index 4edda9fd3..08faf83ac 100644 --- a/ij/ImageListener.java +++ b/ij/ImageListener.java @@ -1,13 +1 @@ -package ij; - - /** Plugins that implement this interface are notified when - an image window is opened, closed or updated. */ - public interface ImageListener { - - public void imageOpened(ImagePlus imp); - - public void imageClosed(ImagePlus imp); - - public void imageUpdated(ImagePlus imp); - -} +package ij; /** Plugins that implement this interface are notified when an image window is opened, closed or updated. */ public interface ImageListener { public void imageOpened(ImagePlus imp); public void imageClosed(ImagePlus imp); public void imageUpdated(ImagePlus imp); } \ No newline at end of file diff --git a/ij/ImagePlus.java b/ij/ImagePlus.java index bda4b256a..10a0c748f 100644 --- a/ij/ImagePlus.java +++ b/ij/ImagePlus.java @@ -1,1943 +1,1943 @@ -package ij; -import java.awt.*; -import java.awt.image.*; -import java.net.URL; -import java.util.*; -import ij.process.*; -import ij.io.*; -import ij.gui.*; -import ij.measure.*; -import ij.plugin.filter.Analyzer; -import ij.util.Tools; -import ij.macro.Interpreter; -import ij.plugin.frame.ContrastAdjuster; -import ij.plugin.Converter; - -/** -This is an extended image class that supports 8-bit, 16-bit, -32-bit (real) and RGB images. It also provides support for -3D image stacks. -@see ij.process.ImageProcessor -@see ij.ImageStack -@see ij.gui.ImageWindow -@see ij.gui.ImageCanvas -*/ - -public class ImagePlus implements ImageObserver, Measurements { - - /** 8-bit grayscale (unsigned)*/ - public static final int GRAY8 = 0; - - /** 16-bit grayscale (unsigned) */ - public static final int GRAY16 = 1; - - /** 32-bit floating-point grayscale */ - public static final int GRAY32 = 2; - - /** 8-bit indexed color */ - public static final int COLOR_256 = 3; - - /** 32-bit RGB color */ - public static final int COLOR_RGB = 4; - - /** True if any changes have been made to this image. */ - public boolean changes; - - /** Obsolete. Use GetCalibration(). */ - public double pixelWidth=1.0, pixelHeight=1.0; - /** Obsolete. Use GetCalibration(). */ - public String unit="pixel"; - /** Obsolete. Use GetCalibration(). */ - public String units=unit; - /** Obsolete. Use GetCalibration(). */ - public boolean sCalibrated; - - protected Image img; - protected ImageProcessor ip; - protected ImageWindow win; - protected Roi roi; - protected int currentSlice; - protected static final int OPENED=0, CLOSED=1, UPDATED=2; - protected boolean compositeImage; - protected int width; - protected int height; - protected boolean locked = false; - - private ImageJ ij = IJ.getInstance(); - private String title; - private String url; - private FileInfo fileInfo; - private int nSlices = 1; - private int nChannels = 1; - private int nFrames = 1; - private int imageType = GRAY8; - private ImageStack stack; - private static int currentID = -1; - private int ID; - private static Component comp; - private boolean imageLoaded; - private int imageUpdateY, imageUpdateW; - private Properties properties; - private long startTime; - private Calibration calibration; - private static Calibration globalCalibration; - private boolean activated; - private boolean ignoreFlush; - private boolean errorLoadingImage; - private static ImagePlus clipboard; - private static Vector listeners = new Vector(); - private boolean openAsHyperStack; - private int[] position = {1,1,1}; - private boolean noUpdateMode; - - /** Constructs an uninitialized ImagePlus. */ - public ImagePlus() { - ID = --currentID; - title="null"; - } - - /** Constructs an ImagePlus from an Image or BufferedImage. The first - argument will be used as the title of the window that displays the image. - Throws an IllegalStateException if an error occurs while loading the image. */ - public ImagePlus(String title, Image img) { - this.title = title; - ID = --currentID; - if (img!=null) - setImage(img); - } - - /** Constructs an ImagePlus from an ImageProcessor. */ - public ImagePlus(String title, ImageProcessor ip) { - setProcessor(title, ip); - ID = --currentID; - } - - /** Constructs an ImagePlus from a TIFF, BMP, DICOM, FITS, - PGM, GIF or JPRG specified by a path or from a TIFF, DICOM, - GIF or JPEG specified by a URL. */ - public ImagePlus(String pathOrURL) { - Opener opener = new Opener(); - ImagePlus imp = null; - boolean isURL = pathOrURL.indexOf("://")>0; - if (isURL) - imp = opener.openURL(pathOrURL); - else - imp = opener.openImage(pathOrURL); - if (imp!=null) { - if (imp.getStackSize()>1) - setStack(imp.getTitle(), imp.getStack()); - else - setProcessor(imp.getTitle(), imp.getProcessor()); - setCalibration(imp.getCalibration()); - properties = imp.getProperties(); - setFileInfo(imp.getOriginalFileInfo()); - setDimensions(imp.getNChannels(), imp.getNSlices(), imp.getNFrames()); - if (isURL) - this.url = pathOrURL; - ID = --currentID; - } - } - - /** Constructs an ImagePlus from a stack. */ - public ImagePlus(String title, ImageStack stack) { - setStack(title, stack); - ID = --currentID; - } - - /** Locks the image so other threads can test to see if it - is in use. Returns true if the image was successfully locked. - Beeps, displays a message in the status bar, and returns - false if the image is already locked. */ - public synchronized boolean lock() { - if (locked) { - IJ.beep(); - IJ.showStatus("\"" + title + "\" is locked"); - //if (IJ.macroRunning()) { - // IJ.error("Image is locked"); - // Macro.abort(); - //} - return false; - } else { - locked = true; - if (IJ.debugMode) IJ.log(title + ": lock"); - return true; - } - } - - /** Similar to lock, but doesn't beep and display an error - message if the attempt to lock the image fails. */ - public synchronized boolean lockSilently() { - if (locked) - return false; - else { - locked = true; - if (IJ.debugMode) IJ.log(title + ": lock silently"); - return true; - } - } - - /** Unlocks the image. */ - public synchronized void unlock() { - locked = false; - if (IJ.debugMode) IJ.log(title + ": unlock"); - } - - private void waitForImage(Image img) { - if (comp==null) { - comp = IJ.getInstance(); - if (comp==null) - comp = new Canvas(); - } - imageLoaded = false; - if (!comp.prepareImage(img, this)) { - double progress; - waitStart = System.currentTimeMillis(); - while (!imageLoaded && !errorLoadingImage) { - //IJ.showStatus(imageUpdateY+" "+imageUpdateW); - IJ.wait(30); - if (imageUpdateW>1) { - progress = (double)imageUpdateY/imageUpdateW; - if (!(progress<1.0)) { - progress = 1.0 - (progress-1.0); - if (progress<0.0) progress = 0.9; - } - showProgress(progress); - } - } - showProgress(1.0); - } - } - - long waitStart; - private void showProgress(double percent) { - if ((System.currentTimeMillis()-waitStart)>500L) - IJ.showProgress(percent); - } - - /** Draws the image. If there is an ROI, its - outline is also displayed. Does nothing if there - is no window associated with this image (i.e. show() - has not been called).*/ - public void draw(){ - if (win!=null) - win.getCanvas().repaint(); - } - - /** Draws image and roi outline using a clip rect. */ - public void draw(int x, int y, int width, int height){ - if (win!=null) { - ImageCanvas ic = win.getCanvas(); - double mag = ic.getMagnification(); - x = ic.screenX(x); - y = ic.screenY(y); - width = (int)(width*mag); - height = (int)(height*mag); - ic.repaint(x, y, width, height); - if (listeners.size()>0 && roi!=null && roi.getPasteMode()!=Roi.NOT_PASTING) - notifyListeners(UPDATED); - } - } - - /** Updates this image from the pixel data in its - associated ImageProcessor, then displays it. Does - nothing if there is no window associated with - this image (i.e. show() has not been called).*/ - public void updateAndDraw() { - if (ip!=null) { - if (win!=null) { - win.getCanvas().setImageUpdated(); - if (listeners.size()>0) notifyListeners(UPDATED); - } - draw(); - } - } - - /** Updates this image from the pixel data in its - associated ImageProcessor, then displays it. - The CompositeImage class overrides this method - to only update the current channel. */ - public void updateChannelAndDraw() { - updateAndDraw(); - } - - /** Returns a reference to the current ImageProcessor. The - CompositeImage class overrides this method so it returns - the processor associated with the current channel. */ - public ImageProcessor getChannelProcessor() { - return getProcessor(); - } - - /* The CompositeImage class overrides this method to - return, as an array, copies of this image's channel LUTs. */ - public LUT[] getLuts() { - return null; - //ImageProcessor ip = getProcessor(); - //ColorModel cm = ip.getColorModel(); - //if (cm instanceof IndexColorModel) { - // LUT[] luts = new LUT[1]; - // luts[0] = new LUT((IndexColorModel)cm, ip.getMin(), ip.getMax()); - // return luts; - //} else - // return null; - } - - /** Calls draw to draw the image and also repaints the - image window to force the information displayed above - the image (dimension, type, size) to be updated. */ - public void repaintWindow() { - if (win!=null) { - draw(); - win.repaint(); - } - } - - /** Calls updateAndDraw to update from the pixel data - and draw the image, and also repaints the image - window to force the information displayed above - the image (dimension, type, size) to be updated. */ - public void updateAndRepaintWindow() { - if (win!=null) { - updateAndDraw(); - win.repaint(); - } - } - - /** ImageCanvas.paint() calls this method when the - ImageProcessor has generated new image. */ - public void updateImage() { - if (ip!=null) - img = ip.createImage(); - } - - /** Closes the window, if any, that is displaying this image. */ - public void hide() { - if (win==null) { - Interpreter.removeBatchModeImage(this); - return; - } - boolean unlocked = lockSilently(); - changes = false; - win.close(); - win = null; - if (unlocked) unlock(); - } - - /** Closes this image and sets the ImageProcessor to null. To avoid the - "Save changes?" dialog, first set the public 'changes' variable to false. */ - public void close() { - ImageWindow win = getWindow(); - if (win!=null) { - //if (IJ.isWindows() && IJ.isJava14()) - // changes = false; // avoid 'save changes?' dialog and potential Java 1.5 deadlocks - win.close(); - } else { - if (WindowManager.getCurrentImage()==this) - WindowManager.setTempCurrentImage(null); - killRoi(); //save any ROI so it can be restored later - Interpreter.removeBatchModeImage(this); - } - } - - /** Opens a window to display this image and clears the status bar. */ - public void show() { - show(""); - } - - /** Opens a window to display this image and displays - 'statusMessage' in the status bar. */ - public void show(String statusMessage) { - if (win!=null) return; - if ((IJ.isMacro() && ij==null) || Interpreter.isBatchMode()) { - ImagePlus img = WindowManager.getCurrentImage(); - if (img!=null) img.saveRoi(); - WindowManager.setTempCurrentImage(this); - Interpreter.addBatchModeImage(this); - return; - } - if (Prefs.useInvertingLut && getBitDepth()==8 && ip!=null && !ip.isInvertedLut()&& !ip.isColorLut()) - invertLookupTable(); - img = getImage(); - if ((img!=null) && (width>=0) && (height>=0)) { - activated = false; - int stackSize = getStackSize(); - //if (compositeImage) stackSize /= nChannels; - if (stackSize>1) - win = new StackWindow(this); - else - win = new ImageWindow(this); - if (roi!=null) roi.setImage(this); - draw(); - IJ.showStatus(statusMessage); - if (IJ.isMacro()) { // wait for window to be activated - long start = System.currentTimeMillis(); - while (!activated) { - IJ.wait(5); - if ((System.currentTimeMillis()-start)>2000) { - WindowManager.setTempCurrentImage(this); - break; // 2 second timeout - } - } - } - notifyListeners(OPENED); - } - } - - void invertLookupTable() { - int nImages = getStackSize(); - ip.invertLut(); - if (nImages==1) - ip.invert(); - else { - ImageStack stack2 = getStack(); - for (int i=1; i<=nImages; i++) - stack2.getProcessor(i).invert(); - stack2.setColorModel(ip.getColorModel()); - } - } - - /** Called by ImageWindow.windowActivated(). */ - public void setActivated() { - activated = true; - } - - /** Returns this image as a AWT image. */ - public Image getImage() { - if (img==null && ip!=null) - img = ip.createImage(); - return img; - } - - /** Returns this image as a BufferedImage. */ - public BufferedImage getBufferedImage() { - if (isComposite()) - return (new ColorProcessor(getImage())).getBufferedImage(); - else - return ip.getBufferedImage(); - } - - /** Returns this image's unique numeric ID. */ - public int getID() { - return ID; - } - - /** Replaces the image, if any, with the one specified. - Throws an IllegalStateException if an error occurs - while loading the image. */ - public void setImage(Image img) { - if (img instanceof BufferedImage) { - BufferedImage bi = (BufferedImage)img; - if (bi.getType()==BufferedImage.TYPE_USHORT_GRAY) { - setProcessor(null, new ShortProcessor(bi)); - return; - } else if (bi.getType()==BufferedImage.TYPE_BYTE_GRAY) { - setProcessor(null, new ByteProcessor(bi)); - return; - } - } - roi = null; - errorLoadingImage = false; - waitForImage(img); - if (errorLoadingImage) - throw new IllegalStateException ("Error loading image"); - this.img = img; - int newWidth = img.getWidth(ij); - int newHeight = img.getHeight(ij); - boolean dimensionsChanged = newWidth!=width || newHeight!=height; - width = newWidth; - height = newHeight; - ip = null; - stack = null; - LookUpTable lut = new LookUpTable(img); - int type; - if (lut.getMapSize() > 0) { - if (lut.isGrayscale()) - type = GRAY8; - else - type = COLOR_256; - } else - type = COLOR_RGB; - setType(type); - setupProcessor(); - this.img = ip.createImage(); - if (win!=null) { - if (dimensionsChanged) - win = new ImageWindow(this); - else - repaintWindow(); - } - } - - /** Replaces the ImageProcessor, if any, with the one specified. - Set 'title' to null to leave the image title unchanged. */ - public void setProcessor(String title, ImageProcessor ip) { - if (ip==null || ip.getPixels()==null) - throw new IllegalArgumentException("ip null or ip.getPixels() null"); - int stackSize = getStackSize(); - if (stackSize>1 && (ip.getWidth()!=width || ip.getHeight()!=height)) - throw new IllegalArgumentException("ip wrong size"); - if (stackSize<=1) { - stack = null; - setCurrentSlice(1); - } - setProcessor2(title, ip, null); - } - - void setProcessor2(String title, ImageProcessor ip, ImageStack newStack) { - if (title!=null) setTitle(title); - this.ip = ip; - if (ij!=null) ip.setProgressBar(ij.getProgressBar()); - int stackSize = 1; - if (stack!=null) { - stackSize = stack.getSize(); - if (currentSlice>stackSize) setCurrentSlice(stackSize); - } - img = null; - boolean dimensionsChanged = width>0 && height>0 && (width!=ip.getWidth() || height!=ip.getHeight()); - if (dimensionsChanged) roi = null; - int type; - if (ip instanceof ByteProcessor) - type = GRAY8; - else if (ip instanceof ColorProcessor) - type = COLOR_RGB; - else if (ip instanceof ShortProcessor) - type = GRAY16; - else - type = GRAY32; - if (width==0) - imageType = type; - else - setType(type); - width = ip.getWidth(); - height = ip.getHeight(); - if (win!=null) { - if (dimensionsChanged && stackSize==1) - win.updateImage(this); - else if (newStack==null) - repaintWindow(); - draw(); - } - } - - /** Replaces the stack, if any, with the one specified. - Set 'title' to null to leave the title unchanged. */ - public void setStack(String title, ImageStack stack) { - int stackSize = stack.getSize(); - if (stackSize==0) - throw new IllegalArgumentException("Stack is empty"); - if (!stack.isVirtual()) { - Object[] arrays = stack.getImageArray(); - if (arrays==null || (arrays.length>0&&arrays[0]==null)) - throw new IllegalArgumentException("Stack pixel array null"); - } - boolean stackSizeChanged = this.stack!=null && stackSize!=getStackSize(); - if (currentSlice<1) setCurrentSlice(1); - boolean resetCurrentSlice = currentSlice>stackSize; - if (resetCurrentSlice) setCurrentSlice(stackSize); - ImageProcessor ip = stack.getProcessor(currentSlice); - boolean dimensionsChanged = width>0 && height>0 && (width!=ip.getWidth()||height!=ip.getHeight()); - this.stack = stack; - setProcessor2(title, ip, stack); - if (win==null) return; - boolean invalidDimensions = isDisplayedHyperStack() && !((StackWindow)win).validDimensions(); - if (stackSize==1 && win instanceof StackWindow) - win = new ImageWindow(this, getCanvas()); // replaces this window - else if (dimensionsChanged && !stackSizeChanged) - win.updateImage(this); - else if (stackSize>1 && !(win instanceof StackWindow)) { - if (isDisplayedHyperStack()) setOpenAsHyperStack(true); - win = new StackWindow(this, getCanvas()); // replaces this window - setPosition(1, 1, 1); - } else if (stackSize>1 && (dimensionsChanged||invalidDimensions)) { - if (isDisplayedHyperStack()) setOpenAsHyperStack(true); - win = new StackWindow(this); // replaces this window - setPosition(1, 1, 1); - } else - repaintWindow(); - if (resetCurrentSlice) setSlice(currentSlice); - } - - public void setStack(ImageStack stack, int nChannels, int nSlices, int nFrames) { - if (nChannels*nSlices*nFrames!=stack.getSize()) - throw new IllegalArgumentException("channels*slices*frames!=stackSize"); - this.nChannels = nChannels; - this.nSlices = nSlices; - this.nFrames = nFrames; - setStack(null, stack); - } - - /** Saves this image's FileInfo so it can be later - retieved using getOriginalFileInfo(). */ - public void setFileInfo(FileInfo fi) { - if (fi!=null) - fi.pixels = null; - fileInfo = fi; - } - - /** Returns the ImageWindow that is being used to display - this image. Returns null if show() has not be called - or the ImageWindow has been closed. */ - public ImageWindow getWindow() { - return win; - } - - /** Returns true if this image is currently being displayed in a window. */ - public boolean isVisible() { - return win!=null && win.isVisible(); - } - - /** This method should only be called from an ImageWindow. */ - public void setWindow(ImageWindow win) { - this.win = win; - if (roi!=null) - roi.setImage(this); // update roi's 'ic' field - } - - /** Returns the ImageCanvas being used to - display this image, or null. */ - public ImageCanvas getCanvas() { - return win!=null?win.getCanvas():null; - } - - /** Sets current foreground color. */ - public void setColor(Color c) { - if (ip!=null) - ip.setColor(c); - } - - void setupProcessor() { - if (imageType==COLOR_RGB) { - if (ip == null || ip instanceof ByteProcessor) - ip = new ColorProcessor(getImage()); - } else if (ip==null || (ip instanceof ColorProcessor)) - ip = new ByteProcessor(getImage()); - if (roi!=null && roi.isArea()) - ip.setRoi(roi.getBounds()); - else - ip.resetRoi(); - } - - public boolean isProcessor() { - return ip!=null; - } - - /** Returns a reference to the current ImageProcessor. If there - is no ImageProcessor, it creates one. Returns null if this - ImagePlus contains no ImageProcessor and no AWT Image. */ - public ImageProcessor getProcessor() { - if (ip==null && img==null) - return null; - setupProcessor(); - if (!compositeImage) - ip.setLineWidth(Line.getWidth()); - if (ij!=null) - ip.setProgressBar(ij.getProgressBar()); - Calibration cal = getCalibration(); - if (cal.calibrated()) - ip.setCalibrationTable(cal.getCTable()); - else - ip.setCalibrationTable(null); - return ip; - } - - /** Frees RAM by setting the snapshot (undo) buffer in - the current ImageProcessor to null. */ - public void trimProcessor() { - ImageProcessor ip2 = ip; - if (!locked && ip2!=null) { - if (IJ.debugMode) IJ.log(title + ": trimProcessor"); - ip2.setSnapshotPixels(null); - } - } - - /** Obsolete. */ - public void killProcessor() { - } - - /** For images with irregular ROIs, returns a byte mask, otherwise, returns - null. Mask pixels have a non-zero value. */ - public ImageProcessor getMask() { - if (roi==null) { - if (ip!=null) ip.resetRoi(); - return null; - } - ImageProcessor mask = roi.getMask(); - if (mask==null) - return null; - if (ip!=null) { - ip.setMask(mask); - ip.setRoi(roi.getBounds()); - } - return mask; - } - - /** Returns an ImageStatistics object generated using the standard - measurement options (area, mean, mode, min and max). - This plugin demonstrates how get the area, mean and max of the - current image or selection: -

-   public class Get_Statistics implements PlugIn {
-      public void run(String arg) {
-         ImagePlus imp = IJ.getImage();
-         ImageStatistics stats = imp.getStatistics();
-         IJ.log("Area: "+stats.area);
-         IJ.log("Mean: "+stats.mean);
-         IJ.log("Max: "+stats.max);
-      }
-   }
-		
- @see ij.process.ImageStatistics - @see ij.process.ImageStatistics#getStatistics - */ - public ImageStatistics getStatistics() { - return getStatistics(AREA+MEAN+MODE+MIN_MAX); - } - - /** Returns an ImageStatistics object generated using the - specified measurement options. This plugin demonstrates how - get the area and centroid of the current selection: -
-   public class Get_Statistics implements PlugIn, Measurements {
-      public void run(String arg) {
-         ImagePlus imp = IJ.getImage();
-         ImageStatistics stats = imp.getStatistics(MEDIAN+CENTROID);
-         IJ.log("Median: "+stats.median);
-         IJ.log("xCentroid: "+stats.xCentroid);
-         IJ.log("yCentroid: "+stats.yCentroid);
-      }
-   }
-		
- @see ij.process.ImageStatistics - @see ij.measure.Measurements - */ - public ImageStatistics getStatistics(int mOptions) { - return getStatistics(mOptions, 256, 0.0, 0.0); - } - - /** Returns an ImageStatistics object generated using the - specified measurement options and histogram bin count. - Note: except for float images, the number of bins - is currently fixed at 256. - */ - public ImageStatistics getStatistics(int mOptions, int nBins) { - return getStatistics(mOptions, nBins, 0.0, 0.0); - } - - /** Returns an ImageStatistics object generated using the - specified measurement options, histogram bin count and histogram range. - Note: for 8-bit and RGB images, the number of bins - is fixed at 256 and the histogram range is always 0-255. - */ - public ImageStatistics getStatistics(int mOptions, int nBins, double histMin, double histMax) { - setupProcessor(); - if (roi!=null && roi.isArea()) - ip.setRoi(roi); - else - ip.resetRoi(); - ip.setHistogramSize(nBins); - Calibration cal = getCalibration(); - if (getType()==GRAY16&& !(histMin==0.0&&histMax==0.0)) - {histMin=cal.getRawValue(histMin); histMax=cal.getRawValue(histMax);} - ip.setHistogramRange(histMin, histMax); - ImageStatistics stats = ImageStatistics.getStatistics(ip, mOptions, cal); - ip.setHistogramSize(256); - ip.setHistogramRange(0.0, 0.0); - return stats; - } - - /** Returns the image name. */ - public String getTitle() { - if (title==null) - return ""; - else - return title; - } - - /** Returns a shortened version of image name that does not - include spaces or a file name extension. */ - public String getShortTitle() { - String title = getTitle(); - int index = title.indexOf(' '); - if (index>-1) - title = title.substring(0, index); - index = title.lastIndexOf('.'); - if (index>0) - title = title.substring(0, index); - return title; - } - - /** Sets the image name. */ - public void setTitle(String title) { - if (title==null) - return; - if (win!=null) { - if (ij!=null) - Menus.updateWindowMenuItem(this.title, title); - String virtual = stack!=null && stack.isVirtual()?" (V)":""; - String global = getGlobalCalibration()!=null?" (G)":""; - - String scale = ""; - double magnification = win.getCanvas().getMagnification(); - if (magnification!=1.0) { - double percent = magnification*100.0; - int digits = percent>100.0||percent==(int)percent?0:1; - scale = " (" + IJ.d2s(percent,digits) + "%)"; - } - win.setTitle(title+virtual+global+scale); - } - this.title = title; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - /** If this is a stack, returns the number of slices, else returns 1. */ - public int getStackSize() { - if (stack==null) - return 1; - else { - int slices = stack.getSize(); - //if (compositeImage) slices /= nChannels; - if (slices<=0) slices = 1; - return slices; - } - } - - /** If this is a stack, returns the actual number of images in the stack, else returns 1. */ - public int getImageStackSize() { - if (stack==null) - return 1; - else { - int slices = stack.getSize(); - if (slices==0) slices = 1; - return slices; - } - } - - /** Sets the 3rd, 4th and 5th dimensions, where - nChannels*nSlices*nFrames - must be equal to the stack size. */ - public void setDimensions(int nChannels, int nSlices, int nFrames) { - //IJ.log("setDimensions: "+nChannels+" "+nSlices+" "+nFrames+" "+getImageStackSize()); - if (nChannels*nSlices*nFrames!=getImageStackSize() && ip!=null) { - //throw new IllegalArgumentException("channels*slices*frames!=stackSize"); - nChannels = 1; - nSlices = getImageStackSize(); - nFrames = 1; - if (isDisplayedHyperStack()) { - setOpenAsHyperStack(false); - new StackWindow(this); - setSlice(1); - } - } - boolean updateWin = isDisplayedHyperStack() && (this.nChannels!=nChannels||this.nSlices!=nSlices||this.nFrames!=nFrames); - this.nChannels = nChannels; - this.nSlices = nSlices; - this.nFrames = nFrames; - if (updateWin) { - if (nSlices!=getImageStackSize()) - setOpenAsHyperStack(true); - ip=null; img=null; - setPositionWithoutUpdate(getChannel(), getSlice(), getFrame()); - if (isComposite()) ((CompositeImage)this).reset(); - new StackWindow(this); - setPosition(getChannel(), getSlice(), getFrame()); - } - //IJ.log("setDimensions: "+ nChannels+" "+nSlices+" "+nFrames); - } - - /** Returns 'true' if this image is a hyperstack. */ - public boolean isHyperStack() { - return isDisplayedHyperStack() || (openAsHyperStack&&getNDimensions()>3); - } - - /** Returns the number of dimensions (2, 3, 4 or 5). */ - public int getNDimensions() { - int dimensions = 2; - int[] dim = getDimensions(); - if (dim[2]>1) dimensions++; - if (dim[3]>1) dimensions++; - if (dim[4]>1) dimensions++; - return dimensions; - } - - /** Returns 'true' if this is a hyperstack currently being displayed in a StackWindow. */ - public boolean isDisplayedHyperStack() { - return win!=null && win instanceof StackWindow && ((StackWindow)win).isHyperStack(); - } - - /** Returns the number of channels. */ - public int getNChannels() { - verifyDimensions(); - return nChannels; - } - - /** Returns the image depth (number of z-slices). */ - public int getNSlices() { - //IJ.log("getNSlices: "+ nChannels+" "+nSlices+" "+nFrames); - verifyDimensions(); - return nSlices; - } - - /** Returns the number of frames (time-points). */ - public int getNFrames() { - verifyDimensions(); - return nFrames; - } - - /** Returns the dimensions of this image (width, height, nChannels, - nSlices, nFrames) as a 5 element int array. */ - public int[] getDimensions() { - verifyDimensions(); - int[] d = new int[5]; - d[0] = width; - d[1] = height; - d[2] = nChannels; - d[3] = nSlices; - d[4] = nFrames; - return d; - } - - - void verifyDimensions() { - int stackSize = getImageStackSize(); - if (nSlices==1) { - if (nChannels>1 && nFrames==1) - nChannels = stackSize; - else if (nFrames>1 && nChannels==1) - nFrames = stackSize; - } - if (nChannels*nSlices*nFrames!=stackSize) { - nSlices = stackSize; - nChannels = 1; - nFrames = 1; - } - } - - /** Returns the current image type (ImagePlus.GRAY8, ImagePlus.GRAY16, - ImagePlus.GRAY32, ImagePlus.COLOR_256 or ImagePlus.COLOR_RGB). - @see #getBitDepth - */ - public int getType() { - return imageType; - } - - /** Returns the bit depth, 8, 16, 24 (RGB) or 32. RGB images actually use 32 bits per pixel. */ - public int getBitDepth() { - int bitDepth = 0; - switch (imageType) { - case GRAY8: case COLOR_256: bitDepth=8; break; - case GRAY16: bitDepth=16; break; - case GRAY32: bitDepth=32; break; - case COLOR_RGB: bitDepth=24; break; - } - return bitDepth; - } - - /** Returns the number of bytes per pixel. */ - public int getBytesPerPixel() { - switch (imageType) { - case GRAY16: return 2; - case GRAY32: case COLOR_RGB: return 4; - default: return 1; - } - } - - protected void setType(int type) { - if ((type<0) || (type>COLOR_RGB)) - return; - int previousType = imageType; - imageType = type; - if (imageType!=previousType) { - if (win!=null) - Menus.updateMenus(); - getLocalCalibration().setImage(this); - } - } - - /** Adds a key-value pair to this image's properties. The key - is removed from the properties table if value is null. */ - public void setProperty(String key, Object value) { - if (properties==null) - properties = new Properties(); - if (value==null) - properties.remove(key); - else - properties.put(key, value); - } - - /** Returns the property associated with 'key'. May return null. */ - public Object getProperty(String key) { - if (properties==null) - return null; - else - return properties.get(key); - } - - /** Returns this image's Properties. May return null. */ - public Properties getProperties() { - return properties; - } - - /** Creates a LookUpTable object that corresponds to this image. */ - public LookUpTable createLut() { - ImageProcessor ip2 = getProcessor(); - if (ip2!=null) - return new LookUpTable(ip2.getColorModel()); - else - return new LookUpTable(LookUpTable.createGrayscaleColorModel(false)); - } - - /** Returns true is this image uses an inverting LUT that - displays zero as white and 255 as black. */ - public boolean isInvertedLut() { - if (ip==null) { - if (img==null) - return false; - setupProcessor(); - } - return ip.isInvertedLut(); - } - - private int[] pvalue = new int[4]; - - /** - Returns the pixel value at (x,y) as a 4 element array. Grayscale values - are retuned in the first element. RGB values are returned in the first - 3 elements. For indexed color images, the RGB values are returned in the - first 3 three elements and the index (0-255) is returned in the last. - */ - public int[] getPixel(int x, int y) { - pvalue[0]=pvalue[1]=pvalue[2]=pvalue[3]=0; - if (img == null) - return pvalue; - switch (imageType) { - case GRAY8: case COLOR_256: - int index; - if (ip!=null) - index = ip.getPixel(x, y); - else { - byte[] pixels8; - PixelGrabber pg = new PixelGrabber(img,x,y,1,1,false); - try {pg.grabPixels();} - catch (InterruptedException e){return pvalue;}; - pixels8 = (byte[])(pg.getPixels()); - index = pixels8!=null?pixels8[0]&0xff:0; - } - if (imageType!=COLOR_256) { - pvalue[0] = index; - return pvalue; - } - pvalue[3] = index; - // fall through to get rgb values - case COLOR_RGB: - int[] pixels32 = new int[1]; - if (win==null) break; - PixelGrabber pg = new PixelGrabber(img, x, y, 1, 1, pixels32, 0, width); - try {pg.grabPixels();} - catch (InterruptedException e){return pvalue;}; - int c = pixels32[0]; - int r = (c&0xff0000)>>16; - int g = (c&0xff00)>>8; - int b = c&0xff; - pvalue[0] = r; - pvalue[1] = g; - pvalue[2] = b; - break; - case GRAY16: case GRAY32: - if (ip!=null) pvalue[0] = ip.getPixel(x, y); - break; - } - return pvalue; - } - - /** Returns an empty image stack that has the same - width, height and color table as this image. */ - public ImageStack createEmptyStack() { - ColorModel cm; - if (ip!=null) - cm = ip.getColorModel(); - else - cm = createLut().getColorModel(); - return new ImageStack(width, height, cm); - } - - /** Returns the image stack. The stack may have only - one slice. After adding or removing slices, call - setStack() to update the image and - the window that is displaying it. - @see #setStack - */ - public ImageStack getStack() { - ImageStack s; - if (stack==null) { - s = createEmptyStack(); - ImageProcessor ip2 = getProcessor(); - if (ip2==null) - return s; - String info = (String)getProperty("Info"); - String label = info!=null?getTitle()+"\n"+info:null; - s.addSlice(label, ip2); - s.update(ip2); - } else { - s = stack; - s.update(ip); - } - if (roi!=null) - s.setRoi(roi.getBounds()); - else - s.setRoi(null); - return s; - } - - /** Returns the base image stack. */ - public ImageStack getImageStack() { - if (stack==null) - return getStack(); - else { - stack.update(ip); - return stack; - } - } - - /** Returns the current stack slice number or 1 if - this is a single image. */ - public int getCurrentSlice() { - if (currentSlice<1) setCurrentSlice(1); - if (currentSlice>getStackSize()) setCurrentSlice(getStackSize()); - return currentSlice; - } - - final void setCurrentSlice(int slice) { - currentSlice = slice; - int stackSize = getStackSize(); - if (nChannels==stackSize) updatePosition(currentSlice, 1, 1); - if (nSlices==stackSize) updatePosition(1, currentSlice, 1); - if (nFrames==stackSize) updatePosition(1, 1, currentSlice); - } - - public int getChannel() { - return position[0]; - } - - public int getSlice() { - return position[1]; - } - - public int getFrame() { - return position[2]; - } - - public void killStack() { - stack = null; - trimProcessor(); - } - - public void setPosition(int channel, int slice, int frame) { - //IJ.log("setPosition: "+channel+" "+slice+" "+frame+" "+noUpdateMode); - verifyDimensions(); - if (channel<1) channel = 1; - if (channel>nChannels) channel = nChannels; - if (slice<1) slice = 1; - if (slice>nSlices) slice = nSlices; - if (frame<1) frame = 1; - if (frame>nFrames) frame = nFrames; - if (isDisplayedHyperStack()) - ((StackWindow)win).setPosition(channel, slice, frame); - else { - setSlice((frame-1)*nChannels*nSlices + (slice-1)*nChannels + channel); - updatePosition(channel, slice, frame); - } - } - - public void setPositionWithoutUpdate(int channel, int slice, int frame) { - noUpdateMode = true; - setPosition(channel, slice, frame); - noUpdateMode = false; - } - - /** Returns that stack index (1-based) corresponding to the specified position. */ - public int getStackIndex(int channel, int slice, int frame) { - if (channel<1) channel = 1; - if (channel>nChannels) channel = nChannels; - if (slice<1) slice = 1; - if (slice>nSlices) slice = nSlices; - if (frame<1) frame = 1; - if (frame>nFrames) frame = nFrames; - return (frame-1)*nChannels*nSlices + (slice-1)*nChannels + channel; - } - - /* Hack needed to make the HyperStackReducer work. */ - public void resetStack() { - if (currentSlice==1 && stack!=null && stack.getSize()>0) { - ColorModel cm = ip.getColorModel(); - double min = ip.getMin(); - double max = ip.getMax(); - ip = stack.getProcessor(1); - ip.setColorModel(cm); - ip.setMinAndMax(min, max); - } - } - - public void setPosition(int n) { - int[] dim = getDimensions(); - int c = ((n-1)%dim[2])+1; - int z = (((n-1)/dim[2])%dim[3])+1; - int t = (((n-1)/(dim[2]*dim[3]))%dim[4])+1; - setPosition(c, z, t); - } - - /** Displays the specified stack image, where 1<=n<=stackSize. - Does nothing if this image is not a stack. */ - public synchronized void setSlice(int n) { - if (stack==null || (n==currentSlice&&ip!=null)) { - updateAndRepaintWindow(); - return; - } - if (n>=1 && n<=stack.getSize()) { - Roi roi = getRoi(); - if (roi!=null) - roi.endPaste(); - if (isProcessor()) - stack.setPixels(ip.getPixels(),currentSlice); - ip = getProcessor(); - setCurrentSlice(n); - Object pixels = stack.getPixels(currentSlice); - if (ip!=null && pixels!=null) { - ip.setSnapshotPixels(null); - ip.setPixels(pixels); - } else - ip = stack.getProcessor(n); - if (win!=null && win instanceof StackWindow) - ((StackWindow)win).updateSliceSelector(); - //if (IJ.altKeyDown() && !IJ.isMacro()) { - // if (imageType==GRAY16 || imageType==GRAY32) { - // ip.resetMinAndMax(); - // IJ.showStatus(n+": min="+ip.getMin()+", max="+ip.getMax()); - // } - // ContrastAdjuster.update(); - //} - if (imageType==COLOR_RGB) - ContrastAdjuster.update(); - if (!(Interpreter.isBatchMode()||noUpdateMode)) - updateAndRepaintWindow(); - else - img = null; - } - } - - /** Displays the specified stack image (1<=n<=stackSize) - without updating the display. */ - public void setSliceWithoutUpdate(int n) { - noUpdateMode = true; - setSlice(n); - noUpdateMode = false; - } - - /** Obsolete */ - void undoFilter() { - if (ip!=null) { - ip.reset(); - updateAndDraw(); - } - } - - /** Returns the current selection, or null if there is no selection. */ - public Roi getRoi() { - return roi; - } - - /** Assigns the specified ROI to this image and displays it. Any existing - ROI is deleted if roi is null or its width or height is zero. */ - public void setRoi(Roi newRoi) { - setRoi(newRoi, true); - } - - /** Assigns 'newRoi' to this image and displays it if 'updateDisplay' is true. */ - public void setRoi(Roi newRoi, boolean updateDisplay) { - if (newRoi==null) - {killRoi(); return;} - if (newRoi.isVisible()) { - newRoi = (Roi)newRoi.clone(); - if (newRoi==null) - {killRoi(); return;} - } - Rectangle bounds = newRoi.getBounds(); - if (bounds.width==0 && bounds.height==0 && !(newRoi.getType()==Roi.POINT||newRoi.getType()==Roi.LINE)) - {killRoi(); return;} - roi = newRoi; - if (ip!=null) { - ip.setMask(null); - if (roi.isArea()) - ip.setRoi(bounds); - else - ip.resetRoi(); - } - roi.setImage(this); - if (updateDisplay) draw(); - } - - /** Creates a rectangular selection. */ - public void setRoi(int x, int y, int width, int height) { - setRoi(new Rectangle(x, y, width, height)); - } - - /** Creates a rectangular selection. */ - public void setRoi(Rectangle r) { - setRoi(new Roi(r.x, r.y, r.width, r.height)); - } - - /** Starts the process of creating a new selection, where sx and sy are the - starting screen coordinates. The selection type is determined by which tool in - the tool bar is active. The user interactively sets the selection size and shape. */ - public void createNewRoi(int sx, int sy) { - killRoi(); - switch (Toolbar.getToolId()) { - case Toolbar.RECTANGLE: - roi = new Roi(sx, sy, this); - break; - case Toolbar.OVAL: - roi = new OvalRoi(sx, sy, this); - break; - case Toolbar.POLYGON: - case Toolbar.POLYLINE: - case Toolbar.ANGLE: - roi = new PolygonRoi(sx, sy, this); - break; - case Toolbar.FREEROI: - case Toolbar.FREELINE: - roi = new FreehandRoi(sx, sy, this); - break; - case Toolbar.LINE: - roi = new Line(sx, sy, this); - break; - case Toolbar.TEXT: - roi = new TextRoi(sx, sy, this); - break; - case Toolbar.POINT: - roi = new PointRoi(sx, sy, this); - if (Prefs.pointAutoMeasure || (Prefs.pointAutoNextSlice&&!Prefs.pointAddToManager)) IJ.run("Measure"); - if (Prefs.pointAddToManager) { - IJ.run("Add to Manager "); - ImageCanvas ic = getCanvas(); - if (ic!=null && !ic.getShowAllROIs()) - ic.setShowAllROIs(true); - } - if (Prefs.pointAutoNextSlice && getStackSize()>1) { - IJ.run("Next Slice [>]"); - killRoi(); - } - break; - } - } - - /** Deletes the current region of interest. Makes a copy - of the current ROI so it can be recovered by the - Edit/Restore Selection command. */ - public void killRoi() { - if (roi!=null) { - saveRoi(); - roi = null; - if (ip!=null) - ip.resetRoi(); - draw(); - } - } - - public void saveRoi() { - if (roi!=null) { - roi.endPaste(); - Rectangle r = roi.getBounds(); - if (r.width>0 && r.height>0) { - Roi.previousRoi = (Roi)roi.clone(); - if (IJ.debugMode) IJ.log("saveRoi: "+roi); - } - } - } - - public void restoreRoi() { - if (Roi.previousRoi!=null) { - Roi pRoi = Roi.previousRoi; - Rectangle r = pRoi.getBounds(); - if (r.width<=width || r.height<=height || isSmaller(pRoi)) { // will it (mostly) fit in this image? - roi = (Roi)pRoi.clone(); - roi.setImage(this); - if (r.x>=width || r.y>=height || (r.x+r.width)<=0 || (r.y+r.height)<=0) // does it need to be moved? - roi.setLocation((width-r.width)/2, (height-r.height)/2); - else if (r.width==width && r.height==height) // is it the same size as the image - roi.setLocation(0, 0); - draw(); - } - } - } - - boolean isSmaller(Roi r) { - ImageProcessor mask = r.getMask(); - if (mask==null) return false; - mask.setThreshold(255, 255, ImageProcessor.NO_LUT_UPDATE); - ImageStatistics stats = ImageStatistics.getStatistics(mask, MEAN+LIMIT, null); - return stats.area<=width*height; - } - - /** Implements the File/Revert command. */ - public void revert() { - if (getStackSize()>1) // can't revert stacks - return; - FileInfo fi = getOriginalFileInfo(); - boolean isFileInfo = fi!=null && fi.fileFormat!=FileInfo.UNKNOWN; - if (!(isFileInfo || url!=null)) - return; - if (getStackSize()>1 && (fi==null||fi.fileFormat!=FileInfo.TIFF||fi.compression!=FileInfo.COMPRESSION_NONE)) - return; - if (ij!=null && changes && isFileInfo && !Interpreter.isBatchMode() && !IJ.isMacro() && !IJ.altKeyDown()) { - if (!IJ.showMessageWithCancel("Revert?", "Revert to saved version of\n\""+getTitle()+"\"?")) - return; - } - Roi saveRoi = null; - if (roi!=null) { - roi.endPaste(); - saveRoi = (Roi)roi.clone(); - } - trimProcessor(); - if (isFileInfo && !(url!=null&&(fi.directory==null||fi.directory.equals("")))) - new FileOpener(fi).revertToSaved(this); - else if (url!=null) { - IJ.showStatus("Loading: " + url); - Opener opener = new Opener(); - try { - ImagePlus imp = opener.openURL(url); - if (imp!=null) - setProcessor(null, imp.getProcessor()); - } catch (Exception e) {} - if (getType()==COLOR_RGB && getTitle().endsWith(".jpg")) - Opener.convertGrayJpegTo8Bits(this); - } - if (Prefs.useInvertingLut && getBitDepth()==8 && ip!=null && !ip.isInvertedLut()&& !ip.isColorLut()) - invertLookupTable(); - if (getProperty("FHT")!=null) { - properties.remove("FHT"); - if (getTitle().startsWith("FFT of ")) - setTitle(getTitle().substring(6)); - } - ContrastAdjuster.update(); - if (saveRoi!=null) setRoi(saveRoi); - repaintWindow(); - IJ.showStatus(""); - changes = false; - notifyListeners(UPDATED); - } - - /** Returns a FileInfo object containing information, including the - pixel array, needed to save this image. Use getOriginalFileInfo() - to get a copy of the FileInfo object used to open the image. - @see ij.io.FileInfo - @see #getOriginalFileInfo - @see #setFileInfo - */ - public FileInfo getFileInfo() { - FileInfo fi = new FileInfo(); - fi.width = width; - fi.height = height; - fi.nImages = getStackSize(); - if (compositeImage) - fi.nImages = getImageStackSize(); - fi.whiteIsZero = isInvertedLut(); - fi.intelByteOrder = false; - setupProcessor(); - if (fi.nImages==1) - fi.pixels = ip.getPixels(); - else - fi.pixels = stack.getImageArray(); - Calibration cal = getCalibration(); - if (cal.scaled()) { - fi.pixelWidth = cal.pixelWidth; - fi.pixelHeight = cal.pixelHeight; - fi.unit = cal.getUnit(); - } - if (fi.nImages>1) - fi.pixelDepth = cal.pixelDepth; - fi.frameInterval = cal.frameInterval; - if (cal.calibrated()) { - fi.calibrationFunction = cal.getFunction(); - fi.coefficients = cal.getCoefficients(); - fi.valueUnit = cal.getValueUnit(); - } - switch (imageType) { - case GRAY8: case COLOR_256: - LookUpTable lut = createLut(); - if (imageType==COLOR_256 || !lut.isGrayscale()) - fi.fileType = FileInfo.COLOR8; - else - fi.fileType = FileInfo.GRAY8; - fi.lutSize = lut.getMapSize(); - fi.reds = lut.getReds(); - fi.greens = lut.getGreens(); - fi.blues = lut.getBlues(); - break; - case GRAY16: - if (compositeImage && fi.nImages==3) - fi.fileType = fi.RGB48; - else - fi.fileType = fi.GRAY16_UNSIGNED; - break; - case GRAY32: - fi.fileType = fi.GRAY32_FLOAT; - break; - case COLOR_RGB: - fi.fileType = fi.RGB; - break; - default: - } - return fi; - } - - /** Returns the FileInfo object that was used to open this image. - Returns null for images created using the File/New command. - @see ij.io.FileInfo - @see #getFileInfo - */ - public FileInfo getOriginalFileInfo() { - if (fileInfo==null & url!=null) { - fileInfo = new FileInfo(); - fileInfo.width = width; - fileInfo.height = height; - fileInfo.url = url; - fileInfo.directory = null; - } - return fileInfo; - } - - /** Used by ImagePlus to monitor loading of images. */ - public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) { - imageUpdateY = y; - imageUpdateW = w; - if ((flags & ERROR) != 0) { - errorLoadingImage = true; - return false; - } - imageLoaded = (flags & (ALLBITS|FRAMEBITS|ABORT)) != 0; - return !imageLoaded; - } - - /** Sets the ImageProcessor, Roi, AWT Image and stack image - arrays to null. Does nothing if the image is locked. */ - public synchronized void flush() { - notifyListeners(CLOSED); - if (locked || ignoreFlush) return; - ip = null; - if (roi!=null) roi.setImage(null); - roi = null; - if (stack!=null) { - Object[] arrays = stack.getImageArray(); - if (arrays!=null) { - for (int i=0; icut is true. */ - public void copy(boolean cut) { - Roi roi = getRoi(); - if (roi!=null && !roi.isArea()) { - IJ.error("Cut/Copy", "The Cut and Copy commands require\n" - +"an area selection, or no selection."); - return; - } - boolean batchMode = Interpreter.isBatchMode(); - String msg = (cut)?"Cut":"Copy"; - if (!batchMode) IJ.showStatus(msg+ "ing..."); - ImageProcessor ip = getProcessor(); - ImageProcessor ip2; - Roi roi2 = null; - ip2 = ip.crop(); - if (roi!=null && roi.getType()!=Roi.RECTANGLE) { - roi2 = (Roi)roi.clone(); - Rectangle r = roi.getBounds(); - if (r.x<0 || r.y<0 || r.x+r.width>width || r.y+r.height>height) { - roi2 = new ShapeRoi(roi2); - ShapeRoi image = new ShapeRoi(new Roi(0, 0, width, height)); - roi2 = image.and((ShapeRoi)roi2); - } - } - clipboard = new ImagePlus("Clipboard", ip2); - if (roi2!=null) clipboard.setRoi(roi2); - if (cut) { - ip.snapshot(); - ip.setColor(Toolbar.getBackgroundColor()); - ip.fill(); - if (roi!=null && roi.getType()!=Roi.RECTANGLE) { - getMask(); - ip.reset(ip.getMask()); - } setColor(Toolbar.getForegroundColor()); - Undo.setup(Undo.FILTER, this); - updateAndDraw(); - } - int bytesPerPixel = 1; - switch (clipboard.getType()) { - case ImagePlus.GRAY16: bytesPerPixel = 2; break; - case ImagePlus.GRAY32: case ImagePlus.COLOR_RGB: bytesPerPixel = 4; - } - //Roi roi3 = clipboard.getRoi(); - //IJ.log("copy: "+clipboard +" "+ "roi3="+(roi3!=null?""+roi3:"")); - if (!batchMode) - IJ.showStatus(msg + ": " + (clipboard.getWidth()*clipboard.getHeight()*bytesPerPixel)/1024 + "k"); - } - - - /** Inserts the contents of the internal clipboard into the active image. If there - is a selection the same size as the image on the clipboard, the image is inserted - into that selection, otherwise the selection is inserted into the center of the image.*/ - public void paste() { - if (clipboard==null) return; - int cType = clipboard.getType(); - int iType = getType(); - - int w = clipboard.getWidth(); - int h = clipboard.getHeight(); - Roi cRoi = clipboard.getRoi(); - Rectangle r = null; - Roi roi = getRoi(); - if (roi!=null) - r = roi.getBounds(); - if (w==width && h==height && (r==null||w!=r.width||h!=r.height)) { - setRoi(0, 0, width, height); - roi = getRoi(); - r = roi.getBounds(); - } - if (r==null || (r!=null && (w!=r.width || h!=r.height))) { - // create a new roi centered on visible part of image - ImageCanvas ic = null; - if (win!=null) - ic = win.getCanvas(); - Rectangle srcRect = ic!=null?ic.getSrcRect():new Rectangle(0,0,width, height); - int xCenter = srcRect.x + srcRect.width/2; - int yCenter = srcRect.y + srcRect.height/2; - if (cRoi!=null && cRoi.getType()!=Roi.RECTANGLE) { - cRoi.setImage(this); - cRoi.setLocation(xCenter-w/2, yCenter-h/2); - setRoi(cRoi); - } else - setRoi(xCenter-w/2, yCenter-h/2, w, h); - roi = getRoi(); - } - if (IJ.isMacro()) { - //non-interactive paste - int pasteMode = Roi.getCurrentPasteMode(); - boolean nonRect = roi.getType()!=Roi.RECTANGLE; - ImageProcessor ip = getProcessor(); - if (nonRect) ip.snapshot(); - r = roi.getBounds(); - ip.copyBits(clipboard.getProcessor(), r.x, r.y, pasteMode); - if (nonRect) ip.reset(getMask()); - updateAndDraw(); - //killRoi(); - } else if (roi!=null) { - roi.startPaste(clipboard); - Undo.setup(Undo.PASTE, this); - } - changes = true; - } - - /** Returns the internal clipboard or null if the internal clipboard is empty. */ - public static ImagePlus getClipboard() { - return clipboard; - } - - /** Clears the internal clipboard. */ - public static void resetClipboard() { - clipboard = null; - } - - protected void notifyListeners(int id) { - synchronized (listeners) { - for (int i=0; i1 && (this instanceof CompositeImage); - } - - /** Sets the display range of the current channel. With non-composite - images it is identical to ip.setMinAndMax(min, max). */ - public void setDisplayRange(double min, double max) { - if (ip!=null) - ip.setMinAndMax(min, max); - } - - public double getDisplayRangeMin() { - return ip.getMin(); - } - - public double getDisplayRangeMax() { - return ip.getMax(); - } - - /** Sets the display range of specified channels in an RGB image, where 4=red, - 2=green, 1=blue, 6=red+green, etc. With non-RGB images, this method is - identical to setDisplayRange(min, max). This method is used by the - Image/Adjust/Color Balance tool . */ - public void setDisplayRange(double min, double max, int channels) { - if (ip instanceof ColorProcessor) - ((ColorProcessor)ip).setMinAndMax(min, max, channels); - else - ip.setMinAndMax(min, max); - } - - public void resetDisplayRange() { - ip.resetMinAndMax(); - } - - public void updatePosition(int c, int z, int t) { - //IJ.log("updatePosition: "+c+", "+z+", "+t); - position[0] = c; - position[1] = z; - position[2] = t; - } - - public Object clone() { - try {return super.clone();} - catch (CloneNotSupportedException e) {return null;} - } - - public String toString() { - return "imp["+getTitle()+" "+width+"x"+height+"x"+getStackSize()+"]"; - } - -} +package ij; +import java.awt.*; +import java.awt.image.*; +import java.net.URL; +import java.util.*; +import ij.process.*; +import ij.io.*; +import ij.gui.*; +import ij.measure.*; +import ij.plugin.filter.Analyzer; +import ij.util.Tools; +import ij.macro.Interpreter; +import ij.plugin.frame.ContrastAdjuster; +import ij.plugin.Converter; + +/** +This is an extended image class that supports 8-bit, 16-bit, +32-bit (real) and RGB images. It also provides support for +3D image stacks. +@see ij.process.ImageProcessor +@see ij.ImageStack +@see ij.gui.ImageWindow +@see ij.gui.ImageCanvas +*/ + +public class ImagePlus implements ImageObserver, Measurements { + + /** 8-bit grayscale (unsigned)*/ + public static final int GRAY8 = 0; + + /** 16-bit grayscale (unsigned) */ + public static final int GRAY16 = 1; + + /** 32-bit floating-point grayscale */ + public static final int GRAY32 = 2; + + /** 8-bit indexed color */ + public static final int COLOR_256 = 3; + + /** 32-bit RGB color */ + public static final int COLOR_RGB = 4; + + /** True if any changes have been made to this image. */ + public boolean changes; + + /** Obsolete. Use GetCalibration(). */ + public double pixelWidth=1.0, pixelHeight=1.0; + /** Obsolete. Use GetCalibration(). */ + public String unit="pixel"; + /** Obsolete. Use GetCalibration(). */ + public String units=unit; + /** Obsolete. Use GetCalibration(). */ + public boolean sCalibrated; + + protected Image img; + protected ImageProcessor ip; + protected ImageWindow win; + protected Roi roi; + protected int currentSlice; + protected static final int OPENED=0, CLOSED=1, UPDATED=2; + protected boolean compositeImage; + protected int width; + protected int height; + protected boolean locked = false; + + private ImageJ ij = IJ.getInstance(); + private String title; + private String url; + private FileInfo fileInfo; + private int nSlices = 1; + private int nChannels = 1; + private int nFrames = 1; + private int imageType = GRAY8; + private ImageStack stack; + private static int currentID = -1; + private int ID; + private static Component comp; + private boolean imageLoaded; + private int imageUpdateY, imageUpdateW; + private Properties properties; + private long startTime; + private Calibration calibration; + private static Calibration globalCalibration; + private boolean activated; + private boolean ignoreFlush; + private boolean errorLoadingImage; + private static ImagePlus clipboard; + private static Vector listeners = new Vector(); + private boolean openAsHyperStack; + private int[] position = {1,1,1}; + private boolean noUpdateMode; + + /** Constructs an uninitialized ImagePlus. */ + public ImagePlus() { + ID = --currentID; + title="null"; + } + + /** Constructs an ImagePlus from an Image or BufferedImage. The first + argument will be used as the title of the window that displays the image. + Throws an IllegalStateException if an error occurs while loading the image. */ + public ImagePlus(String title, Image img) { + this.title = title; + ID = --currentID; + if (img!=null) + setImage(img); + } + + /** Constructs an ImagePlus from an ImageProcessor. */ + public ImagePlus(String title, ImageProcessor ip) { + setProcessor(title, ip); + ID = --currentID; + } + + /** Constructs an ImagePlus from a TIFF, BMP, DICOM, FITS, + PGM, GIF or JPRG specified by a path or from a TIFF, DICOM, + GIF or JPEG specified by a URL. */ + public ImagePlus(String pathOrURL) { + Opener opener = new Opener(); + ImagePlus imp = null; + boolean isURL = pathOrURL.indexOf("://")>0; + if (isURL) + imp = opener.openURL(pathOrURL); + else + imp = opener.openImage(pathOrURL); + if (imp!=null) { + if (imp.getStackSize()>1) + setStack(imp.getTitle(), imp.getStack()); + else + setProcessor(imp.getTitle(), imp.getProcessor()); + setCalibration(imp.getCalibration()); + properties = imp.getProperties(); + setFileInfo(imp.getOriginalFileInfo()); + setDimensions(imp.getNChannels(), imp.getNSlices(), imp.getNFrames()); + if (isURL) + this.url = pathOrURL; + ID = --currentID; + } + } + + /** Constructs an ImagePlus from a stack. */ + public ImagePlus(String title, ImageStack stack) { + setStack(title, stack); + ID = --currentID; + } + + /** Locks the image so other threads can test to see if it + is in use. Returns true if the image was successfully locked. + Beeps, displays a message in the status bar, and returns + false if the image is already locked. */ + public synchronized boolean lock() { + if (locked) { + IJ.beep(); + IJ.showStatus("\"" + title + "\" is locked"); + //if (IJ.macroRunning()) { + // IJ.error("Image is locked"); + // Macro.abort(); + //} + return false; + } else { + locked = true; + if (IJ.debugMode) IJ.log(title + ": lock"); + return true; + } + } + + /** Similar to lock, but doesn't beep and display an error + message if the attempt to lock the image fails. */ + public synchronized boolean lockSilently() { + if (locked) + return false; + else { + locked = true; + if (IJ.debugMode) IJ.log(title + ": lock silently"); + return true; + } + } + + /** Unlocks the image. */ + public synchronized void unlock() { + locked = false; + if (IJ.debugMode) IJ.log(title + ": unlock"); + } + + private void waitForImage(Image img) { + if (comp==null) { + comp = IJ.getInstance(); + if (comp==null) + comp = new Canvas(); + } + imageLoaded = false; + if (!comp.prepareImage(img, this)) { + double progress; + waitStart = System.currentTimeMillis(); + while (!imageLoaded && !errorLoadingImage) { + //IJ.showStatus(imageUpdateY+" "+imageUpdateW); + IJ.wait(30); + if (imageUpdateW>1) { + progress = (double)imageUpdateY/imageUpdateW; + if (!(progress<1.0)) { + progress = 1.0 - (progress-1.0); + if (progress<0.0) progress = 0.9; + } + showProgress(progress); + } + } + showProgress(1.0); + } + } + + long waitStart; + private void showProgress(double percent) { + if ((System.currentTimeMillis()-waitStart)>500L) + IJ.showProgress(percent); + } + + /** Draws the image. If there is an ROI, its + outline is also displayed. Does nothing if there + is no window associated with this image (i.e. show() + has not been called).*/ + public void draw(){ + if (win!=null) + win.getCanvas().repaint(); + } + + /** Draws image and roi outline using a clip rect. */ + public void draw(int x, int y, int width, int height){ + if (win!=null) { + ImageCanvas ic = win.getCanvas(); + double mag = ic.getMagnification(); + x = ic.screenX(x); + y = ic.screenY(y); + width = (int)(width*mag); + height = (int)(height*mag); + ic.repaint(x, y, width, height); + if (listeners.size()>0 && roi!=null && roi.getPasteMode()!=Roi.NOT_PASTING) + notifyListeners(UPDATED); + } + } + + /** Updates this image from the pixel data in its + associated ImageProcessor, then displays it. Does + nothing if there is no window associated with + this image (i.e. show() has not been called).*/ + public void updateAndDraw() { + if (ip!=null) { + if (win!=null) { + win.getCanvas().setImageUpdated(); + if (listeners.size()>0) notifyListeners(UPDATED); + } + draw(); + } + } + + /** Updates this image from the pixel data in its + associated ImageProcessor, then displays it. + The CompositeImage class overrides this method + to only update the current channel. */ + public void updateChannelAndDraw() { + updateAndDraw(); + } + + /** Returns a reference to the current ImageProcessor. The + CompositeImage class overrides this method so it returns + the processor associated with the current channel. */ + public ImageProcessor getChannelProcessor() { + return getProcessor(); + } + + /* The CompositeImage class overrides this method to + return, as an array, copies of this image's channel LUTs. */ + public LUT[] getLuts() { + return null; + //ImageProcessor ip = getProcessor(); + //ColorModel cm = ip.getColorModel(); + //if (cm instanceof IndexColorModel) { + // LUT[] luts = new LUT[1]; + // luts[0] = new LUT((IndexColorModel)cm, ip.getMin(), ip.getMax()); + // return luts; + //} else + // return null; + } + + /** Calls draw to draw the image and also repaints the + image window to force the information displayed above + the image (dimension, type, size) to be updated. */ + public void repaintWindow() { + if (win!=null) { + draw(); + win.repaint(); + } + } + + /** Calls updateAndDraw to update from the pixel data + and draw the image, and also repaints the image + window to force the information displayed above + the image (dimension, type, size) to be updated. */ + public void updateAndRepaintWindow() { + if (win!=null) { + updateAndDraw(); + win.repaint(); + } + } + + /** ImageCanvas.paint() calls this method when the + ImageProcessor has generated new image. */ + public void updateImage() { + if (ip!=null) + img = ip.createImage(); + } + + /** Closes the window, if any, that is displaying this image. */ + public void hide() { + if (win==null) { + Interpreter.removeBatchModeImage(this); + return; + } + boolean unlocked = lockSilently(); + changes = false; + win.close(); + win = null; + if (unlocked) unlock(); + } + + /** Closes this image and sets the ImageProcessor to null. To avoid the + "Save changes?" dialog, first set the public 'changes' variable to false. */ + public void close() { + ImageWindow win = getWindow(); + if (win!=null) { + //if (IJ.isWindows() && IJ.isJava14()) + // changes = false; // avoid 'save changes?' dialog and potential Java 1.5 deadlocks + win.close(); + } else { + if (WindowManager.getCurrentImage()==this) + WindowManager.setTempCurrentImage(null); + killRoi(); //save any ROI so it can be restored later + Interpreter.removeBatchModeImage(this); + } + } + + /** Opens a window to display this image and clears the status bar. */ + public void show() { + show(""); + } + + /** Opens a window to display this image and displays + 'statusMessage' in the status bar. */ + public void show(String statusMessage) { + if (win!=null) return; + if ((IJ.isMacro() && ij==null) || Interpreter.isBatchMode()) { + ImagePlus img = WindowManager.getCurrentImage(); + if (img!=null) img.saveRoi(); + WindowManager.setTempCurrentImage(this); + Interpreter.addBatchModeImage(this); + return; + } + if (Prefs.useInvertingLut && getBitDepth()==8 && ip!=null && !ip.isInvertedLut()&& !ip.isColorLut()) + invertLookupTable(); + img = getImage(); + if ((img!=null) && (width>=0) && (height>=0)) { + activated = false; + int stackSize = getStackSize(); + //if (compositeImage) stackSize /= nChannels; + if (stackSize>1) + win = new StackWindow(this); + else + win = new ImageWindow(this); + if (roi!=null) roi.setImage(this); + draw(); + IJ.showStatus(statusMessage); + if (IJ.isMacro()) { // wait for window to be activated + long start = System.currentTimeMillis(); + while (!activated) { + IJ.wait(5); + if ((System.currentTimeMillis()-start)>2000) { + WindowManager.setTempCurrentImage(this); + break; // 2 second timeout + } + } + } + notifyListeners(OPENED); + } + } + + void invertLookupTable() { + int nImages = getStackSize(); + ip.invertLut(); + if (nImages==1) + ip.invert(); + else { + ImageStack stack2 = getStack(); + for (int i=1; i<=nImages; i++) + stack2.getProcessor(i).invert(); + stack2.setColorModel(ip.getColorModel()); + } + } + + /** Called by ImageWindow.windowActivated(). */ + public void setActivated() { + activated = true; + } + + /** Returns this image as a AWT image. */ + public Image getImage() { + if (img==null && ip!=null) + img = ip.createImage(); + return img; + } + + /** Returns this image as a BufferedImage. */ + public BufferedImage getBufferedImage() { + if (isComposite()) + return (new ColorProcessor(getImage())).getBufferedImage(); + else + return ip.getBufferedImage(); + } + + /** Returns this image's unique numeric ID. */ + public int getID() { + return ID; + } + + /** Replaces the image, if any, with the one specified. + Throws an IllegalStateException if an error occurs + while loading the image. */ + public void setImage(Image img) { + if (img instanceof BufferedImage) { + BufferedImage bi = (BufferedImage)img; + if (bi.getType()==BufferedImage.TYPE_USHORT_GRAY) { + setProcessor(null, new ShortProcessor(bi)); + return; + } else if (bi.getType()==BufferedImage.TYPE_BYTE_GRAY) { + setProcessor(null, new ByteProcessor(bi)); + return; + } + } + roi = null; + errorLoadingImage = false; + waitForImage(img); + if (errorLoadingImage) + throw new IllegalStateException ("Error loading image"); + this.img = img; + int newWidth = img.getWidth(ij); + int newHeight = img.getHeight(ij); + boolean dimensionsChanged = newWidth!=width || newHeight!=height; + width = newWidth; + height = newHeight; + ip = null; + stack = null; + LookUpTable lut = new LookUpTable(img); + int type; + if (lut.getMapSize() > 0) { + if (lut.isGrayscale()) + type = GRAY8; + else + type = COLOR_256; + } else + type = COLOR_RGB; + setType(type); + setupProcessor(); + this.img = ip.createImage(); + if (win!=null) { + if (dimensionsChanged) + win = new ImageWindow(this); + else + repaintWindow(); + } + } + + /** Replaces the ImageProcessor, if any, with the one specified. + Set 'title' to null to leave the image title unchanged. */ + public void setProcessor(String title, ImageProcessor ip) { + if (ip==null || ip.getPixels()==null) + throw new IllegalArgumentException("ip null or ip.getPixels() null"); + int stackSize = getStackSize(); + if (stackSize>1 && (ip.getWidth()!=width || ip.getHeight()!=height)) + throw new IllegalArgumentException("ip wrong size"); + if (stackSize<=1) { + stack = null; + setCurrentSlice(1); + } + setProcessor2(title, ip, null); + } + + void setProcessor2(String title, ImageProcessor ip, ImageStack newStack) { + if (title!=null) setTitle(title); + this.ip = ip; + if (ij!=null) ip.setProgressBar(ij.getProgressBar()); + int stackSize = 1; + if (stack!=null) { + stackSize = stack.getSize(); + if (currentSlice>stackSize) setCurrentSlice(stackSize); + } + img = null; + boolean dimensionsChanged = width>0 && height>0 && (width!=ip.getWidth() || height!=ip.getHeight()); + if (dimensionsChanged) roi = null; + int type; + if (ip instanceof ByteProcessor) + type = GRAY8; + else if (ip instanceof ColorProcessor) + type = COLOR_RGB; + else if (ip instanceof ShortProcessor) + type = GRAY16; + else + type = GRAY32; + if (width==0) + imageType = type; + else + setType(type); + width = ip.getWidth(); + height = ip.getHeight(); + if (win!=null) { + if (dimensionsChanged && stackSize==1) + win.updateImage(this); + else if (newStack==null) + repaintWindow(); + draw(); + } + } + + /** Replaces the stack, if any, with the one specified. + Set 'title' to null to leave the title unchanged. */ + public void setStack(String title, ImageStack stack) { + int stackSize = stack.getSize(); + if (stackSize==0) + throw new IllegalArgumentException("Stack is empty"); + if (!stack.isVirtual()) { + Object[] arrays = stack.getImageArray(); + if (arrays==null || (arrays.length>0&&arrays[0]==null)) + throw new IllegalArgumentException("Stack pixel array null"); + } + boolean stackSizeChanged = this.stack!=null && stackSize!=getStackSize(); + if (currentSlice<1) setCurrentSlice(1); + boolean resetCurrentSlice = currentSlice>stackSize; + if (resetCurrentSlice) setCurrentSlice(stackSize); + ImageProcessor ip = stack.getProcessor(currentSlice); + boolean dimensionsChanged = width>0 && height>0 && (width!=ip.getWidth()||height!=ip.getHeight()); + this.stack = stack; + setProcessor2(title, ip, stack); + if (win==null) return; + boolean invalidDimensions = isDisplayedHyperStack() && !((StackWindow)win).validDimensions(); + if (stackSize==1 && win instanceof StackWindow) + win = new ImageWindow(this, getCanvas()); // replaces this window + else if (dimensionsChanged && !stackSizeChanged) + win.updateImage(this); + else if (stackSize>1 && !(win instanceof StackWindow)) { + if (isDisplayedHyperStack()) setOpenAsHyperStack(true); + win = new StackWindow(this, getCanvas()); // replaces this window + setPosition(1, 1, 1); + } else if (stackSize>1 && (dimensionsChanged||invalidDimensions)) { + if (isDisplayedHyperStack()) setOpenAsHyperStack(true); + win = new StackWindow(this); // replaces this window + setPosition(1, 1, 1); + } else + repaintWindow(); + if (resetCurrentSlice) setSlice(currentSlice); + } + + public void setStack(ImageStack stack, int nChannels, int nSlices, int nFrames) { + if (nChannels*nSlices*nFrames!=stack.getSize()) + throw new IllegalArgumentException("channels*slices*frames!=stackSize"); + this.nChannels = nChannels; + this.nSlices = nSlices; + this.nFrames = nFrames; + setStack(null, stack); + } + + /** Saves this image's FileInfo so it can be later + retieved using getOriginalFileInfo(). */ + public void setFileInfo(FileInfo fi) { + if (fi!=null) + fi.pixels = null; + fileInfo = fi; + } + + /** Returns the ImageWindow that is being used to display + this image. Returns null if show() has not be called + or the ImageWindow has been closed. */ + public ImageWindow getWindow() { + return win; + } + + /** Returns true if this image is currently being displayed in a window. */ + public boolean isVisible() { + return win!=null && win.isVisible(); + } + + /** This method should only be called from an ImageWindow. */ + public void setWindow(ImageWindow win) { + this.win = win; + if (roi!=null) + roi.setImage(this); // update roi's 'ic' field + } + + /** Returns the ImageCanvas being used to + display this image, or null. */ + public ImageCanvas getCanvas() { + return win!=null?win.getCanvas():null; + } + + /** Sets current foreground color. */ + public void setColor(Color c) { + if (ip!=null) + ip.setColor(c); + } + + void setupProcessor() { + if (imageType==COLOR_RGB) { + if (ip == null || ip instanceof ByteProcessor) + ip = new ColorProcessor(getImage()); + } else if (ip==null || (ip instanceof ColorProcessor)) + ip = new ByteProcessor(getImage()); + if (roi!=null && roi.isArea()) + ip.setRoi(roi.getBounds()); + else + ip.resetRoi(); + } + + public boolean isProcessor() { + return ip!=null; + } + + /** Returns a reference to the current ImageProcessor. If there + is no ImageProcessor, it creates one. Returns null if this + ImagePlus contains no ImageProcessor and no AWT Image. */ + public ImageProcessor getProcessor() { + if (ip==null && img==null) + return null; + setupProcessor(); + if (!compositeImage) + ip.setLineWidth(Line.getWidth()); + if (ij!=null) + ip.setProgressBar(ij.getProgressBar()); + Calibration cal = getCalibration(); + if (cal.calibrated()) + ip.setCalibrationTable(cal.getCTable()); + else + ip.setCalibrationTable(null); + return ip; + } + + /** Frees RAM by setting the snapshot (undo) buffer in + the current ImageProcessor to null. */ + public void trimProcessor() { + ImageProcessor ip2 = ip; + if (!locked && ip2!=null) { + if (IJ.debugMode) IJ.log(title + ": trimProcessor"); + ip2.setSnapshotPixels(null); + } + } + + /** Obsolete. */ + public void killProcessor() { + } + + /** For images with irregular ROIs, returns a byte mask, otherwise, returns + null. Mask pixels have a non-zero value. */ + public ImageProcessor getMask() { + if (roi==null) { + if (ip!=null) ip.resetRoi(); + return null; + } + ImageProcessor mask = roi.getMask(); + if (mask==null) + return null; + if (ip!=null) { + ip.setMask(mask); + ip.setRoi(roi.getBounds()); + } + return mask; + } + + /** Returns an ImageStatistics object generated using the standard + measurement options (area, mean, mode, min and max). + This plugin demonstrates how get the area, mean and max of the + current image or selection: +
+   public class Get_Statistics implements PlugIn {
+      public void run(String arg) {
+         ImagePlus imp = IJ.getImage();
+         ImageStatistics stats = imp.getStatistics();
+         IJ.log("Area: "+stats.area);
+         IJ.log("Mean: "+stats.mean);
+         IJ.log("Max: "+stats.max);
+      }
+   }
+		
+ @see ij.process.ImageStatistics + @see ij.process.ImageStatistics#getStatistics + */ + public ImageStatistics getStatistics() { + return getStatistics(AREA+MEAN+MODE+MIN_MAX); + } + + /** Returns an ImageStatistics object generated using the + specified measurement options. This plugin demonstrates how + get the area and centroid of the current selection: +
+   public class Get_Statistics implements PlugIn, Measurements {
+      public void run(String arg) {
+         ImagePlus imp = IJ.getImage();
+         ImageStatistics stats = imp.getStatistics(MEDIAN+CENTROID);
+         IJ.log("Median: "+stats.median);
+         IJ.log("xCentroid: "+stats.xCentroid);
+         IJ.log("yCentroid: "+stats.yCentroid);
+      }
+   }
+		
+ @see ij.process.ImageStatistics + @see ij.measure.Measurements + */ + public ImageStatistics getStatistics(int mOptions) { + return getStatistics(mOptions, 256, 0.0, 0.0); + } + + /** Returns an ImageStatistics object generated using the + specified measurement options and histogram bin count. + Note: except for float images, the number of bins + is currently fixed at 256. + */ + public ImageStatistics getStatistics(int mOptions, int nBins) { + return getStatistics(mOptions, nBins, 0.0, 0.0); + } + + /** Returns an ImageStatistics object generated using the + specified measurement options, histogram bin count and histogram range. + Note: for 8-bit and RGB images, the number of bins + is fixed at 256 and the histogram range is always 0-255. + */ + public ImageStatistics getStatistics(int mOptions, int nBins, double histMin, double histMax) { + setupProcessor(); + if (roi!=null && roi.isArea()) + ip.setRoi(roi); + else + ip.resetRoi(); + ip.setHistogramSize(nBins); + Calibration cal = getCalibration(); + if (getType()==GRAY16&& !(histMin==0.0&&histMax==0.0)) + {histMin=cal.getRawValue(histMin); histMax=cal.getRawValue(histMax);} + ip.setHistogramRange(histMin, histMax); + ImageStatistics stats = ImageStatistics.getStatistics(ip, mOptions, cal); + ip.setHistogramSize(256); + ip.setHistogramRange(0.0, 0.0); + return stats; + } + + /** Returns the image name. */ + public String getTitle() { + if (title==null) + return ""; + else + return title; + } + + /** Returns a shortened version of image name that does not + include spaces or a file name extension. */ + public String getShortTitle() { + String title = getTitle(); + int index = title.indexOf(' '); + if (index>-1) + title = title.substring(0, index); + index = title.lastIndexOf('.'); + if (index>0) + title = title.substring(0, index); + return title; + } + + /** Sets the image name. */ + public void setTitle(String title) { + if (title==null) + return; + if (win!=null) { + if (ij!=null) + Menus.updateWindowMenuItem(this.title, title); + String virtual = stack!=null && stack.isVirtual()?" (V)":""; + String global = getGlobalCalibration()!=null?" (G)":""; + + String scale = ""; + double magnification = win.getCanvas().getMagnification(); + if (magnification!=1.0) { + double percent = magnification*100.0; + int digits = percent>100.0||percent==(int)percent?0:1; + scale = " (" + IJ.d2s(percent,digits) + "%)"; + } + win.setTitle(title+virtual+global+scale); + } + this.title = title; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + /** If this is a stack, returns the number of slices, else returns 1. */ + public int getStackSize() { + if (stack==null) + return 1; + else { + int slices = stack.getSize(); + //if (compositeImage) slices /= nChannels; + if (slices<=0) slices = 1; + return slices; + } + } + + /** If this is a stack, returns the actual number of images in the stack, else returns 1. */ + public int getImageStackSize() { + if (stack==null) + return 1; + else { + int slices = stack.getSize(); + if (slices==0) slices = 1; + return slices; + } + } + + /** Sets the 3rd, 4th and 5th dimensions, where + nChannels*nSlices*nFrames + must be equal to the stack size. */ + public void setDimensions(int nChannels, int nSlices, int nFrames) { + //IJ.log("setDimensions: "+nChannels+" "+nSlices+" "+nFrames+" "+getImageStackSize()); + if (nChannels*nSlices*nFrames!=getImageStackSize() && ip!=null) { + //throw new IllegalArgumentException("channels*slices*frames!=stackSize"); + nChannels = 1; + nSlices = getImageStackSize(); + nFrames = 1; + if (isDisplayedHyperStack()) { + setOpenAsHyperStack(false); + new StackWindow(this); + setSlice(1); + } + } + boolean updateWin = isDisplayedHyperStack() && (this.nChannels!=nChannels||this.nSlices!=nSlices||this.nFrames!=nFrames); + this.nChannels = nChannels; + this.nSlices = nSlices; + this.nFrames = nFrames; + if (updateWin) { + if (nSlices!=getImageStackSize()) + setOpenAsHyperStack(true); + ip=null; img=null; + setPositionWithoutUpdate(getChannel(), getSlice(), getFrame()); + if (isComposite()) ((CompositeImage)this).reset(); + new StackWindow(this); + setPosition(getChannel(), getSlice(), getFrame()); + } + //IJ.log("setDimensions: "+ nChannels+" "+nSlices+" "+nFrames); + } + + /** Returns 'true' if this image is a hyperstack. */ + public boolean isHyperStack() { + return isDisplayedHyperStack() || (openAsHyperStack&&getNDimensions()>3); + } + + /** Returns the number of dimensions (2, 3, 4 or 5). */ + public int getNDimensions() { + int dimensions = 2; + int[] dim = getDimensions(); + if (dim[2]>1) dimensions++; + if (dim[3]>1) dimensions++; + if (dim[4]>1) dimensions++; + return dimensions; + } + + /** Returns 'true' if this is a hyperstack currently being displayed in a StackWindow. */ + public boolean isDisplayedHyperStack() { + return win!=null && win instanceof StackWindow && ((StackWindow)win).isHyperStack(); + } + + /** Returns the number of channels. */ + public int getNChannels() { + verifyDimensions(); + return nChannels; + } + + /** Returns the image depth (number of z-slices). */ + public int getNSlices() { + //IJ.log("getNSlices: "+ nChannels+" "+nSlices+" "+nFrames); + verifyDimensions(); + return nSlices; + } + + /** Returns the number of frames (time-points). */ + public int getNFrames() { + verifyDimensions(); + return nFrames; + } + + /** Returns the dimensions of this image (width, height, nChannels, + nSlices, nFrames) as a 5 element int array. */ + public int[] getDimensions() { + verifyDimensions(); + int[] d = new int[5]; + d[0] = width; + d[1] = height; + d[2] = nChannels; + d[3] = nSlices; + d[4] = nFrames; + return d; + } + + + void verifyDimensions() { + int stackSize = getImageStackSize(); + if (nSlices==1) { + if (nChannels>1 && nFrames==1) + nChannels = stackSize; + else if (nFrames>1 && nChannels==1) + nFrames = stackSize; + } + if (nChannels*nSlices*nFrames!=stackSize) { + nSlices = stackSize; + nChannels = 1; + nFrames = 1; + } + } + + /** Returns the current image type (ImagePlus.GRAY8, ImagePlus.GRAY16, + ImagePlus.GRAY32, ImagePlus.COLOR_256 or ImagePlus.COLOR_RGB). + @see #getBitDepth + */ + public int getType() { + return imageType; + } + + /** Returns the bit depth, 8, 16, 24 (RGB) or 32. RGB images actually use 32 bits per pixel. */ + public int getBitDepth() { + int bitDepth = 0; + switch (imageType) { + case GRAY8: case COLOR_256: bitDepth=8; break; + case GRAY16: bitDepth=16; break; + case GRAY32: bitDepth=32; break; + case COLOR_RGB: bitDepth=24; break; + } + return bitDepth; + } + + /** Returns the number of bytes per pixel. */ + public int getBytesPerPixel() { + switch (imageType) { + case GRAY16: return 2; + case GRAY32: case COLOR_RGB: return 4; + default: return 1; + } + } + + protected void setType(int type) { + if ((type<0) || (type>COLOR_RGB)) + return; + int previousType = imageType; + imageType = type; + if (imageType!=previousType) { + if (win!=null) + Menus.updateMenus(); + getLocalCalibration().setImage(this); + } + } + + /** Adds a key-value pair to this image's properties. The key + is removed from the properties table if value is null. */ + public void setProperty(String key, Object value) { + if (properties==null) + properties = new Properties(); + if (value==null) + properties.remove(key); + else + properties.put(key, value); + } + + /** Returns the property associated with 'key'. May return null. */ + public Object getProperty(String key) { + if (properties==null) + return null; + else + return properties.get(key); + } + + /** Returns this image's Properties. May return null. */ + public Properties getProperties() { + return properties; + } + + /** Creates a LookUpTable object that corresponds to this image. */ + public LookUpTable createLut() { + ImageProcessor ip2 = getProcessor(); + if (ip2!=null) + return new LookUpTable(ip2.getColorModel()); + else + return new LookUpTable(LookUpTable.createGrayscaleColorModel(false)); + } + + /** Returns true is this image uses an inverting LUT that + displays zero as white and 255 as black. */ + public boolean isInvertedLut() { + if (ip==null) { + if (img==null) + return false; + setupProcessor(); + } + return ip.isInvertedLut(); + } + + private int[] pvalue = new int[4]; + + /** + Returns the pixel value at (x,y) as a 4 element array. Grayscale values + are retuned in the first element. RGB values are returned in the first + 3 elements. For indexed color images, the RGB values are returned in the + first 3 three elements and the index (0-255) is returned in the last. + */ + public int[] getPixel(int x, int y) { + pvalue[0]=pvalue[1]=pvalue[2]=pvalue[3]=0; + if (img == null) + return pvalue; + switch (imageType) { + case GRAY8: case COLOR_256: + int index; + if (ip!=null) + index = ip.getPixel(x, y); + else { + byte[] pixels8; + PixelGrabber pg = new PixelGrabber(img,x,y,1,1,false); + try {pg.grabPixels();} + catch (InterruptedException e){return pvalue;}; + pixels8 = (byte[])(pg.getPixels()); + index = pixels8!=null?pixels8[0]&0xff:0; + } + if (imageType!=COLOR_256) { + pvalue[0] = index; + return pvalue; + } + pvalue[3] = index; + // fall through to get rgb values + case COLOR_RGB: + int[] pixels32 = new int[1]; + if (win==null) break; + PixelGrabber pg = new PixelGrabber(img, x, y, 1, 1, pixels32, 0, width); + try {pg.grabPixels();} + catch (InterruptedException e){return pvalue;}; + int c = pixels32[0]; + int r = (c&0xff0000)>>16; + int g = (c&0xff00)>>8; + int b = c&0xff; + pvalue[0] = r; + pvalue[1] = g; + pvalue[2] = b; + break; + case GRAY16: case GRAY32: + if (ip!=null) pvalue[0] = ip.getPixel(x, y); + break; + } + return pvalue; + } + + /** Returns an empty image stack that has the same + width, height and color table as this image. */ + public ImageStack createEmptyStack() { + ColorModel cm; + if (ip!=null) + cm = ip.getColorModel(); + else + cm = createLut().getColorModel(); + return new ImageStack(width, height, cm); + } + + /** Returns the image stack. The stack may have only + one slice. After adding or removing slices, call + setStack() to update the image and + the window that is displaying it. + @see #setStack + */ + public ImageStack getStack() { + ImageStack s; + if (stack==null) { + s = createEmptyStack(); + ImageProcessor ip2 = getProcessor(); + if (ip2==null) + return s; + String info = (String)getProperty("Info"); + String label = info!=null?getTitle()+"\n"+info:null; + s.addSlice(label, ip2); + s.update(ip2); + } else { + s = stack; + s.update(ip); + } + if (roi!=null) + s.setRoi(roi.getBounds()); + else + s.setRoi(null); + return s; + } + + /** Returns the base image stack. */ + public ImageStack getImageStack() { + if (stack==null) + return getStack(); + else { + stack.update(ip); + return stack; + } + } + + /** Returns the current stack slice number or 1 if + this is a single image. */ + public int getCurrentSlice() { + if (currentSlice<1) setCurrentSlice(1); + if (currentSlice>getStackSize()) setCurrentSlice(getStackSize()); + return currentSlice; + } + + final void setCurrentSlice(int slice) { + currentSlice = slice; + int stackSize = getStackSize(); + if (nChannels==stackSize) updatePosition(currentSlice, 1, 1); + if (nSlices==stackSize) updatePosition(1, currentSlice, 1); + if (nFrames==stackSize) updatePosition(1, 1, currentSlice); + } + + public int getChannel() { + return position[0]; + } + + public int getSlice() { + return position[1]; + } + + public int getFrame() { + return position[2]; + } + + public void killStack() { + stack = null; + trimProcessor(); + } + + public void setPosition(int channel, int slice, int frame) { + //IJ.log("setPosition: "+channel+" "+slice+" "+frame+" "+noUpdateMode); + verifyDimensions(); + if (channel<1) channel = 1; + if (channel>nChannels) channel = nChannels; + if (slice<1) slice = 1; + if (slice>nSlices) slice = nSlices; + if (frame<1) frame = 1; + if (frame>nFrames) frame = nFrames; + if (isDisplayedHyperStack()) + ((StackWindow)win).setPosition(channel, slice, frame); + else { + setSlice((frame-1)*nChannels*nSlices + (slice-1)*nChannels + channel); + updatePosition(channel, slice, frame); + } + } + + public void setPositionWithoutUpdate(int channel, int slice, int frame) { + noUpdateMode = true; + setPosition(channel, slice, frame); + noUpdateMode = false; + } + + /** Returns that stack index (1-based) corresponding to the specified position. */ + public int getStackIndex(int channel, int slice, int frame) { + if (channel<1) channel = 1; + if (channel>nChannels) channel = nChannels; + if (slice<1) slice = 1; + if (slice>nSlices) slice = nSlices; + if (frame<1) frame = 1; + if (frame>nFrames) frame = nFrames; + return (frame-1)*nChannels*nSlices + (slice-1)*nChannels + channel; + } + + /* Hack needed to make the HyperStackReducer work. */ + public void resetStack() { + if (currentSlice==1 && stack!=null && stack.getSize()>0) { + ColorModel cm = ip.getColorModel(); + double min = ip.getMin(); + double max = ip.getMax(); + ip = stack.getProcessor(1); + ip.setColorModel(cm); + ip.setMinAndMax(min, max); + } + } + + public void setPosition(int n) { + int[] dim = getDimensions(); + int c = ((n-1)%dim[2])+1; + int z = (((n-1)/dim[2])%dim[3])+1; + int t = (((n-1)/(dim[2]*dim[3]))%dim[4])+1; + setPosition(c, z, t); + } + + /** Displays the specified stack image, where 1<=n<=stackSize. + Does nothing if this image is not a stack. */ + public synchronized void setSlice(int n) { + if (stack==null || (n==currentSlice&&ip!=null)) { + updateAndRepaintWindow(); + return; + } + if (n>=1 && n<=stack.getSize()) { + Roi roi = getRoi(); + if (roi!=null) + roi.endPaste(); + if (isProcessor()) + stack.setPixels(ip.getPixels(),currentSlice); + ip = getProcessor(); + setCurrentSlice(n); + Object pixels = stack.getPixels(currentSlice); + if (ip!=null && pixels!=null) { + ip.setSnapshotPixels(null); + ip.setPixels(pixels); + } else + ip = stack.getProcessor(n); + if (win!=null && win instanceof StackWindow) + ((StackWindow)win).updateSliceSelector(); + //if (IJ.altKeyDown() && !IJ.isMacro()) { + // if (imageType==GRAY16 || imageType==GRAY32) { + // ip.resetMinAndMax(); + // IJ.showStatus(n+": min="+ip.getMin()+", max="+ip.getMax()); + // } + // ContrastAdjuster.update(); + //} + if (imageType==COLOR_RGB) + ContrastAdjuster.update(); + if (!(Interpreter.isBatchMode()||noUpdateMode)) + updateAndRepaintWindow(); + else + img = null; + } + } + + /** Displays the specified stack image (1<=n<=stackSize) + without updating the display. */ + public void setSliceWithoutUpdate(int n) { + noUpdateMode = true; + setSlice(n); + noUpdateMode = false; + } + + /** Obsolete */ + void undoFilter() { + if (ip!=null) { + ip.reset(); + updateAndDraw(); + } + } + + /** Returns the current selection, or null if there is no selection. */ + public Roi getRoi() { + return roi; + } + + /** Assigns the specified ROI to this image and displays it. Any existing + ROI is deleted if roi is null or its width or height is zero. */ + public void setRoi(Roi newRoi) { + setRoi(newRoi, true); + } + + /** Assigns 'newRoi' to this image and displays it if 'updateDisplay' is true. */ + public void setRoi(Roi newRoi, boolean updateDisplay) { + if (newRoi==null) + {killRoi(); return;} + if (newRoi.isVisible()) { + newRoi = (Roi)newRoi.clone(); + if (newRoi==null) + {killRoi(); return;} + } + Rectangle bounds = newRoi.getBounds(); + if (bounds.width==0 && bounds.height==0 && !(newRoi.getType()==Roi.POINT||newRoi.getType()==Roi.LINE)) + {killRoi(); return;} + roi = newRoi; + if (ip!=null) { + ip.setMask(null); + if (roi.isArea()) + ip.setRoi(bounds); + else + ip.resetRoi(); + } + roi.setImage(this); + if (updateDisplay) draw(); + } + + /** Creates a rectangular selection. */ + public void setRoi(int x, int y, int width, int height) { + setRoi(new Rectangle(x, y, width, height)); + } + + /** Creates a rectangular selection. */ + public void setRoi(Rectangle r) { + setRoi(new Roi(r.x, r.y, r.width, r.height)); + } + + /** Starts the process of creating a new selection, where sx and sy are the + starting screen coordinates. The selection type is determined by which tool in + the tool bar is active. The user interactively sets the selection size and shape. */ + public void createNewRoi(int sx, int sy) { + killRoi(); + switch (Toolbar.getToolId()) { + case Toolbar.RECTANGLE: + roi = new Roi(sx, sy, this); + break; + case Toolbar.OVAL: + roi = new OvalRoi(sx, sy, this); + break; + case Toolbar.POLYGON: + case Toolbar.POLYLINE: + case Toolbar.ANGLE: + roi = new PolygonRoi(sx, sy, this); + break; + case Toolbar.FREEROI: + case Toolbar.FREELINE: + roi = new FreehandRoi(sx, sy, this); + break; + case Toolbar.LINE: + roi = new Line(sx, sy, this); + break; + case Toolbar.TEXT: + roi = new TextRoi(sx, sy, this); + break; + case Toolbar.POINT: + roi = new PointRoi(sx, sy, this); + if (Prefs.pointAutoMeasure || (Prefs.pointAutoNextSlice&&!Prefs.pointAddToManager)) IJ.run("Measure"); + if (Prefs.pointAddToManager) { + IJ.run("Add to Manager "); + ImageCanvas ic = getCanvas(); + if (ic!=null && !ic.getShowAllROIs()) + ic.setShowAllROIs(true); + } + if (Prefs.pointAutoNextSlice && getStackSize()>1) { + IJ.run("Next Slice [>]"); + killRoi(); + } + break; + } + } + + /** Deletes the current region of interest. Makes a copy + of the current ROI so it can be recovered by the + Edit/Restore Selection command. */ + public void killRoi() { + if (roi!=null) { + saveRoi(); + roi = null; + if (ip!=null) + ip.resetRoi(); + draw(); + } + } + + public void saveRoi() { + if (roi!=null) { + roi.endPaste(); + Rectangle r = roi.getBounds(); + if (r.width>0 && r.height>0) { + Roi.previousRoi = (Roi)roi.clone(); + if (IJ.debugMode) IJ.log("saveRoi: "+roi); + } + } + } + + public void restoreRoi() { + if (Roi.previousRoi!=null) { + Roi pRoi = Roi.previousRoi; + Rectangle r = pRoi.getBounds(); + if (r.width<=width || r.height<=height || isSmaller(pRoi)) { // will it (mostly) fit in this image? + roi = (Roi)pRoi.clone(); + roi.setImage(this); + if (r.x>=width || r.y>=height || (r.x+r.width)<=0 || (r.y+r.height)<=0) // does it need to be moved? + roi.setLocation((width-r.width)/2, (height-r.height)/2); + else if (r.width==width && r.height==height) // is it the same size as the image + roi.setLocation(0, 0); + draw(); + } + } + } + + boolean isSmaller(Roi r) { + ImageProcessor mask = r.getMask(); + if (mask==null) return false; + mask.setThreshold(255, 255, ImageProcessor.NO_LUT_UPDATE); + ImageStatistics stats = ImageStatistics.getStatistics(mask, MEAN+LIMIT, null); + return stats.area<=width*height; + } + + /** Implements the File/Revert command. */ + public void revert() { + if (getStackSize()>1) // can't revert stacks + return; + FileInfo fi = getOriginalFileInfo(); + boolean isFileInfo = fi!=null && fi.fileFormat!=FileInfo.UNKNOWN; + if (!(isFileInfo || url!=null)) + return; + if (getStackSize()>1 && (fi==null||fi.fileFormat!=FileInfo.TIFF||fi.compression!=FileInfo.COMPRESSION_NONE)) + return; + if (ij!=null && changes && isFileInfo && !Interpreter.isBatchMode() && !IJ.isMacro() && !IJ.altKeyDown()) { + if (!IJ.showMessageWithCancel("Revert?", "Revert to saved version of\n\""+getTitle()+"\"?")) + return; + } + Roi saveRoi = null; + if (roi!=null) { + roi.endPaste(); + saveRoi = (Roi)roi.clone(); + } + trimProcessor(); + if (isFileInfo && !(url!=null&&(fi.directory==null||fi.directory.equals("")))) + new FileOpener(fi).revertToSaved(this); + else if (url!=null) { + IJ.showStatus("Loading: " + url); + Opener opener = new Opener(); + try { + ImagePlus imp = opener.openURL(url); + if (imp!=null) + setProcessor(null, imp.getProcessor()); + } catch (Exception e) {} + if (getType()==COLOR_RGB && getTitle().endsWith(".jpg")) + Opener.convertGrayJpegTo8Bits(this); + } + if (Prefs.useInvertingLut && getBitDepth()==8 && ip!=null && !ip.isInvertedLut()&& !ip.isColorLut()) + invertLookupTable(); + if (getProperty("FHT")!=null) { + properties.remove("FHT"); + if (getTitle().startsWith("FFT of ")) + setTitle(getTitle().substring(6)); + } + ContrastAdjuster.update(); + if (saveRoi!=null) setRoi(saveRoi); + repaintWindow(); + IJ.showStatus(""); + changes = false; + notifyListeners(UPDATED); + } + + /** Returns a FileInfo object containing information, including the + pixel array, needed to save this image. Use getOriginalFileInfo() + to get a copy of the FileInfo object used to open the image. + @see ij.io.FileInfo + @see #getOriginalFileInfo + @see #setFileInfo + */ + public FileInfo getFileInfo() { + FileInfo fi = new FileInfo(); + fi.width = width; + fi.height = height; + fi.nImages = getStackSize(); + if (compositeImage) + fi.nImages = getImageStackSize(); + fi.whiteIsZero = isInvertedLut(); + fi.intelByteOrder = false; + setupProcessor(); + if (fi.nImages==1) + fi.pixels = ip.getPixels(); + else + fi.pixels = stack.getImageArray(); + Calibration cal = getCalibration(); + if (cal.scaled()) { + fi.pixelWidth = cal.pixelWidth; + fi.pixelHeight = cal.pixelHeight; + fi.unit = cal.getUnit(); + } + if (fi.nImages>1) + fi.pixelDepth = cal.pixelDepth; + fi.frameInterval = cal.frameInterval; + if (cal.calibrated()) { + fi.calibrationFunction = cal.getFunction(); + fi.coefficients = cal.getCoefficients(); + fi.valueUnit = cal.getValueUnit(); + } + switch (imageType) { + case GRAY8: case COLOR_256: + LookUpTable lut = createLut(); + if (imageType==COLOR_256 || !lut.isGrayscale()) + fi.fileType = FileInfo.COLOR8; + else + fi.fileType = FileInfo.GRAY8; + fi.lutSize = lut.getMapSize(); + fi.reds = lut.getReds(); + fi.greens = lut.getGreens(); + fi.blues = lut.getBlues(); + break; + case GRAY16: + if (compositeImage && fi.nImages==3) + fi.fileType = fi.RGB48; + else + fi.fileType = fi.GRAY16_UNSIGNED; + break; + case GRAY32: + fi.fileType = fi.GRAY32_FLOAT; + break; + case COLOR_RGB: + fi.fileType = fi.RGB; + break; + default: + } + return fi; + } + + /** Returns the FileInfo object that was used to open this image. + Returns null for images created using the File/New command. + @see ij.io.FileInfo + @see #getFileInfo + */ + public FileInfo getOriginalFileInfo() { + if (fileInfo==null & url!=null) { + fileInfo = new FileInfo(); + fileInfo.width = width; + fileInfo.height = height; + fileInfo.url = url; + fileInfo.directory = null; + } + return fileInfo; + } + + /** Used by ImagePlus to monitor loading of images. */ + public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) { + imageUpdateY = y; + imageUpdateW = w; + if ((flags & ERROR) != 0) { + errorLoadingImage = true; + return false; + } + imageLoaded = (flags & (ALLBITS|FRAMEBITS|ABORT)) != 0; + return !imageLoaded; + } + + /** Sets the ImageProcessor, Roi, AWT Image and stack image + arrays to null. Does nothing if the image is locked. */ + public synchronized void flush() { + notifyListeners(CLOSED); + if (locked || ignoreFlush) return; + ip = null; + if (roi!=null) roi.setImage(null); + roi = null; + if (stack!=null) { + Object[] arrays = stack.getImageArray(); + if (arrays!=null) { + for (int i=0; icut is true. */ + public void copy(boolean cut) { + Roi roi = getRoi(); + if (roi!=null && !roi.isArea()) { + IJ.error("Cut/Copy", "The Cut and Copy commands require\n" + +"an area selection, or no selection."); + return; + } + boolean batchMode = Interpreter.isBatchMode(); + String msg = (cut)?"Cut":"Copy"; + if (!batchMode) IJ.showStatus(msg+ "ing..."); + ImageProcessor ip = getProcessor(); + ImageProcessor ip2; + Roi roi2 = null; + ip2 = ip.crop(); + if (roi!=null && roi.getType()!=Roi.RECTANGLE) { + roi2 = (Roi)roi.clone(); + Rectangle r = roi.getBounds(); + if (r.x<0 || r.y<0 || r.x+r.width>width || r.y+r.height>height) { + roi2 = new ShapeRoi(roi2); + ShapeRoi image = new ShapeRoi(new Roi(0, 0, width, height)); + roi2 = image.and((ShapeRoi)roi2); + } + } + clipboard = new ImagePlus("Clipboard", ip2); + if (roi2!=null) clipboard.setRoi(roi2); + if (cut) { + ip.snapshot(); + ip.setColor(Toolbar.getBackgroundColor()); + ip.fill(); + if (roi!=null && roi.getType()!=Roi.RECTANGLE) { + getMask(); + ip.reset(ip.getMask()); + } setColor(Toolbar.getForegroundColor()); + Undo.setup(Undo.FILTER, this); + updateAndDraw(); + } + int bytesPerPixel = 1; + switch (clipboard.getType()) { + case ImagePlus.GRAY16: bytesPerPixel = 2; break; + case ImagePlus.GRAY32: case ImagePlus.COLOR_RGB: bytesPerPixel = 4; + } + //Roi roi3 = clipboard.getRoi(); + //IJ.log("copy: "+clipboard +" "+ "roi3="+(roi3!=null?""+roi3:"")); + if (!batchMode) + IJ.showStatus(msg + ": " + (clipboard.getWidth()*clipboard.getHeight()*bytesPerPixel)/1024 + "k"); + } + + + /** Inserts the contents of the internal clipboard into the active image. If there + is a selection the same size as the image on the clipboard, the image is inserted + into that selection, otherwise the selection is inserted into the center of the image.*/ + public void paste() { + if (clipboard==null) return; + int cType = clipboard.getType(); + int iType = getType(); + + int w = clipboard.getWidth(); + int h = clipboard.getHeight(); + Roi cRoi = clipboard.getRoi(); + Rectangle r = null; + Roi roi = getRoi(); + if (roi!=null) + r = roi.getBounds(); + if (w==width && h==height && (r==null||w!=r.width||h!=r.height)) { + setRoi(0, 0, width, height); + roi = getRoi(); + r = roi.getBounds(); + } + if (r==null || (r!=null && (w!=r.width || h!=r.height))) { + // create a new roi centered on visible part of image + ImageCanvas ic = null; + if (win!=null) + ic = win.getCanvas(); + Rectangle srcRect = ic!=null?ic.getSrcRect():new Rectangle(0,0,width, height); + int xCenter = srcRect.x + srcRect.width/2; + int yCenter = srcRect.y + srcRect.height/2; + if (cRoi!=null && cRoi.getType()!=Roi.RECTANGLE) { + cRoi.setImage(this); + cRoi.setLocation(xCenter-w/2, yCenter-h/2); + setRoi(cRoi); + } else + setRoi(xCenter-w/2, yCenter-h/2, w, h); + roi = getRoi(); + } + if (IJ.isMacro()) { + //non-interactive paste + int pasteMode = Roi.getCurrentPasteMode(); + boolean nonRect = roi.getType()!=Roi.RECTANGLE; + ImageProcessor ip = getProcessor(); + if (nonRect) ip.snapshot(); + r = roi.getBounds(); + ip.copyBits(clipboard.getProcessor(), r.x, r.y, pasteMode); + if (nonRect) ip.reset(getMask()); + updateAndDraw(); + //killRoi(); + } else if (roi!=null) { + roi.startPaste(clipboard); + Undo.setup(Undo.PASTE, this); + } + changes = true; + } + + /** Returns the internal clipboard or null if the internal clipboard is empty. */ + public static ImagePlus getClipboard() { + return clipboard; + } + + /** Clears the internal clipboard. */ + public static void resetClipboard() { + clipboard = null; + } + + protected void notifyListeners(int id) { + synchronized (listeners) { + for (int i=0; i1 && (this instanceof CompositeImage); + } + + /** Sets the display range of the current channel. With non-composite + images it is identical to ip.setMinAndMax(min, max). */ + public void setDisplayRange(double min, double max) { + if (ip!=null) + ip.setMinAndMax(min, max); + } + + public double getDisplayRangeMin() { + return ip.getMin(); + } + + public double getDisplayRangeMax() { + return ip.getMax(); + } + + /** Sets the display range of specified channels in an RGB image, where 4=red, + 2=green, 1=blue, 6=red+green, etc. With non-RGB images, this method is + identical to setDisplayRange(min, max). This method is used by the + Image/Adjust/Color Balance tool . */ + public void setDisplayRange(double min, double max, int channels) { + if (ip instanceof ColorProcessor) + ((ColorProcessor)ip).setMinAndMax(min, max, channels); + else + ip.setMinAndMax(min, max); + } + + public void resetDisplayRange() { + ip.resetMinAndMax(); + } + + public void updatePosition(int c, int z, int t) { + //IJ.log("updatePosition: "+c+", "+z+", "+t); + position[0] = c; + position[1] = z; + position[2] = t; + } + + public Object clone() { + try {return super.clone();} + catch (CloneNotSupportedException e) {return null;} + } + + public String toString() { + return "imp["+getTitle()+" "+width+"x"+height+"x"+getStackSize()+"]"; + } + +} diff --git a/ij/ImageStack.java b/ij/ImageStack.java index 58e836d87..1759b21ab 100644 --- a/ij/ImageStack.java +++ b/ij/ImageStack.java @@ -1,304 +1,304 @@ -package ij; -import java.awt.*; -import java.awt.image.*; -import ij.process.*; - -/** -This class represents an expandable array of images. -@see ImagePlus -*/ - -public class ImageStack { - - static final int INITIAL_SIZE = 25; - static final String outOfRange = "Argument out of range: "; - private int nSlices = 0; - private Object[] stack; - private String[] label; - private int width, height; - private Rectangle roi; - private ColorModel cm; - private double min=Double.MAX_VALUE; - private double max; - private float[] cTable; - - /** Default constructor. */ - public ImageStack() { } - - /** Creates a new, empty image stack. */ - public ImageStack(int width, int height) { - this(width, height, null); - } - - /** Creates a new image stack with a capacity of 'size'. */ - public ImageStack(int width, int height, int size) { - this.width = width; - this.height = height; - stack = new Object[size]; - label = new String[size]; - nSlices = size; - } - - /** Creates a new, empty image stack. */ - public ImageStack(int width, int height, ColorModel cm) { - this.width = width; - this.height = height; - this.cm = cm; - stack = new Object[INITIAL_SIZE]; - label = new String[INITIAL_SIZE]; - nSlices = 0; - } - - /** Adds an image in the forma of a pixel array to the end of the stack. */ - public void addSlice(String sliceLabel, Object pixels) { - if (pixels==null) - throw new IllegalArgumentException("'pixels' is null!"); - if (!pixels.getClass().isArray()) - throw new IllegalArgumentException("'pixels' is not an array"); - nSlices++; - if (nSlices==stack.length) { - Object[] tmp1 = new Object[nSlices*2]; - System.arraycopy(stack, 0, tmp1, 0, nSlices); - stack = tmp1; - String[] tmp2 = new String[nSlices*2]; - System.arraycopy(label, 0, tmp2, 0, nSlices); - label = tmp2; - } - stack[nSlices-1] = pixels; - this.label[nSlices-1] = sliceLabel; - } - - /** Obsolete. Short images are always unsigned. */ - public void addUnsignedShortSlice(String sliceLabel, Object pixels) { - addSlice(sliceLabel, pixels); - } - - /** Adds the image in 'ip' to the end of the stack. */ - public void addSlice(String sliceLabel, ImageProcessor ip) { - if (ip.getWidth()!=width || ip.getHeight()!=height) - throw new IllegalArgumentException("Dimensions do not match"); - if (nSlices==0) { - cm = ip.getColorModel(); - min = ip.getMin(); - max = ip.getMax(); - } - addSlice(sliceLabel, ip.getPixels()); - } - - /** Adds the image in 'ip' to the stack following slice 'n'. Adds - the slice to the beginning of the stack if 'n' is zero. */ - public void addSlice(String sliceLabel, ImageProcessor ip, int n) { - if (n<0 || n>nSlices) - throw new IllegalArgumentException(outOfRange+n); - addSlice(sliceLabel, ip); - Object tempSlice = stack[nSlices-1]; - String tempLabel = label[nSlices-1]; - int first = n>0?n:1; - for (int i=nSlices-1; i>=first; i--) { - stack[i] = stack[i-1]; - label[i] = label[i-1]; - } - stack[n] = tempSlice; - label[n] = tempLabel; - } - - /** Deletes the specified slice, were 1<=n<=nslices. */ - public void deleteSlice(int n) { - if (n<1 || n>nSlices) - throw new IllegalArgumentException(outOfRange+n); - if (nSlices<1) - return; - for (int i=n; i0) - deleteSlice(nSlices); - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public void setRoi(Rectangle roi) { - this.roi = roi; - } - - public Rectangle getRoi() { - if (roi==null) - return new Rectangle(0, 0, width, height); - else - return(roi); - } - - /** Updates this stack so its attributes, such as min, max, - calibration table and color model, are the same as 'ip'. */ - public void update(ImageProcessor ip) { - if (ip!=null) { - min = ip.getMin(); - max = ip.getMax(); - cTable = ip.getCalibrationTable(); - cm = ip.getColorModel(); - } - } - - /** Returns the pixel array for the specified slice, were 1<=n<=nslices. */ - public Object getPixels(int n) { - if (n<1 || n>nSlices) - throw new IllegalArgumentException(outOfRange+n); - return stack[n-1]; - } - - /** Assigns a pixel array to the specified slice, - were 1<=n<=nslices. */ - public void setPixels(Object pixels, int n) { - if (n<1 || n>nSlices) - throw new IllegalArgumentException(outOfRange+n); - stack[n-1] = pixels; - } - - /** Returns the stack as an array of 1D pixel arrays. Note - that the size of the returned array may be greater than - the number of slices currently in the stack, with - unused elements set to null. */ - public Object[] getImageArray() { - return stack; - } - - /** Returns the number of slices in this stack. */ - public int getSize() { - return nSlices; - } - - /** Returns the slice labels as an array of Strings. Note - that the size of the returned array may be greater than - the number of slices currently in the stack. Returns null - if the stack is empty or the label of the first slice is null. */ - public String[] getSliceLabels() { - if (nSlices==0) - return null; - else - return label; - } - - /** Returns the label of the specified slice, were 1<=n<=nslices. - Returns null if the slice does not have a label. For DICOM - and FITS stacks, labels may contain header information. */ - public String getSliceLabel(int n) { - if (n<1 || n>nSlices) - throw new IllegalArgumentException(outOfRange+n); - return label[n-1]; - } - - /** Returns a shortened version (up to the first 60 characters or first newline and - suffix removed) of the label of the specified slice. - Returns null if the slice does not have a label. */ - public String getShortSliceLabel(int n) { - String shortLabel = getSliceLabel(n); - if (shortLabel==null) return null; - int newline = shortLabel.indexOf('\n'); - if (newline==0) return null; - if (newline>0) - shortLabel = shortLabel.substring(0, newline); - int len = shortLabel.length(); - if (len>4 && shortLabel.charAt(len-4)=='.' && !Character.isDigit(shortLabel.charAt(len-1))) - shortLabel = shortLabel.substring(0,len-4); - if (shortLabel.length()>60) - shortLabel = shortLabel.substring(0, 60); - return shortLabel; - } - - /** Sets the label of the specified slice, were 1<=n<=nslices. */ - public void setSliceLabel(String label, int n) { - if (n<1 || n>nSlices) - throw new IllegalArgumentException(outOfRange+n); - this.label[n-1] = label; - } - - /** Returns an ImageProcessor for the specified slice, - were 1<=n<=nslices. Returns null if the stack is empty. - */ - public ImageProcessor getProcessor(int n) { - ImageProcessor ip; - if (n<1 || n>nSlices) - throw new IllegalArgumentException(outOfRange+n); - if (nSlices==0) - return null; - if (stack[0]==null) - throw new IllegalArgumentException("Pixel array is null"); - if (stack[0] instanceof byte[]) - ip = new ByteProcessor(width, height, null, cm); - else if (stack[0] instanceof short[]) - ip = new ShortProcessor(width, height, null, cm); - else if (stack[0] instanceof int[]) - ip = new ColorProcessor(width, height, null); - else if (stack[0] instanceof float[]) - ip = new FloatProcessor(width, height, null, cm); - else - throw new IllegalArgumentException("Unknown stack type"); - ip.setPixels(stack[n-1]); - if (min!=Double.MAX_VALUE && ip!=null && !(ip instanceof ColorProcessor)) - ip.setMinAndMax(min, max); - if (cTable!=null) - ip.setCalibrationTable(cTable); - return ip; - } - - /** Assigns a new color model to this stack. */ - public void setColorModel(ColorModel cm) { - this.cm = cm; - } - - /** Returns this stack's color model. May return null. */ - public ColorModel getColorModel() { - return cm; - } - - /** Returns true if this is a 3-slice RGB stack. */ - public boolean isRGB() { - if (nSlices==3 && (stack[0] instanceof byte[]) && getSliceLabel(1)!=null && getSliceLabel(1).equals("Red")) - return true; - else - return false; - } - - /** Returns true if this is a 3-slice HSB stack. */ - public boolean isHSB() { - if (nSlices==3 && getSliceLabel(1)!=null && getSliceLabel(1).equals("Hue")) - return true; - else - return false; - } - - /** Returns true if this is a virtual (disk resident) stack. - This method is overridden by the VirtualStack subclass. */ - public boolean isVirtual() { - return false; - } - - /** Frees memory by deleting a few slices from the end of the stack. */ - public void trim() { - int n = (int)Math.round(Math.log(nSlices)+1.0); - for (int i=0; inSlices) + throw new IllegalArgumentException(outOfRange+n); + addSlice(sliceLabel, ip); + Object tempSlice = stack[nSlices-1]; + String tempLabel = label[nSlices-1]; + int first = n>0?n:1; + for (int i=nSlices-1; i>=first; i--) { + stack[i] = stack[i-1]; + label[i] = label[i-1]; + } + stack[n] = tempSlice; + label[n] = tempLabel; + } + + /** Deletes the specified slice, were 1<=n<=nslices. */ + public void deleteSlice(int n) { + if (n<1 || n>nSlices) + throw new IllegalArgumentException(outOfRange+n); + if (nSlices<1) + return; + for (int i=n; i0) + deleteSlice(nSlices); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public void setRoi(Rectangle roi) { + this.roi = roi; + } + + public Rectangle getRoi() { + if (roi==null) + return new Rectangle(0, 0, width, height); + else + return(roi); + } + + /** Updates this stack so its attributes, such as min, max, + calibration table and color model, are the same as 'ip'. */ + public void update(ImageProcessor ip) { + if (ip!=null) { + min = ip.getMin(); + max = ip.getMax(); + cTable = ip.getCalibrationTable(); + cm = ip.getColorModel(); + } + } + + /** Returns the pixel array for the specified slice, were 1<=n<=nslices. */ + public Object getPixels(int n) { + if (n<1 || n>nSlices) + throw new IllegalArgumentException(outOfRange+n); + return stack[n-1]; + } + + /** Assigns a pixel array to the specified slice, + were 1<=n<=nslices. */ + public void setPixels(Object pixels, int n) { + if (n<1 || n>nSlices) + throw new IllegalArgumentException(outOfRange+n); + stack[n-1] = pixels; + } + + /** Returns the stack as an array of 1D pixel arrays. Note + that the size of the returned array may be greater than + the number of slices currently in the stack, with + unused elements set to null. */ + public Object[] getImageArray() { + return stack; + } + + /** Returns the number of slices in this stack. */ + public int getSize() { + return nSlices; + } + + /** Returns the slice labels as an array of Strings. Note + that the size of the returned array may be greater than + the number of slices currently in the stack. Returns null + if the stack is empty or the label of the first slice is null. */ + public String[] getSliceLabels() { + if (nSlices==0) + return null; + else + return label; + } + + /** Returns the label of the specified slice, were 1<=n<=nslices. + Returns null if the slice does not have a label. For DICOM + and FITS stacks, labels may contain header information. */ + public String getSliceLabel(int n) { + if (n<1 || n>nSlices) + throw new IllegalArgumentException(outOfRange+n); + return label[n-1]; + } + + /** Returns a shortened version (up to the first 60 characters or first newline and + suffix removed) of the label of the specified slice. + Returns null if the slice does not have a label. */ + public String getShortSliceLabel(int n) { + String shortLabel = getSliceLabel(n); + if (shortLabel==null) return null; + int newline = shortLabel.indexOf('\n'); + if (newline==0) return null; + if (newline>0) + shortLabel = shortLabel.substring(0, newline); + int len = shortLabel.length(); + if (len>4 && shortLabel.charAt(len-4)=='.' && !Character.isDigit(shortLabel.charAt(len-1))) + shortLabel = shortLabel.substring(0,len-4); + if (shortLabel.length()>60) + shortLabel = shortLabel.substring(0, 60); + return shortLabel; + } + + /** Sets the label of the specified slice, were 1<=n<=nslices. */ + public void setSliceLabel(String label, int n) { + if (n<1 || n>nSlices) + throw new IllegalArgumentException(outOfRange+n); + this.label[n-1] = label; + } + + /** Returns an ImageProcessor for the specified slice, + were 1<=n<=nslices. Returns null if the stack is empty. + */ + public ImageProcessor getProcessor(int n) { + ImageProcessor ip; + if (n<1 || n>nSlices) + throw new IllegalArgumentException(outOfRange+n); + if (nSlices==0) + return null; + if (stack[0]==null) + throw new IllegalArgumentException("Pixel array is null"); + if (stack[0] instanceof byte[]) + ip = new ByteProcessor(width, height, null, cm); + else if (stack[0] instanceof short[]) + ip = new ShortProcessor(width, height, null, cm); + else if (stack[0] instanceof int[]) + ip = new ColorProcessor(width, height, null); + else if (stack[0] instanceof float[]) + ip = new FloatProcessor(width, height, null, cm); + else + throw new IllegalArgumentException("Unknown stack type"); + ip.setPixels(stack[n-1]); + if (min!=Double.MAX_VALUE && ip!=null && !(ip instanceof ColorProcessor)) + ip.setMinAndMax(min, max); + if (cTable!=null) + ip.setCalibrationTable(cTable); + return ip; + } + + /** Assigns a new color model to this stack. */ + public void setColorModel(ColorModel cm) { + this.cm = cm; + } + + /** Returns this stack's color model. May return null. */ + public ColorModel getColorModel() { + return cm; + } + + /** Returns true if this is a 3-slice RGB stack. */ + public boolean isRGB() { + if (nSlices==3 && (stack[0] instanceof byte[]) && getSliceLabel(1)!=null && getSliceLabel(1).equals("Red")) + return true; + else + return false; + } + + /** Returns true if this is a 3-slice HSB stack. */ + public boolean isHSB() { + if (nSlices==3 && getSliceLabel(1)!=null && getSliceLabel(1).equals("Hue")) + return true; + else + return false; + } + + /** Returns true if this is a virtual (disk resident) stack. + This method is overridden by the VirtualStack subclass. */ + public boolean isVirtual() { + return false; + } + + /** Frees memory by deleting a few slices from the end of the stack. */ + public void trim() { + int n = (int)Math.round(Math.log(nSlices)+1.0); + for (int i=0; itrue if this is a 256 entry grayscale LUT. - @see ij.process.ImageProcessor#isColorLut - */ - public boolean isGrayscale() { - boolean isGray = true; - - if (mapSize < 256) - return false; - for (int i=0; itrue if this is a 256 entry grayscale LUT. + @see ij.process.ImageProcessor#isColorLut + */ + public boolean isGrayscale() { + boolean isGray = true; + + if (mapSize < 256) + return false; + for (int i=0; i1) - return fs.saveAsTiffStack(path); - else - return fs.saveAsTiff(path); - } - - public static String getName(String path) { - int i = path.lastIndexOf('/'); - if (i==-1) - i = path.lastIndexOf('\\'); - if (i>0) - return path.substring(i+1); - else - return path; - } - - public static String getDir(String path) { - int i = path.lastIndexOf('/'); - if (i==-1) - i = path.lastIndexOf('\\'); - if (i>0) - return path.substring(0, i+1); - else - return ""; - } - - /** Aborts the currently running macro or any plugin using IJ.run(). */ - public static void abort() { - abort = true; - //IJ.log("Abort: "+Thread.currentThread().getName()); - if (Thread.currentThread().getName().endsWith("Macro$")) { - table.remove(Thread.currentThread()); - throw new RuntimeException(MACRO_CANCELED); - } - } - - /** If a command started using run(name, options) is running, - and the current thread is the same thread, - returns the options string, otherwise, returns null. - @see ij.gui.GenericDialog - @see ij.io.OpenDialog - */ - public static String getOptions() { - //IJ.log("getOptions: "+Thread.currentThread().hashCode()); //ts - if (Thread.currentThread().getName().startsWith("Run$_")) { - Object options = table.get(Thread.currentThread()); - return options==null?null:options+" "; - } else - return null; - } - - /** Define a set of Macro options for the current Thread. */ - public static void setOptions(String options) { - //IJ.log("setOptions: "+Thread.currentThread().hashCode()+" "+options); //ts - if (options==null || options.equals("")) - table.remove(Thread.currentThread()); - else - table.put(Thread.currentThread(), options); - } - - /** Define a set of Macro options for a Thread. */ - public static void setOptions(Thread thread, String options) { - if (null==thread) - throw new RuntimeException("Need a non-null thread instance"); - if (null==options) - table.remove(thread); - else - table.put(thread, options); - } - - public static String getValue(String options, String key, String defaultValue) { - key = trimKey(key); - key += '='; - int index=-1; - do { // Require that key not be preceded by a letter - index = options.indexOf(key, ++index); - if (index<0) return defaultValue; - } while (index!=0&&Character.isLetter(options.charAt(index-1))); - options = options.substring(index+key.length(), options.length()); - if (options.charAt(0)=='\'') { - index = options.indexOf("'",1); - if (index<0) - return defaultValue; - else - return options.substring(1, index); - } else if (options.charAt(0)=='[') { - index = options.indexOf("]",1); - if (index<0) - return defaultValue; - else - return options.substring(1, index); - } else { - //if (options.indexOf('=')==-1) { - // options = options.trim(); - // IJ.log("getValue: "+key+" |"+options+"|"); - // if (options.length()>0) - // return options; - // else - // return defaultValue; - //} - index = options.indexOf(" "); - if (index<0) - return defaultValue; - else - return options.substring(0, index); - } - } - - public static String trimKey(String key) { - int index = key.indexOf(" "); - if (index>-1) - key = key.substring(0,index); - index = key.indexOf(":"); - if (index>-1) - key = key.substring(0,index); - key = key.toLowerCase(Locale.US); - return key; - } - -} - +package ij; +import ij.process.*; +import ij.gui.*; +import ij.io.*; +import ij.measure.*; +import ij.plugin.filter.*; +import ij.macro.Interpreter; +import java.awt.*; +import java.awt.image.*; +import java.io.*; +import java.util.Locale; +import java.util.Hashtable; + +/** The class contains static methods that perform macro operations. */ +public class Macro { + + public static final String MACRO_CANCELED = "Macro canceled"; + + // A table of Thread as keys and String as values, so + // Macro options are local to each calling thread. + static private Hashtable table = new Hashtable(); + static boolean abort; + + public static boolean open(String path) { + if (path==null || path.equals("")) { + Opener o = new Opener(); + return true; + } + Opener o = new Opener(); + ImagePlus img = o.openImage(path); + if (img==null) + return false; + img.show(); + return true; + } + + public static boolean saveAs(String path) { + ImagePlus imp = WindowManager.getCurrentImage(); + if (imp==null) + return false; + FileSaver fs = new FileSaver(imp); + if (path==null || path.equals("")) + return fs.saveAsTiff(); + if (imp.getStackSize()>1) + return fs.saveAsTiffStack(path); + else + return fs.saveAsTiff(path); + } + + public static String getName(String path) { + int i = path.lastIndexOf('/'); + if (i==-1) + i = path.lastIndexOf('\\'); + if (i>0) + return path.substring(i+1); + else + return path; + } + + public static String getDir(String path) { + int i = path.lastIndexOf('/'); + if (i==-1) + i = path.lastIndexOf('\\'); + if (i>0) + return path.substring(0, i+1); + else + return ""; + } + + /** Aborts the currently running macro or any plugin using IJ.run(). */ + public static void abort() { + abort = true; + //IJ.log("Abort: "+Thread.currentThread().getName()); + if (Thread.currentThread().getName().endsWith("Macro$")) { + table.remove(Thread.currentThread()); + throw new RuntimeException(MACRO_CANCELED); + } + } + + /** If a command started using run(name, options) is running, + and the current thread is the same thread, + returns the options string, otherwise, returns null. + @see ij.gui.GenericDialog + @see ij.io.OpenDialog + */ + public static String getOptions() { + //IJ.log("getOptions: "+Thread.currentThread().hashCode()); //ts + if (Thread.currentThread().getName().startsWith("Run$_")) { + Object options = table.get(Thread.currentThread()); + return options==null?null:options+" "; + } else + return null; + } + + /** Define a set of Macro options for the current Thread. */ + public static void setOptions(String options) { + //IJ.log("setOptions: "+Thread.currentThread().hashCode()+" "+options); //ts + if (options==null || options.equals("")) + table.remove(Thread.currentThread()); + else + table.put(Thread.currentThread(), options); + } + + /** Define a set of Macro options for a Thread. */ + public static void setOptions(Thread thread, String options) { + if (null==thread) + throw new RuntimeException("Need a non-null thread instance"); + if (null==options) + table.remove(thread); + else + table.put(thread, options); + } + + public static String getValue(String options, String key, String defaultValue) { + key = trimKey(key); + key += '='; + int index=-1; + do { // Require that key not be preceded by a letter + index = options.indexOf(key, ++index); + if (index<0) return defaultValue; + } while (index!=0&&Character.isLetter(options.charAt(index-1))); + options = options.substring(index+key.length(), options.length()); + if (options.charAt(0)=='\'') { + index = options.indexOf("'",1); + if (index<0) + return defaultValue; + else + return options.substring(1, index); + } else if (options.charAt(0)=='[') { + index = options.indexOf("]",1); + if (index<0) + return defaultValue; + else + return options.substring(1, index); + } else { + //if (options.indexOf('=')==-1) { + // options = options.trim(); + // IJ.log("getValue: "+key+" |"+options+"|"); + // if (options.length()>0) + // return options; + // else + // return defaultValue; + //} + index = options.indexOf(" "); + if (index<0) + return defaultValue; + else + return options.substring(0, index); + } + } + + public static String trimKey(String key) { + int index = key.indexOf(" "); + if (index>-1) + key = key.substring(0,index); + index = key.indexOf(":"); + if (index>-1) + key = key.substring(0,index); + key = key.toLowerCase(Locale.US); + return key; + } + +} + diff --git a/ij/Prefs.java b/ij/Prefs.java index 4ef4d8abb..3877fa705 100644 --- a/ij/Prefs.java +++ b/ij/Prefs.java @@ -1,538 +1,538 @@ -package ij; -import ij.util.Java2; -import java.io.*; -import java.util.*; -import java.applet.*; -import java.net.URL; -import java.awt.*; -import java.applet.Applet; -import ij.io.*; -import ij.util.Tools; -import ij.gui.*; -import ij.plugin.filter.*; -import ij.process.ImageConverter; -import ij.plugin.Animator; -import ij.process.FloatBlitter; -import ij.plugin.GelAnalyzer; -import ij.process.ColorProcessor; -import ij.text.TextWindow; - -/** -This class contains the ImageJ preferences, which are -loaded from the "IJ_Props.txt" and "IJ_Prefs.txt" files. -@see ij.ImageJ -*/ -public class Prefs { - - public static final String PROPS_NAME = "IJ_Props.txt"; - public static final String PREFS_NAME = "IJ_Prefs.txt"; - public static final String DIR_IMAGE = "dir.image"; - public static final String FCOLOR = "fcolor"; - public static final String BCOLOR = "bcolor"; - public static final String ROICOLOR = "roicolor"; - public static final String SHOW_ALL_COLOR = "showcolor"; - public static final String JPEG = "jpeg"; - public static final String FPS = "fps"; - public static final String DIV_BY_ZERO_VALUE = "div-by-zero"; - public static final String NOISE_SD = "noise.sd"; - public static final String MENU_SIZE = "menu.size"; - public static final String THREADS = "threads"; - public static final String KEY_PREFIX = "."; - - private static final int USE_POINTER=1<<0, ANTIALIASING=1<<1, INTERPOLATE=1<<2, ONE_HUNDRED_PERCENT=1<<3, - BLACK_BACKGROUND=1<<4, JFILE_CHOOSER=1<<5, UNUSED=1<<6, BLACK_CANVAS=1<<7, WEIGHTED=1<<8, - AUTO_MEASURE=1<<9, REQUIRE_CONTROL=1<<10, USE_INVERTING_LUT=1<<11, ANTIALIASED_TOOLS=1<<12, - INTEL_BYTE_ORDER=1<<13, DOUBLE_BUFFER=1<<14, NO_POINT_LABELS=1<<15, NO_BORDER=1<<16, - SHOW_ALL_SLICE_ONLY=1<<17, COPY_HEADERS=1<<18, NO_ROW_NUMBERS=1<<19, - MOVE_TO_MISC=1<<20, ADD_TO_MANAGER=1<<21, RUN_SOCKET_LISTENER=1<<22; - public static final String OPTIONS = "prefs.options"; - - public static final String vistaHint = "\n \nOn Windows Vista, ImageJ must be installed in a directory that\nthe user can write to, such as \"Desktop\" or \"Documents\""; - - /** file.separator system property */ - public static String separator = System.getProperty("file.separator"); - /** Use pointer cursor instead of cross */ - public static boolean usePointerCursor; - /** No longer used */ - public static boolean antialiasedText; - /** Display images scaled <100% using bilinear interpolation */ - public static boolean interpolateScaledImages; - /** Open images at 100% magnification*/ - public static boolean open100Percent; - /** Backgound is black in binary images*/ - public static boolean blackBackground; - /** Use JFileChooser instead of FileDialog to open and save files. */ - public static boolean useJFileChooser; - /** Color to grayscale conversion is weighted (0.299, 0.587, 0.114) if the variable is true. */ - public static boolean weightedColor; - /** Use black image border. */ - public static boolean blackCanvas; - /** Point tool auto-measure mode. */ - public static boolean pointAutoMeasure; - /** Point tool auto-next slice mode (not saved in IJ_Prefs). */ - public static boolean pointAutoNextSlice; - /** Require control or command key for keybaord shortcuts. */ - public static boolean requireControlKey; - /** Open 8-bit images with inverting LUT so 0 is white and 255 is black. */ - public static boolean useInvertingLut; - /** Draw tool icons using antialiasing. */ - public static boolean antialiasedTools; - /** Export Raw using little-endian byte order. */ - public static boolean intelByteOrder; - /** Double buffer display of selections and overlays. */ - public static boolean doubleBuffer = true; - /** Do not label multiple points created using point tool. */ - public static boolean noPointLabels = true; - /** Disable Edit/Undo command. */ - public static boolean disableUndo; - /** Do not draw black border around image. */ - public static boolean noBorder; - /** Only show ROIs associated with current slice in Roi Manager "Show All" mode. */ - public static boolean showAllSliceOnly; - /** Include column headers when copying tables to clipboard. */ - public static boolean copyColumnHeaders; - /** Do not include row numbers when copying tables to clipboard. */ - public static boolean noRowNumbers; - /** Move isolated plugins to Miscellaneous submenu. */ - public static boolean moveToMisc; - /** Add points to ROI Manager. */ - public static boolean pointAddToManager; - /** Extend the borders to foreground for binary erosions and closings. */ - public static boolean padEdges; - /** Run the SocketListener. */ - public static boolean runSocketListener; - - static Properties ijPrefs = new Properties(); - static Properties props = new Properties(ijPrefs); - static String prefsDir; - static String imagesURL; - static String homeDir; // ImageJ folder - static int threads; - static int transparentIndex = -1; - static boolean commandLineMacro; - - /** Finds and loads the ImageJ configuration file, "IJ_Props.txt". - @return an error message if "IJ_Props.txt" not found. - */ - public static String load(Object ij, Applet applet) { - InputStream f = ij.getClass().getResourceAsStream("/"+PROPS_NAME); - if (applet!=null) - return loadAppletProps(f, applet); - if (homeDir==null) - homeDir = System.getProperty("user.dir"); - String userHome = System.getProperty("user.home"); - if (IJ.isWindows()) { - prefsDir = homeDir; //ImageJ folder on Windows - if (prefsDir.endsWith("Desktop")) - prefsDir = userHome; - } else { - prefsDir = userHome; // Mac Preferences folder or Unix home dir - if (IJ.isMacOSX()) - prefsDir += "/Library/Preferences"; - else - prefsDir += "/.imagej"; - } - if (f==null) { - try {f = new FileInputStream(homeDir+"/"+PROPS_NAME);} - catch (FileNotFoundException e) {f=null;} - } - if (f==null) - return PROPS_NAME+" not found in ij.jar or in "+homeDir; - f = new BufferedInputStream(f); - try {props.load(f); f.close();} - catch (IOException e) {return("Error loading "+PROPS_NAME);} - imagesURL = props.getProperty("images.location"); - loadPreferences(); - loadOptions(); - return null; - } - - /* - static void dumpPrefs(String title) { - IJ.log(""); - IJ.log(title); - Enumeration e = ijPrefs.keys(); - while (e.hasMoreElements()) { - String key = (String) e.nextElement(); - IJ.log(key+": "+ijPrefs.getProperty(key)); - } - } - */ - - static String loadAppletProps(InputStream f, Applet applet) { - if (f==null) - return PROPS_NAME+" not found in ij.jar"; - try { - props.load(f); - f.close(); - } - catch (IOException e) {return("Error loading "+PROPS_NAME);} - try { - URL url = new URL(applet.getDocumentBase(), "images/"); - imagesURL = url.toString(); - } - catch (Exception e) {} - return null; - } - - /** Returns the URL of the directory that contains the ImageJ sample images. */ - public static String getImagesURL() { - return imagesURL; - } - - /** Sets the URL of the directory that contains the ImageJ sample images. */ - public static void setImagesURL(String url) { - imagesURL = url; - } - - /** Returns the path to the ImageJ directory. */ - public static String getHomeDir() { - return homeDir; - } - - /** Gets the path to the directory where the - preferences file (IJPrefs.txt) is saved. */ - public static String getPrefsDir() { - return prefsDir; - } - - /** Sets the path to the ImageJ directory. */ - static void setHomeDir(String path) { - if (path.endsWith(File.separator)) - path = path.substring(0, path.length()-1); - homeDir = path; - } - - /** Returns the default directory, if any, or null. */ - public static String getDefaultDirectory() { - if (commandLineMacro) - return null; - else - return getString(DIR_IMAGE); - } - - /** Finds an string in IJ_Props or IJ_Prefs.txt. */ - public static String getString(String key) { - return props.getProperty(key); - } - - /** Finds an string in IJ_Props or IJ_Prefs.txt. */ - public static String getString(String key, String defaultString) { - if (props==null) - return defaultString; - String s = props.getProperty(key); - if (s==null) - return defaultString; - else - return s; - } - - /** Finds a boolean in IJ_Props or IJ_Prefs.txt. */ - public static boolean getBoolean(String key, boolean defaultValue) { - if (props==null) return defaultValue; - String s = props.getProperty(key); - if (s==null) - return defaultValue; - else - return s.equals("true"); - } - - /** Finds an int in IJ_Props or IJ_Prefs.txt. */ - public static int getInt(String key, int defaultValue) { - if (props==null) //workaround for Netscape JIT bug - return defaultValue; - String s = props.getProperty(key); - if (s!=null) { - try { - return Integer.decode(s).intValue(); - } catch (NumberFormatException e) {IJ.write(""+e);} - } - return defaultValue; - } - - /** Looks up a real number in IJ_Props or IJ_Prefs.txt. */ - public static double getDouble(String key, double defaultValue) { - if (props==null) - return defaultValue; - String s = props.getProperty(key); - Double d = null; - if (s!=null) { - try {d = new Double(s);} - catch (NumberFormatException e){d = null;} - if (d!=null) - return(d.doubleValue()); - } - return defaultValue; - } - - /** Finds a color in IJ_Props or IJ_Prefs.txt. */ - public static Color getColor(String key, Color defaultColor) { - int i = getInt(key, 0xaaa); - if (i == 0xaaa) - return defaultColor; - return new Color((i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF); - } - - /** Returns the file.separator system property. */ - public static String getFileSeparator() { - return separator; - } - - /** Opens the IJ_Prefs.txt file. */ - static void loadPreferences() { - String path = prefsDir+separator+PREFS_NAME; - boolean ok = loadPrefs(path); - if (!ok && !IJ.isWindows()) { - path = System.getProperty("user.home")+separator+PREFS_NAME; - ok = loadPrefs(path); // look in home dir - if (ok) new File(path).delete(); - } - - } - - static boolean loadPrefs(String path) { - try { - InputStream is = new BufferedInputStream(new FileInputStream(path)); - ijPrefs.load(is); - is.close(); - return true; - } catch (Exception e) { - return false; - } - } - - /** Saves user preferences in the IJ_Prefs.txt properties file. */ - public static void savePreferences() { - try { - Properties prefs = new Properties(); - String dir = OpenDialog.getDefaultDirectory(); - if (dir!=null) - prefs.put(DIR_IMAGE, dir); - prefs.put(ROICOLOR, Tools.c2hex(Roi.getColor())); - prefs.put(SHOW_ALL_COLOR, Tools.c2hex(ImageCanvas.getShowAllColor())); - prefs.put(FCOLOR, Tools.c2hex(Toolbar.getForegroundColor())); - prefs.put(BCOLOR, Tools.c2hex(Toolbar.getBackgroundColor())); - prefs.put(JPEG, Integer.toString(FileSaver.getJpegQuality())); - prefs.put(FPS, Double.toString(Animator.getFrameRate())); - prefs.put(DIV_BY_ZERO_VALUE, Double.toString(FloatBlitter.divideByZeroValue)); - prefs.put(NOISE_SD, Double.toString(Filters.getSD())); - if (threads>1) prefs.put(THREADS, Integer.toString(threads)); - if (IJ.isMacOSX()) useJFileChooser = false; - saveOptions(prefs); - savePluginPrefs(prefs); - IJ.getInstance().savePreferences(prefs); - Menus.savePreferences(prefs); - ParticleAnalyzer.savePreferences(prefs); - Analyzer.savePreferences(prefs); - ImportDialog.savePreferences(prefs); - PlotWindow.savePreferences(prefs); - GelAnalyzer.savePreferences(prefs); - NewImage.savePreferences(prefs); - String path = prefsDir+separator+PREFS_NAME; - if (prefsDir.endsWith(".imagej")) { - File f = new File(prefsDir); - if (!f.exists()) f.mkdir(); // create .imagej directory - } - savePrefs(prefs, path); - } catch (Throwable t) { - String msg = t.getMessage(); - if (msg==null) msg = ""+t; - int delay = 4000; - if (IJ.isVista()) { - msg += vistaHint; - delay = 8000; - } - try { - new TextWindow("Error Saving Preferences", msg, 500, 200); - IJ.wait(delay); - } catch (Throwable t2) {} - } - } - - static void loadOptions() { - int options = getInt(OPTIONS, ANTIALIASING); - usePointerCursor = (options&USE_POINTER)!=0; - //antialiasedText = (options&ANTIALIASING)!=0; - antialiasedText = false; - interpolateScaledImages = (options&INTERPOLATE)!=0; - open100Percent = (options&ONE_HUNDRED_PERCENT)!=0; - open100Percent = (options&ONE_HUNDRED_PERCENT)!=0; - blackBackground = (options&BLACK_BACKGROUND)!=0; - useJFileChooser = (options&JFILE_CHOOSER)!=0; - weightedColor = (options&WEIGHTED)!=0; - if (weightedColor) - ColorProcessor.setWeightingFactors(0.299, 0.587, 0.114); - blackCanvas = (options&BLACK_CANVAS)!=0; - pointAutoMeasure = (options&AUTO_MEASURE)!=0; - requireControlKey = (options&REQUIRE_CONTROL)!=0; - useInvertingLut = (options&USE_INVERTING_LUT)!=0; - antialiasedTools = (options&ANTIALIASED_TOOLS)!=0; - intelByteOrder = (options&INTEL_BYTE_ORDER)!=0; - // doubleBuffer = (options&DOUBLE_BUFFER)!=0; // always double buffer - noPointLabels = (options&NO_POINT_LABELS)!=0; - noBorder = (options&NO_BORDER)!=0; - showAllSliceOnly = (options&SHOW_ALL_SLICE_ONLY)!=0; - copyColumnHeaders = (options©_HEADERS)!=0; - noRowNumbers = (options&NO_ROW_NUMBERS)!=0; - moveToMisc = (options&MOVE_TO_MISC)!=0; - pointAddToManager = (options&ADD_TO_MANAGER)!=0; - runSocketListener = (options&RUN_SOCKET_LISTENER)!=0; - } - - static void saveOptions(Properties prefs) { - int options = (usePointerCursor?USE_POINTER:0) + (antialiasedText?ANTIALIASING:0) - + (interpolateScaledImages?INTERPOLATE:0) + (open100Percent?ONE_HUNDRED_PERCENT:0) - + (blackBackground?BLACK_BACKGROUND:0) + (useJFileChooser?JFILE_CHOOSER:0) - + (blackCanvas?BLACK_CANVAS:0) + (weightedColor?WEIGHTED:0) - + (pointAutoMeasure?AUTO_MEASURE:0) + (requireControlKey?REQUIRE_CONTROL:0) - + (useInvertingLut?USE_INVERTING_LUT:0) + (antialiasedTools?ANTIALIASED_TOOLS:0) - + (intelByteOrder?INTEL_BYTE_ORDER:0) + (doubleBuffer?DOUBLE_BUFFER:0) - + (noPointLabels?NO_POINT_LABELS:0) + (noBorder?NO_BORDER:0) - + (showAllSliceOnly?SHOW_ALL_SLICE_ONLY:0) + (copyColumnHeaders?COPY_HEADERS:0) - + (noRowNumbers?NO_ROW_NUMBERS:0) + (moveToMisc?MOVE_TO_MISC:0) - + (pointAddToManager?ADD_TO_MANAGER:0) + (runSocketListener?RUN_SOCKET_LISTENER:0); - prefs.put(OPTIONS, Integer.toString(options)); - } - - /** Saves the value of the string text in the preferences - file using the keyword key. This string can be - retrieved using the appropriate get() method. */ - public static void set(String key, String text) { - if (key.indexOf('.')<1) - throw new IllegalArgumentException("Key must have a prefix"); - ijPrefs.put(KEY_PREFIX+key, text); - } - - /** Saves value in the preferences file using - the keyword key. This value can be retrieved - using the appropriate getPref() method. */ - public static void set(String key, int value) { - set(key, Integer.toString(value)); - } - - /** Saves value in the preferences file using - the keyword key. This value can be retrieved - using the appropriate getPref() method. */ - public static void set(String key, double value) { - set(key, ""+value); - } - - /** Saves the boolean variable value in the preferences - file using the keyword key. This value can be retrieved - using the appropriate getPref() method. */ - public static void set(String key, boolean value) { - set(key, ""+value); - } - - /** Uses the keyword key to retrieve a string from the - preferences file. Returns defaultValue if the key - is not found. */ - public static String get(String key, String defaultValue) { - String value = ijPrefs.getProperty(KEY_PREFIX+key); - if (value == null) - return defaultValue; - else - return value; - } - - /** Uses the keyword key to retrieve a number from the - preferences file. Returns defaultValue if the key - is not found. */ - public static double get(String key, double defaultValue) { - String s = ijPrefs.getProperty(KEY_PREFIX+key); - Double d = null; - if (s!=null) { - try {d = new Double(s);} - catch (NumberFormatException e) {d = null;} - if (d!=null) - return(d.doubleValue()); - } - return defaultValue; - } - - /** Uses the keyword key to retrieve a boolean from - the preferences file. Returns defaultValue if - the key is not found. */ - public static boolean get(String key, boolean defaultValue) { - String value = ijPrefs.getProperty(KEY_PREFIX+key); - if (value==null) - return defaultValue; - else - return value.equals("true"); - } - - /** Saves the Point loc in the preferences - file as a string using the keyword key. */ - public static void saveLocation(String key, Point loc) { - set(key, loc.x+","+loc.y); - } - - /** Uses the keyword key to retrieve a location - from the preferences file. Returns null if the - key is not found or the location is not valid (e.g., offscreen). */ - public static Point getLocation(String key) { - String value = ijPrefs.getProperty(KEY_PREFIX+key); - if (value==null) return null; - int index = value.indexOf(","); - if (index==-1) return null; - double xloc = Tools.parseDouble(value.substring(0, index)); - if (Double.isNaN(xloc) || index==value.length()-1) return null; - double yloc = Tools.parseDouble(value.substring(index+1)); - if (Double.isNaN(yloc)) return null; - Point p = new Point((int)xloc, (int)yloc); - Dimension screen = IJ.getScreenSize(); - if (p.x>screen.width-100 || p.y>screen.height-40) - return null; - else - return p; - } - - /** Save plugin preferences. */ - static void savePluginPrefs(Properties prefs) { - Enumeration e = ijPrefs.keys(); - while (e.hasMoreElements()) { - String key = (String) e.nextElement(); - if (key.indexOf(KEY_PREFIX) == 0) - prefs.put(key, ijPrefs.getProperty(key)); - } - } - - public static void savePrefs(Properties prefs, String path) throws IOException{ - FileOutputStream fos = new FileOutputStream(path); - BufferedOutputStream bos = new BufferedOutputStream(fos); - prefs.store(bos, "ImageJ "+ImageJ.VERSION+" Preferences"); - bos.close(); - } - - /** Returns the number of threads used by PlugInFilters to process stacks. */ - public static int getThreads() { - if (threads==0) { - threads = getInt(THREADS, 0); - int processors = Runtime.getRuntime().availableProcessors(); - if (threads<1 || threads>processors) threads = processors; - } - return threads; - } - - /** Sets the number of threads (1-32) used by PlugInFilters to process stacks. */ - public static void setThreads(int n) { - if (n<1) n = 1; - if (n>32) n = 32; - threads = n; - } - - /** Sets the transparent index (0-255), or set to -1 to disable transparency. */ - public static void setTransparentIndex(int index) { - if (index<-1 || index>255) index = -1; - transparentIndex = index; - } - - /** Returns the transparent index (0-255), or -1 if transparency is disabled. */ - public static int getTransparentIndex() { - return transparentIndex; - } - - - -} - +package ij; +import ij.util.Java2; +import java.io.*; +import java.util.*; +import java.applet.*; +import java.net.URL; +import java.awt.*; +import java.applet.Applet; +import ij.io.*; +import ij.util.Tools; +import ij.gui.*; +import ij.plugin.filter.*; +import ij.process.ImageConverter; +import ij.plugin.Animator; +import ij.process.FloatBlitter; +import ij.plugin.GelAnalyzer; +import ij.process.ColorProcessor; +import ij.text.TextWindow; + +/** +This class contains the ImageJ preferences, which are +loaded from the "IJ_Props.txt" and "IJ_Prefs.txt" files. +@see ij.ImageJ +*/ +public class Prefs { + + public static final String PROPS_NAME = "IJ_Props.txt"; + public static final String PREFS_NAME = "IJ_Prefs.txt"; + public static final String DIR_IMAGE = "dir.image"; + public static final String FCOLOR = "fcolor"; + public static final String BCOLOR = "bcolor"; + public static final String ROICOLOR = "roicolor"; + public static final String SHOW_ALL_COLOR = "showcolor"; + public static final String JPEG = "jpeg"; + public static final String FPS = "fps"; + public static final String DIV_BY_ZERO_VALUE = "div-by-zero"; + public static final String NOISE_SD = "noise.sd"; + public static final String MENU_SIZE = "menu.size"; + public static final String THREADS = "threads"; + public static final String KEY_PREFIX = "."; + + private static final int USE_POINTER=1<<0, ANTIALIASING=1<<1, INTERPOLATE=1<<2, ONE_HUNDRED_PERCENT=1<<3, + BLACK_BACKGROUND=1<<4, JFILE_CHOOSER=1<<5, UNUSED=1<<6, BLACK_CANVAS=1<<7, WEIGHTED=1<<8, + AUTO_MEASURE=1<<9, REQUIRE_CONTROL=1<<10, USE_INVERTING_LUT=1<<11, ANTIALIASED_TOOLS=1<<12, + INTEL_BYTE_ORDER=1<<13, DOUBLE_BUFFER=1<<14, NO_POINT_LABELS=1<<15, NO_BORDER=1<<16, + SHOW_ALL_SLICE_ONLY=1<<17, COPY_HEADERS=1<<18, NO_ROW_NUMBERS=1<<19, + MOVE_TO_MISC=1<<20, ADD_TO_MANAGER=1<<21, RUN_SOCKET_LISTENER=1<<22; + public static final String OPTIONS = "prefs.options"; + + public static final String vistaHint = "\n \nOn Windows Vista, ImageJ must be installed in a directory that\nthe user can write to, such as \"Desktop\" or \"Documents\""; + + /** file.separator system property */ + public static String separator = System.getProperty("file.separator"); + /** Use pointer cursor instead of cross */ + public static boolean usePointerCursor; + /** No longer used */ + public static boolean antialiasedText; + /** Display images scaled <100% using bilinear interpolation */ + public static boolean interpolateScaledImages; + /** Open images at 100% magnification*/ + public static boolean open100Percent; + /** Backgound is black in binary images*/ + public static boolean blackBackground; + /** Use JFileChooser instead of FileDialog to open and save files. */ + public static boolean useJFileChooser; + /** Color to grayscale conversion is weighted (0.299, 0.587, 0.114) if the variable is true. */ + public static boolean weightedColor; + /** Use black image border. */ + public static boolean blackCanvas; + /** Point tool auto-measure mode. */ + public static boolean pointAutoMeasure; + /** Point tool auto-next slice mode (not saved in IJ_Prefs). */ + public static boolean pointAutoNextSlice; + /** Require control or command key for keybaord shortcuts. */ + public static boolean requireControlKey; + /** Open 8-bit images with inverting LUT so 0 is white and 255 is black. */ + public static boolean useInvertingLut; + /** Draw tool icons using antialiasing. */ + public static boolean antialiasedTools; + /** Export Raw using little-endian byte order. */ + public static boolean intelByteOrder; + /** Double buffer display of selections and overlays. */ + public static boolean doubleBuffer = true; + /** Do not label multiple points created using point tool. */ + public static boolean noPointLabels = true; + /** Disable Edit/Undo command. */ + public static boolean disableUndo; + /** Do not draw black border around image. */ + public static boolean noBorder; + /** Only show ROIs associated with current slice in Roi Manager "Show All" mode. */ + public static boolean showAllSliceOnly; + /** Include column headers when copying tables to clipboard. */ + public static boolean copyColumnHeaders; + /** Do not include row numbers when copying tables to clipboard. */ + public static boolean noRowNumbers; + /** Move isolated plugins to Miscellaneous submenu. */ + public static boolean moveToMisc; + /** Add points to ROI Manager. */ + public static boolean pointAddToManager; + /** Extend the borders to foreground for binary erosions and closings. */ + public static boolean padEdges; + /** Run the SocketListener. */ + public static boolean runSocketListener; + + static Properties ijPrefs = new Properties(); + static Properties props = new Properties(ijPrefs); + static String prefsDir; + static String imagesURL; + static String homeDir; // ImageJ folder + static int threads; + static int transparentIndex = -1; + static boolean commandLineMacro; + + /** Finds and loads the ImageJ configuration file, "IJ_Props.txt". + @return an error message if "IJ_Props.txt" not found. + */ + public static String load(Object ij, Applet applet) { + InputStream f = ij.getClass().getResourceAsStream("/"+PROPS_NAME); + if (applet!=null) + return loadAppletProps(f, applet); + if (homeDir==null) + homeDir = System.getProperty("user.dir"); + String userHome = System.getProperty("user.home"); + if (IJ.isWindows()) { + prefsDir = homeDir; //ImageJ folder on Windows + if (prefsDir.endsWith("Desktop")) + prefsDir = userHome; + } else { + prefsDir = userHome; // Mac Preferences folder or Unix home dir + if (IJ.isMacOSX()) + prefsDir += "/Library/Preferences"; + else + prefsDir += "/.imagej"; + } + if (f==null) { + try {f = new FileInputStream(homeDir+"/"+PROPS_NAME);} + catch (FileNotFoundException e) {f=null;} + } + if (f==null) + return PROPS_NAME+" not found in ij.jar or in "+homeDir; + f = new BufferedInputStream(f); + try {props.load(f); f.close();} + catch (IOException e) {return("Error loading "+PROPS_NAME);} + imagesURL = props.getProperty("images.location"); + loadPreferences(); + loadOptions(); + return null; + } + + /* + static void dumpPrefs(String title) { + IJ.log(""); + IJ.log(title); + Enumeration e = ijPrefs.keys(); + while (e.hasMoreElements()) { + String key = (String) e.nextElement(); + IJ.log(key+": "+ijPrefs.getProperty(key)); + } + } + */ + + static String loadAppletProps(InputStream f, Applet applet) { + if (f==null) + return PROPS_NAME+" not found in ij.jar"; + try { + props.load(f); + f.close(); + } + catch (IOException e) {return("Error loading "+PROPS_NAME);} + try { + URL url = new URL(applet.getDocumentBase(), "images/"); + imagesURL = url.toString(); + } + catch (Exception e) {} + return null; + } + + /** Returns the URL of the directory that contains the ImageJ sample images. */ + public static String getImagesURL() { + return imagesURL; + } + + /** Sets the URL of the directory that contains the ImageJ sample images. */ + public static void setImagesURL(String url) { + imagesURL = url; + } + + /** Returns the path to the ImageJ directory. */ + public static String getHomeDir() { + return homeDir; + } + + /** Gets the path to the directory where the + preferences file (IJPrefs.txt) is saved. */ + public static String getPrefsDir() { + return prefsDir; + } + + /** Sets the path to the ImageJ directory. */ + static void setHomeDir(String path) { + if (path.endsWith(File.separator)) + path = path.substring(0, path.length()-1); + homeDir = path; + } + + /** Returns the default directory, if any, or null. */ + public static String getDefaultDirectory() { + if (commandLineMacro) + return null; + else + return getString(DIR_IMAGE); + } + + /** Finds an string in IJ_Props or IJ_Prefs.txt. */ + public static String getString(String key) { + return props.getProperty(key); + } + + /** Finds an string in IJ_Props or IJ_Prefs.txt. */ + public static String getString(String key, String defaultString) { + if (props==null) + return defaultString; + String s = props.getProperty(key); + if (s==null) + return defaultString; + else + return s; + } + + /** Finds a boolean in IJ_Props or IJ_Prefs.txt. */ + public static boolean getBoolean(String key, boolean defaultValue) { + if (props==null) return defaultValue; + String s = props.getProperty(key); + if (s==null) + return defaultValue; + else + return s.equals("true"); + } + + /** Finds an int in IJ_Props or IJ_Prefs.txt. */ + public static int getInt(String key, int defaultValue) { + if (props==null) //workaround for Netscape JIT bug + return defaultValue; + String s = props.getProperty(key); + if (s!=null) { + try { + return Integer.decode(s).intValue(); + } catch (NumberFormatException e) {IJ.write(""+e);} + } + return defaultValue; + } + + /** Looks up a real number in IJ_Props or IJ_Prefs.txt. */ + public static double getDouble(String key, double defaultValue) { + if (props==null) + return defaultValue; + String s = props.getProperty(key); + Double d = null; + if (s!=null) { + try {d = new Double(s);} + catch (NumberFormatException e){d = null;} + if (d!=null) + return(d.doubleValue()); + } + return defaultValue; + } + + /** Finds a color in IJ_Props or IJ_Prefs.txt. */ + public static Color getColor(String key, Color defaultColor) { + int i = getInt(key, 0xaaa); + if (i == 0xaaa) + return defaultColor; + return new Color((i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF); + } + + /** Returns the file.separator system property. */ + public static String getFileSeparator() { + return separator; + } + + /** Opens the IJ_Prefs.txt file. */ + static void loadPreferences() { + String path = prefsDir+separator+PREFS_NAME; + boolean ok = loadPrefs(path); + if (!ok && !IJ.isWindows()) { + path = System.getProperty("user.home")+separator+PREFS_NAME; + ok = loadPrefs(path); // look in home dir + if (ok) new File(path).delete(); + } + + } + + static boolean loadPrefs(String path) { + try { + InputStream is = new BufferedInputStream(new FileInputStream(path)); + ijPrefs.load(is); + is.close(); + return true; + } catch (Exception e) { + return false; + } + } + + /** Saves user preferences in the IJ_Prefs.txt properties file. */ + public static void savePreferences() { + try { + Properties prefs = new Properties(); + String dir = OpenDialog.getDefaultDirectory(); + if (dir!=null) + prefs.put(DIR_IMAGE, dir); + prefs.put(ROICOLOR, Tools.c2hex(Roi.getColor())); + prefs.put(SHOW_ALL_COLOR, Tools.c2hex(ImageCanvas.getShowAllColor())); + prefs.put(FCOLOR, Tools.c2hex(Toolbar.getForegroundColor())); + prefs.put(BCOLOR, Tools.c2hex(Toolbar.getBackgroundColor())); + prefs.put(JPEG, Integer.toString(FileSaver.getJpegQuality())); + prefs.put(FPS, Double.toString(Animator.getFrameRate())); + prefs.put(DIV_BY_ZERO_VALUE, Double.toString(FloatBlitter.divideByZeroValue)); + prefs.put(NOISE_SD, Double.toString(Filters.getSD())); + if (threads>1) prefs.put(THREADS, Integer.toString(threads)); + if (IJ.isMacOSX()) useJFileChooser = false; + saveOptions(prefs); + savePluginPrefs(prefs); + IJ.getInstance().savePreferences(prefs); + Menus.savePreferences(prefs); + ParticleAnalyzer.savePreferences(prefs); + Analyzer.savePreferences(prefs); + ImportDialog.savePreferences(prefs); + PlotWindow.savePreferences(prefs); + GelAnalyzer.savePreferences(prefs); + NewImage.savePreferences(prefs); + String path = prefsDir+separator+PREFS_NAME; + if (prefsDir.endsWith(".imagej")) { + File f = new File(prefsDir); + if (!f.exists()) f.mkdir(); // create .imagej directory + } + savePrefs(prefs, path); + } catch (Throwable t) { + String msg = t.getMessage(); + if (msg==null) msg = ""+t; + int delay = 4000; + if (IJ.isVista()) { + msg += vistaHint; + delay = 8000; + } + try { + new TextWindow("Error Saving Preferences", msg, 500, 200); + IJ.wait(delay); + } catch (Throwable t2) {} + } + } + + static void loadOptions() { + int options = getInt(OPTIONS, ANTIALIASING); + usePointerCursor = (options&USE_POINTER)!=0; + //antialiasedText = (options&ANTIALIASING)!=0; + antialiasedText = false; + interpolateScaledImages = (options&INTERPOLATE)!=0; + open100Percent = (options&ONE_HUNDRED_PERCENT)!=0; + open100Percent = (options&ONE_HUNDRED_PERCENT)!=0; + blackBackground = (options&BLACK_BACKGROUND)!=0; + useJFileChooser = (options&JFILE_CHOOSER)!=0; + weightedColor = (options&WEIGHTED)!=0; + if (weightedColor) + ColorProcessor.setWeightingFactors(0.299, 0.587, 0.114); + blackCanvas = (options&BLACK_CANVAS)!=0; + pointAutoMeasure = (options&AUTO_MEASURE)!=0; + requireControlKey = (options&REQUIRE_CONTROL)!=0; + useInvertingLut = (options&USE_INVERTING_LUT)!=0; + antialiasedTools = (options&ANTIALIASED_TOOLS)!=0; + intelByteOrder = (options&INTEL_BYTE_ORDER)!=0; + // doubleBuffer = (options&DOUBLE_BUFFER)!=0; // always double buffer + noPointLabels = (options&NO_POINT_LABELS)!=0; + noBorder = (options&NO_BORDER)!=0; + showAllSliceOnly = (options&SHOW_ALL_SLICE_ONLY)!=0; + copyColumnHeaders = (options©_HEADERS)!=0; + noRowNumbers = (options&NO_ROW_NUMBERS)!=0; + moveToMisc = (options&MOVE_TO_MISC)!=0; + pointAddToManager = (options&ADD_TO_MANAGER)!=0; + runSocketListener = (options&RUN_SOCKET_LISTENER)!=0; + } + + static void saveOptions(Properties prefs) { + int options = (usePointerCursor?USE_POINTER:0) + (antialiasedText?ANTIALIASING:0) + + (interpolateScaledImages?INTERPOLATE:0) + (open100Percent?ONE_HUNDRED_PERCENT:0) + + (blackBackground?BLACK_BACKGROUND:0) + (useJFileChooser?JFILE_CHOOSER:0) + + (blackCanvas?BLACK_CANVAS:0) + (weightedColor?WEIGHTED:0) + + (pointAutoMeasure?AUTO_MEASURE:0) + (requireControlKey?REQUIRE_CONTROL:0) + + (useInvertingLut?USE_INVERTING_LUT:0) + (antialiasedTools?ANTIALIASED_TOOLS:0) + + (intelByteOrder?INTEL_BYTE_ORDER:0) + (doubleBuffer?DOUBLE_BUFFER:0) + + (noPointLabels?NO_POINT_LABELS:0) + (noBorder?NO_BORDER:0) + + (showAllSliceOnly?SHOW_ALL_SLICE_ONLY:0) + (copyColumnHeaders?COPY_HEADERS:0) + + (noRowNumbers?NO_ROW_NUMBERS:0) + (moveToMisc?MOVE_TO_MISC:0) + + (pointAddToManager?ADD_TO_MANAGER:0) + (runSocketListener?RUN_SOCKET_LISTENER:0); + prefs.put(OPTIONS, Integer.toString(options)); + } + + /** Saves the value of the string text in the preferences + file using the keyword key. This string can be + retrieved using the appropriate get() method. */ + public static void set(String key, String text) { + if (key.indexOf('.')<1) + throw new IllegalArgumentException("Key must have a prefix"); + ijPrefs.put(KEY_PREFIX+key, text); + } + + /** Saves value in the preferences file using + the keyword key. This value can be retrieved + using the appropriate getPref() method. */ + public static void set(String key, int value) { + set(key, Integer.toString(value)); + } + + /** Saves value in the preferences file using + the keyword key. This value can be retrieved + using the appropriate getPref() method. */ + public static void set(String key, double value) { + set(key, ""+value); + } + + /** Saves the boolean variable value in the preferences + file using the keyword key. This value can be retrieved + using the appropriate getPref() method. */ + public static void set(String key, boolean value) { + set(key, ""+value); + } + + /** Uses the keyword key to retrieve a string from the + preferences file. Returns defaultValue if the key + is not found. */ + public static String get(String key, String defaultValue) { + String value = ijPrefs.getProperty(KEY_PREFIX+key); + if (value == null) + return defaultValue; + else + return value; + } + + /** Uses the keyword key to retrieve a number from the + preferences file. Returns defaultValue if the key + is not found. */ + public static double get(String key, double defaultValue) { + String s = ijPrefs.getProperty(KEY_PREFIX+key); + Double d = null; + if (s!=null) { + try {d = new Double(s);} + catch (NumberFormatException e) {d = null;} + if (d!=null) + return(d.doubleValue()); + } + return defaultValue; + } + + /** Uses the keyword key to retrieve a boolean from + the preferences file. Returns defaultValue if + the key is not found. */ + public static boolean get(String key, boolean defaultValue) { + String value = ijPrefs.getProperty(KEY_PREFIX+key); + if (value==null) + return defaultValue; + else + return value.equals("true"); + } + + /** Saves the Point loc in the preferences + file as a string using the keyword key. */ + public static void saveLocation(String key, Point loc) { + set(key, loc.x+","+loc.y); + } + + /** Uses the keyword key to retrieve a location + from the preferences file. Returns null if the + key is not found or the location is not valid (e.g., offscreen). */ + public static Point getLocation(String key) { + String value = ijPrefs.getProperty(KEY_PREFIX+key); + if (value==null) return null; + int index = value.indexOf(","); + if (index==-1) return null; + double xloc = Tools.parseDouble(value.substring(0, index)); + if (Double.isNaN(xloc) || index==value.length()-1) return null; + double yloc = Tools.parseDouble(value.substring(index+1)); + if (Double.isNaN(yloc)) return null; + Point p = new Point((int)xloc, (int)yloc); + Dimension screen = IJ.getScreenSize(); + if (p.x>screen.width-100 || p.y>screen.height-40) + return null; + else + return p; + } + + /** Save plugin preferences. */ + static void savePluginPrefs(Properties prefs) { + Enumeration e = ijPrefs.keys(); + while (e.hasMoreElements()) { + String key = (String) e.nextElement(); + if (key.indexOf(KEY_PREFIX) == 0) + prefs.put(key, ijPrefs.getProperty(key)); + } + } + + public static void savePrefs(Properties prefs, String path) throws IOException{ + FileOutputStream fos = new FileOutputStream(path); + BufferedOutputStream bos = new BufferedOutputStream(fos); + prefs.store(bos, "ImageJ "+ImageJ.VERSION+" Preferences"); + bos.close(); + } + + /** Returns the number of threads used by PlugInFilters to process stacks. */ + public static int getThreads() { + if (threads==0) { + threads = getInt(THREADS, 0); + int processors = Runtime.getRuntime().availableProcessors(); + if (threads<1 || threads>processors) threads = processors; + } + return threads; + } + + /** Sets the number of threads (1-32) used by PlugInFilters to process stacks. */ + public static void setThreads(int n) { + if (n<1) n = 1; + if (n>32) n = 32; + threads = n; + } + + /** Sets the transparent index (0-255), or set to -1 to disable transparency. */ + public static void setTransparentIndex(int index) { + if (index<-1 || index>255) index = -1; + transparentIndex = index; + } + + /** Returns the transparent index (0-255), or -1 if transparency is disabled. */ + public static int getTransparentIndex() { + return transparentIndex; + } + + + +} + diff --git a/ij/RecentOpener.java b/ij/RecentOpener.java index bf815d45c..27b90f8b3 100644 --- a/ij/RecentOpener.java +++ b/ij/RecentOpener.java @@ -1,37 +1 @@ -package ij; -import ij.io.*; -import java.awt.*; -import java.io.*; - -/** Opens, in a separate thread, files selected from the File/Open Recent submenu.*/ -public class RecentOpener implements Runnable { - private String path; - - RecentOpener(String path) { - this.path = path; - Thread thread = new Thread(this, "RecentOpener"); - thread.start(); - } - - /** Open the file and move the path to top of the submenu. */ - public void run() { - Opener o = new Opener(); - o.open(path); - Menu menu = Menus.openRecentMenu; - int n = menu.getItemCount(); - int index = 0; - for (int i=0; i0) { - MenuItem item = menu.getItem(index); - menu.remove(index); - menu.insert(item, 0); - } - } - -} - +package ij; import ij.io.*; import java.awt.*; import java.io.*; /** Opens, in a separate thread, files selected from the File/Open Recent submenu.*/ public class RecentOpener implements Runnable { private String path; RecentOpener(String path) { this.path = path; Thread thread = new Thread(this, "RecentOpener"); thread.start(); } /** Open the file and move the path to top of the submenu. */ public void run() { Opener o = new Opener(); o.open(path); Menu menu = Menus.openRecentMenu; int n = menu.getItemCount(); int index = 0; for (int i=0; i0) { MenuItem item = menu.getItem(index); menu.remove(index); menu.insert(item, 0); } } } \ No newline at end of file diff --git a/ij/SocketListener.java b/ij/SocketListener.java deleted file mode 100644 index 417a9aa6b..000000000 --- a/ij/SocketListener.java +++ /dev/null @@ -1,66 +0,0 @@ -package ij; -import ij.*; -import ij.io.*; -import java.io.*; -import java.net.*; - -/** Runs commands sent to the port returned by ImageJ.getPort(). -
-  Commands:
-    open path (opens a file)
-    macro path  (runs a macro file)
-    run command-name  (runs an ImageJ menu command)
-    eval macro  (evaluates a macro)
-    user.dir path  (sets the current directory)
-
-*/ -public class SocketListener implements Runnable { - - public SocketListener() { - Thread thread = new Thread(this, "SocketListener"); - thread.start(); - } - - public void run() { - ServerSocket serverSocket = null; - BufferedReader is; - Socket clientSocket; - try { - serverSocket = new ServerSocket(ImageJ.getPort()); - while (true) { - clientSocket = serverSocket.accept(); - try { - if (IJ.debugMode) IJ.log("SocketServer: waiting on port "+ImageJ.getPort()); - is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); - String cmd = is.readLine(); - if (IJ. debugMode) IJ.log("SocketServer: command: \""+ cmd+"\""); - if (cmd.startsWith("open ")) - (new Opener()).openAndAddToRecent(cmd.substring(5)); - else if (cmd.startsWith("macro ")) { - String name = cmd.substring(6); - String name2 = name; - String arg = null; - if (name2.endsWith(")")) { - int index = name2.lastIndexOf("("); - if (index>0) { - name = name2.substring(0, index); - arg = name2.substring(index+1, name2.length()-1); - } - } - IJ.runMacroFile(name, arg); - } else if (cmd.startsWith("run ")) - IJ.run(cmd.substring(4)); - else if (cmd.startsWith("eval ")) { - String rtn = IJ.runMacro(cmd.substring(5)); - if (rtn!=null) - System.out.print(rtn); - } else if (cmd.startsWith("user.dir ")) - OpenDialog.setDefaultDirectory(cmd.substring(9)); - } catch (Throwable e) {} - clientSocket.close(); - if (IJ. debugMode) IJ.log("SocketServer: connection closed"); - } - } catch (IOException e) {} - } - -} diff --git a/ij/Undo.java b/ij/Undo.java index 4eb90367c..11d32eee8 100644 --- a/ij/Undo.java +++ b/ij/Undo.java @@ -1,113 +1,113 @@ -/**Implements the Edit/Undo command.*/ - -package ij; -import ij.process.*; -import java.awt.*; -import java.awt.image.*; -import ij.gui.*; - -/** This class consists of static methods and - fields that implement ImageJ's Undo command. */ -public class Undo { - - public static final int NOTHING = 0; - public static final int FILTER = 1; - public static final int TYPE_CONVERSION = 2; - public static final int PASTE = 3; - public static final int COMPOUND_FILTER = 4; - public static final int COMPOUND_FILTER_DONE = 5; - public static final int TRANSFORM = 6; - - private static int whatToUndo = NOTHING; - private static int imageID; - private static ImageProcessor ipCopy = null; - private static ImagePlus impCopy; - - public static void setup(int what, ImagePlus imp) { - if (imp==null) { - whatToUndo = NOTHING; - reset(); - return; - } - //IJ.log(imp.getTitle() + ": set up undo (" + what + ")"); - if (what==FILTER && whatToUndo==COMPOUND_FILTER) - return; - if (what==COMPOUND_FILTER_DONE) { - if (whatToUndo==COMPOUND_FILTER) - whatToUndo = what; - return; - } - whatToUndo = what; - imageID = imp.getID(); - if (what==TYPE_CONVERSION) - ipCopy = imp.getProcessor(); - else if (what==TRANSFORM) { - impCopy = new ImagePlus(imp.getTitle(), imp.getProcessor().duplicate()); - Object fht = imp.getProperty("FHT"); - if (fht!=null) { - fht = new FHT((ImageProcessor)fht); // duplicate - impCopy.setProperty("FHT", fht); - } - } else if (what==COMPOUND_FILTER) { - ImageProcessor ip = imp.getProcessor(); - if (ip!=null) - ipCopy = ip.duplicate(); - else - ipCopy = null; - } else - ipCopy = null; - } - - - public static void reset() { - if (whatToUndo==COMPOUND_FILTER) - return; - whatToUndo = NOTHING; - imageID = 0; - ipCopy = null; - impCopy = null; - //IJ.log("Undo: reset"); - } - - - public static void undo() { - ImagePlus imp = WindowManager.getCurrentImage(); - //IJ.log(imp.getTitle() + ": undo (" + whatToUndo + ") "+(imageID!=imp.getID())); - if (imageID!=imp.getID()) { - reset(); - return; - } - switch (whatToUndo) { - case FILTER: - ImageProcessor ip = imp.getProcessor(); - if (ip!=null) { - ip.reset(); - imp.updateAndDraw(); - } - break; - case TYPE_CONVERSION: - case COMPOUND_FILTER: - case COMPOUND_FILTER_DONE: - if (ipCopy!=null) - imp.setProcessor(null, ipCopy); - break; - case TRANSFORM: - if (impCopy!=null) { - imp.setProcessor(impCopy.getTitle(), impCopy.getProcessor()); - Object fht = impCopy.getProperty("FHT"); - if (fht!=null) - imp.setProperty("FHT", fht); - else if (imp.getProperty("FHT")!=null) - imp.getProperties().remove("FHT"); - } - break; - case PASTE: - Roi roi = imp.getRoi(); - if (roi!=null) - roi.abortPaste(); - break; - } - reset(); - } - -} +/**Implements the Edit/Undo command.*/ + +package ij; +import ij.process.*; +import java.awt.*; +import java.awt.image.*; +import ij.gui.*; + +/** This class consists of static methods and + fields that implement ImageJ's Undo command. */ +public class Undo { + + public static final int NOTHING = 0; + public static final int FILTER = 1; + public static final int TYPE_CONVERSION = 2; + public static final int PASTE = 3; + public static final int COMPOUND_FILTER = 4; + public static final int COMPOUND_FILTER_DONE = 5; + public static final int TRANSFORM = 6; + + private static int whatToUndo = NOTHING; + private static int imageID; + private static ImageProcessor ipCopy = null; + private static ImagePlus impCopy; + + public static void setup(int what, ImagePlus imp) { + if (imp==null) { + whatToUndo = NOTHING; + reset(); + return; + } + //IJ.log(imp.getTitle() + ": set up undo (" + what + ")"); + if (what==FILTER && whatToUndo==COMPOUND_FILTER) + return; + if (what==COMPOUND_FILTER_DONE) { + if (whatToUndo==COMPOUND_FILTER) + whatToUndo = what; + return; + } + whatToUndo = what; + imageID = imp.getID(); + if (what==TYPE_CONVERSION) + ipCopy = imp.getProcessor(); + else if (what==TRANSFORM) { + impCopy = new ImagePlus(imp.getTitle(), imp.getProcessor().duplicate()); + Object fht = imp.getProperty("FHT"); + if (fht!=null) { + fht = new FHT((ImageProcessor)fht); // duplicate + impCopy.setProperty("FHT", fht); + } + } else if (what==COMPOUND_FILTER) { + ImageProcessor ip = imp.getProcessor(); + if (ip!=null) + ipCopy = ip.duplicate(); + else + ipCopy = null; + } else + ipCopy = null; + } + + + public static void reset() { + if (whatToUndo==COMPOUND_FILTER) + return; + whatToUndo = NOTHING; + imageID = 0; + ipCopy = null; + impCopy = null; + //IJ.log("Undo: reset"); + } + + + public static void undo() { + ImagePlus imp = WindowManager.getCurrentImage(); + //IJ.log(imp.getTitle() + ": undo (" + whatToUndo + ") "+(imageID!=imp.getID())); + if (imageID!=imp.getID()) { + reset(); + return; + } + switch (whatToUndo) { + case FILTER: + ImageProcessor ip = imp.getProcessor(); + if (ip!=null) { + ip.reset(); + imp.updateAndDraw(); + } + break; + case TYPE_CONVERSION: + case COMPOUND_FILTER: + case COMPOUND_FILTER_DONE: + if (ipCopy!=null) + imp.setProcessor(null, ipCopy); + break; + case TRANSFORM: + if (impCopy!=null) { + imp.setProcessor(impCopy.getTitle(), impCopy.getProcessor()); + Object fht = impCopy.getProperty("FHT"); + if (fht!=null) + imp.setProperty("FHT", fht); + else if (imp.getProperty("FHT")!=null) + imp.getProperties().remove("FHT"); + } + break; + case PASTE: + Roi roi = imp.getRoi(); + if (roi!=null) + roi.abortPaste(); + break; + } + reset(); + } + +} diff --git a/ij/VirtualStack.java b/ij/VirtualStack.java index 8a6e8064d..479490b83 100644 --- a/ij/VirtualStack.java +++ b/ij/VirtualStack.java @@ -1,180 +1 @@ -package ij; -import ij.process.*; -import ij.io.*; -import java.io.*; -import java.awt.image.ColorModel; - -/** This class represents an array of disk-resident images. */ -public class VirtualStack extends ImageStack { - private static final int INITIAL_SIZE = 100; - private String path; - private int nSlices; - private String[] names; - private String[] labels; - private int bitDepth; - - /** Default constructor. */ - public VirtualStack() { } - - /** Creates a new, empty virtual stack. */ - public VirtualStack(int width, int height, ColorModel cm, String path) { - super(width, height, cm); - this.path = path; - names = new String[INITIAL_SIZE]; - labels = new String[INITIAL_SIZE]; - //IJ.log("VirtualStack: "+path); - } - - /** Adds an image to the end of the stack. */ - public void addSlice(String name) { - if (name==null) - throw new IllegalArgumentException("'name' is null!"); - nSlices++; - //IJ.log("addSlice: "+nSlices+" "+name); - if (nSlices==names.length) { - String[] tmp = new String[nSlices*2]; - System.arraycopy(names, 0, tmp, 0, nSlices); - names = tmp; - tmp = new String[nSlices*2]; - System.arraycopy(labels, 0, tmp, 0, nSlices); - labels = tmp; - } - names[nSlices-1] = name; - } - - /** Does nothing. */ - public void addSlice(String sliceLabel, Object pixels) { - } - - /** Does nothing.. */ - public void addSlice(String sliceLabel, ImageProcessor ip) { - } - - /** Does noting. */ - public void addSlice(String sliceLabel, ImageProcessor ip, int n) { - } - - /** Deletes the specified slice, were 1<=n<=nslices. */ - public void deleteSlice(int n) { - if (n<1 || n>nSlices) - throw new IllegalArgumentException("Argument out of range: "+n); - if (nSlices<1) - return; - for (int i=n; i0) - deleteSlice(nSlices); - } - - /** Returns the pixel array for the specified slice, were 1<=n<=nslices. */ - public Object getPixels(int n) { - ImageProcessor ip = getProcessor(n); - if (ip!=null) - return ip.getPixels(); - else - return null; - } - - /** Assigns a pixel array to the specified slice, - were 1<=n<=nslices. */ - public void setPixels(Object pixels, int n) { - } - - /** Returns an ImageProcessor for the specified slice, - were 1<=n<=nslices. Returns null if the stack is empty. - */ - public ImageProcessor getProcessor(int n) { - //IJ.log("getProcessor: "+n+" "+names[n-1]+" "+bitDepth); - ImagePlus imp = new Opener().openImage(path, names[n-1]); - if (imp!=null) { - int w = imp.getWidth(); - int h = imp.getHeight(); - int type = imp.getType(); - ColorModel cm = imp.getProcessor().getColorModel(); - labels[n-1] = (String)imp.getProperty("Info"); - } else { - File f = new File(path, names[n-1]); - String msg = f.exists()?"error opening ":"file not found: "; - throw new RuntimeException(msg+path+names[n-1]); - } - ImageProcessor ip = imp.getProcessor(); - if (imp.getBitDepth()!=bitDepth) { - switch (bitDepth) { - case 8: ip=ip.convertToByte(true); break; - case 16: ip=ip.convertToShort(true); break; - case 24: ip=ip.convertToRGB(); break; - case 32: ip=ip.convertToFloat(); break; - } - } - if (ip.getWidth()!=getWidth() || ip.getHeight()!=getHeight()) - ip = ip.resize(getWidth(), getHeight()); - return ip; - } - - /** Currently not implemented */ - public int saveChanges(int n) { - return -1; - } - - /** Returns the number of slices in this stack. */ - public int getSize() { - return nSlices; - } - - /** Returns the label of the Nth image. */ - public String getSliceLabel(int n) { - String label = labels[n-1]; - if (label==null) - return names[n-1]; - else if (label.length()<=60) - return label; - else - return names[n-1]+"\n"+label; - } - - /** Returns null. */ - public Object[] getImageArray() { - return null; - } - - /** Does nothing. */ - public void setSliceLabel(String label, int n) { - } - - /** Always return true. */ - public boolean isVirtual() { - return true; - } - - /** Does nothing. */ - public void trim() { - } - - /** Returns the path to the directory containing the images. */ - public String getDirectory() { - return path; - } - - /** Returns the file name of the specified slice, were 1<=n<=nslices. */ - public String getFileName(int n) { - return names[n-1]; - } - - /** Sets the bit depth (8, 16, 24 or 32). */ - public void setBitDepth(int bitDepth) { - this.bitDepth = bitDepth; - } - - /** Returns the bit depth (8, 16, 24 or 32), or 0 if the bit depth is not known. */ - public int getBitDepth() { - return bitDepth; - } - -} - +package ij; import ij.process.*; import ij.io.*; import java.io.*; import java.awt.image.ColorModel; /** This class represents an array of disk-resident images. */ public class VirtualStack extends ImageStack { private static final int INITIAL_SIZE = 100; private String path; private int nSlices; private String[] names; private String[] labels; private int bitDepth; /** Default constructor. */ public VirtualStack() { } /** Creates a new, empty virtual stack. */ public VirtualStack(int width, int height, ColorModel cm, String path) { super(width, height, cm); this.path = path; names = new String[INITIAL_SIZE]; labels = new String[INITIAL_SIZE]; //IJ.log("VirtualStack: "+path); } /** Adds an image to the end of the stack. */ public void addSlice(String name) { if (name==null) throw new IllegalArgumentException("'name' is null!"); nSlices++; //IJ.log("addSlice: "+nSlices+" "+name); if (nSlices==names.length) { String[] tmp = new String[nSlices*2]; System.arraycopy(names, 0, tmp, 0, nSlices); names = tmp; tmp = new String[nSlices*2]; System.arraycopy(labels, 0, tmp, 0, nSlices); labels = tmp; } names[nSlices-1] = name; } /** Does nothing. */ public void addSlice(String sliceLabel, Object pixels) { } /** Does nothing.. */ public void addSlice(String sliceLabel, ImageProcessor ip) { } /** Does noting. */ public void addSlice(String sliceLabel, ImageProcessor ip, int n) { } /** Deletes the specified slice, were 1<=n<=nslices. */ public void deleteSlice(int n) { if (n<1 || n>nSlices) throw new IllegalArgumentException("Argument out of range: "+n); if (nSlices<1) return; for (int i=n; i0) deleteSlice(nSlices); } /** Returns the pixel array for the specified slice, were 1<=n<=nslices. */ public Object getPixels(int n) { ImageProcessor ip = getProcessor(n); if (ip!=null) return ip.getPixels(); else return null; } /** Assigns a pixel array to the specified slice, were 1<=n<=nslices. */ public void setPixels(Object pixels, int n) { } /** Returns an ImageProcessor for the specified slice, were 1<=n<=nslices. Returns null if the stack is empty. */ public ImageProcessor getProcessor(int n) { //IJ.log("getProcessor: "+n+" "+names[n-1]+" "+bitDepth); ImagePlus imp = new Opener().openImage(path, names[n-1]); if (imp!=null) { int w = imp.getWidth(); int h = imp.getHeight(); int type = imp.getType(); ColorModel cm = imp.getProcessor().getColorModel(); labels[n-1] = (String)imp.getProperty("Info"); } else { File f = new File(path, names[n-1]); String msg = f.exists()?"error opening ":"file not found: "; throw new RuntimeException(msg+path+names[n-1]); } ImageProcessor ip = imp.getProcessor(); if (imp.getBitDepth()!=bitDepth) { switch (bitDepth) { case 8: ip=ip.convertToByte(true); break; case 16: ip=ip.convertToShort(true); break; case 24: ip=ip.convertToRGB(); break; case 32: ip=ip.convertToFloat(); break; } } if (ip.getWidth()!=getWidth() || ip.getHeight()!=getHeight()) ip = ip.resize(getWidth(), getHeight()); return ip; } /** Currently not implemented */ public int saveChanges(int n) { return -1; } /** Returns the number of slices in this stack. */ public int getSize() { return nSlices; } /** Returns the label of the Nth image. */ public String getSliceLabel(int n) { String label = labels[n-1]; if (label==null) return names[n-1]; else if (label.length()<=60) return label; else return names[n-1]+"\n"+label; } /** Returns null. */ public Object[] getImageArray() { return null; } /** Does nothing. */ public void setSliceLabel(String label, int n) { } /** Always return true. */ public boolean isVirtual() { return true; } /** Does nothing. */ public void trim() { } /** Returns the path to the directory containing the images. */ public String getDirectory() { return path; } /** Returns the file name of the specified slice, were 1<=n<=nslices. */ public String getFileName(int n) { return names[n-1]; } /** Sets the bit depth (8, 16, 24 or 32). */ public void setBitDepth(int bitDepth) { this.bitDepth = bitDepth; } /** Returns the bit depth (8, 16, 24 or 32), or 0 if the bit depth is not known. */ public int getBitDepth() { return bitDepth; } } \ No newline at end of file diff --git a/ij/WindowManager.java b/ij/WindowManager.java index c113720a3..38686a2da 100644 --- a/ij/WindowManager.java +++ b/ij/WindowManager.java @@ -1,451 +1,451 @@ -package ij; -import ij.plugin.Converter; -import ij.plugin.frame.Recorder; -import ij.macro.Interpreter; -import ij.text.TextWindow; -import ij.plugin.frame.PlugInFrame; -import java.awt.*; -import java.util.*; -import ij.gui.*; - -/** This class consists of static methods used to manage ImageJ's windows. */ -public class WindowManager { - - public static boolean checkForDuplicateName; - private static Vector imageList = new Vector(); // list of image windows - private static Vector nonImageList = new Vector(); // list of non-image windows - private static ImageWindow currentWindow; // active image window - private static Frame frontWindow; - private static Hashtable tempImageTable = new Hashtable(); - - private WindowManager() { - } - - /** Makes the image contained in the specified window the active image. */ - public static void setCurrentWindow(ImageWindow win) { - if (win==null || win.isClosed() || win.getImagePlus()==null) // deadlock-"wait to lock" - return; - //IJ.log("setCurrentWindow: "+win.getImagePlus().getTitle()+" ("+(currentWindow!=null?currentWindow.getImagePlus().getTitle():"null") + ")"); - setWindow(win); - tempImageTable.remove(Thread.currentThread()); - if (win==currentWindow || imageList.size()==0) - return; - if (currentWindow!=null) { - // free up pixel buffers used by current window - ImagePlus imp = currentWindow.getImagePlus(); - if (imp!=null ) { - imp.trimProcessor(); - imp.saveRoi(); - } - } - Undo.reset(); - currentWindow = win; - Menus.updateMenus(); - if (Recorder.record && !IJ.isMacro()) - Recorder.record("selectWindow", win.getImagePlus().getTitle()); - } - - /** Returns the active ImageWindow. */ - public static ImageWindow getCurrentWindow() { - //if (IJ.debugMode) IJ.write("ImageWindow.getCurrentWindow"); - return currentWindow; - } - - static int getCurrentIndex() { - return imageList.indexOf(currentWindow); - } - - /** Returns a reference to the active image or null if there isn't one. */ - public static ImagePlus getCurrentImage() { - ImagePlus img = (ImagePlus)tempImageTable.get(Thread.currentThread()); - //String str = (img==null)?" null":""; - if (img==null) - img = getActiveImage(); - //if (img!=null) IJ.log("getCurrentImage: "+img.getTitle()+" "+Thread.currentThread().hashCode()+str); - return img; - } - - /** Makes the specified image temporarily the active - image for this thread. Call again with a null - argument to revert to the previous active image. */ - public static void setTempCurrentImage(ImagePlus img) { - //IJ.log("setTempImage: "+(img!=null?img.getTitle():"null ")+Thread.currentThread().hashCode()); - if (img==null) - tempImageTable.remove(Thread.currentThread()); - else - tempImageTable.put(Thread.currentThread(), img); - } - - /** Sets a temporary image for the specified thread. */ - public static void setTempCurrentImage(Thread thread, ImagePlus img) { - if (thread==null) - throw new RuntimeException("thread==null"); - if (img==null) - tempImageTable.remove(thread); - else - tempImageTable.put(thread, img); - } - - /** Returns the active ImagePlus. */ - private static ImagePlus getActiveImage() { - if (currentWindow!=null) - return currentWindow.getImagePlus(); - else if (frontWindow!=null && (frontWindow instanceof ImageWindow)) - return ((ImageWindow)frontWindow).getImagePlus(); - else if (imageList.size()>0) { - ImageWindow win = (ImageWindow)imageList.elementAt(imageList.size()-1); - return win.getImagePlus(); - } else - return Interpreter.getLastBatchModeImage(); - } - - /** Returns the number of open image windows. */ - public static int getWindowCount() { - int count = imageList.size(); - return count; - } - - /** Returns the number of open images. */ - public static int getImageCount() { - int count = imageList.size(); - count += Interpreter.getBatchModeImageCount(); - if (count==0 && getCurrentImage()!=null) - count = 1; - return count; - } - - /** Returns the front most window or null. */ - public static Frame getFrontWindow() { - return frontWindow; - } - - /** Returns a list of the IDs of open images. Returns - null if no windows are open. */ - public synchronized static int[] getIDList() { - int nWindows = imageList.size(); - int[] batchModeImages = Interpreter.getBatchModeImageIDs(); - int nBatchImages = batchModeImages.length; - if ((nWindows+nBatchImages)==0) - return null; - int[] list = new int[nWindows+nBatchImages]; - for (int i=0; i0) - imageID = getNthImageID(imageID); - if (imageID==0 || getImageCount()==0) - return null; - ImagePlus imp2 = Interpreter.getBatchModeImage(imageID); - if (imp2!=null) - return imp2; - ImagePlus imp = null; - for (int i=0; ilist.length) - return 0; - else - return list[n-1]; - } else { - if (n>imageList.size()) return 0; - ImageWindow win = (ImageWindow)imageList.elementAt(n-1); - if (win!=null) - return win.getImagePlus().getID(); - else - return 0; - } - } - - - /** Returns the first image that has the specified title or null if it is not found. */ - public synchronized static ImagePlus getImage(String title) { - int[] wList = getIDList(); - if (wList==null) return null; - for (int i=0; i=0) { - //if (ij!=null && !ij.quitting()) - Menus.removeWindowMenuItem(index); - nonImageList.removeElement(win); - } - } - setWindow(null); - } - - private static void removeImageWindow(ImageWindow win) { - int index = imageList.indexOf(win); - if (index==-1) - return; // not on the window list - if (imageList.size()>1) { - int newIndex = index-1; - if (newIndex<0) - newIndex = imageList.size()-1; - setCurrentWindow((ImageWindow)imageList.elementAt(newIndex)); - } else - currentWindow = null; - imageList.removeElementAt(index); - setTempCurrentImage(null); //??? - int nonImageCount = nonImageList.size(); - if (nonImageCount>0) - nonImageCount++; - Menus.removeWindowMenuItem(nonImageCount+index); - Menus.updateMenus(); - Undo.reset(); - } - - /** The specified frame becomes the front window, the one returnd by getFrontWindow(). */ - public static void setWindow(Frame win) { - frontWindow = win; - //IJ.log("Set window: "+(win!=null?win.getTitle():"null")); - } - - /** Closes all windows. Stops and returns false if any image "save changes" dialog is canceled. */ - public synchronized static boolean closeAllWindows() { - while (imageList.size()>0) { - if (!((ImageWindow)imageList.elementAt(0)).close()) - return false; - IJ.wait(100); - } - ImageJ ij = IJ.getInstance(); - if (ij!=null && ij.quitting() && IJ.getApplet()==null) - return true; - Frame[] list = getNonImageWindows(); - for (int i=0; i0) // remove image size (e.g., " 90K") - menuItemLabel = menuItemLabel.substring(0, lastSpace); - for (int i=0; i0) { + ImageWindow win = (ImageWindow)imageList.elementAt(imageList.size()-1); + return win.getImagePlus(); + } else + return Interpreter.getLastBatchModeImage(); + } + + /** Returns the number of open image windows. */ + public static int getWindowCount() { + int count = imageList.size(); + return count; + } + + /** Returns the number of open images. */ + public static int getImageCount() { + int count = imageList.size(); + count += Interpreter.getBatchModeImageCount(); + if (count==0 && getCurrentImage()!=null) + count = 1; + return count; + } + + /** Returns the front most window or null. */ + public static Frame getFrontWindow() { + return frontWindow; + } + + /** Returns a list of the IDs of open images. Returns + null if no windows are open. */ + public synchronized static int[] getIDList() { + int nWindows = imageList.size(); + int[] batchModeImages = Interpreter.getBatchModeImageIDs(); + int nBatchImages = batchModeImages.length; + if ((nWindows+nBatchImages)==0) + return null; + int[] list = new int[nWindows+nBatchImages]; + for (int i=0; i0) + imageID = getNthImageID(imageID); + if (imageID==0 || getImageCount()==0) + return null; + ImagePlus imp2 = Interpreter.getBatchModeImage(imageID); + if (imp2!=null) + return imp2; + ImagePlus imp = null; + for (int i=0; ilist.length) + return 0; + else + return list[n-1]; + } else { + if (n>imageList.size()) return 0; + ImageWindow win = (ImageWindow)imageList.elementAt(n-1); + if (win!=null) + return win.getImagePlus().getID(); + else + return 0; + } + } + + + /** Returns the first image that has the specified title or null if it is not found. */ + public synchronized static ImagePlus getImage(String title) { + int[] wList = getIDList(); + if (wList==null) return null; + for (int i=0; i=0) { + //if (ij!=null && !ij.quitting()) + Menus.removeWindowMenuItem(index); + nonImageList.removeElement(win); + } + } + setWindow(null); + } + + private static void removeImageWindow(ImageWindow win) { + int index = imageList.indexOf(win); + if (index==-1) + return; // not on the window list + if (imageList.size()>1) { + int newIndex = index-1; + if (newIndex<0) + newIndex = imageList.size()-1; + setCurrentWindow((ImageWindow)imageList.elementAt(newIndex)); + } else + currentWindow = null; + imageList.removeElementAt(index); + setTempCurrentImage(null); //??? + int nonImageCount = nonImageList.size(); + if (nonImageCount>0) + nonImageCount++; + Menus.removeWindowMenuItem(nonImageCount+index); + Menus.updateMenus(); + Undo.reset(); + } + + /** The specified frame becomes the front window, the one returnd by getFrontWindow(). */ + public static void setWindow(Frame win) { + frontWindow = win; + //IJ.log("Set window: "+(win!=null?win.getTitle():"null")); + } + + /** Closes all windows. Stops and returns false if any image "save changes" dialog is canceled. */ + public synchronized static boolean closeAllWindows() { + while (imageList.size()>0) { + if (!((ImageWindow)imageList.elementAt(0)).close()) + return false; + IJ.wait(100); + } + ImageJ ij = IJ.getInstance(); + if (ij!=null && ij.quitting() && IJ.getApplet()==null) + return true; + Frame[] list = getNonImageWindows(); + for (int i=0; i0) // remove image size (e.g., " 90K") + menuItemLabel = menuItemLabel.substring(0, lastSpace); + for (int i=0; i255) red=255; - if (green<0) green=0; if (green>255) green=255; - if (blue<0) blue=0; if (blue>255) blue=255; - panel.setColor(new Color(red, green, blue)); - panel.repaint(); - } - - public synchronized void adjustmentValueChanged(AdjustmentEvent e) { - Object source = e.getSource(); - for (int i=0; i255) red=255; if (green<0) green=0; if (green>255) green=255; if (blue<0) blue=0; if (blue>255) blue=255; panel.setColor(new Color(red, green, blue)); panel.repaint(); } public synchronized void adjustmentValueChanged(AdjustmentEvent e) { Object source = e.getSource(); for (int i=0; i - * public class Generic_Dialog_Example implements PlugIn { - * static String title="Example"; - * static int width=512,height=512; - * public void run(String arg) { - * GenericDialog gd = new GenericDialog("New Image"); - * gd.addStringField("Title: ", title); - * gd.addNumericField("Width: ", width, 0); - * gd.addNumericField("Height: ", height, 0); - * gd.showDialog(); - * if (gd.wasCanceled()) return; - * title = gd.getNextString(); - * width = (int)gd.getNextNumber(); - * height = (int)gd.getNextNumber(); - * IJ.newImage(title, "8-bit", width, height, 1); - * } - * } - * -* To work with macros, the first word of each component label must be -* unique. If this is not the case, add underscores, which will be converted -* to spaces when the dialog is displayed. For example, change the checkbox labels -* "Show Quality" and "Show Residue" to "Show_Quality" and "Show_Residue". -*/ -public class GenericDialog extends Dialog implements ActionListener, TextListener, -FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener { - - public static final int MAX_SLIDERS = 25; - protected Vector numberField, stringField, checkbox, choice, slider; - protected TextArea textArea1, textArea2; - protected Vector defaultValues,defaultText; - protected Component theLabel; - private Button cancel, okay, no, help; - private String okLabel = " OK "; - private boolean wasCanceled, wasOKed; - private int y; - private int nfIndex, sfIndex, cbIndex, choiceIndex, textAreaIndex; - private GridBagLayout grid; - private GridBagConstraints c; - private boolean firstNumericField=true; - private boolean firstSlider=true; - private boolean invalidNumber; - private String errorMessage; - private boolean firstPaint = true; - private Hashtable labels; - private boolean macro; - private String macroOptions; - private int topInset, leftInset, bottomInset; - private boolean customInsets; - private int[] sliderIndexes; - private Checkbox previewCheckbox; // the "Preview" Checkbox, if any - private Vector dialogListeners; // the Objects to notify on user input - private PlugInFilterRunner pfr; // the PlugInFilterRunner for automatic preview - private String previewLabel = " Preview"; - private final static String previewRunning = "wait..."; - private boolean recorderOn; // whether recording is allowed - private boolean yesNoCancel; - private char echoChar; - private boolean hideCancelButton; - private boolean centerDialog = true; - private String helpURL; - private String yesLabel, noLabel; - - /** Creates a new GenericDialog with the specified title. Uses the current image - image window as the parent frame or the ImageJ frame if no image windows - are open. Dialog parameters are recorded by ImageJ's command recorder but - this requires that the first word of each label be unique. */ - public GenericDialog(String title) { - this(title, WindowManager.getCurrentImage()!=null? - (Frame)WindowManager.getCurrentImage().getWindow():IJ.getInstance()!=null?IJ.getInstance():new Frame()); - } - - /** Creates a new GenericDialog using the specified title and parent frame. */ - public GenericDialog(String title, Frame parent) { - super(parent==null?new Frame():parent, title, true); - if (Prefs.blackCanvas) { - setForeground(SystemColor.controlText); - setBackground(SystemColor.control); - } - //if (IJ.isLinux()) - // setBackground(new Color(238, 238, 238)); - grid = new GridBagLayout(); - c = new GridBagConstraints(); - setLayout(grid); - macroOptions = Macro.getOptions(); - macro = macroOptions!=null; - addKeyListener(this); - addWindowListener(this); - } - - //void showFields(String id) { - // String s = id+": "; - // for (int i=0; i0) { - if (label.charAt(0)==' ') - label = label.trim(); - labels.put(component, label); - } - } - - /** Adds an 8 column text field. - * @param label the label - * @param defaultText the text initially displayed - */ - public void addStringField(String label, String defaultText) { - addStringField(label, defaultText, 8); - } - - /** Adds a text field. - * @param label the label - * @param defaultText text initially displayed - * @param columns width of the text field - */ - public void addStringField(String label, String defaultText, int columns) { - String label2 = label; - if (label2.indexOf('_')!=-1) - label2 = label2.replace('_', ' '); - Label theLabel = makeLabel(label2); - c.gridx = 0; c.gridy = y; - c.anchor = GridBagConstraints.EAST; - c.gridwidth = 1; - boolean custom = customInsets; - if (stringField==null) { - stringField = new Vector(4); - c.insets = getInsets(5, 0, 5, 0); - } else - c.insets = getInsets(0, 0, 5, 0); - grid.setConstraints(theLabel, c); - add(theLabel); - if (custom) { - if (stringField.size()==0) - c.insets = getInsets(5, 0, 5, 0); - else - c.insets = getInsets(0, 0, 5, 0); - } - TextField tf = new TextField(defaultText, columns); - if (IJ.isLinux()) tf.setBackground(Color.white); - tf.setEchoChar(echoChar); - echoChar = 0; - tf.addActionListener(this); - tf.addTextListener(this); - tf.addFocusListener(this); - tf.addKeyListener(this); - c.gridx = 1; c.gridy = y; - c.anchor = GridBagConstraints.WEST; - grid.setConstraints(tf, c); - tf.setEditable(true); - add(tf); - stringField.addElement(tf); - if (Recorder.record || macro) - saveLabel(tf, label); - y++; - } - - /** Sets the echo character for the next string field. */ - public void setEchoChar(char echoChar) { - this.echoChar = echoChar; - } - - /** Adds a checkbox. - * @param label the label - * @param defaultValue the initial state - */ - public void addCheckbox(String label, boolean defaultValue) { - addCheckbox(label, defaultValue, false); - } - - /** Adds a checkbox; does not make it recordable if isPreview is true. - * With isPreview true, the checkbox can be referred to as previewCheckbox - * from hereon. - */ - private void addCheckbox(String label, boolean defaultValue, boolean isPreview) { - String label2 = label; - if (label2.indexOf('_')!=-1) - label2 = label2.replace('_', ' '); - if (checkbox==null) { - checkbox = new Vector(4); - c.insets = getInsets(15, 20, 0, 0); - } else - c.insets = getInsets(0, 20, 0, 0); - c.gridx = 0; c.gridy = y; - c.gridwidth = 2; - c.anchor = GridBagConstraints.WEST; - Checkbox cb = new Checkbox(label2); - grid.setConstraints(cb, c); - cb.setState(defaultValue); - cb.addItemListener(this); - cb.addKeyListener(this); - add(cb); - checkbox.addElement(cb); - //ij.IJ.write("addCheckbox: "+ y+" "+cbIndex); - if (!isPreview &&(Recorder.record || macro)) //preview checkbox is not recordable - saveLabel(cb, label); - if (isPreview) previewCheckbox = cb; - y++; - } - - /** Adds a checkbox labelled "Preview" for "automatic" preview. - * The reference to this checkbox can be retrieved by getPreviewCheckbox() - * and it provides the additional method previewRunning for optical - * feedback while preview is prepared. - * PlugInFilters can have their "run" method automatically called for - * preview under the following conditions: - * - the PlugInFilter must pass a reference to itself (i.e., "this") as an - * argument to the AddPreviewCheckbox - * - it must implement the DialogListener interface and set the filter - * parameters in the dialogItemChanged method. - * - it must have DIALOG and PREVIEW set in its flags. - * A previewCheckbox is always off when the filter is started and does not get - * recorded by the Macro Recorder. - * - * @param pfr A reference to the PlugInFilterRunner calling the PlugInFilter - * if automatic preview is desired, null otherwise. - */ - public void addPreviewCheckbox(PlugInFilterRunner pfr) { - if (previewCheckbox != null) - return; - ImagePlus imp = WindowManager.getCurrentImage(); - if (imp!=null && imp.isComposite() && ((CompositeImage)imp).getMode()==CompositeImage.COMPOSITE) - return; - this.pfr = pfr; - addCheckbox(previewLabel, false, true); - } - - /** Add the preview checkbox with user-defined label; for details see the - * addPreviewCheckbox method with standard "Preview" label. - * Adds the checkbox when the current image is a CompositeImage - * in "Composite" mode, unlike the one argument version. - * Note that a GenericDialog can have only one PreviewCheckbox. - */ - public void addPreviewCheckbox(PlugInFilterRunner pfr, String label) { - if (previewCheckbox!=null) - return; - //ImagePlus imp = WindowManager.getCurrentImage(); - //if (imp!=null && imp.isComposite() && ((CompositeImage)imp).getMode()==CompositeImage.COMPOSITE) - // return; - previewLabel = label; - this.pfr = pfr; - addCheckbox(previewLabel, false, true); - } - - /** Adds a group of checkboxs using a grid layout. - * @param rows the number of rows - * @param columns the number of columns - * @param labels the labels - * @param defaultValues the initial states - */ - public void addCheckboxGroup(int rows, int columns, String[] labels, boolean[] defaultValues) { - Panel panel = new Panel(); - panel.setLayout(new GridLayout(rows,columns, 5, 0)); - int startCBIndex = cbIndex; - int i1 = 0; - int[] index = new int[labels.length]; - if (checkbox==null) - checkbox = new Vector(12); - boolean addListeners = labels.length<=4; - for (int row=0; row=labels.length) break; - index[i1] = i2; - String label = labels[i1]; - if (label.indexOf('_')!=-1) - label = label.replace('_', ' '); - Checkbox cb = new Checkbox(label); - checkbox.addElement(cb); - cb.setState(defaultValues[i1]); - if (addListeners) cb.addItemListener(this); - if (Recorder.record || macro) - saveLabel(cb, labels[i1]); - panel.add(cb); - i1++; - } - } - c.gridx = 0; c.gridy = y; - c.gridwidth = 2; - c.anchor = GridBagConstraints.WEST; - c.insets = getInsets(10, 0, 0, 0); - grid.setConstraints(panel, c); - add(panel); - y++; - } - - /** Adds a popup menu. - * @param label the label - * @param items the menu items - * @param defaultItem the menu item initially selected - */ - public void addChoice(String label, String[] items, String defaultItem) { - String label2 = label; - if (label2.indexOf('_')!=-1) - label2 = label2.replace('_', ' '); - Label theLabel = makeLabel(label2); - c.gridx = 0; c.gridy = y; - c.anchor = GridBagConstraints.EAST; - c.gridwidth = 1; - if (choice==null) { - choice = new Vector(4); - c.insets = getInsets(5, 0, 5, 0); - } else - c.insets = getInsets(0, 0, 5, 0); - grid.setConstraints(theLabel, c); - add(theLabel); - Choice thisChoice = new Choice(); - thisChoice.addKeyListener(this); - thisChoice.addItemListener(this); - for (int i=0; i=0) - theLabel = new MultiLineLabel(text); - else - theLabel = new Label(text); - //theLabel.addKeyListener(this); - c.gridx = 0; c.gridy = y; - c.gridwidth = 2; - c.anchor = GridBagConstraints.WEST; - c.insets = getInsets(text.equals("")?0:10, 20, 0, 0); - grid.setConstraints(theLabel, c); - add(theLabel); - y++; - } - - /** Adds one or two (side by side) text areas. - * @param text1 initial contents of the first text area - * @param text2 initial contents of the second text area or null - * @param rows the number of rows - * @param rows the number of columns - */ - public void addTextAreas(String text1, String text2, int rows, int columns) { - if (textArea1!=null) return; - Panel panel = new Panel(); - textArea1 = new TextArea(text1,rows,columns,TextArea.SCROLLBARS_NONE); - if (IJ.isLinux()) textArea1.setBackground(Color.white); - textArea1.addTextListener(this); - panel.add(textArea1); - if (text2!=null) { - textArea2 = new TextArea(text2,rows,columns,TextArea.SCROLLBARS_NONE); - if (IJ.isLinux()) textArea2.setBackground(Color.white); - panel.add(textArea2); - } - c.gridx = 0; c.gridy = y; - c.gridwidth = 2; - c.anchor = GridBagConstraints.WEST; - c.insets = getInsets(15, 20, 0, 0); - grid.setConstraints(panel, c); - add(panel); - y++; - } - - public void addSlider(String label, double minValue, double maxValue, double defaultValue) { - int columns = 4; - int digits = 0; - String label2 = label; - if (label2.indexOf('_')!=-1) - label2 = label2.replace('_', ' '); - Label theLabel = makeLabel(label2); - c.gridx = 0; c.gridy = y; - c.anchor = GridBagConstraints.EAST; - c.gridwidth = 1; - c.insets = new Insets(0, 0, 3, 0); - grid.setConstraints(theLabel, c); - add(theLabel); - - if (slider==null) { - slider = new Vector(5); - sliderIndexes = new int[MAX_SLIDERS]; - } - Scrollbar s = new Scrollbar(Scrollbar.HORIZONTAL, (int)defaultValue, 1, (int)minValue, (int)maxValue+1); - slider.addElement(s); - s.addAdjustmentListener(this); - s.setUnitIncrement(1); - - if (numberField==null) { - numberField = new Vector(5); - defaultValues = new Vector(5); - defaultText = new Vector(5); - } - if (IJ.isWindows()) columns -= 2; - if (columns<1) columns = 1; - TextField tf = new TextField(IJ.d2s(defaultValue, digits), columns); - if (IJ.isLinux()) tf.setBackground(Color.white); - tf.addActionListener(this); - tf.addTextListener(this); - tf.addFocusListener(this); - tf.addKeyListener(this); - numberField.addElement(tf); - sliderIndexes[slider.size()-1] = numberField.size()-1; - defaultValues.addElement(new Double(defaultValue)); - defaultText.addElement(tf.getText()); - tf.setEditable(true); - //if (firstNumericField && firstSlider) tf.selectAll(); - firstSlider = false; - - Panel panel = new Panel(); - GridBagLayout pgrid = new GridBagLayout(); - GridBagConstraints pc = new GridBagConstraints(); - panel.setLayout(pgrid); - // label - //pc.insets = new Insets(5, 0, 0, 0); - //pc.gridx = 0; pc.gridy = 0; - //pc.gridwidth = 1; - //pc.anchor = GridBagConstraints.EAST; - //pgrid.setConstraints(theLabel, pc); - //panel.add(theLabel); - // slider - pc.gridx = 0; pc.gridy = 0; - pc.gridwidth = 1; - pc.ipadx = 75; - pc.anchor = GridBagConstraints.WEST; - pgrid.setConstraints(s, pc); - panel.add(s); - pc.ipadx = 0; // reset - // text field - pc.gridx = 1; - pc.insets = new Insets(5, 5, 0, 0); - pc.anchor = GridBagConstraints.EAST; - pgrid.setConstraints(tf, pc); - panel.add(tf); - - grid.setConstraints(panel, c); - c.gridx = 1; c.gridy = y; - c.gridwidth = 1; - c.anchor = GridBagConstraints.WEST; - c.insets = new Insets(0, 0, 0, 0); - grid.setConstraints(panel, c); - add(panel); - y++; - if (Recorder.record || macro) - saveLabel(tf, label); - } - - /** Adds a Panel to the dialog. */ - public void addPanel(Panel panel) { - addPanel(panel , GridBagConstraints.WEST, new Insets(5, 0, 0, 0)); - } - - /** Adds a Panel to the dialog with custom contraint and insets. The - defaults are GridBagConstraints.WEST (left justified) and - "new Insets(5, 0, 0, 0)" (5 pixels of padding at the top). */ - public void addPanel(Panel panel, int contraints, Insets insets) { - c.gridx = 0; c.gridy = y; - c.gridwidth = 2; - c.anchor = contraints; - c.insets = insets; - grid.setConstraints(panel, c); - add(panel); - y++; - } - - /** Set the insets (margins), in pixels, that will be - used for the next component added to the dialog. -
-    Default insets:
-        addMessage: 0,20,0 (empty string) or 10,20,0
-        addCheckbox: 15,20,0 (first checkbox) or 0,20,0
-        addCheckboxGroup: 10,0,0 
-        addNumericField: 5,0,3 (first field) or 0,0,3
-        addStringField: 5,0,5 (first field) or 0,0,5
-        addChoice: 5,0,5 (first field) or 0,0,5
-     
- */ - public void setInsets(int top, int left, int bottom) { - topInset = top; - leftInset = left; - bottomInset = bottom; - customInsets = true; - } - - /** Sets a replacement label for the "OK" button. */ - public void setOKLabel(String label) { - okLabel = label; - } - - /** Make this a "Yes No Cancel" dialog. */ - public void enableYesNoCancel() { - enableYesNoCancel(" Yes ", " No "); - } - - /** Make this a "Yes No Cancel" dialog with custom labels. Here is an example: -
-        GenericDialog gd = new GenericDialog("YesNoCancel Demo");
-        gd.addMessage("This is a custom YesNoCancel dialog");
-        gd.enableYesNoCancel("Do something", "Do something else");
-        gd.showDialog();
-        if (gd.wasCanceled())
-            IJ.log("User clicked 'Cancel'");
-        else if (gd.wasOKed())
-            IJ. log("User clicked 'Yes'");
-        else
-            IJ. log("User clicked 'No'");
-    	
- */ - public void enableYesNoCancel(String yesLabel, String noLabel) { - this.yesLabel = yesLabel; - this.noLabel = noLabel; - yesNoCancel = true; - } - - /** No not display "Cancel" button. */ - public void hideCancelButton() { - hideCancelButton = true; - } - - Insets getInsets(int top, int left, int bottom, int right) { - if (customInsets) { - customInsets = false; - return new Insets(topInset, leftInset, bottomInset, 0); - } else - return new Insets(top, left, bottom, right); - } - - /** Add an Object implementing the DialogListener interface. This object will - * be notified by its dialogItemChanged method of input to the dialog. The first - * DialogListener will be also called after the user has typed 'OK' or if the - * dialog has been invoked by a macro; it should read all input fields of the - * dialog. - * For other listeners, the OK button will not cause a call to dialogItemChanged; - * the CANCEL button will never cause such a call. - * @param dl the Object that wants to listen. - */ - public void addDialogListener(DialogListener dl) { - if (dialogListeners == null) - dialogListeners = new Vector(); - dialogListeners.addElement(dl); - if (IJ.debugMode) IJ.log("GenericDialog: Listener added: "+dl); - } - - /** Returns true if the user clicked on "Cancel". */ - public boolean wasCanceled() { - if (wasCanceled) - Macro.abort(); - return wasCanceled; - } - - /** Returns true if the user has clicked on "OK" or a macro is running. */ - public boolean wasOKed() { - return wasOKed || macro; - } - - /** Returns the contents of the next numeric field. */ - public double getNextNumber() { - if (numberField==null) - return -1.0; - TextField tf = (TextField)numberField.elementAt(nfIndex); - String theText = tf.getText(); - String label=null; - if (macro) { - label = (String)labels.get((Object)tf); - theText = Macro.getValue(macroOptions, label, theText); - //IJ.write("getNextNumber: "+label+" "+theText); - } - String originalText = (String)defaultText.elementAt(nfIndex); - double defaultValue = ((Double)(defaultValues.elementAt(nfIndex))).doubleValue(); - double value; - if (theText.equals(originalText)) - value = defaultValue; - else { - Double d = getValue(theText); - if (d!=null) - value = d.doubleValue(); - else { - // Is the value a macro variable? - if (theText.startsWith("&")) theText = theText.substring(1); - Interpreter interp = Interpreter.getInstance(); - value = interp!=null?interp.getVariable(theText):Double.NaN; - if (Double.isNaN(value)) { - invalidNumber = true; - errorMessage = "\""+theText+"\" is an invalid number"; - value = 0.0; - if (macro) { - IJ.error("Macro Error", "Numeric value expected in run() function\n \n" - +" Dialog box title: \""+getTitle()+"\"\n" - +" Key: \""+label.toLowerCase(Locale.US)+"\"\n" - +" Value or variable name: \""+theText+"\""); - } - } - } - } - if (recorderOn) - recordOption(tf, trim(theText)); - nfIndex++; - return value; - } - - private String trim(String value) { - if (value.endsWith(".0")) - value = value.substring(0, value.length()-2); - if (value.endsWith(".00")) - value = value.substring(0, value.length()-3); - return value; - } - - private void recordOption(Component component, String value) { - String label = (String)labels.get((Object)component); - if (value.equals("")) value = "[]"; - Recorder.recordOption(label, value); - } - - private void recordCheckboxOption(Checkbox cb) { - String label = (String)labels.get((Object)cb); - if (label!=null) { - if (cb.getState()) // checked - Recorder.recordOption(label); - else if (Recorder.getCommandOptions()==null) - Recorder.recordOption(" "); - } - } - - protected Double getValue(String text) { - Double d; - try {d = new Double(text);} - catch (NumberFormatException e){ - d = null; - } - return d; - } - - /** Returns true if one or more of the numeric fields contained an - invalid number. Must be called after one or more calls to getNextNumber(). */ - public boolean invalidNumber() { - boolean wasInvalid = invalidNumber; - invalidNumber = false; - return wasInvalid; - } - - /** Returns an error message if getNextNumber was unable to convert a - string into a number, otherwise, returns null. */ - public String getErrorMessage() { - return errorMessage; - } - - /** Returns the contents of the next text field. */ - public String getNextString() { - String theText; - if (stringField==null) - return ""; - TextField tf = (TextField)(stringField.elementAt(sfIndex)); - theText = tf.getText(); - if (macro) { - String label = (String)labels.get((Object)tf); - theText = Macro.getValue(macroOptions, label, theText); - if (theText!=null && (theText.startsWith("&")||label.toLowerCase(Locale.US).startsWith(theText))) { - // Is the value a macro variable? - if (theText.startsWith("&")) theText = theText.substring(1); - Interpreter interp = Interpreter.getInstance(); - String s = interp!=null?interp.getStringVariable(theText):null; - if (s!=null) theText = s; - } - } - if (recorderOn) - recordOption(tf, theText); - sfIndex++; - return theText; - } - - /** Returns the state of the next checkbox. */ - public boolean getNextBoolean() { - if (checkbox==null) - return false; - Checkbox cb = (Checkbox)(checkbox.elementAt(cbIndex)); - if (recorderOn) - recordCheckboxOption(cb); - boolean state = cb.getState(); - if (macro) { - String label = (String)labels.get((Object)cb); - String key = Macro.trimKey(label); - state = isMatch(macroOptions, key+" "); - } - cbIndex++; - return state; - } - - // Returns true if s2 is in s1 and not in a bracketed literal (e.g., "[literal]") - boolean isMatch(String s1, String s2) { - if (s1.startsWith(s2)) - return true; - s2 = " " + s2; - int len1 = s1.length(); - int len2 = s2.length(); - boolean match, inLiteral=false; - char c; - for (int i=0; i1&&s1.charAt(i-1)=='=')) - continue; - match = true; - for (int j=0; j0) { - resetCounters(); - ((DialogListener)dialogListeners.elementAt(0)).dialogItemChanged(this,null); - recorderOn = false; - } - resetCounters(); - } - - /** Reset the counters before reading the dialog parameters */ - private void resetCounters() { - nfIndex = 0; // prepare for readout - sfIndex = 0; - cbIndex = 0; - choiceIndex = 0; - textAreaIndex = 0; - invalidNumber = false; -} - -/** Returns the Vector containing the numeric TextFields. */ - public Vector getNumericFields() { - return numberField; - } - - /** Returns the Vector containing the string TextFields. */ - public Vector getStringFields() { - return stringField; - } - - /** Returns the Vector containing the Checkboxes. */ - public Vector getCheckboxes() { - return checkbox; - } - - /** Returns the Vector containing the Choices. */ - public Vector getChoices() { - return choice; - } - - /** Returns the Vector containing the sliders (Scrollbars). */ - public Vector getSliders() { - return slider; - } - - /** Returns a reference to textArea1. */ - public TextArea getTextArea1() { - return textArea1; - } - - /** Returns a reference to textArea2. */ - public TextArea getTextArea2() { - return textArea2; - } - - /** Returns a reference to the Label or MultiLineLabel created by the - last addMessage() call, or null if addMessage() was not called. */ - public Component getMessage() { - return theLabel; - } - - /** Returns a reference to the Preview Checkbox. */ - public Checkbox getPreviewCheckbox() { - return previewCheckbox; - } - - /** Returns references to the "OK" ("Yes"), "Cancel", - and if present, "No" buttons as an array. */ - public Button[] getButtons() { - Button[] buttons = new Button[3]; - buttons[0] = okay; - buttons[1] = cancel; - buttons[2] = no; - return buttons; - } - - /** Used by PlugInFilterRunner to provide visable feedback whether preview - is running or not by switching from "Preview" to "wait..." - */ - public void previewRunning(boolean isRunning) { - if (previewCheckbox!=null) { - previewCheckbox.setLabel(isRunning ? previewRunning : previewLabel); - if (IJ.isMacOSX()) repaint(); //workaround OSX 10.4 refresh bug - } - } - - /** Display dialog centered on the primary screen? */ - public void centerDialog(boolean b) { - centerDialog = b; - } - - protected void setup() { - } - - public void actionPerformed(ActionEvent e) { - Object source = e.getSource(); - if (source==okay || source==cancel | source==no) { - wasCanceled = source==cancel; - wasOKed = source==okay; - dispose(); - } else if (source==help) - showHelp(); - else - notifyListeners(e); - } - - public void textValueChanged(TextEvent e) { - notifyListeners(e); - if (slider==null) return; - Object source = e.getSource(); - for (int i=0; i + * public class Generic_Dialog_Example implements PlugIn { + * static String title="Example"; + * static int width=512,height=512; + * public void run(String arg) { + * GenericDialog gd = new GenericDialog("New Image"); + * gd.addStringField("Title: ", title); + * gd.addNumericField("Width: ", width, 0); + * gd.addNumericField("Height: ", height, 0); + * gd.showDialog(); + * if (gd.wasCanceled()) return; + * title = gd.getNextString(); + * width = (int)gd.getNextNumber(); + * height = (int)gd.getNextNumber(); + * IJ.newImage(title, "8-bit", width, height, 1); + * } + * } + * +* To work with macros, the first word of each component label must be +* unique. If this is not the case, add underscores, which will be converted +* to spaces when the dialog is displayed. For example, change the checkbox labels +* "Show Quality" and "Show Residue" to "Show_Quality" and "Show_Residue". +*/ +public class GenericDialog extends Dialog implements ActionListener, TextListener, +FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener { + + public static final int MAX_SLIDERS = 25; + protected Vector numberField, stringField, checkbox, choice, slider; + protected TextArea textArea1, textArea2; + protected Vector defaultValues,defaultText; + protected Component theLabel; + private Button cancel, okay, no, help; + private String okLabel = " OK "; + private boolean wasCanceled, wasOKed; + private int y; + private int nfIndex, sfIndex, cbIndex, choiceIndex, textAreaIndex; + private GridBagLayout grid; + private GridBagConstraints c; + private boolean firstNumericField=true; + private boolean firstSlider=true; + private boolean invalidNumber; + private String errorMessage; + private boolean firstPaint = true; + private Hashtable labels; + private boolean macro; + private String macroOptions; + private int topInset, leftInset, bottomInset; + private boolean customInsets; + private int[] sliderIndexes; + private Checkbox previewCheckbox; // the "Preview" Checkbox, if any + private Vector dialogListeners; // the Objects to notify on user input + private PlugInFilterRunner pfr; // the PlugInFilterRunner for automatic preview + private String previewLabel = " Preview"; + private final static String previewRunning = "wait..."; + private boolean recorderOn; // whether recording is allowed + private boolean yesNoCancel; + private char echoChar; + private boolean hideCancelButton; + private boolean centerDialog = true; + private String helpURL; + private String yesLabel, noLabel; + + /** Creates a new GenericDialog with the specified title. Uses the current image + image window as the parent frame or the ImageJ frame if no image windows + are open. Dialog parameters are recorded by ImageJ's command recorder but + this requires that the first word of each label be unique. */ + public GenericDialog(String title) { + this(title, WindowManager.getCurrentImage()!=null? + (Frame)WindowManager.getCurrentImage().getWindow():IJ.getInstance()!=null?IJ.getInstance():new Frame()); + } + + /** Creates a new GenericDialog using the specified title and parent frame. */ + public GenericDialog(String title, Frame parent) { + super(parent==null?new Frame():parent, title, true); + if (Prefs.blackCanvas) { + setForeground(SystemColor.controlText); + setBackground(SystemColor.control); + } + //if (IJ.isLinux()) + // setBackground(new Color(238, 238, 238)); + grid = new GridBagLayout(); + c = new GridBagConstraints(); + setLayout(grid); + macroOptions = Macro.getOptions(); + macro = macroOptions!=null; + addKeyListener(this); + addWindowListener(this); + } + + //void showFields(String id) { + // String s = id+": "; + // for (int i=0; i0) { + if (label.charAt(0)==' ') + label = label.trim(); + labels.put(component, label); + } + } + + /** Adds an 8 column text field. + * @param label the label + * @param defaultText the text initially displayed + */ + public void addStringField(String label, String defaultText) { + addStringField(label, defaultText, 8); + } + + /** Adds a text field. + * @param label the label + * @param defaultText text initially displayed + * @param columns width of the text field + */ + public void addStringField(String label, String defaultText, int columns) { + String label2 = label; + if (label2.indexOf('_')!=-1) + label2 = label2.replace('_', ' '); + Label theLabel = makeLabel(label2); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.gridwidth = 1; + boolean custom = customInsets; + if (stringField==null) { + stringField = new Vector(4); + c.insets = getInsets(5, 0, 5, 0); + } else + c.insets = getInsets(0, 0, 5, 0); + grid.setConstraints(theLabel, c); + add(theLabel); + if (custom) { + if (stringField.size()==0) + c.insets = getInsets(5, 0, 5, 0); + else + c.insets = getInsets(0, 0, 5, 0); + } + TextField tf = new TextField(defaultText, columns); + if (IJ.isLinux()) tf.setBackground(Color.white); + tf.setEchoChar(echoChar); + echoChar = 0; + tf.addActionListener(this); + tf.addTextListener(this); + tf.addFocusListener(this); + tf.addKeyListener(this); + c.gridx = 1; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + grid.setConstraints(tf, c); + tf.setEditable(true); + add(tf); + stringField.addElement(tf); + if (Recorder.record || macro) + saveLabel(tf, label); + y++; + } + + /** Sets the echo character for the next string field. */ + public void setEchoChar(char echoChar) { + this.echoChar = echoChar; + } + + /** Adds a checkbox. + * @param label the label + * @param defaultValue the initial state + */ + public void addCheckbox(String label, boolean defaultValue) { + addCheckbox(label, defaultValue, false); + } + + /** Adds a checkbox; does not make it recordable if isPreview is true. + * With isPreview true, the checkbox can be referred to as previewCheckbox + * from hereon. + */ + private void addCheckbox(String label, boolean defaultValue, boolean isPreview) { + String label2 = label; + if (label2.indexOf('_')!=-1) + label2 = label2.replace('_', ' '); + if (checkbox==null) { + checkbox = new Vector(4); + c.insets = getInsets(15, 20, 0, 0); + } else + c.insets = getInsets(0, 20, 0, 0); + c.gridx = 0; c.gridy = y; + c.gridwidth = 2; + c.anchor = GridBagConstraints.WEST; + Checkbox cb = new Checkbox(label2); + grid.setConstraints(cb, c); + cb.setState(defaultValue); + cb.addItemListener(this); + cb.addKeyListener(this); + add(cb); + checkbox.addElement(cb); + //ij.IJ.write("addCheckbox: "+ y+" "+cbIndex); + if (!isPreview &&(Recorder.record || macro)) //preview checkbox is not recordable + saveLabel(cb, label); + if (isPreview) previewCheckbox = cb; + y++; + } + + /** Adds a checkbox labelled "Preview" for "automatic" preview. + * The reference to this checkbox can be retrieved by getPreviewCheckbox() + * and it provides the additional method previewRunning for optical + * feedback while preview is prepared. + * PlugInFilters can have their "run" method automatically called for + * preview under the following conditions: + * - the PlugInFilter must pass a reference to itself (i.e., "this") as an + * argument to the AddPreviewCheckbox + * - it must implement the DialogListener interface and set the filter + * parameters in the dialogItemChanged method. + * - it must have DIALOG and PREVIEW set in its flags. + * A previewCheckbox is always off when the filter is started and does not get + * recorded by the Macro Recorder. + * + * @param pfr A reference to the PlugInFilterRunner calling the PlugInFilter + * if automatic preview is desired, null otherwise. + */ + public void addPreviewCheckbox(PlugInFilterRunner pfr) { + if (previewCheckbox != null) + return; + ImagePlus imp = WindowManager.getCurrentImage(); + if (imp!=null && imp.isComposite() && ((CompositeImage)imp).getMode()==CompositeImage.COMPOSITE) + return; + this.pfr = pfr; + addCheckbox(previewLabel, false, true); + } + + /** Add the preview checkbox with user-defined label; for details see the + * addPreviewCheckbox method with standard "Preview" label. + * Adds the checkbox when the current image is a CompositeImage + * in "Composite" mode, unlike the one argument version. + * Note that a GenericDialog can have only one PreviewCheckbox. + */ + public void addPreviewCheckbox(PlugInFilterRunner pfr, String label) { + if (previewCheckbox!=null) + return; + //ImagePlus imp = WindowManager.getCurrentImage(); + //if (imp!=null && imp.isComposite() && ((CompositeImage)imp).getMode()==CompositeImage.COMPOSITE) + // return; + previewLabel = label; + this.pfr = pfr; + addCheckbox(previewLabel, false, true); + } + + /** Adds a group of checkboxs using a grid layout. + * @param rows the number of rows + * @param columns the number of columns + * @param labels the labels + * @param defaultValues the initial states + */ + public void addCheckboxGroup(int rows, int columns, String[] labels, boolean[] defaultValues) { + Panel panel = new Panel(); + panel.setLayout(new GridLayout(rows,columns, 5, 0)); + int startCBIndex = cbIndex; + int i1 = 0; + int[] index = new int[labels.length]; + if (checkbox==null) + checkbox = new Vector(12); + boolean addListeners = labels.length<=4; + for (int row=0; row=labels.length) break; + index[i1] = i2; + String label = labels[i1]; + if (label.indexOf('_')!=-1) + label = label.replace('_', ' '); + Checkbox cb = new Checkbox(label); + checkbox.addElement(cb); + cb.setState(defaultValues[i1]); + if (addListeners) cb.addItemListener(this); + if (Recorder.record || macro) + saveLabel(cb, labels[i1]); + panel.add(cb); + i1++; + } + } + c.gridx = 0; c.gridy = y; + c.gridwidth = 2; + c.anchor = GridBagConstraints.WEST; + c.insets = getInsets(10, 0, 0, 0); + grid.setConstraints(panel, c); + add(panel); + y++; + } + + /** Adds a popup menu. + * @param label the label + * @param items the menu items + * @param defaultItem the menu item initially selected + */ + public void addChoice(String label, String[] items, String defaultItem) { + String label2 = label; + if (label2.indexOf('_')!=-1) + label2 = label2.replace('_', ' '); + Label theLabel = makeLabel(label2); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.gridwidth = 1; + if (choice==null) { + choice = new Vector(4); + c.insets = getInsets(5, 0, 5, 0); + } else + c.insets = getInsets(0, 0, 5, 0); + grid.setConstraints(theLabel, c); + add(theLabel); + Choice thisChoice = new Choice(); + thisChoice.addKeyListener(this); + thisChoice.addItemListener(this); + for (int i=0; i=0) + theLabel = new MultiLineLabel(text); + else + theLabel = new Label(text); + //theLabel.addKeyListener(this); + c.gridx = 0; c.gridy = y; + c.gridwidth = 2; + c.anchor = GridBagConstraints.WEST; + c.insets = getInsets(text.equals("")?0:10, 20, 0, 0); + grid.setConstraints(theLabel, c); + add(theLabel); + y++; + } + + /** Adds one or two (side by side) text areas. + * @param text1 initial contents of the first text area + * @param text2 initial contents of the second text area or null + * @param rows the number of rows + * @param rows the number of columns + */ + public void addTextAreas(String text1, String text2, int rows, int columns) { + if (textArea1!=null) return; + Panel panel = new Panel(); + textArea1 = new TextArea(text1,rows,columns,TextArea.SCROLLBARS_NONE); + if (IJ.isLinux()) textArea1.setBackground(Color.white); + textArea1.addTextListener(this); + panel.add(textArea1); + if (text2!=null) { + textArea2 = new TextArea(text2,rows,columns,TextArea.SCROLLBARS_NONE); + if (IJ.isLinux()) textArea2.setBackground(Color.white); + panel.add(textArea2); + } + c.gridx = 0; c.gridy = y; + c.gridwidth = 2; + c.anchor = GridBagConstraints.WEST; + c.insets = getInsets(15, 20, 0, 0); + grid.setConstraints(panel, c); + add(panel); + y++; + } + + public void addSlider(String label, double minValue, double maxValue, double defaultValue) { + int columns = 4; + int digits = 0; + String label2 = label; + if (label2.indexOf('_')!=-1) + label2 = label2.replace('_', ' '); + Label theLabel = makeLabel(label2); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.gridwidth = 1; + c.insets = new Insets(0, 0, 3, 0); + grid.setConstraints(theLabel, c); + add(theLabel); + + if (slider==null) { + slider = new Vector(5); + sliderIndexes = new int[MAX_SLIDERS]; + } + Scrollbar s = new Scrollbar(Scrollbar.HORIZONTAL, (int)defaultValue, 1, (int)minValue, (int)maxValue+1); + slider.addElement(s); + s.addAdjustmentListener(this); + s.setUnitIncrement(1); + + if (numberField==null) { + numberField = new Vector(5); + defaultValues = new Vector(5); + defaultText = new Vector(5); + } + if (IJ.isWindows()) columns -= 2; + if (columns<1) columns = 1; + TextField tf = new TextField(IJ.d2s(defaultValue, digits), columns); + if (IJ.isLinux()) tf.setBackground(Color.white); + tf.addActionListener(this); + tf.addTextListener(this); + tf.addFocusListener(this); + tf.addKeyListener(this); + numberField.addElement(tf); + sliderIndexes[slider.size()-1] = numberField.size()-1; + defaultValues.addElement(new Double(defaultValue)); + defaultText.addElement(tf.getText()); + tf.setEditable(true); + //if (firstNumericField && firstSlider) tf.selectAll(); + firstSlider = false; + + Panel panel = new Panel(); + GridBagLayout pgrid = new GridBagLayout(); + GridBagConstraints pc = new GridBagConstraints(); + panel.setLayout(pgrid); + // label + //pc.insets = new Insets(5, 0, 0, 0); + //pc.gridx = 0; pc.gridy = 0; + //pc.gridwidth = 1; + //pc.anchor = GridBagConstraints.EAST; + //pgrid.setConstraints(theLabel, pc); + //panel.add(theLabel); + // slider + pc.gridx = 0; pc.gridy = 0; + pc.gridwidth = 1; + pc.ipadx = 75; + pc.anchor = GridBagConstraints.WEST; + pgrid.setConstraints(s, pc); + panel.add(s); + pc.ipadx = 0; // reset + // text field + pc.gridx = 1; + pc.insets = new Insets(5, 5, 0, 0); + pc.anchor = GridBagConstraints.EAST; + pgrid.setConstraints(tf, pc); + panel.add(tf); + + grid.setConstraints(panel, c); + c.gridx = 1; c.gridy = y; + c.gridwidth = 1; + c.anchor = GridBagConstraints.WEST; + c.insets = new Insets(0, 0, 0, 0); + grid.setConstraints(panel, c); + add(panel); + y++; + if (Recorder.record || macro) + saveLabel(tf, label); + } + + /** Adds a Panel to the dialog. */ + public void addPanel(Panel panel) { + addPanel(panel , GridBagConstraints.WEST, new Insets(5, 0, 0, 0)); + } + + /** Adds a Panel to the dialog with custom contraint and insets. The + defaults are GridBagConstraints.WEST (left justified) and + "new Insets(5, 0, 0, 0)" (5 pixels of padding at the top). */ + public void addPanel(Panel panel, int contraints, Insets insets) { + c.gridx = 0; c.gridy = y; + c.gridwidth = 2; + c.anchor = contraints; + c.insets = insets; + grid.setConstraints(panel, c); + add(panel); + y++; + } + + /** Set the insets (margins), in pixels, that will be + used for the next component added to the dialog. +
+    Default insets:
+        addMessage: 0,20,0 (empty string) or 10,20,0
+        addCheckbox: 15,20,0 (first checkbox) or 0,20,0
+        addCheckboxGroup: 10,0,0 
+        addNumericField: 5,0,3 (first field) or 0,0,3
+        addStringField: 5,0,5 (first field) or 0,0,5
+        addChoice: 5,0,5 (first field) or 0,0,5
+     
+ */ + public void setInsets(int top, int left, int bottom) { + topInset = top; + leftInset = left; + bottomInset = bottom; + customInsets = true; + } + + /** Sets a replacement label for the "OK" button. */ + public void setOKLabel(String label) { + okLabel = label; + } + + /** Make this a "Yes No Cancel" dialog. */ + public void enableYesNoCancel() { + enableYesNoCancel(" Yes ", " No "); + } + + /** Make this a "Yes No Cancel" dialog with custom labels. Here is an example: +
+        GenericDialog gd = new GenericDialog("YesNoCancel Demo");
+        gd.addMessage("This is a custom YesNoCancel dialog");
+        gd.enableYesNoCancel("Do something", "Do something else");
+        gd.showDialog();
+        if (gd.wasCanceled())
+            IJ.log("User clicked 'Cancel'");
+        else if (gd.wasOKed())
+            IJ. log("User clicked 'Yes'");
+        else
+            IJ. log("User clicked 'No'");
+    	
+ */ + public void enableYesNoCancel(String yesLabel, String noLabel) { + this.yesLabel = yesLabel; + this.noLabel = noLabel; + yesNoCancel = true; + } + + /** No not display "Cancel" button. */ + public void hideCancelButton() { + hideCancelButton = true; + } + + Insets getInsets(int top, int left, int bottom, int right) { + if (customInsets) { + customInsets = false; + return new Insets(topInset, leftInset, bottomInset, 0); + } else + return new Insets(top, left, bottom, right); + } + + /** Add an Object implementing the DialogListener interface. This object will + * be notified by its dialogItemChanged method of input to the dialog. The first + * DialogListener will be also called after the user has typed 'OK' or if the + * dialog has been invoked by a macro; it should read all input fields of the + * dialog. + * For other listeners, the OK button will not cause a call to dialogItemChanged; + * the CANCEL button will never cause such a call. + * @param dl the Object that wants to listen. + */ + public void addDialogListener(DialogListener dl) { + if (dialogListeners == null) + dialogListeners = new Vector(); + dialogListeners.addElement(dl); + if (IJ.debugMode) IJ.log("GenericDialog: Listener added: "+dl); + } + + /** Returns true if the user clicked on "Cancel". */ + public boolean wasCanceled() { + if (wasCanceled) + Macro.abort(); + return wasCanceled; + } + + /** Returns true if the user has clicked on "OK" or a macro is running. */ + public boolean wasOKed() { + return wasOKed || macro; + } + + /** Returns the contents of the next numeric field. */ + public double getNextNumber() { + if (numberField==null) + return -1.0; + TextField tf = (TextField)numberField.elementAt(nfIndex); + String theText = tf.getText(); + String label=null; + if (macro) { + label = (String)labels.get((Object)tf); + theText = Macro.getValue(macroOptions, label, theText); + //IJ.write("getNextNumber: "+label+" "+theText); + } + String originalText = (String)defaultText.elementAt(nfIndex); + double defaultValue = ((Double)(defaultValues.elementAt(nfIndex))).doubleValue(); + double value; + if (theText.equals(originalText)) + value = defaultValue; + else { + Double d = getValue(theText); + if (d!=null) + value = d.doubleValue(); + else { + // Is the value a macro variable? + if (theText.startsWith("&")) theText = theText.substring(1); + Interpreter interp = Interpreter.getInstance(); + value = interp!=null?interp.getVariable(theText):Double.NaN; + if (Double.isNaN(value)) { + invalidNumber = true; + errorMessage = "\""+theText+"\" is an invalid number"; + value = 0.0; + if (macro) { + IJ.error("Macro Error", "Numeric value expected in run() function\n \n" + +" Dialog box title: \""+getTitle()+"\"\n" + +" Key: \""+label.toLowerCase(Locale.US)+"\"\n" + +" Value or variable name: \""+theText+"\""); + } + } + } + } + if (recorderOn) + recordOption(tf, trim(theText)); + nfIndex++; + return value; + } + + private String trim(String value) { + if (value.endsWith(".0")) + value = value.substring(0, value.length()-2); + if (value.endsWith(".00")) + value = value.substring(0, value.length()-3); + return value; + } + + private void recordOption(Component component, String value) { + String label = (String)labels.get((Object)component); + if (value.equals("")) value = "[]"; + Recorder.recordOption(label, value); + } + + private void recordCheckboxOption(Checkbox cb) { + String label = (String)labels.get((Object)cb); + if (label!=null) { + if (cb.getState()) // checked + Recorder.recordOption(label); + else if (Recorder.getCommandOptions()==null) + Recorder.recordOption(" "); + } + } + + protected Double getValue(String text) { + Double d; + try {d = new Double(text);} + catch (NumberFormatException e){ + d = null; + } + return d; + } + + /** Returns true if one or more of the numeric fields contained an + invalid number. Must be called after one or more calls to getNextNumber(). */ + public boolean invalidNumber() { + boolean wasInvalid = invalidNumber; + invalidNumber = false; + return wasInvalid; + } + + /** Returns an error message if getNextNumber was unable to convert a + string into a number, otherwise, returns null. */ + public String getErrorMessage() { + return errorMessage; + } + + /** Returns the contents of the next text field. */ + public String getNextString() { + String theText; + if (stringField==null) + return ""; + TextField tf = (TextField)(stringField.elementAt(sfIndex)); + theText = tf.getText(); + if (macro) { + String label = (String)labels.get((Object)tf); + theText = Macro.getValue(macroOptions, label, theText); + if (theText!=null && (theText.startsWith("&")||label.toLowerCase(Locale.US).startsWith(theText))) { + // Is the value a macro variable? + if (theText.startsWith("&")) theText = theText.substring(1); + Interpreter interp = Interpreter.getInstance(); + String s = interp!=null?interp.getStringVariable(theText):null; + if (s!=null) theText = s; + } + } + if (recorderOn) + recordOption(tf, theText); + sfIndex++; + return theText; + } + + /** Returns the state of the next checkbox. */ + public boolean getNextBoolean() { + if (checkbox==null) + return false; + Checkbox cb = (Checkbox)(checkbox.elementAt(cbIndex)); + if (recorderOn) + recordCheckboxOption(cb); + boolean state = cb.getState(); + if (macro) { + String label = (String)labels.get((Object)cb); + String key = Macro.trimKey(label); + state = isMatch(macroOptions, key+" "); + } + cbIndex++; + return state; + } + + // Returns true if s2 is in s1 and not in a bracketed literal (e.g., "[literal]") + boolean isMatch(String s1, String s2) { + if (s1.startsWith(s2)) + return true; + s2 = " " + s2; + int len1 = s1.length(); + int len2 = s2.length(); + boolean match, inLiteral=false; + char c; + for (int i=0; i1&&s1.charAt(i-1)=='=')) + continue; + match = true; + for (int j=0; j0) { + resetCounters(); + ((DialogListener)dialogListeners.elementAt(0)).dialogItemChanged(this,null); + recorderOn = false; + } + resetCounters(); + } + + /** Reset the counters before reading the dialog parameters */ + private void resetCounters() { + nfIndex = 0; // prepare for readout + sfIndex = 0; + cbIndex = 0; + choiceIndex = 0; + textAreaIndex = 0; + invalidNumber = false; +} + +/** Returns the Vector containing the numeric TextFields. */ + public Vector getNumericFields() { + return numberField; + } + + /** Returns the Vector containing the string TextFields. */ + public Vector getStringFields() { + return stringField; + } + + /** Returns the Vector containing the Checkboxes. */ + public Vector getCheckboxes() { + return checkbox; + } + + /** Returns the Vector containing the Choices. */ + public Vector getChoices() { + return choice; + } + + /** Returns the Vector containing the sliders (Scrollbars). */ + public Vector getSliders() { + return slider; + } + + /** Returns a reference to textArea1. */ + public TextArea getTextArea1() { + return textArea1; + } + + /** Returns a reference to textArea2. */ + public TextArea getTextArea2() { + return textArea2; + } + + /** Returns a reference to the Label or MultiLineLabel created by the + last addMessage() call, or null if addMessage() was not called. */ + public Component getMessage() { + return theLabel; + } + + /** Returns a reference to the Preview Checkbox. */ + public Checkbox getPreviewCheckbox() { + return previewCheckbox; + } + + /** Returns references to the "OK" ("Yes"), "Cancel", + and if present, "No" buttons as an array. */ + public Button[] getButtons() { + Button[] buttons = new Button[3]; + buttons[0] = okay; + buttons[1] = cancel; + buttons[2] = no; + return buttons; + } + + /** Used by PlugInFilterRunner to provide visable feedback whether preview + is running or not by switching from "Preview" to "wait..." + */ + public void previewRunning(boolean isRunning) { + if (previewCheckbox!=null) { + previewCheckbox.setLabel(isRunning ? previewRunning : previewLabel); + if (IJ.isMacOSX()) repaint(); //workaround OSX 10.4 refresh bug + } + } + + /** Display dialog centered on the primary screen? */ + public void centerDialog(boolean b) { + centerDialog = b; + } + + protected void setup() { + } + + public void actionPerformed(ActionEvent e) { + Object source = e.getSource(); + if (source==okay || source==cancel | source==no) { + wasCanceled = source==cancel; + wasOKed = source==okay; + dispose(); + } else if (source==help) + showHelp(); + else + notifyListeners(e); + } + + public void textValueChanged(TextEvent e) { + notifyListeners(e); + if (slider==null) return; + Object source = e.getSource(); + for (int i=0; i=frame.x && x<=(frame.x+frame.width)) { - x = x - frame.x; - if (x>255) x = 255; - int index = (int)(x*((double)histogram.length)/HIST_WIDTH); - value.setText(" Value: " + IJ.d2s(cal.getCValue(stats.histMin+index*stats.binSize), digits)); - count.setText(" Count: " + histogram[index]); - } else { - value.setText(""); - count.setText(""); - } - } - - protected void drawHistogram(ImageProcessor ip, boolean fixedRange) { - int x, y; - int maxCount2 = 0; - int mode2 = 0; - int saveModalCount; - - ip.setColor(Color.black); - ip.setLineWidth(1); - decimalPlaces = Analyzer.getPrecision(); - digits = cal.calibrated()||stats.binSize!=1.0?decimalPlaces:0; - saveModalCount = histogram[stats.mode]; - for (int i = 0; i maxCount2) && (i != stats.mode)) { - maxCount2 = histogram[i]; - mode2 = i; - } - newMaxCount = stats.maxCount; - if ((newMaxCount>(maxCount2 * 2)) && (maxCount2 != 0)) { - newMaxCount = (int)(maxCount2 * 1.5); - //histogram[stats.mode] = newMaxCount; - } - if (IJ.shiftKeyDown()) { - logScale = true; - drawLogPlot(yMax>0?yMax:newMaxCount, ip); - } - drawPlot(yMax>0?yMax:newMaxCount, ip); - histogram[stats.mode] = saveModalCount; - x = XMARGIN + 1; - y = YMARGIN + HIST_HEIGHT + 2; - lut.drawUnscaledColorBar(ip, x-1, y, 256, BAR_HEIGHT); - y += BAR_HEIGHT+15; - drawText(ip, x, y, fixedRange); - } - - - /** Scales a threshold level to the range 0-255. */ - int scaleDown(ImageProcessor ip, double threshold) { - double min = ip.getMin(); - double max = ip.getMax(); - if (max>min) - return (int)(((threshold-min)/(max-min))*255.0); - else - return 0; - } - - void drawPlot(int maxCount, ImageProcessor ip) { - if (maxCount==0) maxCount = 1; - frame = new Rectangle(XMARGIN, YMARGIN, HIST_WIDTH, HIST_HEIGHT); - ip.drawRect(frame.x-1, frame.y, frame.width+2, frame.height+1); - int index, y; - for (int i = 0; iHIST_HEIGHT) - y = HIST_HEIGHT; - ip.drawLine(i+XMARGIN, YMARGIN+HIST_HEIGHT, i+XMARGIN, YMARGIN+HIST_HEIGHT-y); - } - } - - void drawLogPlot (int maxCount, ImageProcessor ip) { - frame = new Rectangle(XMARGIN, YMARGIN, HIST_WIDTH, HIST_HEIGHT); - ip.drawRect(frame.x-1, frame.y, frame.width+2, frame.height+1); - double max = Math.log(maxCount); - ip.setColor(Color.gray); - int index, y; - for (int i = 0; iHIST_HEIGHT) - y = HIST_HEIGHT; - ip.drawLine(i+XMARGIN, YMARGIN+HIST_HEIGHT, i+XMARGIN, YMARGIN+HIST_HEIGHT-y); - } - ip.setColor(Color.black); - } - - void drawText(ImageProcessor ip, int x, int y, boolean fixedRange) { - ip.setFont(new Font("SansSerif",Font.PLAIN,12)); - ip.setAntialiasedText(true); - double hmin = cal.getCValue(stats.histMin); - double hmax = cal.getCValue(stats.histMax); - double range = hmax-hmin; - if (fixedRange&&!cal.calibrated()&&hmin==0&&hmax==255) - range = 256; - ip.drawString(d2s(hmin), x - 4, y); - ip.drawString(d2s(hmax), x + HIST_WIDTH - getWidth(hmax, ip) + 10, y); - - double binWidth = range/stats.nBins; - binWidth = Math.abs(binWidth); - boolean showBins = binWidth!=1.0 || !fixedRange; - int col1 = XMARGIN + 5; - int col2 = XMARGIN + HIST_WIDTH/2; - int row1 = y+25; - if (showBins) row1 -= 8; - int row2 = row1 + 15; - int row3 = row2 + 15; - int row4 = row3 + 15; - ip.drawString("Count: " + stats.pixelCount, col1, row1); - ip.drawString("Mean: " + d2s(stats.mean), col1, row2); - ip.drawString("StdDev: " + d2s(stats.stdDev), col1, row3); - ip.drawString("Mode: " + d2s(stats.dmode) + " (" + stats.maxCount + ")", col2, row3); - ip.drawString("Min: " + d2s(stats.min), col2, row1); - ip.drawString("Max: " + d2s(stats.max), col2, row2); - - if (showBins) { - ip.drawString("Bins: " + d2s(stats.nBins), col1, row4); - ip.drawString("Bin Width: " + d2s(binWidth), col2, row4); - } - } - - String d2s(double d) { - if (d==Double.MAX_VALUE||d==-Double.MAX_VALUE) - return "0"; - else if (Double.isNaN(d)) - return("NaN"); - else if (Double.isInfinite(d)) - return("Infinity"); - else if ((int)d==d) - return IJ.d2s(d,0); - else - return IJ.d2s(d,decimalPlaces); - } - - int getWidth(double d, ImageProcessor ip) { - return ip.getStringWidth(d2s(d)); - } - - protected void showList() { - StringBuffer sb = new StringBuffer(); - String vheading = stats.binSize==1.0?"value":"bin start"; - if (cal.calibrated() && !cal.isSigned16Bit()) { - for (int i=0; i0?yMax:newMaxCount, ip); - drawPlot(yMax>0?yMax:newMaxCount, ip); - } else - drawPlot(yMax>0?yMax:newMaxCount, ip); - this.imp.updateAndDraw(); - } - - /* - void rescale() { - Graphics g = img.getGraphics(); - plotScale *= 2; - if ((newMaxCount/plotScale)<50) { - plotScale = 1; - frame = new Rectangle(XMARGIN, YMARGIN, HIST_WIDTH, HIST_HEIGHT); - g.setColor(Color.white); - g.fillRect(frame.x, frame.y, frame.width, frame.height); - g.setColor(Color.black); - } - drawPlot(newMaxCount/plotScale, g); - //ImageProcessor ip = new ColorProcessor(img); - //this.imp.setProcessor(null, ip); - this.imp.setImage(img); - } - */ - - public void actionPerformed(ActionEvent e) { - Object b = e.getSource(); - if (b==list) - showList(); - else if (b==copy) - copyToClipboard(); - else if (b==log) - replot(); - } - - public void lostOwnership(Clipboard clipboard, Transferable contents) {} - - public int[] getHistogram() { - return histogram; - } - - public double[] getXValues() { - double[] values = new double[stats.nBins]; - for (int i=0; i=frame.x && x<=(frame.x+frame.width)) { + x = x - frame.x; + if (x>255) x = 255; + int index = (int)(x*((double)histogram.length)/HIST_WIDTH); + value.setText(" Value: " + ResultsTable.d2s(cal.getCValue(stats.histMin+index*stats.binSize), digits)); + count.setText(" Count: " + histogram[index]); + } else { + value.setText(""); + count.setText(""); + } + } + + protected void drawHistogram(ImageProcessor ip, boolean fixedRange) { + int x, y; + int maxCount2 = 0; + int mode2 = 0; + int saveModalCount; + + ip.setColor(Color.black); + ip.setLineWidth(1); + decimalPlaces = Analyzer.getPrecision(); + digits = cal.calibrated()||stats.binSize!=1.0?decimalPlaces:0; + saveModalCount = histogram[stats.mode]; + for (int i = 0; i maxCount2) && (i != stats.mode)) { + maxCount2 = histogram[i]; + mode2 = i; + } + newMaxCount = stats.maxCount; + if ((newMaxCount>(maxCount2 * 2)) && (maxCount2 != 0)) { + newMaxCount = (int)(maxCount2 * 1.5); + //histogram[stats.mode] = newMaxCount; + } + if (IJ.shiftKeyDown()) { + logScale = true; + drawLogPlot(yMax>0?yMax:newMaxCount, ip); + } + drawPlot(yMax>0?yMax:newMaxCount, ip); + histogram[stats.mode] = saveModalCount; + x = XMARGIN + 1; + y = YMARGIN + HIST_HEIGHT + 2; + lut.drawUnscaledColorBar(ip, x-1, y, 256, BAR_HEIGHT); + y += BAR_HEIGHT+15; + drawText(ip, x, y, fixedRange); + } + + + /** Scales a threshold level to the range 0-255. */ + int scaleDown(ImageProcessor ip, double threshold) { + double min = ip.getMin(); + double max = ip.getMax(); + if (max>min) + return (int)(((threshold-min)/(max-min))*255.0); + else + return 0; + } + + void drawPlot(int maxCount, ImageProcessor ip) { + if (maxCount==0) maxCount = 1; + frame = new Rectangle(XMARGIN, YMARGIN, HIST_WIDTH, HIST_HEIGHT); + ip.drawRect(frame.x-1, frame.y, frame.width+2, frame.height+1); + int index, y; + for (int i = 0; iHIST_HEIGHT) + y = HIST_HEIGHT; + ip.drawLine(i+XMARGIN, YMARGIN+HIST_HEIGHT, i+XMARGIN, YMARGIN+HIST_HEIGHT-y); + } + } + + void drawLogPlot (int maxCount, ImageProcessor ip) { + frame = new Rectangle(XMARGIN, YMARGIN, HIST_WIDTH, HIST_HEIGHT); + ip.drawRect(frame.x-1, frame.y, frame.width+2, frame.height+1); + double max = Math.log(maxCount); + ip.setColor(Color.gray); + int index, y; + for (int i = 0; iHIST_HEIGHT) + y = HIST_HEIGHT; + ip.drawLine(i+XMARGIN, YMARGIN+HIST_HEIGHT, i+XMARGIN, YMARGIN+HIST_HEIGHT-y); + } + ip.setColor(Color.black); + } + + void drawText(ImageProcessor ip, int x, int y, boolean fixedRange) { + ip.setFont(new Font("SansSerif",Font.PLAIN,12)); + ip.setAntialiasedText(true); + double hmin = cal.getCValue(stats.histMin); + double hmax = cal.getCValue(stats.histMax); + double range = hmax-hmin; + if (fixedRange&&!cal.calibrated()&&hmin==0&&hmax==255) + range = 256; + ip.drawString(d2s(hmin), x - 4, y); + ip.drawString(d2s(hmax), x + HIST_WIDTH - getWidth(hmax, ip) + 10, y); + + double binWidth = range/stats.nBins; + binWidth = Math.abs(binWidth); + boolean showBins = binWidth!=1.0 || !fixedRange; + int col1 = XMARGIN + 5; + int col2 = XMARGIN + HIST_WIDTH/2; + int row1 = y+25; + if (showBins) row1 -= 8; + int row2 = row1 + 15; + int row3 = row2 + 15; + int row4 = row3 + 15; + ip.drawString("Count: " + stats.pixelCount, col1, row1); + ip.drawString("Mean: " + d2s(stats.mean), col1, row2); + ip.drawString("StdDev: " + d2s(stats.stdDev), col1, row3); + ip.drawString("Mode: " + d2s(stats.dmode) + " (" + stats.maxCount + ")", col2, row3); + ip.drawString("Min: " + d2s(stats.min), col2, row1); + ip.drawString("Max: " + d2s(stats.max), col2, row2); + + if (showBins) { + ip.drawString("Bins: " + d2s(stats.nBins), col1, row4); + ip.drawString("Bin Width: " + d2s(binWidth), col2, row4); + } + } + + String d2s(double d) { + if (d==Double.MAX_VALUE||d==-Double.MAX_VALUE) + return "0"; + else if (Double.isNaN(d)) + return("NaN"); + else if (Double.isInfinite(d)) + return("Infinity"); + else if ((int)d==d) + return ResultsTable.d2s(d,0); + else + return ResultsTable.d2s(d,decimalPlaces); + } + + int getWidth(double d, ImageProcessor ip) { + return ip.getStringWidth(d2s(d)); + } + + protected void showList() { + StringBuffer sb = new StringBuffer(); + String vheading = stats.binSize==1.0?"value":"bin start"; + if (cal.calibrated() && !cal.isSigned16Bit()) { + for (int i=0; i0?yMax:newMaxCount, ip); + drawPlot(yMax>0?yMax:newMaxCount, ip); + } else + drawPlot(yMax>0?yMax:newMaxCount, ip); + this.imp.updateAndDraw(); + } + + /* + void rescale() { + Graphics g = img.getGraphics(); + plotScale *= 2; + if ((newMaxCount/plotScale)<50) { + plotScale = 1; + frame = new Rectangle(XMARGIN, YMARGIN, HIST_WIDTH, HIST_HEIGHT); + g.setColor(Color.white); + g.fillRect(frame.x, frame.y, frame.width, frame.height); + g.setColor(Color.black); + } + drawPlot(newMaxCount/plotScale, g); + //ImageProcessor ip = new ColorProcessor(img); + //this.imp.setProcessor(null, ip); + this.imp.setImage(img); + } + */ + + public void actionPerformed(ActionEvent e) { + Object b = e.getSource(); + if (b==list) + showList(); + else if (b==copy) + copyToClipboard(); + else if (b==log) + replot(); + } + + public void lostOwnership(Clipboard clipboard, Transferable contents) {} + + public int[] getHistogram() { + return histogram; + } + + public double[] getXValues() { + double[] values = new double[stats.nBins]; + for (int i=0; i0) dim.height += vgap; - dim.height += d.height; - } - Insets insets = target.getInsets(); - dim.width += insets.left + insets.right + hgap*2; - dim.height += insets.top + insets.bottom + vgap*2; - return dim; - } - - /** Returns the minimum dimensions for this layout. */ - public Dimension minimumLayoutSize(Container target) { - return preferredLayoutSize(target); - } - - /** Centers the elements in the specified column, if there is any slack.*/ - private void moveComponents(Container target, int x, int y, int width, int height, int nmembers) { - int x2 = 0; - y += height / 2; - for (int i=0; i60) - x2 = x + (width - d.width)/2; - m.setLocation(x2, y); - y += vgap + d.height; - } - } - - /** Lays out the container and calls ImageCanvas.resizeCanvas() - to adjust the image canvas size as needed. */ - public void layoutContainer(Container target) { - Insets insets = target.getInsets(); - int nmembers = target.getComponentCount(); - Dimension d; - int extraHeight = 0; - for (int i=1; i 0) y += vgap; - y += d.height; - colw = Math.max(colw, d.width); - } - moveComponents(target, x, insets.top + vgap, colw, maxheight - y, nmembers); - } - -} +package ij.gui; +import java.awt.*; +import ij.*; + +/** This is a custom layout manager that supports resizing of zoomed +images. It's based on FlowLayout, but with vertical and centered flow. */ +public class ImageLayout implements LayoutManager { + + int hgap; + int vgap; + ImageCanvas ic; + + /** Creates a new ImageLayout with center alignment and 5 pixel horizontal and vertical gaps. */ + public ImageLayout(ImageCanvas ic) { + this.ic = ic; + this.hgap = 5; + this.vgap = 5; + } + + /** Not used by this class. */ + public void addLayoutComponent(String name, Component comp) { + } + + /** Not used by this class. */ + public void removeLayoutComponent(Component comp) { + } + + /** Returns the preferred dimensions for this layout. */ + public Dimension preferredLayoutSize(Container target) { + Dimension dim = new Dimension(0,0); + int nmembers = target.getComponentCount(); + for (int i=0; i0) dim.height += vgap; + dim.height += d.height; + } + Insets insets = target.getInsets(); + dim.width += insets.left + insets.right + hgap*2; + dim.height += insets.top + insets.bottom + vgap*2; + return dim; + } + + /** Returns the minimum dimensions for this layout. */ + public Dimension minimumLayoutSize(Container target) { + return preferredLayoutSize(target); + } + + /** Centers the elements in the specified column, if there is any slack.*/ + private void moveComponents(Container target, int x, int y, int width, int height, int nmembers) { + int x2 = 0; + y += height / 2; + for (int i=0; i60) + x2 = x + (width - d.width)/2; + m.setLocation(x2, y); + y += vgap + d.height; + } + } + + /** Lays out the container and calls ImageCanvas.resizeCanvas() + to adjust the image canvas size as needed. */ + public void layoutContainer(Container target) { + Insets insets = target.getInsets(); + int nmembers = target.getComponentCount(); + Dimension d; + int extraHeight = 0; + for (int i=1; i 0) y += vgap; + y += d.height; + colw = Math.max(colw, d.width); + } + moveComponents(target, x, insets.top + vgap, colw, maxheight - y, nmembers); + } + +} diff --git a/ij/gui/ImageWindow.java b/ij/gui/ImageWindow.java index eee025786..72e36941b 100644 --- a/ij/gui/ImageWindow.java +++ b/ij/gui/ImageWindow.java @@ -1,609 +1,609 @@ -package ij.gui; - -import java.awt.*; -import java.awt.image.*; -import java.util.Properties; -import java.awt.event.*; -import ij.*; -import ij.process.*; -import ij.io.*; -import ij.measure.*; -import ij.plugin.frame.*; -import ij.macro.Interpreter; - -/** A frame for displaying images. */ -public class ImageWindow extends Frame implements FocusListener, WindowListener, WindowStateListener, MouseWheelListener { - - public static final int MIN_WIDTH = 128; - public static final int MIN_HEIGHT = 32; - - protected ImagePlus imp; - protected ImageJ ij; - protected ImageCanvas ic; - private double initialMagnification = 1; - private int newWidth, newHeight; - protected boolean closed; - private boolean newCanvas; - private boolean unzoomWhenMinimizing = true; - Rectangle maxWindowBounds; // largest possible window on this screen - Rectangle maxBounds; // Size of this window after it is maximized - long setMaxBoundsTime; - - private static final int XINC = 8; - private static final int YINC = 12; - private static final int TEXT_GAP = 10; - private static int xbase = -1; - private static int ybase; - private static int xloc; - private static int yloc; - private static int count; - private static boolean centerOnScreen; - - private int textGap = centerOnScreen?0:TEXT_GAP; - - /** This variable is set false if the user presses the escape key or closes the window. */ - public boolean running; - - /** This variable is set false if the user clicks in this - window, presses the escape key, or closes the window. */ - public boolean running2; - - public ImageWindow(String title) { - super(title); - } - - public ImageWindow(ImagePlus imp) { - this(imp, null); - } - - public ImageWindow(ImagePlus imp, ImageCanvas ic) { - super(imp.getTitle()); - if (Prefs.blackCanvas && getClass().getName().equals("ij.gui.ImageWindow")) { - setForeground(Color.white); - setBackground(Color.black); - } else { - setForeground(Color.black); - if (IJ.isLinux()) - setBackground(ImageJ.backgroundColor); - else - setBackground(Color.white); - } - ij = IJ.getInstance(); - this.imp = imp; - if (ic==null) - {ic=new ImageCanvas(imp); newCanvas=true;} - this.ic = ic; - ImageWindow previousWindow = imp.getWindow(); - setLayout(new ImageLayout(ic)); - add(ic); - addFocusListener(this); - addWindowListener(this); - addWindowStateListener(this); - addKeyListener(ij); - setFocusTraversalKeysEnabled(false); - if (!(this instanceof StackWindow)) - addMouseWheelListener(this); - setResizable(true); - WindowManager.addWindow(this); - imp.setWindow(this); - if (previousWindow!=null) { - if (newCanvas) - setLocationAndSize(false); - else - ic.update(previousWindow.getCanvas()); - Point loc = previousWindow.getLocation(); - setLocation(loc.x, loc.y); - if (!(this instanceof StackWindow)) { - pack(); - show(); - } - boolean unlocked = imp.lockSilently(); - boolean changes = imp.changes; - imp.changes = false; - previousWindow.close(); - imp.changes = changes; - if (unlocked) - imp.unlock(); - WindowManager.setCurrentWindow(this); - } else { - setLocationAndSize(false); - if (ij!=null && !IJ.isMacintosh()) { - Image img = ij.getIconImage(); - if (img!=null) - try {setIconImage(img);} catch (Exception e) {} - } - if (centerOnScreen) { - GUI.center(this); - centerOnScreen = false; - } - if (Interpreter.isBatchMode()) { - WindowManager.setTempCurrentImage(imp); - Interpreter.addBatchModeImage(imp); - } else - show(); - } - } - - private void setLocationAndSize(boolean updating) { - int width = imp.getWidth(); - int height = imp.getHeight(); - Rectangle maxWindow = getMaxWindow(0,0); - //if (maxWindow.x==maxWindow.width) // work around for Linux bug - // maxWindow = new Rectangle(0, maxWindow.y, maxWindow.width, maxWindow.height); - if (WindowManager.getWindowCount()<=1) - xbase = -1; - if (width>maxWindow.width/2 && xbase>maxWindow.x+5+XINC*6) - xbase = -1; - if (xbase==-1) { - count = 0; - xbase = maxWindow.x + 5; - if (width*2<=maxWindow.width) - xbase = maxWindow.x+maxWindow.width/2-width/2; - ybase = maxWindow.y; - xloc = xbase; - yloc = ybase; - } - int x = xloc; - int y = yloc; - xloc += XINC; - yloc += YINC; - count++; - if (count%6==0) { - xloc = xbase; - yloc = ybase; - } - - int sliderHeight = (this instanceof StackWindow)?20:0; - int screenHeight = maxWindow.y+maxWindow.height-sliderHeight; - double mag = 1; - while (xbase+XINC*4+width*mag>maxWindow.x+maxWindow.width || ybase+height*mag>=screenHeight) { - //IJ.log(mag+" "+xbase+" "+width*mag+" "+maxWindow.width); - double mag2 = ImageCanvas.getLowerZoomLevel(mag); - if (mag2==mag) break; - mag = mag2; - } - - if (mag<1.0) { - initialMagnification = mag; - ic.setDrawingSize((int)(width*mag), (int)(height*mag)); - } - ic.setMagnification(mag); - if (y+height*mag>screenHeight) - y = ybase; - if (!updating) setLocation(x, y); - if (Prefs.open100Percent && ic.getMagnification()<1.0) { - while(ic.getMagnification()<1.0) - ic.zoomIn(0, 0); - setSize(Math.min(width, maxWindow.width-x), Math.min(height, screenHeight-y)); - validate(); - } else - pack(); - } - - Rectangle getMaxWindow(int xloc, int yloc) { - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - Rectangle bounds = ge.getMaximumWindowBounds(); - //bounds.x=960; bounds.y=0; bounds.width=960; bounds.height=1200; - if (IJ.debugMode) IJ.log("getMaxWindow: "+bounds+" "+xloc+","+yloc); - if (xloc>bounds.x+bounds.width || yloc>bounds.y+bounds.height) { - Rectangle bounds2 = getSecondaryMonitorBounds(ge, xloc, yloc); - if (bounds2!=null) return bounds2; - } - Dimension ijSize = ij!=null?ij.getSize():new Dimension(0,0); - if (bounds.height>600) { - bounds.y += ijSize.height; - bounds.height -= ijSize.height; - } - return bounds; - } - - private Rectangle getSecondaryMonitorBounds(GraphicsEnvironment ge, int xloc, int yloc) { - //IJ.log("getSecondaryMonitorBounds "+wb); - GraphicsDevice[] gs = ge.getScreenDevices(); - for (int j=0; j1) { - ImageStack stack = imp.getStack(); - int currentSlice = imp.getCurrentSlice(); - s += currentSlice+"/"+nSlices; - String label = stack.getShortSliceLabel(currentSlice); - if (label!=null && label.length()>0) { - if (imp.isHyperStack()) label = label.replace(';', ' '); - s += " (" + label + ")"; - } - if ((this instanceof StackWindow) && running2) { - return s; - } - s += "; "; - } else { - String label = (String)imp.getProperty("Label"); - if (label!=null) { - int newline = label.indexOf('\n'); - if (newline>0) - label = label.substring(0, newline); - int len = label.length(); - if (len>4 && label.charAt(len-4)=='.' && !Character.isDigit(label.charAt(len-1))) - label = label.substring(0,len-4); - if (label.length()>60) - label = label.substring(0, 60); - s = label + "; "; - } - } - int type = imp.getType(); - Calibration cal = imp.getCalibration(); - if (cal.scaled()) { - s += IJ.d2s(imp.getWidth()*cal.pixelWidth,2) + "x" + IJ.d2s(imp.getHeight()*cal.pixelHeight,2) - + " " + cal.getUnits() + " (" + imp.getWidth() + "x" + imp.getHeight() + "); "; - } else - s += imp.getWidth() + "x" + imp.getHeight() + " pixels; "; - double size = ((double)imp.getWidth()*imp.getHeight()*imp.getStackSize())/1024.0; - switch (type) { - case ImagePlus.GRAY8: - case ImagePlus.COLOR_256: - s += "8-bit"; - break; - case ImagePlus.GRAY16: - s += "16-bit"; - size *= 2.0; - break; - case ImagePlus.GRAY32: - s += "32-bit"; - size *= 4.0; - break; - case ImagePlus.COLOR_RGB: - s += "RGB"; - size *= 4.0; - break; - } - if (imp.isInvertedLut()) - s += " (inverting LUT)"; - String s2=null, s3=null; - if (size<1024.0) - {s2=IJ.d2s(size,0); s3="K";} - else if (size<10000.0) - {s2=IJ.d2s(size/1024.0,1); s3="MB";} - else if (size<1048576.0) - {s2=IJ.d2s(Math.round(size/1024.0),0); s3="MB";} - else - {s2=IJ.d2s(size/1048576.0,1); s3="GB";} - if (s2.endsWith(".0")) s2 = s2.substring(0, s2.length()-2); - return s+"; "+s2+s3; - } - - public void paint(Graphics g) { - //if (IJ.debugMode) IJ.log("wPaint: " + imp.getTitle()); - drawInfo(g); - Rectangle r = ic.getBounds(); - int extraWidth = MIN_WIDTH - r.width; - int extraHeight = MIN_HEIGHT - r.height; - if (extraWidth<=0 && extraHeight<=0 && !Prefs.noBorder && !IJ.isLinux()) - g.drawRect(r.x-1, r.y-1, r.width+1, r.height+1); - } - - /** Removes this window from the window list and disposes of it. - Returns false if the user cancels the "save changes" dialog. */ - public boolean close() { - boolean isRunning = running || running2; - running = running2 = false; - if (isRunning) IJ.wait(500); - if (ij==null || IJ.getApplet()!=null || Interpreter.isBatchMode() || IJ.macroRunning()) - imp.changes = false; - if (imp.changes) { - String msg; - String name = imp.getTitle(); - if (name.length()>22) - msg = "Save changes to\n" + "\"" + name + "\"?"; - else - msg = "Save changes to \"" + name + "\"?"; - YesNoCancelDialog d = new YesNoCancelDialog(this, "ImageJ", msg); - if (d.cancelPressed()) - return false; - else if (d.yesPressed()) { - FileSaver fs = new FileSaver(imp); - if (!fs.save()) return false; - } - } - closed = true; - if (WindowManager.getWindowCount()==0) - {xloc = 0; yloc = 0;} - WindowManager.removeWindow(this); - setVisible(false); - if (ij!=null && ij.quitting()) // this may help avoid thread deadlocks - return true; - dispose(); - imp.flush(); - return true; - } - - public ImagePlus getImagePlus() { - return imp; - } - - - void setImagePlus(ImagePlus imp) { - this.imp = imp; - repaint(); - } - - public void updateImage(ImagePlus imp) { - if (imp!=this.imp) - throw new IllegalArgumentException("imp!=this.imp"); - this.imp = imp; - ic.updateImage(imp); - setLocationAndSize(true); - pack(); - repaint(); - maxBounds = getMaximumBounds(); - //if (!IJ.isLinux()) { - setMaximizedBounds(maxBounds); - setMaxBoundsTime = System.currentTimeMillis(); - //} - } - - public ImageCanvas getCanvas() { - return ic; - } - - - static ImagePlus getClipboard() { - return ImagePlus.getClipboard(); - } - - public Rectangle getMaximumBounds() { - double width = imp.getWidth(); - double height = imp.getHeight(); - double iAspectRatio = width/height; - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - Rectangle maxWindow = ge.getMaximumWindowBounds(); - maxWindowBounds = maxWindow; - if (iAspectRatio/((double)maxWindow.width/maxWindow.height)>0.75) { - maxWindow.y += 22; // uncover ImageJ menu bar - maxWindow.height -= 22; - } - Dimension extraSize = getExtraSize(); - double maxWidth = maxWindow.width-extraSize.width; - double maxHeight = maxWindow.height-extraSize.height; - double mAspectRatio = maxWidth/maxHeight; - int wWidth, wHeight; - double mag; - if (iAspectRatio>=mAspectRatio) { - mag = maxWidth/width; - wWidth = maxWindow.width; - wHeight = (int)(height*mag+extraSize.height); - } else { - mag = maxHeight/height; - wHeight = maxWindow.height; - wWidth = (int)(width*mag+extraSize.width); - } - int xloc = (int)(maxWidth-wWidth)/2; - if (xloc<0) xloc = 0; - return new Rectangle(xloc, maxWindow.y, wWidth, wHeight); - } - - Dimension getExtraSize() { - Insets insets = getInsets(); - int extraWidth = insets.left+insets.right + 10; - int extraHeight = insets.top+insets.bottom + 10; - if (extraHeight==20) extraHeight = 42; - int members = getComponentCount(); - //if (IJ.debugMode) IJ.log("getExtraSize: "+members+" "+insets); - for (int i=1; iic.getMagnification() || aspectRatio<0.5 || aspectRatio>2.0) { - ic.setMagnification2(mag); - ic.setSrcRect(new Rectangle(0, 0, width, height)); - ic.setDrawingSize((int)(width*mag), (int)(height*mag)); - validate(); - unzoomWhenMinimizing = true; - } else - unzoomWhenMinimizing = false; - } - - public void minimize() { - if (unzoomWhenMinimizing) - ic.unzoom(); - unzoomWhenMinimizing = true; - } - - /** Has this window been closed? */ - public boolean isClosed() { - return closed; - } - - public void focusGained(FocusEvent e) { - //IJ.log("focusGained: "+imp.getTitle()); - if (!Interpreter.isBatchMode() && ij!=null && !ij.quitting()) - WindowManager.setCurrentWindow(this); - } - - public void windowActivated(WindowEvent e) { - if (IJ.debugMode) IJ.log("windowActivated: "+imp.getTitle()); - ImageJ ij = IJ.getInstance(); - boolean quitting = ij!=null && ij.quitting(); - if (IJ.isMacintosh() && ij!=null && !quitting) { - IJ.wait(10); // may be needed for Java 1.4 on OS X - setMenuBar(Menus.getMenuBar()); - } - imp.setActivated(); // notify ImagePlus that image has been activated - if (!closed && !quitting && !Interpreter.isBatchMode()) - WindowManager.setCurrentWindow(this); - Frame channels = Channels.getInstance(); - if (channels!=null && imp.isComposite()) - ((Channels)channels).update(); - } - - public void windowClosing(WindowEvent e) { - //IJ.log("windowClosing: "+imp.getTitle()+" "+closed); - if (closed) - return; - if (ij!=null) { - WindowManager.setCurrentWindow(this); - IJ.doCommand("Close"); - } else { - setVisible(false); - dispose(); - WindowManager.removeWindow(this); - } - } - - public void windowStateChanged(WindowEvent e) { - int oldState = e.getOldState(); - int newState = e.getNewState(); - //IJ.log("WSC: "+getBounds()+" "+oldState+" "+newState); - if ((oldState & Frame.MAXIMIZED_BOTH) == 0 && (newState & Frame.MAXIMIZED_BOTH) != 0) - maximize(); - else if ((oldState & Frame.MAXIMIZED_BOTH) != 0 && (newState & Frame.MAXIMIZED_BOTH) == 0) - minimize(); - } - - public void windowClosed(WindowEvent e) {} - public void windowDeactivated(WindowEvent e) {} - public void focusLost(FocusEvent e) {} - public void windowDeiconified(WindowEvent e) {} - public void windowIconified(WindowEvent e) {} - public void windowOpened(WindowEvent e) {} - - public void mouseWheelMoved(MouseWheelEvent event) { - int rotation = event.getWheelRotation(); - int width = imp.getWidth(); - int height = imp.getHeight(); - Rectangle srcRect = ic.getSrcRect(); - int xstart = srcRect.x; - int ystart = srcRect.y; - if (IJ.spaceBarDown() || srcRect.height==height) { - srcRect.x += rotation*Math.max(width/200, 1); - if (srcRect.x<0) srcRect.x = 0; - if (srcRect.x+srcRect.width>width) srcRect.x = width-srcRect.width; - } else { - srcRect.y += rotation*Math.max(height/200, 1); - if (srcRect.y<0) srcRect.y = 0; - if (srcRect.y+srcRect.height>height) srcRect.y = height-srcRect.height; - } - if (srcRect.x!=xstart || srcRect.y!=ystart) - ic.repaint(); - } - - /** Copies the current ROI to the clipboard. The entire - image is copied if there is no ROI. */ - public void copy(boolean cut) { - imp.copy(cut); - } - - - public void paste() { - imp.paste(); - } - - /** This method is called by ImageCanvas.mouseMoved(MouseEvent). - @see ij.gui.ImageCanvas#mouseMoved - */ - public void mouseMoved(int x, int y) { - imp.mouseMoved(x, y); - } - - public String toString() { - return imp.getTitle(); - } - - /** Causes the next image to be opened to be centered on the screen - and displayed without informational text above the image. */ - public static void centerNextImage() { - centerOnScreen = true; - } - - /** Moves and resizes this window. Changes the - magnification so the image fills the window. */ - public void setLocationAndSize(int x, int y, int width, int height) { - setBounds(x, y, width, height); - getCanvas().fitToWindow(); - pack(); - } - - /** Overrides the setBounds() method in Component so - we can find out when the window is resized. */ - //public void setBounds(int x, int y, int width, int height) { - // super.setBounds(x, y, width, height); - // ic.resizeSourceRect(width, height); - //} - -} //class ImageWindow - +package ij.gui; + +import java.awt.*; +import java.awt.image.*; +import java.util.Properties; +import java.awt.event.*; +import ij.*; +import ij.process.*; +import ij.io.*; +import ij.measure.*; +import ij.plugin.frame.*; +import ij.macro.Interpreter; + +/** A frame for displaying images. */ +public class ImageWindow extends Frame implements FocusListener, WindowListener, WindowStateListener, MouseWheelListener { + + public static final int MIN_WIDTH = 128; + public static final int MIN_HEIGHT = 32; + + protected ImagePlus imp; + protected ImageJ ij; + protected ImageCanvas ic; + private double initialMagnification = 1; + private int newWidth, newHeight; + protected boolean closed; + private boolean newCanvas; + private boolean unzoomWhenMinimizing = true; + Rectangle maxWindowBounds; // largest possible window on this screen + Rectangle maxBounds; // Size of this window after it is maximized + long setMaxBoundsTime; + + private static final int XINC = 8; + private static final int YINC = 12; + private static final int TEXT_GAP = 10; + private static int xbase = -1; + private static int ybase; + private static int xloc; + private static int yloc; + private static int count; + private static boolean centerOnScreen; + + private int textGap = centerOnScreen?0:TEXT_GAP; + + /** This variable is set false if the user presses the escape key or closes the window. */ + public boolean running; + + /** This variable is set false if the user clicks in this + window, presses the escape key, or closes the window. */ + public boolean running2; + + public ImageWindow(String title) { + super(title); + } + + public ImageWindow(ImagePlus imp) { + this(imp, null); + } + + public ImageWindow(ImagePlus imp, ImageCanvas ic) { + super(imp.getTitle()); + if (Prefs.blackCanvas && getClass().getName().equals("ij.gui.ImageWindow")) { + setForeground(Color.white); + setBackground(Color.black); + } else { + setForeground(Color.black); + if (IJ.isLinux()) + setBackground(ImageJ.backgroundColor); + else + setBackground(Color.white); + } + ij = IJ.getInstance(); + this.imp = imp; + if (ic==null) + {ic=new ImageCanvas(imp); newCanvas=true;} + this.ic = ic; + ImageWindow previousWindow = imp.getWindow(); + setLayout(new ImageLayout(ic)); + add(ic); + addFocusListener(this); + addWindowListener(this); + addWindowStateListener(this); + addKeyListener(ij); + setFocusTraversalKeysEnabled(false); + if (!(this instanceof StackWindow)) + addMouseWheelListener(this); + setResizable(true); + WindowManager.addWindow(this); + imp.setWindow(this); + if (previousWindow!=null) { + if (newCanvas) + setLocationAndSize(false); + else + ic.update(previousWindow.getCanvas()); + Point loc = previousWindow.getLocation(); + setLocation(loc.x, loc.y); + if (!(this instanceof StackWindow)) { + pack(); + show(); + } + boolean unlocked = imp.lockSilently(); + boolean changes = imp.changes; + imp.changes = false; + previousWindow.close(); + imp.changes = changes; + if (unlocked) + imp.unlock(); + WindowManager.setCurrentWindow(this); + } else { + setLocationAndSize(false); + if (ij!=null && !IJ.isMacintosh()) { + Image img = ij.getIconImage(); + if (img!=null) + try {setIconImage(img);} catch (Exception e) {} + } + if (centerOnScreen) { + GUI.center(this); + centerOnScreen = false; + } + if (Interpreter.isBatchMode()) { + WindowManager.setTempCurrentImage(imp); + Interpreter.addBatchModeImage(imp); + } else + show(); + } + } + + private void setLocationAndSize(boolean updating) { + int width = imp.getWidth(); + int height = imp.getHeight(); + Rectangle maxWindow = getMaxWindow(0,0); + //if (maxWindow.x==maxWindow.width) // work around for Linux bug + // maxWindow = new Rectangle(0, maxWindow.y, maxWindow.width, maxWindow.height); + if (WindowManager.getWindowCount()<=1) + xbase = -1; + if (width>maxWindow.width/2 && xbase>maxWindow.x+5+XINC*6) + xbase = -1; + if (xbase==-1) { + count = 0; + xbase = maxWindow.x + 5; + if (width*2<=maxWindow.width) + xbase = maxWindow.x+maxWindow.width/2-width/2; + ybase = maxWindow.y; + xloc = xbase; + yloc = ybase; + } + int x = xloc; + int y = yloc; + xloc += XINC; + yloc += YINC; + count++; + if (count%6==0) { + xloc = xbase; + yloc = ybase; + } + + int sliderHeight = (this instanceof StackWindow)?20:0; + int screenHeight = maxWindow.y+maxWindow.height-sliderHeight; + double mag = 1; + while (xbase+XINC*4+width*mag>maxWindow.x+maxWindow.width || ybase+height*mag>=screenHeight) { + //IJ.log(mag+" "+xbase+" "+width*mag+" "+maxWindow.width); + double mag2 = ImageCanvas.getLowerZoomLevel(mag); + if (mag2==mag) break; + mag = mag2; + } + + if (mag<1.0) { + initialMagnification = mag; + ic.setDrawingSize((int)(width*mag), (int)(height*mag)); + } + ic.setMagnification(mag); + if (y+height*mag>screenHeight) + y = ybase; + if (!updating) setLocation(x, y); + if (Prefs.open100Percent && ic.getMagnification()<1.0) { + while(ic.getMagnification()<1.0) + ic.zoomIn(0, 0); + setSize(Math.min(width, maxWindow.width-x), Math.min(height, screenHeight-y)); + validate(); + } else + pack(); + } + + Rectangle getMaxWindow(int xloc, int yloc) { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + Rectangle bounds = ge.getMaximumWindowBounds(); + //bounds.x=960; bounds.y=0; bounds.width=960; bounds.height=1200; + if (IJ.debugMode) IJ.log("getMaxWindow: "+bounds+" "+xloc+","+yloc); + if (xloc>bounds.x+bounds.width || yloc>bounds.y+bounds.height) { + Rectangle bounds2 = getSecondaryMonitorBounds(ge, xloc, yloc); + if (bounds2!=null) return bounds2; + } + Dimension ijSize = ij!=null?ij.getSize():new Dimension(0,0); + if (bounds.height>600) { + bounds.y += ijSize.height; + bounds.height -= ijSize.height; + } + return bounds; + } + + private Rectangle getSecondaryMonitorBounds(GraphicsEnvironment ge, int xloc, int yloc) { + //IJ.log("getSecondaryMonitorBounds "+wb); + GraphicsDevice[] gs = ge.getScreenDevices(); + for (int j=0; j1) { + ImageStack stack = imp.getStack(); + int currentSlice = imp.getCurrentSlice(); + s += currentSlice+"/"+nSlices; + String label = stack.getShortSliceLabel(currentSlice); + if (label!=null && label.length()>0) { + if (imp.isHyperStack()) label = label.replace(';', ' '); + s += " (" + label + ")"; + } + if ((this instanceof StackWindow) && running2) { + return s; + } + s += "; "; + } else { + String label = (String)imp.getProperty("Label"); + if (label!=null) { + int newline = label.indexOf('\n'); + if (newline>0) + label = label.substring(0, newline); + int len = label.length(); + if (len>4 && label.charAt(len-4)=='.' && !Character.isDigit(label.charAt(len-1))) + label = label.substring(0,len-4); + if (label.length()>60) + label = label.substring(0, 60); + s = label + "; "; + } + } + int type = imp.getType(); + Calibration cal = imp.getCalibration(); + if (cal.scaled()) { + s += IJ.d2s(imp.getWidth()*cal.pixelWidth,2) + "x" + IJ.d2s(imp.getHeight()*cal.pixelHeight,2) + + " " + cal.getUnits() + " (" + imp.getWidth() + "x" + imp.getHeight() + "); "; + } else + s += imp.getWidth() + "x" + imp.getHeight() + " pixels; "; + double size = ((double)imp.getWidth()*imp.getHeight()*imp.getStackSize())/1024.0; + switch (type) { + case ImagePlus.GRAY8: + case ImagePlus.COLOR_256: + s += "8-bit"; + break; + case ImagePlus.GRAY16: + s += "16-bit"; + size *= 2.0; + break; + case ImagePlus.GRAY32: + s += "32-bit"; + size *= 4.0; + break; + case ImagePlus.COLOR_RGB: + s += "RGB"; + size *= 4.0; + break; + } + if (imp.isInvertedLut()) + s += " (inverting LUT)"; + String s2=null, s3=null; + if (size<1024.0) + {s2=IJ.d2s(size,0); s3="K";} + else if (size<10000.0) + {s2=IJ.d2s(size/1024.0,1); s3="MB";} + else if (size<1048576.0) + {s2=IJ.d2s(Math.round(size/1024.0),0); s3="MB";} + else + {s2=IJ.d2s(size/1048576.0,1); s3="GB";} + if (s2.endsWith(".0")) s2 = s2.substring(0, s2.length()-2); + return s+"; "+s2+s3; + } + + public void paint(Graphics g) { + //if (IJ.debugMode) IJ.log("wPaint: " + imp.getTitle()); + drawInfo(g); + Rectangle r = ic.getBounds(); + int extraWidth = MIN_WIDTH - r.width; + int extraHeight = MIN_HEIGHT - r.height; + if (extraWidth<=0 && extraHeight<=0 && !Prefs.noBorder && !IJ.isLinux()) + g.drawRect(r.x-1, r.y-1, r.width+1, r.height+1); + } + + /** Removes this window from the window list and disposes of it. + Returns false if the user cancels the "save changes" dialog. */ + public boolean close() { + boolean isRunning = running || running2; + running = running2 = false; + if (isRunning) IJ.wait(500); + if (ij==null || IJ.getApplet()!=null || Interpreter.isBatchMode() || IJ.macroRunning()) + imp.changes = false; + if (imp.changes) { + String msg; + String name = imp.getTitle(); + if (name.length()>22) + msg = "Save changes to\n" + "\"" + name + "\"?"; + else + msg = "Save changes to \"" + name + "\"?"; + YesNoCancelDialog d = new YesNoCancelDialog(this, "ImageJ", msg); + if (d.cancelPressed()) + return false; + else if (d.yesPressed()) { + FileSaver fs = new FileSaver(imp); + if (!fs.save()) return false; + } + } + closed = true; + if (WindowManager.getWindowCount()==0) + {xloc = 0; yloc = 0;} + WindowManager.removeWindow(this); + setVisible(false); + if (ij!=null && ij.quitting()) // this may help avoid thread deadlocks + return true; + dispose(); + imp.flush(); + return true; + } + + public ImagePlus getImagePlus() { + return imp; + } + + + void setImagePlus(ImagePlus imp) { + this.imp = imp; + repaint(); + } + + public void updateImage(ImagePlus imp) { + if (imp!=this.imp) + throw new IllegalArgumentException("imp!=this.imp"); + this.imp = imp; + ic.updateImage(imp); + setLocationAndSize(true); + pack(); + repaint(); + maxBounds = getMaximumBounds(); + //if (!IJ.isLinux()) { + setMaximizedBounds(maxBounds); + setMaxBoundsTime = System.currentTimeMillis(); + //} + } + + public ImageCanvas getCanvas() { + return ic; + } + + + static ImagePlus getClipboard() { + return ImagePlus.getClipboard(); + } + + public Rectangle getMaximumBounds() { + double width = imp.getWidth(); + double height = imp.getHeight(); + double iAspectRatio = width/height; + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + Rectangle maxWindow = ge.getMaximumWindowBounds(); + maxWindowBounds = maxWindow; + if (iAspectRatio/((double)maxWindow.width/maxWindow.height)>0.75) { + maxWindow.y += 22; // uncover ImageJ menu bar + maxWindow.height -= 22; + } + Dimension extraSize = getExtraSize(); + double maxWidth = maxWindow.width-extraSize.width; + double maxHeight = maxWindow.height-extraSize.height; + double mAspectRatio = maxWidth/maxHeight; + int wWidth, wHeight; + double mag; + if (iAspectRatio>=mAspectRatio) { + mag = maxWidth/width; + wWidth = maxWindow.width; + wHeight = (int)(height*mag+extraSize.height); + } else { + mag = maxHeight/height; + wHeight = maxWindow.height; + wWidth = (int)(width*mag+extraSize.width); + } + int xloc = (int)(maxWidth-wWidth)/2; + if (xloc<0) xloc = 0; + return new Rectangle(xloc, maxWindow.y, wWidth, wHeight); + } + + Dimension getExtraSize() { + Insets insets = getInsets(); + int extraWidth = insets.left+insets.right + 10; + int extraHeight = insets.top+insets.bottom + 10; + if (extraHeight==20) extraHeight = 42; + int members = getComponentCount(); + //if (IJ.debugMode) IJ.log("getExtraSize: "+members+" "+insets); + for (int i=1; iic.getMagnification() || aspectRatio<0.5 || aspectRatio>2.0) { + ic.setMagnification2(mag); + ic.setSrcRect(new Rectangle(0, 0, width, height)); + ic.setDrawingSize((int)(width*mag), (int)(height*mag)); + validate(); + unzoomWhenMinimizing = true; + } else + unzoomWhenMinimizing = false; + } + + public void minimize() { + if (unzoomWhenMinimizing) + ic.unzoom(); + unzoomWhenMinimizing = true; + } + + /** Has this window been closed? */ + public boolean isClosed() { + return closed; + } + + public void focusGained(FocusEvent e) { + //IJ.log("focusGained: "+imp.getTitle()); + if (!Interpreter.isBatchMode() && ij!=null && !ij.quitting()) + WindowManager.setCurrentWindow(this); + } + + public void windowActivated(WindowEvent e) { + if (IJ.debugMode) IJ.log("windowActivated: "+imp.getTitle()); + ImageJ ij = IJ.getInstance(); + boolean quitting = ij!=null && ij.quitting(); + if (IJ.isMacintosh() && ij!=null && !quitting) { + IJ.wait(10); // may be needed for Java 1.4 on OS X + setMenuBar(Menus.getMenuBar()); + } + imp.setActivated(); // notify ImagePlus that image has been activated + if (!closed && !quitting && !Interpreter.isBatchMode()) + WindowManager.setCurrentWindow(this); + Frame channels = Channels.getInstance(); + if (channels!=null && imp.isComposite()) + ((Channels)channels).update(); + } + + public void windowClosing(WindowEvent e) { + //IJ.log("windowClosing: "+imp.getTitle()+" "+closed); + if (closed) + return; + if (ij!=null) { + WindowManager.setCurrentWindow(this); + IJ.doCommand("Close"); + } else { + setVisible(false); + dispose(); + WindowManager.removeWindow(this); + } + } + + public void windowStateChanged(WindowEvent e) { + int oldState = e.getOldState(); + int newState = e.getNewState(); + //IJ.log("WSC: "+getBounds()+" "+oldState+" "+newState); + if ((oldState & Frame.MAXIMIZED_BOTH) == 0 && (newState & Frame.MAXIMIZED_BOTH) != 0) + maximize(); + else if ((oldState & Frame.MAXIMIZED_BOTH) != 0 && (newState & Frame.MAXIMIZED_BOTH) == 0) + minimize(); + } + + public void windowClosed(WindowEvent e) {} + public void windowDeactivated(WindowEvent e) {} + public void focusLost(FocusEvent e) {} + public void windowDeiconified(WindowEvent e) {} + public void windowIconified(WindowEvent e) {} + public void windowOpened(WindowEvent e) {} + + public void mouseWheelMoved(MouseWheelEvent event) { + int rotation = event.getWheelRotation(); + int width = imp.getWidth(); + int height = imp.getHeight(); + Rectangle srcRect = ic.getSrcRect(); + int xstart = srcRect.x; + int ystart = srcRect.y; + if (IJ.spaceBarDown() || srcRect.height==height) { + srcRect.x += rotation*Math.max(width/200, 1); + if (srcRect.x<0) srcRect.x = 0; + if (srcRect.x+srcRect.width>width) srcRect.x = width-srcRect.width; + } else { + srcRect.y += rotation*Math.max(height/200, 1); + if (srcRect.y<0) srcRect.y = 0; + if (srcRect.y+srcRect.height>height) srcRect.y = height-srcRect.height; + } + if (srcRect.x!=xstart || srcRect.y!=ystart) + ic.repaint(); + } + + /** Copies the current ROI to the clipboard. The entire + image is copied if there is no ROI. */ + public void copy(boolean cut) { + imp.copy(cut); + } + + + public void paste() { + imp.paste(); + } + + /** This method is called by ImageCanvas.mouseMoved(MouseEvent). + @see ij.gui.ImageCanvas#mouseMoved + */ + public void mouseMoved(int x, int y) { + imp.mouseMoved(x, y); + } + + public String toString() { + return imp.getTitle(); + } + + /** Causes the next image to be opened to be centered on the screen + and displayed without informational text above the image. */ + public static void centerNextImage() { + centerOnScreen = true; + } + + /** Moves and resizes this window. Changes the + magnification so the image fills the window. */ + public void setLocationAndSize(int x, int y, int width, int height) { + setBounds(x, y, width, height); + getCanvas().fitToWindow(); + pack(); + } + + /** Overrides the setBounds() method in Component so + we can find out when the window is resized. */ + //public void setBounds(int x, int y, int width, int height) { + // super.setBounds(x, y, width, height); + // ic.resizeSourceRect(width, height); + //} + +} //class ImageWindow + diff --git a/ij/gui/Line.java b/ij/gui/Line.java index e63c35f5c..29f66c522 100644 --- a/ij/gui/Line.java +++ b/ij/gui/Line.java @@ -1,484 +1,484 @@ -package ij.gui; -import ij.*; -import ij.process.*; -import ij.measure.*; -import ij.plugin.Straightener; -import java.awt.*; -import java.awt.image.*; -import java.awt.event.KeyEvent; -import java.awt.geom.*; - - -/** This class represents a straight line selection. */ -public class Line extends Roi { - - public int x1, y1, x2, y2; // the line - public double x1d, y1d, x2d, y2d; // the line using sub-pixel coordinates - private double x1R, y1R, x2R, y2R; // the line, relative to base of bounding rect - private double xHandleOffset, yHandleOffset; - private double startxd, startyd; - - /** Creates a new straight line selection using the specified - starting and ending offscreen integer coordinates. */ - public Line(int ox1, int oy1, int ox2, int oy2) { - this((double)ox1, (double)oy1, (double)ox2, (double)oy2); - } - - /** Creates a new straight line selection using the specified - starting and ending offscreen double coordinates. */ - public Line(double ox1, double oy1, double ox2, double oy2) { - super((int)ox1, (int)oy1, 0, 0); - type = LINE; - x1d=ox1; y1d=oy1; x2d=ox2; y2d=oy2; - x1=(int)x1d; y1=(int)y1d; x2=(int)x2d; y2=(int)y2d; - x=(int)Math.min(x1d,x2d); y=(int)Math.min(y1d,y2d); - x1R=x1d-x; y1R=y1d-y; x2R=x2d-x; y2R=y2d-y; - width=(int)Math.abs(x2R-x1R); height=(int)Math.abs(y2R-y1R); - updateClipRect(); - oldX=x; oldY=y; oldWidth=width; oldHeight=height; - state = NORMAL; - } - - /** Starts the process of creating a new user-generated straight line - selection. 'sx' and 'sy' are screen coordinates that specify - the start of the line. The user will determine the end of the line - interactively using rubber banding. */ - public Line(int sx, int sy, ImagePlus imp) { - super(sx, sy, imp); - startxd = ic.offScreenXD(sx); - startyd = ic.offScreenYD(sy); - x1R = x2R = startxd - startX; - y1R = y2R = startyd - startY; - type = LINE; - } - - /** Obsolete */ - public Line(int ox1, int oy1, int ox2, int oy2, ImagePlus imp) { - this(ox1, oy1, ox2, oy2); - setImage(imp); - } - - protected void grow(int sx, int sy) { - double xend = ic!=null?ic.offScreenXD(sx):sx; - double yend = ic!=null?ic.offScreenYD(sy):sy; - if (xend<0.0) xend=0.0; if (yend<0.0) yend=0.0; - if (xend>xMax) xend=xMax; if (yend>yMax) yend=yMax; - double xstart=x+x1R, ystart=y+y1R; - if (constrain) { - double dx = Math.abs(xend-xstart); - double dy = Math.abs(yend-ystart); - if (dx>=dy) - yend = ystart; - else - xend = xstart; - } - x=(int)Math.min(x+x1R,xend); y=(int)Math.min(y+y1R,yend); - x1R=xstart-x; y1R=ystart-y; - x2R=xend-x; y2R=yend-y; - if (IJ.controlKeyDown()) { - x1R=(int)Math.round(x1R); y1R=(int)Math.round(y1R); - x2R=(int)Math.round(x2R); y2R=(int)Math.round(y2R); - } - width=(int)Math.abs(x2R-x1R); height=(int)Math.abs(y2R-y1R); - if (width<1) width=1; if (height<1) height=1; - updateClipRect(); - if (imp!=null) { - if (lineWidth==1) - imp.draw(clipX, clipY, clipWidth, clipHeight); - else - imp.draw(); - } - oldX=x; oldY=y; - oldWidth=width; oldHeight=height; - } - - void move(int sx, int sy) { - int xNew = ic.offScreenX(sx); - int yNew = ic.offScreenY(sy); - x += xNew - startxd; - y += yNew - startyd; - clipboard=null; - startxd = xNew; - startyd = yNew; - if (lineWidth==1) { - updateClipRect(); - imp.draw(clipX, clipY, clipWidth, clipHeight); - } else - imp.draw(); - oldX = x; - oldY = y; - oldWidth = width; - oldHeight=height; - } - - protected void moveHandle(int sx, int sy) { - double ox = ic.offScreenXD(sx); - double oy = ic.offScreenYD(sy); - x1d=x+x1R; y1d=y+y1R; x2d=x+x2R; y2d=y+y2R; - double length = Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d)); - switch (activeHandle) { - case 0: - double dx = ox-x1d; - double dy = oy-y1d; - x1d=ox; - y1d=oy; - if(center){ - x2d -= dx; - y2d -= dy; - } - if(aspect){ - double ratio = length/(Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d))); - double xcd = x1d+(x2d-x1d)/2; - double ycd = y1d+(y2d-y1d)/2; - - if(center){ - x1d=xcd-ratio*(xcd-x1d); - x2d=xcd+ratio*(x2d-xcd); - y1d=ycd-ratio*(ycd-y1d); - y2d=ycd+ratio*(y2d-ycd); - } else { - x1d=x2d-ratio*(x2d-x1d); - y1d=y2d-ratio*(y2d-y1d); - } - - } - break; - case 1: - dx = ox-x2d; - dy = oy-y2d; - x2d=ox; - y2d=oy; - if(center){ - x1d -= dx; - y1d -= dy; - } - if(aspect){ - double ratio = length/(Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d))); - double xcd = x1d+(x2d-x1d)/2; - double ycd = y1d+(y2d-y1d)/2; - - if(center){ - x1d=xcd-ratio*(xcd-x1d); - x2d=xcd+ratio*(x2d-xcd); - y1d=ycd-ratio*(ycd-y1d); - y2d=ycd+ratio*(y2d-ycd); - } else { - x2d=x1d+ratio*(x2d-x1d); - y2d=y1d+ratio*(y2d-y1d); - } - - } - break; - case 2: - dx = ox-(x1d+(x2d-x1d)/2); - dy = oy-(y1d+(y2d-y1d)/2); - x1d+=dx; y1d+=dy; x2d+=dx; y2d+=dy; - if (lineWidth>1) { - x1d+=xHandleOffset; y1d+=yHandleOffset; - x2d+=xHandleOffset; y2d+=yHandleOffset; - } - break; - } - if (constrain) { - double dx = Math.abs(x1d-x2d); - double dy = Math.abs(y1d-y2d); - double xcd = Math.min(x1d,x2d)+dx/2; - double ycd = Math.min(y1d,y2d)+dy/2; - - //double ratio = length/(Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d))); - if (activeHandle==0) { - if (dx>=dy) { - if(aspect){ - if(x2d>x1d) x1d=x2d-length; - else x1d=x2d+length; - } - y1d = y2d; - if(center){ - y1d=y2d=ycd; - if(aspect){ - if(xcd>x1d) { - x1d=xcd-length/2; - x2d=xcd+length/2; - } - else{ - x1d=xcd+length/2; - x2d=xcd-length/2; - } - } - } - }else { - if(aspect){ - if(y2d>y1d) y1d=y2d-length; - else y1d=y2d+length; - } - x1d = x2d; - if(center){ - x1d=x2d=xcd; - if(aspect){ - if(ycd>y1d) { - y1d=ycd-length/2; - y2d=ycd+length/2; - } - else{ - y1d=ycd+length/2; - y2d=ycd-length/2; - } - } - } - } - } else if (activeHandle==1) { - if (dx>=dy) { - if(aspect){ - if(x1d>x2d) x2d=x1d-length; - else x2d=x1d+length; - } - y2d= y1d; - if(center){ - y1d=y2d=ycd; - if(aspect){ - if(xcd>x1d) { - x1d=xcd-length/2; - x2d=xcd+length/2; - } - else{ - x1d=xcd+length/2; - x2d=xcd-length/2; - } - } - } - } else { - if(aspect){ - if(y1d>y2d) y2d=y1d-length; - else y2d=y1d+length; - } - x2d = x1d; - if(center){ - x1d=x2d=xcd; - if(aspect){ - if(ycd>y1d) { - y1d=ycd-length/2; - y2d=ycd+length/2; - } - else{ - y1d=ycd+length/2; - y2d=ycd-length/2; - } - } - } - } - } - } - x=(int)Math.min(x1d,x2d); y=(int)Math.min(y1d,y2d); - x1R=x1d-x; y1R=y1d-y; - x2R=x2d-x; y2R=y2d-y; - width=(int)Math.abs(x2R-x1R); height=(int)Math.abs(y2R-y1R); - updateClipRect(); - if (lineWidth==1) - imp.draw(clipX, clipY, clipWidth, clipHeight); - else - imp.draw(); - oldX = x; - oldY = y; - oldWidth = width; - oldHeight = height; - } - - protected void mouseDownInHandle(int handle, int sx, int sy) { - state = MOVING_HANDLE; - activeHandle = handle; - if (lineWidth<=3) - ic.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); - } - - /** Draws this line on the image. */ - public void draw(Graphics g) { - if (ic==null) return; - Color color = outlineColor!=null?outlineColor:ROIColor; - //if (fillColor!=null) color = fillColor; - g.setColor(color); - x1d=x+x1R; y1d=y+y1R; x2d=x+x2R; y2d=y+y2R; - x1=(int)x1d; y1=(int)y1d; x2=(int)x2d; y2=(int)y2d; - int sx1 = ic.screenXD(x1d); - int sy1 = ic.screenYD(y1d); - int sx2 = ic.screenXD(x2d); - int sy2 = ic.screenYD(y2d); - int sx3 = sx1 + (sx2-sx1)/2; - int sy3 = sy1 + (sy2-sy1)/2; - if (lineWidth>1 && !displayList) { - Graphics2D g2d = (Graphics2D)g; - GeneralPath path = new GeneralPath(); - path.moveTo(sx1, sy1); - path.lineTo(sx2, sy2); - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.3f); - g2d.setComposite(ac); - g2d.setStroke(new BasicStroke((float)(lineWidth*ic.getMagnification()),BasicStroke.CAP_BUTT,BasicStroke.JOIN_BEVEL)); - g2d.draw(path); - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); - ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1f); - g2d.setStroke(new BasicStroke(1)); - g2d.setComposite(ac); - } - Graphics2D g2d = (Graphics2D)g; - Stroke saveStroke = null; - if (stroke!=null) { - saveStroke = g2d.getStroke(); - g2d.setStroke(stroke); - } - g.drawLine(sx1, sy1, sx2, sy2); - if (saveStroke!=null) g2d.setStroke(saveStroke); - if (state!=CONSTRUCTING && !displayList) { - int size2 = HANDLE_SIZE/2; - handleColor=outlineColor!=null?outlineColor:ROIColor; drawHandle(g, sx1-size2, sy1-size2); handleColor=Color.white; - drawHandle(g, sx2-size2, sy2-size2); - drawHandle(g, sx3-size2, sy3-size2); - } - if (state!=NORMAL) - IJ.showStatus(imp.getLocationAsString(x2,y2)+", angle=" + IJ.d2s(getAngle(x1,y1,x2,y2)) + ", length=" + IJ.d2s(getLength())); - if (updateFullWindow) - {updateFullWindow = false; imp.draw();} - } - - /** Returns the length of this line. */ - public double getLength() { - if (imp==null || IJ.altKeyDown()) - return getRawLength(); - else { - Calibration cal = imp.getCalibration(); - return Math.sqrt((x2d-x1d)*cal.pixelWidth*(x2d-x1d)*cal.pixelWidth - + (y2d-y1d)*cal.pixelHeight*(y2d-y1d)*cal.pixelHeight); - } - } - - /** Returns the length of this line in pixels. */ - public double getRawLength() { - return Math.sqrt((x2d-x1d)*(x2d-x1d)+(y2d-y1d)*(y2d-y1d)); - } - - /** Returns the pixel values along this line. */ - public double[] getPixels() { - double[] profile; - if (lineWidth==1) { - ImageProcessor ip = imp.getProcessor(); - profile = ip.getLine(x1d, y1d, x2d, y2d); - } else { - ImageProcessor ip2 = (new Straightener()).rotateLine(imp,lineWidth); - if (ip2==null) return null; - int width = ip2.getWidth(); - int height = ip2.getHeight(); - profile = new double[width]; - double[] aLine; - ip2.setInterpolate(false); - for (int y=0; y1) - return getPolygon().contains(x, y); - else - return false; - } - - /** Returns a handle number if the specified screen coordinates are - inside or near a handle, otherwise returns -1. */ - public int isHandle(int sx, int sy) { - int size = HANDLE_SIZE+5; - if (lineWidth>1) size += (int)Math.log(lineWidth); - int halfSize = size/2; - int sx1 = ic.screenXD(x+x1R) - halfSize; - int sy1 = ic.screenYD(y+y1R) - halfSize; - int sx2 = ic.screenXD(x+x2R) - halfSize; - int sy2 = ic.screenYD(y+y2R) - halfSize; - int sx3 = sx1 + (sx2-sx1)/2-1; - int sy3 = sy1 + (sy2-sy1)/2-1; - if (sx>=sx1&&sx<=sx1+size&&sy>=sy1&&sy<=sy1+size) return 0; - if (sx>=sx2&&sx<=sx2+size&&sy>=sy2&&sy<=sy2+size) return 1; - if (sx>=sx3&&sx<=sx3+size+2&&sy>=sy3&&sy<=sy3+size+2) return 2; - return -1; - } - - public static int getWidth() { - return lineWidth; - } - - public static void setWidth(int w) { - if (w<1) w = 1; - int max = 500; - if (w>max) { - ImagePlus imp2 = WindowManager.getCurrentImage(); - if (imp2!=null) { - max = Math.max(max, imp2.getWidth()); - max = Math.max(max, imp2.getHeight()); - } - if (w>max) w = max; - } - lineWidth = w; - } - - /** Return the bounding rectangle of this line. */ - public Rectangle getBounds() { - int xmin = (int)Math.round(Math.min(x1d, x2d)); - int ymin = (int)Math.round(Math.min(y1d, y2d)); - int w = (int)Math.round(Math.abs(x2d - x1d)); - int h = (int)Math.round(Math.abs(y2d - y1d)); - return new Rectangle(xmin, ymin, w, h); - } - - /** Nudge end point of line by one pixel. */ - public void nudgeCorner(int key) { - if (ic==null) return; - double inc = 1.0/ic.getMagnification(); - switch(key) { - case KeyEvent.VK_UP: y2R-=inc; break; - case KeyEvent.VK_DOWN: y2R+=inc; break; - case KeyEvent.VK_LEFT: x2R-=inc; break; - case KeyEvent.VK_RIGHT: x2R+=inc; break; - } - grow(ic.screenXD(x+x2R), ic.screenYD(y+y2R)); - } - - -} +package ij.gui; +import ij.*; +import ij.process.*; +import ij.measure.*; +import ij.plugin.Straightener; +import java.awt.*; +import java.awt.image.*; +import java.awt.event.KeyEvent; +import java.awt.geom.*; + + +/** This class represents a straight line selection. */ +public class Line extends Roi { + + public int x1, y1, x2, y2; // the line + public double x1d, y1d, x2d, y2d; // the line using sub-pixel coordinates + private double x1R, y1R, x2R, y2R; // the line, relative to base of bounding rect + private double xHandleOffset, yHandleOffset; + private double startxd, startyd; + + /** Creates a new straight line selection using the specified + starting and ending offscreen integer coordinates. */ + public Line(int ox1, int oy1, int ox2, int oy2) { + this((double)ox1, (double)oy1, (double)ox2, (double)oy2); + } + + /** Creates a new straight line selection using the specified + starting and ending offscreen double coordinates. */ + public Line(double ox1, double oy1, double ox2, double oy2) { + super((int)ox1, (int)oy1, 0, 0); + type = LINE; + x1d=ox1; y1d=oy1; x2d=ox2; y2d=oy2; + x1=(int)x1d; y1=(int)y1d; x2=(int)x2d; y2=(int)y2d; + x=(int)Math.min(x1d,x2d); y=(int)Math.min(y1d,y2d); + x1R=x1d-x; y1R=y1d-y; x2R=x2d-x; y2R=y2d-y; + width=(int)Math.abs(x2R-x1R); height=(int)Math.abs(y2R-y1R); + updateClipRect(); + oldX=x; oldY=y; oldWidth=width; oldHeight=height; + state = NORMAL; + } + + /** Starts the process of creating a new user-generated straight line + selection. 'sx' and 'sy' are screen coordinates that specify + the start of the line. The user will determine the end of the line + interactively using rubber banding. */ + public Line(int sx, int sy, ImagePlus imp) { + super(sx, sy, imp); + startxd = ic.offScreenXD(sx); + startyd = ic.offScreenYD(sy); + x1R = x2R = startxd - startX; + y1R = y2R = startyd - startY; + type = LINE; + } + + /** Obsolete */ + public Line(int ox1, int oy1, int ox2, int oy2, ImagePlus imp) { + this(ox1, oy1, ox2, oy2); + setImage(imp); + } + + protected void grow(int sx, int sy) { + double xend = ic!=null?ic.offScreenXD(sx):sx; + double yend = ic!=null?ic.offScreenYD(sy):sy; + if (xend<0.0) xend=0.0; if (yend<0.0) yend=0.0; + if (xend>xMax) xend=xMax; if (yend>yMax) yend=yMax; + double xstart=x+x1R, ystart=y+y1R; + if (constrain) { + double dx = Math.abs(xend-xstart); + double dy = Math.abs(yend-ystart); + if (dx>=dy) + yend = ystart; + else + xend = xstart; + } + x=(int)Math.min(x+x1R,xend); y=(int)Math.min(y+y1R,yend); + x1R=xstart-x; y1R=ystart-y; + x2R=xend-x; y2R=yend-y; + if (IJ.controlKeyDown()) { + x1R=(int)Math.round(x1R); y1R=(int)Math.round(y1R); + x2R=(int)Math.round(x2R); y2R=(int)Math.round(y2R); + } + width=(int)Math.abs(x2R-x1R); height=(int)Math.abs(y2R-y1R); + if (width<1) width=1; if (height<1) height=1; + updateClipRect(); + if (imp!=null) { + if (lineWidth==1) + imp.draw(clipX, clipY, clipWidth, clipHeight); + else + imp.draw(); + } + oldX=x; oldY=y; + oldWidth=width; oldHeight=height; + } + + void move(int sx, int sy) { + int xNew = ic.offScreenX(sx); + int yNew = ic.offScreenY(sy); + x += xNew - startxd; + y += yNew - startyd; + clipboard=null; + startxd = xNew; + startyd = yNew; + if (lineWidth==1) { + updateClipRect(); + imp.draw(clipX, clipY, clipWidth, clipHeight); + } else + imp.draw(); + oldX = x; + oldY = y; + oldWidth = width; + oldHeight=height; + } + + protected void moveHandle(int sx, int sy) { + double ox = ic.offScreenXD(sx); + double oy = ic.offScreenYD(sy); + x1d=x+x1R; y1d=y+y1R; x2d=x+x2R; y2d=y+y2R; + double length = Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d)); + switch (activeHandle) { + case 0: + double dx = ox-x1d; + double dy = oy-y1d; + x1d=ox; + y1d=oy; + if(center){ + x2d -= dx; + y2d -= dy; + } + if(aspect){ + double ratio = length/(Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d))); + double xcd = x1d+(x2d-x1d)/2; + double ycd = y1d+(y2d-y1d)/2; + + if(center){ + x1d=xcd-ratio*(xcd-x1d); + x2d=xcd+ratio*(x2d-xcd); + y1d=ycd-ratio*(ycd-y1d); + y2d=ycd+ratio*(y2d-ycd); + } else { + x1d=x2d-ratio*(x2d-x1d); + y1d=y2d-ratio*(y2d-y1d); + } + + } + break; + case 1: + dx = ox-x2d; + dy = oy-y2d; + x2d=ox; + y2d=oy; + if(center){ + x1d -= dx; + y1d -= dy; + } + if(aspect){ + double ratio = length/(Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d))); + double xcd = x1d+(x2d-x1d)/2; + double ycd = y1d+(y2d-y1d)/2; + + if(center){ + x1d=xcd-ratio*(xcd-x1d); + x2d=xcd+ratio*(x2d-xcd); + y1d=ycd-ratio*(ycd-y1d); + y2d=ycd+ratio*(y2d-ycd); + } else { + x2d=x1d+ratio*(x2d-x1d); + y2d=y1d+ratio*(y2d-y1d); + } + + } + break; + case 2: + dx = ox-(x1d+(x2d-x1d)/2); + dy = oy-(y1d+(y2d-y1d)/2); + x1d+=dx; y1d+=dy; x2d+=dx; y2d+=dy; + if (lineWidth>1) { + x1d+=xHandleOffset; y1d+=yHandleOffset; + x2d+=xHandleOffset; y2d+=yHandleOffset; + } + break; + } + if (constrain) { + double dx = Math.abs(x1d-x2d); + double dy = Math.abs(y1d-y2d); + double xcd = Math.min(x1d,x2d)+dx/2; + double ycd = Math.min(y1d,y2d)+dy/2; + + //double ratio = length/(Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d))); + if (activeHandle==0) { + if (dx>=dy) { + if(aspect){ + if(x2d>x1d) x1d=x2d-length; + else x1d=x2d+length; + } + y1d = y2d; + if(center){ + y1d=y2d=ycd; + if(aspect){ + if(xcd>x1d) { + x1d=xcd-length/2; + x2d=xcd+length/2; + } + else{ + x1d=xcd+length/2; + x2d=xcd-length/2; + } + } + } + }else { + if(aspect){ + if(y2d>y1d) y1d=y2d-length; + else y1d=y2d+length; + } + x1d = x2d; + if(center){ + x1d=x2d=xcd; + if(aspect){ + if(ycd>y1d) { + y1d=ycd-length/2; + y2d=ycd+length/2; + } + else{ + y1d=ycd+length/2; + y2d=ycd-length/2; + } + } + } + } + } else if (activeHandle==1) { + if (dx>=dy) { + if(aspect){ + if(x1d>x2d) x2d=x1d-length; + else x2d=x1d+length; + } + y2d= y1d; + if(center){ + y1d=y2d=ycd; + if(aspect){ + if(xcd>x1d) { + x1d=xcd-length/2; + x2d=xcd+length/2; + } + else{ + x1d=xcd+length/2; + x2d=xcd-length/2; + } + } + } + } else { + if(aspect){ + if(y1d>y2d) y2d=y1d-length; + else y2d=y1d+length; + } + x2d = x1d; + if(center){ + x1d=x2d=xcd; + if(aspect){ + if(ycd>y1d) { + y1d=ycd-length/2; + y2d=ycd+length/2; + } + else{ + y1d=ycd+length/2; + y2d=ycd-length/2; + } + } + } + } + } + } + x=(int)Math.min(x1d,x2d); y=(int)Math.min(y1d,y2d); + x1R=x1d-x; y1R=y1d-y; + x2R=x2d-x; y2R=y2d-y; + width=(int)Math.abs(x2R-x1R); height=(int)Math.abs(y2R-y1R); + updateClipRect(); + if (lineWidth==1) + imp.draw(clipX, clipY, clipWidth, clipHeight); + else + imp.draw(); + oldX = x; + oldY = y; + oldWidth = width; + oldHeight = height; + } + + protected void mouseDownInHandle(int handle, int sx, int sy) { + state = MOVING_HANDLE; + activeHandle = handle; + if (lineWidth<=3) + ic.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); + } + + /** Draws this line on the image. */ + public void draw(Graphics g) { + if (ic==null) return; + Color color = outlineColor!=null?outlineColor:ROIColor; + //if (fillColor!=null) color = fillColor; + g.setColor(color); + x1d=x+x1R; y1d=y+y1R; x2d=x+x2R; y2d=y+y2R; + x1=(int)x1d; y1=(int)y1d; x2=(int)x2d; y2=(int)y2d; + int sx1 = ic.screenXD(x1d); + int sy1 = ic.screenYD(y1d); + int sx2 = ic.screenXD(x2d); + int sy2 = ic.screenYD(y2d); + int sx3 = sx1 + (sx2-sx1)/2; + int sy3 = sy1 + (sy2-sy1)/2; + if (lineWidth>1 && !displayList) { + Graphics2D g2d = (Graphics2D)g; + GeneralPath path = new GeneralPath(); + path.moveTo(sx1, sy1); + path.lineTo(sx2, sy2); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.3f); + g2d.setComposite(ac); + g2d.setStroke(new BasicStroke((float)(lineWidth*ic.getMagnification()),BasicStroke.CAP_BUTT,BasicStroke.JOIN_BEVEL)); + g2d.draw(path); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1f); + g2d.setStroke(new BasicStroke(1)); + g2d.setComposite(ac); + } + Graphics2D g2d = (Graphics2D)g; + Stroke saveStroke = null; + if (stroke!=null) { + saveStroke = g2d.getStroke(); + g2d.setStroke(stroke); + } + g.drawLine(sx1, sy1, sx2, sy2); + if (saveStroke!=null) g2d.setStroke(saveStroke); + if (state!=CONSTRUCTING && !displayList) { + int size2 = HANDLE_SIZE/2; + handleColor=outlineColor!=null?outlineColor:ROIColor; drawHandle(g, sx1-size2, sy1-size2); handleColor=Color.white; + drawHandle(g, sx2-size2, sy2-size2); + drawHandle(g, sx3-size2, sy3-size2); + } + if (state!=NORMAL) + IJ.showStatus(imp.getLocationAsString(x2,y2)+", angle=" + IJ.d2s(getAngle(x1,y1,x2,y2)) + ", length=" + IJ.d2s(getLength())); + if (updateFullWindow) + {updateFullWindow = false; imp.draw();} + } + + /** Returns the length of this line. */ + public double getLength() { + if (imp==null || IJ.altKeyDown()) + return getRawLength(); + else { + Calibration cal = imp.getCalibration(); + return Math.sqrt((x2d-x1d)*cal.pixelWidth*(x2d-x1d)*cal.pixelWidth + + (y2d-y1d)*cal.pixelHeight*(y2d-y1d)*cal.pixelHeight); + } + } + + /** Returns the length of this line in pixels. */ + public double getRawLength() { + return Math.sqrt((x2d-x1d)*(x2d-x1d)+(y2d-y1d)*(y2d-y1d)); + } + + /** Returns the pixel values along this line. */ + public double[] getPixels() { + double[] profile; + if (lineWidth==1) { + ImageProcessor ip = imp.getProcessor(); + profile = ip.getLine(x1d, y1d, x2d, y2d); + } else { + ImageProcessor ip2 = (new Straightener()).rotateLine(imp,lineWidth); + if (ip2==null) return null; + int width = ip2.getWidth(); + int height = ip2.getHeight(); + profile = new double[width]; + double[] aLine; + ip2.setInterpolate(false); + for (int y=0; y1) + return getPolygon().contains(x, y); + else + return false; + } + + /** Returns a handle number if the specified screen coordinates are + inside or near a handle, otherwise returns -1. */ + public int isHandle(int sx, int sy) { + int size = HANDLE_SIZE+5; + if (lineWidth>1) size += (int)Math.log(lineWidth); + int halfSize = size/2; + int sx1 = ic.screenXD(x+x1R) - halfSize; + int sy1 = ic.screenYD(y+y1R) - halfSize; + int sx2 = ic.screenXD(x+x2R) - halfSize; + int sy2 = ic.screenYD(y+y2R) - halfSize; + int sx3 = sx1 + (sx2-sx1)/2-1; + int sy3 = sy1 + (sy2-sy1)/2-1; + if (sx>=sx1&&sx<=sx1+size&&sy>=sy1&&sy<=sy1+size) return 0; + if (sx>=sx2&&sx<=sx2+size&&sy>=sy2&&sy<=sy2+size) return 1; + if (sx>=sx3&&sx<=sx3+size+2&&sy>=sy3&&sy<=sy3+size+2) return 2; + return -1; + } + + public static int getWidth() { + return lineWidth; + } + + public static void setWidth(int w) { + if (w<1) w = 1; + int max = 500; + if (w>max) { + ImagePlus imp2 = WindowManager.getCurrentImage(); + if (imp2!=null) { + max = Math.max(max, imp2.getWidth()); + max = Math.max(max, imp2.getHeight()); + } + if (w>max) w = max; + } + lineWidth = w; + } + + /** Return the bounding rectangle of this line. */ + public Rectangle getBounds() { + int xmin = (int)Math.round(Math.min(x1d, x2d)); + int ymin = (int)Math.round(Math.min(y1d, y2d)); + int w = (int)Math.round(Math.abs(x2d - x1d)); + int h = (int)Math.round(Math.abs(y2d - y1d)); + return new Rectangle(xmin, ymin, w, h); + } + + /** Nudge end point of line by one pixel. */ + public void nudgeCorner(int key) { + if (ic==null) return; + double inc = 1.0/ic.getMagnification(); + switch(key) { + case KeyEvent.VK_UP: y2R-=inc; break; + case KeyEvent.VK_DOWN: y2R+=inc; break; + case KeyEvent.VK_LEFT: x2R-=inc; break; + case KeyEvent.VK_RIGHT: x2R+=inc; break; + } + grow(ic.screenXD(x+x2R), ic.screenYD(y+y2R)); + } + + +} diff --git a/ij/gui/MessageDialog.java b/ij/gui/MessageDialog.java index 583314c85..f90fc9ef5 100644 --- a/ij/gui/MessageDialog.java +++ b/ij/gui/MessageDialog.java @@ -1,66 +1,66 @@ -package ij.gui; -import ij.IJ; -import java.awt.*; -import java.awt.event.*; - -/** A modal dialog box that displays information. Based on the - InfoDialogclass from "Java in a Nutshell" by David Flanagan. */ -public class MessageDialog extends Dialog implements ActionListener, KeyListener, WindowListener { - protected Button button; - protected MultiLineLabel label; - - public MessageDialog(Frame parent, String title, String message) { - super(parent, title, true); - setLayout(new BorderLayout()); - if (message==null) message = ""; - label = new MultiLineLabel(message); - if (!IJ.isLinux()) label.setFont(new Font("SansSerif", Font.PLAIN, 14)); - Panel panel = new Panel(); - panel.setLayout(new FlowLayout(FlowLayout.CENTER, 15, 15)); - panel.add(label); - add("Center", panel); - button = new Button(" OK "); - button.addActionListener(this); - button.addKeyListener(this); - panel = new Panel(); - panel.setLayout(new FlowLayout()); - panel.add(button); - add("South", panel); - if (ij.IJ.isMacintosh()) - setResizable(false); - pack(); - GUI.center(this); - addWindowListener(this); - show(); - } - - public void actionPerformed(ActionEvent e) { - dispose(); - } - - public void keyPressed(KeyEvent e) { - int keyCode = e.getKeyCode(); - IJ.setKeyDown(keyCode); - if (keyCode==KeyEvent.VK_ENTER || keyCode==KeyEvent.VK_ESCAPE) - dispose(); - } - - public void keyReleased(KeyEvent e) { - int keyCode = e.getKeyCode(); - IJ.setKeyUp(keyCode); - } - - public void keyTyped(KeyEvent e) {} - - public void windowClosing(WindowEvent e) { - dispose(); - } - - public void windowActivated(WindowEvent e) {} - public void windowOpened(WindowEvent e) {} - public void windowClosed(WindowEvent e) {} - public void windowIconified(WindowEvent e) {} - public void windowDeiconified(WindowEvent e) {} - public void windowDeactivated(WindowEvent e) {} - +package ij.gui; +import ij.IJ; +import java.awt.*; +import java.awt.event.*; + +/** A modal dialog box that displays information. Based on the + InfoDialogclass from "Java in a Nutshell" by David Flanagan. */ +public class MessageDialog extends Dialog implements ActionListener, KeyListener, WindowListener { + protected Button button; + protected MultiLineLabel label; + + public MessageDialog(Frame parent, String title, String message) { + super(parent, title, true); + setLayout(new BorderLayout()); + if (message==null) message = ""; + label = new MultiLineLabel(message); + if (!IJ.isLinux()) label.setFont(new Font("SansSerif", Font.PLAIN, 14)); + Panel panel = new Panel(); + panel.setLayout(new FlowLayout(FlowLayout.CENTER, 15, 15)); + panel.add(label); + add("Center", panel); + button = new Button(" OK "); + button.addActionListener(this); + button.addKeyListener(this); + panel = new Panel(); + panel.setLayout(new FlowLayout()); + panel.add(button); + add("South", panel); + if (ij.IJ.isMacintosh()) + setResizable(false); + pack(); + GUI.center(this); + addWindowListener(this); + show(); + } + + public void actionPerformed(ActionEvent e) { + dispose(); + } + + public void keyPressed(KeyEvent e) { + int keyCode = e.getKeyCode(); + IJ.setKeyDown(keyCode); + if (keyCode==KeyEvent.VK_ENTER || keyCode==KeyEvent.VK_ESCAPE) + dispose(); + } + + public void keyReleased(KeyEvent e) { + int keyCode = e.getKeyCode(); + IJ.setKeyUp(keyCode); + } + + public void keyTyped(KeyEvent e) {} + + public void windowClosing(WindowEvent e) { + dispose(); + } + + public void windowActivated(WindowEvent e) {} + public void windowOpened(WindowEvent e) {} + public void windowClosed(WindowEvent e) {} + public void windowIconified(WindowEvent e) {} + public void windowDeiconified(WindowEvent e) {} + public void windowDeactivated(WindowEvent e) {} + } \ No newline at end of file diff --git a/ij/gui/MultiLineLabel.java b/ij/gui/MultiLineLabel.java index ff3e5864a..1c8b8c6ed 100644 --- a/ij/gui/MultiLineLabel.java +++ b/ij/gui/MultiLineLabel.java @@ -1,98 +1,98 @@ -package ij.gui; -import java.awt.*; -import java.util.*; - -/**Custom component for displaying multiple lines. Based on - MultiLineLabel class from "Java in a Nutshell" by David Flanagan.*/ -public class MultiLineLabel extends Canvas { - String[] lines; - int num_lines; - int margin_width = 6; - int margin_height = 6; - int line_height; - int line_ascent; - int[] line_widths; - int min_width, max_width; - - // Breaks the specified label up into an array of lines. - public MultiLineLabel(String label) { - this(label, 0); - } - - - public MultiLineLabel(String label, int minimumWidth) { - StringTokenizer t = new StringTokenizer(label, "\n"); - num_lines = t.countTokens(); - lines = new String[num_lines]; - line_widths = new int[num_lines]; - for(int i = 0; i < num_lines; i++) lines[i] = t.nextToken(); - min_width = minimumWidth; - } - - // Figures out how wide each line of the label - // is, and how wide the widest line is. - protected void measure() { - FontMetrics fm = this.getFontMetrics(this.getFont()); - // If we don't have font metrics yet, just return. - if (fm == null) return; - - line_height = fm.getHeight(); - line_ascent = fm.getAscent(); - max_width = 0; - for(int i = 0; i < num_lines; i++) { - line_widths[i] = fm.stringWidth(lines[i]); - if (line_widths[i] > max_width) max_width = line_widths[i]; - } - } - - - public void setFont(Font f) { - super.setFont(f); - measure(); - repaint(); - } - - - // This method is invoked after our Canvas is first created - // but before it can actually be displayed. After we've - // invoked our superclass's addNotify() method, we have font - // metrics and can successfully call measure() to figure out - // how big the label is. - public void addNotify() { - super.addNotify(); - measure(); - } - - - // Called by a layout manager when it wants to - // know how big we'd like to be. - public Dimension getPreferredSize() { - return new Dimension(Math.max(min_width, max_width + 2*margin_width), - num_lines * line_height + 2*margin_height); - } - - - // Called when the layout manager wants to know - // the bare minimum amount of space we need to get by. - public Dimension getMinimumSize() { - return new Dimension(Math.max(min_width, max_width), num_lines * line_height); - } - - // Draws the label - public void paint(Graphics g) { - int x, y; - Dimension d = this.getSize(); - if (!ij.IJ.isLinux()) setAntialiasedText(g); - y = line_ascent + (d.height - num_lines * line_height)/2; - for(int i = 0; i < num_lines; i++, y += line_height) { - x = margin_width; - g.drawString(lines[i], x, y); - } - } - - void setAntialiasedText(Graphics g) { - Graphics2D g2d = (Graphics2D)g; - g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - } - +package ij.gui; +import java.awt.*; +import java.util.*; + +/**Custom component for displaying multiple lines. Based on + MultiLineLabel class from "Java in a Nutshell" by David Flanagan.*/ +public class MultiLineLabel extends Canvas { + String[] lines; + int num_lines; + int margin_width = 6; + int margin_height = 6; + int line_height; + int line_ascent; + int[] line_widths; + int min_width, max_width; + + // Breaks the specified label up into an array of lines. + public MultiLineLabel(String label) { + this(label, 0); + } + + + public MultiLineLabel(String label, int minimumWidth) { + StringTokenizer t = new StringTokenizer(label, "\n"); + num_lines = t.countTokens(); + lines = new String[num_lines]; + line_widths = new int[num_lines]; + for(int i = 0; i < num_lines; i++) lines[i] = t.nextToken(); + min_width = minimumWidth; + } + + // Figures out how wide each line of the label + // is, and how wide the widest line is. + protected void measure() { + FontMetrics fm = this.getFontMetrics(this.getFont()); + // If we don't have font metrics yet, just return. + if (fm == null) return; + + line_height = fm.getHeight(); + line_ascent = fm.getAscent(); + max_width = 0; + for(int i = 0; i < num_lines; i++) { + line_widths[i] = fm.stringWidth(lines[i]); + if (line_widths[i] > max_width) max_width = line_widths[i]; + } + } + + + public void setFont(Font f) { + super.setFont(f); + measure(); + repaint(); + } + + + // This method is invoked after our Canvas is first created + // but before it can actually be displayed. After we've + // invoked our superclass's addNotify() method, we have font + // metrics and can successfully call measure() to figure out + // how big the label is. + public void addNotify() { + super.addNotify(); + measure(); + } + + + // Called by a layout manager when it wants to + // know how big we'd like to be. + public Dimension getPreferredSize() { + return new Dimension(Math.max(min_width, max_width + 2*margin_width), + num_lines * line_height + 2*margin_height); + } + + + // Called when the layout manager wants to know + // the bare minimum amount of space we need to get by. + public Dimension getMinimumSize() { + return new Dimension(Math.max(min_width, max_width), num_lines * line_height); + } + + // Draws the label + public void paint(Graphics g) { + int x, y; + Dimension d = this.getSize(); + if (!ij.IJ.isLinux()) setAntialiasedText(g); + y = line_ascent + (d.height - num_lines * line_height)/2; + for(int i = 0; i < num_lines; i++, y += line_height) { + x = margin_width; + g.drawString(lines[i], x, y); + } + } + + void setAntialiasedText(Graphics g) { + Graphics2D g2d = (Graphics2D)g; + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + } + } \ No newline at end of file diff --git a/ij/gui/NewImage.java b/ij/gui/NewImage.java index aae8dfe16..2106c68a8 100644 --- a/ij/gui/NewImage.java +++ b/ij/gui/NewImage.java @@ -1,329 +1,329 @@ -package ij.gui; - -import java.awt.*; -import java.awt.image.*; -import java.io.*; -import java.awt.event.*; -import java.util.*; -import ij.*; -import ij.process.*; - -/** New image dialog box plus several static utility methods for creating images.*/ -public class NewImage { - - public static final int GRAY8=0, GRAY16=1, GRAY32=2, RGB=3; - public static final int FILL_BLACK=1, FILL_RAMP=2, FILL_WHITE=4, CHECK_AVAILABLE_MEMORY=8; - private static final int OLD_FILL_WHITE=0; - - static final String NAME = "new.name"; - static final String TYPE = "new.type"; - static final String FILL = "new.fill"; - static final String WIDTH = "new.width"; - static final String HEIGHT = "new.height"; - static final String SLICES = "new.slices"; - - private static String name = Prefs.getString(NAME, "Untitled"); - private static int width = Prefs.getInt(WIDTH, 400); - private static int height = Prefs.getInt(HEIGHT, 400); - private static int slices = Prefs.getInt(SLICES, 1); - private static int type = Prefs.getInt(TYPE, GRAY8); - private static int fillWith = Prefs.getInt(FILL, OLD_FILL_WHITE); - private static String[] types = {"8-bit", "16-bit", "32-bit", "RGB"}; - private static String[] fill = {"White", "Black", "Ramp"}; - - - public NewImage() { - openImage(); - } - - static boolean createStack(ImagePlus imp, ImageProcessor ip, int nSlices, int type, int options) { - int fill = getFill(options); - int width = imp.getWidth(); - int height = imp.getHeight(); - long bytesPerPixel = 1; - if (type==GRAY16) bytesPerPixel = 2; - else if (type==GRAY32||type==RGB) bytesPerPixel = 4; - long size = (long)width*height*nSlices*bytesPerPixel; - String size2 = size/(1024*1024)+"MB ("+width+"x"+height+"x"+nSlices+")"; - if ((options&CHECK_AVAILABLE_MEMORY)!=0) { - long max = IJ.maxMemory(); // - 100*1024*1024; - if (max>0) { - long inUse = IJ.currentMemory(); - long available = max - inUse; - if (size>available) - System.gc(); - inUse = IJ.currentMemory(); - available = max-inUse; - if (size>available) { - IJ.error("Insufficient Memory", "There is not enough free memory to allocate a \n" - + size2+" stack.\n \n" - + "Memory available: "+available/(1024*1024)+"MB\n" - + "Memory in use: "+IJ.freeMemory()+"\n \n" - + "More information can be found in the \"Memory\"\n" - + "sections of the ImageJ installation notes at\n" - + "\""+IJ.URL+"/docs/install/\"."); - return false; - } - } - } - ImageStack stack = imp.createEmptyStack(); - int inc = nSlices/40; - if (inc<1) inc = 1; - IJ.showStatus("Allocating "+size2+". Press 'Esc' to abort."); - IJ.resetEscape(); - try { - stack.addSlice(null, ip); - for (int i=2; i<=nSlices; i++) { - if ((i%inc)==0) IJ.showProgress(i, nSlices); - Object pixels2 = null; - switch (type) { - case GRAY8: pixels2 = new byte[width*height]; break; - case GRAY16: pixels2 = new short[width*height]; break; - case GRAY32: pixels2 = new float[width*height]; break; - case RGB: pixels2 = new int[width*height]; break; - } - if (fill!=FILL_BLACK || type==RGB) - System.arraycopy(ip.getPixels(), 0, pixels2, 0, width*height); - stack.addSlice(null, pixels2); - if (IJ.escapePressed()) {IJ.beep(); break;}; - } - } - catch(OutOfMemoryError e) { - IJ.outOfMemory(imp.getTitle()); - stack.trim(); - } - IJ.showProgress(nSlices, nSlices); - if (stack.getSize()>1) - imp.setStack(null, stack); - return true; - } - - static ImagePlus createImagePlus() { - //ImagePlus imp = WindowManager.getCurrentImage(); - //if (imp!=null) - // return imp.createImagePlus(); - //else - return new ImagePlus(); - } - - static int getFill(int options) { - int fill = options&7; - if (fill==OLD_FILL_WHITE) - fill = FILL_WHITE; - if (fill==7||fill==6||fill==3||fill==5) - fill = FILL_BLACK; - return fill; - } - - public static ImagePlus createByteImage(String title, int width, int height, int slices, int options) { - int fill = getFill(options); - byte[] pixels = new byte[width*height]; - switch (fill) { - case FILL_WHITE: - for (int i=0; i1) { - boolean ok = createStack(imp, ip, slices, GRAY8, options); - if (!ok) imp = null; - } - return imp; - } - - public static ImagePlus createRGBImage(String title, int width, int height, int slices, int options) { - int fill = getFill(options); - int[] pixels = new int[width*height]; - switch (fill) { - case FILL_WHITE: - for (int i=0; i1) { - boolean ok = createStack(imp, ip, slices, RGB, options); - if (!ok) imp = null; - } - return imp; - } - - /** Creates an unsigned short image. */ - public static ImagePlus createShortImage(String title, int width, int height, int slices, int options) { - int fill = getFill(options); - short[] pixels = new short[width*height]; - switch (fill) { - case FILL_WHITE: case FILL_BLACK: - break; - case FILL_RAMP: - short[] ramp = new short[width]; - for (int i=0; i1) { - boolean ok = createStack(imp, ip, slices, GRAY16, options); - if (!ok) imp = null; - } - imp.getProcessor().setMinAndMax(0, 65535); // default display range - return imp; - } - - /** Obsolete. Short images are always unsigned. */ - public static ImagePlus createUnsignedShortImage(String title, int width, int height, int slices, int options) { - return createShortImage(title, width, height, slices, options); - } - - public static ImagePlus createFloatImage(String title, int width, int height, int slices, int options) { - int fill = getFill(options); - float[] pixels = new float[width*height]; - switch (fill) { - case FILL_WHITE: case FILL_BLACK: - break; - case FILL_RAMP: - float[] ramp = new float[width]; - for (int i=0; i1) { - boolean ok = createStack(imp, ip, slices, GRAY32, options); - if (!ok) imp = null; - } - imp.getProcessor().setMinAndMax(0.0, 1.0); // default display range - return imp; - } - - public static void open(String title, int width, int height, int nSlices, int type, int options) { - int bitDepth = 8; - if (type==GRAY16) bitDepth = 16; - else if (type==GRAY32) bitDepth = 32; - else if (type==RGB) bitDepth = 24; - long startTime = System.currentTimeMillis(); - ImagePlus imp = createImage(title, width, height, nSlices, bitDepth, options); - if (imp!=null) { - WindowManager.checkForDuplicateName = true; - imp.show(); - IJ.showStatus(IJ.d2s(((System.currentTimeMillis()-startTime)/1000.0),2)+" seconds"); - } - } - - public static ImagePlus createImage(String title, int width, int height, int nSlices, int bitDepth, int options) { - ImagePlus imp = null; - switch (bitDepth) { - case 8: imp = createByteImage(title, width, height, nSlices, options); break; - case 16: imp = createShortImage(title, width, height, nSlices, options); break; - case 32: imp = createFloatImage(title, width, height, nSlices, options); break; - case 24: imp = createRGBImage(title, width, height, nSlices, options); break; - default: throw new IllegalArgumentException("Invalid bitDepth: "+bitDepth); - } - return imp; - } - - boolean showDialog() { - if (typeRGB) - type = GRAY8; - if (fillWithFILL_RAMP) - fillWith = OLD_FILL_WHITE; - GenericDialog gd = new GenericDialog("New Image...", IJ.getInstance()); - gd.addStringField("Name:", name, 12); - gd.addChoice("Type:", types, types[type]); - gd.addChoice("Fill With:", fill, fill[fillWith]); - gd.addNumericField("Width:", width, 0, 5, "pixels"); - gd.addNumericField("Height:", height, 0, 5, "pixels"); - gd.addNumericField("Slices:", slices, 0, 5, ""); - gd.showDialog(); - if (gd.wasCanceled()) - return false; - name = gd.getNextString(); - String s = gd.getNextChoice(); - if (s.startsWith("8")) - type = GRAY8; - else if (s.startsWith("16")) - type = GRAY16; - else if (s.endsWith("RGB") || s.endsWith("rgb")) - type = RGB; - else - type = GRAY32; - fillWith = gd.getNextChoiceIndex(); - width = (int)gd.getNextNumber(); - height = (int)gd.getNextNumber(); - slices = (int)gd.getNextNumber(); - return true; - } - - void openImage() { - if (!showDialog()) - return; - try {open(name, width, height, slices, type, fillWith);} - catch(OutOfMemoryError e) {IJ.outOfMemory("New Image...");} - } - - /** Called when ImageJ quits. */ - public static void savePreferences(Properties prefs) { - prefs.put(NAME, name); - prefs.put(TYPE, Integer.toString(type)); - prefs.put(FILL, Integer.toString(fillWith)); - prefs.put(WIDTH, Integer.toString(width)); - prefs.put(HEIGHT, Integer.toString(height)); - prefs.put(SLICES, Integer.toString(slices)); - } - +package ij.gui; + +import java.awt.*; +import java.awt.image.*; +import java.io.*; +import java.awt.event.*; +import java.util.*; +import ij.*; +import ij.process.*; + +/** New image dialog box plus several static utility methods for creating images.*/ +public class NewImage { + + public static final int GRAY8=0, GRAY16=1, GRAY32=2, RGB=3; + public static final int FILL_BLACK=1, FILL_RAMP=2, FILL_WHITE=4, CHECK_AVAILABLE_MEMORY=8; + private static final int OLD_FILL_WHITE=0; + + static final String NAME = "new.name"; + static final String TYPE = "new.type"; + static final String FILL = "new.fill"; + static final String WIDTH = "new.width"; + static final String HEIGHT = "new.height"; + static final String SLICES = "new.slices"; + + private static String name = Prefs.getString(NAME, "Untitled"); + private static int width = Prefs.getInt(WIDTH, 400); + private static int height = Prefs.getInt(HEIGHT, 400); + private static int slices = Prefs.getInt(SLICES, 1); + private static int type = Prefs.getInt(TYPE, GRAY8); + private static int fillWith = Prefs.getInt(FILL, OLD_FILL_WHITE); + private static String[] types = {"8-bit", "16-bit", "32-bit", "RGB"}; + private static String[] fill = {"White", "Black", "Ramp"}; + + + public NewImage() { + openImage(); + } + + static boolean createStack(ImagePlus imp, ImageProcessor ip, int nSlices, int type, int options) { + int fill = getFill(options); + int width = imp.getWidth(); + int height = imp.getHeight(); + long bytesPerPixel = 1; + if (type==GRAY16) bytesPerPixel = 2; + else if (type==GRAY32||type==RGB) bytesPerPixel = 4; + long size = (long)width*height*nSlices*bytesPerPixel; + String size2 = size/(1024*1024)+"MB ("+width+"x"+height+"x"+nSlices+")"; + if ((options&CHECK_AVAILABLE_MEMORY)!=0) { + long max = IJ.maxMemory(); // - 100*1024*1024; + if (max>0) { + long inUse = IJ.currentMemory(); + long available = max - inUse; + if (size>available) + System.gc(); + inUse = IJ.currentMemory(); + available = max-inUse; + if (size>available) { + IJ.error("Insufficient Memory", "There is not enough free memory to allocate a \n" + + size2+" stack.\n \n" + + "Memory available: "+available/(1024*1024)+"MB\n" + + "Memory in use: "+IJ.freeMemory()+"\n \n" + + "More information can be found in the \"Memory\"\n" + + "sections of the ImageJ installation notes at\n" + + "\""+IJ.URL+"/docs/install/\"."); + return false; + } + } + } + ImageStack stack = imp.createEmptyStack(); + int inc = nSlices/40; + if (inc<1) inc = 1; + IJ.showStatus("Allocating "+size2+". Press 'Esc' to abort."); + IJ.resetEscape(); + try { + stack.addSlice(null, ip); + for (int i=2; i<=nSlices; i++) { + if ((i%inc)==0) IJ.showProgress(i, nSlices); + Object pixels2 = null; + switch (type) { + case GRAY8: pixels2 = new byte[width*height]; break; + case GRAY16: pixels2 = new short[width*height]; break; + case GRAY32: pixels2 = new float[width*height]; break; + case RGB: pixels2 = new int[width*height]; break; + } + if (fill!=FILL_BLACK || type==RGB) + System.arraycopy(ip.getPixels(), 0, pixels2, 0, width*height); + stack.addSlice(null, pixels2); + if (IJ.escapePressed()) {IJ.beep(); break;}; + } + } + catch(OutOfMemoryError e) { + IJ.outOfMemory(imp.getTitle()); + stack.trim(); + } + IJ.showProgress(nSlices, nSlices); + if (stack.getSize()>1) + imp.setStack(null, stack); + return true; + } + + static ImagePlus createImagePlus() { + //ImagePlus imp = WindowManager.getCurrentImage(); + //if (imp!=null) + // return imp.createImagePlus(); + //else + return new ImagePlus(); + } + + static int getFill(int options) { + int fill = options&7; + if (fill==OLD_FILL_WHITE) + fill = FILL_WHITE; + if (fill==7||fill==6||fill==3||fill==5) + fill = FILL_BLACK; + return fill; + } + + public static ImagePlus createByteImage(String title, int width, int height, int slices, int options) { + int fill = getFill(options); + byte[] pixels = new byte[width*height]; + switch (fill) { + case FILL_WHITE: + for (int i=0; i1) { + boolean ok = createStack(imp, ip, slices, GRAY8, options); + if (!ok) imp = null; + } + return imp; + } + + public static ImagePlus createRGBImage(String title, int width, int height, int slices, int options) { + int fill = getFill(options); + int[] pixels = new int[width*height]; + switch (fill) { + case FILL_WHITE: + for (int i=0; i1) { + boolean ok = createStack(imp, ip, slices, RGB, options); + if (!ok) imp = null; + } + return imp; + } + + /** Creates an unsigned short image. */ + public static ImagePlus createShortImage(String title, int width, int height, int slices, int options) { + int fill = getFill(options); + short[] pixels = new short[width*height]; + switch (fill) { + case FILL_WHITE: case FILL_BLACK: + break; + case FILL_RAMP: + short[] ramp = new short[width]; + for (int i=0; i1) { + boolean ok = createStack(imp, ip, slices, GRAY16, options); + if (!ok) imp = null; + } + imp.getProcessor().setMinAndMax(0, 65535); // default display range + return imp; + } + + /** Obsolete. Short images are always unsigned. */ + public static ImagePlus createUnsignedShortImage(String title, int width, int height, int slices, int options) { + return createShortImage(title, width, height, slices, options); + } + + public static ImagePlus createFloatImage(String title, int width, int height, int slices, int options) { + int fill = getFill(options); + float[] pixels = new float[width*height]; + switch (fill) { + case FILL_WHITE: case FILL_BLACK: + break; + case FILL_RAMP: + float[] ramp = new float[width]; + for (int i=0; i1) { + boolean ok = createStack(imp, ip, slices, GRAY32, options); + if (!ok) imp = null; + } + imp.getProcessor().setMinAndMax(0.0, 1.0); // default display range + return imp; + } + + public static void open(String title, int width, int height, int nSlices, int type, int options) { + int bitDepth = 8; + if (type==GRAY16) bitDepth = 16; + else if (type==GRAY32) bitDepth = 32; + else if (type==RGB) bitDepth = 24; + long startTime = System.currentTimeMillis(); + ImagePlus imp = createImage(title, width, height, nSlices, bitDepth, options); + if (imp!=null) { + WindowManager.checkForDuplicateName = true; + imp.show(); + IJ.showStatus(IJ.d2s(((System.currentTimeMillis()-startTime)/1000.0),2)+" seconds"); + } + } + + public static ImagePlus createImage(String title, int width, int height, int nSlices, int bitDepth, int options) { + ImagePlus imp = null; + switch (bitDepth) { + case 8: imp = createByteImage(title, width, height, nSlices, options); break; + case 16: imp = createShortImage(title, width, height, nSlices, options); break; + case 32: imp = createFloatImage(title, width, height, nSlices, options); break; + case 24: imp = createRGBImage(title, width, height, nSlices, options); break; + default: throw new IllegalArgumentException("Invalid bitDepth: "+bitDepth); + } + return imp; + } + + boolean showDialog() { + if (typeRGB) + type = GRAY8; + if (fillWithFILL_RAMP) + fillWith = OLD_FILL_WHITE; + GenericDialog gd = new GenericDialog("New Image...", IJ.getInstance()); + gd.addStringField("Name:", name, 12); + gd.addChoice("Type:", types, types[type]); + gd.addChoice("Fill With:", fill, fill[fillWith]); + gd.addNumericField("Width:", width, 0, 5, "pixels"); + gd.addNumericField("Height:", height, 0, 5, "pixels"); + gd.addNumericField("Slices:", slices, 0, 5, ""); + gd.showDialog(); + if (gd.wasCanceled()) + return false; + name = gd.getNextString(); + String s = gd.getNextChoice(); + if (s.startsWith("8")) + type = GRAY8; + else if (s.startsWith("16")) + type = GRAY16; + else if (s.endsWith("RGB") || s.endsWith("rgb")) + type = RGB; + else + type = GRAY32; + fillWith = gd.getNextChoiceIndex(); + width = (int)gd.getNextNumber(); + height = (int)gd.getNextNumber(); + slices = (int)gd.getNextNumber(); + return true; + } + + void openImage() { + if (!showDialog()) + return; + try {open(name, width, height, slices, type, fillWith);} + catch(OutOfMemoryError e) {IJ.outOfMemory("New Image...");} + } + + /** Called when ImageJ quits. */ + public static void savePreferences(Properties prefs) { + prefs.put(NAME, name); + prefs.put(TYPE, Integer.toString(type)); + prefs.put(FILL, Integer.toString(fillWith)); + prefs.put(WIDTH, Integer.toString(width)); + prefs.put(HEIGHT, Integer.toString(height)); + prefs.put(SLICES, Integer.toString(slices)); + } + } \ No newline at end of file diff --git a/ij/gui/Plot.java b/ij/gui/Plot.java index 06abd9c73..dc7cdd1dd 100644 --- a/ij/gui/Plot.java +++ b/ij/gui/Plot.java @@ -1,660 +1,660 @@ -package ij.gui; -import java.awt.*; -import java.awt.image.*; -import java.io.*; -import java.util.*; -import ij.*; -import ij.process.*; -import ij.util.*; -import ij.plugin.filter.Analyzer; -import ij.macro.Interpreter; -import ij.measure.Calibration; - - -/** This class is an image that line graphs can be drawn on. */ -public class Plot { - - /** Display points using a circle 5 pixels in diameter. */ - public static final int CIRCLE = 0; - /** Display points using an X-shaped mark. */ - public static final int X = 1; - /** Display points using an box-shaped mark. */ - public static final int BOX = 3; - /** Display points using an tiangular mark. */ - public static final int TRIANGLE = 4; - /** Display points using an cross-shaped mark. */ - public static final int CROSS = 5; - /** Display points using a single pixel. */ - public static final int DOT = 6; - /** Connect points with solid lines. */ - public static final int LINE = 2; - ///** flag multiplier for maximum number of ticks&grid lines along x */ - //public static final int X_INTERVALS_M = 0x1; - ///** flag multiplier for maximum number of ticks&grid lines along y */ - //public static final int Y_INTERVALS_M = 0x100; - /** flag for numeric labels of x-axis ticks */ - public static final int X_NUMBERS = 0x1; - /** flag for numeric labels of x-axis ticks */ - public static final int Y_NUMBERS = 0x2; - /** flag for drawing x-axis ticks */ - public static final int X_TICKS = 0x4; - /** flag for drawing x-axis ticks */ - public static final int Y_TICKS = 0x8; - /** flag for drawing vertical grid lines for x-axis ticks */ - public static final int X_GRID = 0x10; - /** flag for drawing horizontal grid lines for y-axis ticks */ - public static final int Y_GRID = 0x20; - /** flag for forcing frame to coincide with the grid/ticks in x direction (results in unused space) */ - public static final int X_FORCE2GRID = 0x40; - /** flag for forcing frame to coincide with the grid/ticks in y direction (results in unused space) */ - public static final int Y_FORCE2GRID = 0x80; - /** the default flags */ - public static final int DEFAULT_FLAGS = X_NUMBERS + Y_NUMBERS + /*X_TICKS + Y_TICKS +*/ X_GRID + Y_GRID; - /** the margin width left of the plot frame (enough for 5-digit numbers such as unscaled 16-bit data*/ - public static final int LEFT_MARGIN = 60; - /** the margin width right of the plot frame */ - public static final int RIGHT_MARGIN = 18; - /** the margin width above the plot frame */ - public static final int TOP_MARGIN = 15; - /** the margin width below the plot frame */ - public static final int BOTTOM_MARGIN = 40; - - private static final int WIDTH = 450; - private static final int HEIGHT = 200; - private static final int MAX_INTERVALS = 12; //maximum number of intervals between ticks or grid lines - private static final int MIN_X_GRIDWIDTH = 60; //minimum distance between grid lines or ticks along x - private static final int MIN_Y_GRIDWIDTH = 40; //minimum distance between grid lines or ticks along y - private static final int TICK_LENGTH = 3; //length of ticks - private final Color gridColor = new Color(0xc0c0c0); //light gray - private int frameWidth; - private int frameHeight; - - Rectangle frame = null; - float[] xValues, yValues; - float[] errorBars; - int nPoints; - double xMin, xMax, yMin, yMax; - - private double xScale, yScale; - private static String defaultDirectory = null; - private String xLabel; - private String yLabel; - private int flags; - private Font font = new Font("Helvetica", Font.PLAIN, 12); - private boolean fixedYScale; - private int lineWidth = 1; // Line.getWidth(); - private int markSize = 5; - private ImageProcessor ip; - private String title; - private boolean initialized; - private boolean plotDrawn; - private int plotWidth = PlotWindow.plotWidth; - private int plotHeight = PlotWindow.plotHeight; - private boolean multiplePlots; - private boolean drawPending; - - /** Construct a new PlotWindow. - * @param title the window title - * @param xLabel the x-axis label - * @param yLabel the y-axis label - * @param xValues the x-coodinates, or null - * @param yValues the y-coodinates, or null - * @param flags sum of flag values controlling appearance of ticks, grid, etc. - */ - public Plot(String title, String xLabel, String yLabel, float[] xValues, float[] yValues, int flags) { - this.title = title; - this.xLabel = xLabel; - this.yLabel = yLabel; - this.flags = flags; - if (xValues==null || yValues==null) { - xValues = new float[1]; - yValues = new float[1]; - xValues[0] = -1f; - yValues[0] = -1f; - } - this.xValues = xValues; - this.yValues = yValues; - double[] a = Tools.getMinMax(xValues); - xMin=a[0]; xMax=a[1]; - a = Tools.getMinMax(yValues); - yMin=a[0]; yMax=a[1]; - fixedYScale = false; - nPoints = xValues.length; - drawPending = true; - } - - /** This version of the constructor uses the default flags. */ - public Plot(String title, String xLabel, String yLabel, float[] xValues, float[] yValues) { - this(title, xLabel, yLabel, xValues, yValues, DEFAULT_FLAGS); - } - - /** This version of the constructor accepts double arrays. */ - public Plot(String title, String xLabel, String yLabel, double[] xValues, double[] yValues, int flags) { - this(title, xLabel, yLabel, xValues!=null?Tools.toFloat(xValues):null, yValues!=null?Tools.toFloat(yValues):null, flags); - } - - /** This version of the constructor accepts double arrays and uses the default flags */ - public Plot(String title, String xLabel, String yLabel, double[] xValues, double[] yValues) { - this(title, xLabel, yLabel, xValues!=null?Tools.toFloat(xValues):null, yValues!=null?Tools.toFloat(yValues):null, DEFAULT_FLAGS); - } - - /** Sets the x-axis and y-axis range. */ - public void setLimits(double xMin, double xMax, double yMin, double yMax) { - this.xMin = xMin; - this.xMax = xMax; - this.yMin = yMin; - this.yMax = yMax; - fixedYScale = true; - if (initialized) { - ip.setColor(Color.white); - ip.resetRoi(); - ip.fill(); - ip.setColor(Color.black); - setScaleAndDrawAxisLabels(); - } - } - - /** Sets the canvas size (i.e., size of the resulting ImageProcessor). - * By default, the size is adjusted for the plot frame size specified - * in Edit>Options>Profile Plot Options*/ - public void setSize(int width, int height) { - if (!initialized && width>LEFT_MARGIN+RIGHT_MARGIN+20 && height>TOP_MARGIN+BOTTOM_MARGIN+20) { - plotWidth = width-LEFT_MARGIN-RIGHT_MARGIN; - plotHeight = height-TOP_MARGIN-BOTTOM_MARGIN; - } - } - - /** Adds a set of points to the plot or adds a curve if shape is set to LINE. - * @param x the x-coodinates - * @param y the y-coodinates - * @param shape CIRCLE, X, BOX, TRIANGLE, CROSS, DOT or LINE - */ - public void addPoints(float[] x, float[] y, int shape) { - setup(); - switch(shape) { - case CIRCLE: case X: case BOX: case TRIANGLE: case CROSS: case DOT: - ip.setClipRect(frame); - for (int i=0; ijustification - * is ImageProcessor.LEFT, ImageProcessor.CENTER or ImageProcessor.RIGHT. */ - public void setJustification(int justification) { - setup(); - ip.setJustification(justification); - } - - /** Changes the drawing color. For selecting the color of the data passed with the constructor, - * use setColor before draw. - * The frame and labels are always drawn in black. */ - public void setColor(Color c) { - setup(); - if (!(ip instanceof ColorProcessor)) { - ip = ip.convertToRGB(); - ip.setLineWidth(lineWidth); - ip.setFont(font); - ip.setAntialiasedText(true); - } - ip.setColor(c); - } - - /** Changes the line width. */ - public void setLineWidth(int lineWidth) { - if (lineWidth<1) lineWidth = 1; - setup(); - ip.setLineWidth(lineWidth); - this.lineWidth = lineWidth; - markSize = lineWidth==1?5:7; - } - - /* Draws a line using the coordinate system defined by setLimits(). */ - public void drawLine(double x1, double y1, double x2, double y2) { - setup(); - int ix1 = LEFT_MARGIN + (int)Math.round((x1-xMin)*xScale); - int iy1 = TOP_MARGIN + frameHeight - (int)Math.round((y1-yMin)*yScale); - int ix2 = LEFT_MARGIN + (int)Math.round((x2-xMin)*xScale); - int iy2 = TOP_MARGIN + frameHeight - (int)Math.round((y2-yMin)*yScale); - ip.drawLine(ix1, iy1, ix2, iy2); - } - - /** Changes the font. */ - public void changeFont(Font font) { - setup(); - ip.setFont(font); - this.font = font; - } - - void setup() { - if (initialized) - return; - initialized = true; - createImage(); - ip.setColor(Color.black); - if (lineWidth>3) - lineWidth = 3; - ip.setLineWidth(lineWidth); - ip.setFont(font); - ip.setAntialiasedText(true); - if (frameWidth==0) { - frameWidth = plotWidth; - frameHeight = plotHeight; - } - frame = new Rectangle(LEFT_MARGIN, TOP_MARGIN, frameWidth, frameHeight); - setScaleAndDrawAxisLabels(); - } - - void setScaleAndDrawAxisLabels() { - if ((xMax-xMin)==0.0) - xScale = 1.0; - else - xScale = frame.width/(xMax-xMin); - if ((yMax-yMin)==0.0) - yScale = 1.0; - else - yScale = frame.height/(yMax-yMin); - if (PlotWindow.noGridLines) - drawAxisLabels(); - else - drawTicksEtc(); - } - - void drawAxisLabels() { - int digits = getDigits(yMin, yMax); - String s = IJ.d2s(yMax, digits); - int sw = ip.getStringWidth(s); - if ((sw+4)>LEFT_MARGIN) - ip.drawString(s, 4, TOP_MARGIN-4); - else - ip.drawString(s, LEFT_MARGIN-ip.getStringWidth(s)-4, TOP_MARGIN+10); - s = IJ.d2s(yMin, digits); - sw = ip.getStringWidth(s); - if ((sw+4)>LEFT_MARGIN) - ip.drawString(s, 4, TOP_MARGIN+frame.height); - else - ip.drawString(s, LEFT_MARGIN-ip.getStringWidth(s)-4, TOP_MARGIN+frame.height); - FontMetrics fm = ip.getFontMetrics(); - int x = LEFT_MARGIN; - int y = TOP_MARGIN + frame.height + fm.getAscent() + 6; - digits = getDigits(xMin, xMax); - ip.drawString(IJ.d2s(xMin,digits), x, y); - s = IJ.d2s(xMax,digits); - ip.drawString(s, x + frame.width-ip.getStringWidth(s)+6, y); - ip.drawString(xLabel, LEFT_MARGIN+(frame.width-ip.getStringWidth(xLabel))/2, y+3); - drawYLabel(yLabel,LEFT_MARGIN-4,TOP_MARGIN,frame.height, fm); - } - - void drawTicksEtc() { - int fontAscent = ip.getFontMetrics().getAscent(); - int fontMaxAscent = ip.getFontMetrics().getMaxAscent(); - if ((flags&(X_NUMBERS + X_TICKS + X_GRID)) != 0) { - double step = Math.abs(Math.max(frame.width/MAX_INTERVALS, MIN_X_GRIDWIDTH)/xScale); //the smallest allowable increment - step = niceNumber(step); - int i1, i2; - if ((flags&X_FORCE2GRID) != 0) { - i1 = (int)Math.floor(Math.min(xMin,xMax)/step+1.e-10); //this also allows for inverted xMin, xMax - i2 = (int)Math.ceil(Math.max(xMin,xMax)/step-1.e-10); - xMin = i1*step; - xMax = i2*step; - xScale = frame.width/(xMax-xMin); //rescale to make it fit - } else { - i1 = (int)Math.ceil(Math.min(xMin,xMax)/step-1.e-10); - i2 = (int)Math.floor(Math.max(xMin,xMax)/step+1.e-10); - } - int digits = -(int)Math.floor(Math.log(step)/Math.log(10)+1e-6); - if (digits < 0) digits = 0; - if (digits>5) digits = -3; // use scientific notation - int y1 = TOP_MARGIN; - int y2 = TOP_MARGIN+frame.height; - int yNumbers = y2 + fontAscent + 7; - for (int i=0; i<=(i2-i1); i++) { - double v = (i+i1)*step; - int x = (int)Math.round((v - xMin)*xScale) + LEFT_MARGIN; - if ((flags&X_GRID) != 0) { - ip.setColor(gridColor); - ip.drawLine(x, y1, x, y2); - ip.setColor(Color.black); - } - if ((flags&X_TICKS) !=0) { - ip.drawLine(x, y1, x, y1+TICK_LENGTH); - ip.drawLine(x, y2, x, y2-TICK_LENGTH); - } - if ((flags&X_NUMBERS) != 0) { - String s = IJ.d2s(v,digits); - ip.drawString(s, x-ip.getStringWidth(s)/2, yNumbers); - } - } - } - int maxNumWidth = 0; - if ((flags&(Y_NUMBERS + Y_TICKS + Y_GRID)) != 0) { - double step = Math.abs(Math.max(frame.width/MAX_INTERVALS, MIN_Y_GRIDWIDTH)/yScale); //the smallest allowable increment - step = niceNumber(step); - int i1, i2; - if ((flags&X_FORCE2GRID) != 0) { - i1 = (int)Math.floor(Math.min(yMin,yMax)/step+1.e-10); //this also allows for inverted xMin, xMax - i2 = (int)Math.ceil(Math.max(yMin,yMax)/step-1.e-10); - yMin = i1*step; - yMax = i2*step; - yScale = frame.height/(yMax-yMin); //rescale to make it fit - } else { - i1 = (int)Math.ceil(Math.min(yMin,yMax)/step-1.e-10); - i2 = (int)Math.floor(Math.max(yMin,yMax)/step+1.e-10); - } - int digits = -(int)Math.floor(Math.log(step)/Math.log(10)+1e-6); - if (digits < 0) digits = 0; - if (digits>5) digits = -3; // use scientific notation - int x1 = LEFT_MARGIN; - int x2 = LEFT_MARGIN+frame.width; - for (int i=0; i<=(i2-i1); i++) { - double v = (i+i1)*step; - int y = TOP_MARGIN + frame.height - (int)Math.round((v - yMin)*yScale); - if ((flags&Y_GRID) != 0) { - ip.setColor(gridColor); - ip.drawLine(x1, y, x2, y); - ip.setColor(Color.black); - } - if ((flags&Y_TICKS) !=0) { - ip.drawLine(x1, y, x1+TICK_LENGTH, y); - ip.drawLine(x2, y, x2-TICK_LENGTH, y); - } - if ((flags&Y_NUMBERS) != 0) { - String s = IJ.d2s(v,digits); - int w = ip.getStringWidth(s); - if (w>maxNumWidth) maxNumWidth = w; - ip.drawString(s, LEFT_MARGIN-w-4, y+fontMaxAscent/2+1); - } - } - } - if ((flags&Y_NUMBERS)==0) { //simply note y-axis min&max - int digits = getDigits(yMin, yMax); - String s = IJ.d2s(yMax, digits); - int sw = ip.getStringWidth(s); - if ((sw+4)>LEFT_MARGIN) - ip.drawString(s, 4, TOP_MARGIN-4); - else - ip.drawString(s, LEFT_MARGIN-ip.getStringWidth(s)-4, TOP_MARGIN+10); - s = IJ.d2s(yMin, digits); - sw = ip.getStringWidth(s); - if ((sw+4)>LEFT_MARGIN) - ip.drawString(s, 4, TOP_MARGIN+frame.height); - else - ip.drawString(s, LEFT_MARGIN-ip.getStringWidth(s)-4, TOP_MARGIN+frame.height); - } - FontMetrics fm = ip.getFontMetrics(); - int x = LEFT_MARGIN; - int y = TOP_MARGIN + frame.height + fm.getAscent() + 6; - if ((flags&X_NUMBERS)==0) { //simply note x-axis min&max - int digits = getDigits(xMin, xMax); - ip.drawString(IJ.d2s(xMin,digits), x, y); - String s = IJ.d2s(xMax,digits); - ip.drawString(s, x + frame.width-ip.getStringWidth(s)+6, y); - } else - y += fm.getAscent(); //space needed for x numbers - ip.drawString(xLabel, LEFT_MARGIN+(frame.width-ip.getStringWidth(xLabel))/2, y+6); - drawYLabel(yLabel,LEFT_MARGIN-maxNumWidth-4,TOP_MARGIN,frame.height, fm); - } - - double niceNumber(double v) { //the smallest "nice" number >= v. "Nice" numbers are .. 0.5, 1, 2, 5, 10, 20 ... - double base = Math.pow(10,Math.floor(Math.log(v)/Math.log(10)-1.e-6)); - if (v > 5.0000001*base) return 10*base; - else if (v > 2.0000001*base) return 5*base; - else return 2*base; - } - - void createImage() { - if (ip!=null) return; - int width = plotWidth+LEFT_MARGIN+RIGHT_MARGIN; - int height = plotHeight+TOP_MARGIN+BOTTOM_MARGIN; - byte[] pixels = new byte[width*height]; - for (int i=0; i0.0?n1:n2; - double diff = Math.abs(n2-n1); - if (diff>0.0 && diff5) ip.setLineWidth(5); - ip.drawRect(frame.x, frame.y, frame.width+1, frame.height+1); - ip.setLineWidth(lineWidth); - } - - void drawPolyline(ImageProcessor ip, int[] x, int[] y, int n, boolean clip) { - if (clip) ip.setClipRect(frame); - ip.moveTo(x[0], y[0]); - for (int i=0; iOptions>Profile Plot Options*/ + public void setSize(int width, int height) { + if (!initialized && width>LEFT_MARGIN+RIGHT_MARGIN+20 && height>TOP_MARGIN+BOTTOM_MARGIN+20) { + plotWidth = width-LEFT_MARGIN-RIGHT_MARGIN; + plotHeight = height-TOP_MARGIN-BOTTOM_MARGIN; + } + } + + /** Adds a set of points to the plot or adds a curve if shape is set to LINE. + * @param x the x-coodinates + * @param y the y-coodinates + * @param shape CIRCLE, X, BOX, TRIANGLE, CROSS, DOT or LINE + */ + public void addPoints(float[] x, float[] y, int shape) { + setup(); + switch(shape) { + case CIRCLE: case X: case BOX: case TRIANGLE: case CROSS: case DOT: + ip.setClipRect(frame); + for (int i=0; ijustification + * is ImageProcessor.LEFT, ImageProcessor.CENTER or ImageProcessor.RIGHT. */ + public void setJustification(int justification) { + setup(); + ip.setJustification(justification); + } + + /** Changes the drawing color. For selecting the color of the data passed with the constructor, + * use setColor before draw. + * The frame and labels are always drawn in black. */ + public void setColor(Color c) { + setup(); + if (!(ip instanceof ColorProcessor)) { + ip = ip.convertToRGB(); + ip.setLineWidth(lineWidth); + ip.setFont(font); + ip.setAntialiasedText(true); + } + ip.setColor(c); + } + + /** Changes the line width. */ + public void setLineWidth(int lineWidth) { + if (lineWidth<1) lineWidth = 1; + setup(); + ip.setLineWidth(lineWidth); + this.lineWidth = lineWidth; + markSize = lineWidth==1?5:7; + } + + /* Draws a line using the coordinate system defined by setLimits(). */ + public void drawLine(double x1, double y1, double x2, double y2) { + setup(); + int ix1 = LEFT_MARGIN + (int)Math.round((x1-xMin)*xScale); + int iy1 = TOP_MARGIN + frameHeight - (int)Math.round((y1-yMin)*yScale); + int ix2 = LEFT_MARGIN + (int)Math.round((x2-xMin)*xScale); + int iy2 = TOP_MARGIN + frameHeight - (int)Math.round((y2-yMin)*yScale); + ip.drawLine(ix1, iy1, ix2, iy2); + } + + /** Changes the font. */ + public void changeFont(Font font) { + setup(); + ip.setFont(font); + this.font = font; + } + + void setup() { + if (initialized) + return; + initialized = true; + createImage(); + ip.setColor(Color.black); + if (lineWidth>3) + lineWidth = 3; + ip.setLineWidth(lineWidth); + ip.setFont(font); + ip.setAntialiasedText(true); + if (frameWidth==0) { + frameWidth = plotWidth; + frameHeight = plotHeight; + } + frame = new Rectangle(LEFT_MARGIN, TOP_MARGIN, frameWidth, frameHeight); + setScaleAndDrawAxisLabels(); + } + + void setScaleAndDrawAxisLabels() { + if ((xMax-xMin)==0.0) + xScale = 1.0; + else + xScale = frame.width/(xMax-xMin); + if ((yMax-yMin)==0.0) + yScale = 1.0; + else + yScale = frame.height/(yMax-yMin); + if (PlotWindow.noGridLines) + drawAxisLabels(); + else + drawTicksEtc(); + } + + void drawAxisLabels() { + int digits = getDigits(yMin, yMax); + String s = IJ.d2s(yMax, digits); + int sw = ip.getStringWidth(s); + if ((sw+4)>LEFT_MARGIN) + ip.drawString(s, 4, TOP_MARGIN-4); + else + ip.drawString(s, LEFT_MARGIN-ip.getStringWidth(s)-4, TOP_MARGIN+10); + s = IJ.d2s(yMin, digits); + sw = ip.getStringWidth(s); + if ((sw+4)>LEFT_MARGIN) + ip.drawString(s, 4, TOP_MARGIN+frame.height); + else + ip.drawString(s, LEFT_MARGIN-ip.getStringWidth(s)-4, TOP_MARGIN+frame.height); + FontMetrics fm = ip.getFontMetrics(); + int x = LEFT_MARGIN; + int y = TOP_MARGIN + frame.height + fm.getAscent() + 6; + digits = getDigits(xMin, xMax); + ip.drawString(IJ.d2s(xMin,digits), x, y); + s = IJ.d2s(xMax,digits); + ip.drawString(s, x + frame.width-ip.getStringWidth(s)+6, y); + ip.drawString(xLabel, LEFT_MARGIN+(frame.width-ip.getStringWidth(xLabel))/2, y+3); + drawYLabel(yLabel,LEFT_MARGIN-4,TOP_MARGIN,frame.height, fm); + } + + void drawTicksEtc() { + int fontAscent = ip.getFontMetrics().getAscent(); + int fontMaxAscent = ip.getFontMetrics().getMaxAscent(); + if ((flags&(X_NUMBERS + X_TICKS + X_GRID)) != 0) { + double step = Math.abs(Math.max(frame.width/MAX_INTERVALS, MIN_X_GRIDWIDTH)/xScale); //the smallest allowable increment + step = niceNumber(step); + int i1, i2; + if ((flags&X_FORCE2GRID) != 0) { + i1 = (int)Math.floor(Math.min(xMin,xMax)/step+1.e-10); //this also allows for inverted xMin, xMax + i2 = (int)Math.ceil(Math.max(xMin,xMax)/step-1.e-10); + xMin = i1*step; + xMax = i2*step; + xScale = frame.width/(xMax-xMin); //rescale to make it fit + } else { + i1 = (int)Math.ceil(Math.min(xMin,xMax)/step-1.e-10); + i2 = (int)Math.floor(Math.max(xMin,xMax)/step+1.e-10); + } + int digits = -(int)Math.floor(Math.log(step)/Math.log(10)+1e-6); + if (digits < 0) digits = 0; + if (digits>5) digits = -3; // use scientific notation + int y1 = TOP_MARGIN; + int y2 = TOP_MARGIN+frame.height; + int yNumbers = y2 + fontAscent + 7; + for (int i=0; i<=(i2-i1); i++) { + double v = (i+i1)*step; + int x = (int)Math.round((v - xMin)*xScale) + LEFT_MARGIN; + if ((flags&X_GRID) != 0) { + ip.setColor(gridColor); + ip.drawLine(x, y1, x, y2); + ip.setColor(Color.black); + } + if ((flags&X_TICKS) !=0) { + ip.drawLine(x, y1, x, y1+TICK_LENGTH); + ip.drawLine(x, y2, x, y2-TICK_LENGTH); + } + if ((flags&X_NUMBERS) != 0) { + String s = IJ.d2s(v,digits); + ip.drawString(s, x-ip.getStringWidth(s)/2, yNumbers); + } + } + } + int maxNumWidth = 0; + if ((flags&(Y_NUMBERS + Y_TICKS + Y_GRID)) != 0) { + double step = Math.abs(Math.max(frame.width/MAX_INTERVALS, MIN_Y_GRIDWIDTH)/yScale); //the smallest allowable increment + step = niceNumber(step); + int i1, i2; + if ((flags&X_FORCE2GRID) != 0) { + i1 = (int)Math.floor(Math.min(yMin,yMax)/step+1.e-10); //this also allows for inverted xMin, xMax + i2 = (int)Math.ceil(Math.max(yMin,yMax)/step-1.e-10); + yMin = i1*step; + yMax = i2*step; + yScale = frame.height/(yMax-yMin); //rescale to make it fit + } else { + i1 = (int)Math.ceil(Math.min(yMin,yMax)/step-1.e-10); + i2 = (int)Math.floor(Math.max(yMin,yMax)/step+1.e-10); + } + int digits = -(int)Math.floor(Math.log(step)/Math.log(10)+1e-6); + if (digits < 0) digits = 0; + if (digits>5) digits = -3; // use scientific notation + int x1 = LEFT_MARGIN; + int x2 = LEFT_MARGIN+frame.width; + for (int i=0; i<=(i2-i1); i++) { + double v = (i+i1)*step; + int y = TOP_MARGIN + frame.height - (int)Math.round((v - yMin)*yScale); + if ((flags&Y_GRID) != 0) { + ip.setColor(gridColor); + ip.drawLine(x1, y, x2, y); + ip.setColor(Color.black); + } + if ((flags&Y_TICKS) !=0) { + ip.drawLine(x1, y, x1+TICK_LENGTH, y); + ip.drawLine(x2, y, x2-TICK_LENGTH, y); + } + if ((flags&Y_NUMBERS) != 0) { + String s = IJ.d2s(v,digits); + int w = ip.getStringWidth(s); + if (w>maxNumWidth) maxNumWidth = w; + ip.drawString(s, LEFT_MARGIN-w-4, y+fontMaxAscent/2+1); + } + } + } + if ((flags&Y_NUMBERS)==0) { //simply note y-axis min&max + int digits = getDigits(yMin, yMax); + String s = IJ.d2s(yMax, digits); + int sw = ip.getStringWidth(s); + if ((sw+4)>LEFT_MARGIN) + ip.drawString(s, 4, TOP_MARGIN-4); + else + ip.drawString(s, LEFT_MARGIN-ip.getStringWidth(s)-4, TOP_MARGIN+10); + s = IJ.d2s(yMin, digits); + sw = ip.getStringWidth(s); + if ((sw+4)>LEFT_MARGIN) + ip.drawString(s, 4, TOP_MARGIN+frame.height); + else + ip.drawString(s, LEFT_MARGIN-ip.getStringWidth(s)-4, TOP_MARGIN+frame.height); + } + FontMetrics fm = ip.getFontMetrics(); + int x = LEFT_MARGIN; + int y = TOP_MARGIN + frame.height + fm.getAscent() + 6; + if ((flags&X_NUMBERS)==0) { //simply note x-axis min&max + int digits = getDigits(xMin, xMax); + ip.drawString(IJ.d2s(xMin,digits), x, y); + String s = IJ.d2s(xMax,digits); + ip.drawString(s, x + frame.width-ip.getStringWidth(s)+6, y); + } else + y += fm.getAscent(); //space needed for x numbers + ip.drawString(xLabel, LEFT_MARGIN+(frame.width-ip.getStringWidth(xLabel))/2, y+6); + drawYLabel(yLabel,LEFT_MARGIN-maxNumWidth-4,TOP_MARGIN,frame.height, fm); + } + + double niceNumber(double v) { //the smallest "nice" number >= v. "Nice" numbers are .. 0.5, 1, 2, 5, 10, 20 ... + double base = Math.pow(10,Math.floor(Math.log(v)/Math.log(10)-1.e-6)); + if (v > 5.0000001*base) return 10*base; + else if (v > 2.0000001*base) return 5*base; + else return 2*base; + } + + void createImage() { + if (ip!=null) return; + int width = plotWidth+LEFT_MARGIN+RIGHT_MARGIN; + int height = plotHeight+TOP_MARGIN+BOTTOM_MARGIN; + byte[] pixels = new byte[width*height]; + for (int i=0; i0.0?n1:n2; + double diff = Math.abs(n2-n1); + if (diff>0.0 && diff5) ip.setLineWidth(5); + ip.drawRect(frame.x, frame.y, frame.width+1, frame.height+1); + ip.setLineWidth(lineWidth); + } + + void drawPolyline(ImageProcessor ip, int[] x, int[] y, int n, boolean clip) { + if (clip) ip.setClipRect(frame); + ip.moveTo(x[0], y[0]); + for (int i=0; i0.0?n1:n2; - double diff = Math.abs(n2-n1); - if (diff>0.0 && diff=6) { - ydigits = setDigits; - if (ydigits==0) ydigits = 2; - digits = ydigits; - } - if (ydigits!=defaultDigits) { - realXValues = false; - for (int i=0; i=2 && (plot.xValues[1]-plot.xValues[0])<1.0) - xdigits = digits; - } - - public void lostOwnership(Clipboard clipboard, Transferable contents) {} - - public void actionPerformed(ActionEvent e) { - Object b = e.getSource(); - if (b==list) - showList(); - else if (b==save) - saveAsText(); - else - copyToClipboard(); - } - - public float[] getXValues() { - return plot.xValues; - } - - public float[] getYValues() { - return plot.yValues; - } - - /** Draws a new plot in this window. */ - public void drawPlot(Plot plot) { - this.plot = plot; - imp.setProcessor(null, plot.getProcessor()); - } - - /** Called once when ImageJ quits. */ - public static void savePreferences(Properties prefs) { - double min = ProfilePlot.getFixedMin(); - double max = ProfilePlot.getFixedMax(); - if (!(min==0.0&&max==0.0) && min0.0?n1:n2; + double diff = Math.abs(n2-n1); + if (diff>0.0 && diff=6) { + ydigits = setDigits; + if (ydigits==0) ydigits = 2; + digits = ydigits; + } + if (ydigits!=defaultDigits) { + realXValues = false; + for (int i=0; i=2 && (plot.xValues[1]-plot.xValues[0])<1.0) + xdigits = digits; + } + + public void lostOwnership(Clipboard clipboard, Transferable contents) {} + + public void actionPerformed(ActionEvent e) { + Object b = e.getSource(); + if (b==list) + showList(); + else if (b==save) + saveAsText(); + else + copyToClipboard(); + } + + public float[] getXValues() { + return plot.xValues; + } + + public float[] getYValues() { + return plot.yValues; + } + + /** Draws a new plot in this window. */ + public void drawPlot(Plot plot) { + this.plot = plot; + imp.setProcessor(null, plot.getProcessor()); + } + + /** Called once when ImageJ quits. */ + public static void savePreferences(Properties prefs) { + double min = ProfilePlot.getFixedMin(); + double max = ProfilePlot.getFixedMax(); + if (!(min==0.0&&max==0.0) && minmaxWidth) { - width = maxWidth; - height = (int)(width*ASPECT_RATIO); - } - return new Dimension(width, height); - } - - /** Displays this profile plot in a window. */ - public void createWindow() { - if (profile==null) - return; - Dimension d = getPlotSize(); - String xLabel = "Distance ("+units+")"; - int n = profile.length; - if (xValues==null) { - xValues = new float[n]; - for (int i=0; i0 && (title.length()-index)<=5) - title = title.substring(0, index); - return title; - } - - /** Returns the profile plot data. */ - public double[] getProfile() { - return profile; - } - - /** Returns the calculated minimum value. */ - public double getMin() { - if (!minAndMaxCalculated) - findMinAndMax(); - return min; - } - - /** Returns the calculated maximum value. */ - public double getMax() { - if (!minAndMaxCalculated) - findMinAndMax(); - return max; - } - - /** Sets the y-axis min and max. Specify (0,0) to autoscale. */ - public static void setMinAndMax(double min, double max) { - fixedMin = min; - fixedMax = max; - IJ.register(ProfilePlot.class); - } - - /** Returns the profile plot y-axis min. Auto-scaling is used if min=max=0. */ - public static double getFixedMin() { - return fixedMin; - } - - /** Returns the profile plot y-axis max. Auto-scaling is used if min=max=0. */ - public static double getFixedMax() { - return fixedMax; - } - - double[] getStraightLineProfile(Roi roi, Calibration cal, ImageProcessor ip) { - ip.setInterpolate(PlotWindow.interpolate); - Line line = (Line)roi; - double[] values = line.getPixels(); - if (values==null) return null; - if (cal!=null && cal.pixelWidth!=cal.pixelHeight) { - double dx = cal.pixelWidth*(line.x2 - line.x1); - double dy = cal.pixelHeight*(line.y2 - line.y1); - double length = Math.round(Math.sqrt(dx*dx + dy*dy)); - if (values.length>1) - xInc = length/(values.length-1); - } - return values; - } - - double[] getRowAverageProfile(Rectangle rect, Calibration cal, ImageProcessor ip) { - double[] profile = new double[rect.height]; - double[] aLine; - - ip.setInterpolate(false); - for (int x=rect.x; x0) { - double deltax = cal.pixelWidth*(rx-oldrx); - double deltay = cal.pixelHeight*(ry-oldry); - xvalue += Math.sqrt(deltax*deltax + deltay*deltay); - xValues[index] = (float)xvalue; - } - oldrx = rx; oldry=ry; - } - rx += xinc; - ry += yinc; - } - distance += len; - leftOver = len2 - n2; - } - return values; - } - - double[] getWideLineProfile(ImagePlus imp, int lineWidth) { - Roi roi = (Roi)imp.getRoi().clone(); - ImageProcessor ip2 = (new Straightener()).straightenLine(imp, lineWidth); - int width = ip2.getWidth(); - int height = ip2.getHeight(); - profile = new double[width]; - double[] aLine; - ip2.setInterpolate(false); - for (int y=0; ymax) max=value; - } - this.min = min; - this.max = max; - } - - +package ij.gui; + +import java.awt.*; +import ij.*; +import ij.process.*; +import ij.util.*; +import ij.measure.*; +import ij.plugin.Straightener; + +/** Creates a density profile plot of a rectangular selection or line selection. */ +public class ProfilePlot { + + static final int MIN_WIDTH = 350; + static final double ASPECT_RATIO = 0.5; + private double min, max; + private boolean minAndMaxCalculated; + private static double fixedMin = Prefs.getDouble("pp.min",0.0); + private static double fixedMax = Prefs.getDouble("pp.max",0.0); + + protected ImagePlus imp; + protected double[] profile; + protected double magnification; + protected double xInc; + protected String units; + protected String yLabel; + protected float[] xValues; + + + public ProfilePlot() { + } + + public ProfilePlot(ImagePlus imp) { + this(imp, false); + } + + public ProfilePlot(ImagePlus imp, boolean averageHorizontally) { + this.imp = imp; + Roi roi = imp.getRoi(); + if (roi==null) { + IJ.error("Profile Plot", "Selection required."); + return; + } + int roiType = roi.getType(); + if (!(roi.isLine() || roiType==Roi.RECTANGLE)) { + IJ.error("Line or rectangular selection required."); + return; + } + Calibration cal = imp.getCalibration(); + xInc = cal.pixelWidth; + units = cal.getUnits(); + yLabel = cal.getValueUnit(); + ImageProcessor ip = imp.getProcessor(); + //ip.setCalibrationTable(cal.getCTable()); + if (roiType==Roi.LINE) + profile = getStraightLineProfile(roi, cal, ip); + else if (roiType==Roi.POLYLINE || roiType==Roi.FREELINE) { + int lineWidth = Line.getWidth(); + if (lineWidth==1) + profile = getIrregularProfile(roi, ip, cal); + else + profile = getWideLineProfile(imp, lineWidth); + } else if (averageHorizontally) + profile = getRowAverageProfile(roi.getBounds(), cal, ip); + else + profile = getColumnAverageProfile(roi.getBounds(), ip); + ip.setCalibrationTable(null); + ImageCanvas ic = imp.getCanvas(); + if (ic!=null) + magnification = ic.getMagnification(); + else + magnification = 1.0; + } + + //void calibrate(Calibration cal) { + // float[] cTable = cal.getCTable(); + // if (cTable!=null) + // for () + // profile[i] = profile[i]; + // + //} + + /** Returns the size of the plot that createWindow() creates. */ + public Dimension getPlotSize() { + if (profile==null) return null; + int width = (int)(profile.length*magnification); + int height = (int)(width*ASPECT_RATIO); + if (widthmaxWidth) { + width = maxWidth; + height = (int)(width*ASPECT_RATIO); + } + return new Dimension(width, height); + } + + /** Displays this profile plot in a window. */ + public void createWindow() { + if (profile==null) + return; + Dimension d = getPlotSize(); + String xLabel = "Distance ("+units+")"; + int n = profile.length; + if (xValues==null) { + xValues = new float[n]; + for (int i=0; i0 && (title.length()-index)<=5) + title = title.substring(0, index); + return title; + } + + /** Returns the profile plot data. */ + public double[] getProfile() { + return profile; + } + + /** Returns the calculated minimum value. */ + public double getMin() { + if (!minAndMaxCalculated) + findMinAndMax(); + return min; + } + + /** Returns the calculated maximum value. */ + public double getMax() { + if (!minAndMaxCalculated) + findMinAndMax(); + return max; + } + + /** Sets the y-axis min and max. Specify (0,0) to autoscale. */ + public static void setMinAndMax(double min, double max) { + fixedMin = min; + fixedMax = max; + IJ.register(ProfilePlot.class); + } + + /** Returns the profile plot y-axis min. Auto-scaling is used if min=max=0. */ + public static double getFixedMin() { + return fixedMin; + } + + /** Returns the profile plot y-axis max. Auto-scaling is used if min=max=0. */ + public static double getFixedMax() { + return fixedMax; + } + + double[] getStraightLineProfile(Roi roi, Calibration cal, ImageProcessor ip) { + ip.setInterpolate(PlotWindow.interpolate); + Line line = (Line)roi; + double[] values = line.getPixels(); + if (values==null) return null; + if (cal!=null && cal.pixelWidth!=cal.pixelHeight) { + double dx = cal.pixelWidth*(line.x2 - line.x1); + double dy = cal.pixelHeight*(line.y2 - line.y1); + double length = Math.round(Math.sqrt(dx*dx + dy*dy)); + if (values.length>1) + xInc = length/(values.length-1); + } + return values; + } + + double[] getRowAverageProfile(Rectangle rect, Calibration cal, ImageProcessor ip) { + double[] profile = new double[rect.height]; + double[] aLine; + + ip.setInterpolate(false); + for (int x=rect.x; x0) { + double deltax = cal.pixelWidth*(rx-oldrx); + double deltay = cal.pixelHeight*(ry-oldry); + xvalue += Math.sqrt(deltax*deltax + deltay*deltay); + xValues[index] = (float)xvalue; + } + oldrx = rx; oldry=ry; + } + rx += xinc; + ry += yinc; + } + distance += len; + leftOver = len2 - n2; + } + return values; + } + + double[] getWideLineProfile(ImagePlus imp, int lineWidth) { + Roi roi = (Roi)imp.getRoi().clone(); + ImageProcessor ip2 = (new Straightener()).straightenLine(imp, lineWidth); + int width = ip2.getWidth(); + int height = ip2.getHeight(); + profile = new double[width]; + double[] aLine; + ip2.setInterpolate(false); + for (int y=0; ymax) max=value; + } + this.min = min; + this.max = max; + } + + } \ No newline at end of file diff --git a/ij/gui/ProgressBar.java b/ij/gui/ProgressBar.java index dbbad5284..dccf13e62 100644 --- a/ij/gui/ProgressBar.java +++ b/ij/gui/ProgressBar.java @@ -1,117 +1,117 @@ -package ij.gui; -import ij.macro.Interpreter; -import java.awt.*; -import java.awt.image.*; - -/** This is the progress bar that is displayed in the lower - right hand corner of the ImageJ window. Use one of the static - IJ.showProgress() methods to display and update the progress bar. */ -public class ProgressBar extends Canvas { - - private int canvasWidth, canvasHeight; - private int x, y, width, height; - private double percent; - private long lastTime = 0; - private boolean showBar; - private boolean batchMode; - - private Color barColor = Color.gray; - private Color fillColor = new Color(204,204,255); - private Color backgroundColor = ij.ImageJ.backgroundColor; - private Color frameBrighter = backgroundColor.brighter(); - private Color frameDarker = backgroundColor.darker(); - - /** This constructor is called once by ImageJ at startup. */ - public ProgressBar(int canvasWidth, int canvasHeight) { - this.canvasWidth = canvasWidth; - this.canvasHeight = canvasHeight; - x = 3; - y = 5; - width = canvasWidth - 8; - height = canvasHeight - 7; - } - - void fill3DRect(Graphics g, int x, int y, int width, int height) { - g.setColor(fillColor); - g.fillRect(x+1, y+1, width-2, height-2); - g.setColor(frameDarker); - g.drawLine(x, y, x, y+height); - g.drawLine(x+1, y, x+width-1, y); - g.setColor(frameBrighter); - g.drawLine(x+1, y+height, x+width, y+height); - g.drawLine(x+width, y, x+width, y+height-1); - } - - /** Updates the progress bar, where percent should run from 0 to 1. */ - public void show(double percent) { - show(percent, false); - } - - /** Updates the progress bar, where percent should run from 0 to 1. - * percent = 1.0 erases the bar. - * The bar is updated only if more than 90 ms have passed since - * the last call. Does nothing if the ImageJ window is not present. - * @param percent Length of the progress bar to display (0...1) - * @param showInBatchMode Whether the progress bar should be shown in - * batch mode. - */ - public void show(double percent, boolean showInBatchMode) { - if (!showInBatchMode && (batchMode||Interpreter.isBatchMode())) return; - if (percent>=1.0) { //clear the progress bar - percent = 0.0; - showBar = false; - repaint(); - return; - } - long time = System.currentTimeMillis(); - if (time - lastTime < 90 && percent != 1.0) return; - lastTime = time; - showBar = true; - this.percent = percent; - repaint(); - } - - /** Updates the progress bar, where the length of the bar is set to - * (currentValue+1)/finalValue of the maximum bar length. - * The bar is erased if currentValue>=finalValue. - */ - public void show(int currentIndex, int finalIndex) { - show((currentIndex+1.0)/(double)finalIndex, true); - } - - public void update(Graphics g) { - paint(g); - } - - public void paint(Graphics g) { - if (showBar) { - fill3DRect(g, x-1, y-1, width+1, height+1); - drawBar(g); - } else { - g.setColor(backgroundColor); - g.fillRect(0, 0, canvasWidth, canvasHeight); - } - } - - void drawBar(Graphics g) { - if (percent<0.0) - percent = 0.0; - int barEnd = (int)(width*percent); -// if (negativeProgress) { -// g.setColor(fillColor); -// g.fillRect(barEnd+2, y, width-barEnd, height); -// } else { - g.setColor(barColor); - g.fillRect(x, y, barEnd, height); -// } - } - - public Dimension getPreferredSize() { - return new Dimension(canvasWidth, canvasHeight); - } - - public void setBatchMode(boolean batchMode) { - this.batchMode = batchMode; - } - +package ij.gui; +import ij.macro.Interpreter; +import java.awt.*; +import java.awt.image.*; + +/** This is the progress bar that is displayed in the lower + right hand corner of the ImageJ window. Use one of the static + IJ.showProgress() methods to display and update the progress bar. */ +public class ProgressBar extends Canvas { + + private int canvasWidth, canvasHeight; + private int x, y, width, height; + private double percent; + private long lastTime = 0; + private boolean showBar; + private boolean batchMode; + + private Color barColor = Color.gray; + private Color fillColor = new Color(204,204,255); + private Color backgroundColor = ij.ImageJ.backgroundColor; + private Color frameBrighter = backgroundColor.brighter(); + private Color frameDarker = backgroundColor.darker(); + + /** This constructor is called once by ImageJ at startup. */ + public ProgressBar(int canvasWidth, int canvasHeight) { + this.canvasWidth = canvasWidth; + this.canvasHeight = canvasHeight; + x = 3; + y = 5; + width = canvasWidth - 8; + height = canvasHeight - 7; + } + + void fill3DRect(Graphics g, int x, int y, int width, int height) { + g.setColor(fillColor); + g.fillRect(x+1, y+1, width-2, height-2); + g.setColor(frameDarker); + g.drawLine(x, y, x, y+height); + g.drawLine(x+1, y, x+width-1, y); + g.setColor(frameBrighter); + g.drawLine(x+1, y+height, x+width, y+height); + g.drawLine(x+width, y, x+width, y+height-1); + } + + /** Updates the progress bar, where percent should run from 0 to 1. */ + public void show(double percent) { + show(percent, false); + } + + /** Updates the progress bar, where percent should run from 0 to 1. + * percent = 1.0 erases the bar. + * The bar is updated only if more than 90 ms have passed since + * the last call. Does nothing if the ImageJ window is not present. + * @param percent Length of the progress bar to display (0...1) + * @param showInBatchMode Whether the progress bar should be shown in + * batch mode. + */ + public void show(double percent, boolean showInBatchMode) { + if (!showInBatchMode && (batchMode||Interpreter.isBatchMode())) return; + if (percent>=1.0) { //clear the progress bar + percent = 0.0; + showBar = false; + repaint(); + return; + } + long time = System.currentTimeMillis(); + if (time - lastTime < 90 && percent != 1.0) return; + lastTime = time; + showBar = true; + this.percent = percent; + repaint(); + } + + /** Updates the progress bar, where the length of the bar is set to + * (currentValue+1)/finalValue of the maximum bar length. + * The bar is erased if currentValue>=finalValue. + */ + public void show(int currentIndex, int finalIndex) { + show((currentIndex+1.0)/(double)finalIndex, true); + } + + public void update(Graphics g) { + paint(g); + } + + public void paint(Graphics g) { + if (showBar) { + fill3DRect(g, x-1, y-1, width+1, height+1); + drawBar(g); + } else { + g.setColor(backgroundColor); + g.fillRect(0, 0, canvasWidth, canvasHeight); + } + } + + void drawBar(Graphics g) { + if (percent<0.0) + percent = 0.0; + int barEnd = (int)(width*percent); +// if (negativeProgress) { +// g.setColor(fillColor); +// g.fillRect(barEnd+2, y, width-barEnd, height); +// } else { + g.setColor(barColor); + g.fillRect(x, y, barEnd, height); +// } + } + + public Dimension getPreferredSize() { + return new Dimension(canvasWidth, canvasHeight); + } + + public void setBatchMode(boolean batchMode) { + this.batchMode = batchMode; + } + } \ No newline at end of file diff --git a/ij/gui/Roi.java b/ij/gui/Roi.java index 8bb127497..ce3fce1fd 100644 --- a/ij/gui/Roi.java +++ b/ij/gui/Roi.java @@ -1,1218 +1,1218 @@ -package ij.gui; - -import java.awt.*; -import java.awt.image.*; -import java.awt.event.KeyEvent; -import java.awt.geom.*; -import ij.*; -import ij.process.*; -import ij.measure.*; -import ij.plugin.frame.Recorder; -import ij.plugin.filter.Analyzer; -import ij.macro.Interpreter; - -/** A rectangular region of interest and superclass for the other ROI classes. */ -public class Roi extends Object implements Cloneable, java.io.Serializable { - - public static final int CONSTRUCTING=0, MOVING=1, RESIZING=2, NORMAL=3, MOVING_HANDLE=4; // States - public static final int RECTANGLE=0, OVAL=1, POLYGON=2, FREEROI=3, TRACED_ROI=4, LINE=5, - POLYLINE=6, FREELINE=7, ANGLE=8, COMPOSITE=9, POINT=10; // Types - public static final int HANDLE_SIZE = 5; - public static final int NOT_PASTING = -1; - - static final int NO_MODS=0, ADD_TO_ROI=1, SUBTRACT_FROM_ROI=2; // modification states - - int startX, startY, x, y, width, height; - int activeHandle; - int state; - int modState = NO_MODS; - - public static Roi previousRoi; - protected static Color ROIColor = Prefs.getColor(Prefs.ROICOLOR,Color.yellow); - protected static int pasteMode = Blitter.COPY; - protected static int lineWidth = 1; - protected static Color defaultFillColor; - - protected int type; - protected int xMax, yMax; - protected ImagePlus imp; - protected ImageCanvas ic; - protected int oldX, oldY, oldWidth, oldHeight; - protected int clipX, clipY, clipWidth, clipHeight; - protected ImagePlus clipboard; - protected boolean constrain; // to be square - protected boolean center; - protected boolean aspect; - protected boolean updateFullWindow; - protected double mag = 1.0; - protected double asp_bk; //saves aspect ratio if resizing takes roi very small - protected String name; - protected ImageProcessor cachedMask; - protected Color handleColor = Color.white; - protected Color outlineColor; - protected Color instanceColor; //obsolete; replaced by outlineColor - protected Color fillColor; - protected BasicStroke stroke; - protected boolean nonScalable; - protected boolean displayList; - - - /** Creates a new rectangular Roi. */ - public Roi(int x, int y, int width, int height) { - setImage(null); - if (width<1) width = 1; - if (height<1) height = 1; - if (width>xMax) width = xMax; - if (height>yMax) height = yMax; - //setLocation(x, y); - this.x = x; - this.y = y; - startX = x; startY = y; - oldX = x; oldY = y; oldWidth=0; oldHeight=0; - this.width = width; - this.height = height; - oldWidth=width; - oldHeight=height; - clipX = x; - clipY = y; - clipWidth = width; - clipHeight = height; - state = NORMAL; - type = RECTANGLE; - if (ic!=null) { - Graphics g = ic.getGraphics(); - draw(g); - g.dispose(); - } - fillColor = defaultFillColor; - } - - /** Creates a new rectangular Roi. */ - public Roi(Rectangle r) { - this(r.x, r.y, r.width, r.height); - } - - /** Starts the process of creating a user-defined rectangular Roi, - where sx and sy are the starting screen coordinates. */ - public Roi(int sx, int sy, ImagePlus imp) { - setImage(imp); - int ox=sx, oy=sy; - if (ic!=null) { - ox = ic.offScreenX(sx); - oy = ic.offScreenY(sy); - } - setLocation(ox, oy); - width = 0; - height = 0; - state = CONSTRUCTING; - type = RECTANGLE; - fillColor = defaultFillColor; - } - - /** Obsolete */ - public Roi(int x, int y, int width, int height, ImagePlus imp) { - this(x, y, width, height); - setImage(imp); - } - - /** Set the location of the ROI in image coordinates. */ - public void setLocation(int x, int y) { - this.x = x; - this.y = y; - startX = x; startY = y; - oldX = x; oldY = y; oldWidth=0; oldHeight=0; - } - - public void setImage(ImagePlus imp) { - this.imp = imp; - cachedMask = null; - if (imp==null) { - ic = null; - clipboard = null; - xMax = 99999; - yMax = 99999; - } else { - ic = imp.getCanvas(); - xMax = imp.getWidth(); - yMax = imp.getHeight(); - } - } - - ImagePlus getImage() { - return imp; - } - - public int getType() { - return type; - } - - public int getState() { - return state; - } - - /** Returns the perimeter length. */ - public double getLength() { - double pw=1.0, ph=1.0; - if (imp!=null) { - Calibration cal = imp.getCalibration(); - pw = cal.pixelWidth; - ph = cal.pixelHeight; - } - return 2.0*width*pw+2.0*height*ph; - } - - /** Returns Feret's diameter, the greatest distance between - any two points along the ROI boundary. */ - public double getFeretsDiameter() { - double[] a = getFeretValues(); - return a!=null?a[0]:0.0; - } - - /** Caculates "Feret" (maximum caliper width), "FeretAngle" - and "MinFeret" (minimum caliper width), "FeretX" and "FeretY". */ - public double[] getFeretValues() { - double min=Double.MAX_VALUE, diameter=0.0, angle=0.0, feretX=0.0, feretY=0.0; - int p1=0, p2=0; - double pw=1.0, ph=1.0; - if (imp!=null) { - Calibration cal = imp.getCalibration(); - pw = cal.pixelWidth; - ph = cal.pixelHeight; - } - Polygon poly = getConvexHull(); - if (poly==null) { - poly = getPolygon(); - if (poly==null) return null; - } - double w2=pw*pw, h2=ph*ph; - double dx, dy, d; - for (int i=0; idiameter) {diameter=d; p1=i; p2=j;} - } - } - Rectangle r = getBounds(); - double cx = r.x + r.width/2.0; - double cy = r.y + r.height/2.0; - int n = poly.npoints; - double[] x = new double[n]; - double[] y = new double[n]; - for (int i=0; ixmax) xmax = xr; - if (yrymax) ymax = yr; - } - double width = xmax - xmin; - double height = ymax - ymin; - double min2 = Math.min(width, height); - min = Math.min(min, min2); - } - double x1=poly.xpoints[p1], y1=poly.ypoints[p1]; - double x2=poly.xpoints[p2], y2=poly.ypoints[p2]; - if (x1>x2) { - double tx1=x1, ty1=y1; - x1=x2; y1=y2; x2=tx1; y2=ty1; - } - feretX = x1*pw; - feretY = y1*ph; - dx=x2-x1; dy=y1-y2; - angle = (180.0/Math.PI)*Math.atan2(dy*ph, dx*pw); - if (angle<0) angle = 180.0 + angle; - //breadth = getFeretBreadth(poly, angle, x1, y1, x2, y2); - if (pw==ph) - min *= pw; - double[] a = new double[5]; - a[0] = diameter; - a[1] = angle; - a[2] = min; - a[3] = feretX; - a[4] = feretY; - return a; - } - - public Polygon getConvexHull() { - return getPolygon(); - } - - double getFeretBreadth(Shape shape, double angle, double x1, double y1, double x2, double y2) { - double cx = x1 + (x2-x1)/2; - double cy = y1 + (y2-y1)/2; - AffineTransform at = new AffineTransform(); - at.rotate(angle*Math.PI/180.0, cx, cy); - Shape s = at.createTransformedShape(shape); - Rectangle2D r = s.getBounds2D(); - return Math.min(r.getWidth(), r.getHeight()); - /* - ShapeRoi roi2 = new ShapeRoi(s); - Roi[] rois = roi2.getRois(); - if (rois!=null && rois.length>0) { - Polygon p = rois[0].getPolygon(); - ImageProcessor ip = imp.getProcessor(); - for (int i=0; i=startX)?startX:startX - width; - y = (yNew>=startY)?startY:startY - height; - if (type==RECTANGLE) { - if ((x+width) > xMax) width = xMax-x; - if ((y+height) > yMax) height = yMax-y; - } - } - updateClipRect(); - imp.draw(clipX, clipY, clipWidth, clipHeight); - oldX = x; - oldY = y; - oldWidth = width; - oldHeight = height; - } - - private void growConstrained(int xNew, int yNew) { - int dx = xNew - startX; - int dy = yNew - startY; - width = height = (int)Math.round(Math.sqrt(dx*dx + dy*dy)); - if (type==RECTANGLE) { - x = (xNew>=startX)?startX:startX - width; - y = (yNew>=startY)?startY:startY - height; - if (x<0) x = 0; - if (y<0) y = 0; - if ((x+width) > xMax) width = xMax-x; - if ((y+height) > yMax) height = yMax-y; - } else { - x = startX + dx/2 - width/2; - y = startY + dy/2 - height/2; - } - updateClipRect(); - imp.draw(clipX, clipY, clipWidth, clipHeight); - oldX = x; - oldY = y; - oldWidth = width; - oldHeight = height; - } - - protected void moveHandle(int sx, int sy) { - double asp; - if (clipboard!=null) return; - int ox = ic.offScreenX(sx); - int oy = ic.offScreenY(sy); - if (ox<0) ox=0; if (oy<0) oy=0; - if (ox>xMax) ox=xMax; if (oy>yMax) oy=yMax; - //IJ.log("moveHandle: "+activeHandle+" "+ox+" "+oy); - int x1=x, y1=y, x2=x1+width, y2=y+height, xc=x+width/2, yc=y+height/2; - if (width > 7 && height > 7) { - asp = (double)width/(double)height; - asp_bk = asp; - } else { - asp = asp_bk; - } - - switch (activeHandle) { - case 0: - x=ox; y=oy; - break; - case 1: - y=oy; - break; - case 2: - x2=ox; y=oy; - break; - case 3: - x2=ox; - break; - case 4: - x2=ox; y2=oy; - break; - case 5: - y2=oy; - break; - case 6: - x=ox; y2=oy; - break; - case 7: - x=ox; - break; - } - if (x=x2) { - width=1; - x=x2=xc; - } - if(y>=y2) { - height=1; - y=y2=yc; - } - - } - - if(constrain) { - if (activeHandle==1 || activeHandle==5) - width=height; - else - height=width; - if(center){ - x=xc-width/2; - y=yc-height/2; - } - if(x>=x2) { - width=1; - x=x2=xc; - } - if(y>=y2) { - height=1; - y=y2=yc; - } - switch(activeHandle){ - case 0: - x=x2-width; - y=y2-height; - break; - case 1: - x=xc-width/2; - y=y2-height; - break; - case 2: - y=y2-height; - break; - case 3: - y=yc-height/2; - break; - case 5: - x=xc-width/2; - break; - case 6: - x=x2-width; - break; - case 7: - y=yc-height/2; - x=x2-width; - break; - } - } - - if(aspect && !constrain) { - if(activeHandle==1 || activeHandle==5) width=(int)Math.rint((double)height*asp); - else height=(int)Math.rint((double)width/asp); - - switch(activeHandle){ - case 0: - x=x2-width; - y=y2-height; - break; - case 1: - x=xc-width/2; - y=y2-height; - break; - case 2: - y=y2-height; - break; - case 3: - y=yc-height/2; - break; - case 5: - x=xc-width/2; - break; - case 6: - x=x2-width; - break; - case 7: - y=yc-height/2; - x=x2-width; - break; - } - if(center){ - x=xc-width/2; - y=yc-height/2; - } - - // Attempt to preserve aspect ratio when roi very small: - if (width<8) { - if(width<1) width = 1; - height=(int)Math.rint((double)width/asp_bk); - } - if (height<8) { - if(height<1) height =1; - width=(int)Math.rint((double)height*asp_bk); - } - - } - - updateClipRect(); - imp.draw(clipX, clipY, clipWidth, clipHeight); - oldX=x; oldY=y; - oldWidth=width; oldHeight=height; - } - - void move(int sx, int sy) { - int xNew = ic.offScreenX(sx); - int yNew = ic.offScreenY(sy); - x += xNew - startX; - y += yNew - startY; - if (clipboard==null && type==RECTANGLE) { - if (x<0) x=0; if (y<0) y=0; - if ((x+width)>xMax) x = xMax-width; - if ((y+height)>yMax) y = yMax-height; - } - startX = xNew; - startY = yNew; - updateClipRect(); - if (lineWidth>1 && isLine()) - imp.draw(); - else - imp.draw(clipX, clipY, clipWidth, clipHeight); - oldX = x; - oldY = y; - oldWidth = width; - oldHeight=height; - } - - /** Nudge ROI one pixel on arrow key press. */ - public void nudge(int key) { - switch(key) { - case KeyEvent.VK_UP: - y--; - if (y<0 && (type!=RECTANGLE||clipboard==null)) - y = 0; - break; - case KeyEvent.VK_DOWN: - y++; - if ((y+height)>=yMax && (type!=RECTANGLE||clipboard==null)) - y = yMax-height; - break; - case KeyEvent.VK_LEFT: - x--; - if (x<0 && (type!=RECTANGLE||clipboard==null)) - x = 0; - break; - case KeyEvent.VK_RIGHT: - x++; - if ((x+width)>=xMax && (type!=RECTANGLE||clipboard==null)) - x = xMax-width; - break; - } - updateClipRect(); - imp.draw(clipX, clipY, clipWidth, clipHeight); - oldX = x; oldY = y; - showStatus(); - } - - /** Nudge lower right corner of rectangular and oval ROIs by - one pixel based on arrow key press. */ - public void nudgeCorner(int key) { - if (type>OVAL || clipboard!=null) - return; - switch(key) { - case KeyEvent.VK_UP: - height--; - if (height<1) height = 1; - break; - case KeyEvent.VK_DOWN: - height++; - if ((y+height) > yMax) height = yMax-y; - break; - case KeyEvent.VK_LEFT: - width--; - if (width<1) width = 1; - break; - case KeyEvent.VK_RIGHT: - width++; - if ((x+width) > xMax) width = xMax-x; - break; - } - updateClipRect(); - imp.draw(clipX, clipY, clipWidth, clipHeight); - oldX = x; oldY = y; - cachedMask = null; - showStatus(); - } - - protected void updateClipRect() { - // Finds the union of current and previous roi - clipX = (x<=oldX)?x:oldX; - clipY = (y<=oldY)?y:oldY; - clipWidth = ((x+width>=oldX+oldWidth)?x+width:oldX+oldWidth) - clipX + 1; - clipHeight = ((y+height>=oldY+oldHeight)?y+height:oldY+oldHeight) - clipY + 1; - int m = 3; - if (type==POINT) m += 4; - if (ic!=null) { - double mag = ic.getMagnification(); - if (mag<1.0) - m = (int)(3/mag); - } - m += getLineWidth(); - clipX-=m; clipY-=m; - clipWidth+=m*2; clipHeight+=m*2; - } - - protected void handleMouseDrag(int sx, int sy, int flags) { - if (ic==null) return; - constrain = (flags&Event.SHIFT_MASK)!=0; - center = (flags&Event.CTRL_MASK)!=0 || (IJ.isMacintosh()&&(flags&Event.META_MASK)!=0); - aspect = (flags&Event.ALT_MASK)!=0; - switch(state) { - case CONSTRUCTING: - grow(sx, sy); - break; - case MOVING: - move(sx, sy); - break; - case MOVING_HANDLE: - moveHandle(sx, sy); - break; - default: - break; - } - } - - int getHandleSize() { - double mag = ic!=null?ic.getMagnification():1.0; - double size = HANDLE_SIZE/mag; - return (int)(size*mag); - } - - public void draw(Graphics g) { - if (ic==null) return; - Color color = outlineColor!=null?outlineColor:ROIColor; - if (fillColor!=null) color = fillColor; - g.setColor(color); - mag = ic.getMagnification(); - int sw = (int)(width*mag); - int sh = (int)(height*mag); - int sx1 = ic.screenX(x); - int sy1 = ic.screenY(y); - int sx2 = sx1+sw/2; - int sy2 = sy1+sh/2; - int sx3 = sx1+sw; - int sy3 = sy1+sh; - Graphics2D g2d = (Graphics2D)g; - Stroke saveStroke = null; - if (stroke!=null) { - saveStroke = g2d.getStroke(); - g2d.setStroke(stroke); - } - if (fillColor!=null) - g.fillRect(sx1, sy1, sw, sh); - else - g.drawRect(sx1, sy1, sw, sh); - if (saveStroke!=null) g2d.setStroke(saveStroke); - if (state!=CONSTRUCTING && clipboard==null && !displayList) { - int size2 = HANDLE_SIZE/2; - drawHandle(g, sx1-size2, sy1-size2); - drawHandle(g, sx2-size2, sy1-size2); - drawHandle(g, sx3-size2, sy1-size2); - drawHandle(g, sx3-size2, sy2-size2); - drawHandle(g, sx3-size2, sy3-size2); - drawHandle(g, sx2-size2, sy3-size2); - drawHandle(g, sx1-size2, sy3-size2); - drawHandle(g, sx1-size2, sy2-size2); - } - drawPreviousRoi(g); - if (state!=NORMAL) showStatus(); - if (updateFullWindow) - {updateFullWindow = false; imp.draw();} - } - - public void drawDisplayList(Graphics g) { - displayList = true; - draw(g); - displayList = false; - } - - void drawPreviousRoi(Graphics g) { - if (previousRoi!=null && previousRoi!=this && previousRoi.modState!=NO_MODS) { - if (type!=POINT && previousRoi.getType()==POINT && previousRoi.modState!=SUBTRACT_FROM_ROI) - return; - previousRoi.setImage(imp); - previousRoi.draw(g); - } - } - - void drawHandle(Graphics g, int x, int y) { - double size = (width*height)*mag; - if (type==LINE) { - size = Math.sqrt(width*width+height*height); - size *= size*mag; - } - if (size>6000.0) { - g.setColor(Color.black); - g.fillRect(x,y,5,5); - g.setColor(handleColor); - g.fillRect(x+1,y+1,3,3); - } else if (size>1500.0) { - g.setColor(Color.black); - g.fillRect(x+1,y+1,4,4); - g.setColor(handleColor); - g.fillRect(x+2,y+2,2,2); - } else { - g.setColor(Color.black); - g.fillRect(x+1,y+1,3,3); - g.setColor(handleColor); - g.fillRect(x+2,y+2,1,1); - } - } - - /** Obsolete, use drawPixels(ImageProcessor) */ - public void drawPixels() { - if (imp!=null) - drawPixels(imp.getProcessor()); - } - - /** Draws the selection outline on the specified ImageProcessor. - @see ij.process.ImageProcessor#setColor - @see ij.process.ImageProcessor#setLineWidth - */ - public void drawPixels(ImageProcessor ip) { - endPaste(); - ip.drawRect(x, y, width, height); - if (Line.getWidth()>1) - updateFullWindow = true; - } - - public boolean contains(int x, int y) { - Rectangle r = new Rectangle(this.x, this.y, width, height); - return r.contains(x, y); - } - - /** Returns a handle number if the specified screen coordinates are - inside or near a handle, otherwise returns -1. */ - public int isHandle(int sx, int sy) { - if (clipboard!=null || ic==null) return -1; - double mag = ic.getMagnification(); - int size = HANDLE_SIZE+3; - int halfSize = size/2; - int sx1 = ic.screenX(x) - halfSize; - int sy1 = ic.screenY(y) - halfSize; - int sx3 = ic.screenX(x+width) - halfSize; - int sy3 = ic.screenY(y+height) - halfSize; - int sx2 = sx1 + (sx3 - sx1)/2; - int sy2 = sy1 + (sy3 - sy1)/2; - if (sx>=sx1&&sx<=sx1+size&&sy>=sy1&&sy<=sy1+size) return 0; - if (sx>=sx2&&sx<=sx2+size&&sy>=sy1&&sy<=sy1+size) return 1; - if (sx>=sx3&&sx<=sx3+size&&sy>=sy1&&sy<=sy1+size) return 2; - if (sx>=sx3&&sx<=sx3+size&&sy>=sy2&&sy<=sy2+size) return 3; - if (sx>=sx3&&sx<=sx3+size&&sy>=sy3&&sy<=sy3+size) return 4; - if (sx>=sx2&&sx<=sx2+size&&sy>=sy3&&sy<=sy3+size) return 5; - if (sx>=sx1&&sx<=sx1+size&&sy>=sy3&&sy<=sy3+size) return 6; - if (sx>=sx1&&sx<=sx1+size&&sy>=sy2&&sy<=sy2+size) return 7; - return -1; - } - - protected void mouseDownInHandle(int handle, int sx, int sy) { - state = MOVING_HANDLE; - activeHandle = handle; - } - - protected void handleMouseDown(int sx, int sy) { - if (state==NORMAL && ic!=null) { - state = MOVING; - startX = ic.offScreenX(sx); - startY = ic.offScreenY(sy); - showStatus(); - } - } - - protected void handleMouseUp(int screenX, int screenY) { - state = NORMAL; - if (imp==null) return; - imp.draw(clipX-5, clipY-5, clipWidth+10, clipHeight+10); - if (Recorder.record) { - String method; - if (type==LINE) { - Line line = (Line)imp.getRoi(); - Recorder.record("makeLine", line.x1, line.y1, line.x2, line.y2); - } else if (type==OVAL) - Recorder.record("makeOval", x, y, width, height); - else if (!(this instanceof TextRoi)) - Recorder.record("makeRectangle", x, y, width, height); - } - if (Toolbar.getToolId()==Toolbar.OVAL&&Toolbar.getBrushSize()>0) { - int flags = ic!=null?ic.getModifiers():16; - if ((flags&16)==0) // erase ROI Brush - {imp.draw(); return;} - } - modifyRoi(); - } - - void modifyRoi() { - if (previousRoi==null || previousRoi.modState==NO_MODS || imp==null) - return; - //IJ.log("modifyRoi: "+ type+" "+modState+" "+previousRoi.type+" "+previousRoi.modState); - if (type==POINT || previousRoi.getType()==POINT) { - if (type==POINT && previousRoi.getType()==POINT) - addPoint(); - else if (isArea() && previousRoi.getType()==POINT && previousRoi.modState==SUBTRACT_FROM_ROI) - subtractPoints(); - return; - } - Roi previous = (Roi)previousRoi.clone(); - previous.modState = NO_MODS; - ShapeRoi s1 = null; - ShapeRoi s2 = null; - if (previousRoi instanceof ShapeRoi) - s1 = (ShapeRoi)previousRoi; - else - s1 = new ShapeRoi(previousRoi); - if (this instanceof ShapeRoi) - s2 = (ShapeRoi)this; - else - s2 = new ShapeRoi(this); - if (previousRoi.modState==ADD_TO_ROI) - s1.or(s2); - else - s1.not(s2); - previousRoi.modState = NO_MODS; - Roi[] rois = s1.getRois(); - if (rois.length==0) return; - int type2 = rois[0].getType(); - //IJ.log(rois.length+" "+type2); - Roi roi2 = null; - if (rois.length==1 && (type2==POLYGON||type2==FREEROI)) - roi2 = rois[0]; - else - roi2 = s1; - if (roi2!=null) - roi2.copyAttributes(previousRoi); - imp.setRoi(roi2); - previousRoi = previous; - } - - void addPoint() { - if (!(type==POINT && previousRoi.getType()==POINT)) { - modState = NO_MODS; - imp.draw(); - return; - } - previousRoi.modState = NO_MODS; - PointRoi p1 = (PointRoi)previousRoi; - Rectangle r = getBounds(); - imp.setRoi(p1.addPoint(r.x, r.y)); - } - - void subtractPoints() { - previousRoi.modState = NO_MODS; - PointRoi p1 = (PointRoi)previousRoi; - PointRoi p2 = p1.subtractPoints(this); - if (p2!=null) - imp.setRoi(p1.subtractPoints(this)); - else - imp.killRoi(); - } - - /** If 'add' is true, adds this selection to the previous one. If 'subtract' is true, subtracts - it from the previous selection. Called by the IJ.doWand() method, and the makeRectangle(), - makeOval(), makePolygon() and makeSelection() macro functions. */ - public void update(boolean add, boolean subtract) { - if (previousRoi==null) return; - if (add) { - previousRoi.modState = ADD_TO_ROI; - modifyRoi(); - } else if (subtract) { - previousRoi.modState = SUBTRACT_FROM_ROI; - modifyRoi(); - } else - previousRoi.modState = NO_MODS; - } - - protected void showStatus() { - String value; - if (state!=CONSTRUCTING && (type==RECTANGLE||type==POINT) && width<=25 && height<=25) { - ImageProcessor ip = imp.getProcessor(); - double v = ip.getPixelValue(x,y); - int digits = (imp.getType()==ImagePlus.GRAY8||imp.getType()==ImagePlus.GRAY16)?0:2; - value = ", value="+IJ.d2s(v,digits); - } else - value = ""; - Calibration cal = imp.getCalibration(); - String size; - if (cal.scaled() && !IJ.altKeyDown()) - size = ", w="+IJ.d2s(width*cal.pixelWidth)+", h="+IJ.d2s(height*cal.pixelHeight); - else - size = ", w="+width+", h="+height; - IJ.showStatus(imp.getLocationAsString(x,y)+size+value); - } - - /** Always returns null for rectangular Roi's */ - public ImageProcessor getMask() { - return null; - } - - public void startPaste(ImagePlus clipboard) { - IJ.showStatus("Pasting..."); - this.clipboard = clipboard; - imp.getProcessor().snapshot(); - updateClipRect(); - imp.draw(clipX, clipY, clipWidth, clipHeight); - } - - void updatePaste() { - if (clipboard!=null) { - imp.getMask(); - ImageProcessor ip = imp.getProcessor(); - ip.reset(); - ip.copyBits(clipboard.getProcessor(), x, y, pasteMode); - if (type!=RECTANGLE) - ip.reset(ip.getMask()); - ic.setImageUpdated(); - } - } - - public void endPaste() { - if (clipboard!=null) { - imp.getMask(); - ImageProcessor ip = imp.getProcessor(); - if (pasteMode!=Blitter.COPY) ip.reset(); - ip.copyBits(clipboard.getProcessor(), x, y, pasteMode); - if (type!=RECTANGLE) - ip.reset(ip.getMask()); - ip.snapshot(); - clipboard = null; - imp.updateAndDraw(); - Undo.setup(Undo.FILTER, imp); - } - } - - public void abortPaste() { - clipboard = null; - imp.getProcessor().reset(); - imp.updateAndDraw(); - } - - /** Returns the angle in degrees between the specified line and a horizontal line. */ - public double getAngle(int x1, int y1, int x2, int y2) { - double dx = x2-x1; - double dy = y1-y2; - if (imp!=null && !IJ.altKeyDown()) { - Calibration cal = imp.getCalibration(); - dx *= cal.pixelWidth; - dy *= cal.pixelHeight; - } - return (180.0/Math.PI)*Math.atan2(dy, dx); - } - - /** Sets the default (global) color used for ROI outlines. - * @see #getColor() - * @see #setLineColor(Color) - */ - public static void setColor(Color c) { - ROIColor = c; - } - - /** Returns the default (global) color used for drawing ROI outlines. - * @see #setColor(Color) - * @see #getLineColor() - */ - public static Color getColor() { - return ROIColor; - } - - /** Sets the color used by this ROI to draw its outline. This color, if not null, - * overrides the global color set by the static setColor() method. - * @see #getLineColor() - * @see #setLineWidth(int) - * @see ij.gui.ImageCanvas#setDisplayList(Roi,Color) - */ - public void setLineColor(Color c) { - outlineColor = c; - } - - /** Returns the the color used to draw the ROI outline or null if the default color is being used. - * @see #setLineColor(Color) - */ - public Color getLineColor() { - return outlineColor; - } - - /** Sets the color used to fill ROIs when they are in a display list. - * @see ij.gui.ImageCanvas#setDisplayList(Vector) - * @see ij.gui.ImageCanvas#setDisplayList(Roi,Color) - */ - public void setFillColor(Color color) { - fillColor = color; - } - - /** Returns the the color used to fill this ROI when it is in a display, or null. - * @see #getLineColor() - */ - public Color getFillColor() { - return fillColor; - } - - public static void setDefaultFillColor(Color color) { - defaultFillColor = color; - } - - public static Color getDefaultFillColor() { - return defaultFillColor; - } - - /** Copy the attributes (outline color, fill color, outline width) - of 'roi2' to the this selection. */ - public void copyAttributes(Roi roi2) { - this.outlineColor = roi2.outlineColor; - this.fillColor = roi2.fillColor; - this.stroke = roi2.stroke; - } - - /** Obsolete; replaced by setLineColor(). */ - public void setInstanceColor(Color c) { - outlineColor = c; - } - - /** Set 'nonScalable' true to have TextRois in a display - list drawn at a fixed location and size. */ - public void setNonScalable(boolean nonScalable) { - this.nonScalable = nonScalable; - } - - /** Sets the width of the lines used to draw this ROI when - * it is part of a display list or ROI Manager "Show All" list. - * @see #setLineColor(Color) - * @see ij.gui.ImageCanvas#setDisplayList(Roi,Color) - */ - public void setLineWidth(int width) { - this.stroke = new BasicStroke(width); - if (width>1) fillColor = null; - } - - /** Returns the lineWidth. */ - public int getLineWidth() { - return stroke!=null?(int)stroke.getLineWidth():1; - } - - /** Sets the Stroke used to draw this ROI. */ - public void setStroke(BasicStroke stroke) { - this.stroke = stroke; - } - - /** Returns the Stroke used to draw this ROI, or null if no Stroke is used. */ - public BasicStroke getStroke() { - return stroke; - } - - /** Returns the name of this ROI, or null. */ - public String getName() { - return name; - } - - /** Sets the name of this ROI. */ - public void setName(String name) { - this.name = name; - } - - /** Sets the Paste transfer mode. - @see ij.process.Blitter - */ - public static void setPasteMode(int transferMode) { - if (transferMode==pasteMode) return; - pasteMode = transferMode; - ImagePlus imp = WindowManager.getCurrentImage(); - if (imp!=null) - imp.updateAndDraw(); - } - - /** Returns the current paste transfer mode, or NOT_PASTING (-1) - if no paste operation is in progress. - @see ij.process.Blitter - */ - public int getPasteMode() { - if (clipboard==null) - return NOT_PASTING; - else - return pasteMode; - } - - /** Returns the current paste transfer mode. */ - public static int getCurrentPasteMode() { - return pasteMode; - } - - /** Returns true if this is an area selection. */ - public boolean isArea() { - return (type>=RECTANGLE && type<=TRACED_ROI) || type==COMPOSITE; - } - - /** Returns true if this is a line selection. */ - public boolean isLine() { - return type>=LINE && type<=FREELINE; - } - - /** Convenience method that converts Roi type to a human-readable form. */ - public String getTypeAsString() { - String s=""; - switch(type) { - case POLYGON: s="Polygon"; break; - case FREEROI: s="Freehand"; break; - case TRACED_ROI: s="Traced"; break; - case POLYLINE: s="Polyline"; break; - case FREELINE: s="Freeline"; break; - case ANGLE: s="Angle"; break; - case LINE: s="Straight Line"; break; - case OVAL: s="Oval"; break; - case COMPOSITE: s = "Composite"; break; - case POINT: s = "Point"; break; - default: s="Rectangle"; break; - } - return s; - } - - /** Returns true if this ROI is currently displayed on an image. */ - public boolean isVisible() { - return ic!=null; - } - - - /** Checks whether two rectangles are equal. */ - public boolean equals(Object obj) { - if (obj instanceof Roi) { - Roi roi2 = (Roi)obj; - if (type!=roi2.getType()) return false; - if (!getBounds().equals(roi2.getBounds())) return false; - if (getLength()!=roi2.getLength()) return false; - return true; - } else - return false; - } - - public String toString() { - return ("Roi["+getTypeAsString()+", x="+x+", y="+y+", width="+width+", height="+height+"]"); - } - -} +package ij.gui; + +import java.awt.*; +import java.awt.image.*; +import java.awt.event.KeyEvent; +import java.awt.geom.*; +import ij.*; +import ij.process.*; +import ij.measure.*; +import ij.plugin.frame.Recorder; +import ij.plugin.filter.Analyzer; +import ij.macro.Interpreter; + +/** A rectangular region of interest and superclass for the other ROI classes. */ +public class Roi extends Object implements Cloneable, java.io.Serializable { + + public static final int CONSTRUCTING=0, MOVING=1, RESIZING=2, NORMAL=3, MOVING_HANDLE=4; // States + public static final int RECTANGLE=0, OVAL=1, POLYGON=2, FREEROI=3, TRACED_ROI=4, LINE=5, + POLYLINE=6, FREELINE=7, ANGLE=8, COMPOSITE=9, POINT=10; // Types + public static final int HANDLE_SIZE = 5; + public static final int NOT_PASTING = -1; + + static final int NO_MODS=0, ADD_TO_ROI=1, SUBTRACT_FROM_ROI=2; // modification states + + int startX, startY, x, y, width, height; + int activeHandle; + int state; + int modState = NO_MODS; + + public static Roi previousRoi; + protected static Color ROIColor = Prefs.getColor(Prefs.ROICOLOR,Color.yellow); + protected static int pasteMode = Blitter.COPY; + protected static int lineWidth = 1; + protected static Color defaultFillColor; + + protected int type; + protected int xMax, yMax; + protected ImagePlus imp; + protected ImageCanvas ic; + protected int oldX, oldY, oldWidth, oldHeight; + protected int clipX, clipY, clipWidth, clipHeight; + protected ImagePlus clipboard; + protected boolean constrain; // to be square + protected boolean center; + protected boolean aspect; + protected boolean updateFullWindow; + protected double mag = 1.0; + protected double asp_bk; //saves aspect ratio if resizing takes roi very small + protected String name; + protected ImageProcessor cachedMask; + protected Color handleColor = Color.white; + protected Color outlineColor; + protected Color instanceColor; //obsolete; replaced by outlineColor + protected Color fillColor; + protected BasicStroke stroke; + protected boolean nonScalable; + protected boolean displayList; + + + /** Creates a new rectangular Roi. */ + public Roi(int x, int y, int width, int height) { + setImage(null); + if (width<1) width = 1; + if (height<1) height = 1; + if (width>xMax) width = xMax; + if (height>yMax) height = yMax; + //setLocation(x, y); + this.x = x; + this.y = y; + startX = x; startY = y; + oldX = x; oldY = y; oldWidth=0; oldHeight=0; + this.width = width; + this.height = height; + oldWidth=width; + oldHeight=height; + clipX = x; + clipY = y; + clipWidth = width; + clipHeight = height; + state = NORMAL; + type = RECTANGLE; + if (ic!=null) { + Graphics g = ic.getGraphics(); + draw(g); + g.dispose(); + } + fillColor = defaultFillColor; + } + + /** Creates a new rectangular Roi. */ + public Roi(Rectangle r) { + this(r.x, r.y, r.width, r.height); + } + + /** Starts the process of creating a user-defined rectangular Roi, + where sx and sy are the starting screen coordinates. */ + public Roi(int sx, int sy, ImagePlus imp) { + setImage(imp); + int ox=sx, oy=sy; + if (ic!=null) { + ox = ic.offScreenX(sx); + oy = ic.offScreenY(sy); + } + setLocation(ox, oy); + width = 0; + height = 0; + state = CONSTRUCTING; + type = RECTANGLE; + fillColor = defaultFillColor; + } + + /** Obsolete */ + public Roi(int x, int y, int width, int height, ImagePlus imp) { + this(x, y, width, height); + setImage(imp); + } + + /** Set the location of the ROI in image coordinates. */ + public void setLocation(int x, int y) { + this.x = x; + this.y = y; + startX = x; startY = y; + oldX = x; oldY = y; oldWidth=0; oldHeight=0; + } + + public void setImage(ImagePlus imp) { + this.imp = imp; + cachedMask = null; + if (imp==null) { + ic = null; + clipboard = null; + xMax = 99999; + yMax = 99999; + } else { + ic = imp.getCanvas(); + xMax = imp.getWidth(); + yMax = imp.getHeight(); + } + } + + ImagePlus getImage() { + return imp; + } + + public int getType() { + return type; + } + + public int getState() { + return state; + } + + /** Returns the perimeter length. */ + public double getLength() { + double pw=1.0, ph=1.0; + if (imp!=null) { + Calibration cal = imp.getCalibration(); + pw = cal.pixelWidth; + ph = cal.pixelHeight; + } + return 2.0*width*pw+2.0*height*ph; + } + + /** Returns Feret's diameter, the greatest distance between + any two points along the ROI boundary. */ + public double getFeretsDiameter() { + double[] a = getFeretValues(); + return a!=null?a[0]:0.0; + } + + /** Caculates "Feret" (maximum caliper width), "FeretAngle" + and "MinFeret" (minimum caliper width), "FeretX" and "FeretY". */ + public double[] getFeretValues() { + double min=Double.MAX_VALUE, diameter=0.0, angle=0.0, feretX=0.0, feretY=0.0; + int p1=0, p2=0; + double pw=1.0, ph=1.0; + if (imp!=null) { + Calibration cal = imp.getCalibration(); + pw = cal.pixelWidth; + ph = cal.pixelHeight; + } + Polygon poly = getConvexHull(); + if (poly==null) { + poly = getPolygon(); + if (poly==null) return null; + } + double w2=pw*pw, h2=ph*ph; + double dx, dy, d; + for (int i=0; idiameter) {diameter=d; p1=i; p2=j;} + } + } + Rectangle r = getBounds(); + double cx = r.x + r.width/2.0; + double cy = r.y + r.height/2.0; + int n = poly.npoints; + double[] x = new double[n]; + double[] y = new double[n]; + for (int i=0; ixmax) xmax = xr; + if (yrymax) ymax = yr; + } + double width = xmax - xmin; + double height = ymax - ymin; + double min2 = Math.min(width, height); + min = Math.min(min, min2); + } + double x1=poly.xpoints[p1], y1=poly.ypoints[p1]; + double x2=poly.xpoints[p2], y2=poly.ypoints[p2]; + if (x1>x2) { + double tx1=x1, ty1=y1; + x1=x2; y1=y2; x2=tx1; y2=ty1; + } + feretX = x1*pw; + feretY = y1*ph; + dx=x2-x1; dy=y1-y2; + angle = (180.0/Math.PI)*Math.atan2(dy*ph, dx*pw); + if (angle<0) angle = 180.0 + angle; + //breadth = getFeretBreadth(poly, angle, x1, y1, x2, y2); + if (pw==ph) + min *= pw; + double[] a = new double[5]; + a[0] = diameter; + a[1] = angle; + a[2] = min; + a[3] = feretX; + a[4] = feretY; + return a; + } + + public Polygon getConvexHull() { + return getPolygon(); + } + + double getFeretBreadth(Shape shape, double angle, double x1, double y1, double x2, double y2) { + double cx = x1 + (x2-x1)/2; + double cy = y1 + (y2-y1)/2; + AffineTransform at = new AffineTransform(); + at.rotate(angle*Math.PI/180.0, cx, cy); + Shape s = at.createTransformedShape(shape); + Rectangle2D r = s.getBounds2D(); + return Math.min(r.getWidth(), r.getHeight()); + /* + ShapeRoi roi2 = new ShapeRoi(s); + Roi[] rois = roi2.getRois(); + if (rois!=null && rois.length>0) { + Polygon p = rois[0].getPolygon(); + ImageProcessor ip = imp.getProcessor(); + for (int i=0; i=startX)?startX:startX - width; + y = (yNew>=startY)?startY:startY - height; + if (type==RECTANGLE) { + if ((x+width) > xMax) width = xMax-x; + if ((y+height) > yMax) height = yMax-y; + } + } + updateClipRect(); + imp.draw(clipX, clipY, clipWidth, clipHeight); + oldX = x; + oldY = y; + oldWidth = width; + oldHeight = height; + } + + private void growConstrained(int xNew, int yNew) { + int dx = xNew - startX; + int dy = yNew - startY; + width = height = (int)Math.round(Math.sqrt(dx*dx + dy*dy)); + if (type==RECTANGLE) { + x = (xNew>=startX)?startX:startX - width; + y = (yNew>=startY)?startY:startY - height; + if (x<0) x = 0; + if (y<0) y = 0; + if ((x+width) > xMax) width = xMax-x; + if ((y+height) > yMax) height = yMax-y; + } else { + x = startX + dx/2 - width/2; + y = startY + dy/2 - height/2; + } + updateClipRect(); + imp.draw(clipX, clipY, clipWidth, clipHeight); + oldX = x; + oldY = y; + oldWidth = width; + oldHeight = height; + } + + protected void moveHandle(int sx, int sy) { + double asp; + if (clipboard!=null) return; + int ox = ic.offScreenX(sx); + int oy = ic.offScreenY(sy); + if (ox<0) ox=0; if (oy<0) oy=0; + if (ox>xMax) ox=xMax; if (oy>yMax) oy=yMax; + //IJ.log("moveHandle: "+activeHandle+" "+ox+" "+oy); + int x1=x, y1=y, x2=x1+width, y2=y+height, xc=x+width/2, yc=y+height/2; + if (width > 7 && height > 7) { + asp = (double)width/(double)height; + asp_bk = asp; + } else { + asp = asp_bk; + } + + switch (activeHandle) { + case 0: + x=ox; y=oy; + break; + case 1: + y=oy; + break; + case 2: + x2=ox; y=oy; + break; + case 3: + x2=ox; + break; + case 4: + x2=ox; y2=oy; + break; + case 5: + y2=oy; + break; + case 6: + x=ox; y2=oy; + break; + case 7: + x=ox; + break; + } + if (x=x2) { + width=1; + x=x2=xc; + } + if(y>=y2) { + height=1; + y=y2=yc; + } + + } + + if(constrain) { + if (activeHandle==1 || activeHandle==5) + width=height; + else + height=width; + if(center){ + x=xc-width/2; + y=yc-height/2; + } + if(x>=x2) { + width=1; + x=x2=xc; + } + if(y>=y2) { + height=1; + y=y2=yc; + } + switch(activeHandle){ + case 0: + x=x2-width; + y=y2-height; + break; + case 1: + x=xc-width/2; + y=y2-height; + break; + case 2: + y=y2-height; + break; + case 3: + y=yc-height/2; + break; + case 5: + x=xc-width/2; + break; + case 6: + x=x2-width; + break; + case 7: + y=yc-height/2; + x=x2-width; + break; + } + } + + if(aspect && !constrain) { + if(activeHandle==1 || activeHandle==5) width=(int)Math.rint((double)height*asp); + else height=(int)Math.rint((double)width/asp); + + switch(activeHandle){ + case 0: + x=x2-width; + y=y2-height; + break; + case 1: + x=xc-width/2; + y=y2-height; + break; + case 2: + y=y2-height; + break; + case 3: + y=yc-height/2; + break; + case 5: + x=xc-width/2; + break; + case 6: + x=x2-width; + break; + case 7: + y=yc-height/2; + x=x2-width; + break; + } + if(center){ + x=xc-width/2; + y=yc-height/2; + } + + // Attempt to preserve aspect ratio when roi very small: + if (width<8) { + if(width<1) width = 1; + height=(int)Math.rint((double)width/asp_bk); + } + if (height<8) { + if(height<1) height =1; + width=(int)Math.rint((double)height*asp_bk); + } + + } + + updateClipRect(); + imp.draw(clipX, clipY, clipWidth, clipHeight); + oldX=x; oldY=y; + oldWidth=width; oldHeight=height; + } + + void move(int sx, int sy) { + int xNew = ic.offScreenX(sx); + int yNew = ic.offScreenY(sy); + x += xNew - startX; + y += yNew - startY; + if (clipboard==null && type==RECTANGLE) { + if (x<0) x=0; if (y<0) y=0; + if ((x+width)>xMax) x = xMax-width; + if ((y+height)>yMax) y = yMax-height; + } + startX = xNew; + startY = yNew; + updateClipRect(); + if (lineWidth>1 && isLine()) + imp.draw(); + else + imp.draw(clipX, clipY, clipWidth, clipHeight); + oldX = x; + oldY = y; + oldWidth = width; + oldHeight=height; + } + + /** Nudge ROI one pixel on arrow key press. */ + public void nudge(int key) { + switch(key) { + case KeyEvent.VK_UP: + y--; + if (y<0 && (type!=RECTANGLE||clipboard==null)) + y = 0; + break; + case KeyEvent.VK_DOWN: + y++; + if ((y+height)>=yMax && (type!=RECTANGLE||clipboard==null)) + y = yMax-height; + break; + case KeyEvent.VK_LEFT: + x--; + if (x<0 && (type!=RECTANGLE||clipboard==null)) + x = 0; + break; + case KeyEvent.VK_RIGHT: + x++; + if ((x+width)>=xMax && (type!=RECTANGLE||clipboard==null)) + x = xMax-width; + break; + } + updateClipRect(); + imp.draw(clipX, clipY, clipWidth, clipHeight); + oldX = x; oldY = y; + showStatus(); + } + + /** Nudge lower right corner of rectangular and oval ROIs by + one pixel based on arrow key press. */ + public void nudgeCorner(int key) { + if (type>OVAL || clipboard!=null) + return; + switch(key) { + case KeyEvent.VK_UP: + height--; + if (height<1) height = 1; + break; + case KeyEvent.VK_DOWN: + height++; + if ((y+height) > yMax) height = yMax-y; + break; + case KeyEvent.VK_LEFT: + width--; + if (width<1) width = 1; + break; + case KeyEvent.VK_RIGHT: + width++; + if ((x+width) > xMax) width = xMax-x; + break; + } + updateClipRect(); + imp.draw(clipX, clipY, clipWidth, clipHeight); + oldX = x; oldY = y; + cachedMask = null; + showStatus(); + } + + protected void updateClipRect() { + // Finds the union of current and previous roi + clipX = (x<=oldX)?x:oldX; + clipY = (y<=oldY)?y:oldY; + clipWidth = ((x+width>=oldX+oldWidth)?x+width:oldX+oldWidth) - clipX + 1; + clipHeight = ((y+height>=oldY+oldHeight)?y+height:oldY+oldHeight) - clipY + 1; + int m = 3; + if (type==POINT) m += 4; + if (ic!=null) { + double mag = ic.getMagnification(); + if (mag<1.0) + m = (int)(3/mag); + } + m += getLineWidth(); + clipX-=m; clipY-=m; + clipWidth+=m*2; clipHeight+=m*2; + } + + protected void handleMouseDrag(int sx, int sy, int flags) { + if (ic==null) return; + constrain = (flags&Event.SHIFT_MASK)!=0; + center = (flags&Event.CTRL_MASK)!=0 || (IJ.isMacintosh()&&(flags&Event.META_MASK)!=0); + aspect = (flags&Event.ALT_MASK)!=0; + switch(state) { + case CONSTRUCTING: + grow(sx, sy); + break; + case MOVING: + move(sx, sy); + break; + case MOVING_HANDLE: + moveHandle(sx, sy); + break; + default: + break; + } + } + + int getHandleSize() { + double mag = ic!=null?ic.getMagnification():1.0; + double size = HANDLE_SIZE/mag; + return (int)(size*mag); + } + + public void draw(Graphics g) { + if (ic==null) return; + Color color = outlineColor!=null?outlineColor:ROIColor; + if (fillColor!=null) color = fillColor; + g.setColor(color); + mag = ic.getMagnification(); + int sw = (int)(width*mag); + int sh = (int)(height*mag); + int sx1 = ic.screenX(x); + int sy1 = ic.screenY(y); + int sx2 = sx1+sw/2; + int sy2 = sy1+sh/2; + int sx3 = sx1+sw; + int sy3 = sy1+sh; + Graphics2D g2d = (Graphics2D)g; + Stroke saveStroke = null; + if (stroke!=null) { + saveStroke = g2d.getStroke(); + g2d.setStroke(stroke); + } + if (fillColor!=null) + g.fillRect(sx1, sy1, sw, sh); + else + g.drawRect(sx1, sy1, sw, sh); + if (saveStroke!=null) g2d.setStroke(saveStroke); + if (state!=CONSTRUCTING && clipboard==null && !displayList) { + int size2 = HANDLE_SIZE/2; + drawHandle(g, sx1-size2, sy1-size2); + drawHandle(g, sx2-size2, sy1-size2); + drawHandle(g, sx3-size2, sy1-size2); + drawHandle(g, sx3-size2, sy2-size2); + drawHandle(g, sx3-size2, sy3-size2); + drawHandle(g, sx2-size2, sy3-size2); + drawHandle(g, sx1-size2, sy3-size2); + drawHandle(g, sx1-size2, sy2-size2); + } + drawPreviousRoi(g); + if (state!=NORMAL) showStatus(); + if (updateFullWindow) + {updateFullWindow = false; imp.draw();} + } + + public void drawDisplayList(Graphics g) { + displayList = true; + draw(g); + displayList = false; + } + + void drawPreviousRoi(Graphics g) { + if (previousRoi!=null && previousRoi!=this && previousRoi.modState!=NO_MODS) { + if (type!=POINT && previousRoi.getType()==POINT && previousRoi.modState!=SUBTRACT_FROM_ROI) + return; + previousRoi.setImage(imp); + previousRoi.draw(g); + } + } + + void drawHandle(Graphics g, int x, int y) { + double size = (width*height)*mag; + if (type==LINE) { + size = Math.sqrt(width*width+height*height); + size *= size*mag; + } + if (size>6000.0) { + g.setColor(Color.black); + g.fillRect(x,y,5,5); + g.setColor(handleColor); + g.fillRect(x+1,y+1,3,3); + } else if (size>1500.0) { + g.setColor(Color.black); + g.fillRect(x+1,y+1,4,4); + g.setColor(handleColor); + g.fillRect(x+2,y+2,2,2); + } else { + g.setColor(Color.black); + g.fillRect(x+1,y+1,3,3); + g.setColor(handleColor); + g.fillRect(x+2,y+2,1,1); + } + } + + /** Obsolete, use drawPixels(ImageProcessor) */ + public void drawPixels() { + if (imp!=null) + drawPixels(imp.getProcessor()); + } + + /** Draws the selection outline on the specified ImageProcessor. + @see ij.process.ImageProcessor#setColor + @see ij.process.ImageProcessor#setLineWidth + */ + public void drawPixels(ImageProcessor ip) { + endPaste(); + ip.drawRect(x, y, width, height); + if (Line.getWidth()>1) + updateFullWindow = true; + } + + public boolean contains(int x, int y) { + Rectangle r = new Rectangle(this.x, this.y, width, height); + return r.contains(x, y); + } + + /** Returns a handle number if the specified screen coordinates are + inside or near a handle, otherwise returns -1. */ + public int isHandle(int sx, int sy) { + if (clipboard!=null || ic==null) return -1; + double mag = ic.getMagnification(); + int size = HANDLE_SIZE+3; + int halfSize = size/2; + int sx1 = ic.screenX(x) - halfSize; + int sy1 = ic.screenY(y) - halfSize; + int sx3 = ic.screenX(x+width) - halfSize; + int sy3 = ic.screenY(y+height) - halfSize; + int sx2 = sx1 + (sx3 - sx1)/2; + int sy2 = sy1 + (sy3 - sy1)/2; + if (sx>=sx1&&sx<=sx1+size&&sy>=sy1&&sy<=sy1+size) return 0; + if (sx>=sx2&&sx<=sx2+size&&sy>=sy1&&sy<=sy1+size) return 1; + if (sx>=sx3&&sx<=sx3+size&&sy>=sy1&&sy<=sy1+size) return 2; + if (sx>=sx3&&sx<=sx3+size&&sy>=sy2&&sy<=sy2+size) return 3; + if (sx>=sx3&&sx<=sx3+size&&sy>=sy3&&sy<=sy3+size) return 4; + if (sx>=sx2&&sx<=sx2+size&&sy>=sy3&&sy<=sy3+size) return 5; + if (sx>=sx1&&sx<=sx1+size&&sy>=sy3&&sy<=sy3+size) return 6; + if (sx>=sx1&&sx<=sx1+size&&sy>=sy2&&sy<=sy2+size) return 7; + return -1; + } + + protected void mouseDownInHandle(int handle, int sx, int sy) { + state = MOVING_HANDLE; + activeHandle = handle; + } + + protected void handleMouseDown(int sx, int sy) { + if (state==NORMAL && ic!=null) { + state = MOVING; + startX = ic.offScreenX(sx); + startY = ic.offScreenY(sy); + showStatus(); + } + } + + protected void handleMouseUp(int screenX, int screenY) { + state = NORMAL; + if (imp==null) return; + imp.draw(clipX-5, clipY-5, clipWidth+10, clipHeight+10); + if (Recorder.record) { + String method; + if (type==LINE) { + Line line = (Line)imp.getRoi(); + Recorder.record("makeLine", line.x1, line.y1, line.x2, line.y2); + } else if (type==OVAL) + Recorder.record("makeOval", x, y, width, height); + else if (!(this instanceof TextRoi)) + Recorder.record("makeRectangle", x, y, width, height); + } + if (Toolbar.getToolId()==Toolbar.OVAL&&Toolbar.getBrushSize()>0) { + int flags = ic!=null?ic.getModifiers():16; + if ((flags&16)==0) // erase ROI Brush + {imp.draw(); return;} + } + modifyRoi(); + } + + void modifyRoi() { + if (previousRoi==null || previousRoi.modState==NO_MODS || imp==null) + return; + //IJ.log("modifyRoi: "+ type+" "+modState+" "+previousRoi.type+" "+previousRoi.modState); + if (type==POINT || previousRoi.getType()==POINT) { + if (type==POINT && previousRoi.getType()==POINT) + addPoint(); + else if (isArea() && previousRoi.getType()==POINT && previousRoi.modState==SUBTRACT_FROM_ROI) + subtractPoints(); + return; + } + Roi previous = (Roi)previousRoi.clone(); + previous.modState = NO_MODS; + ShapeRoi s1 = null; + ShapeRoi s2 = null; + if (previousRoi instanceof ShapeRoi) + s1 = (ShapeRoi)previousRoi; + else + s1 = new ShapeRoi(previousRoi); + if (this instanceof ShapeRoi) + s2 = (ShapeRoi)this; + else + s2 = new ShapeRoi(this); + if (previousRoi.modState==ADD_TO_ROI) + s1.or(s2); + else + s1.not(s2); + previousRoi.modState = NO_MODS; + Roi[] rois = s1.getRois(); + if (rois.length==0) return; + int type2 = rois[0].getType(); + //IJ.log(rois.length+" "+type2); + Roi roi2 = null; + if (rois.length==1 && (type2==POLYGON||type2==FREEROI)) + roi2 = rois[0]; + else + roi2 = s1; + if (roi2!=null) + roi2.copyAttributes(previousRoi); + imp.setRoi(roi2); + previousRoi = previous; + } + + void addPoint() { + if (!(type==POINT && previousRoi.getType()==POINT)) { + modState = NO_MODS; + imp.draw(); + return; + } + previousRoi.modState = NO_MODS; + PointRoi p1 = (PointRoi)previousRoi; + Rectangle r = getBounds(); + imp.setRoi(p1.addPoint(r.x, r.y)); + } + + void subtractPoints() { + previousRoi.modState = NO_MODS; + PointRoi p1 = (PointRoi)previousRoi; + PointRoi p2 = p1.subtractPoints(this); + if (p2!=null) + imp.setRoi(p1.subtractPoints(this)); + else + imp.killRoi(); + } + + /** If 'add' is true, adds this selection to the previous one. If 'subtract' is true, subtracts + it from the previous selection. Called by the IJ.doWand() method, and the makeRectangle(), + makeOval(), makePolygon() and makeSelection() macro functions. */ + public void update(boolean add, boolean subtract) { + if (previousRoi==null) return; + if (add) { + previousRoi.modState = ADD_TO_ROI; + modifyRoi(); + } else if (subtract) { + previousRoi.modState = SUBTRACT_FROM_ROI; + modifyRoi(); + } else + previousRoi.modState = NO_MODS; + } + + protected void showStatus() { + String value; + if (state!=CONSTRUCTING && (type==RECTANGLE||type==POINT) && width<=25 && height<=25) { + ImageProcessor ip = imp.getProcessor(); + double v = ip.getPixelValue(x,y); + int digits = (imp.getType()==ImagePlus.GRAY8||imp.getType()==ImagePlus.GRAY16)?0:2; + value = ", value="+IJ.d2s(v,digits); + } else + value = ""; + Calibration cal = imp.getCalibration(); + String size; + if (cal.scaled() && !IJ.altKeyDown()) + size = ", w="+IJ.d2s(width*cal.pixelWidth)+", h="+IJ.d2s(height*cal.pixelHeight); + else + size = ", w="+width+", h="+height; + IJ.showStatus(imp.getLocationAsString(x,y)+size+value); + } + + /** Always returns null for rectangular Roi's */ + public ImageProcessor getMask() { + return null; + } + + public void startPaste(ImagePlus clipboard) { + IJ.showStatus("Pasting..."); + this.clipboard = clipboard; + imp.getProcessor().snapshot(); + updateClipRect(); + imp.draw(clipX, clipY, clipWidth, clipHeight); + } + + void updatePaste() { + if (clipboard!=null) { + imp.getMask(); + ImageProcessor ip = imp.getProcessor(); + ip.reset(); + ip.copyBits(clipboard.getProcessor(), x, y, pasteMode); + if (type!=RECTANGLE) + ip.reset(ip.getMask()); + ic.setImageUpdated(); + } + } + + public void endPaste() { + if (clipboard!=null) { + imp.getMask(); + ImageProcessor ip = imp.getProcessor(); + if (pasteMode!=Blitter.COPY) ip.reset(); + ip.copyBits(clipboard.getProcessor(), x, y, pasteMode); + if (type!=RECTANGLE) + ip.reset(ip.getMask()); + ip.snapshot(); + clipboard = null; + imp.updateAndDraw(); + Undo.setup(Undo.FILTER, imp); + } + } + + public void abortPaste() { + clipboard = null; + imp.getProcessor().reset(); + imp.updateAndDraw(); + } + + /** Returns the angle in degrees between the specified line and a horizontal line. */ + public double getAngle(int x1, int y1, int x2, int y2) { + double dx = x2-x1; + double dy = y1-y2; + if (imp!=null && !IJ.altKeyDown()) { + Calibration cal = imp.getCalibration(); + dx *= cal.pixelWidth; + dy *= cal.pixelHeight; + } + return (180.0/Math.PI)*Math.atan2(dy, dx); + } + + /** Sets the default (global) color used for ROI outlines. + * @see #getColor() + * @see #setLineColor(Color) + */ + public static void setColor(Color c) { + ROIColor = c; + } + + /** Returns the default (global) color used for drawing ROI outlines. + * @see #setColor(Color) + * @see #getLineColor() + */ + public static Color getColor() { + return ROIColor; + } + + /** Sets the color used by this ROI to draw its outline. This color, if not null, + * overrides the global color set by the static setColor() method. + * @see #getLineColor() + * @see #setLineWidth(int) + * @see ij.gui.ImageCanvas#setDisplayList(Roi,Color) + */ + public void setLineColor(Color c) { + outlineColor = c; + } + + /** Returns the the color used to draw the ROI outline or null if the default color is being used. + * @see #setLineColor(Color) + */ + public Color getLineColor() { + return outlineColor; + } + + /** Sets the color used to fill ROIs when they are in a display list. + * @see ij.gui.ImageCanvas#setDisplayList(Vector) + * @see ij.gui.ImageCanvas#setDisplayList(Roi,Color) + */ + public void setFillColor(Color color) { + fillColor = color; + } + + /** Returns the the color used to fill this ROI when it is in a display, or null. + * @see #getLineColor() + */ + public Color getFillColor() { + return fillColor; + } + + public static void setDefaultFillColor(Color color) { + defaultFillColor = color; + } + + public static Color getDefaultFillColor() { + return defaultFillColor; + } + + /** Copy the attributes (outline color, fill color, outline width) + of 'roi2' to the this selection. */ + public void copyAttributes(Roi roi2) { + this.outlineColor = roi2.outlineColor; + this.fillColor = roi2.fillColor; + this.stroke = roi2.stroke; + } + + /** Obsolete; replaced by setLineColor(). */ + public void setInstanceColor(Color c) { + outlineColor = c; + } + + /** Set 'nonScalable' true to have TextRois in a display + list drawn at a fixed location and size. */ + public void setNonScalable(boolean nonScalable) { + this.nonScalable = nonScalable; + } + + /** Sets the width of the lines used to draw this ROI when + * it is part of a display list or ROI Manager "Show All" list. + * @see #setLineColor(Color) + * @see ij.gui.ImageCanvas#setDisplayList(Roi,Color) + */ + public void setLineWidth(int width) { + this.stroke = new BasicStroke(width); + if (width>1) fillColor = null; + } + + /** Returns the lineWidth. */ + public int getLineWidth() { + return stroke!=null?(int)stroke.getLineWidth():1; + } + + /** Sets the Stroke used to draw this ROI. */ + public void setStroke(BasicStroke stroke) { + this.stroke = stroke; + } + + /** Returns the Stroke used to draw this ROI, or null if no Stroke is used. */ + public BasicStroke getStroke() { + return stroke; + } + + /** Returns the name of this ROI, or null. */ + public String getName() { + return name; + } + + /** Sets the name of this ROI. */ + public void setName(String name) { + this.name = name; + } + + /** Sets the Paste transfer mode. + @see ij.process.Blitter + */ + public static void setPasteMode(int transferMode) { + if (transferMode==pasteMode) return; + pasteMode = transferMode; + ImagePlus imp = WindowManager.getCurrentImage(); + if (imp!=null) + imp.updateAndDraw(); + } + + /** Returns the current paste transfer mode, or NOT_PASTING (-1) + if no paste operation is in progress. + @see ij.process.Blitter + */ + public int getPasteMode() { + if (clipboard==null) + return NOT_PASTING; + else + return pasteMode; + } + + /** Returns the current paste transfer mode. */ + public static int getCurrentPasteMode() { + return pasteMode; + } + + /** Returns true if this is an area selection. */ + public boolean isArea() { + return (type>=RECTANGLE && type<=TRACED_ROI) || type==COMPOSITE; + } + + /** Returns true if this is a line selection. */ + public boolean isLine() { + return type>=LINE && type<=FREELINE; + } + + /** Convenience method that converts Roi type to a human-readable form. */ + public String getTypeAsString() { + String s=""; + switch(type) { + case POLYGON: s="Polygon"; break; + case FREEROI: s="Freehand"; break; + case TRACED_ROI: s="Traced"; break; + case POLYLINE: s="Polyline"; break; + case FREELINE: s="Freeline"; break; + case ANGLE: s="Angle"; break; + case LINE: s="Straight Line"; break; + case OVAL: s="Oval"; break; + case COMPOSITE: s = "Composite"; break; + case POINT: s = "Point"; break; + default: s="Rectangle"; break; + } + return s; + } + + /** Returns true if this ROI is currently displayed on an image. */ + public boolean isVisible() { + return ic!=null; + } + + + /** Checks whether two rectangles are equal. */ + public boolean equals(Object obj) { + if (obj instanceof Roi) { + Roi roi2 = (Roi)obj; + if (type!=roi2.getType()) return false; + if (!getBounds().equals(roi2.getBounds())) return false; + if (getLength()!=roi2.getLength()) return false; + return true; + } else + return false; + } + + public String toString() { + return ("Roi["+getTypeAsString()+", x="+x+", y="+y+", width="+width+", height="+height+"]"); + } + +} diff --git a/ij/gui/RoiBrush.java b/ij/gui/RoiBrush.java index 38535d98e..0f7d97c9f 100644 --- a/ij/gui/RoiBrush.java +++ b/ij/gui/RoiBrush.java @@ -1,88 +1 @@ -package ij.gui; -import ij.*; -import java.awt.*; - -/** Implements the ROI Brush tool.*/ -class RoiBrush implements Runnable { - static int ADD=0, SUBTRACT=1; - static int leftClick=16, alt=9, shift=1; - private Polygon poly; - private Point previousP; - private int mode = ADD; - - RoiBrush() { - Thread thread = new Thread(this, "RoiBrush"); - thread.start(); - } - - public void run() { - int size = Toolbar.getBrushSize(); - ImagePlus img = WindowManager.getCurrentImage(); - if (img==null) return; - ImageCanvas ic = img.getCanvas(); - if (ic==null) return; - Roi roi = img.getRoi(); - if (roi!=null && !roi.isArea()) - img.killRoi(); - Point p = ic.getCursorLoc(); - if (roi!=null && !roi.contains(p.x, p.y)) - mode = SUBTRACT; - int flags; - while (true) { - p = ic.getCursorLoc(); - if (p.equals(previousP)) - {IJ.wait(1); continue;} - previousP = p; - flags = ic.getModifiers(); - if ((flags&leftClick)==0) return; - if ((flags&shift)!=0) - mode = ADD; - else if ((flags&alt)!=0) - mode = SUBTRACT; - if (mode==ADD) - addCircle(img, p.x, p.y, size); - else - subtractCircle(img, p.x, p.y, size); - } - } - - void addCircle(ImagePlus img, int x, int y, int width) { - Roi roi = img.getRoi(); - Roi roi2 = roi; - if (roi2!=null) { - if (!(roi2 instanceof ShapeRoi)) - roi2 = new ShapeRoi(roi2); - ((ShapeRoi)roi2).or(getCircularRoi(x, y, width)); - roi2.copyAttributes(roi); - } else - roi2 = new OvalRoi(x-width/2, y-width/2, width, width); - img.setRoi(roi2); - } - - void subtractCircle(ImagePlus img, int x, int y, int width) { - Roi roi = img.getRoi(); - Roi roi2 = roi; - if (roi2!=null) { - if (!(roi2 instanceof ShapeRoi)) - roi2 = new ShapeRoi(roi2); - ((ShapeRoi)roi2).not(getCircularRoi(x, y, width)); - roi2.copyAttributes(roi); - img.setRoi(roi2); - } - } - - - ShapeRoi getCircularRoi(int x, int y, int width) { - if (poly==null) { - Roi roi = new OvalRoi(x-width/2, y-width/2, width, width); - poly = roi.getPolygon(); - for (int i=0; i22) - message = new MultiLineLabel("Save changes to\n" + "\"" + fileName + "\"?"); - else - message = new Label("Save changes to \"" + fileName + "\"?"); - } - message.setFont(new Font("Dialog", Font.BOLD, 12)); - panel.add(message); - add("Center", panel); - - panel = new Panel(); - panel.setLayout(new FlowLayout(FlowLayout.CENTER, 8, 8)); - save = new Button(" Save "); - save.addActionListener(this); - save.addKeyListener(this); - cancel = new Button(" Cancel "); - cancel.addActionListener(this); - cancel.addKeyListener(this); - dontSave = new Button("Don't Save"); - dontSave.addActionListener(this); - dontSave.addKeyListener(this); - if (ij.IJ.isMacintosh()) { - panel.add(dontSave); - panel.add(cancel); - panel.add(save); - } else { - panel.add(save); - panel.add(dontSave); - panel.add(cancel); - } - add("South", panel); - if (ij.IJ.isMacintosh()) - setResizable(false); - pack(); - GUI.center(this); - show(); - } - - public void actionPerformed(ActionEvent e) { - if (e.getSource()==cancel) - cancelPressed = true; - else if (e.getSource()==save) - savePressed = true; - closeDialog(); - } - - /** Returns true if the user dismissed dialog by pressing "Cancel". */ - public boolean cancelPressed() { - if (cancelPressed) - ij.Macro.abort(); - return cancelPressed; - } - - /** Returns true if the user dismissed dialog by pressing "Save". */ - public boolean savePressed() { - return savePressed; - } - - void closeDialog() { - setVisible(false); - dispose(); - } - - public void keyPressed(KeyEvent e) { - int keyCode = e.getKeyCode(); - IJ.setKeyDown(keyCode); - if (keyCode==KeyEvent.VK_ENTER) - closeDialog(); - else if (keyCode==KeyEvent.VK_ESCAPE) { - cancelPressed = true; - closeDialog(); - IJ.resetEscape(); - } - } - - public void keyReleased(KeyEvent e) {} - public void keyTyped(KeyEvent e) {} - -} +package ij.gui; +import ij.IJ; +import java.awt.*; +import java.awt.event.*; + +/** A modal dialog box with a one line message and + "Don't Save", "Cancel" and "Save" buttons. */ +public class SaveChangesDialog extends Dialog implements ActionListener, KeyListener { + private Button dontSave, cancel, save; + private boolean cancelPressed, savePressed; + + public SaveChangesDialog(Frame parent, String fileName) { + super(parent, "Save?", true); + setLayout(new BorderLayout()); + Panel panel = new Panel(); + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10)); + Component message; + if (fileName.startsWith("Save ")) + message = new Label(fileName); + else { + if (fileName.length()>22) + message = new MultiLineLabel("Save changes to\n" + "\"" + fileName + "\"?"); + else + message = new Label("Save changes to \"" + fileName + "\"?"); + } + message.setFont(new Font("Dialog", Font.BOLD, 12)); + panel.add(message); + add("Center", panel); + + panel = new Panel(); + panel.setLayout(new FlowLayout(FlowLayout.CENTER, 8, 8)); + save = new Button(" Save "); + save.addActionListener(this); + save.addKeyListener(this); + cancel = new Button(" Cancel "); + cancel.addActionListener(this); + cancel.addKeyListener(this); + dontSave = new Button("Don't Save"); + dontSave.addActionListener(this); + dontSave.addKeyListener(this); + if (ij.IJ.isMacintosh()) { + panel.add(dontSave); + panel.add(cancel); + panel.add(save); + } else { + panel.add(save); + panel.add(dontSave); + panel.add(cancel); + } + add("South", panel); + if (ij.IJ.isMacintosh()) + setResizable(false); + pack(); + GUI.center(this); + show(); + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource()==cancel) + cancelPressed = true; + else if (e.getSource()==save) + savePressed = true; + closeDialog(); + } + + /** Returns true if the user dismissed dialog by pressing "Cancel". */ + public boolean cancelPressed() { + if (cancelPressed) + ij.Macro.abort(); + return cancelPressed; + } + + /** Returns true if the user dismissed dialog by pressing "Save". */ + public boolean savePressed() { + return savePressed; + } + + void closeDialog() { + setVisible(false); + dispose(); + } + + public void keyPressed(KeyEvent e) { + int keyCode = e.getKeyCode(); + IJ.setKeyDown(keyCode); + if (keyCode==KeyEvent.VK_ENTER) + closeDialog(); + else if (keyCode==KeyEvent.VK_ESCAPE) { + cancelPressed = true; + closeDialog(); + IJ.resetEscape(); + } + } + + public void keyReleased(KeyEvent e) {} + public void keyTyped(KeyEvent e) {} + +} diff --git a/ij/gui/StackWindow.java b/ij/gui/StackWindow.java index 3a147a377..57c27a301 100644 --- a/ij/gui/StackWindow.java +++ b/ij/gui/StackWindow.java @@ -1,244 +1,244 @@ -package ij.gui; -import ij.*; -import ij.measure.Calibration; -import java.awt.*; -import java.awt.image.*; -import java.awt.event.*; - -/** This class is an extended ImageWindow used to display image stacks. */ -public class StackWindow extends ImageWindow implements Runnable, AdjustmentListener, ActionListener, MouseWheelListener { - - protected Scrollbar channelSelector, sliceSelector, frameSelector; - protected Thread thread; - protected volatile boolean done; - protected volatile int slice; - boolean hyperStack; - int nChannels=1, nSlices=1, nFrames=1; - int c=1, z=1, t=1; - - - public StackWindow(ImagePlus imp) { - this(imp, null); - } - - public StackWindow(ImagePlus imp, ImageCanvas ic) { - super(imp, ic); - // add slice selection slider - ImageStack s = imp.getStack(); - int stackSize = s.getSize(); - nSlices = stackSize; - hyperStack = imp.getOpenAsHyperStack(); - imp.setOpenAsHyperStack(false); - int[] dim = imp.getDimensions(); - int nDimensions = 2+(dim[2]>1?1:0)+(dim[3]>1?1:0)+(dim[4]>1?1:0); - if (nDimensions<=3 && dim[2]!=nSlices) hyperStack = false; - if (hyperStack) { - nChannels = dim[2]; - nSlices = dim[3]; - nFrames = dim[4]; - } - //IJ.log("StackWindow: "+hyperStack+" "+nChannels+" "+nSlices+" "+nFrames); - if (nSlices==stackSize) hyperStack = false; - if (nChannels*nSlices*nFrames!=stackSize) hyperStack = false; - addMouseWheelListener(this); - ImageJ ij = IJ.getInstance(); - if (nChannels>1) { - channelSelector = new Scrollbar(Scrollbar.HORIZONTAL, 1, 1, 1, nChannels+1); - Panel panel = new Panel(new BorderLayout(2, 0)); - //panel.add(new Label("c"), BorderLayout.WEST); - //panel.add(channelSelector, BorderLayout.CENTER); - add(channelSelector); - if (ij!=null) channelSelector.addKeyListener(ij); - channelSelector.addAdjustmentListener(this); - channelSelector.setFocusable(false); // prevents scroll bar from blinking on Windows - channelSelector.setUnitIncrement(1); - channelSelector.setBlockIncrement(1); - } - if (nSlices>1) { - sliceSelector = new Scrollbar(Scrollbar.HORIZONTAL, 1, 1, 1, nSlices+1); - add(sliceSelector); - if (ij!=null) sliceSelector.addKeyListener(ij); - sliceSelector.addAdjustmentListener(this); - sliceSelector.setFocusable(false); - int blockIncrement = nSlices/10; - if (blockIncrement<1) blockIncrement = 1; - sliceSelector.setUnitIncrement(1); - sliceSelector.setBlockIncrement(blockIncrement); - } - if (nFrames>1) { - frameSelector = new Scrollbar(Scrollbar.HORIZONTAL, 1, 1, 1, nFrames+1); - add(frameSelector); - if (ij!=null) frameSelector.addKeyListener(ij); - frameSelector.addAdjustmentListener(this); - frameSelector.setFocusable(false); - int blockIncrement = nFrames/10; - if (blockIncrement<1) blockIncrement = 1; - frameSelector.setUnitIncrement(1); - frameSelector.setBlockIncrement(blockIncrement); - } - if (sliceSelector==null && this.getClass().getName().indexOf("Image5D")!=-1) - sliceSelector = new Scrollbar(); // prevents Image5D from crashing - //IJ.log(nChannels+" "+nSlices+" "+nFrames); - pack(); - ic = imp.getCanvas(); - if (ic!=null) ic.setMaxBounds(); - show(); - int previousSlice = imp.getCurrentSlice(); - if (previousSlice>1 && previousSlice<=stackSize) - imp.setSlice(previousSlice); - else - imp.setSlice(1); - thread = new Thread(this, "SliceSelector"); - thread.start(); - } - - public synchronized void adjustmentValueChanged(AdjustmentEvent e) { - if (!running2) { - //slice = sliceSelector.getValue(); - if (e.getSource()==channelSelector) - c = channelSelector.getValue(); - else if (e.getSource()==sliceSelector) - z = sliceSelector.getValue(); - else if (e.getSource()==frameSelector) - t = frameSelector.getValue(); - updatePosition(); - notify(); - } - } - - void updatePosition() { - slice = (t-1)*nChannels*nSlices + (z-1)*nChannels + c; - imp.updatePosition(c, z, t); - } - - public void actionPerformed(ActionEvent e) { - } - - public void mouseWheelMoved(MouseWheelEvent event) { - synchronized(this) { - int rotation = event.getWheelRotation(); - if (hyperStack) { - if (rotation>0) - IJ.runPlugIn("ij.plugin.Animator", "next"); - else if (rotation<0) - IJ.runPlugIn("ij.plugin.Animator", "previous"); - } else { - int slice = imp.getCurrentSlice() + rotation; - if (slice<1) - slice = 1; - else if (slice>imp.getStack().getSize()) - slice = imp.getStack().getSize(); - imp.setSlice(slice); - } - } - } - - public boolean close() { - if (!super.close()) - return false; - synchronized(this) { - done = true; - notify(); - } - return true; - } - - /** Displays the specified slice and updates the stack scrollbar. */ - public void showSlice(int index) { - if (index>=1 && index<=imp.getStackSize()) - imp.setSlice(index); - } - - /** Updates the stack scrollbar. */ - public void updateSliceSelector() { - if (hyperStack) return; - int stackSize = imp.getStackSize(); - int max = sliceSelector.getMaximum(); - if (max!=(stackSize+1)) - sliceSelector.setMaximum(stackSize+1); - sliceSelector.setValue(imp.getCurrentSlice()); - } - - public void run() { - while (!done) { - synchronized(this) { - try {wait();} - catch(InterruptedException e) {} - } - if (done) return; - if (slice>0) { - int s = slice; - slice = 0; - if (s!=imp.getCurrentSlice()) - imp.setSlice(s); - } - } - } - - public String createSubtitle() { - String subtitle = super.createSubtitle(); - if (!hyperStack) return subtitle; - String s=""; - int[] dim = imp.getDimensions(); - int channels=dim[2], slices=dim[3], frames=dim[4]; - if (channels>1) { - s += "c:"+imp.getChannel()+"/"+channels; - if (slices>1||frames>1) s += " "; - } - if (slices>1) { - s += "z:"+imp.getSlice()+"/"+slices; - if (frames>1) s += " "; - } - if (frames>1) - s += "t:"+imp.getFrame()+"/"+frames; - if (running2) return s; - int index = subtitle.indexOf(";"); - if (index!=-1) { - int index2 = subtitle.indexOf("("); - if (index2>=0 && index2index2+4 && !subtitle.substring(index2+1, index2+4).equals("ch:")) { - index = index2; - s = s + " "; - } - subtitle = subtitle.substring(index, subtitle.length()); - } else - subtitle = ""; - return s + subtitle; - } - - public boolean isHyperStack() { - return hyperStack; - } - - public void setPosition(int channel, int slice, int frame) { - if (channelSelector!=null && channel!=c) { - c = channel; - channelSelector.setValue(channel); - } - if (sliceSelector!=null && slice!=z) { - z = slice; - sliceSelector.setValue(slice); - } - if (frameSelector!=null && frame!=t) { - t = frame; - frameSelector.setValue(frame); - } - updatePosition(); - if (this.slice>0) { - int s = this.slice; - this.slice = 0; - if (s!=imp.getCurrentSlice()) - imp.setSlice(s); - } - } - - public boolean validDimensions() { - int c = imp.getNChannels(); - int z = imp.getNSlices(); - int t = imp.getNFrames(); - if (c!=nChannels||z!=nSlices||t!=nFrames||c*z*t!=imp.getStackSize()) - return false; - else - return true; - } - -} +package ij.gui; +import ij.*; +import ij.measure.Calibration; +import java.awt.*; +import java.awt.image.*; +import java.awt.event.*; + +/** This class is an extended ImageWindow used to display image stacks. */ +public class StackWindow extends ImageWindow implements Runnable, AdjustmentListener, ActionListener, MouseWheelListener { + + protected Scrollbar channelSelector, sliceSelector, frameSelector; + protected Thread thread; + protected volatile boolean done; + protected volatile int slice; + boolean hyperStack; + int nChannels=1, nSlices=1, nFrames=1; + int c=1, z=1, t=1; + + + public StackWindow(ImagePlus imp) { + this(imp, null); + } + + public StackWindow(ImagePlus imp, ImageCanvas ic) { + super(imp, ic); + // add slice selection slider + ImageStack s = imp.getStack(); + int stackSize = s.getSize(); + nSlices = stackSize; + hyperStack = imp.getOpenAsHyperStack(); + imp.setOpenAsHyperStack(false); + int[] dim = imp.getDimensions(); + int nDimensions = 2+(dim[2]>1?1:0)+(dim[3]>1?1:0)+(dim[4]>1?1:0); + if (nDimensions<=3 && dim[2]!=nSlices) hyperStack = false; + if (hyperStack) { + nChannels = dim[2]; + nSlices = dim[3]; + nFrames = dim[4]; + } + //IJ.log("StackWindow: "+hyperStack+" "+nChannels+" "+nSlices+" "+nFrames); + if (nSlices==stackSize) hyperStack = false; + if (nChannels*nSlices*nFrames!=stackSize) hyperStack = false; + addMouseWheelListener(this); + ImageJ ij = IJ.getInstance(); + if (nChannels>1) { + channelSelector = new Scrollbar(Scrollbar.HORIZONTAL, 1, 1, 1, nChannels+1); + Panel panel = new Panel(new BorderLayout(2, 0)); + //panel.add(new Label("c"), BorderLayout.WEST); + //panel.add(channelSelector, BorderLayout.CENTER); + add(channelSelector); + if (ij!=null) channelSelector.addKeyListener(ij); + channelSelector.addAdjustmentListener(this); + channelSelector.setFocusable(false); // prevents scroll bar from blinking on Windows + channelSelector.setUnitIncrement(1); + channelSelector.setBlockIncrement(1); + } + if (nSlices>1) { + sliceSelector = new Scrollbar(Scrollbar.HORIZONTAL, 1, 1, 1, nSlices+1); + add(sliceSelector); + if (ij!=null) sliceSelector.addKeyListener(ij); + sliceSelector.addAdjustmentListener(this); + sliceSelector.setFocusable(false); + int blockIncrement = nSlices/10; + if (blockIncrement<1) blockIncrement = 1; + sliceSelector.setUnitIncrement(1); + sliceSelector.setBlockIncrement(blockIncrement); + } + if (nFrames>1) { + frameSelector = new Scrollbar(Scrollbar.HORIZONTAL, 1, 1, 1, nFrames+1); + add(frameSelector); + if (ij!=null) frameSelector.addKeyListener(ij); + frameSelector.addAdjustmentListener(this); + frameSelector.setFocusable(false); + int blockIncrement = nFrames/10; + if (blockIncrement<1) blockIncrement = 1; + frameSelector.setUnitIncrement(1); + frameSelector.setBlockIncrement(blockIncrement); + } + if (sliceSelector==null && this.getClass().getName().indexOf("Image5D")!=-1) + sliceSelector = new Scrollbar(); // prevents Image5D from crashing + //IJ.log(nChannels+" "+nSlices+" "+nFrames); + pack(); + ic = imp.getCanvas(); + if (ic!=null) ic.setMaxBounds(); + show(); + int previousSlice = imp.getCurrentSlice(); + if (previousSlice>1 && previousSlice<=stackSize) + imp.setSlice(previousSlice); + else + imp.setSlice(1); + thread = new Thread(this, "SliceSelector"); + thread.start(); + } + + public synchronized void adjustmentValueChanged(AdjustmentEvent e) { + if (!running2) { + //slice = sliceSelector.getValue(); + if (e.getSource()==channelSelector) + c = channelSelector.getValue(); + else if (e.getSource()==sliceSelector) + z = sliceSelector.getValue(); + else if (e.getSource()==frameSelector) + t = frameSelector.getValue(); + updatePosition(); + notify(); + } + } + + void updatePosition() { + slice = (t-1)*nChannels*nSlices + (z-1)*nChannels + c; + imp.updatePosition(c, z, t); + } + + public void actionPerformed(ActionEvent e) { + } + + public void mouseWheelMoved(MouseWheelEvent event) { + synchronized(this) { + int rotation = event.getWheelRotation(); + if (hyperStack) { + if (rotation>0) + IJ.runPlugIn("ij.plugin.Animator", "next"); + else if (rotation<0) + IJ.runPlugIn("ij.plugin.Animator", "previous"); + } else { + int slice = imp.getCurrentSlice() + rotation; + if (slice<1) + slice = 1; + else if (slice>imp.getStack().getSize()) + slice = imp.getStack().getSize(); + imp.setSlice(slice); + } + } + } + + public boolean close() { + if (!super.close()) + return false; + synchronized(this) { + done = true; + notify(); + } + return true; + } + + /** Displays the specified slice and updates the stack scrollbar. */ + public void showSlice(int index) { + if (index>=1 && index<=imp.getStackSize()) + imp.setSlice(index); + } + + /** Updates the stack scrollbar. */ + public void updateSliceSelector() { + if (hyperStack) return; + int stackSize = imp.getStackSize(); + int max = sliceSelector.getMaximum(); + if (max!=(stackSize+1)) + sliceSelector.setMaximum(stackSize+1); + sliceSelector.setValue(imp.getCurrentSlice()); + } + + public void run() { + while (!done) { + synchronized(this) { + try {wait();} + catch(InterruptedException e) {} + } + if (done) return; + if (slice>0) { + int s = slice; + slice = 0; + if (s!=imp.getCurrentSlice()) + imp.setSlice(s); + } + } + } + + public String createSubtitle() { + String subtitle = super.createSubtitle(); + if (!hyperStack) return subtitle; + String s=""; + int[] dim = imp.getDimensions(); + int channels=dim[2], slices=dim[3], frames=dim[4]; + if (channels>1) { + s += "c:"+imp.getChannel()+"/"+channels; + if (slices>1||frames>1) s += " "; + } + if (slices>1) { + s += "z:"+imp.getSlice()+"/"+slices; + if (frames>1) s += " "; + } + if (frames>1) + s += "t:"+imp.getFrame()+"/"+frames; + if (running2) return s; + int index = subtitle.indexOf(";"); + if (index!=-1) { + int index2 = subtitle.indexOf("("); + if (index2>=0 && index2index2+4 && !subtitle.substring(index2+1, index2+4).equals("ch:")) { + index = index2; + s = s + " "; + } + subtitle = subtitle.substring(index, subtitle.length()); + } else + subtitle = ""; + return s + subtitle; + } + + public boolean isHyperStack() { + return hyperStack; + } + + public void setPosition(int channel, int slice, int frame) { + if (channelSelector!=null && channel!=c) { + c = channel; + channelSelector.setValue(channel); + } + if (sliceSelector!=null && slice!=z) { + z = slice; + sliceSelector.setValue(slice); + } + if (frameSelector!=null && frame!=t) { + t = frame; + frameSelector.setValue(frame); + } + updatePosition(); + if (this.slice>0) { + int s = this.slice; + this.slice = 0; + if (s!=imp.getCurrentSlice()) + imp.setSlice(s); + } + } + + public boolean validDimensions() { + int c = imp.getNChannels(); + int z = imp.getNSlices(); + int t = imp.getNFrames(); + if (c!=nChannels||z!=nSlices||t!=nFrames||c*z*t!=imp.getStackSize()) + return false; + else + return true; + } + +} diff --git a/ij/gui/TextRoi.java b/ij/gui/TextRoi.java index 53b31d6fe..17416aa9d 100644 --- a/ij/gui/TextRoi.java +++ b/ij/gui/TextRoi.java @@ -1,333 +1,333 @@ -package ij.gui; -import java.awt.*; -import ij.*; -import ij.process.*; -import ij.util.*; - - -/** This class is a rectangular ROI containing text. */ -public class TextRoi extends Roi { - - static final int MAX_LINES = 50; - - private String[] theText = new String[MAX_LINES]; - private static String name = "SansSerif"; - private static int style = Font.PLAIN; - private static int size = 18; - private Font instanceFont, cachedFont; - private static boolean newFont = true; - private static boolean antialiasedText = true; - private static boolean recordSetFont = true; - private double previousMag; - private boolean firstChar = true; - private boolean firstMouseUp = true; - private int cline = 0; - - /** Creates a new TextRoi.*/ - public TextRoi(int x, int y, String text) { - this(x, y, text, null, null); - } - - /** Creates a new TextRoi with the specified location and Font. - * @see ij.gui.Roi#setLineColor - * @see ij.gui.Roi#setNonScalable - * @see ij.gui.ImageCanvas#setDisplayList(Roi,Color) - */ - public TextRoi(int x, int y, String text, Font font) { - super(x, y, 1, 1); - String[] lines = Tools.split(text, "\n"); - int count = Math.min(lines.length, MAX_LINES); - for (int i=0; i1.0) - mag = 1.0; - if (size<(12/mag)) - size = (int)(12/mag); - theText[0] = "Type, then"; - theText[1] = "Ctl+D"; - if (previousRoi!=null && (previousRoi instanceof TextRoi)) { - firstMouseUp = false; - //IJ.write(""+previousRoi.getBounds()); - previousRoi = null; - } - } - - /** Adds the specified character to the end of the text string. */ - public void addChar(char c) { - if (!(c>=' ' || c=='\b' || c=='\n')) return; - if (firstChar) { - cline = 0; - theText[cline] = new String(""); - for (int i=1; i0) - theText[cline] = theText[cline].substring(0, theText[cline].length()-1); - else if (cline>0) { - theText[cline] = null; - cline--; - } - imp.draw(clipX, clipY, clipWidth, clipHeight); - firstChar = false; - return; - } else if ((int)c=='\n') { - // newline - if (cline<(MAX_LINES-1)) cline++; - theText[cline] = ""; - updateBounds(); - updateText(); - } else { - char[] chr = {c}; - theText[cline] += new String(chr); - updateBounds(); - updateText(); - firstChar = false; - return; - } - } - - Font getCurrentFont() { - double mag = ic.getMagnification(); - if (instanceFont!=null) { - if (nonScalable) - return instanceFont; - else - return instanceFont.deriveFont((float)(instanceFont.getSize()*mag)); - } - if (newFont || cachedFont==null || mag!=previousMag) { - cachedFont = new Font(name, style, (int)(size*mag)); - previousMag = mag; - newFont = false; - } - return cachedFont; - } - - /** Renders the text on the image. */ - public void drawPixels(ImageProcessor ip) { - Font font = new Font(name, style, size); - ip.setFont(font); - ip.setAntialiasedText(antialiasedText); - FontMetrics metrics = ip.getFontMetrics(); - int fontHeight = metrics.getHeight(); - int descent = metrics.getDescent(); - int i = 0; - int yy = 0; - while (iwidth) - width = w; - i++; - } - g.dispose(); - width += 2; - if (x+width>xMax) - x = xMax-width; - height = nLines*fontHeight+2; - if (height>yMax) - height = yMax; - if (y+height>yMax) - y = yMax-height; - if (IJ.debugMode) IJ.log("adjustSize2: "+theText[0]+" "+width+","+height); - } - - void updateText() { - if (imp!=null) { - updateClipRect(); - imp.draw(clipX, clipY, clipWidth, clipHeight); - } - } - - int stringWidth(String s, FontMetrics metrics, Graphics g) { - return Java2.getStringWidth(s, metrics, g); - } - - public String getMacroCode(ImageProcessor ip) { - String code = ""; - if (recordSetFont) { - String options = ""; - if (style==Font.BOLD) - options += "bold"; - if (style==Font.ITALIC) - options += " italic"; - if (antialiasedText) - options += " antialiased"; - if (options.equals("")) - options = "plain"; - code += "setFont(\""+name+"\", "+size+", \""+options+"\");\n"; - recordSetFont = false; - } - FontMetrics metrics = ip.getFontMetrics(); - int fontHeight = metrics.getHeight(); - String text = ""; - for (int i=0; i1.0) + mag = 1.0; + if (size<(12/mag)) + size = (int)(12/mag); + theText[0] = "Type, then"; + theText[1] = "Ctl+D"; + if (previousRoi!=null && (previousRoi instanceof TextRoi)) { + firstMouseUp = false; + //IJ.write(""+previousRoi.getBounds()); + previousRoi = null; + } + } + + /** Adds the specified character to the end of the text string. */ + public void addChar(char c) { + if (!(c>=' ' || c=='\b' || c=='\n')) return; + if (firstChar) { + cline = 0; + theText[cline] = new String(""); + for (int i=1; i0) + theText[cline] = theText[cline].substring(0, theText[cline].length()-1); + else if (cline>0) { + theText[cline] = null; + cline--; + } + imp.draw(clipX, clipY, clipWidth, clipHeight); + firstChar = false; + return; + } else if ((int)c=='\n') { + // newline + if (cline<(MAX_LINES-1)) cline++; + theText[cline] = ""; + updateBounds(); + updateText(); + } else { + char[] chr = {c}; + theText[cline] += new String(chr); + updateBounds(); + updateText(); + firstChar = false; + return; + } + } + + Font getCurrentFont() { + double mag = ic.getMagnification(); + if (instanceFont!=null) { + if (nonScalable) + return instanceFont; + else + return instanceFont.deriveFont((float)(instanceFont.getSize()*mag)); + } + if (newFont || cachedFont==null || mag!=previousMag) { + cachedFont = new Font(name, style, (int)(size*mag)); + previousMag = mag; + newFont = false; + } + return cachedFont; + } + + /** Renders the text on the image. */ + public void drawPixels(ImageProcessor ip) { + Font font = new Font(name, style, size); + ip.setFont(font); + ip.setAntialiasedText(antialiasedText); + FontMetrics metrics = ip.getFontMetrics(); + int fontHeight = metrics.getHeight(); + int descent = metrics.getDescent(); + int i = 0; + int yy = 0; + while (iwidth) + width = w; + i++; + } + g.dispose(); + width += 2; + if (x+width>xMax) + x = xMax-width; + height = nLines*fontHeight+2; + if (height>yMax) + height = yMax; + if (y+height>yMax) + y = yMax-height; + if (IJ.debugMode) IJ.log("adjustSize2: "+theText[0]+" "+width+","+height); + } + + void updateText() { + if (imp!=null) { + updateClipRect(); + imp.draw(clipX, clipY, clipWidth, clipHeight); + } + } + + int stringWidth(String s, FontMetrics metrics, Graphics g) { + return Java2.getStringWidth(s, metrics, g); + } + + public String getMacroCode(ImageProcessor ip) { + String code = ""; + if (recordSetFont) { + String options = ""; + if (style==Font.BOLD) + options += "bold"; + if (style==Font.ITALIC) + options += " italic"; + if (antialiasedText) + options += " antialiased"; + if (options.equals("")) + options = "plain"; + code += "setFont(\""+name+"\", "+size+", \""+options+"\");\n"; + recordSetFont = false; + } + FontMetrics metrics = ip.getFontMetrics(); + int fontHeight = metrics.getHeight(); + String text = ""; + for (int i=0; i>" - addPopupMenus(); - if (IJ.isMacOSX() || IJ.isVista()) Prefs.antialiasedTools = true; - } - - void addPopupMenus() { - ovalPopup = new PopupMenu(); - if (Menus.getFontSize()!=0) - ovalPopup.setFont(Menus.getFont()); - ovalItem = new CheckboxMenuItem("Elliptical Selection Tool", !brushEnabled); - ovalItem.addItemListener(this); - ovalPopup.add(ovalItem); - brushItem = new CheckboxMenuItem("Selection Brush Tool", brushEnabled); - brushItem.addItemListener(this); - ovalPopup.add(brushItem); - add(ovalPopup); - - linePopup = new PopupMenu(); - if (Menus.getFontSize()!=0) - linePopup.setFont(Menus.getFont()); - straightLineItem = new CheckboxMenuItem("Straight Lines", lineType==LINE); - straightLineItem.addItemListener(this); - linePopup.add(straightLineItem); - polyLineItem = new CheckboxMenuItem("Segmented Lines", lineType==POLYLINE); - polyLineItem.addItemListener(this); - linePopup.add(polyLineItem); - freeLineItem = new CheckboxMenuItem("Freehand Lines", lineType==FREELINE); - freeLineItem.addItemListener(this); - linePopup.add(freeLineItem); - add(linePopup); - - switchPopup = new PopupMenu(); - if (Menus.getFontSize()!=0) - switchPopup.setFont(Menus.getFont()); - add(switchPopup); - } - - /** Returns the ID of the current tool (Toolbar.RECTANGLE, - Toolbar.OVAL, etc.). */ - public static int getToolId() { - return current; - } - - /** Returns the ID of the tool whose name (the description displayed in the status bar) - starts with the specified string, or -1 if the tool is not found. */ - public int getToolId(String name) { - int tool = -1; - for (int i=0; i<=SPARE9; i++) { - if (names[i]!=null && names[i].startsWith(name)) { - tool = i; - break; - } - } - return tool; - } - - /** Returns a reference to the ImageJ toolbar. */ - public static Toolbar getInstance() { - return instance; - } - - private void drawButtons(Graphics g) { - if (Prefs.antialiasedTools) { - Graphics2D g2d = (Graphics2D)g; - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - } - for (int i=0; i=SPARE1 && tool<=SPARE9 && icons[tool]!=null) { - drawIcon(g, tool, x, y); - return; - } - switch (tool) { - case RECTANGLE: - g.drawRect(x+1, y+2, 15, 12); - return; - case OVAL: - xOffset = x; yOffset = y; - if (brushEnabled) { - m(9,2); d(13,2); d(13,2); d(15,5); d(15,8); - d(13,10); d(10,10); d(8,13); d(4,13); - d(2,11); d(2,7); d(4,5); d(7,5); d(9,2); - } else - g.drawOval(x+1, y+2, 15, 12); - drawTriangle(15,14); - return; - case POLYGON: - xOffset = x+1; yOffset = y+3; - m(4,0); d(14,0); d(14,1); d(10,5); d(10,6); - d(13,9); d(13,10); d(0,10); d(0,4); d(4,0); - return; - case FREEROI: - xOffset = x+1; yOffset = y+3; - m(3,0); d(5,0); d(7,2); d(9,2); d(11,0); d(13,0); d(14,1); d(15,2); - d(15,4); d(14,5); d(14,6); d(12,8); d(11,8); d(10,9); d(9,9); d(8,10); - d(5,10); d(3,8); d(2,8); d(1,7); d(1,6); d(0,5); d(0,2); d(1,1); d(2,1); - return; - case LINE: - xOffset = x; yOffset = y; - m(0,12); d(17,3); - //m(0,10); d(16,4); - drawTriangle(12,14); - return; - case POLYLINE: - xOffset = x; yOffset = y; - m(14,6); d(11,3); d(1,3); d(1,4); d(6,9); d(2,13); - drawTriangle(12,14); - return; - case FREELINE: - xOffset = x; yOffset = y; - m(16,4); d(14,6); d(12,6); d(9,3); d(8,3); d(6,7); d(2,11); d(1,11); - drawTriangle(12,14); - return; - case POINT: - xOffset = x; yOffset = y; - m(1,8); d(6,8); d(6,6); d(10,6); d(10,10); d(6,10); d(6,9); - m(8,1); d(8,5); m(11,8); d(15,8); m(8,11); d(8,15); - m(8,8); d(8,8); - g.setColor(Roi.getColor()); - g.fillRect(x+7, y+7, 3, 3); - return; - case WAND: - xOffset = x+2; yOffset = y+2; - dot(4,0); m(2,0); d(3,1); d(4,2); m(0,0); d(1,1); - m(0,2); d(1,3); d(2,4); dot(0,4); m(3,3); d(12,12); - return; - case TEXT: - xOffset = x+2; yOffset = y+1; - m(0,13); d(3,13); - m(1,12); d(7,0); d(12,13); - m(11,13); d(14,13); - m(3,8); d(10,8); - return; - case MAGNIFIER: - xOffset = x+2; yOffset = y+2; - m(3,0); d(3,0); d(5,0); d(8,3); d(8,5); d(7,6); d(7,7); - d(6,7); d(5,8); d(3,8); d(0,5); d(0,3); d(3,0); - m(8,8); d(9,8); d(13,12); d(13,13); d(12,13); d(8,9); d(8,8); - return; - case HAND: - xOffset = x+1; yOffset = y+1; - m(5,14); d(2,11); d(2,10); d(0,8); d(0,7); d(1,6); d(2,6); d(4,8); - d(4,6); d(3,5); d(3,4); d(2,3); d(2,2); d(3,1); d(4,1); d(5,2); d(5,3); - m(6,5); d(6,1); d(7,0); d(8,0); d(9,1); d(9,5); - m(9,1); d(11,1); d(12,2); d(12,6); - m(13,4); d(14,3); d(15,4); d(15,7); d(14,8); - d(14,10); d(13,11); d(13,12); d(12,13); d(12,14); - return; - case DROPPER: - xOffset = x; yOffset = y; - g.setColor(foregroundColor); - //m(0,0); d(17,0); d(17,17); d(0,17); d(0,0); - m(12,2); d(14,2); - m(11,3); d(15,3); - m(11,4); d(15,4); - m(8,5); d(15,5); - m(9,6); d(14,6); - m(10,7); d(12,7); d(12,9); - m(8,7); d(2,13); d(2,15); d(4,15); d(11,8); - g.setColor(backgroundColor); - m(0,0); d(16,0); d(16,16); d(0,16); d(0,0); - return; - case ANGLE: - xOffset = x+1; yOffset = y+2; - m(0,11); d(11,0); m(0,11); d(15,11); - m(10,11); d(10,8); m(9,7); d(9,6); dot(8,5); - return; - } - } - - void drawTriangle(int x, int y) { - g.setColor(triangleColor); - xOffset+=x; yOffset+=y; - m(0,0); d(4,0); m(1,1); d(3,1); dot(2,2); - } - - void drawIcon(Graphics g, int tool, int x, int y) { - if (null==g) return; - icon = icons[tool]; - this.icon = icon; - int length = icon.length(); - int x1, y1, x2, y2; - pc = 0; - while (true) { - char command = icon.charAt(pc++); - if (pc>=length) break; - switch (command) { - case 'B': x+=v(); y+=v(); break; // reset base - case 'R': g.drawRect(x+v(), y+v(), v(), v()); break; // rectangle - case 'F': g.fillRect(x+v(), y+v(), v(), v()); break; // filled rectangle - case 'O': g.drawOval(x+v(), y+v(), v(), v()); break; // oval - case 'o': g.fillOval(x+v(), y+v(), v(), v()); break; // filled oval - case 'C': g.setColor(new Color(v()*16,v()*16,v()*16)); break; // set color - case 'L': g.drawLine(x+v(), y+v(), x+v(), y+v()); break; // line - case 'D': g.fillRect(x+v(), y+v(), 1, 1); break; // dot - case 'P': // polyline - x1=x+v(); y1=y+v(); - while (true) { - x2=v(); if (x2==0) break; - y2=v(); if (y2==0) break; - x2+=x; y2+=y; - g.drawLine(x1, y1, x2, y2); - x1=x2; y1=y2; - } - break; - case 'T': // text (one character) - x2 = x+v(); - y2 = y+v(); - int size = v()*10+v(); - char[] c = new char[1]; - c[0] = pc=length) break; - } - if (menus[tool]!=null && menus[tool].getItemCount()>0) { - xOffset = x; yOffset = y; - drawTriangle(14, 14); - } - } - - int v() { - if (pc>=icon.length()) return 0; - char c = icon.charAt(pc++); - //IJ.log("v: "+pc+" "+c+" "+toInt(c)); - switch (c) { - case '0': return 0; - case '1': return 1; - case '2': return 2; - case '3': return 3; - case '4': return 4; - case '5': return 5; - case '6': return 6; - case '7': return 7; - case '8': return 8; - case '9': return 9; - case 'a': return 10; - case 'b': return 11; - case 'c': return 12; - case 'd': return 13; - case 'e': return 14; - case 'f': return 15; - default: return 0; - } - } - - private void showMessage(int tool) { - if (tool>=SPARE1 && tool<=SPARE9 && names[tool]!=null) { - String name = names[tool]; - int index = name.indexOf("Action Tool"); - if (index!=-1) - name = name.substring(0, index); - else { - index = name.indexOf("Menu Tool"); - if (index!=-1) - name = name.substring(0, index+4); - } - IJ.showStatus(name); - return; - } - switch (tool) { - case RECTANGLE: - IJ.showStatus("Rectangular selections"); - return; - case OVAL: - if (brushEnabled) - IJ.showStatus("Elliptical or *brush* selections"); - else - IJ.showStatus("*Elliptical* or brush selections"); - return; - case POLYGON: - IJ.showStatus("Polygon selections"); - return; - case FREEROI: - IJ.showStatus("Freehand selections"); - return; - case LINE: - IJ.showStatus("Straight line selections (right click for other types)"); - return; - case POLYLINE: - IJ.showStatus("Segmented line selections"); - return; - case FREELINE: - IJ.showStatus("Freehand line selections"); - return; - case POINT: - IJ.showStatus("Point selections (shift click for multiple points)"); - return; - case WAND: - IJ.showStatus("Wand (tracing) tool"); - return; - case TEXT: - IJ.showStatus("Text tool"); - TextRoi.recordSetFont(); - return; - case MAGNIFIER: - IJ.showStatus("Magnifying glass (or use \"+\" and \"-\" keys)"); - return; - case HAND: - IJ.showStatus("Scrolling tool (or press space bar and drag)"); - return; - case DROPPER: - IJ.showStatus("Color picker (" + foregroundColor.getRed() + "," - + foregroundColor.getGreen() + "," + foregroundColor.getBlue() + ")"); - return; - case ANGLE: - IJ.showStatus("Angle tool"); - return; - default: - IJ.showStatus("ImageJ "+IJ.getVersion()+" / Java "+System.getProperty("java.version")+(IJ.is64Bit()?" (64-bit)":" (32-bit)")); - return; - } - } - - private void m(int x, int y) { - this.x = xOffset+x; - this.y = yOffset+y; - } - - private void d(int x, int y) { - x += xOffset; - y += yOffset; - g.drawLine(this.x, this.y, x, y); - this.x = x; - this.y = y; - } - - private void dot(int x, int y) { - g.fillRect(x+xOffset, y+yOffset, 1, 1); - } - - private void resetButtons() { - for (int i=0; i=NUM_TOOLS-1) - return; - if (tool==SPARE1||(tool>=SPARE2&&tool<=SPARE8)) { - if (names[tool]==null) - names[tool] = "Spare tool"; // enable tool - if (names[tool].indexOf("Action Tool")!=-1) - return; - } - if (isLine(tool)) lineType = tool; - setTool2(tool); - } - - private void setTool2(int tool) { - if ((tool==current&&tool!=OVAL) || !isValidTool(tool)) return; - current = tool; - down[current] = true; - down[previous] = false; - Graphics g = this.getGraphics(); - if (Prefs.antialiasedTools) { - Graphics2D g2d = (Graphics2D)g; - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - } - drawButton(g, previous); - drawButton(g, current); - if (null==g) return; - g.dispose(); - showMessage(current); - previous = current; - if (Recorder.record) - Recorder.record("setTool", current); - if (IJ.isMacOSX()) - repaint(); - } - - boolean isValidTool(int tool) { - if (tool<0 || tool>=NUM_TOOLS) - return false; - if ((tool==SPARE1||(tool>=SPARE2&&tool<=SPARE9)) && names[tool]==null) - return false; - return true; - } - - /** Obsolete. Use getForegroundColor(). */ - public Color getColor() { - return foregroundColor; - } - - /** Obsolete. Use setForegroundColor(). */ - public void setColor(Color c) { - if (c!=null) { - foregroundColor = c; - drawButton(this.getGraphics(), DROPPER); - } - } - - public static Color getForegroundColor() { - return foregroundColor; - } - - public static void setForegroundColor(Color c) { - if (c!=null) { - foregroundColor = c; - repaintTool(DROPPER); - } - } - - public static Color getBackgroundColor() { - return backgroundColor; - } - - public static void setBackgroundColor(Color c) { - if (c!=null) { - backgroundColor = c; - repaintTool(DROPPER); - } - } - - /** Returns the size of the brush tool or 0 if the brush tool is not enabled. */ - public static int getBrushSize() { - if (brushEnabled) - return brushSize; - else - return 0; - } - - /** Set the size of the brush tool, which must be greater than 4. */ - public static void setBrushSize(int size) { - brushSize = size; - if (brushSize<5) brushSize = 5; - Prefs.set(BRUSH_SIZE, brushSize); - } - - public static int getButtonSize() { - return SIZE; - } - - static void repaintTool(int tool) { - if (IJ.getInstance()!=null) { - Toolbar tb = getInstance(); - Graphics g = tb.getGraphics(); - if (Prefs.antialiasedTools) - ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - tb.drawButton(g, tool); - if (g!=null) g.dispose(); - } - //Toolbar tb = getInstance(); - //tb.repaint(tool * SIZE , 0, SIZE, SIZE); - } - - // Returns the toolbar position index of the specified tool - int toolIndex(int tool) { - switch (tool) { - case RECTANGLE: return 0; - case OVAL: return 1; - case POLYGON: return 2; - case FREEROI: return 3; - case LINE: return 4; - case POLYLINE: return 4; - case FREELINE: return 4; - case POINT: return 6; - case WAND: return 7; - case TEXT: return 8; - case MAGNIFIER: return 9; - case HAND: return 10; - case DROPPER: return 11; - case ANGLE: return 5; - case SPARE1: return 12; - default: return tool - 2; - } - } - - // Returns the tool corresponding to the specified tool position index - int toolID(int index) { - switch (index) { - case 0: return RECTANGLE; - case 1: return OVAL; - case 2: return POLYGON; - case 3: return FREEROI; - case 4: return lineType; - case 5: return ANGLE; - case 6: return POINT; - case 7: return WAND; - case 8: return TEXT; - case 9: return MAGNIFIER; - case 10: return HAND; - case 11: return DROPPER; - case 12: return SPARE1; - default: return index + 2; - } - } - - public void mousePressed(MouseEvent e) { - int x = e.getX(); - int newTool = 0; - for (int i=0; ii*SIZE && x0) { - menus[newTool].show(e.getComponent(), e.getX(), e.getY()); - return; - } - boolean doubleClick = newTool==current && (System.currentTimeMillis()-mouseDownTime)<=DOUBLE_CLICK_THRESHOLD; - mouseDownTime = System.currentTimeMillis(); - if (!doubleClick) { - mpPrevious = current; - if (isMacroTool(newTool)) { - String name = names[newTool]; - if (name.indexOf("Unused Tool")!=-1) - return; - if (name.indexOf("Action Tool")!=-1) { - if (e.isPopupTrigger()||e.isMetaDown()) { - name = name.endsWith(" ")?name:name+" "; - macroInstaller.runMacroTool(name+"Options"); - } else { - drawTool(newTool, true); - IJ.wait(50); - drawTool(newTool, false); - runMacroTool(newTool); - } - return; - } else { - name = name.endsWith(" ")?name:name+" "; - macroInstaller.runMacroTool(name+"Selected"); - } - } - setTool2(newTool); - boolean isRightClick = e.isPopupTrigger()||e.isMetaDown(); - if (current==OVAL && isRightClick) { - ovalItem.setState(!brushEnabled); - brushItem.setState(brushEnabled); - if (IJ.isMacOSX()) IJ.wait(10); - ovalPopup.show(e.getComponent(),x,y); - mouseDownTime = 0L; - } - if (isLine(current) && isRightClick) { - straightLineItem.setState(lineType==LINE); - polyLineItem.setState(lineType==POLYLINE); - freeLineItem.setState(lineType==FREELINE); - if (IJ.isMacOSX()) IJ.wait(10); - linePopup.show(e.getComponent(),x,y); - mouseDownTime = 0L; - } - if (isMacroTool(current) && isRightClick) { - String name = names[current].endsWith(" ")?names[current]:names[current]+" "; - macroInstaller.runMacroTool(name+"Options"); - } - } else { - if (isMacroTool(current)) { - String name = names[current].endsWith(" ")?names[current]:names[current]+" "; - macroInstaller.runMacroTool(name+"Options"); - return; - } - ImagePlus imp = WindowManager.getCurrentImage(); - switch (current) { - case OVAL: - showBrushDialog(); - break; - case MAGNIFIER: - if (imp!=null) { - ImageCanvas ic = imp.getCanvas(); - if (ic!=null) ic.unzoom(); - } - break; - case LINE: case POLYLINE: case FREELINE: - IJ.runPlugIn("ij.plugin.frame.LineWidthAdjuster", ""); - break; - case POINT: - IJ.doCommand("Point Tool..."); - break; - case WAND: - IJ.doCommand("Wand Tool..."); - break; - case TEXT: - IJ.doCommand("Fonts..."); - break; - case DROPPER: - IJ.doCommand("Color Picker..."); - setTool2(mpPrevious); - break; - default: - } - } - } - - void showSwitchPopupMenu(MouseEvent e) { - String path = IJ.getDirectory("macros")+"toolsets/"; - if (path==null) { - return; - } - boolean applet = IJ.getApplet()!=null; - File f = new File(path); - String[] list; - if (!applet && f.exists() && f.isDirectory()) { - list = f.list(); - if (list==null) return; - } else - list = new String[0]; - boolean stackTools = false; - for (int i=0; i=SPARE1 && tool<=SPARE9 && names[tool]!=null && macroInstaller!=null; - } - - public void mouseReleased(MouseEvent e) {} - public void mouseExited(MouseEvent e) {} - public void mouseClicked(MouseEvent e) {} - public void mouseEntered(MouseEvent e) {} - public void mouseDragged(MouseEvent e) {} - - public void itemStateChanged(ItemEvent e) { - CheckboxMenuItem item = (CheckboxMenuItem)e.getSource(); - if (item==ovalItem || item==brushItem) { - brushEnabled = item==brushItem; - repaintTool(OVAL); - showMessage(OVAL); - } else if (item==straightLineItem) { - lineType = LINE; - setTool2(LINE); - showMessage(LINE); - } else if (item==polyLineItem) { - lineType = POLYLINE; - setTool2(POLYLINE); - showMessage(POLYLINE); - } else if (item==freeLineItem) { - lineType = FREELINE; - setTool2(FREELINE); - showMessage(FREELINE); - } else { - String label = item.getActionCommand(); - if (!label.equals("Help...")) currentSet = label; - String path; - if (label.equals("Help...")) { - IJ.showMessage("Tool Switcher", - "Use this drop down menu to switch to macro tool\n"+ - "sets located in the ImageJ/macros/toolsets folder,\n"+ - "or to revert to the ImageJ/macros/StartupMacros\n"+ - "set. The default tool sets, which have names\n"+ - "ending in '*', are loaded from ij.jar.\n"+ - " \n"+ - "Hold the shift key down while selecting a tool\n"+ - "set to view its source code.\n"+ - " \n"+ - "Several example tool sets are available at\n"+ - "<"+IJ.URL+"/macros/toolsets/>." - ); - return; - } else if (label.endsWith("*")) { - // load from ij.jar - MacroInstaller mi = new MacroInstaller(); - label = label.substring(0, label.length()-1) + ".txt"; - path = "/macros/"+label; - if (IJ.shiftKeyDown()) { - String macros = mi.openFromIJJar(path); - Editor ed = new Editor(); - ed.setSize(350, 300); - ed.create(label, macros); - IJ.setKeyUp(KeyEvent.VK_SHIFT); - } else - mi.installFromIJJar(path); - } else { - // load from ImageJ/macros/toolsets - if (label.equals("Startup Macros")) - path = IJ.getDirectory("macros")+"StartupMacros.txt"; - else if (label.endsWith(" ")) - path = IJ.getDirectory("macros")+"toolsets/"+label.substring(0, label.length()-1)+".ijm"; - else - path = IJ.getDirectory("macros")+"toolsets/"+label+".txt"; - try { - if (IJ.shiftKeyDown()) { - IJ.open(path); - IJ.setKeyUp(KeyEvent.VK_SHIFT); - } else - new MacroInstaller().run(path); - } - catch(Exception ex) {} - } - } - } - - public void actionPerformed(ActionEvent e) { - MenuItem item = (MenuItem)e.getSource(); - String cmd = e.getActionCommand(); - PopupMenu popup = (PopupMenu)item.getParent(); - int tool = -1; - for (int i=SPARE1; i=0 && (toolTip.length()-index)>4; - int tool =-1; - if (names[SPARE1]==null) - tool = SPARE1; - if (tool==-1) { - for (int i=SPARE2; i<=SPARE8; i++) { - if (names[i]==null) { - tool = i; - break; - } - } - } - if (tool==-1) return -1; - if (hasIcon) { - icons[tool] = toolTip.substring(index+1); - if (index>0 && toolTip.charAt(index-1)==' ') - names[tool] = toolTip.substring(0, index-1); - else - names[tool] = toolTip.substring(0, index); - } else { - if (toolTip.endsWith("-")) - toolTip = toolTip.substring(0, toolTip.length()-1); - else if (toolTip.endsWith("- ")) - toolTip = toolTip.substring(0, toolTip.length()-2); - names[tool] = toolTip; - } - if (tool==current && (names[tool].indexOf("Action Tool")!=-1||names[tool].indexOf("Unused Tool")!=-1)) - setTool(RECTANGLE); - if (names[tool].endsWith(" Menu Tool")) - installMenu(tool); - return tool; - } - - void installMenu(int tool) { - Program pgm = macroInstaller.getProgram(); - Hashtable h = pgm.getMenus(); - if (h==null) return; - String[] commands = (String[])h.get(names[tool]); - if (commands==null) return; - if (menus[tool]==null) { - menus[tool] = new PopupMenu(""); - if (Menus.getFontSize()!=0) - menus[tool].setFont(Menus.getFont()); - add(menus[tool] ); - } else - menus[tool].removeAll(); - for (int i=0; i>" + addPopupMenus(); + if (IJ.isMacOSX() || IJ.isVista()) Prefs.antialiasedTools = true; + } + + void addPopupMenus() { + ovalPopup = new PopupMenu(); + if (Menus.getFontSize()!=0) + ovalPopup.setFont(Menus.getFont()); + ovalItem = new CheckboxMenuItem("Elliptical Selection Tool", !brushEnabled); + ovalItem.addItemListener(this); + ovalPopup.add(ovalItem); + brushItem = new CheckboxMenuItem("Selection Brush Tool", brushEnabled); + brushItem.addItemListener(this); + ovalPopup.add(brushItem); + add(ovalPopup); + + linePopup = new PopupMenu(); + if (Menus.getFontSize()!=0) + linePopup.setFont(Menus.getFont()); + straightLineItem = new CheckboxMenuItem("Straight Lines", lineType==LINE); + straightLineItem.addItemListener(this); + linePopup.add(straightLineItem); + polyLineItem = new CheckboxMenuItem("Segmented Lines", lineType==POLYLINE); + polyLineItem.addItemListener(this); + linePopup.add(polyLineItem); + freeLineItem = new CheckboxMenuItem("Freehand Lines", lineType==FREELINE); + freeLineItem.addItemListener(this); + linePopup.add(freeLineItem); + add(linePopup); + + switchPopup = new PopupMenu(); + if (Menus.getFontSize()!=0) + switchPopup.setFont(Menus.getFont()); + add(switchPopup); + } + + /** Returns the ID of the current tool (Toolbar.RECTANGLE, + Toolbar.OVAL, etc.). */ + public static int getToolId() { + return current; + } + + /** Returns the ID of the tool whose name (the description displayed in the status bar) + starts with the specified string, or -1 if the tool is not found. */ + public int getToolId(String name) { + int tool = -1; + for (int i=0; i<=SPARE9; i++) { + if (names[i]!=null && names[i].startsWith(name)) { + tool = i; + break; + } + } + return tool; + } + + /** Returns a reference to the ImageJ toolbar. */ + public static Toolbar getInstance() { + return instance; + } + + private void drawButtons(Graphics g) { + if (Prefs.antialiasedTools) { + Graphics2D g2d = (Graphics2D)g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + } + for (int i=0; i=SPARE1 && tool<=SPARE9 && icons[tool]!=null) { + drawIcon(g, tool, x, y); + return; + } + switch (tool) { + case RECTANGLE: + g.drawRect(x+1, y+2, 15, 12); + return; + case OVAL: + xOffset = x; yOffset = y; + if (brushEnabled) { + m(9,2); d(13,2); d(13,2); d(15,5); d(15,8); + d(13,10); d(10,10); d(8,13); d(4,13); + d(2,11); d(2,7); d(4,5); d(7,5); d(9,2); + } else + g.drawOval(x+1, y+2, 15, 12); + drawTriangle(15,14); + return; + case POLYGON: + xOffset = x+1; yOffset = y+3; + m(4,0); d(14,0); d(14,1); d(10,5); d(10,6); + d(13,9); d(13,10); d(0,10); d(0,4); d(4,0); + return; + case FREEROI: + xOffset = x+1; yOffset = y+3; + m(3,0); d(5,0); d(7,2); d(9,2); d(11,0); d(13,0); d(14,1); d(15,2); + d(15,4); d(14,5); d(14,6); d(12,8); d(11,8); d(10,9); d(9,9); d(8,10); + d(5,10); d(3,8); d(2,8); d(1,7); d(1,6); d(0,5); d(0,2); d(1,1); d(2,1); + return; + case LINE: + xOffset = x; yOffset = y; + m(0,12); d(17,3); + //m(0,10); d(16,4); + drawTriangle(12,14); + return; + case POLYLINE: + xOffset = x; yOffset = y; + m(14,6); d(11,3); d(1,3); d(1,4); d(6,9); d(2,13); + drawTriangle(12,14); + return; + case FREELINE: + xOffset = x; yOffset = y; + m(16,4); d(14,6); d(12,6); d(9,3); d(8,3); d(6,7); d(2,11); d(1,11); + drawTriangle(12,14); + return; + case POINT: + xOffset = x; yOffset = y; + m(1,8); d(6,8); d(6,6); d(10,6); d(10,10); d(6,10); d(6,9); + m(8,1); d(8,5); m(11,8); d(15,8); m(8,11); d(8,15); + m(8,8); d(8,8); + g.setColor(Roi.getColor()); + g.fillRect(x+7, y+7, 3, 3); + return; + case WAND: + xOffset = x+2; yOffset = y+2; + dot(4,0); m(2,0); d(3,1); d(4,2); m(0,0); d(1,1); + m(0,2); d(1,3); d(2,4); dot(0,4); m(3,3); d(12,12); + return; + case TEXT: + xOffset = x+2; yOffset = y+1; + m(0,13); d(3,13); + m(1,12); d(7,0); d(12,13); + m(11,13); d(14,13); + m(3,8); d(10,8); + return; + case MAGNIFIER: + xOffset = x+2; yOffset = y+2; + m(3,0); d(3,0); d(5,0); d(8,3); d(8,5); d(7,6); d(7,7); + d(6,7); d(5,8); d(3,8); d(0,5); d(0,3); d(3,0); + m(8,8); d(9,8); d(13,12); d(13,13); d(12,13); d(8,9); d(8,8); + return; + case HAND: + xOffset = x+1; yOffset = y+1; + m(5,14); d(2,11); d(2,10); d(0,8); d(0,7); d(1,6); d(2,6); d(4,8); + d(4,6); d(3,5); d(3,4); d(2,3); d(2,2); d(3,1); d(4,1); d(5,2); d(5,3); + m(6,5); d(6,1); d(7,0); d(8,0); d(9,1); d(9,5); + m(9,1); d(11,1); d(12,2); d(12,6); + m(13,4); d(14,3); d(15,4); d(15,7); d(14,8); + d(14,10); d(13,11); d(13,12); d(12,13); d(12,14); + return; + case DROPPER: + xOffset = x; yOffset = y; + g.setColor(foregroundColor); + //m(0,0); d(17,0); d(17,17); d(0,17); d(0,0); + m(12,2); d(14,2); + m(11,3); d(15,3); + m(11,4); d(15,4); + m(8,5); d(15,5); + m(9,6); d(14,6); + m(10,7); d(12,7); d(12,9); + m(8,7); d(2,13); d(2,15); d(4,15); d(11,8); + g.setColor(backgroundColor); + m(0,0); d(16,0); d(16,16); d(0,16); d(0,0); + return; + case ANGLE: + xOffset = x+1; yOffset = y+2; + m(0,11); d(11,0); m(0,11); d(15,11); + m(10,11); d(10,8); m(9,7); d(9,6); dot(8,5); + return; + } + } + + void drawTriangle(int x, int y) { + g.setColor(triangleColor); + xOffset+=x; yOffset+=y; + m(0,0); d(4,0); m(1,1); d(3,1); dot(2,2); + } + + void drawIcon(Graphics g, int tool, int x, int y) { + if (null==g) return; + icon = icons[tool]; + this.icon = icon; + int length = icon.length(); + int x1, y1, x2, y2; + pc = 0; + while (true) { + char command = icon.charAt(pc++); + if (pc>=length) break; + switch (command) { + case 'B': x+=v(); y+=v(); break; // reset base + case 'R': g.drawRect(x+v(), y+v(), v(), v()); break; // rectangle + case 'F': g.fillRect(x+v(), y+v(), v(), v()); break; // filled rectangle + case 'O': g.drawOval(x+v(), y+v(), v(), v()); break; // oval + case 'o': g.fillOval(x+v(), y+v(), v(), v()); break; // filled oval + case 'C': g.setColor(new Color(v()*16,v()*16,v()*16)); break; // set color + case 'L': g.drawLine(x+v(), y+v(), x+v(), y+v()); break; // line + case 'D': g.fillRect(x+v(), y+v(), 1, 1); break; // dot + case 'P': // polyline + x1=x+v(); y1=y+v(); + while (true) { + x2=v(); if (x2==0) break; + y2=v(); if (y2==0) break; + x2+=x; y2+=y; + g.drawLine(x1, y1, x2, y2); + x1=x2; y1=y2; + } + break; + case 'T': // text (one character) + x2 = x+v(); + y2 = y+v(); + int size = v()*10+v(); + char[] c = new char[1]; + c[0] = pc=length) break; + } + if (menus[tool]!=null && menus[tool].getItemCount()>0) { + xOffset = x; yOffset = y; + drawTriangle(14, 14); + } + } + + int v() { + if (pc>=icon.length()) return 0; + char c = icon.charAt(pc++); + //IJ.log("v: "+pc+" "+c+" "+toInt(c)); + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': return 10; + case 'b': return 11; + case 'c': return 12; + case 'd': return 13; + case 'e': return 14; + case 'f': return 15; + default: return 0; + } + } + + private void showMessage(int tool) { + if (tool>=SPARE1 && tool<=SPARE9 && names[tool]!=null) { + String name = names[tool]; + int index = name.indexOf("Action Tool"); + if (index!=-1) + name = name.substring(0, index); + else { + index = name.indexOf("Menu Tool"); + if (index!=-1) + name = name.substring(0, index+4); + } + IJ.showStatus(name); + return; + } + switch (tool) { + case RECTANGLE: + IJ.showStatus("Rectangular selections"); + return; + case OVAL: + if (brushEnabled) + IJ.showStatus("Elliptical or *brush* selections"); + else + IJ.showStatus("*Elliptical* or brush selections"); + return; + case POLYGON: + IJ.showStatus("Polygon selections"); + return; + case FREEROI: + IJ.showStatus("Freehand selections"); + return; + case LINE: + IJ.showStatus("Straight line selections (right click for other types)"); + return; + case POLYLINE: + IJ.showStatus("Segmented line selections"); + return; + case FREELINE: + IJ.showStatus("Freehand line selections"); + return; + case POINT: + IJ.showStatus("Point selections (shift click for multiple points)"); + return; + case WAND: + IJ.showStatus("Wand (tracing) tool"); + return; + case TEXT: + IJ.showStatus("Text tool"); + TextRoi.recordSetFont(); + return; + case MAGNIFIER: + IJ.showStatus("Magnifying glass (or use \"+\" and \"-\" keys)"); + return; + case HAND: + IJ.showStatus("Scrolling tool (or press space bar and drag)"); + return; + case DROPPER: + IJ.showStatus("Color picker (" + foregroundColor.getRed() + "," + + foregroundColor.getGreen() + "," + foregroundColor.getBlue() + ")"); + return; + case ANGLE: + IJ.showStatus("Angle tool"); + return; + default: + IJ.showStatus("ImageJ "+IJ.getVersion()+" / Java "+System.getProperty("java.version")+(IJ.is64Bit()?" (64-bit)":" (32-bit)")); + return; + } + } + + private void m(int x, int y) { + this.x = xOffset+x; + this.y = yOffset+y; + } + + private void d(int x, int y) { + x += xOffset; + y += yOffset; + g.drawLine(this.x, this.y, x, y); + this.x = x; + this.y = y; + } + + private void dot(int x, int y) { + g.fillRect(x+xOffset, y+yOffset, 1, 1); + } + + private void resetButtons() { + for (int i=0; i=NUM_TOOLS-1) + return; + if (tool==SPARE1||(tool>=SPARE2&&tool<=SPARE8)) { + if (names[tool]==null) + names[tool] = "Spare tool"; // enable tool + if (names[tool].indexOf("Action Tool")!=-1) + return; + } + if (isLine(tool)) lineType = tool; + setTool2(tool); + } + + private void setTool2(int tool) { + if ((tool==current&&tool!=OVAL) || !isValidTool(tool)) return; + current = tool; + down[current] = true; + down[previous] = false; + Graphics g = this.getGraphics(); + if (Prefs.antialiasedTools) { + Graphics2D g2d = (Graphics2D)g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } + drawButton(g, previous); + drawButton(g, current); + if (null==g) return; + g.dispose(); + showMessage(current); + previous = current; + if (Recorder.record) + Recorder.record("setTool", current); + if (IJ.isMacOSX()) + repaint(); + } + + boolean isValidTool(int tool) { + if (tool<0 || tool>=NUM_TOOLS) + return false; + if ((tool==SPARE1||(tool>=SPARE2&&tool<=SPARE9)) && names[tool]==null) + return false; + return true; + } + + /** Obsolete. Use getForegroundColor(). */ + public Color getColor() { + return foregroundColor; + } + + /** Obsolete. Use setForegroundColor(). */ + public void setColor(Color c) { + if (c!=null) { + foregroundColor = c; + drawButton(this.getGraphics(), DROPPER); + } + } + + public static Color getForegroundColor() { + return foregroundColor; + } + + public static void setForegroundColor(Color c) { + if (c!=null) { + foregroundColor = c; + repaintTool(DROPPER); + } + } + + public static Color getBackgroundColor() { + return backgroundColor; + } + + public static void setBackgroundColor(Color c) { + if (c!=null) { + backgroundColor = c; + repaintTool(DROPPER); + } + } + + /** Returns the size of the brush tool or 0 if the brush tool is not enabled. */ + public static int getBrushSize() { + if (brushEnabled) + return brushSize; + else + return 0; + } + + /** Set the size of the brush tool, which must be greater than 4. */ + public static void setBrushSize(int size) { + brushSize = size; + if (brushSize<5) brushSize = 5; + Prefs.set(BRUSH_SIZE, brushSize); + } + + public static int getButtonSize() { + return SIZE; + } + + static void repaintTool(int tool) { + if (IJ.getInstance()!=null) { + Toolbar tb = getInstance(); + Graphics g = tb.getGraphics(); + if (Prefs.antialiasedTools) + ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + tb.drawButton(g, tool); + if (g!=null) g.dispose(); + } + //Toolbar tb = getInstance(); + //tb.repaint(tool * SIZE , 0, SIZE, SIZE); + } + + // Returns the toolbar position index of the specified tool + int toolIndex(int tool) { + switch (tool) { + case RECTANGLE: return 0; + case OVAL: return 1; + case POLYGON: return 2; + case FREEROI: return 3; + case LINE: return 4; + case POLYLINE: return 4; + case FREELINE: return 4; + case POINT: return 6; + case WAND: return 7; + case TEXT: return 8; + case MAGNIFIER: return 9; + case HAND: return 10; + case DROPPER: return 11; + case ANGLE: return 5; + case SPARE1: return 12; + default: return tool - 2; + } + } + + // Returns the tool corresponding to the specified tool position index + int toolID(int index) { + switch (index) { + case 0: return RECTANGLE; + case 1: return OVAL; + case 2: return POLYGON; + case 3: return FREEROI; + case 4: return lineType; + case 5: return ANGLE; + case 6: return POINT; + case 7: return WAND; + case 8: return TEXT; + case 9: return MAGNIFIER; + case 10: return HAND; + case 11: return DROPPER; + case 12: return SPARE1; + default: return index + 2; + } + } + + public void mousePressed(MouseEvent e) { + int x = e.getX(); + int newTool = 0; + for (int i=0; ii*SIZE && x0) { + menus[newTool].show(e.getComponent(), e.getX(), e.getY()); + return; + } + boolean doubleClick = newTool==current && (System.currentTimeMillis()-mouseDownTime)<=DOUBLE_CLICK_THRESHOLD; + mouseDownTime = System.currentTimeMillis(); + if (!doubleClick) { + mpPrevious = current; + if (isMacroTool(newTool)) { + String name = names[newTool]; + if (name.indexOf("Unused Tool")!=-1) + return; + if (name.indexOf("Action Tool")!=-1) { + if (e.isPopupTrigger()||e.isMetaDown()) { + name = name.endsWith(" ")?name:name+" "; + macroInstaller.runMacroTool(name+"Options"); + } else { + drawTool(newTool, true); + IJ.wait(50); + drawTool(newTool, false); + runMacroTool(newTool); + } + return; + } else { + name = name.endsWith(" ")?name:name+" "; + macroInstaller.runMacroTool(name+"Selected"); + } + } + setTool2(newTool); + boolean isRightClick = e.isPopupTrigger()||e.isMetaDown(); + if (current==OVAL && isRightClick) { + ovalItem.setState(!brushEnabled); + brushItem.setState(brushEnabled); + if (IJ.isMacOSX()) IJ.wait(10); + ovalPopup.show(e.getComponent(),x,y); + mouseDownTime = 0L; + } + if (isLine(current) && isRightClick) { + straightLineItem.setState(lineType==LINE); + polyLineItem.setState(lineType==POLYLINE); + freeLineItem.setState(lineType==FREELINE); + if (IJ.isMacOSX()) IJ.wait(10); + linePopup.show(e.getComponent(),x,y); + mouseDownTime = 0L; + } + if (isMacroTool(current) && isRightClick) { + String name = names[current].endsWith(" ")?names[current]:names[current]+" "; + macroInstaller.runMacroTool(name+"Options"); + } + } else { + if (isMacroTool(current)) { + String name = names[current].endsWith(" ")?names[current]:names[current]+" "; + macroInstaller.runMacroTool(name+"Options"); + return; + } + ImagePlus imp = WindowManager.getCurrentImage(); + switch (current) { + case OVAL: + showBrushDialog(); + break; + case MAGNIFIER: + if (imp!=null) { + ImageCanvas ic = imp.getCanvas(); + if (ic!=null) ic.unzoom(); + } + break; + case LINE: case POLYLINE: case FREELINE: + IJ.runPlugIn("ij.plugin.frame.LineWidthAdjuster", ""); + break; + case POINT: + IJ.doCommand("Point Tool..."); + break; + case WAND: + IJ.doCommand("Wand Tool..."); + break; + case TEXT: + IJ.doCommand("Fonts..."); + break; + case DROPPER: + IJ.doCommand("Color Picker..."); + setTool2(mpPrevious); + break; + default: + } + } + } + + void showSwitchPopupMenu(MouseEvent e) { + String path = IJ.getDirectory("macros")+"toolsets/"; + if (path==null) { + return; + } + boolean applet = IJ.getApplet()!=null; + File f = new File(path); + String[] list; + if (!applet && f.exists() && f.isDirectory()) { + list = f.list(); + if (list==null) return; + } else + list = new String[0]; + boolean stackTools = false; + for (int i=0; i=SPARE1 && tool<=SPARE9 && names[tool]!=null && macroInstaller!=null; + } + + public void mouseReleased(MouseEvent e) {} + public void mouseExited(MouseEvent e) {} + public void mouseClicked(MouseEvent e) {} + public void mouseEntered(MouseEvent e) {} + public void mouseDragged(MouseEvent e) {} + + public void itemStateChanged(ItemEvent e) { + CheckboxMenuItem item = (CheckboxMenuItem)e.getSource(); + if (item==ovalItem || item==brushItem) { + brushEnabled = item==brushItem; + repaintTool(OVAL); + showMessage(OVAL); + } else if (item==straightLineItem) { + lineType = LINE; + setTool2(LINE); + showMessage(LINE); + } else if (item==polyLineItem) { + lineType = POLYLINE; + setTool2(POLYLINE); + showMessage(POLYLINE); + } else if (item==freeLineItem) { + lineType = FREELINE; + setTool2(FREELINE); + showMessage(FREELINE); + } else { + String label = item.getActionCommand(); + if (!label.equals("Help...")) currentSet = label; + String path; + if (label.equals("Help...")) { + IJ.showMessage("Tool Switcher", + "Use this drop down menu to switch to macro tool\n"+ + "sets located in the ImageJ/macros/toolsets folder,\n"+ + "or to revert to the ImageJ/macros/StartupMacros\n"+ + "set. The default tool sets, which have names\n"+ + "ending in '*', are loaded from ij.jar.\n"+ + " \n"+ + "Hold the shift key down while selecting a tool\n"+ + "set to view its source code.\n"+ + " \n"+ + "Several example tool sets are available at\n"+ + "<"+IJ.URL+"/macros/toolsets/>." + ); + return; + } else if (label.endsWith("*")) { + // load from ij.jar + MacroInstaller mi = new MacroInstaller(); + label = label.substring(0, label.length()-1) + ".txt"; + path = "/macros/"+label; + if (IJ.shiftKeyDown()) { + String macros = mi.openFromIJJar(path); + Editor ed = new Editor(); + ed.setSize(350, 300); + ed.create(label, macros); + IJ.setKeyUp(KeyEvent.VK_SHIFT); + } else + mi.installFromIJJar(path); + } else { + // load from ImageJ/macros/toolsets + if (label.equals("Startup Macros")) + path = IJ.getDirectory("macros")+"StartupMacros.txt"; + else if (label.endsWith(" ")) + path = IJ.getDirectory("macros")+"toolsets/"+label.substring(0, label.length()-1)+".ijm"; + else + path = IJ.getDirectory("macros")+"toolsets/"+label+".txt"; + try { + if (IJ.shiftKeyDown()) { + IJ.open(path); + IJ.setKeyUp(KeyEvent.VK_SHIFT); + } else + new MacroInstaller().run(path); + } + catch(Exception ex) {} + } + } + } + + public void actionPerformed(ActionEvent e) { + MenuItem item = (MenuItem)e.getSource(); + String cmd = e.getActionCommand(); + PopupMenu popup = (PopupMenu)item.getParent(); + int tool = -1; + for (int i=SPARE1; i=0 && (toolTip.length()-index)>4; + int tool =-1; + if (names[SPARE1]==null) + tool = SPARE1; + if (tool==-1) { + for (int i=SPARE2; i<=SPARE8; i++) { + if (names[i]==null) { + tool = i; + break; + } + } + } + if (tool==-1) return -1; + if (hasIcon) { + icons[tool] = toolTip.substring(index+1); + if (index>0 && toolTip.charAt(index-1)==' ') + names[tool] = toolTip.substring(0, index-1); + else + names[tool] = toolTip.substring(0, index); + } else { + if (toolTip.endsWith("-")) + toolTip = toolTip.substring(0, toolTip.length()-1); + else if (toolTip.endsWith("- ")) + toolTip = toolTip.substring(0, toolTip.length()-2); + names[tool] = toolTip; + } + if (tool==current && (names[tool].indexOf("Action Tool")!=-1||names[tool].indexOf("Unused Tool")!=-1)) + setTool(RECTANGLE); + if (names[tool].endsWith(" Menu Tool")) + installMenu(tool); + return tool; + } + + void installMenu(int tool) { + Program pgm = macroInstaller.getProgram(); + Hashtable h = pgm.getMenus(); + if (h==null) return; + String[] commands = (String[])h.get(names[tool]); + if (commands==null) return; + if (menus[tool]==null) { + menus[tool] = new PopupMenu(""); + if (Menus.getFontSize()!=0) + menus[tool].setFont(Menus.getFont()); + add(menus[tool] ); + } else + menus[tool].removeAll(); + for (int i=0; i0 and the boundary points can be accessed - * in the public xpoints and ypoints fields. */ - public void autoOutline(int startX, int startY, double lower, double upper, int mode) { - lowerThreshold = (float)lower; - upperThreshold = (float)upper; - autoOutline(startX, startY, 0.0, mode|THRESHOLDED_MODE); - } - - /** Traces an object defined by lower and upper threshold values or an - * interior hole; whatever is found first ('legacy mode'). - * For compatibility with previous versions of ImageJ. - * The start coordinates must be inside the area or left of it. - * When successful, npoints>0 and the boundary points can be accessed - * in the public xpoints and ypoints fields. */ - public void autoOutline(int startX, int startY, double lower, double upper) { - autoOutline(startX, startY, lower, upper, THRESHOLDED_MODE|LEGACY_MODE); - } - - /** This is a variation of legacy autoOutline that uses int threshold arguments. */ - public void autoOutline(int startX, int startY, int lower, int upper) { - autoOutline(startX, startY, (double)lower, (double)upper, THRESHOLDED_MODE|LEGACY_MODE); - } - - /** Traces the boundary of an area of uniform color, where - * 'startX' and 'startY' are somewhere inside the area. - * When successful, npoints>0 and the boundary points can be accessed - * in the public xpoints and ypoints fields. - * For compatibility with previous versions of ImageJ only; otherwise - * use the reliable method specifying 4-connected or 8-connected mode - * and the tolerance. */ - public void autoOutline(int startX, int startY) { - autoOutline(startX, startY, 0.0, LEGACY_MODE); - } - - /** Traces the boundary of the area with pixel values within - * 'tolerance' of the value of the pixel at the starting location. - * 'tolerance' is in uncalibrated units. - * 'mode' can be FOUR_CONNECTED or EIGHT_CONNECTED. - * Mode LEGACY_MODE is for compatibility with previous versions of ImageJ; - * ignored if tolerance > 0. - * Mode bit THRESHOLDED_MODE for internal use only; it is set by autoOutline - * with 'upper' and 'lower' arguments. - * When successful, npoints>0 and the boundary points can be accessed - * in the public xpoints and ypoints fields. */ - public void autoOutline(int startX, int startY, double tolerance, int mode) { - if (startX<0 || startX>=width || startY<0 || startY>=height) return; - if (fpixels!=null && Float.isNaN(getPixel(startX, startY))) return; - exactPixelValue = tolerance==0; - boolean thresholdMode = (mode & THRESHOLDED_MODE) != 0; - boolean legacyMode = (mode & LEGACY_MODE) != 0 && tolerance == 0; - if (!thresholdMode) { - double startValue = getPixel(startX, startY); - lowerThreshold = (float)(startValue - tolerance); - upperThreshold = (float)(startValue + tolerance); - } - int x = startX; - int y = startY; - int seedX; // the first inside pixel - if (inside(x,y)) { // find a border when coming from inside - seedX = x; // (seedX, startY) is an inside pixel - do {x++;} while (inside(x,y)); - } else { // find a border when coming from outside (thresholded only) - do { - x++; - if (x>=width) return; // no border found - } while (!inside(x,y)); - seedX = x; - } - boolean fourConnected; - if (legacyMode) - fourConnected = !thresholdMode && !(isLine(x, y)); - else - fourConnected = (mode & FOUR_CONNECTED) != 0; - //now, we have a border between (x-1, y) and (x,y) - boolean first = true; - while (true) { // loop until we have not traced an inner hole - boolean insideSelected = traceEdge(x, y, fourConnected); - if (legacyMode) return; // in legacy mode, don't care what we have got - if (insideSelected) { // not an inner hole - if (first) return; // started at seed, so we got it (sucessful) - if (xmin<=seedX) { // possibly the correct particle - Polygon poly = new Polygon(xpoints, ypoints, npoints); - if (poly.contains(seedX, startY)) - return; // successful, particle contains seed - } - } - first = false; - // we have traced an inner hole or the wrong particle - if (!inside(x,y)) do { - x++; // traverse the hole - if (x>width) throw new RuntimeException("Wand Malfunction"); //should never happen - } while (!inside(x,y)); - do {x++;} while (inside(x,y)); //retry here; maybe no inner hole any more - } - } - - - /* Trace the outline, starting at a point (startX, startY). - * Pixel (startX-1, startY) must be outside, (startX, startY) must be inside, - * or reverse. Otherwise an endless loop will occur (and eat up all memory). - * Traces 8-connected inside pixels unless fourConnected is true. - * Returns whether the selection created encloses an 'inside' area - * and not an inner hole. - */ - private boolean traceEdge(int startX, int startY, boolean fourConnected) { - // Let us name the crossings between 4 pixels vertices, then the - // vertex (x,y) marked with '+', is between pixels (x-1, y-1) and (x,y): - // - // pixel x-1 x - // y-1 | - // ----+---- - // y | - // - // The four principal directions are numbered such that the direction - // number * 90 degrees gives the angle in the mathematical sense; and - // the directions to the adjacent pixels (for inside(x,y,direction) are - // at (number * 90 - 45) degrees: - // walking pixel - // directions: 1 directions: 2 | 1 - // 2 + 0 ----+---- - // 3 3 | 0 - // - // Directions, like angles, are cyclic; direction -1 = direction 3, etc. - // - // The algorithm: We walk along the border, from one vertex to the next, - // with the outside pixels always being at the left-hand side. - // For 8-connected tracing, we always trying to turn left as much as - // possible, to encompass an area as large as possible. - // Thus, when walking in direction 1 (up, -y), we start looking - // at the pixel in direction 2; if it is inside, we proceed in this - // direction (left); otherwise we try with direction 1 (up); if pixel 1 - // is not inside, we must proceed in direction 0 (right). - // - // 2 | 1 (i=inside, o=outside) - // direction 2 < ---+---- > direction 0 - // o | i - // ^ direction 1 = up = starting direction - // - // For 4-connected pixels, we try to go right as much as possible: - // First try with pixel 1; if it is outside we go in direction 0 (right). - // Otherwise, we examine pixel 2; if it is outside, we go in - // direction 1 (up); otherwise in direction 2 (left). - // - // When moving a closed loop, 'direction' gets incremented or decremented - // by a total of 360 degrees (i.e., 4) for counterclockwise and clockwise - // loops respectively. As the inside pixels are at the right side, we have - // got an outline of inner pixels after a cw loop (direction decremented - // by 4). - // - npoints = 0; - xmin = width; - final int startDirection; - if (inside(startX,startY)) // inside at left, outside right - startDirection = 1; // starting in direction 1 = up - else { - startDirection = 3; // starting in direction 3 = down - startY++; // continue after the boundary that has direction 3 - } - int x = startX; - int y = startY; - int direction = startDirection; - do { - int newDirection; - if (fourConnected) { - newDirection = direction; - do { - if (!inside(x, y, newDirection)) break; - newDirection++; - } while (newDirection < direction+2); - newDirection--; - } else { // 8-connected - newDirection = direction + 1; - do { - if (inside(x, y, newDirection)) break; - newDirection--; - } while (newDirection >= direction); - } - if (allPoints || newDirection!=direction) - addPoint(x,y); // a corner point of the outline polygon: add to list - switch (newDirection & 3) { // '& 3' is remainder modulo 4 - case 0: x++; break; - case 1: y--; break; - case 2: x--; break; - case 3: y++; break; - } - direction = newDirection; - } while (x!=startX || y!=startY || (direction&3)!=startDirection); - if (allPoints || xpoints[0]!=x) // if the start point = end point is a corner: add to list - addPoint(x, y); - return (direction <= 0); // if we have done a clockwise loop, inside pixels are enclosed - } - - // add a point x,y to the outline polygon - private void addPoint (int x, int y) { - if (npoints==maxPoints) { - int[] xtemp = new int[maxPoints*2]; - int[] ytemp = new int[maxPoints*2]; - System.arraycopy(xpoints, 0, xtemp, 0, maxPoints); - System.arraycopy(ypoints, 0, ytemp, 0, maxPoints); - xpoints = xtemp; - ypoints = ytemp; - maxPoints *= 2; - } - xpoints[npoints] = x; - ypoints[npoints] = y; - npoints++; - if (xmin > x) xmin = x; - } - - // check pixel at (x,y), whether it is inside traced area - private boolean inside(int x, int y) { - if (x<0 || x>=width || y<0 || y>=height) - return false; - float value = getPixel(x, y); - return value>=lowerThreshold && value<=upperThreshold; - } - - // check pixel in a given direction from vertex (x,y) - private boolean inside(int x, int y, int direction) { - switch(direction & 3) { // '& 3' is remainder modulo 4 - case 0: return inside(x, y); - case 1: return inside(x, y-1); - case 2: return inside(x-1, y-1); - case 3: return inside(x-1, y); - } - return false; //will never occur, needed for the compiler - } - - // get a pixel value; returns Float.NaN if outside the field. - private float getPixel(int x, int y) { - if (x<0 || x>=width || y<0 || y>=height) - return Float.NaN; - if (bpixels!=null) - return bpixels[y*width + x] & 0xff; - else if (spixels!=null) - return spixels[y*width + x] & 0xffff; - else if (fpixels!=null) - return fpixels[y*width + x]; - else if (exactPixelValue) //RGB for exact match - return cpixels[y*width + x] & 0xffffff; //don't care for upper byte - else //gray value of RGB - return ip.getPixelValue(x,y); - } - - /* Are we tracing a one pixel wide line? Makes Legacy mode 8-connected instead of 4-connected */ - private boolean isLine(int xs, int ys) { - int r = 5; - int xmin=xs; - int xmax=xs+2*r; - if (xmax>=width) xmax=width-1; - int ymin=ys-r; - if (ymin<0) ymin=0; - int ymax=ys+r; - if (ymax>=height) ymax=height-1; - int area = 0; - int insideCount = 0; - for (int x=xmin; (x<=xmax); x++) - for (int y=ymin; y<=ymax; y++) { - area++; - if (inside(x,y)) - insideCount++; - } - if (IJ.debugMode) - IJ.log((((double)insideCount)/area<0.25?"line ":"blob ")+insideCount+" "+area+" "+IJ.d2s(((double)insideCount)/area)); - return ((double)insideCount)/area<0.25; - } - - public static void setAllPoints(boolean b) { - allPoints = b; - } - - public static boolean allPoints() { - return allPoints; - } - +package ij.gui; +import ij.*; +import ij.process.*; +import java.awt.*; + +/** This class implements ImageJ's wand (tracing) tool. + * The wand selects pixels of equal or similar value or thresholded pixels + * forming a contiguous area. + * The wand creates selections that have only one boundary line (inner holes + * are not excluded from the selection). There may be holes at the boundary, + * however, if the boundary line touches the same vertex twice (both in + * 4-connected and 8-connected mode). + * + * Version 2009-06-01 (code refurbished; tolerance, 4- & 8-connected options added) + */ +public class Wand { + /** Wand operation type: trace outline of 4-connected pixels */ + public final static int FOUR_CONNECTED = 4; + /** Wand operation type: trace outline of 8-connected pixels */ + public final static int EIGHT_CONNECTED = 8; + /** Wand operation type similar to that of ImageJ 1.42p and before; for backwards + * compatibility. + * In this mode, no checking is done whether the foreground or the background + * gets selected; four- or 8-connected behaviour depends on foreground/background + * and (if no selection) on whether the initial pixel is on a 1-pixel wide line. */ + public final static int LEGACY_MODE = 1; + /** The number of points in the generated outline. */ + public int npoints; + private int maxPoints = 1000; // will be increased if necessary + /** The x-coordinates of the points in the outline. + A vertical boundary at x separates the pixels at x-1 and x. */ + public int[] xpoints = new int[maxPoints]; + /** The y-coordinates of the points in the outline. + A horizontal boundary at y separates the pixels at y-1 and y. */ + public int[] ypoints = new int[maxPoints]; + + private final static int THRESHOLDED_MODE = 256; //work on threshold + private ImageProcessor ip; + private byte[] bpixels; + private int[] cpixels; + private short[] spixels; + private float[] fpixels; + private int width, height; + private float lowerThreshold, upperThreshold; + private int xmin; //of selection created + private boolean exactPixelValue; //For color, match RGB, not gray value + private static boolean allPoints; + + + /** Constructs a Wand object from an ImageProcessor. */ + public Wand(ImageProcessor ip) { + this.ip = ip; + if (ip instanceof ByteProcessor) + bpixels = (byte[])ip.getPixels(); + else if (ip instanceof ColorProcessor) + cpixels = (int[])ip.getPixels(); + else if (ip instanceof ShortProcessor) + spixels = (short[])ip.getPixels(); + else if (ip instanceof FloatProcessor) + fpixels = (float[])ip.getPixels(); + width = ip.getWidth(); + height = ip.getHeight(); + } + + + + /** Traces an object defined by lower and upper threshold values. + * 'mode' can be FOUR_CONNECTED or EIGHT_CONNECTED. + * ('LEGACY_MODE' is also supported and may result in selection of + * interior holes instead of the thresholded area if one clicks left + * of an interior hole). + * The start coordinates must be inside the area or left of it. + * When successful, npoints>0 and the boundary points can be accessed + * in the public xpoints and ypoints fields. */ + public void autoOutline(int startX, int startY, double lower, double upper, int mode) { + lowerThreshold = (float)lower; + upperThreshold = (float)upper; + autoOutline(startX, startY, 0.0, mode|THRESHOLDED_MODE); + } + + /** Traces an object defined by lower and upper threshold values or an + * interior hole; whatever is found first ('legacy mode'). + * For compatibility with previous versions of ImageJ. + * The start coordinates must be inside the area or left of it. + * When successful, npoints>0 and the boundary points can be accessed + * in the public xpoints and ypoints fields. */ + public void autoOutline(int startX, int startY, double lower, double upper) { + autoOutline(startX, startY, lower, upper, THRESHOLDED_MODE|LEGACY_MODE); + } + + /** This is a variation of legacy autoOutline that uses int threshold arguments. */ + public void autoOutline(int startX, int startY, int lower, int upper) { + autoOutline(startX, startY, (double)lower, (double)upper, THRESHOLDED_MODE|LEGACY_MODE); + } + + /** Traces the boundary of an area of uniform color, where + * 'startX' and 'startY' are somewhere inside the area. + * When successful, npoints>0 and the boundary points can be accessed + * in the public xpoints and ypoints fields. + * For compatibility with previous versions of ImageJ only; otherwise + * use the reliable method specifying 4-connected or 8-connected mode + * and the tolerance. */ + public void autoOutline(int startX, int startY) { + autoOutline(startX, startY, 0.0, LEGACY_MODE); + } + + /** Traces the boundary of the area with pixel values within + * 'tolerance' of the value of the pixel at the starting location. + * 'tolerance' is in uncalibrated units. + * 'mode' can be FOUR_CONNECTED or EIGHT_CONNECTED. + * Mode LEGACY_MODE is for compatibility with previous versions of ImageJ; + * ignored if tolerance > 0. + * Mode bit THRESHOLDED_MODE for internal use only; it is set by autoOutline + * with 'upper' and 'lower' arguments. + * When successful, npoints>0 and the boundary points can be accessed + * in the public xpoints and ypoints fields. */ + public void autoOutline(int startX, int startY, double tolerance, int mode) { + if (startX<0 || startX>=width || startY<0 || startY>=height) return; + if (fpixels!=null && Float.isNaN(getPixel(startX, startY))) return; + exactPixelValue = tolerance==0; + boolean thresholdMode = (mode & THRESHOLDED_MODE) != 0; + boolean legacyMode = (mode & LEGACY_MODE) != 0 && tolerance == 0; + if (!thresholdMode) { + double startValue = getPixel(startX, startY); + lowerThreshold = (float)(startValue - tolerance); + upperThreshold = (float)(startValue + tolerance); + } + int x = startX; + int y = startY; + int seedX; // the first inside pixel + if (inside(x,y)) { // find a border when coming from inside + seedX = x; // (seedX, startY) is an inside pixel + do {x++;} while (inside(x,y)); + } else { // find a border when coming from outside (thresholded only) + do { + x++; + if (x>=width) return; // no border found + } while (!inside(x,y)); + seedX = x; + } + boolean fourConnected; + if (legacyMode) + fourConnected = !thresholdMode && !(isLine(x, y)); + else + fourConnected = (mode & FOUR_CONNECTED) != 0; + //now, we have a border between (x-1, y) and (x,y) + boolean first = true; + while (true) { // loop until we have not traced an inner hole + boolean insideSelected = traceEdge(x, y, fourConnected); + if (legacyMode) return; // in legacy mode, don't care what we have got + if (insideSelected) { // not an inner hole + if (first) return; // started at seed, so we got it (sucessful) + if (xmin<=seedX) { // possibly the correct particle + Polygon poly = new Polygon(xpoints, ypoints, npoints); + if (poly.contains(seedX, startY)) + return; // successful, particle contains seed + } + } + first = false; + // we have traced an inner hole or the wrong particle + if (!inside(x,y)) do { + x++; // traverse the hole + if (x>width) throw new RuntimeException("Wand Malfunction"); //should never happen + } while (!inside(x,y)); + do {x++;} while (inside(x,y)); //retry here; maybe no inner hole any more + } + } + + + /* Trace the outline, starting at a point (startX, startY). + * Pixel (startX-1, startY) must be outside, (startX, startY) must be inside, + * or reverse. Otherwise an endless loop will occur (and eat up all memory). + * Traces 8-connected inside pixels unless fourConnected is true. + * Returns whether the selection created encloses an 'inside' area + * and not an inner hole. + */ + private boolean traceEdge(int startX, int startY, boolean fourConnected) { + // Let us name the crossings between 4 pixels vertices, then the + // vertex (x,y) marked with '+', is between pixels (x-1, y-1) and (x,y): + // + // pixel x-1 x + // y-1 | + // ----+---- + // y | + // + // The four principal directions are numbered such that the direction + // number * 90 degrees gives the angle in the mathematical sense; and + // the directions to the adjacent pixels (for inside(x,y,direction) are + // at (number * 90 - 45) degrees: + // walking pixel + // directions: 1 directions: 2 | 1 + // 2 + 0 ----+---- + // 3 3 | 0 + // + // Directions, like angles, are cyclic; direction -1 = direction 3, etc. + // + // The algorithm: We walk along the border, from one vertex to the next, + // with the outside pixels always being at the left-hand side. + // For 8-connected tracing, we always trying to turn left as much as + // possible, to encompass an area as large as possible. + // Thus, when walking in direction 1 (up, -y), we start looking + // at the pixel in direction 2; if it is inside, we proceed in this + // direction (left); otherwise we try with direction 1 (up); if pixel 1 + // is not inside, we must proceed in direction 0 (right). + // + // 2 | 1 (i=inside, o=outside) + // direction 2 < ---+---- > direction 0 + // o | i + // ^ direction 1 = up = starting direction + // + // For 4-connected pixels, we try to go right as much as possible: + // First try with pixel 1; if it is outside we go in direction 0 (right). + // Otherwise, we examine pixel 2; if it is outside, we go in + // direction 1 (up); otherwise in direction 2 (left). + // + // When moving a closed loop, 'direction' gets incremented or decremented + // by a total of 360 degrees (i.e., 4) for counterclockwise and clockwise + // loops respectively. As the inside pixels are at the right side, we have + // got an outline of inner pixels after a cw loop (direction decremented + // by 4). + // + npoints = 0; + xmin = width; + final int startDirection; + if (inside(startX,startY)) // inside at left, outside right + startDirection = 1; // starting in direction 1 = up + else { + startDirection = 3; // starting in direction 3 = down + startY++; // continue after the boundary that has direction 3 + } + int x = startX; + int y = startY; + int direction = startDirection; + do { + int newDirection; + if (fourConnected) { + newDirection = direction; + do { + if (!inside(x, y, newDirection)) break; + newDirection++; + } while (newDirection < direction+2); + newDirection--; + } else { // 8-connected + newDirection = direction + 1; + do { + if (inside(x, y, newDirection)) break; + newDirection--; + } while (newDirection >= direction); + } + if (allPoints || newDirection!=direction) + addPoint(x,y); // a corner point of the outline polygon: add to list + switch (newDirection & 3) { // '& 3' is remainder modulo 4 + case 0: x++; break; + case 1: y--; break; + case 2: x--; break; + case 3: y++; break; + } + direction = newDirection; + } while (x!=startX || y!=startY || (direction&3)!=startDirection); + if (allPoints || xpoints[0]!=x) // if the start point = end point is a corner: add to list + addPoint(x, y); + return (direction <= 0); // if we have done a clockwise loop, inside pixels are enclosed + } + + // add a point x,y to the outline polygon + private void addPoint (int x, int y) { + if (npoints==maxPoints) { + int[] xtemp = new int[maxPoints*2]; + int[] ytemp = new int[maxPoints*2]; + System.arraycopy(xpoints, 0, xtemp, 0, maxPoints); + System.arraycopy(ypoints, 0, ytemp, 0, maxPoints); + xpoints = xtemp; + ypoints = ytemp; + maxPoints *= 2; + } + xpoints[npoints] = x; + ypoints[npoints] = y; + npoints++; + if (xmin > x) xmin = x; + } + + // check pixel at (x,y), whether it is inside traced area + private boolean inside(int x, int y) { + if (x<0 || x>=width || y<0 || y>=height) + return false; + float value = getPixel(x, y); + return value>=lowerThreshold && value<=upperThreshold; + } + + // check pixel in a given direction from vertex (x,y) + private boolean inside(int x, int y, int direction) { + switch(direction & 3) { // '& 3' is remainder modulo 4 + case 0: return inside(x, y); + case 1: return inside(x, y-1); + case 2: return inside(x-1, y-1); + case 3: return inside(x-1, y); + } + return false; //will never occur, needed for the compiler + } + + // get a pixel value; returns Float.NaN if outside the field. + private float getPixel(int x, int y) { + if (x<0 || x>=width || y<0 || y>=height) + return Float.NaN; + if (bpixels!=null) + return bpixels[y*width + x] & 0xff; + else if (spixels!=null) + return spixels[y*width + x] & 0xffff; + else if (fpixels!=null) + return fpixels[y*width + x]; + else if (exactPixelValue) //RGB for exact match + return cpixels[y*width + x] & 0xffffff; //don't care for upper byte + else //gray value of RGB + return ip.getPixelValue(x,y); + } + + /* Are we tracing a one pixel wide line? Makes Legacy mode 8-connected instead of 4-connected */ + private boolean isLine(int xs, int ys) { + int r = 5; + int xmin=xs; + int xmax=xs+2*r; + if (xmax>=width) xmax=width-1; + int ymin=ys-r; + if (ymin<0) ymin=0; + int ymax=ys+r; + if (ymax>=height) ymax=height-1; + int area = 0; + int insideCount = 0; + for (int x=xmin; (x<=xmax); x++) + for (int y=ymin; y<=ymax; y++) { + area++; + if (inside(x,y)) + insideCount++; + } + if (IJ.debugMode) + IJ.log((((double)insideCount)/area<0.25?"line ":"blob ")+insideCount+" "+area+" "+IJ.d2s(((double)insideCount)/area)); + return ((double)insideCount)/area<0.25; + } + + public static void setAllPoints(boolean b) { + allPoints = b; + } + + public static boolean allPoints() { + return allPoints; + } + } \ No newline at end of file diff --git a/ij/gui/YesNoCancelDialog.java b/ij/gui/YesNoCancelDialog.java index 287b19e87..e35ebbee5 100644 --- a/ij/gui/YesNoCancelDialog.java +++ b/ij/gui/YesNoCancelDialog.java @@ -1,109 +1,109 @@ -package ij.gui; -import ij.IJ; -import java.awt.*; -import java.awt.event.*; - -/** A modal dialog box with a one line message and - "Yes", "No" and "Cancel" buttons. */ -public class YesNoCancelDialog extends Dialog implements ActionListener, KeyListener { - private Button yesB, noB, cancelB; - private boolean cancelPressed, yesPressed; - private boolean firstPaint = true; - - public YesNoCancelDialog(Frame parent, String title, String msg) { - super(parent, title, true); - setLayout(new BorderLayout()); - Panel panel = new Panel(); - panel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10)); - MultiLineLabel message = new MultiLineLabel(msg); - message.setFont(new Font("Dialog", Font.PLAIN, 12)); - panel.add(message); - add("North", panel); - - panel = new Panel(); - panel.setLayout(new FlowLayout(FlowLayout.RIGHT, 15, 8)); - if (IJ.isMacintosh() && msg.startsWith("Save")) { - yesB = new Button(" Save "); - noB = new Button("Don't Save"); - cancelB = new Button(" Cancel "); - } else { - yesB = new Button(" Yes "); - noB = new Button(" No "); - cancelB = new Button(" Cancel "); - } - yesB.addActionListener(this); - noB.addActionListener(this); - cancelB.addActionListener(this); - yesB.addKeyListener(this); - noB.addKeyListener(this); - cancelB.addKeyListener(this); - if (IJ.isMacintosh()) { - panel.add(noB); - panel.add(cancelB); - panel.add(yesB); - setResizable(false); - } else { - panel.add(yesB); - panel.add(noB); - panel.add(cancelB); - } - add("South", panel); - pack(); - GUI.center(this); - show(); - } - - public void actionPerformed(ActionEvent e) { - if (e.getSource()==cancelB) - cancelPressed = true; - else if (e.getSource()==yesB) - yesPressed = true; - closeDialog(); - } - - /** Returns true if the user dismissed dialog by pressing "Cancel". */ - public boolean cancelPressed() { - return cancelPressed; - } - - /** Returns true if the user dismissed dialog by pressing "Yes". */ - public boolean yesPressed() { - return yesPressed; - } - - void closeDialog() { - setVisible(false); - dispose(); - } - - public void keyPressed(KeyEvent e) { - int keyCode = e.getKeyCode(); - IJ.setKeyDown(keyCode); - if (keyCode==KeyEvent.VK_ENTER||keyCode==KeyEvent.VK_Y||keyCode==KeyEvent.VK_S) { - yesPressed = true; - closeDialog(); - } else if (keyCode==KeyEvent.VK_N || keyCode==KeyEvent.VK_D) { - closeDialog(); - } else if (keyCode==KeyEvent.VK_ESCAPE||keyCode==KeyEvent.VK_C) { - cancelPressed = true; - closeDialog(); - IJ.resetEscape(); - } - } - - public void keyReleased(KeyEvent e) { - int keyCode = e.getKeyCode(); - IJ.setKeyUp(keyCode); - } - - public void keyTyped(KeyEvent e) {} - - public void paint(Graphics g) { - super.paint(g); - if (firstPaint) { - yesB.requestFocus(); - firstPaint = false; - } - } - +package ij.gui; +import ij.IJ; +import java.awt.*; +import java.awt.event.*; + +/** A modal dialog box with a one line message and + "Yes", "No" and "Cancel" buttons. */ +public class YesNoCancelDialog extends Dialog implements ActionListener, KeyListener { + private Button yesB, noB, cancelB; + private boolean cancelPressed, yesPressed; + private boolean firstPaint = true; + + public YesNoCancelDialog(Frame parent, String title, String msg) { + super(parent, title, true); + setLayout(new BorderLayout()); + Panel panel = new Panel(); + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10)); + MultiLineLabel message = new MultiLineLabel(msg); + message.setFont(new Font("Dialog", Font.PLAIN, 12)); + panel.add(message); + add("North", panel); + + panel = new Panel(); + panel.setLayout(new FlowLayout(FlowLayout.RIGHT, 15, 8)); + if (IJ.isMacintosh() && msg.startsWith("Save")) { + yesB = new Button(" Save "); + noB = new Button("Don't Save"); + cancelB = new Button(" Cancel "); + } else { + yesB = new Button(" Yes "); + noB = new Button(" No "); + cancelB = new Button(" Cancel "); + } + yesB.addActionListener(this); + noB.addActionListener(this); + cancelB.addActionListener(this); + yesB.addKeyListener(this); + noB.addKeyListener(this); + cancelB.addKeyListener(this); + if (IJ.isMacintosh()) { + panel.add(noB); + panel.add(cancelB); + panel.add(yesB); + setResizable(false); + } else { + panel.add(yesB); + panel.add(noB); + panel.add(cancelB); + } + add("South", panel); + pack(); + GUI.center(this); + show(); + } + + public void actionPerformed(ActionEvent e) { + if (e.getSource()==cancelB) + cancelPressed = true; + else if (e.getSource()==yesB) + yesPressed = true; + closeDialog(); + } + + /** Returns true if the user dismissed dialog by pressing "Cancel". */ + public boolean cancelPressed() { + return cancelPressed; + } + + /** Returns true if the user dismissed dialog by pressing "Yes". */ + public boolean yesPressed() { + return yesPressed; + } + + void closeDialog() { + setVisible(false); + dispose(); + } + + public void keyPressed(KeyEvent e) { + int keyCode = e.getKeyCode(); + IJ.setKeyDown(keyCode); + if (keyCode==KeyEvent.VK_ENTER||keyCode==KeyEvent.VK_Y||keyCode==KeyEvent.VK_S) { + yesPressed = true; + closeDialog(); + } else if (keyCode==KeyEvent.VK_N || keyCode==KeyEvent.VK_D) { + closeDialog(); + } else if (keyCode==KeyEvent.VK_ESCAPE||keyCode==KeyEvent.VK_C) { + cancelPressed = true; + closeDialog(); + IJ.resetEscape(); + } + } + + public void keyReleased(KeyEvent e) { + int keyCode = e.getKeyCode(); + IJ.setKeyUp(keyCode); + } + + public void keyTyped(KeyEvent e) {} + + public void paint(Graphics g) { + super.paint(g); + if (firstPaint) { + yesB.requestFocus(); + firstPaint = false; + } + } + } \ No newline at end of file diff --git a/ij/io/DirectoryChooser.java b/ij/io/DirectoryChooser.java index 3dcd39383..1ee9c37a0 100644 --- a/ij/io/DirectoryChooser.java +++ b/ij/io/DirectoryChooser.java @@ -1,124 +1,124 @@ -package ij.io; -import ij.*; -import ij.gui.*; -import ij.plugin.frame.Recorder; -import ij.util.Java2; -import java.awt.*; -import java.io.*; -import javax.swing.*; -import javax.swing.filechooser.*; - -/** This class displays a dialog box that allows the user can select a directory. */ - public class DirectoryChooser { - private String directory; - private static String defaultDir; - private String title; - - /** Display a dialog using the specified title. */ - public DirectoryChooser(String title) { - this.title = title; - if (IJ.isMacOSX()) - getDirectoryUsingFileDialog(title); - else { - String macroOptions = Macro.getOptions(); - if (macroOptions!=null) - directory = Macro.getValue(macroOptions, title, null); - if (directory==null) { - if (EventQueue.isDispatchThread()) - getDirectoryUsingJFileChooserOnThisThread(title); - else - getDirectoryUsingJFileChooser(title); - } - } - } - - // runs JFileChooser on event dispatch thread to avoid possible thread deadlocks - void getDirectoryUsingJFileChooser(final String title) { - Java2.setSystemLookAndFeel(); - try { - EventQueue.invokeAndWait(new Runnable() { - public void run() { - JFileChooser chooser = new JFileChooser(); - if (defaultDir!=null) - chooser.setCurrentDirectory(new File(defaultDir)); - chooser.setDialogTitle(title); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setApproveButtonText("Select"); - if (chooser.showOpenDialog(null)==JFileChooser.APPROVE_OPTION) { - File dir = chooser.getCurrentDirectory(); - File file = chooser.getSelectedFile(); - directory = dir.getPath(); - if (!directory.endsWith(File.separator)) - directory += File.separator; - if (directory!=null) - defaultDir = (new File(directory)).getParent(); - String fileName = file.getName(); - if (fileName.indexOf(":\\")!=-1) - directory = defaultDir = fileName; - else - directory += fileName+File.separator; - } - } - }); - } catch (Exception e) {} - } - - // Choose a directory using JFileChooser on the current thread - void getDirectoryUsingJFileChooserOnThisThread(final String title) { - Java2.setSystemLookAndFeel(); - try { - JFileChooser chooser = new JFileChooser(); - if (defaultDir!=null) { - chooser.setCurrentDirectory(new File(defaultDir)); - } - chooser.setDialogTitle(title); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setApproveButtonText("Select"); - if (chooser.showOpenDialog(null)==JFileChooser.APPROVE_OPTION) { - File dir = chooser.getCurrentDirectory(); - File file = chooser.getSelectedFile(); - directory = dir.getPath(); - if (!directory.endsWith(File.separator)) - directory += File.separator; - if (directory!=null) - defaultDir = (new File(directory)).getParent(); - String fileName = file.getName(); - if (fileName.indexOf(":\\")!=-1) - directory = defaultDir = fileName; - else - directory += fileName+File.separator; - } - } catch (Exception e) {} - } - - // On Mac OS X, we can select directories using the native file open dialog - void getDirectoryUsingFileDialog(String title) { - boolean saveUseJFC = Prefs.useJFileChooser; - Prefs.useJFileChooser = false; - System.setProperty("apple.awt.fileDialogForDirectories", "true"); - OpenDialog od = new OpenDialog(title, defaultDir, null); - if (od.getDirectory()==null) - directory = null; - else - directory = od.getDirectory() + od.getFileName() + "/"; - if (directory!=null) - defaultDir = (new File(directory)).getParent(); - System.setProperty("apple.awt.fileDialogForDirectories", "false"); - Prefs.useJFileChooser = saveUseJFC; - } - - /** Returns the directory selected by the user. */ - public String getDirectory() { - //IJ.log("getDirectory: "+directory); - if (Recorder.record && !IJ.isMacOSX()) - Recorder.recordPath(title, directory); - return directory; - } - - /** Sets the default directory presented in the dialog. */ - public static void setDefaultDirectory(String dir) { - if (dir==null || (new File(dir)).isDirectory()) - defaultDir = dir; - } - -} +package ij.io; +import ij.*; +import ij.gui.*; +import ij.plugin.frame.Recorder; +import ij.util.Java2; +import java.awt.*; +import java.io.*; +import javax.swing.*; +import javax.swing.filechooser.*; + +/** This class displays a dialog box that allows the user can select a directory. */ + public class DirectoryChooser { + private String directory; + private static String defaultDir; + private String title; + + /** Display a dialog using the specified title. */ + public DirectoryChooser(String title) { + this.title = title; + if (IJ.isMacOSX()) + getDirectoryUsingFileDialog(title); + else { + String macroOptions = Macro.getOptions(); + if (macroOptions!=null) + directory = Macro.getValue(macroOptions, title, null); + if (directory==null) { + if (EventQueue.isDispatchThread()) + getDirectoryUsingJFileChooserOnThisThread(title); + else + getDirectoryUsingJFileChooser(title); + } + } + } + + // runs JFileChooser on event dispatch thread to avoid possible thread deadlocks + void getDirectoryUsingJFileChooser(final String title) { + Java2.setSystemLookAndFeel(); + try { + EventQueue.invokeAndWait(new Runnable() { + public void run() { + JFileChooser chooser = new JFileChooser(); + if (defaultDir!=null) + chooser.setCurrentDirectory(new File(defaultDir)); + chooser.setDialogTitle(title); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setApproveButtonText("Select"); + if (chooser.showOpenDialog(null)==JFileChooser.APPROVE_OPTION) { + File dir = chooser.getCurrentDirectory(); + File file = chooser.getSelectedFile(); + directory = dir.getPath(); + if (!directory.endsWith(File.separator)) + directory += File.separator; + if (directory!=null) + defaultDir = (new File(directory)).getParent(); + String fileName = file.getName(); + if (fileName.indexOf(":\\")!=-1) + directory = defaultDir = fileName; + else + directory += fileName+File.separator; + } + } + }); + } catch (Exception e) {} + } + + // Choose a directory using JFileChooser on the current thread + void getDirectoryUsingJFileChooserOnThisThread(final String title) { + Java2.setSystemLookAndFeel(); + try { + JFileChooser chooser = new JFileChooser(); + if (defaultDir!=null) { + chooser.setCurrentDirectory(new File(defaultDir)); + } + chooser.setDialogTitle(title); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setApproveButtonText("Select"); + if (chooser.showOpenDialog(null)==JFileChooser.APPROVE_OPTION) { + File dir = chooser.getCurrentDirectory(); + File file = chooser.getSelectedFile(); + directory = dir.getPath(); + if (!directory.endsWith(File.separator)) + directory += File.separator; + if (directory!=null) + defaultDir = (new File(directory)).getParent(); + String fileName = file.getName(); + if (fileName.indexOf(":\\")!=-1) + directory = defaultDir = fileName; + else + directory += fileName+File.separator; + } + } catch (Exception e) {} + } + + // On Mac OS X, we can select directories using the native file open dialog + void getDirectoryUsingFileDialog(String title) { + boolean saveUseJFC = Prefs.useJFileChooser; + Prefs.useJFileChooser = false; + System.setProperty("apple.awt.fileDialogForDirectories", "true"); + OpenDialog od = new OpenDialog(title, defaultDir, null); + if (od.getDirectory()==null) + directory = null; + else + directory = od.getDirectory() + od.getFileName() + "/"; + if (directory!=null) + defaultDir = (new File(directory)).getParent(); + System.setProperty("apple.awt.fileDialogForDirectories", "false"); + Prefs.useJFileChooser = saveUseJFC; + } + + /** Returns the directory selected by the user. */ + public String getDirectory() { + //IJ.log("getDirectory: "+directory); + if (Recorder.record && !IJ.isMacOSX()) + Recorder.recordPath(title, directory); + return directory; + } + + /** Sets the default directory presented in the dialog. */ + public static void setDefaultDirectory(String dir) { + if (dir==null || (new File(dir)).isDirectory()) + defaultDir = dir; + } + +} diff --git a/ij/io/FileInfo.java b/ij/io/FileInfo.java index 509f32ff9..0f402fde8 100644 --- a/ij/io/FileInfo.java +++ b/ij/io/FileInfo.java @@ -1,213 +1,213 @@ -package ij.io; -import java.io.*; -import java.util.Properties; - -/** This class consists of public fields that describe an image file. */ -public class FileInfo implements Cloneable { - - /** 8-bit unsigned integer (0-255). */ - public static final int GRAY8 = 0; - - /** 16-bit signed integer (-32768-32767). Imported signed images - are converted to unsigned by adding 32768. */ - public static final int GRAY16_SIGNED = 1; - - /** 16-bit unsigned integer (0-65535). */ - public static final int GRAY16_UNSIGNED = 2; - - /** 32-bit signed integer. Imported 32-bit integer images are - converted to floating-point. */ - public static final int GRAY32_INT = 3; - - /** 32-bit floating-point. */ - public static final int GRAY32_FLOAT = 4; - - /** 8-bit unsigned integer with color lookup table. */ - public static final int COLOR8 = 5; - - /** 24-bit interleaved RGB. Import/export only. */ - public static final int RGB = 6; - - /** 24-bit planer RGB. Import only. */ - public static final int RGB_PLANAR = 7; - - /** 1-bit black and white. Import only. */ - public static final int BITMAP = 8; - - /** 32-bit interleaved ARGB. Import only. */ - public static final int ARGB = 9; - - /** 24-bit interleaved BGR. Import only. */ - public static final int BGR = 10; - - /** 32-bit unsigned integer. Imported 32-bit integer images are - converted to floating-point. */ - public static final int GRAY32_UNSIGNED = 11; - - /** 48-bit interleaved RGB. */ - public static final int RGB48 = 12; - - /** 12-bit unsigned integer (0-4095). Import only. */ - public static final int GRAY12_UNSIGNED = 13; - - /** 24-bit unsigned integer. Import only. */ - public static final int GRAY24_UNSIGNED = 14; - - /** 32-bit interleaved BARG (MCID). Import only. */ - public static final int BARG = 15; - - /** 64-bit floating-point. Import only.*/ - public static final int GRAY64_FLOAT = 16; - - /** 48-bit planar RGB. Import only. */ - public static final int RGB48_PLANAR = 17; - - /** 32-bit interleaved ABGR. Import only. */ - public static final int ABGR = 18; - - // File formats - public static final int UNKNOWN = 0; - public static final int RAW = 1; - public static final int TIFF = 2; - public static final int GIF_OR_JPG = 3; - public static final int FITS = 4; - public static final int BMP = 5; - public static final int DICOM = 6; - public static final int ZIP_ARCHIVE = 7; - public static final int PGM = 8; - public static final int IMAGEIO = 9; - - // Compression modes - public static final int COMPRESSION_UNKNOWN = 0; - public static final int COMPRESSION_NONE= 1; - public static final int LZW = 2; - public static final int LZW_WITH_DIFFERENCING = 3; - public static final int JPEG = 4; - public static final int PACK_BITS = 5; - - /* File format (TIFF, GIF_OR_JPG, BMP, etc.). Used by the File/Revert command */ - public int fileFormat; - - /* File type (GRAY8, GRAY_16_UNSIGNED, RGB, etc.) */ - public int fileType; - - public String fileName; - public String directory; - public String url; - public int width; - public int height; - public int offset=0; // Use getOffset() to read - public int nImages; - public int gapBetweenImages; - public boolean whiteIsZero; - public boolean intelByteOrder; - public int compression; - public int[] stripOffsets; - public int[] stripLengths; - public int rowsPerStrip; - public int lutSize; - public byte[] reds; - public byte[] greens; - public byte[] blues; - public Object pixels; - public String debugInfo; - public String[] sliceLabels; - public String info; - public InputStream inputStream; - - public double pixelWidth=1.0; - public double pixelHeight=1.0; - public double pixelDepth=1.0; - public String unit; - public int calibrationFunction; - public double[] coefficients; - public String valueUnit; - public double frameInterval; - public String description; - // Use longOffset instead of offset when offset>2147483647. - public long longOffset; // Use getOffset() to read - // Extra metadata to be stored in the TIFF header - public int[] metaDataTypes; // must be < 0xffffff - public byte[][] metaData; - public double[] displayRanges; - public byte[][] channelLuts; - public int samplesPerPixel; - - /** Creates a FileInfo object with all of its fields set to their default value. */ - public FileInfo() { - // assign default values - fileFormat = UNKNOWN; - fileType = GRAY8; - fileName = "Untitled"; - directory = ""; - url = ""; - nImages = 1; - compression = COMPRESSION_NONE; - samplesPerPixel = 1; - } - - /** Returns the offset as a long. */ - public final long getOffset() { - return longOffset>0L?longOffset:((long)offset)&0xffffffffL; - } - - /** Returns the number of bytes used per pixel. */ - public int getBytesPerPixel() { - switch (fileType) { - case GRAY8: case COLOR8: case BITMAP: return 1; - case GRAY16_SIGNED: case GRAY16_UNSIGNED: return 2; - case GRAY32_INT: case GRAY32_UNSIGNED: case GRAY32_FLOAT: case ARGB: case GRAY24_UNSIGNED: case BARG: case ABGR:return 4; - case RGB: case RGB_PLANAR: case BGR: return 3; - case RGB48: case RGB48_PLANAR: return 6; - case GRAY64_FLOAT : return 8; - default: return 0; - } - } - - public String toString() { - return - "name=" + fileName - + ", dir=" + directory - + ", url=" + url - + ", width=" + width - + ", height=" + height - + ", nImages=" + nImages - + ", type=" + getType() - + ", offset=" + getOffset() - + ", whiteZero=" + (whiteIsZero?"t":"f") - + ", Intel=" + (intelByteOrder?"t":"f") - + ", lutSize=" + lutSize - + ", comp=" + compression - + ", ranges=" + (displayRanges!=null?""+displayRanges.length/2:"null") - + ", samples=" + samplesPerPixel; - } - - private String getType() { - switch (fileType) { - case GRAY8: return "byte"; - case GRAY16_SIGNED: return "short"; - case GRAY16_UNSIGNED: return "ushort"; - case GRAY32_INT: return "int"; - case GRAY32_UNSIGNED: return "uint"; - case GRAY32_FLOAT: return "float"; - case COLOR8: return "byte+lut"; - case RGB: return "RGB"; - case RGB_PLANAR: return "RGB(p)"; - case RGB48: return "RGB48"; - case BITMAP: return "bitmap"; - case ARGB: return "ARGB"; - case ABGR: return "ABGR"; - case BGR: return "BGR"; - case BARG: return "BARG"; - case GRAY64_FLOAT: return "double"; - case RGB48_PLANAR: return "RGB48(p)"; - default: return ""; - } - } - - public synchronized Object clone() { - try {return super.clone();} - catch (CloneNotSupportedException e) {return null;} - } - -} +package ij.io; +import java.io.*; +import java.util.Properties; + +/** This class consists of public fields that describe an image file. */ +public class FileInfo implements Cloneable { + + /** 8-bit unsigned integer (0-255). */ + public static final int GRAY8 = 0; + + /** 16-bit signed integer (-32768-32767). Imported signed images + are converted to unsigned by adding 32768. */ + public static final int GRAY16_SIGNED = 1; + + /** 16-bit unsigned integer (0-65535). */ + public static final int GRAY16_UNSIGNED = 2; + + /** 32-bit signed integer. Imported 32-bit integer images are + converted to floating-point. */ + public static final int GRAY32_INT = 3; + + /** 32-bit floating-point. */ + public static final int GRAY32_FLOAT = 4; + + /** 8-bit unsigned integer with color lookup table. */ + public static final int COLOR8 = 5; + + /** 24-bit interleaved RGB. Import/export only. */ + public static final int RGB = 6; + + /** 24-bit planer RGB. Import only. */ + public static final int RGB_PLANAR = 7; + + /** 1-bit black and white. Import only. */ + public static final int BITMAP = 8; + + /** 32-bit interleaved ARGB. Import only. */ + public static final int ARGB = 9; + + /** 24-bit interleaved BGR. Import only. */ + public static final int BGR = 10; + + /** 32-bit unsigned integer. Imported 32-bit integer images are + converted to floating-point. */ + public static final int GRAY32_UNSIGNED = 11; + + /** 48-bit interleaved RGB. */ + public static final int RGB48 = 12; + + /** 12-bit unsigned integer (0-4095). Import only. */ + public static final int GRAY12_UNSIGNED = 13; + + /** 24-bit unsigned integer. Import only. */ + public static final int GRAY24_UNSIGNED = 14; + + /** 32-bit interleaved BARG (MCID). Import only. */ + public static final int BARG = 15; + + /** 64-bit floating-point. Import only.*/ + public static final int GRAY64_FLOAT = 16; + + /** 48-bit planar RGB. Import only. */ + public static final int RGB48_PLANAR = 17; + + /** 32-bit interleaved ABGR. Import only. */ + public static final int ABGR = 18; + + // File formats + public static final int UNKNOWN = 0; + public static final int RAW = 1; + public static final int TIFF = 2; + public static final int GIF_OR_JPG = 3; + public static final int FITS = 4; + public static final int BMP = 5; + public static final int DICOM = 6; + public static final int ZIP_ARCHIVE = 7; + public static final int PGM = 8; + public static final int IMAGEIO = 9; + + // Compression modes + public static final int COMPRESSION_UNKNOWN = 0; + public static final int COMPRESSION_NONE= 1; + public static final int LZW = 2; + public static final int LZW_WITH_DIFFERENCING = 3; + public static final int JPEG = 4; + public static final int PACK_BITS = 5; + + /* File format (TIFF, GIF_OR_JPG, BMP, etc.). Used by the File/Revert command */ + public int fileFormat; + + /* File type (GRAY8, GRAY_16_UNSIGNED, RGB, etc.) */ + public int fileType; + + public String fileName; + public String directory; + public String url; + public int width; + public int height; + public int offset=0; // Use getOffset() to read + public int nImages; + public int gapBetweenImages; + public boolean whiteIsZero; + public boolean intelByteOrder; + public int compression; + public int[] stripOffsets; + public int[] stripLengths; + public int rowsPerStrip; + public int lutSize; + public byte[] reds; + public byte[] greens; + public byte[] blues; + public Object pixels; + public String debugInfo; + public String[] sliceLabels; + public String info; + public InputStream inputStream; + + public double pixelWidth=1.0; + public double pixelHeight=1.0; + public double pixelDepth=1.0; + public String unit; + public int calibrationFunction; + public double[] coefficients; + public String valueUnit; + public double frameInterval; + public String description; + // Use longOffset instead of offset when offset>2147483647. + public long longOffset; // Use getOffset() to read + // Extra metadata to be stored in the TIFF header + public int[] metaDataTypes; // must be < 0xffffff + public byte[][] metaData; + public double[] displayRanges; + public byte[][] channelLuts; + public int samplesPerPixel; + + /** Creates a FileInfo object with all of its fields set to their default value. */ + public FileInfo() { + // assign default values + fileFormat = UNKNOWN; + fileType = GRAY8; + fileName = "Untitled"; + directory = ""; + url = ""; + nImages = 1; + compression = COMPRESSION_NONE; + samplesPerPixel = 1; + } + + /** Returns the offset as a long. */ + public final long getOffset() { + return longOffset>0L?longOffset:((long)offset)&0xffffffffL; + } + + /** Returns the number of bytes used per pixel. */ + public int getBytesPerPixel() { + switch (fileType) { + case GRAY8: case COLOR8: case BITMAP: return 1; + case GRAY16_SIGNED: case GRAY16_UNSIGNED: return 2; + case GRAY32_INT: case GRAY32_UNSIGNED: case GRAY32_FLOAT: case ARGB: case GRAY24_UNSIGNED: case BARG: case ABGR:return 4; + case RGB: case RGB_PLANAR: case BGR: return 3; + case RGB48: case RGB48_PLANAR: return 6; + case GRAY64_FLOAT : return 8; + default: return 0; + } + } + + public String toString() { + return + "name=" + fileName + + ", dir=" + directory + + ", url=" + url + + ", width=" + width + + ", height=" + height + + ", nImages=" + nImages + + ", type=" + getType() + + ", offset=" + getOffset() + + ", whiteZero=" + (whiteIsZero?"t":"f") + + ", Intel=" + (intelByteOrder?"t":"f") + + ", lutSize=" + lutSize + + ", comp=" + compression + + ", ranges=" + (displayRanges!=null?""+displayRanges.length/2:"null") + + ", samples=" + samplesPerPixel; + } + + private String getType() { + switch (fileType) { + case GRAY8: return "byte"; + case GRAY16_SIGNED: return "short"; + case GRAY16_UNSIGNED: return "ushort"; + case GRAY32_INT: return "int"; + case GRAY32_UNSIGNED: return "uint"; + case GRAY32_FLOAT: return "float"; + case COLOR8: return "byte+lut"; + case RGB: return "RGB"; + case RGB_PLANAR: return "RGB(p)"; + case RGB48: return "RGB48"; + case BITMAP: return "bitmap"; + case ARGB: return "ARGB"; + case ABGR: return "ABGR"; + case BGR: return "BGR"; + case BARG: return "BARG"; + case GRAY64_FLOAT: return "double"; + case RGB48_PLANAR: return "RGB48(p)"; + default: return ""; + } + } + + public synchronized Object clone() { + try {return super.clone();} + catch (CloneNotSupportedException e) {return null;} + } + +} diff --git a/ij/io/FileOpener.java b/ij/io/FileOpener.java index 805b39f1e..acfeae4df 100644 --- a/ij/io/FileOpener.java +++ b/ij/io/FileOpener.java @@ -1,578 +1,578 @@ -package ij.io; -import java.awt.*; -import java.awt.image.*; -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.zip.GZIPInputStream; -import ij.gui.*; -import ij.process.*; -import ij.measure.*; -import ij.*; - -/** - * Opens or reverts an image specified by a FileInfo object. Images can - * be loaded from either a file (directory+fileName) or a URL (url+fileName). - * Here is an example: - *
- *   public class FileInfo_Test implements PlugIn {
- *     public void run(String arg) {
- *       FileInfo fi = new FileInfo();
- *       fi.width = 256;
- *       fi.height = 254;
- *       fi.offset = 768;
- *       fi.fileName = "blobs.tif";
- *       fi.directory = "/Users/wayne/Desktop/";
- *       new FileOpener(fi).open();
- *     }  
- *   }	
- * 
- */ -public class FileOpener { - - private FileInfo fi; - private int width, height; - private static boolean showConflictMessage = true; - private double minValue, maxValue; - private static boolean silentMode; - - public FileOpener(FileInfo fi) { - this.fi = fi; - if (fi!=null) { - width = fi.width; - height = fi.height; - } - if (IJ.debugMode) IJ.log("FileInfo: "+fi); - } - - /** Opens the image and displays it. */ - public void open() { - open(true); - } - - /** Opens the image. Displays it if 'show' is - true. Returns an ImagePlus object if successful. */ - public ImagePlus open(boolean show) { - ImagePlus imp=null; - Object pixels; - ProgressBar pb=null; - ImageProcessor ip; - - ColorModel cm = createColorModel(fi); - if (fi.nImages>1) - {return openStack(cm, show);} - switch (fi.fileType) { - case FileInfo.GRAY8: - case FileInfo.COLOR8: - case FileInfo.BITMAP: - pixels = readPixels(fi); - if (pixels==null) return null; - ip = new ByteProcessor(width, height, (byte[])pixels, cm); - imp = new ImagePlus(fi.fileName, ip); - break; - case FileInfo.GRAY16_SIGNED: - case FileInfo.GRAY16_UNSIGNED: - case FileInfo.GRAY12_UNSIGNED: - pixels = readPixels(fi); - if (pixels==null) return null; - ip = new ShortProcessor(width, height, (short[])pixels, cm); - imp = new ImagePlus(fi.fileName, ip); - break; - case FileInfo.GRAY32_INT: - case FileInfo.GRAY32_UNSIGNED: - case FileInfo.GRAY32_FLOAT: - case FileInfo.GRAY24_UNSIGNED: - case FileInfo.GRAY64_FLOAT: - pixels = readPixels(fi); - if (pixels==null) return null; - ip = new FloatProcessor(width, height, (float[])pixels, cm); - imp = new ImagePlus(fi.fileName, ip); - break; - case FileInfo.RGB: - case FileInfo.BGR: - case FileInfo.ARGB: - case FileInfo.ABGR: - case FileInfo.BARG: - case FileInfo.RGB_PLANAR: - pixels = readPixels(fi); - if (pixels==null) return null; - ip = new ColorProcessor(width, height, (int[])pixels); - imp = new ImagePlus(fi.fileName, ip); - break; - case FileInfo.RGB48: - case FileInfo.RGB48_PLANAR: - boolean planar = fi.fileType==FileInfo.RGB48_PLANAR; - Object[] pixelArray = (Object[])readPixels(fi); - if (pixelArray==null) return null; - ImageStack stack = new ImageStack(width, height); - stack.addSlice("Red", pixelArray[0]); - stack.addSlice("Green", pixelArray[1]); - stack.addSlice("Blue", pixelArray[2]); - imp = new ImagePlus(fi.fileName, stack); - imp.setDimensions(3, 1, 1); - if (planar) - imp.getProcessor().resetMinAndMax(); - imp.setFileInfo(fi); - int mode = CompositeImage.COMPOSITE; - if (fi.description!=null) { - if (fi.description.indexOf("mode=color")!=-1) - mode = CompositeImage.COLOR; - else if (fi.description.indexOf("mode=gray")!=-1) - mode = CompositeImage.GRAYSCALE; - } - imp = new CompositeImage(imp, mode); - if (!planar && fi.displayRanges==null) { - for (int c=1; c<=3; c++) { - imp.setPosition(c, 1, 1); - imp.setDisplayRange(minValue, maxValue); - } - imp.setPosition(1, 1, 1); - } - break; - } - imp.setFileInfo(fi); - setCalibration(imp); - if (fi.info!=null) - imp.setProperty("Info", fi.info); - if (fi.sliceLabels!=null&&fi.sliceLabels.length==1&&fi.sliceLabels[0]!=null) - imp.setProperty("Label", fi.sliceLabels[0]); - if (show) imp.show(); - return imp; - } - - /** Opens a stack of images. */ - ImagePlus openStack(ColorModel cm, boolean show) { - ImageStack stack = new ImageStack(fi.width, fi.height, cm); - long skip = fi.getOffset(); - Object pixels; - try { - ImageReader reader = new ImageReader(fi); - InputStream is = createInputStream(fi); - if (is==null) return null; - IJ.resetEscape(); - for (int i=1; i<=fi.nImages; i++) { - if (!silentMode) - IJ.showStatus("Reading: " + i + "/" + fi.nImages); - if (IJ.escapePressed()) { - IJ.beep(); - IJ.showProgress(1.0); - silentMode = false; - return null; - } - pixels = reader.readPixels(is, skip); - if (pixels==null) break; - stack.addSlice(null, pixels); - skip = fi.gapBetweenImages; - if (!silentMode) - IJ.showProgress(i, fi.nImages); - } - is.close(); - } - catch (Exception e) { - IJ.log("" + e); - } - catch(OutOfMemoryError e) { - IJ.outOfMemory(fi.fileName); - stack.trim(); - } - if (!silentMode) IJ.showProgress(1.0); - if (stack.getSize()==0) - return null; - if (fi.sliceLabels!=null && fi.sliceLabels.length<=stack.getSize()) { - for (int i=0; imax) - max = ip.getMax(); - } - imp.getProcessor().setMinAndMax(min, max); - imp.updateAndDraw(); - } - - /** Restores original disk or network version of image. */ - public void revertToSaved(ImagePlus imp) { - Image img; - ProgressBar pb = IJ.getInstance().getProgressBar(); - ImageProcessor ip; - String path = fi.directory + fi.fileName; - - if (fi.fileFormat==fi.GIF_OR_JPG) { - // restore gif or jpg - img = Toolkit.getDefaultToolkit().createImage(path); - imp.setImage(img); - if (imp.getType()==ImagePlus.COLOR_RGB) - Opener.convertGrayJpegTo8Bits(imp); - return; - } - - if (fi.fileFormat==fi.DICOM) { - // restore DICOM - ImagePlus imp2 = (ImagePlus)IJ.runPlugIn("ij.plugin.DICOM", path); - if (imp2!=null) - imp.setProcessor(null, imp2.getProcessor()); - return; - } - - if (fi.fileFormat==fi.BMP) { - // restore BMP - ImagePlus imp2 = (ImagePlus)IJ.runPlugIn("ij.plugin.BMP_Reader", path); - if (imp2!=null) - imp.setProcessor(null, imp2.getProcessor()); - return; - } - - if (fi.fileFormat==fi.PGM) { - // restore PGM - ImagePlus imp2 = (ImagePlus)IJ.runPlugIn("ij.plugin.PGM_Reader", path); - if (imp2!=null) - imp.setProcessor(null, imp2.getProcessor()); - return; - } - - if (fi.fileFormat==fi.ZIP_ARCHIVE) { - // restore ".zip" file - ImagePlus imp2 = (new Opener()).openZip(path); - if (imp2!=null) - imp.setProcessor(null, imp2.getProcessor()); - return; - } - - // restore PNG or another image opened using ImageIO - if (fi.fileFormat==fi.IMAGEIO) { - ImagePlus imp2 = (new Opener()).openUsingImageIO(path); - if (imp2!=null) imp.setProcessor(null, imp2.getProcessor()); - return; - } - - if (fi.nImages>1) - return; - - ColorModel cm; - if (fi.url==null || fi.url.equals("")) - IJ.showStatus("Loading: " + path); - else - IJ.showStatus("Loading: " + fi.url + fi.fileName); - Object pixels = readPixels(fi); - if (pixels==null) return; - cm = createColorModel(fi); - switch (fi.fileType) { - case FileInfo.GRAY8: - case FileInfo.COLOR8: - case FileInfo.BITMAP: - ip = new ByteProcessor(width, height, (byte[])pixels, cm); - imp.setProcessor(null, ip); - break; - case FileInfo.GRAY16_SIGNED: - case FileInfo.GRAY16_UNSIGNED: - case FileInfo.GRAY12_UNSIGNED: - ip = new ShortProcessor(width, height, (short[])pixels, cm); - imp.setProcessor(null, ip); - break; - case FileInfo.GRAY32_INT: - case FileInfo.GRAY32_FLOAT: - ip = new FloatProcessor(width, height, (float[])pixels, cm); - imp.setProcessor(null, ip); - break; - case FileInfo.RGB: - case FileInfo.BGR: - case FileInfo.ARGB: - case FileInfo.ABGR: - case FileInfo.RGB_PLANAR: - img = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(width, height, (int[])pixels, 0, width)); - imp.setImage(img); - break; - } - } - - void setCalibration(ImagePlus imp) { - if (fi.fileType==FileInfo.GRAY16_SIGNED) { - if (IJ.debugMode) IJ.log("16-bit signed"); - double[] coeff = new double[2]; - coeff[0] = -32768.0; - coeff[1] = 1.0; - imp.getLocalCalibration().setFunction(Calibration.STRAIGHT_LINE, coeff, "gray value"); - } - - Properties props = decodeDescriptionString(fi); - Calibration cal = imp.getCalibration(); - boolean calibrated = false; - if (fi.pixelWidth>0.0 && fi.unit!=null) { - cal.pixelWidth = fi.pixelWidth; - cal.pixelHeight = fi.pixelHeight; - cal.pixelDepth = fi.pixelDepth; - cal.setUnit(fi.unit); - calibrated = true; - } - - if (fi.valueUnit!=null) { - int f = fi.calibrationFunction; - if ((f>=Calibration.STRAIGHT_LINE && f<=Calibration.RODBARD2 && fi.coefficients!=null) - || f==Calibration.UNCALIBRATED_OD) { - boolean zeroClip = props!=null && props.getProperty("zeroclip", "false").equals("true"); - cal.setFunction(f, fi.coefficients, fi.valueUnit, zeroClip); - calibrated = true; - } - } - - if (calibrated) - checkForCalibrationConflict(imp, cal); - - if (fi.frameInterval!=0.0) - cal.frameInterval = fi.frameInterval; - - if (props==null) - return; - - cal.xOrigin = getDouble(props,"xorigin"); - cal.yOrigin = getDouble(props,"yorigin"); - cal.zOrigin = getDouble(props,"zorigin"); - cal.info = props.getProperty("info"); - - cal.fps = getDouble(props,"fps"); - cal.loop = getBoolean(props, "loop"); - cal.frameInterval = getDouble(props,"finterval"); - cal.setTimeUnit(props.getProperty("tunit", "sec")); - - double displayMin = getDouble(props,"min"); - double displayMax = getDouble(props,"max"); - if (!(displayMin==0.0&&displayMax==0.0)) { - int type = imp.getType(); - ImageProcessor ip = imp.getProcessor(); - if (type==ImagePlus.GRAY8 || type==ImagePlus.COLOR_256) - ip.setMinAndMax(displayMin, displayMax); - else if (type==ImagePlus.GRAY16 || type==ImagePlus.GRAY32) { - if (ip.getMin()!=displayMin || ip.getMax()!=displayMax) - ip.setMinAndMax(displayMin, displayMax); - } - } - - int stackSize = imp.getStackSize(); - if (stackSize>1) { - int channels = (int)getDouble(props,"channels"); - int slices = (int)getDouble(props,"slices"); - int frames = (int)getDouble(props,"frames"); - if (channels==0) channels = 1; - if (slices==0) slices = 1; - if (frames==0) frames = 1; - //IJ.log("setCalibration: "+channels+" "+slices+" "+frames); - if (channels*slices*frames==stackSize) { - imp.setDimensions(channels, slices, frames); - if (getBoolean(props, "hyperstack")) - imp.setOpenAsHyperStack(true); - } - } - } - - - void checkForCalibrationConflict(ImagePlus imp, Calibration cal) { - Calibration gcal = imp.getGlobalCalibration(); - if (gcal==null || !showConflictMessage || IJ.isMacro()) - return; - if (cal.pixelWidth==gcal.pixelWidth && cal.getUnit().equals(gcal.getUnit())) - return; - GenericDialog gd = new GenericDialog(imp.getTitle()); - gd.addMessage("The calibration of this image conflicts\nwith the current global calibration."); - gd.addCheckbox("Disable_Global Calibration", true); - gd.addCheckbox("Disable_these Messages", false); - gd.showDialog(); - if (gd.wasCanceled()) return; - boolean disable = gd.getNextBoolean(); - if (disable) { - imp.setGlobalCalibration(null); - imp.setCalibration(cal); - WindowManager.repaintImageWindows(); - } - boolean dontShow = gd.getNextBoolean(); - if (dontShow) showConflictMessage = false; - } - - /** Returns an IndexColorModel for the image specified by this FileInfo. */ - public ColorModel createColorModel(FileInfo fi) { - if (fi.fileType==FileInfo.COLOR8 && fi.lutSize>0) - return new IndexColorModel(8, fi.lutSize, fi.reds, fi.greens, fi.blues); - else - return LookUpTable.createGrayscaleColorModel(fi.whiteIsZero); - } - - /** Returns an InputStream for the image described by this FileInfo. */ - public InputStream createInputStream(FileInfo fi) throws IOException, MalformedURLException { - InputStream is = null; - boolean gzip = fi.fileName!=null && (fi.fileName.endsWith(".gz")||fi.fileName.endsWith(".GZ")); - if (fi.inputStream!=null) - is = fi.inputStream; - else if (fi.url!=null && !fi.url.equals("")) - is = new URL(fi.url+fi.fileName).openStream(); - else { - if (fi.directory.length()>0 && !fi.directory.endsWith(Prefs.separator)) - fi.directory += Prefs.separator; - File f = new File(fi.directory + fi.fileName); - if (gzip) fi.compression = FileInfo.COMPRESSION_UNKNOWN; - if (f==null || f.isDirectory() || !validateFileInfo(f, fi)) - is = null; - else - is = new FileInputStream(f); - } - if (is!=null) { - if (fi.compression>=FileInfo.LZW) - is = new RandomAccessStream(is); - else if (gzip) - is = new GZIPInputStream(is, 50000); - } - return is; - } - - static boolean validateFileInfo(File f, FileInfo fi) { - long offset = fi.getOffset(); - long length = 0; - if (fi.width<=0 || fi.height<0) { - error("Width or height <= 0.", fi, offset, length); - return false; - } - if (offset>=0 && offset<1000L) - return true; - if (offset<0L) { - error("Offset is negative.", fi, offset, length); - return false; - } - if (fi.fileType==FileInfo.BITMAP || fi.compression!=FileInfo.COMPRESSION_NONE) - return true; - length = f.length(); - long size = fi.width*fi.height*fi.getBytesPerPixel(); - size = fi.nImages>1?size:size/4; - if (fi.height==1) size = 0; // allows plugins to read info of unknown length at end of file - if (offset+size>length) { - error("Offset + image size > file length.", fi, offset, length); - return false; - } - return true; - } - - static void error(String msg, FileInfo fi, long offset, long length) { - IJ.error("FileOpener", "FileInfo parameter error. \n" - +msg + "\n \n" - +" Width: " + fi.width + "\n" - +" Height: " + fi.height + "\n" - +" Offset: " + offset + "\n" - +" Bytes/pixel: " + fi.getBytesPerPixel() + "\n" - +(length>0?" File length: " + length + "\n":"") - ); - } - - - /** Reads the pixel data from an image described by a FileInfo object. */ - Object readPixels(FileInfo fi) { - Object pixels = null; - try { - InputStream is = createInputStream(fi); - if (is==null) - return null; - ImageReader reader = new ImageReader(fi); - pixels = reader.readPixels(is); - minValue = reader.min; - maxValue = reader.max; - is.close(); - } - catch (Exception e) { - if (!Macro.MACRO_CANCELED.equals(e.getMessage())) - IJ.handleException(e); - } - return pixels; - } - - public Properties decodeDescriptionString(FileInfo fi) { - if (fi.description==null || fi.description.length()<7) - return null; - if (IJ.debugMode) - IJ.log("Image Description: " + new String(fi.description).replace('\n',' ')); - if (!fi.description.startsWith("ImageJ")) - return null; - Properties props = new Properties(); - InputStream is = new ByteArrayInputStream(fi.description.getBytes()); - try {props.load(is); is.close();} - catch (IOException e) {return null;} - fi.unit = props.getProperty("unit",""); - Double n = getNumber(props,"cf"); - if (n!=null) fi.calibrationFunction = n.intValue(); - double c[] = new double[5]; - int count = 0; - for (int i=0; i<5; i++) { - n = getNumber(props,"c"+i); - if (n==null) break; - c[i] = n.doubleValue(); - count++; - } - if (count>=2) { - fi.coefficients = new double[count]; - for (int i=0; i1.0) - fi.nImages = (int)n.doubleValue(); - if (fi.nImages>1) { - double spacing = getDouble(props,"spacing"); - if (spacing!=0.0) { - if (spacing<0) spacing = -spacing; - fi.pixelDepth = spacing; - } - } - return props; - } - - private Double getNumber(Properties props, String key) { - String s = props.getProperty(key); - if (s!=null) { - try { - return Double.valueOf(s); - } catch (NumberFormatException e) {} - } - return null; - } - - private double getDouble(Properties props, String key) { - Double n = getNumber(props, key); - return n!=null?n.doubleValue():0.0; - } - - private boolean getBoolean(Properties props, String key) { - String s = props.getProperty(key); - return s!=null&&s.equals("true")?true:false; - } - - public static void setShowConflictMessage(boolean b) { - showConflictMessage = b; - } - - static void setSilentMode(boolean mode) { - silentMode = mode; - } - - +package ij.io; +import java.awt.*; +import java.awt.image.*; +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.zip.GZIPInputStream; +import ij.gui.*; +import ij.process.*; +import ij.measure.*; +import ij.*; + +/** + * Opens or reverts an image specified by a FileInfo object. Images can + * be loaded from either a file (directory+fileName) or a URL (url+fileName). + * Here is an example: + *
+ *   public class FileInfo_Test implements PlugIn {
+ *     public void run(String arg) {
+ *       FileInfo fi = new FileInfo();
+ *       fi.width = 256;
+ *       fi.height = 254;
+ *       fi.offset = 768;
+ *       fi.fileName = "blobs.tif";
+ *       fi.directory = "/Users/wayne/Desktop/";
+ *       new FileOpener(fi).open();
+ *     }  
+ *   }	
+ * 
+ */ +public class FileOpener { + + private FileInfo fi; + private int width, height; + private static boolean showConflictMessage = true; + private double minValue, maxValue; + private static boolean silentMode; + + public FileOpener(FileInfo fi) { + this.fi = fi; + if (fi!=null) { + width = fi.width; + height = fi.height; + } + if (IJ.debugMode) IJ.log("FileInfo: "+fi); + } + + /** Opens the image and displays it. */ + public void open() { + open(true); + } + + /** Opens the image. Displays it if 'show' is + true. Returns an ImagePlus object if successful. */ + public ImagePlus open(boolean show) { + ImagePlus imp=null; + Object pixels; + ProgressBar pb=null; + ImageProcessor ip; + + ColorModel cm = createColorModel(fi); + if (fi.nImages>1) + {return openStack(cm, show);} + switch (fi.fileType) { + case FileInfo.GRAY8: + case FileInfo.COLOR8: + case FileInfo.BITMAP: + pixels = readPixels(fi); + if (pixels==null) return null; + ip = new ByteProcessor(width, height, (byte[])pixels, cm); + imp = new ImagePlus(fi.fileName, ip); + break; + case FileInfo.GRAY16_SIGNED: + case FileInfo.GRAY16_UNSIGNED: + case FileInfo.GRAY12_UNSIGNED: + pixels = readPixels(fi); + if (pixels==null) return null; + ip = new ShortProcessor(width, height, (short[])pixels, cm); + imp = new ImagePlus(fi.fileName, ip); + break; + case FileInfo.GRAY32_INT: + case FileInfo.GRAY32_UNSIGNED: + case FileInfo.GRAY32_FLOAT: + case FileInfo.GRAY24_UNSIGNED: + case FileInfo.GRAY64_FLOAT: + pixels = readPixels(fi); + if (pixels==null) return null; + ip = new FloatProcessor(width, height, (float[])pixels, cm); + imp = new ImagePlus(fi.fileName, ip); + break; + case FileInfo.RGB: + case FileInfo.BGR: + case FileInfo.ARGB: + case FileInfo.ABGR: + case FileInfo.BARG: + case FileInfo.RGB_PLANAR: + pixels = readPixels(fi); + if (pixels==null) return null; + ip = new ColorProcessor(width, height, (int[])pixels); + imp = new ImagePlus(fi.fileName, ip); + break; + case FileInfo.RGB48: + case FileInfo.RGB48_PLANAR: + boolean planar = fi.fileType==FileInfo.RGB48_PLANAR; + Object[] pixelArray = (Object[])readPixels(fi); + if (pixelArray==null) return null; + ImageStack stack = new ImageStack(width, height); + stack.addSlice("Red", pixelArray[0]); + stack.addSlice("Green", pixelArray[1]); + stack.addSlice("Blue", pixelArray[2]); + imp = new ImagePlus(fi.fileName, stack); + imp.setDimensions(3, 1, 1); + if (planar) + imp.getProcessor().resetMinAndMax(); + imp.setFileInfo(fi); + int mode = CompositeImage.COMPOSITE; + if (fi.description!=null) { + if (fi.description.indexOf("mode=color")!=-1) + mode = CompositeImage.COLOR; + else if (fi.description.indexOf("mode=gray")!=-1) + mode = CompositeImage.GRAYSCALE; + } + imp = new CompositeImage(imp, mode); + if (!planar && fi.displayRanges==null) { + for (int c=1; c<=3; c++) { + imp.setPosition(c, 1, 1); + imp.setDisplayRange(minValue, maxValue); + } + imp.setPosition(1, 1, 1); + } + break; + } + imp.setFileInfo(fi); + setCalibration(imp); + if (fi.info!=null) + imp.setProperty("Info", fi.info); + if (fi.sliceLabels!=null&&fi.sliceLabels.length==1&&fi.sliceLabels[0]!=null) + imp.setProperty("Label", fi.sliceLabels[0]); + if (show) imp.show(); + return imp; + } + + /** Opens a stack of images. */ + ImagePlus openStack(ColorModel cm, boolean show) { + ImageStack stack = new ImageStack(fi.width, fi.height, cm); + long skip = fi.getOffset(); + Object pixels; + try { + ImageReader reader = new ImageReader(fi); + InputStream is = createInputStream(fi); + if (is==null) return null; + IJ.resetEscape(); + for (int i=1; i<=fi.nImages; i++) { + if (!silentMode) + IJ.showStatus("Reading: " + i + "/" + fi.nImages); + if (IJ.escapePressed()) { + IJ.beep(); + IJ.showProgress(1.0); + silentMode = false; + return null; + } + pixels = reader.readPixels(is, skip); + if (pixels==null) break; + stack.addSlice(null, pixels); + skip = fi.gapBetweenImages; + if (!silentMode) + IJ.showProgress(i, fi.nImages); + } + is.close(); + } + catch (Exception e) { + IJ.log("" + e); + } + catch(OutOfMemoryError e) { + IJ.outOfMemory(fi.fileName); + stack.trim(); + } + if (!silentMode) IJ.showProgress(1.0); + if (stack.getSize()==0) + return null; + if (fi.sliceLabels!=null && fi.sliceLabels.length<=stack.getSize()) { + for (int i=0; imax) + max = ip.getMax(); + } + imp.getProcessor().setMinAndMax(min, max); + imp.updateAndDraw(); + } + + /** Restores original disk or network version of image. */ + public void revertToSaved(ImagePlus imp) { + Image img; + ProgressBar pb = IJ.getInstance().getProgressBar(); + ImageProcessor ip; + String path = fi.directory + fi.fileName; + + if (fi.fileFormat==fi.GIF_OR_JPG) { + // restore gif or jpg + img = Toolkit.getDefaultToolkit().createImage(path); + imp.setImage(img); + if (imp.getType()==ImagePlus.COLOR_RGB) + Opener.convertGrayJpegTo8Bits(imp); + return; + } + + if (fi.fileFormat==fi.DICOM) { + // restore DICOM + ImagePlus imp2 = (ImagePlus)IJ.runPlugIn("ij.plugin.DICOM", path); + if (imp2!=null) + imp.setProcessor(null, imp2.getProcessor()); + return; + } + + if (fi.fileFormat==fi.BMP) { + // restore BMP + ImagePlus imp2 = (ImagePlus)IJ.runPlugIn("ij.plugin.BMP_Reader", path); + if (imp2!=null) + imp.setProcessor(null, imp2.getProcessor()); + return; + } + + if (fi.fileFormat==fi.PGM) { + // restore PGM + ImagePlus imp2 = (ImagePlus)IJ.runPlugIn("ij.plugin.PGM_Reader", path); + if (imp2!=null) + imp.setProcessor(null, imp2.getProcessor()); + return; + } + + if (fi.fileFormat==fi.ZIP_ARCHIVE) { + // restore ".zip" file + ImagePlus imp2 = (new Opener()).openZip(path); + if (imp2!=null) + imp.setProcessor(null, imp2.getProcessor()); + return; + } + + // restore PNG or another image opened using ImageIO + if (fi.fileFormat==fi.IMAGEIO) { + ImagePlus imp2 = (new Opener()).openUsingImageIO(path); + if (imp2!=null) imp.setProcessor(null, imp2.getProcessor()); + return; + } + + if (fi.nImages>1) + return; + + ColorModel cm; + if (fi.url==null || fi.url.equals("")) + IJ.showStatus("Loading: " + path); + else + IJ.showStatus("Loading: " + fi.url + fi.fileName); + Object pixels = readPixels(fi); + if (pixels==null) return; + cm = createColorModel(fi); + switch (fi.fileType) { + case FileInfo.GRAY8: + case FileInfo.COLOR8: + case FileInfo.BITMAP: + ip = new ByteProcessor(width, height, (byte[])pixels, cm); + imp.setProcessor(null, ip); + break; + case FileInfo.GRAY16_SIGNED: + case FileInfo.GRAY16_UNSIGNED: + case FileInfo.GRAY12_UNSIGNED: + ip = new ShortProcessor(width, height, (short[])pixels, cm); + imp.setProcessor(null, ip); + break; + case FileInfo.GRAY32_INT: + case FileInfo.GRAY32_FLOAT: + ip = new FloatProcessor(width, height, (float[])pixels, cm); + imp.setProcessor(null, ip); + break; + case FileInfo.RGB: + case FileInfo.BGR: + case FileInfo.ARGB: + case FileInfo.ABGR: + case FileInfo.RGB_PLANAR: + img = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(width, height, (int[])pixels, 0, width)); + imp.setImage(img); + break; + } + } + + void setCalibration(ImagePlus imp) { + if (fi.fileType==FileInfo.GRAY16_SIGNED) { + if (IJ.debugMode) IJ.log("16-bit signed"); + double[] coeff = new double[2]; + coeff[0] = -32768.0; + coeff[1] = 1.0; + imp.getLocalCalibration().setFunction(Calibration.STRAIGHT_LINE, coeff, "gray value"); + } + + Properties props = decodeDescriptionString(fi); + Calibration cal = imp.getCalibration(); + boolean calibrated = false; + if (fi.pixelWidth>0.0 && fi.unit!=null) { + cal.pixelWidth = fi.pixelWidth; + cal.pixelHeight = fi.pixelHeight; + cal.pixelDepth = fi.pixelDepth; + cal.setUnit(fi.unit); + calibrated = true; + } + + if (fi.valueUnit!=null) { + int f = fi.calibrationFunction; + if ((f>=Calibration.STRAIGHT_LINE && f<=Calibration.RODBARD2 && fi.coefficients!=null) + || f==Calibration.UNCALIBRATED_OD) { + boolean zeroClip = props!=null && props.getProperty("zeroclip", "false").equals("true"); + cal.setFunction(f, fi.coefficients, fi.valueUnit, zeroClip); + calibrated = true; + } + } + + if (calibrated) + checkForCalibrationConflict(imp, cal); + + if (fi.frameInterval!=0.0) + cal.frameInterval = fi.frameInterval; + + if (props==null) + return; + + cal.xOrigin = getDouble(props,"xorigin"); + cal.yOrigin = getDouble(props,"yorigin"); + cal.zOrigin = getDouble(props,"zorigin"); + cal.info = props.getProperty("info"); + + cal.fps = getDouble(props,"fps"); + cal.loop = getBoolean(props, "loop"); + cal.frameInterval = getDouble(props,"finterval"); + cal.setTimeUnit(props.getProperty("tunit", "sec")); + + double displayMin = getDouble(props,"min"); + double displayMax = getDouble(props,"max"); + if (!(displayMin==0.0&&displayMax==0.0)) { + int type = imp.getType(); + ImageProcessor ip = imp.getProcessor(); + if (type==ImagePlus.GRAY8 || type==ImagePlus.COLOR_256) + ip.setMinAndMax(displayMin, displayMax); + else if (type==ImagePlus.GRAY16 || type==ImagePlus.GRAY32) { + if (ip.getMin()!=displayMin || ip.getMax()!=displayMax) + ip.setMinAndMax(displayMin, displayMax); + } + } + + int stackSize = imp.getStackSize(); + if (stackSize>1) { + int channels = (int)getDouble(props,"channels"); + int slices = (int)getDouble(props,"slices"); + int frames = (int)getDouble(props,"frames"); + if (channels==0) channels = 1; + if (slices==0) slices = 1; + if (frames==0) frames = 1; + //IJ.log("setCalibration: "+channels+" "+slices+" "+frames); + if (channels*slices*frames==stackSize) { + imp.setDimensions(channels, slices, frames); + if (getBoolean(props, "hyperstack")) + imp.setOpenAsHyperStack(true); + } + } + } + + + void checkForCalibrationConflict(ImagePlus imp, Calibration cal) { + Calibration gcal = imp.getGlobalCalibration(); + if (gcal==null || !showConflictMessage || IJ.isMacro()) + return; + if (cal.pixelWidth==gcal.pixelWidth && cal.getUnit().equals(gcal.getUnit())) + return; + GenericDialog gd = new GenericDialog(imp.getTitle()); + gd.addMessage("The calibration of this image conflicts\nwith the current global calibration."); + gd.addCheckbox("Disable_Global Calibration", true); + gd.addCheckbox("Disable_these Messages", false); + gd.showDialog(); + if (gd.wasCanceled()) return; + boolean disable = gd.getNextBoolean(); + if (disable) { + imp.setGlobalCalibration(null); + imp.setCalibration(cal); + WindowManager.repaintImageWindows(); + } + boolean dontShow = gd.getNextBoolean(); + if (dontShow) showConflictMessage = false; + } + + /** Returns an IndexColorModel for the image specified by this FileInfo. */ + public ColorModel createColorModel(FileInfo fi) { + if (fi.fileType==FileInfo.COLOR8 && fi.lutSize>0) + return new IndexColorModel(8, fi.lutSize, fi.reds, fi.greens, fi.blues); + else + return LookUpTable.createGrayscaleColorModel(fi.whiteIsZero); + } + + /** Returns an InputStream for the image described by this FileInfo. */ + public InputStream createInputStream(FileInfo fi) throws IOException, MalformedURLException { + InputStream is = null; + boolean gzip = fi.fileName!=null && (fi.fileName.endsWith(".gz")||fi.fileName.endsWith(".GZ")); + if (fi.inputStream!=null) + is = fi.inputStream; + else if (fi.url!=null && !fi.url.equals("")) + is = new URL(fi.url+fi.fileName).openStream(); + else { + if (fi.directory.length()>0 && !fi.directory.endsWith(Prefs.separator)) + fi.directory += Prefs.separator; + File f = new File(fi.directory + fi.fileName); + if (gzip) fi.compression = FileInfo.COMPRESSION_UNKNOWN; + if (f==null || f.isDirectory() || !validateFileInfo(f, fi)) + is = null; + else + is = new FileInputStream(f); + } + if (is!=null) { + if (fi.compression>=FileInfo.LZW) + is = new RandomAccessStream(is); + else if (gzip) + is = new GZIPInputStream(is, 50000); + } + return is; + } + + static boolean validateFileInfo(File f, FileInfo fi) { + long offset = fi.getOffset(); + long length = 0; + if (fi.width<=0 || fi.height<0) { + error("Width or height <= 0.", fi, offset, length); + return false; + } + if (offset>=0 && offset<1000L) + return true; + if (offset<0L) { + error("Offset is negative.", fi, offset, length); + return false; + } + if (fi.fileType==FileInfo.BITMAP || fi.compression!=FileInfo.COMPRESSION_NONE) + return true; + length = f.length(); + long size = fi.width*fi.height*fi.getBytesPerPixel(); + size = fi.nImages>1?size:size/4; + if (fi.height==1) size = 0; // allows plugins to read info of unknown length at end of file + if (offset+size>length) { + error("Offset + image size > file length.", fi, offset, length); + return false; + } + return true; + } + + static void error(String msg, FileInfo fi, long offset, long length) { + IJ.error("FileOpener", "FileInfo parameter error. \n" + +msg + "\n \n" + +" Width: " + fi.width + "\n" + +" Height: " + fi.height + "\n" + +" Offset: " + offset + "\n" + +" Bytes/pixel: " + fi.getBytesPerPixel() + "\n" + +(length>0?" File length: " + length + "\n":"") + ); + } + + + /** Reads the pixel data from an image described by a FileInfo object. */ + Object readPixels(FileInfo fi) { + Object pixels = null; + try { + InputStream is = createInputStream(fi); + if (is==null) + return null; + ImageReader reader = new ImageReader(fi); + pixels = reader.readPixels(is); + minValue = reader.min; + maxValue = reader.max; + is.close(); + } + catch (Exception e) { + if (!Macro.MACRO_CANCELED.equals(e.getMessage())) + IJ.handleException(e); + } + return pixels; + } + + public Properties decodeDescriptionString(FileInfo fi) { + if (fi.description==null || fi.description.length()<7) + return null; + if (IJ.debugMode) + IJ.log("Image Description: " + new String(fi.description).replace('\n',' ')); + if (!fi.description.startsWith("ImageJ")) + return null; + Properties props = new Properties(); + InputStream is = new ByteArrayInputStream(fi.description.getBytes()); + try {props.load(is); is.close();} + catch (IOException e) {return null;} + fi.unit = props.getProperty("unit",""); + Double n = getNumber(props,"cf"); + if (n!=null) fi.calibrationFunction = n.intValue(); + double c[] = new double[5]; + int count = 0; + for (int i=0; i<5; i++) { + n = getNumber(props,"c"+i); + if (n==null) break; + c[i] = n.doubleValue(); + count++; + } + if (count>=2) { + fi.coefficients = new double[count]; + for (int i=0; i1.0) + fi.nImages = (int)n.doubleValue(); + if (fi.nImages>1) { + double spacing = getDouble(props,"spacing"); + if (spacing!=0.0) { + if (spacing<0) spacing = -spacing; + fi.pixelDepth = spacing; + } + } + return props; + } + + private Double getNumber(Properties props, String key) { + String s = props.getProperty(key); + if (s!=null) { + try { + return Double.valueOf(s); + } catch (NumberFormatException e) {} + } + return null; + } + + private double getDouble(Properties props, String key) { + Double n = getNumber(props, key); + return n!=null?n.doubleValue():0.0; + } + + private boolean getBoolean(Properties props, String key) { + String s = props.getProperty(key); + return s!=null&&s.equals("true")?true:false; + } + + public static void setShowConflictMessage(boolean b) { + showConflictMessage = b; + } + + static void setSilentMode(boolean mode) { + silentMode = mode; + } + + } \ No newline at end of file diff --git a/ij/io/FileSaver.java b/ij/io/FileSaver.java index e8e028829..9c742d4a4 100644 --- a/ij/io/FileSaver.java +++ b/ij/io/FileSaver.java @@ -1,617 +1,617 @@ -package ij.io; -import java.awt.*; -import java.io.*; -import java.util.zip.*; -import ij.*; -import ij.process.*; -import ij.measure.Calibration; -import ij.plugin.filter.Analyzer; -import ij.plugin.frame.Recorder; -import ij.plugin.JpegWriter; -import javax.imageio.*; - -/** Saves images in tiff, gif, jpeg, raw, zip and text format. */ -public class FileSaver { - - public static final int DEFAULT_JPEG_QUALITY = 75; - private static int jpegQuality; - - static {setJpegQuality(ij.Prefs.getInt(ij.Prefs.JPEG, DEFAULT_JPEG_QUALITY));} - - private static String defaultDirectory = null; - private ImagePlus imp; - private FileInfo fi; - private String name; - private String directory; - - /** Constructs a FileSaver from an ImagePlus. */ - public FileSaver(ImagePlus imp) { - this.imp = imp; - fi = imp.getFileInfo(); - } - - /** Resaves the image. Calls saveAsTiff() if this is a new image, not a TIFF, - or if the image was loaded using a URL. Returns false if saveAsTiff() is - called and the user selects cancel in the file save dialog box. */ - public boolean save() { - FileInfo ofi = null; - if (imp!=null) ofi = imp.getOriginalFileInfo(); - boolean validName = ofi!=null && imp.getTitle().equals(ofi.fileName); - if (validName && ofi.fileFormat==FileInfo.TIFF && ofi.directory!=null && !ofi.directory.equals("") && (ofi.url==null||ofi.url.equals(""))) { - name = imp.getTitle(); - directory = ofi.directory; - String path = directory+name; - File f = new File(path); - if (f==null || !f.exists()) - return saveAsTiff(); - if (!IJ.isMacro()) { - if (!IJ.showMessageWithCancel("Save as TIFF", "The file "+ofi.fileName+" already exists.\nDo you want to replace it?")) - return false; - } - IJ.showStatus("Saving "+path); - if (imp.getStackSize()>1) { - IJ.saveAs(imp, "tif", path); - return true; - } else - return saveAsTiff(path); - } else - return saveAsTiff(); - } - - String getPath(String type, String extension) { - name = imp.getTitle(); - SaveDialog sd = new SaveDialog("Save as "+type, name, extension); - name = sd.getFileName(); - if (name==null) - return null; - directory = sd.getDirectory(); - imp.startTiming(); - String path = directory+name; - return path; - } - - /** Save the image or stack in TIFF format using a save file - dialog. Returns false if the user selects cancel. */ - public boolean saveAsTiff() { - String path = getPath("TIFF", ".tif"); - if (path==null) - return false; - if (fi.nImages>1) - return saveAsTiffStack(path); - else - return saveAsTiff(path); - } - - /** Save the image in TIFF format using the specified path. */ - public boolean saveAsTiff(String path) { - fi.nImages = 1; - Object info = imp.getProperty("Info"); - if (info!=null && (info instanceof String)) - fi.info = (String)info; - Object label = imp.getProperty("Label"); - if (label!=null && (label instanceof String)) { - fi.sliceLabels = new String[1]; - fi.sliceLabels[0] = (String)label; - } - fi.description = getDescriptionString(); - try { - TiffEncoder file = new TiffEncoder(fi); - DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(path))); - file.write(out); - out.close(); - } - catch (IOException e) { - showErrorMessage(e); - return false; - } - updateImp(fi, fi.TIFF); - return true; - } - - /** Save the stack as a multi-image TIFF using the specified path. */ - public boolean saveAsTiffStack(String path) { - if (fi.nImages==1) - {IJ.error("This is not a stack"); return false;} - if (fi.pixels==null && imp.getStack().isVirtual()) - {IJ.error("Save As Tiff", "Virtual stacks not supported."); return false;} - Object info = imp.getProperty("Info"); - if (info!=null && (info instanceof String)) - fi.info = (String)info; - fi.description = getDescriptionString(); - fi.sliceLabels = imp.getStack().getSliceLabels(); - if (imp.isComposite()) saveDisplayRangesAndLuts(imp, fi); - try { - TiffEncoder file = new TiffEncoder(fi); - DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(path))); - file.write(out); - out.close(); - } - catch (IOException e) { - showErrorMessage(e); - return false; - } - updateImp(fi, fi.TIFF); - return true; - } - - void saveDisplayRangesAndLuts(ImagePlus imp, FileInfo fi) { - CompositeImage ci = (CompositeImage)imp; - int channels = imp.getNChannels(); - fi.displayRanges = new double[channels*2]; - for (int i=1; i<=channels; i++) { - LUT lut = ci.getChannelLut(i); - fi.displayRanges[(i-1)*2] = lut.min; - fi.displayRanges[(i-1)*2+1] = lut.max; - } - if (ci.hasCustomLuts()) { - fi.channelLuts = new byte[channels][]; - for (int i=0; i100) - msg = msg.substring(0, 100); - IJ.error("FileSaver", "An error occured writing the file.\n \n" + msg); - } - - /** Returns a string containing information about the specified image. */ - public String getDescriptionString() { - Calibration cal = imp.getCalibration(); - StringBuffer sb = new StringBuffer(100); - sb.append("ImageJ="+ImageJ.VERSION+"\n"); - if (fi.nImages>1 && fi.fileType!=FileInfo.RGB48) - sb.append("images="+fi.nImages+"\n"); - int channels = imp.getNChannels(); - if (channels>1) - sb.append("channels="+channels+"\n"); - int slices = imp.getNSlices(); - if (slices>1) - sb.append("slices="+slices+"\n"); - int frames = imp.getNFrames(); - if (frames>1) - sb.append("frames="+frames+"\n"); - if (imp.isHyperStack()) sb.append("hyperstack=true\n"); - if (imp.isComposite()) { - String mode = ((CompositeImage)imp).getModeAsString(); - sb.append("mode="+mode+"\n"); - } - if (fi.unit!=null) - sb.append("unit="+(fi.unit.equals("\u00B5m")?"um":fi.unit)+"\n"); - if (fi.valueUnit!=null && fi.calibrationFunction!=Calibration.CUSTOM) { - sb.append("cf="+fi.calibrationFunction+"\n"); - if (fi.coefficients!=null) { - for (int i=0; i1) { - if (fi.pixelDepth!=0.0 && fi.pixelDepth!=1.0) - sb.append("spacing="+fi.pixelDepth+"\n"); - if (cal.fps!=0.0) { - if ((int)cal.fps==cal.fps) - sb.append("fps="+(int)cal.fps+"\n"); - else - sb.append("fps="+cal.fps+"\n"); - } - sb.append("loop="+(cal.loop?"true":"false")+"\n"); - } - - // get min and max display values - ImageProcessor ip = imp.getProcessor(); - double min = ip.getMin(); - double max = ip.getMax(); - int type = imp.getType(); - boolean enhancedLut = (type==ImagePlus.GRAY8 || type==ImagePlus.COLOR_256) && (min!=0.0 || max !=255.0); - if (enhancedLut || type==ImagePlus.GRAY16 || type==ImagePlus.GRAY32) { - sb.append("min="+min+"\n"); - sb.append("max="+max+"\n"); - } - - // get non-zero origins - if (cal.xOrigin!=0.0) - sb.append("xorigin="+cal.xOrigin+"\n"); - if (cal.yOrigin!=0.0) - sb.append("yorigin="+cal.yOrigin+"\n"); - if (cal.zOrigin!=0.0) - sb.append("zorigin="+cal.zOrigin+"\n"); - if (cal.info!=null && cal.info.length()<=64 && cal.info.indexOf('=')==-1 && cal.info.indexOf('\n')==-1) - sb.append("info="+cal.info+"\n"); - sb.append((char)0); - return new String(sb); - } - - /** Specifies the image quality (0-100). 0 is poorest image quality, - highest compression, and 100 is best image quality, lowest compression. */ - public static void setJpegQuality(int quality) { - jpegQuality = quality; - if (jpegQuality<0) jpegQuality = 0; - if (jpegQuality>100) jpegQuality = 100; - } - - /** Returns the current JPEG quality setting (0-100). */ - public static int getJpegQuality() { - return jpegQuality; - } - - -} +package ij.io; +import java.awt.*; +import java.io.*; +import java.util.zip.*; +import ij.*; +import ij.process.*; +import ij.measure.Calibration; +import ij.plugin.filter.Analyzer; +import ij.plugin.frame.Recorder; +import ij.plugin.JpegWriter; +import javax.imageio.*; + +/** Saves images in tiff, gif, jpeg, raw, zip and text format. */ +public class FileSaver { + + public static final int DEFAULT_JPEG_QUALITY = 75; + private static int jpegQuality; + + static {setJpegQuality(ij.Prefs.getInt(ij.Prefs.JPEG, DEFAULT_JPEG_QUALITY));} + + private static String defaultDirectory = null; + private ImagePlus imp; + private FileInfo fi; + private String name; + private String directory; + + /** Constructs a FileSaver from an ImagePlus. */ + public FileSaver(ImagePlus imp) { + this.imp = imp; + fi = imp.getFileInfo(); + } + + /** Resaves the image. Calls saveAsTiff() if this is a new image, not a TIFF, + or if the image was loaded using a URL. Returns false if saveAsTiff() is + called and the user selects cancel in the file save dialog box. */ + public boolean save() { + FileInfo ofi = null; + if (imp!=null) ofi = imp.getOriginalFileInfo(); + boolean validName = ofi!=null && imp.getTitle().equals(ofi.fileName); + if (validName && ofi.fileFormat==FileInfo.TIFF && ofi.directory!=null && !ofi.directory.equals("") && (ofi.url==null||ofi.url.equals(""))) { + name = imp.getTitle(); + directory = ofi.directory; + String path = directory+name; + File f = new File(path); + if (f==null || !f.exists()) + return saveAsTiff(); + if (!IJ.isMacro()) { + if (!IJ.showMessageWithCancel("Save as TIFF", "The file "+ofi.fileName+" already exists.\nDo you want to replace it?")) + return false; + } + IJ.showStatus("Saving "+path); + if (imp.getStackSize()>1) { + IJ.saveAs(imp, "tif", path); + return true; + } else + return saveAsTiff(path); + } else + return saveAsTiff(); + } + + String getPath(String type, String extension) { + name = imp.getTitle(); + SaveDialog sd = new SaveDialog("Save as "+type, name, extension); + name = sd.getFileName(); + if (name==null) + return null; + directory = sd.getDirectory(); + imp.startTiming(); + String path = directory+name; + return path; + } + + /** Save the image or stack in TIFF format using a save file + dialog. Returns false if the user selects cancel. */ + public boolean saveAsTiff() { + String path = getPath("TIFF", ".tif"); + if (path==null) + return false; + if (fi.nImages>1) + return saveAsTiffStack(path); + else + return saveAsTiff(path); + } + + /** Save the image in TIFF format using the specified path. */ + public boolean saveAsTiff(String path) { + fi.nImages = 1; + Object info = imp.getProperty("Info"); + if (info!=null && (info instanceof String)) + fi.info = (String)info; + Object label = imp.getProperty("Label"); + if (label!=null && (label instanceof String)) { + fi.sliceLabels = new String[1]; + fi.sliceLabels[0] = (String)label; + } + fi.description = getDescriptionString(); + try { + TiffEncoder file = new TiffEncoder(fi); + DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(path))); + file.write(out); + out.close(); + } + catch (IOException e) { + showErrorMessage(e); + return false; + } + updateImp(fi, fi.TIFF); + return true; + } + + /** Save the stack as a multi-image TIFF using the specified path. */ + public boolean saveAsTiffStack(String path) { + if (fi.nImages==1) + {IJ.error("This is not a stack"); return false;} + if (fi.pixels==null && imp.getStack().isVirtual()) + {IJ.error("Save As Tiff", "Virtual stacks not supported."); return false;} + Object info = imp.getProperty("Info"); + if (info!=null && (info instanceof String)) + fi.info = (String)info; + fi.description = getDescriptionString(); + fi.sliceLabels = imp.getStack().getSliceLabels(); + if (imp.isComposite()) saveDisplayRangesAndLuts(imp, fi); + try { + TiffEncoder file = new TiffEncoder(fi); + DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(path))); + file.write(out); + out.close(); + } + catch (IOException e) { + showErrorMessage(e); + return false; + } + updateImp(fi, fi.TIFF); + return true; + } + + void saveDisplayRangesAndLuts(ImagePlus imp, FileInfo fi) { + CompositeImage ci = (CompositeImage)imp; + int channels = imp.getNChannels(); + fi.displayRanges = new double[channels*2]; + for (int i=1; i<=channels; i++) { + LUT lut = ci.getChannelLut(i); + fi.displayRanges[(i-1)*2] = lut.min; + fi.displayRanges[(i-1)*2+1] = lut.max; + } + if (ci.hasCustomLuts()) { + fi.channelLuts = new byte[channels][]; + for (int i=0; i100) + msg = msg.substring(0, 100); + IJ.error("FileSaver", "An error occured writing the file.\n \n" + msg); + } + + /** Returns a string containing information about the specified image. */ + public String getDescriptionString() { + Calibration cal = imp.getCalibration(); + StringBuffer sb = new StringBuffer(100); + sb.append("ImageJ="+ImageJ.VERSION+"\n"); + if (fi.nImages>1 && fi.fileType!=FileInfo.RGB48) + sb.append("images="+fi.nImages+"\n"); + int channels = imp.getNChannels(); + if (channels>1) + sb.append("channels="+channels+"\n"); + int slices = imp.getNSlices(); + if (slices>1) + sb.append("slices="+slices+"\n"); + int frames = imp.getNFrames(); + if (frames>1) + sb.append("frames="+frames+"\n"); + if (imp.isHyperStack()) sb.append("hyperstack=true\n"); + if (imp.isComposite()) { + String mode = ((CompositeImage)imp).getModeAsString(); + sb.append("mode="+mode+"\n"); + } + if (fi.unit!=null) + sb.append("unit="+(fi.unit.equals("\u00B5m")?"um":fi.unit)+"\n"); + if (fi.valueUnit!=null && fi.calibrationFunction!=Calibration.CUSTOM) { + sb.append("cf="+fi.calibrationFunction+"\n"); + if (fi.coefficients!=null) { + for (int i=0; i1) { + if (fi.pixelDepth!=0.0 && fi.pixelDepth!=1.0) + sb.append("spacing="+fi.pixelDepth+"\n"); + if (cal.fps!=0.0) { + if ((int)cal.fps==cal.fps) + sb.append("fps="+(int)cal.fps+"\n"); + else + sb.append("fps="+cal.fps+"\n"); + } + sb.append("loop="+(cal.loop?"true":"false")+"\n"); + } + + // get min and max display values + ImageProcessor ip = imp.getProcessor(); + double min = ip.getMin(); + double max = ip.getMax(); + int type = imp.getType(); + boolean enhancedLut = (type==ImagePlus.GRAY8 || type==ImagePlus.COLOR_256) && (min!=0.0 || max !=255.0); + if (enhancedLut || type==ImagePlus.GRAY16 || type==ImagePlus.GRAY32) { + sb.append("min="+min+"\n"); + sb.append("max="+max+"\n"); + } + + // get non-zero origins + if (cal.xOrigin!=0.0) + sb.append("xorigin="+cal.xOrigin+"\n"); + if (cal.yOrigin!=0.0) + sb.append("yorigin="+cal.yOrigin+"\n"); + if (cal.zOrigin!=0.0) + sb.append("zorigin="+cal.zOrigin+"\n"); + if (cal.info!=null && cal.info.length()<=64 && cal.info.indexOf('=')==-1 && cal.info.indexOf('\n')==-1) + sb.append("info="+cal.info+"\n"); + sb.append((char)0); + return new String(sb); + } + + /** Specifies the image quality (0-100). 0 is poorest image quality, + highest compression, and 100 is best image quality, lowest compression. */ + public static void setJpegQuality(int quality) { + jpegQuality = quality; + if (jpegQuality<0) jpegQuality = 0; + if (jpegQuality>100) jpegQuality = 100; + } + + /** Returns the current JPEG quality setting (0-100). */ + public static int getJpegQuality() { + return jpegQuality; + } + + +} diff --git a/ij/io/ImageReader.java b/ij/io/ImageReader.java index fc916f414..2e62804db 100644 --- a/ij/io/ImageReader.java +++ b/ij/io/ImageReader.java @@ -1,954 +1,954 @@ -package ij.io; -import ij.*; -import ij.process.*; -import java.io.*; -import java.net.*; -import java.awt.image.BufferedImage; -import javax.imageio.ImageIO; - -/** Reads raw 8-bit, 16-bit or 32-bit (float or RGB) - images from a stream or URL. */ -public class ImageReader { - - private static final int CLEAR_CODE = 256; - private static final int EOI_CODE = 257; - - private FileInfo fi; - private int width, height; - private long skipCount; - private int bytesPerPixel, bufferSize, byteCount, nPixels; - private boolean showProgressBar=true; - private int eofErrorCount; - private long startTime; - public double min, max; // readRGB48() calculates min/max pixel values - - /** - Constructs a new ImageReader using a FileInfo object to describe the file to be read. - @see ij.io.FileInfo - */ - public ImageReader (FileInfo fi) { - this.fi = fi; - width = fi.width; - height = fi.height; - skipCount = fi.getOffset(); - } - - void eofError() { - eofErrorCount++; - } - - byte[] read8bitImage(InputStream in) throws IOException { - if (fi.compression==FileInfo.LZW || fi.compression==FileInfo.LZW_WITH_DIFFERENCING || fi.compression==FileInfo.PACK_BITS) - return readCompressed8bitImage(in); - byte[] pixels = new byte[nPixels]; - // assume contiguous strips - int count, actuallyRead; - int totalRead = 0; - while (totalReadbyteCount) - count = byteCount-totalRead; - else - count = bufferSize; - actuallyRead = in.read(pixels, totalRead, count); - if (actuallyRead==-1) {eofError(); break;} - totalRead += actuallyRead; - showProgress(totalRead, byteCount); - } - return pixels; - } - - byte[] readCompressed8bitImage(InputStream in) throws IOException { - byte[] pixels = new byte[nPixels]; - int current = 0; - byte last = 0; - for (int i=0; i 0) { - int skip = fi.stripOffsets[i] - fi.stripOffsets[i-1] - fi.stripLengths[i-1]; - if (skip > 0) in.skip(skip); - } - byte[] byteArray = new byte[fi.stripLengths[i]]; - int read = 0, left = byteArray.length; - while (left > 0) { - int r = in.read(byteArray, read, left); - if (r == -1) {eofError(); break;} - read += r; - left -= r; - } - byteArray = uncompress(byteArray); - int length = byteArray.length; - length = length - (length%fi.width); - if (fi.compression == FileInfo.LZW_WITH_DIFFERENCING) { - for (int b=0; bpixels.length) length = pixels.length-current; - System.arraycopy(byteArray, 0, pixels, current, length); - current += length; - showProgress(i+1, fi.stripOffsets.length); - } - return pixels; - } - - /** Reads a 16-bit image. Signed pixels are converted to unsigned by adding 32768. */ - short[] read16bitImage(InputStream in) throws IOException { - if (fi.compression==FileInfo.LZW || fi.compression==FileInfo.LZW_WITH_DIFFERENCING || fi.compression==FileInfo.PACK_BITS) - return readCompressed16bitImage(in); - int pixelsRead; - byte[] buffer = new byte[bufferSize]; - short[] pixels = new short[nPixels]; - int totalRead = 0; - int base = 0; - int count, value; - int bufferCount; - - while (totalReadbyteCount) - bufferSize = byteCount-totalRead; - bufferCount = 0; - while (bufferCount0) - for (int i=bufferCount; i 0) { - int skip = fi.stripOffsets[k] - fi.stripOffsets[k-1] - fi.stripLengths[k-1]; - if (skip > 0) in.skip(skip); - } - byte[] byteArray = new byte[fi.stripLengths[k]]; - int read = 0, left = byteArray.length; - while (left > 0) { - int r = in.read(byteArray, read, left); - if (r == -1) {eofError(); break;} - read += r; - left -= r; - } - byteArray = uncompress(byteArray); - int pixelsRead = byteArray.length/bytesPerPixel; - pixelsRead = pixelsRead - (pixelsRead%fi.width); - int pmax = base+pixelsRead; - if (pmax > nPixels) pmax = nPixels; - if (fi.intelByteOrder) { - if (fi.fileType==FileInfo.GRAY16_SIGNED) - for (int i=base,j=0; ibyteCount) - bufferSize = byteCount-totalRead; - bufferCount = 0; - while (bufferCount0) - for (int i=bufferCount; ibyteCount) - bufferSize = byteCount-totalRead; - bufferCount = 0; - while (bufferCount0) - for (int i=bufferCount; ibyteCount) - bufferSize = byteCount-totalRead; - bufferCount = 0; - while (bufferCount0) - for (int i=bufferCount; i 0) { - int skip = fi.stripOffsets[i] - fi.stripOffsets[i-1] - fi.stripLengths[i-1]; - if (skip > 0) in.skip(skip); - } - byte[] byteArray = new byte[fi.stripLengths[i]]; - int read = 0, left = byteArray.length; - while (left > 0) { - int r = in.read(byteArray, read, left); - if (r == -1) {eofError(); break;} - read += r; - left -= r; - } - byteArray = uncompress(byteArray); - if (differencing) { - for (int b=0; b nPixels) pmax = nPixels; - for (int j=base; j500L) - IJ.showProgress(current, last); - } - - Object readRGB48(InputStream in) throws IOException { - if (fi.compression==FileInfo.LZW || fi.compression==FileInfo.LZW_WITH_DIFFERENCING || fi.compression==FileInfo.PACK_BITS) - return readCompressedRGB48(in); - int channels = 3; - short[][] stack = new short[channels][nPixels]; - DataInputStream dis = new DataInputStream(in); - int pixel = 0; - int min=65535, max=0; - for (int i=0; i0) { - int skip = fi.stripOffsets[i] - fi.stripOffsets[i-1] - fi.stripLengths[i-1]; - if (skip>0) dis.skip(skip); - } - int len = fi.stripLengths[i]; - int bytesToGo = (nPixels-pixel)*channels*2; - if (len>bytesToGo) len = bytesToGo; - byte[] buffer = new byte[len]; - dis.readFully(buffer); - int value; - int channel=0; - boolean intel = fi.intelByteOrder; - for (int base=0; basemax) max = value; - stack[channel][pixel] = (short)(value); - channel++; - if (channel==channels) { - channel = 0; - pixel++; - } - } - showProgress(i+1, fi.stripOffsets.length); - } - this.min=min; this.max=max; - return stack; - } - - Object readCompressedRGB48(InputStream in) throws IOException { - if (fi.compression==FileInfo.LZW_WITH_DIFFERENCING) - throw new IOException("ImageJ cannot open 48-bit LZW compressed TIFFs with predictor"); - int channels = 3; - short[][] stack = new short[channels][nPixels]; - DataInputStream dis = new DataInputStream(in); - int pixel = 0; - int min=65535, max=0; - for (int i=0; i0) { - int skip = fi.stripOffsets[i] - fi.stripOffsets[i-1] - fi.stripLengths[i-1]; - if (skip>0) dis.skip(skip); - } - int len = fi.stripLengths[i]; - byte[] buffer = new byte[len]; - dis.readFully(buffer); - buffer = uncompress(buffer); - len = buffer.length; - if (len % 2 != 0) len--; - int value; - int channel=0; - boolean intel = fi.intelByteOrder; - for (int base=0; basemax) max = value; - stack[channel][pixel] = (short)(value); - channel++; - if (channel==channels) { - channel = 0; - pixel++; - } - } - showProgress(i+1, fi.stripOffsets.length); - } - this.min=min; this.max=max; - return stack; - } - - Object readRGB48Planar(InputStream in) throws IOException { - short[] red = read16bitImage(in); - short[] green = read16bitImage(in); - short[] blue = read16bitImage(in); - Object[] stack = new Object[3]; - stack[0] = red; - stack[1] = green; - stack[2] = blue; - return stack; - } - - short[] read12bitImage(InputStream in) throws IOException { - int nBytes = (int)(nPixels*1.5); - if ((nPixels&1)==1) nBytes++; // add 1 if odd - byte[] buffer = new byte[nBytes]; - short[] pixels = new short[nPixels]; - DataInputStream dis = new DataInputStream(in); - dis.readFully(buffer); - int i = 0; - int j = 0; - for (int index=0; index>4)&0xf)); - pixels[j++] = (short)(((buffer[i+1]&0xf)*256) + (buffer[i+2]&0xff)); - i += 3; - } - return pixels; - } - - float[] read24bitImage(InputStream in) throws IOException { - byte[] buffer = new byte[width*3]; - float[] pixels = new float[nPixels]; - int b1, b2, b3; - DataInputStream dis = new DataInputStream(in); - for (int y=0; y=0; i--) { - value2 = (value1&(1<0) { - long bytesRead = 0; - int skipAttempts = 0; - long count; - while (bytesRead5) break; - bytesRead += count; - //IJ.log("skip: "+skipCount+" "+count+" "+bytesRead+" "+skipAttempts); - } - } - byteCount = width*height*bytesPerPixel; - if (fi.fileType==FileInfo.BITMAP) { - int scan=width/8, pad = width%8; - if (pad>0) scan++; - byteCount = scan*height; - } - nPixels = width*height; - bufferSize = byteCount/25; - if (bufferSize<8192) - bufferSize = 8192; - else - bufferSize = (bufferSize/8192)*8192; - } - - /** - Reads the image from the InputStream and returns the pixel - array (byte, short, int or float). Returns null if there - was an IO exception. Does not close the InputStream. - */ - public Object readPixels(InputStream in) { - Object pixels; - startTime = System.currentTimeMillis(); - try { - switch (fi.fileType) { - case FileInfo.GRAY8: - case FileInfo.COLOR8: - bytesPerPixel = 1; - skip(in); - pixels = (Object)read8bitImage(in); - break; - case FileInfo.GRAY16_SIGNED: - case FileInfo.GRAY16_UNSIGNED: - bytesPerPixel = 2; - skip(in); - pixels = (Object)read16bitImage(in); - break; - case FileInfo.GRAY32_INT: - case FileInfo.GRAY32_UNSIGNED: - case FileInfo.GRAY32_FLOAT: - bytesPerPixel = 4; - skip(in); - pixels = (Object)read32bitImage(in); - break; - case FileInfo.GRAY64_FLOAT: - bytesPerPixel = 8; - skip(in); - pixels = (Object)read64bitImage(in); - break; - case FileInfo.RGB: - case FileInfo.BGR: - case FileInfo.ARGB: - case FileInfo.ABGR: - case FileInfo.BARG: - bytesPerPixel = fi.getBytesPerPixel(); - skip(in); - pixels = (Object)readChunkyRGB(in); - break; - case FileInfo.RGB_PLANAR: - bytesPerPixel = 3; - skip(in); - pixels = (Object)readPlanarRGB(in); - break; - case FileInfo.BITMAP: - bytesPerPixel = 1; - skip(in); - pixels = (Object)read1bitImage(in); - break; - case FileInfo.RGB48: - bytesPerPixel = 6; - skip(in); - pixels = (Object)readRGB48(in); - break; - case FileInfo.RGB48_PLANAR: - bytesPerPixel = 2; - skip(in); - pixels = (Object)readRGB48Planar(in); - break; - case FileInfo.GRAY12_UNSIGNED: - skip(in); - short[] data = read12bitImage(in); - pixels = (Object)data; - break; - case FileInfo.GRAY24_UNSIGNED: - skip(in); - pixels = (Object)read24bitImage(in); - break; - default: - pixels = null; - } - showProgress(1, 1); - return pixels; - } - catch (IOException e) { - IJ.log("" + e); - return null; - } - } - - /** - Skips the specified number of bytes, then reads an image and - returns the pixel array (byte, short, int or float). Returns - null if there was an IO exception. Does not close the InputStream. - */ - public Object readPixels(InputStream in, long skipCount) { - this.skipCount = skipCount; - showProgressBar = false; - Object pixels = readPixels(in); - if (eofErrorCount>0) - return null; - else - return pixels; - } - - /** - Reads the image from a URL and returns the pixel array (byte, - short, int or float). Returns null if there was an IO exception. - */ - public Object readPixels(String url) { - java.net.URL theURL; - InputStream is; - try {theURL = new URL(url);} - catch (MalformedURLException e) {IJ.log(""+e); return null;} - try {is = theURL.openStream();} - catch (IOException e) {IJ.log(""+e); return null;} - return readPixels(is); - } - - byte[] uncompress(byte[] input) { - if (fi.compression==FileInfo.PACK_BITS) - return packBitsUncompress(input, fi.rowsPerStrip*fi.width*fi.getBytesPerPixel()); - else - return lzwUncompress(input); - } - - /** - * Utility method for decoding an LZW-compressed image strip. - * Adapted from the TIFF 6.0 Specification: - * http://partners.adobe.com/asn/developer/pdfs/tn/TIFF6.pdf (page 61) - * @author Curtis Rueden (ctrueden at wisc.edu) - */ - public byte[] lzwUncompress(byte[] input) { - if (input == null || input.length == 0) - return input; - byte[][] symbolTable = new byte[4096][1]; - int bitsToRead = 9; - int nextSymbol = 258; - int code; - int oldCode = -1; - ByteVector out = new ByteVector(8192); - BitBuffer bb = new BitBuffer(input); - byte[] byteBuffer1 = new byte[16]; - byte[] byteBuffer2 = new byte[16]; - - while (true) { - code = bb.getBits(bitsToRead); - if (code == EOI_CODE || code == -1) - break; - if (code == CLEAR_CODE) { - // initialize symbol table - for (int i = 0; i < 256; i++) - symbolTable[i][0] = (byte)i; - nextSymbol = 258; - bitsToRead = 9; - code = bb.getBits(bitsToRead); - if (code == EOI_CODE || code == -1) - break; - out.add(symbolTable[code]); - oldCode = code; - } else { - if (code < nextSymbol) { - // code is in table - out.add(symbolTable[code]); - // add string to table - ByteVector symbol = new ByteVector(byteBuffer1); - symbol.add(symbolTable[oldCode]); - symbol.add(symbolTable[code][0]); - symbolTable[nextSymbol] = symbol.toByteArray(); //** - oldCode = code; - nextSymbol++; - } else { - // out of table - ByteVector symbol = new ByteVector(byteBuffer2); - symbol.add(symbolTable[oldCode]); - symbol.add(symbolTable[oldCode][0]); - byte[] outString = symbol.toByteArray(); - out.add(outString); - symbolTable[nextSymbol] = outString; //** - oldCode = code; - nextSymbol++; - } - if (nextSymbol == 511) { bitsToRead = 10; } - if (nextSymbol == 1023) { bitsToRead = 11; } - if (nextSymbol == 2047) { bitsToRead = 12; } - } - } - return out.toByteArray(); - } - - /** Based on the Bio-Formats PackbitsCodec written by Melissa Linkert. */ - public byte[] packBitsUncompress(byte[] input, int expected) { - ByteVector output = new ByteVector(1024); - int index = 0; - while (output.size()=0) { // 0 <= n <= 127 - byte[] b = new byte[n+1]; - for (int i=0; ibyteCount) + count = byteCount-totalRead; + else + count = bufferSize; + actuallyRead = in.read(pixels, totalRead, count); + if (actuallyRead==-1) {eofError(); break;} + totalRead += actuallyRead; + showProgress(totalRead, byteCount); + } + return pixels; + } + + byte[] readCompressed8bitImage(InputStream in) throws IOException { + byte[] pixels = new byte[nPixels]; + int current = 0; + byte last = 0; + for (int i=0; i 0) { + int skip = fi.stripOffsets[i] - fi.stripOffsets[i-1] - fi.stripLengths[i-1]; + if (skip > 0) in.skip(skip); + } + byte[] byteArray = new byte[fi.stripLengths[i]]; + int read = 0, left = byteArray.length; + while (left > 0) { + int r = in.read(byteArray, read, left); + if (r == -1) {eofError(); break;} + read += r; + left -= r; + } + byteArray = uncompress(byteArray); + int length = byteArray.length; + length = length - (length%fi.width); + if (fi.compression == FileInfo.LZW_WITH_DIFFERENCING) { + for (int b=0; bpixels.length) length = pixels.length-current; + System.arraycopy(byteArray, 0, pixels, current, length); + current += length; + showProgress(i+1, fi.stripOffsets.length); + } + return pixels; + } + + /** Reads a 16-bit image. Signed pixels are converted to unsigned by adding 32768. */ + short[] read16bitImage(InputStream in) throws IOException { + if (fi.compression==FileInfo.LZW || fi.compression==FileInfo.LZW_WITH_DIFFERENCING || fi.compression==FileInfo.PACK_BITS) + return readCompressed16bitImage(in); + int pixelsRead; + byte[] buffer = new byte[bufferSize]; + short[] pixels = new short[nPixels]; + int totalRead = 0; + int base = 0; + int count, value; + int bufferCount; + + while (totalReadbyteCount) + bufferSize = byteCount-totalRead; + bufferCount = 0; + while (bufferCount0) + for (int i=bufferCount; i 0) { + int skip = fi.stripOffsets[k] - fi.stripOffsets[k-1] - fi.stripLengths[k-1]; + if (skip > 0) in.skip(skip); + } + byte[] byteArray = new byte[fi.stripLengths[k]]; + int read = 0, left = byteArray.length; + while (left > 0) { + int r = in.read(byteArray, read, left); + if (r == -1) {eofError(); break;} + read += r; + left -= r; + } + byteArray = uncompress(byteArray); + int pixelsRead = byteArray.length/bytesPerPixel; + pixelsRead = pixelsRead - (pixelsRead%fi.width); + int pmax = base+pixelsRead; + if (pmax > nPixels) pmax = nPixels; + if (fi.intelByteOrder) { + if (fi.fileType==FileInfo.GRAY16_SIGNED) + for (int i=base,j=0; ibyteCount) + bufferSize = byteCount-totalRead; + bufferCount = 0; + while (bufferCount0) + for (int i=bufferCount; ibyteCount) + bufferSize = byteCount-totalRead; + bufferCount = 0; + while (bufferCount0) + for (int i=bufferCount; ibyteCount) + bufferSize = byteCount-totalRead; + bufferCount = 0; + while (bufferCount0) + for (int i=bufferCount; i 0) { + int skip = fi.stripOffsets[i] - fi.stripOffsets[i-1] - fi.stripLengths[i-1]; + if (skip > 0) in.skip(skip); + } + byte[] byteArray = new byte[fi.stripLengths[i]]; + int read = 0, left = byteArray.length; + while (left > 0) { + int r = in.read(byteArray, read, left); + if (r == -1) {eofError(); break;} + read += r; + left -= r; + } + byteArray = uncompress(byteArray); + if (differencing) { + for (int b=0; b nPixels) pmax = nPixels; + for (int j=base; j500L) + IJ.showProgress(current, last); + } + + Object readRGB48(InputStream in) throws IOException { + if (fi.compression==FileInfo.LZW || fi.compression==FileInfo.LZW_WITH_DIFFERENCING || fi.compression==FileInfo.PACK_BITS) + return readCompressedRGB48(in); + int channels = 3; + short[][] stack = new short[channels][nPixels]; + DataInputStream dis = new DataInputStream(in); + int pixel = 0; + int min=65535, max=0; + for (int i=0; i0) { + int skip = fi.stripOffsets[i] - fi.stripOffsets[i-1] - fi.stripLengths[i-1]; + if (skip>0) dis.skip(skip); + } + int len = fi.stripLengths[i]; + int bytesToGo = (nPixels-pixel)*channels*2; + if (len>bytesToGo) len = bytesToGo; + byte[] buffer = new byte[len]; + dis.readFully(buffer); + int value; + int channel=0; + boolean intel = fi.intelByteOrder; + for (int base=0; basemax) max = value; + stack[channel][pixel] = (short)(value); + channel++; + if (channel==channels) { + channel = 0; + pixel++; + } + } + showProgress(i+1, fi.stripOffsets.length); + } + this.min=min; this.max=max; + return stack; + } + + Object readCompressedRGB48(InputStream in) throws IOException { + if (fi.compression==FileInfo.LZW_WITH_DIFFERENCING) + throw new IOException("ImageJ cannot open 48-bit LZW compressed TIFFs with predictor"); + int channels = 3; + short[][] stack = new short[channels][nPixels]; + DataInputStream dis = new DataInputStream(in); + int pixel = 0; + int min=65535, max=0; + for (int i=0; i0) { + int skip = fi.stripOffsets[i] - fi.stripOffsets[i-1] - fi.stripLengths[i-1]; + if (skip>0) dis.skip(skip); + } + int len = fi.stripLengths[i]; + byte[] buffer = new byte[len]; + dis.readFully(buffer); + buffer = uncompress(buffer); + len = buffer.length; + if (len % 2 != 0) len--; + int value; + int channel=0; + boolean intel = fi.intelByteOrder; + for (int base=0; basemax) max = value; + stack[channel][pixel] = (short)(value); + channel++; + if (channel==channels) { + channel = 0; + pixel++; + } + } + showProgress(i+1, fi.stripOffsets.length); + } + this.min=min; this.max=max; + return stack; + } + + Object readRGB48Planar(InputStream in) throws IOException { + short[] red = read16bitImage(in); + short[] green = read16bitImage(in); + short[] blue = read16bitImage(in); + Object[] stack = new Object[3]; + stack[0] = red; + stack[1] = green; + stack[2] = blue; + return stack; + } + + short[] read12bitImage(InputStream in) throws IOException { + int nBytes = (int)(nPixels*1.5); + if ((nPixels&1)==1) nBytes++; // add 1 if odd + byte[] buffer = new byte[nBytes]; + short[] pixels = new short[nPixels]; + DataInputStream dis = new DataInputStream(in); + dis.readFully(buffer); + int i = 0; + int j = 0; + for (int index=0; index>4)&0xf)); + pixels[j++] = (short)(((buffer[i+1]&0xf)*256) + (buffer[i+2]&0xff)); + i += 3; + } + return pixels; + } + + float[] read24bitImage(InputStream in) throws IOException { + byte[] buffer = new byte[width*3]; + float[] pixels = new float[nPixels]; + int b1, b2, b3; + DataInputStream dis = new DataInputStream(in); + for (int y=0; y=0; i--) { + value2 = (value1&(1<0) { + long bytesRead = 0; + int skipAttempts = 0; + long count; + while (bytesRead5) break; + bytesRead += count; + //IJ.log("skip: "+skipCount+" "+count+" "+bytesRead+" "+skipAttempts); + } + } + byteCount = width*height*bytesPerPixel; + if (fi.fileType==FileInfo.BITMAP) { + int scan=width/8, pad = width%8; + if (pad>0) scan++; + byteCount = scan*height; + } + nPixels = width*height; + bufferSize = byteCount/25; + if (bufferSize<8192) + bufferSize = 8192; + else + bufferSize = (bufferSize/8192)*8192; + } + + /** + Reads the image from the InputStream and returns the pixel + array (byte, short, int or float). Returns null if there + was an IO exception. Does not close the InputStream. + */ + public Object readPixels(InputStream in) { + Object pixels; + startTime = System.currentTimeMillis(); + try { + switch (fi.fileType) { + case FileInfo.GRAY8: + case FileInfo.COLOR8: + bytesPerPixel = 1; + skip(in); + pixels = (Object)read8bitImage(in); + break; + case FileInfo.GRAY16_SIGNED: + case FileInfo.GRAY16_UNSIGNED: + bytesPerPixel = 2; + skip(in); + pixels = (Object)read16bitImage(in); + break; + case FileInfo.GRAY32_INT: + case FileInfo.GRAY32_UNSIGNED: + case FileInfo.GRAY32_FLOAT: + bytesPerPixel = 4; + skip(in); + pixels = (Object)read32bitImage(in); + break; + case FileInfo.GRAY64_FLOAT: + bytesPerPixel = 8; + skip(in); + pixels = (Object)read64bitImage(in); + break; + case FileInfo.RGB: + case FileInfo.BGR: + case FileInfo.ARGB: + case FileInfo.ABGR: + case FileInfo.BARG: + bytesPerPixel = fi.getBytesPerPixel(); + skip(in); + pixels = (Object)readChunkyRGB(in); + break; + case FileInfo.RGB_PLANAR: + bytesPerPixel = 3; + skip(in); + pixels = (Object)readPlanarRGB(in); + break; + case FileInfo.BITMAP: + bytesPerPixel = 1; + skip(in); + pixels = (Object)read1bitImage(in); + break; + case FileInfo.RGB48: + bytesPerPixel = 6; + skip(in); + pixels = (Object)readRGB48(in); + break; + case FileInfo.RGB48_PLANAR: + bytesPerPixel = 2; + skip(in); + pixels = (Object)readRGB48Planar(in); + break; + case FileInfo.GRAY12_UNSIGNED: + skip(in); + short[] data = read12bitImage(in); + pixels = (Object)data; + break; + case FileInfo.GRAY24_UNSIGNED: + skip(in); + pixels = (Object)read24bitImage(in); + break; + default: + pixels = null; + } + showProgress(1, 1); + return pixels; + } + catch (IOException e) { + IJ.log("" + e); + return null; + } + } + + /** + Skips the specified number of bytes, then reads an image and + returns the pixel array (byte, short, int or float). Returns + null if there was an IO exception. Does not close the InputStream. + */ + public Object readPixels(InputStream in, long skipCount) { + this.skipCount = skipCount; + showProgressBar = false; + Object pixels = readPixels(in); + if (eofErrorCount>0) + return null; + else + return pixels; + } + + /** + Reads the image from a URL and returns the pixel array (byte, + short, int or float). Returns null if there was an IO exception. + */ + public Object readPixels(String url) { + java.net.URL theURL; + InputStream is; + try {theURL = new URL(url);} + catch (MalformedURLException e) {IJ.log(""+e); return null;} + try {is = theURL.openStream();} + catch (IOException e) {IJ.log(""+e); return null;} + return readPixels(is); + } + + byte[] uncompress(byte[] input) { + if (fi.compression==FileInfo.PACK_BITS) + return packBitsUncompress(input, fi.rowsPerStrip*fi.width*fi.getBytesPerPixel()); + else + return lzwUncompress(input); + } + + /** + * Utility method for decoding an LZW-compressed image strip. + * Adapted from the TIFF 6.0 Specification: + * http://partners.adobe.com/asn/developer/pdfs/tn/TIFF6.pdf (page 61) + * @author Curtis Rueden (ctrueden at wisc.edu) + */ + public byte[] lzwUncompress(byte[] input) { + if (input == null || input.length == 0) + return input; + byte[][] symbolTable = new byte[4096][1]; + int bitsToRead = 9; + int nextSymbol = 258; + int code; + int oldCode = -1; + ByteVector out = new ByteVector(8192); + BitBuffer bb = new BitBuffer(input); + byte[] byteBuffer1 = new byte[16]; + byte[] byteBuffer2 = new byte[16]; + + while (true) { + code = bb.getBits(bitsToRead); + if (code == EOI_CODE || code == -1) + break; + if (code == CLEAR_CODE) { + // initialize symbol table + for (int i = 0; i < 256; i++) + symbolTable[i][0] = (byte)i; + nextSymbol = 258; + bitsToRead = 9; + code = bb.getBits(bitsToRead); + if (code == EOI_CODE || code == -1) + break; + out.add(symbolTable[code]); + oldCode = code; + } else { + if (code < nextSymbol) { + // code is in table + out.add(symbolTable[code]); + // add string to table + ByteVector symbol = new ByteVector(byteBuffer1); + symbol.add(symbolTable[oldCode]); + symbol.add(symbolTable[code][0]); + symbolTable[nextSymbol] = symbol.toByteArray(); //** + oldCode = code; + nextSymbol++; + } else { + // out of table + ByteVector symbol = new ByteVector(byteBuffer2); + symbol.add(symbolTable[oldCode]); + symbol.add(symbolTable[oldCode][0]); + byte[] outString = symbol.toByteArray(); + out.add(outString); + symbolTable[nextSymbol] = outString; //** + oldCode = code; + nextSymbol++; + } + if (nextSymbol == 511) { bitsToRead = 10; } + if (nextSymbol == 1023) { bitsToRead = 11; } + if (nextSymbol == 2047) { bitsToRead = 12; } + } + } + return out.toByteArray(); + } + + /** Based on the Bio-Formats PackbitsCodec written by Melissa Linkert. */ + public byte[] packBitsUncompress(byte[] input, int expected) { + ByteVector output = new ByteVector(1024); + int index = 0; + while (output.size()=0) { // 0 <= n <= 127 + byte[] b = new byte[n+1]; + for (int i=0; isize) - count = size - bytesWritten; - //System.out.println(bytesWritten + " " + count + " " + size); - out.write(pixels, bytesWritten, count); - bytesWritten += count; - showProgress((double)bytesWritten/size); - } - } - - void write8BitStack(OutputStream out, Object[] stack) throws IOException { - showProgressBar = false; - for (int i=0; isize) - count = size - bytesWritten; - int j = bytesWritten/2; - int value; - if (fi.intelByteOrder) - for (int i=0; i < count; i+=2) { - value = pixels[j]; - buffer[i] = (byte)value; - buffer[i+1] = (byte)(value>>>8); - j++; - } - else - for (int i=0; i < count; i+=2) { - value = pixels[j]; - buffer[i] = (byte)(value>>>8); - buffer[i+1] = (byte)value; - j++; - } - out.write(buffer, 0, count); - bytesWritten += count; - showProgress((double)bytesWritten/size); - } - } - - void write16BitStack(OutputStream out, Object[] stack) throws IOException { - showProgressBar = false; - for (int i=0; i>>8); - value = g[index1]; - buffer[index2++] = (byte)value; - buffer[index2++] = (byte)(value>>>8); - value = b[index1]; - buffer[index2++] = (byte)value; - buffer[index2++] = (byte)(value>>>8); - index1++; - } - } else { - for (int i=0; i>>8); - buffer[index2++] = (byte)value; - value = g[index1]; - buffer[index2++] = (byte)(value>>>8); - buffer[index2++] = (byte)value; - value = b[index1]; - buffer[index2++] = (byte)(value>>>8); - buffer[index2++] = (byte)value; - index1++; - } - } - out.write(buffer, 0, count); - } - } - - void writeFloatImage(OutputStream out, float[] pixels) throws IOException { - int bytesWritten = 0; - int size = fi.width*fi.height*4; - int count = 8192; - byte[] buffer = new byte[count]; - int tmp; - - while (bytesWrittensize) - count = size - bytesWritten; - int j = bytesWritten/4; - if (fi.intelByteOrder) - for (int i=0; i < count; i+=4) { - tmp = Float.floatToRawIntBits(pixels[j]); - buffer[i] = (byte)tmp; - buffer[i+1] = (byte)(tmp>>8); - buffer[i+2] = (byte)(tmp>>16); - buffer[i+3] = (byte)(tmp>>24); - j++; - } - else - for (int i=0; i < count; i+=4) { - tmp = Float.floatToRawIntBits(pixels[j]); - buffer[i] = (byte)(tmp>>24); - buffer[i+1] = (byte)(tmp>>16); - buffer[i+2] = (byte)(tmp>>8); - buffer[i+3] = (byte)tmp; - j++; - } - out.write(buffer, 0, count); - bytesWritten += count; - showProgress((double)bytesWritten/size); - } - } - - void writeFloatStack(OutputStream out, Object[] stack) throws IOException { - showProgressBar = false; - for (int i=0; isize) - count = size - bytesWritten; - int j = bytesWritten/3; - for (int i=0; i < count; i+=3) { - buffer[i] = (byte)(pixels[j]>>16); //red - buffer[i+1] = (byte)(pixels[j]>>8); //green - buffer[i+2] = (byte)pixels[j]; //blue - j++; - } - out.write(buffer, 0, count); - bytesWritten += count; - showProgress((double)bytesWritten/size); - } - } - - void writeRGBStack(OutputStream out, Object[] stack) throws IOException { - showProgressBar = false; - for (int i=0; i1 - then fi.pixels must be a 2D array, for example an - array of images returned by ImageStack.getImageArray()). - The fi.offset field is ignored. */ - public void write(OutputStream out) throws IOException { - if (fi.pixels==null) - throw new IOException("ImageWriter: fi.pixels==null"); - if (fi.nImages>1 && !(fi.pixels instanceof Object[])) - throw new IOException("ImageWriter: fi.pixels not a stack"); - switch (fi.fileType) { - case FileInfo.GRAY8: - case FileInfo.COLOR8: - if (fi.nImages>1) - write8BitStack(out, (Object[])fi.pixels); - else - write8BitImage(out, (byte[])fi.pixels); - break; - case FileInfo.GRAY16_SIGNED: - case FileInfo.GRAY16_UNSIGNED: - if (fi.nImages>1) - write16BitStack(out, (Object[])fi.pixels); - else - write16BitImage(out, (short[])fi.pixels); - break; - case FileInfo.RGB48: - writeRGB48Image(out, (Object[])fi.pixels); - break; - case FileInfo.GRAY32_FLOAT: - if (fi.nImages>1) - writeFloatStack(out, (Object[])fi.pixels); - else - writeFloatImage(out, (float[])fi.pixels); - break; - case FileInfo.RGB: - if (fi.nImages>1) - writeRGBStack(out, (Object[])fi.pixels); - else - writeRGBImage(out, (int[])fi.pixels); - break; - default: - } - } - -} - +package ij.io; +import java.io.*; +import ij.*; //?? + +/** Writes a raw image described by a FileInfo object to an OutputStream. */ +public class ImageWriter { + private FileInfo fi; + private boolean showProgressBar=true; + + public ImageWriter (FileInfo fi) { + this.fi = fi; + } + + private void showProgress(double progress) { + if (showProgressBar) + IJ.showProgress(progress); + } + + void write8BitImage(OutputStream out, byte[] pixels) throws IOException { + int bytesWritten = 0; + int size = fi.width*fi.height; + int count = 8192; + + while (bytesWrittensize) + count = size - bytesWritten; + //System.out.println(bytesWritten + " " + count + " " + size); + out.write(pixels, bytesWritten, count); + bytesWritten += count; + showProgress((double)bytesWritten/size); + } + } + + void write8BitStack(OutputStream out, Object[] stack) throws IOException { + showProgressBar = false; + for (int i=0; isize) + count = size - bytesWritten; + int j = bytesWritten/2; + int value; + if (fi.intelByteOrder) + for (int i=0; i < count; i+=2) { + value = pixels[j]; + buffer[i] = (byte)value; + buffer[i+1] = (byte)(value>>>8); + j++; + } + else + for (int i=0; i < count; i+=2) { + value = pixels[j]; + buffer[i] = (byte)(value>>>8); + buffer[i+1] = (byte)value; + j++; + } + out.write(buffer, 0, count); + bytesWritten += count; + showProgress((double)bytesWritten/size); + } + } + + void write16BitStack(OutputStream out, Object[] stack) throws IOException { + showProgressBar = false; + for (int i=0; i>>8); + value = g[index1]; + buffer[index2++] = (byte)value; + buffer[index2++] = (byte)(value>>>8); + value = b[index1]; + buffer[index2++] = (byte)value; + buffer[index2++] = (byte)(value>>>8); + index1++; + } + } else { + for (int i=0; i>>8); + buffer[index2++] = (byte)value; + value = g[index1]; + buffer[index2++] = (byte)(value>>>8); + buffer[index2++] = (byte)value; + value = b[index1]; + buffer[index2++] = (byte)(value>>>8); + buffer[index2++] = (byte)value; + index1++; + } + } + out.write(buffer, 0, count); + } + } + + void writeFloatImage(OutputStream out, float[] pixels) throws IOException { + int bytesWritten = 0; + int size = fi.width*fi.height*4; + int count = 8192; + byte[] buffer = new byte[count]; + int tmp; + + while (bytesWrittensize) + count = size - bytesWritten; + int j = bytesWritten/4; + if (fi.intelByteOrder) + for (int i=0; i < count; i+=4) { + tmp = Float.floatToRawIntBits(pixels[j]); + buffer[i] = (byte)tmp; + buffer[i+1] = (byte)(tmp>>8); + buffer[i+2] = (byte)(tmp>>16); + buffer[i+3] = (byte)(tmp>>24); + j++; + } + else + for (int i=0; i < count; i+=4) { + tmp = Float.floatToRawIntBits(pixels[j]); + buffer[i] = (byte)(tmp>>24); + buffer[i+1] = (byte)(tmp>>16); + buffer[i+2] = (byte)(tmp>>8); + buffer[i+3] = (byte)tmp; + j++; + } + out.write(buffer, 0, count); + bytesWritten += count; + showProgress((double)bytesWritten/size); + } + } + + void writeFloatStack(OutputStream out, Object[] stack) throws IOException { + showProgressBar = false; + for (int i=0; isize) + count = size - bytesWritten; + int j = bytesWritten/3; + for (int i=0; i < count; i+=3) { + buffer[i] = (byte)(pixels[j]>>16); //red + buffer[i+1] = (byte)(pixels[j]>>8); //green + buffer[i+2] = (byte)pixels[j]; //blue + j++; + } + out.write(buffer, 0, count); + bytesWritten += count; + showProgress((double)bytesWritten/size); + } + } + + void writeRGBStack(OutputStream out, Object[] stack) throws IOException { + showProgressBar = false; + for (int i=0; i1 + then fi.pixels must be a 2D array, for example an + array of images returned by ImageStack.getImageArray()). + The fi.offset field is ignored. */ + public void write(OutputStream out) throws IOException { + if (fi.pixels==null) + throw new IOException("ImageWriter: fi.pixels==null"); + if (fi.nImages>1 && !(fi.pixels instanceof Object[])) + throw new IOException("ImageWriter: fi.pixels not a stack"); + switch (fi.fileType) { + case FileInfo.GRAY8: + case FileInfo.COLOR8: + if (fi.nImages>1) + write8BitStack(out, (Object[])fi.pixels); + else + write8BitImage(out, (byte[])fi.pixels); + break; + case FileInfo.GRAY16_SIGNED: + case FileInfo.GRAY16_UNSIGNED: + if (fi.nImages>1) + write16BitStack(out, (Object[])fi.pixels); + else + write16BitImage(out, (short[])fi.pixels); + break; + case FileInfo.RGB48: + writeRGB48Image(out, (Object[])fi.pixels); + break; + case FileInfo.GRAY32_FLOAT: + if (fi.nImages>1) + writeFloatStack(out, (Object[])fi.pixels); + else + writeFloatImage(out, (float[])fi.pixels); + break; + case FileInfo.RGB: + if (fi.nImages>1) + writeRGBStack(out, (Object[])fi.pixels); + else + writeRGBImage(out, (int[])fi.pixels); + break; + default: + } + } + +} + diff --git a/ij/io/ImportDialog.java b/ij/io/ImportDialog.java index 219b01446..beea65042 100644 --- a/ij/io/ImportDialog.java +++ b/ij/io/ImportDialog.java @@ -1,247 +1,247 @@ -package ij.io; - -import java.awt.*; -import java.awt.image.*; -import java.io.*; -import java.util.*; -import ij.*; -import ij.gui.*; -import ij.process.*; -import ij.util.StringSorter; -import ij.plugin.frame.Recorder; -import ij.plugin.FolderOpener; -import ij.plugin.FileInfoVirtualStack; -import ij.measure.Calibration; - - -/** This is a dialog box used to imports raw 8, 16, 24 and 32-bit images. */ -public class ImportDialog { - private String fileName; - private String directory; - static final String TYPE = "raw.type"; - static final String WIDTH = "raw.width"; - static final String HEIGHT = "raw.height"; - static final String OFFSET = "raw.offset"; - static final String N = "raw.n"; - static final String GAP = "raw.gap"; - static final String OPTIONS = "raw.options"; - static final int WHITE_IS_ZERO = 1; - static final int INTEL_BYTE_ORDER = 2; - static final int OPEN_ALL = 4; - - // default settings - private static int choiceSelection = Prefs.getInt(TYPE,0); - private static int width = Prefs.getInt(WIDTH,512); - private static int height = Prefs.getInt(HEIGHT,512); - private static long offset = Prefs.getInt(OFFSET,0); - private static int nImages = Prefs.getInt(N,1); - private static int gapBetweenImages = Prefs.getInt(GAP,0); - private static int options; - private static boolean whiteIsZero,intelByteOrder; - private static boolean virtual; - private boolean openAll; - private static FileInfo lastFileInfo; - private static String[] types = {"8-bit", "16-bit Signed", "16-bit Unsigned", - "32-bit Signed", "32-bit Unsigned", "32-bit Real", "64-bit Real", "24-bit RGB", - "24-bit RGB Planar", "24-bit BGR", "24-bit Integer", "32-bit ARGB", "32-bit ABGR", "1-bit Bitmap"}; - - static { - options = Prefs.getInt(OPTIONS,0); - whiteIsZero = (options&WHITE_IS_ZERO)!=0; - intelByteOrder = (options&INTEL_BYTE_ORDER)!=0; - //openAll = (options&OPEN_ALL)!=0; - } - - public ImportDialog(String fileName, String directory) { - this.fileName = fileName; - this.directory = directory; - IJ.showStatus("Importing: " + fileName); - } - - public ImportDialog() { - } - - boolean showDialog() { - if (choiceSelection>=types.length) - choiceSelection = 0; - GenericDialog gd = new GenericDialog("Import...", IJ.getInstance()); - gd.addChoice("Image Type:", types, types[choiceSelection]); - gd.addNumericField("Width:", width, 0, 6, "pixels"); - gd.addNumericField("Height:", height, 0, 6, "pixels"); - gd.addNumericField("Offset to First Image:", offset, 0, 6, "bytes"); - gd.addNumericField("Number of Images:", nImages, 0, 6, null); - gd.addNumericField("Gap Between Images:", gapBetweenImages, 0, 6, "bytes"); - gd.addCheckbox("White is Zero", whiteIsZero); - gd.addCheckbox("Little-Endian Byte Order", intelByteOrder); - gd.addCheckbox("Open All Files in Folder", openAll); - gd.addCheckbox("Use Virtual Stack", virtual); - gd.showDialog(); - if (gd.wasCanceled()) - return false; - choiceSelection = gd.getNextChoiceIndex(); - width = (int)gd.getNextNumber(); - height = (int)gd.getNextNumber(); - offset = (long)gd.getNextNumber(); - nImages = (int)gd.getNextNumber(); - gapBetweenImages = (int)gd.getNextNumber(); - whiteIsZero = gd.getNextBoolean(); - intelByteOrder = gd.getNextBoolean(); - openAll = gd.getNextBoolean(); - virtual = gd.getNextBoolean(); - IJ.register(ImportDialog.class); - return true; - } - - /** Opens all the images in the directory. */ - void openAll(String[] list, FileInfo fi) { - //StringSorter.sort(list); - FolderOpener fo = new FolderOpener(); - list = fo.trimFileList(list); - list = fo.sortFileList(list); - if (list==null) return; - ImageStack stack=null; - ImagePlus imp=null; - double min = Double.MAX_VALUE; - double max = -Double.MAX_VALUE; - for (int i=0; imax) max = ip.getMax(); - stack.addSlice(list[i], ip); - } - catch(OutOfMemoryError e) { - IJ.outOfMemory("OpenAll"); - stack.trim(); - break; - } - IJ.showStatus((stack.getSize()+1) + ": " + list[i]); - } - } - if (stack!=null) { - imp = new ImagePlus("Imported Stack", stack); - if (imp.getBitDepth()==16 || imp.getBitDepth()==32) - imp.getProcessor().setMinAndMax(min, max); - Calibration cal = imp.getCalibration(); - if (fi.fileType==FileInfo.GRAY16_SIGNED) { - double[] coeff = new double[2]; - coeff[0] = -32768.0; - coeff[1] = 1.0; - cal.setFunction(Calibration.STRAIGHT_LINE, coeff, "gray value"); - } - imp.show(); - } - } - - /** Displays the dialog and opens the specified image or images. - Does nothing if the dialog is canceled. */ - public void openImage() { - FileInfo fi = getFileInfo(); - if (fi==null) return; - if (openAll) { - if (virtual) { - virtual = false; - IJ.error("Import Raw", "\"Open All\" does not currently support virtual stacks"); - return; - } - String[] list = new File(directory).list(); - if (list==null) return; - openAll(list, fi); - } else if (virtual) - new FileInfoVirtualStack(fi); - else { - FileOpener fo = new FileOpener(fi); - fo.open(); - } - } - - /** Displays the dialog and returns a FileInfo object that can be used to - open the image. Returns null if the dialog is canceled. The fileName - and directory fields are null if the no argument constructor was used. */ - public FileInfo getFileInfo() { - if (!showDialog()) - return null; - String imageType = types[choiceSelection]; - FileInfo fi = new FileInfo(); - fi.fileFormat = fi.RAW; - fi.fileName = fileName; - fi.directory = directory; - fi.width = width; - fi.height = height; - if (offset>2147483647) - fi.longOffset = offset; - else - fi.offset = (int)offset; - fi.nImages = nImages; - fi.gapBetweenImages = gapBetweenImages; - fi.intelByteOrder = intelByteOrder; - fi.whiteIsZero = whiteIsZero; - if (imageType.equals("8-bit")) - fi.fileType = FileInfo.GRAY8; - else if (imageType.equals("16-bit Signed")) - fi.fileType = FileInfo.GRAY16_SIGNED; - else if (imageType.equals("16-bit Unsigned")) - fi.fileType = FileInfo.GRAY16_UNSIGNED; - else if (imageType.equals("32-bit Signed")) - fi.fileType = FileInfo.GRAY32_INT; - else if (imageType.equals("32-bit Unsigned")) - fi.fileType = FileInfo.GRAY32_UNSIGNED; - else if (imageType.equals("32-bit Real")) - fi.fileType = FileInfo.GRAY32_FLOAT; - else if (imageType.equals("64-bit Real")) - fi.fileType = FileInfo.GRAY64_FLOAT; - else if (imageType.equals("24-bit RGB")) - fi.fileType = FileInfo.RGB; - else if (imageType.equals("24-bit RGB Planar")) - fi.fileType = FileInfo.RGB_PLANAR; - else if (imageType.equals("24-bit BGR")) - fi.fileType = FileInfo.BGR; - else if (imageType.equals("24-bit Integer")) - fi.fileType = FileInfo.GRAY24_UNSIGNED; - else if (imageType.equals("32-bit ARGB")) - fi.fileType = FileInfo.ARGB; - else if (imageType.equals("32-bit ABGR")) - fi.fileType = FileInfo.ABGR; - else if (imageType.equals("1-bit Bitmap")) - fi.fileType = FileInfo.BITMAP; - else - fi.fileType = FileInfo.GRAY8; - if (IJ.debugMode) IJ.log("ImportDialog: "+fi); - lastFileInfo = (FileInfo)fi.clone(); - return fi; - } - - /** Called once when ImageJ quits. */ - public static void savePreferences(Properties prefs) { - prefs.put(TYPE, Integer.toString(choiceSelection)); - prefs.put(WIDTH, Integer.toString(width)); - prefs.put(HEIGHT, Integer.toString(height)); - prefs.put(OFFSET, Integer.toString(offset>2147483647?0:(int)offset)); - prefs.put(N, Integer.toString(nImages)); - prefs.put(GAP, Integer.toString(gapBetweenImages)); - int options = 0; - if (whiteIsZero) - options |= WHITE_IS_ZERO; - if (intelByteOrder) - options |= INTEL_BYTE_ORDER; - //if (openAll) - // options |= OPEN_ALL; - prefs.put(OPTIONS, Integer.toString(options)); - } - - /** Returns the FileInfo object used to import the last raw image, - or null if a raw image has not been imported. */ - public static FileInfo getLastFileInfo() { - return lastFileInfo; - } - +package ij.io; + +import java.awt.*; +import java.awt.image.*; +import java.io.*; +import java.util.*; +import ij.*; +import ij.gui.*; +import ij.process.*; +import ij.util.StringSorter; +import ij.plugin.frame.Recorder; +import ij.plugin.FolderOpener; +import ij.plugin.FileInfoVirtualStack; +import ij.measure.Calibration; + + +/** This is a dialog box used to imports raw 8, 16, 24 and 32-bit images. */ +public class ImportDialog { + private String fileName; + private String directory; + static final String TYPE = "raw.type"; + static final String WIDTH = "raw.width"; + static final String HEIGHT = "raw.height"; + static final String OFFSET = "raw.offset"; + static final String N = "raw.n"; + static final String GAP = "raw.gap"; + static final String OPTIONS = "raw.options"; + static final int WHITE_IS_ZERO = 1; + static final int INTEL_BYTE_ORDER = 2; + static final int OPEN_ALL = 4; + + // default settings + private static int choiceSelection = Prefs.getInt(TYPE,0); + private static int width = Prefs.getInt(WIDTH,512); + private static int height = Prefs.getInt(HEIGHT,512); + private static long offset = Prefs.getInt(OFFSET,0); + private static int nImages = Prefs.getInt(N,1); + private static int gapBetweenImages = Prefs.getInt(GAP,0); + private static int options; + private static boolean whiteIsZero,intelByteOrder; + private static boolean virtual; + private boolean openAll; + private static FileInfo lastFileInfo; + private static String[] types = {"8-bit", "16-bit Signed", "16-bit Unsigned", + "32-bit Signed", "32-bit Unsigned", "32-bit Real", "64-bit Real", "24-bit RGB", + "24-bit RGB Planar", "24-bit BGR", "24-bit Integer", "32-bit ARGB", "32-bit ABGR", "1-bit Bitmap"}; + + static { + options = Prefs.getInt(OPTIONS,0); + whiteIsZero = (options&WHITE_IS_ZERO)!=0; + intelByteOrder = (options&INTEL_BYTE_ORDER)!=0; + //openAll = (options&OPEN_ALL)!=0; + } + + public ImportDialog(String fileName, String directory) { + this.fileName = fileName; + this.directory = directory; + IJ.showStatus("Importing: " + fileName); + } + + public ImportDialog() { + } + + boolean showDialog() { + if (choiceSelection>=types.length) + choiceSelection = 0; + GenericDialog gd = new GenericDialog("Import...", IJ.getInstance()); + gd.addChoice("Image Type:", types, types[choiceSelection]); + gd.addNumericField("Width:", width, 0, 6, "pixels"); + gd.addNumericField("Height:", height, 0, 6, "pixels"); + gd.addNumericField("Offset to First Image:", offset, 0, 6, "bytes"); + gd.addNumericField("Number of Images:", nImages, 0, 6, null); + gd.addNumericField("Gap Between Images:", gapBetweenImages, 0, 6, "bytes"); + gd.addCheckbox("White is Zero", whiteIsZero); + gd.addCheckbox("Little-Endian Byte Order", intelByteOrder); + gd.addCheckbox("Open All Files in Folder", openAll); + gd.addCheckbox("Use Virtual Stack", virtual); + gd.showDialog(); + if (gd.wasCanceled()) + return false; + choiceSelection = gd.getNextChoiceIndex(); + width = (int)gd.getNextNumber(); + height = (int)gd.getNextNumber(); + offset = (long)gd.getNextNumber(); + nImages = (int)gd.getNextNumber(); + gapBetweenImages = (int)gd.getNextNumber(); + whiteIsZero = gd.getNextBoolean(); + intelByteOrder = gd.getNextBoolean(); + openAll = gd.getNextBoolean(); + virtual = gd.getNextBoolean(); + IJ.register(ImportDialog.class); + return true; + } + + /** Opens all the images in the directory. */ + void openAll(String[] list, FileInfo fi) { + //StringSorter.sort(list); + FolderOpener fo = new FolderOpener(); + list = fo.trimFileList(list); + list = fo.sortFileList(list); + if (list==null) return; + ImageStack stack=null; + ImagePlus imp=null; + double min = Double.MAX_VALUE; + double max = -Double.MAX_VALUE; + for (int i=0; imax) max = ip.getMax(); + stack.addSlice(list[i], ip); + } + catch(OutOfMemoryError e) { + IJ.outOfMemory("OpenAll"); + stack.trim(); + break; + } + IJ.showStatus((stack.getSize()+1) + ": " + list[i]); + } + } + if (stack!=null) { + imp = new ImagePlus("Imported Stack", stack); + if (imp.getBitDepth()==16 || imp.getBitDepth()==32) + imp.getProcessor().setMinAndMax(min, max); + Calibration cal = imp.getCalibration(); + if (fi.fileType==FileInfo.GRAY16_SIGNED) { + double[] coeff = new double[2]; + coeff[0] = -32768.0; + coeff[1] = 1.0; + cal.setFunction(Calibration.STRAIGHT_LINE, coeff, "gray value"); + } + imp.show(); + } + } + + /** Displays the dialog and opens the specified image or images. + Does nothing if the dialog is canceled. */ + public void openImage() { + FileInfo fi = getFileInfo(); + if (fi==null) return; + if (openAll) { + if (virtual) { + virtual = false; + IJ.error("Import Raw", "\"Open All\" does not currently support virtual stacks"); + return; + } + String[] list = new File(directory).list(); + if (list==null) return; + openAll(list, fi); + } else if (virtual) + new FileInfoVirtualStack(fi); + else { + FileOpener fo = new FileOpener(fi); + fo.open(); + } + } + + /** Displays the dialog and returns a FileInfo object that can be used to + open the image. Returns null if the dialog is canceled. The fileName + and directory fields are null if the no argument constructor was used. */ + public FileInfo getFileInfo() { + if (!showDialog()) + return null; + String imageType = types[choiceSelection]; + FileInfo fi = new FileInfo(); + fi.fileFormat = fi.RAW; + fi.fileName = fileName; + fi.directory = directory; + fi.width = width; + fi.height = height; + if (offset>2147483647) + fi.longOffset = offset; + else + fi.offset = (int)offset; + fi.nImages = nImages; + fi.gapBetweenImages = gapBetweenImages; + fi.intelByteOrder = intelByteOrder; + fi.whiteIsZero = whiteIsZero; + if (imageType.equals("8-bit")) + fi.fileType = FileInfo.GRAY8; + else if (imageType.equals("16-bit Signed")) + fi.fileType = FileInfo.GRAY16_SIGNED; + else if (imageType.equals("16-bit Unsigned")) + fi.fileType = FileInfo.GRAY16_UNSIGNED; + else if (imageType.equals("32-bit Signed")) + fi.fileType = FileInfo.GRAY32_INT; + else if (imageType.equals("32-bit Unsigned")) + fi.fileType = FileInfo.GRAY32_UNSIGNED; + else if (imageType.equals("32-bit Real")) + fi.fileType = FileInfo.GRAY32_FLOAT; + else if (imageType.equals("64-bit Real")) + fi.fileType = FileInfo.GRAY64_FLOAT; + else if (imageType.equals("24-bit RGB")) + fi.fileType = FileInfo.RGB; + else if (imageType.equals("24-bit RGB Planar")) + fi.fileType = FileInfo.RGB_PLANAR; + else if (imageType.equals("24-bit BGR")) + fi.fileType = FileInfo.BGR; + else if (imageType.equals("24-bit Integer")) + fi.fileType = FileInfo.GRAY24_UNSIGNED; + else if (imageType.equals("32-bit ARGB")) + fi.fileType = FileInfo.ARGB; + else if (imageType.equals("32-bit ABGR")) + fi.fileType = FileInfo.ABGR; + else if (imageType.equals("1-bit Bitmap")) + fi.fileType = FileInfo.BITMAP; + else + fi.fileType = FileInfo.GRAY8; + if (IJ.debugMode) IJ.log("ImportDialog: "+fi); + lastFileInfo = (FileInfo)fi.clone(); + return fi; + } + + /** Called once when ImageJ quits. */ + public static void savePreferences(Properties prefs) { + prefs.put(TYPE, Integer.toString(choiceSelection)); + prefs.put(WIDTH, Integer.toString(width)); + prefs.put(HEIGHT, Integer.toString(height)); + prefs.put(OFFSET, Integer.toString(offset>2147483647?0:(int)offset)); + prefs.put(N, Integer.toString(nImages)); + prefs.put(GAP, Integer.toString(gapBetweenImages)); + int options = 0; + if (whiteIsZero) + options |= WHITE_IS_ZERO; + if (intelByteOrder) + options |= INTEL_BYTE_ORDER; + //if (openAll) + // options |= OPEN_ALL; + prefs.put(OPTIONS, Integer.toString(options)); + } + + /** Returns the FileInfo object used to import the last raw image, + or null if a raw image has not been imported. */ + public static FileInfo getLastFileInfo() { + return lastFileInfo; + } + } \ No newline at end of file diff --git a/ij/io/OpenDialog.java b/ij/io/OpenDialog.java index b971e9b74..f10503e0f 100644 --- a/ij/io/OpenDialog.java +++ b/ij/io/OpenDialog.java @@ -1,226 +1,226 @@ -package ij.io; -import ij.*; -import ij.gui.*; -import ij.plugin.frame.Recorder; -import ij.util.Java2; -import ij.macro.Interpreter; -import java.awt.*; -import java.io.*; -import javax.swing.*; -import javax.swing.filechooser.*; - -/** This class displays a dialog window from - which the user can select an input file. */ - public class OpenDialog { - - private String dir; - private String name; - private boolean recordPath; - private static String defaultDirectory; - private static Frame sharedFrame; - private String title; - private static String lastDir, lastName; - - - /** Displays a file open dialog with 'title' as - the title. If 'path' is non-blank, it is - used and the dialog is not displayed. Uses - and updates the ImageJ default directory. */ - public OpenDialog(String title, String path) { - String macroOptions = Macro.getOptions(); - if (macroOptions!=null && (path==null||path.equals(""))) { - path = Macro.getValue(macroOptions, title, path); - if (path==null || path.equals("")) - path = Macro.getValue(macroOptions, "path", path); - if ((path==null || path.equals("")) && title!=null && title.equals("Open As String")) - path = Macro.getValue(macroOptions, "OpenAsString", path); - if (path!=null && path.indexOf(".")==-1 && !((new File(path)).exists())) { - // Is 'path' a macro variable? - if (path.startsWith("&")) path=path.substring(1); - Interpreter interp = Interpreter.getInstance(); - String path2 = interp!=null?interp.getStringVariable(path):null; - if (path2!=null) path = path2; - } - } - if (path==null || path.equals("")) { - if (Prefs.useJFileChooser) - jOpen(title, getDefaultDirectory(), null); - else - open(title, getDefaultDirectory(), null); - if (name!=null) defaultDirectory = dir; - this.title = title; - recordPath = true; - } else { - decodePath(path); - recordPath = IJ.macroRunning(); - } - IJ.register(OpenDialog.class); - } - - /** Displays a file open dialog, using the specified - default directory and file name. */ - public OpenDialog(String title, String defaultDir, String defaultName) { - String path = null; - String macroOptions = Macro.getOptions(); - if (macroOptions!=null) - path = Macro.getValue(macroOptions, title, path); - if (path!=null) - decodePath(path); - else { - if (Prefs.useJFileChooser) - jOpen(title, defaultDir, defaultName); - else - open(title, defaultDir, defaultName); - this.title = title; - recordPath = true; - } - } - - // Uses JFileChooser to display file open dialog box. - void jOpen(String title, String path, String fileName) { - Java2.setSystemLookAndFeel(); - if (EventQueue.isDispatchThread()) - jOpenDispatchThread(title, path, fileName); - else - jOpenInvokeAndWait(title, path, fileName); - } - - // Uses the JFileChooser class to display the dialog box. - // Assumes we are running on the event dispatch thread - void jOpenDispatchThread(String title, String path, final String fileName) { - JFileChooser fc = new JFileChooser(); - fc.setDialogTitle(title); - File fdir = null; - if (path!=null) - fdir = new File(path); - if (fdir!=null) - fc.setCurrentDirectory(fdir); - if (fileName!=null) - fc.setSelectedFile(new File(fileName)); - int returnVal = fc.showOpenDialog(IJ.getInstance()); - if (returnVal!=JFileChooser.APPROVE_OPTION) - {Macro.abort(); return;} - File file = fc.getSelectedFile(); - if (file==null) - {Macro.abort(); return;} - name = file.getName(); - dir = fc.getCurrentDirectory().getPath()+File.separator; - } - - // Run JFileChooser on event dispatch thread to avoid deadlocks - void jOpenInvokeAndWait(final String title, final String path, final String fileName) { - try { - EventQueue.invokeAndWait(new Runnable() { - public void run() { - JFileChooser fc = new JFileChooser(); - fc.setDialogTitle(title); - File fdir = null; - if (path!=null) - fdir = new File(path); - if (fdir!=null) - fc.setCurrentDirectory(fdir); - if (fileName!=null) - fc.setSelectedFile(new File(fileName)); - int returnVal = fc.showOpenDialog(IJ.getInstance()); - if (returnVal!=JFileChooser.APPROVE_OPTION) - {Macro.abort(); return;} - File file = fc.getSelectedFile(); - if (file==null) - {Macro.abort(); return;} - name = file.getName(); - dir = fc.getCurrentDirectory().getPath()+File.separator; - } - }); - } catch (Exception e) {} - } - - // Uses the AWT FileDialog class to display the dialog box - void open(String title, String path, String fileName) { - Frame parent = IJ.getInstance(); - if (parent==null) { - if (sharedFrame==null) sharedFrame = new Frame(); - parent = sharedFrame; - } - FileDialog fd = new FileDialog(parent, title); - if (path!=null) - fd.setDirectory(path); - if (fileName!=null) - fd.setFile(fileName); - //GUI.center(fd); - fd.show(); - name = fd.getFile(); - if (name==null) { - if (IJ.isMacOSX()) - System.setProperty("apple.awt.fileDialogForDirectories", "false"); - Macro.abort(); - } else - dir = fd.getDirectory(); - } - - void decodePath(String path) { - int i = path.lastIndexOf('/'); - if (i==-1) - i = path.lastIndexOf('\\'); - if (i>0) { - dir = path.substring(0, i+1); - name = path.substring(i+1); - } else { - dir = ""; - name = path; - } - } - - /** Returns the selected directory. */ - public String getDirectory() { - lastDir = dir; - return dir; - } - - /** Returns the selected file name. */ - public String getFileName() { - if (Recorder.record && recordPath) - Recorder.recordPath(title, dir+name); - lastName = name; - return name; - } - - /** Returns the current working directory, which may be null. The - returned string always ends with the separator character ("/" or "\").*/ - public static String getDefaultDirectory() { - if (defaultDirectory==null) - defaultDirectory = Prefs.getDefaultDirectory(); - return defaultDirectory; - } - - /** Sets the current working directory. */ - public static void setDefaultDirectory(String defaultDir) { - defaultDirectory = defaultDir; - if (!defaultDirectory.endsWith(File.separator)) - defaultDirectory = defaultDirectory + File.separator; - } - - /** Returns the path to the last directory opened by the user - using a file open or file save dialog, or using drag and drop. - Returns null if the users has not opened a file. */ - public static String getLastDirectory() { - return lastDir; - } - - /** Sets the path to the directory containing the last file opened by the user. */ - public static void setLastDirectory(String dir) { - lastDir = dir; - } - - /** Returns the name of the last file opened by the user - using a file open or file save dialog, or using drag and drop. - Returns null if the users has not opened a file. */ - public static String getLastName() { - return lastName; - } - - /** Sets the name of the last file opened by the user. */ - public static void setLastName(String name) { - lastName = name; - } - -} +package ij.io; +import ij.*; +import ij.gui.*; +import ij.plugin.frame.Recorder; +import ij.util.Java2; +import ij.macro.Interpreter; +import java.awt.*; +import java.io.*; +import javax.swing.*; +import javax.swing.filechooser.*; + +/** This class displays a dialog window from + which the user can select an input file. */ + public class OpenDialog { + + private String dir; + private String name; + private boolean recordPath; + private static String defaultDirectory; + private static Frame sharedFrame; + private String title; + private static String lastDir, lastName; + + + /** Displays a file open dialog with 'title' as + the title. If 'path' is non-blank, it is + used and the dialog is not displayed. Uses + and updates the ImageJ default directory. */ + public OpenDialog(String title, String path) { + String macroOptions = Macro.getOptions(); + if (macroOptions!=null && (path==null||path.equals(""))) { + path = Macro.getValue(macroOptions, title, path); + if (path==null || path.equals("")) + path = Macro.getValue(macroOptions, "path", path); + if ((path==null || path.equals("")) && title!=null && title.equals("Open As String")) + path = Macro.getValue(macroOptions, "OpenAsString", path); + if (path!=null && path.indexOf(".")==-1 && !((new File(path)).exists())) { + // Is 'path' a macro variable? + if (path.startsWith("&")) path=path.substring(1); + Interpreter interp = Interpreter.getInstance(); + String path2 = interp!=null?interp.getStringVariable(path):null; + if (path2!=null) path = path2; + } + } + if (path==null || path.equals("")) { + if (Prefs.useJFileChooser) + jOpen(title, getDefaultDirectory(), null); + else + open(title, getDefaultDirectory(), null); + if (name!=null) defaultDirectory = dir; + this.title = title; + recordPath = true; + } else { + decodePath(path); + recordPath = IJ.macroRunning(); + } + IJ.register(OpenDialog.class); + } + + /** Displays a file open dialog, using the specified + default directory and file name. */ + public OpenDialog(String title, String defaultDir, String defaultName) { + String path = null; + String macroOptions = Macro.getOptions(); + if (macroOptions!=null) + path = Macro.getValue(macroOptions, title, path); + if (path!=null) + decodePath(path); + else { + if (Prefs.useJFileChooser) + jOpen(title, defaultDir, defaultName); + else + open(title, defaultDir, defaultName); + this.title = title; + recordPath = true; + } + } + + // Uses JFileChooser to display file open dialog box. + void jOpen(String title, String path, String fileName) { + Java2.setSystemLookAndFeel(); + if (EventQueue.isDispatchThread()) + jOpenDispatchThread(title, path, fileName); + else + jOpenInvokeAndWait(title, path, fileName); + } + + // Uses the JFileChooser class to display the dialog box. + // Assumes we are running on the event dispatch thread + void jOpenDispatchThread(String title, String path, final String fileName) { + JFileChooser fc = new JFileChooser(); + fc.setDialogTitle(title); + File fdir = null; + if (path!=null) + fdir = new File(path); + if (fdir!=null) + fc.setCurrentDirectory(fdir); + if (fileName!=null) + fc.setSelectedFile(new File(fileName)); + int returnVal = fc.showOpenDialog(IJ.getInstance()); + if (returnVal!=JFileChooser.APPROVE_OPTION) + {Macro.abort(); return;} + File file = fc.getSelectedFile(); + if (file==null) + {Macro.abort(); return;} + name = file.getName(); + dir = fc.getCurrentDirectory().getPath()+File.separator; + } + + // Run JFileChooser on event dispatch thread to avoid deadlocks + void jOpenInvokeAndWait(final String title, final String path, final String fileName) { + try { + EventQueue.invokeAndWait(new Runnable() { + public void run() { + JFileChooser fc = new JFileChooser(); + fc.setDialogTitle(title); + File fdir = null; + if (path!=null) + fdir = new File(path); + if (fdir!=null) + fc.setCurrentDirectory(fdir); + if (fileName!=null) + fc.setSelectedFile(new File(fileName)); + int returnVal = fc.showOpenDialog(IJ.getInstance()); + if (returnVal!=JFileChooser.APPROVE_OPTION) + {Macro.abort(); return;} + File file = fc.getSelectedFile(); + if (file==null) + {Macro.abort(); return;} + name = file.getName(); + dir = fc.getCurrentDirectory().getPath()+File.separator; + } + }); + } catch (Exception e) {} + } + + // Uses the AWT FileDialog class to display the dialog box + void open(String title, String path, String fileName) { + Frame parent = IJ.getInstance(); + if (parent==null) { + if (sharedFrame==null) sharedFrame = new Frame(); + parent = sharedFrame; + } + FileDialog fd = new FileDialog(parent, title); + if (path!=null) + fd.setDirectory(path); + if (fileName!=null) + fd.setFile(fileName); + //GUI.center(fd); + fd.show(); + name = fd.getFile(); + if (name==null) { + if (IJ.isMacOSX()) + System.setProperty("apple.awt.fileDialogForDirectories", "false"); + Macro.abort(); + } else + dir = fd.getDirectory(); + } + + void decodePath(String path) { + int i = path.lastIndexOf('/'); + if (i==-1) + i = path.lastIndexOf('\\'); + if (i>0) { + dir = path.substring(0, i+1); + name = path.substring(i+1); + } else { + dir = ""; + name = path; + } + } + + /** Returns the selected directory. */ + public String getDirectory() { + lastDir = dir; + return dir; + } + + /** Returns the selected file name. */ + public String getFileName() { + if (Recorder.record && recordPath) + Recorder.recordPath(title, dir+name); + lastName = name; + return name; + } + + /** Returns the current working directory, which may be null. The + returned string always ends with the separator character ("/" or "\").*/ + public static String getDefaultDirectory() { + if (defaultDirectory==null) + defaultDirectory = Prefs.getDefaultDirectory(); + return defaultDirectory; + } + + /** Sets the current working directory. */ + public static void setDefaultDirectory(String defaultDir) { + defaultDirectory = defaultDir; + if (!defaultDirectory.endsWith(File.separator)) + defaultDirectory = defaultDirectory + File.separator; + } + + /** Returns the path to the last directory opened by the user + using a file open or file save dialog, or using drag and drop. + Returns null if the users has not opened a file. */ + public static String getLastDirectory() { + return lastDir; + } + + /** Sets the path to the directory containing the last file opened by the user. */ + public static void setLastDirectory(String dir) { + lastDir = dir; + } + + /** Returns the name of the last file opened by the user + using a file open or file save dialog, or using drag and drop. + Returns null if the users has not opened a file. */ + public static String getLastName() { + return lastName; + } + + /** Sets the name of the last file opened by the user. */ + public static void setLastName(String name) { + lastName = name; + } + +} diff --git a/ij/io/Opener.java b/ij/io/Opener.java index d56147c92..70d0aba86 100644 --- a/ij/io/Opener.java +++ b/ij/io/Opener.java @@ -1,942 +1,942 @@ -package ij.io; -import ij.*; -import ij.gui.*; -import ij.process.*; -import ij.plugin.frame.*; -import ij.plugin.DICOM; -import ij.plugin.AVI_Reader; -import ij.text.TextWindow; -import ij.util.Java2; -import java.awt.*; -import java.awt.image.*; -import java.io.*; -import java.net.URL; -import java.net.*; -import java.util.Hashtable; -import java.util.zip.*; -import java.util.Locale; -import javax.swing.*; -import javax.swing.filechooser.*; -import java.awt.event.KeyEvent; -import javax.imageio.ImageIO; - -/** Opens tiff (and tiff stacks), dicom, fits, pgm, jpeg, bmp or - gif images, and look-up tables, using a file open dialog or a path. - Calls HandleExtraFileTypes plugin if the file type is unrecognised. */ -public class Opener { - - public static final int UNKNOWN=0,TIFF=1,DICOM=2,FITS=3,PGM=4,JPEG=5, - GIF=6,LUT=7,BMP=8,ZIP=9,JAVA_OR_TEXT=10,ROI=11,TEXT=12,PNG=13, - TIFF_AND_DICOM=14,CUSTOM=15, AVI=16, OJJ=17; // don't forget to also update 'types' - private static final String[] types = {"unknown","tif","dcm","fits","pgm", - "jpg","gif","lut","bmp","zip","java/txt","roi","txt","png","t&d","custom","ojj"}; - private static String defaultDirectory = null; - private static int fileType; - private boolean error; - private boolean isRGB48; - private boolean silentMode; - private String omDirectory; - private File[] omFiles; - private static boolean openUsingPlugins; - private static boolean bioformats; - - static { - Hashtable commands = Menus.getCommands(); - bioformats = commands!=null && commands.get("Bio-Formats Importer")!=null; - } - - public Opener() { - } - - /** Displays a file open dialog box and then opens the tiff, dicom, - fits, pgm, jpeg, bmp, gif, lut, roi, or text file selected by - the user. Displays an error message if the selected file is not - in one of the supported formats. This is the method that - ImageJ's File/Open command uses to open files. */ - public void open() { - OpenDialog od = new OpenDialog("Open", ""); - String directory = od.getDirectory(); - String name = od.getFileName(); - if (name!=null) { - String path = directory+name; - error = false; - open(path); - if (!error) Menus.addOpenRecentItem(path); - } - } - - /** Displays a JFileChooser and then opens the tiff, dicom, - fits, pgm, jpeg, bmp, gif, lut, roi, or text files selected by - the user. Displays error messages if one or more of the selected - files is not in one of the supported formats. This is the method - that ImageJ's File/Open command uses to open files if - "Open/Save Using JFileChooser" is checked in EditOptions/Misc. */ - public void openMultiple() { - Java2.setSystemLookAndFeel(); - // run JFileChooser in a separate thread to avoid possible thread deadlocks - try { - EventQueue.invokeAndWait(new Runnable() { - public void run() { - JFileChooser fc = new JFileChooser(); - fc.setMultiSelectionEnabled(true); - File dir = null; - String sdir = OpenDialog.getDefaultDirectory(); - if (sdir!=null) - dir = new File(sdir); - if (dir!=null) - fc.setCurrentDirectory(dir); - int returnVal = fc.showOpenDialog(IJ.getInstance()); - if (returnVal!=JFileChooser.APPROVE_OPTION) - return; - omFiles = fc.getSelectedFiles(); - if (omFiles.length==0) { // getSelectedFiles does not work on some JVMs - omFiles = new File[1]; - omFiles[0] = fc.getSelectedFile(); - } - omDirectory = fc.getCurrentDirectory().getPath()+File.separator; - } - }); - } catch (Exception e) {} - if (omDirectory==null) return; - OpenDialog.setDefaultDirectory(omDirectory); - for (int i=0; i=28000) { - String osName = System.getProperty("os.name"); - if (osName.equals("Windows 95") || osName.equals("Windows 98") || osName.equals("Windows Me")) - maxSize = 60000; - } - if (size64) - path = (new File(path)).getName(); - if (path.length()<=64) - msg += " \n \n"+path; - } - if (openUsingPlugins) - msg += "\n \nNOTE: The \"OpenUsingPlugins\" option is set."; - IJ.wait(IJ.isMacro()?500:100); // work around for OS X thread deadlock problem - IJ.error("Opener", msg); - error = true; - break; - } - } - } - - private boolean isText(String path) { - if (path.endsWith(".txt") || path.endsWith(".ijm") || path.endsWith(".java") - || path.endsWith(".js") || path.endsWith(".html") || path.endsWith(".htm") - || path.endsWith("/")) - return true; - int lastSlash = path.lastIndexOf("/"); - if (lastSlash==-1) lastSlash = 0; - int lastDot = path.lastIndexOf("."); - if (lastDot==-1 || lastDot6) - return true; // no extension - else - return false; - } - - /** Opens the specified file and adds it to the File/Open Recent menu. - Returns true if the file was opened successfully. */ - public boolean openAndAddToRecent(String path) { - open(path); - if (!error) - Menus.addOpenRecentItem(path); - return error; - } - - /** Attempts to open the specified file as a tiff, bmp, dicom, fits, - pgm, gif or jpeg image. Returns an ImagePlus object if successful. - Modified by Gregory Jefferis to call HandleExtraFileTypes plugin if - the file type is unrecognised. */ - public ImagePlus openImage(String directory, String name) { - ImagePlus imp; - FileOpener.setSilentMode(silentMode); - if (directory.length()>0 && !directory.endsWith(Prefs.separator)) - directory += Prefs.separator; - String path = directory+name; - fileType = getFileType(path); - if (IJ.debugMode) - IJ.log("openImage: \""+types[fileType]+"\", "+path); - switch (fileType) { - case TIFF: - imp = openTiff(directory, name); - return imp; - case DICOM: - imp = (ImagePlus)IJ.runPlugIn("ij.plugin.DICOM", path); - if (imp.getWidth()!=0) return imp; else return null; - case TIFF_AND_DICOM: - // "hybrid" files created by GE-Senographe 2000 D */ - imp = openTiff(directory,name); - ImagePlus imp2 = (ImagePlus)IJ.runPlugIn("ij.plugin.DICOM", path); - if (imp!=null) - imp.setProperty("Info",imp2.getProperty("Info")); - return imp; - case FITS: - imp = (ImagePlus)IJ.runPlugIn("ij.plugin.FITS_Reader", path); - if (imp.getWidth()!=0) return imp; else return null; - case PGM: - imp = (ImagePlus)IJ.runPlugIn("ij.plugin.PGM_Reader", path); - if (imp.getWidth()!=0) { - if (imp.getStackSize()==3 && imp.getBitDepth()==16) - imp = new CompositeImage(imp, CompositeImage.COMPOSITE); - return imp; - } else - return null; - case JPEG: case GIF: - imp = openJpegOrGif(directory, name); - if (imp!=null&&imp.getWidth()!=0) return imp; else return null; - case PNG: - imp = openUsingImageIO(directory+name); - if (imp!=null&&imp.getWidth()!=0) return imp; else return null; - case BMP: - imp = (ImagePlus)IJ.runPlugIn("ij.plugin.BMP_Reader", path); - if (imp.getWidth()!=0) return imp; else return null; - case ZIP: - return openZip(path); - case AVI: - AVI_Reader reader = (AVI_Reader)IJ.runPlugIn("ij.plugin.AVI_Reader", path); - return reader.getImagePlus(); - case UNKNOWN: case TEXT: - // Call HandleExtraFileTypes plugin to see if it can handle unknown format - int[] wrap = new int[] {fileType}; - imp = openWithHandleExtraFileTypes(path, wrap); - fileType = wrap[0]; - return imp; - default: - return null; - } - } - - /** Attempts to open the specified file as a tiff, bmp, dicom, fits, - pgm, gif or jpeg. Displays a file open dialog if 'path' is null or - an empty string. Returns an ImagePlus object if successful. */ - public ImagePlus openImage(String path) { - if (path==null || path.equals("")) { - OpenDialog od = new OpenDialog("Open", ""); - String dir = od.getDirectory(); - String name = od.getFileName(); - if (name==null) return null; - path = dir+name; - } - ImagePlus img = null; - if (path.indexOf("://")>0) - img = openURL(path); - else - img = openImage(getDir(path), getName(path)); - return img; - } - - /** Attempts to open the specified url as a tiff, zip compressed tiff, - dicom, gif or jpeg. Tiff file names must end in ".tif", ZIP file names - must end in ".zip" and dicom file names must end in ".dcm". Returns an - ImagePlus object if successful. */ - public ImagePlus openURL(String url) { - try { - String name = ""; - int index = url.lastIndexOf('/'); - if (index==-1) - index = url.lastIndexOf('\\'); - if (index>0) - name = url.substring(index+1); - else - throw new MalformedURLException("Invalid URL: "+url); - if (url.indexOf(" ")!=-1) - url = url.replaceAll(" ", "%20"); - URL u = new URL(url); - IJ.showStatus(""+url); - String lurl = url.toLowerCase(Locale.US); - ImagePlus imp = null; - if (lurl.endsWith(".tif")) - imp = openTiff(u.openStream(), name); - else if (lurl.endsWith(".zip")) - imp = openZipUsingUrl(u); - else if (lurl.endsWith(".jpg") || lurl.endsWith(".gif")) - imp = openJpegOrGifUsingURL(name, u); - else if (lurl.endsWith(".dcm") || lurl.endsWith(".ima")) { - imp = (ImagePlus)IJ.runPlugIn("ij.plugin.DICOM", url); - if (imp!=null && imp.getWidth()==0) imp = null; - } else if (lurl.endsWith(".png")) - imp = openPngUsingURL(name, u); - else { - URLConnection uc = u.openConnection(); - String type = uc.getContentType(); - if (type!=null && (type.equals("image/jpeg")||type.equals("image/gif"))) - imp = openJpegOrGifUsingURL(name, u); - else if (type!=null && type.equals("image/png")) - imp = openPngUsingURL(name, u); - else - imp = openWithHandleExtraFileTypes(url, new int[]{0}); - } - IJ.showStatus(""); - return imp; - } catch (Exception e) { - String msg = e.getMessage(); - if (msg==null || msg.equals("")) - msg = "" + e; - IJ.error("Open URL",msg + "\n \n" + url); - return null; - } - } - - /** Used by open() and IJ.open() to open text URLs. */ - void openTextURL(String url) { - if (url.endsWith(".pdf")||url.endsWith(".zip")) - return; - String text = IJ.openUrlAsString(url); - String name = url.substring(7); - int index = name.lastIndexOf("/"); - int len = name.length(); - if (index==len-1) - name = name.substring(0, len-1); - else if (index!=-1 && index