001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the files COPYING and Copyright.html. *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * Or, see http://hdfgroup.org/products/hdf-java/doc/Copyright.html.         *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.view;
016
017import java.awt.BorderLayout;
018import java.awt.Color;
019import java.awt.Cursor;
020import java.awt.Dimension;
021import java.awt.Font;
022import java.awt.Graphics;
023import java.awt.Graphics2D;
024import java.awt.GridLayout;
025import java.awt.Image;
026import java.awt.Insets;
027import java.awt.Point;
028import java.awt.Rectangle;
029import java.awt.RenderingHints;
030import java.awt.Toolkit;
031import java.awt.event.ActionEvent;
032import java.awt.event.ActionListener;
033import java.awt.event.InputEvent;
034import java.awt.event.KeyEvent;
035import java.awt.event.MouseEvent;
036import java.awt.event.MouseListener;
037import java.awt.event.MouseMotionListener;
038import java.awt.event.MouseWheelEvent;
039import java.awt.event.MouseWheelListener;
040import java.awt.image.BufferedImage;
041import java.awt.image.ColorModel;
042import java.awt.image.DataBufferInt;
043import java.awt.image.FilteredImageSource;
044import java.awt.image.ImageFilter;
045import java.awt.image.ImageProducer;
046import java.awt.image.PixelGrabber;
047import java.awt.image.RGBImageFilter;
048import java.beans.PropertyChangeEvent;
049import java.beans.PropertyChangeListener;
050import java.io.BufferedWriter;
051import java.io.File;
052import java.io.FileWriter;
053import java.io.PrintWriter;
054import java.io.RandomAccessFile;
055import java.lang.reflect.Array;
056import java.text.DecimalFormat;
057import java.util.ArrayList;
058import java.util.BitSet;
059import java.util.Enumeration;
060import java.util.HashMap;
061import java.util.Hashtable;
062import java.util.List;
063import java.util.Vector;
064
065import javax.swing.BorderFactory;
066import javax.swing.JButton;
067import javax.swing.JCheckBoxMenuItem;
068import javax.swing.JComponent;
069import javax.swing.JDialog;
070import javax.swing.JFileChooser;
071import javax.swing.JFormattedTextField;
072import javax.swing.JFrame;
073import javax.swing.JInternalFrame;
074import javax.swing.JLabel;
075import javax.swing.JMenu;
076import javax.swing.JMenuBar;
077import javax.swing.JMenuItem;
078import javax.swing.JOptionPane;
079import javax.swing.JPanel;
080import javax.swing.JScrollBar;
081import javax.swing.JScrollPane;
082import javax.swing.JSlider;
083import javax.swing.JTextField;
084import javax.swing.SwingConstants;
085import javax.swing.border.Border;
086import javax.swing.border.TitledBorder;
087import javax.swing.event.ChangeEvent;
088import javax.swing.event.ChangeListener;
089import javax.swing.filechooser.FileNameExtensionFilter;
090import javax.swing.text.NumberFormatter;
091import javax.swing.tree.DefaultMutableTreeNode;
092import javax.swing.tree.TreeNode;
093
094import hdf.object.Datatype;
095import hdf.object.Group;
096import hdf.object.HObject;
097import hdf.object.ScalarDS;
098import hdf.view.ViewProperties.BITMASK_OP;
099
100/**
101 * ImageView displays an HDF dataset as an image.
102 * <p>
103 * A scalar dataset in HDF can be displayed in image or table. By default, an
104 * HDF4 GR image and HDF5 image is displayed as an image. Other scalar datasets
105 * are display in a two-dimensional table.
106 * <p>
107 * Users can also choose to display a scalar dataset as image. Currently verion
108 * of the ImageView only supports 8-bit raster image with indexed RGB color
109 * model of 256 colors or 24-bit true color raster image. Data of other type
110 * will be converted to 8-bit integer. The simple linear conversion is used for
111 * this purpose:
112 * 
113 * <pre>
114 * y = f * (x - min),
115 *       where y   = the value of 8-bit integer,
116 *             x   = the value of original data,
117 *             f   = 255/(max-min), conversion factor,
118 *             max = the maximum of the original data,
119 *             min = the minimum of the original data.
120 * </pre>
121 * <p>
122 * A default color table is provided for images without palette attached to it.
123 * Current choice of default palettes include Gray, Rainbow, Nature and Wave.
124 * For more infomation on palette, read <a
125 * href="http://hdfgroup.org/HDF5/doc/ADGuide/ImageSpec.html"> HDF5 Image and
126 * Palette Specification </a>
127 * 
128 * @author Peter X. Cao
129 * @version 2.4 9/6/2007
130 */
131public class DefaultImageView extends JInternalFrame implements ImageView,
132ActionListener {
133        private static final long serialVersionUID = -6534336542813587242L;
134
135        private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultImageView.class);
136
137        /** Horizontal direction to flip an image. */
138        public static final int FLIP_HORIZONTAL = 0;
139
140        /** Vertical direction to flip an image. */
141        public static final int FLIP_VERTICAL = 1;
142
143        /** ROTATE IMAGE 90 DEGREE CLOCKWISE. */
144        public static final int ROTATE_CW_90 = 10;
145
146        /** ROTATE IMAGE COUNTER CLOCKWISE 90 DEGREE. */
147        public static final int ROTATE_CCW_90 = 11;
148
149        /**
150         * The main HDFView.
151         */
152        private final ViewManager viewer;
153
154        /**
155         * The Scalar Dataset.
156         */
157        private ScalarDS dataset;
158
159        /**
160         * The JComponent containing the image.
161         */
162        private ImageComponent imageComponent;
163
164        /**
165         * The image contained in the ImageView.
166         */
167        private Image image;
168
169        /**
170         * The zooming factor of this image.
171         */
172        private float zoomFactor;
173
174        /**
175         * The byte data array of the image.
176         */
177        private byte[] imageByteData;
178
179        /**
180         * The color table of the image.
181         */
182        private byte[][] imagePalette;
183
184        /**
185         * The title of this imageview.
186         */
187        private String frameTitle;
188
189        /** TextField to show the image value. */
190        private JTextField valueField;
191
192        /** Flag to indicate if the image is a true color image */
193        private boolean isTrueColor;
194
195        /** Flag to indicate if the image is a 3D */
196        private boolean is3D;
197
198        /** Flag to indicate if the image is plane interleaved */
199        private boolean isPlaneInterlace;
200
201        private boolean isHorizontalFlipped = false;
202
203        private boolean isVerticalFlipped = false;
204
205        private int rotateCount = 0;
206
207        /** the number type of the image data */
208        private char NT;
209
210        /** the raw data of the image */
211        private Object data;
212
213        /** flag to indicate if the original data type is unsigned integer */
214        private boolean isUnsigned;
215
216        private boolean isUnsignedConverted = false;
217
218        private final Toolkit toolkit;
219
220        private double[] dataRange;
221        private final double[] originalRange = {0,0};
222
223        private PaletteComponent paletteComponent;
224
225        private int animationSpeed = 2;
226
227        private List rotateRelatedItems;
228
229        private JScrollPane imageScroller;
230
231        private JTextField frameField;
232
233        private long curFrame = 0, maxFrame = 1;
234
235        private BufferedImage bufferedImage;
236
237        //    private AutoContrastSlider autoContrastSlider;
238
239        private ContrastSlider contrastSlider;
240
241        private int indexBase = 0;
242        private int[] dataDist = null;
243
244        /**
245         * equates to brightness
246         */
247        private boolean doAutoGainContrast = false;
248        private double[] gainBias, gainBias_current;
249
250        /**
251         * int array to hold unsigned short or signed int data from applying the
252         * autogain
253         */
254        private Object autoGainData;
255
256        private BitSet bitmask;
257        private boolean convertByteData = false;
258        private BITMASK_OP bitmaskOP = BITMASK_OP.EXTRACT;
259
260        /* image origin: 0-UL, 1-LL, 2-UR, 3-LR */
261        private int origin = 0;
262
263        private List<Integer> invalidValueIndex;
264
265        /**
266         * Constructs an ImageView.
267         * <p>
268         * 
269         * @param theView
270         *            the main HDFView.
271         */
272        public DefaultImageView(ViewManager theView) {
273                this(theView, null);
274        }
275
276        /**
277         * Constructs an ImageView.
278         * <p>
279         * 
280         * @param theView
281         *            the main HDFView.
282         * @param map
283         *            the properties on how to show the data. The map is used to
284         *            allow applications to pass properties on how to display the
285         *            data, such as, transposing data, showing data as character,
286         *            applying bitmask, and etc. Predefined keys are listed at
287         *            ViewProperties.DATA_VIEW_KEY.
288         */
289        public DefaultImageView(ViewManager theView, HashMap map) {
290                super();
291
292                setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
293                setFrameIcon(ViewProperties.getImageIcon());
294
295                viewer = theView;
296                zoomFactor = 1.0f;
297                imageByteData = null;
298                imagePalette = null;
299                paletteComponent = null;
300                isTrueColor = false;
301                is3D = false;
302                isPlaneInterlace = false;
303                isUnsigned = false;
304                data = null;
305                NT = 0;
306                toolkit = Toolkit.getDefaultToolkit();
307                rotateRelatedItems = new Vector(10);
308                imageScroller = null;
309                gainBias = null;
310                gainBias_current = null;
311                autoGainData = null;
312                contrastSlider = null;
313                bitmask = null;
314                invalidValueIndex = new ArrayList<Integer>();
315
316                String origStr = ViewProperties.getImageOrigin();
317                if (ViewProperties.ORIGIN_LL.equalsIgnoreCase(origStr))
318                        origin = 1;
319                else if (ViewProperties.ORIGIN_UR.equalsIgnoreCase(origStr))
320                        origin = 2;
321                else if (ViewProperties.ORIGIN_LR.equalsIgnoreCase(origStr))
322                        origin = 3;
323
324                if (ViewProperties.isIndexBase1())
325                        indexBase = 1;
326
327                HObject hobject = null;
328
329                if (map != null) {
330                        hobject = (HObject) map.get(ViewProperties.DATA_VIEW_KEY.OBJECT);
331                        bitmask = (BitSet) map.get(ViewProperties.DATA_VIEW_KEY.BITMASK);
332                        bitmaskOP = (BITMASK_OP)map.get(ViewProperties.DATA_VIEW_KEY.BITMASKOP);
333
334                        Boolean b = (Boolean) map
335                        .get(ViewProperties.DATA_VIEW_KEY.CONVERTBYTE);
336                        if (b != null)
337                                convertByteData = b.booleanValue();
338
339                        b = (Boolean) map.get(ViewProperties.DATA_VIEW_KEY.INDEXBASE1);
340                        if (b != null) {
341                                if (b.booleanValue())
342                                        indexBase = 1;
343                                else
344                                        indexBase = 0;
345                        }            
346                }
347
348                if (hobject == null)
349                        hobject = (HObject) theView.getTreeView().getCurrentObject();
350
351                if ((hobject == null) || !(hobject instanceof ScalarDS)) {
352                        viewer.showStatus("Display data in image failed for - " + hobject);
353                        return;
354                }
355
356                dataset = (ScalarDS) hobject;
357                dataRange = dataset.getImageDataRange();
358                if (dataRange == null) {
359                        dataRange = new double[2];
360                        dataRange[0] = dataRange[1] = 0;
361                        if (dataset.getDatatype().getDatatypeSize() == 1
362                                        && !convertByteData) {
363                                dataRange[1] = 255; // byte image data rang = [0, 255]
364                        }
365                }
366                else {
367                        if (dataRange[0] < dataRange[1])
368                                convertByteData = true;
369                }
370
371                JPanel contentPane = (JPanel) getContentPane();
372                contentPane.setName("imagecontentpane");
373                contentPane.setLayout(new BorderLayout());
374
375                // add the text field to display pixel data
376                contentPane.add(valueField = new JTextField(), BorderLayout.SOUTH);
377                valueField.setName("valuefield");
378                valueField.setEditable(false);
379                valueField.setVisible(ViewProperties.showImageValues());
380
381                if (image == null) {
382                        getImage();
383                }
384
385                if (image == null) {
386                        viewer.showStatus("Loading image failed - " + dataset.getName());
387                        dataset = null;
388                        return;
389                }
390
391                originalRange[0] = dataRange[0];
392                originalRange[1] = dataRange[1];
393
394                imageComponent = new ImageComponent(image);
395                JScrollPane scroller = new JScrollPane(imageComponent);
396                scroller.getVerticalScrollBar().setUnitIncrement(50);
397                scroller.getHorizontalScrollBar().setUnitIncrement(50);
398                scroller.setName("imagecontent");
399                imageScroller = scroller;
400                contentPane.add(scroller, BorderLayout.CENTER);
401
402                // add palette convas to show the palette
403                if (imagePalette != null) {
404                        paletteComponent = new PaletteComponent(imagePalette, dataRange);
405                        contentPane.add(paletteComponent, BorderLayout.EAST);
406                }
407
408                if (origin == 1)
409                        flip(FLIP_VERTICAL);
410                else if (origin == 2)
411                        flip(FLIP_HORIZONTAL);
412                if (origin == 3) {
413                        rotate(ROTATE_CW_90);
414                        rotate(ROTATE_CW_90);
415                }
416
417                // set title
418                StringBuffer sb = new StringBuffer(hobject.getName());
419                sb.append("  at  ");
420                sb.append(hobject.getPath());
421                sb.append("  [");
422                sb.append(dataset.getFileFormat().getName());
423                sb.append("  in  ");
424                sb.append(dataset.getFileFormat().getParent());
425                sb.append("]");
426
427                setTitle(sb.toString());
428
429                frameTitle = sb.toString();
430                setTitle(frameTitle);
431                this.setName(frameTitle);
432
433                // setup subset information
434                int rank = dataset.getRank();
435                int[] selectedIndex = dataset.getSelectedIndex();
436                long[] count = dataset.getSelectedDims();
437                long[] stride = dataset.getStride();
438                long[] dims = dataset.getDims();
439                long[] start = dataset.getStartDims();
440                int n = Math.min(3, rank);
441
442                if (rank > 2) {
443                        curFrame = start[selectedIndex[2]]+indexBase;
444                        maxFrame = dims[selectedIndex[2]];
445                }
446
447                sb.append(" [ dims");
448                sb.append(selectedIndex[0]);
449                for (int i = 1; i < n; i++) {
450                        sb.append("x");
451                        sb.append(selectedIndex[i]);
452                }
453                sb.append(", start");
454                sb.append(start[selectedIndex[0]]);
455                for (int i = 1; i < n; i++) {
456                        sb.append("x");
457                        sb.append(start[selectedIndex[i]]);
458                }
459                sb.append(", count");
460                sb.append(count[selectedIndex[0]]);
461                for (int i = 1; i < n; i++) {
462                        sb.append("x");
463                        sb.append(count[selectedIndex[i]]);
464                }
465                sb.append(", stride");
466                sb.append(stride[selectedIndex[0]]);
467                for (int i = 1; i < n; i++) {
468                        sb.append("x");
469                        sb.append(stride[selectedIndex[i]]);
470                }
471                sb.append(" ] ");
472
473                setJMenuBar(createMenuBar());
474                viewer.showStatus(sb.toString());
475
476                int titleJustification = TitledBorder.LEFT;
477                int titlePosition = TitledBorder.TOP;
478                String orgin = ViewProperties.getImageOrigin();
479                if (orgin.equalsIgnoreCase(ViewProperties.ORIGIN_UR))
480                        titleJustification = TitledBorder.RIGHT;
481                else if (orgin.equalsIgnoreCase(ViewProperties.ORIGIN_LL))
482                        titlePosition = TitledBorder.BOTTOM;
483                else if (orgin.equalsIgnoreCase(ViewProperties.ORIGIN_LR)) {
484                        titleJustification = TitledBorder.RIGHT;
485                        titlePosition = TitledBorder.BOTTOM;
486                }
487
488                String originTag = "(0,0)";
489                if (ViewProperties.isIndexBase1())
490                        originTag = "(1,1)";
491
492                Border border = BorderFactory.createCompoundBorder(
493                                BorderFactory.createRaisedBevelBorder(), BorderFactory
494                                .createTitledBorder(BorderFactory
495                                                .createLineBorder(Color.lightGray, 1),
496                                                originTag,
497                                                titleJustification, titlePosition,
498                                                this.getFont(), Color.black));
499                contentPane.setBorder(border);
500
501                //        if (imageComponent.getParent() !=null)
502                        //              imageComponent.getParent().setBackground(Color.black);
503        }
504
505        private JMenuBar createMenuBar() {
506                JMenuBar bar = new JMenuBar();
507                JButton button;
508                boolean isEditable = !dataset.getFileFormat().isReadOnly();
509
510                JMenu menu = new JMenu("Image", false);
511                menu.setMnemonic('I');
512                bar.add(menu);
513
514                JMenu convertImageMenu = new JMenu("Save Image As");
515                menu.add(convertImageMenu);
516
517                JMenuItem item = new JMenuItem(Tools.FILE_TYPE_JPEG);
518                item.addActionListener(this);
519                item.setActionCommand("Save image as jpeg");
520                convertImageMenu.add(item);
521
522                /*
523                 * ImageIO does not support tiff by default item = new
524                 * JMenuItem(Tools.FILE_TYPE_TIFF); item.addActionListener(this);
525                 * item.setActionCommand("Save image as tiff");
526                 * convertImageMenu.add(item);
527                 */
528
529                item = new JMenuItem(Tools.FILE_TYPE_PNG);
530                item.addActionListener(this);
531                item.setActionCommand("Save image as png");
532                convertImageMenu.add(item);
533
534                item = new JMenuItem(Tools.FILE_TYPE_GIF);
535                item.addActionListener(this);
536                item.setActionCommand("Save image as gif");
537                convertImageMenu.add(item);
538
539                item = new JMenuItem(Tools.FILE_TYPE_BMP);
540                item.addActionListener(this);
541                item.setActionCommand("Save image as bmp");
542                convertImageMenu.add(item);
543
544                menu.addSeparator();
545
546                item = new JMenuItem("Write Selection to Image");
547                item.addActionListener(this);
548                item.setActionCommand("Write selection to image");
549                item.setEnabled(isEditable);
550                rotateRelatedItems.add(item);
551                menu.add(item);
552
553                menu.addSeparator();
554
555                item = new JMenuItem("Change Palette");
556                item.addActionListener(this);
557                item.setActionCommand("Edit palette");
558                item.setEnabled(!isTrueColor);
559                menu.add(item);
560
561                item = new JMenuItem("Import Palette");
562                item.addActionListener(this);
563                item.setActionCommand("Import palette");
564                item.setEnabled(!isTrueColor);
565                menu.add(item);
566
567                item = new JMenuItem("Export Palette");
568                item.addActionListener(this);
569                item.setActionCommand("Export palette");
570                item.setEnabled(!isTrueColor);
571                menu.add(item);
572
573                menu.addSeparator();
574
575                item = new JMenuItem("Set Value Range");
576                item.setEnabled(!isTrueColor);
577                item.addActionListener(this);
578                item.setActionCommand("Set data range");
579                menu.add(item);
580                // no need for byte data
581                // commented out for 2.6. May also need to apply range filter to byte
582                // data.
583                // try {
584                // String cname = data.getClass().getName();
585                // char dname = cname.charAt(cname.lastIndexOf("[")+1);
586                // if (dname == 'B') {
587                // item.setEnabled(false);
588                // }
589                // } catch (Exception ex) {}
590
591                menu.addSeparator();
592
593                item = new JMenuItem("Show Histogram");
594                item.addActionListener(this);
595                item.setActionCommand("Show chart");
596                item.setEnabled(!isTrueColor);
597                rotateRelatedItems.add(item);
598                menu.add(item);
599
600                menu.addSeparator();
601
602                item = new JMenuItem("Zoom In");
603                item.addActionListener(this);
604                item.setActionCommand("Zoom in");
605                menu.add(item);
606
607                item = new JMenuItem("Zoom Out");
608                item.addActionListener(this);
609                item.setActionCommand("Zoom out");
610                menu.add(item);
611
612                menu.addSeparator();
613
614                JMenu imageMenu = new JMenu("Flip");
615                menu.add(imageMenu);
616
617                item = new JMenuItem("Horizontal");
618                item.addActionListener(this);
619                item.setActionCommand("Flip horizontal");
620                imageMenu.add(item);
621
622                item = new JMenuItem("Vertical");
623                item.addActionListener(this);
624                item.setActionCommand("Flip vertical");
625                imageMenu.add(item);
626
627                imageMenu = new JMenu("Rotate Image");
628                menu.add(imageMenu);
629
630                char t = 186;
631                item = new JMenuItem("90" + t + " CW");
632                item.addActionListener(this);
633                item.setActionCommand("Rotate clockwise");
634                imageMenu.add(item);
635
636                item = new JMenuItem("90" + t + " CCW");
637                item.addActionListener(this);
638                item.setActionCommand("Rotate counter clockwise");
639                imageMenu.add(item);
640
641                menu.addSeparator();
642
643                item = new JMenuItem("Brightness/Contrast");
644                item.addActionListener(this);
645                item.setActionCommand("Brightness");
646                menu.add(item);
647
648                JMenu contourMenu = new JMenu("Contour");
649                for (int i = 3; i < 10; i=i+2) {
650                        item = new JMenuItem(String.valueOf(i));
651                        item.addActionListener(this);
652                        item.setActionCommand("Contour " + i);
653                        contourMenu.add(item);
654                }
655                menu.add(contourMenu);
656
657                menu.addSeparator();
658
659                item = new JMenuItem("Show Animation");
660                item.addActionListener(this);
661                item.setActionCommand("Show animation");
662                item.setEnabled(is3D);
663                menu.add(item);
664
665                JMenu animationMenu = new JMenu("Animation (frames/second)");
666                for (int i = 2; i < 12; i = i + 2) {
667                        item = new JMenuItem(String.valueOf(i));
668                        item.addActionListener(this);
669                        item.setActionCommand("Animation speed " + i);
670                        animationMenu.add(item);
671                }
672                animationMenu.setEnabled(is3D);
673                menu.add(animationMenu);
674                menu.addSeparator();
675
676                JCheckBoxMenuItem imageValueCheckBox = new JCheckBoxMenuItem(
677                                "Show Value", false);
678                imageValueCheckBox.addActionListener(this);
679                imageValueCheckBox.setActionCommand("Show image value");
680                imageValueCheckBox.setSelected(ViewProperties.showImageValues());
681                rotateRelatedItems.add(imageValueCheckBox);
682                imageValueCheckBox.setName("showvaluebutton");
683                menu.add(imageValueCheckBox);
684
685                item = new JMenuItem("Show Statistics");
686                item.addActionListener(this);
687                item.setActionCommand("Show statistics");
688                menu.add(item);
689
690                menu.addSeparator();
691
692                item = new JMenuItem("Select All");
693                item.addActionListener(this);
694                item.setActionCommand("Select all data");
695                menu.add(item);
696
697                menu.addSeparator();
698
699                item = new JMenuItem("Close");
700                item.addActionListener(this);
701                item.setActionCommand("Close");
702                menu.add(item);
703
704                bar.add(new JLabel("       "));
705
706                // add icons to the menubar
707
708                Insets margin = new Insets(0, 2, 0, 2);
709
710                // chart button
711                button = new JButton(ViewProperties.getChartIcon());
712                bar.add(button);
713                button.setToolTipText("Histogram");
714                button.setMargin(margin);
715                button.addActionListener(this);
716                button.setActionCommand("Show chart");
717                button.setEnabled(!isTrueColor);
718
719                // palette button
720                button = new JButton(ViewProperties.getPaletteIcon());
721                bar.add(button);
722                button.setToolTipText("Palette");
723                button.setMargin(margin);
724                button.addActionListener(this);
725                button.setActionCommand("Edit palette");
726                button.setEnabled(!isTrueColor);
727
728                // brightness button
729                button = new JButton(ViewProperties.getBrightIcon());
730                bar.add(button);
731                button.setToolTipText("Brightness");
732                button.setMargin(margin);
733                button.addActionListener(this);
734                button.setActionCommand("Brightness");
735
736                // brightness button
737                //        button = new JButton(ViewProperties.getAutocontrastIcon());
738                //        bar.add(button);
739                //        button.setToolTipText("Calculate AutoGain");
740                //        button.setMargin(margin);
741                //        button.addActionListener(this);
742                //        button.setActionCommand("Calculate AutoGain");
743                //        button.setEnabled(ViewProperties.isAutoContrast());
744
745                button = new JButton(ViewProperties.getZoominIcon());
746                bar.add(button);
747                button.addActionListener(this);
748                button.setMargin(margin);
749                button.setActionCommand("Zoom in");
750                button.setToolTipText("Zoom In");
751                button.setName("zoomin");
752
753                // zoom out button
754                button = new JButton(ViewProperties.getZoomoutIcon());
755                bar.add(button);
756                button.setToolTipText("Zoom Out");
757                button.setMargin(margin);
758                button.addActionListener(this);
759                button.setActionCommand("Zoom out");
760                button.setName("zoomout");
761
762                if (is3D) {
763                        bar.add(new JLabel("     "));
764
765                        // first button
766                        button = new JButton(ViewProperties.getFirstIcon());
767                        bar.add(button);
768                        button.setToolTipText("First");
769                        button.setMargin(margin);
770                        button.addActionListener(this);
771                        button.setActionCommand("First page");
772                        button.setName("firstframebutton");
773
774                        // previous button
775                        button = new JButton(ViewProperties.getPreviousIcon());
776                        bar.add(button);
777                        button.setToolTipText("Previous");
778                        button.setMargin(margin);
779                        button.addActionListener(this);
780                        button.setActionCommand("Previous page");
781                        button.setName("prevframebutton");
782
783                        frameField = new JTextField(String.valueOf(curFrame));
784                        frameField.setMaximumSize(new Dimension(50, 30));
785                        bar.add(frameField);
786                        frameField.setMargin(margin);
787                        frameField.addActionListener(this);
788                        frameField.setActionCommand("Go to frame");
789                        frameField.setName("enterFrameField");
790
791                        JLabel tmpField = new JLabel(String.valueOf(maxFrame),
792                                        SwingConstants.CENTER);
793                        tmpField.setMaximumSize(new Dimension(50, 30));
794                        bar.add(tmpField);
795
796                        // next button
797                        button = new JButton(ViewProperties.getNextIcon());
798                        bar.add(button);
799                        button.setToolTipText("Next");
800                        button.setMargin(margin);
801                        button.addActionListener(this);
802                        button.setActionCommand("Next page");
803                        button.setName("nextframebutton");
804
805                        // last button
806                        button = new JButton(ViewProperties.getLastIcon());
807                        bar.add(button);
808                        button.setToolTipText("Last");
809                        button.setMargin(margin);
810                        button.addActionListener(this);
811                        button.setActionCommand("Last page");
812                        button.setName("lastframebutton");
813
814                        button = new JButton(ViewProperties.getAnimationIcon());
815                        bar.add(button);
816                        button.setToolTipText("Animation");
817                        button.setMargin(margin);
818                        button.addActionListener(this);
819                        button.setActionCommand("Show animation");
820
821                }
822
823                return bar;
824        }
825
826        // Implementing DataObserver.
827        private void previousPage() {
828                int rank = dataset.getRank();
829
830                if (rank < 3) {
831                        return;
832                }
833
834                int[] selectedIndex = dataset.getSelectedIndex();
835                long[] selectedDims = dataset.getSelectedDims();
836
837                if (selectedDims[selectedIndex[2]] > 1) {
838                        return; // it is a true color image with three color components
839                }
840
841                long[] start = dataset.getStartDims();
842                long[] dims = dataset.getDims();
843                long idx = start[selectedIndex[2]];
844                if (idx == 0) {
845                        return; // current page is the first page
846                }
847
848                gotoPage(start[selectedIndex[2]] - 1);
849        }
850
851        // Implementing DataObserver.
852        private void nextPage() {
853                int rank = dataset.getRank();
854
855                if (rank < 3) {
856                        return;
857                }
858
859                int[] selectedIndex = dataset.getSelectedIndex();
860                long[] selectedDims = dataset.getSelectedDims();
861
862                if (selectedDims[selectedIndex[2]] > 1) {
863                        return; // it is a true color image with three color components
864                }
865
866                long[] start = dataset.getStartDims();
867                long[] dims = dataset.getDims();
868                long idx = start[selectedIndex[2]];
869                if (idx == dims[selectedIndex[2]] - 1) {
870                        return; // current page is the last page
871                }
872
873                gotoPage(start[selectedIndex[2]] + 1);
874        }
875
876        // Implementing DataObserver.
877        private void firstPage() {
878                int rank = dataset.getRank();
879
880                if (rank < 3) {
881                        return;
882                }
883
884                int[] selectedIndex = dataset.getSelectedIndex();
885                long[] selectedDims = dataset.getSelectedDims();
886
887                if (selectedDims[selectedIndex[2]] > 1) {
888                        return; // it is a true color image with three color components
889                }
890
891                long[] start = dataset.getStartDims();
892                long[] dims = dataset.getDims();
893                long idx = start[selectedIndex[2]];
894                if (idx == 0) {
895                        return; // current page is the first page
896                }
897
898                gotoPage(0);
899        }
900
901        // Implementing DataObserver.
902        private void lastPage() {
903                int rank = dataset.getRank();
904
905                if (rank < 3) {
906                        return;
907                }
908
909                int[] selectedIndex = dataset.getSelectedIndex();
910                long[] selectedDims = dataset.getSelectedDims();
911
912                if (selectedDims[selectedIndex[2]] > 1) {
913                        return; // it is a true color image with three color components
914                }
915
916                long[] start = dataset.getStartDims();
917                long[] dims = dataset.getDims();
918                long idx = start[selectedIndex[2]];
919                if (idx == dims[selectedIndex[2]] - 1) {
920                        return; // current page is the last page
921                }
922
923                gotoPage(dims[selectedIndex[2]] - 1);
924        }
925
926        public Image getImage() {
927                if (image != null) {
928                        return image;
929                }
930
931                int rank = dataset.getRank();
932                if (rank <= 0) {
933                        dataset.init();
934                }
935                isTrueColor = dataset.isTrueColor();
936                is3D = (dataset.getRank() > 2) && !((ScalarDS) dataset).isTrueColor();
937
938                String strValue = null;
939                try {
940                        if (isTrueColor) {
941                                getTrueColorImage();
942                        }
943                        else {
944                                getIndexedImage();
945                        }
946                }
947                catch (Throwable ex) {
948                        toolkit.beep();
949                        JOptionPane.showMessageDialog(this, ex, "ImageView:"+getTitle(),
950                                        JOptionPane.ERROR_MESSAGE);
951                        return null;
952                }
953
954                // set number type, ...
955                if (data != null) {
956                        isUnsigned = dataset.isUnsigned();
957                        String cname = data.getClass().getName();
958                        NT = cname.charAt(cname.lastIndexOf("[") + 1);
959                }
960
961                return image;
962        }
963
964        /**
965         * @throws Exception
966         * @throws OutOfMemoryError
967         */
968        private void getIndexedImage() throws Exception, OutOfMemoryError {
969
970                if (imagePalette==null)
971                        imagePalette = dataset.getPalette();
972
973                boolean noPalette = false;
974                boolean isLocalFile = dataset.getFileFormat().exists();
975
976                if (imagePalette == null) {
977                        noPalette = true;
978                        imagePalette = Tools.createGrayPalette();
979                        viewer.showStatus("\nNo attached palette found, default grey palette is used to display image");
980                }
981
982                data = dataset.getData();
983                if (bitmask != null) {
984                        if (Tools.applyBitmask(data, bitmask, bitmaskOP)) {
985                                doAutoGainContrast = false;
986                        }
987                }
988
989                int typeClass = dataset.getDatatype().getDatatypeClass();
990                if (typeClass == Datatype.CLASS_INTEGER || typeClass == Datatype.CLASS_CHAR) {
991                        data = dataset.convertFromUnsignedC();
992                        isUnsignedConverted = true;
993                        doAutoGainContrast = doAutoGainContrast || 
994                        (ViewProperties.isAutoContrast() && noPalette && isLocalFile);
995                }
996                else
997                        doAutoGainContrast = false;
998
999                boolean isAutoContrastFailed = true;
1000                if (doAutoGainContrast) {
1001                        isAutoContrastFailed = (!computeAutoGainImageData(gainBias,null));
1002                }
1003
1004                int w = dataset.getWidth();
1005                int h = dataset.getHeight();
1006
1007                if (isAutoContrastFailed) {
1008                        doAutoGainContrast = false;
1009                        imageByteData = Tools.getBytes(data, dataRange, w, h, !dataset
1010                                        .isDefaultImageOrder(), dataset.getFilteredImageValues(),
1011                                        convertByteData, imageByteData, invalidValueIndex);
1012                } else if (dataRange!= null && dataRange[0]==dataRange[1]) {
1013                        Tools.findMinMax(data, dataRange, null);
1014                }
1015
1016                image = createIndexedImage(imageByteData, imagePalette, w, h);
1017        }
1018
1019        /**
1020         * @throws Exception
1021         * @throws OutOfMemoryError
1022         */
1023        private void getTrueColorImage() throws Exception, OutOfMemoryError {
1024                isPlaneInterlace = (dataset.getInterlace() == ScalarDS.INTERLACE_PLANE);
1025
1026                long[] selected = dataset.getSelectedDims();
1027                long[] start = dataset.getStartDims();
1028                int[] selectedIndex = dataset.getSelectedIndex();
1029                long[] stride = dataset.getStride();
1030
1031                if (start.length > 2) {
1032                        start[selectedIndex[2]] = 0;
1033                        selected[selectedIndex[2]] = 3;
1034                        stride[selectedIndex[2]] = 1;
1035                }
1036
1037                // reload data
1038                dataset.clearData();
1039                data = dataset.getData();
1040
1041
1042                int w = dataset.getWidth();
1043                int h = dataset.getHeight();
1044
1045                // converts raw data to image data
1046                imageByteData = Tools.getBytes(data, dataRange, w, h, false, dataset.getFilteredImageValues(),
1047                                imageByteData);
1048
1049
1050                image = createTrueColorImage(imageByteData, isPlaneInterlace, w, h);
1051        }
1052
1053        /**
1054         * Compute image data from autogain
1055         * 
1056         * @return
1057         */
1058        private boolean computeAutoGainImageData(double[] gb, double[] range) {
1059                boolean retValue = true;
1060
1061                // data is unsigned short. Convert image byte data using auto-contrast
1062                // image algorithm
1063                boolean isUnsigned = dataset.isUnsigned();
1064
1065                if (gainBias == null) { // calculate auto_gain only once
1066                        gainBias = new double[2];
1067                        Tools.autoContrastCompute(data, gainBias, isUnsigned);
1068                }
1069
1070                if (gb == null)
1071                        gb = gainBias;
1072
1073                autoGainData = Tools.autoContrastApply(data, autoGainData, gb, range, isUnsigned);
1074
1075                if (autoGainData != null) {
1076                        if ((imageByteData == null)
1077                                        || (imageByteData.length != Array.getLength(data))) {
1078                                imageByteData = new byte[Array.getLength(data)];
1079                        }
1080                        retValue = (Tools.autoContrastConvertImageBuffer(autoGainData, imageByteData, true) >= 0);
1081                }
1082                else
1083                        retValue = false;
1084
1085                if (gainBias_current == null)
1086                        gainBias_current = new double[2];
1087
1088                gainBias_current[0] = gb[0];
1089                gainBias_current[1] = gb[1];
1090
1091                return retValue;
1092        }
1093
1094        // implementing ImageObserver
1095        private void zoomIn() {
1096                if (zoomFactor >= 1) {
1097                        zoomTo(zoomFactor + 1.0f);
1098                }
1099                else {
1100                        zoomTo(zoomFactor + 0.125f);
1101                }
1102        }
1103
1104        // implementing ImageObserver
1105        private void zoomOut() {
1106                if (zoomFactor > 1) {
1107                        zoomTo(zoomFactor - 1.0f);
1108                }
1109                else {
1110                        zoomTo(zoomFactor - 0.125f);
1111                }
1112        }
1113
1114        // implementing ImageObserver
1115        private void zoomTo(float zf) {
1116                if (zf > 8)
1117                        zf = 8;
1118                else if (zf < 0.125)
1119                        zf = 0.125f;
1120
1121                if (zoomFactor == zf)
1122                        return; // no change in zooming
1123
1124                        zoomFactor = zf;
1125
1126                        Dimension imageSize = new Dimension(
1127                                        (int) (imageComponent.originalSize.width * zoomFactor),
1128                                        (int) (imageComponent.originalSize.height * zoomFactor));
1129
1130                        this.invalidate();
1131                        imageComponent.invalidate();
1132                        imageComponent.setImageSize(imageSize);
1133                        this.validate();
1134                        // updateUI();
1135
1136                        if ((zoomFactor > 0.99) && (zoomFactor < 1.01)) {
1137                                setTitle(frameTitle);
1138                        }
1139                        else {
1140                                setTitle(frameTitle + " - " + 100 * zoomFactor + "%");
1141                        }
1142        }
1143
1144        // implementing ImageObserver
1145        private void showColorTable() {
1146                if (imagePalette == null) {
1147                        return;
1148                }
1149
1150                String viewName = (String) HDFView.getListOfPaletteView().get(0);
1151
1152                try {
1153                        Class theClass = Class.forName(viewName);
1154                        if ("hdf.view.DefaultPaletteView".equals(viewName)) {
1155                                Object[] initargs = { viewer, this };
1156                                Tools.newInstance(theClass, initargs);
1157                        }
1158                        else {
1159                                Object[] initargs = { this };
1160                                Tools.newInstance(theClass, initargs);
1161                        }
1162                }
1163                catch (Exception ex) {
1164                        viewer.showStatus(ex.toString());
1165                }
1166        }
1167
1168        // implementing ImageObserver
1169        private void showHistogram() {
1170                Rectangle rec = imageComponent.selectedArea;
1171
1172                if (isTrueColor) {
1173                        toolkit.beep();
1174                        JOptionPane
1175                        .showMessageDialog(
1176                                        this,
1177                                        "Unsupported operation: unable to draw histogram for true color image.",
1178                                        getTitle(), JOptionPane.ERROR_MESSAGE);
1179                        return;
1180                }
1181
1182                if ((rec == null) || (rec.getWidth() <= 0) || (rec.getHeight() <= 0)) {
1183                        toolkit.beep();
1184                        JOptionPane
1185                        .showMessageDialog(
1186                                        this,
1187                                        "No data for histogram.\nUse Shift+Mouse_drag to select an image area.",
1188                                        getTitle(), JOptionPane.ERROR_MESSAGE);
1189                        return;
1190                }
1191
1192                double chartData[][] = new double[1][256];
1193                for (int i = 0; i < 256; i++) {
1194                        chartData[0][i] = 0.0;
1195                }
1196
1197                int w = dataset.getWidth();
1198                int x0 = (int) (rec.x / zoomFactor);
1199                int y0 = (int) (rec.y / zoomFactor);
1200                int x = x0 + (int) (rec.width / zoomFactor);
1201                int y = y0 + (int) (rec.height / zoomFactor);
1202                int arrayIndex = 0;
1203                for (int i = y0; i < y; i++) {
1204                        for (int j = x0; j < x; j++) {
1205                                arrayIndex = (int) imageByteData[i * w + j];
1206                                if (arrayIndex < 0) {
1207                                        arrayIndex += 256;
1208                                }
1209                                chartData[0][arrayIndex] += 1.0;
1210                        }
1211                }
1212
1213                /* Use original data range */
1214                double[] xRange = originalRange;
1215                if (xRange == null || xRange[0] == xRange[1]) {
1216                        xRange = new double[2];
1217                        Tools.findMinMax(data, xRange, null);
1218                }
1219
1220                // double[] xRange = {0, 255};
1221
1222                Chart cv = new Chart((JFrame) viewer, "Histogram - "
1223                                + dataset.getPath() + dataset.getName() + " - by pixel index",
1224                                Chart.HISTOGRAM, chartData, xRange, null);
1225                cv.setVisible(true);
1226        }
1227
1228        /**
1229         * Selects all whole image.
1230         * 
1231         * @throws Exception
1232         */
1233        private void selectAll() throws Exception {
1234                imageComponent.selectAll();
1235        }
1236
1237        // implementing ImageObserver
1238        private void flip(int direction) {
1239                ImageFilter filter = new FlipFilter(direction);
1240
1241                if (applyImageFilter(filter)) {
1242                        // taggle flip flag
1243                        if (direction == FLIP_HORIZONTAL) {
1244                                isHorizontalFlipped = !isHorizontalFlipped;
1245                        }
1246                        else {
1247                                isVerticalFlipped = !isVerticalFlipped;
1248                        }
1249                }
1250        }
1251
1252        // implementing ImageObserver
1253        private void rotate(int direction) {
1254                if ( !(direction == ROTATE_CW_90 || direction == ROTATE_CCW_90))
1255                        return;
1256
1257                Rotate90Filter filter = new Rotate90Filter(direction);
1258                applyImageFilter(filter);
1259
1260                if (direction == ROTATE_CW_90) {
1261                        rotateCount++;
1262                        if (rotateCount == 4) {
1263                                rotateCount = 0;
1264                        }
1265                }
1266                else {
1267                        rotateCount--;
1268                        if (rotateCount == -4) {
1269                                rotateCount = 0;
1270                        }
1271                }
1272        }
1273
1274        // implementing ImageObserver
1275        private void contour(int level) {
1276                applyImageFilter(new ContourFilter(level));
1277        }
1278
1279        /** Apply contrast/brightness to unsigned short integer */
1280        private void applyAutoGain(double[] gb, double[] range) {
1281
1282                if (computeAutoGainImageData(gb, range)) {
1283                        int w = dataset.getWidth();
1284                        int h = dataset.getHeight();
1285                        image = createIndexedImage(imageByteData, imagePalette, w, h);
1286                        imageComponent.setImage(image);
1287                        zoomTo(zoomFactor);
1288                }
1289        }
1290
1291        // implementing ImageObserver
1292        private void setValueVisible(boolean b) {
1293                valueField.setVisible(b);
1294                validate();
1295                // updateUI(); //bug !!! on Windows. gives NullPointerException at
1296                // javax.swing.plaf.basic.BasicInternalFrameUI$BorderListener.mousePressed(BasicInternalFrameUI.java:693)
1297        }
1298
1299        /** change alpha value for a given list of pixel locations */
1300        private void adjustAlpha(BufferedImage img, int alpha, List<Integer> idx)
1301        {
1302                if (img==null || idx.size()<=0)
1303                        return;
1304
1305                final int[] pixels = ( (DataBufferInt) img.getRaster().getDataBuffer() ).getData();
1306                int len = pixels.length;
1307
1308                alpha = alpha << 24;
1309                for (Integer i : idx) {
1310                        if (i<len)
1311                                pixels[i] = alpha | (pixels[i] & 0x00ffffff);
1312                }
1313
1314                // for test only
1315                // final int[] pixels = ( (DataBufferInt) img.getRaster().getDataBuffer() ).getData();
1316                // for (int i=0; i<pixels.length/2; i++) pixels[i] = (pixels[i] & 0x60ffffff);
1317        }
1318
1319
1320        /**
1321         * This method returns a buffered image with the contents of an image.
1322         * 
1323         * @param image
1324         *            the plain image object.
1325         * @return buffered image for the given image.
1326         */
1327        private BufferedImage toBufferedImage(Image image) {
1328                if (image == null) {
1329                        return null;
1330                }
1331
1332                if (image instanceof BufferedImage) {
1333                        return (BufferedImage) image;
1334                }
1335
1336                // !!!!!!!!!!!!!!!!!! NOTICE !!!!!!!!!!!!!!!!!!!!!
1337                // the following way of creating a buffered image is using
1338                // Component.createImage(). This method can be used only if the
1339                // component is visible on the screen. Also, this method returns
1340                // buffered images that do not support transparent pixels.
1341                // The buffered image created by this way works for package
1342                // com.sun.image.codec.jpeg.*
1343                // It does not work well with JavaTM Advanced Imaging
1344                // com.sun.media.jai.codec.*;
1345                // if the screen setting is less than 32-bit color
1346                int w = image.getWidth(null);
1347                int h = image.getHeight(null);
1348                BufferedImage bimage = (BufferedImage) createImage(w, h);
1349                Graphics g = bimage.createGraphics();
1350                g.drawImage(image, 0, 0, null);
1351
1352                g.dispose();
1353                return bimage;
1354        }
1355
1356        /**
1357         * Save the image to an image file.
1358         * 
1359         * @param type
1360         *            the image type.
1361         * @throws Exception
1362         */
1363        private void saveImageAs(String type) throws Exception {
1364                if (image == null) {
1365                        return;
1366                }
1367
1368                final JFileChooser fchooser = new JFileChooser(dataset.getFile());
1369                if (type.equals(Tools.FILE_TYPE_JPEG)) {
1370                        fchooser.setFileFilter(DefaultFileFilter.getFileFilterJPEG());
1371                        // } else if (type.equals(Tools.FILE_TYPE_TIFF)) {
1372                        // fchooser.setFileFilter(DefaultFileFilter.getFileFilterTIFF());
1373                }
1374                else if (type.equals(Tools.FILE_TYPE_PNG)) {
1375                        fchooser.setFileFilter(DefaultFileFilter.getFileFilterPNG());
1376                }
1377                else if (type.equals(Tools.FILE_TYPE_GIF)) {
1378                        fchooser.setFileFilter(DefaultFileFilter.getFileFilterGIF());
1379                }
1380                else if (type.equals(Tools.FILE_TYPE_BMP)) {
1381                        fchooser.setFileFilter(DefaultFileFilter.getFileFilterBMP());
1382                }
1383
1384                // fchooser.changeToParentDirectory();
1385                fchooser.setDialogTitle("Save Current Image To " + type + " File --- "
1386                                + dataset.getName());
1387
1388                File choosedFile = new File(dataset.getName() + "."
1389                                + type.toLowerCase());
1390                fchooser.setSelectedFile(choosedFile);
1391
1392                int returnVal = fchooser.showSaveDialog(this);
1393                if (returnVal != JFileChooser.APPROVE_OPTION) {
1394                        return;
1395                }
1396
1397                choosedFile = fchooser.getSelectedFile();
1398                if (choosedFile == null) {
1399                        return;
1400                }
1401                String fname = choosedFile.getAbsolutePath();
1402
1403                if (choosedFile.exists()) {
1404                        int newFileFlag = JOptionPane.showConfirmDialog(this,
1405                                        "File exists. Do you want to replace it ?",
1406                                                        this.getTitle(), JOptionPane.YES_NO_OPTION);
1407                        if (newFileFlag == JOptionPane.NO_OPTION) {
1408                                return;
1409                        }
1410                }
1411
1412                BufferedImage bi = null;
1413                try {
1414                        bi = toBufferedImage(image);
1415                }
1416                catch (OutOfMemoryError err) {
1417                        toolkit.beep();
1418                        JOptionPane.showMessageDialog(this, err.getMessage(), getTitle(),
1419                                        JOptionPane.ERROR_MESSAGE);
1420                        return;
1421                }
1422
1423                Tools.saveImageAs(bi, choosedFile, type);
1424
1425                bi = null;
1426
1427                viewer.showStatus("Current image saved to: " + fname);
1428
1429                try {
1430                        RandomAccessFile rf = new RandomAccessFile(choosedFile, "r");
1431                        long size = rf.length();
1432                        rf.close();
1433                        viewer.showStatus("File size (bytes): " + size);
1434                }
1435                catch (Exception ex) {
1436                        log.debug("File {} size:", choosedFile.getName(), ex);
1437                }
1438        }
1439
1440        public void actionPerformed(ActionEvent e) {
1441                try {
1442                        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1443
1444                        Object source = e.getSource();
1445                        String cmd = e.getActionCommand();
1446
1447                        if (cmd.equals("Close")) {
1448                                dispose(); // terminate the application
1449                                ((Vector) rotateRelatedItems).setSize(0);
1450                        }
1451                        else if (cmd.startsWith("Save image as ")) {
1452                                String filetype = null;
1453                                if (cmd.equals("Save image as jpeg")) {
1454                                        filetype = Tools.FILE_TYPE_JPEG;
1455                                }
1456                                else if (cmd.equals("Save image as tiff")) {
1457                                        filetype = Tools.FILE_TYPE_TIFF;
1458                                }
1459                                else if (cmd.equals("Save image as png")) {
1460                                        filetype = Tools.FILE_TYPE_PNG;
1461                                }
1462                                else if (cmd.equals("Save image as gif")) {
1463                                        filetype = Tools.FILE_TYPE_GIF;
1464                                }
1465                                else if (cmd.equals("Save image as bmp")) {
1466                                        filetype = Tools.FILE_TYPE_BMP;
1467                                }
1468
1469                                try {
1470                                        saveImageAs(filetype);
1471                                }
1472                                catch (Exception ex) {
1473                                        toolkit.beep();
1474                                        JOptionPane.showMessageDialog(this, ex, getTitle(),
1475                                                        JOptionPane.ERROR_MESSAGE);
1476                                }
1477                        }
1478                        else if (cmd.equals("Write selection to image")) {
1479                                if ((getSelectedArea().width <= 0)
1480                                                || (getSelectedArea().height <= 0)) {
1481                                        JOptionPane
1482                                        .showMessageDialog(
1483                                                        this,
1484                                                        "No data to write.\nUse Shift+Mouse_drag to select an image area.",
1485                                                        "HDFView", JOptionPane.INFORMATION_MESSAGE);
1486                                        return;
1487                                }
1488
1489                                TreeView treeView = viewer.getTreeView();
1490                                TreeNode node = treeView.findTreeNode(dataset);
1491                                Group pGroup = (Group) ((DefaultMutableTreeNode) node
1492                                                .getParent()).getUserObject();
1493                                TreeNode root = dataset.getFileFormat().getRootNode();
1494
1495                                if (root == null) {
1496                                        return;
1497                                }
1498
1499                                Vector list = new Vector(dataset.getFileFormat()
1500                                                .getNumberOfMembers() + 5);
1501                                DefaultMutableTreeNode theNode = null;
1502                                Enumeration local_enum = ((DefaultMutableTreeNode) root)
1503                                .depthFirstEnumeration();
1504
1505                                while (local_enum.hasMoreElements()) {
1506                                        theNode = (DefaultMutableTreeNode) local_enum.nextElement();
1507                                        list.add(theNode.getUserObject());
1508                                }
1509
1510                                NewDatasetDialog dialog = new NewDatasetDialog((JFrame) viewer,
1511                                                pGroup, list, this);
1512                                dialog.setVisible(true);
1513
1514                                HObject obj = (HObject) dialog.getObject();
1515                                if (obj != null) {
1516                                        Group pgroup = dialog.getParentGroup();
1517                                        try {
1518                                                treeView.addObject(obj, pgroup);
1519                                        }
1520                                        catch (Exception ex) {
1521                                                log.debug("Write selection to image:", ex);
1522                                        }
1523                                }
1524
1525                                list.setSize(0);
1526                        }
1527                        else if (cmd.equals("Zoom in")) {
1528                                zoomIn();
1529                        }
1530                        else if (cmd.equals("Zoom out")) {
1531                                zoomOut();
1532                        }
1533                        else if (cmd.equals("Edit palette")) {
1534                                showColorTable();
1535                        }
1536                        else if (cmd.equals("Import palette")) {
1537                                JFileChooser fchooser = new JFileChooser(ViewProperties
1538                                                .getWorkDir());
1539                                int returnVal = fchooser.showOpenDialog(this);
1540
1541                                if (returnVal != JFileChooser.APPROVE_OPTION) {
1542                                        return;
1543                                }
1544
1545                                File choosedFile = fchooser.getSelectedFile();
1546                                if (choosedFile == null || choosedFile.isDirectory()) {
1547                                        return;
1548                                }
1549
1550                                Vector<String> palList = ViewProperties.getPaletteList();
1551                                String palPath = choosedFile.getAbsolutePath();
1552                                if(!palList.contains(palList))
1553                                        palList.addElement(palPath);
1554                        }
1555                        else if (cmd.equals("Export palette")) {
1556                                if (imagePalette == null)
1557                                        return;
1558
1559                                String wd =ViewProperties.getWorkDir()+File.separator;
1560                                JFileChooser fchooser = new JFileChooser(wd);
1561                                FileNameExtensionFilter filter = new FileNameExtensionFilter("Color lookup table", "lut");
1562                                File pfile = Tools.checkNewFile(wd, ".lut");
1563                                fchooser.setSelectedFile(pfile);
1564                                fchooser.setFileFilter(filter);
1565                                int returnVal = fchooser.showOpenDialog(this);
1566
1567                                if (returnVal != JFileChooser.APPROVE_OPTION) {
1568                                        return;
1569                                }
1570
1571                                File choosedFile = fchooser.getSelectedFile();
1572                                if (choosedFile == null || choosedFile.isDirectory()) {
1573                                        return;
1574                                }
1575
1576                                if (choosedFile.exists())
1577                                {
1578                                        int newFileFlag = JOptionPane.showConfirmDialog(this,
1579                                                        "File exists. Do you want to replace it ?",
1580                                                                        this.getTitle(),
1581                                                                        JOptionPane.YES_NO_OPTION);
1582                                        if (newFileFlag == JOptionPane.NO_OPTION) {
1583                                                return;
1584                                        }
1585                                }
1586
1587                                PrintWriter out = null;
1588
1589                                try {
1590                                        out = new PrintWriter(new BufferedWriter(new FileWriter(choosedFile)));
1591                                } 
1592                                catch (Exception ex) { 
1593                                        out = null; 
1594                                }
1595
1596                                if (out == null)
1597                                        return;
1598
1599                                int cols = 3;
1600                                int rows = 256;
1601                                int rgb = 0;
1602                                for (int i=0; i<rows; i++) {
1603                                        out.print(i);
1604                                        for (int j=0; j<cols; j++) {
1605                                                out.print(' ');
1606                                                rgb = imagePalette[j][i];
1607                                                if (rgb<0) rgb += 256;
1608                                                out.print(rgb);
1609                                        }
1610                                        out.println();
1611                                }
1612
1613                                out.flush();
1614                                out.close();
1615                        }
1616                        else if (cmd.equals("Set data range")) {
1617
1618                                if (originalRange==null || originalRange[0]== originalRange[1])
1619                                        return;
1620
1621                                // call only once
1622                                if (dataDist == null) {
1623                                        dataDist = new int[256];
1624                                        Tools.findDataDist(data, dataDist, originalRange);
1625                                }
1626
1627                                DataRangeDialog drd = new DataRangeDialog((JFrame) viewer, dataRange, originalRange,dataDist);
1628                                double[] drange = drd.getRange();
1629
1630                                if ((drange == null)
1631                                                || (drange[0] == drange[1])
1632                                                || ((drange[0] == dataRange[0]) && (drange[1] == dataRange[1]))) {
1633                                        return;
1634                                }
1635
1636                                applyDataRange(drange);
1637                        }
1638                        else if (cmd.equals("Flip horizontal")) {
1639                                flip(FLIP_HORIZONTAL);
1640                        }
1641                        else if (cmd.equals("Flip vertical")) {
1642                                flip(FLIP_VERTICAL);
1643                        }
1644                        else if (cmd.startsWith("Rotate")) {
1645                                if (cmd.equals("Rotate clockwise"))
1646                                        rotate(ROTATE_CW_90);
1647                                else
1648                                        rotate(ROTATE_CCW_90);
1649
1650                                int n = rotateRelatedItems.size();
1651                                for (int i = 0; i < n; i++) {
1652                                        boolean itemState = (rotateCount == 0);
1653                                        ((javax.swing.JComponent) rotateRelatedItems.get(i))
1654                                        .setEnabled(itemState);
1655                                }
1656                        }
1657                        else if (cmd.equals("Show image value")) {
1658                                boolean b = ((JCheckBoxMenuItem) source).getState();
1659                                setValueVisible(b);
1660                        }
1661                        else if (cmd.startsWith("Go to frame")) {
1662                                int page = 0;
1663                                try {
1664                                        page = Integer.parseInt(frameField.getText().trim())-indexBase;
1665                                }
1666                                catch (Exception ex) {
1667                                        page = -1;
1668                                }
1669
1670                                gotoPage (page);
1671                        }
1672                        else if (cmd.startsWith("Show animation")) {
1673                                setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1674                                new Animation((JFrame) viewer, dataset);
1675                                setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1676                        }
1677                        else if (cmd.startsWith("Animation speed")) {
1678                                animationSpeed = Integer.parseInt((cmd
1679                                                .substring(cmd.length() - 2)).trim());
1680                        }
1681
1682                        else if (cmd.startsWith("Contour")) {
1683                                int level = Integer.parseInt(cmd.substring(cmd.length() - 1));
1684                                contour(level);
1685                        }
1686                        else if (cmd.startsWith("Brightness")) {
1687                                if (contrastSlider == null) {
1688                                        contrastSlider = new ContrastSlider((JFrame) viewer, image.getSource());
1689                                }
1690                                contrastSlider.setVisible(true);
1691                        }
1692                        else if (cmd.equals("Show chart")) {
1693                                showHistogram();
1694                        }
1695                        else if (cmd.equals("First page")) {
1696                                firstPage();
1697                        }
1698                        else if (cmd.equals("Previous page")) {
1699                                previousPage();
1700                        }
1701                        else if (cmd.equals("Next page")) {
1702                                nextPage();
1703                        }
1704                        else if (cmd.equals("Last page")) {
1705                                lastPage();
1706                        }
1707                        else if (cmd.equals("Show statistics")) {
1708                                try {
1709                                        double[] minmax = new double[2];
1710                                        double[] stat = new double[2];
1711
1712                                        Object theData = null;
1713                                        theData = getSelectedData();
1714
1715                                        if (theData == null) {
1716                                                theData = data;
1717                                        }
1718
1719                                        Tools.findMinMax(theData, minmax, dataset.getFillValue());
1720                                        if (Tools.computeStatistics(theData, stat, dataset.getFillValue()) > 0) {
1721                                                String statistics = "Min                      = "
1722                                                + minmax[0] + "\nMax                      = "
1723                                                + minmax[1] + "\nMean                     = "
1724                                                + stat[0] + "\nStandard deviation = " + stat[1];
1725                                                JOptionPane.showMessageDialog(this, statistics,
1726                                                                "Statistics", JOptionPane.INFORMATION_MESSAGE);
1727                                        }
1728                                }
1729                                catch (Exception ex) {
1730                                        toolkit.beep();
1731                                        JOptionPane.showMessageDialog((JFrame) viewer, ex,
1732                                                        getTitle(), JOptionPane.ERROR_MESSAGE);
1733                                }
1734                        }
1735                        else if (cmd.equals("Select all data")) {
1736                                try {
1737                                        selectAll();
1738                                }
1739                                catch (Exception ex) {
1740                                        toolkit.beep();
1741                                        JOptionPane.showMessageDialog((JFrame) viewer, ex,
1742                                                        getTitle(), JOptionPane.ERROR_MESSAGE);
1743                                }
1744                        }
1745                }
1746                finally {
1747                        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1748                }
1749
1750        }
1751
1752        public void dispose() {
1753                // reload the data when it is displayed next time
1754                // because the display type (table or image) may be
1755                // different.
1756                if (!dataset.isImage()) {
1757                        dataset.clearData();
1758                }
1759
1760                data = null;
1761                image = null;
1762                imageByteData = null;
1763                imageComponent = null;
1764                autoGainData = null;
1765                ((Vector) rotateRelatedItems).setSize(0);
1766                System.runFinalization();
1767                System.gc();
1768
1769                viewer.removeDataView(this);
1770
1771                super.dispose();
1772        }
1773
1774        // Implementing DataView.
1775        public HObject getDataObject() {
1776                return dataset;
1777        }
1778
1779        public byte[] getImageByteData() {
1780                return imageByteData;
1781        }
1782
1783        /**
1784         * Returns the selected data values.
1785         * 
1786         * @return the selected data object.
1787         */
1788        public Object getSelectedData() {
1789                Object selectedData = null;
1790
1791                int cols = imageComponent.originalSelectedArea.width;
1792                int rows = imageComponent.originalSelectedArea.height;
1793
1794                if ((cols <= 0) || (rows <= 0)) {
1795                        return null; // no data is selected
1796                }
1797
1798                int size = cols * rows;
1799                if (isTrueColor) {
1800                        size *= 3;
1801                }
1802
1803                if (NT == 'B') {
1804                        selectedData = new byte[size];
1805                }
1806                else if (NT == 'S') {
1807                        selectedData = new short[size];
1808                }
1809                else if (NT == 'I') {
1810                        selectedData = new int[size];
1811                }
1812                else if (NT == 'J') {
1813                        selectedData = new long[size];
1814                }
1815                else if (NT == 'F') {
1816                        selectedData = new float[size];
1817                }
1818                else if (NT == 'D') {
1819                        selectedData = new double[size];
1820                }
1821                else {
1822                        return null;
1823                }
1824
1825                int r0 = imageComponent.originalSelectedArea.y;
1826                int c0 = imageComponent.originalSelectedArea.x;
1827                int w = imageComponent.originalSize.width;
1828                int h = imageComponent.originalSize.height;
1829
1830                // transfer location to the original coordinator
1831                if (isHorizontalFlipped) {
1832                        c0 = w - 1 - c0 - cols;
1833                }
1834
1835                if (isVerticalFlipped) {
1836                        r0 = h - 1 - r0 - rows;
1837                }
1838
1839                int idx_src = 0, idx_dst = 0;
1840                if (isTrueColor) {
1841                        int imageSize = w * h;
1842                        if (isPlaneInterlace) {
1843                                for (int j = 0; j < 3; j++) {
1844                                        int plane = imageSize * j;
1845                                        for (int i = 0; i < rows; i++) {
1846                                                idx_src = plane + (r0 + i) * w + c0;
1847                                                System.arraycopy(data, idx_src, selectedData, idx_dst,
1848                                                                cols);
1849                                                idx_dst += cols;
1850                                        }
1851                                }
1852                        }
1853                        else {
1854                                int numberOfDataPoints = cols * 3;
1855                                for (int i = 0; i < rows; i++) {
1856                                        idx_src = (r0 + i) * w + c0;
1857                                        System.arraycopy(data, idx_src * 3, selectedData, idx_dst,
1858                                                        numberOfDataPoints);
1859                                        idx_dst += numberOfDataPoints;
1860                                }
1861                        }
1862                }
1863                else { // indexed image
1864                        for (int i = 0; i < rows; i++) {
1865                                idx_src = (r0 + i) * w + c0;
1866                                System.arraycopy(data, idx_src, selectedData, idx_dst, cols);
1867                                idx_dst += cols;
1868                        }
1869                }
1870
1871                return selectedData;
1872        }
1873
1874        /**
1875         * returns the selected area of the image
1876         * 
1877         * @return the rectangle of the selected image area.
1878         */
1879        public Rectangle getSelectedArea() {
1880                return imageComponent.originalSelectedArea;
1881        }
1882
1883        /** @return true if the image is a truecolor image. */
1884        public boolean isTrueColor() {
1885                return isTrueColor;
1886        }
1887
1888        /** @return true if the image interlace is plance interlace. */
1889        public boolean isPlaneInterlace() {
1890                return isPlaneInterlace;
1891        }
1892
1893        public void setImage(Image img) {
1894                image = img;
1895                imageComponent.setImage(img);
1896
1897                setImageDirection();
1898        }
1899
1900        private void setImageDirection() {
1901                boolean isHF = isHorizontalFlipped;
1902                boolean isVF = isVerticalFlipped;
1903                int rc = rotateCount;
1904
1905                if (isHF || isVF || rc!=0) {
1906                        isHorizontalFlipped = false;
1907                        isVerticalFlipped = false;
1908                        rotateCount = 0;        
1909
1910                        if (isHF)
1911                                flip(FLIP_HORIZONTAL);
1912
1913                        if (isVF)
1914                                flip(FLIP_VERTICAL);
1915
1916                        while (rc > 0)  {
1917                                rotate(ROTATE_CW_90);
1918                                rc--;
1919                        }
1920
1921                        while (rc < 0)  {
1922                                rotate(ROTATE_CCW_90);
1923                                rc++;
1924                        }
1925
1926                } 
1927                else {
1928                        if (origin == 1)
1929                                flip(FLIP_VERTICAL);
1930                        else if (origin == 2)
1931                                flip(FLIP_HORIZONTAL);
1932                        if (origin == 3) {
1933                                rotate(ROTATE_CW_90);
1934                                rotate(ROTATE_CW_90);
1935                        }
1936                }
1937
1938                zoomTo(zoomFactor);        
1939        }
1940
1941        public byte[][] getPalette() {
1942                return imagePalette;
1943        }
1944
1945        public void setPalette(byte[][] pal) {
1946                imagePalette = pal;
1947                paletteComponent.updatePalette(pal);
1948        }
1949
1950        private void gotoPage(long idx) {
1951                if (dataset.getRank() < 3 ||
1952                                idx == (curFrame-indexBase) ) {
1953                        return;
1954                }
1955
1956                long[] start = dataset.getStartDims();
1957                int[] selectedIndex = dataset.getSelectedIndex();
1958                long[] dims = dataset.getDims();
1959
1960                if ((idx < 0) || (idx >= dims[selectedIndex[2]])) {
1961                        toolkit.beep();
1962                        JOptionPane.showMessageDialog(this,
1963                                        "Frame number must be between "+indexBase+" and "
1964                                        + (dims[selectedIndex[2]] - 1+indexBase), getTitle(),
1965                                        JOptionPane.ERROR_MESSAGE);
1966                        return;
1967                }
1968
1969                setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1970
1971                start[selectedIndex[2]] = idx;
1972                curFrame = idx+indexBase;
1973                dataset.clearData();
1974                image = null;
1975                gainBias = null;
1976                imageComponent.setImage(getImage());
1977                frameField.setText(String.valueOf(curFrame));
1978
1979                isHorizontalFlipped = false;
1980                isVerticalFlipped = false;
1981                rotateCount = 0;        
1982
1983                if (origin == 1)
1984                        flip(FLIP_VERTICAL);
1985                else if (origin == 2)
1986                        flip(FLIP_HORIZONTAL);
1987                if (origin == 3) {
1988                        rotate(ROTATE_CW_90);
1989                        rotate(ROTATE_CW_90);
1990                }
1991
1992                setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1993
1994                updateUI();
1995        }
1996
1997        /**
1998         * Creates a RGB indexed image of 256 colors.
1999         * 
2000         * @param imageData
2001         *            the byte array of the image data.
2002         * @param palette
2003         *            the color lookup table.
2004         * @param w
2005         *            the width of the image.
2006         * @param h
2007         *            the height of the image.
2008         * @return the image.
2009         */
2010        private Image createIndexedImage(byte[] imageData, byte[][] palette, int w, int h) 
2011        {
2012                bufferedImage = (BufferedImage)Tools.createIndexedImage(bufferedImage, imageData, palette, w, h);
2013                adjustAlpha(bufferedImage, 0, invalidValueIndex);        
2014
2015                return bufferedImage;
2016        }
2017
2018        /**
2019         * Creates a true color image.
2020         * <p>
2021         * The data may be arranged in one of two ways: by pixel or by plane. In
2022         * both cases, the dataset will have a dataspace with three dimensions,
2023         * height, width, and components.
2024         * <p>
2025         * For HDF4, the interlace modes specify orders for the dimensions as:
2026         * 
2027         * <pre>
2028         * INTERLACE_PIXEL = [width][height][pixel components]
2029         *            INTERLACE_PLANE = [pixel components][width][height]
2030         * </pre>
2031         * <p>
2032         * For HDF5, the interlace modes specify orders for the dimensions as:
2033         * 
2034         * <pre>
2035         * INTERLACE_PIXEL = [height][width][pixel components]
2036         *            INTERLACE_PLANE = [pixel components][height][width]
2037         * </pre>
2038         * <p>
2039         * 
2040         * @param imageData
2041         *            the byte array of the image data.
2042         * @param planeInterlace
2043         *            flag if the image is plane intelace.
2044         * @param w
2045         *            the width of the image.
2046         * @param h
2047         *            the height of the image.
2048         * @return the image.
2049         */
2050        private Image createTrueColorImage(byte[] imageData, boolean planeInterlace,
2051                        int w, int h) 
2052        {
2053
2054                if (bufferedImage == null)
2055                        bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
2056
2057                final int[] pixels = ( (DataBufferInt) bufferedImage.getRaster().getDataBuffer() ).getData();
2058                int len = pixels.length;
2059
2060                int idx = 0, r = 0, g = 0, b = 0;
2061                for (int i = 0; i < h; i++) {
2062                        for (int j = 0; j < w; j++) {
2063                                if (planeInterlace) {
2064                                        r = ((int)imageData[idx] & 0xff)<<16;
2065                                        g = ((int)imageData[len + idx] & 0xff)<<8;
2066                                        b = ((int)imageData[len * 2 + idx] & 0xff);
2067                                }
2068                                else {
2069                                        r = ((int)imageData[idx * 3] & 0xff)<<16;
2070                                        g = ((int)imageData[idx * 3 + 1] & 0xff)<<8;
2071                                        b = ((int)imageData[idx * 3 + 2] & 0xff);
2072                                }
2073                                pixels[idx++] = 0xff000000 | r | g | b;
2074                        } 
2075                } 
2076
2077                adjustAlpha(bufferedImage, 0, invalidValueIndex);        
2078                return bufferedImage;
2079        }
2080
2081        private boolean applyImageFilter(ImageFilter filter) {
2082                boolean status = true;
2083                ImageProducer imageProducer = image.getSource();
2084
2085                try {
2086                        image = createImage(new FilteredImageSource(imageProducer, filter));
2087                        imageComponent.setImage(image);
2088                        zoomTo(zoomFactor);
2089                }
2090                catch (Throwable err) {
2091                        toolkit.beep();
2092                        JOptionPane.showMessageDialog(this, err.getMessage(), getTitle(),
2093                                        JOptionPane.ERROR_MESSAGE);
2094                        status = false;
2095                }
2096
2097                return status;
2098        }
2099
2100        private void applyDataRange(double[] newRange) {
2101                if (doAutoGainContrast && gainBias!= null) {
2102                        applyAutoGain(gainBias_current, newRange);
2103                } 
2104                else {
2105                        int w = dataset.getWidth();
2106                        int h = dataset.getHeight();
2107
2108                        invalidValueIndex.clear(); // data range changed. need to reset invalid values
2109                        imageByteData = Tools.getBytes(data, newRange, w, h, !dataset
2110                                        .isDefaultImageOrder(), dataset.getFilteredImageValues(), true,
2111                                        null, invalidValueIndex);
2112
2113                        image = createIndexedImage(imageByteData, imagePalette, w, h);
2114                        setImage(image);
2115                        zoomTo(zoomFactor);
2116                        paletteComponent.updateRange(newRange);
2117                }
2118
2119                dataRange[0] = newRange[0];
2120                dataRange[1] = newRange[1];
2121        }
2122
2123        /** PaletteComponent draws the palette on the side of the image. */
2124        private class PaletteComponent extends JComponent {
2125                private static final long serialVersionUID = -5194383032992628565L;
2126                private Color[] colors = null;
2127                private double[] pixelData = null;
2128                private Dimension paintSize = null;
2129                java.text.DecimalFormat format;
2130                double[] dRange = null;
2131
2132                private PaletteComponent(byte[][] palette, double[] range) {
2133                        paintSize = new Dimension(25, 2);
2134                        format = new java.text.DecimalFormat("0.00E0");
2135                        dRange = range;
2136                        double unsigned_celling = 0;
2137
2138                        if ((palette != null) && (range != null)) {
2139                                double ratio = (dRange[1] - dRange[0]) / 255;
2140
2141                                pixelData = new double[256];
2142                                for (int i = 0; i < 256; i++) {
2143                                        pixelData[i] = (dRange[0] + ratio * i);
2144                                }
2145                        }
2146
2147                        updatePalette(palette);
2148
2149                        setPreferredSize(new Dimension(paintSize.width + 60,
2150                                        paintSize.height * 256));
2151                        setVisible(true);
2152                }
2153
2154                private void updatePalette(byte[][] palette) {
2155                        if ((palette != null) && (dRange != null)) {
2156                                colors = new Color[256];
2157
2158                                int r, g, b;
2159                                for (int i = 0; i < 256; i++) {
2160                                        r = (int) palette[0][i];
2161                                        if (r < 0) {
2162                                                r += 256;
2163                                        }
2164                                        g = (int) palette[1][i];
2165                                        if (g < 0) {
2166                                                g += 256;
2167                                        }
2168                                        b = (int) palette[2][i];
2169                                        if (b < 0) {
2170                                                b += 256;
2171                                        }
2172
2173                                        colors[i] = new Color(r, g, b);
2174                                }
2175                        }
2176
2177                        repaint();
2178                }
2179
2180                private void updateRange(double[] newRange) {
2181                        if (newRange == null) {
2182                                return;
2183                        }
2184
2185                        dRange = newRange;
2186                        double ratio = (dRange[1] - dRange[0]) / 255;
2187                        for (int i = 0; i < 256; i++) {
2188                                pixelData[i] = (dRange[0] + ratio * i);
2189                        }
2190
2191                        repaint();
2192                }
2193
2194                public void paint(Graphics g) {
2195                        if ((colors == null) && (pixelData == null)) {
2196                                return;
2197                        }
2198
2199                        Font font = g.getFont();
2200                        g.setFont(new Font(font.getName(), font.getStyle(), 12));
2201                        for (int i = 0; i < 256; i++) {
2202                                g.setColor(colors[i]);
2203                                g.fillRect(0, paintSize.height * i, paintSize.width,
2204                                                paintSize.height);
2205                        }
2206
2207                        g.setColor(Color.black);
2208                        for (int i = 0; i < 25; i++) {
2209                                g.drawString(format.format(pixelData[i * 10]),
2210                                                paintSize.width + 5, 10 + paintSize.height * i * 10);
2211                        }
2212                        g.drawString(format.format(pixelData[255]), paintSize.width + 5,
2213                                        paintSize.height * 255);
2214                }
2215        }
2216
2217        /** ImageComponent draws the image. */
2218        private class ImageComponent extends JComponent implements MouseListener,
2219        MouseMotionListener, MouseWheelListener {
2220                private static final long serialVersionUID = -2690648149547151532L;
2221                private Dimension originalSize, imageSize;
2222                private Image image;
2223                private Point startPosition, currentPosition; // mouse clicked position
2224                private Rectangle selectedArea, originalSelectedArea;
2225                private StringBuffer strBuff; // to hold display value
2226                private int yMousePosition = 0; /*
2227                 * the vertical position of the current
2228                 * mouse
2229                 */
2230                private Dimension scrollDim = null;
2231                private JScrollBar hbar = null;
2232                private JScrollBar vbar = null;
2233
2234                private ImageComponent(Image img) {
2235                        image = img;
2236                        imageSize = new Dimension(image.getWidth(this), image
2237                                        .getHeight(this));
2238                        originalSize = imageSize;
2239                        selectedArea = new Rectangle();
2240                        originalSelectedArea = new Rectangle();
2241                        setPreferredSize(imageSize);
2242                        strBuff = new StringBuffer();
2243
2244                        addMouseListener(this);
2245                        addMouseMotionListener(this);
2246                        addMouseWheelListener(this);
2247                }
2248
2249                public void paint(Graphics g) {
2250                        if (g instanceof Graphics2D && (zoomFactor<0.99)) {
2251                                Graphics2D g2 = (Graphics2D) g;
2252
2253                                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
2254                                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
2255                                Image scaledImg = multiBiliner(image, imageSize.width, imageSize.height, true);
2256                                g2.drawImage(scaledImg, 0, 0, imageSize.width, imageSize.height, this);
2257
2258                        } 
2259                        else
2260                                g.drawImage(image, 0, 0, imageSize.width, imageSize.height, this);
2261
2262                        if ((selectedArea.width > 0) && (selectedArea.height > 0)) {
2263                                g.setColor(Color.red);
2264                                g.drawRect(selectedArea.x, selectedArea.y, selectedArea.width,
2265                                                selectedArea.height);
2266                        }
2267                }
2268
2269                /**
2270                 * Create an image using multiple step bilinear, see details at
2271                 * http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
2272                 *
2273                 * @param img the original image to be scaled
2274                 * @param targetWidth the desired width of the scaled instance
2275                 * @param targetHeight the desired height of the scaled instance,
2276                 * @return a scaled version of the original 
2277                 */
2278                private Image multiBiliner(Image img, int targetWidth, int targetHeight, boolean highquality)
2279                {
2280                        Image ret = img;
2281                        int w = img.getWidth(null)/2;
2282                        int h = img.getHeight(null)/2;
2283
2284                        // only do multiple step bilinear for down scale more than two times
2285                        if (!highquality || w <=targetWidth || h <=targetHeight)
2286                                return ret;
2287
2288                        int type = BufferedImage.TYPE_INT_RGB;
2289                        if (image instanceof BufferedImage) {
2290                                BufferedImage tmp = (BufferedImage)image;
2291                                if (tmp.getColorModel().hasAlpha())
2292                                        type = BufferedImage.TYPE_INT_ARGB;
2293                        } 
2294                        else {
2295                                PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
2296                                ColorModel cm = pg.getColorModel();
2297                                if (cm!=null && cm.hasAlpha())
2298                                        type = BufferedImage.TYPE_INT_ARGB;
2299                        }
2300
2301                        do {
2302                                BufferedImage tmp = new BufferedImage(w, h, type);
2303                                Graphics2D g2 = tmp.createGraphics();
2304                                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
2305                                g2.drawImage(ret, 0, 0, w, h, null);
2306                                g2.dispose();
2307                                ret = tmp;
2308
2309                                w /= 2;
2310                                if (w < targetWidth) {
2311                                        w = targetWidth;
2312                                }
2313
2314                                h /= 2;
2315                                if (h < targetHeight) {
2316                                        h = targetHeight;
2317                                }
2318
2319                        } while (w != targetWidth || h != targetHeight);
2320
2321                        return ret;
2322                }
2323                public void mousePressed(MouseEvent e) {
2324                        startPosition = e.getPoint();
2325                        selectedArea.setBounds(startPosition.x, startPosition.y, 0, 0);
2326                        scrollDim = imageScroller.getSize();
2327                        hbar = imageScroller.getHorizontalScrollBar();
2328                        vbar = imageScroller.getVerticalScrollBar();
2329
2330                        if ((e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK) {
2331                                setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
2332                        }
2333                        else {
2334                                setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
2335                        }
2336                }
2337
2338                public void mouseClicked(MouseEvent e) {
2339                        startPosition = e.getPoint();
2340                        selectedArea.setBounds(startPosition.x, startPosition.y, 0, 0);
2341
2342                        if (hbar.isVisible()) {
2343                                hbar.setValue(startPosition.x - scrollDim.width / 2);
2344                        }
2345
2346                        if (vbar.isVisible()) {
2347                                vbar.setValue(startPosition.y - scrollDim.height / 2);
2348                        }
2349
2350                        repaint();
2351                }
2352
2353                public void mouseDragged(MouseEvent e) {
2354                        // don't update too often.
2355                        try {
2356                                Thread.sleep(20);
2357                        }
2358                        catch (Exception ex) {
2359                                log.debug("thread sleep:", ex);
2360                        }
2361                        currentPosition = e.getPoint();
2362
2363                        if ((e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK) {
2364                                int x0 = Math.max(0, Math.min(startPosition.x,
2365                                                currentPosition.x));
2366                                int y0 = Math.max(0, Math.min(startPosition.y,
2367                                                currentPosition.y));
2368                                int x1 = Math.min(imageSize.width, Math.max(startPosition.x,
2369                                                currentPosition.x));
2370                                int y1 = Math.min(imageSize.height, Math.max(startPosition.y,
2371                                                currentPosition.y));
2372
2373                                int w = x1 - x0;
2374                                int h = y1 - y0;
2375
2376                                selectedArea.setBounds(x0, y0, w, h);
2377                                double ratio = 1.0 / zoomFactor;
2378
2379                                originalSelectedArea.setBounds((int) (x0 * ratio),
2380                                                (int) (y0 * ratio), (int) (w * ratio),
2381                                                (int) (h * ratio));
2382
2383                                repaint();
2384                        }
2385                        else {
2386                                if (hbar.isVisible()) {
2387                                        int dx = startPosition.x - currentPosition.x;
2388                                        hbar.setValue(hbar.getValue() + dx);
2389                                }
2390
2391                                if (vbar.isVisible()) {
2392                                        int dy = startPosition.y - currentPosition.y;
2393                                        vbar.setValue(vbar.getValue() + dy);
2394                                }
2395                        }
2396                }
2397
2398                public void mouseReleased(MouseEvent e) {
2399                        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
2400                }
2401
2402                public void mouseEntered(MouseEvent e) {
2403                }
2404
2405                public void mouseExited(MouseEvent e) {
2406                }
2407
2408                public void mouseMoved(MouseEvent e) {
2409                        yMousePosition = e.getY();
2410                        showPixelValue(e.getX(), yMousePosition);
2411                }
2412
2413                public void mouseWheelMoved(MouseWheelEvent e) {
2414                        JScrollBar jb = imageScroller.getVerticalScrollBar();
2415                        int us = e.getUnitsToScroll();
2416                        int wr = e.getWheelRotation();
2417                        int n = us * jb.getUnitIncrement();
2418                        int y = jb.getValue();
2419
2420                        if (((y <= 0) && (wr < 0))
2421                                        || (y + jb.getVisibleAmount() * wr >= zoomFactor
2422                                                        * originalSize.height)) {
2423                                return;
2424                        }
2425
2426                        yMousePosition += n;
2427                        jb.setValue(jb.getValue() + n);
2428
2429                        showPixelValue(e.getX(), yMousePosition);
2430                }
2431
2432                private void showPixelValue(int x, int y) {
2433                        if (!valueField.isVisible() || rotateCount != 0) {
2434                                return;
2435                        }
2436
2437                        if (data == null) {
2438                                return;
2439                        }
2440
2441                        x = (int) (x / zoomFactor);
2442                        int w = originalSize.width;
2443
2444                        if ((x < 0) || (x >= w)) {
2445                                return; // out of image bound
2446                        }
2447
2448                        y = (int) (y / zoomFactor);
2449                        int h = originalSize.height;
2450                        if ((y < 0) || (y >= h)) {
2451                                return; // out of image bound
2452                        }
2453
2454                        // transfer location to the original coordinator
2455                        if (isHorizontalFlipped) {
2456                                x = w - 1 - x;
2457                        }
2458
2459                        if (isVerticalFlipped) {
2460                                y = h - 1 - y;
2461                        }
2462
2463                        strBuff.setLength(0); // reset the string buffer
2464                        strBuff.append("x=");
2465                        strBuff.append(x+indexBase);
2466                        strBuff.append(",   y=");
2467                        strBuff.append(y+indexBase);
2468                        strBuff.append(",   value=");
2469
2470                        if (isTrueColor) {
2471                                strBuff.append("(");
2472                                int i0, i1, i2;
2473                                String r, g, b;
2474
2475                                if (isPlaneInterlace) {
2476                                        i0 = y * w + x; // index for the first plane
2477                                        i1 = i0 + w * h; // index for the second plane
2478                                        i2 = i0 + 2 * w * h; // index for the third plane
2479                                }
2480                                else {
2481                                        i0 = 3 * (y * w + x); // index for the first pixel
2482                                        i1 = i0 + 1; // index for the second pixel
2483                                        i2 = i0 + 2; // index for the third pixel
2484                                }
2485
2486                                if (isUnsigned && !isUnsignedConverted) {
2487                                        r = String.valueOf(convertUnsignedPoint(i0));
2488                                        g = String.valueOf(convertUnsignedPoint(i1));
2489                                        b = String.valueOf(convertUnsignedPoint(i2));
2490                                }
2491                                else {
2492                                        r = String.valueOf(Array.get(data, i0));
2493                                        g = String.valueOf(Array.get(data, i1));
2494                                        b = String.valueOf(Array.get(data, i2));
2495                                }
2496
2497                                strBuff.append(r + ", " + g + ", " + b);
2498                                strBuff.append(")");
2499                        } // if (isTrueColor)
2500                        else {
2501
2502                                int idx = y * w + x;
2503                                if (!dataset.isDefaultImageOrder())
2504                                        idx = x*h+y;
2505
2506                                if (isUnsigned && !isUnsignedConverted) {
2507                                        strBuff.append(convertUnsignedPoint(idx));
2508                                }
2509                                else {
2510                                        strBuff.append(Array.get(data, idx));
2511                                }
2512                        }
2513
2514                        valueField.setText(strBuff.toString());
2515                } // private void showPixelValue
2516
2517                private void selectAll() {
2518                        selectedArea.setBounds(0, 0, imageSize.width, imageSize.height);
2519                        originalSelectedArea.setBounds(0, 0, originalSize.width,
2520                                        originalSize.height);
2521                        repaint();
2522                }
2523
2524                private long convertUnsignedPoint(int idx) {
2525                        long l = 0;
2526
2527                        if (NT == 'B') {
2528                                byte b = Array.getByte(data, idx);
2529
2530                                if (b < 0) {
2531                                        l = b + 256;
2532                                }
2533                                else {
2534                                        l = b;
2535                                }
2536                        }
2537                        else if (NT == 'S') {
2538                                short s = Array.getShort(data, idx);
2539                                if (s < 0) {
2540                                        l = s + 65536;
2541                                }
2542                                else {
2543                                        l = s;
2544                                }
2545                        }
2546                        else if (NT == 'I') {
2547                                int i = Array.getInt(data, idx);
2548                                if (i < 0) {
2549                                        l = i + 4294967296L;
2550                                }
2551                                else {
2552                                        l = i;
2553                                }
2554                        }
2555
2556                        return l;
2557                }
2558
2559                private void setImageSize(Dimension size) {
2560                        imageSize = size;
2561                        setPreferredSize(imageSize);
2562
2563                        int w = selectedArea.width;
2564                        int h = selectedArea.height;
2565                        if ((w > 0) && (h > 0)) {
2566                                // use fixed selected area to reduce the rounding error
2567                                selectedArea.setBounds(
2568                                                (int) (originalSelectedArea.x * zoomFactor),
2569                                                (int) (originalSelectedArea.y * zoomFactor),
2570                                                (int) (originalSelectedArea.width * zoomFactor),
2571                                                (int) (originalSelectedArea.height * zoomFactor));
2572                        }
2573
2574                        repaint();
2575                }
2576
2577                private void setImage(Image img) {
2578                        image = img;
2579                        imageSize = new Dimension(image.getWidth(this), image
2580                                        .getHeight(this));
2581                        originalSize = imageSize;
2582                        selectedArea.setSize(0, 0);
2583                        setPreferredSize(imageSize);
2584
2585                        setImageSize(new Dimension((int) (originalSize.width * zoomFactor),
2586                                        (int) (originalSize.height * zoomFactor)));
2587
2588                        repaint();
2589                }
2590        } // private class ImageComponent extends JComponent
2591
2592        /**
2593         * FlipFileter creates image filter to flip image horizontally or
2594         * vertically.
2595         */
2596        private class FlipFilter extends ImageFilter {
2597                /** flip direction */
2598                private int direction;
2599
2600                /** pixel value */
2601                private int raster[] = null;
2602
2603                /** width & height */
2604                private int imageWidth, imageHeight;
2605
2606                /**
2607                 * Constructs an image filter to flip horizontally or vertically.
2608                 * <p>
2609                 * 
2610                 * @param d
2611                 *            the flip direction.
2612                 */
2613                private FlipFilter(int d) {
2614                        if (d < FLIP_HORIZONTAL) {
2615                                d = FLIP_HORIZONTAL;
2616                        }
2617                        else if (d > FLIP_VERTICAL) {
2618                                d = FLIP_VERTICAL;
2619                        }
2620
2621                        direction = d;
2622                }
2623
2624                public void setDimensions(int w, int h) {
2625                        imageWidth = w;
2626                        imageHeight = h;
2627
2628                        // specify the raster
2629                        if (raster == null) {
2630                                raster = new int[imageWidth * imageHeight];
2631                        }
2632
2633                        consumer.setDimensions(imageWidth, imageHeight);
2634                }
2635
2636                public void setPixels(int x, int y, int w, int h, ColorModel model,
2637                                byte pixels[], int off, int scansize) {
2638                        int srcoff = off;
2639                        int dstoff = y * imageWidth + x;
2640                        for (int yc = 0; yc < h; yc++) {
2641                                for (int xc = 0; xc < w; xc++) {
2642                                        raster[dstoff++] = model.getRGB(pixels[srcoff++] & 0xff);
2643                                }
2644
2645                                srcoff += (scansize - w);
2646                                dstoff += (imageWidth - w);
2647                        }
2648                }
2649
2650                public void setPixels(int x, int y, int w, int h, ColorModel model,
2651                                int pixels[], int off, int scansize) {
2652                        int srcoff = off;
2653                        int dstoff = y * imageWidth + x;
2654
2655                        for (int yc = 0; yc < h; yc++) {
2656                                for (int xc = 0; xc < w; xc++) {
2657                                        raster[dstoff++] = model.getRGB(pixels[srcoff++]);
2658                                }
2659                                srcoff += (scansize - w);
2660                                dstoff += (imageWidth - w);
2661                        }
2662                }
2663
2664                public void imageComplete(int status) {
2665                        if ((status == IMAGEERROR) || (status == IMAGEABORTED)) {
2666                                consumer.imageComplete(status);
2667                                return;
2668                        }
2669
2670                        int pixels[] = new int[imageWidth];
2671                        for (int y = 0; y < imageHeight; y++) {
2672                                if (direction == FLIP_VERTICAL) {
2673                                        // grab pixel values of the target line ...
2674                                        int pos = (imageHeight - 1 - y) * imageWidth;
2675                                        for (int kk = 0; kk < imageWidth; kk++) {
2676                                                pixels[kk] = raster[pos + kk];
2677                                        }
2678                                }
2679                                else {
2680                                        int pos = y * imageWidth;
2681                                        for (int kk = 0; kk < imageWidth; kk++) {
2682                                                pixels[kk] = raster[pos + kk];
2683                                        }
2684
2685                                        // swap the pixel values of the target line
2686                                        int hw = imageWidth / 2;
2687                                        for (int kk = 0; kk < hw; kk++) {
2688                                                int tmp = pixels[kk];
2689                                                pixels[kk] = pixels[imageWidth - kk - 1];
2690                                                pixels[imageWidth - kk - 1] = tmp;
2691                                        }
2692                                }
2693
2694                                // consumer it ....
2695                                consumer.setPixels(0, y, imageWidth, 1, ColorModel
2696                                                .getRGBdefault(), pixels, 0, imageWidth);
2697                        } // for (int y = 0; y < imageHeight; y++)
2698
2699                        // complete ?
2700                        consumer.imageComplete(status);
2701                }
2702        } // private class FlipFilter extends ImageFilter
2703
2704        /**
2705         * Apply general brightness/contrast algorithm. For details, visit
2706         * http://www.developerfusion.co.uk/
2707         * 
2708         * The general algorithm is represented by: If Brighten = True New_Value =
2709         * Old_Value + Adjustment_Amount Else New_Value = Old_Value -
2710         * Adjustment_Amount If New_Value < Value_Minimum New_Value = Value_Minimum
2711         * If New_Value > Value_Maximum New_Value = Value_Maximum
2712         * 
2713         * Contrast is a complicated operation. It is hard to formulate a
2714         * "general algorithm". Here is the closest representation
2715         * (Contrast_Value=[0, 2]):
2716         * 
2717         * //Converts to a percent //[0, 1] New_Value = Old_Value / 255
2718         * 
2719         * //Centers on 0 instead of .5 //[-.5, .5] New_Value -= 0.5
2720         * 
2721         * //Adjusts by Contrast_Value //[-127.5, 127.5], usually [-1, 1] New_Value
2722         * *= Contrast_Value
2723         * 
2724         * //Re-add .5 (un-center over 0) //[-127, 128] New_Value += 0.5
2725         * 
2726         * //Re-multiply by 255 (un-convert to percent) //[-32385, 32640], usually
2727         * [0, 255] New_Value *= 255 //Clamp [0, 255] If(New_Value > 255) New_Value
2728         * = 255 If(New_Value < 0) New_Value = 0
2729         */
2730        private class BrightnessFilter extends RGBImageFilter {
2731                // brightness level = [-200, 200]
2732                int brightLevel = 0;
2733
2734                // contrast level [0, 4]
2735                float contrastLevel = 0;
2736
2737                public BrightnessFilter(int blevel, int clevel) {
2738                        if (blevel < -100) {
2739                                brightLevel = -100;
2740                        }
2741                        else if (blevel > 100) {
2742                                brightLevel = 100;
2743                        }
2744                        else {
2745                                brightLevel = blevel;
2746                        }
2747                        brightLevel *= 2;
2748
2749                        if (clevel < -100) {
2750                                clevel = -100;
2751                        }
2752                        else if (clevel > 100) {
2753                                clevel = 100;
2754                        }
2755
2756                        if (clevel > 0) {
2757                                contrastLevel = (clevel / 100f + 1) * 2;
2758                        }
2759                        else if (clevel < 0) {
2760                                contrastLevel = (clevel / 100f + 1) / 2;
2761                        }
2762                        else {
2763                                contrastLevel = 0;
2764                        }
2765
2766                        canFilterIndexColorModel = true;
2767                }
2768
2769                public int filterRGB(int x, int y, int rgb) {
2770                        // adjust brightness first, then adjust contrast
2771                        // it gives more color depth
2772
2773                        if (brightLevel != 0) {
2774                                int r = (rgb & 0x00ff0000) >> 16;
2775                        int g = (rgb & 0x0000ff00) >> 8;
2776                                        int b = (rgb & 0x000000ff);
2777
2778                                        r += brightLevel;
2779                                        g += brightLevel;
2780                                        b += brightLevel;
2781
2782                                        if (r < 0) {
2783                                                r = 0;
2784                                        }
2785                                        if (r > 255) {
2786                                                r = 255;
2787                                        }
2788                                        if (g < 0) {
2789                                                g = 0;
2790                                        }
2791                                        if (g > 255) {
2792                                                g = 255;
2793                                        }
2794                                        if (b < 0) {
2795                                                b = 0;
2796                                        }
2797                                        if (b > 255) {
2798                                                b = 255;
2799                                        }
2800
2801                                        r = (r << 16) & 0x00ff0000;
2802                                        g = (g << 8) & 0x0000ff00;
2803                                        b = b & 0x000000ff;
2804
2805                                        rgb = ((rgb & 0xff000000) | r | g | b);
2806                        }
2807
2808                        if (contrastLevel > 0.000001) { // do not compare float using !=0 or
2809                                // ==0
2810                                int r = (rgb & 0x00ff0000) >> 16;
2811                                        int g = (rgb & 0x0000ff00) >> 8;
2812                                        int b = (rgb & 0x000000ff);
2813
2814                                        float f = (float) r / 255f;
2815                                        f -= 0.5;
2816                                        f *= contrastLevel;
2817                                        f += 0.5;
2818                                        f *= 255f;
2819                                        if (f < 0) {
2820                                                f = 0;
2821                                        }
2822                                        if (f > 255) {
2823                                                f = 255;
2824                                        }
2825                                        r = (int) f;
2826
2827                                        f = (float) g / 255f;
2828                                        f -= 0.5;
2829                                        f *= contrastLevel;
2830                                        f += 0.5;
2831                                        f *= 255f;
2832                                        if (f < 0) {
2833                                                f = 0;
2834                                        }
2835                                        if (f > 255) {
2836                                                f = 255;
2837                                        }
2838                                        g = (int) f;
2839
2840                                        f = (float) b / 255f;
2841                                        f -= 0.5;
2842                                        f *= contrastLevel;
2843                                        f += 0.5;
2844                                        f *= 255f;
2845                                        if (f < 0) {
2846                                                f = 0;
2847                                        }
2848                                        if (f > 255) {
2849                                                f = 255;
2850                                        }
2851                                        b = (int) f;
2852
2853                                        r = (r << 16) & 0x00ff0000;
2854                                        g = (g << 8) & 0x0000ff00;
2855                                        b = b & 0x000000ff;
2856
2857                                        rgb = ((rgb & 0xff000000) | r | g | b);
2858                        }
2859
2860                        return rgb;
2861                }
2862        }
2863
2864        /**
2865         * Makes an image filter for contour.
2866         */
2867        private class ContourFilter extends ImageFilter {
2868                // default color model
2869                private ColorModel defaultRGB;
2870
2871                // contour level
2872                int level;
2873
2874                // the table of the contour levels
2875                int levels[];
2876
2877                // colors for drawable contour line
2878                int[] levelColors;
2879
2880                // default RGB
2881
2882                // pixel value
2883                private int raster[] = null;
2884
2885                // width & height
2886                private int imageWidth, imageHeight;
2887
2888                /**
2889                 * Create an contour filter for a given level contouring.
2890                 * 
2891                 * @param theLevel
2892                 *            the contour level.
2893                 */
2894                private ContourFilter(int theLevel) {
2895                        defaultRGB = ColorModel.getRGBdefault();
2896
2897                        levelColors = new int[9];
2898                        levelColors[0] = Color.red.getRGB();
2899                        levelColors[1] = Color.green.getRGB();
2900                        levelColors[2] = Color.blue.getRGB();
2901                        levelColors[3] = Color.magenta.getRGB();
2902                        levelColors[4] = Color.orange.getRGB();
2903                        levelColors[5] = Color.cyan.getRGB();
2904                        levelColors[6] = Color.black.getRGB();
2905                        levelColors[7] = Color.pink.getRGB();
2906                        levelColors[8] = Color.yellow.getRGB();
2907
2908
2909                        if (theLevel < 1) {
2910                                theLevel = 1;
2911                        }
2912                        else if (theLevel > 9) {
2913                                theLevel = 9;
2914                        }
2915
2916                        level = theLevel;
2917                        levels = new int[level];
2918
2919                        int dx = 128 / level;
2920                        for (int i = 0; i < level; i++) {
2921                                levels[i] = (i + 1) * dx;
2922                        }
2923                }
2924
2925                public void setDimensions(int width, int height) {
2926                        this.imageWidth = width;
2927                        this.imageHeight = height;
2928
2929                        // specify the raster
2930                        if (raster == null) {
2931                                raster = new int[imageWidth * imageHeight];
2932                        }
2933
2934                        consumer.setDimensions(width, height);
2935                }
2936
2937                public void setPixels(int x, int y, int w, int h, ColorModel model,
2938                                byte pixels[], int off, int scansize) {
2939                        int rgb = 0;
2940                        int srcoff = off;
2941                        int dstoff = y * imageWidth + x;
2942
2943                        for (int yc = 0; yc < h; yc++) {
2944                                for (int xc = 0; xc < w; xc++) {
2945                                        rgb = model.getRGB(pixels[srcoff++] & 0xff);
2946                                        raster[dstoff++] = (((rgb >> 16) & 0xff)
2947                                                        + ((rgb >> 8) & 0xff) + (rgb & 0xff)) / 3;
2948                                }
2949                                srcoff += (scansize - w);
2950                                dstoff += (imageWidth - w);
2951                        }
2952
2953                }
2954
2955                public void setPixels(int x, int y, int w, int h, ColorModel model,
2956                                int pixels[], int off, int scansize) {
2957                        int rgb = 0;
2958                        int srcoff = off;
2959                        int dstoff = y * imageWidth + x;
2960
2961                        for (int yc = 0; yc < h; yc++) {
2962                                for (int xc = 0; xc < w; xc++) {
2963                                        rgb = model.getRGB(pixels[srcoff++] & 0xff);
2964                                        raster[dstoff++] = (((rgb >> 16) & 0xff)
2965                                                        + ((rgb >> 8) & 0xff) + (rgb & 0xff)) / 3;
2966                                }
2967
2968                                srcoff += (scansize - w);
2969                                dstoff += (imageWidth - w);
2970                        }
2971                }
2972
2973                public void imageComplete(int status) {
2974                        if ((status == IMAGEERROR) || (status == IMAGEABORTED)) {
2975                                consumer.imageComplete(status);
2976                                return;
2977                        }
2978
2979                        int pixels[] = new int[imageWidth * imageHeight];
2980                        for (int z = 0; z < levels.length; z++) {
2981                                int currentLevel = levels[z];
2982                                int color = levelColors[z];
2983
2984                                setContourLine(raster, pixels, currentLevel, color, imageWidth,
2985                                                imageHeight);
2986                        }
2987
2988                        int line[] = new int[imageWidth];
2989                        for (int y = 0; y < imageHeight; y++) {
2990                                for (int x = 0; x < imageWidth; x++) {
2991                                        line[x] = pixels[y * imageWidth + x];
2992                                }
2993
2994                                consumer.setPixels(0, y, imageWidth, 1, defaultRGB, line, 0,
2995                                                imageWidth);
2996                        } // for (int y = 0; y < imageHeight; y++) {
2997
2998                                // complete ?
2999                                                consumer.imageComplete(status);
3000                        }
3001
3002                        /**
3003                         * draw a contour line based on the current parameter---level, color
3004                         * 
3005                         * @param raster
3006                         *            the data of the raster image.
3007                         * @param pixels
3008                         *            the pixel value of the image.
3009                         * @param level
3010                         *            the contour level.
3011                         * @param color
3012                         *            the color of the contour line.
3013                         * @param w
3014                         *            the width of the image.
3015                         * @param h
3016                         *            the height of the image.
3017                         */
3018                private void setContourLine(int[] raster, int[] pixels, int level,
3019                                int color, int w, int h) {
3020                        int p = 0; // entrance point
3021                        int q = p + (w * h - 1); // bottom right point
3022                        int u = 0 + (w - 1); // top right point
3023
3024                        // first round
3025                        while (true) {
3026                                while (p < u) {
3027                                        int rgb = raster[p];
3028                                        if (rgb < level) {
3029                                                while ((raster[p] < level) && (p < u)) {
3030                                                        p++;
3031                                                }
3032                                                if (raster[p] >= level) {
3033                                                        pixels[p] = color;
3034                                                }
3035                                        }
3036                                        else if (rgb == level) {
3037                                                while ((raster[p] == level) && (p < u)) {
3038                                                        p++;
3039                                                }
3040                                                if ((raster[p] < level) || (raster[p] > level)) {
3041                                                        pixels[p] = color;
3042                                                }
3043                                        }
3044                                        else {
3045                                                while ((raster[p] > level) && (p < u)) {
3046                                                        p++;
3047                                                }
3048                                                if ((raster[p] <= level)) {
3049                                                        pixels[p] = color;
3050                                                }
3051                                        }
3052                                }
3053
3054                                if (u == q) {
3055                                        break;
3056                                }
3057                                else {
3058                                        u += w;
3059                                        p++;
3060                                }
3061                        }
3062                }
3063
3064        } // private class ContourFilter extends ImageFilter
3065
3066        private class Rotate90Filter extends ImageFilter {
3067                private ColorModel defaultRGB = ColorModel.getRGBdefault();
3068
3069                private double coord[] = new double[2];
3070
3071                private int raster[];
3072                private int xoffset, yoffset;
3073                private int srcW, srcH;
3074                private int dstW, dstH;
3075                private int direction;
3076
3077                public Rotate90Filter(int dir) {
3078                        direction = dir;
3079                }
3080
3081                public void transform(double x, double y, double[] retcoord) {
3082                        if (direction == ROTATE_CW_90) {
3083                                retcoord[0] = -y;
3084                                retcoord[1] = x;
3085                        }
3086                        else {
3087                                retcoord[0] = y;
3088                                retcoord[1] = -x;
3089                        }
3090                }
3091
3092                public void itransform(double x, double y, double[] retcoord) {
3093                        if (direction == ROTATE_CCW_90) {
3094                                retcoord[0] = -y;
3095                                retcoord[1] = x;
3096                        }
3097                        else {
3098                                retcoord[0] = y;
3099                                retcoord[1] = -x;
3100                        }
3101                }
3102
3103                public void transformBBox(Rectangle rect) {
3104                        double minx = Double.POSITIVE_INFINITY;
3105                        double miny = Double.POSITIVE_INFINITY;
3106                        double maxx = Double.NEGATIVE_INFINITY;
3107                        double maxy = Double.NEGATIVE_INFINITY;
3108                        for (int y = 0; y <= 1; y++) {
3109                                for (int x = 0; x <= 1; x++) {
3110                                        transform(rect.x + x * rect.width,
3111                                                        rect.y + y * rect.height, coord);
3112                                        minx = Math.min(minx, coord[0]);
3113                                        miny = Math.min(miny, coord[1]);
3114                                        maxx = Math.max(maxx, coord[0]);
3115                                        maxy = Math.max(maxy, coord[1]);
3116                                }
3117                        }
3118                        rect.x = (int) Math.floor(minx);
3119                        rect.y = (int) Math.floor(miny);
3120                        rect.width = (int) Math.ceil(maxx) - rect.x;
3121                        rect.height = (int) Math.ceil(maxy) - rect.y;
3122                }
3123
3124                public void setDimensions(int width, int height) {
3125                        Rectangle rect = new Rectangle(0, 0, width, height);
3126                        transformBBox(rect);
3127                        xoffset = -rect.x;
3128                        yoffset = -rect.y;
3129                        srcW = width;
3130                        srcH = height;
3131                        dstW = rect.width;
3132                        dstH = rect.height;
3133                        raster = new int[srcW * srcH];
3134                        consumer.setDimensions(dstW, dstH);
3135                }
3136
3137                public void setProperties(Hashtable props) {
3138                        props = (Hashtable) props.clone();
3139                        Object o = props.get("filters");
3140                        if (o == null) {
3141                                props.put("filters", toString());
3142                        }
3143                        else if (o instanceof String) {
3144                                props.put("filters", ((String) o) + toString());
3145                        }
3146                        consumer.setProperties(props);
3147                }
3148
3149                public void setColorModel(ColorModel model) {
3150                        consumer.setColorModel(defaultRGB);
3151                }
3152
3153                public void setHints(int hintflags) {
3154                        consumer.setHints(TOPDOWNLEFTRIGHT | COMPLETESCANLINES | SINGLEPASS
3155                                        | (hintflags & SINGLEFRAME));
3156                }
3157
3158                public void setPixels(int x, int y, int w, int h, ColorModel model,
3159                                byte pixels[], int off, int scansize) {
3160                        int srcoff = off;
3161                        int dstoff = y * srcW + x;
3162                        for (int yc = 0; yc < h; yc++) {
3163                                for (int xc = 0; xc < w; xc++) {
3164                                        raster[dstoff++] = model.getRGB(pixels[srcoff++] & 0xff);
3165                                }
3166                                srcoff += (scansize - w);
3167                                dstoff += (srcW - w);
3168                        }
3169                }
3170
3171                public void setPixels(int x, int y, int w, int h, ColorModel model,
3172                                int pixels[], int off, int scansize) {
3173                        int srcoff = off;
3174                        int dstoff = y * srcW + x;
3175                        if (model == defaultRGB) {
3176                                for (int yc = 0; yc < h; yc++) {
3177                                        System.arraycopy(pixels, srcoff, raster, dstoff, w);
3178                                        srcoff += scansize;
3179                                        dstoff += srcW;
3180                                }
3181                        }
3182                        else {
3183                                for (int yc = 0; yc < h; yc++) {
3184                                        for (int xc = 0; xc < w; xc++) {
3185                                                raster[dstoff++] = model.getRGB(pixels[srcoff++]);
3186                                        }
3187                                        srcoff += (scansize - w);
3188                                        dstoff += (srcW - w);
3189                                }
3190                        }
3191                }
3192
3193                public void imageComplete(int status) {
3194                        if ((status == IMAGEERROR) || (status == IMAGEABORTED)) {
3195                                consumer.imageComplete(status);
3196                                return;
3197                        }
3198                        int pixels[] = new int[dstW];
3199                        for (int dy = 0; dy < dstH; dy++) {
3200                                itransform(0 - xoffset, dy - yoffset, coord);
3201                                double x1 = coord[0];
3202                                double y1 = coord[1];
3203                                itransform(dstW - xoffset, dy - yoffset, coord);
3204                                double x2 = coord[0];
3205                                double y2 = coord[1];
3206                                double xinc = (x2 - x1) / dstW;
3207                                double yinc = (y2 - y1) / dstW;
3208                                for (int dx = 0; dx < dstW; dx++) {
3209                                        int sx = (int) Math.round(x1);
3210                                        int sy = (int) Math.round(y1);
3211                                        if ((sx < 0) || (sy < 0) || (sx >= srcW) || (sy >= srcH)) {
3212                                                pixels[dx] = 0;
3213                                        }
3214                                        else {
3215                                                pixels[dx] = raster[sy * srcW + sx];
3216                                        }
3217                                        x1 += xinc;
3218                                        y1 += yinc;
3219                                }
3220                                consumer.setPixels(0, dy, dstW, 1, defaultRGB, pixels, 0, dstW);
3221                        }
3222                        consumer.imageComplete(status);
3223                }
3224        } // private class RotateFilter
3225
3226        /**
3227         * Makes animation for 3D images.
3228         */
3229        private class Animation extends JDialog implements ActionListener, Runnable {
3230                private static final long serialVersionUID = 6717628496771098250L;
3231
3232                private final int MAX_ANIMATION_IMAGE_SIZE = 300;
3233
3234                private Image[] frames = null; // a list of images for animation
3235                private JComponent canvas = null; // canvas to draw the image
3236                private Thread engine = null; // Thread animating the images
3237                private int numberOfImages = 0;
3238                private int currentFrame = 0;
3239                private int sleepTime = 200;
3240                private Image offScrImage; // Offscreen image
3241                private Graphics offScrGC; // Offscreen graphics context
3242                private JFrame owner;
3243                private int x0 = 0, y0 = 0; // offset of the image drawing
3244
3245                public Animation(JFrame theOwner, ScalarDS dataset) {
3246                        super(theOwner, "Animation", true);
3247                        owner = theOwner;
3248                        setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
3249
3250                        long[] dims = dataset.getDims();
3251                        long[] stride = dataset.getStride();
3252                        long[] start = dataset.getStartDims();
3253                        long[] selected = dataset.getSelectedDims();
3254                        int[] selectedIndex = dataset.getSelectedIndex();
3255                        int rank = dataset.getRank();
3256                        if (animationSpeed != 0) {
3257                                sleepTime = 1000 / animationSpeed;
3258                        }
3259
3260                        // back up the start and selected size
3261                        long[] tstart = new long[rank];
3262                        long[] tselected = new long[rank];
3263                        long[] tstride = new long[rank];
3264                        System.arraycopy(start, 0, tstart, 0, rank);
3265                        System.arraycopy(selected, 0, tselected, 0, rank);
3266                        System.arraycopy(stride, 0, tstride, 0, rank);
3267
3268                        int stride_n = 1;
3269                        int max_size = (int) Math.max(selected[selectedIndex[0]],
3270                                        selected[selectedIndex[1]]);
3271                        if (max_size > MAX_ANIMATION_IMAGE_SIZE) {
3272                                stride_n = (int)( (double)max_size / (double)MAX_ANIMATION_IMAGE_SIZE +0.5);
3273                        }
3274
3275                        start[selectedIndex[0]] = 0;
3276                        start[selectedIndex[1]] = 0;
3277                        start[selectedIndex[2]] = 0;
3278                        selected[selectedIndex[0]] = dims[selectedIndex[0]] / stride_n;
3279                        selected[selectedIndex[1]] = dims[selectedIndex[1]] / stride_n;
3280                        selected[selectedIndex[2]] = 1;
3281                        stride[selectedIndex[0]] = stride_n;
3282                        stride[selectedIndex[1]] = stride_n;
3283                        stride[selectedIndex[2]] = 1;
3284
3285                        Object data3d = null;
3286                        byte[] byteData = null;
3287                        int h = (int) selected[selectedIndex[0]];
3288                        int w = (int) selected[selectedIndex[1]];
3289                        int size = w * h;
3290
3291                        numberOfImages = (int) dims[selectedIndex[2]];
3292                        frames = new Image[numberOfImages];
3293                        BufferedImage mir = bufferedImage;
3294                        try {
3295                                for (int i = 0; i < numberOfImages; i++) {
3296                                        bufferedImage = null; // each animation image has its
3297                                        // own image resource
3298                                        start[selectedIndex[2]] = i;
3299
3300                                        dataset.clearData();
3301                                        try {
3302                                                data3d = dataset.read();
3303                                        }
3304                                        catch (Throwable err) {
3305                                                continue;
3306                                        }
3307
3308                                        byteData = new byte[size];
3309
3310                                        byteData=Tools.getBytes(data3d, dataRange, w, h, false, dataset.getFilteredImageValues(),
3311                                                        true, byteData);
3312
3313                                        frames[i] = createIndexedImage(byteData, imagePalette, w, h);
3314                                }
3315                        }
3316                        finally {
3317                                // set back to original state
3318                                bufferedImage = mir;
3319                                System.arraycopy(tstart, 0, start, 0, rank);
3320                                System.arraycopy(tselected, 0, selected, 0, rank);
3321                                System.arraycopy(tstride, 0, stride, 0, rank);
3322                        }
3323
3324                        offScrImage = owner.createImage(w, h);
3325                        offScrGC = offScrImage.getGraphics();
3326                        x0 = Math.max((MAX_ANIMATION_IMAGE_SIZE - w) / 2, 0);
3327                        y0 = Math.max((MAX_ANIMATION_IMAGE_SIZE - h) / 2, 0);
3328
3329                        canvas = new JComponent() {
3330                                private static final long serialVersionUID = -6828735330511795835L;
3331
3332                                public void paint(Graphics g) {
3333                                        g.clearRect(0, 0, MAX_ANIMATION_IMAGE_SIZE,
3334                                                        MAX_ANIMATION_IMAGE_SIZE);
3335
3336                                        if ((offScrGC == null) || (frames == null)) {
3337                                                return;
3338                                        }
3339
3340                                        offScrGC.drawImage(frames[currentFrame], 0, 0, owner);
3341                                        g.drawImage(offScrImage, x0, y0, owner);
3342                                }
3343                        };
3344
3345                        JPanel contentPane = (JPanel) getContentPane();
3346                        contentPane.setPreferredSize(new Dimension(
3347                                        MAX_ANIMATION_IMAGE_SIZE, MAX_ANIMATION_IMAGE_SIZE));
3348                        contentPane.setLayout(new BorderLayout());
3349                        JButton b = new JButton("Close");
3350                        b.setActionCommand("Close animation");
3351                        b.addActionListener(this);
3352                        contentPane.add(b, BorderLayout.SOUTH);
3353
3354                        contentPane.add(canvas, BorderLayout.CENTER);
3355
3356                        start();
3357
3358                        Point l = getParent().getLocation();
3359                        l.x += 300;
3360                        l.y += 200;
3361                        setLocation(l);
3362
3363                        pack();
3364                        setVisible(true);
3365                }
3366
3367                public void actionPerformed(ActionEvent e) {
3368                        Object source = e.getSource();
3369                        String cmd = e.getActionCommand();
3370
3371                        if (cmd.equals("Close animation")) {
3372                                dispose(); // terminate the animation
3373                        }
3374                }
3375
3376                public void dispose() {
3377                        engine = null;
3378                        frames = null;
3379                        super.dispose();
3380                }
3381
3382                /**
3383                 * No need to clear anything; just paint.
3384                 */
3385                public void update(Graphics g) {
3386                        paint(g);
3387                }
3388
3389                /**
3390                 * Paint the current frame
3391                 */
3392                public void paint(Graphics g) {
3393                        canvas.paint(g);
3394                }
3395
3396                /**
3397                 * Start the applet by forking an animation thread.
3398                 */
3399                private void start() {
3400                        engine = new Thread(this);
3401                        engine.start();
3402                }
3403
3404                /**
3405                 * Run the animation. This method is called by class Thread.
3406                 * 
3407                 * @see java.lang.Thread
3408                 */
3409                public void run() {
3410                        Thread me = Thread.currentThread();
3411
3412                        if ((frames == null) || (canvas == null)) {
3413                                return;
3414                        }
3415
3416                        while (me == engine) {
3417                                if (++currentFrame >= numberOfImages)
3418                                        currentFrame = 0;
3419                                repaint();
3420                                this.getToolkit().sync(); // Force it to be drawn *now*.
3421                                try {
3422                                        Thread.sleep(sleepTime);
3423                                }
3424                                catch (InterruptedException e) {
3425                                        log.debug("Thread.sleep({}):", sleepTime, e);
3426                                }
3427                        }
3428                } // public void run() {
3429        } // private class Animation extends JDialog
3430
3431        private class DataRangeDialog extends JDialog implements ActionListener,
3432        ChangeListener, PropertyChangeListener {
3433                final int NTICKS = 10;
3434                double tickRatio = 1;
3435                final int W = 500, H = 400;
3436                double[] minmax_current = {0, 0};
3437                double min, max, min_org, max_org;
3438                final double[] minmax_previous = {0, 0};
3439                final double[] minmax_dist = {0,0};
3440                JSlider minSlider, maxSlider;
3441                JFormattedTextField minField, maxField;
3442
3443                public DataRangeDialog(JFrame theOwner, double[] minmaxCurrent, 
3444                                double[] minmaxOriginal, final int[] dataDist) 
3445                {
3446                        super(theOwner, "Image Value Range", true);
3447
3448                        Tools.findMinMax(dataDist, minmax_dist, null);
3449
3450                        if ((minmaxCurrent == null) || (minmaxCurrent.length <= 1)) {
3451                                minmax_current[0] = 0;
3452                                minmax_current[1] = 255;
3453                        }
3454                        else {
3455                                if (minmaxCurrent[0] == minmaxCurrent[1]) {
3456                                        Tools.findMinMax(data, minmaxCurrent, dataset.getFillValue());
3457                                }
3458
3459                                minmax_current[0] = minmaxCurrent[0];
3460                                minmax_current[1] = minmaxCurrent[1];
3461                        }
3462
3463                        minmax_previous[0] = min = minmax_current[0];
3464                        minmax_previous[1] = max = minmax_current[1];
3465                        min_org = originalRange[0];
3466                        max_org = originalRange[1];
3467
3468                        tickRatio = (max_org-min_org)/(double)NTICKS;
3469
3470                        final DecimalFormat numberFormat = new DecimalFormat("#.##E0");
3471                        NumberFormatter formatter = new NumberFormatter(numberFormat);
3472                        formatter.setMinimum(new Double(min));
3473                        formatter.setMaximum(new Double(max));
3474
3475                        minField = new JFormattedTextField(formatter);
3476                        minField.addPropertyChangeListener(this);
3477                        minField.setValue(new Double(min));
3478                        maxField = new JFormattedTextField(formatter);
3479                        maxField.addPropertyChangeListener(this);
3480                        maxField.setValue(new Double(max));
3481
3482                        minSlider = new JSlider(JSlider.HORIZONTAL, 0, NTICKS, 0);
3483                        minSlider.setMajorTickSpacing(1);
3484                        minSlider.setPaintTicks(true);
3485                        minSlider.setPaintLabels(false);
3486                        minSlider.addChangeListener(this);
3487                        minSlider.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
3488
3489                        maxSlider = new JSlider(JSlider.HORIZONTAL, 0, NTICKS, NTICKS);
3490                        maxSlider.setMajorTickSpacing(1);
3491                        maxSlider.setPaintTicks(true);
3492                        maxSlider.setPaintLabels(false);
3493                        maxSlider.addChangeListener(this);
3494                        maxSlider.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
3495
3496                        JPanel contentPane = (JPanel) getContentPane();
3497                        contentPane.setLayout(new BorderLayout(5, 5));
3498                        contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
3499                        contentPane.setPreferredSize(new Dimension(W, H));
3500
3501                        JPanel minPane = new JPanel();
3502                        minPane.setBorder(new TitledBorder("Lower Bound"));
3503                        minPane.setLayout(new BorderLayout());
3504                        minPane.add(minField, BorderLayout.CENTER);
3505                        minPane.add(minSlider, BorderLayout.SOUTH);
3506
3507                        JPanel maxPane = new JPanel();
3508                        maxPane.setBorder(new TitledBorder("Upper Bound"));
3509                        maxPane.setLayout(new BorderLayout());
3510                        maxPane.add(maxField, BorderLayout.CENTER);
3511                        maxPane.add(maxSlider, BorderLayout.SOUTH);
3512
3513                        JPanel chartPane = new JPanel() {
3514                                int numberOfPoints = dataDist.length;
3515                                int gap = 5;
3516                                int xgap = 2 * gap;
3517                                double xmin = originalRange[0];
3518                                double xmax = originalRange[1];
3519
3520                                public void paint(Graphics g) {
3521                                        int h = H/3 -50;
3522                                        int w = W;
3523                                        int xnpoints = Math.min(10, numberOfPoints - 1);
3524
3525                                        // draw the X axis
3526                                        g.drawLine(xgap, h, w + xgap, h);
3527
3528                                        // draw x labels
3529                                        double xp = 0, x = xmin;
3530                                        double dw = (double) w / (double) xnpoints;
3531                                        double dx = (xmax - xmin) / xnpoints;
3532                                        for (int i = 0; i <= xnpoints; i++) {
3533                                                x = xmin + i * dx;
3534                                                xp = xgap + i * dw;
3535                                                g.drawLine((int) xp, h, (int) xp, h - 5);
3536                                                g.drawString(numberFormat.format(x), (int) xp - 5, h + 20);
3537                                        }
3538
3539                                        Color c = g.getColor();
3540                                        double yp, ymin=minmax_dist[0], dy=minmax_dist[1]-minmax_dist[0];
3541                                        if (dy<=0)
3542                                                dy =1;
3543
3544                                        xp = xgap;
3545                                        yp = 0;
3546                                        g.setColor(Color.blue);
3547                                        int barWidth = w / numberOfPoints;
3548                                        if (barWidth <= 0) {
3549                                                barWidth = 1;
3550                                        }
3551                                        dw = (double) w / (double) numberOfPoints;
3552
3553                                        for (int j = 0; j < numberOfPoints; j++) {
3554                                                xp = xgap + j * dw;
3555                                                yp = (int) (h * (dataDist[j] - ymin) / dy);
3556                                                g.fillRect((int) xp, (int) (h - yp), barWidth, (int) yp);
3557                                        }
3558
3559                                        g.setColor(c); // set the color back to its default
3560                                } // public void paint(Graphics g)
3561                        } ;
3562
3563                        JPanel mainPane = new JPanel();
3564                        mainPane.setLayout(new GridLayout(3, 1, 5, 5));
3565                        mainPane.add(chartPane);
3566                        mainPane.add(minPane);
3567                        mainPane.add(maxPane);
3568                        contentPane.add(mainPane, BorderLayout.CENTER);
3569
3570                        // add OK and CANCEL buttons
3571                        JPanel confirmP = new JPanel();
3572                        JButton button = new JButton("   Ok   ");
3573                        button.setMnemonic(KeyEvent.VK_O);
3574                        button.setActionCommand("Ok");
3575                        button.addActionListener(this);
3576                        confirmP.add(button);
3577                        button = new JButton("Cancel");
3578                        button.setMnemonic(KeyEvent.VK_C);
3579                        button.setActionCommand("Cancel");
3580                        button.addActionListener(this);
3581                        confirmP.add(button);
3582                        button = new JButton("Apply");
3583                        button.setMnemonic(KeyEvent.VK_A);
3584                        button.setActionCommand("Apply");
3585                        button.addActionListener(this);
3586                        confirmP.add(button);
3587                        contentPane.add(confirmP, BorderLayout.SOUTH);
3588                        contentPane.add(new JLabel(" "), BorderLayout.NORTH);
3589
3590                        if (min==max) {
3591                                minSlider.setEnabled(false);
3592                                maxSlider.setEnabled(false);
3593                        }
3594
3595                        Point l = getParent().getLocation();
3596                        Dimension d = getParent().getPreferredSize();
3597                        l.x += 300;
3598                        l.y += 200;
3599                        setLocation(l);
3600                        pack();
3601                        setVisible(true);
3602                }
3603
3604                public void actionPerformed(ActionEvent e) {
3605                        String cmd = e.getActionCommand();
3606
3607                        if (cmd.equals("Ok")) {
3608                                minmax_current[0] = ((Number) minField.getValue()).doubleValue();
3609                                minmax_current[1] = ((Number) maxField.getValue()).doubleValue();
3610
3611                                this.dispose();
3612                        }
3613                        if (cmd.equals("Apply")) {
3614                                minmax_previous[0] = minmax_current[0];
3615                                minmax_previous[1] = minmax_current[1];
3616
3617                                minmax_current[0] = ((Number) minField.getValue()).doubleValue();
3618                                minmax_current[1] = ((Number) maxField.getValue()).doubleValue();
3619
3620                                applyDataRange(minmax_current);
3621                                minmax_current[0] = minmax_current[1] = 0;
3622                        }
3623                        else if (cmd.equals("Cancel")) {
3624
3625                                minmax_current[0] = minmax_previous[0];
3626                                minmax_current[1] = minmax_previous[1];
3627
3628                                applyDataRange(minmax_previous);
3629
3630                                this.dispose();
3631                        }
3632                }
3633
3634                /** Listen to the slider. */
3635                public void stateChanged(ChangeEvent e) {
3636                        Object source = e.getSource();
3637
3638                        if (!(source instanceof JSlider)) {
3639                                return;
3640                        }
3641
3642                        JSlider slider = (JSlider) source;
3643                        if (!slider.isEnabled())
3644                                return;
3645
3646                        double value = slider.getValue();
3647                        if (slider.equals(minSlider)) {
3648                                double maxValue = maxSlider.getValue();
3649                                if (value > maxValue) {
3650                                        value = maxValue;
3651                                        slider.setValue((int)value);
3652                                }
3653
3654                                minField.setValue(new Double(value*tickRatio+min_org));
3655                        }
3656                        else if (slider.equals(maxSlider)) {
3657                                double minValue = minSlider.getValue();
3658                                if (value < minValue) {
3659                                        value = minValue;
3660                                        slider.setValue((int)value);
3661                                }
3662                                maxField.setValue(new Double(value*tickRatio+min_org));
3663                        }
3664                }
3665
3666                /**
3667                 * Listen to the text field. This method detects when the value of the
3668                 * text field changes.
3669                 */
3670                public void propertyChange(PropertyChangeEvent e) {
3671                        Object source = e.getSource();
3672                        if ("value".equals(e.getPropertyName())) {
3673                                Number num = (Number) e.getNewValue();
3674                                if (num == null) {
3675                                        return;
3676                                }
3677                                double value = num.doubleValue();
3678
3679                                if (source.equals(minField) && (minSlider != null) && minSlider.isEnabled()) {
3680                                        if (value > max_org) {
3681                                                value = max_org;
3682                                                minField.setText(String.valueOf(value));
3683                                        }
3684
3685                                        minSlider.setValue((int) ((value-min_org)/tickRatio));
3686                                }
3687                                else if (source.equals(maxField) && (maxSlider != null)  && minSlider.isEnabled()) {
3688                                        if (value < min_org) {
3689                                                value = min_org;
3690                                                maxField.setText(String.valueOf(value));
3691                                        }
3692                                        //minmax[1] = value;
3693                                        maxSlider.setValue((int) ((value-min_org)/tickRatio));
3694                                }
3695                        }
3696                }
3697
3698                public double[] getRange() {
3699                        return minmax_current;
3700                }
3701        } // private class DataRangeDialog extends JDialog implements ActionListener
3702
3703        private class ContrastSlider extends JDialog implements
3704        ActionListener, ChangeListener, PropertyChangeListener {
3705                private static final long serialVersionUID = -3002524363351111565L;
3706                JSlider brightSlider, contrastSlider;
3707                JFormattedTextField brightField, contrastField;
3708                ImageProducer imageProducer;
3709                double[] autoGainBias = {0, 0};
3710                int bLevel=0, cLevel=0;
3711
3712                public ContrastSlider(JFrame theOwner, ImageProducer producer) 
3713                {
3714                        super(theOwner, "Brightness/Contrast", true);
3715                        String bLabel = "Brightness", cLabel="Contrast";
3716
3717                        imageProducer = producer;
3718
3719                        if (doAutoGainContrast && gainBias!= null) {
3720                                bLabel = "Bias";
3721                                cLabel="Gain";
3722                                this.setTitle(bLabel+"/"+cLabel);
3723                        }
3724
3725                        java.text.NumberFormat numberFormat = java.text.NumberFormat
3726                        .getNumberInstance();
3727                        NumberFormatter formatter = new NumberFormatter(numberFormat);
3728
3729                        formatter.setMinimum(new Integer(-100));
3730                        formatter.setMaximum(new Integer(100));
3731                        brightField = new JFormattedTextField(formatter);
3732                        brightField.addPropertyChangeListener(this);
3733                        brightField.setValue(new Integer(0));
3734
3735                        brightSlider = new JSlider(JSlider.HORIZONTAL, -100, 100, 0);
3736                        brightSlider.setMajorTickSpacing(20);
3737                        brightSlider.setPaintTicks(true);
3738                        brightSlider.setPaintLabels(true);
3739                        brightSlider.addChangeListener(this);
3740                        brightSlider
3741                        .setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
3742
3743                        formatter = new NumberFormatter(numberFormat);
3744                        formatter.setMinimum(new Integer(-100));
3745                        formatter.setMaximum(new Integer(100));
3746                        contrastField = new JFormattedTextField(formatter);
3747                        contrastField.addPropertyChangeListener(this);
3748                        contrastField.setValue(new Integer(0));
3749
3750                        contrastSlider = new JSlider(JSlider.HORIZONTAL, -100, 100, 0);
3751                        contrastSlider.setMajorTickSpacing(20);
3752                        contrastSlider.setPaintTicks(true);
3753                        contrastSlider.setPaintLabels(true);
3754                        contrastSlider.addChangeListener(this);
3755                        contrastSlider.setBorder(BorderFactory.createEmptyBorder(0, 0, 10,0));
3756
3757                        JPanel contentPane = (JPanel) getContentPane();
3758                        contentPane.setLayout(new BorderLayout(5, 5));
3759                        contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
3760                        contentPane.setPreferredSize(new Dimension(500, 300));
3761
3762                        JPanel brightPane = new JPanel();
3763                        brightPane.setBorder(new TitledBorder(bLabel+"%"));
3764                        brightPane.setLayout(new BorderLayout());
3765                        brightPane.add(brightField, BorderLayout.NORTH);
3766                        brightPane.add(brightSlider, BorderLayout.CENTER);
3767
3768                        JPanel contrastPane = new JPanel();
3769                        contrastPane.setBorder(new TitledBorder(cLabel+"%"));
3770                        contrastPane.setLayout(new BorderLayout());
3771                        contrastPane.add(contrastField, BorderLayout.NORTH);
3772                        contrastPane.add(contrastSlider, BorderLayout.CENTER);
3773
3774                        JPanel mainPane = new JPanel();
3775                        mainPane.setLayout(new GridLayout(2, 1, 5, 5));
3776                        mainPane.add(brightPane);
3777                        mainPane.add(contrastPane);
3778                        contentPane.add(mainPane, BorderLayout.CENTER);
3779
3780                        // add OK and CANCEL buttons
3781                        JPanel confirmP = new JPanel();
3782                        JButton button = new JButton("   Ok   ");
3783                        button.setMnemonic(KeyEvent.VK_O);
3784                        button.setActionCommand("Ok_brightness_change");
3785                        button.addActionListener(this);
3786                        confirmP.add(button);
3787                        button = new JButton("Cancel");
3788                        button.setMnemonic(KeyEvent.VK_C);
3789                        button.setActionCommand("Cancel_brightness_change");
3790                        button.addActionListener(this);
3791                        confirmP.add(button);
3792
3793                        button = new JButton("Apply");
3794                        button.setMnemonic(KeyEvent.VK_A);
3795                        button.setActionCommand("Apply_brightness_change");
3796                        button.addActionListener(this);
3797                        confirmP.add(button);
3798
3799                        contentPane.add(confirmP, BorderLayout.SOUTH);
3800                        contentPane.add(new JLabel(" "), BorderLayout.NORTH);
3801
3802                        Point l = getParent().getLocation();
3803                        Dimension d = getParent().getPreferredSize();
3804                        l.x += 300;
3805                        l.y += 200;
3806                        setLocation(l);
3807                        pack();
3808                }
3809
3810                public void actionPerformed(ActionEvent e) {
3811                        String cmd = e.getActionCommand();
3812
3813                        if (cmd.equals("Ok_brightness_change")
3814                                        || cmd.equals("Apply_brightness_change")) {
3815                                int b = ((Number) brightField.getValue()).intValue();
3816                                int c = ((Number) contrastField.getValue()).intValue();
3817
3818                                applyBrightContrast(b, c);
3819
3820                                if (cmd.startsWith("Ok")) {
3821                                        bLevel = b;
3822                                        cLevel = c;
3823                                        setVisible(false);
3824                                }
3825                        }
3826                        else if (cmd.equals("Cancel_brightness_change")) {
3827                                applyBrightContrast(bLevel, cLevel);
3828                                setVisible(false);
3829                        }
3830                }
3831
3832                /** Listen to the slider. */
3833                public void stateChanged(ChangeEvent e) {
3834                        Object source = e.getSource();
3835
3836                        if (!(source instanceof JSlider)) {
3837                                return;
3838                        }
3839
3840                        JSlider slider = (JSlider) source;
3841                        int value = slider.getValue();
3842                        if (slider.equals(brightSlider)) {
3843                                brightField.setValue(new Integer(value));
3844                        }
3845                        else if (slider.equals(contrastSlider)) {
3846                                contrastField.setValue(new Integer(value));
3847                        }
3848                }
3849
3850                /**
3851                 * Listen to the text field. This method detects when the value of the
3852                 * text field changes.
3853                 */
3854                public void propertyChange(PropertyChangeEvent e) {
3855                        Object source = e.getSource();
3856                        if ("value".equals(e.getPropertyName())) {
3857                                Number num = (Number) e.getNewValue();
3858                                if (num == null) {
3859                                        return;
3860                                }
3861
3862                                double value = num.doubleValue();
3863                                if (value > 100) {
3864                                        value = 100;
3865                                }
3866                                else if (value < -100) {
3867                                        value = -100;
3868                                }
3869
3870                                if (source.equals(brightField) && (brightSlider != null)) {
3871                                        brightSlider.setValue((int) value);
3872                                }
3873                                else if (source.equals(contrastField)
3874                                                && (contrastSlider != null)) {
3875                                        contrastSlider.setValue((int) value);
3876                                }
3877                        }
3878                }
3879
3880                private void applyBrightContrast(int blevel, int clevel) {
3881                        // do not separate autogain and simple contrast process
3882                        //            ImageFilter filter = new BrightnessFilter(blevel, clevel);
3883                        //            image = createImage(new FilteredImageSource(imageProducer, filter));
3884                        //            imageComponent.setImage(image);
3885                        //            zoomTo(zoomFactor);
3886
3887                        // separate autodain and simple contrast process
3888                        if (doAutoGainContrast && gainBias!= null) {
3889                                autoGainBias[0] = gainBias[0]*(1+((double)clevel)/100.0);
3890                                autoGainBias[1] = gainBias[1]*(1+((double)blevel)/100.0);
3891                                applyAutoGain(autoGainBias, null);
3892                        } 
3893                        else {
3894                                ImageFilter filter = new BrightnessFilter(blevel, clevel);
3895                                image = createImage(new FilteredImageSource(imageProducer, filter));
3896                                imageComponent.setImage(image);
3897                                zoomTo(zoomFactor);           
3898                        }
3899                }
3900
3901        } // private class ContrastSlider extends JDialog implements ActionListener
3902
3903}