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; iT7c12>"; // ">>"
- 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; iT7c12>"; // ">>"
+ 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 && index0 && imp.getHeight()>0) {
- fileType[0] = CUSTOM;
- return imp;
- } else {
- if (imp.getWidth()==-1)
- fileType[0] = CUSTOM; // plugin opened image so don't display error
- return null;
- }
- }
-
- /** Opens the ZIP compressed TIFF or DICOM at the specified URL. */
- ImagePlus openZipUsingUrl(URL url) throws IOException {
- URLConnection uc = url.openConnection();
- int fileSize = uc.getContentLength(); // compressed size
- fileSize *=2; // estimate uncompressed size
- InputStream in = uc.getInputStream();
- ZipInputStream zin = new ZipInputStream(in);
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- byte[] buf = new byte[4096];
- ZipEntry entry = zin.getNextEntry();
- if (entry==null)
- return null;
- String name = entry.getName();
- //double fileSize = entry.getSize(); //returns -1
- if (!(name.endsWith(".tif")||name.endsWith(".dcm")))
- throw new IOException("This ZIP archive does not appear to contain a .tif or .dcm file");
- if (name.endsWith(".dcm")) {
- DICOM dcm = new DICOM(zin);
- dcm.run(name);
- return dcm;
- } else
- return openTiff(zin, name);
- }
-
- ImagePlus openJpegOrGifUsingURL(String title, URL url) {
- if (url==null) return null;
- Image img = Toolkit.getDefaultToolkit().createImage(url);
- if (img!=null) {
- ImagePlus imp = new ImagePlus(title, img);
- return imp;
- } else
- return null;
- }
-
- ImagePlus openPngUsingURL(String title, URL url) {
- if (url==null) return null;
- Image img = null;
- try {
- img = ImageIO.read(url);
- } catch (IOException e) {
- IJ.log(""+e);
- }
- if (img!=null) {
- ImagePlus imp = new ImagePlus(title, img);
- return imp;
- } else
- return null;
- }
-
- ImagePlus openJpegOrGif(String dir, String name) {
- ImagePlus imp = null;
- Image img = Toolkit.getDefaultToolkit().createImage(dir+name);
- if (img!=null) {
- try {
- imp = new ImagePlus(name, img);
- } catch (IllegalStateException e) {
- return null; // error loading image
- }
- if (imp.getType()==ImagePlus.COLOR_RGB)
- convertGrayJpegTo8Bits(imp);
- FileInfo fi = new FileInfo();
- fi.fileFormat = fi.GIF_OR_JPG;
- fi.fileName = name;
- fi.directory = dir;
- imp.setFileInfo(fi);
- }
- return imp;
- }
-
- ImagePlus openUsingImageIO(String path) {
- ImagePlus imp = null;
- BufferedImage img = null;
- File f = new File(path);
- try {
- img = ImageIO.read(f);
- } catch (Exception e) {
- IJ.error("Open Using ImageIO", ""+e);
- }
- if (img==null) return null;
- imp = new ImagePlus(f.getName(), img);
- FileInfo fi = new FileInfo();
- fi.fileFormat = fi.IMAGEIO;
- fi.fileName = f.getName();
- fi.directory = f.getParent()+File.separator;
- imp.setFileInfo(fi);
- return imp;
- }
-
- /** If this image is grayscale, convert it to 8-bits. */
- public static void convertGrayJpegTo8Bits(ImagePlus imp) {
- ImageProcessor ip = imp.getProcessor();
- int width = ip.getWidth();
- int height = ip.getHeight();
- int[] pixels = (int[])ip.getPixels();
- int c,r,g,b,offset;
- for (int y=0; y<(height-8); y++) {
- offset = y*width;
- for (int x=0; x<(width-8); x++) {
- c = pixels[offset+x];
- r = (c&0xff0000)>>16;
- g = (c&0xff00)>>8;
- b = c&0xff;
- if (!((r==g)&&(g==b))) {
- //IJ.write("count: "+count+" "+r+" "+g+" "+b);
- return;
- }
- }
- //count++;
- }
- IJ.showStatus("Converting to 8-bits");
- new ImageConverter(imp).convertToGray8();
- }
-
- /** Are all the images in this file the same size and type? */
- boolean allSameSizeAndType(FileInfo[] info) {
- boolean sameSizeAndType = true;
- boolean contiguous = true;
- int startingOffset = info[0].offset;
- int size = info[0].width*info[0].height*info[0].getBytesPerPixel();
- for (int i=1; i1 && !allSameSizeAndType(info))
- return null;
- FileInfo fi = info[0];
- if (fi.nImages>1)
- return new FileOpener(fi).open(false); // open contiguous images as stack
- else {
- ColorModel cm = createColorModel(fi);
- ImageStack stack = new ImageStack(fi.width, fi.height, cm);
- Object pixels = null;
- int skip = fi.offset;
- int imageSize = fi.width*fi.height*fi.getBytesPerPixel();
- if (info[0].fileType==FileInfo.GRAY12_UNSIGNED) {
- imageSize = (int)(fi.width*fi.height*1.5);
- if ((imageSize&1)==1) imageSize++; // add 1 if odd
- } if (info[0].fileType==FileInfo.BITMAP) {
- int scan=(int)Math.ceil(fi.width/8.0);
- imageSize = scan*fi.height;
- }
- int loc = 0;
- int nChannels = 1;
- try {
- InputStream is = createInputStream(fi);
- ImageReader reader = new ImageReader(fi);
- IJ.resetEscape();
- for (int i=0; i=FileInfo.LZW) {
- fi.stripOffsets = info[i].stripOffsets;
- fi.stripLengths = info[i].stripLengths;
- }
- if (info[i].samplesPerPixel>1 && !(info[i].getBytesPerPixel()==3||info[i].getBytesPerPixel()==6)) {
- nChannels = fi.samplesPerPixel;
- channels = new Object[nChannels];
- for (int c=0; c=FileInfo.LZW) skip = 0;
- if (skip<0)
- throw new IOException("Images are not in order");
- }
- if (fi.fileType==FileInfo.RGB48) {
- Object[] pixels2 = (Object[])pixels;
- stack.addSlice(null, pixels2[0]);
- stack.addSlice(null, pixels2[1]);
- stack.addSlice(null, pixels2[2]);
- isRGB48 = true;
- } else if (nChannels>1) {
- for (int c=0; c1 && (stackSize%nChannels)==0) {
- imp.setDimensions(nChannels, stackSize/nChannels, 1);
- imp = new CompositeImage(imp, CompositeImage.COMPOSITE);
- imp.setOpenAsHyperStack(true);
- }
- IJ.showProgress(1.0);
- return imp;
- }
- }
-
- /** Attempts to open the specified file as a tiff.
- Returns an ImagePlus object if successful. */
- public ImagePlus openTiff(String directory, String name) {
- TiffDecoder td = new TiffDecoder(directory, name);
- if (IJ.debugMode) td.enableDebugging();
- FileInfo[] info=null;
- try {info = td.getTiffInfo();}
- catch (IOException e) {
- String msg = e.getMessage();
- if (msg==null||msg.equals("")) msg = ""+e;
- IJ.error("TiffDecoder", msg);
- return null;
- }
- if (info==null)
- return null;
- return openTiff2(info);
- }
-
- /** Attempts to open the specified inputStream as a
- TIFF, returning an ImagePlus object if successful. */
- public ImagePlus openTiff(InputStream in, String name) {
- FileInfo[] info = null;
- try {
- TiffDecoder td = new TiffDecoder(in, name);
- if (IJ.debugMode) td.enableDebugging();
- info = td.getTiffInfo();
- } catch (FileNotFoundException e) {
- IJ.error("TiffDecoder", "File not found: "+e.getMessage());
- return null;
- } catch (Exception e) {
- IJ.error("TiffDecoder", ""+e);
- return null;
- }
- return openTiff2(info);
- }
-
- /** Opens a single TIFF or DICOM contained in a ZIP archive,
- or a ZIPed collection of ".roi" files created by the ROI manager. */
- public ImagePlus openZip(String path) {
- ImagePlus imp = null;
- try {
- ZipInputStream in = new ZipInputStream(new FileInputStream(path));
- if (in==null) return null;
- ZipEntry entry = in.getNextEntry();
- if (entry==null) return null;
- String name = entry.getName();
- if (name.endsWith(".roi")) {
- in.close();
- IJ.runMacro("roiManager(\"Open\", getArgument());", path);
- return null;
- }
- if (name.endsWith(".tif")) {
- imp = openTiff(in, name);
- } else if (name.endsWith(".dcm")) {
- DICOM dcm = new DICOM(in);
- dcm.run(name);
- imp = dcm;
- } else {
- in.close();
- IJ.error("This ZIP archive does not appear to contain a \nTIFF (\".tif\") or DICOM (\".dcm\") file, or ROIs (\".roi\").");
- return null;
- }
- } catch (Exception e) {
- IJ.error("ZipDecoder", ""+e);
- return null;
- }
- File f = new File(path);
- FileInfo fi = imp.getOriginalFileInfo();
- fi.fileFormat = FileInfo.ZIP_ARCHIVE;
- fi.fileName = f.getName();
- fi.directory = f.getParent()+File.separator;
- return imp;
- }
-
- public 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 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 "";
- }
-
- ImagePlus openTiff2(FileInfo[] info) {
- if (info==null)
- return null;
- ImagePlus imp = null;
- if (IJ.debugMode) // dump tiff tags
- IJ.log(info[0].debugInfo);
- if (info.length>1) { // try to open as stack
- imp = openTiffStack(info);
- if (imp!=null)
- return imp;
- }
- FileOpener fo = new FileOpener(info[0]);
- imp = fo.open(false);
- if (imp==null) return null;
- int[] offsets = info[0].stripOffsets;
- if (offsets!=null&&offsets.length>1 && offsets[offsets.length-1]1 && info[0].description!=null && info[0].description.indexOf("mode=")!=-1;
- if (c>1 && (imp.getOpenAsHyperStack()||composite) && !imp.isComposite() && imp.getType()!=ImagePlus.COLOR_RGB) {
- int mode = CompositeImage.COLOR;
- if (info[0].description!=null) {
- if (info[0].description.indexOf("mode=composite")!=-1)
- mode = CompositeImage.COMPOSITE;
- else if (info[0].description.indexOf("mode=gray")!=-1)
- mode = CompositeImage.GRAYSCALE;
- }
- imp = new CompositeImage(imp, mode);
- }
- return imp;
- }
-
- /** Attempts to open the specified ROI, returning null if unsuccessful. */
- public Roi openRoi(String path) {
- Roi roi = null;
- RoiDecoder rd = new RoiDecoder(path);
- try {roi = rd.getRoi();}
- catch (IOException e) {
- IJ.error("RoiDecoder", e.getMessage());
- return null;
- }
- return roi;
- }
-
- /**
- Attempts to determine the image file type by looking for
- 'magic numbers' and the file name extension.
- */
- public int getFileType(String path) {
- if (openUsingPlugins && !path.endsWith(".txt") && !path.endsWith(".java"))
- return UNKNOWN;
- File file = new File(path);
- String name = file.getName();
- InputStream is;
- byte[] buf = new byte[132];
- try {
- is = new FileInputStream(file);
- is.read(buf, 0, 132);
- is.close();
- } catch (IOException e) {
- return UNKNOWN;
- }
-
- int b0=buf[0]&255, b1=buf[1]&255, b2=buf[2]&255, b3=buf[3]&255;
- //IJ.log("getFileType: "+ name+" "+b0+" "+b1+" "+b2+" "+b3);
-
- // Combined TIFF and DICOM created by GE Senographe scanners
- if (buf[128]==68 && buf[129]==73 && buf[130]==67 && buf[131]==77
- && ((b0==73 && b1==73)||(b0==77 && b1==77)))
- return TIFF_AND_DICOM;
-
- // Big-endian TIFF ("MM")
- if (name.endsWith(".lsm"))
- return UNKNOWN; // The LSM Reader plugin opens these files
- if (b0==73 && b1==73 && b2==42 && b3==0 && !(bioformats&&name.endsWith(".flex")))
- return TIFF;
-
- // Little-endian TIFF ("II")
- if (b0==77 && b1==77 && b2==0 && b3==42)
- return TIFF;
-
- // JPEG
- if (b0==255 && b1==216 && b2==255)
- return JPEG;
-
- // GIF ("GIF8")
- if (b0==71 && b1==73 && b2==70 && b3==56)
- return GIF;
-
- name = name.toLowerCase(Locale.US);
-
- // DICOM ("DICM" at offset 128)
- if (buf[128]==68 && buf[129]==73 && buf[130]==67 && buf[131]==77 || name.endsWith(".dcm")) {
- return DICOM;
- }
-
- // ACR/NEMA with first tag = 00002,00xx or 00008,00xx
- if ((b0==8||b0==2) && b1==0 && b3==0 && !name.endsWith(".spe") && !name.equals("fid"))
- return DICOM;
-
- // PGM ("P1", "P4", "P2", "P5", "P3" or "P6")
- if (b0==80&&(b1==49||b1==52||b1==50||b1==53||b1==51||b1==54)&&(b2==10||b2==13||b2==32||b2==9))
- return PGM;
-
- // Lookup table
- if (name.endsWith(".lut"))
- return LUT;
-
- // PNG
- if (b0==137 && b1==80 && b2==78 && b3==71)
- return PNG;
-
- // ZIP containing a TIFF
- if (name.endsWith(".zip"))
- return ZIP;
-
- // FITS ("SIMP")
- if ((b0==83 && b1==73 && b2==77 && b3==80) || name.endsWith(".fts.gz") || name.endsWith(".fits.gz"))
- return FITS;
-
- // Java source file, text file or macro
- if (name.endsWith(".java") || name.endsWith(".txt") || name.endsWith(".ijm") || name.endsWith(".js"))
- return JAVA_OR_TEXT;
-
- // ImageJ, NIH Image, Scion Image for Windows ROI
- if (b0==73 && b1==111) // "Iout"
- return ROI;
-
- // ObjectJ project
- if (name.endsWith(".ojj"))
- return OJJ;
-
- // Text file
- boolean isText = true;
- for (int i=0; i<10; i++) {
- int c = buf[i]&255;
- if ((c<32&&c!=9&&c!=10&&c!=13) || c>126) {
- isText = false;
- break;
- }
- }
- if (isText)
- return TEXT;
-
- // BMP ("BM")
- if ((b0==66 && b1==77)||name.endsWith(".dib"))
- return BMP;
-
- // AVI
- if (name.endsWith(".avi"))
- return AVI;
-
- return UNKNOWN;
- }
-
- /** Returns an IndexColorModel for the image specified by this FileInfo. */
- 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. */
- InputStream createInputStream(FileInfo fi) throws IOException, MalformedURLException {
- if (fi.inputStream!=null)
- return fi.inputStream;
- else if (fi.url!=null && !fi.url.equals(""))
- return new URL(fi.url+fi.fileName).openStream();
- else {
- File f = new File(fi.directory + fi.fileName);
- if (f==null || f.isDirectory())
- return null;
- else {
- InputStream is = new FileInputStream(f);
- if (fi.compression>=FileInfo.LZW)
- is = new RandomAccessStream(is);
- return is;
- }
- }
- }
-
- void openRGB48(ImagePlus imp) {
- isRGB48 = false;
- int stackSize = imp.getStackSize();
- imp.setDimensions(3, stackSize/3, 1);
- imp = new CompositeImage(imp, CompositeImage.COMPOSITE);
- imp.show();
- }
-
- /** The "Opening: path" status message is not displayed in silent mode. */
- public void setSilentMode(boolean mode) {
- silentMode = mode;
- }
-
- /** Open all images using HandleExtraFileTypes. Set from
- a macro using setOption("openUsingPlugins", true). */
- public static void setOpenUsingPlugins(boolean b) {
- openUsingPlugins = b;
- }
-
- /** Returns the state of the openUsingPlugins flag. */
- public static boolean getOpenUsingPlugins() {
- return openUsingPlugins;
- }
-
-}
+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 && index0 && imp.getHeight()>0) {
+ fileType[0] = CUSTOM;
+ return imp;
+ } else {
+ if (imp.getWidth()==-1)
+ fileType[0] = CUSTOM; // plugin opened image so don't display error
+ return null;
+ }
+ }
+
+ /** Opens the ZIP compressed TIFF or DICOM at the specified URL. */
+ ImagePlus openZipUsingUrl(URL url) throws IOException {
+ URLConnection uc = url.openConnection();
+ int fileSize = uc.getContentLength(); // compressed size
+ fileSize *=2; // estimate uncompressed size
+ InputStream in = uc.getInputStream();
+ ZipInputStream zin = new ZipInputStream(in);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buf = new byte[4096];
+ ZipEntry entry = zin.getNextEntry();
+ if (entry==null)
+ return null;
+ String name = entry.getName();
+ //double fileSize = entry.getSize(); //returns -1
+ if (!(name.endsWith(".tif")||name.endsWith(".dcm")))
+ throw new IOException("This ZIP archive does not appear to contain a .tif or .dcm file");
+ if (name.endsWith(".dcm")) {
+ DICOM dcm = new DICOM(zin);
+ dcm.run(name);
+ return dcm;
+ } else
+ return openTiff(zin, name);
+ }
+
+ ImagePlus openJpegOrGifUsingURL(String title, URL url) {
+ if (url==null) return null;
+ Image img = Toolkit.getDefaultToolkit().createImage(url);
+ if (img!=null) {
+ ImagePlus imp = new ImagePlus(title, img);
+ return imp;
+ } else
+ return null;
+ }
+
+ ImagePlus openPngUsingURL(String title, URL url) {
+ if (url==null) return null;
+ Image img = null;
+ try {
+ img = ImageIO.read(url);
+ } catch (IOException e) {
+ IJ.log(""+e);
+ }
+ if (img!=null) {
+ ImagePlus imp = new ImagePlus(title, img);
+ return imp;
+ } else
+ return null;
+ }
+
+ ImagePlus openJpegOrGif(String dir, String name) {
+ ImagePlus imp = null;
+ Image img = Toolkit.getDefaultToolkit().createImage(dir+name);
+ if (img!=null) {
+ try {
+ imp = new ImagePlus(name, img);
+ } catch (IllegalStateException e) {
+ return null; // error loading image
+ }
+ if (imp.getType()==ImagePlus.COLOR_RGB)
+ convertGrayJpegTo8Bits(imp);
+ FileInfo fi = new FileInfo();
+ fi.fileFormat = fi.GIF_OR_JPG;
+ fi.fileName = name;
+ fi.directory = dir;
+ imp.setFileInfo(fi);
+ }
+ return imp;
+ }
+
+ ImagePlus openUsingImageIO(String path) {
+ ImagePlus imp = null;
+ BufferedImage img = null;
+ File f = new File(path);
+ try {
+ img = ImageIO.read(f);
+ } catch (Exception e) {
+ IJ.error("Open Using ImageIO", ""+e);
+ }
+ if (img==null) return null;
+ imp = new ImagePlus(f.getName(), img);
+ FileInfo fi = new FileInfo();
+ fi.fileFormat = fi.IMAGEIO;
+ fi.fileName = f.getName();
+ fi.directory = f.getParent()+File.separator;
+ imp.setFileInfo(fi);
+ return imp;
+ }
+
+ /** If this image is grayscale, convert it to 8-bits. */
+ public static void convertGrayJpegTo8Bits(ImagePlus imp) {
+ ImageProcessor ip = imp.getProcessor();
+ int width = ip.getWidth();
+ int height = ip.getHeight();
+ int[] pixels = (int[])ip.getPixels();
+ int c,r,g,b,offset;
+ for (int y=0; y<(height-8); y++) {
+ offset = y*width;
+ for (int x=0; x<(width-8); x++) {
+ c = pixels[offset+x];
+ r = (c&0xff0000)>>16;
+ g = (c&0xff00)>>8;
+ b = c&0xff;
+ if (!((r==g)&&(g==b))) {
+ //IJ.write("count: "+count+" "+r+" "+g+" "+b);
+ return;
+ }
+ }
+ //count++;
+ }
+ IJ.showStatus("Converting to 8-bits");
+ new ImageConverter(imp).convertToGray8();
+ }
+
+ /** Are all the images in this file the same size and type? */
+ boolean allSameSizeAndType(FileInfo[] info) {
+ boolean sameSizeAndType = true;
+ boolean contiguous = true;
+ int startingOffset = info[0].offset;
+ int size = info[0].width*info[0].height*info[0].getBytesPerPixel();
+ for (int i=1; i1 && !allSameSizeAndType(info))
+ return null;
+ FileInfo fi = info[0];
+ if (fi.nImages>1)
+ return new FileOpener(fi).open(false); // open contiguous images as stack
+ else {
+ ColorModel cm = createColorModel(fi);
+ ImageStack stack = new ImageStack(fi.width, fi.height, cm);
+ Object pixels = null;
+ int skip = fi.offset;
+ int imageSize = fi.width*fi.height*fi.getBytesPerPixel();
+ if (info[0].fileType==FileInfo.GRAY12_UNSIGNED) {
+ imageSize = (int)(fi.width*fi.height*1.5);
+ if ((imageSize&1)==1) imageSize++; // add 1 if odd
+ } if (info[0].fileType==FileInfo.BITMAP) {
+ int scan=(int)Math.ceil(fi.width/8.0);
+ imageSize = scan*fi.height;
+ }
+ int loc = 0;
+ int nChannels = 1;
+ try {
+ InputStream is = createInputStream(fi);
+ ImageReader reader = new ImageReader(fi);
+ IJ.resetEscape();
+ for (int i=0; i=FileInfo.LZW) {
+ fi.stripOffsets = info[i].stripOffsets;
+ fi.stripLengths = info[i].stripLengths;
+ }
+ if (info[i].samplesPerPixel>1 && !(info[i].getBytesPerPixel()==3||info[i].getBytesPerPixel()==6)) {
+ nChannels = fi.samplesPerPixel;
+ channels = new Object[nChannels];
+ for (int c=0; c=FileInfo.LZW) skip = 0;
+ if (skip<0)
+ throw new IOException("Images are not in order");
+ }
+ if (fi.fileType==FileInfo.RGB48) {
+ Object[] pixels2 = (Object[])pixels;
+ stack.addSlice(null, pixels2[0]);
+ stack.addSlice(null, pixels2[1]);
+ stack.addSlice(null, pixels2[2]);
+ isRGB48 = true;
+ } else if (nChannels>1) {
+ for (int c=0; c1 && (stackSize%nChannels)==0) {
+ imp.setDimensions(nChannels, stackSize/nChannels, 1);
+ imp = new CompositeImage(imp, CompositeImage.COMPOSITE);
+ imp.setOpenAsHyperStack(true);
+ }
+ IJ.showProgress(1.0);
+ return imp;
+ }
+ }
+
+ /** Attempts to open the specified file as a tiff.
+ Returns an ImagePlus object if successful. */
+ public ImagePlus openTiff(String directory, String name) {
+ TiffDecoder td = new TiffDecoder(directory, name);
+ if (IJ.debugMode) td.enableDebugging();
+ FileInfo[] info=null;
+ try {info = td.getTiffInfo();}
+ catch (IOException e) {
+ String msg = e.getMessage();
+ if (msg==null||msg.equals("")) msg = ""+e;
+ IJ.error("TiffDecoder", msg);
+ return null;
+ }
+ if (info==null)
+ return null;
+ return openTiff2(info);
+ }
+
+ /** Attempts to open the specified inputStream as a
+ TIFF, returning an ImagePlus object if successful. */
+ public ImagePlus openTiff(InputStream in, String name) {
+ FileInfo[] info = null;
+ try {
+ TiffDecoder td = new TiffDecoder(in, name);
+ if (IJ.debugMode) td.enableDebugging();
+ info = td.getTiffInfo();
+ } catch (FileNotFoundException e) {
+ IJ.error("TiffDecoder", "File not found: "+e.getMessage());
+ return null;
+ } catch (Exception e) {
+ IJ.error("TiffDecoder", ""+e);
+ return null;
+ }
+ return openTiff2(info);
+ }
+
+ /** Opens a single TIFF or DICOM contained in a ZIP archive,
+ or a ZIPed collection of ".roi" files created by the ROI manager. */
+ public ImagePlus openZip(String path) {
+ ImagePlus imp = null;
+ try {
+ ZipInputStream in = new ZipInputStream(new FileInputStream(path));
+ if (in==null) return null;
+ ZipEntry entry = in.getNextEntry();
+ if (entry==null) return null;
+ String name = entry.getName();
+ if (name.endsWith(".roi")) {
+ in.close();
+ IJ.runMacro("roiManager(\"Open\", getArgument());", path);
+ return null;
+ }
+ if (name.endsWith(".tif")) {
+ imp = openTiff(in, name);
+ } else if (name.endsWith(".dcm")) {
+ DICOM dcm = new DICOM(in);
+ dcm.run(name);
+ imp = dcm;
+ } else {
+ in.close();
+ IJ.error("This ZIP archive does not appear to contain a \nTIFF (\".tif\") or DICOM (\".dcm\") file, or ROIs (\".roi\").");
+ return null;
+ }
+ } catch (Exception e) {
+ IJ.error("ZipDecoder", ""+e);
+ return null;
+ }
+ File f = new File(path);
+ FileInfo fi = imp.getOriginalFileInfo();
+ fi.fileFormat = FileInfo.ZIP_ARCHIVE;
+ fi.fileName = f.getName();
+ fi.directory = f.getParent()+File.separator;
+ return imp;
+ }
+
+ public 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 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 "";
+ }
+
+ ImagePlus openTiff2(FileInfo[] info) {
+ if (info==null)
+ return null;
+ ImagePlus imp = null;
+ if (IJ.debugMode) // dump tiff tags
+ IJ.log(info[0].debugInfo);
+ if (info.length>1) { // try to open as stack
+ imp = openTiffStack(info);
+ if (imp!=null)
+ return imp;
+ }
+ FileOpener fo = new FileOpener(info[0]);
+ imp = fo.open(false);
+ if (imp==null) return null;
+ int[] offsets = info[0].stripOffsets;
+ if (offsets!=null&&offsets.length>1 && offsets[offsets.length-1]1 && info[0].description!=null && info[0].description.indexOf("mode=")!=-1;
+ if (c>1 && (imp.getOpenAsHyperStack()||composite) && !imp.isComposite() && imp.getType()!=ImagePlus.COLOR_RGB) {
+ int mode = CompositeImage.COLOR;
+ if (info[0].description!=null) {
+ if (info[0].description.indexOf("mode=composite")!=-1)
+ mode = CompositeImage.COMPOSITE;
+ else if (info[0].description.indexOf("mode=gray")!=-1)
+ mode = CompositeImage.GRAYSCALE;
+ }
+ imp = new CompositeImage(imp, mode);
+ }
+ return imp;
+ }
+
+ /** Attempts to open the specified ROI, returning null if unsuccessful. */
+ public Roi openRoi(String path) {
+ Roi roi = null;
+ RoiDecoder rd = new RoiDecoder(path);
+ try {roi = rd.getRoi();}
+ catch (IOException e) {
+ IJ.error("RoiDecoder", e.getMessage());
+ return null;
+ }
+ return roi;
+ }
+
+ /**
+ Attempts to determine the image file type by looking for
+ 'magic numbers' and the file name extension.
+ */
+ public int getFileType(String path) {
+ if (openUsingPlugins && !path.endsWith(".txt") && !path.endsWith(".java"))
+ return UNKNOWN;
+ File file = new File(path);
+ String name = file.getName();
+ InputStream is;
+ byte[] buf = new byte[132];
+ try {
+ is = new FileInputStream(file);
+ is.read(buf, 0, 132);
+ is.close();
+ } catch (IOException e) {
+ return UNKNOWN;
+ }
+
+ int b0=buf[0]&255, b1=buf[1]&255, b2=buf[2]&255, b3=buf[3]&255;
+ //IJ.log("getFileType: "+ name+" "+b0+" "+b1+" "+b2+" "+b3);
+
+ // Combined TIFF and DICOM created by GE Senographe scanners
+ if (buf[128]==68 && buf[129]==73 && buf[130]==67 && buf[131]==77
+ && ((b0==73 && b1==73)||(b0==77 && b1==77)))
+ return TIFF_AND_DICOM;
+
+ // Big-endian TIFF ("MM")
+ if (name.endsWith(".lsm"))
+ return UNKNOWN; // The LSM Reader plugin opens these files
+ if (b0==73 && b1==73 && b2==42 && b3==0 && !(bioformats&&name.endsWith(".flex")))
+ return TIFF;
+
+ // Little-endian TIFF ("II")
+ if (b0==77 && b1==77 && b2==0 && b3==42)
+ return TIFF;
+
+ // JPEG
+ if (b0==255 && b1==216 && b2==255)
+ return JPEG;
+
+ // GIF ("GIF8")
+ if (b0==71 && b1==73 && b2==70 && b3==56)
+ return GIF;
+
+ name = name.toLowerCase(Locale.US);
+
+ // DICOM ("DICM" at offset 128)
+ if (buf[128]==68 && buf[129]==73 && buf[130]==67 && buf[131]==77 || name.endsWith(".dcm")) {
+ return DICOM;
+ }
+
+ // ACR/NEMA with first tag = 00002,00xx or 00008,00xx
+ if ((b0==8||b0==2) && b1==0 && b3==0 && !name.endsWith(".spe") && !name.equals("fid"))
+ return DICOM;
+
+ // PGM ("P1", "P4", "P2", "P5", "P3" or "P6")
+ if (b0==80&&(b1==49||b1==52||b1==50||b1==53||b1==51||b1==54)&&(b2==10||b2==13||b2==32||b2==9))
+ return PGM;
+
+ // Lookup table
+ if (name.endsWith(".lut"))
+ return LUT;
+
+ // PNG
+ if (b0==137 && b1==80 && b2==78 && b3==71)
+ return PNG;
+
+ // ZIP containing a TIFF
+ if (name.endsWith(".zip"))
+ return ZIP;
+
+ // FITS ("SIMP")
+ if ((b0==83 && b1==73 && b2==77 && b3==80) || name.endsWith(".fts.gz") || name.endsWith(".fits.gz"))
+ return FITS;
+
+ // Java source file, text file or macro
+ if (name.endsWith(".java") || name.endsWith(".txt") || name.endsWith(".ijm") || name.endsWith(".js"))
+ return JAVA_OR_TEXT;
+
+ // ImageJ, NIH Image, Scion Image for Windows ROI
+ if (b0==73 && b1==111) // "Iout"
+ return ROI;
+
+ // ObjectJ project
+ if (name.endsWith(".ojj"))
+ return OJJ;
+
+ // Text file
+ boolean isText = true;
+ for (int i=0; i<10; i++) {
+ int c = buf[i]&255;
+ if ((c<32&&c!=9&&c!=10&&c!=13) || c>126) {
+ isText = false;
+ break;
+ }
+ }
+ if (isText)
+ return TEXT;
+
+ // BMP ("BM")
+ if ((b0==66 && b1==77)||name.endsWith(".dib"))
+ return BMP;
+
+ // AVI
+ if (name.endsWith(".avi"))
+ return AVI;
+
+ return UNKNOWN;
+ }
+
+ /** Returns an IndexColorModel for the image specified by this FileInfo. */
+ 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. */
+ InputStream createInputStream(FileInfo fi) throws IOException, MalformedURLException {
+ if (fi.inputStream!=null)
+ return fi.inputStream;
+ else if (fi.url!=null && !fi.url.equals(""))
+ return new URL(fi.url+fi.fileName).openStream();
+ else {
+ File f = new File(fi.directory + fi.fileName);
+ if (f==null || f.isDirectory())
+ return null;
+ else {
+ InputStream is = new FileInputStream(f);
+ if (fi.compression>=FileInfo.LZW)
+ is = new RandomAccessStream(is);
+ return is;
+ }
+ }
+ }
+
+ void openRGB48(ImagePlus imp) {
+ isRGB48 = false;
+ int stackSize = imp.getStackSize();
+ imp.setDimensions(3, stackSize/3, 1);
+ imp = new CompositeImage(imp, CompositeImage.COMPOSITE);
+ imp.show();
+ }
+
+ /** The "Opening: path" status message is not displayed in silent mode. */
+ public void setSilentMode(boolean mode) {
+ silentMode = mode;
+ }
+
+ /** Open all images using HandleExtraFileTypes. Set from
+ a macro using setOption("openUsingPlugins", true). */
+ public static void setOpenUsingPlugins(boolean b) {
+ openUsingPlugins = b;
+ }
+
+ /** Returns the state of the openUsingPlugins flag. */
+ public static boolean getOpenUsingPlugins() {
+ return openUsingPlugins;
+ }
+
+}
diff --git a/ij/io/PluginClassLoader.java b/ij/io/PluginClassLoader.java
index ee6dffa7c..8ac758bda 100644
--- a/ij/io/PluginClassLoader.java
+++ b/ij/io/PluginClassLoader.java
@@ -1,84 +1,84 @@
-package ij.io;
-import java.io.*;
-import java.net.*;
-
-/** ImageJ uses this class loader to load plugins and resources from the
- * plugins directory and immediate subdirectories. This class loader will
- * also load classes and resources from JAR files.
- *
- * The class loader searches for classes and resources in the following order:
- *
- * - Plugins directory
- * - Subdirectories of the Plugins directory
- * - JAR and ZIP files in the plugins directory and subdirectories
- *
- * The class loader does not recurse into subdirectories beyond the first level.
-*/
-public class PluginClassLoader extends URLClassLoader {
- protected String path;
-
- /**
- * Creates a new PluginClassLoader that searches in the directory path
- * passed as a parameter. The constructor automatically finds all JAR and ZIP
- * files in the path and first level of subdirectories. The JAR and ZIP files
- * are stored in a Vector for future searches.
- * @param path the path to the plugins directory.
- */
- public PluginClassLoader(String path) {
- super(new URL[0]);
- init(path);
- }
-
- /** This version of the constructor is used when ImageJ is launched using Java WebStart. */
- public PluginClassLoader(String path, boolean callSuper) {
- super(new URL[0], Thread.currentThread().getContextClassLoader());
- init(path);
- }
-
- void init(String path) {
- this.path = path;
-
- //find all JAR files on the path and subdirectories
- File f = new File(path);
- try {
- // Add plugin directory to search path
- addURL(f.toURI().toURL());
- } catch (MalformedURLException e) {
- ij.IJ.log("PluginClassLoader: "+e);
- }
- String[] list = f.list();
- if (list==null)
- return;
- for (int i=0; i The class loader searches for classes and resources in the following order:
+ *
+ * - Plugins directory
+ * - Subdirectories of the Plugins directory
+ * - JAR and ZIP files in the plugins directory and subdirectories
+ *
+ * The class loader does not recurse into subdirectories beyond the first level.
+*/
+public class PluginClassLoader extends URLClassLoader {
+ protected String path;
+
+ /**
+ * Creates a new PluginClassLoader that searches in the directory path
+ * passed as a parameter. The constructor automatically finds all JAR and ZIP
+ * files in the path and first level of subdirectories. The JAR and ZIP files
+ * are stored in a Vector for future searches.
+ * @param path the path to the plugins directory.
+ */
+ public PluginClassLoader(String path) {
+ super(new URL[0]);
+ init(path);
+ }
+
+ /** This version of the constructor is used when ImageJ is launched using Java WebStart. */
+ public PluginClassLoader(String path, boolean callSuper) {
+ super(new URL[0], Thread.currentThread().getContextClassLoader());
+ init(path);
+ }
+
+ void init(String path) {
+ this.path = path;
+
+ //find all JAR files on the path and subdirectories
+ File f = new File(path);
+ try {
+ // Add plugin directory to search path
+ addURL(f.toURI().toURL());
+ } catch (MalformedURLException e) {
+ ij.IJ.log("PluginClassLoader: "+e);
+ }
+ String[] list = f.list();
+ if (list==null)
+ return;
+ for (int i=0; i= l) {
- byte abyte0[] = (byte[])data.elementAt((int)(pointer>>BLOCK_SHIFT));
- return abyte0[(int)(pointer++ & BLOCK_MASK)] & 0xff;
- } else
- return -1;
- }
-
- public int read(byte[] bytes, int off, int len) throws IOException {
- if(bytes == null)
- throw new NullPointerException();
- if (ras!=null)
- return ras.read(bytes, off, len);
- if(off<0 || len<0 || off+len>bytes.length)
- throw new IndexOutOfBoundsException();
- if(len == 0)
- return 0;
- long l = readUntil(pointer+len);
- if (l<=pointer)
- return -1;
- else {
- byte abyte1[] = (byte[])data.elementAt((int)(pointer >> BLOCK_SHIFT));
- int k = Math.min(len, BLOCK_SIZE - (int)(pointer & BLOCK_MASK));
- System.arraycopy(abyte1, (int)(pointer & BLOCK_MASK), bytes, off, k);
- pointer += k;
- return k;
- }
- }
-
- public final void readFully(byte[] bytes) throws IOException {
- readFully(bytes, bytes.length);
- }
-
- public final void readFully(byte[] bytes, int len) throws IOException {
- int read = 0;
- do {
- int l = read(bytes, read, len - read);
- if(l < 0) break;
- read += l;
- } while (read> BLOCK_SHIFT);
- int j = length >> BLOCK_SHIFT;
- for(int k = j; k <= i; k++) {
- byte abyte0[] = new byte[BLOCK_SIZE];
- data.addElement(abyte0);
- int i1 = BLOCK_SIZE;
- int j1 = 0;
- while(i1 > 0) {
- int k1 = src.read(abyte0, j1, i1);
- if(k1 == -1) {
- foundEOS = true;
- return length;
- }
- j1 += k1;
- i1 -= k1;
- length += k1;
- }
-
- }
-
- return length;
- }
-
- public void seek(long loc) throws IOException {
- if (ras!=null)
- {ras.seek(loc); return;}
- if(loc < 0)
- pointer = 0L;
- else
- pointer = loc;
- }
-
- public void seek(int loc) throws IOException {
- if (ras!=null)
- {ras.seek(loc&0xffffffff); return;}
- if(loc < 0)
- pointer = 0L;
- else
- pointer = loc;
- }
-
- public final int readInt() throws IOException {
- int i = read();
- int j = read();
- int k = read();
- int l = read();
- if((i | j | k | l) < 0)
- throw new EOFException();
- else
- return (i << 24) + (j << 16) + (k << 8) + l;
- }
-
- public final long readLong() throws IOException {
- return ((long)readInt() << 32) + ((long)readInt() & 0xffffffffL);
- }
-
- public final double readDouble() throws IOException {
- return Double.longBitsToDouble(readLong());
- }
-
- public final short readShort() throws IOException {
- int i = read();
- int j = read();
- if((i | j) < 0)
- throw new EOFException();
- else
- return (short)((i << 8) + j);
- }
-
- public final float readFloat() throws IOException {
- return Float.intBitsToFloat(readInt());
- }
-
- public void close() throws IOException {
- //if (ij.IJ.debugMode) ij.IJ.log("close: "+(data!=null?""+data.size():""));
- if (ras!=null)
- ras.close();
- else {
- data.removeAllElements();
- src.close();
- }
- }
-
-
-}
+package ij.io;
+import java.io.*;
+import java.util.Vector;
+
+
+/** This is a class that uses a memory cache to allow seeking within
+ an InputStream. Based on the JAI MemoryCacheSeekableStream class.
+ Can also be constructed from a RandomAccessFile, which uses less
+ memory since the memory cache is not required.
+*/
+public final class RandomAccessStream extends InputStream {
+
+ private static final int BLOCK_SIZE = 512;
+ private static final int BLOCK_MASK = 511;
+ private static final int BLOCK_SHIFT = 9;
+
+ private InputStream src;
+ private RandomAccessFile ras;
+ private long pointer;
+ private Vector data;
+ private int length;
+ private boolean foundEOS;
+
+ /** Constructs a RandomAccessStream from an InputStream. Seeking
+ backwards is supported using a memory cache. */
+ public RandomAccessStream(InputStream inputstream) {
+ pointer = 0L;
+ data = new Vector();
+ length = 0;
+ foundEOS = false;
+ src = inputstream;
+ }
+
+ /** Constructs a RandomAccessStream from an RandomAccessFile. */
+ public RandomAccessStream(RandomAccessFile ras) {
+ this.ras = ras;
+ }
+
+ public int getFilePointer() throws IOException {
+ if (ras!=null)
+ return (int)ras.getFilePointer();
+ else
+ return (int)pointer;
+ }
+
+ public long getLongFilePointer() throws IOException {
+ if (ras!=null)
+ return ras.getFilePointer();
+ else
+ return pointer;
+ }
+
+ public int read() throws IOException {
+ if (ras!=null)
+ return ras.read();
+ long l = pointer + 1L;
+ long l1 = readUntil(l);
+ if(l1 >= l) {
+ byte abyte0[] = (byte[])data.elementAt((int)(pointer>>BLOCK_SHIFT));
+ return abyte0[(int)(pointer++ & BLOCK_MASK)] & 0xff;
+ } else
+ return -1;
+ }
+
+ public int read(byte[] bytes, int off, int len) throws IOException {
+ if(bytes == null)
+ throw new NullPointerException();
+ if (ras!=null)
+ return ras.read(bytes, off, len);
+ if(off<0 || len<0 || off+len>bytes.length)
+ throw new IndexOutOfBoundsException();
+ if(len == 0)
+ return 0;
+ long l = readUntil(pointer+len);
+ if (l<=pointer)
+ return -1;
+ else {
+ byte abyte1[] = (byte[])data.elementAt((int)(pointer >> BLOCK_SHIFT));
+ int k = Math.min(len, BLOCK_SIZE - (int)(pointer & BLOCK_MASK));
+ System.arraycopy(abyte1, (int)(pointer & BLOCK_MASK), bytes, off, k);
+ pointer += k;
+ return k;
+ }
+ }
+
+ public final void readFully(byte[] bytes) throws IOException {
+ readFully(bytes, bytes.length);
+ }
+
+ public final void readFully(byte[] bytes, int len) throws IOException {
+ int read = 0;
+ do {
+ int l = read(bytes, read, len - read);
+ if(l < 0) break;
+ read += l;
+ } while (read> BLOCK_SHIFT);
+ int j = length >> BLOCK_SHIFT;
+ for(int k = j; k <= i; k++) {
+ byte abyte0[] = new byte[BLOCK_SIZE];
+ data.addElement(abyte0);
+ int i1 = BLOCK_SIZE;
+ int j1 = 0;
+ while(i1 > 0) {
+ int k1 = src.read(abyte0, j1, i1);
+ if(k1 == -1) {
+ foundEOS = true;
+ return length;
+ }
+ j1 += k1;
+ i1 -= k1;
+ length += k1;
+ }
+
+ }
+
+ return length;
+ }
+
+ public void seek(long loc) throws IOException {
+ if (ras!=null)
+ {ras.seek(loc); return;}
+ if(loc < 0)
+ pointer = 0L;
+ else
+ pointer = loc;
+ }
+
+ public void seek(int loc) throws IOException {
+ if (ras!=null)
+ {ras.seek(loc&0xffffffff); return;}
+ if(loc < 0)
+ pointer = 0L;
+ else
+ pointer = loc;
+ }
+
+ public final int readInt() throws IOException {
+ int i = read();
+ int j = read();
+ int k = read();
+ int l = read();
+ if((i | j | k | l) < 0)
+ throw new EOFException();
+ else
+ return (i << 24) + (j << 16) + (k << 8) + l;
+ }
+
+ public final long readLong() throws IOException {
+ return ((long)readInt() << 32) + ((long)readInt() & 0xffffffffL);
+ }
+
+ public final double readDouble() throws IOException {
+ return Double.longBitsToDouble(readLong());
+ }
+
+ public final short readShort() throws IOException {
+ int i = read();
+ int j = read();
+ if((i | j) < 0)
+ throw new EOFException();
+ else
+ return (short)((i << 8) + j);
+ }
+
+ public final float readFloat() throws IOException {
+ return Float.intBitsToFloat(readInt());
+ }
+
+ public void close() throws IOException {
+ //if (ij.IJ.debugMode) ij.IJ.log("close: "+(data!=null?""+data.size():""));
+ if (ras!=null)
+ ras.close();
+ else {
+ data.removeAllElements();
+ src.close();
+ }
+ }
+
+
+}
diff --git a/ij/io/RoiDecoder.java b/ij/io/RoiDecoder.java
index 81ab0d7cf..64f7c583c 100644
--- a/ij/io/RoiDecoder.java
+++ b/ij/io/RoiDecoder.java
@@ -1,195 +1,195 @@
-package ij.io;
-import ij.gui.*;
-import java.io.*;
-import java.util.*;
-import java.net.*;
-
-/* ImageJ/NIH Image 64 byte ROI outline header
- 2 byte numbers are big-endian signed shorts
-
- 0-3 "Iout"
- 4-5 version (>=217)
- 6-7 roi type
- 8-9 top
- 10-11 left
- 12-13 bottom
- 14-15 right
- 16-17 NCoordinates
- 18-33 x1,y1,x2,y2 (straight line)
- 34-35 line width (unused)
- 36-39 ShapeRoi size (type must be 1 if this value>0)
- 40-63 reserved (zero)
- 64-67 x0, y0 (polygon)
- 68-71 x1, y1
- etc.
-
-*/
-
-/** Decodes an ImageJ, NIH Image or Scion Image ROI. */
-public class RoiDecoder {
-
- private final int polygon=0, rect=1, oval=2, line=3, freeline=4, polyline=5, noRoi=6, freehand=7, traced=8, angle=9, point=10;
- private byte[] data;
- private String path;
- private InputStream is;
- private String name;
- private int size;
-
- /** Constructs an RoiDecoder using a file path. */
- public RoiDecoder(String path) {
- this.path = path;
- }
-
- /** Constructs an RoiDecoder using a byte array. */
- public RoiDecoder(byte[] bytes, String name) {
- is = new ByteArrayInputStream(bytes);
- this.name = name;
- this.size = bytes.length;
- }
-
- /** Returns the ROI. */
- public Roi getRoi() throws IOException {
- if (path!=null) {
- File f = new File(path);
- size = (int)f.length();
- if (size>500000)
- throw new IOException("This is not an ImageJ ROI");
- name = f.getName();
- is = new FileInputStream(path);
- }
- data = new byte[size];
-
- int total = 0;
- while (total0;
- if (isComposite)
- return getShapeRoi();
-
- Roi roi = null;
- switch (type) {
- case rect:
- roi = new Roi(left, top, width, height);
- break;
- case oval:
- roi = new OvalRoi(left, top, width, height);
- break;
- case line:
- int x1 = (int)getFloat(18);
- int y1 = (int)getFloat(22);
- int x2 = (int)getFloat(26);
- int y2 = (int)getFloat(30);
- roi = new Line(x1, y1, x2, y2);
- //IJ.write("line roi: "+x1+" "+y1+" "+x2+" "+y2);
- break;
- case polygon: case freehand: case traced: case polyline: case freeline: case angle: case point:
- //IJ.write("type: "+type);
- //IJ.write("n: "+n);
- //IJ.write("rect: "+left+","+top+" "+width+" "+height);
- if (n==0) break;
- int[] x = new int[n];
- int[] y = new int[n];
- int base1 = 64;
- int base2 = base1+2*n;
- int xtmp, ytmp;
- for (int i=0; i32767 and unsigned
- return n;
- }
-
- int getInt(int base) {
- int b0 = data[base]&255;
- int b1 = data[base+1]&255;
- int b2 = data[base+2]&255;
- int b3 = data[base+3]&255;
- return ((b0<<24) + (b1<<16) + (b2<<8) + b3);
- }
-
- float getFloat(int base) {
- return Float.intBitsToFloat(getInt(base));
- }
-
+package ij.io;
+import ij.gui.*;
+import java.io.*;
+import java.util.*;
+import java.net.*;
+
+/* ImageJ/NIH Image 64 byte ROI outline header
+ 2 byte numbers are big-endian signed shorts
+
+ 0-3 "Iout"
+ 4-5 version (>=217)
+ 6-7 roi type
+ 8-9 top
+ 10-11 left
+ 12-13 bottom
+ 14-15 right
+ 16-17 NCoordinates
+ 18-33 x1,y1,x2,y2 (straight line)
+ 34-35 line width (unused)
+ 36-39 ShapeRoi size (type must be 1 if this value>0)
+ 40-63 reserved (zero)
+ 64-67 x0, y0 (polygon)
+ 68-71 x1, y1
+ etc.
+
+*/
+
+/** Decodes an ImageJ, NIH Image or Scion Image ROI. */
+public class RoiDecoder {
+
+ private final int polygon=0, rect=1, oval=2, line=3, freeline=4, polyline=5, noRoi=6, freehand=7, traced=8, angle=9, point=10;
+ private byte[] data;
+ private String path;
+ private InputStream is;
+ private String name;
+ private int size;
+
+ /** Constructs an RoiDecoder using a file path. */
+ public RoiDecoder(String path) {
+ this.path = path;
+ }
+
+ /** Constructs an RoiDecoder using a byte array. */
+ public RoiDecoder(byte[] bytes, String name) {
+ is = new ByteArrayInputStream(bytes);
+ this.name = name;
+ this.size = bytes.length;
+ }
+
+ /** Returns the ROI. */
+ public Roi getRoi() throws IOException {
+ if (path!=null) {
+ File f = new File(path);
+ size = (int)f.length();
+ if (size>500000)
+ throw new IOException("This is not an ImageJ ROI");
+ name = f.getName();
+ is = new FileInputStream(path);
+ }
+ data = new byte[size];
+
+ int total = 0;
+ while (total0;
+ if (isComposite)
+ return getShapeRoi();
+
+ Roi roi = null;
+ switch (type) {
+ case rect:
+ roi = new Roi(left, top, width, height);
+ break;
+ case oval:
+ roi = new OvalRoi(left, top, width, height);
+ break;
+ case line:
+ int x1 = (int)getFloat(18);
+ int y1 = (int)getFloat(22);
+ int x2 = (int)getFloat(26);
+ int y2 = (int)getFloat(30);
+ roi = new Line(x1, y1, x2, y2);
+ //IJ.write("line roi: "+x1+" "+y1+" "+x2+" "+y2);
+ break;
+ case polygon: case freehand: case traced: case polyline: case freeline: case angle: case point:
+ //IJ.write("type: "+type);
+ //IJ.write("n: "+n);
+ //IJ.write("rect: "+left+","+top+" "+width+" "+height);
+ if (n==0) break;
+ int[] x = new int[n];
+ int[] y = new int[n];
+ int base1 = 64;
+ int base2 = base1+2*n;
+ int xtmp, ytmp;
+ for (int i=0; i32767 and unsigned
+ return n;
+ }
+
+ int getInt(int base) {
+ int b0 = data[base]&255;
+ int b1 = data[base+1]&255;
+ int b2 = data[base+2]&255;
+ int b3 = data[base+3]&255;
+ return ((b0<<24) + (b1<<16) + (b2<<8) + b3);
+ }
+
+ float getFloat(int base) {
+ return Float.intBitsToFloat(getInt(base));
+ }
+
}
\ No newline at end of file
diff --git a/ij/io/RoiEncoder.java b/ij/io/RoiEncoder.java
index ce865cd72..0576989ce 100644
--- a/ij/io/RoiEncoder.java
+++ b/ij/io/RoiEncoder.java
@@ -1,153 +1 @@
-package ij.io;
-import ij.gui.*;
-import java.awt.*;
-import java.io.*;
-import java.util.*;
-import java.net.*;
-
-/** Saves an ROI to a file or stream. RoiDecoder.java has a description of the file format.
- @see ij.io.RoiDecoder
- @see ij.plugin.RoiReader
-*/
-public class RoiEncoder {
- String path;
- OutputStream f;
- static final int HEADER_SIZE = 64;
- static final int VERSION = 217;
- final int polygon=0, rect=1, oval=2, line=3, freeline=4, polyline=5, noRoi=6, freehand=7,
- traced=8, angle=9, point=10;
- byte[] data;
-
- /** Creates an RoiEncoder using the specified path. */
- public RoiEncoder(String path) {
- this.path = path;
- }
-
- /** Creates an RoiEncoder using the specified OutputStream. */
- public RoiEncoder(OutputStream f) {
- this.f = f;
- }
-
- /** Save the Roi to the file of stream. */
- public void write(Roi roi) throws IOException {
- if (f!=null) {
- write(roi, f);
- } else {
- f = new FileOutputStream(path);
- write(roi, f);
- f.close();
- }
- }
-
- void write(Roi roi, OutputStream f) throws IOException {
- int roiType = roi.getType();
- int type = rect;
- switch (roiType) {
- case Roi.POLYGON: type=polygon; break;
- case Roi.FREEROI: type=freehand; break;
- case Roi.TRACED_ROI: type=traced; break;
- case Roi.OVAL: type=oval; break;
- case Roi.LINE: type=line; break;
- case Roi.POLYLINE: type=polyline; break;
- case Roi.FREELINE: type=freeline; break;
- case Roi.ANGLE: type=angle; break;
- case Roi.COMPOSITE: type=rect; break;// shape array size (36-39) will be >0 to indicate composite type
- case Roi.POINT: type=point; break;
- default: type = rect; break;
- }
-
- if (roiType==Roi.COMPOSITE) {
- saveShapeRoi(roi, type, f);
- return;
- }
-
- int n=0;
- int[] x=null,y=null;
- if (roi instanceof PolygonRoi) {
- Polygon p = ((PolygonRoi)roi).getNonSplineCoordinates();
- n = p.npoints;
- x = p.xpoints;
- y = p.ypoints;
- }
- data = new byte[HEADER_SIZE+n*4];
-
- Rectangle r = roi.getBounds();
-
- data[0]=73; data[1]=111; data[2]=117; data[3]=116; // "Iout"
- putShort(4, VERSION);
- data[6] = (byte)type;
- putShort(8, r.y); //top
- putShort(10, r.x); //left
- putShort(12, r.y+r.height); //bottom
- putShort(14, r.x+r.width); //right
- putShort(16, n);
-
- if (roi instanceof Line) {
- Line l = (Line)roi;
- putFloat(18, l.x1);
- putFloat(22, l.y1);
- putFloat(26, l.x2);
- putFloat(30, l.y2);
- }
-
- if (n>0) {
- int base1 = 64;
- int base2 = base1+2*n;
- for (int i=0; i>>8);
- data[base+1] = (byte)v;
- }
-
- void putFloat(int base, float v) {
- int tmp = Float.floatToIntBits(v);
- data[base] = (byte)(tmp>>24);
- data[base+1] = (byte)(tmp>>16);
- data[base+2] = (byte)(tmp>>8);
- data[base+3] = (byte)tmp;
- }
-
- void putInt(int base, int i) {
- data[base] = (byte)(i>>24);
- data[base+1] = (byte)(i>>16);
- data[base+2] = (byte)(i>>8);
- data[base+3] = (byte)i;
- }
-
-
-}
+package ij.io;
import ij.gui.*;
import java.awt.*;
import java.io.*;
import java.util.*;
import java.net.*;
/** Saves an ROI to a file or stream. RoiDecoder.java has a description of the file format.
@see ij.io.RoiDecoder
@see ij.plugin.RoiReader
*/
public class RoiEncoder {
String path;
OutputStream f;
static final int HEADER_SIZE = 64;
static final int VERSION = 217;
final int polygon=0, rect=1, oval=2, line=3, freeline=4, polyline=5, noRoi=6, freehand=7,
traced=8, angle=9, point=10;
byte[] data;
/** Creates an RoiEncoder using the specified path. */
public RoiEncoder(String path) {
this.path = path;
}
/** Creates an RoiEncoder using the specified OutputStream. */
public RoiEncoder(OutputStream f) {
this.f = f;
}
/** Save the Roi to the file of stream. */
public void write(Roi roi) throws IOException {
if (f!=null) {
write(roi, f);
} else {
f = new FileOutputStream(path);
write(roi, f);
f.close();
}
}
void write(Roi roi, OutputStream f) throws IOException {
int roiType = roi.getType();
int type = rect;
switch (roiType) {
case Roi.POLYGON: type=polygon; break;
case Roi.FREEROI: type=freehand; break;
case Roi.TRACED_ROI: type=traced; break;
case Roi.OVAL: type=oval; break;
case Roi.LINE: type=line; break;
case Roi.POLYLINE: type=polyline; break;
case Roi.FREELINE: type=freeline; break;
case Roi.ANGLE: type=angle; break;
case Roi.COMPOSITE: type=rect; break;// shape array size (36-39) will be >0 to indicate composite type
case Roi.POINT: type=point; break;
default: type = rect; break;
}
if (roiType==Roi.COMPOSITE) {
saveShapeRoi(roi, type, f);
return;
}
int n=0;
int[] x=null,y=null;
if (roi instanceof PolygonRoi) {
Polygon p = ((PolygonRoi)roi).getNonSplineCoordinates();
n = p.npoints;
x = p.xpoints;
y = p.ypoints;
}
data = new byte[HEADER_SIZE+n*4];
Rectangle r = roi.getBounds();
data[0]=73; data[1]=111; data[2]=117; data[3]=116; // "Iout"
putShort(4, VERSION);
data[6] = (byte)type;
putShort(8, r.y); //top
putShort(10, r.x); //left
putShort(12, r.y+r.height); //bottom
putShort(14, r.x+r.width); //right
putShort(16, n);
if (roi instanceof Line) {
Line l = (Line)roi;
putFloat(18, l.x1);
putFloat(22, l.y1);
putFloat(26, l.x2);
putFloat(30, l.y2);
}
if (n>0) {
int base1 = 64;
int base2 = base1+2*n;
for (int i=0; i>>8);
data[base+1] = (byte)v;
}
void putFloat(int base, float v) {
int tmp = Float.floatToIntBits(v);
data[base] = (byte)(tmp>>24);
data[base+1] = (byte)(tmp>>16);
data[base+2] = (byte)(tmp>>8);
data[base+3] = (byte)tmp;
}
void putInt(int base, int i) {
data[base] = (byte)(i>>24);
data[base+1] = (byte)(i>>16);
data[base+2] = (byte)(i>>8);
data[base+3] = (byte)i;
}
}
\ No newline at end of file
diff --git a/ij/io/SaveDialog.java b/ij/io/SaveDialog.java
index d772f00e5..63b863082 100644
--- a/ij/io/SaveDialog.java
+++ b/ij/io/SaveDialog.java
@@ -1,203 +1,203 @@
-package ij.io;
-import java.awt.*;
-import java.io.*;
-import javax.swing.*;
-import javax.swing.filechooser.*;
-import ij.*;
-import ij.plugin.frame.Recorder;
-import ij.util.Java2;
-
-/** This class displays a dialog window from
- which the user can save a file. */
-public class SaveDialog {
-
- private String dir;
- private String name;
- private String title;
- private String ext;
-
- /** Displays a file save dialog with 'title' as the
- title, 'defaultName' as the initial file name, and
- 'extension' (e.g. ".tif") as the default extension.
- */
- public SaveDialog(String title, String defaultName, String extension) {
- this.title = title;
- ext = extension;
- if (isMacro())
- return;
- String defaultDir = OpenDialog.getDefaultDirectory();
- defaultName = setExtension(defaultName, extension);
- if (Prefs.useJFileChooser)
- jSave(title, defaultDir, defaultName);
- else
- save(title, defaultDir, defaultName);
- if (name!=null && dir!=null)
- OpenDialog.setDefaultDirectory(dir);
- IJ.showStatus(title+": "+dir+name);
- }
-
- /** Displays a file save dialog, using the specified
- default directory and file name and extension. */
- public SaveDialog(String title, String defaultDir, String defaultName, String extension) {
- this.title = title;
- ext = extension;
- if (isMacro())
- return;
- defaultName = setExtension(defaultName, extension);
- if (Prefs.useJFileChooser)
- jSave(title, defaultDir, defaultName);
- else
- save(title, defaultDir, defaultName);
- IJ.showStatus(title+": "+dir+name);
- }
-
- boolean isMacro() {
- String macroOptions = Macro.getOptions();
- if (macroOptions!=null) {
- String path = Macro.getValue(macroOptions, title, null);
- if (path==null)
- path = Macro.getValue(macroOptions, "path", null);
- //if (path==null && oneRunArg) {
- // path = macroOptions;
- //}
- if (path!=null) {
- Opener o = new Opener();
- dir = o.getDir(path);
- name = o.getName(path);
- return true;
- }
- }
- return false;
- }
-
- public static String setExtension(String name, String extension) {
- if (name==null || extension==null || extension.length()==0)
- return name;
- int dotIndex = name.lastIndexOf(".");
- if (dotIndex>=0 && (name.length()-dotIndex)<=5) {
- if (dotIndex+1=0 && (name.length()-dotIndex)<=5) {
+ if (dotIndex+10) {
- int index2 = id.indexOf("\n", index1);
- if (index2>0) {
- String images = id.substring(index1+7,index2);
- int n = (int)Tools.parseDouble(images, 0.0);
- if (n>1) fi.nImages = n;
- }
- }
- }
-
- public void saveMetadata(String name, String data) {
- if (data==null) return;
- String str = name+": "+data+"\n";
- if (tiffMetadata==null)
- tiffMetadata = str;
- else
- tiffMetadata += str;
- }
-
- void decodeNIHImageHeader(int offset, FileInfo fi) throws IOException {
- long saveLoc = in.getLongFilePointer();
-
- in.seek(offset+12);
- int version = in.readShort();
-
- in.seek(offset+160);
- double scale = in.readDouble();
- if (version>106 && scale!=0.0) {
- fi.pixelWidth = 1.0/scale;
- fi.pixelHeight = fi.pixelWidth;
- }
-
- // spatial calibration
- in.seek(offset+172);
- int units = in.readShort();
- if (version<=153) units += 5;
- switch (units) {
- case 5: fi.unit = "nanometer"; break;
- case 6: fi.unit = "micrometer"; break;
- case 7: fi.unit = "mm"; break;
- case 8: fi.unit = "cm"; break;
- case 9: fi.unit = "meter"; break;
- case 10: fi.unit = "km"; break;
- case 11: fi.unit = "inch"; break;
- case 12: fi.unit = "ft"; break;
- case 13: fi.unit = "mi"; break;
- }
-
- // density calibration
- in.seek(offset+182);
- int fitType = in.read();
- int unused = in.read();
- int nCoefficients = in.readShort();
- if (fitType==11) {
- fi.calibrationFunction = 21; //Calibration.UNCALIBRATED_OD
- fi.valueUnit = "U. OD";
- } else if (fitType>=0 && fitType<=8 && nCoefficients>=1 && nCoefficients<=5) {
- switch (fitType) {
- case 0: fi.calibrationFunction = 0; break; //Calibration.STRAIGHT_LINE
- case 1: fi.calibrationFunction = 1; break; //Calibration.POLY2
- case 2: fi.calibrationFunction = 2; break; //Calibration.POLY3
- case 3: fi.calibrationFunction = 3; break; //Calibration.POLY4
- case 5: fi.calibrationFunction = 4; break; //Calibration.EXPONENTIAL
- case 6: fi.calibrationFunction = 5; break; //Calibration.POWER
- case 7: fi.calibrationFunction = 6; break; //Calibration.LOG
- case 8: fi.calibrationFunction = 10; break; //Calibration.RODBARD2 (NIH Image)
- }
- fi.coefficients = new double[nCoefficients];
- for (int i=0; i=1 && size<=16) {
- for (int i=0; i=2 && (fi.fileType==FileInfo.GRAY8||fi.fileType==FileInfo.COLOR8)) {
- fi.nImages = nImages;
- fi.pixelDepth = in.readFloat(); //SliceSpacing
- int skip = in.readShort(); //CurrentSlice
- fi.frameInterval = in.readFloat();
- //ij.IJ.write("fi.pixelDepth: "+fi.pixelDepth);
- }
-
- in.seek(offset+272);
- float aspectRatio = in.readFloat();
- if (version>140 && aspectRatio!=0.0)
- fi.pixelHeight = fi.pixelWidth/aspectRatio;
-
- in.seek(saveLoc);
- }
-
- void dumpTag(int tag, int count, int value, FileInfo fi) {
- String name = getName(tag);
- String cs = (count==1)?"":", count=" + count;
- dInfo += " " + tag + ", \"" + name + "\", value=" + value + cs + "\n";
- //ij.IJ.log(tag + ", \"" + name + "\", value=" + value + cs + "\n");
- }
-
- String getName(int tag) {
- String name;
- switch (tag) {
- case NEW_SUBFILE_TYPE: name="NewSubfileType"; break;
- case IMAGE_WIDTH: name="ImageWidth"; break;
- case IMAGE_LENGTH: name="ImageLength"; break;
- case STRIP_OFFSETS: name="StripOffsets"; break;
- case ORIENTATION: name="Orientation"; break;
- case PHOTO_INTERP: name="PhotoInterp"; break;
- case IMAGE_DESCRIPTION: name="ImageDescription"; break;
- case BITS_PER_SAMPLE: name="BitsPerSample"; break;
- case SAMPLES_PER_PIXEL: name="SamplesPerPixel"; break;
- case ROWS_PER_STRIP: name="RowsPerStrip"; break;
- case STRIP_BYTE_COUNT: name="StripByteCount"; break;
- case X_RESOLUTION: name="XResolution"; break;
- case Y_RESOLUTION: name="YResolution"; break;
- case RESOLUTION_UNIT: name="ResolutionUnit"; break;
- case SOFTWARE: name="Software"; break;
- case DATE_TIME: name="DateTime"; break;
- case ARTEST: name="Artest"; break;
- case HOST_COMPUTER: name="HostComputer"; break;
- case PLANAR_CONFIGURATION: name="PlanarConfiguration"; break;
- case COMPRESSION: name="Compression"; break;
- case PREDICTOR: name="Predictor"; break;
- case COLOR_MAP: name="ColorMap"; break;
- case SAMPLE_FORMAT: name="SampleFormat"; break;
- case JPEG_TABLES: name="JPEGTables"; break;
- case NIH_IMAGE_HDR: name="NIHImageHeader"; break;
- case META_DATA_BYTE_COUNTS: name="MetaDataByteCounts"; break;
- case META_DATA: name="MetaData"; break;
- default: name="???"; break;
- }
- return name;
- }
-
- double getRational(int loc) throws IOException {
- long saveLoc = in.getLongFilePointer();
- in.seek(loc);
- int numerator = getInt();
- int denominator = getInt();
- in.seek(saveLoc);
- //System.out.println("numerator: "+numerator);
- //System.out.println("denominator: "+denominator);
- if (denominator!=0)
- return (double)numerator/denominator;
- else
- return 0.0;
- }
-
- FileInfo OpenIFD() throws IOException {
- // Get Image File Directory data
- int tag, fieldType, count, value;
- int nEntries = getShort();
- if (nEntries<1 || nEntries>1000)
- return null;
- ifdCount++;
- FileInfo fi = new FileInfo();
- for (int i=0; i0||name.indexOf(".stk")>0) && fi.compression==FileInfo.COMPRESSION_NONE) {
- if (tag==METAMORPH2)
- fi.nImages=count;
- else
- fi.nImages=9999;
- }
- break;
- case IPLAB:
- fi.nImages=value;
- break;
- case NIH_IMAGE_HDR:
- if (count==256)
- decodeNIHImageHeader(value, fi);
- break;
- case META_DATA_BYTE_COUNTS:
- long saveLoc = in.getLongFilePointer();
- in.seek(lvalue);
- metaDataCounts = new int[count];
- for (int c=0; c10000 && tag<32768 && ifdCount>1)
- return null;
- }
- }
- fi.fileFormat = fi.TIFF;
- fi.fileName = name;
- fi.directory = directory;
- if (url!=null)
- fi.url = url;
- return fi;
- }
-
- void getMetaData(int loc, FileInfo fi) throws IOException {
- if (metaDataCounts==null || metaDataCounts.length==0)
- return;
- int maxTypes = 10;
- long saveLoc = in.getLongFilePointer();
- in.seek(loc);
- int n = metaDataCounts.length;
- int hdrSize = metaDataCounts[0];
- if (hdrSize<12 || hdrSize>804)
- {in.seek(saveLoc); return;}
- int magicNumber = getInt();
- if (magicNumber!=MAGIC_NUMBER) // "IJIJ"
- {in.seek(saveLoc); return;}
- int nTypes = (hdrSize-4)/8;
- int[] types = new int[nTypes];
- int[] counts = new int[nTypes];
-
- if (debugMode) dInfo += "Metadata:\n";
- int extraMetaDataEntries = 0;
- for (int i=0; i0) {
- if (len>buffer.length)
- buffer = new byte[len];
- in.readFully(buffer, len);
- len /= 2;
- char[] chars = new char[len];
- if (littleEndian) {
- for (int j=0, k=0; jbuffer.length)
- buffer = new byte[len];
- in.readFully(buffer, len);
- }
- }
-
- public void enableDebugging() {
- debugMode = true;
- }
-
-
- public FileInfo[] getTiffInfo() throws IOException {
- long ifdOffset;
- Vector info;
-
- if (in==null)
- in = new RandomAccessStream(new RandomAccessFile(new File(directory, name), "r"));
- info = new Vector();
- ifdOffset = OpenImageFileHeader();
- if (ifdOffset<0L) {
- in.close();
- return null;
- }
- if (debugMode) dInfo = "\n " + name + ": opening\n";
- while (ifdOffset>0L) {
- in.seek(ifdOffset);
- FileInfo fi = OpenIFD();
- if (fi!=null) {
- info.addElement(fi);
- ifdOffset = ((long)getInt())&0xffffffffL;
- } else
- ifdOffset = 0L;
- if (debugMode && ifdCount<10) dInfo += " nextIFD=" + ifdOffset + "\n";
- if (fi!=null) {
- if (fi.nImages>1) // ignore extra IFDs in ImageJ and NIH Image stacks
- ifdOffset = 0L;
- }
- }
- if (info.size()==0) {
- in.close();
- return null;
- } else {
- FileInfo[] fi = new FileInfo[info.size()];
- info.copyInto((Object[])fi);
- if (debugMode) fi[0].debugInfo = dInfo;
- if (url!=null) {
- in.seek(0);
- fi[0].inputStream = in;
- } else
- in.close();
- if (fi[0].info==null)
- fi[0].info = tiffMetadata;
- return fi;
- }
- }
-
-}
+package ij.io;
+import ij.util.Tools;
+import java.io.*;
+import java.util.*;
+import java.net.*;
+
+/**
+Decodes single and multi-image TIFF files. The LZW decompression
+code was contributed by Curtis Rueden.
+*/
+public class TiffDecoder {
+
+ // tags
+ public static final int NEW_SUBFILE_TYPE = 254;
+ public static final int IMAGE_WIDTH = 256;
+ public static final int IMAGE_LENGTH = 257;
+ public static final int BITS_PER_SAMPLE = 258;
+ public static final int COMPRESSION = 259;
+ public static final int PHOTO_INTERP = 262;
+ public static final int IMAGE_DESCRIPTION = 270;
+ public static final int STRIP_OFFSETS = 273;
+ public static final int ORIENTATION = 274;
+ public static final int SAMPLES_PER_PIXEL = 277;
+ public static final int ROWS_PER_STRIP = 278;
+ public static final int STRIP_BYTE_COUNT = 279;
+ public static final int X_RESOLUTION = 282;
+ public static final int Y_RESOLUTION = 283;
+ public static final int PLANAR_CONFIGURATION = 284;
+ public static final int RESOLUTION_UNIT = 296;
+ public static final int SOFTWARE = 305;
+ public static final int DATE_TIME = 306;
+ public static final int ARTEST = 315;
+ public static final int HOST_COMPUTER = 316;
+ public static final int PREDICTOR = 317;
+ public static final int COLOR_MAP = 320;
+ public static final int SAMPLE_FORMAT = 339;
+ public static final int JPEG_TABLES = 347;
+ public static final int METAMORPH1 = 33628;
+ public static final int METAMORPH2 = 33629;
+ public static final int IPLAB = 34122;
+ public static final int NIH_IMAGE_HDR = 43314;
+ public static final int META_DATA_BYTE_COUNTS = 50838; // private tag registered with Adobe
+ public static final int META_DATA = 50839; // private tag registered with Adobe
+
+ //constants
+ static final int UNSIGNED = 1;
+ static final int SIGNED = 2;
+ static final int FLOATING_POINT = 3;
+
+ //field types
+ static final int SHORT = 3;
+ static final int LONG = 4;
+
+ // metadata types
+ static final int MAGIC_NUMBER = 0x494a494a; // "IJIJ"
+ static final int INFO = 0x696e666f; // "info" (Info image property)
+ static final int LABELS = 0x6c61626c; // "labl" (slice labels)
+ static final int RANGES = 0x72616e67; // "rang" (display ranges)
+ static final int LUTS = 0x6c757473; // "luts" (channel LUTs)
+
+ private String directory;
+ private String name;
+ private String url;
+ protected RandomAccessStream in;
+ protected boolean debugMode;
+ private boolean littleEndian;
+ private String dInfo;
+ private int ifdCount;
+ private int[] metaDataCounts;
+ private String tiffMetadata;
+
+ public TiffDecoder(String directory, String name) {
+ this.directory = directory;
+ this.name = name;
+ }
+
+ public TiffDecoder(InputStream in, String name) {
+ directory = "";
+ this.name = name;
+ url = "";
+ this.in = new RandomAccessStream(in);
+ }
+
+ final int getInt() throws IOException {
+ int b1 = in.read();
+ int b2 = in.read();
+ int b3 = in.read();
+ int b4 = in.read();
+ if (littleEndian)
+ return ((b4 << 24) + (b3 << 16) + (b2 << 8) + (b1 << 0));
+ else
+ return ((b1 << 24) + (b2 << 16) + (b3 << 8) + b4);
+ }
+
+ final int getShort() throws IOException {
+ int b1 = in.read();
+ int b2 = in.read();
+ if (littleEndian)
+ return ((b2<<8) + b1);
+ else
+ return ((b1<<8) + b2);
+ }
+
+ final long readLong() throws IOException {
+ if (littleEndian)
+ return ((long)getInt()&0xffffffffL) + ((long)getInt()<<32);
+ else
+ return ((long)getInt()<<32) + ((long)getInt()&0xffffffffL);
+ //return in.read()+(in.read()<<8)+(in.read()<<16)+(in.read()<<24)+(in.read()<<32)+(in.read()<<40)+(in.read()<<48)+(in.read()<<56);
+ }
+
+ final double readDouble() throws IOException {
+ return Double.longBitsToDouble(readLong());
+ }
+
+ long OpenImageFileHeader() throws IOException {
+ // Open 8-byte Image File Header at start of file.
+ // Returns the offset in bytes to the first IFD or -1
+ // if this is not a valid tiff file.
+ int byteOrder = in.readShort();
+ if (byteOrder==0x4949) // "II"
+ littleEndian = true;
+ else if (byteOrder==0x4d4d) // "MM"
+ littleEndian = false;
+ else {
+ in.close();
+ return -1;
+ }
+ int magicNumber = getShort(); // 42
+ long offset = ((long)getInt())&0xffffffffL;
+ return offset;
+ }
+
+ int getValue(int fieldType, int count) throws IOException {
+ int value = 0;
+ int unused;
+ if (fieldType==SHORT && count==1) {
+ value = getShort();
+ unused = getShort();
+ } else
+ value = getInt();
+ return value;
+ }
+
+ void getColorMap(long offset, FileInfo fi) throws IOException {
+ byte[] colorTable16 = new byte[768*2];
+ long saveLoc = in.getLongFilePointer();
+ in.seek(offset);
+ in.readFully(colorTable16);
+ in.seek(saveLoc);
+ fi.lutSize = 256;
+ fi.reds = new byte[256];
+ fi.greens = new byte[256];
+ fi.blues = new byte[256];
+ int j = 0;
+ if (littleEndian) j++;
+ int sum = 0;
+ for (int i=0; i<256; i++) {
+ fi.reds[i] = colorTable16[j];
+ sum += fi.reds[i];
+ fi.greens[i] = colorTable16[512+j];
+ sum += fi.greens[i];
+ fi.blues[i] = colorTable16[1024+j];
+ sum += fi.blues[i];
+ j += 2;
+ }
+ if (sum!=0)
+ fi.fileType = FileInfo.COLOR8;
+ }
+
+ byte[] getString(int count, long offset) throws IOException {
+ count--; // skip null byte at end of string
+ if (count<=0)
+ return null;
+ byte[] bytes = new byte[count];
+ long saveLoc = in.getLongFilePointer();
+ in.seek(offset);
+ in.readFully(bytes);
+ in.seek(saveLoc);
+ return bytes;
+ }
+
+ /** Save the image description in the specified FileInfo. ImageJ
+ saves spatial and density calibration data in this string. For
+ stacks, it also saves the number of images to avoid having to
+ decode an IFD for each image. */
+ public void saveImageDescription(byte[] description, FileInfo fi) {
+ String id = new String(description);
+ if (!id.startsWith("ImageJ"))
+ saveMetadata(getName(IMAGE_DESCRIPTION), id);
+ if (id.length()<7) return;
+ fi.description = id;
+ int index1 = id.indexOf("images=");
+ if (index1>0) {
+ int index2 = id.indexOf("\n", index1);
+ if (index2>0) {
+ String images = id.substring(index1+7,index2);
+ int n = (int)Tools.parseDouble(images, 0.0);
+ if (n>1) fi.nImages = n;
+ }
+ }
+ }
+
+ public void saveMetadata(String name, String data) {
+ if (data==null) return;
+ String str = name+": "+data+"\n";
+ if (tiffMetadata==null)
+ tiffMetadata = str;
+ else
+ tiffMetadata += str;
+ }
+
+ void decodeNIHImageHeader(int offset, FileInfo fi) throws IOException {
+ long saveLoc = in.getLongFilePointer();
+
+ in.seek(offset+12);
+ int version = in.readShort();
+
+ in.seek(offset+160);
+ double scale = in.readDouble();
+ if (version>106 && scale!=0.0) {
+ fi.pixelWidth = 1.0/scale;
+ fi.pixelHeight = fi.pixelWidth;
+ }
+
+ // spatial calibration
+ in.seek(offset+172);
+ int units = in.readShort();
+ if (version<=153) units += 5;
+ switch (units) {
+ case 5: fi.unit = "nanometer"; break;
+ case 6: fi.unit = "micrometer"; break;
+ case 7: fi.unit = "mm"; break;
+ case 8: fi.unit = "cm"; break;
+ case 9: fi.unit = "meter"; break;
+ case 10: fi.unit = "km"; break;
+ case 11: fi.unit = "inch"; break;
+ case 12: fi.unit = "ft"; break;
+ case 13: fi.unit = "mi"; break;
+ }
+
+ // density calibration
+ in.seek(offset+182);
+ int fitType = in.read();
+ int unused = in.read();
+ int nCoefficients = in.readShort();
+ if (fitType==11) {
+ fi.calibrationFunction = 21; //Calibration.UNCALIBRATED_OD
+ fi.valueUnit = "U. OD";
+ } else if (fitType>=0 && fitType<=8 && nCoefficients>=1 && nCoefficients<=5) {
+ switch (fitType) {
+ case 0: fi.calibrationFunction = 0; break; //Calibration.STRAIGHT_LINE
+ case 1: fi.calibrationFunction = 1; break; //Calibration.POLY2
+ case 2: fi.calibrationFunction = 2; break; //Calibration.POLY3
+ case 3: fi.calibrationFunction = 3; break; //Calibration.POLY4
+ case 5: fi.calibrationFunction = 4; break; //Calibration.EXPONENTIAL
+ case 6: fi.calibrationFunction = 5; break; //Calibration.POWER
+ case 7: fi.calibrationFunction = 6; break; //Calibration.LOG
+ case 8: fi.calibrationFunction = 10; break; //Calibration.RODBARD2 (NIH Image)
+ }
+ fi.coefficients = new double[nCoefficients];
+ for (int i=0; i=1 && size<=16) {
+ for (int i=0; i=2 && (fi.fileType==FileInfo.GRAY8||fi.fileType==FileInfo.COLOR8)) {
+ fi.nImages = nImages;
+ fi.pixelDepth = in.readFloat(); //SliceSpacing
+ int skip = in.readShort(); //CurrentSlice
+ fi.frameInterval = in.readFloat();
+ //ij.IJ.write("fi.pixelDepth: "+fi.pixelDepth);
+ }
+
+ in.seek(offset+272);
+ float aspectRatio = in.readFloat();
+ if (version>140 && aspectRatio!=0.0)
+ fi.pixelHeight = fi.pixelWidth/aspectRatio;
+
+ in.seek(saveLoc);
+ }
+
+ void dumpTag(int tag, int count, int value, FileInfo fi) {
+ String name = getName(tag);
+ String cs = (count==1)?"":", count=" + count;
+ dInfo += " " + tag + ", \"" + name + "\", value=" + value + cs + "\n";
+ //ij.IJ.log(tag + ", \"" + name + "\", value=" + value + cs + "\n");
+ }
+
+ String getName(int tag) {
+ String name;
+ switch (tag) {
+ case NEW_SUBFILE_TYPE: name="NewSubfileType"; break;
+ case IMAGE_WIDTH: name="ImageWidth"; break;
+ case IMAGE_LENGTH: name="ImageLength"; break;
+ case STRIP_OFFSETS: name="StripOffsets"; break;
+ case ORIENTATION: name="Orientation"; break;
+ case PHOTO_INTERP: name="PhotoInterp"; break;
+ case IMAGE_DESCRIPTION: name="ImageDescription"; break;
+ case BITS_PER_SAMPLE: name="BitsPerSample"; break;
+ case SAMPLES_PER_PIXEL: name="SamplesPerPixel"; break;
+ case ROWS_PER_STRIP: name="RowsPerStrip"; break;
+ case STRIP_BYTE_COUNT: name="StripByteCount"; break;
+ case X_RESOLUTION: name="XResolution"; break;
+ case Y_RESOLUTION: name="YResolution"; break;
+ case RESOLUTION_UNIT: name="ResolutionUnit"; break;
+ case SOFTWARE: name="Software"; break;
+ case DATE_TIME: name="DateTime"; break;
+ case ARTEST: name="Artest"; break;
+ case HOST_COMPUTER: name="HostComputer"; break;
+ case PLANAR_CONFIGURATION: name="PlanarConfiguration"; break;
+ case COMPRESSION: name="Compression"; break;
+ case PREDICTOR: name="Predictor"; break;
+ case COLOR_MAP: name="ColorMap"; break;
+ case SAMPLE_FORMAT: name="SampleFormat"; break;
+ case JPEG_TABLES: name="JPEGTables"; break;
+ case NIH_IMAGE_HDR: name="NIHImageHeader"; break;
+ case META_DATA_BYTE_COUNTS: name="MetaDataByteCounts"; break;
+ case META_DATA: name="MetaData"; break;
+ default: name="???"; break;
+ }
+ return name;
+ }
+
+ double getRational(int loc) throws IOException {
+ long saveLoc = in.getLongFilePointer();
+ in.seek(loc);
+ int numerator = getInt();
+ int denominator = getInt();
+ in.seek(saveLoc);
+ //System.out.println("numerator: "+numerator);
+ //System.out.println("denominator: "+denominator);
+ if (denominator!=0)
+ return (double)numerator/denominator;
+ else
+ return 0.0;
+ }
+
+ FileInfo OpenIFD() throws IOException {
+ // Get Image File Directory data
+ int tag, fieldType, count, value;
+ int nEntries = getShort();
+ if (nEntries<1 || nEntries>1000)
+ return null;
+ ifdCount++;
+ FileInfo fi = new FileInfo();
+ for (int i=0; i0||name.indexOf(".stk")>0) && fi.compression==FileInfo.COMPRESSION_NONE) {
+ if (tag==METAMORPH2)
+ fi.nImages=count;
+ else
+ fi.nImages=9999;
+ }
+ break;
+ case IPLAB:
+ fi.nImages=value;
+ break;
+ case NIH_IMAGE_HDR:
+ if (count==256)
+ decodeNIHImageHeader(value, fi);
+ break;
+ case META_DATA_BYTE_COUNTS:
+ long saveLoc = in.getLongFilePointer();
+ in.seek(lvalue);
+ metaDataCounts = new int[count];
+ for (int c=0; c10000 && tag<32768 && ifdCount>1)
+ return null;
+ }
+ }
+ fi.fileFormat = fi.TIFF;
+ fi.fileName = name;
+ fi.directory = directory;
+ if (url!=null)
+ fi.url = url;
+ return fi;
+ }
+
+ void getMetaData(int loc, FileInfo fi) throws IOException {
+ if (metaDataCounts==null || metaDataCounts.length==0)
+ return;
+ int maxTypes = 10;
+ long saveLoc = in.getLongFilePointer();
+ in.seek(loc);
+ int n = metaDataCounts.length;
+ int hdrSize = metaDataCounts[0];
+ if (hdrSize<12 || hdrSize>804)
+ {in.seek(saveLoc); return;}
+ int magicNumber = getInt();
+ if (magicNumber!=MAGIC_NUMBER) // "IJIJ"
+ {in.seek(saveLoc); return;}
+ int nTypes = (hdrSize-4)/8;
+ int[] types = new int[nTypes];
+ int[] counts = new int[nTypes];
+
+ if (debugMode) dInfo += "Metadata:\n";
+ int extraMetaDataEntries = 0;
+ for (int i=0; i0) {
+ if (len>buffer.length)
+ buffer = new byte[len];
+ in.readFully(buffer, len);
+ len /= 2;
+ char[] chars = new char[len];
+ if (littleEndian) {
+ for (int j=0, k=0; jbuffer.length)
+ buffer = new byte[len];
+ in.readFully(buffer, len);
+ }
+ }
+
+ public void enableDebugging() {
+ debugMode = true;
+ }
+
+
+ public FileInfo[] getTiffInfo() throws IOException {
+ long ifdOffset;
+ Vector info;
+
+ if (in==null)
+ in = new RandomAccessStream(new RandomAccessFile(new File(directory, name), "r"));
+ info = new Vector();
+ ifdOffset = OpenImageFileHeader();
+ if (ifdOffset<0L) {
+ in.close();
+ return null;
+ }
+ if (debugMode) dInfo = "\n " + name + ": opening\n";
+ while (ifdOffset>0L) {
+ in.seek(ifdOffset);
+ FileInfo fi = OpenIFD();
+ if (fi!=null) {
+ info.addElement(fi);
+ ifdOffset = ((long)getInt())&0xffffffffL;
+ } else
+ ifdOffset = 0L;
+ if (debugMode && ifdCount<10) dInfo += " nextIFD=" + ifdOffset + "\n";
+ if (fi!=null) {
+ if (fi.nImages>1) // ignore extra IFDs in ImageJ and NIH Image stacks
+ ifdOffset = 0L;
+ }
+ }
+ if (info.size()==0) {
+ in.close();
+ return null;
+ } else {
+ FileInfo[] fi = new FileInfo[info.size()];
+ info.copyInto((Object[])fi);
+ if (debugMode) fi[0].debugInfo = dInfo;
+ if (url!=null) {
+ in.seek(0);
+ fi[0].inputStream = in;
+ } else
+ in.close();
+ if (fi[0].info==null)
+ fi[0].info = tiffMetadata;
+ return fi;
+ }
+ }
+
+}
diff --git a/ij/io/TiffEncoder.java b/ij/io/TiffEncoder.java
index 2f0e5f244..4d203d11a 100644
--- a/ij/io/TiffEncoder.java
+++ b/ij/io/TiffEncoder.java
@@ -1,476 +1,476 @@
-package ij.io;
-import java.io.*;
-
-/**Saves an image described by a FileInfo object as an uncompressed, big-endian TIFF file.*/
-public class TiffEncoder {
- static final int HDR_SIZE = 8;
- static final int MAP_SIZE = 768; // in 16-bit words
- static final int BPS_DATA_SIZE = 6;
- static final int SCALE_DATA_SIZE = 16;
-
- private FileInfo fi;
- private int bitsPerSample;
- private int photoInterp;
- private int samplesPerPixel;
- private int nEntries;
- private int ifdSize;
- private long imageOffset;
- private int imageSize;
- private long stackSize;
- private byte[] description;
- private int metaDataSize;
- private int nMetaDataTypes;
- private int nMetaDataEntries;
- private int nSliceLabels;
- private int extraMetaDataEntries;
- private int scaleSize;
- private boolean littleEndian = ij.Prefs.intelByteOrder;
- private byte buffer[] = new byte[8];
-
- public TiffEncoder (FileInfo fi) {
- this.fi = fi;
- fi.intelByteOrder = littleEndian;
- bitsPerSample = 8;
- samplesPerPixel = 1;
- nEntries = 9;
- int bytesPerPixel = 1;
- int bpsSize = 0;
- int colorMapSize = 0;
-
- switch (fi.fileType) {
- case FileInfo.GRAY8:
- photoInterp = fi.whiteIsZero?0:1;
- break;
- case FileInfo.GRAY16_UNSIGNED:
- case FileInfo.GRAY16_SIGNED:
- bitsPerSample = 16;
- photoInterp = fi.whiteIsZero?0:1;
- bytesPerPixel = 2;
- break;
- case FileInfo.GRAY32_FLOAT:
- bitsPerSample = 32;
- photoInterp = fi.whiteIsZero?0:1;
- bytesPerPixel = 4;
- break;
- case FileInfo.RGB:
- photoInterp = 2;
- samplesPerPixel = 3;
- bytesPerPixel = 3;
- bpsSize = BPS_DATA_SIZE;
- break;
- case FileInfo.RGB48:
- bitsPerSample = 16;
- photoInterp = 2;
- samplesPerPixel = 3;
- bytesPerPixel = 6;
- fi.nImages /= 3;
- bpsSize = BPS_DATA_SIZE;
- break;
- case FileInfo.COLOR8:
- photoInterp = 3;
- nEntries = 10;
- colorMapSize = MAP_SIZE*2;
- break;
- default:
- photoInterp = 0;
- }
- if (fi.unit!=null && fi.pixelWidth!=0 && fi.pixelHeight!=0)
- nEntries += 3; // XResolution, YResolution and ResolutionUnit
- if (fi.fileType==fi.GRAY32_FLOAT)
- nEntries++; // SampleFormat tag
- makeDescriptionString();
- if (description!=null)
- nEntries++; // ImageDescription tag
- imageSize = fi.width*fi.height*bytesPerPixel;
- stackSize = (long)imageSize*fi.nImages;
- metaDataSize = getMetaDataSize();
- if (metaDataSize>0)
- nEntries += 2; // MetaData & MetaDataCounts
- ifdSize = 2 + nEntries*12 + 4;
- int descriptionSize = description!=null?description.length:0;
- scaleSize = fi.unit!=null && fi.pixelWidth!=0 && fi.pixelHeight!=0?SCALE_DATA_SIZE:0;
- imageOffset = HDR_SIZE+ifdSize+bpsSize+descriptionSize+scaleSize+colorMapSize + nMetaDataEntries*4 + metaDataSize;
- fi.offset = (int)imageOffset;
- //ij.IJ.log(imageOffset+", "+ifdSize+", "+bpsSize+", "+descriptionSize+", "+scaleSize+", "+colorMapSize+", "+nMetaDataEntries*4+", "+metaDataSize);
- }
-
- /** Saves the image as a TIFF file. The OutputStream is not closed.
- The fi.pixels field must contain the image data. If fi.nImages>1
- then fi.pixels must be a 2D array. The fi.offset field is ignored. */
- public void write(OutputStream out) throws IOException {
- writeHeader(out);
- long nextIFD = 0L;
- if (fi.nImages>1)
- nextIFD = imageOffset+stackSize;
- if (nextIFD+fi.nImages*ifdSize>=0xffffffffL)
- nextIFD = 0L;
- writeIFD(out, (int)imageOffset, (int)nextIFD);
- if (fi.fileType==FileInfo.RGB||fi.fileType==FileInfo.RGB48)
- writeBitsPerPixel(out);
- if (description!=null)
- writeDescription(out);
- if (scaleSize>0)
- writeScale(out);
- if (fi.fileType==FileInfo.COLOR8)
- writeColorMap(out);
- if (metaDataSize>0)
- writeMetaData(out);
- new ImageWriter(fi).write(out);
- if (nextIFD>0L) {
- int ifdSize2 = ifdSize;
- if (metaDataSize>0) {
- metaDataSize = 0;
- nEntries -= 2;
- ifdSize2 -= 2*12;
- }
- for (int i=2; i<=fi.nImages; i++) {
- if (i==fi.nImages)
- nextIFD = 0;
- else
- nextIFD += ifdSize2;
- imageOffset += imageSize;
- writeIFD(out, (int)imageOffset, (int)nextIFD);
- }
- }
- }
-
- public void write(DataOutputStream out) throws IOException {
- write((OutputStream)out);
- }
-
- int getMetaDataSize() {
- //if (stackSize+IMAGE_START>0xffffffffL) return 0;
- nSliceLabels = 0;
- nMetaDataEntries = 0;
- int size = 0;
- int nTypes = 0;
- if (fi.info!=null && fi.info.length()>0) {
- nMetaDataEntries = 1;
- size = fi.info.length()*2;
- nTypes++;
- }
- if (fi.sliceLabels!=null) {
- int max = Math.min(fi.sliceLabels.length, fi.nImages);
- boolean isNonNullLabel = false;
- for (int i=0; i0) {
- isNonNullLabel = true;
- break;
- }
- }
- if (isNonNullLabel) {
- for (int i=0; i0) nTypes++;
- nMetaDataEntries += nSliceLabels;
- }
- }
-
- if (fi.displayRanges!=null) {
- nMetaDataEntries++;
- size += fi.displayRanges.length*8;
- nTypes++;
- }
-
- if (fi.channelLuts!=null) {
- for (int i=0; i0) nMetaDataEntries++; // add entry for header
- int hdrSize = 4 + nTypes*8;
- if (size>0) size += hdrSize;
- nMetaDataTypes = nTypes;
- return size;
- }
-
- /** Writes the 8-byte image file header. */
- void writeHeader(OutputStream out) throws IOException {
- byte[] hdr = new byte[8];
- if (littleEndian) {
- hdr[0] = 73; // "II" (Intel byte order)
- hdr[1] = 73;
- hdr[2] = 42; // 42 (magic number)
- hdr[3] = 0;
- hdr[4] = 8; // 8 (offset to first IFD)
- hdr[5] = 0;
- hdr[6] = 0;
- hdr[7] = 0;
- } else {
- hdr[0] = 77; // "MM" (Motorola byte order)
- hdr[1] = 77;
- hdr[2] = 0; // 42 (magic number)
- hdr[3] = 42;
- hdr[4] = 0; // 8 (offset to first IFD)
- hdr[5] = 0;
- hdr[6] = 0;
- hdr[7] = 8;
- }
- out.write(hdr);
- }
-
- /** Writes one 12-byte IFD entry. */
- void writeEntry(OutputStream out, int tag, int fieldType, int count, int value) throws IOException {
- writeShort(out, tag);
- writeShort(out, fieldType);
- writeInt(out, count);
- if (count==1 && fieldType==TiffDecoder.SHORT) {
- writeShort(out, value);
- writeShort(out, 0);
- } else
- writeInt(out, value); // may be an offset
- }
-
- /** Writes one IFD (Image File Directory). */
- void writeIFD(OutputStream out, int imageOffset, int nextIFD) throws IOException {
- int tagDataOffset = HDR_SIZE + ifdSize;
- writeShort(out, nEntries);
- writeEntry(out, TiffDecoder.NEW_SUBFILE_TYPE, 4, 1, 0);
- writeEntry(out, TiffDecoder.IMAGE_WIDTH, 4, 1, fi.width);
- writeEntry(out, TiffDecoder.IMAGE_LENGTH, 4, 1, fi.height);
- if (fi.fileType==FileInfo.RGB||fi.fileType==FileInfo.RGB48) {
- writeEntry(out, TiffDecoder.BITS_PER_SAMPLE, 3, 3, tagDataOffset);
- tagDataOffset += BPS_DATA_SIZE;
- } else
- writeEntry(out, TiffDecoder.BITS_PER_SAMPLE, 3, 1, bitsPerSample);
- writeEntry(out, TiffDecoder.PHOTO_INTERP, 3, 1, photoInterp);
- if (description!=null) {
- writeEntry(out, TiffDecoder.IMAGE_DESCRIPTION, 2, description.length, tagDataOffset);
- tagDataOffset += description.length;
- }
- writeEntry(out, TiffDecoder.STRIP_OFFSETS, 4, 1, imageOffset);
- writeEntry(out, TiffDecoder.SAMPLES_PER_PIXEL,3, 1, samplesPerPixel);
- writeEntry(out, TiffDecoder.ROWS_PER_STRIP, 3, 1, fi.height);
- writeEntry(out, TiffDecoder.STRIP_BYTE_COUNT, 4, 1, imageSize);
- if (fi.unit!=null && fi.pixelWidth!=0 && fi.pixelHeight!=0) {
- writeEntry(out, TiffDecoder.X_RESOLUTION, 5, 1, tagDataOffset);
- writeEntry(out, TiffDecoder.Y_RESOLUTION, 5, 1, tagDataOffset+8);
- tagDataOffset += SCALE_DATA_SIZE;
- int unit = 1;
- if (fi.unit.equals("inch"))
- unit = 2;
- else if (fi.unit.equals("cm"))
- unit = 3;
- writeEntry(out, TiffDecoder.RESOLUTION_UNIT, 3, 1, unit);
- }
- if (fi.fileType==fi.GRAY32_FLOAT) {
- int format = TiffDecoder.FLOATING_POINT;
- writeEntry(out, TiffDecoder.SAMPLE_FORMAT, 3, 1, format);
- }
- if (fi.fileType==FileInfo.COLOR8) {
- writeEntry(out, TiffDecoder.COLOR_MAP, 3, MAP_SIZE, tagDataOffset);
- tagDataOffset += MAP_SIZE*2;
- }
- if (metaDataSize>0) {
- writeEntry(out, TiffDecoder.META_DATA_BYTE_COUNTS, 4, nMetaDataEntries, tagDataOffset);
- writeEntry(out, TiffDecoder.META_DATA, 1, metaDataSize, tagDataOffset+4*nMetaDataEntries);
- tagDataOffset += nMetaDataEntries*4 + metaDataSize;
- }
- writeInt(out, nextIFD);
- }
-
- /** Writes the 6 bytes of data required by RGB BitsPerSample tag. */
- void writeBitsPerPixel(OutputStream out) throws IOException {
- int bitsPerPixel = fi.fileType==FileInfo.RGB48?16:8;
- writeShort(out, bitsPerPixel);
- writeShort(out, bitsPerPixel);
- writeShort(out, bitsPerPixel);
- }
-
- /** Writes the 16 bytes of data required by the XResolution and YResolution tags. */
- void writeScale(OutputStream out) throws IOException {
- double xscale = 1.0/fi.pixelWidth;
- double yscale = 1.0/fi.pixelHeight;
- double scale = 1000000.0;
- if (xscale>1000.0) scale = 1000.0;
- writeInt(out, (int)(xscale*scale));
- writeInt(out, (int)scale);
- writeInt(out, (int)(yscale*scale));
- writeInt(out, (int)scale);
- }
-
- /** Writes the variable length ImageDescription string. */
- void writeDescription(OutputStream out) throws IOException {
- out.write(description,0,description.length);
- }
-
- /** Writes color palette following the image. */
- void writeColorMap(OutputStream out) throws IOException {
- byte[] colorTable16 = new byte[MAP_SIZE*2];
- int j=littleEndian?1:0;
- for (int i=0; i