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