    001/*****************************************************************************
    002 * Copyright by The HDF Group.                                               *
    003 * Copyright by the Board of Trustees of the University of Illinois.         *
    004 * All rights reserved.                                                      *
    005 *                                                                           *
    006 * This file is part of the HDF Java Products distribution.                  *
    007 * The full copyright notice, including terms governing use, modification,   *
    008 * and redistribution, is contained in the COPYING file, which can be found  *
    009 * at the root of the source code distribution tree,                         *
    010 * or in https://www.hdfgroup.org/licenses.                                  *
    011 * If you do not have access to either file, you may request a copy from     *
    012 * help@hdfgroup.org.                                                        *
    013 ****************************************************************************/
    014
    015package hdf.view;
    016
    017import java.awt.Graphics;
    018import java.awt.Image;
    019import java.awt.Toolkit;
    020import java.awt.image.BufferedImage;
    021import java.awt.image.ColorModel;
    022import java.awt.image.DataBufferInt;
    023import java.awt.image.DirectColorModel;
    024import java.awt.image.MemoryImageSource;
    025import java.awt.image.PixelGrabber;
    026
    027import java.io.BufferedInputStream;
    028import java.io.BufferedOutputStream;
    029import java.io.BufferedReader;
    030import java.io.DataOutputStream;
    031import java.io.File;
    032import java.io.FileInputStream;
    033import java.io.FileOutputStream;
    034import java.io.FileReader;
    035import java.io.IOException;
    036import java.io.RandomAccessFile;
    037
    038import java.lang.reflect.Array;
    039import java.lang.reflect.Constructor;
    040import java.lang.reflect.Method;
    041
    042import java.math.BigDecimal;
    043import java.math.BigInteger;
    044
    045import java.nio.ByteBuffer;
    046import java.nio.ByteOrder;
    047import java.nio.DoubleBuffer;
    048import java.nio.FloatBuffer;
    049import java.nio.IntBuffer;
    050import java.nio.LongBuffer;
    051import java.nio.ShortBuffer;
    052
    053import java.util.BitSet;
    054import java.util.Iterator;
    055import java.util.List;
    056import java.util.StringTokenizer;
    057
    058import javax.imageio.ImageIO;
    059
    060import org.slf4j.Logger;
    061import org.slf4j.LoggerFactory;
    062
    063import org.eclipse.jface.dialogs.MessageDialog;
    064import org.eclipse.swt.widgets.Composite;
    065import org.eclipse.swt.widgets.Display;
    066import org.eclipse.swt.widgets.Shell;
    067
    068import hdf.object.Datatype;
    069import hdf.object.FileFormat;
    070import hdf.object.Group;
    071import hdf.object.ScalarDS;
    072
    073import hdf.view.ViewProperties.BITMASK_OP;
    074
    075/**
    076 * The "Tools" class contains various tools for HDF files such as jpeg to HDF
    077 * converter.
    078 *
    079 * @author Peter X. Cao
    080 * @version 2.4 9/6/2007
    081 */
    082public final class Tools {
    083    private static final Display display = Display.getDefault();
    084
    085    private static final Logger log = LoggerFactory.getLogger(Tools.class);
    086
    087    /**  */
    088    public static final long       MAX_INT8        = 127;
    089    /**  */
    090    public static final long       MAX_UINT8       = 255;
    091    /**  */
    092    public static final long       MAX_INT16       = 32767;
    093    /**  */
    094    public static final long       MAX_UINT16      = 65535;
    095    /**  */
    096    public static final long       MAX_INT32       = 2147483647;
    097    /**  */
    098    public static final long       MAX_UINT32      = 4294967295L;
    099    /**  */
    100    public static final long       MAX_INT64       = 9223372036854775807L;
    101    /**  */
    102    public static final BigInteger MAX_UINT64      = new BigInteger("18446744073709551615");
    103
    104    private static final int       FLOAT_BUFFER_SIZE = 524288;
    105    private static final int       INT_BUFFER_SIZE = 524288;
    106    private static final int       SHORT_BUFFER_SIZE = 1048576;
    107    private static final int       LONG_BUFFER_SIZE = 262144;
    108    private static final int       DOUBLE_BUFFER_SIZE = 262144;
    109    private static final int       BYTE_BUFFER_SIZE = 2097152;
    110
    111    /** Key for JPEG image file type. */
    112    public static final String     FILE_TYPE_JPEG  = "JPEG";
    113
    114    /** Key for TIFF image file type. */
    115    public static final String     FILE_TYPE_TIFF  = "TIFF";
    116
    117    /** Key for PNG image file type. */
    118    public static final String     FILE_TYPE_PNG   = "PNG";
    119
    120    /** Key for GIF image file type. */
    121    public static final String     FILE_TYPE_GIF   = "GIF";
    122
    123    /** Key for BMP image file type. */
    124    public static final String FILE_TYPE_BMP = "BMP";
    125
    126    /** Key for all image file type. */
    127    public static final String FILE_TYPE_IMAGE = "IMG";
    128
    129    /**
    130     * Converts unsigned 64-bit integer data to a BigInteger since Java does not
    131     * have unsigned types.
    132     *
    133     * @param l
    134     *        The long value to convert to a BigInteger
    135     *
    136     * @return A BigInteger representing the unsigned value of the given long.
    137     */
    138    public static BigInteger convertUINT64toBigInt(Long l) {
    139        if (l < 0) {
    140            l = (l << 1) >>> 1;
    141            BigInteger big1 = new BigInteger("9223372036854775808"); // 2^65
    142            BigInteger big2 = new BigInteger(l.toString());
    143            return big1.add(big2);
    144        }
    145        else {
    146            return new BigInteger(l.toString());
    147        }
    148    }
    149
    150    /**
    151     * Converts an image file into HDF4/5 file.
    152     *
    153     * @param imgFileName
    154     *            the input image file.
    155     * @param hFileName
    156     *            the name of the HDF4/5 file.
    157     * @param fromType
    158     *            the type of image.
    159     * @param toType
    160     *            the type of file converted to.
    161     *
    162     * @throws Exception if a failure occurred
    163     */
    164    public static void convertImageToHDF(String imgFileName, String hFileName, String fromType, String toType)
    165            throws Exception {
    166        File imgFile = null;
    167
    168        if (imgFileName == null)
    169            throw new NullPointerException("The source image file is null.");
    170
    171        imgFile = new File(imgFileName);
    172        if (!imgFile.exists())
    173            throw new NullPointerException("The source image file does not exist.");
    174        if (hFileName == null)
    175            throw new NullPointerException("The target HDF file is null.");
    176
    177        if (!fromType.equals(FILE_TYPE_IMAGE))
    178            throw new UnsupportedOperationException("Unsupported image type.");
    179        else if (!(toType.equals(FileFormat.FILE_TYPE_HDF4) || toType.equals(FileFormat.FILE_TYPE_HDF5)))
    180            throw new UnsupportedOperationException("Unsupported destination file type.");
    181
    182        BufferedImage image = null;
    183        try {
    184            BufferedInputStream in = new BufferedInputStream(new FileInputStream(imgFileName));
    185            image = ImageIO.read(in);
    186            in.close();
    187        }
    188        catch (Exception err) {
    189            image = null;
    190        }
    191
    192        if (image == null)
    193            throw new UnsupportedOperationException("Failed to read image: " + imgFileName);
    194
    195        long h = image.getHeight();
    196        long w = image.getWidth();
    197        byte[] data = null;
    198
    199        try {
    200            data = new byte[(int)(3 * h * w)];
    201        }
    202        catch (OutOfMemoryError err) {
    203            err.printStackTrace();
    204            throw err;
    205        }
    206
    207        int idx = 0;
    208        int rgb = 0;
    209        for (int i = 0; i < h; i++) {
    210            for (int j = 0; j < w; j++) {
    211                rgb = image.getRGB(j, i);
    212                data[idx++] = (byte) (rgb >> 16);
    213                data[idx++] = (byte) (rgb >> 8);
    214                data[idx++] = (byte) rgb;
    215            }
    216        }
    217
    218        long[] dims = null;
    219        Datatype type = null;
    220        Group pgroup = null;
    221        String imgName = imgFile.getName();
    222        FileFormat newfile = null;
    223        FileFormat thefile = null;
    224        if (toType.equals(FileFormat.FILE_TYPE_HDF5)) {
    225            thefile = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5);
    226            long[] h5dims = { h, w, 3 }; // RGB pixel interlace
    227            dims = h5dims;
    228        }
    229        else if (toType.equals(FileFormat.FILE_TYPE_HDF4)) {
    230            thefile = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4);
    231            long[] h4dims = { w, h, 3 }; // RGB pixel interlace
    232            dims = h4dims;
    233        }
    234        else {
    235            thefile = null;
    236        }
    237
    238        if (thefile != null) {
    239            newfile = thefile.createInstance(hFileName, FileFormat.CREATE);
    240            newfile.open();
    241            pgroup = (Group) newfile.getRootObject();
    242            type = newfile.createDatatype(Datatype.CLASS_CHAR, 1, Datatype.NATIVE, Datatype.SIGN_NONE);
    243            newfile.createImage(imgName, pgroup, type, dims, null, null, -1, 3, ScalarDS.INTERLACE_PIXEL, data);
    244            newfile.close();
    245        }
    246
    247        Runtime.getRuntime().gc();
    248    }
    249
    250    /**
    251     * Save a BufferedImage into an image file.
    252     *
    253     * @param image
    254     *            the BufferedImage to save.
    255     * @param file
    256     *            the image file.
    257     * @param type
    258     *            the image type.
    259     *
    260     * @throws IOException
    261     *             if a failure occurred
    262     */
    263    public static void saveImageAs(BufferedImage image, File file, String type) throws IOException {
    264        if (image == null)
    265            throw new NullPointerException("The source image is null.");
    266
    267        ImageIO.write(image, type, file);
    268    }
    269
    270    /**
    271     * Creates the gray palette of the indexed 256-color table.
    272     *
    273     * The palette values are stored in a two-dimensional byte array and arrange
    274     * by color components of red, green and blue. palette[][] = byte[3][256],
    275     * where, palette[0][], palette[1][] and palette[2][] are the red, green and
    276     * blue components respectively.
    277     *
    278     * @return the gray palette in the form of byte[3][256]
    279     */
    280    public static final byte[][] createGrayPalette() {
    281        byte[][] p = new byte[3][256];
    282
    283        for (int i = 0; i < 256; i++)
    284            p[0][i] = p[1][i] = p[2][i] = (byte) (i);
    285
    286        return p;
    287    }
    288
    289    /**
    290     * Creates the reverse gray palette of the indexed 256-color table.
    291     *
    292     * The palette values are stored in a two-dimensional byte array and arrange
    293     * by color components of red, green and blue. palette[][] = byte[3][256],
    294     * where, palette[0][], palette[1][] and palette[2][] are the red, green and
    295     * blue components respectively.
    296     *
    297     * @return the gray palette in the form of byte[3][256]
    298     */
    299    public static final byte[][] createReverseGrayPalette() {
    300        byte[][] p = new byte[3][256];
    301
    302        for (int i = 0; i < 256; i++)
    303            p[0][i] = p[1][i] = p[2][i] = (byte) (255 - i);
    304
    305        return p;
    306    }
    307
    308    /**
    309     * Creates the gray wave palette of the indexed 256-color table.
    310     *
    311     * The palette values are stored in a two-dimensional byte array and arrange
    312     * by color components of red, green and blue. palette[][] = byte[3][256],
    313     * where, palette[0][], palette[1][] and palette[2][] are the red, green and
    314     * blue components respectively.
    315     *
    316     * @return the gray palette in the form of byte[3][256]
    317     */
    318    public static final byte[][] createGrayWavePalette() {
    319        byte[][] p = new byte[3][256];
    320
    321        for (int i = 0; i < 256; i++)
    322            p[0][i] = p[1][i] = p[2][i] = (byte) ((double) 255 / 2 + ((double) 255 / 2) * Math.sin((i - 32) / 20.3));
    323
    324        return p;
    325    }
    326
    327    /**
    328     * Creates the rainbow palette of the indexed 256-color table.
    329     *
    330     * The palette values are stored in a two-dimensional byte array and arrange
    331     * by color components of red, green and blue. palette[][] = byte[3][256],
    332     * where, palette[0][], palette[1][] and palette[2][] are the red, green and
    333     * blue components respectively.
    334     *
    335     * @return the rainbow palette in the form of byte[3][256]
    336     */
    337    public static final byte[][] createRainbowPalette() {
    338        byte r;
    339        byte g;
    340        byte b;
    341        byte[][] p = new byte[3][256];
    342
    343        for (int i = 1; i < 255; i++) {
    344            if (i <= 29) {
    345                r = (byte) (129.36 - i * 4.36);
    346                g = 0;
    347                b = (byte) 255;
    348            }
    349            else if (i <= 86) {
    350                r = 0;
    351                g = (byte) (-133.54 + i * 4.52);
    352                b = (byte) 255;
    353            }
    354            else if (i <= 141) {
    355                r = 0;
    356                g = (byte) 255;
    357                b = (byte) (665.83 - i * 4.72);
    358            }
    359            else if (i <= 199) {
    360                r = (byte) (-635.26 + i * 4.47);
    361                g = (byte) 255;
    362                b = 0;
    363            }
    364            else {
    365                r = (byte) 255;
    366                g = (byte) (1166.81 - i * 4.57);
    367                b = 0;
    368            }
    369
    370            p[0][i] = r;
    371            p[1][i] = g;
    372            p[2][i] = b;
    373        }
    374
    375        p[0][0] = p[1][0] = p[2][0] = 0;
    376        p[0][255] = p[1][255] = p[2][255] = (byte) 255;
    377
    378        return p;
    379    }
    380
    381    /**
    382     * Creates the nature palette of the indexed 256-color table.
    383     *
    384     * The palette values are stored in a two-dimensional byte array and arrange
    385     * by color components of red, green and blue. palette[][] = byte[3][256],
    386     * where, palette[0][], palette[1][] and palette[2][] are the red, green and
    387     * blue components respectively.
    388     *
    389     * @return the nature palette in the form of byte[3][256]
    390     */
    391    public static final byte[][] createNaturePalette() {
    392        byte[][] p = new byte[3][256];
    393
    394        for (int i = 1; i < 210; i++) {
    395            p[0][i] = (byte) ((Math.sin((double) (i - 5) / 16) + 1) * 90);
    396            p[1][i] = (byte) ((1 - Math.sin((double) (i - 30) / 12)) * 64 * (1 - (double) i / 255) + 128 - (double) i / 2);
    397            p[2][i] = (byte) ((1 - Math.sin((double) (i - 8) / 9)) * 110 + 30);
    398        }
    399
    400        for (int i = 210; i < 255; i++) {
    401            p[0][i] = (byte) 80;
    402            p[1][i] = (byte) 0;
    403            p[2][i] = (byte) 200;
    404        }
    405
    406        p[0][0] = p[1][0] = p[2][0] = 0;
    407        p[0][255] = p[1][255] = p[2][255] = (byte) 255;
    408
    409        return p;
    410    }
    411
    412    /**
    413     * Creates the wave palette of the indexed 256-color table.
    414     *
    415     * The palette values are stored in a two-dimensional byte array and arrange
    416     * by color components of red, green and blue. palette[][] = byte[3][256],
    417     * where, palette[0][], palette[1][] and palette[2][] are the red, green and
    418     * blue components respectively.
    419     *
    420     * @return the wave palette in the form of byte[3][256]
    421     */
    422    public static final byte[][] createWavePalette() {
    423        byte[][] p = new byte[3][256];
    424
    425        for (int i = 1; i < 255; i++) {
    426            p[0][i] = (byte) ((Math.sin(((double) i / 40 - 3.2)) + 1) * 128);
    427            p[1][i] = (byte) ((1 - Math.sin((i / 2.55 - 3.1))) * 70 + 30);
    428            p[2][i] = (byte) ((1 - Math.sin(((double) i / 40 - 3.1))) * 128);
    429        }
    430
    431        p[0][0] = p[1][0] = p[2][0] = 0;
    432        p[0][255] = p[1][255] = p[2][255] = (byte) 255;
    433
    434        return p;
    435    }
    436
    437    /**
    438     * read an image palette from a file.
    439     *
    440     * A palette file has format of (value, red, green, blue). The color value
    441     * in palette file can be either unsigned char [0..255] or float [0..1].
    442     * Float value will be converted to [0..255].
    443     *
    444     * The color table in file can have any number of entries between 2 to 256.
    445     * It will be converted to a color table of 256 entries. Any missing index
    446     * will calculated by linear interpolation between the neighboring index
    447     * values. For example, index 11 is missing in the following table 10 200 60
    448     * 20 12 100 100 60 Index 11 will be calculated based on index 10 and index
    449     * 12, i.e. 11 150 80 40
    450     *
    451     * @param filename
    452     *            the name of the palette file.
    453     *
    454     * @return the wave palette in the form of byte[3][256]
    455     */
    456    public static final byte[][] readPalette(String filename) {
    457        final int COLOR256 = 256;
    458        int nentries = 0;
    459        int i = 0;
    460        int j = 0;
    461        int idx = 0;
    462        float v = 0;
    463        float r = 0;
    464        float g = 0;
    465        float b = 0;
    466        float ratio = 0;
    467        float maxV = 0;
    468        float minV = 0;
    469        float maxColor = 0;
    470        float minColor = 0;
    471        float[][] tbl = new float[COLOR256][4]; /* value, red, green, blue */
    472
    473        if (filename == null)
    474            return null;
    475
    476        try (BufferedReader in = new BufferedReader(new FileReader(filename))) {
    477            String line = null;
    478            do {
    479                try {
    480                    line = in.readLine();
    481                }
    482                catch (Exception ex) {
    483                    log.debug("input file:", ex);
    484                    line = null;
    485                }
    486
    487                if (line == null)
    488                    continue;
    489
    490                StringTokenizer st = new StringTokenizer(line);
    491
    492                // invalid line
    493                if (st.countTokens() != 4) {
    494                    continue;
    495                }
    496
    497                try {
    498                    v = Float.valueOf(st.nextToken());
    499                    r = Float.valueOf(st.nextToken());
    500                    g = Float.valueOf(st.nextToken());
    501                    b = Float.valueOf(st.nextToken());
    502                }
    503                catch (NumberFormatException ex) {
    504                    log.debug("input file:", ex);
    505                    continue;
    506                }
    507
    508                tbl[idx][0] = v;
    509                tbl[idx][1] = r;
    510                tbl[idx][2] = g;
    511                tbl[idx][3] = b;
    512
    513                if (idx == 0) {
    514                    maxV = minV = v;
    515                    maxColor = minColor = r;
    516                }
    517
    518                maxV = Math.max(maxV, v);
    519                maxColor = Math.max(maxColor, r);
    520                maxColor = Math.max(maxColor, g);
    521                maxColor = Math.max(maxColor, b);
    522
    523                minV = Math.min(minV, v);
    524                minColor = Math.min(minColor, r);
    525                minColor = Math.min(minColor, g);
    526                minColor = Math.min(minColor, b);
    527
    528                idx++;
    529                if (idx >= COLOR256)
    530                    break; /* only support to 256 colors */
    531            } while (line != null);
    532        }
    533        catch (Exception ex) {
    534            log.debug("input file:", ex);
    535        }
    536
    537        nentries = idx;
    538        if (nentries <= 1) // must have more than one entries
    539            return null;
    540
    541        // convert color table to byte
    542        nentries = idx;
    543        if (maxColor <= 1) {
    544            ratio = (minColor == maxColor) ? 1.0f : ((COLOR256 - 1.0f) / (maxColor - minColor));
    545
    546            for (i = 0; i < nentries; i++) {
    547                for (j = 1; j < 4; j++)
    548                    tbl[i][j] = (tbl[i][j] - minColor) * ratio;
    549            }
    550        }
    551
    552        // convert table to 256 entries
    553        idx = 0;
    554        ratio = (minV == maxV) ? 1.0f : ((COLOR256 - 1.0f) / (maxV - minV));
    555
    556        int[][] p = new int[3][COLOR256];
    557        for (i = 0; i < nentries; i++) {
    558            idx = (int) ((tbl[i][0] - minV) * ratio);
    559            for (j = 0; j < 3; j++)
    560                p[j][idx] = (int) tbl[i][j + 1];
    561        }
    562
    563        /* linear interpolating missing values in the color table */
    564        for (i = 1; i < COLOR256; i++) {
    565            if ((p[0][i] + p[1][i] + p[2][i]) == 0) {
    566                j = i + 1;
    567
    568                // figure out number of missing points between two given points
    569                while (j < COLOR256 && (p[0][j] + p[1][j] + p[2][j]) == 0)
    570                    j++;
    571
    572                if (j >= COLOR256) break; // nothing in the table to interpolating
    573
    574                float d1 = (p[0][j] - p[0][i - 1]) / (float) (j - i);
    575                float d2 = (p[1][j] - p[1][i - 1]) / (float) (j - i);
    576                float d3 = (p[2][j] - p[2][i - 1]) / (float) (j - i);
    577
    578                for (int k = i; k <= j; k++) {
    579                    p[0][k] = (int) (p[0][i - 1] + d1 * (k - i + 1));
    580                    p[1][k] = (int) (p[1][i - 1] + d2 * (k - i + 1));
    581                    p[2][k] = (int) (p[2][i - 1] + d3 * (k - i + 1));
    582                }
    583                i = j + 1;
    584            } // ((p[0][i] + p[1][i] + p[2][i]) == 0)
    585        } // (i = 1; i < COLOR256; i++)
    586
    587        byte[][] pal = new byte[3][COLOR256];
    588        for (i = 1; i < COLOR256; i++) {
    589            for (j = 0; j < 3; j++)
    590                pal[j][i] = (byte) (p[j][i]);
    591        }
    592
    593        return pal;
    594    }
    595
    596    /**
    597     * This method returns true if the specified image has transparent pixels.
    598     *
    599     * @param image
    600     *            the image to be check if has alpha.
    601     *
    602     * @return true if the image has alpha setting.
    603     */
    604    public static boolean hasAlpha(Image image) {
    605        if (image == null)
    606            return false;
    607
    608        // If buffered image, the color model is readily available
    609        if (image instanceof BufferedImage) {
    610            BufferedImage bimage = (BufferedImage) image;
    611            return bimage.getColorModel().hasAlpha();
    612        }
    613
    614        // Use a pixel grabber to retrieve the image's color model
    615        // grabbing a single pixel is usually sufficient
    616        PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
    617        try {
    618            pg.grabPixels();
    619        }
    620        catch (InterruptedException e) {
    621            log.debug("transparent pixels:", e);
    622        }
    623        ColorModel cm = pg.getColorModel();
    624
    625        return cm.hasAlpha();
    626    }
    627
    628    /**
    629     * Creates a RGB indexed image of 256 colors.
    630     *
    631     * @param bufferedImage
    632     *            the target image.
    633     * @param imageData
    634     *            the byte array of the image data.
    635     * @param palette
    636     *            the color lookup table.
    637     * @param w
    638     *            the width of the image.
    639     * @param h
    640     *            the height of the image.
    641     *
    642     * @return the image.
    643     */
    644    public static Image createIndexedImage(BufferedImage bufferedImage, byte[] imageData, byte[][] palette, long w, long h)
    645    {
    646        if (imageData==null || w<=0 || h<=0)
    647            return null;
    648
    649        if (palette==null)
    650            palette = Tools.createGrayPalette();
    651
    652        if (bufferedImage == null)
    653            bufferedImage = new BufferedImage((int)w, (int)h, BufferedImage.TYPE_INT_ARGB);
    654
    655        final int[] pixels = ( (DataBufferInt) bufferedImage.getRaster().getDataBuffer() ).getData();
    656        int len = pixels.length;
    657
    658        for (int i=0; i<len; i++) {
    659            int idx = imageData[i] & 0xff;
    660            int r = (palette[0][idx] & 0xff)<<16;
    661            int g = (palette[1][idx] & 0xff)<<8;
    662            int b = palette[2][idx] & 0xff;
    663
    664            pixels[i] = 0xff000000 | r | g | b;
    665        }
    666
    667        return bufferedImage;
    668    }
    669
    670    /**
    671     * Creates a true color image.
    672     *
    673     * DirectColorModel is used to construct the image from raw data. The DirectColorModel model is
    674     * similar to an X11 TrueColor visual, which has the following parameters: <br>
    675     *
    676     * <pre>
    677     * Number of bits:        32
    678     *             Red mask:              0x00ff0000
    679     *             Green mask:            0x0000ff00
    680     *             Blue mask:             0x000000ff
    681     *             Alpha mask:            0xff000000
    682     *             Color space:           sRGB
    683     *             isAlphaPremultiplied:  False
    684     *             Transparency:          Transparency.TRANSLUCENT
    685     *             transferType:          DataBuffer.TYPE_INT
    686     * </pre>
    687     *
    688     * The data may be arranged in one of two ways: by pixel or by plane. In both cases, the dataset
    689     * will have a dataspace with three dimensions, height, width, and components.
    690     *
    691     * For HDF4, the interlace modes specify orders for the dimensions as:
    692     *
    693     * <pre>
    694     * INTERLACE_PIXEL = [width][height][pixel components]
    695     *            INTERLACE_PLANE = [pixel components][width][height]
    696     * </pre>
    697     *
    698     * For HDF5, the interlace modes specify orders for the dimensions as:
    699     *
    700     * <pre>
    701     * INTERLACE_PIXEL = [height][width][pixel components]
    702     *            INTERLACE_PLANE = [pixel components][height][width]
    703     * </pre>
    704     *
    705     * @param imageData
    706     *            the byte array of the image data.
    707     * @param planeInterlace
    708     *            flag if the image is plane interlace.
    709     * @param w
    710     *            the width of the image.
    711     * @param h
    712     *            the height of the image.
    713     *
    714     * @return the image.
    715     */
    716    public static Image createTrueColorImage(byte[] imageData, boolean planeInterlace, long w, long h) {
    717        Image theImage = null;
    718        long imgSize = w * h;
    719        int[] packedImageData = new int[(int) imgSize];
    720        int pixel = 0;
    721        int idx = 0;
    722        int r = 0;
    723        int g = 0;
    724        int b = 0;
    725        for (int i = 0; i < h; i++) {
    726            for (int j = 0; j < w; j++) {
    727                if (planeInterlace) {
    728                    r = imageData[idx];
    729                    g = imageData[(int)imgSize + idx];
    730                    b = imageData[(int)imgSize * 2 + idx];
    731                }
    732                else {
    733                    r = imageData[idx * 3];
    734                    g = imageData[idx * 3 + 1];
    735                    b = imageData[idx * 3 + 2];
    736                }
    737
    738                r = (r << 16) & 0x00ff0000;
    739                g = (g << 8) & 0x0000ff00;
    740                b = b & 0x000000ff;
    741
    742                // bits packed into alpha (1), red (r), green (g) and blue (b)
    743                // as 11111111rrrrrrrrggggggggbbbbbbbb
    744                pixel = 0xff000000 | r | g | b;
    745                packedImageData[idx++] = pixel;
    746            } // (int j=0; j<w; j++)
    747        } // (int i=0; i<h; i++)
    748
    749        DirectColorModel dcm = (DirectColorModel) ColorModel.getRGBdefault();
    750        theImage = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource((int)w, (int)h, dcm, packedImageData, 0, (int)w));
    751
    752        return theImage;
    753    }
    754
    755    /**
    756     * This method returns a buffered image with the contents of an image.
    757     *
    758     * @param image
    759     *            the plain image object.
    760     *
    761     * @return buffered image for the given image.
    762     */
    763    public static BufferedImage toBufferedImage(Image image) {
    764        if (image == null)
    765            return null;
    766
    767        if (image instanceof BufferedImage)
    768            return (BufferedImage) image;
    769
    770        // !!!!!!!!!!!!!!!!!! NOTICE !!!!!!!!!!!!!!!!!!!!!
    771        // the following way of creating a buffered image is using
    772        // Component.createImage(). This method can be used only if the
    773        // component is visible on the screen. Also, this method returns
    774        // buffered images that do not support transparent pixels.
    775        // The buffered image created by this way works for package
    776        // com.sun.image.codec.jpeg.*
    777        // It does not work well with JavaTM Advanced Imaging
    778        // com.sun.media.jai.codec.*
    779        // if the screen setting is less than 32-bit color
    780        int w = image.getWidth(null);
    781        int h = image.getHeight(null);
    782        BufferedImage bimage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    783        Graphics g = bimage.createGraphics();
    784        g.drawImage(image, 0, 0, null);
    785
    786        g.dispose();
    787        return bimage;
    788    }
    789
    790    /**
    791     * Convert an array of raw data into array of a byte data.
    792     *
    793     * @param rawData
    794     *            The input raw data.
    795     * @param minmax
    796     *            the range of the raw data.
    797     * @param w
    798     *            the width of the raw data.
    799     * @param h
    800     *            the height of the raw data.
    801     * @param isTransposed
    802     *            if the data is transposed.
    803     * @param byteData
    804     *            the data in.
    805     *
    806     * @return the byte array of pixel data.
    807     */
    808    public static byte[] getBytes(Object rawData, double[] minmax, long w, long h, boolean isTransposed, byte[] byteData) {
    809        return Tools.getBytes(rawData, minmax, w, h, isTransposed, null, false, byteData);
    810    }
    811
    812    /**
    813     * Convert an array of raw data into array of a byte data.
    814     *
    815     * @param rawData
    816     *            The input raw data.
    817     * @param minmax
    818     *            the range of the raw data.
    819     * @param w
    820     *            the width of the raw data.
    821     * @param h
    822     *            the height of the raw data.
    823     * @param isTransposed
    824     *            if the data is transposed.
    825     * @param invalidValues
    826     *            list of values out of range.
    827     * @param byteData
    828     *            the data in.
    829     *
    830     * @return the byte array of pixel data.
    831     */
    832    public static byte[] getBytes(Object rawData, double[] minmax, long w, long h, boolean isTransposed,
    833            List<Number> invalidValues, byte[] byteData) {
    834        return getBytes(rawData, minmax, w, h, isTransposed, invalidValues, false, byteData);
    835    }
    836
    837    /**
    838     * Convert an array of raw data into array of a byte data.
    839     *
    840     * @param rawData
    841     *            The input raw data.
    842     * @param minmax
    843     *            the range of the raw data.
    844     * @param w
    845     *            the width of the raw data.
    846     * @param h
    847     *            the height of the raw data.
    848     * @param isTransposed
    849     *            if the data is transposed.
    850     * @param invalidValues
    851     *            list of values out of range.
    852     * @param convertByteData
    853     *            the converted data out.
    854     * @param byteData
    855     *            the data in.
    856     *
    857     * @return the byte array of pixel data.
    858     */
    859    public static byte[] getBytes(Object rawData, double[] minmax, long w, long h, boolean isTransposed,
    860            List<Number> invalidValues, boolean convertByteData, byte[] byteData) {
    861        return getBytes(rawData, minmax, w, h, isTransposed,invalidValues, convertByteData, byteData, null);
    862    }
    863
    864    /**
    865     * Convert an array of raw data into array of a byte data.
    866     *
    867     * @param rawData
    868     *            The input raw data.
    869     * @param minmax
    870     *            the range of the raw data.
    871     * @param w
    872     *            the width of the raw data.
    873     * @param h
    874     *            the height of the raw data.
    875     * @param isTransposed
    876     *            if the data is transposed.
    877     * @param invalidValues
    878     *            the list of invalid values.
    879     * @param convertByteData
    880     *            the converted data out.
    881     * @param byteData
    882     *            the data in.
    883     * @param list
    884     *            the list of integers.
    885     *
    886     * @return the byte array of pixel data.
    887     */
    888    public static byte[] getBytes(Object rawData, double[] minmax, long w, long h, boolean isTransposed,
    889            List<Number> invalidValues, boolean convertByteData, byte[] byteData, List<Integer> list) {
    890        double[] fillValue = null;
    891
    892        // no input data
    893        if (rawData == null || w <= 0 || h <= 0)
    894            return null;
    895
    896        // input data is not an array
    897        if (!rawData.getClass().isArray())
    898            return null;
    899
    900        String cname = rawData.getClass().getName();
    901        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
    902        int size = Array.getLength(rawData);
    903
    904        if (minmax == null) {
    905            minmax = new double[2];
    906            minmax[0] = minmax[1] = 0;
    907        }
    908
    909        if (dname == 'B')
    910            return convertByteData((byte[]) rawData, minmax, w, h, isTransposed, fillValue, convertByteData, byteData, list);
    911
    912        if ((byteData == null) || (size != byteData.length))
    913            byteData = new byte[size]; // reuse the old buffer
    914
    915        if (minmax[0] == minmax[1])
    916            Tools.findMinMax(rawData, minmax, fillValue);
    917
    918        double min = minmax[0];
    919        double max = minmax[1];
    920
    921        if (invalidValues != null && !invalidValues.isEmpty()) {
    922            int n = invalidValues.size();
    923            fillValue = new double[n];
    924            for (int i=0; i<n; i++) {
    925                fillValue[i] = invalidValues.get(i).doubleValue();
    926            }
    927        }
    928        double ratio = (min == max) ? 1.00d : (double) (255.00 / (max - min));
    929        long idxSrc = 0;
    930        long idxDst = 0;
    931        switch (dname) {
    932            case 'S':
    933                short[] s = (short[]) rawData;
    934                for (long i = 0; i < h; i++) {
    935                    for (long j = 0; j < w; j++) {
    936                        idxSrc = idxDst = j * h + i;
    937                        if (isTransposed)
    938                            idxDst = i * w + j;
    939                        byteData[(int)idxDst] = toByte(s[(int)idxSrc], ratio, min, max, fillValue, (int)idxSrc, list);
    940                    }
    941                }
    942                break;
    943
    944            case 'I':
    945                int[] ia = (int[]) rawData;
    946                for (long i = 0; i < h; i++) {
    947                    for (long j = 0; j < w; j++) {
    948                        idxSrc = idxDst = (j * h + i);
    949                        if (isTransposed)
    950                            idxDst = i * w + j;
    951                        byteData[(int)idxDst] = toByte(ia[(int)idxSrc], ratio, min, max, fillValue, (int)idxSrc, list);
    952                    }
    953                }
    954                break;
    955
    956            case 'J':
    957                long[] l = (long[]) rawData;
    958                for (long i = 0; i < h; i++) {
    959                    for (long j = 0; j < w; j++) {
    960                        idxSrc = idxDst = j * h + i;
    961                        if (isTransposed)
    962                            idxDst = i * w + j;
    963                        byteData[(int)idxDst] = toByte(l[(int)idxSrc], ratio, min, max, fillValue, (int)idxSrc, list);
    964                    }
    965                }
    966                break;
    967
    968            case 'F':
    969                float[] f = (float[]) rawData;
    970                for (long i = 0; i < h; i++) {
    971                    for (long j = 0; j < w; j++) {
    972                        idxSrc = idxDst = j * h + i;
    973                        if (isTransposed)
    974                            idxDst = i * w + j;
    975                        byteData[(int)idxDst] = toByte(f[(int)idxSrc], ratio, min, max, fillValue, (int)idxSrc, list);
    976                    }
    977                }
    978                break;
    979
    980            case 'D':
    981                double[] d = (double[]) rawData;
    982                for (long i = 0; i < h; i++) {
    983                    for (long j = 0; j < w; j++) {
    984                        idxSrc = idxDst = j * h + i;
    985                        if (isTransposed)
    986                            idxDst = i * w + j;
    987                        byteData[(int)idxDst] = toByte(d[(int)idxSrc], ratio, min, max, fillValue, (int)idxSrc, list);
    988                    }
    989                }
    990                break;
    991
    992            default:
    993                byteData = null;
    994                break;
    995        } // (dname)
    996
    997        return byteData;
    998    }
    999
    1000    private static byte toByte(double in, double ratio, double min, double max, double[] fill, int idx,  List<Integer> list) {
    1001        byte out = 0;
    1002
    1003        if (in < min || in > max || isFillValue(in, fill) || isNaNINF(in)) {
    1004            out = 0;
    1005            if (list != null)
    1006                list.add(idx);
    1007        }
    1008        else
    1009            out = (byte) ((in-min)*ratio);
    1010
    1011        return out;
    1012    }
    1013
    1014    private static boolean isFillValue(double in, double[] fill) {
    1015        if (fill == null)
    1016            return false;
    1017
    1018        for (int i = 0; i < fill.length; i++) {
    1019            if (fill[i] == in)
    1020                return true;
    1021        }
    1022
    1023        return false;
    1024    }
    1025
    1026    private static byte[] convertByteData(byte[] rawData, double[] minmax, long w, long h, boolean isTransposed,
    1027            Object fillValue, boolean convertByteData, byte[] byteData, List<Integer> list) {
    1028        if (rawData == null) return null;
    1029
    1030        if (convertByteData) {
    1031            if (minmax[0] == minmax[1])
    1032                Tools.findMinMax(rawData, minmax, fillValue);
    1033        }
    1034
    1035        if (minmax[0] == 0 && minmax[1] == 255)
    1036            convertByteData = false; // no need to convert data
    1037
    1038        // no conversion and no transpose
    1039        if (!convertByteData && !isTransposed) {
    1040            if (byteData != null && byteData.length == rawData.length) {
    1041                System.arraycopy(rawData, 0, byteData, 0, rawData.length);
    1042                return byteData;
    1043            }
    1044
    1045            return rawData;
    1046        }
    1047
    1048        // don't want to change the original raw data
    1049        if (byteData == null || rawData == byteData)
    1050            byteData = new byte[rawData.length];
    1051
    1052        if (!convertByteData) {
    1053            // do not convert data, just transpose the data
    1054            minmax[0] = 0;
    1055            minmax[1] = 255;
    1056            if (isTransposed) {
    1057                for (long i = 0; i < h; i++) {
    1058                    for (long j = 0; j < w; j++)
    1059                        byteData[(int)(i * w + j)] = rawData[(int)(j * h + i)];
    1060                }
    1061            }
    1062            return byteData;
    1063        }
    1064
    1065        // special data range used, must convert the data
    1066        double min = minmax[0];
    1067        double max = minmax[1];
    1068        double ratio = (min == max) ? 1.00d : (double) (255.00 / (max - min));
    1069        long idxSrc = 0;
    1070        long idxDst = 0;
    1071        for (long i = 0; i < h; i++) {
    1072            for (long j = 0; j < w; j++) {
    1073                idxSrc = idxDst =j * h + i;
    1074                if (isTransposed)
    1075                    idxDst = i * w + j;
    1076
    1077                if (rawData[(int) idxSrc] > max || rawData[(int) idxSrc] < min) {
    1078                    byteData[(int)idxDst] = (byte) 0;
    1079                    if (list!=null)
    1080                        list.add((int)idxSrc);
    1081                }
    1082                else
    1083                    byteData[(int)idxDst] = (byte) ((rawData[(int)idxSrc] - min) * ratio);
    1084            }
    1085        }
    1086
    1087        return byteData;
    1088    }
    1089
    1090    /**
    1091     * Create and initialize a new instance of the given class.
    1092     *
    1093     * @param cls
    1094     *           the class of the instance
    1095     * @param initargs
    1096     *            array of objects to be passed as arguments.
    1097     *
    1098     * @return a new instance of the given class.
    1099     *
    1100     * @throws Exception if a failure occurred
    1101     */
    1102    public static Object newInstance(Class<?> cls, Object[] initargs) throws Exception {
    1103        log.trace("newInstance(Class = {}): start", cls);
    1104
    1105        if (cls == null)
    1106            return null;
    1107
    1108        Object instance = null;
    1109
    1110        if ((initargs == null) || (initargs.length == 0)) {
    1111            instance = cls.getDeclaredConstructor().newInstance();
    1112        }
    1113        else {
    1114            Constructor<?>[] constructors = cls.getConstructors();
    1115            if ((constructors == null) || (constructors.length == 0))
    1116                return null;
    1117
    1118            boolean isConstructorMatched = false;
    1119            Constructor<?> constructor = null;
    1120            Class<?>[] params = null;
    1121            int m = constructors.length;
    1122            int n = initargs.length;
    1123            for (int i = 0; i < m; i++) {
    1124                constructor = constructors[i];
    1125                params = constructor.getParameterTypes();
    1126                if (params.length == n) {
    1127                    // check if all the parameters are matched
    1128                    isConstructorMatched = params[0].isInstance(initargs[0]);
    1129                    for (int j = 0; j < n; j++)
    1130                        isConstructorMatched = isConstructorMatched && params[j].isInstance(initargs[j]);
    1131
    1132                    if (isConstructorMatched) {
    1133                        try {
    1134                            instance = constructor.newInstance(initargs);
    1135                        }
    1136                        catch (Exception ex) {
    1137                            log.debug("Error creating instance of {}: ", cls, ex);
    1138                            ex.printStackTrace();
    1139                        }
    1140                        break;
    1141                    }
    1142                }
    1143            } // (int i=0; i<m; i++)
    1144        }
    1145
    1146        return instance;
    1147    }
    1148
    1149    /**
    1150     * Computes autocontrast parameters (gain equates to contrast and bias
    1151     * equates to brightness) for integers.
    1152     *
    1153     * The computation is based on the following scaling
    1154     *
    1155     * <pre>
    1156     *      int_8       [0, 127]
    1157     *      uint_8      [0, 255]
    1158     *      int_16      [0, 32767]
    1159     *      uint_16     [0, 65535]
    1160     *      int_32      [0, 2147483647]
    1161     *      uint_32     [0, 4294967295]
    1162     *      int_64      [0, 9223372036854775807]
    1163     *      uint_64     [0, 18446744073709551615] // Not supported.
    1164     * </pre>
    1165     *
    1166     * @param data
    1167     *            the raw data array of signed/unsigned integers
    1168     * @param params
    1169     *            the auto gain parameter. params[0]=gain, params[1]=bias,
    1170     * @param isUnsigned
    1171     *            the flag to indicate if the data array is unsigned integer.
    1172     *
    1173     * @return non-negative if successful; otherwise, returns negative
    1174     */
    1175    public static int autoContrastCompute(Object data, double[] params, boolean isUnsigned) {
    1176        int retval = 1;
    1177        long maxDataValue = 255;
    1178        double[] minmax = new double[2];
    1179
    1180        // check parameters
    1181        if ((data == null) || (params == null) || (Array.getLength(data) <= 0) || (params.length < 2))
    1182            return -1;
    1183
    1184        retval = autoContrastComputeMinMax(data, minmax);
    1185
    1186        // force the min_max method so we can look at the target grids data sets
    1187        if ((retval < 0) || (minmax[1] - minmax[0] < 10))
    1188            retval = findMinMax(data, minmax, null);
    1189
    1190        if (retval < 0)
    1191            return -1;
    1192
    1193        String cname = data.getClass().getName();
    1194        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
    1195        switch (dname) {
    1196            case 'B':
    1197                maxDataValue = MAX_INT8;
    1198                break;
    1199            case 'S':
    1200                maxDataValue = MAX_INT16;
    1201                if (isUnsigned)
    1202                    maxDataValue = MAX_UINT8; // data was upgraded from unsigned byte
    1203                break;
    1204            case 'I':
    1205                maxDataValue = MAX_INT32;
    1206                if (isUnsigned)
    1207                    maxDataValue = MAX_UINT16; // data was upgraded from unsigned short
    1208                break;
    1209            case 'J':
    1210                maxDataValue = MAX_INT64;
    1211                if (isUnsigned)
    1212                    maxDataValue = MAX_UINT32; // data was upgraded from unsigned int
    1213                break;
    1214            default:
    1215                retval = -1;
    1216                break;
    1217        } // (dname)
    1218
    1219        if (minmax[0] == minmax[1]) {
    1220            params[0] = 1.0;
    1221            params[1] = 0.0;
    1222        }
    1223        else {
    1224            // This histogram method has a tendency to stretch the
    1225            // range of values to be a bit too big, so we can
    1226            // account for this by adding and subtracting some percent
    1227            // of the difference to the max/min values
    1228            // to prevent the gain from going too high.
    1229            double diff = minmax[1] - minmax[0];
    1230            double newmax = (minmax[1] + (diff * 0.1));
    1231            double newmin = (minmax[0] - (diff * 0.1));
    1232
    1233            if (newmax <= maxDataValue)
    1234                minmax[1] = newmax;
    1235
    1236            if (newmin >= 0)
    1237                minmax[0] = newmin;
    1238
    1239            params[0] = maxDataValue / (minmax[1] - minmax[0]);
    1240            params[1] = -minmax[0];
    1241        }
    1242
    1243        return retval;
    1244    }
    1245
    1246    /**
    1247     * Apply autocontrast parameters to the original data in place (destructive)
    1248     *
    1249     * @param dataIN
    1250     *            the original data array of signed/unsigned integers
    1251     * @param dataOUT
    1252     *            the converted data array of signed/unsigned integers
    1253     * @param params
    1254     *            the auto gain parameter. params[0]=gain, params[1]=bias
    1255     * @param minmax
    1256     *            the data range. minmax[0]=min, minmax[1]=max
    1257     * @param isUnsigned
    1258     *            the flag to indicate if the data array is unsigned integer
    1259     *
    1260     * @return the data array with the auto contrast conversion; otherwise,
    1261     *         returns null
    1262     */
    1263    public static Object autoContrastApply(Object dataIN, Object dataOUT, double[] params, double[] minmax,
    1264            boolean isUnsigned) {
    1265        int size = 0;
    1266        double min = -MAX_INT64;
    1267        double max = MAX_INT64;
    1268
    1269        if ((dataIN == null) || (params == null) || (params.length < 2))
    1270            return null;
    1271
    1272        if (minmax != null) {
    1273            min = minmax[0];
    1274            max = minmax[1];
    1275        }
    1276        // input and output array must be the same size
    1277        size = Array.getLength(dataIN);
    1278        if ((dataOUT != null) && (size != Array.getLength(dataOUT)))
    1279            return null;
    1280
    1281        double gain = params[0];
    1282        double bias = params[1];
    1283        double valueOut;
    1284        double valueIn;
    1285        String cname = dataIN.getClass().getName();
    1286        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
    1287
    1288        switch (dname) {
    1289            case 'B':
    1290                byte[] bIn = (byte[]) dataIN;
    1291                if (dataOUT == null)
    1292                    dataOUT = new byte[size];
    1293                byte[] bOut = (byte[]) dataOUT;
    1294                byte bMax = (byte) MAX_INT8;
    1295
    1296                for (int i = 0; i < size; i++) {
    1297                    valueIn = Math.max(bIn[i], min);
    1298                    valueIn = Math.min(valueIn, max);
    1299                    valueOut = (valueIn + bias) * gain;
    1300                    valueOut = Math.max(valueOut, 0.0);
    1301                    valueOut = Math.min(valueOut, bMax);
    1302                    bOut[i] = (byte) valueOut;
    1303                }
    1304                break;
    1305            case 'S':
    1306                short[] sIn = (short[]) dataIN;
    1307                if (dataOUT == null)
    1308                    dataOUT = new short[size];
    1309                short[] sOut = (short[]) dataOUT;
    1310                short sMax = (short) MAX_INT16;
    1311
    1312                if (isUnsigned)
    1313                    sMax = (short) MAX_UINT8; // data was upgraded from unsigned byte
    1314
    1315                for (int i = 0; i < size; i++) {
    1316                    valueIn = Math.max(sIn[i], min);
    1317                    valueIn = Math.min(valueIn, max);
    1318                    valueOut = (valueIn + bias) * gain;
    1319                    valueOut = Math.max(valueOut, 0.0);
    1320                    valueOut = Math.min(valueOut, sMax);
    1321                    sOut[i] = (byte) valueOut;
    1322                }
    1323                break;
    1324            case 'I':
    1325                int[] iIn = (int[]) dataIN;
    1326                if (dataOUT == null)
    1327                    dataOUT = new int[size];
    1328                int[] iOut = (int[]) dataOUT;
    1329                int iMax = (int) MAX_INT32;
    1330                if (isUnsigned)
    1331                    iMax = (int) MAX_UINT16; // data was upgraded from unsigned short
    1332
    1333                for (int i = 0; i < size; i++) {
    1334                    valueIn = Math.max(iIn[i], min);
    1335                    valueIn = Math.min(valueIn, max);
    1336                    valueOut = (valueIn + bias) * gain;
    1337                    valueOut = Math.max(valueOut, 0.0);
    1338                    valueOut = Math.min(valueOut, iMax);
    1339                    iOut[i] = (byte) valueOut;
    1340                }
    1341                break;
    1342            case 'J':
    1343                long[] lIn = (long[]) dataIN;
    1344                if (dataOUT == null)
    1345                    dataOUT = new long[size];
    1346                long[] lOut = (long[]) dataOUT;
    1347                long lMax = MAX_INT64;
    1348                if (isUnsigned)
    1349                    lMax = MAX_UINT32; // data was upgraded from unsigned int
    1350
    1351                for (int i = 0; i < size; i++) {
    1352                    valueIn = Math.max(lIn[i], min);
    1353                    valueIn = Math.min(valueIn, max);
    1354                    valueOut = (valueIn + bias) * gain;
    1355                    valueOut = Math.max(valueOut, 0.0);
    1356                    valueOut = Math.min(valueOut, lMax);
    1357                    lOut[i] = (byte) valueOut;
    1358                }
    1359                break;
    1360            default:
    1361                break;
    1362        } // (dname)
    1363
    1364        return dataOUT;
    1365    }
    1366
    1367    /**
    1368     * Converts image raw data to bytes.
    1369     *
    1370     * The integer data is converted to byte data based on the following rule
    1371     *
    1372     * <pre>
    1373     *         uint_8       x
    1374     *         int_8       (x &amp; 0x7F) &lt;&lt; 1
    1375     *         uint_16     (x &gt;&gt; 8) &amp; 0xFF
    1376     *         int_16      (x &gt;&gt; 7) &amp; 0xFF
    1377     *         uint_32     (x &gt;&gt; 24) &amp; 0xFF
    1378     *         int_32      (x &gt;&gt; 23) &amp; 0xFF
    1379     *         uint_64     (x &gt;&gt; 56) &amp; 0xFF
    1380     *         int_64      (x &gt;&gt; 55) &amp; 0xFF
    1381     * </pre>
    1382     *
    1383     * @param src
    1384     *            the source data array of signed integers or unsigned shorts
    1385     * @param dst
    1386     *            the destination data array of bytes
    1387     * @param isUnsigned
    1388     *            the flag to indicate if the data array is unsigned integer.
    1389     *
    1390     * @return non-negative if successful; otherwise, returns negative
    1391     */
    1392    public static int autoContrastConvertImageBuffer(Object src, byte[] dst, boolean isUnsigned) {
    1393        int retval = 0;
    1394
    1395        if ((src == null) || (dst == null) || (dst.length != Array.getLength(src)))
    1396            return -1;
    1397
    1398        int size = dst.length;
    1399        String cname = src.getClass().getName();
    1400        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
    1401        switch (dname) {
    1402            case 'B':
    1403                byte[] bSrc = (byte[]) src;
    1404                if (isUnsigned) {
    1405                    for (int i = 0; i < size; i++)
    1406                        dst[i] = bSrc[i];
    1407                }
    1408                else {
    1409                    for (int i = 0; i < size; i++)
    1410                        dst[i] = (byte) ((bSrc[i] & 0x7F) << 1);
    1411                }
    1412                break;
    1413            case 'S':
    1414                short[] sSrc = (short[]) src;
    1415                if (isUnsigned) { // data was upgraded from unsigned byte
    1416                    for (int i = 0; i < size; i++)
    1417                        dst[i] = (byte) sSrc[i];
    1418                }
    1419                else {
    1420                    for (int i = 0; i < size; i++)
    1421                        dst[i] = (byte) ((sSrc[i] >> 7) & 0xFF);
    1422                }
    1423                break;
    1424            case 'I':
    1425                int[] iSrc = (int[]) src;
    1426                if (isUnsigned) { // data was upgraded from unsigned short
    1427                    for (int i = 0; i < size; i++)
    1428                        dst[i] = (byte) ((iSrc[i] >> 8) & 0xFF);
    1429                }
    1430                else {
    1431                    for (int i = 0; i < size; i++)
    1432                        dst[i] = (byte) ((iSrc[i] >> 23) & 0xFF);
    1433                }
    1434                break;
    1435            case 'J':
    1436                long[] lSrc = (long[]) src;
    1437                if (isUnsigned) { // data was upgraded from unsigned int
    1438                    for (int i = 0; i < size; i++)
    1439                        dst[i] = (byte) ((lSrc[i] >> 24) & 0xFF);
    1440                }
    1441                else {
    1442                    for (int i = 0; i < size; i++)
    1443                        dst[i] = (byte) ((lSrc[i] >> 55) & 0xFF);
    1444                }
    1445                break;
    1446            default:
    1447                retval = -1;
    1448                break;
    1449        } // (dname)
    1450
    1451        return retval;
    1452    }
    1453
    1454    /**
    1455     * Computes autocontrast parameters by
    1456     *
    1457     * <pre>
    1458     *    min = mean - 3 * std.dev
    1459     *    max = mean + 3 * std.dev
    1460     * </pre>
    1461     *
    1462     * @param data
    1463     *            the raw data array
    1464     * @param minmax
    1465     *            the min and max values.
    1466     *
    1467     * @return non-negative if successful; otherwise, returns negative
    1468     */
    1469    public static int autoContrastComputeMinMax(Object data, double[] minmax) {
    1470        int retval = 1;
    1471
    1472        if ((data == null) || (minmax == null) || (Array.getLength(data) <= 0) || (Array.getLength(minmax) < 2))
    1473            return -1;
    1474
    1475        double[] avgstd = { 0, 0 };
    1476        retval = computeStatistics(data, avgstd, null);
    1477        if (retval < 0)
    1478            return retval;
    1479
    1480        minmax[0] = avgstd[0] - 3.0 * avgstd[1];
    1481        minmax[1] = avgstd[0] + 3.0 * avgstd[1];
    1482
    1483        return retval;
    1484    }
    1485
    1486    /**
    1487     * Finds the min and max values of the data array
    1488     *
    1489     * @param data
    1490     *            the raw data array
    1491     * @param minmax
    1492     *            the mmin and max values of the array.
    1493     * @param fillValue
    1494     *            the missing value or fill value. Exclude this value when check
    1495     *            for min/max
    1496     *
    1497     * @return non-negative if successful; otherwise, returns negative
    1498     */
    1499    public static int findMinMax(Object data, double[] minmax, Object fillValue) {
    1500        int retval = 1;
    1501
    1502        if ((data == null) || (minmax == null) || (Array.getLength(data) <= 0) || (Array.getLength(minmax) < 2))
    1503            return -1;
    1504
    1505        int n = Array.getLength(data);
    1506        double fill = 0.0;
    1507        boolean hasFillValue = (fillValue != null && fillValue.getClass().isArray());
    1508
    1509        String cname = data.getClass().getName();
    1510        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
    1511        log.trace("findMinMax() cname={} : dname={}", cname, dname);
    1512
    1513        minmax[0] = Float.MAX_VALUE;
    1514        minmax[1] = -Float.MAX_VALUE;
    1515
    1516        switch (dname) {
    1517            case 'B':
    1518                byte[] b = (byte[]) data;
    1519                minmax[0] = minmax[1] = b[0];
    1520
    1521                if (hasFillValue)
    1522                    fill = ((byte[]) fillValue)[0];
    1523                for (int i = 0; i < n; i++) {
    1524                    if (hasFillValue && b[i] == fill)
    1525                        continue;
    1526                    if (minmax[0] > b[i])
    1527                        minmax[0] = b[i];
    1528                    if (minmax[1] < b[i])
    1529                        minmax[1] = b[i];
    1530                }
    1531                break;
    1532            case 'S':
    1533                short[] s = (short[]) data;
    1534                minmax[0] = minmax[1] = s[0];
    1535
    1536                if (hasFillValue)
    1537                    fill = ((short[]) fillValue)[0];
    1538                for (int i = 0; i < n; i++) {
    1539                    if (hasFillValue && s[i] == fill)
    1540                        continue;
    1541                    if (minmax[0] > s[i])
    1542                        minmax[0] = s[i];
    1543                    if (minmax[1] < s[i])
    1544                        minmax[1] = s[i];
    1545                }
    1546                break;
    1547            case 'I':
    1548                int[] ia = (int[]) data;
    1549                minmax[0] = minmax[1] = ia[0];
    1550
    1551                if (hasFillValue)
    1552                    fill = ((int[]) fillValue)[0];
    1553                for (int i = 0; i < n; i++) {
    1554                    if (hasFillValue && ia[i] == fill)
    1555                        continue;
    1556                    if (minmax[0] > ia[i])
    1557                        minmax[0] = ia[i];
    1558                    if (minmax[1] < ia[i])
    1559                        minmax[1] = ia[i];
    1560                }
    1561                break;
    1562            case 'J':
    1563                long[] l = (long[]) data;
    1564                minmax[0] = minmax[1] = l[0];
    1565
    1566                if (hasFillValue)
    1567                    fill = ((long[]) fillValue)[0];
    1568                for (int i = 0; i < n; i++) {
    1569                    if (hasFillValue && l[i] == fill)
    1570                        continue;
    1571                    if (minmax[0] > l[i])
    1572                        minmax[0] = l[i];
    1573                    if (minmax[1] < l[i])
    1574                        minmax[1] = l[i];
    1575                }
    1576                break;
    1577            case 'F':
    1578                float[] f = (float[]) data;
    1579                minmax[0] = minmax[1] = f[0];
    1580
    1581                if (hasFillValue)
    1582                    fill = ((float[]) fillValue)[0];
    1583                for (int i = 0; i < n; i++) {
    1584                    if ((hasFillValue && f[i] == fill) || isNaNINF(f[i]))
    1585                        continue;
    1586                    if (minmax[0] > f[i])
    1587                        minmax[0] = f[i];
    1588                    if (minmax[1] < f[i])
    1589                        minmax[1] = f[i];
    1590                }
    1591
    1592                break;
    1593            case 'D':
    1594                double[] d = (double[]) data;
    1595                minmax[0] = minmax[1] = d[0];
    1596
    1597                if (hasFillValue)
    1598                    fill = ((double[]) fillValue)[0];
    1599                for (int i = 0; i < n; i++) {
    1600                    if ((hasFillValue && d[i] == fill) || isNaNINF(d[i]))
    1601                        continue;
    1602
    1603                    if (minmax[0] > d[i])
    1604                        minmax[0] = d[i];
    1605                    if (minmax[1] < d[i])
    1606                        minmax[1] = d[i];
    1607                }
    1608                break;
    1609            default:
    1610                retval = -1;
    1611                break;
    1612        } // (dname)
    1613
    1614        return retval;
    1615    }
    1616
    1617    /**
    1618     * Finds the distribution of data values
    1619     *
    1620     * @param data
    1621     *            the raw data array
    1622     * @param dataDist
    1623     *            the data distirbution.
    1624     * @param minmax
    1625     *            the data range
    1626     *
    1627     * @return non-negative if successful; otherwise, returns negative
    1628     */
    1629    public static int findDataDist(Object data, int[] dataDist, double[] minmax) {
    1630        int retval = 0;
    1631        double delt = 1;
    1632
    1633        if ((data == null) || (minmax == null) || dataDist == null)
    1634            return -1;
    1635
    1636        int n = Array.getLength(data);
    1637
    1638        if (minmax[1] != minmax[0])
    1639            delt = (dataDist.length - 1) / (minmax[1] - minmax[0]);
    1640
    1641        for (int i = 0; i < dataDist.length; i++)
    1642            dataDist[i] = 0;
    1643
    1644        int idx;
    1645        double val;
    1646        for (int i = 0; i < n; i++) {
    1647            val = ((Number) Array.get(data, i)).doubleValue();
    1648            if (val>=minmax[0] && val <=minmax[1]) {
    1649                idx = (int) ((val - minmax[0]) * delt);
    1650                dataDist[idx]++;
    1651            } // don't count invalid values
    1652        }
    1653
    1654        return retval;
    1655    }
    1656
    1657    /**
    1658     * Computes mean and standard deviation of a data array
    1659     *
    1660     * @param data
    1661     *            the raw data array
    1662     * @param avgstd
    1663     *            the statistics: avgstd[0]=mean and avgstd[1]=stdev.
    1664     * @param fillValue
    1665     *            the missing value or fill value. Exclude this value when
    1666     *            compute statistics
    1667     *
    1668     * @return non-negative if successful; otherwise, returns negative
    1669     */
    1670    public static int computeStatistics(Object data, double[] avgstd, Object fillValue) {
    1671        int retval = 1;
    1672        double sum = 0;
    1673        double avg = 0.0;
    1674        double var = 0.0;
    1675        double diff = 0.0;
    1676        double fill = 0.0;
    1677
    1678        if ((data == null) || (avgstd == null) || (Array.getLength(data) <= 0) || (Array.getLength(avgstd) < 2)) {
    1679            return -1;
    1680        }
    1681
    1682        int n = Array.getLength(data);
    1683        boolean hasFillValue = (fillValue != null && fillValue.getClass().isArray());
    1684
    1685        String cname = data.getClass().getName();
    1686        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
    1687        log.trace("computeStatistics() cname={} : dname={}", cname, dname);
    1688
    1689        int npoints = 0;
    1690        switch (dname) {
    1691            case 'B':
    1692                byte[] b = (byte[]) data;
    1693                if (hasFillValue) fill = ((byte[]) fillValue)[0];
    1694                for (int i = 0; i < n; i++) {
    1695                    if (hasFillValue && b[i] == fill) continue;
    1696                    sum += b[i];
    1697                    npoints++;
    1698                }
    1699                if (npoints > 0) {
    1700                    avg = sum / npoints;
    1701                    for (int i = 0; i < n; i++) {
    1702                        if (hasFillValue && b[i] == fill)
    1703                            continue;
    1704                        diff = b[i] - avg;
    1705                        var += diff * diff;
    1706                    }
    1707                }
    1708                break;
    1709            case 'S':
    1710                short[] s = (short[]) data;
    1711                if (hasFillValue) fill = ((short[]) fillValue)[0];
    1712                for (int i = 0; i < n; i++) {
    1713                    if (hasFillValue && s[i] == fill) continue;
    1714                    sum += s[i];
    1715                    npoints++;
    1716                }
    1717                if (npoints > 0) {
    1718                    avg = sum / npoints;
    1719                    for (int i = 0; i < n; i++) {
    1720                        if (hasFillValue && s[i] == fill)
    1721                            continue;
    1722                        diff = s[i] - avg;
    1723                        var += diff * diff;
    1724                    }
    1725                }
    1726                break;
    1727            case 'I':
    1728                int[] ia = (int[]) data;
    1729                if (hasFillValue) fill = ((int[]) fillValue)[0];
    1730                for (int i = 0; i < n; i++) {
    1731                    if (hasFillValue && ia[i] == fill) continue;
    1732                    sum += ia[i];
    1733                    npoints++;
    1734                }
    1735                if (npoints > 0) {
    1736                    avg = sum / npoints;
    1737                    for (int i = 0; i < n; i++) {
    1738                        if (hasFillValue && ia[i] == fill)
    1739                            continue;
    1740                        diff = ia[i] - avg;
    1741                        var += diff * diff;
    1742                    }
    1743                }
    1744                break;
    1745            case 'J':
    1746                long[] l = (long[]) data;
    1747                if (hasFillValue) fill = ((long[]) fillValue)[0];
    1748                for (int i = 0; i < n; i++) {
    1749                    if (hasFillValue && l[i] == fill) continue;
    1750                    sum += l[i];
    1751                    npoints++;
    1752                }
    1753                if (npoints > 0) {
    1754                    avg = sum / npoints;
    1755                    for (int i = 0; i < n; i++) {
    1756                        if (hasFillValue && l[i] == fill)
    1757                            continue;
    1758                        diff = l[i] - avg;
    1759                        var += diff * diff;
    1760                    }
    1761                }
    1762                break;
    1763            case 'F':
    1764                float[] f = (float[]) data;
    1765                if (hasFillValue) fill = ((float[]) fillValue)[0];
    1766                for (int i = 0; i < n; i++) {
    1767                    if (hasFillValue && f[i] == fill) continue;
    1768                    sum += f[i];
    1769                    npoints++;
    1770                }
    1771                if (npoints > 0) {
    1772                    avg = sum / npoints;
    1773                    for (int i = 0; i < n; i++) {
    1774                        if (hasFillValue && f[i] == fill)
    1775                            continue;
    1776                        diff = f[i] - avg;
    1777                        var += diff * diff;
    1778                    }
    1779                }
    1780                break;
    1781            case 'D':
    1782                double[] d = (double[]) data;
    1783                if (hasFillValue) fill = ((double[]) fillValue)[0];
    1784                for (int i = 0; i < n; i++) {
    1785                    if (hasFillValue && d[i] == fill) continue;
    1786                    sum += d[i];
    1787                    npoints++;
    1788                }
    1789                if (npoints > 0) {
    1790                    avg = sum / npoints;
    1791                    for (int i = 0; i < n; i++) {
    1792                        if (hasFillValue && d[i] == fill)
    1793                            continue;
    1794                        diff = d[i] - avg;
    1795                        var += diff * diff;
    1796                    }
    1797                }
    1798                break;
    1799            default:
    1800                retval = -1;
    1801                break;
    1802        } // (dname)
    1803
    1804        if (npoints <= 1) {
    1805            if (npoints < 1)
    1806                avgstd[0] = fill;
    1807            avgstd[1] = 0;
    1808        }
    1809        else {
    1810            avgstd[0] = avg;
    1811            avgstd[1] = Math.sqrt(var / (npoints - 1));
    1812        }
    1813
    1814        return retval;
    1815    }
    1816
    1817    /**
    1818     * Save the data as binary
    1819     *
    1820     * @param out
    1821     *            the output stream
    1822     * @param data
    1823     *            the raw data array
    1824     * @param order
    1825     *            the order of bytes
    1826     *
    1827     * @throws Exception if a failure occurred
    1828     */
    1829    public static void saveAsBinary(DataOutputStream out, Object data, ByteOrder order) throws Exception {
    1830        String cname = data.getClass().getName();
    1831        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
    1832        ByteBuffer bb = null;
    1833
    1834        int size = Array.getLength(data);
    1835
    1836        if (dname == 'B') {
    1837            byte[] bdata = (byte[]) data;
    1838
    1839            bb = ByteBuffer.allocate(BYTE_BUFFER_SIZE);
    1840            bb.order(order);
    1841
    1842            int remainingSize = size - BYTE_BUFFER_SIZE;
    1843            int allocValue = 0;
    1844            int iterationNumber = 0;
    1845            do {
    1846                if (remainingSize <= 0)
    1847                    allocValue = remainingSize + BYTE_BUFFER_SIZE;
    1848                else
    1849                    allocValue = BYTE_BUFFER_SIZE;
    1850                bb.clear();
    1851                bb.put(bdata, (iterationNumber * BYTE_BUFFER_SIZE), allocValue);
    1852                out.write(bb.array(), 0, allocValue);
    1853                remainingSize = remainingSize - BYTE_BUFFER_SIZE;
    1854                iterationNumber++;
    1855            } while (remainingSize > -BYTE_BUFFER_SIZE);
    1856
    1857            out.flush();
    1858            out.close();
    1859        }
    1860        else if (dname == 'S') {
    1861            short[] sdata = (short[]) data;
    1862            bb = ByteBuffer.allocate(SHORT_BUFFER_SIZE * 2);
    1863            bb.order(order);
    1864
    1865            ShortBuffer sb = bb.asShortBuffer();
    1866            int remainingSize = size - SHORT_BUFFER_SIZE;
    1867            int allocValue = 0;
    1868            int iterationNumber = 0;
    1869            do {
    1870                if (remainingSize <= 0)
    1871                    allocValue = remainingSize + SHORT_BUFFER_SIZE;
    1872                else
    1873                    allocValue = SHORT_BUFFER_SIZE;
    1874                bb.clear();
    1875                sb.clear();
    1876                sb.put(sdata, (iterationNumber * SHORT_BUFFER_SIZE), allocValue);
    1877                out.write(bb.array(), 0, allocValue * 2);
    1878                remainingSize = remainingSize - SHORT_BUFFER_SIZE;
    1879                iterationNumber++;
    1880            } while (remainingSize > -SHORT_BUFFER_SIZE);
    1881
    1882            out.flush();
    1883            out.close();
    1884        }
    1885        else if (dname == 'I') {
    1886            int[] idata = (int[]) data;
    1887            bb = ByteBuffer.allocate(INT_BUFFER_SIZE * 4);
    1888            bb.order(order);
    1889
    1890            IntBuffer ib = bb.asIntBuffer();
    1891            int remainingSize = size - INT_BUFFER_SIZE;
    1892            int allocValue = 0;
    1893            int iterationNumber = 0;
    1894            do {
    1895                if (remainingSize <= 0)
    1896                    allocValue = remainingSize + INT_BUFFER_SIZE;
    1897                else
    1898                    allocValue = INT_BUFFER_SIZE;
    1899                bb.clear();
    1900                ib.clear();
    1901                ib.put(idata, (iterationNumber * INT_BUFFER_SIZE), allocValue);
    1902                out.write(bb.array(), 0, allocValue * 4);
    1903                remainingSize = remainingSize - INT_BUFFER_SIZE;
    1904                iterationNumber++;
    1905            } while (remainingSize > -INT_BUFFER_SIZE);
    1906
    1907            out.flush();
    1908            out.close();
    1909        }
    1910        else if (dname == 'J') {
    1911            long[] ldata = (long[]) data;
    1912
    1913            bb = ByteBuffer.allocate(LONG_BUFFER_SIZE * 8);
    1914            bb.order(order);
    1915
    1916            LongBuffer lb = bb.asLongBuffer();
    1917            int remainingSize = size - LONG_BUFFER_SIZE;
    1918            int allocValue = 0;
    1919            int iterationNumber = 0;
    1920            do {
    1921                if (remainingSize <= 0)
    1922                    allocValue = remainingSize + LONG_BUFFER_SIZE;
    1923                else
    1924                    allocValue = LONG_BUFFER_SIZE;
    1925                bb.clear();
    1926                lb.clear();
    1927                lb.put(ldata, (iterationNumber * LONG_BUFFER_SIZE), allocValue);
    1928                out.write(bb.array(), 0, allocValue * 8);
    1929                remainingSize = remainingSize - LONG_BUFFER_SIZE;
    1930                iterationNumber++;
    1931            } while (remainingSize > -LONG_BUFFER_SIZE);
    1932
    1933            out.flush();
    1934            out.close();
    1935        }
    1936        else if (dname == 'F') {
    1937            float[] fdata = (float[]) data;
    1938
    1939            bb = ByteBuffer.allocate(FLOAT_BUFFER_SIZE * 4);
    1940            bb.order(order);
    1941
    1942            FloatBuffer fb = bb.asFloatBuffer();
    1943            int remainingSize = size - FLOAT_BUFFER_SIZE;
    1944            int allocValue = 0;
    1945            int iterationNumber = 0;
    1946            do {
    1947                if (remainingSize <= 0)
    1948                    allocValue = remainingSize + FLOAT_BUFFER_SIZE;
    1949                else
    1950                    allocValue = FLOAT_BUFFER_SIZE;
    1951                bb.clear();
    1952                fb.clear();
    1953                fb.put(fdata, (iterationNumber * FLOAT_BUFFER_SIZE), allocValue);
    1954                out.write(bb.array(), 0, allocValue * 4);
    1955                remainingSize = remainingSize - FLOAT_BUFFER_SIZE;
    1956                iterationNumber++;
    1957            } while (remainingSize > -FLOAT_BUFFER_SIZE);
    1958
    1959            out.flush();
    1960            out.close();
    1961        }
    1962        else if (dname == 'D') {
    1963            double[] ddata = (double[]) data;
    1964
    1965            bb = ByteBuffer.allocate(DOUBLE_BUFFER_SIZE * 8);
    1966            bb.order(order);
    1967
    1968            DoubleBuffer db = bb.asDoubleBuffer();
    1969            int remainingSize = size - DOUBLE_BUFFER_SIZE;
    1970            int allocValue = 0;
    1971            int iterationNumber = 0;
    1972            do {
    1973                if (remainingSize <= 0)
    1974                    allocValue = remainingSize + DOUBLE_BUFFER_SIZE;
    1975                else
    1976                    allocValue = DOUBLE_BUFFER_SIZE;
    1977                bb.clear();
    1978                db.clear();
    1979                db.put(ddata, (iterationNumber * DOUBLE_BUFFER_SIZE), allocValue);
    1980                out.write(bb.array(), 0, allocValue * 8);
    1981                remainingSize = remainingSize - DOUBLE_BUFFER_SIZE;
    1982                iterationNumber++;
    1983            } while (remainingSize > -DOUBLE_BUFFER_SIZE);
    1984
    1985            out.flush();
    1986            out.close();
    1987        }
    1988    }
    1989
    1990    /**
    1991     * Reads data from a binary file into a buffer.
    1992     *
    1993     * @param dataOut
    1994     *            the output stream
    1995     * @param fileName
    1996     *            the file to read binary data from
    1997     * @param order
    1998     *            the new byte order, either BIG_ENDIAN or LITTLE_ENDIAN
    1999     *
    2000     * @return true if successful; otherwise, false.
    2001     */
    2002    public static boolean getBinaryDataFromFile(Object dataOut, String fileName, ByteOrder order) {
    2003        if (dataOut == null)
    2004            return false;
    2005
    2006        String fname = fileName;
    2007        BufferedInputStream in = null;
    2008        ByteBuffer byteBuffer = null;
    2009        boolean valChanged = false;
    2010
    2011        try (FileInputStream inputFile = new FileInputStream(fname)) {
    2012            long fileSize = inputFile.getChannel().size();
    2013            in = new BufferedInputStream(inputFile);
    2014
    2015            int datasetSize = Array.getLength(dataOut);
    2016            String cname = dataOut.getClass().getName();
    2017            char dname = cname.charAt(cname.lastIndexOf('[') + 1);
    2018
    2019            if (dname == 'B') {
    2020                long datasetByteSize = datasetSize;
    2021                byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_SIZE);
    2022                byteBuffer.order(order);
    2023
    2024                int bufferSize = (int) Math.min(fileSize, datasetByteSize);
    2025
    2026                int remainingSize = bufferSize - (BYTE_BUFFER_SIZE);
    2027                int allocValue = 0;
    2028                int iterationNumber = 0;
    2029                byte[] byteArray = new byte[BYTE_BUFFER_SIZE];
    2030                do {
    2031                    if (remainingSize <= 0)
    2032                        allocValue = remainingSize + (BYTE_BUFFER_SIZE);
    2033                    else
    2034                        allocValue = (BYTE_BUFFER_SIZE);
    2035
    2036                    in.read(byteBuffer.array(), 0, allocValue);
    2037
    2038                    byteBuffer.get(byteArray, 0, allocValue);
    2039                    System.arraycopy(byteArray, 0, dataOut, (iterationNumber * BYTE_BUFFER_SIZE), allocValue);
    2040                    byteBuffer.clear();
    2041                    remainingSize = remainingSize - (BYTE_BUFFER_SIZE);
    2042                    iterationNumber++;
    2043                } while (remainingSize > -(BYTE_BUFFER_SIZE));
    2044
    2045                valChanged = true;
    2046            }
    2047            else if (dname == 'S') {
    2048                long datasetShortSize = (long) datasetSize * 2;
    2049                byteBuffer = ByteBuffer.allocate(SHORT_BUFFER_SIZE * 2);
    2050                byteBuffer.order(order);
    2051
    2052                int bufferSize = (int) Math.min(fileSize, datasetShortSize);
    2053                int remainingSize = bufferSize - (SHORT_BUFFER_SIZE * 2);
    2054                int allocValue = 0;
    2055                int iterationNumber = 0;
    2056                ShortBuffer sb = byteBuffer.asShortBuffer();
    2057                short[] shortArray = new short[SHORT_BUFFER_SIZE];
    2058
    2059                do {
    2060                    if (remainingSize <= 0)
    2061                        allocValue = remainingSize + (SHORT_BUFFER_SIZE * 2);
    2062                    else
    2063                        allocValue = (SHORT_BUFFER_SIZE * 2);
    2064                    in.read(byteBuffer.array(), 0, allocValue);
    2065                    sb.get(shortArray, 0, allocValue / 2);
    2066                    System.arraycopy(shortArray, 0, dataOut, (iterationNumber * SHORT_BUFFER_SIZE), allocValue / 2);
    2067                    byteBuffer.clear();
    2068                    sb.clear();
    2069                    remainingSize = remainingSize - (SHORT_BUFFER_SIZE * 2);
    2070                    iterationNumber++;
    2071                } while (remainingSize > -(SHORT_BUFFER_SIZE * 2));
    2072
    2073                valChanged = true;
    2074            }
    2075            else if (dname == 'I') {
    2076                long datasetIntSize = (long) datasetSize * 4;
    2077                byteBuffer = ByteBuffer.allocate(INT_BUFFER_SIZE * 4);
    2078                byteBuffer.order(order);
    2079
    2080                int bufferSize = (int) Math.min(fileSize, datasetIntSize);
    2081                int remainingSize = bufferSize - (INT_BUFFER_SIZE * 4);
    2082                int allocValue = 0;
    2083                int iterationNumber = 0;
    2084                int[] intArray = new int[INT_BUFFER_SIZE];
    2085                byte[] tmpBuf = byteBuffer.array();
    2086                IntBuffer ib = byteBuffer.asIntBuffer();
    2087
    2088                do {
    2089                    if (remainingSize <= 0)
    2090                        allocValue = remainingSize + (INT_BUFFER_SIZE * 4);
    2091                    else
    2092                        allocValue = (INT_BUFFER_SIZE * 4);
    2093                    in.read(tmpBuf, 0, allocValue);
    2094                    ib.get(intArray, 0, allocValue / 4);
    2095                    System.arraycopy(intArray, 0, dataOut, (iterationNumber * INT_BUFFER_SIZE), allocValue / 4);
    2096                    byteBuffer.clear();
    2097                    ib.clear();
    2098                    remainingSize = remainingSize - (INT_BUFFER_SIZE * 4);
    2099                    iterationNumber++;
    2100                } while (remainingSize > -(INT_BUFFER_SIZE * 4));
    2101
    2102                valChanged = true;
    2103            }
    2104            else if (dname == 'J') {
    2105                long datasetLongSize = (long) datasetSize * 8;
    2106                byteBuffer = ByteBuffer.allocate(LONG_BUFFER_SIZE * 8);
    2107                byteBuffer.order(order);
    2108
    2109                int bufferSize = (int) Math.min(fileSize, datasetLongSize);
    2110                int remainingSize = bufferSize - (LONG_BUFFER_SIZE * 8);
    2111                int allocValue = 0;
    2112                int iterationNumber = 0;
    2113                long[] longArray = new long[LONG_BUFFER_SIZE];
    2114                LongBuffer lb = byteBuffer.asLongBuffer();
    2115
    2116                do {
    2117                    if (remainingSize <= 0)
    2118                        allocValue = remainingSize + (LONG_BUFFER_SIZE * 8);
    2119                    else
    2120                        allocValue = (LONG_BUFFER_SIZE * 8);
    2121
    2122                    in.read(byteBuffer.array(), 0, allocValue);
    2123                    lb.get(longArray, 0, allocValue / 8);
    2124                    System.arraycopy(longArray, 0, dataOut, (iterationNumber * LONG_BUFFER_SIZE), allocValue / 8);
    2125                    byteBuffer.clear();
    2126                    lb.clear();
    2127                    remainingSize = remainingSize - (LONG_BUFFER_SIZE * 8);
    2128                    iterationNumber++;
    2129                } while (remainingSize > -(LONG_BUFFER_SIZE * 8));
    2130
    2131                valChanged = true;
    2132            }
    2133            else if (dname == 'F') {
    2134                long datasetFloatSize = (long) datasetSize * 4;
    2135                byteBuffer = ByteBuffer.allocate(FLOAT_BUFFER_SIZE * 4);
    2136                byteBuffer.order(order);
    2137
    2138                int bufferSize = (int) Math.min(fileSize, datasetFloatSize);
    2139                int remainingSize = bufferSize - (FLOAT_BUFFER_SIZE * 4);
    2140                int allocValue = 0;
    2141                int iterationNumber = 0;
    2142                FloatBuffer fb = byteBuffer.asFloatBuffer();
    2143                float[] floatArray = new float[FLOAT_BUFFER_SIZE];
    2144                do {
    2145                    if (remainingSize <= 0)
    2146                        allocValue = remainingSize + (FLOAT_BUFFER_SIZE * 4);
    2147                    else
    2148                        allocValue = (FLOAT_BUFFER_SIZE * 4);
    2149
    2150                    in.read(byteBuffer.array(), 0, allocValue);
    2151                    fb.get(floatArray, 0, allocValue / 4);
    2152                    System.arraycopy(floatArray, 0, dataOut, (iterationNumber * FLOAT_BUFFER_SIZE), allocValue / 4);
    2153                    byteBuffer.clear();
    2154                    fb.clear();
    2155                    remainingSize = remainingSize - (FLOAT_BUFFER_SIZE * 4);
    2156                    iterationNumber++;
    2157                } while (remainingSize > -(FLOAT_BUFFER_SIZE * 4));
    2158
    2159                valChanged = true;
    2160            }
    2161            else if (dname == 'D') {
    2162                long datasetDoubleSize = (long) datasetSize * 8;
    2163                byteBuffer = ByteBuffer.allocate(DOUBLE_BUFFER_SIZE * 8);
    2164                byteBuffer.order(order);
    2165
    2166                int bufferSize = (int) Math.min(fileSize, datasetDoubleSize);
    2167                int remainingSize = bufferSize - (DOUBLE_BUFFER_SIZE * 8);
    2168                int allocValue = 0;
    2169                int iterationNumber = 0;
    2170                DoubleBuffer db = byteBuffer.asDoubleBuffer();
    2171                double[] doubleArray = new double[DOUBLE_BUFFER_SIZE];
    2172
    2173                do {
    2174                    if (remainingSize <= 0)
    2175                        allocValue = remainingSize + (DOUBLE_BUFFER_SIZE * 8);
    2176                    else
    2177                        allocValue = (DOUBLE_BUFFER_SIZE * 8);
    2178
    2179                    in.read(byteBuffer.array(), 0, allocValue);
    2180                    db.get(doubleArray, 0, allocValue / 8);
    2181                    System.arraycopy(doubleArray, 0, dataOut, (iterationNumber * DOUBLE_BUFFER_SIZE), allocValue / 8);
    2182                    byteBuffer.clear();
    2183                    db.clear();
    2184                    remainingSize = remainingSize - (DOUBLE_BUFFER_SIZE * 8);
    2185                    iterationNumber++;
    2186                } while (remainingSize > -(DOUBLE_BUFFER_SIZE * 8));
    2187
    2188                valChanged = true;
    2189            }
    2190        }
    2191        catch (Exception es) {
    2192            es.printStackTrace();
    2193        }
    2194        finally {
    2195            try {
    2196                in.close();
    2197            }
    2198            catch (IOException ex) {
    2199                // Empty on purpose
    2200            }
    2201        }
    2202
    2203        return valChanged;
    2204    }
    2205
    2206    /**
    2207     * Returns a string representation of the long argument as an unsigned
    2208     * integer in base 2. This is different from Long.toBinaryString(long i).
    2209     * This function add padding (0's) to the string based on the nbytes. For
    2210     * example, if v=15, nbytes=1, the string will be "00001111".
    2211     *
    2212     * @param v
    2213     *            the long value
    2214     * @param nbytes
    2215     *            number of bytes in the integer
    2216     *
    2217     * @return the string representation of the unsigned long value represented
    2218     *         by the argument in binary (base 2).
    2219     */
    2220    public static final String toBinaryString(long v, int nbytes) {
    2221        if (nbytes <= 0)
    2222            return null;
    2223
    2224        int nhex = nbytes * 2;
    2225        short[] hex = new short[nhex];
    2226
    2227        for (int i = 0; i < nhex; i++)
    2228            hex[i] = (short) (0x0F & (v >> (i * 4)));
    2229
    2230        StringBuilder sb = new StringBuilder();
    2231        boolean isEven = true;
    2232        for (int i = nhex - 1; i >= 0; i--) {
    2233            if (isEven && i < nhex - 1)
    2234                sb.append(" ");
    2235            isEven = !isEven; // toggle
    2236
    2237            switch (hex[i]) {
    2238                case 0:
    2239                    sb.append("0000");
    2240                    break;
    2241                case 1:
    2242                    sb.append("0001");
    2243                    break;
    2244                case 2:
    2245                    sb.append("0010");
    2246                    break;
    2247                case 3:
    2248                    sb.append("0011");
    2249                    break;
    2250                case 4:
    2251                    sb.append("0100");
    2252                    break;
    2253                case 5:
    2254                    sb.append("0101");
    2255                    break;
    2256                case 6:
    2257                    sb.append("0110");
    2258                    break;
    2259                case 7:
    2260                    sb.append("0111");
    2261                    break;
    2262                case 8:
    2263                    sb.append("1000");
    2264                    break;
    2265                case 9:
    2266                    sb.append("1001");
    2267                    break;
    2268                case 10:
    2269                    sb.append("1010");
    2270                    break;
    2271                case 11:
    2272                    sb.append("1011");
    2273                    break;
    2274                case 12:
    2275                    sb.append("1100");
    2276                    break;
    2277                case 13:
    2278                    sb.append("1101");
    2279                    break;
    2280                case 14:
    2281                    sb.append("1110");
    2282                    break;
    2283                case 15:
    2284                    sb.append("1111");
    2285                    break;
    2286                default:
    2287                    break;
    2288            }
    2289        }
    2290
    2291        return sb.toString();
    2292    }
    2293
    2294    /**
    2295     * Returns a string representation of the BigDecimal argument as an unsigned
    2296     * integer in base 2. This is different from BigDecimal.toBinaryString(long i).
    2297     * This function add padding (0's) to the string based on the nbytes. For
    2298     * example, if v=15, nbytes=1, the string will be "00001111".
    2299     *
    2300     * @param v
    2301     *            the BigDecimal value
    2302     * @param nbytes
    2303     *            number of bytes in the integer
    2304     *
    2305     * @return the string representation of the BigDecimal value represented
    2306     *         by the argument in binary (base 2).
    2307     */
    2308    public static final String toBinaryString(BigDecimal v, int nbytes) {
    2309        StringBuilder sb = new StringBuilder();
    2310        /*
    2311         * String val = String.format("%" + (8 * nbytes) + "s", v.toString(2)).replace(" ", "0").toUpperCase(); //
    2312         * Insert spacing for (int i = 0; i < nbytes; i++) { sb.append(val.substring(i * nbytes, nbytes * (i + 1))); if
    2313         * (i < nbytes - 1) sb.append(" "); }
    2314         */
    2315        return sb.toString();
    2316    }
    2317
    2318    /**
    2319     * Returns a string representation of the BigInteger argument as an unsigned
    2320     * integer in base 2. This is different from BigInteger.toBinaryString(long i).
    2321     * This function add padding (0's) to the string based on the nbytes. For
    2322     * example, if v=15, nbytes=1, the string will be "00001111".
    2323     *
    2324     * @param v
    2325     *            the BigInteger value
    2326     * @param nbytes
    2327     *            number of bytes in the integer
    2328     *
    2329     * @return the string representation of the BigInteger value represented
    2330     *         by the argument in binary (base 2).
    2331     */
    2332    public static final String toBinaryString(BigInteger v, int nbytes) {
    2333        StringBuilder sb = new StringBuilder();
    2334        String val = String.format("%" + (8 * nbytes) + "s", v.toString(2)).replace(" ", "0").toUpperCase();
    2335
    2336        // Insert spacing
    2337        for (int i = 0; i < nbytes; i++) {
    2338            sb.append(val.substring(i * nbytes, nbytes * (i + 1)));
    2339            if (i < nbytes - 1)
    2340                sb.append(" ");
    2341        }
    2342
    2343        return sb.toString();
    2344    }
    2345
    2346    /**
    2347     * Returns a string representation of the long argument as an unsigned integer in base 16. This
    2348     * is different from Long.toHexString(long i). This function add padding (0's) to the string
    2349     * based on the nbytes. For example, if v=42543, nbytes=4, the string will be "0000A62F".
    2350     *
    2351     * @param v
    2352     *            the long value
    2353     * @param nbytes
    2354     *            number of bytes in the integer
    2355     * @return the string representation of the unsigned long value represented by the argument in
    2356     *         hexadecimal (base 16).
    2357     */
    2358    public static final String toHexString (long v, int nbytes) {
    2359        char[] HEXCHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    2360
    2361        if (nbytes <= 0)
    2362            return null;
    2363
    2364        int nhex = nbytes * 2;
    2365        short[] hex = new short[nhex];
    2366
    2367        for (int i = 0; i < nhex; i++)
    2368            hex[i] = (short) (0x0F & (v >> (i * 4)));
    2369
    2370        StringBuilder sb = new StringBuilder();
    2371        for (int i = nhex - 1; i >= 0; i--)
    2372            sb.append(HEXCHARS[hex[i]]);
    2373
    2374        return sb.toString();
    2375    }
    2376
    2377    /**
    2378     * Returns a string representation of the BigInteger argument as an unsigned integer in base 16.
    2379     * This is different from BigInteger.toString(16). This function adds padding (0's) to the string
    2380     * based on the nbytes. For example, if v=42543, nbytes=4, the string will be "0000A62F".
    2381     *
    2382     * @param v
    2383     *            the BigInteger value
    2384     * @param nbytes
    2385     *            number of bytes in the integer
    2386     * @return the string representation of the BigInteger value represented by the argument in
    2387     *         hexadecimal (base 16).
    2388     */
    2389    public static final String toHexString (BigInteger v, int nbytes) {
    2390        return String.format("%" + (2 * nbytes) + "s", v.toString(16)).replace(" ", "0").toUpperCase();
    2391    }
    2392
    2393    /**
    2394     * Returns a string representation of the BigDecimal argument as an unsigned integer in base 16.
    2395     * This is different from BigDecimal.toString(16). This function adds padding (0's) to the string
    2396     * based on the nbytes. For example, if v=42543, nbytes=4, the string will be "0000A62F".
    2397     *
    2398     * @param v
    2399     *            the BigDecimal value
    2400     * @param nbytes
    2401     *            number of bytes in the integer
    2402     * @return the string representation of the BigDecimal value represented by the argument in
    2403     *         hexadecimal (base 16).
    2404     */
    2405    public static final String toHexString (BigDecimal v, int nbytes) {
    2406        return null; // String.format("%" + (2 * nbytes) + "s", v.toString(16)).replace(" ", "0").toUpperCase();
    2407    }
    2408
    2409    /**
    2410     * Apply bitmask to a data array.
    2411     *
    2412     * @param theData
    2413     *            the data array which the bitmask is applied to.
    2414     * @param theMask
    2415     *            the bitmask to be applied to the data array.
    2416     * @param op
    2417     *            the bitmask op to be applied
    2418     *
    2419     * @return true if bitmask is applied successfully; otherwise, false.
    2420     */
    2421    public static final boolean applyBitmask(Object theData, BitSet theMask, ViewProperties.BITMASK_OP op) {
    2422        if (theData == null
    2423            || !(theData instanceof Array)
    2424            || ((theData instanceof Array) && (Array.getLength(theData) <= 0))
    2425            || theMask == null) return false;
    2426
    2427        char nt = '0';
    2428        String cName = theData.getClass().getName();
    2429        int cIndex = cName.lastIndexOf('[');
    2430        if (cIndex >= 0)
    2431            nt = cName.charAt(cIndex + 1);
    2432
    2433        // only deal with 8/16/32/64 bit datasets
    2434        if (!(nt == 'B' || nt == 'S' || nt == 'I' || nt == 'J'))
    2435            return false;
    2436
    2437        long bmask = 0;
    2438        long theValue = 0;
    2439        long packedValue = 0;
    2440
    2441        int nbits = theMask.length();
    2442        int len = Array.getLength(theData);
    2443
    2444        for (int i = 0; i < nbits; i++)
    2445            if (theMask.get(i)) bmask += 1 << i;
    2446
    2447        for (int i = 0; i < len; i++) {
    2448            if (nt == 'B')
    2449                theValue = ((byte[]) theData)[i] & bmask;
    2450            else if (nt == 'S')
    2451                theValue = ((short[]) theData)[i] & bmask;
    2452            else if (nt == 'I')
    2453                theValue = ((int[]) theData)[i] & bmask;
    2454            else if (nt == 'J')
    2455                theValue = ((long[]) theData)[i] & bmask;
    2456
    2457            // apply bitmask only
    2458            if (op == BITMASK_OP.AND)
    2459                packedValue = theValue;
    2460            else {
    2461                // extract bits
    2462                packedValue = 0;
    2463                int bitPosition = 0;
    2464
    2465                for (int j = 0; j < nbits; j++) {
    2466                    if (theMask.get(j)) {
    2467                        long bitValue = (theValue & 1);
    2468                        packedValue += (bitValue << bitPosition);
    2469                        bitPosition++;
    2470                    }
    2471                    // move to the next bit
    2472                    theValue = theValue >> 1;
    2473                }
    2474            }
    2475
    2476            if (nt == 'B')
    2477                ((byte[]) theData)[i] = (byte) packedValue;
    2478            else if (nt == 'S')
    2479                ((short[]) theData)[i] = (short) packedValue;
    2480            else if (nt == 'I')
    2481                ((int[]) theData)[i] = (int) packedValue;
    2482            else if (nt == 'J')
    2483                ((long[]) theData)[i] = packedValue;
    2484        } // (int i = 0; i < len; i++)
    2485
    2486        return true;
    2487    } /* public static final boolean applyBitmask() */
    2488
    2489    /**
    2490     * Read HDF5 user block data into byte array.
    2491     *
    2492     * @param filename the HDF5 file from which to get the user block
    2493     *
    2494     * @return a byte array of user block, or null if there is user data.
    2495     */
    2496    public static byte[] getHDF5UserBlock(String filename) {
    2497        byte[] userBlock = null;
    2498
    2499        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
    2500            byte[] header = new byte[8];
    2501            long fileSize = raf.length();
    2502
    2503            // The super block is located by searching for the HDF5 file signature
    2504            // at byte offset 0, byte offset 512 and at successive locations in the
    2505            // file, each a multiple of two of the previous location, i.e. 0, 512,
    2506            // 1024, 2048, etc
    2507            long offset = 0;
    2508            boolean ish5 = false;
    2509            while (offset < fileSize) {
    2510                raf.seek(offset);
    2511                raf.read(header);
    2512
    2513                if ((header[0] == -119) && (header[1] == 72) && (header[2] == 68) && (header[3] == 70) && (header[4] == 13) && (header[5] == 10) && (header[6] == 26)
    2514                        && (header[7] == 10)) {
    2515                    ish5 = true;
    2516                    break; // find the end of user block
    2517                }
    2518                else {
    2519                    ish5 = false;
    2520                    if (offset == 0)
    2521                        offset = 512;
    2522                    else
    2523                        offset *= 2;
    2524                }
    2525            }
    2526
    2527            if (!ish5 || (offset == 0))
    2528                return null;
    2529
    2530            int blockSize = (int) offset;
    2531            userBlock = new byte[blockSize];
    2532            raf.seek(0);
    2533            raf.read(userBlock, 0, blockSize);
    2534        }
    2535        catch (Exception ex) {
    2536            userBlock = null;
    2537        }
    2538
    2539        return userBlock;
    2540    }
    2541
    2542    /**
    2543     * Write HDF5 user block data into byte array.
    2544     *
    2545     * @param fin the input filename
    2546     * @param fout the output filename
    2547     * @param buf  the data to write into the user block
    2548     *
    2549     * @return a byte array of user block, or null if there is user data.
    2550     */
    2551    public static boolean setHDF5UserBlock(String fin, String fout, byte[] buf) {
    2552        boolean ish5 = false;
    2553
    2554        if ((buf == null) || (buf.length <= 0))
    2555            return false;
    2556
    2557        File tmpFile = new File(fin);
    2558        if (!tmpFile.exists())
    2559            return false;
    2560
    2561        long offset = 0;
    2562        // find the end of user block for the input file
    2563        try (RandomAccessFile raf = new RandomAccessFile(fin, "r")) {
    2564            byte[] header = new byte[8];
    2565            long fileSize = raf.length();
    2566
    2567            // The super block is located by searching for the HDF5 file signature
    2568            // at byte offset 0, byte offset 512 and at successive locations in the
    2569            // file, each a multiple of two of the previous location, i.e. 0, 512,
    2570            // 1024, 2048, etc
    2571            while (offset < fileSize) {
    2572                raf.seek(offset);
    2573                raf.read(header);
    2574
    2575                if ((header[0] == -119) && (header[1] == 72) && (header[2] == 68) && (header[3] == 70) && (header[4] == 13) && (header[5] == 10) && (header[6] == 26)
    2576                        && (header[7] == 10)) {
    2577                    ish5 = true;
    2578                    break;
    2579                }
    2580                else {
    2581                    ish5 = false;
    2582                    if (offset == 0)
    2583                        offset = 512;
    2584                    else
    2585                        offset *= 2;
    2586                }
    2587            }
    2588        }
    2589        catch (Exception ex) {
    2590            return false;
    2591        }
    2592
    2593        if (!ish5)
    2594            return false;
    2595
    2596        int length = 0;
    2597        int bsize = 1024;
    2598        byte[] buffer;
    2599
    2600        try (BufferedInputStream bi = new BufferedInputStream(new FileInputStream(fin))) {
    2601            try (BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(fout))) {
    2602                // skip the header of original file
    2603                try {
    2604                    long count = bi.skip(offset);
    2605                    if (count != offset)
    2606                        log.debug("file skip actual:{} req:{}", count, offset);
    2607                }
    2608                catch (Exception ex) {
    2609                    // Empty on purpose
    2610                }
    2611
    2612                // write the header into the new file
    2613                try {
    2614                    bo.write(buf, 0, buf.length);
    2615                }
    2616                catch (Exception ex) {
    2617                    // Empty on purpose
    2618                }
    2619
    2620                // The super block space is allocated by offset 0, 512, 1024, 2048, etc
    2621                offset = 512;
    2622                while (offset < buf.length)
    2623                    offset *= 2;
    2624
    2625                int padSize = (int) (offset - buf.length);
    2626                if (padSize > 0) {
    2627                    byte[] padBuf = new byte[padSize];
    2628                    try {
    2629                        bo.write(padBuf, 0, padSize);
    2630                    }
    2631                    catch (Exception ex) {
    2632                        // Empty on purpose
    2633                    }
    2634                }
    2635
    2636                // copy the hdf5 file content from input file to the output file
    2637                buffer = new byte[bsize];
    2638                try {
    2639                    length = bi.read(buffer, 0, bsize);
    2640                }
    2641                catch (Exception ex) {
    2642                    length = 0;
    2643                }
    2644                while (length > 0) {
    2645                    try {
    2646                        bo.write(buffer, 0, length);
    2647                        length = bi.read(buffer, 0, bsize);
    2648                    }
    2649                    catch (Exception ex) {
    2650                        length = 0;
    2651                    }
    2652                }
    2653
    2654                try {
    2655                    bo.flush();
    2656                }
    2657                catch (Exception ex) {
    2658                    // Empty on purpose
    2659                }
    2660            }
    2661            catch (Exception ex) {
    2662                return false;
    2663            }
    2664        }
    2665        catch (Exception ex) {
    2666            return false;
    2667        }
    2668        return true;
    2669    }
    2670
    2671    /**
    2672     * look at the first 4 bytes of the file to see if it is an HDF4 file.
    2673     * byte[0]=14, byte[1]=3, byte[2]=19, byte[3]=1 or if it is a netCDF file
    2674     * byte[0]=67, byte[1]=68, byte[2]=70, byte[3]=1
    2675     *
    2676     * @param filename the file to test if HDF4
    2677     *
    2678     * @return true if the file is of type HDF4
    2679     */
    2680    public static boolean isHDF4(String filename) {
    2681        boolean ish4 = false;
    2682
    2683        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
    2684            byte[] header = new byte[4];
    2685            raf.read(header);
    2686
    2687            if ((header[0] == 14) && (header[1] == 3) && (header[2] == 19) && (header[3] == 1))
    2688                ish4 = true;
    2689            else
    2690                ish4 = false;
    2691        }
    2692        catch (Exception ex) {
    2693            return false;
    2694        }
    2695
    2696        return ish4;
    2697    }
    2698
    2699    /**
    2700     * look at the first 8 bytes of the file to see if it is an HDF5 file.
    2701     * byte[0]=-199 which is 137 in unsigned byte, byte[1]=72, byte[2]=68,
    2702     * byte[3]=70, byte[4]=13, byte[5]=10, byte[6]=26, byte[7]=10
    2703     *
    2704     * @param filename the file to test if HDF5
    2705     *
    2706     * @return true if the file is of type HDF5
    2707     */
    2708    public static boolean isHDF5(String filename) {
    2709        boolean ish5 = false;
    2710
    2711        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
    2712            byte[] header = new byte[8];
    2713            long fileSize = raf.length();
    2714
    2715            // The super block is located by searching for the HDF5 file signature
    2716            // at byte offset 0, byte offset 512 and at successive locations in the
    2717            // file, each a multiple of two of the previous location, i.e. 0, 512,
    2718            // 1024, 2048, etc
    2719            long offset = 0;
    2720            while (!ish5 && (offset < fileSize)) {
    2721                raf.seek(offset);
    2722                raf.read(header);
    2723
    2724                if ((header[0] == -119) && (header[1] == 72) && (header[2] == 68) && (header[3] == 70) && (header[4] == 13) && (header[5] == 10) && (header[6] == 26)
    2725                        && (header[7] == 10)) {
    2726                    ish5 = true;
    2727                }
    2728                else {
    2729                    ish5 = false;
    2730                    if (offset == 0)
    2731                        offset = 512;
    2732                    else
    2733                        offset *= 2;
    2734                }
    2735            }
    2736        }
    2737        catch (Exception ex) {
    2738            return false;
    2739        }
    2740
    2741        return ish5;
    2742    }
    2743
    2744    /**
    2745     * look at the first 4 bytes of the file to see if it is a netCDF file
    2746     * byte[0]=67, byte[1]=68, byte[2]=70, byte[3]=1 or
    2747     *
    2748     * @param filename the file to test if netcdf
    2749     *
    2750     * @return true if the file is of type netcdf
    2751     */
    2752    public static boolean isNetcdf(String filename) {
    2753        boolean isnc = false;
    2754
    2755        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
    2756
    2757        byte[] header = new byte[4];
    2758            raf.read(header);
    2759            // netCDF
    2760            if ((header[0] == 67) && (header[1] == 68) && (header[2] == 70) && (header[3] == 1))
    2761                isnc = true;
    2762            else
    2763                isnc = false;
    2764        }
    2765        catch (Exception ex) {
    2766            return false;
    2767        }
    2768
    2769        return isnc;
    2770    }
    2771
    2772    /**
    2773     * Launch default browser for a given URL.
    2774     *
    2775     * @param url
    2776     *            the URL to open.
    2777     *
    2778     * @throws Exception if a failure occurred
    2779     */
    2780    public static final void launchBrowser(String url) throws Exception {
    2781        String os = System.getProperty("os.name");
    2782        Runtime runtime = Runtime.getRuntime();
    2783
    2784        // Block for Windows Platform
    2785        if (os.startsWith("Windows")) {
    2786            String cmd = "rundll32 url.dll,FileProtocolHandler " + url;
    2787
    2788            if (new File(url).exists())
    2789                cmd = "cmd /c start \"\" \"" + url + "\"";
    2790            runtime.exec(cmd);
    2791        }
    2792        // Block for Mac OS
    2793        else if (os.startsWith("Mac OS")) {
    2794            Class<?> fileMgr = Class.forName("com.apple.eio.FileManager");
    2795            Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[] { String.class });
    2796
    2797            // local file
    2798            if (new File(url).exists())
    2799                url = "file://" + url;
    2800
    2801            openURL.invoke(null, new Object[] { url });
    2802        }
    2803        // Block for UNIX Platform
    2804        else {
    2805            String[] browsers = { "firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape" };
    2806            String browser = null;
    2807            for (int count = 0; count < browsers.length && browser == null; count++)
    2808                if (runtime.exec(new String[] { "which", browsers[count] }).waitFor() == 0)
    2809                    browser = browsers[count];
    2810            if (browser == null)
    2811                throw new Exception("Could not find web browser");
    2812            else
    2813                runtime.exec(new String[] { browser, url });
    2814        }
    2815    }
    2816
    2817    /** Create a new HDF file with default file creation properties
    2818     *
    2819     * @param filename
    2820     *          the file to create
    2821     * @param dir
    2822     *          the directory for file
    2823     * @param type
    2824     *          the type of the file
    2825     * @param openFiles
    2826     *          the list of already opened files
    2827     *
    2828     * @return the FileFormat instance of the newly created file
    2829     *
    2830     * @throws Exception if a failure occurred
    2831     */
    2832    public static FileFormat createNewFile(String filename, String dir,
    2833            String type, List<FileFormat> openFiles) throws Exception {
    2834        log.trace("createNewFile: {} start", filename);
    2835        File f = new File(filename);
    2836
    2837        String fname = f.getAbsolutePath();
    2838        if (fname == null)
    2839            return null;
    2840
    2841        fname = fname.trim();
    2842        if ((fname == null) || (fname.length() == 0))
    2843            throw new Exception("Invalid file name.");
    2844
    2845        String extensions = FileFormat.getFileExtensions();
    2846        boolean noExtension = true;
    2847        if ((extensions != null) && (extensions.length() > 0)) {
    2848            java.util.StringTokenizer currentExt = new java.util.StringTokenizer(extensions, ",");
    2849            String extension = "";
    2850            String tmpFilename = fname.toLowerCase();
    2851            while (currentExt.hasMoreTokens() && noExtension) {
    2852                extension = currentExt.nextToken().trim().toLowerCase();
    2853                noExtension = !tmpFilename.endsWith("." + extension);
    2854            }
    2855        }
    2856
    2857        if (noExtension) {
    2858            if (type.equals(FileFormat.FILE_TYPE_HDF4)) {
    2859                fname += ".hdf";
    2860                f = new File(fname);
    2861            }
    2862            else if (type.equals(FileFormat.FILE_TYPE_HDF5)) {
    2863                fname += ".h5";
    2864                f = new File(fname);
    2865            }
    2866        }
    2867
    2868        if (f.exists() && f.isDirectory())
    2869            throw new Exception("File is a directory.");
    2870        log.trace("createNewFile: {} not a directory", filename);
    2871
    2872        File pfile = f.getParentFile();
    2873        if (pfile == null) {
    2874            fname = dir + File.separator + fname;
    2875            f = new File(fname);
    2876        }
    2877        else if (!pfile.exists()) {
    2878            throw new Exception("File path does not exist at\n" + pfile.getPath());
    2879        }
    2880
    2881        // check if the file is in use
    2882        log.trace("createNewFile: {} check if the file is in use", filename);
    2883        if (openFiles != null) {
    2884            FileFormat theFile = null;
    2885            Iterator<FileFormat> iterator = openFiles.iterator();
    2886            while (iterator.hasNext()) {
    2887                theFile = iterator.next();
    2888                if (theFile.getFilePath().equals(fname))
    2889                    throw new Exception("Unable to create the new file. \nThe file is being used.");
    2890            }
    2891        }
    2892
    2893        if (f.exists()) {
    2894            log.trace("createNewFile: {} file exists", filename);
    2895
    2896            if (!MessageDialog.openConfirm(display.getShells()[0], "Create New File",
    2897                    "File exists. Do you want to replace it?"))
    2898                return null;
    2899        }
    2900
    2901        try {
    2902            int aFlag = FileFormat.FILE_CREATE_DELETE;
    2903            if (!ViewProperties.getEarlyLib().equalsIgnoreCase("Latest")) {
    2904                aFlag = FileFormat.FILE_CREATE_DELETE | FileFormat.FILE_CREATE_EARLY_LIB;
    2905                FileFormat.getFileFormat(type).setNewLibBounds(ViewProperties.getEarlyLib(), ViewProperties.getLateLib());
    2906            }
    2907            log.trace("createNewFile: {} FileFormat create", filename);
    2908            return FileFormat.getFileFormat(type).createFile(fname, aFlag);
    2909        }
    2910        catch (Exception ex) {
    2911            throw new Exception(ex.getMessage());
    2912        }
    2913    }
    2914
    2915    /**
    2916     * Check and find a non-exist file.
    2917     *
    2918     * @param path
    2919     *            -- the path that the new file will be checked.
    2920     * @param ext
    2921     *            -- the extention of the new file.
    2922     *
    2923     * @return -- the new file.
    2924     */
    2925    public static final File checkNewFile(String path, String ext) {
    2926        File file = new File(path + "new" + ext);
    2927        int i = 1;
    2928
    2929        while (file.exists()) {
    2930            file = new File(path + "new" + i + ext);
    2931            i++;
    2932        }
    2933
    2934        return file;
    2935    }
    2936
    2937    /**
    2938     * Check if a given number if NaN or INF.
    2939     *
    2940     * @param val
    2941     *            the number to be checked
    2942     *
    2943     * @return true if the number is Nan or INF; otherwise, false.
    2944     */
    2945    public static final boolean isNaNINF(double val) {
    2946        return (Double.isNaN(val) || val == Float.NEGATIVE_INFINITY || val == Float.POSITIVE_INFINITY || val == Double.NEGATIVE_INFINITY || val == Double.POSITIVE_INFINITY);
    2947    }
    2948
    2949    /**
    2950     * Since Java does not allow array indices to be larger than int type, check the
    2951     * given value to see if it is within the valid range of a Java int.
    2952     *
    2953     * @param value
    2954     *            The value to check
    2955     *
    2956     * @return false if the value is outside the range of a Java int, true
    2957     *         otherwise.
    2958     */
    2959    public static boolean checkValidJavaArrayIndex(final long value) {
    2960        return (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE);
    2961    }
    2962
    2963    /**
    2964     * Show an SWT error dialog with the given error message.
    2965     * @param parent
    2966     *           The parent Shell of the MessageDialog
    2967     * @param title
    2968     *           The title to set for the MessageDialog
    2969     * @param errorMsg
    2970     *           The error message to display in the MessageDialog
    2971     */
    2972    public static void showError(Shell parent, String title, String errorMsg) {
    2973        String dlgTitlePrefix = "";
    2974        String dlgTitleSuffix = (title == null) ? "" : title;
    2975
    2976        if (parent != null) {
    2977            dlgTitlePrefix = parent.getText();
    2978            if (dlgTitlePrefix.length() > 0)
    2979                dlgTitlePrefix += " - ";
    2980        }
    2981
    2982        MessageDialog.openError(parent, dlgTitlePrefix + dlgTitleSuffix, (errorMsg == null) ? "UNKNOWN" : errorMsg);
    2983    }
    2984
    2985    /**
    2986     * Show an SWT Information dialog with the given message.
    2987     * @param parent
    2988     *           The parent Shell of the MessageDialog
    2989     * @param title
    2990     *           The title to set for the MessageDialog
    2991     * @param infoMsg
    2992     *           The message to display in the MessageDialog
    2993     */
    2994    public static void showInformation(Shell parent, String title, String infoMsg) {
    2995        String dlgTitlePrefix = "";
    2996        String dlgTitleSuffix = (title == null) ? "" : title;
    2997
    2998        if (parent != null) {
    2999            dlgTitlePrefix = parent.getText();
    3000            if (dlgTitlePrefix.length() > 0)
    3001                dlgTitlePrefix += " - ";
    3002        }
    3003
    3004        MessageDialog.openInformation(parent, dlgTitlePrefix + dlgTitleSuffix, (infoMsg == null) ? "UNKNOWN" : infoMsg);
    3005    }
    3006
    3007    /**
    3008     * Show an SWT Confirm dialog with the given message.
    3009     *
    3010     * @param parent
    3011     *            The parent Shell of the MessageDialog
    3012     * @param title
    3013     *            The title to set for the MessageDialog
    3014     * @param confMsg
    3015     *            The message to display in the MessageDialog
    3016     * @return The status of the dialog after closing
    3017     */
    3018    public static boolean showConfirm(Shell parent, String title, String confMsg) {
    3019        String dlgTitlePrefix = "";
    3020        String dlgTitleSuffix = (title == null) ? "" : title;
    3021
    3022        if (parent != null) {
    3023            dlgTitlePrefix = parent.getText();
    3024            if (dlgTitlePrefix.length() > 0)
    3025                dlgTitlePrefix += " - ";
    3026        }
    3027
    3028        return MessageDialog.openConfirm(parent, dlgTitlePrefix + dlgTitleSuffix, (confMsg == null) ? "UNKNOWN" : confMsg);
    3029    }
    3030
    3031    /**
    3032     * Show an SWT Warning dialog with the given message.
    3033     * @param parent
    3034     *           The parent Shell of the MessageDialog
    3035     * @param title
    3036     *           The title to set for the MessageDialog
    3037     * @param warnMsg
    3038     *           The message to display in the MessageDialog
    3039     */
    3040    public static void showWarning(Shell parent, String title, String warnMsg) {
    3041        String dlgTitlePrefix = "";
    3042        String dlgTitleSuffix = (title == null) ? "" : title;
    3043
    3044        if (parent != null) {
    3045            dlgTitlePrefix = parent.getText();
    3046            if (dlgTitlePrefix.length() > 0)
    3047                dlgTitlePrefix += " - ";
    3048        }
    3049
    3050        MessageDialog.openWarning(parent, dlgTitlePrefix + dlgTitleSuffix, (warnMsg == null) ? "UNKNOWN" : warnMsg);
    3051    }
    3052}