    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.object;
    016
    017import java.util.Iterator;
    018import java.util.List;
    019import java.util.Vector;
    020
    021import org.slf4j.Logger;
    022import org.slf4j.LoggerFactory;
    023
    024/**
    025 * A scalar dataset is a multiple dimension array of scalar points. The Datatype of a scalar dataset must be an atomic
    026 * datatype. Common datatypes of scalar datasets include char, byte, short, int, long, float, double and string.
    027 *
    028 * A ScalarDS can be an image or spreadsheet data. ScalarDS defines methods to deal with both images and
    029 * spreadsheets.
    030 *
    031 * ScalarDS is an abstract class. Current implementing classes are the H4SDS, H5GRImage and H5ScalarDS.
    032 *
    033 * @version 1.1 9/4/2007
    034 * @author Peter X. Cao
    035 */
    036public abstract class ScalarDS extends Dataset
    037{
    038    private static final long serialVersionUID = 8925371455928203981L;
    039
    040    private static final Logger log = LoggerFactory.getLogger(ScalarDS.class);
    041
    042    /************************************************************
    043     * The following constant strings are copied from * https://hdfgroup.github.io/hdf5/_i_m_g.html * to make the
    044     * definition consistent with the image specs. *
    045     ************************************************************/
    046
    047    /**
    048     * Indicates that the pixel RGB values are contiguous.
    049     */
    050    public static final int INTERLACE_PIXEL = 0;
    051
    052    /** Indicates that each pixel component of RGB is stored as a scan line. */
    053    public static final int INTERLACE_LINE = 1;
    054
    055    /** Indicates that each pixel component of RGB is stored as a plane. */
    056    public static final int INTERLACE_PLANE = 2;
    057
    058    /**
    059     * The interlace mode of the stored raster image data. Valid values are INTERLACE_PIXEL, INTERLACE_LINE and
    060     * INTERLACE_PLANE.
    061     */
    062    protected int interlace;
    063
    064    /**
    065     * The min-max range of image data values. For example, [0, 255] indicates the min is 0, and the max is 255.
    066     */
    067    protected double[] imageDataRange;
    068
    069    /**
    070     * The indexed RGB color model with 256 colors.
    071     *
    072     * The palette values are stored in a two-dimensional byte array and arrange by color components of red, green and
    073     * blue. palette[][] = byte[3][256], where, palette[0][], palette[1][] and palette[2][] are the red, green and blue
    074     * components respectively.
    075     */
    076    protected byte[][] palette;
    077
    078    /**
    079     * True if this dataset is a true color image.
    080     */
    081    protected boolean isTrueColor;
    082
    083    /**
    084     * Flag to indicate is the original unsigned C data is converted.
    085     */
    086    protected boolean unsignedConverted;
    087
    088    /** The fill value of the dataset. */
    089    protected Object fillValue = null;
    090
    091    /** The list of filtered image values. */
    092    private List<Number> filteredImageValues;
    093
    094    /** Flag to indicate if the dataset is displayed as an image. */
    095    protected boolean isImageDisplay;
    096
    097    /**
    098     * Flag to indicate if the dataset is displayed as an image with default order of dimensions.
    099     */
    100    protected boolean isDefaultImageOrder;
    101
    102    /**
    103     * Flag to indicate if the FillValue is converted from unsigned C.
    104     */
    105    public boolean isFillValueConverted;
    106
    107    /**
    108     * Constructs an instance of a ScalarDS with specific name and path. An HDF data object must have a name. The path
    109     * is the group path starting from the root.
    110     *
    111     * For example, in H5ScalarDS(h5file, "dset", "/arrays/"), "dset" is the name of the dataset, "/arrays" is the group
    112     * path of the dataset.
    113     *
    114     * @param theFile
    115     *            the file that contains the data object.
    116     * @param theName
    117     *            the name of the data object, e.g. "dset".
    118     * @param thePath
    119     *            the full path of the data object, e.g. "/arrays/".
    120     */
    121    public ScalarDS(FileFormat theFile, String theName, String thePath) {
    122        this(theFile, theName, thePath, null);
    123    }
    124
    125    /**
    126     * @deprecated Not for public use in the future.<br>
    127     *             Using {@link #ScalarDS(FileFormat, String, String)}
    128     *
    129     * @param theFile
    130     *            the file that contains the data object.
    131     * @param theName
    132     *            the name of the data object, e.g. "dset".
    133     * @param thePath
    134     *            the full path of the data object, e.g. "/arrays/".
    135     * @param oid
    136     *            the object id of the data object.
    137     */
    138    @Deprecated
    139    public ScalarDS(FileFormat theFile, String theName, String thePath, long[] oid) {
    140        super(theFile, theName, thePath, oid);
    141
    142        palette = null;
    143        isImage = false;
    144        isTrueColor = false;
    145        isText = false;
    146        interlace = -1;
    147        imageDataRange = null;
    148        isImageDisplay = false;
    149        isDefaultImageOrder = true;
    150        isFillValueConverted = false;
    151        filteredImageValues = new Vector<>();
    152    }
    153
    154    /**
    155     * Clears the current data buffer in memory and forces the next read() to load
    156     * the data from file.
    157     *
    158     * The function read() loads data from file into memory only if the data is not
    159     * read. If data is already in memory, read() just returns the memory buffer.
    160     * Sometimes we want to force read() to re-read data from file. For example,
    161     * when the selection is changed, we need to re-read the data.
    162     *
    163     * @see #getData()
    164     * @see #read()
    165     */
    166    @Override
    167    public void clearData() {
    168        super.clearData();
    169        unsignedConverted = false;
    170    }
    171
    172    /**
    173     * Converts the data values of this dataset to appropriate Java integer if they are unsigned integers.
    174     *
    175     * @see hdf.object.Dataset#convertToUnsignedC(Object)
    176     * @see hdf.object.Dataset#convertFromUnsignedC(Object, Object)
    177     *
    178     * @return the converted data buffer.
    179     */
    180    @Override
    181    public Object convertFromUnsignedC() {
    182        // keep a copy of original buffer and the converted buffer
    183        // so that they can be reused later to save memory
    184        log.trace("convertFromUnsignedC(): unsigned={}", getDatatype().isUnsigned());
    185        if ((data != null) && getDatatype().isUnsigned() && !unsignedConverted) {
    186            log.trace("convertFromUnsignedC(): convert");
    187            originalBuf = data;
    188            convertedBuf = convertFromUnsignedC(originalBuf, convertedBuf);
    189            data = convertedBuf;
    190            unsignedConverted = true;
    191
    192            if (fillValue != null) {
    193                if (!isFillValueConverted) {
    194                    fillValue = convertFromUnsignedC(fillValue, null);
    195                    isFillValueConverted = true;
    196                }
    197            }
    198        }
    199
    200        return data;
    201    }
    202
    203    /**
    204     * Converts Java integer data of this dataset back to unsigned C-type integer data if they are unsigned integers.
    205     *
    206     * @see hdf.object.Dataset#convertToUnsignedC(Object)
    207     * @see hdf.object.Dataset#convertToUnsignedC(Object, Object)
    208     * @see #convertFromUnsignedC(Object data_in)
    209     *
    210     * @return the converted data buffer.
    211     */
    212    @Override
    213    public Object convertToUnsignedC() {
    214        // keep a copy of original buffer and the converted buffer
    215        // so that they can be reused later to save memory
    216        log.trace("convertToUnsignedC(): unsigned={}", getDatatype().isUnsigned());
    217        if ((data != null) && getDatatype().isUnsigned()) {
    218            log.trace("convertToUnsignedC(): convert");
    219            convertedBuf = data;
    220            originalBuf = convertToUnsignedC(convertedBuf, originalBuf);
    221            data = originalBuf;
    222        }
    223
    224        return data;
    225    }
    226
    227    /**
    228     * Returns the palette of this scalar dataset or null if palette does not exist.
    229     *
    230     * A Scalar dataset can be displayed as spreadsheet data or an image. When a scalar dataset is displayed as an
    231     * image, the palette or color table may be needed to translate a pixel value to color components (for example, red,
    232     * green, and blue). Some scalar datasets have no palette and some datasets have one or more than one palettes. If
    233     * an associated palette exists but is not loaded, this interface retrieves the palette from the file and returns the
    234     * palette. If the palette is loaded, it returns the palette. It returns null if there is no palette associated with
    235     * the dataset.
    236     *
    237     * Current implementation only supports palette model of indexed RGB with 256 colors. Other models such as
    238     * YUV", "CMY", "CMYK", "YCbCr", "HSV will be supported in the future.
    239     *
    240     * The palette values are stored in a two-dimensional byte array and are arranges by color components of red, green and
    241     * blue. palette[][] = byte[3][256], where, palette[0][], palette[1][] and palette[2][] are the red, green and blue
    242     * components respectively.
    243     *
    244     * Sub-classes have to implement this interface. HDF4 and HDF5 images use different libraries to retrieve the
    245     * associated palette.
    246     *
    247     * @return the 2D palette byte array.
    248     */
    249    public byte[][] getPalette() {
    250        return palette;
    251    }
    252
    253    /**
    254     * Sets the palette for this dataset.
    255     *
    256     * @param pal
    257     *            the 2D palette byte array.
    258     */
    259    public final void setPalette(byte[][] pal) {
    260        palette = pal;
    261    }
    262
    263    /**
    264     * Reads a specific image palette from file.
    265     *
    266     * A scalar dataset may have multiple palettes attached to it. readPalette(int idx) returns a specific palette
    267     * identified by its index.
    268     *
    269     * @param idx
    270     *            the index of the palette to read.
    271     *
    272     * @return the image palette
    273     */
    274    public byte[][] readPalette(int idx) {
    275        return null;
    276    }
    277
    278    /**
    279     * Get the name of a specific image palette from file.
    280     *
    281     * A scalar dataset may have multiple palettes attached to it. getPaletteName(int idx) returns the name of a
    282     * specific palette identified by its index.
    283     *
    284     * @param idx
    285     *            the index of the palette to retrieve the name.
    286     *
    287     * @return The name of the palette
    288     */
    289    public String getPaletteName(int idx) {
    290        String paletteName = "Default ";
    291        if (idx != 0)
    292            paletteName = "Default " + idx;
    293        return paletteName;
    294    }
    295
    296    /**
    297     * Get the number of pallettes for this object.
    298     *
    299     * @return the number of palettes if it has any,
    300     *         0 if there is no palette attribute attached to this dataset.
    301     */
    302    public int getNumberOfPalettes() {
    303        return 0;
    304    }
    305
    306    /**
    307     * Returns true if this dataset is an image.
    308     *
    309     * For all Images, they must have an attribute called "CLASS". The value of this attribute is "IMAGE". For more
    310     * details, read <a href="https://hdfgroup.github.io/hdf5/_i_m_g.html"> HDF5 Image and Palette Specification</a>
    311     *
    312     * @return true if the dataset is an image; otherwise, returns false.
    313     */
    314    public final boolean isImage() {
    315        return isImage;
    316    }
    317
    318    /**
    319     * Returns true if this dataset is displayed as an image.
    320     *
    321     * A ScalarDS can be displayed as an image or a spreadsheet in a table.
    322     *
    323     * @return true if this dataset is displayed as an image; otherwise, returns false.
    324     */
    325    public final boolean isImageDisplay() {
    326
    327        return isImageDisplay;
    328    }
    329
    330    /**
    331     * Returns true if this dataset is displayed as an image with default image order.
    332     *
    333     * A ScalarDS can be displayed as an image with different orders of dimensions.
    334     *
    335     * @return true if this dataset is displayed as an image with default image order; otherwise, returns false.
    336     */
    337    public final boolean isDefaultImageOrder() {
    338        return isDefaultImageOrder;
    339    }
    340
    341    /**
    342     * Sets the flag to display the dataset as an image.
    343     *
    344     * @param b
    345     *            if b is true, display the dataset as an image
    346     */
    347    public final void setIsImageDisplay(boolean b) {
    348        isImageDisplay = b;
    349    }
    350
    351    /**
    352     * Sets the flag to indicate this dataset is an image.
    353     *
    354     * @param b
    355     *            if b is true, the dataset is an image.
    356     */
    357    public final void setIsImage(boolean b) {
    358        isImage = b;
    359    }
    360
    361    /**
    362     * Sets data range for an image.
    363     *
    364     * @param min
    365     *            the data range start.
    366     * @param max
    367     *            the data range end.
    368     */
    369    public final void setImageDataRange(double min, double max) {
    370        if (max <= min)
    371            return;
    372
    373        if (imageDataRange == null)
    374            imageDataRange = new double[2];
    375
    376        imageDataRange[0] = min;
    377        imageDataRange[1] = max;
    378    }
    379
    380    /**
    381     * Add a value that will be filtered out in an image.
    382     *
    383     * @param x
    384     *            value to be filtered
    385     */
    386    public void addFilteredImageValue(Number x) {
    387        Iterator<Number> it = filteredImageValues.iterator();
    388        while (it.hasNext()) {
    389            if (it.next().toString().equals(x.toString()))
    390                return;
    391        }
    392
    393        filteredImageValues.add(x);
    394    }
    395
    396    /**
    397     * Get a list of values that will be filtered out in an image.
    398     *
    399     * @return the list of Image values
    400     */
    401    public List<Number> getFilteredImageValues() {
    402        return filteredImageValues;
    403    }
    404
    405    /**
    406     * @return true if this dataset is a true color image.
    407     *
    408     */
    409
    410    public final boolean isTrueColor() {
    411        return isTrueColor;
    412    }
    413
    414    /**
    415     * Returns the interlace mode of a true color image (RGB).
    416     *
    417     * Valid values:
    418     *
    419     * <pre>
    420     *     INTERLACE_PIXEL -- RGB components are contiguous, i.e. rgb, rgb, rgb, ...
    421     *     INTERLACE_LINE -- each RGB component is stored as a scan line
    422     *     INTERLACE_PLANE -- each RGB component is stored as a plane
    423     * </pre>
    424     *
    425     * @return the interlace mode of a true color image (RGB).
    426     */
    427    public final int getInterlace() {
    428        return interlace;
    429    }
    430
    431    /**
    432     * Returns the (min, max) pair of image data range.
    433     *
    434     * @return the (min, max) pair of image data range.
    435     */
    436    public double[] getImageDataRange() {
    437        return imageDataRange;
    438    }
    439
    440    /**
    441     * Returns the fill values for the dataset.
    442     *
    443     * @return the fill values for the dataset.
    444     */
    445    @Override
    446    public final Object getFillValue() {
    447        return fillValue;
    448    }
    449}