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