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