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 byte[][] getPalette() {
248        return palette;
249    }
250
251    /**
252     * Sets the palette for this dataset.
253     *
254     * @param pal
255     *            the 2D palette byte array.
256     */
257    public final void setPalette(byte[][] pal) {
258        palette = pal;
259    }
260
261    /**
262     * Reads a specific image palette from file.
263     *
264     * A scalar dataset may have multiple palettes attached to it. readPalette(int idx) returns a specific palette
265     * identified by its index.
266     *
267     * @param idx
268     *            the index of the palette to read.
269     *
270     * @return the image palette
271     */
272    public byte[][] readPalette(int idx) {
273        return null;
274    }
275
276    /**
277     * Get the name of a specific image palette from file.
278     *
279     * A scalar dataset may have multiple palettes attached to it. getPaletteName(int idx) returns the name of a
280     * specific palette identified by its index.
281     *
282     * @param idx
283     *            the index of the palette to retrieve the name.
284     *
285     * @return The name of the palette
286     */
287    public String getPaletteName(int idx) {
288        String paletteName = "Default ";
289        if (idx != 0)
290            paletteName = "Default " + idx;
291        return paletteName;
292    }
293
294    /**
295     * Get the number of pallettes for this object.
296     *
297     * @return the number of palettes if it has any,
298     *         0 if there is no palette attribute attached to this dataset.
299     */
300    public int getNumberOfPalettes() {
301        return 0;
302    }
303
304    /**
305     * Returns true if this dataset is an image.
306     *
307     * For all Images, they must have an attribute called "CLASS". The value of this attribute is "IMAGE". For more
308     * details, read <a href="https://support.hdfgroup.org/HDF5/doc/ADGuide/ImageSpec.html"> HDF5 Image and Palette Specification</a>
309     *
310     * @return true if the dataset is an image; otherwise, returns false.
311     */
312    public final boolean isImage() {
313        return isImage;
314    }
315
316    /**
317     * Returns true if this dataset is displayed as an image.
318     *
319     * A ScalarDS can be displayed as an image or a spreadsheet in a table.
320     *
321     * @return true if this dataset is displayed as an image; otherwise, returns false.
322     */
323    public final boolean isImageDisplay() {
324
325        return isImageDisplay;
326    }
327
328    /**
329     * Returns true if this dataset is displayed as an image with default image order.
330     *
331     * A ScalarDS can be displayed as an image with different orders of dimensions.
332     *
333     * @return true if this dataset is displayed as an image with default image order; otherwise, returns false.
334     */
335    public final boolean isDefaultImageOrder() {
336        return isDefaultImageOrder;
337    }
338
339    /**
340     * Sets the flag to display the dataset as an image.
341     *
342     * @param b
343     *            if b is true, display the dataset as an image
344     */
345    public final void setIsImageDisplay(boolean b) {
346        isImageDisplay = b;
347    }
348
349    /**
350     * Sets the flag to indicate this dataset is an image.
351     *
352     * @param b
353     *            if b is true, the dataset is an image.
354     */
355    public final void setIsImage(boolean b) {
356        isImage = b;
357    }
358
359    /**
360     * Sets data range for an image.
361     *
362     * @param min
363     *            the data range start.
364     * @param max
365     *            the data range end.
366     */
367    public final void setImageDataRange(double min, double max) {
368        if (max <= min)
369            return;
370
371        if (imageDataRange == null)
372            imageDataRange = new double[2];
373
374        imageDataRange[0] = min;
375        imageDataRange[1] = max;
376    }
377
378    /**
379     * Add a value that will be filtered out in an image.
380     *
381     * @param x
382     *            value to be filtered
383     */
384    public void addFilteredImageValue(Number x) {
385        Iterator<Number> it = filteredImageValues.iterator();
386        while (it.hasNext()) {
387            if (it.next().toString().equals(x.toString()))
388                return;
389        }
390
391        filteredImageValues.add(x);
392    }
393
394    /**
395     * Get a list of values that will be filtered out in an image.
396     *
397     * @return the list of Image values
398     */
399    public List<Number> getFilteredImageValues() {
400        return filteredImageValues;
401    }
402
403    /**
404     * @return true if this dataset is a true color image.
405     *
406     */
407
408    public final boolean isTrueColor() {
409        return isTrueColor;
410    }
411
412    /**
413     * Returns the interlace mode of a true color image (RGB).
414     *
415     * Valid values:
416     *
417     * <pre>
418     *     INTERLACE_PIXEL -- RGB components are contiguous, i.e. rgb, rgb, rgb, ...
419     *     INTERLACE_LINE -- each RGB component is stored as a scan line
420     *     INTERLACE_PLANE -- each RGB component is stored as a plane
421     * </pre>
422     *
423     * @return the interlace mode of a true color image (RGB).
424     */
425    public final int getInterlace() {
426        return interlace;
427    }
428
429    /**
430     * Returns the (min, max) pair of image data range.
431     *
432     * @return the (min, max) pair of image data range.
433     */
434    public double[] getImageDataRange() {
435        return imageDataRange;
436    }
437
438    /**
439     * Returns the fill values for the dataset.
440     *
441     * @return the fill values for the dataset.
442     */
443    @Override
444    public final Object getFillValue() {
445        return fillValue;
446    }
447}