diff --git a/ij/ImageJ.java b/ij/ImageJ.java index 025323d8..2007bfbd 100644 --- a/ij/ImageJ.java +++ b/ij/ImageJ.java @@ -79,7 +79,7 @@ public class ImageJ extends Frame implements ActionListener, /** Plugins should call IJ.getVersion() or IJ.getFullVersion() to get the version string. */ public static final String VERSION = "1.54k"; - public static final String BUILD = "16"; + public static final String BUILD = "19"; public static Color backgroundColor = new Color(237,237,237); /** SansSerif, 12-point, plain font. */ public static final Font SansSerif12 = new Font("SansSerif", Font.PLAIN, 12); diff --git a/ij/Undo.java b/ij/Undo.java index c9b22f46..233dc00f 100644 --- a/ij/Undo.java +++ b/ij/Undo.java @@ -24,6 +24,8 @@ public class Undo { public static final int OVERLAY_ADDITION = 7; public static final int ROI = 8; public static final int MACRO = 9; + /** Undo of overlay modification */ + public static final int OVERLAY = 10; private static int whatToUndo = NOTHING; private static int imageID; @@ -34,6 +36,7 @@ public class Undo { private static double displayRangeMin, displayRangeMax; private static LUT lutCopy; private static Overlay overlayCopy; + private static int overlayImageID; public static void setup(int what, ImagePlus imp) { if (imp==null) { @@ -81,18 +84,23 @@ public static void setup(int what, ImagePlus imp) { roiCopy.setImage(null); } else whatToUndo = NOTHING; + } else if (what==OVERLAY) { + saveOverlay(imp); } else { ipCopy = null; - ImageProcessor ip = imp.getProcessor(); + //ImageProcessor ip = imp.getProcessor(); //lutCopy = (LUT)ip.getLut().clone(); } } - + + /** This function should be called from PlugInFilters that modify the overlay prior to the operation. + * For the type 'FILTER', undo of overlays requires that the modified image also has an overlay. */ public static void saveOverlay(ImagePlus imp) { Overlay overlay = imp!=null?imp.getOverlay():null; - if (overlay!=null) + if (overlay!=null) { overlayCopy = overlay.duplicate(); - else + overlayImageID = imp.getID(); + } else overlayCopy = null; } @@ -114,7 +122,7 @@ public static void undo() { ImagePlus imp = WindowManager.getCurrentImage(); if (IJ.debugMode) IJ.log("Undo.undo: "+ whatToUndo+" "+imp+" "+impCopy); if (imp==null || imageID!=imp.getID()) { - if (imp!=null && !IJ.macroRunning()) { // does image still have an undo buffer? + if (imp!=null && !IJ.macroRunning()) { // does foreground image still have an undo buffer? ImageProcessor ip2 = imp.getProcessor(); ip2.swapPixelArrays(); imp.updateAndDraw(); @@ -124,7 +132,7 @@ public static void undo() { } switch (whatToUndo) { case FILTER: - undoOverlay(imp); + undoOverlay(imp, true); ImageProcessor ip = imp.getProcessor(); if (ip!=null) { if (!IJ.macroRunning()) { @@ -148,8 +156,8 @@ public static void undo() { return; } else imp.setProcessor(null, ipCopy); - if (whatToUndo==COMPOUND_FILTER_DONE) - undoOverlay(imp); + if (whatToUndo==COMPOUND_FILTER_DONE || whatToUndo==TYPE_CONVERSION) + undoOverlay(imp, true); } break; case TRANSFORM: @@ -184,18 +192,22 @@ public static void undo() { IJ.beep(); return; } - return; //don't reset + return; //don't reset; successive undo removes further rois + case OVERLAY: + undoOverlay(imp, false); + imp.draw(); + break; } reset(); } - - private static void undoOverlay(ImagePlus imp) { - if (overlayCopy!=null) { + + /** Reverts the overlay to the saved version. */ + private static void undoOverlay(ImagePlus imp, boolean onlyModifyOvly) { + if (overlayCopy!=null && imp.getID()==overlayImageID) { Overlay overlay = imp.getOverlay(); - if (overlay!=null) { - imp.setOverlay(overlayCopy); - overlayCopy = overlay.duplicate(); - } + imp.setOverlay(overlayCopy); + if (overlay != null) + overlayCopy = overlay.duplicate(); //swap } } diff --git a/ij/gui/RoiProperties.java b/ij/gui/RoiProperties.java index 9834c16a..4e968737 100644 --- a/ij/gui/RoiProperties.java +++ b/ij/gui/RoiProperties.java @@ -31,13 +31,15 @@ public class RoiProperties implements TextListener, WindowListener { private TextField groupField, colorField; private Label groupName; - /** Constructs a RoiProperties using the specified title for a given roi; + /** Constructs a RoiProperties using the specified title for a given image and roi; * call showDialog for the actual dialog. * Note that the title determines which fields will be shown in the dialog. */ - public RoiProperties(String title, Roi roi) { + public RoiProperties(String title, ImagePlus imp, Roi roi) { if (roi==null) throw new IllegalArgumentException("ROI is null"); this.title = title; + this.imp = imp; + this.roi = roi; showName = title.startsWith("Prop"); showListCoordinates = showName && title.endsWith(" "); nProperties = showListCoordinates?roi.getPropertyCount():0; @@ -48,12 +50,19 @@ public RoiProperties(String title, Roi roi) { overlay = imp!=null?imp.getOverlay():null; setPositions = roi.getPosition()!=0; } - this.roi = roi; + } + + /** Constructs a RoiProperties using the specified title for a given roi; + * call showDialog for the actual dialog. + * Note that the title determines which fields will be shown in the dialog. */ + + public RoiProperties(String title, Roi roi) { + this(title, WindowManager.getCurrentImage(), roi); } /** Displays the dialog box and returns 'false' if the user cancels it. */ public boolean showDialog() { - String name= roi.getName(); + String name = roi.getName(); boolean isRange = name!=null && name.startsWith("range:"); String nameLabel = isRange?"Range:":"Name:"; if (isRange) name = name.substring(7); @@ -99,7 +108,6 @@ else if (position.equals(""+PointRoi.POINTWISE_POSITION)) if (showName) { gd.addStringField(nameLabel, name, 20); String label = "Position:"; - ImagePlus imp = WindowManager.getCurrentImage(); if (position.contains(",") || (imp!=null&&imp.isHyperStack())) label = "Position (c,z,t):"; gd.addStringField(label, position, 20); @@ -140,6 +148,9 @@ else if (position.equals(""+PointRoi.POINTWISE_POSITION)) gd.addStringField("Fill color:", fillc); } } + boolean askShowOnAllSlices = addToOverlay && imp!=null && imp.getNSlices()>1; + if (askShowOnAllSlices) + gd.addCheckbox("Show on all Slices", roi.getPosition()==0&&!roi.hasHyperStackPosition()); if (addToOverlay) gd.addCheckbox("New overlay", false); if (overlayOptions) { @@ -211,8 +222,15 @@ else if (position.equals(""+PointRoi.POINTWISE_POSITION)) } else fillc = gd.getNextString(); } + if (askShowOnAllSlices) { + boolean overlayOnAllSlices = gd.getNextBoolean(); + if (overlayOnAllSlices) + roi.setPosition(0); + else if (roi.getPosition() == 0) + roi.setPosition(imp); + } boolean applyToOverlay = false; - boolean newOverlay = addToOverlay?gd.getNextBoolean():false; + boolean newOverlay = addToOverlay ? gd.getNextBoolean() : false; if (overlayOptions) { setPositions = gd.getNextBoolean(); if (overlay!=null) { @@ -267,12 +285,19 @@ else if (position.equals(""+PointRoi.POINTWISE_POSITION)) if (applyToOverlay) { if (imp==null || overlay==null) return true; + Undo.setup(Undo.OVERLAY, imp); Roi[] rois = overlay.toArray(); for (int i=0; i1; if (IJ.altKeyDown() || (IJ.macroRunning() && Macro.getOptions()!=null)) { RoiProperties rp = new RoiProperties("Add to Overlay", roi); if (!rp.showDialog()) return; + boolean hasPosition2 = roi.getPosition()!=0 || roi.hasHyperStackPosition(); defaultRoi.setStrokeColor(roi.getStrokeColor()); defaultRoi.setStrokeWidth(roi.getStrokeWidth()); defaultRoi.setFillColor(roi.getFillColor()); + if (hasPosition2 != hasPosition) + defaultRoi.setPosition(hasPosition2 ? 1 : 0); } String name = roi.getName(); boolean newOverlay = name!=null && name.equals("new-overlay"); @@ -289,6 +295,7 @@ void remove() { ImageCanvas ic = imp.getCanvas(); if (ic!=null) ic.setShowAllList(null); + Undo.setup(Undo.OVERLAY, imp); imp.setOverlay(null); } } @@ -403,7 +410,7 @@ void options() { boolean points = roi instanceof PointRoi && ((PolygonRoi)roi).getNCoordinates()>1; if (points) roi.setStrokeColor(Color.red); roi.setPosition(defaultRoi.getPosition()); - RoiProperties rp = new RoiProperties("Overlay Options", roi); + RoiProperties rp = new RoiProperties("Overlay Options", imp, roi); if (!rp.showDialog()) return; defaultRoi = roi; } diff --git a/ij/plugin/Resizer.java b/ij/plugin/Resizer.java index c9bd282c..a6617ecc 100644 --- a/ij/plugin/Resizer.java +++ b/ij/plugin/Resizer.java @@ -142,7 +142,10 @@ else if (z1>1 && z1Selection>Properties command. + * With the alt key down and a multipoint selection, shows the point counts instead. */ + boolean setProperties(String title, ImagePlus imp) { + if (imp == null) return false; //should never happen (called only from 'run', where imp!=null) + Roi roi = imp.getRoi(); if ((roi instanceof PointRoi) && Toolbar.getMultiPointMode() && IJ.altKeyDown()) { ((PointRoi)roi).displayCounts(); return true; @@ -820,7 +824,7 @@ boolean setProperties(String title, Roi roi) { Color color = roi.getStrokeColor(); Color fillColor = roi.getFillColor(); int width = (int)roi.getStrokeWidth(); - RoiProperties rp = new RoiProperties(title, roi); + RoiProperties rp = new RoiProperties(title, imp, roi); boolean ok = rp.showDialog(); if (Recorder.record) { boolean groupChanged = false; diff --git a/ij/plugin/frame/ContrastAdjuster.java b/ij/plugin/frame/ContrastAdjuster.java index 7f6efe94..80ba21e8 100644 --- a/ij/plugin/frame/ContrastAdjuster.java +++ b/ij/plugin/frame/ContrastAdjuster.java @@ -435,25 +435,24 @@ void updateLabels(ImagePlus imp) { max = cal.getCValue((int)max); realValue = true; } + digits = realValue?4:0; + if (realValue) { + double s = min<0||max<0?0.1:1.0; + double amin = Math.abs(min); + double amax = Math.abs(max); + if (amin>99.0*s||amax>99.0*s) digits = 3; + if (amin>999.0*s||amax>999.0*s) digits = 2; + if (amin>9999.0*s||amax>9999.0*s) digits = 1; + if (amin>99999.0*s||amax>99999.0*s) digits = 0; + if (amin>9999999.0*s||amax>9999999.0*s) digits = -2; + if ((amin>0&&amin<0.001)||(amax>0&&amax<0.001)) digits = -2; + } if (windowLevel) { - digits = realValue?3:0; double window = max-min; double level = min+(window)/2.0; windowLabel.setText(ResultsTable.d2s(window, digits)); levelLabel.setText(ResultsTable.d2s(level, digits)); } else { - digits = realValue?4:0; - if (realValue) { - double s = min<0||max<0?0.1:1.0; - double amin = Math.abs(min); - double amax = Math.abs(max); - if (amin>99.0*s||amax>99.0*s) digits = 3; - if (amin>999.0*s||amax>999.0*s) digits = 2; - if (amin>9999.0*s||amax>9999.0*s) digits = 1; - if (amin>99999.0*s||amax>99999.0*s) digits = 0; - if (amin>9999999.0*s||amax>9999999.0*s) digits = -2; - if ((amin>0&&amin<0.001)||(amax>0&&amax<0.001)) digits = -2; - } String minString = IJ.d2s(min, min==0.0?0:digits) + blankLabel8; minLabel.setText(minString.substring(0,blankLabel8.length())); String maxString = blankLabel8 + IJ.d2s(max, digits); @@ -868,7 +867,6 @@ void setMinAndMax(ImagePlus imp, ImageProcessor ip) { min = imp.getDisplayRangeMin(); max = imp.getDisplayRangeMax(); Calibration cal = imp.getCalibration(); - //int digits = (ip instanceof FloatProcessor)||cal.calibrated()?2:0; double minValue = cal.getCValue(min); double maxValue = cal.getCValue(max); int channels = imp.getNChannels(); @@ -1024,15 +1022,14 @@ void setWindowLevel(ImagePlus imp, ImageProcessor ip) { min = imp.getDisplayRangeMin(); max = imp.getDisplayRangeMax(); Calibration cal = imp.getCalibration(); - int digits = (ip instanceof FloatProcessor)||cal.calibrated()?2:0; double minValue = cal.getCValue(min); double maxValue = cal.getCValue(max); //IJ.log("setWindowLevel: "+min+" "+max); double windowValue = maxValue - minValue; double levelValue = minValue + windowValue/2.0; GenericDialog gd = new GenericDialog("Set W&L"); - gd.addNumericField("Window Center (Level): ", levelValue, digits); - gd.addNumericField("Window Width: ", windowValue, digits); + gd.addNumericField("Window Center (Level): ", levelValue, digits, 9, ""); + gd.addNumericField("Window Width: ", windowValue, digits, 9, ""); gd.addCheckbox("Propagate to all open images", false); gd.showDialog(); if (gd.wasCanceled()) diff --git a/release-notes.html b/release-notes.html index 3d1e39b3..8ec7a607 100644 --- a/release-notes.html +++ b/release-notes.html @@ -5,8 +5,17 @@ -
  • 1.54k16 4 September 2024 +
  • 1.54k19 6 September 2024
      +
    • Thanks to Michael Schmid, the dialog shown for Image>Overlay>Add Selection +with the ALT key down has an additional "Show on all slices" checkbox. +Using "Apply" in the Image>Overlay>Overlay Options dialog +can be also used to remove the stack positions for the current overlay +("Set stack positions" off, equivalent to "Show on all slices"). With +"Set stack positions" on, the stack position of the overlay elements is set +to the current slice if it was previously unspecified. Modifying an overlay +with "Apply" in Image>Overlay>Overlay Options and removing an +overlay are undoable operations (ctrl+z).
    • Thanks to Michael Schmid, the stack positions of multi-point ROIs can be set in in the Edit>Selection>Properties dialog or in the ROI Manager's Properties dialog. The ROI Manager's More>>Remove Positions command also works @@ -35,6 +44,16 @@ function to not work as expected.
    • Thanks to Jonathan Cottet, fixed bug with IJ.openImage(path,n) opening .ome.tif files. +
    • Thanks to Fred Demen and Michael Schmid, fixed bug +with the run("Size...",option) macro function when +the "width=" option was missing and the "constrain" +option was presendt. +
    • Thanks to Fred Demen, fixed bug with the +Image>Adjust>Window/Level "Set" dialog +using inadequate precision when displaying values. +
    • Thanks to Michael Schmid, fixed Undo exception +after using Image>Adjust>Size on +a stack.
    • Thanks to Felix Rudolphi and Curtis Rueden, fixed a 1.54i regression that caused FileOpener to throw HeadlessExceptions in headless environments.