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