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