Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
ImageCanvas.java
1 package ij.gui;
2 
3 import java.awt.*;
4 import java.util.Properties;
5 import java.awt.image.*;
6 import java.awt.event.*;
7 import ij.process.ImageProcessor;
8 import ij.measure.*;
9 import ij.plugin.frame.Recorder;
10 import ij.*;
11 import ij.util.Java2;
12 import javax.swing.*;
13 
15 public class ImageCanvas extends JPanel implements MouseListener, MouseMotionListener, Cloneable {
16 
17  protected static Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
18  protected static Cursor handCursor = new Cursor(Cursor.HAND_CURSOR);
19  protected static Cursor moveCursor = new Cursor(Cursor.MOVE_CURSOR);
20  protected static Cursor crosshairCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
21 
22  protected static ImagePlus clipboard;
23 
24 
25  public static boolean usePointer = Prefs.usePointerCursor;
26 
27  static final int NO_MODS=0, ADD_TO_ROI=1, SUBTRACT_FROM_ROI=2; // ROI modification states
28 
29  protected ImagePlus imp;
30  protected boolean imageUpdated;
31  protected Rectangle srcRect;
32  protected int imageWidth, imageHeight;
33  protected int xMouse; // current cursor offscreen x location
34  protected int yMouse; // current cursor offscreen y location
35 
36  private ImageJ ij;
37  private double magnification;
38 
39  private int xMouseStart;
40  private int yMouseStart;
41  private int xSrcStart;
42  private int ySrcStart;
43  private int flags;
44 
45  int roiModState = NO_MODS;
46 
47  public ImageCanvas(ImagePlus imp) {
48  this.imp = imp;
49  ij = IJ.getInstance();
50  int width = imp.getWidth();
51  int height = imp.getHeight();
52  imageWidth = width;
53  imageHeight = height;
54  srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
55  addMouseListener(this);
56  addMouseMotionListener(this);
57  addKeyListener(ij); // ImageJ handles keyboard shortcuts
58  Dimension viewportSize = ij.getViewportSize();
59  //magnification = 1.0;
60  magnification = Math.min(1.0, Math.min((float)viewportSize.width / (float)imageWidth, (float)(viewportSize.height - 20) / (float)imageHeight));
61 
62  if (Prefs.blackCanvas && getClass().getName().equals("ij.gui.ImagePanel")) {
63  setForeground(Color.white);
64  setBackground(Color.black);
65  } else {
66  setForeground(Color.black);
67  setBackground(Color.white);
68  }
69 
70  addKeyListener(ij);
71  }
72 
73  public ImagePlus getImagePlus()
74  {
75  return this.imp;
76  }
77 
78 
81  public void setImageUpdated() {
82  imageUpdated = true;
83  }
84 
85  public void update(Graphics g) {
86  paint(g);
87  }
88 
89  public void paintComponent(Graphics g) {
90  Roi roi = imp.getRoi();
91  if (roi != null) roi.updatePaste();
92  try {
93  if (imageUpdated) {
94  imageUpdated = false;
95  imp.updateImage();
96  }
97  if (IJ.isJava2()) {
98  if (magnification<1.0)
99  Java2.setBilinearInterpolation(g, true);
100  else if (IJ.isMacOSX())
101  Java2.setBilinearInterpolation(g, false);
102  }
103  Image img = imp.getImage();
104  if (img!=null)
105  g.drawImage(img, 0, 0, (int)(srcRect.width*magnification), (int)(srcRect.height*magnification),
106  srcRect.x, srcRect.y, srcRect.x+srcRect.width, srcRect.y+srcRect.height, null);
107  if (roi != null)
108  roi.draw(g);
109  }
110  catch(OutOfMemoryError e) {IJ.outOfMemory("Paint");}
111  }
112 
113 
115  public Point getCursorLoc() {
116  return new Point(xMouse, yMouse);
117  }
118 
120  public int getModifiers() {
121  return flags;
122  }
123 
125  public void setCursor(int sx, int sy, int ox, int oy) {
126  xMouse = ox;
127  yMouse = oy;
128  Roi roi = imp.getRoi();
129  if (IJ.spaceBarDown()) {
130  setCursor(handCursor);
131  return;
132  }
133  switch (Toolbar.getToolId()) {
134  case Toolbar.MAGNIFIER:
135  if (IJ.isMacintosh())
136  setCursor(defaultCursor);
137  else
138  setCursor(moveCursor);
139  break;
140  case Toolbar.HAND:
141  setCursor(handCursor);
142  break;
143  default: //selection tool
144  if (roi!=null && roi.getState()!=roi.CONSTRUCTING && roi.isHandle(sx, sy)>=0)
145  setCursor(handCursor);
146  else if (usePointer || (roi!=null && roi.getState()!=roi.CONSTRUCTING && roi.contains(ox, oy)))
147  setCursor(defaultCursor);
148  else
149  setCursor(crosshairCursor);
150  }
151  }
152 
154  public int offScreenX(int x) {
155  return srcRect.x + (int)(x/magnification);
156  }
157 
159  public int offScreenY(int y) {
160  return srcRect.y + (int)(y/magnification);
161  }
162 
164  public int screenX(int x) {
165  return (int)((x-srcRect.x)*magnification);
166  }
167 
169  public int screenY(int y) {
170  return (int)((y-srcRect.y)*magnification);
171  }
172 
173  public double getMagnification() {
174  return magnification;
175  }
176 
177  public void setMagnification(double magnification) {
178  this.magnification = magnification;
179  imp.setTitle(imp.getTitle());
180  setSize((int)(imp.getWidth() * magnification), (int)(imp.getHeight() * magnification));
181  setPreferredSize(getSize());
182  revalidate();
183  ij.repaint();
184  }
185 
186  public Rectangle getSrcRect() {
187  return srcRect;
188  }
189 
190  private static final double[] zoomLevels = {
191  1/72.0, 1/48.0, 1/32.0, 1/24.0, 1/16.0, 1/12.0,
192  1/8.0, 1/6.0, 1/4.0, 1/3.0, 1/2.0, 0.75, 1.0,
193  2.0, 3.0, 4.0, 6.0, 8.0, 12.0, 16.0, 24.0, 32.0 };
194 
195  static double getLowerZoomLevel(double currentMag) {
196  double newMag = zoomLevels[0];
197  for (int i=0; i<zoomLevels.length; i++) {
198  if (zoomLevels[i] < currentMag)
199  newMag = zoomLevels[i];
200  else
201  break;
202  }
203  return newMag;
204  }
205 
206  static double getHigherZoomLevel(double currentMag) {
207  double newMag = 32.0;
208  for (int i=zoomLevels.length-1; i>=0; i--) {
209  if (zoomLevels[i]>currentMag)
210  newMag = zoomLevels[i];
211  else
212  break;
213  }
214  return newMag;
215  }
216 
219  public void zoomIn(int x, int y) {
220  if (magnification>=32)
221  return;
222  double newMag = getHigherZoomLevel(magnification);
223  setMagnification(newMag);
224  repaint();
225  }
226 
227  boolean canEnlarge(int newWidth, int newHeight) {
228  return true;
229  }
230 
233  public void zoomOut(int x, int y) {
234  if (magnification<=0.03125)
235  return;
236  double newMag = getLowerZoomLevel(magnification);
237  setMagnification(newMag);
238  ij.getContentPane().repaint();
239  }
240 
241  void unzoom() {
242  Dimension viewportSize = ij.getViewportSize();
243  double imag = Math.min(1.0, Math.min((float)viewportSize.width / (float)imageWidth, (float)(viewportSize.height - 20) / (float)imageHeight));
244  if (magnification==imag)
245  return;
246  setMagnification(imag);
247  ij.getContentPane().repaint();
248  }
249 
250 
251  Color getColor(int index){
252  IndexColorModel cm = (IndexColorModel)imp.getProcessor().getColorModel();
253  //IJ.write(""+index+" "+(new Color(cm.getRGB(index))));
254  return new Color(cm.getRGB(index));
255  }
256 
257  protected void setDrawingColor(int ox, int oy, boolean setBackground) {
258  //IJ.write("setDrawingColor: "+setBackground+this);
259  int type = imp.getType();
260  int[] v = imp.getPixel(ox, oy);
261  switch (type) {
262  case ImagePlus.GRAY8: {
263  if (setBackground)
264  setBackgroundColor(getColor(v[0]));
265  else
266  setForegroundColor(getColor(v[0]));
267  break;
268  }
269  case ImagePlus.GRAY16: case ImagePlus.GRAY32: {
270  double min = imp.getProcessor().getMin();
271  double max = imp.getProcessor().getMax();
272  double value = (type==ImagePlus.GRAY32)?Float.intBitsToFloat(v[0]):v[0];
273  int index = (int)(255.0*((value-min)/(max-min)));
274  if (index<0) index = 0;
275  if (index>255) index = 255;
276  if (setBackground)
277  setBackgroundColor(getColor(index));
278  else
279  setForegroundColor(getColor(index));
280  break;
281  }
282  case ImagePlus.COLOR_RGB: case ImagePlus.COLOR_256: {
283  Color c = new Color(v[0], v[1], v[2]);
284  if (setBackground)
285  setBackgroundColor(c);
286  else
287  setForegroundColor(c);
288  break;
289  }
290  }
291  Color c;
292  if (setBackground)
293  c = Toolbar.getBackgroundColor();
294  else {
295  c = Toolbar.getForegroundColor();
296  imp.setColor(c);
297  }
298  IJ.showStatus("("+c.getRed()+", "+c.getGreen()+", "+c.getBlue()+")");
299  }
300 
301  private void setForegroundColor(Color c) {
302  Toolbar.setForegroundColor(c);
303  if (Recorder.record)
304  Recorder.record("setForegroundColor", c.getRed(), c.getGreen(), c.getBlue());
305  }
306 
307  private void setBackgroundColor(Color c) {
308  Toolbar.setBackgroundColor(c);
309  if (Recorder.record)
310  Recorder.record("setBackgroundColor", c.getRed(), c.getGreen(), c.getBlue());
311  }
312 
313  public void mousePressed(MouseEvent e) {
314  if (ij==null) return;
315  int toolID = Toolbar.getToolId();
316 
317  int x = e.getX();
318  int y = e.getY();
319 
320  flags = e.getModifiers();
321  if (IJ.debugMode) IJ.log("Mouse pressed: (" + x + "," + y + ")" + ij.modifiers(flags));
322  //if (toolID!=Toolbar.MAGNIFIER && e.isPopupTrigger()) {
323  if (toolID!=Toolbar.MAGNIFIER && (e.isPopupTrigger() || (flags & Event.META_MASK)!=0)) {
324  handlePopupMenu(e);
325  return;
326  }
327 
328  int ox = offScreenX(x);
329  int oy = offScreenY(y);
330  xMouse = ox; yMouse = oy;
331  switch (toolID) {
332  case Toolbar.MAGNIFIER:
333  if ((flags & (Event.ALT_MASK|Event.META_MASK|Event.CTRL_MASK))!=0)
334  zoomOut(x, y);
335  else
336  zoomIn(x, y);
337  break;
338  case Toolbar.DROPPER:
339  setDrawingColor(ox, oy, IJ.altKeyDown());
340  break;
341  case Toolbar.CROSSHAIR:
342  IJ.doCommand("Measure");
343  break;
344  case Toolbar.WAND:
345  Roi roi = imp.getRoi();
346  if (roi!=null && roi.contains(ox, oy)) {
347  Rectangle r = roi.getBounds();
348  if (r.width==imageWidth && r.height==imageHeight)
349  imp.killRoi();
350  else {
351  handleRoiMouseDown(e);
352  return;
353  }
354  }
355  if (roi!=null) {
356  int handle = roi.isHandle(x, y);
357  if (handle>=0) {
358  roi.mouseDownInHandle(handle, x, y);
359  return;
360  }
361  }
362  setRoiModState(e, roi, -1);
363  int npoints = IJ.doWand(ox, oy);
364  if (Recorder.record && npoints>0)
365  Recorder.record("doWand", ox, oy);
366  break;
367  default: //selection tool
368  handleRoiMouseDown(e);
369  }
370  }
371 
372  protected void handlePopupMenu(MouseEvent e) {
373  if (IJ.debugMode) IJ.log("show popup: " + (e.isPopupTrigger()?"true":"false"));
374  int x = e.getX();
375  int y = e.getY();
376  Roi roi = imp.getRoi();
377  if (roi!=null && (roi.getType()==Roi.POLYGON || roi.getType()==Roi.POLYLINE || roi.getType()==Roi.ANGLE)
378  && roi.getState()==roi.CONSTRUCTING) {
379  roi.handleMouseUp(x, y); // simulate double-click to finalize
380  roi.handleMouseUp(x, y); // polygon or polyline selection
381  return;
382  }
383  JPopupMenu popup = Menus.getPopupMenu();
384  if (popup!=null) {
385  add(popup);
386  popup.show(this, x, y);
387  }
388  }
389 
390  public void mouseExited(MouseEvent e) {
391  setCursor(defaultCursor);
392  IJ.showStatus("");
393  }
394 
395 
396  public void mouseDragged(MouseEvent e) {
397  int x = e.getX();
398  int y = e.getY();
399  xMouse = offScreenX(x);
400  yMouse = offScreenY(y);
401  flags = e.getModifiers();
402  if (flags==0) // workaround for Mac OS 9 bug
403  flags = InputEvent.BUTTON1_MASK;
404  if (Toolbar.getToolId()==Toolbar.HAND || IJ.spaceBarDown())
405  ;//scroll(x, y);
406  else {
407  IJ.setInputEvent(e);
408  Roi roi = imp.getRoi();
409  if (roi != null)
410  roi.handleMouseDrag(x, y, flags);
411  }
412  }
413 
414  void handleRoiMouseDown(MouseEvent e) {
415  int sx = e.getX();
416  int sy = e.getY();
417  int ox = offScreenX(sx);
418  int oy = offScreenY(sy);
419  Roi roi = imp.getRoi();
420  int handle = roi!=null?roi.isHandle(sx, sy):-1;
421  setRoiModState(e, roi, handle);
422  if (roi!=null) {
423  Rectangle r = roi.getBounds();
424  int type = roi.getType();
425  if (type==Roi.RECTANGLE && r.width==imp.getWidth() && r.height==imp.getHeight()
426  && roi.getPasteMode()==Roi.NOT_PASTING) {
427  imp.killRoi();
428  return;
429  }
430  if (handle>=0) {
431  roi.mouseDownInHandle(handle, sx, sy);
432  return;
433  }
434  if (roi.contains(ox, oy)) {
435  if (roiModState==NO_MODS)
436  roi.handleMouseDown(sx, sy);
437  else {
438  imp.killRoi();
439  imp.createNewRoi(ox,oy);
440  }
441  return;
442  }
443  if ((type==Roi.POLYGON || type==Roi.POLYLINE || type==Roi.ANGLE)
444  && roi.getState()==roi.CONSTRUCTING)
445  return;
446  }
447  imp.createNewRoi(ox,oy);
448  }
449 
450  void setRoiModState(MouseEvent e, Roi roi, int handle) {
451  if (roi==null || !IJ.isJava2())
452  {roiModState = NO_MODS; return;}
453  if (handle>=0 && roiModState==NO_MODS)
454  return;
455  if (roi.state==Roi.CONSTRUCTING)
456  return;
457  int tool = Toolbar.getToolId();
458  if (tool>Toolbar.FREEROI && tool!=Toolbar.WAND)
459  {roiModState = NO_MODS; return;}
460  if (e.isShiftDown())
461  roiModState = ADD_TO_ROI;
462  else if (e.isAltDown())
463  roiModState = SUBTRACT_FROM_ROI;
464  else
465  roiModState = NO_MODS;
466  //IJ.log("setRoiModState: "+roiModState+" "+ (roi==null?"null":""+roi.state));
467  }
468 
469  public void mouseReleased(MouseEvent e) {
470  flags = e.getModifiers();
471  flags &= ~InputEvent.BUTTON1_MASK; // make sure button 1 bit is not set
472  //IJ.log("mouseReleased: "+flags);
473  Roi roi = imp.getRoi();
474  if (roi != null) {
475  Rectangle r = roi.getBounds();
476  if ((r.width==0 || r.height==0)
477  && !(roi.getType()==Roi.POLYGON || roi.getType()==Roi.POLYLINE || roi.getType()==Roi.ANGLE)
478  && !(roi instanceof TextRoi)
479  && roi.getState()==roi.CONSTRUCTING)
480  imp.killRoi();
481  else
482  roi.handleMouseUp(e.getX(), e.getY());
483  }
484  }
485 
486  public void mouseMoved(MouseEvent e) {
487  if (ij==null) return;
488  int sx = e.getX();
489  int sy = e.getY();
490  int ox = offScreenX(sx);
491  int oy = offScreenY(sy);
492  flags = e.getModifiers();
493  //if (IJ.debugMode) IJ.log(e.getX() + " " + e.getY() + " " + ox + " " + oy);
494  setCursor(sx, sy, ox, oy);
495  IJ.setInputEvent(e);
496  Roi roi = imp.getRoi();
497  if (roi!=null && (roi.getType()==Roi.POLYGON || roi.getType()==Roi.POLYLINE || roi.getType()==Roi.ANGLE)
498  && roi.getState()==roi.CONSTRUCTING) {
499  PolygonRoi pRoi = (PolygonRoi)roi;
500  pRoi.handleMouseMove(ox, oy);
501  } else {
502  IJ.showStatus("");
503  }
504  imp.mouseMoved(sx, sy);
505  }
506 
507  public void mouseClicked(MouseEvent e) {}
508  public void mouseEntered(MouseEvent e) {}
509 
510 
511  static ImagePlus getClipboard() {
512  return clipboard;
513  }
514 
515 
518  public void copy(boolean cut) {
519  Roi roi = imp.getRoi();
520  String msg = (cut)?"Cut":"Copy";
521  IJ.showStatus(msg+ "ing...");
522  ImageProcessor ip = imp.getProcessor();
523  ImageProcessor ip2 = ip.crop();
524  clipboard = new ImagePlus("Clipboard", ip2);
525  if (roi!=null && roi.getType()!=Roi.RECTANGLE)
526  clipboard.setRoi((Roi)roi.clone());
527  if (cut) {
528  ip.snapshot();
529  ip.setColor(Toolbar.getBackgroundColor());
530  ip.fill();
531  if (roi!=null && roi.getType()!=Roi.RECTANGLE)
532  ip.reset(imp.getMask());
533  imp.setColor(Toolbar.getForegroundColor());
534  Undo.setup(Undo.FILTER, imp);
535  imp.updateAndDraw();
536  }
537  int bytesPerPixel = 1;
538  switch (clipboard.getType()) {
539  case ImagePlus.GRAY16: bytesPerPixel = 2; break;
540  case ImagePlus.GRAY32: case ImagePlus.COLOR_RGB: bytesPerPixel = 4;
541  }
542  IJ.showStatus(msg + ": " + (clipboard.getWidth()*clipboard.getHeight()*bytesPerPixel)/1024 + "k");
543  }
544 
545 
546  public void paste()
547  {
548  if (clipboard==null) {
549  return;
550  }
551  int cType = clipboard.getType();
552  int iType = imp.getType();
553 
554  boolean sameType = false;
555  if ((cType==ImagePlus.GRAY8|cType==ImagePlus.COLOR_256)&&(iType==ImagePlus.GRAY8|iType==ImagePlus.COLOR_256)) sameType = true;
556  else if ((cType==ImagePlus.COLOR_RGB|cType==ImagePlus.GRAY8|cType==ImagePlus.COLOR_256)&&iType==ImagePlus.COLOR_RGB) sameType = true;
557  else if (cType==ImagePlus.GRAY16&&iType==ImagePlus.GRAY16) sameType = true;
558  else if (cType==ImagePlus.GRAY32&&iType==ImagePlus.GRAY32) sameType = true;
559  if (!sameType) {
560  IJ.error("Images must be the same type to paste.");
561  return;
562  }
563  int w = clipboard.getWidth();
564  int h = clipboard.getHeight();
565  if (w>imp.getWidth() || h>imp.getHeight()) {
566  IJ.error("Image is too large to paste.");
567  return;
568  }
569  Roi roi = imp.getRoi();
570  Rectangle r = null;
571  if (roi!=null)
572  r = roi.getBounds();
573  if (r==null || (r!=null && (w!=r.width || h!=r.height))) {
574  // create a new roi centered on visible part of image
575  Rectangle srcRect = getSrcRect();
576  int xCenter = srcRect.x + srcRect.width/2;
577  int yCenter = srcRect.y + srcRect.height/2;
578  Roi cRoi = clipboard.getRoi();
579  if (cRoi!=null && cRoi.getType()!=Roi.RECTANGLE) {
580  cRoi.setImage(imp);
581  cRoi.setLocation(xCenter-w/2, yCenter-h/2);
582  imp.setRoi(cRoi);
583  } else
584  imp.setRoi(xCenter-w/2, yCenter-h/2, w, h);
585  roi = imp.getRoi();
586  }
587  if (IJ.macroRunning()) {
588  //non-interactive paste
589  int pasteMode = Roi.getCurrentPasteMode();
590  boolean nonRect = roi.getType()!=Roi.RECTANGLE;
591  ImageProcessor ip = imp.getProcessor();
592  if (nonRect) ip.snapshot();
593  r = roi.getBounds();
594  ip.copyBits(clipboard.getProcessor(), r.x, r.y, pasteMode);
595  if (nonRect)
596  ip.reset(imp.getMask());
597  imp.updateAndDraw();
598  imp.killRoi();
599  } else {
600  roi.startPaste(clipboard);
601  Undo.setup(Undo.PASTE, imp);
602  }
603  imp.changes = true;
604  //Image img = clipboard.getImage();
605  //ImagePlus imp2 = new ImagePlus("Clipboard", img);
606  //imp2.show();
607  }
608 
609 
610 }