// // ImageViewer.java // /* LOCI Bio-Formats package for reading and converting biological file formats. Copyright (C) 2005-@year@ Melissa Linkert, Curtis Rueden, Chris Allan, Eric Kjellman and Brian Loranger. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package loci.formats.gui; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.io.File; import java.io.IOException; import javax.swing.*; import javax.swing.border.BevelBorder; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import loci.formats.*; /** * ImageViewer is a simple viewer/converter * for the Bio-Formats image formats. * * <dl><dt><b>Source code:</b></dt> * <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/loci/formats/gui/ImageViewer.java">Trac</a>, * <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/loci/formats/gui/ImageViewer.java">SVN</a></dd></dl> * * @author Curtis Rueden ctrueden at wisc.edu */ public class ImageViewer extends JFrame implements ActionListener, ChangeListener, MouseMotionListener { // -- Constants -- protected static final String TITLE = "Bio-Formats Viewer"; protected static final GraphicsConfiguration GC = ImageTools.getDefaultConfiguration(); // -- Fields -- protected JPanel pane; protected ImageIcon icon; protected JLabel iconLabel; protected JPanel sliderPanel; protected JSlider nSlider, zSlider, tSlider, cSlider; protected JLabel probeLabel; protected JMenuItem fileSave; protected IFormatReader myReader; protected ImageWriter myWriter; protected String filename; protected IFormatReader in; protected BufferedImage[] images; protected int sizeZ, sizeT, sizeC; // -- Constructor -- /** Constructs an image viewer. */ public ImageViewer() { super(TITLE); setDefaultCloseOperation(DISPOSE_ON_CLOSE); pane = new JPanel(); pane.setLayout(new BorderLayout()); setContentPane(pane); setSize(350, 350); // default size // navigation sliders sliderPanel = new JPanel(); sliderPanel.setVisible(false); sliderPanel.setBorder(new EmptyBorder(5, 3, 5, 3)); sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.Y_AXIS)); pane.add(BorderLayout.SOUTH, sliderPanel); JPanel nPanel = new JPanel(); nPanel.setLayout(new BoxLayout(nPanel, BoxLayout.X_AXIS)); sliderPanel.add(nPanel); sliderPanel.add(Box.createVerticalStrut(2)); nSlider = new JSlider(1, 1); nSlider.setEnabled(false); nSlider.addChangeListener(this); nPanel.add(new JLabel("N")); nPanel.add(Box.createHorizontalStrut(3)); nPanel.add(nSlider); JPanel ztcPanel = new JPanel(); ztcPanel.setLayout(new BoxLayout(ztcPanel, BoxLayout.X_AXIS)); sliderPanel.add(ztcPanel); zSlider = new JSlider(1, 1); Dimension dim = zSlider.getPreferredSize(); dim.width = 50; zSlider.setPreferredSize(dim); zSlider.setEnabled(false); zSlider.addChangeListener(this); ztcPanel.add(new JLabel("Z")); ztcPanel.add(Box.createHorizontalStrut(3)); ztcPanel.add(zSlider); ztcPanel.add(Box.createHorizontalStrut(7)); tSlider = new JSlider(1, 1); tSlider.setPreferredSize(dim); tSlider.setEnabled(false); tSlider.addChangeListener(this); ztcPanel.add(new JLabel("T")); ztcPanel.add(Box.createHorizontalStrut(3)); ztcPanel.add(tSlider); ztcPanel.add(Box.createHorizontalStrut(7)); cSlider = new JSlider(1, 1); cSlider.setPreferredSize(dim); cSlider.setEnabled(false); cSlider.addChangeListener(this); ztcPanel.add(new JLabel("C")); ztcPanel.add(Box.createHorizontalStrut(3)); ztcPanel.add(cSlider); ztcPanel.add(Box.createHorizontalStrut(7)); // image icon BufferedImage dummy = ImageTools.makeImage(new byte[1][1], 1, 1); icon = new ImageIcon(dummy); iconLabel = new JLabel(icon, SwingConstants.LEFT); iconLabel.setVerticalAlignment(SwingConstants.TOP); pane.add(new JScrollPane(iconLabel)); // cursor probe probeLabel = new JLabel(" "); probeLabel.setHorizontalAlignment(SwingConstants.CENTER); probeLabel.setBorder(new BevelBorder(BevelBorder.RAISED)); pane.add(BorderLayout.NORTH, probeLabel); iconLabel.addMouseMotionListener(this); // menu bar JMenuBar menubar = new JMenuBar(); setJMenuBar(menubar); JMenu file = new JMenu("File"); file.setMnemonic('f'); menubar.add(file); JMenuItem fileOpen = new JMenuItem("Open..."); fileOpen.setMnemonic('o'); fileOpen.setActionCommand("open"); fileOpen.addActionListener(this); file.add(fileOpen); fileSave = new JMenuItem("Save..."); fileSave.setMnemonic('s'); fileSave.setEnabled(false); fileSave.setActionCommand("save"); fileSave.addActionListener(this); file.add(fileSave); boolean canDoNotes = false; try { Class c = Class.forName("loci.ome.notes.Notes"); if (c != null) canDoNotes = true; } catch (Throwable t) { if (FormatHandler.debug) LogTools.trace(t); } if (canDoNotes) { JMenuItem fileView = new JMenuItem("View Metadata..."); fileView.setMnemonic('m'); fileView.setEnabled(true); fileView.setActionCommand("view"); fileView.addActionListener(this); file.add(fileView); } JMenuItem fileExit = new JMenuItem("Exit"); fileExit.setMnemonic('x'); fileExit.setActionCommand("exit"); fileExit.addActionListener(this); file.add(fileExit); JMenu help = new JMenu("Help"); help.setMnemonic('h'); menubar.add(help); JMenuItem helpAbout = new JMenuItem("About..."); helpAbout.setMnemonic('a'); helpAbout.setActionCommand("about"); helpAbout.addActionListener(this); help.add(helpAbout); // image I/O engine myReader = new ChannelMerger(new FileStitcher()); myWriter = new ImageWriter(); } /** Opens the given file using the ImageReader. */ public void open(String id) { wait(true); try { Location f = new Location(id); id = f.getAbsolutePath(); myReader.setId(id); int num = myReader.getImageCount(); ProgressMonitor progress = new ProgressMonitor(this, "Reading " + id, null, 0, num + 1); sizeZ = myReader.getSizeZ(); sizeT = myReader.getSizeT(); sizeC = myReader.getEffectiveSizeC(); //if (myReader.isRGB(id)) sizeC = (sizeC + 2) / 3; // adjust for RGB progress.setProgress(1); BufferedImage[] img = new BufferedImage[num]; for (int i=0; i<num; i++) { if (progress.isCanceled()) break; img[i] = myReader.openImage(i); if (i == 0) setImages(myReader, img); progress.setProgress(i + 2); } myReader.close(true); } catch (FormatException exc) { LogTools.trace(exc); wait(false); return; } catch (IOException exc) { LogTools.trace(exc); wait(false); return; } wait(false); } /** Saves the current images to the given file using the ImageWriter. */ public void save(String id) { if (images == null) return; wait(true); try { myWriter.setId(id); boolean stack = myWriter.canDoStacks(); ProgressMonitor progress = new ProgressMonitor(this, "Saving " + id, null, 0, stack ? images.length : 1); if (stack) { // save entire stack for (int i=0; i<images.length; i++) { progress.setProgress(i); boolean canceled = progress.isCanceled(); myWriter.saveImage(images[i], i == images.length - 1 || canceled); if (canceled) break; } progress.setProgress(images.length); } else { // save current image only myWriter.saveImage(getImage(), true); progress.setProgress(1); } } catch (FormatException exc) { LogTools.trace(exc); } catch (IOException exc) { LogTools.trace(exc); } wait(false); } /** Sets the viewer to display the given images. */ public void setImages(BufferedImage[] img) { setImages(null, img); } /** * Sets the viewer to display the given images, obtaining * corresponding core metadata from the specified format reader. */ public void setImages(IFormatReader reader, BufferedImage[] img) { filename = reader == null ? null : reader.getCurrentFile(); in = reader; images = img; if (reader == null) sizeZ = sizeC = sizeT = 1; else { sizeZ = reader.getSizeZ(); sizeT = reader.getSizeT(); sizeC = reader.getEffectiveSizeC(); } fileSave.setEnabled(true); nSlider.removeChangeListener(this); zSlider.removeChangeListener(this); tSlider.removeChangeListener(this); cSlider.removeChangeListener(this); nSlider.setValue(1); nSlider.setMaximum(images.length); nSlider.setEnabled(images.length > 1); zSlider.setValue(1); zSlider.setMaximum(sizeZ); zSlider.setEnabled(sizeZ > 1); tSlider.setValue(1); tSlider.setMaximum(sizeT); tSlider.setEnabled(sizeT > 1); cSlider.setValue(1); cSlider.setMaximum(sizeC); cSlider.setEnabled(sizeC > 1); nSlider.addChangeListener(this); zSlider.addChangeListener(this); tSlider.addChangeListener(this); cSlider.addChangeListener(this); sliderPanel.setVisible(images.length > 1); updateLabel(-1, -1); sb.setLength(0); if (filename != null) { sb.append(reader.getCurrentFile()); sb.append(" "); } String format = reader == null ? null : reader.getFormat(); if (format != null) { sb.append("("); sb.append(format); sb.append(")"); sb.append(" "); } if (filename != null || format != null) sb.append("- "); sb.append(TITLE); setTitle(sb.toString()); icon.setImage(images == null ? null : images[0]); pack(); } /** Gets the currently displayed image. */ public BufferedImage getImage() { int ndx = getImageIndex(); return images == null || ndx >= images.length ? null : images[ndx]; } /** Gets the index of the currently displayed image. */ public int getImageIndex() { return nSlider.getValue() - 1; } /** Gets the Z value of the currently displayed image. */ public int getZ() { return zSlider.getValue() - 1; } /** Gets the T value of the currently displayed image. */ public int getT() { return tSlider.getValue() - 1; } /** Gets the C value of the currently displayed image. */ public int getC() { return cSlider.getValue() - 1; } // -- ActionListener API methods -- /** Handles menu commands. */ public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if ("open".equals(cmd)) { wait(true); JFileChooser chooser = GUITools.buildFileChooser(myReader); wait(false); int rval = chooser.showOpenDialog(this); if (rval == JFileChooser.APPROVE_OPTION) { final File file = chooser.getSelectedFile(); if (file != null) { new Thread("ImageViewer-Opener") { public void run() { open(file.getAbsolutePath()); } }.start(); } } } else if ("save".equals(cmd)) { wait(true); JFileChooser chooser = GUITools.buildFileChooser(myWriter); wait(false); int rval = chooser.showSaveDialog(this); if (rval == JFileChooser.APPROVE_OPTION) { final File file = chooser.getSelectedFile(); if (file != null) { new Thread("ImageViewer-Saver") { public void run() { save(file.getPath()); } }.start(); } } } else if ("view".equals(cmd)) { // NB: avoid dependencies on optional loci.ome.notes package ReflectedUniverse r = new ReflectedUniverse(); try { r.exec("import loci.ome.notes.Notes"); r.setVar("filename", filename); r.exec("new Notes(null, filename)"); } catch (ReflectException exc) { LogTools.trace(exc); } } else if ("exit".equals(cmd)) dispose(); else if ("about".equals(cmd)) { // HACK - JOptionPane prevents shutdown on dispose setDefaultCloseOperation(EXIT_ON_CLOSE); JOptionPane.showMessageDialog(this, "LOCI Bio-Formats\n" + "Built @date@\n\n" + "The Bio-Formats library is LOCI software written by\n" + "Melissa Linkert, Curtis Rueden, Chris Allan, Eric Kjellman\n" + "and Brian Loranger.\n" + "http://www.loci.wisc.edu/ome/formats.html", "Bio-Formats", JOptionPane.INFORMATION_MESSAGE); } } // -- ChangeListener API methods -- /** Handles slider events. */ public void stateChanged(ChangeEvent e) { Object src = e.getSource(); if (src == nSlider) { // update Z, T and C sliders int ndx = getImageIndex(); int[] zct = in == null ? new int[] {-1, -1, -1} : in.getZCTCoords(ndx); if (zct[0] >= 0) { zSlider.removeChangeListener(this); zSlider.setValue(zct[0] + 1); zSlider.addChangeListener(this); } if (zct[1] >= 0) { cSlider.removeChangeListener(this); cSlider.setValue(zct[1] + 1); cSlider.addChangeListener(this); } if (zct[2] >= 0) { tSlider.removeChangeListener(this); tSlider.setValue(zct[2] + 1); tSlider.addChangeListener(this); } } else { // update N slider int ndx = in == null ? -1 : in.getIndex(getZ(), getC(), getT()); if (ndx >= 0) { nSlider.removeChangeListener(this); nSlider.setValue(ndx + 1); nSlider.addChangeListener(this); } } updateLabel(-1, -1); BufferedImage image = getImage(); if (image != null) icon.setImage(getImage()); iconLabel.repaint(); } // -- MouseMotionListener API methods -- /** Handles cursor probes. */ public void mouseDragged(MouseEvent e) { updateLabel(e.getX(), e.getY()); } /** Handles cursor probes. */ public void mouseMoved(MouseEvent e) { updateLabel(e.getX(), e.getY()); } // -- Helper methods -- protected StringBuffer sb = new StringBuffer(); /** Updates cursor probe label. */ protected void updateLabel(int x, int y) { if (images == null) return; int ndx = getImageIndex(); sb.setLength(0); if (images.length > 1) { sb.append("N="); sb.append(ndx + 1); sb.append("/"); sb.append(images.length); } if (sizeZ > 1) { sb.append("; Z="); sb.append(getZ() + 1); sb.append("/"); sb.append(sizeZ); } if (sizeT > 1) { sb.append("; T="); sb.append(getT() + 1); sb.append("/"); sb.append(sizeT); } if (sizeC > 1) { sb.append("; C="); sb.append(getC() + 1); sb.append("/"); sb.append(sizeC); } BufferedImage image = images[ndx]; int w = image == null ? -1 : image.getWidth(); int h = image == null ? -1 : image.getHeight(); if (x >= w) x = w - 1; if (y >= h) y = h - 1; if (x >= 0 && y >= 0) { if (images.length > 1) sb.append("; "); sb.append("X="); sb.append(x); if (w > 0) { sb.append("/"); sb.append(w); } sb.append("; Y="); sb.append(y); if (h > 0) { sb.append("/"); sb.append(h); } if (image != null) { Raster r = image.getRaster(); double[] pix = r.getPixel(x, y, (double[]) null); sb.append("; value"); sb.append(pix.length > 1 ? "s=(" : "="); for (int i=0; i<pix.length; i++) { if (i > 0) sb.append(", "); sb.append(pix[i]); } if (pix.length > 1) sb.append(")"); sb.append("; type="); int pixelType = ImageTools.getPixelType(image); sb.append(FormatTools.getPixelTypeString(pixelType)); } } sb.append(" "); probeLabel.setText(sb.toString()); } /** Toggles wait cursor. */ protected void wait(boolean wait) { setCursor(wait ? Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR) : null); } // -- Main method -- public static void main(String[] args) { ImageViewer viewer = new ImageViewer(); viewer.setVisible(true); if (args.length > 0) viewer.open(args[0]); } }