Exemple #1
0
 public Point getPreferredLocation() {
   Rectangle maxBounds = GUI.getMaxWindowBounds();
   int ijX = Prefs.getInt(IJ_X, -99);
   int ijY = Prefs.getInt(IJ_Y, -99);
   if (ijX >= maxBounds.x && ijY >= maxBounds.y && 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);
 }
Exemple #2
0
 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);
 }
Exemple #3
0
 void configureProxy() {
   if (Prefs.useSystemProxies) {
     try {
       System.setProperty("java.net.useSystemProxies", "true");
     } catch (Exception e) {
     }
   } else {
     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;
     Properties props = System.getProperties();
     props.put("proxySet", "true");
     props.put("http.proxyHost", server);
     props.put("http.proxyPort", "" + port);
   }
   // new ProxySettings().logProperties();
 }
Exemple #4
0
 /** Quit using a separate thread, hopefully avoiding thread deadlocks. */
 public void run() {
   quitting = true;
   boolean changes = false;
   int[] wList = WindowManager.getIDList();
   if (wList != null) {
     for (int i = 0; i < wList.length; i++) {
       ImagePlus imp = WindowManager.getImage(wList[i]);
       if (imp != null && imp.changes == true) {
         changes = true;
         break;
       }
     }
   }
   Frame[] frames = WindowManager.getNonImageWindows();
   if (frames != null) {
     for (int i = 0; i < frames.length; i++) {
       if (frames[i] != null && (frames[i] instanceof Editor)) {
         if (((Editor) frames[i]).fileChanged()) {
           changes = true;
           break;
         }
       }
     }
   }
   if (windowClosed
       && !changes
       && Menus.window.getItemCount() > Menus.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();
   }
   IJ.cleanup();
   // setVisible(false);
   // IJ.log("dispose");
   dispose();
   if (exitWhenQuitting) System.exit(0);
 }
Exemple #5
0
 private void loadCursors() {
   Toolkit toolkit = Toolkit.getDefaultToolkit();
   String path = Prefs.getImageJDir() + "images/crosshair-cursor.gif";
   File f = new File(path);
   if (!f.exists()) return;
   // Image image = toolkit.getImage(path);
   ImageIcon icon = new ImageIcon(path);
   Image image = icon.getImage();
   if (image == null) return;
   int width = icon.getIconWidth();
   int height = icon.getIconHeight();
   Point hotSpot = new Point(width / 2, height / 2);
   Cursor crosshairCursor = toolkit.createCustomCursor(image, hotSpot, "crosshair-cursor.gif");
   ImageCanvas.setCursor(crosshairCursor, 0);
 }
Exemple #6
0
 void saveWindowLocations() {
   Window win = WindowManager.getWindow("B&C");
   if (win != null) Prefs.saveLocation(ContrastAdjuster.LOC_KEY, win.getLocation());
   win = WindowManager.getWindow("Threshold");
   if (win != null) Prefs.saveLocation(ThresholdAdjuster.LOC_KEY, win.getLocation());
   win = WindowManager.getWindow("Results");
   if (win != null) {
     Prefs.saveLocation(TextWindow.LOC_KEY, win.getLocation());
     Dimension d = win.getSize();
     Prefs.set(TextWindow.WIDTH_KEY, d.width);
     Prefs.set(TextWindow.HEIGHT_KEY, d.height);
   }
   win = WindowManager.getWindow("Log");
   if (win != null) {
     Prefs.saveLocation(TextWindow.LOG_LOC_KEY, win.getLocation());
     Dimension d = win.getSize();
     Prefs.set(TextWindow.LOG_WIDTH_KEY, d.width);
     Prefs.set(TextWindow.LOG_HEIGHT_KEY, d.height);
   }
   win = WindowManager.getWindow("ROI Manager");
   if (win != null) Prefs.saveLocation(RoiManager.LOC_KEY, win.getLocation());
 }
Exemple #7
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);
   }
   frame = WindowManager.getFrame("Log");
   if (frame != null) {
     Prefs.saveLocation(TextWindow.LOG_LOC_KEY, frame.getLocation());
     Dimension d = frame.getSize();
     Prefs.set(TextWindow.LOG_WIDTH_KEY, d.width);
     Prefs.set(TextWindow.LOG_HEIGHT_KEY, d.height);
   }
 }
Exemple #8
0
 public static void main(String args[]) {
   if (System.getProperty("java.version").substring(0, 3).compareTo("1.5") < 0) {
     javax.swing.JOptionPane.showMessageDialog(
         null, "ImageJ " + VERSION + " requires Java 1.5 or later.");
     System.exit(0);
   }
   boolean noGUI = false;
   int mode = STANDALONE;
   arguments = args;
   // System.setProperty("file.encoding", "UTF-8");
   int nArgs = args != null ? args.length : 0;
   boolean commandLine = false;
   for (int i = 0; i < nArgs; i++) {
     String arg = args[i];
     if (arg == null) continue;
     if (args[i].startsWith("-")) {
       if (args[i].startsWith("-batch")) noGUI = true;
       else if (args[i].startsWith("-debug")) IJ.setDebugMode(true);
       else if (args[i].startsWith("-ijpath") && i + 1 < nArgs) {
         if (IJ.debugMode) IJ.log("-ijpath: " + args[i + 1]);
         Prefs.setHomeDir(args[i + 1]);
         commandLine = true;
         args[i + 1] = null;
       } else if (args[i].startsWith("-port")) {
         int delta = (int) Tools.parseDouble(args[i].substring(5, args[i].length()), 0.0);
         commandLine = true;
         if (delta == 0) mode = EMBEDDED;
         else if (delta > 0 && DEFAULT_PORT + delta < 65536) port = DEFAULT_PORT + delta;
       }
     }
   }
   // If existing ImageJ instance, pass arguments to it and quit.
   boolean passArgs = mode == STANDALONE && !noGUI;
   if (IJ.isMacOSX() && !commandLine) passArgs = false;
   if (passArgs && 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; i < nArgs; i++) {
     String arg = args[i];
     if (arg == null) continue;
     if (arg.startsWith("-")) {
       if ((arg.startsWith("-macro") || arg.startsWith("-batch")) && i + 1 < nArgs) {
         String arg2 = i + 2 < nArgs ? args[i + 2] : null;
         Prefs.commandLineMacro = true;
         if (noGUI && args[i + 1].endsWith(".js")) Interpreter.batchMode = true;
         IJ.runMacroFile(args[i + 1], arg2);
         break;
       } else if (arg.startsWith("-eval") && i + 1 < nArgs) {
         String rtn = IJ.runMacro(args[i + 1]);
         if (rtn != null) System.out.print(rtn);
         args[i + 1] = null;
       } else if (arg.startsWith("-run") && i + 1 < nArgs) {
         IJ.run(args[i + 1]);
         args[i + 1] = null;
       }
     } else if (macros == 0 && (arg.endsWith(".ijm") || arg.endsWith(".txt"))) {
       IJ.runMacroFile(arg);
       macros++;
     } else if (arg.length() > 0 && arg.indexOf("ij.ImageJ") == -1) {
       File file = new File(arg);
       IJ.open(file.getAbsolutePath());
     }
   }
   if (IJ.debugMode && IJ.getInstance() == null) new JavaProperties().run("");
   if (noGUI) System.exit(0);
 }
Exemple #9
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 (non-standalone) version of ImageJ.
   */
  public ImageJ(java.applet.Applet applet, int mode) {
    super("ImageJ");
    if ((mode & DEBUG) != 0) IJ.setDebugMode(true);
    mode = mode & 255;
    if (IJ.debugMode) IJ.log("ImageJ starting in debug mode: " + mode);
    embedded = applet == null && (mode == EMBEDDED || mode == NO_SHOW);
    this.applet = applet;
    String err1 = Prefs.load(this, applet);
    setBackground(backgroundColor);
    Menus m = new Menus(this, applet);
    String err2 = m.addMenuBar();
    m.installPopupMenu(this);
    setLayout(new BorderLayout());

    // Tool bar
    toolbar = new Toolbar();
    toolbar.addKeyListener(this);
    add("Center", toolbar);

    // Status bar
    statusBar = new Panel();
    statusBar.setLayout(new BorderLayout());
    statusBar.setForeground(Color.black);
    statusBar.setBackground(backgroundColor);
    statusLine = new JLabel();
    statusLine.setFont(new Font("SansSerif", Font.PLAIN, 13));
    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);
    add("South", statusBar);

    IJ.init(this, applet);
    addKeyListener(this);
    addWindowListener(this);
    setFocusTraversalKeysEnabled(false);
    m.installStartupMacroSet(); // add custom tools
    runStartupMacro();

    Point loc = getPreferredLocation();
    Dimension tbSize = toolbar.getPreferredSize();
    setCursor(Cursor.getDefaultCursor()); // work-around for JDK 1.1.8 bug
    if (mode != NO_SHOW) {
      if (IJ.isWindows())
        try {
          setIcon();
        } catch (Exception e) {
        }
      setLocation(loc.x, loc.y);
      setResizable(!IJ.isMacOSX());
      pack();
      setVisible(true);
    }
    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", "");
    String str = m.getMacroCount() == 1 ? " macro" : " macros";
    IJ.showStatus(version() + m.getPluginCount() + " commands; " + m.getMacroCount() + str);
    configureProxy();
    if (applet == null) loadCursors();
  }
Exemple #10
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 (non-standalone) version of ImageJ.
   */
  public ImageJ(java.applet.Applet applet, int mode) {
    super("ImageJ");
    embedded = applet == null && (mode == EMBEDDED || mode == NO_SHOW);
    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 (mode != NO_SHOW) {
      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
      show();
    }
    if (err1 != null) IJ.error(err1);
    if (err2 != null) {
      IJ.error(err2);
      IJ.runPlugIn("ij.plugin.ClassChecker", "");
    }
    m.installStartupMacroSet();
    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", "");
    String str = m.getMacroCount() == 1 ? " macro" : " macros";
    IJ.showStatus(version() + m.getPluginCount() + " commands; " + m.getMacroCount() + str);
    // if (applet==null && !embedded && Prefs.runSocketListener)
    //	new SocketListener();
    configureProxy();
    if (applet == null) loadCursors();
  }
/** This is a Canvas used to display images in a Window. */
public class ImageCanvas extends Canvas implements MouseListener, MouseMotionListener, Cloneable {

  protected static Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
  protected static Cursor handCursor = new Cursor(Cursor.HAND_CURSOR);
  protected static Cursor moveCursor = new Cursor(Cursor.MOVE_CURSOR);
  protected static Cursor crosshairCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);

  public static boolean usePointer = Prefs.usePointerCursor;

  protected ImagePlus imp;
  protected boolean imageUpdated;
  protected Rectangle srcRect;
  protected int imageWidth, imageHeight;
  protected int xMouse; // current cursor offscreen x location
  protected int yMouse; // current cursor offscreen y location

  private boolean showCursorStatus = true;
  private int sx2, sy2;
  private boolean disablePopupMenu;
  private boolean showAllROIs;
  private static Color zoomIndicatorColor;
  private static Font smallFont, largeFont;
  private Font font;
  private Rectangle[] labelRects;
  private boolean maxBoundsReset;
  private Overlay overlay, showAllList;
  private static final int LIST_OFFSET = 100000;
  private static Color showAllColor = Prefs.getColor(Prefs.SHOW_ALL_COLOR, new Color(0, 255, 255));
  private Color defaultColor = showAllColor;
  private static Color labelColor, bgColor;
  private int resetMaxBoundsCount;
  private Roi currentRoi;

  protected ImageJ ij;
  protected double magnification;
  protected int dstWidth, dstHeight;

  protected int xMouseStart;
  protected int yMouseStart;
  protected int xSrcStart;
  protected int ySrcStart;
  protected int flags;

  private Image offScreenImage;
  private int offScreenWidth = 0;
  private int offScreenHeight = 0;
  private boolean mouseExited = true;
  private boolean customRoi;
  private boolean drawNames;

  public ImageCanvas(ImagePlus imp) {
    this.imp = imp;
    ij = IJ.getInstance();
    int width = imp.getWidth();
    int height = imp.getHeight();
    imageWidth = width;
    imageHeight = height;
    srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
    setDrawingSize(imageWidth, (int) (imageHeight));
    magnification = 1.0;
    addMouseListener(this);
    addMouseMotionListener(this);
    addKeyListener(ij); // ImageJ handles keyboard shortcuts
    setFocusTraversalKeysEnabled(false);
  }

  void updateImage(ImagePlus imp) {
    this.imp = imp;
    int width = imp.getWidth();
    int height = imp.getHeight();
    imageWidth = width;
    imageHeight = height;
    srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
    setDrawingSize(imageWidth, (int) imageHeight);
    magnification = 1.0;
  }

  /** Update this ImageCanvas to have the same zoom and scale settings as the one specified. */
  void update(ImageCanvas ic) {
    if (ic == null || ic == this || ic.imp == null) return;
    if (ic.imp.getWidth() != imageWidth || ic.imp.getHeight() != imageHeight) return;
    srcRect = new Rectangle(ic.srcRect.x, ic.srcRect.y, ic.srcRect.width, ic.srcRect.height);
    setMagnification(ic.magnification);
    setDrawingSize(ic.dstWidth, ic.dstHeight);
  }

  public void setSourceRect(Rectangle r) {
    srcRect = r;
  }

  void setSrcRect(Rectangle srcRect) {
    this.srcRect = srcRect;
  }

  public Rectangle getSrcRect() {
    return srcRect;
  }

  public void setDrawingSize(int width, int height) {
    dstWidth = width;
    dstHeight = height;
    setSize(dstWidth, dstHeight);
  }

  /**
   * ImagePlus.updateAndDraw calls this method to force the paint() method to update the image from
   * the ImageProcessor.
   */
  public void setImageUpdated() {
    imageUpdated = true;
  }

  public void update(Graphics g) {
    paint(g);
  }

  public void paint(Graphics g) {
    Roi roi = imp.getRoi();
    if (roi != null || showAllROIs || overlay != null) {
      if (roi != null) roi.updatePaste();
      if (!IJ.isMacOSX() && imageWidth != 0) {
        paintDoubleBuffered(g);
        return;
      }
    }
    try {
      if (imageUpdated) {
        imageUpdated = false;
        imp.updateImage();
      }
      Java2.setBilinearInterpolation(g, Prefs.interpolateScaledImages);
      Image img = imp.getImage();
      if (img != null)
        g.drawImage(
            img,
            0,
            0,
            (int) (srcRect.width * magnification),
            (int) (srcRect.height * magnification),
            srcRect.x,
            srcRect.y,
            srcRect.x + srcRect.width,
            srcRect.y + srcRect.height,
            null);
      if (overlay != null) drawOverlay(g);
      if (showAllROIs) drawAllROIs(g);
      if (roi != null) drawRoi(roi, g);
      if (srcRect.width < imageWidth || srcRect.height < imageHeight) drawZoomIndicator(g);
      if (IJ.debugMode) showFrameRate(g);
    } catch (OutOfMemoryError e) {
      IJ.outOfMemory("Paint");
    }
  }

  private void drawRoi(Roi roi, Graphics g) {
    if (roi == currentRoi) {
      Color lineColor = roi.getStrokeColor();
      Color fillColor = roi.getFillColor();
      float lineWidth = roi.getStrokeWidth();
      roi.setStrokeColor(null);
      roi.setFillColor(null);
      boolean strokeSet = roi.getStroke() != null;
      if (strokeSet) roi.setStrokeWidth(1);
      roi.draw(g);
      roi.setStrokeColor(lineColor);
      if (strokeSet) roi.setStrokeWidth(lineWidth);
      roi.setFillColor(fillColor);
      currentRoi = null;
    } else roi.draw(g);
  }

  void drawAllROIs(Graphics g) {
    RoiManager rm = RoiManager.getInstance();
    if (rm == null) {
      rm = Interpreter.getBatchModeRoiManager();
      if (rm != null && rm.getList().getItemCount() == 0) rm = null;
    }
    if (rm == null) {
      // if (showAllList!=null)
      //	overlay = showAllList;
      showAllROIs = false;
      repaint();
      return;
    }
    initGraphics(g, null, showAllColor);
    Hashtable rois = rm.getROIs();
    java.awt.List list = rm.getList();
    boolean drawLabels = rm.getDrawLabels();
    currentRoi = null;
    int n = list.getItemCount();
    if (IJ.debugMode) IJ.log("paint: drawing " + n + " \"Show All\" ROIs");
    if (labelRects == null || labelRects.length != n) labelRects = new Rectangle[n];
    if (!drawLabels) showAllList = new Overlay();
    else showAllList = null;
    if (imp == null) return;
    int currentImage = imp.getCurrentSlice();
    int channel = 0, slice = 0, frame = 0;
    boolean hyperstack = imp.isHyperStack();
    if (hyperstack) {
      channel = imp.getChannel();
      slice = imp.getSlice();
      frame = imp.getFrame();
    }
    drawNames = Prefs.useNamesAsLabels;
    for (int i = 0; i < n; i++) {
      String label = list.getItem(i);
      Roi roi = (Roi) rois.get(label);
      if (roi == null) continue;
      if (showAllList != null) showAllList.add(roi);
      if (i < 200 && drawLabels && roi == imp.getRoi()) currentRoi = roi;
      if (Prefs.showAllSliceOnly && imp.getStackSize() > 1) {
        if (hyperstack && roi.getPosition() == 0) {
          int c = roi.getCPosition();
          int z = roi.getZPosition();
          int t = roi.getTPosition();
          if ((c == 0 || c == channel) && (z == 0 || z == slice) && (t == 0 || t == frame))
            drawRoi(g, roi, drawLabels ? i : -1);
        } else {
          int position = roi.getPosition();
          if (position == 0) position = getSliceNumber(roi.getName());
          if (position == 0 || position == currentImage) drawRoi(g, roi, drawLabels ? i : -1);
        }
      } else drawRoi(g, roi, drawLabels ? i : -1);
    }
    ((Graphics2D) g).setStroke(Roi.onePixelWide);
    drawNames = false;
  }

  public int getSliceNumber(String label) {
    int slice = 0;
    if (label.length() >= 14 && label.charAt(4) == '-' && label.charAt(9) == '-')
      slice = (int) Tools.parseDouble(label.substring(0, 4), -1);
    else if (label.length() >= 17 && label.charAt(5) == '-' && label.charAt(11) == '-')
      slice = (int) Tools.parseDouble(label.substring(0, 5), -1);
    else if (label.length() >= 20 && label.charAt(6) == '-' && label.charAt(13) == '-')
      slice = (int) Tools.parseDouble(label.substring(0, 6), -1);
    return slice;
  }

  void drawOverlay(Graphics g) {
    if (imp != null && imp.getHideOverlay()) return;
    Color labelColor = overlay.getLabelColor();
    if (labelColor == null) labelColor = Color.white;
    initGraphics(g, labelColor, Roi.getColor());
    int n = overlay.size();
    if (IJ.debugMode) IJ.log("paint: drawing " + n + " ROI display list");
    int currentImage = imp != null ? imp.getCurrentSlice() : -1;
    if (imp.getStackSize() == 1) currentImage = -1;
    int channel = 0, slice = 0, frame = 0;
    boolean hyperstack = imp.isHyperStack();
    if (hyperstack) {
      channel = imp.getChannel();
      slice = imp.getSlice();
      frame = imp.getFrame();
    }
    drawNames = overlay.getDrawNames();
    boolean drawLabels = drawNames || overlay.getDrawLabels();
    for (int i = 0; i < n; i++) {
      if (overlay == null) break;
      Roi roi = overlay.get(i);
      if (hyperstack && roi.getPosition() == 0) {
        int c = roi.getCPosition();
        int z = roi.getZPosition();
        int t = roi.getTPosition();
        if ((c == 0 || c == channel) && (z == 0 || z == slice) && (t == 0 || t == frame))
          drawRoi(g, roi, drawLabels ? i + LIST_OFFSET : -1);
      } else {
        int position = roi.getPosition();
        if (position == 0 || position == currentImage)
          drawRoi(g, roi, drawLabels ? i + LIST_OFFSET : -1);
      }
    }
    ((Graphics2D) g).setStroke(Roi.onePixelWide);
    drawNames = false;
  }

  void initGraphics(Graphics g, Color textColor, Color defaultColor) {
    if (smallFont == null) {
      smallFont = new Font("SansSerif", Font.PLAIN, 9);
      largeFont = new Font("SansSerif", Font.PLAIN, 12);
    }
    if (textColor != null) {
      labelColor = textColor;
      if (overlay != null && overlay.getDrawBackgrounds())
        bgColor =
            new Color(
                255 - labelColor.getRed(), 255 - labelColor.getGreen(), 255 - labelColor.getBlue());
      else bgColor = null;
    } else {
      int red = defaultColor.getRed();
      int green = defaultColor.getGreen();
      int blue = defaultColor.getBlue();
      if ((red + green + blue) / 3 < 128) labelColor = Color.white;
      else labelColor = Color.black;
      bgColor = defaultColor;
    }
    this.defaultColor = defaultColor;
    g.setColor(defaultColor);
  }

  void drawRoi(Graphics g, Roi roi, int index) {
    int type = roi.getType();
    ImagePlus imp2 = roi.getImage();
    roi.setImage(imp);
    Color saveColor = roi.getStrokeColor();
    if (saveColor == null) roi.setStrokeColor(defaultColor);
    if (roi instanceof TextRoi) ((TextRoi) roi).drawText(g);
    else roi.drawOverlay(g);
    roi.setStrokeColor(saveColor);
    if (index >= 0) {
      if (roi == currentRoi) g.setColor(Roi.getColor());
      else g.setColor(defaultColor);
      drawRoiLabel(g, index, roi);
    }
    if (imp2 != null) roi.setImage(imp2);
    else roi.setImage(null);
  }

  void drawRoiLabel(Graphics g, int index, Roi roi) {
    Rectangle r = roi.getBounds();
    int x = screenX(r.x);
    int y = screenY(r.y);
    double mag = getMagnification();
    int width = (int) (r.width * mag);
    int height = (int) (r.height * mag);
    int size = width > 40 && height > 40 ? 12 : 9;
    if (font != null) {
      g.setFont(font);
      size = font.getSize();
    } else if (size == 12) g.setFont(largeFont);
    else g.setFont(smallFont);
    boolean drawingList = index >= LIST_OFFSET;
    if (drawingList) index -= LIST_OFFSET;
    String label = "" + (index + 1);
    if (drawNames && roi.getName() != null) label = roi.getName();
    FontMetrics metrics = g.getFontMetrics();
    int w = metrics.stringWidth(label);
    x = x + width / 2 - w / 2;
    y = y + height / 2 + Math.max(size / 2, 6);
    int h = metrics.getAscent() + metrics.getDescent();
    if (bgColor != null) {
      g.setColor(bgColor);
      g.fillRoundRect(x - 1, y - h + 2, w + 1, h - 3, 5, 5);
    }
    if (!drawingList && labelRects != null && index < labelRects.length)
      labelRects[index] = new Rectangle(x - 1, y - h + 2, w + 1, h);
    g.setColor(labelColor);
    g.drawString(label, x, y - 2);
    g.setColor(defaultColor);
  }

  void drawZoomIndicator(Graphics g) {
    int x1 = 10;
    int y1 = 10;
    double aspectRatio = (double) imageHeight / imageWidth;
    int w1 = 64;
    if (aspectRatio > 1.0) w1 = (int) (w1 / aspectRatio);
    int h1 = (int) (w1 * aspectRatio);
    if (w1 < 4) w1 = 4;
    if (h1 < 4) h1 = 4;
    int w2 = (int) (w1 * ((double) srcRect.width / imageWidth));
    int h2 = (int) (h1 * ((double) srcRect.height / imageHeight));
    if (w2 < 1) w2 = 1;
    if (h2 < 1) h2 = 1;
    int x2 = (int) (w1 * ((double) srcRect.x / imageWidth));
    int y2 = (int) (h1 * ((double) srcRect.y / imageHeight));
    if (zoomIndicatorColor == null) zoomIndicatorColor = new Color(128, 128, 255);
    g.setColor(zoomIndicatorColor);
    ((Graphics2D) g).setStroke(Roi.onePixelWide);
    g.drawRect(x1, y1, w1, h1);
    if (w2 * h2 <= 200 || w2 < 10 || h2 < 10) g.fillRect(x1 + x2, y1 + y2, w2, h2);
    else g.drawRect(x1 + x2, y1 + y2, w2, h2);
  }

  // Use double buffer to reduce flicker when drawing complex ROIs.
  // Author: Erik Meijering
  void paintDoubleBuffered(Graphics g) {
    final int srcRectWidthMag = (int) (srcRect.width * magnification);
    final int srcRectHeightMag = (int) (srcRect.height * magnification);
    if (offScreenImage == null
        || offScreenWidth != srcRectWidthMag
        || offScreenHeight != srcRectHeightMag) {
      offScreenImage = createImage(srcRectWidthMag, srcRectHeightMag);
      offScreenWidth = srcRectWidthMag;
      offScreenHeight = srcRectHeightMag;
    }
    Roi roi = imp.getRoi();
    try {
      if (imageUpdated) {
        imageUpdated = false;
        imp.updateImage();
      }
      Graphics offScreenGraphics = offScreenImage.getGraphics();
      Java2.setBilinearInterpolation(offScreenGraphics, Prefs.interpolateScaledImages);
      Image img = imp.getImage();
      if (img != null)
        offScreenGraphics.drawImage(
            img,
            0,
            0,
            srcRectWidthMag,
            srcRectHeightMag,
            srcRect.x,
            srcRect.y,
            srcRect.x + srcRect.width,
            srcRect.y + srcRect.height,
            null);
      if (overlay != null) drawOverlay(offScreenGraphics);
      if (showAllROIs) drawAllROIs(offScreenGraphics);
      if (roi != null) drawRoi(roi, offScreenGraphics);
      if (srcRect.width < imageWidth || srcRect.height < imageHeight)
        drawZoomIndicator(offScreenGraphics);
      if (IJ.debugMode) showFrameRate(offScreenGraphics);
      g.drawImage(offScreenImage, 0, 0, null);
    } catch (OutOfMemoryError e) {
      IJ.outOfMemory("Paint");
    }
  }

  public void resetDoubleBuffer() {
    offScreenImage = null;
  }

  long firstFrame;
  int frames, fps;

  void showFrameRate(Graphics g) {
    frames++;
    if (System.currentTimeMillis() > firstFrame + 1000) {
      firstFrame = System.currentTimeMillis();
      fps = frames;
      frames = 0;
    }
    g.setColor(Color.white);
    g.fillRect(10, 12, 50, 15);
    g.setColor(Color.black);
    g.drawString((int) (fps + 0.5) + " fps", 10, 25);
  }

  public Dimension getPreferredSize() {
    return new Dimension(dstWidth, dstHeight);
  }

  int count;

  /*
    public Graphics getGraphics() {
     	Graphics g = super.getGraphics();
  IJ.write("getGraphics: "+count++);
  if (IJ.altKeyDown())
  	throw new IllegalArgumentException("");
    	return g;
    }
    */

  /** Returns the current cursor location in image coordinates. */
  public Point getCursorLoc() {
    return new Point(xMouse, yMouse);
  }

  /** Returns 'true' if the cursor is over this image. */
  public boolean cursorOverImage() {
    return !mouseExited;
  }

  /** Returns the mouse event modifiers. */
  public int getModifiers() {
    return flags;
  }

  /** Returns the ImagePlus object that is associated with this ImageCanvas. */
  public ImagePlus getImage() {
    return imp;
  }

  /** Sets the cursor based on the current tool and cursor location. */
  public void setCursor(int sx, int sy, int ox, int oy) {
    xMouse = ox;
    yMouse = oy;
    mouseExited = false;
    Roi roi = imp.getRoi();
    ImageWindow win = imp.getWindow();
    if (win == null) return;
    if (IJ.spaceBarDown()) {
      setCursor(handCursor);
      return;
    }
    int id = Toolbar.getToolId();
    switch (Toolbar.getToolId()) {
      case Toolbar.MAGNIFIER:
        setCursor(moveCursor);
        break;
      case Toolbar.HAND:
        setCursor(handCursor);
        break;
      default: // selection tool
        if (id == Toolbar.SPARE1 || id >= Toolbar.SPARE2) {
          if (Prefs.usePointerCursor) setCursor(defaultCursor);
          else setCursor(crosshairCursor);
        } else if (roi != null && roi.getState() != roi.CONSTRUCTING && roi.isHandle(sx, sy) >= 0)
          setCursor(handCursor);
        else if (Prefs.usePointerCursor
            || (roi != null && roi.getState() != roi.CONSTRUCTING && roi.contains(ox, oy)))
          setCursor(defaultCursor);
        else setCursor(crosshairCursor);
    }
  }

  /** Converts a screen x-coordinate to an offscreen x-coordinate. */
  public int offScreenX(int sx) {
    return srcRect.x + (int) (sx / magnification);
  }

  /** Converts a screen y-coordinate to an offscreen y-coordinate. */
  public int offScreenY(int sy) {
    return srcRect.y + (int) (sy / magnification);
  }

  /** Converts a screen x-coordinate to a floating-point offscreen x-coordinate. */
  public double offScreenXD(int sx) {
    return srcRect.x + sx / magnification;
  }

  /** Converts a screen y-coordinate to a floating-point offscreen y-coordinate. */
  public double offScreenYD(int sy) {
    return srcRect.y + sy / magnification;
  }

  /** Converts an offscreen x-coordinate to a screen x-coordinate. */
  public int screenX(int ox) {
    return (int) ((ox - srcRect.x) * magnification);
  }

  /** Converts an offscreen y-coordinate to a screen y-coordinate. */
  public int screenY(int oy) {
    return (int) ((oy - srcRect.y) * magnification);
  }

  /** Converts a floating-point offscreen x-coordinate to a screen x-coordinate. */
  public int screenXD(double ox) {
    return (int) ((ox - srcRect.x) * magnification);
  }

  /** Converts a floating-point offscreen x-coordinate to a screen x-coordinate. */
  public int screenYD(double oy) {
    return (int) ((oy - srcRect.y) * magnification);
  }

  public double getMagnification() {
    return magnification;
  }

  public void setMagnification(double magnification) {
    setMagnification2(magnification);
  }

  void setMagnification2(double magnification) {
    if (magnification > 32.0) magnification = 32.0;
    if (magnification < 0.03125) magnification = 0.03125;
    this.magnification = magnification;
    imp.setTitle(imp.getTitle());
  }

  /** Enlarge the canvas if the user enlarges the window. */
  void resizeCanvas(int width, int height) {
    ImageWindow win = imp.getWindow();
    // IJ.log("resizeCanvas: "+srcRect+" "+imageWidth+"  "+imageHeight+" "+width+"  "+height+"
    // "+dstWidth+"  "+dstHeight+" "+win.maxBounds);
    if (!maxBoundsReset
        && (width > dstWidth || height > dstHeight)
        && win != null
        && win.maxBounds != null
        && width != win.maxBounds.width - 10) {
      if (resetMaxBoundsCount != 0)
        resetMaxBounds(); // Works around problem that prevented window from being larger than
                          // maximized size
      resetMaxBoundsCount++;
    }
    if (IJ.altKeyDown()) {
      fitToWindow();
      return;
    }
    if (srcRect.width < imageWidth || srcRect.height < imageHeight) {
      if (width > imageWidth * magnification) width = (int) (imageWidth * magnification);
      if (height > imageHeight * magnification) height = (int) (imageHeight * magnification);
      setDrawingSize(width, height);
      srcRect.width = (int) (dstWidth / magnification);
      srcRect.height = (int) (dstHeight / magnification);
      if ((srcRect.x + srcRect.width) > imageWidth) srcRect.x = imageWidth - srcRect.width;
      if ((srcRect.y + srcRect.height) > imageHeight) srcRect.y = imageHeight - srcRect.height;
      repaint();
    }
    // IJ.log("resizeCanvas2: "+srcRect+" "+dstWidth+"  "+dstHeight+" "+width+"  "+height);
  }

  public void fitToWindow() {
    ImageWindow win = imp.getWindow();
    if (win == null) return;
    Rectangle bounds = win.getBounds();
    Insets insets = win.getInsets();
    int sliderHeight = (win instanceof StackWindow) ? 20 : 0;
    double xmag = (double) (bounds.width - 10) / srcRect.width;
    double ymag = (double) (bounds.height - (10 + insets.top + sliderHeight)) / srcRect.height;
    setMagnification(Math.min(xmag, ymag));
    int width = (int) (imageWidth * magnification);
    int height = (int) (imageHeight * magnification);
    if (width == dstWidth && height == dstHeight) return;
    srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
    setDrawingSize(width, height);
    getParent().doLayout();
  }

  void setMaxBounds() {
    if (maxBoundsReset) {
      maxBoundsReset = false;
      ImageWindow win = imp.getWindow();
      if (win != null && !IJ.isLinux() && win.maxBounds != null) {
        win.setMaximizedBounds(win.maxBounds);
        win.setMaxBoundsTime = System.currentTimeMillis();
      }
    }
  }

  void resetMaxBounds() {
    ImageWindow win = imp.getWindow();
    if (win != null && (System.currentTimeMillis() - win.setMaxBoundsTime) > 500L) {
      win.setMaximizedBounds(win.maxWindowBounds);
      maxBoundsReset = true;
    }
  }

  private static final double[] zoomLevels = {
    1 / 72.0, 1 / 48.0, 1 / 32.0, 1 / 24.0, 1 / 16.0, 1 / 12.0, 1 / 8.0, 1 / 6.0, 1 / 4.0, 1 / 3.0,
    1 / 2.0, 0.75, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 12.0, 16.0, 24.0, 32.0
  };

  public static double getLowerZoomLevel(double currentMag) {
    double newMag = zoomLevels[0];
    for (int i = 0; i < zoomLevels.length; i++) {
      if (zoomLevels[i] < currentMag) newMag = zoomLevels[i];
      else break;
    }
    return newMag;
  }

  public static double getHigherZoomLevel(double currentMag) {
    double newMag = 32.0;
    for (int i = zoomLevels.length - 1; i >= 0; i--) {
      if (zoomLevels[i] > currentMag) newMag = zoomLevels[i];
      else break;
    }
    return newMag;
  }

  /**
   * Zooms in by making the window bigger. If it can't be made bigger, then make the source
   * rectangle (srcRect) smaller and center it at (sx,sy). Note that sx and sy are screen
   * coordinates.
   */
  public void zoomIn(int sx, int sy) {
    if (magnification >= 32) return;
    double newMag = getHigherZoomLevel(magnification);
    int newWidth = (int) (imageWidth * newMag);
    int newHeight = (int) (imageHeight * newMag);
    Dimension newSize = canEnlarge(newWidth, newHeight);
    if (newSize != null) {
      setDrawingSize(newSize.width, newSize.height);
      if (newSize.width != newWidth || newSize.height != newHeight)
        adjustSourceRect(newMag, sx, sy);
      else setMagnification(newMag);
      imp.getWindow().pack();
    } else adjustSourceRect(newMag, sx, sy);
    repaint();
    if (srcRect.width < imageWidth || srcRect.height < imageHeight) resetMaxBounds();
  }

  void adjustSourceRect(double newMag, int x, int y) {
    // IJ.log("adjustSourceRect1: "+newMag+" "+dstWidth+"  "+dstHeight);
    int w = (int) Math.round(dstWidth / newMag);
    if (w * newMag < dstWidth) w++;
    int h = (int) Math.round(dstHeight / newMag);
    if (h * newMag < dstHeight) h++;
    x = offScreenX(x);
    y = offScreenY(y);
    Rectangle r = new Rectangle(x - w / 2, y - h / 2, w, h);
    if (r.x < 0) r.x = 0;
    if (r.y < 0) r.y = 0;
    if (r.x + w > imageWidth) r.x = imageWidth - w;
    if (r.y + h > imageHeight) r.y = imageHeight - h;
    srcRect = r;
    setMagnification(newMag);
    // IJ.log("adjustSourceRect2: "+srcRect+" "+dstWidth+"  "+dstHeight);
  }

  protected Dimension canEnlarge(int newWidth, int newHeight) {
    // if ((flags&Event.CTRL_MASK)!=0 || IJ.controlKeyDown()) return null;
    ImageWindow win = imp.getWindow();
    if (win == null) return null;
    Rectangle r1 = win.getBounds();
    Insets insets = win.getInsets();
    Point loc = getLocation();
    if (loc.x > insets.left + 5 || loc.y > insets.top + 5) {
      r1.width = newWidth + insets.left + insets.right + 10;
      r1.height = newHeight + insets.top + insets.bottom + 10;
      if (win instanceof StackWindow) r1.height += 20;
    } else {
      r1.width = r1.width - dstWidth + newWidth + 10;
      r1.height = r1.height - dstHeight + newHeight + 10;
    }
    Rectangle max = win.getMaxWindow(r1.x, r1.y);
    boolean fitsHorizontally = r1.x + r1.width < max.x + max.width;
    boolean fitsVertically = r1.y + r1.height < max.y + max.height;
    if (fitsHorizontally && fitsVertically) return new Dimension(newWidth, newHeight);
    else if (fitsVertically && newHeight < dstWidth) return new Dimension(dstWidth, newHeight);
    else if (fitsHorizontally && newWidth < dstHeight) return new Dimension(newWidth, dstHeight);
    else return null;
  }

  /**
   * Zooms out by making the source rectangle (srcRect) larger and centering it on (x,y). If we
   * can't make it larger, then make the window smaller.
   */
  public void zoomOut(int x, int y) {
    if (magnification <= 0.03125) return;
    double oldMag = magnification;
    double newMag = getLowerZoomLevel(magnification);
    double srcRatio = (double) srcRect.width / srcRect.height;
    double imageRatio = (double) imageWidth / imageHeight;
    double initialMag = imp.getWindow().getInitialMagnification();
    if (Math.abs(srcRatio - imageRatio) > 0.05) {
      double scale = oldMag / newMag;
      int newSrcWidth = (int) Math.round(srcRect.width * scale);
      int newSrcHeight = (int) Math.round(srcRect.height * scale);
      if (newSrcWidth > imageWidth) newSrcWidth = imageWidth;
      if (newSrcHeight > imageHeight) newSrcHeight = imageHeight;
      int newSrcX = srcRect.x - (newSrcWidth - srcRect.width) / 2;
      int newSrcY = srcRect.y - (newSrcHeight - srcRect.height) / 2;
      if (newSrcX < 0) newSrcX = 0;
      if (newSrcY < 0) newSrcY = 0;
      srcRect = new Rectangle(newSrcX, newSrcY, newSrcWidth, newSrcHeight);
      // IJ.log(newMag+" "+srcRect+" "+dstWidth+" "+dstHeight);
      int newDstWidth = (int) (srcRect.width * newMag);
      int newDstHeight = (int) (srcRect.height * newMag);
      setMagnification(newMag);
      setMaxBounds();
      // IJ.log(newDstWidth+" "+dstWidth+" "+newDstHeight+" "+dstHeight);
      if (newDstWidth < dstWidth || newDstHeight < dstHeight) {
        // IJ.log("pack");
        setDrawingSize(newDstWidth, newDstHeight);
        imp.getWindow().pack();
      } else repaint();
      return;
    }
    if (imageWidth * newMag > dstWidth) {
      int w = (int) Math.round(dstWidth / newMag);
      if (w * newMag < dstWidth) w++;
      int h = (int) Math.round(dstHeight / newMag);
      if (h * newMag < dstHeight) h++;
      x = offScreenX(x);
      y = offScreenY(y);
      Rectangle r = new Rectangle(x - w / 2, y - h / 2, w, h);
      if (r.x < 0) r.x = 0;
      if (r.y < 0) r.y = 0;
      if (r.x + w > imageWidth) r.x = imageWidth - w;
      if (r.y + h > imageHeight) r.y = imageHeight - h;
      srcRect = r;
    } else {
      srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
      setDrawingSize((int) (imageWidth * newMag), (int) (imageHeight * newMag));
      // setDrawingSize(dstWidth/2, dstHeight/2);
      imp.getWindow().pack();
    }
    // IJ.write(newMag + " " + srcRect.x+" "+srcRect.y+" "+srcRect.width+" "+srcRect.height+"
    // "+dstWidth + " " + dstHeight);
    setMagnification(newMag);
    // IJ.write(srcRect.x + " " + srcRect.width + " " + dstWidth);
    setMaxBounds();
    repaint();
  }

  /** Implements the Image/Zoom/Original Scale command. */
  public void unzoom() {
    double imag = imp.getWindow().getInitialMagnification();
    if (magnification == imag) return;
    srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
    ImageWindow win = imp.getWindow();
    setDrawingSize((int) (imageWidth * imag), (int) (imageHeight * imag));
    setMagnification(imag);
    setMaxBounds();
    win.pack();
    setMaxBounds();
    repaint();
  }

  /** Implements the Image/Zoom/View 100% command. */
  public void zoom100Percent() {
    if (magnification == 1.0) return;
    double imag = imp.getWindow().getInitialMagnification();
    if (magnification != imag) unzoom();
    if (magnification == 1.0) return;
    if (magnification < 1.0) {
      while (magnification < 1.0) zoomIn(imageWidth / 2, imageHeight / 2);
    } else if (magnification > 1.0) {
      while (magnification > 1.0) zoomOut(imageWidth / 2, imageHeight / 2);
    } else return;
    int x = xMouse, y = yMouse;
    if (mouseExited) {
      x = imageWidth / 2;
      y = imageHeight / 2;
    }
    int sx = screenX(x);
    int sy = screenY(y);
    adjustSourceRect(1.0, sx, sy);
    repaint();
  }

  protected void scroll(int sx, int sy) {
    int ox = xSrcStart + (int) (sx / magnification); // convert to offscreen coordinates
    int oy = ySrcStart + (int) (sy / magnification);
    // IJ.log("scroll: "+ox+" "+oy+" "+xMouseStart+" "+yMouseStart);
    int newx = xSrcStart + (xMouseStart - ox);
    int newy = ySrcStart + (yMouseStart - oy);
    if (newx < 0) newx = 0;
    if (newy < 0) newy = 0;
    if ((newx + srcRect.width) > imageWidth) newx = imageWidth - srcRect.width;
    if ((newy + srcRect.height) > imageHeight) newy = imageHeight - srcRect.height;
    srcRect.x = newx;
    srcRect.y = newy;
    // IJ.log(sx+"  "+sy+"  "+newx+"  "+newy+"  "+srcRect);
    imp.draw();
    Thread.yield();
  }

  Color getColor(int index) {
    IndexColorModel cm = (IndexColorModel) imp.getProcessor().getColorModel();
    // IJ.write(""+index+" "+(new Color(cm.getRGB(index))));
    return new Color(cm.getRGB(index));
  }

  protected void setDrawingColor(int ox, int oy, boolean setBackground) {
    // IJ.log("setDrawingColor: "+setBackground+this);
    int type = imp.getType();
    int[] v = imp.getPixel(ox, oy);
    switch (type) {
      case ImagePlus.GRAY8:
        {
          if (setBackground) setBackgroundColor(getColor(v[0]));
          else setForegroundColor(getColor(v[0]));
          break;
        }
      case ImagePlus.GRAY16:
      case ImagePlus.GRAY32:
        {
          double min = imp.getProcessor().getMin();
          double max = imp.getProcessor().getMax();
          double value = (type == ImagePlus.GRAY32) ? Float.intBitsToFloat(v[0]) : v[0];
          int index = (int) (255.0 * ((value - min) / (max - min)));
          if (index < 0) index = 0;
          if (index > 255) index = 255;
          if (setBackground) setBackgroundColor(getColor(index));
          else setForegroundColor(getColor(index));
          break;
        }
      case ImagePlus.COLOR_RGB:
      case ImagePlus.COLOR_256:
        {
          Color c = new Color(v[0], v[1], v[2]);
          if (setBackground) setBackgroundColor(c);
          else setForegroundColor(c);
          break;
        }
    }
    Color c;
    if (setBackground) c = Toolbar.getBackgroundColor();
    else {
      c = Toolbar.getForegroundColor();
      imp.setColor(c);
    }
    IJ.showStatus("(" + c.getRed() + ", " + c.getGreen() + ", " + c.getBlue() + ")");
  }

  private void setForegroundColor(Color c) {
    Toolbar.setForegroundColor(c);
    if (Recorder.record)
      Recorder.record("setForegroundColor", c.getRed(), c.getGreen(), c.getBlue());
  }

  private void setBackgroundColor(Color c) {
    Toolbar.setBackgroundColor(c);
    if (Recorder.record)
      Recorder.record("setBackgroundColor", c.getRed(), c.getGreen(), c.getBlue());
  }

  public void mousePressed(MouseEvent e) {
    // if (ij==null) return;
    showCursorStatus = true;
    int toolID = Toolbar.getToolId();
    ImageWindow win = imp.getWindow();
    if (win != null && win.running2 && toolID != Toolbar.MAGNIFIER) {
      if (win instanceof StackWindow) ((StackWindow) win).setAnimate(false);
      else win.running2 = false;
      return;
    }

    int x = e.getX();
    int y = e.getY();
    flags = e.getModifiers();
    // IJ.log("Mouse pressed: " + e.isPopupTrigger() + "  " + ij.modifiers(flags));
    // if (toolID!=Toolbar.MAGNIFIER && e.isPopupTrigger()) {
    if (toolID != Toolbar.MAGNIFIER
        && (e.isPopupTrigger() || (!IJ.isMacintosh() && (flags & Event.META_MASK) != 0))) {
      handlePopupMenu(e);
      return;
    }

    int ox = offScreenX(x);
    int oy = offScreenY(y);
    xMouse = ox;
    yMouse = oy;
    if (IJ.spaceBarDown()) {
      // temporarily switch to "hand" tool of space bar down
      setupScroll(ox, oy);
      return;
    }
    if (showAllROIs) {
      Roi roi = imp.getRoi();
      if (!(roi != null && (roi.contains(ox, oy) || roi.isHandle(x, y) >= 0))
          && roiManagerSelect(x, y)) return;
    }
    if (customRoi && overlay != null) return;

    switch (toolID) {
      case Toolbar.MAGNIFIER:
        if (IJ.shiftKeyDown()) zoomToSelection(ox, oy);
        else if ((flags & (Event.ALT_MASK | Event.META_MASK | Event.CTRL_MASK)) != 0) {
          // IJ.run("Out");
          zoomOut(x, y);
          if (getMagnification() < 1.0) imp.repaintWindow();
        } else {
          // IJ.run("In");
          zoomIn(x, y);
          if (getMagnification() <= 1.0) imp.repaintWindow();
        }
        break;
      case Toolbar.HAND:
        setupScroll(ox, oy);
        break;
      case Toolbar.DROPPER:
        setDrawingColor(ox, oy, IJ.altKeyDown());
        break;
      case Toolbar.WAND:
        Roi roi = imp.getRoi();
        if (roi != null && roi.contains(ox, oy)) {
          Rectangle r = roi.getBounds();
          if (r.width == imageWidth && r.height == imageHeight) imp.killRoi();
          else if (!e.isAltDown()) {
            handleRoiMouseDown(e);
            return;
          }
        }
        if (roi != null) {
          int handle = roi.isHandle(x, y);
          if (handle >= 0) {
            roi.mouseDownInHandle(handle, x, y);
            return;
          }
        }
        setRoiModState(e, roi, -1);
        String mode = WandToolOptions.getMode();
        double tolerance = WandToolOptions.getTolerance();
        int npoints = IJ.doWand(ox, oy, tolerance, mode);
        if (Recorder.record && npoints > 0) {
          if (tolerance == 0.0 && mode.equals("Legacy")) Recorder.record("doWand", ox, oy);
          else
            Recorder.recordString(
                "doWand(" + ox + ", " + oy + ", " + tolerance + ", \"" + mode + "\");\n");
        }
        break;
      case Toolbar.OVAL:
        if (Toolbar.getBrushSize() > 0) new RoiBrush();
        else handleRoiMouseDown(e);
        break;
      case Toolbar.SPARE1:
      case Toolbar.SPARE2:
      case Toolbar.SPARE3:
      case Toolbar.SPARE4:
      case Toolbar.SPARE5:
      case Toolbar.SPARE6:
      case Toolbar.SPARE7:
      case Toolbar.SPARE8:
      case Toolbar.SPARE9:
        Toolbar.getInstance().runMacroTool(toolID);
        break;
      default: // selection tool
        handleRoiMouseDown(e);
    }
  }

  boolean roiManagerSelect(int x, int y) {
    RoiManager rm = RoiManager.getInstance();
    if (rm == null) return false;
    Hashtable rois = rm.getROIs();
    java.awt.List list = rm.getList();
    int n = list.getItemCount();
    if (labelRects == null || labelRects.length != n) return false;
    boolean stackMode = imp != null && imp.getStackSize() > 1 && Prefs.showAllSliceOnly;
    for (int i = 0; i < n; i++) {
      if (labelRects[i] != null && labelRects[i].contains(x, y)) {
        if (stackMode) {
          int slice = getSliceNumber(list.getItem(i));
          if (slice != imp.getCurrentSlice()) continue;
        }
        // rm.select(i);
        // this needs to run on a separate thread, at least on OS X
        // "update2" does not clone the ROI so the "Show All"
        // outline moves as the user moves the RO.
        new ij.macro.MacroRunner("roiManager('select', " + i + "); roiManager('update2');");
        return true;
      }
    }
    return false;
  }

  void zoomToSelection(int x, int y) {
    IJ.setKeyUp(IJ.ALL_KEYS);
    String macro =
        "args = split(getArgument);\n"
            + "x1=parseInt(args[0]); y1=parseInt(args[1]); flags=20;\n"
            + "while (flags&20!=0) {\n"
            + "getCursorLoc(x2, y2, z, flags);\n"
            + "if (x2>=x1) x=x1; else x=x2;\n"
            + "if (y2>=y1) y=y1; else y=y2;\n"
            + "makeRectangle(x, y, abs(x2-x1), abs(y2-y1));\n"
            + "wait(10);\n"
            + "}\n"
            + "run('To Selection');\n";
    new MacroRunner(macro, x + " " + y);
  }

  protected void setupScroll(int ox, int oy) {
    xMouseStart = ox;
    yMouseStart = oy;
    xSrcStart = srcRect.x;
    ySrcStart = srcRect.y;
  }

  protected void handlePopupMenu(MouseEvent e) {
    if (disablePopupMenu) return;
    if (IJ.debugMode) IJ.log("show popup: " + (e.isPopupTrigger() ? "true" : "false"));
    int x = e.getX();
    int y = e.getY();
    Roi roi = imp.getRoi();
    if (roi != null
        && (roi.getType() == Roi.POLYGON
            || roi.getType() == Roi.POLYLINE
            || roi.getType() == Roi.ANGLE)
        && roi.getState() == roi.CONSTRUCTING) {
      roi.handleMouseUp(x, y); // simulate double-click to finalize
      roi.handleMouseUp(x, y); // polygon or polyline selection
      return;
    }
    PopupMenu popup = Menus.getPopupMenu();
    if (popup != null) {
      add(popup);
      if (IJ.isMacOSX()) IJ.wait(10);
      popup.show(this, x, y);
    }
  }

  public void mouseExited(MouseEvent e) {
    // autoScroll(e);
    ImageWindow win = imp.getWindow();
    if (win != null) setCursor(defaultCursor);
    IJ.showStatus("");
    mouseExited = true;
  }

  /*
  public void autoScroll(MouseEvent e) {
  	Roi roi = imp.getRoi();
  	if (roi==null || roi.getState()!=roi.CONSTRUCTING || srcRect.width>=imageWidth || srcRect.height>=imageHeight
  	|| !(roi.getType()==Roi.POLYGON || roi.getType()==Roi.POLYLINE || roi.getType()==Roi.ANGLE))
  		return;
  	int sx = e.getX();
  	int sy = e.getY();
  	xMouseStart = srcRect.x+srcRect.width/2;
  	yMouseStart = srcRect.y+srcRect.height/2;
  	Rectangle r = roi.getBounds();
  	Dimension size = getSize();
  	int deltax=0, deltay=0;
  	if (sx<0)
  		deltax = srcRect.width/4;
  	else if (sx>size.width)
  		deltax = -srcRect.width/4;
  	if (sy<0)
  		deltay = srcRect.height/4;
  	else if (sy>size.height)
  		deltay = -srcRect.height/4;
  	//IJ.log("autoscroll: "+sx+" "+sy+" "+deltax+" "+deltay+" "+r);
  	scroll(screenX(xMouseStart+deltax), screenY(yMouseStart+deltay));
  }
  */

  public void mouseDragged(MouseEvent e) {
    int x = e.getX();
    int y = e.getY();
    xMouse = offScreenX(x);
    yMouse = offScreenY(y);
    flags = e.getModifiers();
    // IJ.log("mouseDragged: "+flags);
    if (flags == 0) // workaround for Mac OS 9 bug
    flags = InputEvent.BUTTON1_MASK;
    if (Toolbar.getToolId() == Toolbar.HAND || IJ.spaceBarDown()) scroll(x, y);
    else {
      IJ.setInputEvent(e);
      Roi roi = imp.getRoi();
      if (roi != null) roi.handleMouseDrag(x, y, flags);
    }
  }

  protected void handleRoiMouseDown(MouseEvent e) {
    int sx = e.getX();
    int sy = e.getY();
    int ox = offScreenX(sx);
    int oy = offScreenY(sy);
    Roi roi = imp.getRoi();
    int handle = roi != null ? roi.isHandle(sx, sy) : -1;
    boolean multiPointMode =
        roi != null
            && (roi instanceof PointRoi)
            && handle == -1
            && Toolbar.getToolId() == Toolbar.POINT
            && Toolbar.getMultiPointMode();
    if (multiPointMode) {
      imp.setRoi(((PointRoi) roi).addPoint(ox, oy));
      return;
    }
    setRoiModState(e, roi, handle);
    if (roi != null) {
      if (handle >= 0) {
        roi.mouseDownInHandle(handle, sx, sy);
        return;
      }
      Rectangle r = roi.getBounds();
      int type = roi.getType();
      if (type == Roi.RECTANGLE
          && r.width == imp.getWidth()
          && r.height == imp.getHeight()
          && roi.getPasteMode() == Roi.NOT_PASTING
          && !(roi instanceof ImageRoi)) {
        imp.killRoi();
        return;
      }
      if (roi.contains(ox, oy)) {
        if (roi.modState == Roi.NO_MODS) roi.handleMouseDown(sx, sy);
        else {
          imp.killRoi();
          imp.createNewRoi(sx, sy);
        }
        return;
      }
      if ((type == Roi.POLYGON || type == Roi.POLYLINE || type == Roi.ANGLE)
          && roi.getState() == roi.CONSTRUCTING) return;
      int tool = Toolbar.getToolId();
      if ((tool == Toolbar.POLYGON || tool == Toolbar.POLYLINE || tool == Toolbar.ANGLE)
          && !(IJ.shiftKeyDown() || IJ.altKeyDown())) {
        imp.killRoi();
        return;
      }
    }
    imp.createNewRoi(sx, sy);
  }

  void setRoiModState(MouseEvent e, Roi roi, int handle) {
    if (roi == null || (handle >= 0 && roi.modState == Roi.NO_MODS)) return;
    if (roi.state == Roi.CONSTRUCTING) return;
    int tool = Toolbar.getToolId();
    if (tool > Toolbar.FREEROI && tool != Toolbar.WAND && tool != Toolbar.POINT) {
      roi.modState = Roi.NO_MODS;
      return;
    }
    if (e.isShiftDown()) roi.modState = Roi.ADD_TO_ROI;
    else if (e.isAltDown()) roi.modState = Roi.SUBTRACT_FROM_ROI;
    else roi.modState = Roi.NO_MODS;
    // IJ.log("setRoiModState: "+roi.modState+" "+ roi.state);
  }

  /** Disable/enable popup menu. */
  public void disablePopupMenu(boolean status) {
    disablePopupMenu = status;
  }

  /** Enables/disables the ROI Manager "Show All" mode. */
  public void setShowAllROIs(boolean showAllROIs) {
    this.showAllROIs = showAllROIs;
  }

  /** Returns the state of the ROI Manager "Show All" flag. */
  public boolean getShowAllROIs() {
    return showAllROIs;
  }

  /** Return the ROI Manager "Show All" list as an overlay. */
  public Overlay getShowAllList() {
    if (!showAllROIs) return null;
    if (showAllList != null) return showAllList;
    RoiManager rm = RoiManager.getInstance();
    if (rm == null) return null;
    Roi[] rois = rm.getRoisAsArray();
    if (rois.length == 0) return null;
    Overlay overlay = new Overlay();
    for (int i = 0; i < rois.length; i++) overlay.add((Roi) rois[i].clone());
    return overlay;
  }

  /** Returns the color used for "Show All" mode. */
  public static Color getShowAllColor() {
    if (showAllColor != null && showAllColor.getRGB() == 0xff80ffff) showAllColor = Color.cyan;
    return showAllColor;
  }

  /** Sets the color used used for the ROI Manager "Show All" mode. */
  public static void setShowAllColor(Color c) {
    if (c == null) return;
    showAllColor = c;
    labelColor = null;
    ImagePlus img = WindowManager.getCurrentImage();
    if (img != null) {
      ImageCanvas ic = img.getCanvas();
      if (ic != null && ic.getShowAllROIs()) img.draw();
    }
  }

  /** Use ImagePlus.setOverlay(ij.gui.Overlay). */
  public void setOverlay(Overlay overlay) {
    this.overlay = overlay;
    if (overlay != null) font = overlay.getLabelsFont();
    repaint();
  }

  /** Use ImagePlus.getOverlay(). */
  public Overlay getOverlay() {
    return overlay;
  }

  /** @deprecated replaced by ImagePlus.setOverlay(ij.gui.Overlay) */
  public void setDisplayList(Vector list) {
    if (list != null) {
      Overlay list2 = new Overlay();
      list2.setVector(list);
      setOverlay(list2);
    } else setOverlay(null);
    if (overlay != null)
      overlay.drawLabels(overlay.size() > 0 && overlay.get(0).getStrokeColor() == null);
    else customRoi = false;
    repaint();
  }

  /** @deprecated replaced by ImagePlus.setOverlay(Shape, Color, BasicStroke) */
  public void setDisplayList(Shape shape, Color color, BasicStroke stroke) {
    if (shape == null) {
      setOverlay(null);
      return;
    }
    Roi roi = new ShapeRoi(shape);
    roi.setStrokeColor(color);
    roi.setStroke(stroke);
    Overlay list = new Overlay();
    list.add(roi);
    setOverlay(list);
  }

  /** @deprecated replaced by ImagePlus.setOverlay(Roi, Color, int, Color) */
  public void setDisplayList(Roi roi, Color color) {
    roi.setStrokeColor(color);
    Overlay list = new Overlay();
    list.add(roi);
    setOverlay(list);
  }

  /** @deprecated replaced by ImagePlus.getOverlay() */
  public Vector getDisplayList() {
    if (overlay == null) return null;
    Vector displayList = new Vector();
    for (int i = 0; i < overlay.size(); i++) displayList.add(overlay.get(i));
    return displayList;
  }

  /** Allows plugins (e.g., Orthogonal_Views) to create a custom ROI using a display list. */
  public void setCustomRoi(boolean customRoi) {
    this.customRoi = customRoi;
  }

  public boolean getCustomRoi() {
    return customRoi;
  }

  /**
   * Called by IJ.showStatus() to prevent status bar text from being overwritten until the cursor
   * moves at least 12 pixels.
   */
  public void setShowCursorStatus(boolean status) {
    showCursorStatus = status;
    if (status == true) sx2 = sy2 = -1000;
    else {
      sx2 = screenX(xMouse);
      sy2 = screenY(yMouse);
    }
  }

  public void mouseReleased(MouseEvent e) {
    flags = e.getModifiers();
    flags &= ~InputEvent.BUTTON1_MASK; // make sure button 1 bit is not set
    flags &= ~InputEvent.BUTTON2_MASK; // make sure button 2 bit is not set
    flags &= ~InputEvent.BUTTON3_MASK; // make sure button 3 bit is not set
    Roi roi = imp.getRoi();
    if (roi != null) {
      Rectangle r = roi.getBounds();
      int type = roi.getType();
      if ((r.width == 0 || r.height == 0)
          && !(type == Roi.POLYGON || type == Roi.POLYLINE || type == Roi.ANGLE || type == Roi.LINE)
          && !(roi instanceof TextRoi)
          && roi.getState() == roi.CONSTRUCTING
          && type != roi.POINT) imp.killRoi();
      else {
        roi.handleMouseUp(e.getX(), e.getY());
        if (roi.getType() == Roi.LINE && roi.getLength() == 0.0) imp.killRoi();
      }
    }
  }

  public void mouseMoved(MouseEvent e) {
    // if (ij==null) return;
    int sx = e.getX();
    int sy = e.getY();
    int ox = offScreenX(sx);
    int oy = offScreenY(sy);
    flags = e.getModifiers();
    setCursor(sx, sy, ox, oy);
    IJ.setInputEvent(e);
    Roi roi = imp.getRoi();
    if (roi != null
        && (roi.getType() == Roi.POLYGON
            || roi.getType() == Roi.POLYLINE
            || roi.getType() == Roi.ANGLE)
        && roi.getState() == roi.CONSTRUCTING) {
      PolygonRoi pRoi = (PolygonRoi) roi;
      pRoi.handleMouseMove(ox, oy);
    } else {
      if (ox < imageWidth && oy < imageHeight) {
        ImageWindow win = imp.getWindow();
        // Cursor must move at least 12 pixels before text
        // displayed using IJ.showStatus() is overwritten.
        if ((sx - sx2) * (sx - sx2) + (sy - sy2) * (sy - sy2) > 144) showCursorStatus = true;
        if (win != null && showCursorStatus) win.mouseMoved(ox, oy);
      } else IJ.showStatus("");
    }
  }

  public void mouseClicked(MouseEvent e) {}

  public void mouseEntered(MouseEvent e) {}
}
Exemple #12
0
  public void run(String arg) {
    imp = IJ.getImage();
    int stackSize = imp.getStackSize();
    if (imp == null) {
      IJ.noImage();
      return;
    }

    //  Make sure input image is a stack.
    if (stackSize == 1) {
      IJ.error("Z Project", "Stack required");
      return;
    }

    //  Check for inverting LUT.
    if (imp.getProcessor().isInvertedLut()) {
      if (!IJ.showMessageWithCancel("ZProjection", lutMessage)) return;
    }

    // Set default bounds.
    int channels = imp.getNChannels();
    int frames = imp.getNFrames();
    int slices = imp.getNSlices();
    isHyperstack =
        imp.isHyperStack()
            || (ij.macro.Interpreter.isBatchMode()
                && ((frames > 1 && frames < stackSize) || (slices > 1 && slices < stackSize)));
    boolean simpleComposite = channels == stackSize;
    if (simpleComposite) isHyperstack = false;
    startSlice = 1;
    if (isHyperstack) {
      int nSlices = imp.getNSlices();
      if (nSlices > 1) stopSlice = nSlices;
      else stopSlice = imp.getNFrames();
    } else stopSlice = stackSize;

    // Build control dialog
    GenericDialog gd = buildControlDialog(startSlice, stopSlice);
    gd.showDialog();
    if (gd.wasCanceled()) return;

    if (!imp.lock()) return; // exit if in use
    long tstart = System.currentTimeMillis();
    setStartSlice((int) gd.getNextNumber());
    setStopSlice((int) gd.getNextNumber());
    method = gd.getNextChoiceIndex();
    Prefs.set(METHOD_KEY, method);
    if (isHyperstack) {
      allTimeFrames = imp.getNFrames() > 1 && imp.getNSlices() > 1 ? gd.getNextBoolean() : false;
      doHyperStackProjection(allTimeFrames);
    } else if (imp.getType() == ImagePlus.COLOR_RGB) doRGBProjection(true);
    else doProjection(true);

    if (arg.equals("") && projImage != null) {
      long tstop = System.currentTimeMillis();
      projImage.setCalibration(imp.getCalibration());
      if (simpleComposite) IJ.run(projImage, "Grays", "");
      projImage.show("ZProjector: " + IJ.d2s((tstop - tstart) / 1000.0, 2) + " seconds");
    }

    imp.unlock();
    IJ.register(ZProjector.class);
    return;
  }
Exemple #13
0
/**
 * This plugin performs a z-projection of the input stack. Type of output image is same as type of
 * input image.
 *
 * @author Patrick Kelly <*****@*****.**>
 */
public class ZProjector implements PlugIn {
  public static final int AVG_METHOD = 0;
  public static final int MAX_METHOD = 1;
  public static final int MIN_METHOD = 2;
  public static final int SUM_METHOD = 3;
  public static final int SD_METHOD = 4;
  public static final int MEDIAN_METHOD = 5;
  public static final String[] METHODS = {
    "Average Intensity",
    "Max Intensity",
    "Min Intensity",
    "Sum Slices",
    "Standard Deviation",
    "Median"
  };
  private static final String METHOD_KEY = "zproject.method";
  private int method = (int) Prefs.get(METHOD_KEY, AVG_METHOD);

  private static final int BYTE_TYPE = 0;
  private static final int SHORT_TYPE = 1;
  private static final int FLOAT_TYPE = 2;

  public static final String lutMessage =
      "Stacks with inverter LUTs may not project correctly.\n"
          + "To create a standard LUT, invert the stack (Edit/Invert)\n"
          + "and invert the LUT (Image/Lookup Tables/Invert LUT).";

  /** Image to hold z-projection. */
  private ImagePlus projImage = null;

  /** Image stack to project. */
  private ImagePlus imp = null;

  /** Projection starts from this slice. */
  private int startSlice = 1;
  /** Projection ends at this slice. */
  private int stopSlice = 1;
  /** Project all time points? */
  private boolean allTimeFrames = true;

  private String color = "";
  private boolean isHyperstack;
  private int increment = 1;
  private int sliceCount;

  public ZProjector() {}

  /** Construction of ZProjector with image to be projected. */
  public ZProjector(ImagePlus imp) {
    setImage(imp);
  }

  /**
   * Explicitly set image to be projected. This is useful if ZProjection_ object is to be used not
   * as a plugin but as a stand alone processing object.
   */
  public void setImage(ImagePlus imp) {
    this.imp = imp;
    startSlice = 1;
    stopSlice = imp.getStackSize();
  }

  public void setStartSlice(int slice) {
    if (imp == null || slice < 1 || slice > imp.getStackSize()) return;
    startSlice = slice;
  }

  public void setStopSlice(int slice) {
    if (imp == null || slice < 1 || slice > imp.getStackSize()) return;
    stopSlice = slice;
  }

  public void setMethod(int projMethod) {
    method = projMethod;
  }

  /** Retrieve results of most recent projection operation. */
  public ImagePlus getProjection() {
    return projImage;
  }

  public void run(String arg) {
    imp = IJ.getImage();
    int stackSize = imp.getStackSize();
    if (imp == null) {
      IJ.noImage();
      return;
    }

    //  Make sure input image is a stack.
    if (stackSize == 1) {
      IJ.error("Z Project", "Stack required");
      return;
    }

    //  Check for inverting LUT.
    if (imp.getProcessor().isInvertedLut()) {
      if (!IJ.showMessageWithCancel("ZProjection", lutMessage)) return;
    }

    // Set default bounds.
    int channels = imp.getNChannels();
    int frames = imp.getNFrames();
    int slices = imp.getNSlices();
    isHyperstack =
        imp.isHyperStack()
            || (ij.macro.Interpreter.isBatchMode()
                && ((frames > 1 && frames < stackSize) || (slices > 1 && slices < stackSize)));
    boolean simpleComposite = channels == stackSize;
    if (simpleComposite) isHyperstack = false;
    startSlice = 1;
    if (isHyperstack) {
      int nSlices = imp.getNSlices();
      if (nSlices > 1) stopSlice = nSlices;
      else stopSlice = imp.getNFrames();
    } else stopSlice = stackSize;

    // Build control dialog
    GenericDialog gd = buildControlDialog(startSlice, stopSlice);
    gd.showDialog();
    if (gd.wasCanceled()) return;

    if (!imp.lock()) return; // exit if in use
    long tstart = System.currentTimeMillis();
    setStartSlice((int) gd.getNextNumber());
    setStopSlice((int) gd.getNextNumber());
    method = gd.getNextChoiceIndex();
    Prefs.set(METHOD_KEY, method);
    if (isHyperstack) {
      allTimeFrames = imp.getNFrames() > 1 && imp.getNSlices() > 1 ? gd.getNextBoolean() : false;
      doHyperStackProjection(allTimeFrames);
    } else if (imp.getType() == ImagePlus.COLOR_RGB) doRGBProjection(true);
    else doProjection(true);

    if (arg.equals("") && projImage != null) {
      long tstop = System.currentTimeMillis();
      projImage.setCalibration(imp.getCalibration());
      if (simpleComposite) IJ.run(projImage, "Grays", "");
      projImage.show("ZProjector: " + IJ.d2s((tstop - tstart) / 1000.0, 2) + " seconds");
    }

    imp.unlock();
    IJ.register(ZProjector.class);
    return;
  }

  public void doRGBProjection() {
    doRGBProjection(imp.getStack());
  }

  // Added by Marcel Boeglin 2013.09.23
  public void doRGBProjection(boolean handleOverlay) {
    doRGBProjection(imp.getStack());
    Overlay overlay = imp.getOverlay();
    if (handleOverlay && overlay != null) projImage.setOverlay(projectRGBHyperStackRois(overlay));
  }

  private void doRGBProjection(ImageStack stack) {
    ImageStack[] channels = ChannelSplitter.splitRGB(stack, true);
    ImagePlus red = new ImagePlus("Red", channels[0]);
    ImagePlus green = new ImagePlus("Green", channels[1]);
    ImagePlus blue = new ImagePlus("Blue", channels[2]);
    imp.unlock();
    ImagePlus saveImp = imp;
    imp = red;
    color = "(red)";
    doProjection();
    ImagePlus red2 = projImage;
    imp = green;
    color = "(green)";
    doProjection();
    ImagePlus green2 = projImage;
    imp = blue;
    color = "(blue)";
    doProjection();
    ImagePlus blue2 = projImage;
    int w = red2.getWidth(), h = red2.getHeight(), d = red2.getStackSize();
    if (method == SD_METHOD) {
      ImageProcessor r = red2.getProcessor();
      ImageProcessor g = green2.getProcessor();
      ImageProcessor b = blue2.getProcessor();
      double max = 0;
      double rmax = r.getStatistics().max;
      if (rmax > max) max = rmax;
      double gmax = g.getStatistics().max;
      if (gmax > max) max = gmax;
      double bmax = b.getStatistics().max;
      if (bmax > max) max = bmax;
      double scale = 255 / max;
      r.multiply(scale);
      g.multiply(scale);
      b.multiply(scale);
      red2.setProcessor(r.convertToByte(false));
      green2.setProcessor(g.convertToByte(false));
      blue2.setProcessor(b.convertToByte(false));
    }
    RGBStackMerge merge = new RGBStackMerge();
    ImageStack stack2 =
        merge.mergeStacks(w, h, d, red2.getStack(), green2.getStack(), blue2.getStack(), true);
    imp = saveImp;
    projImage = new ImagePlus(makeTitle(), stack2);
  }

  /**
   * Builds dialog to query users for projection parameters.
   *
   * @param start starting slice to display
   * @param stop last slice
   */
  protected GenericDialog buildControlDialog(int start, int stop) {
    GenericDialog gd = new GenericDialog("ZProjection", IJ.getInstance());
    gd.addNumericField("Start slice:", startSlice, 0 /*digits*/);
    gd.addNumericField("Stop slice:", stopSlice, 0 /*digits*/);
    gd.addChoice("Projection type", METHODS, METHODS[method]);
    if (isHyperstack && imp.getNFrames() > 1 && imp.getNSlices() > 1)
      gd.addCheckbox("All time frames", allTimeFrames);
    return gd;
  }

  /** Performs actual projection using specified method. */
  public void doProjection() {
    if (imp == null) return;
    sliceCount = 0;
    if (method < AVG_METHOD || method > MEDIAN_METHOD) method = AVG_METHOD;
    for (int slice = startSlice; slice <= stopSlice; slice += increment) sliceCount++;
    if (method == MEDIAN_METHOD) {
      projImage = doMedianProjection();
      return;
    }

    // Create new float processor for projected pixels.
    FloatProcessor fp = new FloatProcessor(imp.getWidth(), imp.getHeight());
    ImageStack stack = imp.getStack();
    RayFunction rayFunc = getRayFunction(method, fp);
    if (IJ.debugMode == true) {
      IJ.log("\nProjecting stack from: " + startSlice + " to: " + stopSlice);
    }

    // Determine type of input image. Explicit determination of
    // processor type is required for subsequent pixel
    // manipulation.  This approach is more efficient than the
    // more general use of ImageProcessor's getPixelValue and
    // putPixel methods.
    int ptype;
    if (stack.getProcessor(1) instanceof ByteProcessor) ptype = BYTE_TYPE;
    else if (stack.getProcessor(1) instanceof ShortProcessor) ptype = SHORT_TYPE;
    else if (stack.getProcessor(1) instanceof FloatProcessor) ptype = FLOAT_TYPE;
    else {
      IJ.error("Z Project", "Non-RGB stack required");
      return;
    }

    // Do the projection.
    for (int n = startSlice; n <= stopSlice; n += increment) {
      IJ.showStatus("ZProjection " + color + ": " + n + "/" + stopSlice);
      IJ.showProgress(n - startSlice, stopSlice - startSlice);
      projectSlice(stack.getPixels(n), rayFunc, ptype);
    }

    // Finish up projection.
    if (method == SUM_METHOD) {
      fp.resetMinAndMax();
      projImage = new ImagePlus(makeTitle(), fp);
    } else if (method == SD_METHOD) {
      rayFunc.postProcess();
      fp.resetMinAndMax();
      projImage = new ImagePlus(makeTitle(), fp);
    } else {
      rayFunc.postProcess();
      projImage = makeOutputImage(imp, fp, ptype);
    }

    if (projImage == null) IJ.error("Z Project", "Error computing projection.");
  }

  // Added by Marcel Boeglin 2013.09.23
  /**
   * Performs actual projection using specified method. If handleOverlay, adds stack overlay
   * elements from startSlice to stopSlice to projection
   */
  public void doProjection(boolean handleOverlay) {
    doProjection();
    Overlay overlay = imp.getOverlay();
    if (handleOverlay && overlay != null) projImage.setOverlay(projectStackRois(overlay));
  }

  // Added by Marcel Boeglin 2013.09.23
  private Overlay projectStackRois(Overlay overlay) {
    if (overlay == null) return null;
    Overlay overlay2 = new Overlay();
    Roi roi;
    int s;
    for (Roi r : overlay.toArray()) {
      s = r.getPosition();
      roi = (Roi) r.clone();
      if (s >= startSlice && s <= stopSlice || s == 0) {
        roi.setPosition(s);
        overlay2.add(roi);
      }
    }
    return overlay2;
  }

  public void doHyperStackProjection(boolean allTimeFrames) {
    int start = startSlice;
    int stop = stopSlice;
    int firstFrame = 1;
    int lastFrame = imp.getNFrames();
    if (!allTimeFrames) firstFrame = lastFrame = imp.getFrame();
    ImageStack stack = new ImageStack(imp.getWidth(), imp.getHeight());
    int channels = imp.getNChannels();
    int slices = imp.getNSlices();
    if (slices == 1) {
      slices = imp.getNFrames();
      firstFrame = lastFrame = 1;
    }
    int frames = lastFrame - firstFrame + 1;
    increment = channels;
    boolean rgb = imp.getBitDepth() == 24;
    for (int frame = firstFrame; frame <= lastFrame; frame++) {
      for (int channel = 1; channel <= channels; channel++) {
        startSlice = (frame - 1) * channels * slices + (start - 1) * channels + channel;
        stopSlice = (frame - 1) * channels * slices + (stop - 1) * channels + channel;
        if (rgb) doHSRGBProjection(imp);
        else doProjection();
        stack.addSlice(null, projImage.getProcessor());
      }
    }
    projImage = new ImagePlus(makeTitle(), stack);
    projImage.setDimensions(channels, 1, frames);
    if (channels > 1) {
      projImage = new CompositeImage(projImage, 0);
      ((CompositeImage) projImage).copyLuts(imp);
      if (method == SUM_METHOD || method == SD_METHOD)
        ((CompositeImage) projImage).resetDisplayRanges();
    }
    if (frames > 1) projImage.setOpenAsHyperStack(true);
    Overlay overlay = imp.getOverlay();
    if (overlay != null) {
      startSlice = start;
      stopSlice = stop;
      if (imp.getType() == ImagePlus.COLOR_RGB)
        projImage.setOverlay(projectRGBHyperStackRois(overlay));
      else projImage.setOverlay(projectHyperStackRois(overlay));
    }
    IJ.showProgress(1, 1);
  }

  // Added by Marcel Boeglin 2013.09.22
  private Overlay projectRGBHyperStackRois(Overlay overlay) {
    if (overlay == null) return null;
    int frames = projImage.getNFrames();
    int t1 = imp.getFrame();
    Overlay overlay2 = new Overlay();
    Roi roi;
    int c, z, t;
    for (Roi r : overlay.toArray()) {
      c = r.getCPosition();
      z = r.getZPosition();
      t = r.getTPosition();
      roi = (Roi) r.clone();
      if (z >= startSlice && z <= stopSlice || z == 0 || c == 0 || t == 0) {
        if (frames == 1 && t != t1 && t != 0) // current time frame
        continue;
        roi.setPosition(t);
        overlay2.add(roi);
      }
    }
    return overlay2;
  }

  // Added by Marcel Boeglin 2013.09.22
  private Overlay projectHyperStackRois(Overlay overlay) {
    if (overlay == null) return null;
    int t1 = imp.getFrame();
    int channels = projImage.getNChannels();
    int slices = 1;
    int frames = projImage.getNFrames();
    Overlay overlay2 = new Overlay();
    Roi roi;
    int c, z, t;
    int size = channels * slices * frames;
    for (Roi r : overlay.toArray()) {
      c = r.getCPosition();
      z = r.getZPosition();
      t = r.getTPosition();
      roi = (Roi) r.clone();
      if (size == channels) { // current time frame
        if (z >= startSlice && z <= stopSlice && t == t1 || c == 0) {
          roi.setPosition(c);
          overlay2.add(roi);
        }
      } else if (size == frames * channels) { // all time frames
        if (z >= startSlice && z <= stopSlice) roi.setPosition(c, 1, t);
        else if (z == 0) roi.setPosition(c, 0, t);
        else continue;
        overlay2.add(roi);
      }
    }
    return overlay2;
  }

  private void doHSRGBProjection(ImagePlus rgbImp) {
    ImageStack stack = rgbImp.getStack();
    ImageStack stack2 = new ImageStack(stack.getWidth(), stack.getHeight());
    for (int i = startSlice; i <= stopSlice; i++) stack2.addSlice(null, stack.getProcessor(i));
    startSlice = 1;
    stopSlice = stack2.getSize();
    doRGBProjection(stack2);
  }

  private RayFunction getRayFunction(int method, FloatProcessor fp) {
    switch (method) {
      case AVG_METHOD:
      case SUM_METHOD:
        return new AverageIntensity(fp, sliceCount);
      case MAX_METHOD:
        return new MaxIntensity(fp);
      case MIN_METHOD:
        return new MinIntensity(fp);
      case SD_METHOD:
        return new StandardDeviation(fp, sliceCount);
      default:
        IJ.error("Z Project", "Unknown method.");
        return null;
    }
  }

  /** Generate output image whose type is same as input image. */
  private ImagePlus makeOutputImage(ImagePlus imp, FloatProcessor fp, int ptype) {
    int width = imp.getWidth();
    int height = imp.getHeight();
    float[] pixels = (float[]) fp.getPixels();
    ImageProcessor oip = null;

    // Create output image consistent w/ type of input image.
    int size = pixels.length;
    switch (ptype) {
      case BYTE_TYPE:
        oip = imp.getProcessor().createProcessor(width, height);
        byte[] pixels8 = (byte[]) oip.getPixels();
        for (int i = 0; i < size; i++) pixels8[i] = (byte) pixels[i];
        break;
      case SHORT_TYPE:
        oip = imp.getProcessor().createProcessor(width, height);
        short[] pixels16 = (short[]) oip.getPixels();
        for (int i = 0; i < size; i++) pixels16[i] = (short) pixels[i];
        break;
      case FLOAT_TYPE:
        oip = new FloatProcessor(width, height, pixels, null);
        break;
    }

    // Adjust for display.
    // Calling this on non-ByteProcessors ensures image
    // processor is set up to correctly display image.
    oip.resetMinAndMax();

    // Create new image plus object. Don't use
    // ImagePlus.createImagePlus here because there may be
    // attributes of input image that are not appropriate for
    // projection.
    return new ImagePlus(makeTitle(), oip);
  }

  /**
   * Handles mechanics of projection by selecting appropriate pixel array type. We do this rather
   * than using more general ImageProcessor getPixelValue() and putPixel() methods because direct
   * manipulation of pixel arrays is much more efficient.
   */
  private void projectSlice(Object pixelArray, RayFunction rayFunc, int ptype) {
    switch (ptype) {
      case BYTE_TYPE:
        rayFunc.projectSlice((byte[]) pixelArray);
        break;
      case SHORT_TYPE:
        rayFunc.projectSlice((short[]) pixelArray);
        break;
      case FLOAT_TYPE:
        rayFunc.projectSlice((float[]) pixelArray);
        break;
    }
  }

  String makeTitle() {
    String prefix = "AVG_";
    switch (method) {
      case SUM_METHOD:
        prefix = "SUM_";
        break;
      case MAX_METHOD:
        prefix = "MAX_";
        break;
      case MIN_METHOD:
        prefix = "MIN_";
        break;
      case SD_METHOD:
        prefix = "STD_";
        break;
      case MEDIAN_METHOD:
        prefix = "MED_";
        break;
    }
    return WindowManager.makeUniqueName(prefix + imp.getTitle());
  }

  ImagePlus doMedianProjection() {
    IJ.showStatus("Calculating median...");
    ImageStack stack = imp.getStack();
    ImageProcessor[] slices = new ImageProcessor[sliceCount];
    int index = 0;
    for (int slice = startSlice; slice <= stopSlice; slice += increment)
      slices[index++] = stack.getProcessor(slice);
    ImageProcessor ip2 = slices[0].duplicate();
    ip2 = ip2.convertToFloat();
    float[] values = new float[sliceCount];
    int width = ip2.getWidth();
    int height = ip2.getHeight();
    int inc = Math.max(height / 30, 1);
    for (int y = 0; y < height; y++) {
      if (y % inc == 0) IJ.showProgress(y, height - 1);
      for (int x = 0; x < width; x++) {
        for (int i = 0; i < sliceCount; i++) values[i] = slices[i].getPixelValue(x, y);
        ip2.putPixelValue(x, y, median(values));
      }
    }
    if (imp.getBitDepth() == 8) ip2 = ip2.convertToByte(false);
    IJ.showProgress(1, 1);
    return new ImagePlus(makeTitle(), ip2);
  }

  float median(float[] a) {
    Arrays.sort(a);
    int middle = a.length / 2;
    if ((a.length & 1) == 0) // even
    return (a[middle - 1] + a[middle]) / 2f;
    else return a[middle];
  }

  /**
   * Abstract class that specifies structure of ray function. Preprocessing should be done in
   * derived class constructors.
   */
  abstract class RayFunction {
    /** Do actual slice projection for specific data types. */
    public abstract void projectSlice(byte[] pixels);

    public abstract void projectSlice(short[] pixels);

    public abstract void projectSlice(float[] pixels);

    /** Perform any necessary post processing operations, e.g. averging values. */
    public void postProcess() {}
  } // end RayFunction

  /** Compute average intensity projection. */
  class AverageIntensity extends RayFunction {
    private float[] fpixels;
    private int num, len;

    /**
     * Constructor requires number of slices to be projected. This is used to determine average at
     * each pixel.
     */
    public AverageIntensity(FloatProcessor fp, int num) {
      fpixels = (float[]) fp.getPixels();
      len = fpixels.length;
      this.num = num;
    }

    public void projectSlice(byte[] pixels) {
      for (int i = 0; i < len; i++) fpixels[i] += (pixels[i] & 0xff);
    }

    public void projectSlice(short[] pixels) {
      for (int i = 0; i < len; i++) fpixels[i] += pixels[i] & 0xffff;
    }

    public void projectSlice(float[] pixels) {
      for (int i = 0; i < len; i++) fpixels[i] += pixels[i];
    }

    public void postProcess() {
      float fnum = num;
      for (int i = 0; i < len; i++) fpixels[i] /= fnum;
    }
  } // end AverageIntensity

  /** Compute max intensity projection. */
  class MaxIntensity extends RayFunction {
    private float[] fpixels;
    private int len;

    /** Simple constructor since no preprocessing is necessary. */
    public MaxIntensity(FloatProcessor fp) {
      fpixels = (float[]) fp.getPixels();
      len = fpixels.length;
      for (int i = 0; i < len; i++) fpixels[i] = -Float.MAX_VALUE;
    }

    public void projectSlice(byte[] pixels) {
      for (int i = 0; i < len; i++) {
        if ((pixels[i] & 0xff) > fpixels[i]) fpixels[i] = (pixels[i] & 0xff);
      }
    }

    public void projectSlice(short[] pixels) {
      for (int i = 0; i < len; i++) {
        if ((pixels[i] & 0xffff) > fpixels[i]) fpixels[i] = pixels[i] & 0xffff;
      }
    }

    public void projectSlice(float[] pixels) {
      for (int i = 0; i < len; i++) {
        if (pixels[i] > fpixels[i]) fpixels[i] = pixels[i];
      }
    }
  } // end MaxIntensity

  /** Compute min intensity projection. */
  class MinIntensity extends RayFunction {
    private float[] fpixels;
    private int len;

    /** Simple constructor since no preprocessing is necessary. */
    public MinIntensity(FloatProcessor fp) {
      fpixels = (float[]) fp.getPixels();
      len = fpixels.length;
      for (int i = 0; i < len; i++) fpixels[i] = Float.MAX_VALUE;
    }

    public void projectSlice(byte[] pixels) {
      for (int i = 0; i < len; i++) {
        if ((pixels[i] & 0xff) < fpixels[i]) fpixels[i] = (pixels[i] & 0xff);
      }
    }

    public void projectSlice(short[] pixels) {
      for (int i = 0; i < len; i++) {
        if ((pixels[i] & 0xffff) < fpixels[i]) fpixels[i] = pixels[i] & 0xffff;
      }
    }

    public void projectSlice(float[] pixels) {
      for (int i = 0; i < len; i++) {
        if (pixels[i] < fpixels[i]) fpixels[i] = pixels[i];
      }
    }
  } // end MaxIntensity

  /** Compute standard deviation projection. */
  class StandardDeviation extends RayFunction {
    private float[] result;
    private double[] sum, sum2;
    private int num, len;

    public StandardDeviation(FloatProcessor fp, int num) {
      result = (float[]) fp.getPixels();
      len = result.length;
      this.num = num;
      sum = new double[len];
      sum2 = new double[len];
    }

    public void projectSlice(byte[] pixels) {
      int v;
      for (int i = 0; i < len; i++) {
        v = pixels[i] & 0xff;
        sum[i] += v;
        sum2[i] += v * v;
      }
    }

    public void projectSlice(short[] pixels) {
      double v;
      for (int i = 0; i < len; i++) {
        v = pixels[i] & 0xffff;
        sum[i] += v;
        sum2[i] += v * v;
      }
    }

    public void projectSlice(float[] pixels) {
      double v;
      for (int i = 0; i < len; i++) {
        v = pixels[i];
        sum[i] += v;
        sum2[i] += v * v;
      }
    }

    public void postProcess() {
      double stdDev;
      double n = num;
      for (int i = 0; i < len; i++) {
        if (num > 1) {
          stdDev = (n * sum2[i] - sum[i] * sum[i]) / n;
          if (stdDev > 0.0) result[i] = (float) Math.sqrt(stdDev / (n - 1.0));
          else result[i] = 0f;
        } else result[i] = 0f;
      }
    }
  } // end StandardDeviation
} // end ZProjection