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