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