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,
125 * read <a 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                        // Convert JPG and BMP images to TYPE_INT_RGB so ImageIO.write succeeds
1417                        if (bi.getType() == BufferedImage.TYPE_INT_ARGB &&
1418                                (type.equals("JPEG") || type.equals("BMP"))) {
1419                            BufferedImage newImage = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_INT_RGB);
1420                            Graphics g = newImage.createGraphics();
1421                            g.drawImage(bi, 0, 0, Color.BLACK, null);
1422                            g.dispose();
1423                            bi = newImage;
1424                        }
1425                }
1426                catch (OutOfMemoryError err) {
1427                        toolkit.beep();
1428                        JOptionPane.showMessageDialog(this, err.getMessage(), getTitle(),
1429                                        JOptionPane.ERROR_MESSAGE);
1430                        return;
1431                }
1432
1433                Tools.saveImageAs(bi, choosedFile, type);
1434
1435                bi = null;
1436
1437                viewer.showStatus("Current image saved to: " + fname);
1438
1439                try {
1440                        RandomAccessFile rf = new RandomAccessFile(choosedFile, "r");
1441                        long size = rf.length();
1442                        rf.close();
1443                        viewer.showStatus("File size (bytes): " + size);
1444                }
1445                catch (Exception ex) {
1446                        log.debug("File {} size:", choosedFile.getName(), ex);
1447                }
1448        }
1449
1450        public void actionPerformed(ActionEvent e) {
1451                try {
1452                        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1453
1454                        Object source = e.getSource();
1455                        String cmd = e.getActionCommand();
1456
1457                        if (cmd.equals("Close")) {
1458                                dispose(); // terminate the application
1459                                ((Vector) rotateRelatedItems).setSize(0);
1460                        }
1461                        else if (cmd.startsWith("Save image as ")) {
1462                                String filetype = null;
1463                                if (cmd.equals("Save image as jpeg")) {
1464                                        filetype = Tools.FILE_TYPE_JPEG;
1465                                }
1466                                else if (cmd.equals("Save image as tiff")) {
1467                                        filetype = Tools.FILE_TYPE_TIFF;
1468                                }
1469                                else if (cmd.equals("Save image as png")) {
1470                                        filetype = Tools.FILE_TYPE_PNG;
1471                                }
1472                                else if (cmd.equals("Save image as gif")) {
1473                                        filetype = Tools.FILE_TYPE_GIF;
1474                                }
1475                                else if (cmd.equals("Save image as bmp")) {
1476                                        filetype = Tools.FILE_TYPE_BMP;
1477                                }
1478
1479                                try {
1480                                        saveImageAs(filetype);
1481                                }
1482                                catch (Exception ex) {
1483                                        toolkit.beep();
1484                                        JOptionPane.showMessageDialog(this, ex, getTitle(),
1485                                                        JOptionPane.ERROR_MESSAGE);
1486                                }
1487                        }
1488                        else if (cmd.equals("Write selection to image")) {
1489                                if ((getSelectedArea().width <= 0)
1490                                                || (getSelectedArea().height <= 0)) {
1491                                        JOptionPane
1492                                        .showMessageDialog(
1493                                                        this,
1494                                                        "No data to write.\nUse Shift+Mouse_drag to select an image area.",
1495                                                        "HDFView", JOptionPane.INFORMATION_MESSAGE);
1496                                        return;
1497                                }
1498
1499                                TreeView treeView = viewer.getTreeView();
1500                                TreeNode node = treeView.findTreeNode(dataset);
1501                                Group pGroup = (Group) ((DefaultMutableTreeNode) node
1502                                                .getParent()).getUserObject();
1503                                TreeNode root = dataset.getFileFormat().getRootNode();
1504
1505                                if (root == null) {
1506                                        return;
1507                                }
1508
1509                                Vector list = new Vector(dataset.getFileFormat()
1510                                                .getNumberOfMembers() + 5);
1511                                DefaultMutableTreeNode theNode = null;
1512                                Enumeration local_enum = ((DefaultMutableTreeNode) root)
1513                                .depthFirstEnumeration();
1514
1515                                while (local_enum.hasMoreElements()) {
1516                                        theNode = (DefaultMutableTreeNode) local_enum.nextElement();
1517                                        list.add(theNode.getUserObject());
1518                                }
1519
1520                                NewDatasetDialog dialog = new NewDatasetDialog((JFrame) viewer,
1521                                                pGroup, list, this);
1522                                dialog.setVisible(true);
1523
1524                                HObject obj = (HObject) dialog.getObject();
1525                                if (obj != null) {
1526                                        Group pgroup = dialog.getParentGroup();
1527                                        try {
1528                                                treeView.addObject(obj, pgroup);
1529                                        }
1530                                        catch (Exception ex) {
1531                                                log.debug("Write selection to image:", ex);
1532                                        }
1533                                }
1534
1535                                list.setSize(0);
1536                        }
1537                        else if (cmd.equals("Zoom in")) {
1538                                zoomIn();
1539                        }
1540                        else if (cmd.equals("Zoom out")) {
1541                                zoomOut();
1542                        }
1543                        else if (cmd.equals("Edit palette")) {
1544                                showColorTable();
1545                        }
1546                        else if (cmd.equals("Import palette")) {
1547                                JFileChooser fchooser = new JFileChooser(ViewProperties
1548                                                .getWorkDir());
1549                                int returnVal = fchooser.showOpenDialog(this);
1550
1551                                if (returnVal != JFileChooser.APPROVE_OPTION) {
1552                                        return;
1553                                }
1554
1555                                File choosedFile = fchooser.getSelectedFile();
1556                                if (choosedFile == null || choosedFile.isDirectory()) {
1557                                        return;
1558                                }
1559
1560                                Vector<String> palList = ViewProperties.getPaletteList();
1561                                String palPath = choosedFile.getAbsolutePath();
1562                                if(!palList.contains(palList))
1563                                        palList.addElement(palPath);
1564                        }
1565                        else if (cmd.equals("Export palette")) {
1566                                if (imagePalette == null)
1567                                        return;
1568
1569                                String wd =ViewProperties.getWorkDir()+File.separator;
1570                                JFileChooser fchooser = new JFileChooser(wd);
1571                                FileNameExtensionFilter filter = new FileNameExtensionFilter("Color lookup table", "lut");
1572                                File pfile = Tools.checkNewFile(wd, ".lut");
1573                                fchooser.setSelectedFile(pfile);
1574                                fchooser.setFileFilter(filter);
1575                                int returnVal = fchooser.showOpenDialog(this);
1576
1577                                if (returnVal != JFileChooser.APPROVE_OPTION) {
1578                                        return;
1579                                }
1580
1581                                File choosedFile = fchooser.getSelectedFile();
1582                                if (choosedFile == null || choosedFile.isDirectory()) {
1583                                        return;
1584                                }
1585
1586                                if (choosedFile.exists())
1587                                {
1588                                        int newFileFlag = JOptionPane.showConfirmDialog(this,
1589                                                        "File exists. Do you want to replace it ?",
1590                                                                        this.getTitle(),
1591                                                                        JOptionPane.YES_NO_OPTION);
1592                                        if (newFileFlag == JOptionPane.NO_OPTION) {
1593                                                return;
1594                                        }
1595                                }
1596
1597                                PrintWriter out = null;
1598
1599                                try {
1600                                        out = new PrintWriter(new BufferedWriter(new FileWriter(choosedFile)));
1601                                } 
1602                                catch (Exception ex) { 
1603                                        out = null; 
1604                                }
1605
1606                                if (out == null)
1607                                        return;
1608
1609                                int cols = 3;
1610                                int rows = 256;
1611                                int rgb = 0;
1612                                for (int i=0; i<rows; i++) {
1613                                        out.print(i);
1614                                        for (int j=0; j<cols; j++) {
1615                                                out.print(' ');
1616                                                rgb = imagePalette[j][i];
1617                                                if (rgb<0) rgb += 256;
1618                                                out.print(rgb);
1619                                        }
1620                                        out.println();
1621                                }
1622
1623                                out.flush();
1624                                out.close();
1625                        }
1626                        else if (cmd.equals("Set data range")) {
1627
1628                                if (originalRange==null || originalRange[0]== originalRange[1])
1629                                        return;
1630
1631                                // call only once
1632                                if (dataDist == null) {
1633                                        dataDist = new int[256];
1634                                        Tools.findDataDist(data, dataDist, originalRange);
1635                                }
1636
1637                                DataRangeDialog drd = new DataRangeDialog((JFrame) viewer, dataRange, originalRange,dataDist);
1638                                double[] drange = drd.getRange();
1639
1640                                if ((drange == null)
1641                                                || (drange[0] == drange[1])
1642                                                || ((drange[0] == dataRange[0]) && (drange[1] == dataRange[1]))) {
1643                                        return;
1644                                }
1645
1646                                applyDataRange(drange);
1647                        }
1648                        else if (cmd.equals("Flip horizontal")) {
1649                                flip(FLIP_HORIZONTAL);
1650                        }
1651                        else if (cmd.equals("Flip vertical")) {
1652                                flip(FLIP_VERTICAL);
1653                        }
1654                        else if (cmd.startsWith("Rotate")) {
1655                                if (cmd.equals("Rotate clockwise"))
1656                                        rotate(ROTATE_CW_90);
1657                                else
1658                                        rotate(ROTATE_CCW_90);
1659
1660                                int n = rotateRelatedItems.size();
1661                                for (int i = 0; i < n; i++) {
1662                                        boolean itemState = (rotateCount == 0);
1663                                        ((javax.swing.JComponent) rotateRelatedItems.get(i))
1664                                        .setEnabled(itemState);
1665                                }
1666                        }
1667                        else if (cmd.equals("Show image value")) {
1668                                boolean b = ((JCheckBoxMenuItem) source).getState();
1669                                setValueVisible(b);
1670                        }
1671                        else if (cmd.startsWith("Go to frame")) {
1672                                int page = 0;
1673                                try {
1674                                        page = Integer.parseInt(frameField.getText().trim())-indexBase;
1675                                }
1676                                catch (Exception ex) {
1677                                        page = -1;
1678                                }
1679
1680                                gotoPage (page);
1681                        }
1682                        else if (cmd.startsWith("Show animation")) {
1683                                setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1684                                new Animation((JFrame) viewer, dataset);
1685                                setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1686                        }
1687                        else if (cmd.startsWith("Animation speed")) {
1688                                animationSpeed = Integer.parseInt((cmd
1689                                                .substring(cmd.length() - 2)).trim());
1690                        }
1691
1692                        else if (cmd.startsWith("Contour")) {
1693                                int level = Integer.parseInt(cmd.substring(cmd.length() - 1));
1694                                contour(level);
1695                        }
1696                        else if (cmd.startsWith("Brightness")) {
1697                                if (contrastSlider == null) {
1698                                        contrastSlider = new ContrastSlider((JFrame) viewer, image.getSource());
1699                                }
1700                                contrastSlider.setVisible(true);
1701                        }
1702                        else if (cmd.equals("Show chart")) {
1703                                showHistogram();
1704                        }
1705                        else if (cmd.equals("First page")) {
1706                                firstPage();
1707                        }
1708                        else if (cmd.equals("Previous page")) {
1709                                previousPage();
1710                        }
1711                        else if (cmd.equals("Next page")) {
1712                                nextPage();
1713                        }
1714                        else if (cmd.equals("Last page")) {
1715                                lastPage();
1716                        }
1717                        else if (cmd.equals("Show statistics")) {
1718                                try {
1719                                        double[] minmax = new double[2];
1720                                        double[] stat = new double[2];
1721
1722                                        Object theData = null;
1723                                        theData = getSelectedData();
1724
1725                                        if (theData == null) {
1726                                                theData = data;
1727                                        }
1728
1729                                        Tools.findMinMax(theData, minmax, dataset.getFillValue());
1730                                        if (Tools.computeStatistics(theData, stat, dataset.getFillValue()) > 0) {
1731                                                String statistics = "Min                      = "
1732                                                + minmax[0] + "\nMax                      = "
1733                                                + minmax[1] + "\nMean                     = "
1734                                                + stat[0] + "\nStandard deviation = " + stat[1];
1735                                                JOptionPane.showMessageDialog(this, statistics,
1736                                                                "Statistics", JOptionPane.INFORMATION_MESSAGE);
1737                                        }
1738                                }
1739                                catch (Exception ex) {
1740                                        toolkit.beep();
1741                                        JOptionPane.showMessageDialog((JFrame) viewer, ex,
1742                                                        getTitle(), JOptionPane.ERROR_MESSAGE);
1743                                }
1744                        }
1745                        else if (cmd.equals("Select all data")) {
1746                                try {
1747                                        selectAll();
1748                                }
1749                                catch (Exception ex) {
1750                                        toolkit.beep();
1751                                        JOptionPane.showMessageDialog((JFrame) viewer, ex,
1752                                                        getTitle(), JOptionPane.ERROR_MESSAGE);
1753                                }
1754                        }
1755                }
1756                finally {
1757                        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1758                }
1759
1760        }
1761
1762        public void dispose() {
1763                // reload the data when it is displayed next time
1764                // because the display type (table or image) may be
1765                // different.
1766                if (!dataset.isImage()) {
1767                        dataset.clearData();
1768                }
1769
1770                data = null;
1771                image = null;
1772                imageByteData = null;
1773                imageComponent = null;
1774                autoGainData = null;
1775                ((Vector) rotateRelatedItems).setSize(0);
1776                System.runFinalization();
1777                System.gc();
1778
1779                viewer.removeDataView(this);
1780
1781                super.dispose();
1782        }
1783
1784        // Implementing DataView.
1785        public HObject getDataObject() {
1786                return dataset;
1787        }
1788
1789        public byte[] getImageByteData() {
1790                return imageByteData;
1791        }
1792
1793        /**
1794         * Returns the selected data values.
1795         * 
1796         * @return the selected data object.
1797         */
1798        public Object getSelectedData() {
1799                Object selectedData = null;
1800
1801                int cols = imageComponent.originalSelectedArea.width;
1802                int rows = imageComponent.originalSelectedArea.height;
1803
1804                if ((cols <= 0) || (rows <= 0)) {
1805                        return null; // no data is selected
1806                }
1807
1808                int size = cols * rows;
1809                if (isTrueColor) {
1810                        size *= 3;
1811                }
1812
1813                if (NT == 'B') {
1814                        selectedData = new byte[size];
1815                }
1816                else if (NT == 'S') {
1817                        selectedData = new short[size];
1818                }
1819                else if (NT == 'I') {
1820                        selectedData = new int[size];
1821                }
1822                else if (NT == 'J') {
1823                        selectedData = new long[size];
1824                }
1825                else if (NT == 'F') {
1826                        selectedData = new float[size];
1827                }
1828                else if (NT == 'D') {
1829                        selectedData = new double[size];
1830                }
1831                else {
1832                        return null;
1833                }
1834
1835                int r0 = imageComponent.originalSelectedArea.y;
1836                int c0 = imageComponent.originalSelectedArea.x;
1837                int w = imageComponent.originalSize.width;
1838                int h = imageComponent.originalSize.height;
1839
1840                // transfer location to the original coordinator
1841                if (isHorizontalFlipped) {
1842                        c0 = w - 1 - c0 - cols;
1843                }
1844
1845                if (isVerticalFlipped) {
1846                        r0 = h - 1 - r0 - rows;
1847                }
1848
1849                int idx_src = 0, idx_dst = 0;
1850                if (isTrueColor) {
1851                        int imageSize = w * h;
1852                        if (isPlaneInterlace) {
1853                                for (int j = 0; j < 3; j++) {
1854                                        int plane = imageSize * j;
1855                                        for (int i = 0; i < rows; i++) {
1856                                                idx_src = plane + (r0 + i) * w + c0;
1857                                                System.arraycopy(data, idx_src, selectedData, idx_dst,
1858                                                                cols);
1859                                                idx_dst += cols;
1860                                        }
1861                                }
1862                        }
1863                        else {
1864                                int numberOfDataPoints = cols * 3;
1865                                for (int i = 0; i < rows; i++) {
1866                                        idx_src = (r0 + i) * w + c0;
1867                                        System.arraycopy(data, idx_src * 3, selectedData, idx_dst,
1868                                                        numberOfDataPoints);
1869                                        idx_dst += numberOfDataPoints;
1870                                }
1871                        }
1872                }
1873                else { // indexed image
1874                        for (int i = 0; i < rows; i++) {
1875                                idx_src = (r0 + i) * w + c0;
1876                                System.arraycopy(data, idx_src, selectedData, idx_dst, cols);
1877                                idx_dst += cols;
1878                        }
1879                }
1880
1881                return selectedData;
1882        }
1883
1884        /**
1885         * returns the selected area of the image
1886         * 
1887         * @return the rectangle of the selected image area.
1888         */
1889        public Rectangle getSelectedArea() {
1890                return imageComponent.originalSelectedArea;
1891        }
1892
1893        /** @return true if the image is a truecolor image. */
1894        public boolean isTrueColor() {
1895                return isTrueColor;
1896        }
1897
1898        /** @return true if the image interlace is plance interlace. */
1899        public boolean isPlaneInterlace() {
1900                return isPlaneInterlace;
1901        }
1902
1903        public void setImage(Image img) {
1904                image = img;
1905                imageComponent.setImage(img);
1906
1907                setImageDirection();
1908        }
1909
1910        private void setImageDirection() {
1911                boolean isHF = isHorizontalFlipped;
1912                boolean isVF = isVerticalFlipped;
1913                int rc = rotateCount;
1914
1915                if (isHF || isVF || rc!=0) {
1916                        isHorizontalFlipped = false;
1917                        isVerticalFlipped = false;
1918                        rotateCount = 0;        
1919
1920                        if (isHF)
1921                                flip(FLIP_HORIZONTAL);
1922
1923                        if (isVF)
1924                                flip(FLIP_VERTICAL);
1925
1926                        while (rc > 0)  {
1927                                rotate(ROTATE_CW_90);
1928                                rc--;
1929                        }
1930
1931                        while (rc < 0)  {
1932                                rotate(ROTATE_CCW_90);
1933                                rc++;
1934                        }
1935
1936                } 
1937                else {
1938                        if (origin == 1)
1939                                flip(FLIP_VERTICAL);
1940                        else if (origin == 2)
1941                                flip(FLIP_HORIZONTAL);
1942                        if (origin == 3) {
1943                                rotate(ROTATE_CW_90);
1944                                rotate(ROTATE_CW_90);
1945                        }
1946                }
1947
1948                zoomTo(zoomFactor);        
1949        }
1950
1951        public byte[][] getPalette() {
1952                return imagePalette;
1953        }
1954
1955        public void setPalette(byte[][] pal) {
1956                imagePalette = pal;
1957                paletteComponent.updatePalette(pal);
1958        }
1959
1960        private void gotoPage(long idx) {
1961                if (dataset.getRank() < 3 ||
1962                                idx == (curFrame-indexBase) ) {
1963                        return;
1964                }
1965
1966                long[] start = dataset.getStartDims();
1967                int[] selectedIndex = dataset.getSelectedIndex();
1968                long[] dims = dataset.getDims();
1969
1970                if ((idx < 0) || (idx >= dims[selectedIndex[2]])) {
1971                        toolkit.beep();
1972                        JOptionPane.showMessageDialog(this,
1973                                        "Frame number must be between "+indexBase+" and "
1974                                        + (dims[selectedIndex[2]] - 1+indexBase), getTitle(),
1975                                        JOptionPane.ERROR_MESSAGE);
1976                        return;
1977                }
1978
1979                setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1980
1981                start[selectedIndex[2]] = idx;
1982                curFrame = idx+indexBase;
1983                dataset.clearData();
1984                image = null;
1985                gainBias = null;
1986                imageComponent.setImage(getImage());
1987                frameField.setText(String.valueOf(curFrame));
1988
1989                isHorizontalFlipped = false;
1990                isVerticalFlipped = false;
1991                rotateCount = 0;        
1992
1993                if (origin == 1)
1994                        flip(FLIP_VERTICAL);
1995                else if (origin == 2)
1996                        flip(FLIP_HORIZONTAL);
1997                if (origin == 3) {
1998                        rotate(ROTATE_CW_90);
1999                        rotate(ROTATE_CW_90);
2000                }
2001
2002                setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
2003
2004                updateUI();
2005        }
2006
2007        /**
2008         * Creates a RGB indexed image of 256 colors.
2009         * 
2010         * @param imageData
2011         *            the byte array of the image data.
2012         * @param palette
2013         *            the color lookup table.
2014         * @param w
2015         *            the width of the image.
2016         * @param h
2017         *            the height of the image.
2018         * @return the image.
2019         */
2020        private Image createIndexedImage(byte[] imageData, byte[][] palette, int w, int h) 
2021        {
2022                bufferedImage = (BufferedImage)Tools.createIndexedImage(bufferedImage, imageData, palette, w, h);
2023                adjustAlpha(bufferedImage, 0, invalidValueIndex);        
2024
2025                return bufferedImage;
2026        }
2027
2028        /**
2029         * Creates a true color image.
2030         * <p>
2031         * The data may be arranged in one of two ways: by pixel or by plane. In
2032         * both cases, the dataset will have a dataspace with three dimensions,
2033         * height, width, and components.
2034         * <p>
2035         * For HDF4, the interlace modes specify orders for the dimensions as:
2036         * 
2037         * <pre>
2038         * INTERLACE_PIXEL = [width][height][pixel components]
2039         *            INTERLACE_PLANE = [pixel components][width][height]
2040         * </pre>
2041         * <p>
2042         * For HDF5, the interlace modes specify orders for the dimensions as:
2043         * 
2044         * <pre>
2045         * INTERLACE_PIXEL = [height][width][pixel components]
2046         *            INTERLACE_PLANE = [pixel components][height][width]
2047         * </pre>
2048         * <p>
2049         * 
2050         * @param imageData
2051         *            the byte array of the image data.
2052         * @param planeInterlace
2053         *            flag if the image is plane intelace.
2054         * @param w
2055         *            the width of the image.
2056         * @param h
2057         *            the height of the image.
2058         * @return the image.
2059         */
2060        private Image createTrueColorImage(byte[] imageData, boolean planeInterlace,
2061                        int w, int h) 
2062        {
2063
2064                if (bufferedImage == null)
2065                        bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
2066
2067                final int[] pixels = ( (DataBufferInt) bufferedImage.getRaster().getDataBuffer() ).getData();
2068                int len = pixels.length;
2069
2070                int idx = 0, r = 0, g = 0, b = 0;
2071                for (int i = 0; i < h; i++) {
2072                        for (int j = 0; j < w; j++) {
2073                                if (planeInterlace) {
2074                                        r = ((int)imageData[idx] & 0xff)<<16;
2075                                        g = ((int)imageData[len + idx] & 0xff)<<8;
2076                                        b = ((int)imageData[len * 2 + idx] & 0xff);
2077                                }
2078                                else {
2079                                        r = ((int)imageData[idx * 3] & 0xff)<<16;
2080                                        g = ((int)imageData[idx * 3 + 1] & 0xff)<<8;
2081                                        b = ((int)imageData[idx * 3 + 2] & 0xff);
2082                                }
2083                                pixels[idx++] = 0xff000000 | r | g | b;
2084                        } 
2085                } 
2086
2087                adjustAlpha(bufferedImage, 0, invalidValueIndex);        
2088                return bufferedImage;
2089        }
2090
2091        private boolean applyImageFilter(ImageFilter filter) {
2092                boolean status = true;
2093                ImageProducer imageProducer = image.getSource();
2094
2095                try {
2096                        image = createImage(new FilteredImageSource(imageProducer, filter));
2097                        imageComponent.setImage(image);
2098                        zoomTo(zoomFactor);
2099                }
2100                catch (Throwable err) {
2101                        toolkit.beep();
2102                        JOptionPane.showMessageDialog(this, err.getMessage(), getTitle(),
2103                                        JOptionPane.ERROR_MESSAGE);
2104                        status = false;
2105                }
2106
2107                return status;
2108        }
2109
2110        private void applyDataRange(double[] newRange) {
2111                if (doAutoGainContrast && gainBias!= null) {
2112                        applyAutoGain(gainBias_current, newRange);
2113                } 
2114                else {
2115                        int w = dataset.getWidth();
2116                        int h = dataset.getHeight();
2117
2118                        invalidValueIndex.clear(); // data range changed. need to reset invalid values
2119                        imageByteData = Tools.getBytes(data, newRange, w, h, !dataset
2120                                        .isDefaultImageOrder(), dataset.getFilteredImageValues(), true,
2121                                        null, invalidValueIndex);
2122
2123                        image = createIndexedImage(imageByteData, imagePalette, w, h);
2124                        setImage(image);
2125                        zoomTo(zoomFactor);
2126                        paletteComponent.updateRange(newRange);
2127                }
2128
2129                dataRange[0] = newRange[0];
2130                dataRange[1] = newRange[1];
2131        }
2132
2133        /** PaletteComponent draws the palette on the side of the image. */
2134        private class PaletteComponent extends JComponent {
2135                private static final long serialVersionUID = -5194383032992628565L;
2136                private Color[] colors = null;
2137                private double[] pixelData = null;
2138                private Dimension paintSize = null;
2139                java.text.DecimalFormat format;
2140                double[] dRange = null;
2141
2142                private PaletteComponent(byte[][] palette, double[] range) {
2143                        paintSize = new Dimension(25, 2);
2144                        format = new java.text.DecimalFormat("0.00E0");
2145                        dRange = range;
2146                        double unsigned_celling = 0;
2147
2148                        if ((palette != null) && (range != null)) {
2149                                double ratio = (dRange[1] - dRange[0]) / 255;
2150
2151                                pixelData = new double[256];
2152                                for (int i = 0; i < 256; i++) {
2153                                        pixelData[i] = (dRange[0] + ratio * i);
2154                                }
2155                        }
2156
2157                        updatePalette(palette);
2158
2159                        setPreferredSize(new Dimension(paintSize.width + 60,
2160                                        paintSize.height * 256));
2161                        setVisible(true);
2162                }
2163
2164                private void updatePalette(byte[][] palette) {
2165                        if ((palette != null) && (dRange != null)) {
2166                                colors = new Color[256];
2167
2168                                int r, g, b;
2169                                for (int i = 0; i < 256; i++) {
2170                                        r = (int) palette[0][i];
2171                                        if (r < 0) {
2172                                                r += 256;
2173                                        }
2174                                        g = (int) palette[1][i];
2175                                        if (g < 0) {
2176                                                g += 256;
2177                                        }
2178                                        b = (int) palette[2][i];
2179                                        if (b < 0) {
2180                                                b += 256;
2181                                        }
2182
2183                                        colors[i] = new Color(r, g, b);
2184                                }
2185                        }
2186
2187                        repaint();
2188                }
2189
2190                private void updateRange(double[] newRange) {
2191                        if (newRange == null) {
2192                                return;
2193                        }
2194
2195                        dRange = newRange;
2196                        double ratio = (dRange[1] - dRange[0]) / 255;
2197                        for (int i = 0; i < 256; i++) {
2198                                pixelData[i] = (dRange[0] + ratio * i);
2199                        }
2200
2201                        repaint();
2202                }
2203
2204                public void paint(Graphics g) {
2205                        if ((colors == null) && (pixelData == null)) {
2206                                return;
2207                        }
2208
2209                        Font font = g.getFont();
2210                        g.setFont(new Font(font.getName(), font.getStyle(), 12));
2211                        for (int i = 0; i < 256; i++) {
2212                                g.setColor(colors[i]);
2213                                g.fillRect(0, paintSize.height * i, paintSize.width,
2214                                                paintSize.height);
2215                        }
2216
2217                        g.setColor(Color.black);
2218                        for (int i = 0; i < 25; i++) {
2219                                g.drawString(format.format(pixelData[i * 10]),
2220                                                paintSize.width + 5, 10 + paintSize.height * i * 10);
2221                        }
2222                        g.drawString(format.format(pixelData[255]), paintSize.width + 5,
2223                                        paintSize.height * 255);
2224                }
2225        }
2226
2227        /** ImageComponent draws the image. */
2228        private class ImageComponent extends JComponent implements MouseListener,
2229        MouseMotionListener, MouseWheelListener {
2230                private static final long serialVersionUID = -2690648149547151532L;
2231                private Dimension originalSize, imageSize;
2232                private Image image;
2233                private Point startPosition, currentPosition; // mouse clicked position
2234                private Rectangle selectedArea, originalSelectedArea;
2235                private StringBuffer strBuff; // to hold display value
2236                private int yMousePosition = 0; /*
2237                 * the vertical position of the current
2238                 * mouse
2239                 */
2240                private Dimension scrollDim = null;
2241                private JScrollBar hbar = null;
2242                private JScrollBar vbar = null;
2243
2244                private ImageComponent(Image img) {
2245                        image = img;
2246                        imageSize = new Dimension(image.getWidth(this), image
2247                                        .getHeight(this));
2248                        originalSize = imageSize;
2249                        selectedArea = new Rectangle();
2250                        originalSelectedArea = new Rectangle();
2251                        setPreferredSize(imageSize);
2252                        strBuff = new StringBuffer();
2253
2254                        addMouseListener(this);
2255                        addMouseMotionListener(this);
2256                        addMouseWheelListener(this);
2257                }
2258
2259                public void paint(Graphics g) {
2260                        if (g instanceof Graphics2D && (zoomFactor<0.99)) {
2261                                Graphics2D g2 = (Graphics2D) g;
2262
2263                                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
2264                                                RenderingHints.VALUE_INTERPOLATION_BILINEAR);
2265                                Image scaledImg = multiBiliner(image, imageSize.width, imageSize.height, true);
2266                                g2.drawImage(scaledImg, 0, 0, imageSize.width, imageSize.height, this);
2267
2268                        } 
2269                        else
2270                                g.drawImage(image, 0, 0, imageSize.width, imageSize.height, this);
2271
2272                        if ((selectedArea.width > 0) && (selectedArea.height > 0)) {
2273                                g.setColor(Color.red);
2274                                g.drawRect(selectedArea.x, selectedArea.y, selectedArea.width,
2275                                                selectedArea.height);
2276                        }
2277                }
2278
2279                /**
2280                 * Create an image using multiple step bilinear, see details at
2281                 * http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
2282                 *
2283                 * @param img the original image to be scaled
2284                 * @param targetWidth the desired width of the scaled instance
2285                 * @param targetHeight the desired height of the scaled instance,
2286                 * @return a scaled version of the original 
2287                 */
2288                private Image multiBiliner(Image img, int targetWidth, int targetHeight, boolean highquality)
2289                {
2290                        Image ret = img;
2291                        int w = img.getWidth(null)/2;
2292                        int h = img.getHeight(null)/2;
2293
2294                        // only do multiple step bilinear for down scale more than two times
2295                        if (!highquality || w <=targetWidth || h <=targetHeight)
2296                                return ret;
2297
2298                        int type = BufferedImage.TYPE_INT_RGB;
2299                        if (image instanceof BufferedImage) {
2300                                BufferedImage tmp = (BufferedImage)image;
2301                                if (tmp.getColorModel().hasAlpha())
2302                                        type = BufferedImage.TYPE_INT_ARGB;
2303                        } 
2304                        else {
2305                                PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
2306                                ColorModel cm = pg.getColorModel();
2307                                if (cm!=null && cm.hasAlpha())
2308                                        type = BufferedImage.TYPE_INT_ARGB;
2309                        }
2310
2311                        do {
2312                                BufferedImage tmp = new BufferedImage(w, h, type);
2313                                Graphics2D g2 = tmp.createGraphics();
2314                                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
2315                                g2.drawImage(ret, 0, 0, w, h, null);
2316                                g2.dispose();
2317                                ret = tmp;
2318
2319                                w /= 2;
2320                                if (w < targetWidth) {
2321                                        w = targetWidth;
2322                                }
2323
2324                                h /= 2;
2325                                if (h < targetHeight) {
2326                                        h = targetHeight;
2327                                }
2328
2329                        } while (w != targetWidth || h != targetHeight);
2330
2331                        return ret;
2332                }
2333                public void mousePressed(MouseEvent e) {
2334                        startPosition = e.getPoint();
2335                        selectedArea.setBounds(startPosition.x, startPosition.y, 0, 0);
2336                        scrollDim = imageScroller.getSize();
2337                        hbar = imageScroller.getHorizontalScrollBar();
2338                        vbar = imageScroller.getVerticalScrollBar();
2339
2340                        if ((e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK) {
2341                                setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
2342                        }
2343                        else {
2344                                setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
2345                        }
2346                }
2347
2348                public void mouseClicked(MouseEvent e) {
2349                        startPosition = e.getPoint();
2350                        selectedArea.setBounds(startPosition.x, startPosition.y, 0, 0);
2351
2352                        if (hbar.isVisible()) {
2353                                hbar.setValue(startPosition.x - scrollDim.width / 2);
2354                        }
2355
2356                        if (vbar.isVisible()) {
2357                                vbar.setValue(startPosition.y - scrollDim.height / 2);
2358                        }
2359
2360                        repaint();
2361                }
2362
2363                public void mouseDragged(MouseEvent e) {
2364                        // don't update too often.
2365                        try {
2366                                Thread.sleep(20);
2367                        }
2368                        catch (Exception ex) {
2369                                log.debug("thread sleep:", ex);
2370                        }
2371                        currentPosition = e.getPoint();
2372
2373                        if ((e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK) {
2374                                int x0 = Math.max(0, Math.min(startPosition.x,
2375                                                currentPosition.x));
2376                                int y0 = Math.max(0, Math.min(startPosition.y,
2377                                                currentPosition.y));
2378                                int x1 = Math.min(imageSize.width, Math.max(startPosition.x,
2379                                                currentPosition.x));
2380                                int y1 = Math.min(imageSize.height, Math.max(startPosition.y,
2381                                                currentPosition.y));
2382
2383                                int w = x1 - x0;
2384                                int h = y1 - y0;
2385
2386                                selectedArea.setBounds(x0, y0, w, h);
2387                                double ratio = 1.0 / zoomFactor;
2388
2389                                originalSelectedArea.setBounds((int) (x0 * ratio),
2390                                                (int) (y0 * ratio), (int) (w * ratio),
2391                                                (int) (h * ratio));
2392
2393                                repaint();
2394                        }
2395                        else {
2396                                if (hbar.isVisible()) {
2397                                        int dx = startPosition.x - currentPosition.x;
2398                                        hbar.setValue(hbar.getValue() + dx);
2399                                }
2400
2401                                if (vbar.isVisible()) {
2402                                        int dy = startPosition.y - currentPosition.y;
2403                                        vbar.setValue(vbar.getValue() + dy);
2404                                }
2405                        }
2406                }
2407
2408                public void mouseReleased(MouseEvent e) {
2409                        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
2410                }
2411
2412                public void mouseEntered(MouseEvent e) {
2413                }
2414
2415                public void mouseExited(MouseEvent e) {
2416                }
2417
2418                public void mouseMoved(MouseEvent e) {
2419                        yMousePosition = e.getY();
2420                        showPixelValue(e.getX(), yMousePosition);
2421                }
2422
2423                public void mouseWheelMoved(MouseWheelEvent e) {
2424                        JScrollBar jb = imageScroller.getVerticalScrollBar();
2425                        int us = e.getUnitsToScroll();
2426                        int wr = e.getWheelRotation();
2427                        int n = us * jb.getUnitIncrement();
2428                        int y = jb.getValue();
2429
2430                        if (((y <= 0) && (wr < 0))
2431                                        || (y + jb.getVisibleAmount() * wr >= zoomFactor
2432                                                        * originalSize.height)) {
2433                                return;
2434                        }
2435
2436                        yMousePosition += n;
2437                        jb.setValue(jb.getValue() + n);
2438
2439                        showPixelValue(e.getX(), yMousePosition);
2440                }
2441
2442                private void showPixelValue(int x, int y) {
2443                        if (!valueField.isVisible() || rotateCount != 0) {
2444                                return;
2445                        }
2446
2447                        if (data == null) {
2448                                return;
2449                        }
2450
2451                        x = (int) (x / zoomFactor);
2452                        int w = originalSize.width;
2453
2454                        if ((x < 0) || (x >= w)) {
2455                                return; // out of image bound
2456                        }
2457
2458                        y = (int) (y / zoomFactor);
2459                        int h = originalSize.height;
2460                        if ((y < 0) || (y >= h)) {
2461                                return; // out of image bound
2462                        }
2463
2464                        // transfer location to the original coordinator
2465                        if (isHorizontalFlipped) {
2466                                x = w - 1 - x;
2467                        }
2468
2469                        if (isVerticalFlipped) {
2470                                y = h - 1 - y;
2471                        }
2472
2473                        strBuff.setLength(0); // reset the string buffer
2474                        strBuff.append("x=");
2475                        strBuff.append(x+indexBase);
2476                        strBuff.append(",   y=");
2477                        strBuff.append(y+indexBase);
2478                        strBuff.append(",   value=");
2479
2480                        if (isTrueColor) {
2481                                strBuff.append("(");
2482                                int i0, i1, i2;
2483                                String r, g, b;
2484
2485                                if (isPlaneInterlace) {
2486                                        i0 = y * w + x; // index for the first plane
2487                                        i1 = i0 + w * h; // index for the second plane
2488                                        i2 = i0 + 2 * w * h; // index for the third plane
2489                                }
2490                                else {
2491                                        i0 = 3 * (y * w + x); // index for the first pixel
2492                                        i1 = i0 + 1; // index for the second pixel
2493                                        i2 = i0 + 2; // index for the third pixel
2494                                }
2495
2496                                if (isUnsigned && !isUnsignedConverted) {
2497                                        r = String.valueOf(convertUnsignedPoint(i0));
2498                                        g = String.valueOf(convertUnsignedPoint(i1));
2499                                        b = String.valueOf(convertUnsignedPoint(i2));
2500                                }
2501                                else {
2502                                        r = String.valueOf(Array.get(data, i0));
2503                                        g = String.valueOf(Array.get(data, i1));
2504                                        b = String.valueOf(Array.get(data, i2));
2505                                }
2506
2507                                strBuff.append(r + ", " + g + ", " + b);
2508                                strBuff.append(")");
2509                        } // if (isTrueColor)
2510                        else {
2511
2512                                int idx = y * w + x;
2513                                if (!dataset.isDefaultImageOrder())
2514                                        idx = x*h+y;
2515
2516                                if (isUnsigned && !isUnsignedConverted) {
2517                                        strBuff.append(convertUnsignedPoint(idx));
2518                                }
2519                                else {
2520                                        strBuff.append(Array.get(data, idx));
2521                                }
2522                        }
2523
2524                        valueField.setText(strBuff.toString());
2525                } // private void showPixelValue
2526
2527                private void selectAll() {
2528                        selectedArea.setBounds(0, 0, imageSize.width, imageSize.height);
2529                        originalSelectedArea.setBounds(0, 0, originalSize.width,
2530                                        originalSize.height);
2531                        repaint();
2532                }
2533
2534                private long convertUnsignedPoint(int idx) {
2535                        long l = 0;
2536
2537                        if (NT == 'B') {
2538                                byte b = Array.getByte(data, idx);
2539
2540                                if (b < 0) {
2541                                        l = b + 256;
2542                                }
2543                                else {
2544                                        l = b;
2545                                }
2546                        }
2547                        else if (NT == 'S') {
2548                                short s = Array.getShort(data, idx);
2549                                if (s < 0) {
2550                                        l = s + 65536;
2551                                }
2552                                else {
2553                                        l = s;
2554                                }
2555                        }
2556                        else if (NT == 'I') {
2557                                int i = Array.getInt(data, idx);
2558                                if (i < 0) {
2559                                        l = i + 4294967296L;
2560                                }
2561                                else {
2562                                        l = i;
2563                                }
2564                        }
2565
2566                        return l;
2567                }
2568
2569                private void setImageSize(Dimension size) {
2570                        imageSize = size;
2571                        setPreferredSize(imageSize);
2572
2573                        int w = selectedArea.width;
2574                        int h = selectedArea.height;
2575                        if ((w > 0) && (h > 0)) {
2576                                // use fixed selected area to reduce the rounding error
2577                                selectedArea.setBounds(
2578                                                (int) (originalSelectedArea.x * zoomFactor),
2579                                                (int) (originalSelectedArea.y * zoomFactor),
2580                                                (int) (originalSelectedArea.width * zoomFactor),
2581                                                (int) (originalSelectedArea.height * zoomFactor));
2582                        }
2583
2584                        repaint();
2585                }
2586
2587                private void setImage(Image img) {
2588                        image = img;
2589                        imageSize = new Dimension(image.getWidth(this), image
2590                                        .getHeight(this));
2591                        originalSize = imageSize;
2592                        selectedArea.setSize(0, 0);
2593                        setPreferredSize(imageSize);
2594
2595                        setImageSize(new Dimension((int) (originalSize.width * zoomFactor),
2596                                        (int) (originalSize.height * zoomFactor)));
2597
2598                        repaint();
2599                }
2600        } // private class ImageComponent extends JComponent
2601
2602        /**
2603         * FlipFileter creates image filter to flip image horizontally or
2604         * vertically.
2605         */
2606        private class FlipFilter extends ImageFilter {
2607                /** flip direction */
2608                private int direction;
2609
2610                /** pixel value */
2611                private int raster[] = null;
2612
2613                /** width & height */
2614                private int imageWidth, imageHeight;
2615
2616                /**
2617                 * Constructs an image filter to flip horizontally or vertically.
2618                 * <p>
2619                 * 
2620                 * @param d
2621                 *            the flip direction.
2622                 */
2623                private FlipFilter(int d) {
2624                        if (d < FLIP_HORIZONTAL) {
2625                                d = FLIP_HORIZONTAL;
2626                        }
2627                        else if (d > FLIP_VERTICAL) {
2628                                d = FLIP_VERTICAL;
2629                        }
2630
2631                        direction = d;
2632                }
2633
2634                public void setDimensions(int w, int h) {
2635                        imageWidth = w;
2636                        imageHeight = h;
2637
2638                        // specify the raster
2639                        if (raster == null) {
2640                                raster = new int[imageWidth * imageHeight];
2641                        }
2642
2643                        consumer.setDimensions(imageWidth, imageHeight);
2644                }
2645
2646                public void setPixels(int x, int y, int w, int h, ColorModel model,
2647                                byte pixels[], int off, int scansize) {
2648                        int srcoff = off;
2649                        int dstoff = y * imageWidth + x;
2650                        for (int yc = 0; yc < h; yc++) {
2651                                for (int xc = 0; xc < w; xc++) {
2652                                        raster[dstoff++] = model.getRGB(pixels[srcoff++] & 0xff);
2653                                }
2654
2655                                srcoff += (scansize - w);
2656                                dstoff += (imageWidth - w);
2657                        }
2658                }
2659
2660                public void setPixels(int x, int y, int w, int h, ColorModel model,
2661                                int pixels[], int off, int scansize) {
2662                        int srcoff = off;
2663                        int dstoff = y * imageWidth + x;
2664
2665                        for (int yc = 0; yc < h; yc++) {
2666                                for (int xc = 0; xc < w; xc++) {
2667                                        raster[dstoff++] = model.getRGB(pixels[srcoff++]);
2668                                }
2669                                srcoff += (scansize - w);
2670                                dstoff += (imageWidth - w);
2671                        }
2672                }
2673
2674                public void imageComplete(int status) {
2675                        if ((status == IMAGEERROR) || (status == IMAGEABORTED)) {
2676                                consumer.imageComplete(status);
2677                                return;
2678                        }
2679
2680                        int pixels[] = new int[imageWidth];
2681                        for (int y = 0; y < imageHeight; y++) {
2682                                if (direction == FLIP_VERTICAL) {
2683                                        // grab pixel values of the target line ...
2684                                        int pos = (imageHeight - 1 - y) * imageWidth;
2685                                        for (int kk = 0; kk < imageWidth; kk++) {
2686                                                pixels[kk] = raster[pos + kk];
2687                                        }
2688                                }
2689                                else {
2690                                        int pos = y * imageWidth;
2691                                        for (int kk = 0; kk < imageWidth; kk++) {
2692                                                pixels[kk] = raster[pos + kk];
2693                                        }
2694
2695                                        // swap the pixel values of the target line
2696                                        int hw = imageWidth / 2;
2697                                        for (int kk = 0; kk < hw; kk++) {
2698                                                int tmp = pixels[kk];
2699                                                pixels[kk] = pixels[imageWidth - kk - 1];
2700                                                pixels[imageWidth - kk - 1] = tmp;
2701                                        }
2702                                }
2703
2704                                // consumer it ....
2705                                consumer.setPixels(0, y, imageWidth, 1, ColorModel
2706                                                .getRGBdefault(), pixels, 0, imageWidth);
2707                        } // for (int y = 0; y < imageHeight; y++)
2708
2709                        // complete ?
2710                        consumer.imageComplete(status);
2711                }
2712        } // private class FlipFilter extends ImageFilter
2713
2714        /**
2715         * Apply general brightness/contrast algorithm. For details, visit
2716         * http://www.developerfusion.co.uk/
2717         * 
2718         * The general algorithm is represented by: If Brighten = True New_Value =
2719         * Old_Value + Adjustment_Amount Else New_Value = Old_Value -
2720         * Adjustment_Amount If New_Value < Value_Minimum New_Value = Value_Minimum
2721         * If New_Value > Value_Maximum New_Value = Value_Maximum
2722         * 
2723         * Contrast is a complicated operation. It is hard to formulate a
2724         * "general algorithm". Here is the closest representation
2725         * (Contrast_Value=[0, 2]):
2726         * 
2727         * //Converts to a percent //[0, 1] New_Value = Old_Value / 255
2728         * 
2729         * //Centers on 0 instead of .5 //[-.5, .5] New_Value -= 0.5
2730         * 
2731         * //Adjusts by Contrast_Value //[-127.5, 127.5], usually [-1, 1] New_Value
2732         * *= Contrast_Value
2733         * 
2734         * //Re-add .5 (un-center over 0) //[-127, 128] New_Value += 0.5
2735         * 
2736         * //Re-multiply by 255 (un-convert to percent) //[-32385, 32640], usually
2737         * [0, 255] New_Value *= 255 //Clamp [0, 255] If(New_Value > 255) New_Value
2738         * = 255 If(New_Value < 0) New_Value = 0
2739         */
2740        private class BrightnessFilter extends RGBImageFilter {
2741                // brightness level = [-200, 200]
2742                int brightLevel = 0;
2743
2744                // contrast level [0, 4]
2745                float contrastLevel = 0;
2746
2747                public BrightnessFilter(int blevel, int clevel) {
2748                        if (blevel < -100) {
2749                                brightLevel = -100;
2750                        }
2751                        else if (blevel > 100) {
2752                                brightLevel = 100;
2753                        }
2754                        else {
2755                                brightLevel = blevel;
2756                        }
2757                        brightLevel *= 2;
2758
2759                        if (clevel < -100) {
2760                                clevel = -100;
2761                        }
2762                        else if (clevel > 100) {
2763                                clevel = 100;
2764                        }
2765
2766                        if (clevel > 0) {
2767                                contrastLevel = (clevel / 100f + 1) * 2;
2768                        }
2769                        else if (clevel < 0) {
2770                                contrastLevel = (clevel / 100f + 1) / 2;
2771                        }
2772                        else {
2773                                contrastLevel = 0;
2774                        }
2775
2776                        canFilterIndexColorModel = true;
2777                }
2778
2779                public int filterRGB(int x, int y, int rgb) {
2780                        // adjust brightness first, then adjust contrast
2781                        // it gives more color depth
2782
2783                        if (brightLevel != 0) {
2784                                int r = (rgb & 0x00ff0000) >> 16;
2785                        int g = (rgb & 0x0000ff00) >> 8;
2786                                        int b = (rgb & 0x000000ff);
2787
2788                                        r += brightLevel;
2789                                        g += brightLevel;
2790                                        b += brightLevel;
2791
2792                                        if (r < 0) {
2793                                                r = 0;
2794                                        }
2795                                        if (r > 255) {
2796                                                r = 255;
2797                                        }
2798                                        if (g < 0) {
2799                                                g = 0;
2800                                        }
2801                                        if (g > 255) {
2802                                                g = 255;
2803                                        }
2804                                        if (b < 0) {
2805                                                b = 0;
2806                                        }
2807                                        if (b > 255) {
2808                                                b = 255;
2809                                        }
2810
2811                                        r = (r << 16) & 0x00ff0000;
2812                                        g = (g << 8) & 0x0000ff00;
2813                                        b = b & 0x000000ff;
2814
2815                                        rgb = ((rgb & 0xff000000) | r | g | b);
2816                        }
2817
2818                        if (contrastLevel > 0.000001) { // do not compare float using !=0 or
2819                                // ==0
2820                                int r = (rgb & 0x00ff0000) >> 16;
2821                                        int g = (rgb & 0x0000ff00) >> 8;
2822                                        int b = (rgb & 0x000000ff);
2823
2824                                        float f = (float) r / 255f;
2825                                        f -= 0.5;
2826                                        f *= contrastLevel;
2827                                        f += 0.5;
2828                                        f *= 255f;
2829                                        if (f < 0) {
2830                                                f = 0;
2831                                        }
2832                                        if (f > 255) {
2833                                                f = 255;
2834                                        }
2835                                        r = (int) f;
2836
2837                                        f = (float) g / 255f;
2838                                        f -= 0.5;
2839                                        f *= contrastLevel;
2840                                        f += 0.5;
2841                                        f *= 255f;
2842                                        if (f < 0) {
2843                                                f = 0;
2844                                        }
2845                                        if (f > 255) {
2846                                                f = 255;
2847                                        }
2848                                        g = (int) f;
2849
2850                                        f = (float) b / 255f;
2851                                        f -= 0.5;
2852                                        f *= contrastLevel;
2853                                        f += 0.5;
2854                                        f *= 255f;
2855                                        if (f < 0) {
2856                                                f = 0;
2857                                        }
2858                                        if (f > 255) {
2859                                                f = 255;
2860                                        }
2861                                        b = (int) f;
2862
2863                                        r = (r << 16) & 0x00ff0000;
2864                                        g = (g << 8) & 0x0000ff00;
2865                                        b = b & 0x000000ff;
2866
2867                                        rgb = ((rgb & 0xff000000) | r | g | b);
2868                        }
2869
2870                        return rgb;
2871                }
2872        }
2873
2874        /**
2875         * Makes an image filter for contour.
2876         */
2877        private class ContourFilter extends ImageFilter {
2878                // default color model
2879                private ColorModel defaultRGB;
2880
2881                // contour level
2882                int level;
2883
2884                // the table of the contour levels
2885                int levels[];
2886
2887                // colors for drawable contour line
2888                int[] levelColors;
2889
2890                // default RGB
2891
2892                // pixel value
2893                private int raster[] = null;
2894
2895                // width & height
2896                private int imageWidth, imageHeight;
2897
2898                /**
2899                 * Create an contour filter for a given level contouring.
2900                 * 
2901                 * @param theLevel
2902                 *            the contour level.
2903                 */
2904                private ContourFilter(int theLevel) {
2905                        defaultRGB = ColorModel.getRGBdefault();
2906
2907                        levelColors = new int[9];
2908                        levelColors[0] = Color.red.getRGB();
2909                        levelColors[1] = Color.green.getRGB();
2910                        levelColors[2] = Color.blue.getRGB();
2911                        levelColors[3] = Color.magenta.getRGB();
2912                        levelColors[4] = Color.orange.getRGB();
2913                        levelColors[5] = Color.cyan.getRGB();
2914                        levelColors[6] = Color.black.getRGB();
2915                        levelColors[7] = Color.pink.getRGB();
2916                        levelColors[8] = Color.yellow.getRGB();
2917
2918
2919                        if (theLevel < 1) {
2920                                theLevel = 1;
2921                        }
2922                        else if (theLevel > 9) {
2923                                theLevel = 9;
2924                        }
2925
2926                        level = theLevel;
2927                        levels = new int[level];
2928
2929                        int dx = 128 / level;
2930                        for (int i = 0; i < level; i++) {
2931                                levels[i] = (i + 1) * dx;
2932                        }
2933                }
2934
2935                public void setDimensions(int width, int height) {
2936                        this.imageWidth = width;
2937                        this.imageHeight = height;
2938
2939                        // specify the raster
2940                        if (raster == null) {
2941                                raster = new int[imageWidth * imageHeight];
2942                        }
2943
2944                        consumer.setDimensions(width, height);
2945                }
2946
2947                public void setPixels(int x, int y, int w, int h, ColorModel model,
2948                                byte pixels[], int off, int scansize) {
2949                        int rgb = 0;
2950                        int srcoff = off;
2951                        int dstoff = y * imageWidth + x;
2952
2953                        for (int yc = 0; yc < h; yc++) {
2954                                for (int xc = 0; xc < w; xc++) {
2955                                        rgb = model.getRGB(pixels[srcoff++] & 0xff);
2956                                        raster[dstoff++] = (((rgb >> 16) & 0xff)
2957                                                        + ((rgb >> 8) & 0xff) + (rgb & 0xff)) / 3;
2958                                }
2959                                srcoff += (scansize - w);
2960                                dstoff += (imageWidth - w);
2961                        }
2962
2963                }
2964
2965                public void setPixels(int x, int y, int w, int h, ColorModel model,
2966                                int pixels[], int off, int scansize) {
2967                        int rgb = 0;
2968                        int srcoff = off;
2969                        int dstoff = y * imageWidth + x;
2970
2971                        for (int yc = 0; yc < h; yc++) {
2972                                for (int xc = 0; xc < w; xc++) {
2973                                        rgb = model.getRGB(pixels[srcoff++] & 0xff);
2974                                        raster[dstoff++] = (((rgb >> 16) & 0xff)
2975                                                        + ((rgb >> 8) & 0xff) + (rgb & 0xff)) / 3;
2976                                }
2977
2978                                srcoff += (scansize - w);
2979                                dstoff += (imageWidth - w);
2980                        }
2981                }
2982
2983                public void imageComplete(int status) {
2984                        if ((status == IMAGEERROR) || (status == IMAGEABORTED)) {
2985                                consumer.imageComplete(status);
2986                                return;
2987                        }
2988
2989                        int pixels[] = new int[imageWidth * imageHeight];
2990                        for (int z = 0; z < levels.length; z++) {
2991                                int currentLevel = levels[z];
2992                                int color = levelColors[z];
2993
2994                                setContourLine(raster, pixels, currentLevel, color, imageWidth,
2995                                                imageHeight);
2996                        }
2997
2998                        int line[] = new int[imageWidth];
2999                        for (int y = 0; y < imageHeight; y++) {
3000                                for (int x = 0; x < imageWidth; x++) {
3001                                        line[x] = pixels[y * imageWidth + x];
3002                                }
3003
3004                                consumer.setPixels(0, y, imageWidth, 1, defaultRGB, line, 0,
3005                                                imageWidth);
3006                        } // for (int y = 0; y < imageHeight; y++) {
3007
3008                                // complete ?
3009                                                consumer.imageComplete(status);
3010                        }
3011
3012                        /**
3013                         * draw a contour line based on the current parameter---level, color
3014                         * 
3015                         * @param raster
3016                         *            the data of the raster image.
3017                         * @param pixels
3018                         *            the pixel value of the image.
3019                         * @param level
3020                         *            the contour level.
3021                         * @param color
3022                         *            the color of the contour line.
3023                         * @param w
3024                         *            the width of the image.
3025                         * @param h
3026                         *            the height of the image.
3027                         */
3028                private void setContourLine(int[] raster, int[] pixels, int level,
3029                                int color, int w, int h) {
3030                        int p = 0; // entrance point
3031                        int q = p + (w * h - 1); // bottom right point
3032                        int u = 0 + (w - 1); // top right point
3033
3034                        // first round
3035                        while (true) {
3036                                while (p < u) {
3037                                        int rgb = raster[p];
3038                                        if (rgb < level) {
3039                                                while ((raster[p] < level) && (p < u)) {
3040                                                        p++;
3041                                                }
3042                                                if (raster[p] >= level) {
3043                                                        pixels[p] = color;
3044                                                }
3045                                        }
3046                                        else if (rgb == level) {
3047                                                while ((raster[p] == level) && (p < u)) {
3048                                                        p++;
3049                                                }
3050                                                if ((raster[p] < level) || (raster[p] > level)) {
3051                                                        pixels[p] = color;
3052                                                }
3053                                        }
3054                                        else {
3055                                                while ((raster[p] > level) && (p < u)) {
3056                                                        p++;
3057                                                }
3058                                                if ((raster[p] <= level)) {
3059                                                        pixels[p] = color;
3060                                                }
3061                                        }
3062                                }
3063
3064                                if (u == q) {
3065                                        break;
3066                                }
3067                                else {
3068                                        u += w;
3069                                        p++;
3070                                }
3071                        }
3072                }
3073
3074        } // private class ContourFilter extends ImageFilter
3075
3076        private class Rotate90Filter extends ImageFilter {
3077                private ColorModel defaultRGB = ColorModel.getRGBdefault();
3078
3079                private double coord[] = new double[2];
3080
3081                private int raster[];
3082                private int xoffset, yoffset;
3083                private int srcW, srcH;
3084                private int dstW, dstH;
3085                private int direction;
3086
3087                public Rotate90Filter(int dir) {
3088                        direction = dir;
3089                }
3090
3091                public void transform(double x, double y, double[] retcoord) {
3092                        if (direction == ROTATE_CW_90) {
3093                                retcoord[0] = -y;
3094                                retcoord[1] = x;
3095                        }
3096                        else {
3097                                retcoord[0] = y;
3098                                retcoord[1] = -x;
3099                        }
3100                }
3101
3102                public void itransform(double x, double y, double[] retcoord) {
3103                        if (direction == ROTATE_CCW_90) {
3104                                retcoord[0] = -y;
3105                                retcoord[1] = x;
3106                        }
3107                        else {
3108                                retcoord[0] = y;
3109                                retcoord[1] = -x;
3110                        }
3111                }
3112
3113                public void transformBBox(Rectangle rect) {
3114                        double minx = Double.POSITIVE_INFINITY;
3115                        double miny = Double.POSITIVE_INFINITY;
3116                        double maxx = Double.NEGATIVE_INFINITY;
3117                        double maxy = Double.NEGATIVE_INFINITY;
3118                        for (int y = 0; y <= 1; y++) {
3119                                for (int x = 0; x <= 1; x++) {
3120                                        transform(rect.x + x * rect.width,
3121                                                        rect.y + y * rect.height, coord);
3122                                        minx = Math.min(minx, coord[0]);
3123                                        miny = Math.min(miny, coord[1]);
3124                                        maxx = Math.max(maxx, coord[0]);
3125                                        maxy = Math.max(maxy, coord[1]);
3126                                }
3127                        }
3128                        rect.x = (int) Math.floor(minx);
3129                        rect.y = (int) Math.floor(miny);
3130                        rect.width = (int) Math.ceil(maxx) - rect.x;
3131                        rect.height = (int) Math.ceil(maxy) - rect.y;
3132                }
3133
3134                public void setDimensions(int width, int height) {
3135                        Rectangle rect = new Rectangle(0, 0, width, height);
3136                        transformBBox(rect);
3137                        xoffset = -rect.x;
3138                        yoffset = -rect.y;
3139                        srcW = width;
3140                        srcH = height;
3141                        dstW = rect.width;
3142                        dstH = rect.height;
3143                        raster = new int[srcW * srcH];
3144                        consumer.setDimensions(dstW, dstH);
3145                }
3146
3147                public void setProperties(Hashtable props) {
3148                        props = (Hashtable) props.clone();
3149                        Object o = props.get("filters");
3150                        if (o == null) {
3151                                props.put("filters", toString());
3152                        }
3153                        else if (o instanceof String) {
3154                                props.put("filters", ((String) o) + toString());
3155                        }
3156                        consumer.setProperties(props);
3157                }
3158
3159                public void setColorModel(ColorModel model) {
3160                        consumer.setColorModel(defaultRGB);
3161                }
3162
3163                public void setHints(int hintflags) {
3164                        consumer.setHints(TOPDOWNLEFTRIGHT | COMPLETESCANLINES | SINGLEPASS
3165                                        | (hintflags & SINGLEFRAME));
3166                }
3167
3168                public void setPixels(int x, int y, int w, int h, ColorModel model,
3169                                byte pixels[], int off, int scansize) {
3170                        int srcoff = off;
3171                        int dstoff = y * srcW + x;
3172                        for (int yc = 0; yc < h; yc++) {
3173                                for (int xc = 0; xc < w; xc++) {
3174                                        raster[dstoff++] = model.getRGB(pixels[srcoff++] & 0xff);
3175                                }
3176                                srcoff += (scansize - w);
3177                                dstoff += (srcW - w);
3178                        }
3179                }
3180
3181                public void setPixels(int x, int y, int w, int h, ColorModel model,
3182                                int pixels[], int off, int scansize) {
3183                        int srcoff = off;
3184                        int dstoff = y * srcW + x;
3185                        if (model == defaultRGB) {
3186                                for (int yc = 0; yc < h; yc++) {
3187                                        System.arraycopy(pixels, srcoff, raster, dstoff, w);
3188                                        srcoff += scansize;
3189                                        dstoff += srcW;
3190                                }
3191                        }
3192                        else {
3193                                for (int yc = 0; yc < h; yc++) {
3194                                        for (int xc = 0; xc < w; xc++) {
3195                                                raster[dstoff++] = model.getRGB(pixels[srcoff++]);
3196                                        }
3197                                        srcoff += (scansize - w);
3198                                        dstoff += (srcW - w);
3199                                }
3200                        }
3201                }
3202
3203                public void imageComplete(int status) {
3204                        if ((status == IMAGEERROR) || (status == IMAGEABORTED)) {
3205                                consumer.imageComplete(status);
3206                                return;
3207                        }
3208                        int pixels[] = new int[dstW];
3209                        for (int dy = 0; dy < dstH; dy++) {
3210                                itransform(0 - xoffset, dy - yoffset, coord);
3211                                double x1 = coord[0];
3212                                double y1 = coord[1];
3213                                itransform(dstW - xoffset, dy - yoffset, coord);
3214                                double x2 = coord[0];
3215                                double y2 = coord[1];
3216                                double xinc = (x2 - x1) / dstW;
3217                                double yinc = (y2 - y1) / dstW;
3218                                for (int dx = 0; dx < dstW; dx++) {
3219                                        int sx = (int) Math.round(x1);
3220                                        int sy = (int) Math.round(y1);
3221                                        if ((sx < 0) || (sy < 0) || (sx >= srcW) || (sy >= srcH)) {
3222                                                pixels[dx] = 0;
3223                                        }
3224                                        else {
3225                                                pixels[dx] = raster[sy * srcW + sx];
3226                                        }
3227                                        x1 += xinc;
3228                                        y1 += yinc;
3229                                }
3230                                consumer.setPixels(0, dy, dstW, 1, defaultRGB, pixels, 0, dstW);
3231                        }
3232                        consumer.imageComplete(status);
3233                }
3234        } // private class RotateFilter
3235
3236        /**
3237         * Makes animation for 3D images.
3238         */
3239        private class Animation extends JDialog implements ActionListener, Runnable {
3240                private static final long serialVersionUID = 6717628496771098250L;
3241
3242                private final int MAX_ANIMATION_IMAGE_SIZE = 300;
3243
3244                private Image[] frames = null; // a list of images for animation
3245                private JComponent canvas = null; // canvas to draw the image
3246                private Thread engine = null; // Thread animating the images
3247                private int numberOfImages = 0;
3248                private int currentFrame = 0;
3249                private int sleepTime = 200;
3250                private Image offScrImage; // Offscreen image
3251                private Graphics offScrGC; // Offscreen graphics context
3252                private JFrame owner;
3253                private int x0 = 0, y0 = 0; // offset of the image drawing
3254
3255                public Animation(JFrame theOwner, ScalarDS dataset) {
3256                        super(theOwner, "Animation", true);
3257                        owner = theOwner;
3258                        setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
3259
3260                        long[] dims = dataset.getDims();
3261                        long[] stride = dataset.getStride();
3262                        long[] start = dataset.getStartDims();
3263                        long[] selected = dataset.getSelectedDims();
3264                        int[] selectedIndex = dataset.getSelectedIndex();
3265                        int rank = dataset.getRank();
3266                        if (animationSpeed != 0) {
3267                                sleepTime = 1000 / animationSpeed;
3268                        }
3269
3270                        // back up the start and selected size
3271                        long[] tstart = new long[rank];
3272                        long[] tselected = new long[rank];
3273                        long[] tstride = new long[rank];
3274                        System.arraycopy(start, 0, tstart, 0, rank);
3275                        System.arraycopy(selected, 0, tselected, 0, rank);
3276                        System.arraycopy(stride, 0, tstride, 0, rank);
3277
3278                        int stride_n = 1;
3279                        int max_size = (int) Math.max(selected[selectedIndex[0]],
3280                                        selected[selectedIndex[1]]);
3281                        if (max_size > MAX_ANIMATION_IMAGE_SIZE) {
3282                                stride_n = (int)( (double)max_size / (double)MAX_ANIMATION_IMAGE_SIZE +0.5);
3283                        }
3284
3285                        start[selectedIndex[0]] = 0;
3286                        start[selectedIndex[1]] = 0;
3287                        start[selectedIndex[2]] = 0;
3288                        selected[selectedIndex[0]] = dims[selectedIndex[0]] / stride_n;
3289                        selected[selectedIndex[1]] = dims[selectedIndex[1]] / stride_n;
3290                        selected[selectedIndex[2]] = 1;
3291                        stride[selectedIndex[0]] = stride_n;
3292                        stride[selectedIndex[1]] = stride_n;
3293                        stride[selectedIndex[2]] = 1;
3294
3295                        Object data3d = null;
3296                        byte[] byteData = null;
3297                        int h = (int) selected[selectedIndex[0]];
3298                        int w = (int) selected[selectedIndex[1]];
3299                        int size = w * h;
3300
3301                        numberOfImages = (int) dims[selectedIndex[2]];
3302                        frames = new Image[numberOfImages];
3303                        BufferedImage mir = bufferedImage;
3304                        try {
3305                                for (int i = 0; i < numberOfImages; i++) {
3306                                        bufferedImage = null; // each animation image has its
3307                                        // own image resource
3308                                        start[selectedIndex[2]] = i;
3309
3310                                        dataset.clearData();
3311                                        try {
3312                                                data3d = dataset.read();
3313                                        }
3314                                        catch (Throwable err) {
3315                                                continue;
3316                                        }
3317
3318                                        byteData = new byte[size];
3319
3320                                        byteData=Tools.getBytes(data3d, dataRange, w, h, false, dataset.getFilteredImageValues(),
3321                                                        true, byteData);
3322
3323                                        frames[i] = createIndexedImage(byteData, imagePalette, w, h);
3324                                }
3325                        }
3326                        finally {
3327                                // set back to original state
3328                                bufferedImage = mir;
3329                                System.arraycopy(tstart, 0, start, 0, rank);
3330                                System.arraycopy(tselected, 0, selected, 0, rank);
3331                                System.arraycopy(tstride, 0, stride, 0, rank);
3332                        }
3333
3334                        offScrImage = owner.createImage(w, h);
3335                        offScrGC = offScrImage.getGraphics();
3336                        x0 = Math.max((MAX_ANIMATION_IMAGE_SIZE - w) / 2, 0);
3337                        y0 = Math.max((MAX_ANIMATION_IMAGE_SIZE - h) / 2, 0);
3338
3339                        canvas = new JComponent() {
3340                                private static final long serialVersionUID = -6828735330511795835L;
3341
3342                                public void paint(Graphics g) {
3343                                        g.clearRect(0, 0, MAX_ANIMATION_IMAGE_SIZE,
3344                                                        MAX_ANIMATION_IMAGE_SIZE);
3345
3346                                        if ((offScrGC == null) || (frames == null)) {
3347                                                return;
3348                                        }
3349
3350                                        offScrGC.drawImage(frames[currentFrame], 0, 0, owner);
3351                                        g.drawImage(offScrImage, x0, y0, owner);
3352                                }
3353                        };
3354
3355                        JPanel contentPane = (JPanel) getContentPane();
3356                        contentPane.setPreferredSize(new Dimension(
3357                                        MAX_ANIMATION_IMAGE_SIZE, MAX_ANIMATION_IMAGE_SIZE));
3358                        contentPane.setLayout(new BorderLayout());
3359                        JButton b = new JButton("Close");
3360                        b.setActionCommand("Close animation");
3361                        b.addActionListener(this);
3362                        contentPane.add(b, BorderLayout.SOUTH);
3363
3364                        contentPane.add(canvas, BorderLayout.CENTER);
3365
3366                        start();
3367
3368                        Point l = getParent().getLocation();
3369                        l.x += 300;
3370                        l.y += 200;
3371                        setLocation(l);
3372
3373                        pack();
3374                        setVisible(true);
3375                }
3376
3377                public void actionPerformed(ActionEvent e) {
3378                        Object source = e.getSource();
3379                        String cmd = e.getActionCommand();
3380
3381                        if (cmd.equals("Close animation")) {
3382                                dispose(); // terminate the animation
3383                        }
3384                }
3385
3386                public void dispose() {
3387                        engine = null;
3388                        frames = null;
3389                        super.dispose();
3390                }
3391
3392                /**
3393                 * No need to clear anything; just paint.
3394                 */
3395                public void update(Graphics g) {
3396                        paint(g);
3397                }
3398
3399                /**
3400                 * Paint the current frame
3401                 */
3402                public void paint(Graphics g) {
3403                        canvas.paint(g);
3404                }
3405
3406                /**
3407                 * Start the applet by forking an animation thread.
3408                 */
3409                private void start() {
3410                        engine = new Thread(this);
3411                        engine.start();
3412                }
3413
3414                /**
3415                 * Run the animation. This method is called by class Thread.
3416                 * 
3417                 * @see java.lang.Thread
3418                 */
3419                public void run() {
3420                        Thread me = Thread.currentThread();
3421
3422                        if ((frames == null) || (canvas == null)) {
3423                                return;
3424                        }
3425
3426                        while (me == engine) {
3427                                if (++currentFrame >= numberOfImages)
3428                                        currentFrame = 0;
3429                                repaint();
3430                                this.getToolkit().sync(); // Force it to be drawn *now*.
3431                                try {
3432                                        Thread.sleep(sleepTime);
3433                                }
3434                                catch (InterruptedException e) {
3435                                        log.debug("Thread.sleep({}):", sleepTime, e);
3436                                }
3437                        }
3438                } // public void run() {
3439        } // private class Animation extends JDialog
3440
3441        private class DataRangeDialog extends JDialog implements ActionListener,
3442        ChangeListener, PropertyChangeListener {
3443                final int NTICKS = 10;
3444                double tickRatio = 1;
3445                final int W = 500, H = 400;
3446                double[] minmax_current = {0, 0};
3447                double min, max, min_org, max_org;
3448                final double[] minmax_previous = {0, 0};
3449                final double[] minmax_dist = {0,0};
3450                JSlider minSlider, maxSlider;
3451                JFormattedTextField minField, maxField;
3452
3453                public DataRangeDialog(JFrame theOwner, double[] minmaxCurrent, 
3454                                double[] minmaxOriginal, final int[] dataDist) 
3455                {
3456                        super(theOwner, "Image Value Range", true);
3457
3458                        Tools.findMinMax(dataDist, minmax_dist, null);
3459
3460                        if ((minmaxCurrent == null) || (minmaxCurrent.length <= 1)) {
3461                                minmax_current[0] = 0;
3462                                minmax_current[1] = 255;
3463                        }
3464                        else {
3465                                if (minmaxCurrent[0] == minmaxCurrent[1]) {
3466                                        Tools.findMinMax(data, minmaxCurrent, dataset.getFillValue());
3467                                }
3468
3469                                minmax_current[0] = minmaxCurrent[0];
3470                                minmax_current[1] = minmaxCurrent[1];
3471                        }
3472
3473                        minmax_previous[0] = min = minmax_current[0];
3474                        minmax_previous[1] = max = minmax_current[1];
3475                        min_org = originalRange[0];
3476                        max_org = originalRange[1];
3477
3478                        tickRatio = (max_org-min_org)/(double)NTICKS;
3479
3480                        final DecimalFormat numberFormat = new DecimalFormat("#.##E0");
3481                        NumberFormatter formatter = new NumberFormatter(numberFormat);
3482                        formatter.setMinimum(new Double(min));
3483                        formatter.setMaximum(new Double(max));
3484
3485                        minField = new JFormattedTextField(formatter);
3486                        minField.addPropertyChangeListener(this);
3487                        minField.setValue(new Double(min));
3488                        maxField = new JFormattedTextField(formatter);
3489                        maxField.addPropertyChangeListener(this);
3490                        maxField.setValue(new Double(max));
3491
3492                        minSlider = new JSlider(JSlider.HORIZONTAL, 0, NTICKS, 0);
3493                        minSlider.setMajorTickSpacing(1);
3494                        minSlider.setPaintTicks(true);
3495                        minSlider.setPaintLabels(false);
3496                        minSlider.addChangeListener(this);
3497                        minSlider.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
3498
3499                        maxSlider = new JSlider(JSlider.HORIZONTAL, 0, NTICKS, NTICKS);
3500                        maxSlider.setMajorTickSpacing(1);
3501                        maxSlider.setPaintTicks(true);
3502                        maxSlider.setPaintLabels(false);
3503                        maxSlider.addChangeListener(this);
3504                        maxSlider.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
3505
3506                        JPanel contentPane = (JPanel) getContentPane();
3507                        contentPane.setLayout(new BorderLayout(5, 5));
3508                        contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
3509                        contentPane.setPreferredSize(new Dimension(W, H));
3510
3511                        JPanel minPane = new JPanel();
3512                        minPane.setBorder(new TitledBorder("Lower Bound"));
3513                        minPane.setLayout(new BorderLayout());
3514                        minPane.add(minField, BorderLayout.CENTER);
3515                        minPane.add(minSlider, BorderLayout.SOUTH);
3516
3517                        JPanel maxPane = new JPanel();
3518                        maxPane.setBorder(new TitledBorder("Upper Bound"));
3519                        maxPane.setLayout(new BorderLayout());
3520                        maxPane.add(maxField, BorderLayout.CENTER);
3521                        maxPane.add(maxSlider, BorderLayout.SOUTH);
3522
3523                        JPanel chartPane = new JPanel() {
3524                                int numberOfPoints = dataDist.length;
3525                                int gap = 5;
3526                                int xgap = 2 * gap;
3527                                double xmin = originalRange[0];
3528                                double xmax = originalRange[1];
3529
3530                                public void paint(Graphics g) {
3531                                        int h = H/3 -50;
3532                                        int w = W;
3533                                        int xnpoints = Math.min(10, numberOfPoints - 1);
3534
3535                                        // draw the X axis
3536                                        g.drawLine(xgap, h, w + xgap, h);
3537
3538                                        // draw x labels
3539                                        double xp = 0, x = xmin;
3540                                        double dw = (double) w / (double) xnpoints;
3541                                        double dx = (xmax - xmin) / xnpoints;
3542                                        for (int i = 0; i <= xnpoints; i++) {
3543                                                x = xmin + i * dx;
3544                                                xp = xgap + i * dw;
3545                                                g.drawLine((int) xp, h, (int) xp, h - 5);
3546                                                g.drawString(numberFormat.format(x), (int) xp - 5, h + 20);
3547                                        }
3548
3549                                        Color c = g.getColor();
3550                                        double yp, ymin=minmax_dist[0], dy=minmax_dist[1]-minmax_dist[0];
3551                                        if (dy<=0)
3552                                                dy =1;
3553
3554                                        xp = xgap;
3555                                        yp = 0;
3556                                        g.setColor(Color.blue);
3557                                        int barWidth = w / numberOfPoints;
3558                                        if (barWidth <= 0) {
3559                                                barWidth = 1;
3560                                        }
3561                                        dw = (double) w / (double) numberOfPoints;
3562
3563                                        for (int j = 0; j < numberOfPoints; j++) {
3564                                                xp = xgap + j * dw;
3565                                                yp = (int) (h * (dataDist[j] - ymin) / dy);
3566                                                g.fillRect((int) xp, (int) (h - yp), barWidth, (int) yp);
3567                                        }
3568
3569                                        g.setColor(c); // set the color back to its default
3570                                } // public void paint(Graphics g)
3571                        } ;
3572
3573                        JPanel mainPane = new JPanel();
3574                        mainPane.setLayout(new GridLayout(3, 1, 5, 5));
3575                        mainPane.add(chartPane);
3576                        mainPane.add(minPane);
3577                        mainPane.add(maxPane);
3578                        contentPane.add(mainPane, BorderLayout.CENTER);
3579
3580                        // add OK and CANCEL buttons
3581                        JPanel confirmP = new JPanel();
3582                        JButton button = new JButton("   Ok   ");
3583                        button.setMnemonic(KeyEvent.VK_O);
3584                        button.setActionCommand("Ok");
3585                        button.addActionListener(this);
3586                        confirmP.add(button);
3587                        button = new JButton("Cancel");
3588                        button.setMnemonic(KeyEvent.VK_C);
3589                        button.setActionCommand("Cancel");
3590                        button.addActionListener(this);
3591                        confirmP.add(button);
3592                        button = new JButton("Apply");
3593                        button.setMnemonic(KeyEvent.VK_A);
3594                        button.setActionCommand("Apply");
3595                        button.addActionListener(this);
3596                        confirmP.add(button);
3597                        contentPane.add(confirmP, BorderLayout.SOUTH);
3598                        contentPane.add(new JLabel(" "), BorderLayout.NORTH);
3599
3600                        if (min==max) {
3601                                minSlider.setEnabled(false);
3602                                maxSlider.setEnabled(false);
3603                        }
3604
3605                        Point l = getParent().getLocation();
3606                        Dimension d = getParent().getPreferredSize();
3607                        l.x += 300;
3608                        l.y += 200;
3609                        setLocation(l);
3610                        pack();
3611                        setVisible(true);
3612                }
3613
3614                public void actionPerformed(ActionEvent e) {
3615                        String cmd = e.getActionCommand();
3616
3617                        if (cmd.equals("Ok")) {
3618                                minmax_current[0] = ((Number) minField.getValue()).doubleValue();
3619                                minmax_current[1] = ((Number) maxField.getValue()).doubleValue();
3620
3621                                this.dispose();
3622                        }
3623                        if (cmd.equals("Apply")) {
3624                                minmax_previous[0] = minmax_current[0];
3625                                minmax_previous[1] = minmax_current[1];
3626
3627                                minmax_current[0] = ((Number) minField.getValue()).doubleValue();
3628                                minmax_current[1] = ((Number) maxField.getValue()).doubleValue();
3629
3630                                applyDataRange(minmax_current);
3631                                minmax_current[0] = minmax_current[1] = 0;
3632                        }
3633                        else if (cmd.equals("Cancel")) {
3634
3635                                minmax_current[0] = minmax_previous[0];
3636                                minmax_current[1] = minmax_previous[1];
3637
3638                                applyDataRange(minmax_previous);
3639
3640                                this.dispose();
3641                        }
3642                }
3643
3644                /** Listen to the slider. */
3645                public void stateChanged(ChangeEvent e) {
3646                        Object source = e.getSource();
3647
3648                        if (!(source instanceof JSlider)) {
3649                                return;
3650                        }
3651
3652                        JSlider slider = (JSlider) source;
3653                        if (!slider.isEnabled())
3654                                return;
3655
3656                        double value = slider.getValue();
3657                        if (slider.equals(minSlider)) {
3658                                double maxValue = maxSlider.getValue();
3659                                if (value > maxValue) {
3660                                        value = maxValue;
3661                                        slider.setValue((int)value);
3662                                }
3663
3664                                minField.setValue(new Double(value*tickRatio+min_org));
3665                        }
3666                        else if (slider.equals(maxSlider)) {
3667                                double minValue = minSlider.getValue();
3668                                if (value < minValue) {
3669                                        value = minValue;
3670                                        slider.setValue((int)value);
3671                                }
3672                                maxField.setValue(new Double(value*tickRatio+min_org));
3673                        }
3674                }
3675
3676                /**
3677                 * Listen to the text field. This method detects when the value of the
3678                 * text field changes.
3679                 */
3680                public void propertyChange(PropertyChangeEvent e) {
3681                        Object source = e.getSource();
3682                        if ("value".equals(e.getPropertyName())) {
3683                                Number num = (Number) e.getNewValue();
3684                                if (num == null) {
3685                                        return;
3686                                }
3687                                double value = num.doubleValue();
3688
3689                                if (source.equals(minField) && (minSlider != null) && minSlider.isEnabled()) {
3690                                        if (value > max_org) {
3691                                                value = max_org;
3692                                                minField.setText(String.valueOf(value));
3693                                        }
3694
3695                                        minSlider.setValue((int) ((value-min_org)/tickRatio));
3696                                }
3697                                else if (source.equals(maxField) && (maxSlider != null)  && minSlider.isEnabled()) {
3698                                        if (value < min_org) {
3699                                                value = min_org;
3700                                                maxField.setText(String.valueOf(value));
3701                                        }
3702                                        //minmax[1] = value;
3703                                        maxSlider.setValue((int) ((value-min_org)/tickRatio));
3704                                }
3705                        }
3706                }
3707
3708                public double[] getRange() {
3709                        return minmax_current;
3710                }
3711        } // private class DataRangeDialog extends JDialog implements ActionListener
3712
3713        private class ContrastSlider extends JDialog implements
3714        ActionListener, ChangeListener, PropertyChangeListener {
3715                private static final long serialVersionUID = -3002524363351111565L;
3716                JSlider brightSlider, contrastSlider;
3717                JFormattedTextField brightField, contrastField;
3718                ImageProducer imageProducer;
3719                double[] autoGainBias = {0, 0};
3720                int bLevel=0, cLevel=0;
3721
3722                public ContrastSlider(JFrame theOwner, ImageProducer producer) 
3723                {
3724                        super(theOwner, "Brightness/Contrast", true);
3725                        String bLabel = "Brightness", cLabel="Contrast";
3726
3727                        imageProducer = producer;
3728
3729                        if (doAutoGainContrast && gainBias!= null) {
3730                                bLabel = "Bias";
3731                                cLabel="Gain";
3732                                this.setTitle(bLabel+"/"+cLabel);
3733                        }
3734
3735                        java.text.NumberFormat numberFormat = java.text.NumberFormat
3736                        .getNumberInstance();
3737                        NumberFormatter formatter = new NumberFormatter(numberFormat);
3738
3739                        formatter.setMinimum(new Integer(-100));
3740                        formatter.setMaximum(new Integer(100));
3741                        brightField = new JFormattedTextField(formatter);
3742                        brightField.addPropertyChangeListener(this);
3743                        brightField.setValue(new Integer(0));
3744
3745                        brightSlider = new JSlider(JSlider.HORIZONTAL, -100, 100, 0);
3746                        brightSlider.setMajorTickSpacing(20);
3747                        brightSlider.setPaintTicks(true);
3748                        brightSlider.setPaintLabels(true);
3749                        brightSlider.addChangeListener(this);
3750                        brightSlider
3751                        .setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
3752
3753                        formatter = new NumberFormatter(numberFormat);
3754                        formatter.setMinimum(new Integer(-100));
3755                        formatter.setMaximum(new Integer(100));
3756                        contrastField = new JFormattedTextField(formatter);
3757                        contrastField.addPropertyChangeListener(this);
3758                        contrastField.setValue(new Integer(0));
3759
3760                        contrastSlider = new JSlider(JSlider.HORIZONTAL, -100, 100, 0);
3761                        contrastSlider.setMajorTickSpacing(20);
3762                        contrastSlider.setPaintTicks(true);
3763                        contrastSlider.setPaintLabels(true);
3764                        contrastSlider.addChangeListener(this);
3765                        contrastSlider.setBorder(BorderFactory.createEmptyBorder(0, 0, 10,0));
3766
3767                        JPanel contentPane = (JPanel) getContentPane();
3768                        contentPane.setLayout(new BorderLayout(5, 5));
3769                        contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
3770                        contentPane.setPreferredSize(new Dimension(500, 300));
3771
3772                        JPanel brightPane = new JPanel();
3773                        brightPane.setBorder(new TitledBorder(bLabel+"%"));
3774                        brightPane.setLayout(new BorderLayout());
3775                        brightPane.add(brightField, BorderLayout.NORTH);
3776                        brightPane.add(brightSlider, BorderLayout.CENTER);
3777
3778                        JPanel contrastPane = new JPanel();
3779                        contrastPane.setBorder(new TitledBorder(cLabel+"%"));
3780                        contrastPane.setLayout(new BorderLayout());
3781                        contrastPane.add(contrastField, BorderLayout.NORTH);
3782                        contrastPane.add(contrastSlider, BorderLayout.CENTER);
3783
3784                        JPanel mainPane = new JPanel();
3785                        mainPane.setLayout(new GridLayout(2, 1, 5, 5));
3786                        mainPane.add(brightPane);
3787                        mainPane.add(contrastPane);
3788                        contentPane.add(mainPane, BorderLayout.CENTER);
3789
3790                        // add OK and CANCEL buttons
3791                        JPanel confirmP = new JPanel();
3792                        JButton button = new JButton("   Ok   ");
3793                        button.setMnemonic(KeyEvent.VK_O);
3794                        button.setActionCommand("Ok_brightness_change");
3795                        button.addActionListener(this);
3796                        confirmP.add(button);
3797                        button = new JButton("Cancel");
3798                        button.setMnemonic(KeyEvent.VK_C);
3799                        button.setActionCommand("Cancel_brightness_change");
3800                        button.addActionListener(this);
3801                        confirmP.add(button);
3802
3803                        button = new JButton("Apply");
3804                        button.setMnemonic(KeyEvent.VK_A);
3805                        button.setActionCommand("Apply_brightness_change");
3806                        button.addActionListener(this);
3807                        confirmP.add(button);
3808
3809                        contentPane.add(confirmP, BorderLayout.SOUTH);
3810                        contentPane.add(new JLabel(" "), BorderLayout.NORTH);
3811
3812                        Point l = getParent().getLocation();
3813                        Dimension d = getParent().getPreferredSize();
3814                        l.x += 300;
3815                        l.y += 200;
3816                        setLocation(l);
3817                        pack();
3818                }
3819
3820                public void actionPerformed(ActionEvent e) {
3821                        String cmd = e.getActionCommand();
3822
3823                        if (cmd.equals("Ok_brightness_change")
3824                                        || cmd.equals("Apply_brightness_change")) {
3825                                int b = ((Number) brightField.getValue()).intValue();
3826                                int c = ((Number) contrastField.getValue()).intValue();
3827
3828                                applyBrightContrast(b, c);
3829
3830                                if (cmd.startsWith("Ok")) {
3831                                        bLevel = b;
3832                                        cLevel = c;
3833                                        setVisible(false);
3834                                }
3835                        }
3836                        else if (cmd.equals("Cancel_brightness_change")) {
3837                                applyBrightContrast(bLevel, cLevel);
3838                                setVisible(false);
3839                        }
3840                }
3841
3842                /** Listen to the slider. */
3843                public void stateChanged(ChangeEvent e) {
3844                        Object source = e.getSource();
3845
3846                        if (!(source instanceof JSlider)) {
3847                                return;
3848                        }
3849
3850                        JSlider slider = (JSlider) source;
3851                        int value = slider.getValue();
3852                        if (slider.equals(brightSlider)) {
3853                                brightField.setValue(new Integer(value));
3854                        }
3855                        else if (slider.equals(contrastSlider)) {
3856                                contrastField.setValue(new Integer(value));
3857                        }
3858                }
3859
3860                /**
3861                 * Listen to the text field. This method detects when the value of the
3862                 * text field changes.
3863                 */
3864                public void propertyChange(PropertyChangeEvent e) {
3865                        Object source = e.getSource();
3866                        if ("value".equals(e.getPropertyName())) {
3867                                Number num = (Number) e.getNewValue();
3868                                if (num == null) {
3869                                        return;
3870                                }
3871
3872                                double value = num.doubleValue();
3873                                if (value > 100) {
3874                                        value = 100;
3875                                }
3876                                else if (value < -100) {
3877                                        value = -100;
3878                                }
3879
3880                                if (source.equals(brightField) && (brightSlider != null)) {
3881                                        brightSlider.setValue((int) value);
3882                                }
3883                                else if (source.equals(contrastField)
3884                                                && (contrastSlider != null)) {
3885                                        contrastSlider.setValue((int) value);
3886                                }
3887                        }
3888                }
3889
3890                private void applyBrightContrast(int blevel, int clevel) {
3891                        // do not separate autogain and simple contrast process
3892                        //            ImageFilter filter = new BrightnessFilter(blevel, clevel);
3893                        //            image = createImage(new FilteredImageSource(imageProducer, filter));
3894                        //            imageComponent.setImage(image);
3895                        //            zoomTo(zoomFactor);
3896
3897                        // separate autodain and simple contrast process
3898                        if (doAutoGainContrast && gainBias!= null) {
3899                                autoGainBias[0] = gainBias[0]*(1+((double)clevel)/100.0);
3900                                autoGainBias[1] = gainBias[1]*(1+((double)blevel)/100.0);
3901                                applyAutoGain(autoGainBias, null);
3902                        } 
3903                        else {
3904                                ImageFilter filter = new BrightnessFilter(blevel, clevel);
3905                                image = createImage(new FilteredImageSource(imageProducer, filter));
3906                                imageComponent.setImage(image);
3907                                zoomTo(zoomFactor);           
3908                        }
3909                }
3910
3911        } // private class ContrastSlider extends JDialog implements ActionListener
3912
3913}