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