    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}